<commit_before>package ai

import (
	"log"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/hysios/apiai-go"
)

type ApiAiOrder struct {
	AiResult apiai.Result
}

func (aa ApiAiOrder) Items() []Item {
	products := aa.Products()
	quantities := aa.Quantities()

	for i, q := range quantities {
		if len(products) >= i+1 {
			products[i].Quantity = q.Quantity
		}
	}

	return products
}

func (aa ApiAiOrder) Products() []Item {
	return aa.ExtractProducts("products")
}

func (aa ApiAiOrder) Quantities() []Item {
	return aa.ExtractQuantities("quantity")
}

func (aa ApiAiOrder) GiftItems() []Item {
	gifts := aa.GiftProducts()
	quantities := aa.GiftQuantities()

	for i, q := range quantities {
		if len(gifts) >= i+1 {
			gifts[i].Quantity = q.Quantity
		}
	}

	return gifts
}

func (aa ApiAiOrder) GiftProducts() []Item {
	return aa.ExtractProducts("gifts")
}

func (aa ApiAiOrder) GiftQuantities() []Item {
	return aa.ExtractQuantities("giftNumber")
}

func (aa ApiAiOrder) Address() string {
	if a, exist := aa.AiResult.Params["street-address"]; exist {
		return a.(string) + aa.Customer()
	}

	return aa.Customer()
}

func (aa ApiAiOrder) Customer() string {
	if c, exist := aa.AiResult.Params["customer"]; exist {
		return c.(string)
	}

	return ""
}

func (aa ApiAiOrder) Time() time.Time {
	if t, exist := aa.AiResult.Params["date"]; exist {
		if aiTime, err := time.Parse("2006-01-02", t.(string)); err == nil {
			return aiTime
		}
	}

	return time.Time{}
}

func (aa ApiAiOrder) Fulfiled() bool {
	return true
}

func (aa ApiAiOrder) Note() string {
	if imp, exist := aa.AiResult.Params["important"]; exist {
		return imp.(string)
	}

	return ""
}

func (aa ApiAiOrder) ExtractProducts(t string) []Item {
	result := make([]Item, 0, 50)

	if products, exist := aa.AiResult.Params[t]; exist {
		ps := reflect.ValueOf(products)

		for i := 0; i < ps.Len(); i++ {
			p := ps.Index(i)
			name := p.Interface().(string)
			item := Item{Product: name}
			result = append(result, item)
		}
	}

	return result
}

func (aa ApiAiOrder) ExtractQuantities(t string) []Item {
	result := make([]Item, 0, 50)

	if quantities, exist := aa.AiResult.Params[t]; exist {
		qs := reflect.ValueOf(quantities)

		for i := 0; i < qs.Len(); i++ {
			q := qs.Index(i).Interface()

			switch t := q.(type) {
			case string:
				qs := q.(string)
				qi := extractQuantity(qs)
				item := Item{Quantity: qi}
				result = append(result, item)
			case float64:
				qf := q.(float64)
				item := Item{Quantity: int(qf)}
				result = append(result, item)
			default:
				log.Println("Unknown Quantity type: %v", t)
			}
		}
	}

	return result
}

func extractQuantity(s string) int {
	nums := strings.TrimFunc(s, TrimToNum)
	numsCgd := DBCtoSBC(nums)

	if len(numsCgd) > 0 {
		q, _ := strconv.Atoi(numsCgd)
		return q
	} else {
		return 0
	}
	// re := regexp.MustCompile("[0-9]+")
}

func TrimToNum(r rune) bool {
	if n := r - '0'; n >= 0 && n <= 9 {
		return false
	} else if m := r - '０'; m >= 0 && m <= 9 {
		return false
	}

	return true
}

func DBCtoSBC(s string) string {
	retstr := ""
	for _, i := range s {
		inside_code := i
		if inside_code == 12288 {
			inside_code = 32
		} else {
			inside_code -= 65248
		}
		if inside_code < 32 || inside_code > 126 {
			retstr += string(i)
		} else {
			retstr += string(inside_code)
		}
	}
	return retstr
}
<commit_msg>deal with address list<commit_after>package ai

import (
	"log"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/hysios/apiai-go"
)

type ApiAiOrder struct {
	AiResult apiai.Result
}

func (aa ApiAiOrder) Items() []Item {
	products := aa.Products()
	quantities := aa.Quantities()

	for i, q := range quantities {
		if len(products) >= i+1 {
			products[i].Quantity = q.Quantity
		}
	}

	return products
}

func (aa ApiAiOrder) Products() []Item {
	return aa.ExtractProducts("products")
}

func (aa ApiAiOrder) Quantities() []Item {
	return aa.ExtractQuantities("quantity")
}

func (aa ApiAiOrder) GiftItems() []Item {
	gifts := aa.GiftProducts()
	quantities := aa.GiftQuantities()

	for i, q := range quantities {
		if len(gifts) >= i+1 {
			gifts[i].Quantity = q.Quantity
		}
	}

	return gifts
}

func (aa ApiAiOrder) GiftProducts() []Item {
	return aa.ExtractProducts("gifts")
}

func (aa ApiAiOrder) GiftQuantities() []Item {
	return aa.ExtractQuantities("giftNumber")
}

func (aa ApiAiOrder) Address() string {
	if a, exist := aa.AiResult.Params["street-address"]; exist {

		rt := reflect.TypeOf(a)
		vals := reflect.ValueOf(a)

		switch rt.Kind() {
		case reflect.Slice:
			if vals.Len() > 0 {
				return vals.Index(0).Interface().(string) + aa.Customer()
			}
		case reflect.Array:
			if vals.Len() > 0 {
				return vals.Index(0).Interface().(string) + aa.Customer()
			}
		case reflect.String:
			return vals.Interface().(string) + aa.Customer()
		}
	}

	return aa.Customer()
}

func (aa ApiAiOrder) Customer() string {
	if c, exist := aa.AiResult.Params["customer"]; exist {

		rt := reflect.TypeOf(c)
		vals := reflect.ValueOf(c)

		switch rt.Kind() {
		case reflect.Slice:
			if vals.Len() > 0 {
				return vals.Index(0).Interface().(string) + aa.Customer()
			}
		case reflect.Array:
			if vals.Len() > 0 {
				return vals.Index(0).Interface().(string) + aa.Customer()
			}
		case reflect.String:
			return vals.Interface().(string)
		}
	}

	return ""
}

func (aa ApiAiOrder) Time() time.Time {
	if t, exist := aa.AiResult.Params["date"]; exist {
		if aiTime, err := time.Parse("2006-01-02", t.(string)); err == nil {
			return aiTime
		}
	}

	return time.Time{}
}

func (aa ApiAiOrder) Fulfiled() bool {
	return true
}

func (aa ApiAiOrder) Note() string {
	if imp, exist := aa.AiResult.Params["important"]; exist {
		return imp.(string)
	}

	return ""
}

func (aa ApiAiOrder) ExtractProducts(t string) []Item {
	result := make([]Item, 0, 50)

	if products, exist := aa.AiResult.Params[t]; exist {
		ps := reflect.ValueOf(products)

		for i := 0; i < ps.Len(); i++ {
			p := ps.Index(i)
			name := p.Interface().(string)
			item := Item{Product: name}
			result = append(result, item)
		}
	}

	return result
}

func (aa ApiAiOrder) ExtractQuantities(t string) []Item {
	result := make([]Item, 0, 50)

	if quantities, exist := aa.AiResult.Params[t]; exist {
		qs := reflect.ValueOf(quantities)

		for i := 0; i < qs.Len(); i++ {
			q := qs.Index(i).Interface()

			switch t := q.(type) {
			case string:
				qs := q.(string)
				qi := extractQuantity(qs)
				item := Item{Quantity: qi}
				result = append(result, item)
			case float64:
				qf := q.(float64)
				item := Item{Quantity: int(qf)}
				result = append(result, item)
			default:
				log.Println("Unknown Quantity type: %v", t)
			}
		}
	}

	return result
}

func extractQuantity(s string) int {
	nums := strings.TrimFunc(s, TrimToNum)
	numsCgd := DBCtoSBC(nums)

	if len(numsCgd) > 0 {
		q, _ := strconv.Atoi(numsCgd)
		return q
	} else {
		return 0
	}
	// re := regexp.MustCompile("[0-9]+")
}

func TrimToNum(r rune) bool {
	if n := r - '0'; n >= 0 && n <= 9 {
		return false
	} else if m := r - '０'; m >= 0 && m <= 9 {
		return false
	}

	return true
}

func DBCtoSBC(s string) string {
	retstr := ""
	for _, i := range s {
		inside_code := i
		if inside_code == 12288 {
			inside_code = 32
		} else {
			inside_code -= 65248
		}
		if inside_code < 32 || inside_code > 126 {
			retstr += string(i)
		} else {
			retstr += string(inside_code)
		}
	}
	return retstr
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"google.golang.org/grpc"

	"github.com/relab/gorums/gridq"
)

var localAddrs2x2 = []string{
	":8080", ":8081",
	":8082", ":8083",
}

var localAddrs3x3 = []string{
	":8080", ":8081", ":8082",
	":8083", ":8084", ":8085",
	":8086", ":8087", ":8088",
}

func main() {
	var (
		saddrs     = flag.String("addrs", "", "server addresses separated by ','")
		srows      = flag.Int("rows", 0, "number of rows")
		predefined = flag.String("predef", "", "predefined grids ('2x2' or '3x3') for local testing")
		printGrid  = flag.Bool("gprid", true, "print quorums to screen")
		psize      = flag.Int("p", 1024, "payload size in bytes")
	)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\nOptions:\n")
		flag.PrintDefaults()
	}
	flag.Parse()

	var rows int
	var addrs []string
	if *predefined != "" {
		switch *predefined {
		case "2x2":
			addrs = localAddrs2x2
			rows = 2
		case "3x3":
			rows = 3
			addrs = localAddrs3x3
		default:
			dief("unknown predefined grid: %q", *predefined)
		}
	} else {
		rows = *srows
		addrs = strings.Split(*saddrs, ",")
	}

	if len(addrs) == 0 {
		dief("no server addresses provided")
	}
	if rows == 0 {
		dief("rows must be > 0")
	}
	if len(addrs)%rows != 0 {
		dief("%d addresse(s) and %d row(s) does not form a complete grid", len(addrs), rows)
	}
	cols := len(addrs) / rows

	fmt.Println("#addrs:", len(addrs), "rows:", rows, "cols:", cols)

	mgr, err := gridq.NewManager(
		addrs,
		gridq.WithGrpcDialOptions(
			grpc.WithBlock(),
			grpc.WithInsecure(),
			grpc.WithTimeout(5*time.Second),
		),
	)
	if err != nil {
		dief("error creating manager: %v", err)
	}

	ids := mgr.NodeIDs()

	gqspec := gridq.NewGQSort(rows, cols, *printGrid)

	conf, err := mgr.NewConfiguration(ids, gqspec)
	if err != nil {
		dief("error creating config: %v", err)
	}

	for {
		state := &gridq.State{
			Value:     strings.Repeat("x", *psize),
			Timestamp: time.Now().Unix(),
		}

		fmt.Println("writing:", state)
		wreply, err := conf.Write(context.Background(), state)
		if err != nil {
			fmt.Println("error writing value:", err)
			os.Exit(2)
		}
		fmt.Println("write response:", wreply)

		time.Sleep(2 * time.Second)

		rreply, err := conf.Read(context.Background(), &gridq.Empty{})
		if err != nil {
			fmt.Println("error reading value:", err)
			os.Exit(2)
		}
		fmt.Println("read response:", rreply.State)

		time.Sleep(3 * time.Second)
	}
}

func dief(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a...)
	fmt.Fprint(os.Stderr, "\n")
	flag.Usage()
	os.Exit(2)
}
<commit_msg>cmd/gqclient: add flag to enable tracing<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"google.golang.org/grpc"

	"github.com/relab/gorums/gridq"
)

var localAddrs2x2 = []string{
	":8080", ":8081",
	":8082", ":8083",
}

var localAddrs3x3 = []string{
	":8080", ":8081", ":8082",
	":8083", ":8084", ":8085",
	":8086", ":8087", ":8088",
}

func main() {
	var (
		saddrs     = flag.String("addrs", "", "server addresses separated by ','")
		srows      = flag.Int("rows", 0, "number of rows")
		predefined = flag.String("predef", "", "predefined grids ('2x2' or '3x3') for local testing")
		printGrid  = flag.Bool("gprid", true, "print quorums to screen")
		psize      = flag.Int("p", 1024, "payload size in bytes")
		trace      = flag.Bool("t", false, "enable tracing")
	)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\nOptions:\n")
		flag.PrintDefaults()
	}
	flag.Parse()

	var rows int
	var addrs []string
	if *predefined != "" {
		switch *predefined {
		case "2x2":
			addrs = localAddrs2x2
			rows = 2
		case "3x3":
			rows = 3
			addrs = localAddrs3x3
		default:
			dief("unknown predefined grid: %q", *predefined)
		}
	} else {
		rows = *srows
		addrs = strings.Split(*saddrs, ",")
	}

	if len(addrs) == 0 {
		dief("no server addresses provided")
	}
	if rows == 0 {
		dief("rows must be > 0")
	}
	if len(addrs)%rows != 0 {
		dief("%d addresse(s) and %d row(s) does not form a complete grid", len(addrs), rows)
	}
	cols := len(addrs) / rows

	fmt.Println("#addrs:", len(addrs), "rows:", rows, "cols:", cols)

	mgrOpts := []gridq.ManagerOption{
		gridq.WithGrpcDialOptions(
			grpc.WithBlock(),
			grpc.WithInsecure(),
			grpc.WithTimeout(5*time.Second),
		),
	}
	if *trace {
		mgrOpts = append(mgrOpts, gridq.WithTracing())
		go func() {
			http.ListenAndServe(":9090", nil)
		}()
	}
	mgr, err := gridq.NewManager(
		addrs,
		mgrOpts...,
	)
	if err != nil {
		dief("error creating manager: %v", err)
	}

	ids := mgr.NodeIDs()

	gqspec := gridq.NewGQSort(rows, cols, *printGrid)

	conf, err := mgr.NewConfiguration(ids, gqspec)
	if err != nil {
		dief("error creating config: %v", err)
	}

	for {
		state := &gridq.State{
			Value:     strings.Repeat("x", *psize),
			Timestamp: time.Now().Unix(),
		}

		fmt.Println("writing:", state)
		wreply, err := conf.Write(context.Background(), state)
		if err != nil {
			fmt.Println("error writing value:", err)
			os.Exit(2)
		}
		fmt.Println("write response:", wreply)

		time.Sleep(2 * time.Second)

		rreply, err := conf.Read(context.Background(), &gridq.Empty{})
		if err != nil {
			fmt.Println("error reading value:", err)
			os.Exit(2)
		}
		fmt.Println("read response:", rreply.State)

		time.Sleep(3 * time.Second)
	}
}

func dief(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a...)
	fmt.Fprint(os.Stderr, "\n")
	flag.Usage()
	os.Exit(2)
}
<|endoftext|>


<commit_before>package main

/*
	Wgetpipe takes a list of fully-qualified URLs over STDIN and Gets them,
	outputting the code, url and elapsed fetch time.

	scans stdin in a goro, spawning up to MAX getters at a time, which stream their
	responses over a channel back to main which formats the output
*/

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/fatih/color"
	"github.com/viki-org/dnscache"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

var (
	MAX        int           // maximum number of outstanding HTTP get requests allowed
	SleepTime  time.Duration // Duration to sleep between GETter spawns
	ErrOnly    bool          // Quiet unless 0 == Code >= 400
	NoColor    bool          // Disable colorizing
	NoDnsCache bool          // Disable DNS caching
	Summary    bool          // Output final stats
	debug      bool          // Enable debugging

	OutFormat int         = log.Ldate | log.Ltime | log.Lshortfile
	DebugOut  *log.Logger = log.New(ioutil.Discard, "[DEBUG] ", OutFormat)
)

type urlCode struct {
	Url  string
	Code int
	Dur  time.Duration
	Err  error
}

func init() {
	flag.IntVar(&MAX, "max", 5, "Maximium in-flight GET requests at a time")
	flag.BoolVar(&ErrOnly, "errorsonly", false, "Only output errors (HTTP Codes >= 400)")
	flag.BoolVar(&NoColor, "nocolor", false, "Don't colorize the output")
	flag.BoolVar(&Summary, "stats", false, "Output stats at the end")
	flag.DurationVar(&SleepTime, "sleep", 0, "Amount of time to sleep between spawning a GETter (e.g. 1ms, 10s)")
	flag.BoolVar(&debug, "debug", false, "Enable debug output")
	flag.BoolVar(&NoDnsCache, "nodnscache", false, "Disable DNS caching")
	flag.Parse()

	// Handle boring people
	if NoColor {
		color.NoColor = true
	}

	// Handle debug
	if debug {
		DebugOut = log.New(os.Stderr, "[DEBUG] ", OutFormat)
	}

	// Sets the default http client to use dnscache, because duh
	if NoDnsCache == false {
		res := dnscache.New(1 * time.Hour)
		http.DefaultClient.Transport = &http.Transport{
			MaxIdleConnsPerHost: 64,
			Dial: func(network string, address string) (net.Conn, error) {
				separator := strings.LastIndex(address, ":")
				ip, _ := res.FetchOneString(address[:separator])
				return net.Dial("tcp", ip+address[separator:])
			},
		}
	}
}

func main() {

	getChan := make(chan string)       // Channel to stream URLs to get
	rChan := make(chan urlCode)        // Channel to stream responses from the Gets
	doneChan := make(chan bool)        // Channel to signal a getter is done
	sigChan := make(chan os.Signal, 1) // Channel to stream signals
	abortChan := make(chan bool, 1)    // Channel to tell the getters to abort
	count := 0
	error4s := 0
	error5s := 0
	errors := 0

	// Stream the signals we care about
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// Signal handler
	go func() {
		<-sigChan
		DebugOut.Println("Signal seen, sending abort!")

		// Add an abort for each of the getters, plus the setter
		for a := 0; a <= MAX; a++ {
			abortChan <- true
		}
	}()

	// Spawn off the getters
	for g := 0; g < MAX; g++ {
		go getter(getChan, rChan, doneChan, abortChan)
	}

	// Block until all the getters are done, and then close rChan
	go func() {
		defer close(rChan)

		for c := 0; c < MAX; c++ {
			<-doneChan
			DebugOut.Printf("Done %d/%d\n", c+1, MAX)
		}
	}()

	// spawn off the scanner
	start := time.Now()
	go scanStdIn(getChan, abortChan)

	// Collate the results
	for i := range rChan {
		count++

		if i.Code == 0 {
			errors++
			color.Red("%d %s %s (%s)\n", i.Code, i.Url, i.Dur.String(), i.Err)
		} else if i.Code < 400 {
			if ErrOnly {
				// skip
				continue
			}
			color.Green("%d %s %s\n", i.Code, i.Url, i.Dur.String())
		} else if i.Code < 500 {
			error4s++
			color.Yellow("%d %s %s\n", i.Code, i.Url, i.Dur.String())
		} else {
			error5s++
			color.Red("%d %s %s\n", i.Code, i.Url, i.Dur.String())
		}
	}

	elapsed := time.Since(start)

	if Summary {
		e := color.RedString("%d", errors)
		e4 := color.YellowString("%d", error4s)
		e5 := color.RedString("%d", error5s)
		fmt.Printf("\n\nGETs: %d\nErrors: %s\n500 Errors: %s\n400 Errors: %s\nElapsed Time: %s\n", count, e, e5, e4, elapsed.String())
	}
}

// scanStdIn takes a channel to pass inputted strings to,
// and does so until EOF, whereafter it closes the channel
func scanStdIn(getChan chan string, abortChan chan bool) {
	defer close(getChan)

	scanner := bufio.NewScanner(os.Stdin)
SCAN:
	for scanner.Scan() {
		select {
		case <-abortChan:
			DebugOut.Println("scanner abort seen!")
			break SCAN
		default:
		}
		DebugOut.Println("scanner sending...")

		getChan <- scanner.Text()

	}
	// POST: we've seen EOF
	DebugOut.Println("EOF seen")

}

// getter takes a receive channel, send channel, and done channel,
// running HTTP GETs for anything in the receive channel, returning
// formatted responses to the send channel, and signalling completion
// via the done channel
func getter(getChan chan string, rChan chan urlCode, doneChan chan bool, abortChan chan bool) {
	defer func() { doneChan <- true }()

GETTING:
	for url := range getChan {
		select {
		case <-abortChan:
			DebugOut.Println("getter abort seen!")
			break GETTING
		default:
		}

		DebugOut.Printf("getter getting %s\n", url)

		s := time.Now()
		response, err := http.Get(url)
		d := time.Since(s)
		if err != nil {
			// We assume code 0 to be a non-HTTP error
			rChan <- urlCode{url, 0, d, err}
		} else {
			response.Body.Close() // else leak
			rChan <- urlCode{url, response.StatusCode, d, nil}
		}

		if SleepTime > 0 {
			// Zzzzzzz
			time.Sleep(SleepTime)
		}
	}

}
<commit_msg>Shores up the end conditions, changing around the getter loops<commit_after>package main

/*
	Wgetpipe takes a list of fully-qualified URLs over STDIN and Gets them,
	outputting the code, url and elapsed fetch time.

	scans stdin in a goro, spawning up to MAX getters at a time, which stream their
	responses over a channel back to main which formats the output
*/

import (
	"bufio"
	"flag"
	"fmt"
	"github.com/fatih/color"
	"github.com/viki-org/dnscache"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

var (
	MAX        int           // maximum number of outstanding HTTP get requests allowed
	SleepTime  time.Duration // Duration to sleep between GETter spawns
	ErrOnly    bool          // Quiet unless 0 == Code >= 400
	NoColor    bool          // Disable colorizing
	NoDnsCache bool          // Disable DNS caching
	Summary    bool          // Output final stats
	debug      bool          // Enable debugging

	OutFormat int         = log.Ldate | log.Ltime | log.Lshortfile
	DebugOut  *log.Logger = log.New(ioutil.Discard, "[DEBUG] ", OutFormat)
)

type urlCode struct {
	Url  string
	Code int
	Dur  time.Duration
	Err  error
}

func init() {
	flag.IntVar(&MAX, "max", 5, "Maximium in-flight GET requests at a time")
	flag.BoolVar(&ErrOnly, "errorsonly", false, "Only output errors (HTTP Codes >= 400)")
	flag.BoolVar(&NoColor, "nocolor", false, "Don't colorize the output")
	flag.BoolVar(&Summary, "stats", false, "Output stats at the end")
	flag.DurationVar(&SleepTime, "sleep", 0, "Amount of time to sleep between spawning a GETter (e.g. 1ms, 10s)")
	flag.BoolVar(&debug, "debug", false, "Enable debug output")
	flag.BoolVar(&NoDnsCache, "nodnscache", false, "Disable DNS caching")
	flag.Parse()

	// Handle boring people
	if NoColor {
		color.NoColor = true
	}

	// Handle debug
	if debug {
		DebugOut = log.New(os.Stderr, "[DEBUG] ", OutFormat)
	}

	// Sets the default http client to use dnscache, because duh
	if NoDnsCache == false {
		res := dnscache.New(1 * time.Hour)
		http.DefaultClient.Transport = &http.Transport{
			MaxIdleConnsPerHost: 64,
			Dial: func(network string, address string) (net.Conn, error) {
				separator := strings.LastIndex(address, ":")
				ip, _ := res.FetchOneString(address[:separator])
				return net.Dial("tcp", ip+address[separator:])
			},
		}
	}
}

func main() {

	getChan := make(chan string)        // Channel to stream URLs to get
	rChan := make(chan urlCode)         // Channel to stream responses from the Gets
	doneChan := make(chan bool)         // Channel to signal a getter is done
	sigChan := make(chan os.Signal, 1)  // Channel to stream signals
	abortChan := make(chan bool, MAX+1) // Channel to tell the getters to abort
	count := 0
	error4s := 0
	error5s := 0
	errors := 0

	// Stream the signals we care about
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// Signal handler
	go func() {
		<-sigChan
		DebugOut.Println("Signal seen, sending abort!")

		// Add an abort for each of the getters, plus the setter
		for a := 0; a <= MAX; a++ {
			abortChan <- true
		}
	}()

	// Spawn off the getters
	for g := 0; g < MAX; g++ {
		go getter(getChan, rChan, doneChan, abortChan)
	}

	// Block until all the getters are done, and then close rChan
	go func() {
		defer close(rChan)

		for c := 0; c < MAX; c++ {
			<-doneChan
			DebugOut.Printf("Done %d/%d\n", c+1, MAX)
		}
	}()

	// spawn off the scanner
	start := time.Now()
	go scanStdIn(getChan, abortChan)

	// Collate the results
	for i := range rChan {
		count++

		if i.Code == 0 {
			errors++
			color.Red("%d %s %s (%s)\n", i.Code, i.Url, i.Dur.String(), i.Err)
		} else if i.Code < 400 {
			if ErrOnly {
				// skip
				continue
			}
			color.Green("%d %s %s\n", i.Code, i.Url, i.Dur.String())
		} else if i.Code < 500 {
			error4s++
			color.Yellow("%d %s %s\n", i.Code, i.Url, i.Dur.String())
		} else {
			error5s++
			color.Red("%d %s %s\n", i.Code, i.Url, i.Dur.String())
		}
	}

	elapsed := time.Since(start)

	if Summary {
		e := color.RedString("%d", errors)
		e4 := color.YellowString("%d", error4s)
		e5 := color.RedString("%d", error5s)
		fmt.Printf("\n\nGETs: %d\nErrors: %s\n500 Errors: %s\n400 Errors: %s\nElapsed Time: %s\n", count, e, e5, e4, elapsed.String())
	}
}

// scanStdIn takes a channel to pass inputted strings to,
// and does so until EOF, whereafter it closes the channel
func scanStdIn(getChan chan string, abortChan chan bool) {
	defer close(getChan)

	scanner := bufio.NewScanner(os.Stdin)
SCAN:
	for scanner.Scan() {
		select {
		case <-abortChan:
			DebugOut.Println("scanner abort seen!")
			break SCAN
		default:
		}
		DebugOut.Println("scanner sending...")

		getChan <- scanner.Text()

	}
	// POST: we've seen EOF
	DebugOut.Println("EOF seen")

}

// getter takes a receive channel, send channel, and done channel,
// running HTTP GETs for anything in the receive channel, returning
// formatted responses to the send channel, and signalling completion
// via the done channel
func getter(getChan chan string, rChan chan urlCode, doneChan chan bool, abortChan chan bool) {
	defer func() { doneChan <- true }()

	for {
		select {
		case <-abortChan:
			DebugOut.Println("getter abort seen!")
			return
		case url := <-getChan:
			if url == "" {
				// We assume an empty request is a closer
				// as that simplifies our for{select{}} loop
				// considerably
				DebugOut.Println("getter empty request seen!")
				return
			}
			DebugOut.Printf("getter getting %s\n", url)

			s := time.Now()
			response, err := http.Get(url)
			d := time.Since(s)
			if err != nil {
				// We assume code 0 to be a non-HTTP error
				rChan <- urlCode{url, 0, d, err}
			} else {
				response.Body.Close() // else leak
				rChan <- urlCode{url, response.StatusCode, d, nil}
			}

			if SleepTime > 0 {
				// Zzzzzzz
				time.Sleep(SleepTime)
			}
		}
	}
}
<|endoftext|>


<commit_before>// The purpose of this tool is to provide a realistic load on the
// system. It creates 98 albums, each contains 20 images. In total, it
// sends 9212 requests.
package main

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"strings"
	"time"

	"github.com/cheggaaa/pb/v3"

	"github.com/zitryss/aye-and-nay/pkg/debug"
)

const (
	n = 9212
)

var (
	apiAddress   string
	minioAddress string
	connections  int
	testdata     string
	verbose      bool
)

func main() {
	flag.StringVar(&apiAddress, "api-address", "https://localhost", "")
	flag.StringVar(&minioAddress, "minio-address", "https://localhost", "")
	flag.IntVar(&connections, "connections", 2, "")
	flag.StringVar(&testdata, "testdata", "./testdata", "")
	flag.BoolVar(&verbose, "verbose", true, "")
	flag.Parse()

	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

	bar := pb.StartNew(n)
	if !verbose {
		bar.SetWriter(ioutil.Discard)
	}

	sem := make(chan struct{}, connections)
	for i := 0; i < 98; i++ {
		sem <- struct{}{}
		go func() {
			defer func() { <-sem }()
			albumHtml()
			album := albumApi()
			bar.Increment()
			readyApi(album)
			bar.Increment()
			for j := 0; j < 4; j++ {
				pairHtml()
				for k := 0; k < 11; k++ {
					src1, token1, src2, token2 := pairApi(album)
					bar.Increment()
					pairMinio(src1, src2)
					voteApi(album, token1, token2)
					bar.Increment()
				}
				topHtml()
				src := topApi(album)
				bar.Increment()
				topMinio(src)
			}
		}()
	}
	for i := 0; i < connections; i++ {
		sem <- struct{}{}
	}

	bar.Finish()
	fmt.Println(time.Since(bar.StartTime()), "sec")
	fmt.Println(n/time.Since(bar.StartTime()).Seconds(), "rps")
}

func albumHtml() {
	html("/index.html")
}

func albumApi() string {
	body := bytes.Buffer{}
	multi := multipart.NewWriter(&body)
	for i := 0; i < 4; i++ {
		for _, filename := range []string{"alan.jpg", "john.bmp", "dennis.png", "tim.gif", "linus.jpg"} {
			part, err := multi.CreateFormFile("images", filename)
			debug.Check(err)
			b, err := ioutil.ReadFile(testdata + "/" + filename)
			debug.Check(err)
			_, err = part.Write(b)
			debug.Check(err)
		}
	}
	err := multi.Close()
	debug.Check(err)

	req, err := http.NewRequest("POST", apiAddress+"/api/albums/", &body)
	debug.Check(err)
	req.Header.Set("Content-Type", multi.FormDataContentType())

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 201)

	type result struct {
		Album struct {
			Id string
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)

	return res.Album.Id
}

func readyApi(album string) {
	req, err := http.NewRequest("GET", apiAddress+"/api/albums/"+album+"/ready/", nil)
	debug.Check(err)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)

	type result struct {
		Album struct {
			Progress float64
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)
}

func pairHtml() {
	html("/pair.html")
}

func pairApi(album string) (string, string, string, string) {
	req, err := http.NewRequest("GET", apiAddress+"/api/albums/"+album+"/", nil)
	debug.Check(err)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)

	type result struct {
		Img1 struct {
			Token string
			Src   string
		}
		Img2 struct {
			Token string
			Src   string
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)

	return res.Img1.Src, res.Img1.Token, res.Img2.Src, res.Img2.Token
}

func pairMinio(src1 string, src2 string) {
	minio(src1)
	minio(src2)
}

func voteApi(album string, token1 string, token2 string) {
	body := strings.NewReader("{\"album\":{\"imgFrom\":{\"token\":\"" + token1 + "\"},\"imgTo\":{\"token\":\"" + token2 + "\"}}}")
	req, err := http.NewRequest("PATCH", apiAddress+"/api/albums/"+album+"/", body)
	debug.Check(err)
	req.Header.Set("Content-Type", "application/json; charset=utf-8")

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)
	_, err = io.Copy(ioutil.Discard, resp.Body)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)
}

func topHtml() {
	html("/top.html")
}

func topApi(album string) []string {
	req, err := http.NewRequest("GET", apiAddress+"/api/albums/"+album+"/top/", nil)
	debug.Check(err)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)

	type image struct {
		Src    string
		Rating float64
	}
	type result struct {
		Images []image
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)

	src := []string(nil)
	for _, image := range res.Images {
		src = append(src, image.Src)
	}
	return src
}

func topMinio(src []string) {
	for _, s := range src {
		minio(s)
	}
}

func html(page string) {
	if apiAddress != minioAddress {
		return
	}
	req, err := http.NewRequest("GET", apiAddress+page, nil)
	if err != nil {
		return
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}
	_, _ = io.Copy(ioutil.Discard, resp.Body)
	_ = resp.Body.Close()
}

func minio(src string) {
	req, err := http.NewRequest("GET", minioAddress+src, nil)
	if err != nil {
		return
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}
	_, _ = io.Copy(ioutil.Discard, resp.Body)
	_ = resp.Body.Close()
}
<commit_msg>Make CI job faster<commit_after>// The purpose of this tool is to provide a realistic load on the
// system. It creates "n" albums (default 2), each contains 20 images.
// In total, it sends n x 94 requests.
package main

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"strings"
	"time"

	"github.com/cheggaaa/pb/v3"

	"github.com/zitryss/aye-and-nay/pkg/debug"
)

var (
	n            int
	apiAddress   string
	minioAddress string
	connections  int
	testdata     string
	verbose      bool
)

func main() {
	flag.IntVar(&n, "n", 2, "#albums")
	flag.StringVar(&apiAddress, "api-address", "https://localhost", "")
	flag.StringVar(&minioAddress, "minio-address", "https://localhost", "")
	flag.IntVar(&connections, "connections", 2, "")
	flag.StringVar(&testdata, "testdata", "./testdata", "")
	flag.BoolVar(&verbose, "verbose", true, "")
	flag.Parse()

	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

	bar := pb.StartNew(n * 94)
	if !verbose {
		bar.SetWriter(ioutil.Discard)
	}

	sem := make(chan struct{}, connections)
	for i := 0; i < n; i++ {
		sem <- struct{}{}
		go func() {
			defer func() { <-sem }()
			albumHtml()
			album := albumApi()
			bar.Increment()
			readyApi(album)
			bar.Increment()
			for j := 0; j < 4; j++ {
				pairHtml()
				for k := 0; k < 11; k++ {
					src1, token1, src2, token2 := pairApi(album)
					bar.Increment()
					pairMinio(src1, src2)
					voteApi(album, token1, token2)
					bar.Increment()
				}
				topHtml()
				src := topApi(album)
				bar.Increment()
				topMinio(src)
			}
		}()
	}
	for i := 0; i < connections; i++ {
		sem <- struct{}{}
	}

	bar.Finish()
	fmt.Println(time.Since(bar.StartTime()), "sec")
	fmt.Println(float64(n*94)/time.Since(bar.StartTime()).Seconds(), "rps")
}

func albumHtml() {
	html("/index.html")
}

func albumApi() string {
	body := bytes.Buffer{}
	multi := multipart.NewWriter(&body)
	for i := 0; i < 4; i++ {
		for _, filename := range []string{"alan.jpg", "john.bmp", "dennis.png", "tim.gif", "linus.jpg"} {
			part, err := multi.CreateFormFile("images", filename)
			debug.Check(err)
			b, err := ioutil.ReadFile(testdata + "/" + filename)
			debug.Check(err)
			_, err = part.Write(b)
			debug.Check(err)
		}
	}
	err := multi.Close()
	debug.Check(err)

	req, err := http.NewRequest("POST", apiAddress+"/api/albums/", &body)
	debug.Check(err)
	req.Header.Set("Content-Type", multi.FormDataContentType())

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 201)

	type result struct {
		Album struct {
			Id string
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)

	return res.Album.Id
}

func readyApi(album string) {
	req, err := http.NewRequest("GET", apiAddress+"/api/albums/"+album+"/ready/", nil)
	debug.Check(err)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)

	type result struct {
		Album struct {
			Progress float64
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)
}

func pairHtml() {
	html("/pair.html")
}

func pairApi(album string) (string, string, string, string) {
	req, err := http.NewRequest("GET", apiAddress+"/api/albums/"+album+"/", nil)
	debug.Check(err)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)

	type result struct {
		Img1 struct {
			Token string
			Src   string
		}
		Img2 struct {
			Token string
			Src   string
		}
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)

	return res.Img1.Src, res.Img1.Token, res.Img2.Src, res.Img2.Token
}

func pairMinio(src1 string, src2 string) {
	minio(src1)
	minio(src2)
}

func voteApi(album string, token1 string, token2 string) {
	body := strings.NewReader("{\"album\":{\"imgFrom\":{\"token\":\"" + token1 + "\"},\"imgTo\":{\"token\":\"" + token2 + "\"}}}")
	req, err := http.NewRequest("PATCH", apiAddress+"/api/albums/"+album+"/", body)
	debug.Check(err)
	req.Header.Set("Content-Type", "application/json; charset=utf-8")

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)
	_, err = io.Copy(ioutil.Discard, resp.Body)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)
}

func topHtml() {
	html("/top.html")
}

func topApi(album string) []string {
	req, err := http.NewRequest("GET", apiAddress+"/api/albums/"+album+"/top/", nil)
	debug.Check(err)

	resp, err := http.DefaultClient.Do(req)
	debug.Check(err)
	debug.Assert(resp.StatusCode == 200)

	type image struct {
		Src    string
		Rating float64
	}
	type result struct {
		Images []image
	}

	res := result{}
	err = json.NewDecoder(resp.Body).Decode(&res)
	debug.Check(err)
	err = resp.Body.Close()
	debug.Check(err)

	src := []string(nil)
	for _, image := range res.Images {
		src = append(src, image.Src)
	}
	return src
}

func topMinio(src []string) {
	for _, s := range src {
		minio(s)
	}
}

func html(page string) {
	if apiAddress != minioAddress {
		return
	}
	req, err := http.NewRequest("GET", apiAddress+page, nil)
	if err != nil {
		return
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}
	_, _ = io.Copy(ioutil.Discard, resp.Body)
	_ = resp.Body.Close()
}

func minio(src string) {
	req, err := http.NewRequest("GET", minioAddress+src, nil)
	if err != nil {
		return
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}
	_, _ = io.Copy(ioutil.Discard, resp.Body)
	_ = resp.Body.Close()
}
<|endoftext|>


<commit_before>// Copyright (C) 2016  Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

// The occlient tool is a client for the gRPC service for getting and setting the
// OpenConfig configuration and state of a network device.
package main

import (
	"encoding/json"
	"flag"
	"io"
	"strconv"
	"strings"
	"sync"

	"golang.org/x/net/context"

	"github.com/aristanetworks/goarista/kafka"
	"github.com/aristanetworks/goarista/kafka/producer"
	cli "github.com/aristanetworks/goarista/openconfig/client"

	"github.com/aristanetworks/glog"
	pb "github.com/aristanetworks/goarista/openconfig"
	"github.com/golang/protobuf/proto"

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

const defaultPort = "6042"

var jsonOutputFlag = flag.Bool("json", false,
	"Print the output in JSON instead of protobuf")

var kafkaKeysFlag = flag.String("kafkakey", "",
	"Keys for kafka messages (comma-separated, default: the value of -addrs")

type client struct {
	addr          string
	client        pb.OpenConfigClient
	ctx           context.Context
	kafkaProducer producer.Producer
}

// newClient creates a new gRPC client and pipes it into the given producer.
// The producer is typically something responsible for pushing updates to a
// backend system like Kafka or Redis or HBase.
func newClient(addr string, opts *[]grpc.DialOption, username, password string,
	p producer.Producer) (*client, error) {
	c := &client{
		addr:          addr,
		kafkaProducer: p,
	}
	conn, err := grpc.Dial(addr, *opts...)
	if err != nil {
		return nil, err
	}
	glog.Infof("Connected to %s", addr)
	c.client = pb.NewOpenConfigClient(conn)
	c.ctx = context.Background()
	if username != "" {
		c.ctx = metadata.NewContext(c.ctx, metadata.Pairs(
			"username", username,
			"password", password))
	}
	return c, nil
}

func (c *client) run(wg sync.WaitGroup, subscribePaths []string) error {
	defer wg.Done()
	stream, err := c.client.Subscribe(c.ctx)
	if err != nil {
		glog.Fatalf("Subscribe failed: %s", err)
	}
	for _, path := range subscribePaths {
		sub := &pb.SubscribeRequest{
			Request: &pb.SubscribeRequest_Subscribe{
				Subscribe: &pb.SubscriptionList{
					Subscription: []*pb.Subscription{
						{
							Path: &pb.Path{Element: strings.Split(path, "/")},
						},
					},
				},
			},
		}

		err = stream.Send(sub)
		if err != nil {
			return err
		}
	}
	if c.kafkaProducer != nil {
		go c.kafkaProducer.Run()
	}
	for {
		resp, err := stream.Recv()
		if err != nil {
			if err != io.EOF {
				glog.Fatalf("Error received from the %s: %s", c.addr, err)
			}
		}
		var respTxt string
		if *jsonOutputFlag {
			respTxt = jsonify(resp)
		} else {
			respTxt = proto.MarshalTextString(resp)
		}
		log := glog.Info
		if resp.GetHeartbeat() != nil {
			log = glog.V(1).Info // Log heartbeats with verbose logging only.
		}
		if c.kafkaProducer != nil {
			c.kafkaProducer.Write(resp)
		} else {
			log(respTxt)
		}
	}
}

func main() {
	username, password, subscriptions, addrs, opts := cli.ParseFlags()

	if *kafkaKeysFlag == "" {
		*kafkaKeysFlag = strings.Join(addrs, ",")
	}
	kafkaKeys := strings.Split(*kafkaKeysFlag, ",")
	if len(addrs) != len(kafkaKeys) {
		glog.Fatal("Please provide the same number of addresses and Kafka keys")
	}
	var kafkaAddresses []string
	if *kafka.Addresses != "" {
		kafkaAddresses = strings.Split(*kafka.Addresses, ",")
	}
	var wg sync.WaitGroup
	for i := 0; i < len(addrs); i++ {
		addr := addrs[i]
		kafkaKey := kafkaKeys[i]
		if !strings.ContainsRune(addr, ':') {
			addr += ":" + defaultPort
		}
		p, err := newKafkaProducer(kafkaAddresses, *kafka.Topic, kafkaKey)
		if err != nil {
			glog.Fatal("Failed to initialize producer: ", err)
		}
		c, err := newClient(addr, &opts, username, password, p)
		if err != nil {
			glog.Fatal("Failed to initialize client: ", err)
		}
		wg.Add(1)
		go c.run(wg, subscriptions)
	}
	wg.Wait()
}

func joinPath(path *pb.Path) string {
	return strings.Join(path.Element, "/")
}

func convertUpdate(update *pb.Update) interface{} {
	switch update.Value.Type {
	case pb.Type_JSON:
		var value interface{}
		err := json.Unmarshal(update.Value.Value, &value)
		if err != nil {
			glog.Fatalf("Malformed JSON update %q in %s", update.Value.Value, update)
		}
		return value
	case pb.Type_BYTES:
		return strconv.Quote(string(update.Value.Value))
	default:
		glog.Fatalf("Unhandled type of value %v in %s", update.Value.Type, update)
		return nil
	}
}

func jsonify(resp *pb.SubscribeResponse) string {
	m := make(map[string]interface{}, 1)
	switch resp := resp.Response.(type) {
	case *pb.SubscribeResponse_Update:
		notif := resp.Update
		m["timestamp"] = notif.Timestamp
		m["path"] = "/" + joinPath(notif.Prefix)
		if len(notif.Update) != 0 {
			updates := make(map[string]interface{}, len(notif.Update))
			for _, update := range notif.Update {
				updates[joinPath(update.Path)] = convertUpdate(update)
			}
			m["updates"] = updates
		}
		if len(notif.Delete) != 0 {
			deletes := make([]string, len(notif.Delete))
			for i, del := range notif.Delete {
				deletes[i] = joinPath(del)
			}
			m["deletes"] = deletes
		}
		m = map[string]interface{}{"notification": m}
	case *pb.SubscribeResponse_Heartbeat:
		m["heartbeat"] = resp.Heartbeat.Interval
	case *pb.SubscribeResponse_SyncResponse:
		m["syncResponse"] = resp.SyncResponse
	default:
		glog.Fatalf("Unknown type of response: %T: %s", resp, resp)
	}
	js, err := json.MarshalIndent(m, "", "  ")
	if err != nil {
		glog.Fatal("json: ", err)
	}
	return string(js)
}
<commit_msg>cmd/occlient: Use the code factored out in `openconfig/client`.<commit_after>// Copyright (C) 2016  Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

// The occlient tool is a client for the gRPC service for getting and setting the
// OpenConfig configuration and state of a network device.
package main

import (
	"flag"
	"strings"
	"sync"

	"github.com/aristanetworks/goarista/kafka"
	"github.com/aristanetworks/goarista/openconfig/client"

	"github.com/aristanetworks/glog"
	pb "github.com/aristanetworks/goarista/openconfig"
)

const defaultPort = "6042"

var kafkaKeysFlag = flag.String("kafkakey", "",
	"Keys for kafka messages (comma-separated, default: the value of -addrs")

func main() {
	username, password, subscriptions, addrs, opts := client.ParseFlags()

	if *kafkaKeysFlag == "" {
		*kafkaKeysFlag = strings.Join(addrs, ",")
	}
	kafkaKeys := strings.Split(*kafkaKeysFlag, ",")
	if len(addrs) != len(kafkaKeys) {
		glog.Fatal("Please provide the same number of addresses and Kafka keys")
	}
	var kafkaAddresses []string
	if *kafka.Addresses != "" {
		kafkaAddresses = strings.Split(*kafka.Addresses, ",")
	}
	var wg sync.WaitGroup
	for i, addr := range addrs {
		kafkaKey := kafkaKeys[i]
		if !strings.ContainsRune(addr, ':') {
			addr += ":" + defaultPort
		}
		p, err := newKafkaProducer(kafkaAddresses, *kafka.Topic, kafkaKey)
		if err != nil {
			glog.Fatal("Failed to initialize producer: ", err)
		}
		publish := func(notif *pb.SubscribeResponse) {
			p.Write(notif)
		}
		wg.Add(1)
		go p.Run()
		go client.Run(publish, wg, username, password, addr, subscriptions, opts)
	}
	wg.Wait()
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2016 VMware, Inc. All Rights Reserved.
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package models

const (
	SYSADMIN     = 1
	PROJECTADMIN = 2
	DEVELOPER    = 3
	GUEST        = 4
)

type Role struct {
	RoleID   int    `json:"role_id", orm:"column(role_id)"`
	RoleCode string `json:"role_code, orm:"column(role_code)""`
	Name     string `json:"role_name", orm:"column(name)"`
}
<commit_msg>golint refactor for model<commit_after>/*
   Copyright (c) 2016 VMware, Inc. All Rights Reserved.
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package models

const (
	SYSADMIN     = 1
	PROJECTADMIN = 2
	DEVELOPER    = 3
	GUEST        = 4
)

type Role struct {
	RoleID   int    `json:"role_id" orm:"column(role_id)"`
	RoleCode string `json:"role_code" orm:"column(role_code)"`
	Name     string `json:"role_name" orm:"column(name)"`
}
<|endoftext|>


<commit_before>// Copyright (c) 2018, Google, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package pipeline

import (
	"fmt"
	"net/http"

	"github.com/spf13/cobra"

	"github.com/spinnaker/spin/util"
)

type saveOptions struct {
	*PipelineOptions
	output       string
	pipelineFile string
}

var (
	savePipelineShort = "Save the provided pipeline"
	savePipelineLong  = "Save the provided pipeline"
)

func NewSaveCmd(pipelineOptions *PipelineOptions) *cobra.Command {
	options := &saveOptions{
		PipelineOptions: pipelineOptions,
	}
	cmd := &cobra.Command{
		Use:     "save",
		Aliases: []string{},
		Short:   savePipelineShort,
		Long:    savePipelineLong,
		RunE: func(cmd *cobra.Command, args []string) error {
			return savePipeline(cmd, options)
		},
	}

	cmd.PersistentFlags().StringVarP(&options.pipelineFile, "file", "f", "", "path to the pipeline file")

	return cmd
}

func savePipeline(cmd *cobra.Command, options *saveOptions) error {
	pipelineJson, err := util.ParseJsonFromFileOrStdin(options.pipelineFile, false)
	if err != nil {
		return err
	}
	valid := true
	if _, exists := pipelineJson["name"]; !exists {
		options.Ui.Error("Required pipeline key 'name' missing...\n")
		valid = false
	}

	if _, exists := pipelineJson["application"]; !exists {
		options.Ui.Error("Required pipeline key 'application' missing...\n")
		valid = false
	}

	if template, exists := pipelineJson["template"]; exists && len(template.(map[string]interface{})) > 0 {
		if _, exists := pipelineJson["schema"]; !exists {
			options.Ui.Error("Required pipeline key 'schema' missing for templated pipeline...\n")
			valid = false
		}
		pipelineJson["type"] = "templatedPipeline"
	}

	if !valid {
		return fmt.Errorf("Submitted pipeline is invalid: %s\n", pipelineJson)
	}
	application := pipelineJson["application"].(string)
	pipelineName := pipelineJson["name"].(string)

	foundPipeline, queryResp, _ := options.GateClient.ApplicationControllerApi.GetPipelineConfigUsingGET(options.GateClient.Context, application, pipelineName)

	_, exists := pipelineJson["id"].(string)
	var foundPipelineId string
	if queryResp.StatusCode == http.StatusOK && len(foundPipeline) > 0 {
		foundPipelineId = foundPipeline["id"].(string)
	}
	if !exists && foundPipelineId != "" {
		pipelineJson["id"] = foundPipelineId
	}

	saveResp, saveErr := options.GateClient.PipelineControllerApi.SavePipelineUsingPOST(options.GateClient.Context, pipelineJson)

	if saveErr != nil {
		return saveErr
	}
	if saveResp.StatusCode != http.StatusOK {
		return fmt.Errorf("Encountered an error saving pipeline, status code: %d\n", saveResp.StatusCode)
	}

	options.Ui.Success("Pipeline save succeeded")
	return nil
}
<commit_msg>feat(pipelineSave): verify pipelineId matches found pipeline<commit_after>// Copyright (c) 2018, Google, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package pipeline

import (
	"fmt"
	"net/http"

	"github.com/spf13/cobra"

	"github.com/spinnaker/spin/util"
)

type saveOptions struct {
	*PipelineOptions
	output       string
	pipelineFile string
}

var (
	savePipelineShort = "Save the provided pipeline"
	savePipelineLong  = "Save the provided pipeline"
)

func NewSaveCmd(pipelineOptions *PipelineOptions) *cobra.Command {
	options := &saveOptions{
		PipelineOptions: pipelineOptions,
	}
	cmd := &cobra.Command{
		Use:     "save",
		Aliases: []string{},
		Short:   savePipelineShort,
		Long:    savePipelineLong,
		RunE: func(cmd *cobra.Command, args []string) error {
			return savePipeline(cmd, options)
		},
	}

	cmd.PersistentFlags().StringVarP(&options.pipelineFile, "file", "f", "", "path to the pipeline file")

	return cmd
}

func savePipeline(cmd *cobra.Command, options *saveOptions) error {
	pipelineJson, err := util.ParseJsonFromFileOrStdin(options.pipelineFile, false)
	if err != nil {
		return err
	}
	valid := true
	if _, exists := pipelineJson["name"]; !exists {
		options.Ui.Error("Required pipeline key 'name' missing...\n")
		valid = false
	}

	if _, exists := pipelineJson["application"]; !exists {
		options.Ui.Error("Required pipeline key 'application' missing...\n")
		valid = false
	}

	if template, exists := pipelineJson["template"]; exists && len(template.(map[string]interface{})) > 0 {
		if _, exists := pipelineJson["schema"]; !exists {
			options.Ui.Error("Required pipeline key 'schema' missing for templated pipeline...\n")
			valid = false
		}
		pipelineJson["type"] = "templatedPipeline"
	}

	if !valid {
		return fmt.Errorf("Submitted pipeline is invalid: %s\n", pipelineJson)
	}

	application := pipelineJson["application"].(string)
	pipelineName := pipelineJson["name"].(string)
	pipelineID, pipelineIDExists := pipelineJson["id"].(string)

	foundPipeline, queryResp, _ := options.GateClient.ApplicationControllerApi.GetPipelineConfigUsingGET(options.GateClient.Context, application, pipelineName)
	if queryResp.StatusCode == http.StatusNotFound {
		// must be a new pipeline, let's go ahead and create it
		if pipelineIDExists {
			delete(pipelineJson, "id")
		}
	} else {
		// it's exists already, make sure we're updating the same one
		if queryResp.StatusCode == http.StatusOK && len(foundPipeline) > 0 {
			foundId := foundPipeline["id"].(string)
			if pipelineIDExists && pipelineID != foundId {
				return fmt.Errorf(`pipeline ID "%s" does not match the found pipeline "%s" in application "%s" with ID "%s"`, pipelineID, pipelineName, application, foundId)
			}

			pipelineJson["id"] = foundId
		}
	}

	saveResp, saveErr := options.GateClient.PipelineControllerApi.SavePipelineUsingPOST(options.GateClient.Context, pipelineJson)

	if saveErr != nil {
		return saveErr
	} else if saveResp.StatusCode != http.StatusOK {
		return fmt.Errorf("Encountered an error saving pipeline, status code: %d\n", saveResp.StatusCode)
	}

	options.Ui.Success("Pipeline save succeeded")
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2019 Gitea. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"context"
	"fmt"

	"code.gitea.io/gitea/models/db"
	repo_model "code.gitea.io/gitea/models/repo"
	user_model "code.gitea.io/gitea/models/user"
	"code.gitea.io/gitea/modules/json"
	"code.gitea.io/gitea/modules/migration"
	"code.gitea.io/gitea/modules/secret"
	"code.gitea.io/gitea/modules/setting"
	"code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/modules/timeutil"
	"code.gitea.io/gitea/modules/util"

	"xorm.io/builder"
)

// Task represents a task
type Task struct {
	ID             int64
	DoerID         int64                  `xorm:"index"` // operator
	Doer           *user_model.User       `xorm:"-"`
	OwnerID        int64                  `xorm:"index"` // repo owner id, when creating, the repoID maybe zero
	Owner          *user_model.User       `xorm:"-"`
	RepoID         int64                  `xorm:"index"`
	Repo           *repo_model.Repository `xorm:"-"`
	Type           structs.TaskType
	Status         structs.TaskStatus `xorm:"index"`
	StartTime      timeutil.TimeStamp
	EndTime        timeutil.TimeStamp
	PayloadContent string             `xorm:"TEXT"`
	Message        string             `xorm:"TEXT"` // if task failed, saved the error reason
	Created        timeutil.TimeStamp `xorm:"created"`
}

func init() {
	db.RegisterModel(new(Task))
}

// TranslatableMessage represents JSON struct that can be translated with a Locale
type TranslatableMessage struct {
	Format string
	Args   []interface{} `json:"omitempty"`
}

// LoadRepo loads repository of the task
func (task *Task) LoadRepo() error {
	return task.loadRepo(db.DefaultContext)
}

func (task *Task) loadRepo(ctx context.Context) error {
	if task.Repo != nil {
		return nil
	}
	var repo repo_model.Repository
	has, err := db.GetEngine(ctx).ID(task.RepoID).Get(&repo)
	if err != nil {
		return err
	} else if !has {
		return repo_model.ErrRepoNotExist{
			ID: task.RepoID,
		}
	}
	task.Repo = &repo
	return nil
}

// LoadDoer loads do user
func (task *Task) LoadDoer() error {
	if task.Doer != nil {
		return nil
	}

	var doer user_model.User
	has, err := db.GetEngine(db.DefaultContext).ID(task.DoerID).Get(&doer)
	if err != nil {
		return err
	} else if !has {
		return user_model.ErrUserNotExist{
			UID: task.DoerID,
		}
	}
	task.Doer = &doer

	return nil
}

// LoadOwner loads owner user
func (task *Task) LoadOwner() error {
	if task.Owner != nil {
		return nil
	}

	var owner user_model.User
	has, err := db.GetEngine(db.DefaultContext).ID(task.OwnerID).Get(&owner)
	if err != nil {
		return err
	} else if !has {
		return user_model.ErrUserNotExist{
			UID: task.OwnerID,
		}
	}
	task.Owner = &owner

	return nil
}

// UpdateCols updates some columns
func (task *Task) UpdateCols(cols ...string) error {
	_, err := db.GetEngine(db.DefaultContext).ID(task.ID).Cols(cols...).Update(task)
	return err
}

// MigrateConfig returns task config when migrate repository
func (task *Task) MigrateConfig() (*migration.MigrateOptions, error) {
	if task.Type == structs.TaskTypeMigrateRepo {
		var opts migration.MigrateOptions
		err := json.Unmarshal([]byte(task.PayloadContent), &opts)
		if err != nil {
			return nil, err
		}

		// decrypt credentials
		if opts.CloneAddrEncrypted != "" {
			if opts.CloneAddr, err = secret.DecryptSecret(setting.SecretKey, opts.CloneAddrEncrypted); err != nil {
				return nil, err
			}
		}
		if opts.AuthPasswordEncrypted != "" {
			if opts.AuthPassword, err = secret.DecryptSecret(setting.SecretKey, opts.AuthPasswordEncrypted); err != nil {
				return nil, err
			}
		}
		if opts.AuthTokenEncrypted != "" {
			if opts.AuthToken, err = secret.DecryptSecret(setting.SecretKey, opts.AuthTokenEncrypted); err != nil {
				return nil, err
			}
		}

		return &opts, nil
	}
	return nil, fmt.Errorf("Task type is %s, not Migrate Repo", task.Type.Name())
}

// ErrTaskDoesNotExist represents a "TaskDoesNotExist" kind of error.
type ErrTaskDoesNotExist struct {
	ID     int64
	RepoID int64
	Type   structs.TaskType
}

// IsErrTaskDoesNotExist checks if an error is a ErrTaskIsNotExist.
func IsErrTaskDoesNotExist(err error) bool {
	_, ok := err.(ErrTaskDoesNotExist)
	return ok
}

func (err ErrTaskDoesNotExist) Error() string {
	return fmt.Sprintf("task is not exist [id: %d, repo_id: %d, type: %d]",
		err.ID, err.RepoID, err.Type)
}

// GetMigratingTask returns the migrating task by repo's id
func GetMigratingTask(repoID int64) (*Task, error) {
	task := Task{
		RepoID: repoID,
		Type:   structs.TaskTypeMigrateRepo,
	}
	has, err := db.GetEngine(db.DefaultContext).Get(&task)
	if err != nil {
		return nil, err
	} else if !has {
		return nil, ErrTaskDoesNotExist{0, repoID, task.Type}
	}
	return &task, nil
}

// HasFinishedMigratingTask returns if a finished migration task exists for the repo.
func HasFinishedMigratingTask(repoID int64) (bool, error) {
	return db.GetEngine(db.DefaultContext).
		Where("repo_id=? AND type=? AND status=?", repoID, structs.TaskTypeMigrateRepo, structs.TaskStatusFinished).
		Table("task").
		Exist()
}

// GetMigratingTaskByID returns the migrating task by repo's id
func GetMigratingTaskByID(id, doerID int64) (*Task, *migration.MigrateOptions, error) {
	task := Task{
		ID:     id,
		DoerID: doerID,
		Type:   structs.TaskTypeMigrateRepo,
	}
	has, err := db.GetEngine(db.DefaultContext).Get(&task)
	if err != nil {
		return nil, nil, err
	} else if !has {
		return nil, nil, ErrTaskDoesNotExist{id, 0, task.Type}
	}

	var opts migration.MigrateOptions
	if err := json.Unmarshal([]byte(task.PayloadContent), &opts); err != nil {
		return nil, nil, err
	}
	return &task, &opts, nil
}

// FindTaskOptions find all tasks
type FindTaskOptions struct {
	Status int
}

// ToConds generates conditions for database operation.
func (opts FindTaskOptions) ToConds() builder.Cond {
	cond := builder.NewCond()
	if opts.Status >= 0 {
		cond = cond.And(builder.Eq{"status": opts.Status})
	}
	return cond
}

// FindTasks find all tasks
func FindTasks(opts FindTaskOptions) ([]*Task, error) {
	tasks := make([]*Task, 0, 10)
	err := db.GetEngine(db.DefaultContext).Where(opts.ToConds()).Find(&tasks)
	return tasks, err
}

// CreateTask creates a task on database
func CreateTask(task *Task) error {
	return db.Insert(db.DefaultContext, task)
}

// FinishMigrateTask updates database when migrate task finished
func FinishMigrateTask(task *Task) error {
	task.Status = structs.TaskStatusFinished
	task.EndTime = timeutil.TimeStampNow()

	// delete credentials when we're done, they're a liability.
	conf, err := task.MigrateConfig()
	if err != nil {
		return err
	}
	conf.AuthPassword = ""
	conf.AuthToken = ""
	conf.CloneAddr = util.SanitizeCredentialURLs(conf.CloneAddr)
	conf.AuthPasswordEncrypted = ""
	conf.AuthTokenEncrypted = ""
	conf.CloneAddrEncrypted = ""
	confBytes, err := json.Marshal(conf)
	if err != nil {
		return err
	}
	task.PayloadContent = string(confBytes)

	_, err = db.GetEngine(db.DefaultContext).ID(task.ID).Cols("status", "end_time", "payload_content").Update(task)
	return err
}
<commit_msg>Fix typos related to ErrTaskDoesNotExist error (#20118)<commit_after>// Copyright 2019 Gitea. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"context"
	"fmt"

	"code.gitea.io/gitea/models/db"
	repo_model "code.gitea.io/gitea/models/repo"
	user_model "code.gitea.io/gitea/models/user"
	"code.gitea.io/gitea/modules/json"
	"code.gitea.io/gitea/modules/migration"
	"code.gitea.io/gitea/modules/secret"
	"code.gitea.io/gitea/modules/setting"
	"code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/modules/timeutil"
	"code.gitea.io/gitea/modules/util"

	"xorm.io/builder"
)

// Task represents a task
type Task struct {
	ID             int64
	DoerID         int64                  `xorm:"index"` // operator
	Doer           *user_model.User       `xorm:"-"`
	OwnerID        int64                  `xorm:"index"` // repo owner id, when creating, the repoID maybe zero
	Owner          *user_model.User       `xorm:"-"`
	RepoID         int64                  `xorm:"index"`
	Repo           *repo_model.Repository `xorm:"-"`
	Type           structs.TaskType
	Status         structs.TaskStatus `xorm:"index"`
	StartTime      timeutil.TimeStamp
	EndTime        timeutil.TimeStamp
	PayloadContent string             `xorm:"TEXT"`
	Message        string             `xorm:"TEXT"` // if task failed, saved the error reason
	Created        timeutil.TimeStamp `xorm:"created"`
}

func init() {
	db.RegisterModel(new(Task))
}

// TranslatableMessage represents JSON struct that can be translated with a Locale
type TranslatableMessage struct {
	Format string
	Args   []interface{} `json:"omitempty"`
}

// LoadRepo loads repository of the task
func (task *Task) LoadRepo() error {
	return task.loadRepo(db.DefaultContext)
}

func (task *Task) loadRepo(ctx context.Context) error {
	if task.Repo != nil {
		return nil
	}
	var repo repo_model.Repository
	has, err := db.GetEngine(ctx).ID(task.RepoID).Get(&repo)
	if err != nil {
		return err
	} else if !has {
		return repo_model.ErrRepoNotExist{
			ID: task.RepoID,
		}
	}
	task.Repo = &repo
	return nil
}

// LoadDoer loads do user
func (task *Task) LoadDoer() error {
	if task.Doer != nil {
		return nil
	}

	var doer user_model.User
	has, err := db.GetEngine(db.DefaultContext).ID(task.DoerID).Get(&doer)
	if err != nil {
		return err
	} else if !has {
		return user_model.ErrUserNotExist{
			UID: task.DoerID,
		}
	}
	task.Doer = &doer

	return nil
}

// LoadOwner loads owner user
func (task *Task) LoadOwner() error {
	if task.Owner != nil {
		return nil
	}

	var owner user_model.User
	has, err := db.GetEngine(db.DefaultContext).ID(task.OwnerID).Get(&owner)
	if err != nil {
		return err
	} else if !has {
		return user_model.ErrUserNotExist{
			UID: task.OwnerID,
		}
	}
	task.Owner = &owner

	return nil
}

// UpdateCols updates some columns
func (task *Task) UpdateCols(cols ...string) error {
	_, err := db.GetEngine(db.DefaultContext).ID(task.ID).Cols(cols...).Update(task)
	return err
}

// MigrateConfig returns task config when migrate repository
func (task *Task) MigrateConfig() (*migration.MigrateOptions, error) {
	if task.Type == structs.TaskTypeMigrateRepo {
		var opts migration.MigrateOptions
		err := json.Unmarshal([]byte(task.PayloadContent), &opts)
		if err != nil {
			return nil, err
		}

		// decrypt credentials
		if opts.CloneAddrEncrypted != "" {
			if opts.CloneAddr, err = secret.DecryptSecret(setting.SecretKey, opts.CloneAddrEncrypted); err != nil {
				return nil, err
			}
		}
		if opts.AuthPasswordEncrypted != "" {
			if opts.AuthPassword, err = secret.DecryptSecret(setting.SecretKey, opts.AuthPasswordEncrypted); err != nil {
				return nil, err
			}
		}
		if opts.AuthTokenEncrypted != "" {
			if opts.AuthToken, err = secret.DecryptSecret(setting.SecretKey, opts.AuthTokenEncrypted); err != nil {
				return nil, err
			}
		}

		return &opts, nil
	}
	return nil, fmt.Errorf("Task type is %s, not Migrate Repo", task.Type.Name())
}

// ErrTaskDoesNotExist represents a "TaskDoesNotExist" kind of error.
type ErrTaskDoesNotExist struct {
	ID     int64
	RepoID int64
	Type   structs.TaskType
}

// IsErrTaskDoesNotExist checks if an error is a ErrTaskDoesNotExist.
func IsErrTaskDoesNotExist(err error) bool {
	_, ok := err.(ErrTaskDoesNotExist)
	return ok
}

func (err ErrTaskDoesNotExist) Error() string {
	return fmt.Sprintf("task does not exist [id: %d, repo_id: %d, type: %d]",
		err.ID, err.RepoID, err.Type)
}

// GetMigratingTask returns the migrating task by repo's id
func GetMigratingTask(repoID int64) (*Task, error) {
	task := Task{
		RepoID: repoID,
		Type:   structs.TaskTypeMigrateRepo,
	}
	has, err := db.GetEngine(db.DefaultContext).Get(&task)
	if err != nil {
		return nil, err
	} else if !has {
		return nil, ErrTaskDoesNotExist{0, repoID, task.Type}
	}
	return &task, nil
}

// HasFinishedMigratingTask returns if a finished migration task exists for the repo.
func HasFinishedMigratingTask(repoID int64) (bool, error) {
	return db.GetEngine(db.DefaultContext).
		Where("repo_id=? AND type=? AND status=?", repoID, structs.TaskTypeMigrateRepo, structs.TaskStatusFinished).
		Table("task").
		Exist()
}

// GetMigratingTaskByID returns the migrating task by repo's id
func GetMigratingTaskByID(id, doerID int64) (*Task, *migration.MigrateOptions, error) {
	task := Task{
		ID:     id,
		DoerID: doerID,
		Type:   structs.TaskTypeMigrateRepo,
	}
	has, err := db.GetEngine(db.DefaultContext).Get(&task)
	if err != nil {
		return nil, nil, err
	} else if !has {
		return nil, nil, ErrTaskDoesNotExist{id, 0, task.Type}
	}

	var opts migration.MigrateOptions
	if err := json.Unmarshal([]byte(task.PayloadContent), &opts); err != nil {
		return nil, nil, err
	}
	return &task, &opts, nil
}

// FindTaskOptions find all tasks
type FindTaskOptions struct {
	Status int
}

// ToConds generates conditions for database operation.
func (opts FindTaskOptions) ToConds() builder.Cond {
	cond := builder.NewCond()
	if opts.Status >= 0 {
		cond = cond.And(builder.Eq{"status": opts.Status})
	}
	return cond
}

// FindTasks find all tasks
func FindTasks(opts FindTaskOptions) ([]*Task, error) {
	tasks := make([]*Task, 0, 10)
	err := db.GetEngine(db.DefaultContext).Where(opts.ToConds()).Find(&tasks)
	return tasks, err
}

// CreateTask creates a task on database
func CreateTask(task *Task) error {
	return db.Insert(db.DefaultContext, task)
}

// FinishMigrateTask updates database when migrate task finished
func FinishMigrateTask(task *Task) error {
	task.Status = structs.TaskStatusFinished
	task.EndTime = timeutil.TimeStampNow()

	// delete credentials when we're done, they're a liability.
	conf, err := task.MigrateConfig()
	if err != nil {
		return err
	}
	conf.AuthPassword = ""
	conf.AuthToken = ""
	conf.CloneAddr = util.SanitizeCredentialURLs(conf.CloneAddr)
	conf.AuthPasswordEncrypted = ""
	conf.AuthTokenEncrypted = ""
	conf.CloneAddrEncrypted = ""
	confBytes, err := json.Marshal(conf)
	if err != nil {
		return err
	}
	task.PayloadContent = string(confBytes)

	_, err = db.GetEngine(db.DefaultContext).ID(task.ID).Cols("status", "end_time", "payload_content").Update(task)
	return err
}
<|endoftext|>


<commit_before>// Package main generates Railroad Diagrams from Participle grammar EBNF.
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/alecthomas/repr"

	"github.com/alecthomas/participle/v2/ebnf"
)

const (
	mergeRefThreshold  = -1
	mergeSizeThreshold = 0
)

type production struct {
	*ebnf.Production
	refs int
	size int
}

func generate(productions map[string]*production, n ebnf.Node) (s string) {
	switch n := n.(type) {
	case *ebnf.EBNF:
		s += `<!DOCTYPE html>

<link rel='stylesheet' href='railroad-diagrams.css'>

`
		for _, p := range n.Productions {
			s += generate(productions, p) + "\n"
		}
		s += "\n"

	case *ebnf.Production:
		if productions[n.Production].refs <= mergeRefThreshold {
			break
		}
		s += `<h1 id="` + n.Production + `">` + n.Production + "
\n"
		s += "\n"

	case *ebnf.Expression:
		s += "Choice(0, "
		for i, a := range n.Alternatives {
			if i > 0 {
				s += ", "
			}
			s += generate(productions, a)
		}
		s += ")"

	case *ebnf.SubExpression:
		if n.Lookahead != ebnf.LookaheadAssertionNone {
			log.Printf("lookahead assertions are not supported")
		}
		s += generate(productions, n.Expr)

	case *ebnf.Sequence:
		s += "Sequence("
		for i, t := range n.Terms {
			if i > 0 {
				s += ", "
			}
			s += generate(productions, t)
		}
		s += ")"

	case *ebnf.Term:
		if n.Negation {
			log.Printf("negation (~) is not supported")
		}
		switch n.Repetition {
		case "*":
			s += "ZeroOrMore("
		case "+":
			s += "OneOrMore("
		case "?":
			s += "Optional("
		}
		switch {
		case n.Name != "":
			p := productions[n.Name]
			if p.refs > mergeRefThreshold {
				s += fmt.Sprintf("NonTerminal(%q, {href:\"#%s\"})", n.Name, n.Name)
			} else {
				s += generate(productions, p.Expression)
			}

		case n.Group != nil:
			s += generate(productions, n.Group)

		case n.Literal != "":
			s += fmt.Sprintf("Terminal(%s)", n.Literal)

		case n.Token != "":
			s += fmt.Sprintf("NonTerminal(%q)", n.Token)

		default:
			panic(repr.String(n))

		}
		if n.Repetition != "" {
			s += ")"
		}

	default:
		panic(repr.String(n))
	}
	return
}

func countProductions(productions map[string]*production, n ebnf.Node) (size int) {
	switch n := n.(type) {
	case *ebnf.EBNF:
		for _, p := range n.Productions {
			productions[p.Production] = &production{Production: p}
		}
		for _, p := range n.Productions {
			countProductions(productions, p)
		}
		for _, p := range n.Productions {
			if productions[p.Production].size <= mergeSizeThreshold {
				productions[p.Production].refs = mergeRefThreshold
			}
		}
	case *ebnf.Production:
		productions[n.Production].size = countProductions(productions, n.Expression)
	case *ebnf.Expression:
		for _, a := range n.Alternatives {
			size += countProductions(productions, a)
		}
	case *ebnf.Sequence:
		for _, t := range n.Terms {
			size += countProductions(productions, t)
		}
	case *ebnf.Term:
		if n.Name != "" {
			productions[n.Name].refs++
			size++
		} else if n.Group != nil {
			size += countProductions(productions, n.Group)
		} else {
			size++
		}
	default:
		panic(repr.String(n))
	}
	return
}

func main() {
	fmt.Fprintln(os.Stderr, "Generates railroad diagrams from a Participle EBNF grammar on stdin.")
	fmt.Fprintln(os.Stderr, "  (EBNF is available from .String() on your parser)")
	ast, err := ebnf.Parse(os.Stdin)
	if err != nil {
		panic(err)
	}
	productions := map[string]*production{}
	countProductions(productions, ast)
	fmt.Println(generate(productions, ast))
	fmt.Fprintln(os.Stderr, ">>> Copy railroad-diagrams.{css,js} from https://github.com/tabatkins/railroad-diagrams")
}
<commit_msg>Hopefully fix the railroad tool.<commit_after>// Package main generates Railroad Diagrams from Participle grammar EBNF.
package main

import (
	"fmt"
	"os"

	"github.com/alecthomas/repr"

	"github.com/alecthomas/participle/v2/ebnf"
)

const (
	mergeRefThreshold  = -1
	mergeSizeThreshold = 0
)

type production struct {
	*ebnf.Production
	refs int
	size int
}

func generate(productions map[string]*production, n ebnf.Node) (s string) {
	switch n := n.(type) {
	case *ebnf.EBNF:
		s += `<!DOCTYPE html>

<link rel='stylesheet' href='railroad-diagrams.css'>

`
		for _, p := range n.Productions {
			s += generate(productions, p) + "\n"
		}
		s += "\n"

	case *ebnf.Production:
		if productions[n.Production].refs <= mergeRefThreshold {
			break
		}
		s += `<h1 id="` + n.Production + `">` + n.Production + "
\n"
		s += "\n"

	case *ebnf.Expression:
		s += "Choice(0, "
		for i, a := range n.Alternatives {
			if i > 0 {
				s += ", "
			}
			s += generate(productions, a)
		}
		s += ")"

	case *ebnf.SubExpression:
		s += generate(productions, n.Expr)
		if n.Lookahead != ebnf.LookaheadAssertionNone {
			s = fmt.Sprintf(`Group(%s, "?%c")`, s, n.Lookahead)
		}

	case *ebnf.Sequence:
		s += "Sequence("
		for i, t := range n.Terms {
			if i > 0 {
				s += ", "
			}
			s += generate(productions, t)
		}
		s += ")"

	case *ebnf.Term:
		switch n.Repetition {
		case "*":
			s += "ZeroOrMore("
		case "+":
			s += "OneOrMore("
		case "?":
			s += "Optional("
		}
		switch {
		case n.Name != "":
			p := productions[n.Name]
			if p.refs > mergeRefThreshold {
				s += fmt.Sprintf("NonTerminal(%q, {href:\"#%s\"})", n.Name, n.Name)
			} else {
				s += generate(productions, p.Expression)
			}

		case n.Group != nil:
			s += generate(productions, n.Group)

		case n.Literal != "":
			s += fmt.Sprintf("Terminal(%s)", n.Literal)

		case n.Token != "":
			s += fmt.Sprintf("NonTerminal(%q)", n.Token)

		default:
			panic(repr.String(n))

		}
		if n.Repetition != "" {
			s += ")"
		}
		if n.Negation {
			s = fmt.Sprintf(`Group(%s, "~")`, s)
		}

	default:
		panic(repr.String(n))
	}
	return
}

func countProductions(productions map[string]*production, n ebnf.Node) (size int) {
	switch n := n.(type) {
	case *ebnf.EBNF:
		for _, p := range n.Productions {
			productions[p.Production] = &production{Production: p}
		}
		for _, p := range n.Productions {
			countProductions(productions, p)
		}
		for _, p := range n.Productions {
			if productions[p.Production].size <= mergeSizeThreshold {
				productions[p.Production].refs = mergeRefThreshold
			}
		}
	case *ebnf.Production:
		productions[n.Production].size = countProductions(productions, n.Expression)
	case *ebnf.Expression:
		for _, a := range n.Alternatives {
			size += countProductions(productions, a)
		}
	case *ebnf.Sequence:
		for _, t := range n.Terms {
			size += countProductions(productions, t)
		}
	case *ebnf.Term:
		if n.Name != "" {
			productions[n.Name].refs++
			size++
		} else if n.Group != nil {
			size += countProductions(productions, n.Group)
		} else {
			size++
		}
	default:
		panic(repr.String(n))
	}
	return
}

func main() {
	fmt.Fprintln(os.Stderr, "Generates railroad diagrams from a Participle EBNF grammar on stdin.")
	fmt.Fprintln(os.Stderr, "  (EBNF is available from .String() on your parser)")
	ast, err := ebnf.Parse(os.Stdin)
	if err != nil {
		panic(err)
	}
	productions := map[string]*production{}
	countProductions(productions, ast)
	fmt.Println(generate(productions, ast))
	fmt.Fprintln(os.Stderr, ">>> Copy railroad-diagrams.{css,js} from https://github.com/tabatkins/railroad-diagrams")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"time"

	"github.com/toomore/gogrs/realtime"
	"github.com/toomore/gogrs/utils"
)

func TaipeiNow() time.Time {
	d := time.Now()
	return time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, utils.TaipeiTimeZone)
}

func prettyprint(data realtime.Data) {
	log.Printf("%s(%s) $%.2f(%.2f) %.0f/%.0f [%s] [%s %s]\n", data.Info.Name, data.Info.No,
		data.Price, data.Price-data.Open, data.Volume, data.VolumeAcc,
		data.TradeTime, data.SysInfo["sysDate"], data.SysInfo["sysTime"])
}

var twseNo = flag.String("twse", "", "TWSE NO.")
var otcNo = flag.String("otc", "", "OTC NO.")

func main() {
	flag.Parse()
	var r *realtime.StockRealTime
	if *twseNo != "" {
		r = realtime.NewTWSE(*twseNo, TaipeiNow())
		data, _ := r.Get()
		prettyprint(data)
	}
	if *otcNo != "" {
		r = realtime.NewOTC(*otcNo, TaipeiNow())
		data, _ := r.Get()
		prettyprint(data)
	}
	//flag.PrintDefaults()
}
<commit_msg>Add show index.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/toomore/gogrs/realtime"
	"github.com/toomore/gogrs/utils"
)

func TaipeiNow() time.Time {
	d := time.Now()
	return time.Date(d.Year(), d.Month(), d.Day(), 0, 0, 0, 0, utils.TaipeiTimeZone)
}

func prettyprint(data realtime.Data) string {
	return fmt.Sprintf("%s(%s) $%.2f(%.2f) %.0f/%.0f [%s] [%s %s]",
		data.Info.Name, data.Info.No,
		data.Price, data.Price-data.Open, data.Volume, data.VolumeAcc,
		data.TradeTime, data.SysInfo["sysDate"], data.SysInfo["sysTime"])
}

var twseNo = flag.String("twse", "", "TWSE NO.")
var otcNo = flag.String("otc", "", "OTC NO.")
var index = flag.Bool("index", false, "Show Index.")

func main() {
	flag.Parse()
	var r *realtime.StockRealTime
	if *twseNo != "" {
		r = realtime.NewTWSE(*twseNo, TaipeiNow())
		data, _ := r.Get()
		log.Println(prettyprint(data))
	}
	if *otcNo != "" {
		r = realtime.NewOTC(*otcNo, TaipeiNow())
		data, _ := r.Get()
		log.Println(prettyprint(data))
	}
	if *index {
		var wg sync.WaitGroup
		indexList := [3]*realtime.StockRealTime{realtime.NewWeight(TaipeiNow()),
			realtime.NewOTCI(TaipeiNow()), realtime.NewFRMSA(TaipeiNow())}
		result := make(chan string)
		wg.Add(3)
		for _, r := range indexList {
			go func(r *realtime.StockRealTime) {
				data, _ := r.Get()
				result <- prettyprint(data)
			}(r)
		}
		go func() {
			for v := range result {
				wg.Done()
				log.Println(v)
			}
		}()
		wg.Wait()
	}
	//flag.PrintDefaults()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/pborman/uuid"

	. "github.com/starkandwayne/shield/api"
	"github.com/starkandwayne/shield/tui"
)

func FindStore(search string, strict bool) (Store, uuid.UUID, error) {
	id := uuid.Parse(search)
	if id != nil {
		s, err := GetStore(id)
		if err == nil {
			return s, uuid.Parse(s.UUID), err
		}
		return s, nil, err
	}

	stores, err := GetStores(StoreFilter{
		Name: search,
	})
	if err != nil {
		return Store{}, nil, fmt.Errorf("Failed to retrieve list of archive stores: %s", err)
	}

	switch len(stores) {
	case 0:
		return Store{}, nil, fmt.Errorf("no matching archive stores found")

	case 1:
		return stores[0], uuid.Parse(stores[0].UUID), nil

	default:
		if strict {
			return Store{}, nil, fmt.Errorf("more than one matching archive store found")
		}
		t := tui.NewTable("Name", "Summary", "Plugin", "Configuration")
		for _, store := range stores {
			t.Row(store, store.Name, store.Summary, store.Plugin, store.Endpoint)
		}
		want := tui.Menu(
			fmt.Sprintf("More than one archive store matched your search for '%s':", search),
			&t, "Which archive store do you wanh?")
		return want.(Store), uuid.Parse(want.(Store).UUID), nil
	}
}

func FindTarget(search string, strict bool) (Target, uuid.UUID, error) {
	id := uuid.Parse(search)
	if id != nil {
		want, err := GetTarget(id)
		if err != nil {
			return Target{}, nil, err
		}
		return want, uuid.Parse(want.UUID), nil
	}

	targets, err := GetTargets(TargetFilter{
		Name: search,
	})
	if err != nil {
		return Target{}, nil, fmt.Errorf("Failed to retrieve list of backup targets: %s", err)
	}
	switch len(targets) {
	case 0:
		return Target{}, nil, fmt.Errorf("no matching backup targets found")

	case 1:
		return targets[0], uuid.Parse(targets[0].UUID), nil

	default:
		if strict {
			return Target{}, nil, fmt.Errorf("more than one matching backup target found")
		}
		t := tui.NewTable("Name", "Summary", "Plugin", "Configuration")
		for _, target := range targets {
			t.Row(target, target.Name, target.Summary, target.Plugin, target.Endpoint)
		}
		want := tui.Menu(
			fmt.Sprintf("More than one backup target matched your search query of '%s':", search),
			&t, "Which backup target do you want to use for this backup job?")
		return want.(Target), uuid.Parse(want.(Target).UUID), nil
	}
}

func FindRetentionPolicy(search string, strict bool) (RetentionPolicy, uuid.UUID, error) {
	id := uuid.Parse(search)
	if id != nil {
		want, err := GetRetentionPolicy(id)
		if err != nil {
			return RetentionPolicy{}, nil, err
		}
		return want, uuid.Parse(want.UUID), nil
	}

	policies, err := GetRetentionPolicies(RetentionPolicyFilter{
		Name: search,
	})
	if err != nil {
		return RetentionPolicy{}, nil, fmt.Errorf("Failed to retrieve list of retention policies: %s", err)
	}
	switch len(policies) {
	case 0:
		return RetentionPolicy{}, nil, fmt.Errorf("no matching retention policies found")

	case 1:
		return policies[0], uuid.Parse(policies[0].UUID), nil

	default:
		if strict {
			return RetentionPolicy{}, nil, fmt.Errorf("more than one matching retention policies found")
		}
		t := tui.NewTable("Name", "Summary", "Expires in")
		for _, policy := range policies {
			t.Row(policy, policy.Name, policy.Summary, fmt.Sprintf("%d days", policy.Expires/86400))
		}
		want := tui.Menu(
			fmt.Sprintf("More than one retention policy matched your search query of '%s':", search),
			&t, "Which retention policy do you want to use for this backup job?")
		return want.(RetentionPolicy), uuid.Parse(want.(RetentionPolicy).UUID), nil
	}
}

func FindSchedule(search string, strict bool) (Schedule, uuid.UUID, error) {
	id := uuid.Parse(search)
	if id != nil {
		want, err := GetSchedule(id)
		if err != nil {
			return Schedule{}, nil, err
		}
		return want, uuid.Parse(want.UUID), nil
	}

	schedules, err := GetSchedules(ScheduleFilter{
		Name: search,
	})
	if err != nil {
		return Schedule{}, nil, fmt.Errorf("Failed to retrieve list of backup schedules: %s", err)
	}
	switch len(schedules) {
	case 0:
		return Schedule{}, nil, fmt.Errorf("no matching backup schedules found")

	case 1:
		return schedules[0], uuid.Parse(schedules[0].UUID), nil

	default:
		if strict {
			return Schedule{}, nil, fmt.Errorf("more than one matching backup schedule found")
		}
		t := tui.NewTable("Name", "Summary", "Frequency / Interval (UTC)")
		for _, schedule := range schedules {
			t.Row(schedule, schedule.Name, schedule.Summary, schedule.When)
		}
		want := tui.Menu(
			fmt.Sprintf("More than one backup schedule matched your search query of '%s':", search),
			&t, "Which backup schedule do you want to use for this backup job?")
		return want.(Schedule), uuid.Parse(want.(Schedule).UUID), nil
	}
}

func FindJob(search string, strict bool) (Job, uuid.UUID, error) {
	id := uuid.Parse(search)
	if id != nil {
		want, err := GetJob(id)
		if err != nil {
			return Job{}, nil, err
		}
		return want, uuid.Parse(want.UUID), nil
	}

	jobs, err := GetJobs(JobFilter{
		Name: search,
	})
	if err != nil {
		return Job{}, nil, fmt.Errorf("Failed to retrieve list of jobs: %s", err)
	}
	switch len(jobs) {
	case 0:
		return Job{}, nil, fmt.Errorf("no matching jobs found")

	case 1:
		return jobs[0], uuid.Parse(jobs[0].UUID), nil

	default:
		if strict {
			return Job{}, nil, fmt.Errorf("more than one matching job found")
		}
		t := tui.NewTable("Name", "Summary", "Target", "Store", "Schedule")
		for _, job := range jobs {
			t.Row(job, job.Name, job.Summary, job.TargetName, job.StoreName, job.ScheduleWhen)
		}
		want := tui.Menu(
			fmt.Sprintf("More than one job matched your search query of '%s':", search),
			&t, "Which job do you want?")
		return want.(Job), uuid.Parse(want.(Job).UUID), nil
	}
}
<commit_msg>typo fix<commit_after>package main

import (
	"fmt"

	"github.com/pborman/uuid"

	. "github.com/starkandwayne/shield/api"
	"github.com/starkandwayne/shield/tui"
)

func FindStore(search string, strict bool) (Store, uuid.UUID, error) {
	id := uuid.Parse(search)
	if id != nil {
		s, err := GetStore(id)
		if err == nil {
			return s, uuid.Parse(s.UUID), err
		}
		return s, nil, err
	}

	stores, err := GetStores(StoreFilter{
		Name: search,
	})
	if err != nil {
		return Store{}, nil, fmt.Errorf("Failed to retrieve list of archive stores: %s", err)
	}

	switch len(stores) {
	case 0:
		return Store{}, nil, fmt.Errorf("no matching archive stores found")

	case 1:
		return stores[0], uuid.Parse(stores[0].UUID), nil

	default:
		if strict {
			return Store{}, nil, fmt.Errorf("more than one matching archive store found")
		}
		t := tui.NewTable("Name", "Summary", "Plugin", "Configuration")
		for _, store := range stores {
			t.Row(store, store.Name, store.Summary, store.Plugin, store.Endpoint)
		}
		want := tui.Menu(
			fmt.Sprintf("More than one archive store matched your search for '%s':", search),
			&t, "Which archive store do you want?")
		return want.(Store), uuid.Parse(want.(Store).UUID), nil
	}
}

func FindTarget(search string, strict bool) (Target, uuid.UUID, error) {
	id := uuid.Parse(search)
	if id != nil {
		want, err := GetTarget(id)
		if err != nil {
			return Target{}, nil, err
		}
		return want, uuid.Parse(want.UUID), nil
	}

	targets, err := GetTargets(TargetFilter{
		Name: search,
	})
	if err != nil {
		return Target{}, nil, fmt.Errorf("Failed to retrieve list of backup targets: %s", err)
	}
	switch len(targets) {
	case 0:
		return Target{}, nil, fmt.Errorf("no matching backup targets found")

	case 1:
		return targets[0], uuid.Parse(targets[0].UUID), nil

	default:
		if strict {
			return Target{}, nil, fmt.Errorf("more than one matching backup target found")
		}
		t := tui.NewTable("Name", "Summary", "Plugin", "Configuration")
		for _, target := range targets {
			t.Row(target, target.Name, target.Summary, target.Plugin, target.Endpoint)
		}
		want := tui.Menu(
			fmt.Sprintf("More than one backup target matched your search query of '%s':", search),
			&t, "Which backup target do you want to use for this backup job?")
		return want.(Target), uuid.Parse(want.(Target).UUID), nil
	}
}

func FindRetentionPolicy(search string, strict bool) (RetentionPolicy, uuid.UUID, error) {
	id := uuid.Parse(search)
	if id != nil {
		want, err := GetRetentionPolicy(id)
		if err != nil {
			return RetentionPolicy{}, nil, err
		}
		return want, uuid.Parse(want.UUID), nil
	}

	policies, err := GetRetentionPolicies(RetentionPolicyFilter{
		Name: search,
	})
	if err != nil {
		return RetentionPolicy{}, nil, fmt.Errorf("Failed to retrieve list of retention policies: %s", err)
	}
	switch len(policies) {
	case 0:
		return RetentionPolicy{}, nil, fmt.Errorf("no matching retention policies found")

	case 1:
		return policies[0], uuid.Parse(policies[0].UUID), nil

	default:
		if strict {
			return RetentionPolicy{}, nil, fmt.Errorf("more than one matching retention policies found")
		}
		t := tui.NewTable("Name", "Summary", "Expires in")
		for _, policy := range policies {
			t.Row(policy, policy.Name, policy.Summary, fmt.Sprintf("%d days", policy.Expires/86400))
		}
		want := tui.Menu(
			fmt.Sprintf("More than one retention policy matched your search query of '%s':", search),
			&t, "Which retention policy do you want to use for this backup job?")
		return want.(RetentionPolicy), uuid.Parse(want.(RetentionPolicy).UUID), nil
	}
}

func FindSchedule(search string, strict bool) (Schedule, uuid.UUID, error) {
	id := uuid.Parse(search)
	if id != nil {
		want, err := GetSchedule(id)
		if err != nil {
			return Schedule{}, nil, err
		}
		return want, uuid.Parse(want.UUID), nil
	}

	schedules, err := GetSchedules(ScheduleFilter{
		Name: search,
	})
	if err != nil {
		return Schedule{}, nil, fmt.Errorf("Failed to retrieve list of backup schedules: %s", err)
	}
	switch len(schedules) {
	case 0:
		return Schedule{}, nil, fmt.Errorf("no matching backup schedules found")

	case 1:
		return schedules[0], uuid.Parse(schedules[0].UUID), nil

	default:
		if strict {
			return Schedule{}, nil, fmt.Errorf("more than one matching backup schedule found")
		}
		t := tui.NewTable("Name", "Summary", "Frequency / Interval (UTC)")
		for _, schedule := range schedules {
			t.Row(schedule, schedule.Name, schedule.Summary, schedule.When)
		}
		want := tui.Menu(
			fmt.Sprintf("More than one backup schedule matched your search query of '%s':", search),
			&t, "Which backup schedule do you want to use for this backup job?")
		return want.(Schedule), uuid.Parse(want.(Schedule).UUID), nil
	}
}

func FindJob(search string, strict bool) (Job, uuid.UUID, error) {
	id := uuid.Parse(search)
	if id != nil {
		want, err := GetJob(id)
		if err != nil {
			return Job{}, nil, err
		}
		return want, uuid.Parse(want.UUID), nil
	}

	jobs, err := GetJobs(JobFilter{
		Name: search,
	})
	if err != nil {
		return Job{}, nil, fmt.Errorf("Failed to retrieve list of jobs: %s", err)
	}
	switch len(jobs) {
	case 0:
		return Job{}, nil, fmt.Errorf("no matching jobs found")

	case 1:
		return jobs[0], uuid.Parse(jobs[0].UUID), nil

	default:
		if strict {
			return Job{}, nil, fmt.Errorf("more than one matching job found")
		}
		t := tui.NewTable("Name", "Summary", "Target", "Store", "Schedule")
		for _, job := range jobs {
			t.Row(job, job.Name, job.Summary, job.TargetName, job.StoreName, job.ScheduleWhen)
		}
		want := tui.Menu(
			fmt.Sprintf("More than one job matched your search query of '%s':", search),
			&t, "Which job do you want?")
		return want.(Job), uuid.Parse(want.(Job).UUID), nil
	}
}
<|endoftext|>


<commit_before>// Copied and modified from: https://github.com/bcicen/ctop
// MIT License - Copyright (c) 2017 VektorLab

package termdash

import (
	ui "github.com/gizak/termui"
	"github.com/ohsu-comp-bio/funnel/cmd/termdash/config"
	"github.com/ohsu-comp-bio/funnel/cmd/termdash/expanded"
	"github.com/ohsu-comp-bio/funnel/proto/tes"
)

func RedrawRows(clr bool) {
	// reinit body rows
	cGrid.Clear()

	// build layout
	y := 1
	if config.GetSwitchVal("enableHeader") {
		header.SetPrevious(cursor.PreviousPageExists())
		header.SetNext(cursor.NextPageExists())
		header.SetFilter(config.GetVal("filterStr"))
		y += header.Height()
	}
	cGrid.SetY(y)

	for _, c := range cursor.filtered {
		cGrid.AddRows(c.Widgets)
	}

	if clr {
		ui.Clear()
	}
	if config.GetSwitchVal("enableHeader") {
		ui.Render(header)
	}
	cGrid.Align()
	ui.Render(cGrid)
}

func ExpandView(t *TaskWidget) {
	ui.Clear()
	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()

	ex := expanded.NewExpanded(t.Task)
	ex.Align()
	ui.Render(ex)

	HandleKeys("up", ex.Up)
	HandleKeys("down", ex.Down)
	HandleKeys("exit", ui.StopLoop)
	ui.Handle("/timer/1s", func(e ui.Event) {
		if !tes.TerminalState(t.Task.State) {
			task, err := cursor.RefreshTask(t.Task.Id)
			if err != nil {
				ex.DisplayError(err)
			} else {
				ex.Update(task.Task)
			}
			ui.Clear()
			ex.Align()
			ui.Render(ex)
		}
	})
	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		ex.SetWidth(ui.TermWidth())
		ex.Align()
	})

	ui.Loop()
}

func RefreshDisplay() {
	// skip display refresh during scroll
	if !cursor.isScrolling {
		needsClear := cursor.RefreshTaskList(false, false)
		RedrawRows(needsClear)
	}
}

func NextPage() {
	cursor.RefreshTaskList(false, true)
	RedrawRows(true)
}

func PreviousPage() {
	cursor.RefreshTaskList(true, false)
	RedrawRows(true)
}

func Display() bool {
	var menu func()
	var expand bool

	cGrid.SetWidth(ui.TermWidth())

	// initial draw
	header.Align()
	cursor.RefreshTaskList(false, false)
	RedrawRows(true)

	HandleKeys("up", cursor.Up)
	HandleKeys("down", cursor.Down)
	HandleKeys("left", PreviousPage)
	HandleKeys("right", NextPage)
	HandleKeys("pgup", cursor.PgUp)
	HandleKeys("pgdown", cursor.PgDown)

	HandleKeys("exit", ui.StopLoop)
	HandleKeys("help", func() {
		menu = HelpMenu
		ui.StopLoop()
	})

	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		expand = true
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/a", func(ui.Event) {
		config.Toggle("allTasks")
		RefreshDisplay()
	})
	ui.Handle("/sys/kbd/f", func(ui.Event) {
		menu = FilterMenu
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/H", func(ui.Event) {
		config.Toggle("enableHeader")
		RedrawRows(true)
	})
	ui.Handle("/sys/kbd/r", func(e ui.Event) {
		config.Toggle("sortReversed")
	})
	ui.Handle("/sys/kbd/s", func(ui.Event) {
		menu = SortMenu
		ui.StopLoop()
	})
	ui.Handle("/timer/1s", func(e ui.Event) {
		RefreshDisplay()
	})
	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		header.Align()
		cursor.ScrollPage()
		cGrid.SetWidth(ui.TermWidth())
		RedrawRows(true)
	})
	ui.Loop()

	if menu != nil {
		menu()
		return false
	}

	if expand {
		task := cursor.Selected()
		if task != nil {
			ExpandView(task)
		}
		return false
	}

	return true
}
<commit_msg>termdash: ensure expanded task widget gets full view<commit_after>// Copied and modified from: https://github.com/bcicen/ctop
// MIT License - Copyright (c) 2017 VektorLab

package termdash

import (
	ui "github.com/gizak/termui"
	"github.com/ohsu-comp-bio/funnel/cmd/termdash/config"
	"github.com/ohsu-comp-bio/funnel/cmd/termdash/expanded"
	"github.com/ohsu-comp-bio/funnel/proto/tes"
)

func RedrawRows(clr bool) {
	// reinit body rows
	cGrid.Clear()

	// build layout
	y := 1
	if config.GetSwitchVal("enableHeader") {
		header.SetPrevious(cursor.PreviousPageExists())
		header.SetNext(cursor.NextPageExists())
		header.SetFilter(config.GetVal("filterStr"))
		y += header.Height()
	}
	cGrid.SetY(y)

	for _, c := range cursor.filtered {
		cGrid.AddRows(c.Widgets)
	}

	if clr {
		ui.Clear()
	}
	if config.GetSwitchVal("enableHeader") {
		ui.Render(header)
	}
	cGrid.Align()
	ui.Render(cGrid)
}

func ExpandView(t *TaskWidget) {
	ui.Clear()
	ui.DefaultEvtStream.ResetHandlers()
	defer ui.DefaultEvtStream.ResetHandlers()

	ex := expanded.NewExpanded(t.Task)
	task, err := cursor.RefreshTask(t.Task.Id)
	if err != nil {
		ex.DisplayError(err)
	} else {
		ex.Update(task.Task)
	}
	ex.Align()
	ui.Render(ex)

	HandleKeys("up", ex.Up)
	HandleKeys("down", ex.Down)
	HandleKeys("exit", ui.StopLoop)
	ui.Handle("/timer/1s", func(e ui.Event) {
		if !tes.TerminalState(t.Task.State) {
			task, err := cursor.RefreshTask(t.Task.Id)
			if err != nil {
				ex.DisplayError(err)
			} else {
				ex.Update(task.Task)
			}
			ui.Clear()
			ex.Align()
			ui.Render(ex)
		}
	})
	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		ex.SetWidth(ui.TermWidth())
		ex.Align()
	})

	ui.Loop()
}

func RefreshDisplay() {
	// skip display refresh during scroll
	if !cursor.isScrolling {
		needsClear := cursor.RefreshTaskList(false, false)
		RedrawRows(needsClear)
	}
}

func NextPage() {
	cursor.RefreshTaskList(false, true)
	RedrawRows(true)
}

func PreviousPage() {
	cursor.RefreshTaskList(true, false)
	RedrawRows(true)
}

func Display() bool {
	var menu func()
	var expand bool

	cGrid.SetWidth(ui.TermWidth())

	// initial draw
	header.Align()
	cursor.RefreshTaskList(false, false)
	RedrawRows(true)

	HandleKeys("up", cursor.Up)
	HandleKeys("down", cursor.Down)
	HandleKeys("left", PreviousPage)
	HandleKeys("right", NextPage)
	HandleKeys("pgup", cursor.PgUp)
	HandleKeys("pgdown", cursor.PgDown)

	HandleKeys("exit", ui.StopLoop)
	HandleKeys("help", func() {
		menu = HelpMenu
		ui.StopLoop()
	})

	ui.Handle("/sys/kbd/<enter>", func(ui.Event) {
		expand = true
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/a", func(ui.Event) {
		config.Toggle("allTasks")
		RefreshDisplay()
	})
	ui.Handle("/sys/kbd/f", func(ui.Event) {
		menu = FilterMenu
		ui.StopLoop()
	})
	ui.Handle("/sys/kbd/H", func(ui.Event) {
		config.Toggle("enableHeader")
		RedrawRows(true)
	})
	ui.Handle("/sys/kbd/r", func(e ui.Event) {
		config.Toggle("sortReversed")
	})
	ui.Handle("/sys/kbd/s", func(ui.Event) {
		menu = SortMenu
		ui.StopLoop()
	})
	ui.Handle("/timer/1s", func(e ui.Event) {
		RefreshDisplay()
	})
	ui.Handle("/sys/wnd/resize", func(e ui.Event) {
		header.Align()
		cursor.ScrollPage()
		cGrid.SetWidth(ui.TermWidth())
		RedrawRows(true)
	})
	ui.Loop()

	if menu != nil {
		menu()
		return false
	}

	if expand {
		task := cursor.Selected()
		if task != nil {
			ExpandView(task)
		}
		return false
	}

	return true
}
<|endoftext|>