<commit_before>package main

import (
	"bytes"
	"fmt"
	"sort"
)

func main() {

	items := create(
		"items",
		[]string{"item_id", "item_name", "type_id", "price"},
	)
	items.insert(1, "apple", 1, 300)
	items.insert(2, "orange", 1, 130)
	items.insert(3, "cabbage", 2, 200)
	items.insert(4, "saury", 3, 220)
	items.insert(5, "seaweed", nil, 250)
	items.insert(6, "mushroom", 4, 180)

	types := create(
		"types",
		[]string{"type_id", "type_name"},
	)
	types.insert(1, "fruit")
	types.insert(2, "vegetable")
	types.insert(3, "fish")

	fmt.Println(items)
	fmt.Println(from("items"))
	fmt.Println(from("items").selectQ("item_name", "price"))
	fmt.Println(from("items").lessThan("price", 250))
	fmt.Println(from("items").leftJoin("types", "type_id"))
	fmt.Println(
		from(
			from("items").lessThan("price", 250),
		).leftJoin(
			from("types").lessThan("type_id", 3), "type_id",
		),
	)
}

var tables = map[string]*table{}

type column struct {
	parent string
	name   string
}

func newColumn(parent string, name string) *column {
	return &column{parent: parent, name: name}
}

type tuple struct {
	values []interface{}
}

func newTuple(vals []interface{}) *tuple {
	return &tuple{values: vals}
}

type relation struct {
	columns []*column
	tuples  []*tuple
}

func newRelation(cols []*column, tups []*tuple) *relation {
	return &relation{columns: cols, tuples: tups}
}

// TODO: rewrite by interfaces
//       this implementation is to use immediate string values as arguments
func from(x interface{}) *relation {
	if r, ok := x.(*relation); ok {
		return r
	}
	tblName := fmt.Sprint(x)
	t := tables[tblName]
	cols := []*column{}
	for _, c := range t.columns {
		cols = append(cols, newColumn(tblName, c.name))
	}
	return newRelation(cols, t.tuples)
}

func (r *relation) findColumn(name string) int {
	for i, c := range r.columns {
		if c.name == name {
			return i
		}
	}
	// we can simplify checking the existence of n in r,
	// by r.findColumn(n) <= len(r.columns) before random accesses
	return len(r.columns)
}

func (r *relation) selectQ(colNames ...string) *relation {
	idxs := []int{}
	newCols := []*column{}
	for _, cn := range colNames {
		idx := r.findColumn(cn)
		idxs = append(idxs, idx)
		if idx < len(r.columns) {
			newCols = append(newCols, r.columns[idx])
		}
	}
	newTups := []*tuple{}
	for _, tup := range r.tuples {
		vals := []interface{}{}
		for _, idx := range idxs {
			// TODO: Can I avoid to refer the nil pointer?
			if idx < len(tup.values) {
				vals = append(vals, tup.values[idx])
			} else {
				vals = append(vals, nil)
			}
		}
		newTups = append(newTups, newTuple(vals))
	}
	return newRelation(newCols, newTups)
}

func (r *relation) leftJoin(x interface{}, colName string) *relation {
	j := from(x)
	newCols := []*column{}
	newCols = append(newCols, r.columns...)
	newCols = append(newCols, j.columns...)
	rIdx := r.findColumn(colName)
	if len(r.columns) <= rIdx {
		return newRelation(newCols, []*tuple{})
	}
	newTups := []*tuple{}
	for _, rTup := range r.tuples {
		keyVal := rTup.values[rIdx]
		jRel := j.equal(colName, keyVal)
		if len(jRel.tuples) == 0 {
			vals := []interface{}{}
			vals = append(vals, rTup.values...)
			for len(vals) < len(newCols) {
				vals = append(vals, nil)
			}
			newTups = append(newTups, newTuple(vals))
		}
		for _, jTup := range jRel.tuples {
			vals := []interface{}{}
			vals = append(vals, rTup.values...)
			vals = append(vals, jTup.values...)
			newTups = append(newTups, newTuple(vals))
		}
	}
	return newRelation(newCols, newTups)
}

func (r *relation) lessThan(colName string, n int) *relation {
	idx := r.findColumn(colName)
	if idx >= len(r.columns) {
		return newRelation(r.columns, []*tuple{})
	}
	newTups := []*tuple{}
	for _, tup := range r.tuples {
		v, ok := tup.values[idx].(int)
		if ok && v < n {
			newTups = append(newTups, tup)
		}
	}
	return newRelation(r.columns, newTups)
}

func (r *relation) equal(colName string, key interface{}) *relation {
	// null check should be by isNull condition
	if key == nil {
		return newRelation(r.columns, []*tuple{})
	}
	idx := r.findColumn(colName)
	if idx >= len(r.columns) {
		return newRelation(r.columns, []*tuple{})
	}
	newTups := []*tuple{}
	for _, tup := range r.tuples {
		if tup.values[idx] == key {
			newTups = append(newTups, tup)
		}
	}
	return newRelation(r.columns, newTups)
}

type tupleSorter struct {
	tuples  []*tuple
	compare func(t1, t2 *tuple) bool
}

func (ts *tupleSorter) Len() int {
	return len(ts.tuples)
}

func (ts *tupleSorter) Swap(i, j int) {
	ts.tuples[i], ts.tuples[j] = ts.tuples[j], ts.tuples[i]
}

func (ts *tupleSorter) Less(i, j int) bool {
	return ts.compare(ts.tuples[i], ts.tuples[j])
}

func (r *relation) orderBy(colName string) *relation {
	idx := r.findColumn(colName)
	if idx >= len(r.columns) {
		return r
	}
	compare := func(t1, t2 *tuple) bool {
		n1, ok1 := t1.values[idx].(int)
		n2, ok2 := t2.values[idx].(int)
		if ok1 && ok2 {
			return n1 < n2
		}
		s1, ok1 := t1.values[idx].(string)
		s2, ok2 := t2.values[idx].(string)
		if ok1 && ok2 {
			return s1 < s2
		}
		return true
	}
	newTups := []*tuple{}
	newTups = append(newTups, r.tuples...)
	ts := &tupleSorter{tuples: newTups, compare: compare}
	sort.Sort(ts)
	return newRelation(r.columns, ts.tuples)
}

type aggregator func([]interface{}) interface{}

func (r *relation) groupBy(colName string, aggs ...aggregator) *relation {
	return nil
}

func (r *relation) String() string {
	var buf bytes.Buffer
	for _, c := range r.columns {
		buf.WriteByte('|')
		if c.parent != "" {
			buf.WriteString(c.parent)
			buf.WriteByte('.')
		}
		buf.WriteString(c.name)
	}
	buf.WriteString("|\n")
	for _, t := range r.tuples {
		for _, v := range t.values {
			buf.WriteByte('|')
			buf.WriteString(fmt.Sprint(v))
		}
		buf.WriteString("|\n")
	}
	return buf.String()
}

type table struct {
	relation
	name string
}

func newTable(name string, cols []*column) *table {
	t := &table{}
	t.name = name
	t.columns = cols
	t.tuples = []*tuple{}
	return t
}

func create(name string, colNames []string) *table {
	cols := []*column{}
	for _, cn := range colNames {
		cols = append(cols, newColumn("", cn))
	}
	t := newTable(name, cols)
	tables[name] = t
	return t
}

func (t *table) insert(vals ...interface{}) *table {
	t.tuples = append(t.tuples, newTuple(vals))
	return t
}
<commit_msg>Enrich aggregators to interfaces<commit_after>package main

import (
	"bytes"
	"fmt"
	"sort"
)

func main() {

	items := create(
		"items",
		[]string{"item_id", "item_name", "type_id", "price"},
	)
	items.insert(1, "apple", 1, 300)
	items.insert(2, "orange", 1, 130)
	items.insert(3, "cabbage", 2, 200)
	items.insert(4, "saury", 3, 220)
	items.insert(5, "seaweed", nil, 250)
	items.insert(6, "mushroom", 4, 180)

	types := create(
		"types",
		[]string{"type_id", "type_name"},
	)
	types.insert(1, "fruit")
	types.insert(2, "vegetable")
	types.insert(3, "fish")

	fmt.Println(items)
	fmt.Println(from("items"))
	fmt.Println(from("items").selectQ("item_name", "price"))
	fmt.Println(from("items").lessThan("price", 250))
	fmt.Println(from("items").leftJoin("types", "type_id"))
	fmt.Println(
		from(
			from("items").lessThan("price", 250),
		).leftJoin(
			from("types").lessThan("type_id", 3), "type_id",
		),
	)
}

var tables = map[string]*table{}

type column struct {
	parent string
	name   string
}

func newColumn(parent string, name string) *column {
	return &column{parent: parent, name: name}
}

type tuple struct {
	values []interface{}
}

func newTuple(vals []interface{}) *tuple {
	return &tuple{values: vals}
}

type relation struct {
	columns []*column
	tuples  []*tuple
}

func newRelation(cols []*column, tups []*tuple) *relation {
	return &relation{columns: cols, tuples: tups}
}

// TODO: rewrite by interfaces
//       this implementation is to use immediate string values as arguments
func from(x interface{}) *relation {
	if r, ok := x.(*relation); ok {
		return r
	}
	tblName := fmt.Sprint(x)
	t := tables[tblName]
	cols := []*column{}
	for _, c := range t.columns {
		cols = append(cols, newColumn(tblName, c.name))
	}
	return newRelation(cols, t.tuples)
}

func (r *relation) findColumn(name string) int {
	for i, c := range r.columns {
		if c.name == name {
			return i
		}
	}
	// we can simplify checking the existence of n in r,
	// by r.findColumn(n) <= len(r.columns) before random accesses
	return len(r.columns)
}

func (r *relation) selectQ(colNames ...string) *relation {
	idxs := []int{}
	newCols := []*column{}
	for _, cn := range colNames {
		idx := r.findColumn(cn)
		idxs = append(idxs, idx)
		if idx < len(r.columns) {
			newCols = append(newCols, r.columns[idx])
		}
	}
	newTups := []*tuple{}
	for _, tup := range r.tuples {
		vals := []interface{}{}
		for _, idx := range idxs {
			// TODO: Can I avoid to refer the nil pointer?
			if idx < len(tup.values) {
				vals = append(vals, tup.values[idx])
			} else {
				vals = append(vals, nil)
			}
		}
		newTups = append(newTups, newTuple(vals))
	}
	return newRelation(newCols, newTups)
}

func (r *relation) leftJoin(x interface{}, colName string) *relation {
	j := from(x)
	newCols := []*column{}
	newCols = append(newCols, r.columns...)
	newCols = append(newCols, j.columns...)
	rIdx := r.findColumn(colName)
	if len(r.columns) <= rIdx {
		return newRelation(newCols, []*tuple{})
	}
	newTups := []*tuple{}
	for _, rTup := range r.tuples {
		keyVal := rTup.values[rIdx]
		jRel := j.equal(colName, keyVal)
		if len(jRel.tuples) == 0 {
			vals := []interface{}{}
			vals = append(vals, rTup.values...)
			for len(vals) < len(newCols) {
				vals = append(vals, nil)
			}
			newTups = append(newTups, newTuple(vals))
		}
		for _, jTup := range jRel.tuples {
			vals := []interface{}{}
			vals = append(vals, rTup.values...)
			vals = append(vals, jTup.values...)
			newTups = append(newTups, newTuple(vals))
		}
	}
	return newRelation(newCols, newTups)
}

func (r *relation) lessThan(colName string, n int) *relation {
	idx := r.findColumn(colName)
	if idx >= len(r.columns) {
		return newRelation(r.columns, []*tuple{})
	}
	newTups := []*tuple{}
	for _, tup := range r.tuples {
		v, ok := tup.values[idx].(int)
		if ok && v < n {
			newTups = append(newTups, tup)
		}
	}
	return newRelation(r.columns, newTups)
}

func (r *relation) equal(colName string, key interface{}) *relation {
	// null check should be by isNull condition
	if key == nil {
		return newRelation(r.columns, []*tuple{})
	}
	idx := r.findColumn(colName)
	if idx >= len(r.columns) {
		return newRelation(r.columns, []*tuple{})
	}
	newTups := []*tuple{}
	for _, tup := range r.tuples {
		if tup.values[idx] == key {
			newTups = append(newTups, tup)
		}
	}
	return newRelation(r.columns, newTups)
}

type tupleSorter struct {
	tuples  []*tuple
	compare func(t1, t2 *tuple) bool
}

func (ts *tupleSorter) Len() int {
	return len(ts.tuples)
}

func (ts *tupleSorter) Swap(i, j int) {
	ts.tuples[i], ts.tuples[j] = ts.tuples[j], ts.tuples[i]
}

func (ts *tupleSorter) Less(i, j int) bool {
	return ts.compare(ts.tuples[i], ts.tuples[j])
}

func (r *relation) orderBy(colName string) *relation {
	idx := r.findColumn(colName)
	if idx >= len(r.columns) {
		return r
	}
	compare := func(t1, t2 *tuple) bool {
		n1, ok1 := t1.values[idx].(int)
		n2, ok2 := t2.values[idx].(int)
		if ok1 && ok2 {
			return n1 < n2
		}
		s1, ok1 := t1.values[idx].(string)
		s2, ok2 := t2.values[idx].(string)
		if ok1 && ok2 {
			return s1 < s2
		}
		return true
	}
	newTups := []*tuple{}
	newTups = append(newTups, r.tuples...)
	ts := &tupleSorter{tuples: newTups, compare: compare}
	sort.Sort(ts)
	return newRelation(r.columns, ts.tuples)
}

type aggregator interface {
	srcColName() string
	tgtColName() string
	compute() func(xs []interface{}) interface{}
}

func (r *relation) groupBy(colName string, aggs ...aggregator) *relation {
	return nil
}

func (r *relation) String() string {
	var buf bytes.Buffer
	for _, c := range r.columns {
		buf.WriteByte('|')
		if c.parent != "" {
			buf.WriteString(c.parent)
			buf.WriteByte('.')
		}
		buf.WriteString(c.name)
	}
	buf.WriteString("|\n")
	for _, t := range r.tuples {
		for _, v := range t.values {
			buf.WriteByte('|')
			buf.WriteString(fmt.Sprint(v))
		}
		buf.WriteString("|\n")
	}
	return buf.String()
}

type table struct {
	relation
	name string
}

func newTable(name string, cols []*column) *table {
	t := &table{}
	t.name = name
	t.columns = cols
	t.tuples = []*tuple{}
	return t
}

func create(name string, colNames []string) *table {
	cols := []*column{}
	for _, cn := range colNames {
		cols = append(cols, newColumn("", cn))
	}
	t := newTable(name, cols)
	tables[name] = t
	return t
}

func (t *table) insert(vals ...interface{}) *table {
	t.tuples = append(t.tuples, newTuple(vals))
	return t
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"time"

	conf "github.com/citruspi/iago/configuration"
	"github.com/citruspi/iago/handlers"
	"github.com/citruspi/iago/notifications"
	"github.com/citruspi/iago/projects"
	"github.com/fzzy/radix/extra/pubsub"
	"github.com/fzzy/radix/redis"
)

func main() {
	conf.Process()

	if conf.Iago.Mode == "server" {
		http.HandleFunc("/", handlers.TravisWebhook)
		http.ListenAndServe(conf.Web.Address, nil)
	} else if confi.Iago.Mode == "client" {
		for _, project := range projects.List {
			project.Deploy()
		}

		timeout := time.Duration(10) * time.Second

		conn, err := redis.DialTimeout("tcp", "127.0.0.1:6379", timeout)

		if err != nil {
			log.Fatal(err)
		}

		defer conn.Close()

		psc := pubsub.NewSubClient(conn)

		for _, project := range projects.List {
			_ = psc.Subscribe("iago." + project.Owner + "." + project.Repository)
		}

		for {
			psr := psc.Receive()

			if !psr.Timeout() {
				var notification notifications.Notification

				err = json.Unmarshal([]byte(psr.Message), &notification)

				if err != nil {
					log.Fatal(err)
				}

				projects.Process(notification)
			} else {
				continue
			}
		}
	}
}
<commit_msg>Fixed a typo<commit_after>package main

import (
	"encoding/json"
	"log"
	"net/http"
	"time"

	conf "github.com/citruspi/iago/configuration"
	"github.com/citruspi/iago/handlers"
	"github.com/citruspi/iago/notifications"
	"github.com/citruspi/iago/projects"
	"github.com/fzzy/radix/extra/pubsub"
	"github.com/fzzy/radix/redis"
)

func main() {
	conf.Process()

	if conf.Iago.Mode == "server" {
		http.HandleFunc("/", handlers.TravisWebhook)
		http.ListenAndServe(conf.Web.Address, nil)
	} else if conf.Iago.Mode == "client" {
		for _, project := range projects.List {
			project.Deploy()
		}

		timeout := time.Duration(10) * time.Second

		conn, err := redis.DialTimeout("tcp", "127.0.0.1:6379", timeout)

		if err != nil {
			log.Fatal(err)
		}

		defer conn.Close()

		psc := pubsub.NewSubClient(conn)

		for _, project := range projects.List {
			_ = psc.Subscribe("iago." + project.Owner + "." + project.Repository)
		}

		for {
			psr := psc.Receive()

			if !psr.Timeout() {
				var notification notifications.Notification

				err = json.Unmarshal([]byte(psr.Message), &notification)

				if err != nil {
					log.Fatal(err)
				}

				projects.Process(notification)
			} else {
				continue
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"github.com/Bpazy/wl520/welove"
	"github.com/bitly/go-simplejson"
	"github.com/ltt1987/alidayu"
	"io"
	"io/ioutil"
	"log"
	"os"
	"reflect"
	"strconv"
	"sync"
)

var w sync.WaitGroup

var (
	isServer   *bool
	path       *string
	port       *string
	allTask    *bool
	configPath *string
	visitTimes *int
	outputPath *string
	tree       *bool
	pet        *bool
	buyItemId  *int
	coin       *int
	farmSign   *bool
	smsNotify  *bool
)

func init() {
	isServer = flag.Bool("s", false, "启动我们的家HTTP代理")
	path = flag.String("out", "wl520.json", "生成的配置文件路径")
	port = flag.String("port", ":8080", "我们的家Http代理端口号")
	allTask = flag.Bool("a", false, "完成所有我们的家互动任务")
	configPath = flag.String("c", "wl520.json", "配置文件位置")
	visitTimes = flag.Int("v", -1, "每日拜访次数")
	outputPath = flag.String("log", "welove.log", "日志路径")
	tree = flag.Bool("t", false, "完成爱情树任务")
	pet = flag.Bool("p", false, "完成宠物任务")
	buyItemId = flag.Int("buy", 0, "农场购买物品ID")
	coin = flag.Int("coin", -1, "农场被购买物品ID的价格上限(闭区间)")
	farmSign = flag.Bool("farm-sign", false, "农场签到")
	smsNotify = flag.Bool("sms-notify", false, "检查签到情况并发送短信")
	flag.Parse()
}

func main() {

	welove.ServerRun(*path, *port, *isServer)    // 是否开启代理服务器
	love := initConfig(*outputPath, *configPath) // 读取配置文件

	log.Println("wl520 start.")
	goFunc(buyItem, love, *buyItemId, *coin, *buyItemId) // 购买指定物品
	goFunc(doAllTasks, love, *allTask)                   // 完成互动任务
	goFunc(doVisit, *visitTimes, love)                   // 拜访任务
	goFunc(doTreePost, love, *tree)                      // 爱情树任务
	goFunc(doPetTasks, love, *pet)                       // 宠物任务
	goFunc(doFarmSign, love, *farmSign)                  // 农场签到
	goFunc(doSmsNotify, love, *smsNotify)                // 检查签到情况并发送短信
	w.Wait()
	log.Println("wl520 end.")

}

func goFunc(f interface{}, args ...interface{}) {
	v := reflect.ValueOf(f)
	var a []reflect.Value
	for _, v := range args {
		a = append(a, reflect.ValueOf(v))
	}
	w.Add(1)
	go v.Call(a)
}

func doSmsNotify(love welove.Love, smsNotify bool) {
	defer w.Done()
	if !smsNotify {
		return
	} else {
		alidayu.AppKey = love.DayuAppKey
		alidayu.AppSecret = love.DayuAppSecret
	}
	loveTreeSms(love)
}

func loveTreeSms(love welove.Love) {
	bytes, err := welove.QueryTreeInfo(love.AccessToken, love.AppKey)
	if err != nil {
		log.Fatal(err)
	}
	js, _ := simplejson.NewJson(bytes)
	var loverLackWater, _ = js.Get("lover_lack_water").Int()
	var loverLackSunlight, _ = js.Get("lover_lack_sunlight").Int()
	var lackWater, _ = js.Get("lack_water").Int()
	var lackSunlight, _ = js.Get("lack_sunlight").Int()
	if loverLackSunlight == 1 || loverLackWater == 1 {
		success, resp := alidayu.SendSMS(love.LoverMobile, "ZY通知", love.DayuSunlightTemplateCode, `{"name":"女主人"}`)
		log.Println(success, resp)
	}
	if lackWater == 1 || lackSunlight == 1 {
		success, resp := alidayu.SendSMS(love.Mobile, "ZY通知", love.DayuSunlightTemplateCode, `{"name":"主人"}`)
		log.Println(success, resp)
	}
}

func doFarmSign(love welove.Love, do bool) {
	defer w.Done()
	if !do {
		return
	}
	res, err := welove.FarmSign(love)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	bytes, _ := ioutil.ReadAll(res.Body)
	log.Printf("农场签到: %s\n", string(bytes))
}

func buyItem(love welove.Love, itemId, coin, buyItemId int) {
	defer w.Done()
	if buyItemId == 0 {
		return
	}
	items := welove.QueryItems(love.AccessToken).Messages[0].AdItems
	for _, v := range items {
		if v.ItemID == itemId && v.Coin <= coin {
			status := welove.BuyItem(love.AccessToken, v.SellerFarmID, v.ID)
			log.Printf("农场购买result: %d, Raw: %+v\n", status.Result, status)
		}
	}
}

func doTreePost(love welove.Love, tree bool) {
	defer w.Done()
	if !tree {
		return
	}
	op := []int{1, 2}
	for _, v := range op {
		res, err := welove.TreePost(love.AccessToken, love.AppKey, v)
		if err != nil {
			log.Fatal(err)
		}
		bytes, _ := ioutil.ReadAll(res.Body)
		js, _ := simplejson.NewJson(bytes)
		result, _ := js.Get("result").Int()
		log.Printf("爱情树result: %d, Raw: %s\n", result, string(bytes))
	}
}

func initConfig(outputPath, configPath string) welove.Love {
	setLogFile(outputPath)
	return readConfig(configPath)
}

//读取配置文件
func readConfig(configPath string) welove.Love {
	bytes, err := ioutil.ReadFile(configPath)
	if err != nil {
		log.Fatal(err)
	}
	love := welove.Love{}
	json.Unmarshal(bytes, &love)
	return love
}

//配置日志
func setLogFile(outputPath string) {
	var file, err = os.OpenFile(outputPath, os.O_APPEND|os.O_CREATE|os.O_RDWR, os.ModeAppend)
	if err != nil {
		log.Fatal(err)
	}
	log.SetOutput(io.MultiWriter(os.Stdout, file))
}

func doVisit(visitTimes int, love welove.Love) {
	defer w.Done()
	if visitTimes == -1 {
		return
	}
	for i := 0; i < visitTimes; i++ {
		if id, ok := welove.RandomHouse(love.AccessToken); ok {
			res, err := welove.Visit(love.AccessToken, id)
			if err != nil {
				log.Fatal(err)
			}
			bytes, _ := ioutil.ReadAll(res.Body)
			js, _ := simplejson.NewJson(bytes)
			result, _ := js.Get("result").Int()
			log.Printf("拜访result: %d, Raw: %s\n", result, string(bytes))
		}
	}
}

func doAllTasks(love welove.Love, allTask bool) {
	defer w.Done()
	if !allTask {
		return
	}
	res, err := welove.GetLoveSpaceIdRaw(love.AccessToken, love.AppKey)
	if err != nil {
		log.Fatal(err)
	}
	bytes, _ := ioutil.ReadAll(res.Body)
	loveSpaceId := welove.GetLoveSpaceId(string(bytes))
	taskTypes := []int{1, 4, 5, 6, 7, 11}
	for _, v := range taskTypes {
		res, err := welove.HomePost(love.AccessToken, v, loveSpaceId)
		if err != nil {
			log.Printf("任务%d错误\n", v)
		}
		bytes, _ := ioutil.ReadAll(res.Body)
		m := make(map[string]interface{})
		json.Unmarshal(bytes, &m)
		log.Printf("任务%d, Raw: %s\n", v, string(bytes))
	}
}

func doPetTasks(love welove.Love, doPet bool) {
	defer w.Done()
	if !doPet {
		return
	}
	petStatus := welove.GetPetStatus(love.AccessToken)
	log.Printf("宠物状态Raw: %+v\n", petStatus)
	pet := petStatus.Messages[0].Pets[0]
	for _, v := range pet.PetTasks {
		if v.RemainTime != 0 {
			continue
		}
		taskResult := welove.DoPetTask(love.AccessToken, strconv.Itoa(pet.PetID), strconv.Itoa(v.TaskType))
		log.Printf("宠物任务%d, Raw: %+v\n", v.TaskType, taskResult)
	}
}
<commit_msg>Reformat code<commit_after>package main

import (
	"encoding/json"
	"flag"
	"github.com/Bpazy/wl520/welove"
	"github.com/bitly/go-simplejson"
	"github.com/ltt1987/alidayu"
	"io"
	"io/ioutil"
	"log"
	"os"
	"reflect"
	"strconv"
	"sync"
)

var w sync.WaitGroup

var (
	isServer   *bool
	path       *string
	port       *string
	allTask    *bool
	configPath *string
	visitTimes *int
	outputPath *string
	tree       *bool
	pet        *bool
	buyItemId  *int
	coin       *int
	farmSign   *bool
	smsNotify  *bool
)

func init() {
	isServer = flag.Bool("s", false, "启动我们的家HTTP代理")
	path = flag.String("out", "wl520.json", "生成的配置文件路径")
	port = flag.String("port", ":8080", "我们的家Http代理端口号")
	allTask = flag.Bool("a", false, "完成所有我们的家互动任务")
	configPath = flag.String("c", "wl520.json", "配置文件位置")
	visitTimes = flag.Int("v", -1, "每日拜访次数")
	outputPath = flag.String("log", "welove.log", "日志路径")
	tree = flag.Bool("t", false, "完成爱情树任务")
	pet = flag.Bool("p", false, "完成宠物任务")
	buyItemId = flag.Int("buy", 0, "农场购买物品ID")
	coin = flag.Int("coin", -1, "农场被购买物品ID的价格上限(闭区间)")
	farmSign = flag.Bool("farm-sign", false, "农场签到")
	smsNotify = flag.Bool("sms-notify", false, "检查签到情况并发送短信")
	flag.Parse()
}

func main() {
	welove.ServerRun(*path, *port, *isServer)    // 是否开启代理服务器
	love := initConfig(*outputPath, *configPath) // 读取配置文件

	log.Println("wl520 start.")
	goFunc(buyItem, love, *buyItemId, *coin, *buyItemId) // 购买指定物品
	goFunc(doAllTasks, love, *allTask)                   // 完成互动任务
	goFunc(doVisit, *visitTimes, love)                   // 拜访任务
	goFunc(doTreePost, love, *tree)                      // 爱情树任务
	goFunc(doPetTasks, love, *pet)                       // 宠物任务
	goFunc(doFarmSign, love, *farmSign)                  // 农场签到
	goFunc(doSmsNotify, love, *smsNotify)                // 检查签到情况并发送短信
	w.Wait()
	log.Println("wl520 end.")

}

func goFunc(f interface{}, args ...interface{}) {
	v := reflect.ValueOf(f)
	var a []reflect.Value
	for _, v := range args {
		a = append(a, reflect.ValueOf(v))
	}
	w.Add(1)
	go v.Call(a)
}

func doSmsNotify(love welove.Love, smsNotify bool) {
	defer w.Done()
	if !smsNotify {
		return
	} else {
		alidayu.AppKey = love.DayuAppKey
		alidayu.AppSecret = love.DayuAppSecret
	}
	loveTreeSms(love)
}

func loveTreeSms(love welove.Love) {
	bytes, err := welove.QueryTreeInfo(love.AccessToken, love.AppKey)
	if err != nil {
		log.Fatal(err)
	}
	js, _ := simplejson.NewJson(bytes)
	var loverLackWater, _ = js.Get("lover_lack_water").Int()
	var loverLackSunlight, _ = js.Get("lover_lack_sunlight").Int()
	var lackWater, _ = js.Get("lack_water").Int()
	var lackSunlight, _ = js.Get("lack_sunlight").Int()
	if loverLackSunlight == 1 || loverLackWater == 1 {
		success, resp := alidayu.SendSMS(love.LoverMobile, "ZY通知", love.DayuSunlightTemplateCode, `{"name":"女主人"}`)
		log.Println(success, resp)
	}
	if lackWater == 1 || lackSunlight == 1 {
		success, resp := alidayu.SendSMS(love.Mobile, "ZY通知", love.DayuSunlightTemplateCode, `{"name":"主人"}`)
		log.Println(success, resp)
	}
}

func doFarmSign(love welove.Love, do bool) {
	defer w.Done()
	if !do {
		return
	}
	res, err := welove.FarmSign(love)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	bytes, _ := ioutil.ReadAll(res.Body)
	log.Printf("农场签到: %s\n", string(bytes))
}

func buyItem(love welove.Love, itemId, coin, buyItemId int) {
	defer w.Done()
	if buyItemId == 0 {
		return
	}
	items := welove.QueryItems(love.AccessToken).Messages[0].AdItems
	for _, v := range items {
		if v.ItemID == itemId && v.Coin <= coin {
			status := welove.BuyItem(love.AccessToken, v.SellerFarmID, v.ID)
			log.Printf("农场购买result: %d, Raw: %+v\n", status.Result, status)
		}
	}
}

func doTreePost(love welove.Love, tree bool) {
	defer w.Done()
	if !tree {
		return
	}
	op := []int{1, 2}
	for _, v := range op {
		res, err := welove.TreePost(love.AccessToken, love.AppKey, v)
		if err != nil {
			log.Fatal(err)
		}
		bytes, _ := ioutil.ReadAll(res.Body)
		js, _ := simplejson.NewJson(bytes)
		result, _ := js.Get("result").Int()
		log.Printf("爱情树result: %d, Raw: %s\n", result, string(bytes))
	}
}

func initConfig(outputPath, configPath string) welove.Love {
	setLogFile(outputPath)
	return readConfig(configPath)
}

//读取配置文件
func readConfig(configPath string) welove.Love {
	bytes, err := ioutil.ReadFile(configPath)
	if err != nil {
		log.Fatal(err)
	}
	love := welove.Love{}
	json.Unmarshal(bytes, &love)
	return love
}

//配置日志
func setLogFile(outputPath string) {
	var file, err = os.OpenFile(outputPath, os.O_APPEND|os.O_CREATE|os.O_RDWR, os.ModeAppend)
	if err != nil {
		log.Fatal(err)
	}
	log.SetOutput(io.MultiWriter(os.Stdout, file))
}

func doVisit(visitTimes int, love welove.Love) {
	defer w.Done()
	if visitTimes == -1 {
		return
	}
	for i := 0; i < visitTimes; i++ {
		if id, ok := welove.RandomHouse(love.AccessToken); ok {
			res, err := welove.Visit(love.AccessToken, id)
			if err != nil {
				log.Fatal(err)
			}
			bytes, _ := ioutil.ReadAll(res.Body)
			js, _ := simplejson.NewJson(bytes)
			result, _ := js.Get("result").Int()
			log.Printf("拜访result: %d, Raw: %s\n", result, string(bytes))
		}
	}
}

func doAllTasks(love welove.Love, allTask bool) {
	defer w.Done()
	if !allTask {
		return
	}
	res, err := welove.GetLoveSpaceIdRaw(love.AccessToken, love.AppKey)
	if err != nil {
		log.Fatal(err)
	}
	bytes, _ := ioutil.ReadAll(res.Body)
	loveSpaceId := welove.GetLoveSpaceId(string(bytes))
	taskTypes := []int{1, 4, 5, 6, 7, 11}
	for _, v := range taskTypes {
		res, err := welove.HomePost(love.AccessToken, v, loveSpaceId)
		if err != nil {
			log.Printf("任务%d错误\n", v)
		}
		bytes, _ := ioutil.ReadAll(res.Body)
		m := make(map[string]interface{})
		json.Unmarshal(bytes, &m)
		log.Printf("任务%d, Raw: %s\n", v, string(bytes))
	}
}

func doPetTasks(love welove.Love, doPet bool) {
	defer w.Done()
	if !doPet {
		return
	}
	petStatus := welove.GetPetStatus(love.AccessToken)
	log.Printf("宠物状态Raw: %+v\n", petStatus)
	pet := petStatus.Messages[0].Pets[0]
	for _, v := range pet.PetTasks {
		if v.RemainTime != 0 {
			continue
		}
		taskResult := welove.DoPetTask(love.AccessToken, strconv.Itoa(pet.PetID), strconv.Itoa(v.TaskType))
		log.Printf("宠物任务%d, Raw: %+v\n", v.TaskType, taskResult)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"mime"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	mgo "gopkg.in/mgo.v2"

	"github.com/ONSdigital/florence/assets"
	"github.com/ONSdigital/florence/healthcheck"
	"github.com/ONSdigital/florence/upload"
	"github.com/ONSdigital/go-ns/handlers/reverseProxy"
	hc "github.com/ONSdigital/go-ns/healthcheck"
	"github.com/ONSdigital/go-ns/log"
	"github.com/ONSdigital/go-ns/server"
	"github.com/gorilla/pat"
	"github.com/gorilla/websocket"
)

var bindAddr = ":8080"
var babbageURL = "http://localhost:8080"
var zebedeeURL = "http://localhost:8082"
var recipeAPIURL = "http://localhost:22300"
var importAPIURL = "http://localhost:21800"
var datasetAPIURL = "http://localhost:22000"
var uploadBucketName = "dp-frontend-florence-file-uploads"
var enableNewApp = false
var mongoURI = "localhost:27017"

var getAsset = assets.Asset
var upgrader = websocket.Upgrader{}
var session *mgo.Session

// Version is set by the make target
var Version string

func main() {
	log.Debug("florence version", log.Data{"version": Version})

	if v := os.Getenv("BIND_ADDR"); len(v) > 0 {
		bindAddr = v
	}
	if v := os.Getenv("BABBAGE_URL"); len(v) > 0 {
		babbageURL = v
	}
	if v := os.Getenv("ZEBEDEE_URL"); len(v) > 0 {
		zebedeeURL = v
	}
	if v := os.Getenv("RECIPE_API_URL"); len(v) > 0 {
		recipeAPIURL = v
	}
	if v := os.Getenv("UPLOAD_BUCKET_NAME"); len(v) > 0 {
		uploadBucketName = v
	}
	if v := os.Getenv("IMPORT_API_URL"); len(v) > 0 {
		recipeAPIURL = v
	}
	if v := os.Getenv("DATASET_API_URL"); len(v) > 0 {
		datasetAPIURL = v
	}
	if v := os.Getenv("ENABLE_NEW_APP"); len(v) > 0 {
		enableNewApp, _ = strconv.ParseBool(v)
	}

	log.Namespace = "florence"

	zc := healthcheck.New(zebedeeURL, "zebedee")
	bc := healthcheck.New(babbageURL, "babbage")
	dc := healthcheck.New(datasetAPIURL, "dataset-api")
	rc := healthcheck.New(recipeAPIURL, "recipe-api")
	ic := healthcheck.New(importAPIURL, "import-api")

	/*
		NOTE:
		If there's any issues with this Florence server proxying redirects
		from either Babbage or Zebedee then the code in the previous Java
		Florence server might give some clues for a solution: https://github.com/ONSdigital/florence/blob/b13df0708b30493b98e9ce239103c59d7f409f98/src/main/java/com/github/onsdigital/florence/filter/Proxy.java#L125-L135

		The code has purposefully not been included in this Go replacement
		because we can't see what issue it's fixing and whether it's necessary.
	*/

	babbageURL, err := url.Parse(babbageURL)
	if err != nil {
		log.Error(err, nil)
		os.Exit(1)
	}
	babbageProxy := reverseProxy.Create(babbageURL, nil)

	zebedeeURL, err := url.Parse(zebedeeURL)
	if err != nil {
		log.Error(err, nil)
		os.Exit(1)
	}
	zebedeeProxy := reverseProxy.Create(zebedeeURL, zebedeeDirector)

	recipeAPIURL, err := url.Parse(recipeAPIURL)
	if err != nil {
		log.Error(err, nil)
		os.Exit(1)
	}
	recipeAPIProxy := reverseProxy.Create(recipeAPIURL, nil)

	importAPIURL, err := url.Parse(importAPIURL)
	if err != nil {
		log.Error(err, nil)
		os.Exit(1)
	}
	importAPIProxy := reverseProxy.Create(importAPIURL, importAPIDirector)

	datasetAPIURL, err := url.Parse(datasetAPIURL)
	if err != nil {
		log.Error(err, nil)
		os.Exit(1)
	}
	datasetAPIProxy := reverseProxy.Create(datasetAPIURL, datasetAPIDirector)

	router := pat.New()

	newAppHandler := refactoredIndexFile

	if !enableNewApp {
		newAppHandler = legacyIndexFile
	}

	uploader, err := upload.New(uploadBucketName)
	if err != nil {
		log.Error(err, nil)
		os.Exit(1)
	}

	router.Path("/healthcheck").HandlerFunc(hc.Do)

	router.Path("/upload").Methods("GET").HandlerFunc(uploader.CheckUploaded)
	router.Path("/upload").Methods("POST").HandlerFunc(uploader.Upload)
	router.Path("/upload/{id}").Methods("GET").HandlerFunc(uploader.GetS3URL)

	router.Handle("/zebedee{uri:/.*}", zebedeeProxy)
	router.Handle("/recipes{uri:.*}", recipeAPIProxy)
	router.Handle("/import{uri:.*}", importAPIProxy)
	router.Handle("/dataset{uri:.*}", datasetAPIProxy)
	router.HandleFunc("/florence/dist/{uri:.*}", staticFiles)
	router.HandleFunc("/florence", legacyIndexFile)
	router.HandleFunc("/florence/", redirectToFlorence)
	router.HandleFunc("/florence/index.html", redirectToFlorence)
	router.HandleFunc("/florence/collections", legacyIndexFile)
	router.HandleFunc("/florence/publishing-queue", legacyIndexFile)
	router.HandleFunc("/florence/reports", legacyIndexFile)
	router.HandleFunc("/florence/users-and-access", legacyIndexFile)
	router.HandleFunc("/florence/websocket", websocketHandler)
	router.HandleFunc("/florence{uri:/.*}", newAppHandler)
	router.Handle("/{uri:.*}", babbageProxy)

	log.Debug("Starting server", log.Data{
		"bind_addr":       bindAddr,
		"babbage_url":     babbageURL,
		"zebedee_url":     zebedeeURL,
		"recipe_api_url":  recipeAPIURL,
		"import_api_url":  importAPIURL,
		"dataset_api_url": datasetAPIURL,
		"enable_new_app":  enableNewApp,
	})

	s := server.New(bindAddr, router)
	// TODO need to reconsider default go-ns server timeouts
	s.Server.IdleTimeout = 120 * time.Second
	s.Server.WriteTimeout = 120 * time.Second
	s.Server.ReadTimeout = 30 * time.Second
	s.HandleOSSignals = false
	s.MiddlewareOrder = []string{"RequestID", "Log"}

	// FIXME temporary hack to remove timeout middleware (doesn't support hijacker interface)
	mo := s.MiddlewareOrder
	var newMo []string
	for _, mw := range mo {
		if mw != "Timeout" {
			newMo = append(newMo, mw)
		}
	}
	s.MiddlewareOrder = newMo

	go func() {
		if err := s.ListenAndServe(); err != nil {
			log.Error(err, nil)
			os.Exit(2)
		}
	}()

	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt, os.Kill)

	for {
		hc.MonitorExternal(bc, zc, ic, rc, dc)

		timer := time.NewTimer(time.Second * 60)

		select {
		case <-timer.C:
			continue
		case <-stop:
			log.Info("shutting service down gracefully", nil)
			timer.Stop()
			ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
			defer cancel()
			if err := s.Server.Shutdown(ctx); err != nil {
				log.Error(err, nil)
			}
			return
		}
	}
}

func redirectToFlorence(w http.ResponseWriter, req *http.Request) {
	http.Redirect(w, req, "/florence", 301)
}

func staticFiles(w http.ResponseWriter, req *http.Request) {
	path := req.URL.Query().Get(":uri")

	b, err := getAsset("../dist/" + path)
	if err != nil {
		log.Error(err, nil)
		w.WriteHeader(404)
		return
	}

	w.Header().Set(`Content-Type`, mime.TypeByExtension(filepath.Ext(path)))
	w.WriteHeader(200)
	w.Write(b)
}

func legacyIndexFile(w http.ResponseWriter, req *http.Request) {
	log.Debug("Getting legacy HTML file", nil)

	b, err := getAsset("../dist/legacy-assets/index.html")
	if err != nil {
		log.Error(err, nil)
		w.WriteHeader(404)
		return
	}

	w.Header().Set(`Content-Type`, "text/html")
	w.WriteHeader(200)
	w.Write(b)
}

func refactoredIndexFile(w http.ResponseWriter, req *http.Request) {
	log.Debug("Getting refactored HTML file", nil)

	b, err := getAsset("../dist/refactored.html")
	if err != nil {
		log.Error(err, nil)
		w.WriteHeader(404)
		return
	}

	w.Header().Set(`Content-Type`, "text/html")
	w.WriteHeader(200)
	w.Write(b)
}

func zebedeeDirector(req *http.Request) {
	if c, err := req.Cookie(`access_token`); err == nil && len(c.Value) > 0 {
		req.Header.Set(`X-Florence-Token`, c.Value)
	}
	req.URL.Path = strings.TrimPrefix(req.URL.Path, "/zebedee")
}

func importAPIDirector(req *http.Request) {
	req.URL.Path = strings.TrimPrefix(req.URL.Path, "/import")
}

func datasetAPIDirector(req *http.Request) {
	req.URL.Path = strings.TrimPrefix(req.URL.Path, "/dataset")
}

func websocketHandler(w http.ResponseWriter, req *http.Request) {
	c, err := upgrader.Upgrade(w, req, nil)
	if err != nil {
		log.ErrorR(req, err, nil)
		return
	}

	defer c.Close()

	err = c.WriteJSON(florenceServerEvent{"version", florenceVersionPayload{Version: Version}})
	if err != nil {
		log.ErrorR(req, err, nil)
		return
	}

	for {
		_, message, err := c.ReadMessage()
		if err != nil {
			log.ErrorR(req, err, nil)
			break
		}

		rdr := bufio.NewReader(bytes.NewReader(message))
		b, err := rdr.ReadBytes('{')
		if err != nil {
			log.ErrorR(req, err, log.Data{"bytes": string(b)})
			continue
		}

		tags := strings.Split(string(b), ":")
		eventID := tags[0]
		eventType := tags[1]
		eventData := message[len(eventID)+len(eventType)+2:]

		switch eventType {
		case "log":
			var e florenceLogEvent
			e.ServerTimestamp = time.Now().UTC().Format("2006-01-02T15:04:05.000-0700Z")
			err = json.Unmarshal(eventData, &e)
			if err != nil {
				log.ErrorR(req, err, log.Data{"data": string(eventData)})
				continue
			}
			log.Debug("client log", log.Data{"data": e})

			err = c.WriteJSON(florenceServerEvent{"ack", eventID})
			if err != nil {
				log.ErrorR(req, err, nil)
			}
		default:
			log.DebugR(req, "unknown event type", log.Data{"type": eventType, "data": string(eventData)})
		}

		// err = c.WriteMessage(mt, message)
		// if err != nil {
		// 	log.ErrorR(req, err, nil)
		// 	break
		// }
	}
}

type florenceLogEvent struct {
	ServerTimestamp string      `json:"-"`
	ClientTimestamp time.Time   `json:"clientTimestamp"`
	Type            string      `json:"type"`
	Location        string      `json:"location"`
	InstanceID      int         `json:"instanceID"`
	Payload         interface{} `json:"payload"`
}

type florenceServerEvent struct {
	Type    string      `json:"type"`
	Payload interface{} `json:"payload"`
}

type florenceVersionPayload struct {
	Version string `json:"version"`
}
<commit_msg>Fix logging failure due to type change on log event<commit_after>package main

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"mime"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	mgo "gopkg.in/mgo.v2"

	"github.com/ONSdigital/florence/assets"
	"github.com/ONSdigital/florence/healthcheck"
	"github.com/ONSdigital/florence/upload"
	"github.com/ONSdigital/go-ns/handlers/reverseProxy"
	hc "github.com/ONSdigital/go-ns/healthcheck"
	"github.com/ONSdigital/go-ns/log"
	"github.com/ONSdigital/go-ns/server"
	"github.com/gorilla/pat"
	"github.com/gorilla/websocket"
)

var bindAddr = ":8080"
var babbageURL = "http://localhost:8080"
var zebedeeURL = "http://localhost:8082"
var recipeAPIURL = "http://localhost:22300"
var importAPIURL = "http://localhost:21800"
var datasetAPIURL = "http://localhost:22000"
var uploadBucketName = "dp-frontend-florence-file-uploads"
var enableNewApp = false
var mongoURI = "localhost:27017"

var getAsset = assets.Asset
var upgrader = websocket.Upgrader{}
var session *mgo.Session

// Version is set by the make target
var Version string

func main() {
	log.Debug("florence version", log.Data{"version": Version})

	if v := os.Getenv("BIND_ADDR"); len(v) > 0 {
		bindAddr = v
	}
	if v := os.Getenv("BABBAGE_URL"); len(v) > 0 {
		babbageURL = v
	}
	if v := os.Getenv("ZEBEDEE_URL"); len(v) > 0 {
		zebedeeURL = v
	}
	if v := os.Getenv("RECIPE_API_URL"); len(v) > 0 {
		recipeAPIURL = v
	}
	if v := os.Getenv("UPLOAD_BUCKET_NAME"); len(v) > 0 {
		uploadBucketName = v
	}
	if v := os.Getenv("IMPORT_API_URL"); len(v) > 0 {
		recipeAPIURL = v
	}
	if v := os.Getenv("DATASET_API_URL"); len(v) > 0 {
		datasetAPIURL = v
	}
	if v := os.Getenv("ENABLE_NEW_APP"); len(v) > 0 {
		enableNewApp, _ = strconv.ParseBool(v)
	}

	log.Namespace = "florence"

	zc := healthcheck.New(zebedeeURL, "zebedee")
	bc := healthcheck.New(babbageURL, "babbage")
	dc := healthcheck.New(datasetAPIURL, "dataset-api")
	rc := healthcheck.New(recipeAPIURL, "recipe-api")
	ic := healthcheck.New(importAPIURL, "import-api")

	/*
		NOTE:
		If there's any issues with this Florence server proxying redirects
		from either Babbage or Zebedee then the code in the previous Java
		Florence server might give some clues for a solution: https://github.com/ONSdigital/florence/blob/b13df0708b30493b98e9ce239103c59d7f409f98/src/main/java/com/github/onsdigital/florence/filter/Proxy.java#L125-L135

		The code has purposefully not been included in this Go replacement
		because we can't see what issue it's fixing and whether it's necessary.
	*/

	babbageURL, err := url.Parse(babbageURL)
	if err != nil {
		log.Error(err, nil)
		os.Exit(1)
	}
	babbageProxy := reverseProxy.Create(babbageURL, nil)

	zebedeeURL, err := url.Parse(zebedeeURL)
	if err != nil {
		log.Error(err, nil)
		os.Exit(1)
	}
	zebedeeProxy := reverseProxy.Create(zebedeeURL, zebedeeDirector)

	recipeAPIURL, err := url.Parse(recipeAPIURL)
	if err != nil {
		log.Error(err, nil)
		os.Exit(1)
	}
	recipeAPIProxy := reverseProxy.Create(recipeAPIURL, nil)

	importAPIURL, err := url.Parse(importAPIURL)
	if err != nil {
		log.Error(err, nil)
		os.Exit(1)
	}
	importAPIProxy := reverseProxy.Create(importAPIURL, importAPIDirector)

	datasetAPIURL, err := url.Parse(datasetAPIURL)
	if err != nil {
		log.Error(err, nil)
		os.Exit(1)
	}
	datasetAPIProxy := reverseProxy.Create(datasetAPIURL, datasetAPIDirector)

	router := pat.New()

	newAppHandler := refactoredIndexFile

	if !enableNewApp {
		newAppHandler = legacyIndexFile
	}

	uploader, err := upload.New(uploadBucketName)
	if err != nil {
		log.Error(err, nil)
		os.Exit(1)
	}

	router.Path("/healthcheck").HandlerFunc(hc.Do)

	router.Path("/upload").Methods("GET").HandlerFunc(uploader.CheckUploaded)
	router.Path("/upload").Methods("POST").HandlerFunc(uploader.Upload)
	router.Path("/upload/{id}").Methods("GET").HandlerFunc(uploader.GetS3URL)

	router.Handle("/zebedee{uri:/.*}", zebedeeProxy)
	router.Handle("/recipes{uri:.*}", recipeAPIProxy)
	router.Handle("/import{uri:.*}", importAPIProxy)
	router.Handle("/dataset{uri:.*}", datasetAPIProxy)
	router.HandleFunc("/florence/dist/{uri:.*}", staticFiles)
	router.HandleFunc("/florence", legacyIndexFile)
	router.HandleFunc("/florence/", redirectToFlorence)
	router.HandleFunc("/florence/index.html", redirectToFlorence)
	router.HandleFunc("/florence/collections", legacyIndexFile)
	router.HandleFunc("/florence/publishing-queue", legacyIndexFile)
	router.HandleFunc("/florence/reports", legacyIndexFile)
	router.HandleFunc("/florence/users-and-access", legacyIndexFile)
	router.HandleFunc("/florence/websocket", websocketHandler)
	router.HandleFunc("/florence{uri:/.*}", newAppHandler)
	router.Handle("/{uri:.*}", babbageProxy)

	log.Debug("Starting server", log.Data{
		"bind_addr":       bindAddr,
		"babbage_url":     babbageURL,
		"zebedee_url":     zebedeeURL,
		"recipe_api_url":  recipeAPIURL,
		"import_api_url":  importAPIURL,
		"dataset_api_url": datasetAPIURL,
		"enable_new_app":  enableNewApp,
	})

	s := server.New(bindAddr, router)
	// TODO need to reconsider default go-ns server timeouts
	s.Server.IdleTimeout = 120 * time.Second
	s.Server.WriteTimeout = 120 * time.Second
	s.Server.ReadTimeout = 30 * time.Second
	s.HandleOSSignals = false
	s.MiddlewareOrder = []string{"RequestID", "Log"}

	// FIXME temporary hack to remove timeout middleware (doesn't support hijacker interface)
	mo := s.MiddlewareOrder
	var newMo []string
	for _, mw := range mo {
		if mw != "Timeout" {
			newMo = append(newMo, mw)
		}
	}
	s.MiddlewareOrder = newMo

	go func() {
		if err := s.ListenAndServe(); err != nil {
			log.Error(err, nil)
			os.Exit(2)
		}
	}()

	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt, os.Kill)

	for {
		hc.MonitorExternal(bc, zc, ic, rc, dc)

		timer := time.NewTimer(time.Second * 60)

		select {
		case <-timer.C:
			continue
		case <-stop:
			log.Info("shutting service down gracefully", nil)
			timer.Stop()
			ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
			defer cancel()
			if err := s.Server.Shutdown(ctx); err != nil {
				log.Error(err, nil)
			}
			return
		}
	}
}

func redirectToFlorence(w http.ResponseWriter, req *http.Request) {
	http.Redirect(w, req, "/florence", 301)
}

func staticFiles(w http.ResponseWriter, req *http.Request) {
	path := req.URL.Query().Get(":uri")

	b, err := getAsset("../dist/" + path)
	if err != nil {
		log.Error(err, nil)
		w.WriteHeader(404)
		return
	}

	w.Header().Set(`Content-Type`, mime.TypeByExtension(filepath.Ext(path)))
	w.WriteHeader(200)
	w.Write(b)
}

func legacyIndexFile(w http.ResponseWriter, req *http.Request) {
	log.Debug("Getting legacy HTML file", nil)

	b, err := getAsset("../dist/legacy-assets/index.html")
	if err != nil {
		log.Error(err, nil)
		w.WriteHeader(404)
		return
	}

	w.Header().Set(`Content-Type`, "text/html")
	w.WriteHeader(200)
	w.Write(b)
}

func refactoredIndexFile(w http.ResponseWriter, req *http.Request) {
	log.Debug("Getting refactored HTML file", nil)

	b, err := getAsset("../dist/refactored.html")
	if err != nil {
		log.Error(err, nil)
		w.WriteHeader(404)
		return
	}

	w.Header().Set(`Content-Type`, "text/html")
	w.WriteHeader(200)
	w.Write(b)
}

func zebedeeDirector(req *http.Request) {
	if c, err := req.Cookie(`access_token`); err == nil && len(c.Value) > 0 {
		req.Header.Set(`X-Florence-Token`, c.Value)
	}
	req.URL.Path = strings.TrimPrefix(req.URL.Path, "/zebedee")
}

func importAPIDirector(req *http.Request) {
	req.URL.Path = strings.TrimPrefix(req.URL.Path, "/import")
}

func datasetAPIDirector(req *http.Request) {
	req.URL.Path = strings.TrimPrefix(req.URL.Path, "/dataset")
}

func websocketHandler(w http.ResponseWriter, req *http.Request) {
	c, err := upgrader.Upgrade(w, req, nil)
	if err != nil {
		log.ErrorR(req, err, nil)
		return
	}

	defer c.Close()

	err = c.WriteJSON(florenceServerEvent{"version", florenceVersionPayload{Version: Version}})
	if err != nil {
		log.ErrorR(req, err, nil)
		return
	}

	for {
		_, message, err := c.ReadMessage()
		if err != nil {
			log.ErrorR(req, err, nil)
			break
		}

		rdr := bufio.NewReader(bytes.NewReader(message))
		b, err := rdr.ReadBytes('{')
		if err != nil {
			log.ErrorR(req, err, log.Data{"bytes": string(b)})
			continue
		}

		tags := strings.Split(string(b), ":")
		eventID := tags[0]
		eventType := tags[1]
		eventData := message[len(eventID)+len(eventType)+2:]

		switch eventType {
		case "log":
			var e florenceLogEvent
			e.ServerTimestamp = time.Now().UTC().Format("2006-01-02T15:04:05.000-0700Z")
			err = json.Unmarshal(eventData, &e)
			if err != nil {
				log.ErrorR(req, err, log.Data{"data": string(eventData)})
				continue
			}
			log.Debug("client log", log.Data{"data": e})

			err = c.WriteJSON(florenceServerEvent{"ack", eventID})
			if err != nil {
				log.ErrorR(req, err, nil)
			}
		default:
			log.DebugR(req, "unknown event type", log.Data{"type": eventType, "data": string(eventData)})
		}

		// err = c.WriteMessage(mt, message)
		// if err != nil {
		// 	log.ErrorR(req, err, nil)
		// 	break
		// }
	}
}

type florenceLogEvent struct {
	ServerTimestamp string      `json:"-"`
	ClientTimestamp time.Time   `json:"clientTimestamp"`
	Type            string      `json:"type"`
	Location        string      `json:"location"`
	InstanceID      string      `json:"instanceID"`
	Payload         interface{} `json:"payload"`
}

type florenceServerEvent struct {
	Type    string      `json:"type"`
	Payload interface{} `json:"payload"`
}

type florenceVersionPayload struct {
	Version string `json:"version"`
}
<|endoftext|>


<commit_before><commit_msg>fix output<commit_after><|endoftext|>


<commit_before>// Sample datastore-quickstart fetches an entity from Google Cloud Datastore.
package main

import (
        "fmt"
        "log"
        "errors"

        // Imports the Google Cloud Datastore client package.
        "cloud.google.com/go/datastore"
        "golang.org/x/net/context"
)

type database struct {
    projectID string
    ctx context.Context
    client datastore.Client
}

func (db *database) Init(projectID string) {
    db.projectID = projectID
    db.ctx = context.Background()
    var err error
    db.client, err = datastore.NewClient(db.ctx, db.projectID)
    if err != nil {
            log.Fatalf("Failed to create client: %v", err)
    }
}

type Task struct {
    Description string
}

func (db *database) StoreTask(kind, name string, task Task) string {
    key := datastore.NameKey(kind, name, nill)
    if _, err := db.client.Put(db.ctx, key, &task); err != nill {
        log.Fatalf("Failed to save task: %v", err)
    }
    return key
}

func main() {
    var db database
    db.Init("alert-height-150418")
    kind := "Task"
    name := "task001"
    task := Task{
            Description: "Buy milk",
    }
    fmt.Printf("Saved %v\n", db.StoreTask(kind, name, task))
}<commit_msg>fix database client typ e because datastore.NewClient returns a pointer type<commit_after>// Sample datastore-quickstart fetches an entity from Google Cloud Datastore.
package main

import (
        "fmt"
        "log"

        // Imports the Google Cloud Datastore client package.
        "cloud.google.com/go/datastore"
        "golang.org/x/net/context"
)

type database struct {
    projectID string
    ctx context.Context
    client *datastore.Client
}

func (db *database) Init(projectID string) {
    db.projectID = projectID
    db.ctx = context.Background()
    var err error
    db.client, err = datastore.NewClient(db.ctx, db.projectID)
    if err != nil {
            log.Fatalf("Failed to create client: %v", err)
    }
}

type Task struct {
    Description string
}

func (db *database) StoreTask(kind, name string, task Task) string {
    key := datastore.NameKey(kind, name, nill)
    if _, err := db.client.Put(db.ctx, key, &task); err != nill {
        log.Fatalf("Failed to save task: %v", err)
    }
    return key
}

func main() {
    var db database
    db.Init("alert-height-150418")
    kind := "Task"
    name := "task001"
    task := Task{
            Description: "Buy milk",
    }
    fmt.Printf("Saved %v\n", db.StoreTask(kind, name, task))
}<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/orchardup/orchard/cli"
	"github.com/orchardup/orchard/github.com/docopt/docopt.go"
	"github.com/orchardup/orchard/proxy"
	"github.com/orchardup/orchard/tlsconfig"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"text/tabwriter"
)

func main() {
	usage := `Orchard.

Usage:
  orchard hosts
  orchard hosts create NAME
  orchard hosts rm NAME
  orchard [options] docker [COMMAND...]
  orchard [options] proxy

Options:
  -h --help               Show this screen.
  --version               Show version.
  -H NAME, --host=NAME    Name of host to connect to (instead of 'default')`

	args, err := docopt.Parse(usage, nil, true, "Orchard 2.0.0", true)
	if err != nil {
		fmt.Println("Error parsing arguments: %s\n", err)
		return
	}

	var cmdErr error = nil

	if args["hosts"] == true {
		cmdErr = Hosts(args)
	} else if args["docker"] == true || args["proxy"] == true {
		cmdErr = Docker(args)
	}

	if cmdErr != nil {
		fmt.Fprintln(os.Stderr, cmdErr)
		os.Exit(1)
	}
}

func Docker(args map[string]interface{}) error {
	hostName := "default"
	if args["--host"] != nil {
		hostName = args["--host"].(string)
	}

	dirname, err := ioutil.TempDir("/tmp", "orchard-")
	if err != nil {
		return fmt.Errorf("Error creating temporary directory: %s\n", err)
	}
	defer os.RemoveAll(dirname)
	socketPath := path.Join(dirname, "orchard.sock")

	p, err := MakeProxy(socketPath, hostName)
	if err != nil {
		return fmt.Errorf("Error starting proxy: %v\n", err)
	}

	go p.Start()
	defer p.Stop()

	if err := <-p.ErrorChannel; err != nil {
		return fmt.Errorf("Error starting proxy: %v\n", err)
	}

	if args["docker"] == true {
		err := CallDocker(args["COMMAND"].([]string), []string{"DOCKER_HOST=unix://" + socketPath})
		if err != nil {
			return fmt.Errorf("Docker exited with error")
		}
	} else {
		fmt.Println("Started proxy at unix://" + socketPath)

		c := make(chan os.Signal)
		signal.Notify(c, syscall.SIGINT, syscall.SIGKILL)
		<-c

		fmt.Println("\nStopping proxy")
	}

	return nil
}

func MakeProxy(socketPath string, hostName string) (*proxy.Proxy, error) {
	httpClient, err := authenticator.Authenticate()
	if err != nil {
		return nil, err
	}

	host, err := httpClient.GetHost(hostName)
	if err != nil {
		return nil, err
	}
	destination := host.IPAddress + ":4243"

	fmt.Printf("Connecting to %s...\n", destination)

	certData := []byte(host.ClientCert)
	keyData := []byte(host.ClientKey)
	config, err := tlsconfig.GetTLSConfig(certData, keyData)
	if err != nil {
		return nil, err
	}

	return proxy.New(
		func() (net.Listener, error) { return net.Listen("unix", socketPath) },
		func() (net.Conn, error) { return tls.Dial("tcp", destination, config) },
	), nil
}

func CallDocker(args []string, env []string) error {
	dockerPath := GetDockerPath()
	if dockerPath == "" {
		return errors.New("Can't find `docker` executable in $PATH.\nYou might need to install it: http://docs.docker.io/en/latest/installation/#installation-list")
	}

	cmd := exec.Command(dockerPath, args...)
	cmd.Env = env
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func GetDockerPath() string {
	for _, dir := range strings.Split(os.Getenv("PATH"), ":") {
		dockerPath := path.Join(dir, "docker")
		_, err := os.Stat(dockerPath)
		if err == nil {
			return dockerPath
		}
	}
	return ""
}

func Hosts(args map[string]interface{}) error {
	httpClient, err := authenticator.Authenticate()
	if err != nil {
		fmt.Printf("Error authenticating:\n%s\n", err)
	}

	if args["create"] == true {
		host, err := httpClient.CreateHost(args["NAME"].(string))
		if err != nil {
			return err
		}
		fmt.Printf("Created %s with IP address %s\n", host.Name, host.IPAddress)
	} else if args["rm"] == true {
		err := httpClient.DeleteHost(args["NAME"].(string))
		if err != nil {
			return err
		}
		fmt.Printf("Removed %s\n", args["NAME"].(string))
	} else {
		hosts, err := httpClient.GetHosts()
		if err != nil {
			return err
		}

		writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
		fmt.Fprintln(writer, "NAME\tIP")
		for _, host := range hosts {
			fmt.Fprintf(writer, "%s\t%s\n", host.Name, host.IPAddress)
		}
		writer.Flush()
	}

	return nil
}
<commit_msg>Remove 'connecting...' message<commit_after>package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/orchardup/orchard/cli"
	"github.com/orchardup/orchard/github.com/docopt/docopt.go"
	"github.com/orchardup/orchard/proxy"
	"github.com/orchardup/orchard/tlsconfig"
	"io/ioutil"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"strings"
	"syscall"
	"text/tabwriter"
)

func main() {
	usage := `Orchard.

Usage:
  orchard hosts
  orchard hosts create NAME
  orchard hosts rm NAME
  orchard [options] docker [COMMAND...]
  orchard [options] proxy

Options:
  -h --help               Show this screen.
  --version               Show version.
  -H NAME, --host=NAME    Name of host to connect to (instead of 'default')`

	args, err := docopt.Parse(usage, nil, true, "Orchard 2.0.0", true)
	if err != nil {
		fmt.Println("Error parsing arguments: %s\n", err)
		return
	}

	var cmdErr error = nil

	if args["hosts"] == true {
		cmdErr = Hosts(args)
	} else if args["docker"] == true || args["proxy"] == true {
		cmdErr = Docker(args)
	}

	if cmdErr != nil {
		fmt.Fprintln(os.Stderr, cmdErr)
		os.Exit(1)
	}
}

func Docker(args map[string]interface{}) error {
	hostName := "default"
	if args["--host"] != nil {
		hostName = args["--host"].(string)
	}

	dirname, err := ioutil.TempDir("/tmp", "orchard-")
	if err != nil {
		return fmt.Errorf("Error creating temporary directory: %s\n", err)
	}
	defer os.RemoveAll(dirname)
	socketPath := path.Join(dirname, "orchard.sock")

	p, err := MakeProxy(socketPath, hostName)
	if err != nil {
		return fmt.Errorf("Error starting proxy: %v\n", err)
	}

	go p.Start()
	defer p.Stop()

	if err := <-p.ErrorChannel; err != nil {
		return fmt.Errorf("Error starting proxy: %v\n", err)
	}

	if args["docker"] == true {
		err := CallDocker(args["COMMAND"].([]string), []string{"DOCKER_HOST=unix://" + socketPath})
		if err != nil {
			return fmt.Errorf("Docker exited with error")
		}
	} else {
		fmt.Println("Started proxy at unix://" + socketPath)

		c := make(chan os.Signal)
		signal.Notify(c, syscall.SIGINT, syscall.SIGKILL)
		<-c

		fmt.Println("\nStopping proxy")
	}

	return nil
}

func MakeProxy(socketPath string, hostName string) (*proxy.Proxy, error) {
	httpClient, err := authenticator.Authenticate()
	if err != nil {
		return nil, err
	}

	host, err := httpClient.GetHost(hostName)
	if err != nil {
		return nil, err
	}
	destination := host.IPAddress + ":4243"

	certData := []byte(host.ClientCert)
	keyData := []byte(host.ClientKey)
	config, err := tlsconfig.GetTLSConfig(certData, keyData)
	if err != nil {
		return nil, err
	}

	return proxy.New(
		func() (net.Listener, error) { return net.Listen("unix", socketPath) },
		func() (net.Conn, error) { return tls.Dial("tcp", destination, config) },
	), nil
}

func CallDocker(args []string, env []string) error {
	dockerPath := GetDockerPath()
	if dockerPath == "" {
		return errors.New("Can't find `docker` executable in $PATH.\nYou might need to install it: http://docs.docker.io/en/latest/installation/#installation-list")
	}

	cmd := exec.Command(dockerPath, args...)
	cmd.Env = env
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func GetDockerPath() string {
	for _, dir := range strings.Split(os.Getenv("PATH"), ":") {
		dockerPath := path.Join(dir, "docker")
		_, err := os.Stat(dockerPath)
		if err == nil {
			return dockerPath
		}
	}
	return ""
}

func Hosts(args map[string]interface{}) error {
	httpClient, err := authenticator.Authenticate()
	if err != nil {
		fmt.Printf("Error authenticating:\n%s\n", err)
	}

	if args["create"] == true {
		host, err := httpClient.CreateHost(args["NAME"].(string))
		if err != nil {
			return err
		}
		fmt.Printf("Created %s with IP address %s\n", host.Name, host.IPAddress)
	} else if args["rm"] == true {
		err := httpClient.DeleteHost(args["NAME"].(string))
		if err != nil {
			return err
		}
		fmt.Printf("Removed %s\n", args["NAME"].(string))
	} else {
		hosts, err := httpClient.GetHosts()
		if err != nil {
			return err
		}

		writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
		fmt.Fprintln(writer, "NAME\tIP")
		for _, host := range hosts {
			fmt.Fprintf(writer, "%s\t%s\n", host.Name, host.IPAddress)
		}
		writer.Flush()
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"./config"
	"./endpoints"
	"./models"

	"database/sql"
	"fmt"
	"net/http"
	"os"

	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
)

func main() {
	cfgPath := os.Getenv("CONFIG_FILE")
	if cfgPath == "" {
		cfgPath = "./config/config.json"
	}
	cfg := config.MustLoad(cfgPath)

	db, err := sql.Open("mysql", cfg.Database)
	if err != nil {
		panic(err)
	}

	initErr := models.InitDB(db)
	if initErr != nil {
		panic(initErr)
	}

	router := mux.NewRouter()
	projectsRouter := router.PathPrefix("/projects").Subrouter()
	endpoints.RegisterProjectHandlers(projectsRouter, db, &cfg)
	releasesRouter := router.PathPrefix("/projects/{projectId}/releases").Subrouter()
	endpoints.RegisterReleaseHandlers(releasesRouter, db, &cfg)
	pagesRouter := router.PathPrefix("/projects/{projectId}/releases/{releaseId}/pages").Subrouter()
	endpoints.RegisterPageHandlers(pagesRouter, db, &cfg)

	// Should match /{projectName} - {chapter}[{version}]/{page}.{ext}
	router.HandleFunc("/{pc:\\w+\\s-\\s\\w+\\[\\d+\\]}/{page:\\w+\\.\\w+}", endpoints.DownloadImage(db, &cfg)).Methods("GET")
	// Should match /{projectName} - {chapter}[{version}][{groupName}].zip
	router.HandleFunc("/{path:\\w+\\s-\\s\\w+\\[\\d+\\]\\[\\w+\\]\\.zip}", endpoints.DownloadArchive(db, &cfg)).Methods("GET")

	address := cfg.BindAddress
	fmt.Printf("Listening on %s\n", address)
	http.ListenAndServe(address, router)
}
<commit_msg>Add HTTP request logging<commit_after>package main

import (
	"./config"
	"./endpoints"
	"./models"

	"database/sql"
	"fmt"
	"net/http"
	"os"

	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

func main() {
	cfgPath := os.Getenv("CONFIG_FILE")
	if cfgPath == "" {
		cfgPath = "./config/config.json"
	}
	cfg := config.MustLoad(cfgPath)

	db, err := sql.Open("mysql", cfg.Database)
	if err != nil {
		panic(err)
	}

	initErr := models.InitDB(db)
	if initErr != nil {
		panic(initErr)
	}

	router := mux.NewRouter()
	projectsRouter := router.PathPrefix("/projects").Subrouter()
	endpoints.RegisterProjectHandlers(projectsRouter, db, &cfg)
	releasesRouter := router.PathPrefix("/projects/{projectId}/releases").Subrouter()
	endpoints.RegisterReleaseHandlers(releasesRouter, db, &cfg)
	pagesRouter := router.PathPrefix("/projects/{projectId}/releases/{releaseId}/pages").Subrouter()
	endpoints.RegisterPageHandlers(pagesRouter, db, &cfg)

	// Should match /{projectName} - {chapter}[{version}]/{page}.{ext}
	router.HandleFunc("/{pc:\\w+\\s-\\s\\w+\\[\\d+\\]}/{page:\\w+\\.\\w+}", endpoints.DownloadImage(db, &cfg)).Methods("GET")
	// Should match /{projectName} - {chapter}[{version}][{groupName}].zip
	router.HandleFunc("/{path:\\w+\\s-\\s\\w+\\[\\d+\\]\\[\\w+\\]\\.zip}", endpoints.DownloadArchive(db, &cfg)).Methods("GET")

	address := cfg.BindAddress
	fmt.Printf("Listening on %s\n", address)
	loggedRouter := handlers.LoggingHandler(os.Stdout, router)
	http.ListenAndServe(address, loggedRouter)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"regexp"
	"strconv"
	"time"

	"golang.org/x/crypto/ssh/terminal"
)

type key int

type cursorPos struct {
	row, col int
}

const (
	unknown key = iota
	up
	down
	enter
)

var (
	cursorPosRegex = regexp.MustCompile("^\033\\[([0-9]+);([0-9]+)R$")
)

func clearLine() {
	fmt.Print("\033[2K")
}

func clearScreen() {
	fmt.Print("\033[2J")
}

func hideCursor() {
	fmt.Print("\033[?25l")
}

func showCursor() {
	fmt.Print("\033[?25h")
}

func fullScreenEnter() {
	fmt.Printf("\033[?1049h\033[H")
}

func fullScreenExit() {
	fmt.Printf("\033[?1049l")
}

func main() {
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		panic(err)
	}
	defer terminal.Restore(0, oldState)
	//hideCursor()
	//defer showCursor()

	keyPresses := make(chan key)
	cursorPosReply := make(chan cursorPos)
	go func() {
		buf := make([]byte, 128)
		for {
			n, err := os.Stdin.Read(buf)
			if err != nil {
				panic(err)
			}
			input := string(buf[:n])
			switch {
			case input == "\033[A":
				keyPresses <- up
			case input == "\033[B":
				keyPresses <- down
			case input == "\x0D":
				keyPresses <- enter
			case cursorPosRegex.MatchString(input):
				matches := cursorPosRegex.FindStringSubmatch(input)
				row, col := matches[1], matches[2]
				//fmt.Println("row", row)
				//fmt.Println("col", col)
				r, _ := strconv.Atoi(row)
				c, _ := strconv.Atoi(col)
				cursorPosReply <- cursorPos{row: r, col: c}
			default:
				keyPresses <- unknown
			}
		}
	}()

	// ask for the cursor position
	fmt.Printf("\033[6n")

	pos := <-cursorPosReply
	fmt.Println(pos, "\r")

	fmt.Printf("1 Use UP and DOWN arrow keys\n")
	fmt.Printf("\r2 Use UP and DOWN arrow keys\n")

	tick := time.NewTicker(1 * time.Second)
	defer tick.Stop()
	count := 5
	fmt.Printf("\rreturning in %d...", count)
	for {
		select {
		case <-tick.C:
			count--

			// TODO(robbiev) this does not work if we're at the bottom of the screen
			fmt.Printf("\033[%d;%dH", pos.row, pos.col)

			// erase from the cursor onwards
			fmt.Printf("\033[J")

			//clearLine()
			fmt.Printf("returning in %d...", count)
			if count == 0 {
				fmt.Println("\r")
				return
			}
		case key := <-keyPresses:
			switch key {
			case enter:
				clearLine()
				fmt.Printf("\renter")
			case up:
				clearLine()
				fmt.Printf("\rup")
			case down:
				clearLine()
				fmt.Printf("\rdown")
			case unknown:
				clearLine()
				fmt.Printf("\runknown key")
			}
		}
	}
}
<commit_msg>fix bottom of screen offset<commit_after>package main

import (
	"fmt"
	"math"
	"os"
	"regexp"
	"strconv"
	"time"

	"golang.org/x/crypto/ssh/terminal"
)

type key int

type cursorPos struct {
	row, col int
}

const (
	unknown key = iota
	up
	down
	enter
)

var (
	cursorPosRegex = regexp.MustCompile("^\033\\[([0-9]+);([0-9]+)R$")
)

func clearLine() {
	fmt.Print("\033[2K")
}

func clearScreen() {
	fmt.Print("\033[2J")
}

func hideCursor() {
	fmt.Print("\033[?25l")
}

func showCursor() {
	fmt.Print("\033[?25h")
}

func fullScreenEnter() {
	fmt.Printf("\033[?1049h\033[H")
}

func fullScreenExit() {
	fmt.Printf("\033[?1049l")
}

func main() {
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		panic(err)
	}
	defer terminal.Restore(0, oldState)
	//hideCursor()
	//defer showCursor()

	keyPresses := make(chan key)
	cursorPosReply := make(chan cursorPos)
	go func() {
		buf := make([]byte, 128)
		for {
			n, err := os.Stdin.Read(buf)
			if err != nil {
				panic(err)
			}
			input := string(buf[:n])
			switch {
			case input == "\033[A":
				keyPresses <- up
			case input == "\033[B":
				keyPresses <- down
			case input == "\x0D":
				keyPresses <- enter
			case cursorPosRegex.MatchString(input):
				matches := cursorPosRegex.FindStringSubmatch(input)
				row, col := matches[1], matches[2]
				//fmt.Println("row", row)
				//fmt.Println("col", col)
				r, _ := strconv.Atoi(row)
				c, _ := strconv.Atoi(col)
				cursorPosReply <- cursorPos{row: r, col: c}
			default:
				keyPresses <- unknown
			}
		}
	}()

	// ask for the cursor position
	fmt.Printf("\033[6n")

	pos := <-cursorPosReply

	width, height, _ := terminal.GetSize(0)
	fmt.Println("width", width)
	fmt.Println("\rheight", height, "\r")

	fmt.Println(pos, "\r")

	fmt.Printf("1 Use UP and DOWN arrow keys\n")
	fmt.Printf("\r2 Use UP and DOWN arrow keys\n")

	tick := time.NewTicker(1 * time.Second)
	defer tick.Stop()
	lines := 6
	count := 5
	fmt.Printf("\rreturning in %d...", count)
	for {
		select {
		case <-tick.C:
			count--

			// the line where we started is also filled with text so we don't need to
			// count it when moving up
			moveOffset := lines - 1
			// correct the position when we're at the bottom of the screen
			correct := height - pos.row
			correct = moveOffset - int(math.Min(float64(correct), float64(moveOffset)))

			// set the cursor to where we started
			fmt.Printf("\033[%d;%dH", pos.row-correct, pos.col)

			// erase from the cursor onwards
			fmt.Printf("\033[J")

			//clearLine()
			fmt.Printf("returning in %d...", count)
			if count == 0 {
				fmt.Println("\r")
				return
			}
		case key := <-keyPresses:
			switch key {
			case enter:
				clearLine()
				fmt.Printf("\renter")
			case up:
				clearLine()
				fmt.Printf("\rup")
			case down:
				clearLine()
				fmt.Printf("\rdown")
			case unknown:
				clearLine()
				fmt.Printf("\runknown key")
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/csv"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
)

var user = flag.String("user", "user", "jira username")
var pass = flag.String("pass", "NotAPassword1", "jira password")
var prj = flag.String("prj", "PROJ", "jira project to add stories to")
var svr = flag.String("svr", "https://jira.example.com", "jira server url")

func main() {
	if len(os.Args) != 9 {
		flag.PrintDefaults()
		return
	}

	flag.Parse()

	// fmt.Printf("%v", os.Args)
	// fmt.Printf(*svr)

	bufrdr := bufio.NewReader(os.Stdin)
	bufrdr.ReadLine()
	bufrdr.ReadLine()
	// processIssue(csvrdr, printJSON)

	processIssue(bufrdr, submitIssue, *user, *pass, *svr, *prj)

}

type procFunc func(i []string, user string, pass string, srv string, prj string) error

func processIssue(r io.Reader, process procFunc, user string, pass string, svr string, prj string) error {

	csvrdr := csv.NewReader(r)

	for {
		record, err := csvrdr.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}

		err = process(record, user, pass, svr, prj)
		if err != nil {
			log.Fatal(err)
		}
	}

	return nil
}

func printCSV(issue []string, user string, pass string, svr string, prj string) error {
	fmt.Println(issue)
	return nil
}
func printJSON(issue []string, user string, pass string, svr string, prj string) error {
	i := newIssue(issue, prj)
	fmt.Println(string(toJSON(i)))
	return nil
}
<commit_msg>removed bad readline which was causing issues<commit_after>package main

import (
	"bufio"
	"encoding/csv"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
)

var user = flag.String("user", "user", "jira username")
var pass = flag.String("pass", "NotAPassword1", "jira password")
var prj = flag.String("prj", "PROJ", "jira project to add stories to")
var svr = flag.String("svr", "https://jira.example.com", "jira server url")

func main() {
	if len(os.Args) != 9 {
		flag.PrintDefaults()
		return
	}

	flag.Parse()

	// fmt.Printf("%v", os.Args)
	// fmt.Printf(*svr)

	bufrdr := bufio.NewReader(os.Stdin)
	bufrdr.ReadLine()
	// processIssue(csvrdr, printJSON)

	processIssue(bufrdr, submitIssue, *user, *pass, *svr, *prj)

}

type procFunc func(i []string, user string, pass string, srv string, prj string) error

func processIssue(r io.Reader, process procFunc, user string, pass string, svr string, prj string) error {

	csvrdr := csv.NewReader(r)

	for {
		record, err := csvrdr.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}

		err = process(record, user, pass, svr, prj)
		if err != nil {
			log.Fatal(err)
		}
	}

	return nil
}

func printCSV(issue []string, user string, pass string, svr string, prj string) error {
	fmt.Println(issue)
	return nil
}
func printJSON(issue []string, user string, pass string, svr string, prj string) error {
	i := newIssue(issue, prj)
	fmt.Println(string(toJSON(i)))
	return nil
}
<|endoftext|>


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

package main

import (
	"flag"
	"github.com/hanwen/go-fuse/fuse"
	"io/ioutil"
	"log"
	"os"
)

func main() {
	fsdebug := flag.Bool("fs-debug", false, "switch on FS debugging")
	mtpDebug := flag.Int("mtp-debug", 0, "switch on MTP debugging")
	backing := flag.String("backing-dir", "/tmp", "backing store for locally cached files.")
	flag.Parse()

	if len(flag.Args()) != 1 {
		log.Fatal("Usage: mtpfs MOUNT-POINT")
	}
	mountpoint := flag.Arg(0)

	Init()
	SetDebug(*mtpDebug)
	devs, err := Detect()
	if err != nil {
		log.Fatalf("detect: %v", err)
	}
	for _, d := range devs {
		log.Printf("device %v: ", d)
	}
	if len(devs) == 0 {
		log.Fatal("no device found.  Try replugging it.")
	}
	if len(devs) > 1 {
		log.Fatal("must have exactly one device")
	}

	rdev := devs[0]

	dev, err := rdev.Open()
	if err != nil {
		log.Fatalf("rdev.open: %v", err)
	}
	defer dev.Release()
	dev.GetStorage(0)
	for _, s := range dev.ListStorage() {
		log.Printf("storage ID %d: %s", s.Id(), s.Description())
	}

	if len(dev.ListStorage()) == 0 {
		log.Fatalf("No storages found.  Try unlocking the device.")
	}

	if *backing == "" {
		*backing, err = ioutil.TempDir("", "go-mtpfs")
		if err != nil {
			log.Fatalf("TempDir failed: %v", err)
		}
	} else {
		*backing = *backing + "/go-mtpfs"
		err = os.Mkdir(*backing, 0700)
		if err != nil {
			log.Fatalf("Mkdir failed: %v", err)
		}
	}
	log.Println("backing data", *backing)
	defer os.RemoveAll(*backing)

	fs := NewDeviceFs(dev, *backing)
	conn := fuse.NewFileSystemConnector(fs, fuse.NewFileSystemOptions())
	rawFs := fuse.NewLockingRawFileSystem(conn)

	mount := fuse.NewMountState(rawFs)
	if err := mount.Mount(mountpoint, nil); err != nil {
		log.Fatalf("mount failed: %v", err)
	}

	conn.Debug = *fsdebug
	mount.Debug = *fsdebug
	log.Println("starting FUSE.")
	mount.Loop()
}
<commit_msg>Fix /tmp backing store.<commit_after>// Copyright 2012 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"github.com/hanwen/go-fuse/fuse"
	"io/ioutil"
	"log"
	"os"
)

func main() {
	fsdebug := flag.Bool("fs-debug", false, "switch on FS debugging")
	mtpDebug := flag.Int("mtp-debug", 0, "switch on MTP debugging")
	backing := flag.String("backing-dir", "", "backing store for locally cached files. Default: use a temporary directory.")
	flag.Parse()

	if len(flag.Args()) != 1 {
		log.Fatal("Usage: mtpfs MOUNT-POINT")
	}
	mountpoint := flag.Arg(0)

	Init()
	SetDebug(*mtpDebug)
	devs, err := Detect()
	if err != nil {
		log.Fatalf("detect: %v", err)
	}
	for _, d := range devs {
		log.Printf("device %v: ", d)
	}
	if len(devs) == 0 {
		log.Fatal("no device found.  Try replugging it.")
	}
	if len(devs) > 1 {
		log.Fatal("must have exactly one device")
	}

	rdev := devs[0]

	dev, err := rdev.Open()
	if err != nil {
		log.Fatalf("rdev.open: %v", err)
	}
	defer dev.Release()
	dev.GetStorage(0)
	for _, s := range dev.ListStorage() {
		log.Printf("storage ID %d: %s", s.Id(), s.Description())
	}

	if len(dev.ListStorage()) == 0 {
		log.Fatalf("No storages found.  Try unlocking the device.")
	}

	if *backing == "" {
		*backing, err = ioutil.TempDir("", "go-mtpfs")
		if err != nil {
			log.Fatalf("TempDir failed: %v", err)
		}
	} else {
		*backing = *backing + "/go-mtpfs"
		err = os.Mkdir(*backing, 0700)
		if err != nil {
			log.Fatalf("Mkdir failed: %v", err)
		}
	}
	log.Println("backing data", *backing)
	defer os.RemoveAll(*backing)

	fs := NewDeviceFs(dev, *backing)
	conn := fuse.NewFileSystemConnector(fs, fuse.NewFileSystemOptions())
	rawFs := fuse.NewLockingRawFileSystem(conn)

	mount := fuse.NewMountState(rawFs)
	if err := mount.Mount(mountpoint, nil); err != nil {
		log.Fatalf("mount failed: %v", err)
	}

	conn.Debug = *fsdebug
	mount.Debug = *fsdebug
	log.Println("starting FUSE.")
	mount.Loop()
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"fmt"
	"math/big"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/codegangsta/cli"
	docker "github.com/fsouza/go-dockerclient"
	"github.com/montanaflynn/stats"
)

const MILLIS_IN_SECOND = 1000

func worker(name string, id, requests int, image string, labels map[string]string, args []string, completeCh chan time.Duration) {
	client, err := docker.NewClientFromEnv()
	if err != nil {
		panic(err)
	}

	for i := 0; i < requests; i++ {
		start := time.Now()

		containerName := fmt.Sprintf("%s-%d", name, i+(requests*id))
		container, err := client.CreateContainer(docker.CreateContainerOptions{
			Name: containerName,
			Config: &docker.Config{
				Image:  image,
				Cmd:    args,
				Labels: labels,
			},
			HostConfig: &docker.HostConfig{},
		})
		if err != nil {
			panic(err)
		}

		err = client.StartContainer(container.ID, nil)
		if err != nil {
			panic(err)
		}

		completeCh <- time.Since(start)
	}
}

func session(name string, requests, concurrency int, images []string, labels map[string]string, args []string, completeCh chan time.Duration) {
	var wg sync.WaitGroup
	var size = len(images)
	n := requests / concurrency

	for i := 0; i < concurrency; i++ {
		wg.Add(1)
		image := images[i%size]
		go func(i int) {
			worker(name, i, n, image, labels, args, completeCh)
			wg.Done()
		}(i)
	}
	wg.Wait()
}

func bench(requests, concurrency int, images []string, labels map[string]string, args []string) {
	id, _ := rand.Int(rand.Reader, big.NewInt(0xffffff))
	name := fmt.Sprintf("swarm-bench-%X", id)

	start := time.Now()
	timings := make([]float64, requests)
	// Create a buffered channel so our display goroutine can't slow down the workers.
	completeCh := make(chan time.Duration, requests)
	doneCh := make(chan struct{})
	current := 0
	go func() {
		for timing := range completeCh {
			timings = append(timings, timing.Seconds())
			current++
			percent := float64(current) / float64(requests) * 100
			fmt.Printf("[%3.f%%] %d/%d containers started\n", percent, current, requests)
		}
		doneCh <- struct{}{}
	}()
	session(name, requests, concurrency, images, labels, args, completeCh)
	close(completeCh)
	<-doneCh

	total := time.Since(start)
	mean, _ := stats.Mean(timings)
	p90th, _ := stats.Percentile(timings, 90)
	p99th, _ := stats.Percentile(timings, 99)

	meanMillis := mean * MILLIS_IN_SECOND
	p90thMillis := p90th * MILLIS_IN_SECOND
	p99thMillis := p99th * MILLIS_IN_SECOND

	fmt.Printf("\n")
	fmt.Printf("Time taken for tests: %.3fs\n", total.Seconds())
	fmt.Printf("Time per container: %.3fms [mean] | %.3fms [90th] | %.3fms [99th]\n", meanMillis, p90thMillis, p99thMillis)
}

func main() {
	app := cli.NewApp()
	app.Name = "swarm-bench"
	app.Usage = "Swarm Benchmarking Tool"
	app.Version = "0.1.0"
	app.Author = ""
	app.Email = ""
	app.Flags = []cli.Flag{
		cli.IntFlag{
			Name:  "concurrency, c",
			Value: 1,
			Usage: "Number of multiple requests to perform at a time. Default is one request at a time.",
		},
		cli.IntFlag{
			Name:  "requests, n",
			Value: 1,
			Usage: "Number of containers to start for the benchmarking session. The default is to just start a single container.",
		},
		cli.StringSliceFlag{
			Name:  "image, i",
			Value: &cli.StringSlice{},
			Usage: "Image(s) to use for benchmarking.",
		},
		cli.StringSliceFlag{
			Name:  "label, l",
			Value: &cli.StringSlice{},
			Usage: "Label(s) to apply to containers.",
		},
	}

	app.Action = func(c *cli.Context) {
		if !c.IsSet("image") && !c.IsSet("i") {
			cli.ShowAppHelp(c)
			os.Exit(1)
		}
		var labels map[string]string
		if c.IsSet("l") || c.IsSet("label") {
			labels = map[string]string{}
			for _, l := range c.StringSlice("label") {
				parts := strings.SplitN(l, "=", 2)
				if len(parts) != 2 {
					panic("Malformed label " + l)
				}
				labels[parts[0]] = parts[1]
			}
		}

		bench(c.Int("requests"), c.Int("concurrency"), c.StringSlice("image"), labels, c.Args())
	}

	app.Run(os.Args)
}
<commit_msg>Expose all ports<commit_after>package main

import (
	"crypto/rand"
	"fmt"
	"math/big"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/codegangsta/cli"
	docker "github.com/fsouza/go-dockerclient"
	"github.com/montanaflynn/stats"
)

const MILLIS_IN_SECOND = 1000

func worker(name string, id, requests int, image string, labels map[string]string, args []string, completeCh chan time.Duration) {
	client, err := docker.NewClientFromEnv()
	if err != nil {
		panic(err)
	}

	for i := 0; i < requests; i++ {
		start := time.Now()

		containerName := fmt.Sprintf("%s-%d", name, i+(requests*id))
		container, err := client.CreateContainer(docker.CreateContainerOptions{
			Name: containerName,
			Config: &docker.Config{
				Image:  image,
				Cmd:    args,
				Labels: labels,
			},
			HostConfig: &docker.HostConfig{
				PublishAllPorts: true,
			},
		})
		if err != nil {
			panic(err)
		}

		err = client.StartContainer(container.ID, nil)
		if err != nil {
			panic(err)
		}

		completeCh <- time.Since(start)
	}
}

func session(name string, requests, concurrency int, images []string, labels map[string]string, args []string, completeCh chan time.Duration) {
	var wg sync.WaitGroup
	var size = len(images)
	n := requests / concurrency

	for i := 0; i < concurrency; i++ {
		wg.Add(1)
		image := images[i%size]
		go func(i int) {
			worker(name, i, n, image, labels, args, completeCh)
			wg.Done()
		}(i)
	}
	wg.Wait()
}

func bench(requests, concurrency int, images []string, labels map[string]string, args []string) {
	id, _ := rand.Int(rand.Reader, big.NewInt(0xffffff))
	name := fmt.Sprintf("swarm-bench-%X", id)

	start := time.Now()
	timings := make([]float64, requests)
	// Create a buffered channel so our display goroutine can't slow down the workers.
	completeCh := make(chan time.Duration, requests)
	doneCh := make(chan struct{})
	current := 0
	go func() {
		for timing := range completeCh {
			timings = append(timings, timing.Seconds())
			current++
			percent := float64(current) / float64(requests) * 100
			fmt.Printf("[%3.f%%] %d/%d containers started\n", percent, current, requests)
		}
		doneCh <- struct{}{}
	}()
	session(name, requests, concurrency, images, labels, args, completeCh)
	close(completeCh)
	<-doneCh

	total := time.Since(start)
	mean, _ := stats.Mean(timings)
	p90th, _ := stats.Percentile(timings, 90)
	p99th, _ := stats.Percentile(timings, 99)

	meanMillis := mean * MILLIS_IN_SECOND
	p90thMillis := p90th * MILLIS_IN_SECOND
	p99thMillis := p99th * MILLIS_IN_SECOND

	fmt.Printf("\n")
	fmt.Printf("Time taken for tests: %.3fs\n", total.Seconds())
	fmt.Printf("Time per container: %.3fms [mean] | %.3fms [90th] | %.3fms [99th]\n", meanMillis, p90thMillis, p99thMillis)
}

func main() {
	app := cli.NewApp()
	app.Name = "swarm-bench"
	app.Usage = "Swarm Benchmarking Tool"
	app.Version = "0.1.0"
	app.Author = ""
	app.Email = ""
	app.Flags = []cli.Flag{
		cli.IntFlag{
			Name:  "concurrency, c",
			Value: 1,
			Usage: "Number of multiple requests to perform at a time. Default is one request at a time.",
		},
		cli.IntFlag{
			Name:  "requests, n",
			Value: 1,
			Usage: "Number of containers to start for the benchmarking session. The default is to just start a single container.",
		},
		cli.StringSliceFlag{
			Name:  "image, i",
			Value: &cli.StringSlice{},
			Usage: "Image(s) to use for benchmarking.",
		},
		cli.StringSliceFlag{
			Name:  "label, l",
			Value: &cli.StringSlice{},
			Usage: "Label(s) to apply to containers.",
		},
	}

	app.Action = func(c *cli.Context) {
		if !c.IsSet("image") && !c.IsSet("i") {
			cli.ShowAppHelp(c)
			os.Exit(1)
		}
		var labels map[string]string
		if c.IsSet("l") || c.IsSet("label") {
			labels = map[string]string{}
			for _, l := range c.StringSlice("label") {
				parts := strings.SplitN(l, "=", 2)
				if len(parts) != 2 {
					panic("Malformed label " + l)
				}
				labels[parts[0]] = parts[1]
			}
		}

		bench(c.Int("requests"), c.Int("concurrency"), c.StringSlice("image"), labels, c.Args())
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>// CLI testing package for the Go language.
//
// Developing a command line application? Wanna be able to test your app from the
// outside? If the answer is Yes to at least one of the questions, keep reading.
//
// `testcli` is a wrapper around os/exec to test CLI apps in Go lang,
// minimalistic, so you can do your tests with `testing` or any other testing
// framework.
package testcli

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"sync"
	"time"
)

type output struct {
	content string
	mu      *sync.Mutex
}

// Cmd is typically constructed through the Command() call and provides state
// to the execution engine.
type Cmd struct {
	cmd       *exec.Cmd
	env       []string
	exitError error
	status    string
	stdout    *output
	stderr    *output
	stdin     io.Reader
}

// ErrUninitializedCmd is returned when members are accessed before a run, that
// can only be used after a command has been run.
var ErrUninitializedCmd = errors.New("You need to run this command first")
// ErrCmdNotFinished is returned when members are accessed before or during a run,
// that can only be used after a command has finished executing.
var ErrCmdNotFinished = errors.New("Command is still executing")

var pkgCmd = &Cmd{
	status: "initialized",
	stdout: &output{mu: &sync.Mutex{}},
	stderr: &output{mu: &sync.Mutex{}},
}

// Command constructs a *Cmd. It is passed the command name and arguments.
func Command(name string, arg ...string) *Cmd {
	return &Cmd{
		cmd:    exec.Command(name, arg...),
		status: "initialized",
		stdout: &output{mu: &sync.Mutex{}},
		stderr: &output{mu: &sync.Mutex{}},
	}
}

func (c *Cmd) validateIsDone() {
	if c.status != "executed" {
		log.Fatal(ErrCmdNotFinished)
	}
}

func (c *Cmd) validateHasStarted() {
	if c.status == "initialized" {
		log.Fatal(ErrUninitializedCmd)
	}
}

// SetEnv overwrites the environment with the provided one. Otherwise, the
// parent environment will be supplied.
func (c *Cmd) SetEnv(env []string) {
	c.env = env
}

// SetStdin sets the stdin stream. It makes no attempt to determine if the
// command accepts anything over stdin.
func (c *Cmd) SetStdin(stdin io.Reader) {
	c.stdin = stdin
}

// Run runs the command.
func (c *Cmd) Run() {
	if c.stdin != nil {
		c.cmd.Stdin = c.stdin
	}

	if c.env != nil {
		c.cmd.Env = c.env
	} else {
		c.cmd.Env = os.Environ()
	}

	var outBuf bytes.Buffer
	c.cmd.Stdout = &outBuf

	var errBuf bytes.Buffer
	c.cmd.Stderr = &errBuf

	if err := c.cmd.Run(); err != nil {
		c.exitError = err
	}
	c.stdout.content = string(outBuf.Bytes())
	c.stderr.content = string(errBuf.Bytes())
	c.status = "executed"
}

// Start starts the command without waiting for it to complete
func (c *Cmd) Start() {
	if c.stdin != nil {
		c.cmd.Stdin = c.stdin
	}

	if c.env != nil {
		c.cmd.Env = c.env
	} else {
		c.cmd.Env = os.Environ()
	}

	stdoutPipe, err := c.cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}

	stderrPipe, err := c.cmd.StderrPipe()
	if err != nil {
		log.Fatal(err)
	}

	if err := c.cmd.Start(); err != nil {
		c.exitError = err
	}

	go func() {
		scanner := bufio.NewScanner(stdoutPipe)
		for scanner.Scan() {
			c.stdout.mu.Lock()
			c.stdout.content += scanner.Text()
			c.stdout.mu.Unlock()
		}
		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}
	}()

	go func() {
		scanner := bufio.NewScanner(stderrPipe)
		for scanner.Scan() {
			c.stderr.mu.Lock()
			c.stderr.content += scanner.Text()
			c.stderr.mu.Unlock()
		}
		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}
	}()
	c.status = "running"
}

// Wait waits for the command to exit
func (c *Cmd) Wait() {
	if c.status != "running" {
		log.Fatal("Can't wait on command that isnt running")
	}
	if err := c.cmd.Wait(); err != nil {
		c.exitError = err
	}
	c.status = "executed"
}

// Run runs a command with name and arguments. After this, package-level
// functions will return the data about the last command run.
func Run(name string, arg ...string) {
	pkgCmd = Command(name, arg...)
	pkgCmd.Run()
}

// Error is the command's error, if any.
func (c *Cmd) Error() error {
	c.validateIsDone()
	return c.exitError
}

// Error is the command's error, if any.
func Error() error {
	return pkgCmd.Error()
}

// Stdout stream for the command
func (c *Cmd) Stdout() string {
	c.validateHasStarted()
	c.stdout.mu.Lock()
	defer c.stdout.mu.Unlock()
	return c.stdout.content
}

// Stdout stream for the command
func Stdout() string {
	return pkgCmd.Stdout()
}

// Stderr stream for the command
func (c *Cmd) Stderr() string {
	c.validateHasStarted()
	c.stderr.mu.Lock()
	defer c.stderr.mu.Unlock()
	return c.stderr.content
}

// Stderr stream for the command
func Stderr() string {
	return pkgCmd.Stderr()
}

// StdoutContains determines if command's STDOUT contains `str`, this operation
// is case insensitive.
func (c *Cmd) StdoutContains(str string) bool {
	c.validateHasStarted()
	str = strings.ToLower(str)
	return retryStringTest(strings.Contains, c.stdout, str)

}

// StdoutContains determines if command's STDOUT contains `str`, this operation
// is case insensitive.
func StdoutContains(str string) bool {
	return pkgCmd.StdoutContains(str)
}

// StderrContains determines if command's STDERR contains `str`, this operation
// is case insensitive.
func (c *Cmd) StderrContains(str string) bool {
	c.validateHasStarted()
	str = strings.ToLower(str)
	return retryStringTest(strings.Contains, c.stderr, str)
	// return strings.Contains(strings.ToLower(c.stderr.content), str)
}

// StderrContains determines if command's STDERR contains `str`, this operation
// is case insensitive.
func StderrContains(str string) bool {
	return pkgCmd.StderrContains(str)
}

// Success is a boolean status which indicates if the program exited non-zero
// or not.
func (c *Cmd) Success() bool {
	c.validateIsDone()
	return c.exitError == nil
}

// Success is a boolean status which indicates if the program exited non-zero
// or not.
func Success() bool {
	return pkgCmd.Success()
}

// Failure is the inverse of Success().
func (c *Cmd) Failure() bool {
	c.validateIsDone()
	return c.exitError != nil
}

// Failure is the inverse of Success().
func Failure() bool {
	return pkgCmd.Failure()
}

// StdoutMatches compares a regex to the stdout produced by the command.
func (c *Cmd) StdoutMatches(regex string) bool {
	c.validateHasStarted()
	re := regexp.MustCompile(regex)
	return retryStringTest(func(got, want string) bool {
		return re.MatchString(got)
	}, c.stdout, regex)
}

// StdoutMatches compares a regex to the stdout produced by the command.
func StdoutMatches(regex string) bool {
	return pkgCmd.StdoutMatches(regex)
}

// StderrMatches compares a regex to the stderr produced by the command.
func (c *Cmd) StderrMatches(regex string) bool {
	c.validateHasStarted()
	re := regexp.MustCompile(regex)
	return retryStringTest(func(got, want string) bool {
		return re.MatchString(got)
	}, c.stderr, regex)
}

// StderrMatches compares a regex to the stderr produced by the command.
func StderrMatches(regex string) bool {
	return pkgCmd.StderrMatches(regex)
}

// retryStringTest takes in a testFunc and will test output for the expected string until either it
// finds the expected string or times out (default 1 second)
func retryStringTest(testFunc func(string, string) bool, output *output, expected string) bool {
	ticker := time.NewTicker(100 * time.Millisecond)
	timeout := time.After(1 * time.Second)
	for {
		select {
		case <-ticker.C:
			output.mu.Lock()
			found := testFunc(strings.ToLower(output.content), expected)
			output.mu.Unlock()
			if found == true {
				return true
			}
		case <-timeout:
			return false
		}
	}
}
<commit_msg>Replace Wait conditional with validateHasStarted<commit_after>// CLI testing package for the Go language.
//
// Developing a command line application? Wanna be able to test your app from the
// outside? If the answer is Yes to at least one of the questions, keep reading.
//
// `testcli` is a wrapper around os/exec to test CLI apps in Go lang,
// minimalistic, so you can do your tests with `testing` or any other testing
// framework.
package testcli

import (
	"bufio"
	"bytes"
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"sync"
	"time"
)

type output struct {
	content string
	mu      *sync.Mutex
}

// Cmd is typically constructed through the Command() call and provides state
// to the execution engine.
type Cmd struct {
	cmd       *exec.Cmd
	env       []string
	exitError error
	status    string
	stdout    *output
	stderr    *output
	stdin     io.Reader
}

// ErrUninitializedCmd is returned when members are accessed before a run, that
// can only be used after a command has been run.
var ErrUninitializedCmd = errors.New("You need to run this command first")
// ErrCmdNotFinished is returned when members are accessed before or during a run,
// that can only be used after a command has finished executing.
var ErrCmdNotFinished = errors.New("Command is still executing")

var pkgCmd = &Cmd{
	status: "initialized",
	stdout: &output{mu: &sync.Mutex{}},
	stderr: &output{mu: &sync.Mutex{}},
}

// Command constructs a *Cmd. It is passed the command name and arguments.
func Command(name string, arg ...string) *Cmd {
	return &Cmd{
		cmd:    exec.Command(name, arg...),
		status: "initialized",
		stdout: &output{mu: &sync.Mutex{}},
		stderr: &output{mu: &sync.Mutex{}},
	}
}

func (c *Cmd) validateIsDone() {
	if c.status != "executed" {
		log.Fatal(ErrCmdNotFinished)
	}
}

func (c *Cmd) validateHasStarted() {
	if c.status == "initialized" {
		log.Fatal(ErrUninitializedCmd)
	}
}

// SetEnv overwrites the environment with the provided one. Otherwise, the
// parent environment will be supplied.
func (c *Cmd) SetEnv(env []string) {
	c.env = env
}

// SetStdin sets the stdin stream. It makes no attempt to determine if the
// command accepts anything over stdin.
func (c *Cmd) SetStdin(stdin io.Reader) {
	c.stdin = stdin
}

// Run runs the command.
func (c *Cmd) Run() {
	if c.stdin != nil {
		c.cmd.Stdin = c.stdin
	}

	if c.env != nil {
		c.cmd.Env = c.env
	} else {
		c.cmd.Env = os.Environ()
	}

	var outBuf bytes.Buffer
	c.cmd.Stdout = &outBuf

	var errBuf bytes.Buffer
	c.cmd.Stderr = &errBuf

	if err := c.cmd.Run(); err != nil {
		c.exitError = err
	}
	c.stdout.content = string(outBuf.Bytes())
	c.stderr.content = string(errBuf.Bytes())
	c.status = "executed"
}

// Start starts the command without waiting for it to complete
func (c *Cmd) Start() {
	if c.stdin != nil {
		c.cmd.Stdin = c.stdin
	}

	if c.env != nil {
		c.cmd.Env = c.env
	} else {
		c.cmd.Env = os.Environ()
	}

	stdoutPipe, err := c.cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}

	stderrPipe, err := c.cmd.StderrPipe()
	if err != nil {
		log.Fatal(err)
	}

	if err := c.cmd.Start(); err != nil {
		c.exitError = err
	}

	go func() {
		scanner := bufio.NewScanner(stdoutPipe)
		for scanner.Scan() {
			c.stdout.mu.Lock()
			c.stdout.content += scanner.Text()
			c.stdout.mu.Unlock()
		}
		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}
	}()

	go func() {
		scanner := bufio.NewScanner(stderrPipe)
		for scanner.Scan() {
			c.stderr.mu.Lock()
			c.stderr.content += scanner.Text()
			c.stderr.mu.Unlock()
		}
		if err := scanner.Err(); err != nil {
			log.Fatal(err)
		}
	}()
	c.status = "running"
}

// Wait waits for the command to exit
func (c *Cmd) Wait() {
	c.validateHasStarted()
	if err := c.cmd.Wait(); err != nil {
		c.exitError = err
	}
	c.status = "executed"
}

// Run runs a command with name and arguments. After this, package-level
// functions will return the data about the last command run.
func Run(name string, arg ...string) {
	pkgCmd = Command(name, arg...)
	pkgCmd.Run()
}

// Error is the command's error, if any.
func (c *Cmd) Error() error {
	c.validateIsDone()
	return c.exitError
}

// Error is the command's error, if any.
func Error() error {
	return pkgCmd.Error()
}

// Stdout stream for the command
func (c *Cmd) Stdout() string {
	c.validateHasStarted()
	c.stdout.mu.Lock()
	defer c.stdout.mu.Unlock()
	return c.stdout.content
}

// Stdout stream for the command
func Stdout() string {
	return pkgCmd.Stdout()
}

// Stderr stream for the command
func (c *Cmd) Stderr() string {
	c.validateHasStarted()
	c.stderr.mu.Lock()
	defer c.stderr.mu.Unlock()
	return c.stderr.content
}

// Stderr stream for the command
func Stderr() string {
	return pkgCmd.Stderr()
}

// StdoutContains determines if command's STDOUT contains `str`, this operation
// is case insensitive.
func (c *Cmd) StdoutContains(str string) bool {
	c.validateHasStarted()
	str = strings.ToLower(str)
	return retryStringTest(strings.Contains, c.stdout, str)

}

// StdoutContains determines if command's STDOUT contains `str`, this operation
// is case insensitive.
func StdoutContains(str string) bool {
	return pkgCmd.StdoutContains(str)
}

// StderrContains determines if command's STDERR contains `str`, this operation
// is case insensitive.
func (c *Cmd) StderrContains(str string) bool {
	c.validateHasStarted()
	str = strings.ToLower(str)
	return retryStringTest(strings.Contains, c.stderr, str)
	// return strings.Contains(strings.ToLower(c.stderr.content), str)
}

// StderrContains determines if command's STDERR contains `str`, this operation
// is case insensitive.
func StderrContains(str string) bool {
	return pkgCmd.StderrContains(str)
}

// Success is a boolean status which indicates if the program exited non-zero
// or not.
func (c *Cmd) Success() bool {
	c.validateIsDone()
	return c.exitError == nil
}

// Success is a boolean status which indicates if the program exited non-zero
// or not.
func Success() bool {
	return pkgCmd.Success()
}

// Failure is the inverse of Success().
func (c *Cmd) Failure() bool {
	c.validateIsDone()
	return c.exitError != nil
}

// Failure is the inverse of Success().
func Failure() bool {
	return pkgCmd.Failure()
}

// StdoutMatches compares a regex to the stdout produced by the command.
func (c *Cmd) StdoutMatches(regex string) bool {
	c.validateHasStarted()
	re := regexp.MustCompile(regex)
	return retryStringTest(func(got, want string) bool {
		return re.MatchString(got)
	}, c.stdout, regex)
}

// StdoutMatches compares a regex to the stdout produced by the command.
func StdoutMatches(regex string) bool {
	return pkgCmd.StdoutMatches(regex)
}

// StderrMatches compares a regex to the stderr produced by the command.
func (c *Cmd) StderrMatches(regex string) bool {
	c.validateHasStarted()
	re := regexp.MustCompile(regex)
	return retryStringTest(func(got, want string) bool {
		return re.MatchString(got)
	}, c.stderr, regex)
}

// StderrMatches compares a regex to the stderr produced by the command.
func StderrMatches(regex string) bool {
	return pkgCmd.StderrMatches(regex)
}

// retryStringTest takes in a testFunc and will test output for the expected string until either it
// finds the expected string or times out (default 1 second)
func retryStringTest(testFunc func(string, string) bool, output *output, expected string) bool {
	ticker := time.NewTicker(100 * time.Millisecond)
	timeout := time.After(1 * time.Second)
	for {
		select {
		case <-ticker.C:
			output.mu.Lock()
			found := testFunc(strings.ToLower(output.content), expected)
			output.mu.Unlock()
			if found == true {
				return true
			}
		case <-timeout:
			return false
		}
	}
}
<|endoftext|>