<commit_before>/*

Package docstore implements a Lua module to interact with the docstore extension.

*/
package docstore

import (
	"github.com/yuin/gopher-lua"

	docstoreExt "github.com/tsileo/blobstash/ext/docstore"
	"github.com/tsileo/blobstash/ext/lua/luautil"
)

type DocstoreModule struct {
	docstore *docstoreExt.DocStoreExt
}

func New(docstore *docstoreExt.DocStoreExt) *DocstoreModule {
	return &DocstoreModule{
		docstore: docstore,
	}

}

func (ds *DocstoreModule) Loader(L *lua.LState) int {
	registerColType(L)
	mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
		"col": ds.col,
	})
	L.Push(mod)
	return 1
}

func (ds *DocstoreModule) col(L *lua.LState) int {
	col := &Col{ds.docstore, L.CheckString(1)}
	ud := L.NewUserData()
	ud.Value = col
	L.SetMetatable(ud, L.GetTypeMetatable(luaColTypeName))
	L.Push(ud)
	return 1
}

type Col struct {
	docstore *docstoreExt.DocStoreExt
	Name     string
}

const luaColTypeName = "col"

func registerColType(L *lua.LState) {
	mt := L.NewTypeMetatable(luaColTypeName)
	L.SetGlobal("col", mt)
	// static attributes
	// L.SetField(mt, "new", L.NewFunction(newCol))
	// methods
	L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), colMethods))
}

// Constructor
// FIXME(tsileo): ensure the `new` field isn't required
// func newCol(L *lua.LState) int {
// 	col := &Col{L.CheckString(1)}
// 	ud := L.NewUserData()
// 	ud.Value = col
// 	L.SetMetatable(ud, L.GetTypeMetatable(luaColTypeName))
// 	L.Push(ud)
// 	return 1
// }

// Checks whether the first lua argument is a *LUserData with *Person and returns this *Person.
func checkCol(L *lua.LState) *Col {
	ud := L.CheckUserData(1)
	if v, ok := ud.Value.(*Col); ok {
		return v
	}
	L.ArgError(1, "col expected")
	return nil
}

var colMethods = map[string]lua.LGFunction{
	"name":   colGetName,
	"insert": colInsert,
	"get":    colGet,
	// "query":  colQuery,
}

// Getter and setter for the Person#Name
func colGetName(L *lua.LState) int {
	c := checkCol(L)
	// if L.GetTop() == 2 {
	// 	p.Name = L.CheckString(2)
	// 	return 0
	// }
	L.Push(lua.LString(c.Name))
	return 1
}

func colInsert(L *lua.LState) int {
	c := checkCol(L)
	doc := L.CheckTable(2)
	table := luautil.TableToMap(doc)
	_id, err := c.docstore.Insert(c.Name, &table, "", false)
	if err != nil {
		panic(err)
	}
	L.Push(lua.LString(_id.String()))
	return 1
}

func colGet(L *lua.LState) int {
	c := checkCol(L)
	res := map[string]interface{}{}
	_id := L.CheckString(2)
	if _, err := c.docstore.Fetch(c.Name, _id, &res); err != nil {
		panic(err)
	}
	L.Push(luautil.InterfaceToLValue(L, res))
	return 1
}
<commit_msg>ext/lua: support indexing and namespace for the Lua `docstore` module<commit_after>/*

Package docstore implements a Lua module to interact with the docstore extension.

*/
package docstore

import (
	"github.com/yuin/gopher-lua"

	docstoreExt "github.com/tsileo/blobstash/ext/docstore"
	"github.com/tsileo/blobstash/ext/lua/luautil"
)

type DocstoreModule struct {
	docstore *docstoreExt.DocStoreExt
}

func New(docstore *docstoreExt.DocStoreExt) *DocstoreModule {
	return &DocstoreModule{
		docstore: docstore,
	}

}

func (ds *DocstoreModule) Loader(L *lua.LState) int {
	registerColType(L)
	mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
		"col": ds.col,
	})
	L.Push(mod)
	return 1
}

func (ds *DocstoreModule) col(L *lua.LState) int {
	col := &Col{ds.docstore, L.CheckString(1)}
	ud := L.NewUserData()
	ud.Value = col
	L.SetMetatable(ud, L.GetTypeMetatable(luaColTypeName))
	L.Push(ud)
	return 1
}

type Col struct {
	docstore *docstoreExt.DocStoreExt
	Name     string
}

const luaColTypeName = "col"

func registerColType(L *lua.LState) {
	mt := L.NewTypeMetatable(luaColTypeName)
	L.SetGlobal("col", mt)
	// static attributes
	// L.SetField(mt, "new", L.NewFunction(newCol))
	// methods
	L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), colMethods))
}

// Constructor
// FIXME(tsileo): ensure the `new` field isn't required
// func newCol(L *lua.LState) int {
// 	col := &Col{L.CheckString(1)}
// 	ud := L.NewUserData()
// 	ud.Value = col
// 	L.SetMetatable(ud, L.GetTypeMetatable(luaColTypeName))
// 	L.Push(ud)
// 	return 1
// }

// Checks whether the first lua argument is a *LUserData with *Person and returns this *Person.
func checkCol(L *lua.LState) *Col {
	ud := L.CheckUserData(1)
	if v, ok := ud.Value.(*Col); ok {
		return v
	}
	L.ArgError(1, "col expected")
	return nil
}

var colMethods = map[string]lua.LGFunction{
	"name":   colGetName,
	"insert": colInsert,
	"get":    colGet,
	// "query":  colQuery,
}

// Getter and setter for the Person#Name
func colGetName(L *lua.LState) int {
	c := checkCol(L)
	// if L.GetTop() == 2 {
	// 	p.Name = L.CheckString(2)
	// 	return 0
	// }
	L.Push(lua.LString(c.Name))
	return 1
}

func colInsert(L *lua.LState) int {
	c := checkCol(L)
	doc := L.CheckTable(2)
	opts := L.ToTable(3)
	var ns string
	var index bool
	if opts != nil {
		if tns := L.RawGet(opts, lua.LString("namespace")); tns != lua.LNil {
			if sns, ok := tns.(lua.LString); ok {
				ns = string(sns)
			}
		}
		if tindex := L.RawGet(opts, lua.LString("index")); tindex != lua.LNil {
			if bindex, ok := tindex.(lua.LBool); ok {
				if lua.LVAsBool(bindex) {
					index = true
				}
			}
		}
	}
	table := luautil.TableToMap(doc)
	_id, err := c.docstore.Insert(c.Name, &table, ns, index)
	if err != nil {
		panic(err)
	}
	L.Push(lua.LString(_id.String()))
	return 1
}

func colGet(L *lua.LState) int {
	c := checkCol(L)
	res := map[string]interface{}{}
	_id := L.CheckString(2)
	if _, err := c.docstore.Fetch(c.Name, _id, &res); err != nil {
		panic(err)
	}
	L.Push(luautil.InterfaceToLValue(L, res))
	return 1
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
)

const basePort = 26257
const dataDir = "cockroach-data"

var cockroachBin = func() string {
	bin := "./cockroach"
	if _, err := os.Stat(bin); err == nil {
		return bin
	}
	return "cockroach"
}()

type cluster struct {
	Nodes    map[string]*node
	NextPort int
}

func newCluster() *cluster {
	return &cluster{
		Nodes:    map[string]*node{},
		NextPort: basePort,
	}
}

func (c *cluster) close() {
	for _, t := range c.Nodes {
		if t.Active != nil && t.Active.Cmd != nil && t.Active.Cmd.Process != nil {
			t.Active.Cmd.Process.Kill()
		}
	}
}

func (c *cluster) newNode() *node {
	name := fmt.Sprintf("%d", len(c.Nodes)+1)
	dir := filepath.Join(dataDir, name)
	logdir := filepath.Join(dir, "logs")
	if err := os.MkdirAll(logdir, 0755); err != nil {
		log.Fatal(err)
	}

	port := c.NextPort
	c.NextPort++

	args := []string{
		cockroachBin,
		"start",
		"--insecure",
		fmt.Sprintf("--port=%d", port),
		fmt.Sprintf("--store=%s", dir),
	}
	if port != basePort {
		args = append(args, fmt.Sprintf("--join=localhost:%d", basePort))
	}

	node := newNode(name, args, nil, true,
		filepath.Join(logdir, "${RUN}.stdout"),
		filepath.Join(logdir, "${RUN}.stderr"))
	node.URL = fmt.Sprintf("http://localhost:%d", port)

	c.Nodes[node.Name] = node
	return node
}

func redirect(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Location", req.Referer())
	rw.WriteHeader(http.StatusFound)
}

func (c *cluster) showCluster(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	data := map[string]interface{}{
		"Title":   "cluster",
		"Page":    "Nodes",
		"Cluster": c,
		"Nodes":   c.Nodes,
	}
	renderLayout(rw, "cluster.html", "layout.html", "Content", data)
}

func (c *cluster) addNode(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	c.newNode()
	redirect(rw, req)
}

func (c *cluster) findNode(rw http.ResponseWriter, args map[string]string) *node {
	id := args["node"]
	t, ok := c.Nodes[id]
	if !ok {
		rw.WriteHeader(http.StatusBadRequest)
		renderError(rw, fmt.Sprintf("node %s not found", id))
		return nil
	}
	return t
}

func (c *cluster) findNodeRun(rw http.ResponseWriter, t *node, args map[string]string) *nodeRun {
	run, err := strconv.Atoi(args["run"])
	if err != nil {
		rw.WriteHeader(http.StatusBadRequest)
		renderError(rw, err.Error())
		return nil
	}
	if run < 0 || run >= len(t.Runs) {
		rw.WriteHeader(http.StatusBadRequest)
		renderError(rw, fmt.Sprintf("run %d of node %s not found", run, t.Name))
		return nil
	}
	return t.Runs[run]
}

func (c *cluster) startNode(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	t.Service = true
	t.start()

	redirect(rw, req)
}

func (c *cluster) stopNode(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	t.Service = false
	t.stop()

	redirect(rw, req)
}

func (c *cluster) pauseNode(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	t.pause()

	redirect(rw, req)
}

func (c *cluster) resumeNode(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	t.resume()

	redirect(rw, req)
}

func (c *cluster) nodeHistory(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	data := map[string]interface{}{
		"Title":   "Node",
		"Page":    "History",
		"Cluster": c,
		"Node":    t,
	}

	renderLayout(rw, "node.html", "layout.html", "Content", data)
}

func (c *cluster) nodeRunPage(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	run := c.findNodeRun(rw, t, args)
	if run == nil {
		return
	}

	data := map[string]interface{}{
		"Title":   "Node run",
		"Page":    "NodeRun",
		"Cluster": c,
		"Node":    t,
		"NodeRun": run,
	}

	renderLayout(rw, "run.html", "layout.html", "Content", data)
}

func (c *cluster) nodeRunStdout(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	run := c.findNodeRun(rw, t, args)
	if run == nil {
		return
	}

	data := map[string]interface{}{
		"Title":     "Node run stdout",
		"Page":      "NodeOutput",
		"Type":      "stdout",
		"Cluster":   c,
		"Node":      t,
		"NodeRun":   run,
		"LogOutput": run.StdoutBuf.String(),
	}

	renderLayout(rw, "log.html", "layout.html", "Content", data)
}

func (c *cluster) nodeRunStderr(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	run := c.findNodeRun(rw, t, args)
	if run == nil {
		return
	}

	data := map[string]interface{}{
		"Title":     "Node run stderr",
		"Page":      "NodeOutput",
		"Type":      "stderr",
		"Cluster":   c,
		"Node":      t,
		"NodeRun":   run,
		"LogOutput": run.StderrBuf.String(),
	}

	renderLayout(rw, "log.html", "layout.html", "Content", data)
}
<commit_msg>Add support for --http-port.<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
)

const basePort = 26257
const dataDir = "cockroach-data"

var cockroachBin = func() string {
	bin := "./cockroach"
	if _, err := os.Stat(bin); err == nil {
		return bin
	}
	return "cockroach"
}()

type cluster struct {
	Nodes    map[string]*node
	NextPort int
}

func newCluster() *cluster {
	return &cluster{
		Nodes:    map[string]*node{},
		NextPort: basePort,
	}
}

func (c *cluster) close() {
	for _, t := range c.Nodes {
		if t.Active != nil && t.Active.Cmd != nil && t.Active.Cmd.Process != nil {
			t.Active.Cmd.Process.Kill()
		}
	}
}

func (c *cluster) newNode() *node {
	name := fmt.Sprintf("%d", len(c.Nodes)+1)
	dir := filepath.Join(dataDir, name)
	logdir := filepath.Join(dir, "logs")
	if err := os.MkdirAll(logdir, 0755); err != nil {
		log.Fatal(err)
	}

	port := c.NextPort
	httpPort := c.NextPort + 1
	c.NextPort += 2

	args := []string{
		cockroachBin,
		"start",
		"--insecure",
		fmt.Sprintf("--port=%d", port),
		fmt.Sprintf("--http-port=%d", httpPort),
		fmt.Sprintf("--store=%s", dir),
	}
	if port != basePort {
		args = append(args, fmt.Sprintf("--join=localhost:%d", basePort))
	}

	node := newNode(name, args, nil, true,
		filepath.Join(logdir, "${RUN}.stdout"),
		filepath.Join(logdir, "${RUN}.stderr"))
	node.URL = fmt.Sprintf("http://localhost:%d", httpPort)

	c.Nodes[node.Name] = node
	return node
}

func redirect(rw http.ResponseWriter, req *http.Request) {
	rw.Header().Set("Location", req.Referer())
	rw.WriteHeader(http.StatusFound)
}

func (c *cluster) showCluster(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	data := map[string]interface{}{
		"Title":   "cluster",
		"Page":    "Nodes",
		"Cluster": c,
		"Nodes":   c.Nodes,
	}
	renderLayout(rw, "cluster.html", "layout.html", "Content", data)
}

func (c *cluster) addNode(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	c.newNode()
	redirect(rw, req)
}

func (c *cluster) findNode(rw http.ResponseWriter, args map[string]string) *node {
	id := args["node"]
	t, ok := c.Nodes[id]
	if !ok {
		rw.WriteHeader(http.StatusBadRequest)
		renderError(rw, fmt.Sprintf("node %s not found", id))
		return nil
	}
	return t
}

func (c *cluster) findNodeRun(rw http.ResponseWriter, t *node, args map[string]string) *nodeRun {
	run, err := strconv.Atoi(args["run"])
	if err != nil {
		rw.WriteHeader(http.StatusBadRequest)
		renderError(rw, err.Error())
		return nil
	}
	if run < 0 || run >= len(t.Runs) {
		rw.WriteHeader(http.StatusBadRequest)
		renderError(rw, fmt.Sprintf("run %d of node %s not found", run, t.Name))
		return nil
	}
	return t.Runs[run]
}

func (c *cluster) startNode(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	t.Service = true
	t.start()

	redirect(rw, req)
}

func (c *cluster) stopNode(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	t.Service = false
	t.stop()

	redirect(rw, req)
}

func (c *cluster) pauseNode(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	t.pause()

	redirect(rw, req)
}

func (c *cluster) resumeNode(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	t.resume()

	redirect(rw, req)
}

func (c *cluster) nodeHistory(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	data := map[string]interface{}{
		"Title":   "Node",
		"Page":    "History",
		"Cluster": c,
		"Node":    t,
	}

	renderLayout(rw, "node.html", "layout.html", "Content", data)
}

func (c *cluster) nodeRunPage(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	run := c.findNodeRun(rw, t, args)
	if run == nil {
		return
	}

	data := map[string]interface{}{
		"Title":   "Node run",
		"Page":    "NodeRun",
		"Cluster": c,
		"Node":    t,
		"NodeRun": run,
	}

	renderLayout(rw, "run.html", "layout.html", "Content", data)
}

func (c *cluster) nodeRunStdout(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	run := c.findNodeRun(rw, t, args)
	if run == nil {
		return
	}

	data := map[string]interface{}{
		"Title":     "Node run stdout",
		"Page":      "NodeOutput",
		"Type":      "stdout",
		"Cluster":   c,
		"Node":      t,
		"NodeRun":   run,
		"LogOutput": run.StdoutBuf.String(),
	}

	renderLayout(rw, "log.html", "layout.html", "Content", data)
}

func (c *cluster) nodeRunStderr(rw http.ResponseWriter, req *http.Request, args map[string]string) {
	t := c.findNode(rw, args)
	if t == nil {
		return
	}

	run := c.findNodeRun(rw, t, args)
	if run == nil {
		return
	}

	data := map[string]interface{}{
		"Title":     "Node run stderr",
		"Page":      "NodeOutput",
		"Type":      "stderr",
		"Cluster":   c,
		"Node":      t,
		"NodeRun":   run,
		"LogOutput": run.StderrBuf.String(),
	}

	renderLayout(rw, "log.html", "layout.html", "Content", data)
}
<|endoftext|>


<commit_before>package playedplugin

import (
	"encoding/json"
	"fmt"
	"log"
	"regexp"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/iopred/bruxism"
	"github.com/iopred/discordgo"
)

type playedEntry struct {
	Name     string
	Duration time.Duration
}

type byDuration []*playedEntry

func (a byDuration) Len() int           { return len(a) }
func (a byDuration) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byDuration) Less(i, j int) bool { return a[i].Duration >= a[j].Duration }

type playedUser struct {
	Entries     map[string]*playedEntry
	Current     string
	LastChanged time.Time
	FirstSeen   time.Time
}

func (p *playedUser) Update(name string, now time.Time) {
	if p.Current != "" {
		pe := p.Entries[p.Current]
		if pe == nil {
			pe = &playedEntry{
				Name: p.Current,
			}
			p.Entries[p.Current] = pe
		}
		pe.Duration += now.Sub(p.LastChanged)
	}

	p.Current = name
	p.LastChanged = now
}

type playedPlugin struct {
	sync.RWMutex
	Users map[string]*playedUser
}

// Load will load plugin state from a byte array.
func (p *playedPlugin) Load(bot *bruxism.Bot, service bruxism.Service, data []byte) error {
	if service.Name() != bruxism.DiscordServiceName {
		panic("Carbonitex Plugin only supports Discord.")
	}

	if data != nil {
		if err := json.Unmarshal(data, p); err != nil {
			log.Println("Error loading data", err)
		}
	}

	go p.Run(bot, service)
	return nil
}

// Save will save plugin state to a byte array.
func (p *playedPlugin) Save() ([]byte, error) {
	return json.Marshal(p)
}

func (p *playedPlugin) Update(user string, entry string) {
	p.Lock()
	defer p.Unlock()

	t := time.Now()

	u := p.Users[user]
	if u == nil {
		u = &playedUser{
			Entries:     map[string]*playedEntry{},
			Current:     entry,
			LastChanged: t,
			FirstSeen:   t,
		}
		p.Users[user] = u
	}
	u.Update(entry, t)
}

// Run is the background go routine that executes for the life of the plugin.
func (p *playedPlugin) Run(bot *bruxism.Bot, service bruxism.Service) {
	discord := service.(*bruxism.Discord)

	discord.Session.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) {
		for _, g := range r.Guilds {
			for _, pu := range g.Presences {
				e := ""
				if pu.Game != nil {
					e = pu.Game.Name
				}
				p.Update(pu.User.ID, e)
			}
		}
	})

	discord.Session.AddHandler(func(s *discordgo.Session, g *discordgo.GuildCreate) {
		if g.Unavailable == nil || *g.Unavailable {
			return
		}

		for _, pu := range g.Presences {
			e := ""
			if pu.Game != nil {
				e = pu.Game.Name
			}
			p.Update(pu.User.ID, e)
		}
	})

	discord.Session.AddHandler(func(s *discordgo.Session, pr *discordgo.PresencesReplace) {
		for _, pu := range *pr {
			e := ""
			if pu.Game != nil {
				e = pu.Game.Name
			}
			p.Update(pu.User.ID, e)
		}
	})

	discord.Session.AddHandler(func(s *discordgo.Session, pu *discordgo.PresenceUpdate) {
		e := ""
		if pu.Game != nil {
			e = pu.Game.Name
		}
		p.Update(pu.User.ID, e)
	})
}

// Help returns a list of help strings that are printed when the user requests them.
func (p *playedPlugin) Help(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	if detailed {
		return nil
	}

	return bruxism.CommandHelp(service, "played", "[@username]", "Returns your most played games, or a users most played games if provided.")
}

var userIDRegex = regexp.MustCompile("<@!?([0-9]*)>")

func (p *playedPlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	defer bruxism.MessageRecover()
	if service.Name() == bruxism.DiscordServiceName && !service.IsMe(message) {
		if bruxism.MatchesCommand(service, "played", message) {
			query := strings.Join(strings.Split(message.RawMessage(), " ")[1:], " ")

			id := message.UserID()
			match := userIDRegex.FindStringSubmatch(query)
			if match != nil {
				id = match[1]
			}

			p.Lock()
			defer p.Unlock()

			u := p.Users[id]
			if u == nil {
				service.SendMessage(message.Channel(), "I haven't seen that user.")
				return
			}

			if len(u.Entries) == 0 {
				service.SendMessage(message.Channel(), "I haven't seen anything played by that user.")
				return
			}

			lc := humanize.Time(u.LastChanged)
			u.Update(u.Current, time.Now())

			pes := make(byDuration, len(u.Entries))
			i := 0
			for _, pe := range u.Entries {
				pes[i] = pe
				i++
			}

			sort.Sort(pes)

			messageText := fmt.Sprintf("*First seen %s, last update %s*\n", humanize.Time(u.FirstSeen), lc)
			for i = 0; i < len(pes) && i < 5; i++ {
				pe := pes[i]

				du := pe.Duration

				ds := ""
				hours := int(du / time.Hour)
				if hours > 0 {
					ds += fmt.Sprintf("%dh ", hours)
					du -= time.Duration(hours) * time.Hour
				}

				minutes := int(du / time.Minute)
				ds += fmt.Sprintf("%dm ", minutes)
				du -= time.Duration(minutes) * time.Minute

				seconds := int(du / time.Second)
				ds += fmt.Sprintf("%ds", seconds)

				messageText += fmt.Sprintf("**%s**: %s\n", pe.Name, ds)
			}
			service.SendMessage(message.Channel(), messageText)
		}
	}
}

// Name returns the name of the plugin.
func (p *playedPlugin) Name() string {
	return "Played"
}

// New will create a played plugin.
func New() bruxism.Plugin {
	return &playedPlugin{
		Users: map[string]*playedUser{},
	}
}
<commit_msg>only show minutes if we have to.<commit_after>package playedplugin

import (
	"encoding/json"
	"fmt"
	"log"
	"regexp"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/iopred/bruxism"
	"github.com/iopred/discordgo"
)

type playedEntry struct {
	Name     string
	Duration time.Duration
}

type byDuration []*playedEntry

func (a byDuration) Len() int           { return len(a) }
func (a byDuration) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a byDuration) Less(i, j int) bool { return a[i].Duration >= a[j].Duration }

type playedUser struct {
	Entries     map[string]*playedEntry
	Current     string
	LastChanged time.Time
	FirstSeen   time.Time
}

func (p *playedUser) Update(name string, now time.Time) {
	if p.Current != "" {
		pe := p.Entries[p.Current]
		if pe == nil {
			pe = &playedEntry{
				Name: p.Current,
			}
			p.Entries[p.Current] = pe
		}
		pe.Duration += now.Sub(p.LastChanged)
	}

	p.Current = name
	p.LastChanged = now
}

type playedPlugin struct {
	sync.RWMutex
	Users map[string]*playedUser
}

// Load will load plugin state from a byte array.
func (p *playedPlugin) Load(bot *bruxism.Bot, service bruxism.Service, data []byte) error {
	if service.Name() != bruxism.DiscordServiceName {
		panic("Carbonitex Plugin only supports Discord.")
	}

	if data != nil {
		if err := json.Unmarshal(data, p); err != nil {
			log.Println("Error loading data", err)
		}
	}

	go p.Run(bot, service)
	return nil
}

// Save will save plugin state to a byte array.
func (p *playedPlugin) Save() ([]byte, error) {
	return json.Marshal(p)
}

func (p *playedPlugin) Update(user string, entry string) {
	p.Lock()
	defer p.Unlock()

	t := time.Now()

	u := p.Users[user]
	if u == nil {
		u = &playedUser{
			Entries:     map[string]*playedEntry{},
			Current:     entry,
			LastChanged: t,
			FirstSeen:   t,
		}
		p.Users[user] = u
	}
	u.Update(entry, t)
}

// Run is the background go routine that executes for the life of the plugin.
func (p *playedPlugin) Run(bot *bruxism.Bot, service bruxism.Service) {
	discord := service.(*bruxism.Discord)

	discord.Session.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) {
		for _, g := range r.Guilds {
			for _, pu := range g.Presences {
				e := ""
				if pu.Game != nil {
					e = pu.Game.Name
				}
				p.Update(pu.User.ID, e)
			}
		}
	})

	discord.Session.AddHandler(func(s *discordgo.Session, g *discordgo.GuildCreate) {
		if g.Unavailable == nil || *g.Unavailable {
			return
		}

		for _, pu := range g.Presences {
			e := ""
			if pu.Game != nil {
				e = pu.Game.Name
			}
			p.Update(pu.User.ID, e)
		}
	})

	discord.Session.AddHandler(func(s *discordgo.Session, pr *discordgo.PresencesReplace) {
		for _, pu := range *pr {
			e := ""
			if pu.Game != nil {
				e = pu.Game.Name
			}
			p.Update(pu.User.ID, e)
		}
	})

	discord.Session.AddHandler(func(s *discordgo.Session, pu *discordgo.PresenceUpdate) {
		e := ""
		if pu.Game != nil {
			e = pu.Game.Name
		}
		p.Update(pu.User.ID, e)
	})
}

// Help returns a list of help strings that are printed when the user requests them.
func (p *playedPlugin) Help(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	if detailed {
		return nil
	}

	return bruxism.CommandHelp(service, "played", "[@username]", "Returns your most played games, or a users most played games if provided.")
}

var userIDRegex = regexp.MustCompile("<@!?([0-9]*)>")

func (p *playedPlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	defer bruxism.MessageRecover()
	if service.Name() == bruxism.DiscordServiceName && !service.IsMe(message) {
		if bruxism.MatchesCommand(service, "played", message) {
			query := strings.Join(strings.Split(message.RawMessage(), " ")[1:], " ")

			id := message.UserID()
			match := userIDRegex.FindStringSubmatch(query)
			if match != nil {
				id = match[1]
			}

			p.Lock()
			defer p.Unlock()

			u := p.Users[id]
			if u == nil {
				service.SendMessage(message.Channel(), "I haven't seen that user.")
				return
			}

			if len(u.Entries) == 0 {
				service.SendMessage(message.Channel(), "I haven't seen anything played by that user.")
				return
			}

			lc := humanize.Time(u.LastChanged)
			u.Update(u.Current, time.Now())

			pes := make(byDuration, len(u.Entries))
			i := 0
			for _, pe := range u.Entries {
				pes[i] = pe
				i++
			}

			sort.Sort(pes)

			messageText := fmt.Sprintf("*First seen %s, last update %s*\n", humanize.Time(u.FirstSeen), lc)
			for i = 0; i < len(pes) && i < 5; i++ {
				pe := pes[i]

				du := pe.Duration

				ds := ""
				hours := int(du / time.Hour)
				if hours > 0 {
					ds += fmt.Sprintf("%dh ", hours)
					du -= time.Duration(hours) * time.Hour
				}

				minutes := int(du / time.Minute)
				if minutes > 0 || len(ds) > 0 {
					ds += fmt.Sprintf("%dm ", minutes)
					du -= time.Duration(minutes) * time.Minute
				}

				seconds := int(du / time.Second)
				ds += fmt.Sprintf("%ds", seconds)

				messageText += fmt.Sprintf("**%s**: %s\n", pe.Name, ds)
			}
			service.SendMessage(message.Channel(), messageText)
		}
	}
}

// Name returns the name of the plugin.
func (p *playedPlugin) Name() string {
	return "Played"
}

// New will create a played plugin.
func New() bruxism.Plugin {
	return &playedPlugin{
		Users: map[string]*playedUser{},
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	flag "github.com/spf13/pflag"
)

var commands = map[string]*Command{}

// inspired by https://github.com/constabulary/gb/blob/master/cmd/cmd.go

// Command represents a subcommand, or plugin that is executed
type Command struct {
	// Name of the command
	name string

	// UsageLine demonstrates how to use this command
	usageLine string

	// Single line description of the purpose of the command
	short string

	// Description of this command
	long string

	// Run is invoked with arguments left over after flag parsing.
	run func(args []string) error

	// FlagSet for adding flags for that command
	fs *flag.FlagSet

	// function for adding flags for that command and any sub-Command FlagSet
	gfs func(*flag.FlagSet)

	// Parent Command
	parent parent

	// Subcommands
	subcmds map[string]*Command

	// Args passed when running command
	args []string
}

type parent interface {
	fullCommand() string
	parseFlags()
}

func NewCommand(name, usageLine, short, long string, run func([]string) error, parent *Command) *Command {
	cmd := &Command{
		name:      name,
		usageLine: usageLine,
		short:     short,
		long:      long,
		run:       run,
		fs:        flag.NewFlagSet(name, flag.ExitOnError),
		subcmds:   make(map[string]*Command),
		args:      []string{},
	}
	if parent == nil {
		// This is a root command
		commands[name] = cmd
	} else {
		cmd.parent = *parent
	}
	return cmd
}

// FlagSet for adding flags for that command
func (cmd *Command) FS() *flag.FlagSet {
	return cmd.fs
}

// Set function for adding flags for that command and any sub-Command FlagSet
func (cmd *Command) SetGFS(gfs func(*flag.FlagSet)) {
	cmd.gfs = gfs
	gfs(cmd.fs)
}

// Runnable indicates this is a command that can be involved.
// Non runnable commands are only informational.
func (c *Command) Runnable() bool { return c.run != nil }

// RunCommand parses flags and runs the Command.
func RunCommand(args []string) error {
	cmd, err := commandFromArgs(os.Args)
	if err != nil {
		return err
	}
	cmd.parseFlags()
	if cmd.run == nil {
		return nil
	}
	return cmd.run(cmd.fs.Args())
}

func (cmd Command) parseFlags() {
	if cmd.parent != nil {
		cmd.parent.parseFlags()
	}
	if err := cmd.fs.Parse(cmd.args); err != nil {
		fmt.Printf("Incorrect usage of %s:", cmd.fullCommand())
		cmd.fs.Usage()
		os.Exit(1)
	}
}

func (cmd Command) fullCommand() string {
	res := cmd.name
	if cmd.parent != nil {
		res = cmd.parent.fullCommand() + " " + res
	}
	return res
}

func commandFromArgs(args []string) (*Command, error) {
	var cmd *Command
	for i, arg := range args {
		if i == 0 {
			arg = filepath.Base(arg)
			ext := filepath.Ext(arg)
			if ext != "" {
				arg = arg[:len(arg)-len(ext)]
			}
		}
		if arg == "--" {
			cmd.args = append(cmd.args, args[i:]...)
			return cmd, nil
		}
		if strings.HasPrefix(arg, "-") {
			cmd.args = append(cmd.args, arg)
			continue
		}
		var subcmd *Command
		if cmd == nil {
			cmd = commands[arg]
			if cmd == nil {
				return nil, fmt.Errorf("Unknown command '%s'", arg)
			}
		} else {
			subcmd = cmd.subcmds[arg]
			if subcmd == nil {
				cmd.args = append(cmd.args, arg)
			} else {
				cmd = subcmd
			}
		}
	}
	if cmd == nil {
		return nil, fmt.Errorf("Unknown command from args '%v'", args)
	}
	return cmd, nil
}
<commit_msg>cmd.go: add some debug Printf commented<commit_after>package cmd

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	flag "github.com/spf13/pflag"
)

var commands = map[string]*Command{}

// inspired by https://github.com/constabulary/gb/blob/master/cmd/cmd.go

// Command represents a subcommand, or plugin that is executed
type Command struct {
	// Name of the command
	name string

	// UsageLine demonstrates how to use this command
	usageLine string

	// Single line description of the purpose of the command
	short string

	// Description of this command
	long string

	// Run is invoked with arguments left over after flag parsing.
	run func(args []string) error

	// FlagSet for adding flags for that command
	fs *flag.FlagSet

	// function for adding flags for that command and any sub-Command FlagSet
	gfs func(*flag.FlagSet)

	// Parent Command
	parent parent

	// Subcommands
	subcmds map[string]*Command

	// Args passed when running command
	args []string
}

type parent interface {
	fullCommand() string
	parseFlags()
}

func NewCommand(name, usageLine, short, long string, run func([]string) error, parent *Command) *Command {
	cmd := &Command{
		name:      name,
		usageLine: usageLine,
		short:     short,
		long:      long,
		run:       run,
		fs:        flag.NewFlagSet(name, flag.ExitOnError),
		subcmds:   make(map[string]*Command),
		args:      []string{},
	}
	// fmt.Printf("New Command '%s', nil parent: %v\n", cmd.name, cmd.parent == nil)
	if parent == nil {
		// This is a root command
		commands[name] = cmd
	} else {
		cmd.parent = *parent
	}
	// fmt.Printf("registered parent for name '%s': %v\n", name, commands)
	return cmd
}

// FlagSet for adding flags for that command
func (cmd *Command) FS() *flag.FlagSet {
	return cmd.fs
}

// Set function for adding flags for that command and any sub-Command FlagSet
func (cmd *Command) SetGFS(gfs func(*flag.FlagSet)) {
	cmd.gfs = gfs
	gfs(cmd.fs)
}

// Runnable indicates this is a command that can be involved.
// Non runnable commands are only informational.
func (c *Command) Runnable() bool { return c.run != nil }

// RunCommand parses flags and runs the Command.
func RunCommand(args []string) error {
	// fmt.Printf("args %+v, c %v '%v'\n", os.Args, commands == nil, commands)
	cmd, err := commandFromArgs(os.Args)
	// fmt.Printf("cmd nil?? %v, args %+v, err='%v'\n", cmd == nil, os.Args, err)
	if err != nil {
		return err
	}
	cmd.parseFlags()
	if cmd.run == nil {
		return nil
	}
	return cmd.run(cmd.fs.Args())
}

func (cmd Command) parseFlags() {
	if cmd.parent != nil {
		cmd.parent.parseFlags()
	}
	if err := cmd.fs.Parse(cmd.args); err != nil {
		fmt.Printf("Incorrect usage of %s:", cmd.fullCommand())
		cmd.fs.Usage()
		os.Exit(1)
	}
}

func (cmd Command) fullCommand() string {
	res := cmd.name
	if cmd.parent != nil {
		res = cmd.parent.fullCommand() + " " + res
	}
	return res
}

func commandFromArgs(args []string) (*Command, error) {
	var cmd *Command
	// fmt.Printf("len %v\n", len(args))
	for i, arg := range args {
		if i == 0 {
			arg = filepath.Base(arg)
			ext := filepath.Ext(arg)
			if ext != "" {
				arg = arg[:len(arg)-len(ext)]
			}
		}
		// fmt.Printf("arg='%s'\n", arg)
		if arg == "--" {
			cmd.args = append(cmd.args, args[i:]...)
			return cmd, nil
		}
		if strings.HasPrefix(arg, "-") {
			cmd.args = append(cmd.args, arg)
			continue
		}
		var subcmd *Command
		// fmt.Printf("cmd nil %v, reg %v\n", cmd == nil, commands)
		if cmd == nil {
			cmd = commands[arg]
			if cmd == nil {
				return nil, fmt.Errorf("Unknown command '%s'", arg)
			}
		} else {
			subcmd = cmd.subcmds[arg]
			if subcmd == nil {
				cmd.args = append(cmd.args, arg)
			} else {
				cmd = subcmd
			}
		}
	}
	if cmd == nil {
		return nil, fmt.Errorf("Unknown command from args '%v'", args)
	}
	return cmd, nil
}
<|endoftext|>


<commit_before>// Package cmd implemnts the rclone command
//
// It is in a sub package so it's internals can be re-used elsewhere
package cmd

// FIXME only attach the remote flags when using a remote???
// would probably mean bringing all the flags in to here? Or define some flagsets in fs...

import (
	"fmt"
	"log"
	"os"
	"path"
	"runtime"
	"runtime/pprof"
	"time"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"github.com/ncw/rclone/fs"
)

// Globals
var (
	// Flags
	cpuProfile    = pflag.StringP("cpuprofile", "", "", "Write cpu profile to file")
	memProfile    = pflag.String("memprofile", "", "Write memory profile to file")
	statsInterval = pflag.DurationP("stats", "", time.Minute*1, "Interval to print stats (0 to disable)")
	version       bool
	logFile       = pflag.StringP("log-file", "", "", "Log everything to this file")
	retries       = pflag.IntP("retries", "", 3, "Retry operations this many times if they fail")
)

// Root is the main rclone command
var Root = &cobra.Command{
	Use:   "rclone",
	Short: "Sync files and directories to and from local and remote object stores - " + fs.Version,
	Long: `
Rclone is a command line program to sync files and directories to and
from various cloud storage systems, such as:

  * Google Drive
  * Amazon S3
  * Openstack Swift / Rackspace cloud files / Memset Memstore
  * Dropbox
  * Google Cloud Storage
  * Amazon Drive
  * Microsoft One Drive
  * Hubic
  * Backblaze B2
  * Yandex Disk
  * The local filesystem

Features

  * MD5/SHA1 hashes checked at all times for file integrity
  * Timestamps preserved on files
  * Partial syncs supported on a whole file basis
  * Copy mode to just copy new/changed files
  * Sync (one way) mode to make a directory identical
  * Check mode to check for file hash equality
  * Can sync to and from network, eg two different cloud accounts

See the home page for installation, usage, documentation, changelog
and configuration walkthroughs.

  * http://rclone.org/
`,
}

// runRoot implements the main rclone command with no subcommands
func runRoot(cmd *cobra.Command, args []string) {
	if version {
		ShowVersion()
		os.Exit(0)
	} else {
		_ = Root.Usage()
		fmt.Fprintf(os.Stderr, "Command not found.\n")
		os.Exit(1)
	}
}

func init() {
	Root.Run = runRoot
	Root.Flags().BoolVarP(&version, "version", "V", false, "Print the version number")
	cobra.OnInitialize(initConfig)
}

// ShowVersion prints the version to stdout
func ShowVersion() {
	fmt.Printf("rclone %s\n", fs.Version)
}

// newFsSrc creates a src Fs from a name
//
// This can point to a file
func newFsSrc(remote string) fs.Fs {
	fsInfo, configName, fsPath, err := fs.ParseRemote(remote)
	if err != nil {
		fs.Stats.Error()
		log.Fatalf("Failed to create file system for %q: %v", remote, err)
	}
	f, err := fsInfo.NewFs(configName, fsPath)
	if err == fs.ErrorIsFile {
		if !fs.Config.Filter.InActive() {
			fs.Stats.Error()
			log.Fatalf("Can't limit to single files when using filters: %v", remote)
		}
		// Limit transfers to this file
		err = fs.Config.Filter.AddFile(path.Base(fsPath))
		// Set --no-traverse as only one file
		fs.Config.NoTraverse = true
	}
	if err != nil {
		fs.Stats.Error()
		log.Fatalf("Failed to create file system for %q: %v", remote, err)
	}
	return f
}

// newFsDst creates a dst Fs from a name
//
// This must point to a directory
func newFsDst(remote string) fs.Fs {
	f, err := fs.NewFs(remote)
	if err != nil {
		fs.Stats.Error()
		log.Fatalf("Failed to create file system for %q: %v", remote, err)
	}
	return f
}

// NewFsSrcDst creates a new src and dst fs from the arguments
func NewFsSrcDst(args []string) (fs.Fs, fs.Fs) {
	fsrc, fdst := newFsSrc(args[0]), newFsDst(args[1])
	fs.CalculateModifyWindow(fdst, fsrc)
	return fsrc, fdst
}

// NewFsSrc creates a new src fs from the arguments
func NewFsSrc(args []string) fs.Fs {
	fsrc := newFsSrc(args[0])
	fs.CalculateModifyWindow(fsrc)
	return fsrc
}

// NewFsDst creates a new dst fs from the arguments
//
// Dst fs-es can't point to single files
func NewFsDst(args []string) fs.Fs {
	fdst := newFsDst(args[0])
	fs.CalculateModifyWindow(fdst)
	return fdst
}

// Run the function with stats and retries if required
func Run(Retry bool, cmd *cobra.Command, f func() error) {
	var err error
	stopStats := startStats()
	for try := 1; try <= *retries; try++ {
		err = f()
		if !Retry || (err == nil && !fs.Stats.Errored()) {
			if try > 1 {
				fs.ErrorLog(nil, "Attempt %d/%d succeeded", try, *retries)
			}
			break
		}
		if fs.IsFatalError(err) {
			fs.ErrorLog(nil, "Fatal error received - not attempting retries")
			break
		}
		if fs.IsNoRetryError(err) {
			fs.ErrorLog(nil, "Can't retry this error - not attempting retries")
			break
		}
		if err != nil {
			fs.ErrorLog(nil, "Attempt %d/%d failed with %d errors and: %v", try, *retries, fs.Stats.GetErrors(), err)
		} else {
			fs.ErrorLog(nil, "Attempt %d/%d failed with %d errors", try, *retries, fs.Stats.GetErrors())
		}
		if try < *retries {
			fs.Stats.ResetErrors()
		}
	}
	close(stopStats)
	if err != nil {
		log.Fatalf("Failed to %s: %v", cmd.Name(), err)
	}
	if !fs.Config.Quiet || fs.Stats.Errored() || *statsInterval > 0 {
		fs.Log(nil, "%s", fs.Stats)
	}
	if fs.Config.Verbose {
		fs.Debug(nil, "Go routines at exit %d\n", runtime.NumGoroutine())
	}
	if fs.Stats.Errored() {
		os.Exit(1)
	}
}

// CheckArgs checks there are enough arguments and prints a message if not
func CheckArgs(MinArgs, MaxArgs int, cmd *cobra.Command, args []string) {
	if len(args) < MinArgs {
		_ = cmd.Usage()
		fmt.Fprintf(os.Stderr, "Command %s needs %d arguments mininum\n", cmd.Name(), MinArgs)
		os.Exit(1)
	} else if len(args) > MaxArgs {
		_ = cmd.Usage()
		fmt.Fprintf(os.Stderr, "Command %s needs %d arguments maximum\n", cmd.Name(), MaxArgs)
		os.Exit(1)
	}
}

// startStats prints the stats every statsInterval
//
// It returns a channel which should be closed to stop the stats.
func startStats() chan struct{} {
	stopStats := make(chan struct{})
	if *statsInterval > 0 {
		go func() {
			ticker := time.NewTicker(*statsInterval)
			for {
				select {
				case <-ticker.C:
					fs.Stats.Log()
				case <-stopStats:
					ticker.Stop()
					return
				}
			}
		}()
	}
	return stopStats
}

// initConfig is run by cobra after initialising the flags
func initConfig() {
	// Log file output
	if *logFile != "" {
		f, err := os.OpenFile(*logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
		if err != nil {
			log.Fatalf("Failed to open log file: %v", err)
		}
		_, err = f.Seek(0, os.SEEK_END)
		if err != nil {
			fs.ErrorLog(nil, "Failed to seek log file to end: %v", err)
		}
		log.SetOutput(f)
		fs.DebugLogger.SetOutput(f)
		redirectStderr(f)
	}

	// Load the rest of the config now we have started the logger
	fs.LoadConfig()

	// Write the args for debug purposes
	fs.Debug("rclone", "Version %q starting with parameters %q", fs.Version, os.Args)

	// Setup CPU profiling if desired
	if *cpuProfile != "" {
		fs.Log(nil, "Creating CPU profile %q\n", *cpuProfile)
		f, err := os.Create(*cpuProfile)
		if err != nil {
			fs.Stats.Error()
			log.Fatal(err)
		}
		err = pprof.StartCPUProfile(f)
		if err != nil {
			fs.Stats.Error()
			log.Fatal(err)
		}
		defer pprof.StopCPUProfile()
	}

	// Setup memory profiling if desired
	if *memProfile != "" {
		defer func() {
			fs.Log(nil, "Saving Memory profile %q\n", *memProfile)
			f, err := os.Create(*memProfile)
			if err != nil {
				fs.Stats.Error()
				log.Fatal(err)
			}
			err = pprof.WriteHeapProfile(f)
			if err != nil {
				fs.Stats.Error()
				log.Fatal(err)
			}
			err = f.Close()
			if err != nil {
				fs.Stats.Error()
				log.Fatal(err)
			}
		}()
	}
}
<commit_msg>make the parameter format for --stats flag more obvious<commit_after>// Package cmd implemnts the rclone command
//
// It is in a sub package so it's internals can be re-used elsewhere
package cmd

// FIXME only attach the remote flags when using a remote???
// would probably mean bringing all the flags in to here? Or define some flagsets in fs...

import (
	"fmt"
	"log"
	"os"
	"path"
	"runtime"
	"runtime/pprof"
	"time"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"github.com/ncw/rclone/fs"
)

// Globals
var (
	// Flags
	cpuProfile    = pflag.StringP("cpuprofile", "", "", "Write cpu profile to file")
	memProfile    = pflag.String("memprofile", "", "Write memory profile to file")
	statsInterval = pflag.DurationP("stats", "", time.Minute*1, "Interval between printing stats, e.g 500ms, 60s, 5m. (0 to disable)")
	version       bool
	logFile       = pflag.StringP("log-file", "", "", "Log everything to this file")
	retries       = pflag.IntP("retries", "", 3, "Retry operations this many times if they fail")
)

// Root is the main rclone command
var Root = &cobra.Command{
	Use:   "rclone",
	Short: "Sync files and directories to and from local and remote object stores - " + fs.Version,
	Long: `
Rclone is a command line program to sync files and directories to and
from various cloud storage systems, such as:

  * Google Drive
  * Amazon S3
  * Openstack Swift / Rackspace cloud files / Memset Memstore
  * Dropbox
  * Google Cloud Storage
  * Amazon Drive
  * Microsoft One Drive
  * Hubic
  * Backblaze B2
  * Yandex Disk
  * The local filesystem

Features

  * MD5/SHA1 hashes checked at all times for file integrity
  * Timestamps preserved on files
  * Partial syncs supported on a whole file basis
  * Copy mode to just copy new/changed files
  * Sync (one way) mode to make a directory identical
  * Check mode to check for file hash equality
  * Can sync to and from network, eg two different cloud accounts

See the home page for installation, usage, documentation, changelog
and configuration walkthroughs.

  * http://rclone.org/
`,
}

// runRoot implements the main rclone command with no subcommands
func runRoot(cmd *cobra.Command, args []string) {
	if version {
		ShowVersion()
		os.Exit(0)
	} else {
		_ = Root.Usage()
		fmt.Fprintf(os.Stderr, "Command not found.\n")
		os.Exit(1)
	}
}

func init() {
	Root.Run = runRoot
	Root.Flags().BoolVarP(&version, "version", "V", false, "Print the version number")
	cobra.OnInitialize(initConfig)
}

// ShowVersion prints the version to stdout
func ShowVersion() {
	fmt.Printf("rclone %s\n", fs.Version)
}

// newFsSrc creates a src Fs from a name
//
// This can point to a file
func newFsSrc(remote string) fs.Fs {
	fsInfo, configName, fsPath, err := fs.ParseRemote(remote)
	if err != nil {
		fs.Stats.Error()
		log.Fatalf("Failed to create file system for %q: %v", remote, err)
	}
	f, err := fsInfo.NewFs(configName, fsPath)
	if err == fs.ErrorIsFile {
		if !fs.Config.Filter.InActive() {
			fs.Stats.Error()
			log.Fatalf("Can't limit to single files when using filters: %v", remote)
		}
		// Limit transfers to this file
		err = fs.Config.Filter.AddFile(path.Base(fsPath))
		// Set --no-traverse as only one file
		fs.Config.NoTraverse = true
	}
	if err != nil {
		fs.Stats.Error()
		log.Fatalf("Failed to create file system for %q: %v", remote, err)
	}
	return f
}

// newFsDst creates a dst Fs from a name
//
// This must point to a directory
func newFsDst(remote string) fs.Fs {
	f, err := fs.NewFs(remote)
	if err != nil {
		fs.Stats.Error()
		log.Fatalf("Failed to create file system for %q: %v", remote, err)
	}
	return f
}

// NewFsSrcDst creates a new src and dst fs from the arguments
func NewFsSrcDst(args []string) (fs.Fs, fs.Fs) {
	fsrc, fdst := newFsSrc(args[0]), newFsDst(args[1])
	fs.CalculateModifyWindow(fdst, fsrc)
	return fsrc, fdst
}

// NewFsSrc creates a new src fs from the arguments
func NewFsSrc(args []string) fs.Fs {
	fsrc := newFsSrc(args[0])
	fs.CalculateModifyWindow(fsrc)
	return fsrc
}

// NewFsDst creates a new dst fs from the arguments
//
// Dst fs-es can't point to single files
func NewFsDst(args []string) fs.Fs {
	fdst := newFsDst(args[0])
	fs.CalculateModifyWindow(fdst)
	return fdst
}

// Run the function with stats and retries if required
func Run(Retry bool, cmd *cobra.Command, f func() error) {
	var err error
	stopStats := startStats()
	for try := 1; try <= *retries; try++ {
		err = f()
		if !Retry || (err == nil && !fs.Stats.Errored()) {
			if try > 1 {
				fs.ErrorLog(nil, "Attempt %d/%d succeeded", try, *retries)
			}
			break
		}
		if fs.IsFatalError(err) {
			fs.ErrorLog(nil, "Fatal error received - not attempting retries")
			break
		}
		if fs.IsNoRetryError(err) {
			fs.ErrorLog(nil, "Can't retry this error - not attempting retries")
			break
		}
		if err != nil {
			fs.ErrorLog(nil, "Attempt %d/%d failed with %d errors and: %v", try, *retries, fs.Stats.GetErrors(), err)
		} else {
			fs.ErrorLog(nil, "Attempt %d/%d failed with %d errors", try, *retries, fs.Stats.GetErrors())
		}
		if try < *retries {
			fs.Stats.ResetErrors()
		}
	}
	close(stopStats)
	if err != nil {
		log.Fatalf("Failed to %s: %v", cmd.Name(), err)
	}
	if !fs.Config.Quiet || fs.Stats.Errored() || *statsInterval > 0 {
		fs.Log(nil, "%s", fs.Stats)
	}
	if fs.Config.Verbose {
		fs.Debug(nil, "Go routines at exit %d\n", runtime.NumGoroutine())
	}
	if fs.Stats.Errored() {
		os.Exit(1)
	}
}

// CheckArgs checks there are enough arguments and prints a message if not
func CheckArgs(MinArgs, MaxArgs int, cmd *cobra.Command, args []string) {
	if len(args) < MinArgs {
		_ = cmd.Usage()
		fmt.Fprintf(os.Stderr, "Command %s needs %d arguments mininum\n", cmd.Name(), MinArgs)
		os.Exit(1)
	} else if len(args) > MaxArgs {
		_ = cmd.Usage()
		fmt.Fprintf(os.Stderr, "Command %s needs %d arguments maximum\n", cmd.Name(), MaxArgs)
		os.Exit(1)
	}
}

// startStats prints the stats every statsInterval
//
// It returns a channel which should be closed to stop the stats.
func startStats() chan struct{} {
	stopStats := make(chan struct{})
	if *statsInterval > 0 {
		go func() {
			ticker := time.NewTicker(*statsInterval)
			for {
				select {
				case <-ticker.C:
					fs.Stats.Log()
				case <-stopStats:
					ticker.Stop()
					return
				}
			}
		}()
	}
	return stopStats
}

// initConfig is run by cobra after initialising the flags
func initConfig() {
	// Log file output
	if *logFile != "" {
		f, err := os.OpenFile(*logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
		if err != nil {
			log.Fatalf("Failed to open log file: %v", err)
		}
		_, err = f.Seek(0, os.SEEK_END)
		if err != nil {
			fs.ErrorLog(nil, "Failed to seek log file to end: %v", err)
		}
		log.SetOutput(f)
		fs.DebugLogger.SetOutput(f)
		redirectStderr(f)
	}

	// Load the rest of the config now we have started the logger
	fs.LoadConfig()

	// Write the args for debug purposes
	fs.Debug("rclone", "Version %q starting with parameters %q", fs.Version, os.Args)

	// Setup CPU profiling if desired
	if *cpuProfile != "" {
		fs.Log(nil, "Creating CPU profile %q\n", *cpuProfile)
		f, err := os.Create(*cpuProfile)
		if err != nil {
			fs.Stats.Error()
			log.Fatal(err)
		}
		err = pprof.StartCPUProfile(f)
		if err != nil {
			fs.Stats.Error()
			log.Fatal(err)
		}
		defer pprof.StopCPUProfile()
	}

	// Setup memory profiling if desired
	if *memProfile != "" {
		defer func() {
			fs.Log(nil, "Saving Memory profile %q\n", *memProfile)
			f, err := os.Create(*memProfile)
			if err != nil {
				fs.Stats.Error()
				log.Fatal(err)
			}
			err = pprof.WriteHeapProfile(f)
			if err != nil {
				fs.Stats.Error()
				log.Fatal(err)
			}
			err = f.Close()
			if err != nil {
				fs.Stats.Error()
				log.Fatal(err)
			}
		}()
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/env"
	"github.com/getgauge/gauge/execution"
	"github.com/getgauge/gauge/filter"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/order"
	"github.com/getgauge/gauge/reporter"
	"github.com/getgauge/gauge/skel"
	"github.com/getgauge/gauge/track"
	"github.com/getgauge/gauge/util"
	"github.com/getgauge/gauge/validation"
	"github.com/spf13/cobra"
)

const (
	logLevelDefault        = "info"
	dirDefault             = "."
	machineReadableDefault = false
	gaugeVersionDefault    = false

	logLevelName        = "log-level"
	dirName             = "dir"
	machineReadableName = "machine-readable"
	gaugeVersionName    = "version"
)

var (
	GaugeCmd = &cobra.Command{
		Use: "gauge <command> [flags] [args]",
		Example: `  gauge run specs/
  gauge run --parallel specs/`,
		Run: func(cmd *cobra.Command, args []string) {
			if gaugeVersion {
				printVersion()
				return
			}
			if len(args) < 1 {
				cmd.Help()
			}
		},
		DisableAutoGenTag: true,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			skel.CreateSkelFilesIfRequired()
			track.Init()
			config.SetProjectRoot(args)
			if e := env.LoadEnv(environment); e != nil {
				logger.Fatalf(true, e.Error())
			}
			initLogger(cmd.Name())
			setGlobalFlags()
			initPackageFlags()
		},
		PersistentPostRun: notifyTelemetryIfNeeded,
	}
	logLevel        string
	dir             string
	machineReadable bool
	gaugeVersion    bool
)

type notification struct {
	Title   string `json:"title"`
	Message string `json:"message"`
	Type    string `json:"type"`
}

func (status *notification) getJSON() (string, error) {
	j, err := json.Marshal(status)
	if err != nil {
		return "", err
	}
	return string(j), nil
}

func notifyTelemetryIfNeeded(cmd *cobra.Command, args []string) {
	if !gaugeVersion && !config.TelemetryConsent() {
		if machineReadable {
			n := &notification{
				Title:   "Gauge Telemetry",
				Message: track.GaugeTelemetryMachineRedableMessage,
				Type:    "info",
			}
			s, _ := n.getJSON()
			fmt.Printf("{\"type\":\"notification\",\"notification\":%s}\n", s)
		} else {
			fmt.Printf("%s\n%s\n", track.GaugeTelemetryMessageHeading, track.GaugeTelemetryMessage)
		}
	}
}

func initLogger(n string) {
	if lsp {
		logger.Initialize(machineReadable, logLevel, logger.LSP)
	} else if n == "daemon" {
		logger.Initialize(machineReadable, logLevel, logger.API)
	} else {
		logger.Initialize(machineReadable, logLevel, logger.CLI)
	}
}

func init() {
	GaugeCmd.SetUsageTemplate(`Usage:{{if .Runnable}}
  {{.UseLine}}{{end}}{{if gt (len .Aliases) 0}}

Aliases:
  {{.NameAndAliases}}{{end}}{{if .HasExample}}

Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}

Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}

Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}

Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}

Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}

Use "{{.CommandPath}} [command] --help" for more information about a command.
Complete manual is available at https://manpage.gauge.org/.{{end}}
`)
	GaugeCmd.PersistentFlags().StringVarP(&logLevel, logLevelName, "l", logLevelDefault, "Set level of logging to debug, info, warning, error or critical")
	GaugeCmd.PersistentFlags().StringVarP(&dir, dirName, "d", dirDefault, "Set the working directory for the current command, accepts a path relative to current directory")
	GaugeCmd.PersistentFlags().BoolVarP(&machineReadable, machineReadableName, "m", machineReadableDefault, "Prints output in JSON format")
	GaugeCmd.Flags().BoolVarP(&gaugeVersion, gaugeVersionName, "v", gaugeVersionDefault, "Print Gauge and plugin versions")
}

func Parse() error {
	InitHelp(GaugeCmd)
	return GaugeCmd.Execute()
}

func InitHelp(c *cobra.Command) {
	c.Flags().BoolP("help", "h", false, "Help for "+c.Name())
	if c.HasSubCommands() {
		for _, sc := range c.Commands() {
			InitHelp(sc)
		}
	}
}

func getSpecsDir(args []string) []string {
	if len(args) > 0 {
		return args
	}
	return util.GetSpecDirs()
}

func setGlobalFlags() {
	util.SetWorkingDir(dir)
}

func initPackageFlags() {
	if parallel {
		simpleConsole = true
		reporter.IsParallel = true
	}
	reporter.SimpleConsoleOutput = simpleConsole
	reporter.Verbose = verbose
	reporter.MachineReadable = machineReadable
	execution.MachineReadable = machineReadable
	execution.ExecuteTags = tags
	execution.SetTableRows(rows)
	validation.TableRows = rows
	execution.NumberOfExecutionStreams = streams
	execution.InParallel = parallel
	execution.Strategy = strategy
	filter.ExecuteTags = tags
	order.Sorted = sort
	filter.Distribute = group
	filter.NumberOfExecutionStreams = streams
	reporter.NumberOfExecutionStreams = streams
	validation.HideSuggestion = hideSuggestion
	if group != -1 {
		execution.Strategy = execution.Eager
	}
	filter.ScenariosName = scenarios
}

var exit = func(err error, additionalText string) {
	if err != nil {
		logger.Errorf(true, err.Error())
	}
	if additionalText != "" {
		logger.Infof(true, additionalText)
	}
	os.Exit(0)
}
<commit_msg>Set working directory before loading environments. #1278<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/env"
	"github.com/getgauge/gauge/execution"
	"github.com/getgauge/gauge/filter"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/order"
	"github.com/getgauge/gauge/reporter"
	"github.com/getgauge/gauge/skel"
	"github.com/getgauge/gauge/track"
	"github.com/getgauge/gauge/util"
	"github.com/getgauge/gauge/validation"
	"github.com/spf13/cobra"
)

const (
	logLevelDefault        = "info"
	dirDefault             = "."
	machineReadableDefault = false
	gaugeVersionDefault    = false

	logLevelName        = "log-level"
	dirName             = "dir"
	machineReadableName = "machine-readable"
	gaugeVersionName    = "version"
)

var (
	GaugeCmd = &cobra.Command{
		Use: "gauge <command> [flags] [args]",
		Example: `  gauge run specs/
  gauge run --parallel specs/`,
		Run: func(cmd *cobra.Command, args []string) {
			if gaugeVersion {
				printVersion()
				return
			}
			if len(args) < 1 {
				cmd.Help()
			}
		},
		DisableAutoGenTag: true,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			skel.CreateSkelFilesIfRequired()
			track.Init()
			config.SetProjectRoot(args)
			setGlobalFlags()
			if e := env.LoadEnv(environment); e != nil {
				logger.Fatalf(true, e.Error())
			}
			initLogger(cmd.Name())
			initPackageFlags()
		},
		PersistentPostRun: notifyTelemetryIfNeeded,
	}
	logLevel        string
	dir             string
	machineReadable bool
	gaugeVersion    bool
)

type notification struct {
	Title   string `json:"title"`
	Message string `json:"message"`
	Type    string `json:"type"`
}

func (status *notification) getJSON() (string, error) {
	j, err := json.Marshal(status)
	if err != nil {
		return "", err
	}
	return string(j), nil
}

func notifyTelemetryIfNeeded(cmd *cobra.Command, args []string) {
	if !gaugeVersion && !config.TelemetryConsent() {
		if machineReadable {
			n := &notification{
				Title:   "Gauge Telemetry",
				Message: track.GaugeTelemetryMachineRedableMessage,
				Type:    "info",
			}
			s, _ := n.getJSON()
			fmt.Printf("{\"type\":\"notification\",\"notification\":%s}\n", s)
		} else {
			fmt.Printf("%s\n%s\n", track.GaugeTelemetryMessageHeading, track.GaugeTelemetryMessage)
		}
	}
}

func initLogger(n string) {
	if lsp {
		logger.Initialize(machineReadable, logLevel, logger.LSP)
	} else if n == "daemon" {
		logger.Initialize(machineReadable, logLevel, logger.API)
	} else {
		logger.Initialize(machineReadable, logLevel, logger.CLI)
	}
}

func init() {
	GaugeCmd.SetUsageTemplate(`Usage:{{if .Runnable}}
  {{.UseLine}}{{end}}{{if gt (len .Aliases) 0}}

Aliases:
  {{.NameAndAliases}}{{end}}{{if .HasExample}}

Examples:
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}

Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}

Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}

Global Flags:
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}

Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}

Use "{{.CommandPath}} [command] --help" for more information about a command.
Complete manual is available at https://manpage.gauge.org/.{{end}}
`)
	GaugeCmd.PersistentFlags().StringVarP(&logLevel, logLevelName, "l", logLevelDefault, "Set level of logging to debug, info, warning, error or critical")
	GaugeCmd.PersistentFlags().StringVarP(&dir, dirName, "d", dirDefault, "Set the working directory for the current command, accepts a path relative to current directory")
	GaugeCmd.PersistentFlags().BoolVarP(&machineReadable, machineReadableName, "m", machineReadableDefault, "Prints output in JSON format")
	GaugeCmd.Flags().BoolVarP(&gaugeVersion, gaugeVersionName, "v", gaugeVersionDefault, "Print Gauge and plugin versions")
}

func Parse() error {
	InitHelp(GaugeCmd)
	return GaugeCmd.Execute()
}

func InitHelp(c *cobra.Command) {
	c.Flags().BoolP("help", "h", false, "Help for "+c.Name())
	if c.HasSubCommands() {
		for _, sc := range c.Commands() {
			InitHelp(sc)
		}
	}
}

func getSpecsDir(args []string) []string {
	if len(args) > 0 {
		return args
	}
	return util.GetSpecDirs()
}

func setGlobalFlags() {
	util.SetWorkingDir(dir)
}

func initPackageFlags() {
	if parallel {
		simpleConsole = true
		reporter.IsParallel = true
	}
	reporter.SimpleConsoleOutput = simpleConsole
	reporter.Verbose = verbose
	reporter.MachineReadable = machineReadable
	execution.MachineReadable = machineReadable
	execution.ExecuteTags = tags
	execution.SetTableRows(rows)
	validation.TableRows = rows
	execution.NumberOfExecutionStreams = streams
	execution.InParallel = parallel
	execution.Strategy = strategy
	filter.ExecuteTags = tags
	order.Sorted = sort
	filter.Distribute = group
	filter.NumberOfExecutionStreams = streams
	reporter.NumberOfExecutionStreams = streams
	validation.HideSuggestion = hideSuggestion
	if group != -1 {
		execution.Strategy = execution.Eager
	}
	filter.ScenariosName = scenarios
}

var exit = func(err error, additionalText string) {
	if err != nil {
		logger.Errorf(true, err.Error())
	}
	if additionalText != "" {
		logger.Infof(true, additionalText)
	}
	os.Exit(0)
}
<|endoftext|>


<commit_before>package forward

import (
	"crypto/tls"
	"sort"
	"time"

	"github.com/miekg/dns"
)

// a persistConn hold the dns.Conn and the last used time.
type persistConn struct {
	c    *dns.Conn
	used time.Time
}

// Transport hold the persistent cache.
type Transport struct {
	avgDialTime int64                          // kind of average time of dial time
	conns       [typeTotalCount][]*persistConn // Buckets for udp, tcp and tcp-tls.
	expire      time.Duration                  // After this duration a connection is expired.
	addr        string
	tlsConfig   *tls.Config

	dial  chan string
	yield chan *persistConn
	ret   chan *persistConn
	stop  chan bool
}

func newTransport(addr string) *Transport {
	t := &Transport{
		avgDialTime: int64(maxDialTimeout / 2),
		conns:       [typeTotalCount][]*persistConn{},
		expire:      defaultExpire,
		addr:        addr,
		dial:        make(chan string),
		yield:       make(chan *persistConn),
		ret:         make(chan *persistConn),
		stop:        make(chan bool),
	}
	return t
}

// connManagers manages the persistent connection cache for UDP and TCP.
func (t *Transport) connManager() {
	ticker := time.NewTicker(defaultExpire)
Wait:
	for {
		select {
		case proto := <-t.dial:
			transtype := stringToTransportType(proto)
			// take the last used conn - complexity O(1)
			if stack := t.conns[transtype]; len(stack) > 0 {
				pc := stack[len(stack)-1]
				if time.Since(pc.used) < t.expire {
					// Found one, remove from pool and return this conn.
					t.conns[transtype] = stack[:len(stack)-1]
					t.ret <- pc
					continue Wait
				}
				// clear entire cache if the last conn is expired
				t.conns[transtype] = nil
				// now, the connections being passed to closeConns() are not reachable from
				// transport methods anymore. So, it's safe to close them in a separate goroutine
				go closeConns(stack)
			}
			t.ret <- nil

		case pc := <-t.yield:
			transtype := t.transportTypeFromConn(pc)
			t.conns[transtype] = append(t.conns[transtype], pc)

		case <-ticker.C:
			t.cleanup(false)

		case <-t.stop:
			t.cleanup(true)
			close(t.ret)
			return
		}
	}
}

// closeConns closes connections.
func closeConns(conns []*persistConn) {
	for _, pc := range conns {
		pc.c.Close()
	}
}

// cleanup removes connections from cache.
func (t *Transport) cleanup(all bool) {
	staleTime := time.Now().Add(-t.expire)
	for transtype, stack := range t.conns {
		if len(stack) == 0 {
			continue
		}
		if all {
			t.conns[transtype] = nil
			// now, the connections being passed to closeConns() are not reachable from
			// transport methods anymore. So, it's safe to close them in a separate goroutine
			go closeConns(stack)
			continue
		}
		if stack[0].used.After(staleTime) {
			continue
		}

		// connections in stack are sorted by "used"
		good := sort.Search(len(stack), func(i int) bool {
			return stack[i].used.After(staleTime)
		})
		t.conns[transtype] = stack[good:]
		// now, the connections being passed to closeConns() are not reachable from
		// transport methods anymore. So, it's safe to close them in a separate goroutine
		go closeConns(stack[:good])
	}
}

// It is hard to pin a value to this, the import thing is to no block forever, losing at cached connection is not terrible.
const yieldTimeout = 25 * time.Millisecond

// Yield returns the connection to transport for reuse.
func (t *Transport) Yield(pc *persistConn) {
	pc.used = time.Now() // update used time

	// Make this non-blocking, because in the case of a very busy forwarder we will *block* on this yield. This
	// blocks the outer go-routine and stuff will just pile up.  We timeout when the send fails to as returning
	// these connection is an optimization anyway.
	select {
	case t.yield <- pc:
		return
	case <-time.After(yieldTimeout):
		return
	}
}

// Start starts the transport's connection manager.
func (t *Transport) Start() { go t.connManager() }

// Stop stops the transport's connection manager.
func (t *Transport) Stop() { close(t.stop) }

// SetExpire sets the connection expire time in transport.
func (t *Transport) SetExpire(expire time.Duration) { t.expire = expire }

// SetTLSConfig sets the TLS config in transport.
func (t *Transport) SetTLSConfig(cfg *tls.Config) { t.tlsConfig = cfg }

const (
	defaultExpire  = 10 * time.Second
	minDialTimeout = 1 * time.Second
	maxDialTimeout = 30 * time.Second
)

// Make a var for minimizing this value in tests.
var (
	// Some resolves might take quite a while, usually (cached) responses are fast. Set to 2s to give us some time to retry a different upstream.
	readTimeout = 2 * time.Second
)
<commit_msg>plugin/forward: fix ticker leak in golang (#5689)<commit_after>package forward

import (
	"crypto/tls"
	"sort"
	"time"

	"github.com/miekg/dns"
)

// a persistConn hold the dns.Conn and the last used time.
type persistConn struct {
	c    *dns.Conn
	used time.Time
}

// Transport hold the persistent cache.
type Transport struct {
	avgDialTime int64                          // kind of average time of dial time
	conns       [typeTotalCount][]*persistConn // Buckets for udp, tcp and tcp-tls.
	expire      time.Duration                  // After this duration a connection is expired.
	addr        string
	tlsConfig   *tls.Config

	dial  chan string
	yield chan *persistConn
	ret   chan *persistConn
	stop  chan bool
}

func newTransport(addr string) *Transport {
	t := &Transport{
		avgDialTime: int64(maxDialTimeout / 2),
		conns:       [typeTotalCount][]*persistConn{},
		expire:      defaultExpire,
		addr:        addr,
		dial:        make(chan string),
		yield:       make(chan *persistConn),
		ret:         make(chan *persistConn),
		stop:        make(chan bool),
	}
	return t
}

// connManagers manages the persistent connection cache for UDP and TCP.
func (t *Transport) connManager() {
	ticker := time.NewTicker(defaultExpire)
	defer ticker.Stop()
Wait:
	for {
		select {
		case proto := <-t.dial:
			transtype := stringToTransportType(proto)
			// take the last used conn - complexity O(1)
			if stack := t.conns[transtype]; len(stack) > 0 {
				pc := stack[len(stack)-1]
				if time.Since(pc.used) < t.expire {
					// Found one, remove from pool and return this conn.
					t.conns[transtype] = stack[:len(stack)-1]
					t.ret <- pc
					continue Wait
				}
				// clear entire cache if the last conn is expired
				t.conns[transtype] = nil
				// now, the connections being passed to closeConns() are not reachable from
				// transport methods anymore. So, it's safe to close them in a separate goroutine
				go closeConns(stack)
			}
			t.ret <- nil

		case pc := <-t.yield:
			transtype := t.transportTypeFromConn(pc)
			t.conns[transtype] = append(t.conns[transtype], pc)

		case <-ticker.C:
			t.cleanup(false)

		case <-t.stop:
			t.cleanup(true)
			close(t.ret)
			return
		}
	}
}

// closeConns closes connections.
func closeConns(conns []*persistConn) {
	for _, pc := range conns {
		pc.c.Close()
	}
}

// cleanup removes connections from cache.
func (t *Transport) cleanup(all bool) {
	staleTime := time.Now().Add(-t.expire)
	for transtype, stack := range t.conns {
		if len(stack) == 0 {
			continue
		}
		if all {
			t.conns[transtype] = nil
			// now, the connections being passed to closeConns() are not reachable from
			// transport methods anymore. So, it's safe to close them in a separate goroutine
			go closeConns(stack)
			continue
		}
		if stack[0].used.After(staleTime) {
			continue
		}

		// connections in stack are sorted by "used"
		good := sort.Search(len(stack), func(i int) bool {
			return stack[i].used.After(staleTime)
		})
		t.conns[transtype] = stack[good:]
		// now, the connections being passed to closeConns() are not reachable from
		// transport methods anymore. So, it's safe to close them in a separate goroutine
		go closeConns(stack[:good])
	}
}

// It is hard to pin a value to this, the import thing is to no block forever, losing at cached connection is not terrible.
const yieldTimeout = 25 * time.Millisecond

// Yield returns the connection to transport for reuse.
func (t *Transport) Yield(pc *persistConn) {
	pc.used = time.Now() // update used time

	// Make this non-blocking, because in the case of a very busy forwarder we will *block* on this yield. This
	// blocks the outer go-routine and stuff will just pile up.  We timeout when the send fails to as returning
	// these connection is an optimization anyway.
	select {
	case t.yield <- pc:
		return
	case <-time.After(yieldTimeout):
		return
	}
}

// Start starts the transport's connection manager.
func (t *Transport) Start() { go t.connManager() }

// Stop stops the transport's connection manager.
func (t *Transport) Stop() { close(t.stop) }

// SetExpire sets the connection expire time in transport.
func (t *Transport) SetExpire(expire time.Duration) { t.expire = expire }

// SetTLSConfig sets the TLS config in transport.
func (t *Transport) SetTLSConfig(cfg *tls.Config) { t.tlsConfig = cfg }

const (
	defaultExpire  = 10 * time.Second
	minDialTimeout = 1 * time.Second
	maxDialTimeout = 30 * time.Second
)

// Make a var for minimizing this value in tests.
var (
	// Some resolves might take quite a while, usually (cached) responses are fast. Set to 2s to give us some time to retry a different upstream.
	readTimeout = 2 * time.Second
)
<|endoftext|>


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

package cmd

import (
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	Aws "github.com/cm-igarashi-ryosuke/lazy-awslogs/lib/aws"
	Config "github.com/cm-igarashi-ryosuke/lazy-awslogs/lib/config"
	"github.com/spf13/cobra"

	"github.com/cm-igarashi-ryosuke/lazy-awslogs/lib/flags"
)

type GetFlags struct {
	Profile   string
	Region    string
	Log       flags.CWLogIdentifyFlags
	Pattern   string
	TimeRange flags.CWLogTimeRange
	Watch     bool
}

const watchInterval = 5 // seconds

// Create FilterLogEventsInput by GetFlags
func (this *GetFlags) GetCloudWatchLogsFilterLogEventsParam(nextToken *string) cloudwatchlogs.FilterLogEventsInput {
	startTime := this.TimeRange.StartTimeMilliseconds()
	endTime := this.TimeRange.EndTimeMilliseconds()
	pattern := fmt.Sprintf("\"%s\"", this.Pattern)
	input := cloudwatchlogs.FilterLogEventsInput{
		LogGroupName:   &this.Log.Group,
		LogStreamNames: []*string{&this.Log.Stream},
		FilterPattern:  &pattern,
	}
	if startTime != 0 {
		input.StartTime = &startTime
	}
	if endTime != 0 {
		input.EndTime = &endTime
	}
	input.NextToken = nextToken
	return input
}

// Create GetLogEventsInput by GetFlags
func (this *GetFlags) GetCloudWatchLogsGetLogEventsParam(nextToken *string) cloudwatchlogs.GetLogEventsInput {
	startTime := this.TimeRange.StartTimeMilliseconds()
	endTime := this.TimeRange.EndTimeMilliseconds()
	input := cloudwatchlogs.GetLogEventsInput{
		LogGroupName:  &this.Log.Group,
		LogStreamName: &this.Log.Stream,
	}
	if startTime != 0 {
		input.StartTime = &startTime
	}
	if endTime != 0 {
		input.EndTime = &endTime
	}
	input.NextToken = nextToken
	return input
}

// Print any events to stdout
func printEvents(any interface{}) {
	if events, ok := any.([]*cloudwatchlogs.FilteredLogEvent); ok {
		for _, event := range events {
			fmt.Print("[" + time.Unix(*event.Timestamp / 1000, 0).String() + "] ")
			fmt.Println(strings.TrimRight(*event.Message, "\n"))
		}
	} else if events, ok := any.([]*cloudwatchlogs.OutputLogEvent); ok {
		for _, event := range events {
			fmt.Print("[" + time.Unix(*event.Timestamp / 1000, 0).String() + "] ")
			fmt.Println(strings.TrimRight(*event.Message, "\n"))
		}
	}
}

var _getFlags = &GetFlags{}

// getCmd represents the get command
var getCmd = &cobra.Command{
	Use:   "get",
	Short: "Lists log events from the specified log stream.",
	Long: `Lists log events from the specified log stream. You can list all the log events or filter using a time range.

By default, this operation returns as many log events as can fit in a response  size of 1MB (up to 10,000 log events).
If the results include tokens, there are more log events available.
You can get additional log events by specifying one of the tokens in a subsequent call.`,
	Run:    getRun,
	PreRun: preRun,
}

func preRun(cmd *cobra.Command, args []string) {
	if rootFlag.profile == "" {
		fmt.Printf("%v\n\n", fmt.Errorf("Error: Profile is not specified."))
		cmd.Usage()
		os.Exit(1)
	}

	config := Config.Load()
	env := config.CurrentEnvironment()

	if _getFlags.Log.Group == "" && env.DefaultGroup != "" {
		_getFlags.Log.Group = env.DefaultGroup
	}
	if _getFlags.Log.Group == "" {
		fmt.Printf("%v\n\n", fmt.Errorf("Error: Group is not specified."))
		cmd.Usage()
		os.Exit(1)
	}

	if _getFlags.Log.Stream == "" && env.DefaultStream != "" {
		_getFlags.Log.Stream = env.DefaultStream
	}
	if _getFlags.Log.Stream == "" {
		fmt.Printf("%v\n\n", fmt.Errorf("Error: Stream is not specified."))
		cmd.Usage()
		os.Exit(1)
	}

	if rootFlag.verbose {
		fmt.Printf("Local flags: %#v\n", _getFlags)
	}
}

func getRun(cmd *cobra.Command, args []string) {
	// fmt.Printf(">>>>> _getFlags: %#v\n", _getFlags)

	cwlogsClient := Aws.NewCloudWatchLogsClient(rootFlag.GetCloudWatchLogsClientParams())

	var nextToken *string

Start:
	var err error
	if _getFlags.Pattern != "" {
		options := _getFlags.GetCloudWatchLogsFilterLogEventsParam(nextToken)
		if rootFlag.verbose {
			fmt.Printf("FilterLogEvents: options=%v\n", options)
		}
		err = cwlogsClient.FilterLogEvents(&options, func(out *cloudwatchlogs.FilterLogEventsOutput) {
			printEvents(out.Events)
			nextToken = out.NextToken
		})
	} else {
		options := _getFlags.GetCloudWatchLogsGetLogEventsParam(nextToken)
		if rootFlag.verbose {
			fmt.Printf("GetLogEvents: options=%v\n", options)
		}
		err = cwlogsClient.GetLogEvents(&options, func(out *cloudwatchlogs.GetLogEventsOutput) {
			printEvents(out.Events)
			nextToken = out.NextForwardToken
		})
	}
	if _getFlags.Watch && nextToken != nil{
		if rootFlag.verbose {
			fmt.Printf("`watch` enabled. now sleep(%d) Zzz...\n", watchInterval)
		}
		time.Sleep(watchInterval * time.Second);
		if rootFlag.verbose {
			fmt.Println("`watch` enabled. wakeup now!")
		}
		goto Start
	}
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(2)
	}
}

func init() {
	RootCmd.AddCommand(getCmd)

	localFlags := getCmd.Flags()
	_getFlags.Log.Load(localFlags)
	_getFlags.TimeRange.Load(localFlags)
	localFlags.StringVarP(&_getFlags.Pattern, "filter-pattern", "f", "", "The filter pattern to use. If not provided, all the events are matched.")
	localFlags.BoolVarP(&_getFlags.Watch, "watch", "w", false, "Do not stop when end of log is reached, but rather to wait for additional data to be appended to the input.")

	// There is no guarantee that _getFlags will have a value here
	// I do not know until getRun function
}
<commit_msg>add output stream name if FilteredLogEvent<commit_after>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	Aws "github.com/cm-igarashi-ryosuke/lazy-awslogs/lib/aws"
	Config "github.com/cm-igarashi-ryosuke/lazy-awslogs/lib/config"
	"github.com/spf13/cobra"

	"github.com/cm-igarashi-ryosuke/lazy-awslogs/lib/flags"
)

type GetFlags struct {
	Profile   string
	Region    string
	Log       flags.CWLogIdentifyFlags
	Pattern   string
	TimeRange flags.CWLogTimeRange
	Watch     bool
}

const watchInterval = 5 // seconds

// Create FilterLogEventsInput by GetFlags
func (this *GetFlags) GetCloudWatchLogsFilterLogEventsParam(nextToken *string) cloudwatchlogs.FilterLogEventsInput {
	startTime := this.TimeRange.StartTimeMilliseconds()
	endTime := this.TimeRange.EndTimeMilliseconds()
	pattern := fmt.Sprintf("\"%s\"", this.Pattern)
	input := cloudwatchlogs.FilterLogEventsInput{
		LogGroupName:   &this.Log.Group,
		LogStreamNames: []*string{&this.Log.Stream},
		FilterPattern:  &pattern,
	}
	if startTime != 0 {
		input.StartTime = &startTime
	}
	if endTime != 0 {
		input.EndTime = &endTime
	}
	input.NextToken = nextToken
	return input
}

// Create GetLogEventsInput by GetFlags
func (this *GetFlags) GetCloudWatchLogsGetLogEventsParam(nextToken *string) cloudwatchlogs.GetLogEventsInput {
	startTime := this.TimeRange.StartTimeMilliseconds()
	endTime := this.TimeRange.EndTimeMilliseconds()
	input := cloudwatchlogs.GetLogEventsInput{
		LogGroupName:  &this.Log.Group,
		LogStreamName: &this.Log.Stream,
	}
	if startTime != 0 {
		input.StartTime = &startTime
	}
	if endTime != 0 {
		input.EndTime = &endTime
	}
	input.NextToken = nextToken
	return input
}

// Print any events to stdout
func printEvents(any interface{}) {
	if events, ok := any.([]*cloudwatchlogs.FilteredLogEvent); ok {
		for _, event := range events {
			fmt.Print("[" + time.Unix(*event.Timestamp / 1000, 0).String() + "] ")
			fmt.Print("[" + *event.LogStreamName + "] ")
			fmt.Println(strings.TrimRight(*event.Message, "\n"))
		}
	} else if events, ok := any.([]*cloudwatchlogs.OutputLogEvent); ok {
		for _, event := range events {
			fmt.Print("[" + time.Unix(*event.Timestamp / 1000, 0).String() + "] ")
			fmt.Println(strings.TrimRight(*event.Message, "\n"))
		}
	}
}

var _getFlags = &GetFlags{}

// getCmd represents the get command
var getCmd = &cobra.Command{
	Use:   "get",
	Short: "Lists log events from the specified log stream.",
	Long: `Lists log events from the specified log stream. You can list all the log events or filter using a time range.

By default, this operation returns as many log events as can fit in a response  size of 1MB (up to 10,000 log events).
If the results include tokens, there are more log events available.
You can get additional log events by specifying one of the tokens in a subsequent call.`,
	Run:    getRun,
	PreRun: preRun,
}

func preRun(cmd *cobra.Command, args []string) {
	if rootFlag.profile == "" {
		fmt.Printf("%v\n\n", fmt.Errorf("Error: Profile is not specified."))
		cmd.Usage()
		os.Exit(1)
	}

	config := Config.Load()
	env := config.CurrentEnvironment()

	if _getFlags.Log.Group == "" && env.DefaultGroup != "" {
		_getFlags.Log.Group = env.DefaultGroup
	}
	if _getFlags.Log.Group == "" {
		fmt.Printf("%v\n\n", fmt.Errorf("Error: Group is not specified."))
		cmd.Usage()
		os.Exit(1)
	}

	if _getFlags.Log.Stream == "" && env.DefaultStream != "" {
		_getFlags.Log.Stream = env.DefaultStream
	}
	if _getFlags.Log.Stream == "" {
		fmt.Printf("%v\n\n", fmt.Errorf("Error: Stream is not specified."))
		cmd.Usage()
		os.Exit(1)
	}

	if rootFlag.verbose {
		fmt.Printf("Local flags: %#v\n", _getFlags)
	}
}

func getRun(cmd *cobra.Command, args []string) {
	// fmt.Printf(">>>>> _getFlags: %#v\n", _getFlags)

	cwlogsClient := Aws.NewCloudWatchLogsClient(rootFlag.GetCloudWatchLogsClientParams())

	var nextToken *string

Start:
	var err error
	if _getFlags.Pattern != "" {
		options := _getFlags.GetCloudWatchLogsFilterLogEventsParam(nextToken)
		if rootFlag.verbose {
			fmt.Printf("FilterLogEvents: options=%v\n", options)
		}
		err = cwlogsClient.FilterLogEvents(&options, func(out *cloudwatchlogs.FilterLogEventsOutput) {
			printEvents(out.Events)
			nextToken = out.NextToken
		})
	} else {
		options := _getFlags.GetCloudWatchLogsGetLogEventsParam(nextToken)
		if rootFlag.verbose {
			fmt.Printf("GetLogEvents: options=%v\n", options)
		}
		err = cwlogsClient.GetLogEvents(&options, func(out *cloudwatchlogs.GetLogEventsOutput) {
			printEvents(out.Events)
			nextToken = out.NextForwardToken
		})
	}
	if _getFlags.Watch && nextToken != nil{
		if rootFlag.verbose {
			fmt.Printf("`watch` enabled. now sleep(%d) Zzz...\n", watchInterval)
		}
		time.Sleep(watchInterval * time.Second);
		if rootFlag.verbose {
			fmt.Println("`watch` enabled. wakeup now!")
		}
		goto Start
	}
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(2)
	}
}

func init() {
	RootCmd.AddCommand(getCmd)

	localFlags := getCmd.Flags()
	_getFlags.Log.Load(localFlags)
	_getFlags.TimeRange.Load(localFlags)
	localFlags.StringVarP(&_getFlags.Pattern, "filter-pattern", "f", "", "The filter pattern to use. If not provided, all the events are matched.")
	localFlags.BoolVarP(&_getFlags.Watch, "watch", "w", false, "Do not stop when end of log is reached, but rather to wait for additional data to be appended to the input.")

	// There is no guarantee that _getFlags will have a value here
	// I do not know until getRun function
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/robfig/revel"
	"github.com/robfig/revel/harness"
	"strconv"
)

var cmdRun = &Command{
	UsageLine: "run [import path] [run mode] [port]",
	Short:     "run a Revel application",
	Long: `
Run the Revel web application named by the given import path.

For example, to run the chat room sample application:

    revel run github.com/robfig/revel/samples/chat dev

The run mode is used to select which set of app.conf configuration should
apply and may be used to determine logic in the application itself.

Run mode defaults to "dev".

You can set a port as an optional third parameter.  For example:

    revel run github.com/robfig/revel/samples/chat prod 8080`,
}

func init() {
	cmdRun.Run = runApp
}

func runApp(args []string) {
	if len(args) == 0 {
		errorf("No import path given.\nRun 'revel help run' for usage.\n")
	}

	// Determine the run mode.
	mode := "dev"
	if len(args) >= 2 {
		mode = args[1]
	}

	// Determine the override port, if any.
	port := 0
	if len(args) == 3 {
		var err error
		if port, err = strconv.Atoi(args[2]); err != nil {
			errorf("Failed to parse port as integer: %s", args[2])
		}
	}

	// Find and parse app.conf
	rev.Init(mode, args[0], "")

	rev.INFO.Printf("Running %s (%s) in %s mode\n", rev.AppName, rev.ImportPath, mode)
	rev.TRACE.Println("Base path:", rev.BasePath)

	// If the app is run in "watched" mode, use the harness to run it.
	if rev.Config.BoolDefault("watch", true) && rev.Config.BoolDefault("watch.code", true) {
		rev.HttpPort = port
		harness.NewHarness().Run() // Never returns.
	}

	// Else, just build and run the app.
	app, err := harness.Build()
	if err != nil {
		errorf("Failed to build app: %s", err)
	}
	app.Port = port
	app.Cmd().Run()
}
<commit_msg>Parse config before checking for port override.<commit_after>package main

import (
	"github.com/robfig/revel"
	"github.com/robfig/revel/harness"
	"strconv"
)

var cmdRun = &Command{
	UsageLine: "run [import path] [run mode] [port]",
	Short:     "run a Revel application",
	Long: `
Run the Revel web application named by the given import path.

For example, to run the chat room sample application:

    revel run github.com/robfig/revel/samples/chat dev

The run mode is used to select which set of app.conf configuration should
apply and may be used to determine logic in the application itself.

Run mode defaults to "dev".

You can set a port as an optional third parameter.  For example:

    revel run github.com/robfig/revel/samples/chat prod 8080`,
}

func init() {
	cmdRun.Run = runApp
}

func runApp(args []string) {
	if len(args) == 0 {
		errorf("No import path given.\nRun 'revel help run' for usage.\n")
	}

	// Determine the run mode.
	mode := "dev"
	if len(args) >= 2 {
		mode = args[1]
	}

	// Find and parse app.conf
	rev.Init(mode, args[0], "")

	// Determine the override port, if any.
	port := rev.HttpPort
	if len(args) == 3 {
		var err error
		if port, err = strconv.Atoi(args[2]); err != nil {
			errorf("Failed to parse port as integer: %s", args[2])
		}
	}

	rev.INFO.Printf("Running %s (%s) in %s mode\n", rev.AppName, rev.ImportPath, mode)
	rev.TRACE.Println("Base path:", rev.BasePath)

	// If the app is run in "watched" mode, use the harness to run it.
	if rev.Config.BoolDefault("watch", true) && rev.Config.BoolDefault("watch.code", true) {
		rev.HttpPort = port
		harness.NewHarness().Run() // Never returns.
	}

	// Else, just build and run the app.
	app, err := harness.Build()
	if err != nil {
		errorf("Failed to build app: %s", err)
	}
	app.Port = port
	app.Cmd().Run()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
)

func lbpkr_make_cmd_rpm() *commander.Command {
	cmd := &commander.Command{
		Run:       lbpkr_run_cmd_rpm,
		UsageLine: "rpm [options] -- <rpm-command-args>",
		Short:     "rpm passes through command-args to the RPM binary",
		Long: `
rpm passes through command-args to the RPM binary.

ex:
 $ lbpkr rpm -- --version
`,
		Flag: *flag.NewFlagSet("lbpkr-rpm", flag.ExitOnError),
	}
	add_default_options(cmd)
	return cmd
}

func lbpkr_run_cmd_rpm(cmd *commander.Command, args []string) error {
	var err error

	siteroot := cmd.Flag.Lookup("siteroot").Value.Get().(string)
	debug := cmd.Flag.Lookup("v").Value.Get().(bool)

	switch len(args) {
	case 0:
		cmd.Usage()
		return fmt.Errorf("lbpkr: invalid number of arguments. expected at least one argument. got=%d (%v)",
			len(args),
			args,
		)
	}

	cfg := NewConfig(siteroot)
	ctx, err := New(cfg, debug)
	if err != nil {
		return err
	}
	defer ctx.Close()

	err = ctx.Rpm(args...)
	return err
}
<commit_msg>cmd-rpm: short-doc<commit_after>package main

import (
	"fmt"

	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
)

func lbpkr_make_cmd_rpm() *commander.Command {
	cmd := &commander.Command{
		Run:       lbpkr_run_cmd_rpm,
		UsageLine: "rpm [options] -- <rpm-command-args>",
		Short:     "pass through command-args to the RPM binary",
		Long: `
rpm passes through command-args to the RPM binary.

ex:
 $ lbpkr rpm -- --version
`,
		Flag: *flag.NewFlagSet("lbpkr-rpm", flag.ExitOnError),
	}
	add_default_options(cmd)
	return cmd
}

func lbpkr_run_cmd_rpm(cmd *commander.Command, args []string) error {
	var err error

	siteroot := cmd.Flag.Lookup("siteroot").Value.Get().(string)
	debug := cmd.Flag.Lookup("v").Value.Get().(bool)

	switch len(args) {
	case 0:
		cmd.Usage()
		return fmt.Errorf("lbpkr: invalid number of arguments. expected at least one argument. got=%d (%v)",
			len(args),
			args,
		)
	}

	cfg := NewConfig(siteroot)
	ctx, err := New(cfg, debug)
	if err != nil {
		return err
	}
	defer ctx.Close()

	err = ctx.Rpm(args...)
	return err
}
<|endoftext|>