<commit_before>package main

import (
	"fmt"
	"github.com/asdine/storm"
	"github.com/filebrowser/filebrowser"
	"github.com/filebrowser/filebrowser/bolt"
	h "github.com/filebrowser/filebrowser/http"
	"github.com/filebrowser/filebrowser/staticgen"
	"github.com/hacdias/fileutils"
	flag "github.com/spf13/pflag"
	"github.com/spf13/viper"
	"gopkg.in/natefinch/lumberjack.v2"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings")

var (
	addr            string
	config          string
	database        string
	scope           string
	commands        string
	logfile         string
	staticg         string
	locale          string
	baseurl         string
	prefixurl       string
	viewMode        string
	recaptchakey    string
	recaptchasecret string
	port            int
	auth            struct {
		method      string
		loginHeader string
	}
	noAuth         bool
	allowCommands  bool
	allowEdit      bool
	allowNew       bool
	allowPublish   bool
	showVer        bool
	alterRecaptcha bool
)

func init() {
	flag.StringVarP(&config, "config", "c", "", "Configuration file")
	flag.IntVarP(&port, "port", "p", 0, "HTTP Port (default is random)")
	flag.StringVarP(&addr, "address", "a", "", "Address to listen to (default is all of them)")
	flag.StringVarP(&database, "database", "d", "./filebrowser.db", "Database file")
	flag.StringVarP(&logfile, "log", "l", "stdout", "Errors logger; can use 'stdout', 'stderr' or file")
	flag.StringVarP(&scope, "scope", "s", ".", "Default scope option for new users")
	flag.StringVarP(&baseurl, "baseurl", "b", "", "Base URL")
	flag.StringVar(&commands, "commands", "git svn hg", "Default commands option for new users")
	flag.StringVar(&prefixurl, "prefixurl", "", "Prefix URL")
	flag.StringVar(&viewMode, "view-mode", "mosaic", "Default view mode for new users")
	flag.StringVar(&recaptchakey, "recaptcha-key", "", "ReCaptcha site key")
	flag.StringVar(&recaptchasecret, "recaptcha-secret", "", "ReCaptcha secret")
	flag.BoolVar(&allowCommands, "allow-commands", true, "Default allow commands option for new users")
	flag.BoolVar(&allowEdit, "allow-edit", true, "Default allow edit option for new users")
	flag.BoolVar(&allowPublish, "allow-publish", true, "Default allow publish option for new users")
	flag.StringVar(&auth.method, "auth.method", "default", "Switch between 'none', 'default' and 'proxy' authentication.")
	flag.StringVar(&auth.loginHeader, "auth.loginHeader", "X-Forwarded-User", "The header name used for proxy authentication.")
	flag.BoolVar(&allowNew, "allow-new", true, "Default allow new option for new users")
	flag.BoolVar(&noAuth, "no-auth", false, "Disables authentication")
	flag.BoolVar(&alterRecaptcha, "alternative-recaptcha", false, "Use recaptcha.net for serving and handling, useful in China")
	flag.StringVar(&locale, "locale", "", "Default locale for new users, set it empty to enable auto detect from browser")
	flag.StringVar(&staticg, "staticgen", "", "Static Generator you want to enable")
	flag.BoolVarP(&showVer, "version", "v", false, "Show version")
}

func setupViper() {
	viper.SetDefault("Address", "")
	viper.SetDefault("Port", "0")
	viper.SetDefault("Database", "./filebrowser.db")
	viper.SetDefault("Scope", ".")
	viper.SetDefault("Logger", "stdout")
	viper.SetDefault("Commands", []string{"git", "svn", "hg"})
	viper.SetDefault("AllowCommmands", true)
	viper.SetDefault("AllowEdit", true)
	viper.SetDefault("AllowNew", true)
	viper.SetDefault("AllowPublish", true)
	viper.SetDefault("StaticGen", "")
	viper.SetDefault("Locale", "")
	viper.SetDefault("AuthMethod", "default")
	viper.SetDefault("LoginHeader", "X-Fowarded-User")
	viper.SetDefault("NoAuth", false)
	viper.SetDefault("BaseURL", "")
	viper.SetDefault("PrefixURL", "")
	viper.SetDefault("ViewMode", filebrowser.MosaicViewMode)
	viper.SetDefault("AlternativeRecaptcha", false)
	viper.SetDefault("ReCaptchaKey", "")
	viper.SetDefault("ReCaptchaSecret", "")

	viper.BindPFlag("Port", flag.Lookup("port"))
	viper.BindPFlag("Address", flag.Lookup("address"))
	viper.BindPFlag("Database", flag.Lookup("database"))
	viper.BindPFlag("Scope", flag.Lookup("scope"))
	viper.BindPFlag("Logger", flag.Lookup("log"))
	viper.BindPFlag("Commands", flag.Lookup("commands"))
	viper.BindPFlag("AllowCommands", flag.Lookup("allow-commands"))
	viper.BindPFlag("AllowEdit", flag.Lookup("allow-edit"))
	viper.BindPFlag("AllowNew", flag.Lookup("allow-new"))
	viper.BindPFlag("AllowPublish", flag.Lookup("allow-publish"))
	viper.BindPFlag("Locale", flag.Lookup("locale"))
	viper.BindPFlag("StaticGen", flag.Lookup("staticgen"))
	viper.BindPFlag("AuthMethod", flag.Lookup("auth.method"))
	viper.BindPFlag("LoginHeader", flag.Lookup("auth.loginHeader"))
	viper.BindPFlag("NoAuth", flag.Lookup("no-auth"))
	viper.BindPFlag("BaseURL", flag.Lookup("baseurl"))
	viper.BindPFlag("PrefixURL", flag.Lookup("prefixurl"))
	viper.BindPFlag("ViewMode", flag.Lookup("view-mode"))
	viper.BindPFlag("AlternativeRecaptcha", flag.Lookup("alternative-recaptcha"))
	viper.BindPFlag("ReCaptchaKey", flag.Lookup("recaptcha-key"))
	viper.BindPFlag("ReCaptchaSecret", flag.Lookup("recaptcha-secret"))

	viper.SetConfigName("filebrowser")
	viper.AddConfigPath(".")
}

func printVersion() {
	fmt.Println("filebrowser version", filebrowser.Version)
	os.Exit(0)
}

func main() {
	setupViper()
	flag.Parse()

	if showVer {
		printVersion()
	}

	// Add a configuration file if set.
	if config != "" {
		ext := filepath.Ext(config)
		dir := filepath.Dir(config)
		config = strings.TrimSuffix(config, ext)

		if dir != "" {
			viper.AddConfigPath(dir)
			config = strings.TrimPrefix(config, dir)
		}

		viper.SetConfigName(config)
	}

	// Read configuration from a file if exists.
	err := viper.ReadInConfig()
	if err != nil {
		if _, ok := err.(viper.ConfigParseError); ok {
			panic(err)
		}
	}

	// Set up process log before anything bad happens.
	switch viper.GetString("Logger") {
	case "stdout":
		log.SetOutput(os.Stdout)
	case "stderr":
		log.SetOutput(os.Stderr)
	case "":
		log.SetOutput(ioutil.Discard)
	default:
		log.SetOutput(&lumberjack.Logger{
			Filename:   logfile,
			MaxSize:    100,
			MaxAge:     14,
			MaxBackups: 10,
		})
	}

	// Validate the provided config before moving forward
	if viper.GetString("AuthMethod") != "none" && viper.GetString("AuthMethod") != "default" && viper.GetString("AuthMethod") != "proxy" {
		log.Fatal("The property 'auth.method' needs to be set to 'default' or 'proxy'.")
	}

	if viper.GetString("AuthMethod") == "proxy" {
		if viper.GetString("LoginHeader") == "" {
			log.Fatal("The 'loginHeader' needs to be specified when 'proxy' authentication is used.")
		}
		log.Println("[WARN] Filebrowser authentication is configured to 'proxy' authentication. This can cause a huge security issue if the infrastructure is not configured correctly.")
	}

	// Builds the address and a listener.
	laddr := viper.GetString("Address") + ":" + viper.GetString("Port")
	listener, err := net.Listen("tcp", laddr)
	if err != nil {
		log.Fatal(err)
	}

	// Tell the user the port in which is listening.
	fmt.Println("Listening on", listener.Addr().String())

	// Starts the server.
	if err := http.Serve(listener, handler()); err != nil {
		log.Fatal(err)
	}
}

func handler() http.Handler {
	db, err := storm.Open(viper.GetString("Database"))
	if err != nil {
		log.Fatal(err)
	}

	recaptchaHost := "https://www.google.com"
	if viper.GetBool("AlternativeRecaptcha") {
		recaptchaHost = "https://recaptcha.net"
	}

	fm := &filebrowser.FileBrowser{
		AuthMethod:      viper.GetString("AuthMethod"),
		LoginHeader:     viper.GetString("LoginHeader"),
		NoAuth:          viper.GetBool("NoAuth"),
		BaseURL:         viper.GetString("BaseURL"),
		PrefixURL:       viper.GetString("PrefixURL"),
		ReCaptchaHost:   recaptchaHost,
		ReCaptchaKey:    viper.GetString("ReCaptchaKey"),
		ReCaptchaSecret: viper.GetString("ReCaptchaSecret"),
		DefaultUser: &filebrowser.User{
			AllowCommands: viper.GetBool("AllowCommands"),
			AllowEdit:     viper.GetBool("AllowEdit"),
			AllowNew:      viper.GetBool("AllowNew"),
			AllowPublish:  viper.GetBool("AllowPublish"),
			Commands:      viper.GetStringSlice("Commands"),
			Rules:         []*filebrowser.Rule{},
			Locale:        viper.GetString("Locale"),
			CSS:           "",
			Scope:         viper.GetString("Scope"),
			FileSystem:    fileutils.Dir(viper.GetString("Scope")),
			ViewMode:      viper.GetString("ViewMode"),
		},
		Store: &filebrowser.Store{
			Config: bolt.ConfigStore{DB: db},
			Users:  bolt.UsersStore{DB: db},
			Share:  bolt.ShareStore{DB: db},
		},
		NewFS: func(scope string) filebrowser.FileSystem {
			return fileutils.Dir(scope)
		},
	}

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

	switch viper.GetString("StaticGen") {
	case "hugo":
		hugo := &staticgen.Hugo{
			Root:        viper.GetString("Scope"),
			Public:      filepath.Join(viper.GetString("Scope"), "public"),
			Args:        []string{},
			CleanPublic: true,
		}

		if err = fm.Attach(hugo); err != nil {
			log.Fatal(err)
		}
	case "jekyll":
		jekyll := &staticgen.Jekyll{
			Root:        viper.GetString("Scope"),
			Public:      filepath.Join(viper.GetString("Scope"), "_site"),
			Args:        []string{"build"},
			CleanPublic: true,
		}

		if err = fm.Attach(jekyll); err != nil {
			log.Fatal(err)
		}
	}

	return h.Handler(fm)
}
<commit_msg>fix(config): ensure provided config path is used (#508)<commit_after>package main

import (
	"fmt"
	"github.com/asdine/storm"
	"github.com/filebrowser/filebrowser"
	"github.com/filebrowser/filebrowser/bolt"
	h "github.com/filebrowser/filebrowser/http"
	"github.com/filebrowser/filebrowser/staticgen"
	"github.com/hacdias/fileutils"
	flag "github.com/spf13/pflag"
	"github.com/spf13/viper"
	"gopkg.in/natefinch/lumberjack.v2"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings")

var (
	addr            string
	config          string
	database        string
	scope           string
	commands        string
	logfile         string
	staticg         string
	locale          string
	baseurl         string
	prefixurl       string
	viewMode        string
	recaptchakey    string
	recaptchasecret string
	port            int
	auth            struct {
		method      string
		loginHeader string
	}
	noAuth         bool
	allowCommands  bool
	allowEdit      bool
	allowNew       bool
	allowPublish   bool
	showVer        bool
	alterRecaptcha bool
)

func init() {
	flag.StringVarP(&config, "config", "c", "", "Configuration file")
	flag.IntVarP(&port, "port", "p", 0, "HTTP Port (default is random)")
	flag.StringVarP(&addr, "address", "a", "", "Address to listen to (default is all of them)")
	flag.StringVarP(&database, "database", "d", "./filebrowser.db", "Database file")
	flag.StringVarP(&logfile, "log", "l", "stdout", "Errors logger; can use 'stdout', 'stderr' or file")
	flag.StringVarP(&scope, "scope", "s", ".", "Default scope option for new users")
	flag.StringVarP(&baseurl, "baseurl", "b", "", "Base URL")
	flag.StringVar(&commands, "commands", "git svn hg", "Default commands option for new users")
	flag.StringVar(&prefixurl, "prefixurl", "", "Prefix URL")
	flag.StringVar(&viewMode, "view-mode", "mosaic", "Default view mode for new users")
	flag.StringVar(&recaptchakey, "recaptcha-key", "", "ReCaptcha site key")
	flag.StringVar(&recaptchasecret, "recaptcha-secret", "", "ReCaptcha secret")
	flag.BoolVar(&allowCommands, "allow-commands", true, "Default allow commands option for new users")
	flag.BoolVar(&allowEdit, "allow-edit", true, "Default allow edit option for new users")
	flag.BoolVar(&allowPublish, "allow-publish", true, "Default allow publish option for new users")
	flag.StringVar(&auth.method, "auth.method", "default", "Switch between 'none', 'default' and 'proxy' authentication.")
	flag.StringVar(&auth.loginHeader, "auth.loginHeader", "X-Forwarded-User", "The header name used for proxy authentication.")
	flag.BoolVar(&allowNew, "allow-new", true, "Default allow new option for new users")
	flag.BoolVar(&noAuth, "no-auth", false, "Disables authentication")
	flag.BoolVar(&alterRecaptcha, "alternative-recaptcha", false, "Use recaptcha.net for serving and handling, useful in China")
	flag.StringVar(&locale, "locale", "", "Default locale for new users, set it empty to enable auto detect from browser")
	flag.StringVar(&staticg, "staticgen", "", "Static Generator you want to enable")
	flag.BoolVarP(&showVer, "version", "v", false, "Show version")
}

func setupViper() {
	viper.SetDefault("Address", "")
	viper.SetDefault("Port", "0")
	viper.SetDefault("Database", "./filebrowser.db")
	viper.SetDefault("Scope", ".")
	viper.SetDefault("Logger", "stdout")
	viper.SetDefault("Commands", []string{"git", "svn", "hg"})
	viper.SetDefault("AllowCommmands", true)
	viper.SetDefault("AllowEdit", true)
	viper.SetDefault("AllowNew", true)
	viper.SetDefault("AllowPublish", true)
	viper.SetDefault("StaticGen", "")
	viper.SetDefault("Locale", "")
	viper.SetDefault("AuthMethod", "default")
	viper.SetDefault("LoginHeader", "X-Fowarded-User")
	viper.SetDefault("NoAuth", false)
	viper.SetDefault("BaseURL", "")
	viper.SetDefault("PrefixURL", "")
	viper.SetDefault("ViewMode", filebrowser.MosaicViewMode)
	viper.SetDefault("AlternativeRecaptcha", false)
	viper.SetDefault("ReCaptchaKey", "")
	viper.SetDefault("ReCaptchaSecret", "")

	viper.BindPFlag("Port", flag.Lookup("port"))
	viper.BindPFlag("Address", flag.Lookup("address"))
	viper.BindPFlag("Database", flag.Lookup("database"))
	viper.BindPFlag("Scope", flag.Lookup("scope"))
	viper.BindPFlag("Logger", flag.Lookup("log"))
	viper.BindPFlag("Commands", flag.Lookup("commands"))
	viper.BindPFlag("AllowCommands", flag.Lookup("allow-commands"))
	viper.BindPFlag("AllowEdit", flag.Lookup("allow-edit"))
	viper.BindPFlag("AllowNew", flag.Lookup("allow-new"))
	viper.BindPFlag("AllowPublish", flag.Lookup("allow-publish"))
	viper.BindPFlag("Locale", flag.Lookup("locale"))
	viper.BindPFlag("StaticGen", flag.Lookup("staticgen"))
	viper.BindPFlag("AuthMethod", flag.Lookup("auth.method"))
	viper.BindPFlag("LoginHeader", flag.Lookup("auth.loginHeader"))
	viper.BindPFlag("NoAuth", flag.Lookup("no-auth"))
	viper.BindPFlag("BaseURL", flag.Lookup("baseurl"))
	viper.BindPFlag("PrefixURL", flag.Lookup("prefixurl"))
	viper.BindPFlag("ViewMode", flag.Lookup("view-mode"))
	viper.BindPFlag("AlternativeRecaptcha", flag.Lookup("alternative-recaptcha"))
	viper.BindPFlag("ReCaptchaKey", flag.Lookup("recaptcha-key"))
	viper.BindPFlag("ReCaptchaSecret", flag.Lookup("recaptcha-secret"))
}

func printVersion() {
	fmt.Println("filebrowser version", filebrowser.Version)
	os.Exit(0)
}

func initConfig() {
	// Add a configuration file if set.
	if config != "" {
		cfg := strings.TrimSuffix(config, filepath.Ext(config))
		if dir := filepath.Dir(cfg); dir != "" {
			viper.AddConfigPath(dir)
			cfg = strings.TrimPrefix(cfg, dir)
		}
		viper.SetConfigName(cfg)
	} else {
		viper.SetConfigName("filebrowser")
		viper.AddConfigPath(".")
	}

	// Read configuration from a file if exists.
	err := viper.ReadInConfig()
	if err != nil {
		if _, ok := err.(viper.ConfigParseError); ok {
			panic(err)
		}
	}
}

func main() {
	setupViper()
	flag.Parse()

	if showVer {
		printVersion()
	}

	initConfig();

	// Set up process log before anything bad happens.
	switch viper.GetString("Logger") {
	case "stdout":
		log.SetOutput(os.Stdout)
	case "stderr":
		log.SetOutput(os.Stderr)
	case "":
		log.SetOutput(ioutil.Discard)
	default:
		log.SetOutput(&lumberjack.Logger{
			Filename:   logfile,
			MaxSize:    100,
			MaxAge:     14,
			MaxBackups: 10,
		})
	}

	// Validate the provided config before moving forward
	if viper.GetString("AuthMethod") != "none" && viper.GetString("AuthMethod") != "default" && viper.GetString("AuthMethod") != "proxy" {
		log.Fatal("The property 'auth.method' needs to be set to 'default' or 'proxy'.")
	}

	if viper.GetString("AuthMethod") == "proxy" {
		if viper.GetString("LoginHeader") == "" {
			log.Fatal("The 'loginHeader' needs to be specified when 'proxy' authentication is used.")
		}
		log.Println("[WARN] Filebrowser authentication is configured to 'proxy' authentication. This can cause a huge security issue if the infrastructure is not configured correctly.")
	}

	// Builds the address and a listener.
	laddr := viper.GetString("Address") + ":" + viper.GetString("Port")
	listener, err := net.Listen("tcp", laddr)
	if err != nil {
		log.Fatal(err)
	}

	// Tell the user the port in which is listening.
	fmt.Println("Listening on", listener.Addr().String())

	// Starts the server.
	if err := http.Serve(listener, handler()); err != nil {
		log.Fatal(err)
	}
}

func handler() http.Handler {
	db, err := storm.Open(viper.GetString("Database"))
	if err != nil {
		log.Fatal(err)
	}

	recaptchaHost := "https://www.google.com"
	if viper.GetBool("AlternativeRecaptcha") {
		recaptchaHost = "https://recaptcha.net"
	}

	fm := &filebrowser.FileBrowser{
		AuthMethod:      viper.GetString("AuthMethod"),
		LoginHeader:     viper.GetString("LoginHeader"),
		NoAuth:          viper.GetBool("NoAuth"),
		BaseURL:         viper.GetString("BaseURL"),
		PrefixURL:       viper.GetString("PrefixURL"),
		ReCaptchaHost:   recaptchaHost,
		ReCaptchaKey:    viper.GetString("ReCaptchaKey"),
		ReCaptchaSecret: viper.GetString("ReCaptchaSecret"),
		DefaultUser: &filebrowser.User{
			AllowCommands: viper.GetBool("AllowCommands"),
			AllowEdit:     viper.GetBool("AllowEdit"),
			AllowNew:      viper.GetBool("AllowNew"),
			AllowPublish:  viper.GetBool("AllowPublish"),
			Commands:      viper.GetStringSlice("Commands"),
			Rules:         []*filebrowser.Rule{},
			Locale:        viper.GetString("Locale"),
			CSS:           "",
			Scope:         viper.GetString("Scope"),
			FileSystem:    fileutils.Dir(viper.GetString("Scope")),
			ViewMode:      viper.GetString("ViewMode"),
		},
		Store: &filebrowser.Store{
			Config: bolt.ConfigStore{DB: db},
			Users:  bolt.UsersStore{DB: db},
			Share:  bolt.ShareStore{DB: db},
		},
		NewFS: func(scope string) filebrowser.FileSystem {
			return fileutils.Dir(scope)
		},
	}

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

	switch viper.GetString("StaticGen") {
	case "hugo":
		hugo := &staticgen.Hugo{
			Root:        viper.GetString("Scope"),
			Public:      filepath.Join(viper.GetString("Scope"), "public"),
			Args:        []string{},
			CleanPublic: true,
		}

		if err = fm.Attach(hugo); err != nil {
			log.Fatal(err)
		}
	case "jekyll":
		jekyll := &staticgen.Jekyll{
			Root:        viper.GetString("Scope"),
			Public:      filepath.Join(viper.GetString("Scope"), "_site"),
			Args:        []string{"build"},
			CleanPublic: true,
		}

		if err = fm.Attach(jekyll); err != nil {
			log.Fatal(err)
		}
	}

	return h.Handler(fm)
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"net"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/funkygao/Go-Redis"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/gofmt"
	log "github.com/funkygao/log4go"
	"github.com/funkygao/termui"
	"github.com/pmylund/sortutil"
	"github.com/ryanuber/columnize"
)

type Redis struct {
	Ui  cli.Ui
	Cmd string

	mu       sync.Mutex
	topInfos []redisTopInfo
}

func (this *Redis) Run(args []string) (exitCode int) {
	var (
		zone   string
		add    string
		list   bool
		byHost bool
		del    string
		top    bool
		ping   bool
	)
	cmdFlags := flag.NewFlagSet("redis", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&zone, "z", ctx.ZkDefaultZone(), "")
	cmdFlags.StringVar(&add, "add", "", "")
	cmdFlags.BoolVar(&list, "list", true, "")
	cmdFlags.BoolVar(&byHost, "host", false, "")
	cmdFlags.BoolVar(&top, "top", false, "")
	cmdFlags.BoolVar(&ping, "ping", false, "")
	cmdFlags.StringVar(&del, "del", "", "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
	if top || ping {
		list = false
	}

	if add != "" {
		host, port, err := net.SplitHostPort(add)
		swallow(err)

		nport, err := strconv.Atoi(port)
		swallow(err)
		zkzone.AddRedis(host, nport)
	} else if del != "" {
		host, port, err := net.SplitHostPort(del)
		swallow(err)

		nport, err := strconv.Atoi(port)
		swallow(err)
		zkzone.DelRedis(host, nport)
	} else {
		if top {
			this.runTop(zkzone)
		} else if ping {
			this.runPing(zkzone)
		} else if list {
			machineMap := make(map[string]struct{})
			var machines []string
			hostPorts := zkzone.AllRedis()
			sort.Strings(hostPorts)
			for _, hp := range hostPorts {
				host, port, _ := net.SplitHostPort(hp)
				ips, _ := net.LookupIP(host)
				if _, present := machineMap[ips[0].String()]; !present {
					machineMap[ips[0].String()] = struct{}{}

					machines = append(machines, ips[0].String())
				}
				if !byHost {
					this.Ui.Output(fmt.Sprintf("%35s %s", host, port))
				}

			}

			if byHost {
				sort.Strings(machines)
				for _, ip := range machines {
					this.Ui.Output(fmt.Sprintf("%20s", ip))
				}
			}

			this.Ui.Output(fmt.Sprintf("Total instances:%d machines:%d", len(hostPorts), len(machines)))
		}
	}

	return
}

type redisTopInfo struct {
	host                       string
	port                       int
	dbsize, ops, rx, tx, conns int64
	latency                    time.Duration
}

func (this *Redis) runTop(zkzone *zk.ZkZone) {
	termui.Init()
	limit := termui.TermHeight() - 3
	termui.Close()
	this.topInfos = make([]redisTopInfo, 0, 100)
	for {
		var wg sync.WaitGroup
		this.topInfos = this.topInfos[:0]
		for _, hostPort := range zkzone.AllRedis() {
			host, port, err := net.SplitHostPort(hostPort)
			if err != nil {
				log.Error("invalid redis instance: %s", hostPort)
				continue
			}

			nport, err := strconv.Atoi(port)
			if err != nil || nport < 0 {
				log.Error("invalid redis instance: %s", hostPort)
				continue
			}

			wg.Add(1)
			go this.updateRedisInfo(&wg, host, nport)
		}
		wg.Wait()
		refreshScreen()

		sortutil.DescByField(this.topInfos, "ops")
		lines := []string{"Host|Port|dbsize|conns|ops|rx/bps|tx/bps"}

		for i := 0; i < min(limit, len(this.topInfos)); i++ {
			info := this.topInfos[i]
			lines = append(lines, fmt.Sprintf("%s|%d|%s|%s|%s|%s|%s",
				info.host, info.port,
				gofmt.Comma(info.dbsize), gofmt.Comma(info.conns), gofmt.Comma(info.ops),
				gofmt.ByteSize(info.rx*1024/8), gofmt.ByteSize(info.tx*1024/8)))
		}

		this.Ui.Output(columnize.SimpleFormat(lines))

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

func (this *Redis) updateRedisInfo(wg *sync.WaitGroup, host string, port int) {
	defer wg.Done()

	spec := redis.DefaultSpec().Host(host).Port(port)
	client, err := redis.NewSynchClientWithSpec(spec)
	if err != nil {
		return
	}
	defer client.Quit()

	infoMap, err := client.Info()
	if err != nil {
		return
	}

	dbSize, _ := client.Dbsize()
	conns, _ := strconv.ParseInt(infoMap["connected_clients"], 10, 64)
	ops, _ := strconv.ParseInt(infoMap["instantaneous_ops_per_sec"], 10, 64)
	rxKbps, _ := strconv.ParseFloat(infoMap["instantaneous_input_kbps"], 64)
	txKbps, _ := strconv.ParseFloat(infoMap["instantaneous_output_kbps"], 64)

	this.mu.Lock()
	this.topInfos = append(this.topInfos, redisTopInfo{
		host:   host,
		port:   port,
		dbsize: dbSize,
		ops:    ops,
		rx:     int64(rxKbps),
		tx:     int64(txKbps),
		conns:  conns,
	})
	this.mu.Unlock()
}

func (this *Redis) runPing(zkzone *zk.ZkZone) {
	var wg sync.WaitGroup
	this.topInfos = make([]redisTopInfo, 0, 100)

	for _, hostPort := range zkzone.AllRedis() {
		host, port, err := net.SplitHostPort(hostPort)
		if err != nil {
			this.Ui.Error(hostPort)
			continue
		}

		nport, err := strconv.Atoi(port)
		if err != nil || nport < 0 {
			this.Ui.Error(hostPort)
			continue
		}

		wg.Add(1)
		go func(wg *sync.WaitGroup, host string, port int) {
			defer wg.Done()

			t0 := time.Now()

			spec := redis.DefaultSpec().Host(host).Port(port)
			client, err := redis.NewSynchClientWithSpec(spec)
			if err != nil {
				this.Ui.Error(err.Error())
			}
			defer client.Quit()

			if err := client.Ping(); err != nil {
				this.Ui.Error(err.Error())
			}

			this.mu.Lock()
			this.topInfos = append(this.topInfos, redisTopInfo{
				host:    host,
				port:    port,
				latency: time.Since(t0),
			})
			this.mu.Unlock()
		}(&wg, host, nport)
	}
	wg.Wait()

	sortutil.AscByField(this.topInfos, "latency")
	lines := []string{"Host|Port|latency"}
	for _, info := range this.topInfos {
		lines = append(lines, fmt.Sprintf("%s|%d|%s", info.host, info.port, info.latency))
	}
	this.Ui.Output(columnize.SimpleFormat(lines))
}

func (*Redis) Synopsis() string {
	return "Monitor redis instances"
}

func (this *Redis) Help() string {
	help := fmt.Sprintf(`
Usage: %s redis [options]

    %s

    -z zone

    -list

    -top
      Monitor all redis instances ops

    -ping
      Ping all redis instances

    -host
      Work with -list, print host instead of redis instance

    -add host:port

    -del host:port

`, this.Cmd, this.Synopsis())
	return strings.TrimSpace(help)
}
<commit_msg>metrics of redis ping<commit_after>package command

import (
	"flag"
	"fmt"
	"net"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/funkygao/Go-Redis"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/go-metrics"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/gofmt"
	log "github.com/funkygao/log4go"
	"github.com/funkygao/termui"
	"github.com/pmylund/sortutil"
	"github.com/ryanuber/columnize"
)

type Redis struct {
	Ui  cli.Ui
	Cmd string

	mu       sync.Mutex
	topInfos []redisTopInfo
}

func (this *Redis) Run(args []string) (exitCode int) {
	var (
		zone   string
		add    string
		list   bool
		byHost bool
		del    string
		top    bool
		ping   bool
	)
	cmdFlags := flag.NewFlagSet("redis", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&zone, "z", ctx.ZkDefaultZone(), "")
	cmdFlags.StringVar(&add, "add", "", "")
	cmdFlags.BoolVar(&list, "list", true, "")
	cmdFlags.BoolVar(&byHost, "host", false, "")
	cmdFlags.BoolVar(&top, "top", false, "")
	cmdFlags.BoolVar(&ping, "ping", false, "")
	cmdFlags.StringVar(&del, "del", "", "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
	if top || ping {
		list = false
	}

	if add != "" {
		host, port, err := net.SplitHostPort(add)
		swallow(err)

		nport, err := strconv.Atoi(port)
		swallow(err)
		zkzone.AddRedis(host, nport)
	} else if del != "" {
		host, port, err := net.SplitHostPort(del)
		swallow(err)

		nport, err := strconv.Atoi(port)
		swallow(err)
		zkzone.DelRedis(host, nport)
	} else {
		if top {
			this.runTop(zkzone)
		} else if ping {
			this.runPing(zkzone)
		} else if list {
			machineMap := make(map[string]struct{})
			var machines []string
			hostPorts := zkzone.AllRedis()
			sort.Strings(hostPorts)
			for _, hp := range hostPorts {
				host, port, _ := net.SplitHostPort(hp)
				ips, _ := net.LookupIP(host)
				if _, present := machineMap[ips[0].String()]; !present {
					machineMap[ips[0].String()] = struct{}{}

					machines = append(machines, ips[0].String())
				}
				if !byHost {
					this.Ui.Output(fmt.Sprintf("%35s %s", host, port))
				}

			}

			if byHost {
				sort.Strings(machines)
				for _, ip := range machines {
					this.Ui.Output(fmt.Sprintf("%20s", ip))
				}
			}

			this.Ui.Output(fmt.Sprintf("Total instances:%d machines:%d", len(hostPorts), len(machines)))
		}
	}

	return
}

type redisTopInfo struct {
	host                       string
	port                       int
	dbsize, ops, rx, tx, conns int64
	t0                         time.Time
	latency                    time.Duration
}

func (this *Redis) runTop(zkzone *zk.ZkZone) {
	termui.Init()
	limit := termui.TermHeight() - 3
	termui.Close()
	this.topInfos = make([]redisTopInfo, 0, 100)
	for {
		var wg sync.WaitGroup
		this.topInfos = this.topInfos[:0]
		for _, hostPort := range zkzone.AllRedis() {
			host, port, err := net.SplitHostPort(hostPort)
			if err != nil {
				log.Error("invalid redis instance: %s", hostPort)
				continue
			}

			nport, err := strconv.Atoi(port)
			if err != nil || nport < 0 {
				log.Error("invalid redis instance: %s", hostPort)
				continue
			}

			wg.Add(1)
			go this.updateRedisInfo(&wg, host, nport)
		}
		wg.Wait()
		refreshScreen()

		sortutil.DescByField(this.topInfos, "ops")
		lines := []string{"Host|Port|dbsize|conns|ops|rx/bps|tx/bps"}

		for i := 0; i < min(limit, len(this.topInfos)); i++ {
			info := this.topInfos[i]
			lines = append(lines, fmt.Sprintf("%s|%d|%s|%s|%s|%s|%s",
				info.host, info.port,
				gofmt.Comma(info.dbsize), gofmt.Comma(info.conns), gofmt.Comma(info.ops),
				gofmt.ByteSize(info.rx*1024/8), gofmt.ByteSize(info.tx*1024/8)))
		}

		this.Ui.Output(columnize.SimpleFormat(lines))

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

func (this *Redis) updateRedisInfo(wg *sync.WaitGroup, host string, port int) {
	defer wg.Done()

	spec := redis.DefaultSpec().Host(host).Port(port)
	client, err := redis.NewSynchClientWithSpec(spec)
	if err != nil {
		return
	}
	defer client.Quit()

	infoMap, err := client.Info()
	if err != nil {
		return
	}

	dbSize, _ := client.Dbsize()
	conns, _ := strconv.ParseInt(infoMap["connected_clients"], 10, 64)
	ops, _ := strconv.ParseInt(infoMap["instantaneous_ops_per_sec"], 10, 64)
	rxKbps, _ := strconv.ParseFloat(infoMap["instantaneous_input_kbps"], 64)
	txKbps, _ := strconv.ParseFloat(infoMap["instantaneous_output_kbps"], 64)

	this.mu.Lock()
	this.topInfos = append(this.topInfos, redisTopInfo{
		host:   host,
		port:   port,
		dbsize: dbSize,
		ops:    ops,
		rx:     int64(rxKbps),
		tx:     int64(txKbps),
		conns:  conns,
	})
	this.mu.Unlock()
}

func (this *Redis) runPing(zkzone *zk.ZkZone) {
	var wg sync.WaitGroup
	allRedis := zkzone.AllRedis()
	this.topInfos = make([]redisTopInfo, 0, len(allRedis))

	for _, hostPort := range allRedis {
		host, port, err := net.SplitHostPort(hostPort)
		if err != nil {
			this.Ui.Error(hostPort)
			continue
		}

		nport, err := strconv.Atoi(port)
		if err != nil || nport < 0 {
			this.Ui.Error(hostPort)
			continue
		}

		wg.Add(1)
		go func(wg *sync.WaitGroup, host string, port int) {
			defer wg.Done()

			t0 := time.Now()

			spec := redis.DefaultSpec().Host(host).Port(port)
			client, err := redis.NewSynchClientWithSpec(spec)
			if err != nil {
				this.Ui.Error(fmt.Sprintf("[%s:%d] %v", host, port, err))
				return
			}
			defer client.Quit()

			if err := client.Ping(); err != nil {
				this.Ui.Error(fmt.Sprintf("[%s:%d] %v", host, port, err))
				return
			}

			latency := time.Since(t0)

			this.mu.Lock()
			this.topInfos = append(this.topInfos, redisTopInfo{
				host:    host,
				port:    port,
				t0:      t0,
				latency: latency,
			})
			this.mu.Unlock()
		}(&wg, host, nport)
	}
	wg.Wait()

	latency := metrics.NewRegisteredHistogram("redis.latency", metrics.DefaultRegistry, metrics.NewExpDecaySample(1028, 0.015))

	sortutil.AscByField(this.topInfos, "latency")
	lines := []string{"Host|Port|At|latency"}
	for _, info := range this.topInfos {
		latency.Update(info.latency.Nanoseconds() / 1e6)

		lines = append(lines, fmt.Sprintf("%s|%d|%s|%s",
			info.host, info.port, info.t0, info.latency))
	}
	this.Ui.Output(columnize.SimpleFormat(lines))

	// summary
	ps := latency.Percentiles([]float64{0.90, 0.95, 0.99, 0.999})
	this.Ui.Info(fmt.Sprintf("N:%d Min:%dms Max:%dms Mean:%.1fms 90%%:%.1fms 95%%:%.1fms 99%%:%.1fms",
		latency.Count(), latency.Min(), latency.Max(), latency.Mean(), ps[0], ps[1], ps[2]))
}

func (*Redis) Synopsis() string {
	return "Monitor redis instances"
}

func (this *Redis) Help() string {
	help := fmt.Sprintf(`
Usage: %s redis [options]

    %s

    -z zone

    -list

    -top
      Monitor all redis instances ops

    -ping
      Ping all redis instances

    -host
      Work with -list, print host instead of redis instance

    -add host:port

    -del host:port

`, this.Cmd, this.Synopsis())
	return strings.TrimSpace(help)
}
<|endoftext|>


<commit_before>package utron

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

var baseApp *App

func init() {
	baseApp = NewApp()
}

// App is the main utron application.
type App struct {
	router     *Router
	cfg        *Config
	view       View
	log        Logger
	model      *Model
	configPath string
	isInit     bool
}

// NewApp creates a new bare-bone utron application. To use the MVC components, you should call
// the Init method before serving requests.
func NewApp() *App {
	app := &App{}
	app.Set(logThis)
	r := NewRouter(app)
	app.Set(r)
	app.Set(NewModel())
	return app
}

// NewMVC creates a new MVC utron app. If cfg is passed, it should be a directory to look for
// the configuration files. The App returned is initialized.
func NewMVC(cfg ...string) (*App, error) {
	app := NewApp()
	if len(cfg) > 0 {
		app.SetConfigPath(cfg[0])
	}
	if err := app.Init(); err != nil {
		return nil, err
	}
	return app, nil
}

// Init initializes the MVC App.
func (a *App) Init() error {
	if a.configPath == "" {
		a.SetConfigPath("config")
	}
	return a.init()
}

// SetConfigPath sets the directory path to search for the config files.
func (a *App) SetConfigPath(dir string) {
	a.configPath = dir
}

// init initializes values to the app components.
func (a *App) init() error {
	appConfig, err := loadConfig(a.configPath)
	if err != nil {
		return err
	}

	views, err := NewSimpleView(appConfig.ViewsDir)
	if err != nil {
		return err
	}
	if a.model != nil && !a.model.IsOpen() {
		oerr := a.model.OpenWithConfig(appConfig)
		if oerr != nil {
			return oerr
		}
	} else {
		model, err := NewModelWithConfig(appConfig)
		if err != nil {
			return err
		}
		a.Set(model)
	}
	a.router.loadRoutes(a.configPath) // Load a routes file if available.
	a.Set(appConfig)
	a.Set(views)
	a.isInit = true

	// In case the StaticDir is specified in the Config file, register
	// a handler serving contents of that directory under the PathPrefix /static/.
	if appConfig.StaticDir != "" {
		static, err := getAbsolutePath(appConfig.StaticDir)
		if err != nil {
			logThis.Errors(err)
		}
		if static != "" {
			a.router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(static))))
		}

	}
	return nil
}

// getAbsolutePath returns the absolute path to dir. If the dir is relative, then we add
// the current working directory. Checks are made to ensure the directory exist.
// In case of any error, an empty string is returned.
func getAbsolutePath(dir string) (string, error) {
	info, err := os.Stat(dir)
	if err != nil {
		return "", err
	}
	if !info.IsDir() {
		return "", fmt.Errorf("untron: %s is not a directory", dir)
	}

	if filepath.IsAbs(dir) { // If dir is already absolute, return it.
		return dir, nil
	}
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	absDir := filepath.Join(wd, dir)
	_, err = os.Stat(absDir)
	if err != nil {
		return "", err
	}
	return absDir, nil
}

// loadConfig loads the configuration file. If cfg is provided, then it is used as the directory
// for searching the configuration files. It defaults to the directory named config in the current
// working directory.
func loadConfig(cfg ...string) (*Config, error) {
	cfgDir := "config"
	if len(cfg) > 0 {
		cfgDir = cfg[0]
	}

	// Load configurations.
	cfgFile, err := findConfigFile(cfgDir, "app")
	if err != nil {
		return nil, err
	}
	return NewConfig(cfgFile)
}

// findConfigFile finds the configuration file name in the directory specified.
func findConfigFile(dir string, name string) (file string, err error) {
	extensions := []string{".json", ".toml", ".yml"}

	for _, ext := range extensions {
		file = filepath.Join(dir, name)
		if info, serr := os.Stat(file); serr == nil && !info.IsDir() {
			return
		}
		file = file + ext
		if info, serr := os.Stat(file); serr == nil && !info.IsDir() {
			return
		}
	}
	return "", fmt.Errorf("utron: can't find configuration file %s in %s", name, dir)
}

// AddController registers a controller, and middlewares if any is provided.
func (a *App) AddController(ctrl Controller, middlewares ...interface{}) {
	a.router.Add(ctrl, middlewares...)
}

// Set is for assigning a value to *App components. The following can be set:
//	Logger by passing Logger
//	View by passing View
//	Router by passing *Router
//	Config by passing *Config
//	Model by passing *Model
func (a *App) Set(value interface{}) {
	switch value.(type) {
	case Logger:
		a.log = value.(Logger)
	case *Router:
		a.router = value.(*Router)
	case View:
		a.view = value.(View)
	case *Config:
		a.cfg = value.(*Config)
	case *Model:
		a.model = value.(*Model)
	}
}

// ServeHTTP serves http requests. It can be used with other http.Handler implementations.
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.router.ServeHTTP(w, r)
}

// SetConfigPath sets the path to look for the configuration file in the
// global utron App.
func SetConfigPath(path string) {
	baseApp.SetConfigPath(path)
}

// RegisterModels registers models in the global utron App.
func RegisterModels(models ...interface{}) {
	baseApp.model.Register(models...)
}

// RegisterController registers a controller in the global utron App.
func RegisterController(ctrl Controller, middlewares ...interface{}) {
	baseApp.router.Add(ctrl, middlewares...)
}

// ServeHTTP serves request using global utron App.
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !baseApp.isInit {
		if err := baseApp.Init(); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	baseApp.ServeHTTP(w, r)
}

// Migrate runs migrations on the global utron app.
func Migrate() {
	baseApp.model.AutoMigrateAll()
}

// Run runs a http server, serving the global utron App.
//
// By using this, you should make sure you followed the MVC pattern.
func Run() {
	if err := baseApp.Init(); err != nil {
		logThis.Errors(err)
		os.Exit(1)
	}
	Migrate()
	port := baseApp.cfg.Port
	logThis.Info("starting server at ", baseApp.cfg.BaseURL)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), baseApp))
}
<commit_msg>Update utron.go - Fix typos 2<commit_after>package utron

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

var baseApp *App

func init() {
	baseApp = NewApp()
}

// App is the main utron application.
type App struct {
	router     *Router
	cfg        *Config
	view       View
	log        Logger
	model      *Model
	configPath string
	isInit     bool
}

// NewApp creates a new bare-bone utron application. To use the MVC components, you should call
// the Init method before serving requests.
func NewApp() *App {
	app := &App{}
	app.Set(logThis)
	r := NewRouter(app)
	app.Set(r)
	app.Set(NewModel())
	return app
}

// NewMVC creates a new MVC utron app. If cfg is passed, it should be a directory to look for
// the configuration files. The App returned is initialized.
func NewMVC(cfg ...string) (*App, error) {
	app := NewApp()
	if len(cfg) > 0 {
		app.SetConfigPath(cfg[0])
	}
	if err := app.Init(); err != nil {
		return nil, err
	}
	return app, nil
}

// Init initializes the MVC App.
func (a *App) Init() error {
	if a.configPath == "" {
		a.SetConfigPath("config")
	}
	return a.init()
}

// SetConfigPath sets the directory path to search for the config files.
func (a *App) SetConfigPath(dir string) {
	a.configPath = dir
}

// init initializes values to the app components.
func (a *App) init() error {
	appConfig, err := loadConfig(a.configPath)
	if err != nil {
		return err
	}

	views, err := NewSimpleView(appConfig.ViewsDir)
	if err != nil {
		return err
	}
	if a.model != nil && !a.model.IsOpen() {
		oerr := a.model.OpenWithConfig(appConfig)
		if oerr != nil {
			return oerr
		}
	} else {
		model, err := NewModelWithConfig(appConfig)
		if err != nil {
			return err
		}
		a.Set(model)
	}
	a.router.loadRoutes(a.configPath) // Load a routes file if available.
	a.Set(appConfig)
	a.Set(views)
	a.isInit = true

	// In case the StaticDir is specified in the Config file, register
	// a handler serving contents of that directory under the PathPrefix /static/.
	if appConfig.StaticDir != "" {
		static, err := getAbsolutePath(appConfig.StaticDir)
		if err != nil {
			logThis.Errors(err)
		}
		if static != "" {
			a.router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(static))))
		}

	}
	return nil
}

// getAbsolutePath returns the absolute path to dir. If the dir is relative, then we add
// the current working directory. Checks are made to ensure the directory exist.
// In case of any error, an empty string is returned.
func getAbsolutePath(dir string) (string, error) {
	info, err := os.Stat(dir)
	if err != nil {
		return "", err
	}
	if !info.IsDir() {
		return "", fmt.Errorf("untron: %s is not a directory", dir)
	}

	if filepath.IsAbs(dir) { // If dir is already absolute, return it.
		return dir, nil
	}
	wd, err := os.Getwd()
	if err != nil {
		return "", err
	}
	absDir := filepath.Join(wd, dir)
	_, err = os.Stat(absDir)
	if err != nil {
		return "", err
	}
	return absDir, nil
}

// loadConfig loads the configuration file. If cfg is provided, then it is used as the directory
// for searching the configuration files. It defaults to the directory named config in the current
// working directory.
func loadConfig(cfg ...string) (*Config, error) {
	cfgDir := "config"
	if len(cfg) > 0 {
		cfgDir = cfg[0]
	}

	// Load configurations.
	cfgFile, err := findConfigFile(cfgDir, "app")
	if err != nil {
		return nil, err
	}
	return NewConfig(cfgFile)
}

// findConfigFile finds the configuration file name in the directory dir.
func findConfigFile(dir string, name string) (file string, err error) {
	extensions := []string{".json", ".toml", ".yml"}

	for _, ext := range extensions {
		file = filepath.Join(dir, name)
		if info, serr := os.Stat(file); serr == nil && !info.IsDir() {
			return
		}
		file = file + ext
		if info, serr := os.Stat(file); serr == nil && !info.IsDir() {
			return
		}
	}
	return "", fmt.Errorf("utron: can't find configuration file %s in %s", name, dir)
}

// AddController registers a controller, and middlewares if any is provided.
func (a *App) AddController(ctrl Controller, middlewares ...interface{}) {
	a.router.Add(ctrl, middlewares...)
}

// Set is for assigning a value to *App components. The following can be set:
//	Logger by passing Logger
//	View by passing View
//	Router by passing *Router
//	Config by passing *Config
//	Model by passing *Model
func (a *App) Set(value interface{}) {
	switch value.(type) {
	case Logger:
		a.log = value.(Logger)
	case *Router:
		a.router = value.(*Router)
	case View:
		a.view = value.(View)
	case *Config:
		a.cfg = value.(*Config)
	case *Model:
		a.model = value.(*Model)
	}
}

// ServeHTTP serves http requests. It can be used with other http.Handler implementations.
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.router.ServeHTTP(w, r)
}

// SetConfigPath sets the path to look for the configuration files in the
// global utron App.
func SetConfigPath(path string) {
	baseApp.SetConfigPath(path)
}

// RegisterModels registers models in the global utron App.
func RegisterModels(models ...interface{}) {
	baseApp.model.Register(models...)
}

// RegisterController registers a controller in the global utron App.
func RegisterController(ctrl Controller, middlewares ...interface{}) {
	baseApp.router.Add(ctrl, middlewares...)
}

// ServeHTTP serves request using global utron App.
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if !baseApp.isInit {
		if err := baseApp.Init(); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	baseApp.ServeHTTP(w, r)
}

// Migrate runs migrations on the global utron app.
func Migrate() {
	baseApp.model.AutoMigrateAll()
}

// Run runs a http server, serving the global utron App.
//
// By using this, you should make sure you followed the MVC pattern.
func Run() {
	if err := baseApp.Init(); err != nil {
		logThis.Errors(err)
		os.Exit(1)
	}
	Migrate()
	port := baseApp.cfg.Port
	logThis.Info("starting server at ", baseApp.cfg.BaseURL)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), baseApp))
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"net"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/bjtime"
	"github.com/funkygao/golib/color"
)

type Zktop struct {
	Ui  cli.Ui
	Cmd string

	batchMode       bool
	refreshInterval time.Duration
	lastSents       map[string]string
	lastRecvs       map[string]string
}

func (this *Zktop) Run(args []string) (exitCode int) {
	var (
		zone string
	)
	cmdFlags := flag.NewFlagSet("zktop", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&zone, "z", "", "")
	cmdFlags.BoolVar(&this.batchMode, "b", false, "")
	cmdFlags.DurationVar(&this.refreshInterval, "i", time.Second*5, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 2
	}

	this.lastRecvs = make(map[string]string)
	this.lastSents = make(map[string]string)

	if this.batchMode {
		this.Ui = &cli.BasicUi{
			Writer:      os.Stdout,
			Reader:      os.Stdin,
			ErrorWriter: os.Stderr,
		}
	}

	for {
		if !this.batchMode {
			refreshScreen()
		}

		if zone == "" {
			forAllSortedZones(func(zkzone *zk.ZkZone) {
				this.displayZoneTop(zkzone)
			})
		} else {
			zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
			this.displayZoneTop(zkzone)
		}

		time.Sleep(this.refreshInterval)
	}

	return
}

func (this *Zktop) displayZoneTop(zkzone *zk.ZkZone) {
	if this.batchMode {
		this.Ui.Output(fmt.Sprintf("%s %s", zkzone.Name(), bjtime.NowBj()))
	} else {
		this.Ui.Output(color.Green(zkzone.Name()))
	}

	header := "VER             SERVER           PORT M  OUTST            RECVD             SENT CONNS  ZNODES LAT(MIN/AVG/MAX)"
	this.Ui.Output(header)

	stats := zkzone.RunZkFourLetterCommand("stat")
	sortedHosts := make([]string, 0, len(stats))
	for hp, _ := range stats {
		sortedHosts = append(sortedHosts, hp)
	}
	sort.Strings(sortedHosts)

	for _, hostPort := range sortedHosts {
		host, port, err := net.SplitHostPort(hostPort)
		if err != nil {
			panic(err)
		}

		stat := this.parsedStat(stats[hostPort])
		if stat.mode == "" {
			if this.batchMode {
				stat.mode = "E"
			} else {
				stat.mode = color.Red("E")
			}
		} else if stat.mode == "L" && !this.batchMode {
			stat.mode = color.Blue(stat.mode)
		}
		var sentQps, recvQps int
		if lastRecv, present := this.lastRecvs[hostPort]; present {
			r1, _ := strconv.Atoi(stat.received)
			r0, _ := strconv.Atoi(lastRecv)
			recvQps = (r1 - r0) / int(this.refreshInterval.Seconds())

			s1, _ := strconv.Atoi(stat.sent)
			s0, _ := strconv.Atoi(this.lastSents[hostPort])
			sentQps = (s1 - s0) / int(this.refreshInterval.Seconds())
		}
		this.Ui.Output(fmt.Sprintf("%-15s %-15s %5s %1s %6s %16s %16s %5s %7s %s",
			stat.ver,                                     // 15
			host,                                         // 15
			port,                                         // 5
			stat.mode,                                    // 1
			stat.outstanding,                             // 6
			fmt.Sprintf("%s/%d", stat.received, recvQps), // 16
			fmt.Sprintf("%s/%d", stat.sent, sentQps),     // 16
			stat.connections,                             // 5
			stat.znodes,                                  // 7
			stat.latency,
		))

		this.lastRecvs[hostPort] = stat.received
		this.lastSents[hostPort] = stat.sent
	}
}

type zkStat struct {
	ver            string
	latency        string
	connections    string
	outstanding    string
	mode           string
	znodes         string
	received, sent string
}

func (this *Zktop) parsedStat(s string) (stat zkStat) {
	lines := strings.Split(s, "\n")
	for _, l := range lines {
		switch {
		case strings.HasPrefix(l, "Zookeeper version:"):
			p := strings.SplitN(l, ":", 2)
			p = strings.SplitN(p[1], ",", 2)
			stat.ver = strings.TrimSpace(p[0])

		case strings.HasPrefix(l, "Latency"):
			stat.latency = this.extractStatValue(l)

		case strings.HasPrefix(l, "Sent"):
			stat.sent = this.extractStatValue(l)

		case strings.HasPrefix(l, "Received"):
			stat.received = this.extractStatValue(l)

		case strings.HasPrefix(l, "Connections"):
			stat.connections = this.extractStatValue(l)

		case strings.HasPrefix(l, "Mode"):
			stat.mode = strings.ToUpper(this.extractStatValue(l)[:1])

		case strings.HasPrefix(l, "Node count"):
			stat.znodes = this.extractStatValue(l)

		case strings.HasPrefix(l, "Outstanding"):
			stat.outstanding = this.extractStatValue(l)

		}
	}
	return
}

func (this *Zktop) extractStatValue(l string) string {
	p := strings.SplitN(l, ":", 2)
	return strings.TrimSpace(p[1])
}

func (*Zktop) Synopsis() string {
	return "Unix “top” like utility for ZooKeeper"
}

func (this *Zktop) Help() string {
	help := fmt.Sprintf(`
Usage: %s zktop [options]

    Unix “top” like utility for ZooKeeper

Options:

    -z zone

    -i interval
      Refresh interval in seconds.
      e,g. 5s

    -b
      Batch mode operation.

`, this.Cmd)
	return strings.TrimSpace(help)
}
<commit_msg>reuse code<commit_after>package command

import (
	"flag"
	"fmt"
	"net"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/gocli"
	"github.com/funkygao/golib/bjtime"
	"github.com/funkygao/golib/color"
)

type Zktop struct {
	Ui  cli.Ui
	Cmd string

	batchMode       bool
	refreshInterval time.Duration
	lastSents       map[string]string
	lastRecvs       map[string]string
}

func (this *Zktop) Run(args []string) (exitCode int) {
	var (
		zone string
	)
	cmdFlags := flag.NewFlagSet("zktop", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&zone, "z", "", "")
	cmdFlags.BoolVar(&this.batchMode, "b", false, "")
	cmdFlags.DurationVar(&this.refreshInterval, "i", time.Second*5, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 2
	}

	this.lastRecvs = make(map[string]string)
	this.lastSents = make(map[string]string)

	if this.batchMode {
		this.Ui = &cli.BasicUi{
			Writer:      os.Stdout,
			Reader:      os.Stdin,
			ErrorWriter: os.Stderr,
		}
	}

	for {
		if !this.batchMode {
			refreshScreen()
		}

		if zone == "" {
			forAllSortedZones(func(zkzone *zk.ZkZone) {
				this.displayZoneTop(zkzone)
			})
		} else {
			zkzone := zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
			this.displayZoneTop(zkzone)
		}

		time.Sleep(this.refreshInterval)
	}

	return
}

func (this *Zktop) displayZoneTop(zkzone *zk.ZkZone) {
	if this.batchMode {
		this.Ui.Output(fmt.Sprintf("%s %s", zkzone.Name(), bjtime.NowBj()))
	} else {
		this.Ui.Output(color.Green(zkzone.Name()))
	}

	header := "VER             SERVER           PORT M  OUTST            RECVD             SENT CONNS  ZNODES LAT(MIN/AVG/MAX)"
	this.Ui.Output(header)

	stats := zkzone.RunZkFourLetterCommand("stat")
	sortedHosts := make([]string, 0, len(stats))
	for hp, _ := range stats {
		sortedHosts = append(sortedHosts, hp)
	}
	sort.Strings(sortedHosts)

	for _, hostPort := range sortedHosts {
		host, port, err := net.SplitHostPort(hostPort)
		if err != nil {
			panic(err)
		}

		stat := zk.ParseStatResult(stats[hostPort])
		if stat.Mode == "" {
			if this.batchMode {
				stat.Mode = "E"
			} else {
				stat.Mode = color.Red("E")
			}
		} else if stat.Mode == "L" && !this.batchMode {
			stat.Mode = color.Blue(stat.Mode)
		}
		var sentQps, recvQps int
		if lastRecv, present := this.lastRecvs[hostPort]; present {
			r1, _ := strconv.Atoi(stat.Received)
			r0, _ := strconv.Atoi(lastRecv)
			recvQps = (r1 - r0) / int(this.refreshInterval.Seconds())

			s1, _ := strconv.Atoi(stat.Sent)
			s0, _ := strconv.Atoi(this.lastSents[hostPort])
			sentQps = (s1 - s0) / int(this.refreshInterval.Seconds())
		}
		this.Ui.Output(fmt.Sprintf("%-15s %-15s %5s %1s %6s %16s %16s %5s %7s %s",
			stat.Version,                                 // 15
			host,                                         // 15
			port,                                         // 5
			stat.Mode,                                    // 1
			stat.Outstanding,                             // 6
			fmt.Sprintf("%s/%d", stat.Received, recvQps), // 16
			fmt.Sprintf("%s/%d", stat.Sent, sentQps),     // 16
			stat.Connections,                             // 5
			stat.Znodes,                                  // 7
			stat.Latency,
		))

		this.lastRecvs[hostPort] = stat.Received
		this.lastSents[hostPort] = stat.Sent
	}
}

func (*Zktop) Synopsis() string {
	return "Unix “top” like utility for ZooKeeper"
}

func (this *Zktop) Help() string {
	help := fmt.Sprintf(`
Usage: %s zktop [options]

    Unix “top” like utility for ZooKeeper

Options:

    -z zone

    -i interval
      Refresh interval in seconds.
      e,g. 5s

    -b
      Batch mode operation.

`, this.Cmd)
	return strings.TrimSpace(help)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"strings"
	"time"

	"github.com/Dieterbe/profiletrigger/cpu"
	"github.com/Dieterbe/profiletrigger/heap"
	"github.com/raintank/dur"
	"github.com/raintank/statsdaemon"
	"github.com/raintank/statsdaemon/out"

	"net/http"
	_ "net/http/pprof"

	"github.com/grafana/globalconf"
)

const (
	VERSION = "0.6"
	// number of packets we can read out of udp buffer without processing them
	// statsdaemon doesn't really interrupt the udp reader like some other statsd's do (like on flush)
	// but this can still be useful to deal with traffic bursts.
	// keep in mind that one metric is about 30 to 100 bytes of memory.
	MAX_UNPROCESSED_PACKETS = 1000
)

var (
	listen_addr     = flag.String("listen_addr", ":8125", "listener address for statsd, listens on UDP only")
	admin_addr      = flag.String("admin_addr", ":8126", "listener address for admin port")
	profile_addr    = flag.String("profile_addr", "", "listener address for profiler")
	graphite_addr   = flag.String("graphite_addr", "127.0.0.1:2003", "graphite carbon-in url")
	flushInterval   = flag.Int("flush_interval", 10, "flush interval in seconds")
	processes       = flag.Int("processes", 1, "number of processes to use")
	instance        = flag.String("instance", "$HOST", "instance name, defaults to short hostname if not set")
	prefix_counters = flag.String("prefix_counters", "stats_counts.", "counters prefix")
	prefix_gauges   = flag.String("prefix_gauges", "stats.gauges.", "gauges prefix")
	prefix_rates    = flag.String("prefix_rates", "stats.", "rates prefix, it is recommended that you use stats.rates if possible")
	prefix_timers   = flag.String("prefix_timers", "stats.timers.", "timers prefix")

	prefix_m20_counters = flag.String("prefix_m20_counters", "", "counters 2.0 prefix")
	prefix_m20_gauges   = flag.String("prefix_m20_gauges", "", "gauges 2.0 prefix")
	prefix_m20_rates    = flag.String("prefix_m20_rates", "", "rates 2.0 prefix")
	prefix_m20_timers   = flag.String("prefix_m20_timers", "", "timers 2.0 prefix")

	legacy_namespace = flag.Bool("legacy_namespace", true, "legacy namespacing (not recommended)")
	flush_rates      = flag.Bool("flush_rates", true, "send count for counters (using prefix_counters)")
	flush_counts     = flag.Bool("flush_counts", false, "send count for counters (using prefix_counters)")

	percentile_thresholds = flag.String("percentile_thresholds", "", "percential thresholds (used by timers)")
	max_timers_per_s      = flag.Uint64("max_timers_per_s", 1000, "max timers per second")

	proftrigPath = flag.String("proftrigger_path", "/tmp", "profiler file path") // "path to store triggered profiles"

	proftrigHeapFreqStr    = flag.String("proftrigger_heap_freq", "60s", "profiler heap frequency")   // "inspect status frequency. set to 0 to disable"
	proftrigHeapMinDiffStr = flag.String("proftrigger_heap_min_diff", "1h", "profiler heap min difference") // "minimum time between triggered profiles"
	proftrigHeapThresh     = flag.Int("proftrigger_heap_thresh", 10000000, "profiler heap threshold")  // "if this many bytes allocated, trigger a profile"

	proftrigCpuFreqStr    = flag.String("proftrigger_cpu_freq", "60s", "profiler cpu frequency")    // "inspect status frequency. set to 0 to disable"
	proftrigCpuMinDiffStr = flag.String("proftrigger_cpu_min_diff", "1h", "profiler cpu min difference") // "minimum time between triggered profiles"
	proftrigCpuDurStr     = flag.String("proftrigger_cpu_dur", "5s", "profiler cpu duration")      // "duration of cpu profile"
	proftrigCpuThresh     = flag.Int("proftrigger_cpu_thresh", 80, "profiler cpu threshold")        // "if this much percent cpu used, trigger a profile"

	debug       = flag.Bool("debug", false, "log outgoing metrics, bad lines, and received admin commands")
	showVersion = flag.Bool("version", false, "print version string")
	config_file = flag.String("config_file", "/etc/statsdaemon.ini", "config file location")
	cpuprofile  = flag.String("cpuprofile", "", "write cpu profile to file")
	memprofile  = flag.String("memprofile", "", "write memory profile to this file")
	GitHash     = "(none)"
)

func expand_cfg_vars(in string) (out string) {
	switch in {
	case "HOST":
		hostname, _ := os.Hostname()
		// in case hostname is an fqdn or has dots, only take first part
		parts := strings.SplitN(hostname, ".", 2)
		return parts[0]
	default:
		return ""
	}
}
func main() {
	flag.Parse()

	if *showVersion {
		fmt.Printf("statsdaemon v%s (built w/%s, git hash %s)\n", VERSION, runtime.Version(), GitHash)
		return
	}
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		defer f.Close()
		defer pprof.WriteHeapProfile(f)
	}

	path := ""
	if _, err := os.Stat(*config_file); err == nil {
		path = *config_file
        }
	conf, err := globalconf.NewWithOptions(&globalconf.Options{
		Filename:  path,
		EnvPrefix: "SD_",
        })

	conf.ParseAll()

	proftrigHeapFreq := dur.MustParseUsec("proftrigger_heap_freq", *proftrigHeapFreqStr)
	proftrigHeapMinDiff := int(dur.MustParseUNsec("proftrigger_heap_min_diff", *proftrigHeapMinDiffStr))

	proftrigCpuFreq := dur.MustParseUsec("proftrigger_cpu_freq", *proftrigCpuFreqStr)
	proftrigCpuMinDiff := int(dur.MustParseUNsec("proftrigger_cpu_min_diff", *proftrigCpuMinDiffStr))
	proftrigCpuDur := int(dur.MustParseUNsec("proftrigger_cpu_dur", *proftrigCpuDurStr))

	if proftrigHeapFreq > 0 {
		errors := make(chan error)
		trigger, _ := heap.New(*proftrigPath, *proftrigHeapThresh, proftrigHeapMinDiff, time.Duration(proftrigHeapFreq)*time.Second, errors)
		go func() {
			for e := range errors {
				log.Printf("profiletrigger heap: %s", e)
			}
		}()
		go trigger.Run()
	}

	if proftrigCpuFreq > 0 {
		errors := make(chan error)
		freq := time.Duration(proftrigCpuFreq) * time.Second
		duration := time.Duration(proftrigCpuDur) * time.Second
		trigger, _ := cpu.New(*proftrigPath, *proftrigCpuThresh, proftrigCpuMinDiff, freq, duration, errors)
		go func() {
			for e := range errors {
				log.Printf("profiletrigger cpu: %s", e)
			}
		}()
		go trigger.Run()
	}

	runtime.GOMAXPROCS(*processes)
	pct, err := out.NewPercentiles(*percentile_thresholds)
	if err != nil {
		log.Fatal(err)
	}
	inst := os.Expand(*instance, expand_cfg_vars)
	if inst == "" {
		inst = "null"
	}

	signalchan := make(chan os.Signal, 1)
	signal.Notify(signalchan)
	if *profile_addr != "" {
		go func() {
			fmt.Println("Profiling endpoint listening on " + *profile_addr)
			log.Println(http.ListenAndServe(*profile_addr, nil))
		}()
	}

	formatter := out.Formatter{
		PrefixInternal: "service_is_statsdaemon.instance_is_" + inst + ".",

		Legacy_namespace: *legacy_namespace,
		Prefix_counters:  *prefix_counters,
		Prefix_gauges:    *prefix_gauges,
		Prefix_rates:     *prefix_rates,
		Prefix_timers:    *prefix_timers,

		Prefix_m20_counters: *prefix_m20_counters,
		Prefix_m20_gauges:   *prefix_m20_gauges,
		Prefix_m20_rates:    *prefix_m20_rates,
		Prefix_m20_timers:   *prefix_m20_timers,

		Prefix_m20ne_counters: strings.Replace(*prefix_m20_counters, "=", "_is_", -1),
		Prefix_m20ne_gauges:   strings.Replace(*prefix_m20_gauges, "=", "_is_", -1),
		Prefix_m20ne_rates:    strings.Replace(*prefix_m20_rates, "=", "_is_", -1),
		Prefix_m20ne_timers:   strings.Replace(*prefix_m20_timers, "=", "_is_", -1),
	}

	daemon := statsdaemon.New(inst, formatter, *flush_rates, *flush_counts, *pct, *flushInterval, MAX_UNPROCESSED_PACKETS, *max_timers_per_s, *debug, signalchan)
	if *debug {
		consumer := make(chan interface{}, 100)
		daemon.Invalid_lines.Register(consumer)
		go func() {
			for line := range consumer {
				log.Printf("invalid line '%s'\n", line)
			}
		}()
	}
	daemon.Run(*listen_addr, *admin_addr, *graphite_addr)
}
<commit_msg>issue#32 bring default ini into sync with app defaults<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"strings"
	"time"

	"github.com/Dieterbe/profiletrigger/cpu"
	"github.com/Dieterbe/profiletrigger/heap"
	"github.com/raintank/dur"
	"github.com/raintank/statsdaemon"
	"github.com/raintank/statsdaemon/out"

	"net/http"
	_ "net/http/pprof"

	"github.com/grafana/globalconf"
)

const (
	VERSION = "0.6"
	// number of packets we can read out of udp buffer without processing them
	// statsdaemon doesn't really interrupt the udp reader like some other statsd's do (like on flush)
	// but this can still be useful to deal with traffic bursts.
	// keep in mind that one metric is about 30 to 100 bytes of memory.
	MAX_UNPROCESSED_PACKETS = 1000
)

var (

	listen_addr     = flag.String("listen_addr", ":8125", "listener address for statsd, listens on UDP only")
	admin_addr      = flag.String("admin_addr", ":8126", "listener address for admin port")
	profile_addr    = flag.String("profile_addr", "", "listener address for profiler")
	graphite_addr   = flag.String("graphite_addr", "127.0.0.1:2003", "graphite carbon-in url")
	flushInterval   = flag.Int("flush_interval", 60, "flush interval in seconds")
	processes       = flag.Int("processes", 4, "number of processes to use")
	instance        = flag.String("instance", "$HOST", "instance name, defaults to short hostname if not set")
	prefix_counters = flag.String("prefix_counters", "stats_counts.", "counters prefix")
	prefix_gauges   = flag.String("prefix_gauges", "stats.gauges.", "gauges prefix")
	prefix_rates    = flag.String("prefix_rates", "stats.", "rates prefix, it is recommended that you use stats.rates if possible")
	prefix_timers   = flag.String("prefix_timers", "stats.timers.", "timers prefix")

	prefix_m20_counters = flag.String("prefix_m20_counters", "", "counters 2.0 prefix")
	prefix_m20_gauges   = flag.String("prefix_m20_gauges", "", "gauges 2.0 prefix")
	prefix_m20_rates    = flag.String("prefix_m20_rates", "", "rates 2.0 prefix")
	prefix_m20_timers   = flag.String("prefix_m20_timers", "", "timers 2.0 prefix")

	legacy_namespace = flag.Bool("legacy_namespace", true, "legacy namespacing (not recommended)")
	flush_rates      = flag.Bool("flush_rates", true, "send count for counters (using prefix_counters)")
	flush_counts     = flag.Bool("flush_counts", false, "send count for counters (using prefix_counters)")

	percentile_thresholds = flag.String("percentile_thresholds", "90,75", "percential thresholds (used by timers)")
	max_timers_per_s      = flag.Uint64("max_timers_per_s", 1000, "max timers per second")

	proftrigPath = flag.String("proftrigger_path", "/tmp/profiletrigger", "profiler file path") // "path to store triggered profiles"

	proftrigHeapFreqStr    = flag.String("proftrigger_heap_freq", "0", "profiler heap frequency")   // "inspect status frequency. set to 0 to disable"
	proftrigHeapMinDiffStr = flag.String("proftrigger_heap_min_diff", "1h", "profiler heap min difference") // "minimum time between triggered profiles"
	proftrigHeapThresh     = flag.Int("proftrigger_heap_thresh", 10000000, "profiler heap threshold")  // "if this many bytes allocated, trigger a profile"

	proftrigCpuFreqStr    = flag.String("proftrigger_cpu_freq", "0", "profiler cpu frequency")    // "inspect status frequency. set to 0 to disable"
	proftrigCpuMinDiffStr = flag.String("proftrigger_cpu_min_diff", "1h", "profiler cpu min difference") // "minimum time between triggered profiles"
	proftrigCpuDurStr     = flag.String("proftrigger_cpu_dur", "5s", "profiler cpu duration")      // "duration of cpu profile"
	proftrigCpuThresh     = flag.Int("proftrigger_cpu_thresh", 80, "profiler cpu threshold")        // "if this much percent cpu used, trigger a profile"

	debug       = flag.Bool("debug", false, "log outgoing metrics, bad lines, and received admin commands")
	showVersion = flag.Bool("version", false, "print version string")
	config_file = flag.String("config_file", "/etc/statsdaemon.ini", "config file location")
	cpuprofile  = flag.String("cpuprofile", "", "write cpu profile to file")
	memprofile  = flag.String("memprofile", "", "write memory profile to this file")
	GitHash     = "(none)"
)

func expand_cfg_vars(in string) (out string) {
	switch in {
	case "HOST":
		hostname, _ := os.Hostname()
		// in case hostname is an fqdn or has dots, only take first part
		parts := strings.SplitN(hostname, ".", 2)
		return parts[0]
	default:
		return ""
	}
}
func main() {
	flag.Parse()

	if *showVersion {
		fmt.Printf("statsdaemon v%s (built w/%s, git hash %s)\n", VERSION, runtime.Version(), GitHash)
		return
	}
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		defer f.Close()
		defer pprof.WriteHeapProfile(f)
	}

	path := ""
	if _, err := os.Stat(*config_file); err == nil {
		path = *config_file
        }
	conf, err := globalconf.NewWithOptions(&globalconf.Options{
		Filename:  path,
		EnvPrefix: "SD_",
        })

	conf.ParseAll()

	proftrigHeapFreq := dur.MustParseUsec("proftrigger_heap_freq", *proftrigHeapFreqStr)
	proftrigHeapMinDiff := int(dur.MustParseUNsec("proftrigger_heap_min_diff", *proftrigHeapMinDiffStr))

	proftrigCpuFreq := dur.MustParseUsec("proftrigger_cpu_freq", *proftrigCpuFreqStr)
	proftrigCpuMinDiff := int(dur.MustParseUNsec("proftrigger_cpu_min_diff", *proftrigCpuMinDiffStr))
	proftrigCpuDur := int(dur.MustParseUNsec("proftrigger_cpu_dur", *proftrigCpuDurStr))

	if proftrigHeapFreq > 0 {
		errors := make(chan error)
		trigger, _ := heap.New(*proftrigPath, *proftrigHeapThresh, proftrigHeapMinDiff, time.Duration(proftrigHeapFreq)*time.Second, errors)
		go func() {
			for e := range errors {
				log.Printf("profiletrigger heap: %s", e)
			}
		}()
		go trigger.Run()
	}

	if proftrigCpuFreq > 0 {
		errors := make(chan error)
		freq := time.Duration(proftrigCpuFreq) * time.Second
		duration := time.Duration(proftrigCpuDur) * time.Second
		trigger, _ := cpu.New(*proftrigPath, *proftrigCpuThresh, proftrigCpuMinDiff, freq, duration, errors)
		go func() {
			for e := range errors {
				log.Printf("profiletrigger cpu: %s", e)
			}
		}()
		go trigger.Run()
	}

	runtime.GOMAXPROCS(*processes)
	pct, err := out.NewPercentiles(*percentile_thresholds)
	if err != nil {
		log.Fatal(err)
	}
	inst := os.Expand(*instance, expand_cfg_vars)
	if inst == "" {
		inst = "null"
	}

	signalchan := make(chan os.Signal, 1)
	signal.Notify(signalchan)
	if *profile_addr != "" {
		go func() {
			fmt.Println("Profiling endpoint listening on " + *profile_addr)
			log.Println(http.ListenAndServe(*profile_addr, nil))
		}()
	}

	formatter := out.Formatter{
		PrefixInternal: "service_is_statsdaemon.instance_is_" + inst + ".",

		Legacy_namespace: *legacy_namespace,
		Prefix_counters:  *prefix_counters,
		Prefix_gauges:    *prefix_gauges,
		Prefix_rates:     *prefix_rates,
		Prefix_timers:    *prefix_timers,

		Prefix_m20_counters: *prefix_m20_counters,
		Prefix_m20_gauges:   *prefix_m20_gauges,
		Prefix_m20_rates:    *prefix_m20_rates,
		Prefix_m20_timers:   *prefix_m20_timers,

		Prefix_m20ne_counters: strings.Replace(*prefix_m20_counters, "=", "_is_", -1),
		Prefix_m20ne_gauges:   strings.Replace(*prefix_m20_gauges, "=", "_is_", -1),
		Prefix_m20ne_rates:    strings.Replace(*prefix_m20_rates, "=", "_is_", -1),
		Prefix_m20ne_timers:   strings.Replace(*prefix_m20_timers, "=", "_is_", -1),
	}

	daemon := statsdaemon.New(inst, formatter, *flush_rates, *flush_counts, *pct, *flushInterval, MAX_UNPROCESSED_PACKETS, *max_timers_per_s, *debug, signalchan)
	if *debug {
		consumer := make(chan interface{}, 100)
		daemon.Invalid_lines.Register(consumer)
		go func() {
			for line := range consumer {
				log.Printf("invalid line '%s'\n", line)
			}
		}()
	}
	daemon.Run(*listen_addr, *admin_addr, *graphite_addr)
}
<|endoftext|>


<commit_before>package api

import (
	"context"
	"database/sql"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/FederationOfFathers/dashboard/config"
	"github.com/bwmarrin/discordgo"
	"github.com/jinzhu/gorm"
	"go.uber.org/zap"
	"golang.org/x/oauth2"
)

var discordEndpoint = oauth2.Endpoint{
	AuthURL:  "https://discordapp.com/api/oauth2/authorize",
	TokenURL: "https://discordapp.com/api/oauth2/token",
}

var conf = &oauth2.Config{
	ClientID:     "",
	ClientSecret: "",
	Scopes:       []string{"identify"},
	Endpoint:     discordEndpoint,
}

const host = "https://dashboard.fofgaming.com" //TODO make public hostname configurable
const redirectVerifyURL = host + "/api/v1/oauth/discord/verify"
const redirectLoginURL = host + "/api/v1/oauth/discord/login"

func init() {
	// could have more OAuth configs here
	initDiscordOauth()
}

func initDiscordOauth() {

	if config.DiscordConfig != nil {
		// Discord OAuth2
		conf.ClientID = config.DiscordConfig.ClientId
		conf.ClientSecret = config.DiscordConfig.Secret

		Router.Path("/api/v1/oauth/discord").Methods("GET").HandlerFunc(discordOauthHandler)
		Router.Path("/api/v1/oauth/discord/verify").Methods("GET").Handler(authenticated(discordOauthVerify))
		Router.Path("/api/v1/oauth/discord/login").Methods("GET").HandlerFunc(discordOauthVerify)
	} else {
		Router.Path("/api/v1/oauth/discord").Methods("GET").HandlerFunc(NotImplemented)
	}

}

func discordOauthHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain")

	conf.RedirectURL = redirectVerifyURL
	authURL := conf.AuthCodeURL("asdasdasd13424yhion2f0") // TODO get proper state
	json.NewEncoder(w).Encode(authURL)

}

func discordOauthVerify(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	code := query.Get("code")
	state := query.Get("state")

	// get authenticated user
	id := getMemberID(r)

	// if id == 0, then this is a login, not a sync
	isAuthenticated := id != ""

	// IMPORTANT - set the redirect URL, without this OAuth will fail
	if strings.HasSuffix(r.URL.Path, "verify") {
		conf.RedirectURL = redirectVerifyURL
	} else {
		conf.RedirectURL = redirectLoginURL
	}

	if code == "" || state == "" {
		Logger.Error("bad request", zap.String("code", code), zap.String("state", state))
		w.WriteHeader(http.StatusBadRequest)
	} else {

		// exchange code for a user token
		ctx := context.Background()
		token, err := conf.Exchange(ctx, code)
		if err != nil {
			Logger.Error("Could not get token",
				zap.String("code", code),
				zap.String("state", state),
				zap.String("id", id),
				zap.Strings("scopes", conf.Scopes),
				zap.String("redirecturi", conf.RedirectURL),
				zap.Error(err))
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		// create a new client with the token and get the user/@me endpoint
		client := conf.Client(ctx, token)
		res, err := client.Get("https://discordapp.com/api/users/@me")
		if err != nil {
			Logger.Error("Could not get user object", zap.Error(err))
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		// unmarshall the Body to a User{}
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			Logger.Error("Could not parse body", zap.Error(err))
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		userObj := discordgo.User{}
		err = json.Unmarshal(body, &userObj)
		if err != nil {
			Logger.Error("Could not parse JSON", zap.Error(err))
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		// unauthenticated user
		if !isAuthenticated {
			member, err := DB.MemberByDiscordID(userObj.ID)
			if err == gorm.ErrRecordNotFound || err == sql.ErrNoRows {
				w.WriteHeader(http.StatusForbidden)
				w.Write([]byte("not authorizes"))
				return
			} else if err != nil {
				Logger.Error("unable to check member", zap.String("discordid", userObj.ID), zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte("error"))
			}

			// set auth cookie and redirect
			authorize("", member.ID, w, r)

		} else {
			member, err := DB.MemberByAny(id)
			if err != nil {
				Logger.Error("could not find member", zap.String("member_id", id), zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte("error"))
				return
			}

			// set discord id
			member.Discord = userObj.ID

			if err := member.Save(); err != nil {
				Logger.Error("unable to save discord id", zap.Int("member", member.ID), zap.String("discord id", userObj.ID), zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte("error"))
				return
			}
		}

		// redirect
		http.Redirect(w, r, "https://ui.fofgaming.com/#main=members", http.StatusTemporaryRedirect)
	}

}
<commit_msg>Fixing redirect to home<commit_after>package api

import (
	"context"
	"database/sql"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/FederationOfFathers/dashboard/config"
	"github.com/bwmarrin/discordgo"
	"github.com/jinzhu/gorm"
	"go.uber.org/zap"
	"golang.org/x/oauth2"
)

var discordEndpoint = oauth2.Endpoint{
	AuthURL:  "https://discordapp.com/api/oauth2/authorize",
	TokenURL: "https://discordapp.com/api/oauth2/token",
}

var conf = &oauth2.Config{
	ClientID:     "",
	ClientSecret: "",
	Scopes:       []string{"identify"},
	Endpoint:     discordEndpoint,
}

const host = "https://dashboard.fofgaming.com" //TODO make public hostname configurable
const redirectVerifyURL = host + "/api/v1/oauth/discord/verify"
const redirectLoginURL = host + "/api/v1/oauth/discord/login"

func init() {
	// could have more OAuth configs here
	initDiscordOauth()
}

func initDiscordOauth() {

	if config.DiscordConfig != nil {
		// Discord OAuth2
		conf.ClientID = config.DiscordConfig.ClientId
		conf.ClientSecret = config.DiscordConfig.Secret

		Router.Path("/api/v1/oauth/discord").Methods("GET").HandlerFunc(discordOauthHandler)
		Router.Path("/api/v1/oauth/discord/verify").Methods("GET").Handler(authenticated(discordOauthVerify))
		Router.Path("/api/v1/oauth/discord/login").Methods("GET").HandlerFunc(discordOauthVerify)
	} else {
		Router.Path("/api/v1/oauth/discord").Methods("GET").HandlerFunc(NotImplemented)
	}

}

func discordOauthHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain")

	conf.RedirectURL = redirectVerifyURL
	authURL := conf.AuthCodeURL("asdasdasd13424yhion2f0") // TODO get proper state
	json.NewEncoder(w).Encode(authURL)

}

func discordOauthVerify(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	code := query.Get("code")
	state := query.Get("state")

	// get authenticated user
	id := getMemberID(r)

	// if id == 0, then this is a login, not a sync
	isAuthenticated := id != ""

	// IMPORTANT - set the redirect URL, without this OAuth will fail
	if strings.HasSuffix(r.URL.Path, "verify") {
		conf.RedirectURL = redirectVerifyURL
	} else {
		conf.RedirectURL = redirectLoginURL
	}

	if code == "" || state == "" {
		Logger.Error("bad request", zap.String("code", code), zap.String("state", state))
		w.WriteHeader(http.StatusBadRequest)
	} else {

		// exchange code for a user token
		ctx := context.Background()
		token, err := conf.Exchange(ctx, code)
		if err != nil {
			Logger.Error("Could not get token",
				zap.String("code", code),
				zap.String("state", state),
				zap.String("id", id),
				zap.Strings("scopes", conf.Scopes),
				zap.String("redirecturi", conf.RedirectURL),
				zap.Error(err))
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		// create a new client with the token and get the user/@me endpoint
		client := conf.Client(ctx, token)
		res, err := client.Get("https://discordapp.com/api/users/@me")
		if err != nil {
			Logger.Error("Could not get user object", zap.Error(err))
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		// unmarshall the Body to a User{}
		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			Logger.Error("Could not parse body", zap.Error(err))
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		userObj := discordgo.User{}
		err = json.Unmarshal(body, &userObj)
		if err != nil {
			Logger.Error("Could not parse JSON", zap.Error(err))
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		// unauthenticated user
		if !isAuthenticated {
			member, err := DB.MemberByDiscordID(userObj.ID)
			if err == gorm.ErrRecordNotFound || err == sql.ErrNoRows {
				w.WriteHeader(http.StatusForbidden)
				w.Write([]byte("not authorizes"))
				return
			} else if err != nil {
				Logger.Error("unable to check member", zap.String("discordid", userObj.ID), zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte("error"))
			}

			// set auth cookie and redirect
			authorize("", member.ID, w, r)

		} else {
			member, err := DB.MemberByAny(id)
			if err != nil {
				Logger.Error("could not find member", zap.String("member_id", id), zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte("error"))
				return
			}

			// set discord id
			member.Discord = userObj.ID

			if err := member.Save(); err != nil {
				Logger.Error("unable to save discord id", zap.Int("member", member.ID), zap.String("discord id", userObj.ID), zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte("error"))
				return
			}
		}

		// redirect
		http.Redirect(w, r, "https://ui.fofgaming.com/", http.StatusTemporaryRedirect)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/Symantec/Dominator/lib/filesystem"
	objclient "github.com/Symantec/Dominator/lib/objectserver/client"
	"github.com/Symantec/Dominator/lib/triggers"
	"github.com/Symantec/Dominator/proto/sub"
	"github.com/Symantec/Dominator/sub/client"
	"os"
	"syscall"
)

func pushFileSubcommand(getSubClient getSubClientFunc, args []string) {
	if err := pushFile(getSubClient, args[0], args[1]); err != nil {
		fmt.Fprintf(os.Stderr, "Error pushing file: %s\n", err)
		os.Exit(2)
	}
	os.Exit(0)
}

func pushFile(getSubClient getSubClientFunc, source, dest string) error {
	var sourceStat syscall.Stat_t
	if err := syscall.Stat(source, &sourceStat); err != nil {
		return err
	}
	sourceFile, err := os.Open(source)
	if err != nil {
		return err
	}
	defer sourceFile.Close()
	srpcClient := getSubClient()
	objClient := objclient.AttachObjectClient(srpcClient)
	defer objClient.Close()
	if err != nil {
		return err
	}
	hashVal, _, err := objClient.AddObject(sourceFile, uint64(sourceStat.Size),
		nil)
	if err != nil {
		return err
	}
	newRegularInode := &filesystem.RegularInode{
		Mode:             filesystem.FileMode(sourceStat.Mode),
		Uid:              sourceStat.Uid,
		Gid:              sourceStat.Gid,
		MtimeNanoSeconds: int32(sourceStat.Mtim.Nsec),
		MtimeSeconds:     sourceStat.Mtim.Sec,
		Size:             uint64(sourceStat.Size),
		Hash:             hashVal}
	newInode := sub.Inode{Name: dest, GenericInode: newRegularInode}
	var updateRequest sub.UpdateRequest
	var updateReply sub.UpdateResponse
	updateRequest.Wait = true
	updateRequest.InodesToMake = append(updateRequest.InodesToMake, newInode)
	if *triggersFile != "" {
		updateRequest.Triggers, err = triggers.Load(*triggersFile)
		if err != nil {
			return err
		}
	} else if *triggersString != "" {
		updateRequest.Triggers, err = triggers.Decode([]byte(*triggersString))
		if err != nil {
			return err
		}
	}
	return client.CallUpdate(srpcClient, updateRequest, &updateReply)
}
<commit_msg>Subtool: fix build on MacOS and show update time in push-file subcommand.<commit_after>package main

import (
	"fmt"
	"github.com/Symantec/Dominator/lib/filesystem"
	objclient "github.com/Symantec/Dominator/lib/objectserver/client"
	"github.com/Symantec/Dominator/lib/triggers"
	"github.com/Symantec/Dominator/lib/wsyscall"
	"github.com/Symantec/Dominator/proto/sub"
	"github.com/Symantec/Dominator/sub/client"
	"os"
)

func pushFileSubcommand(getSubClient getSubClientFunc, args []string) {
	if err := pushFile(getSubClient, args[0], args[1]); err != nil {
		fmt.Fprintf(os.Stderr, "Error pushing file: %s\n", err)
		os.Exit(2)
	}
	os.Exit(0)
}

func pushFile(getSubClient getSubClientFunc, source, dest string) error {
	var sourceStat wsyscall.Stat_t
	if err := wsyscall.Stat(source, &sourceStat); err != nil {
		return err
	}
	sourceFile, err := os.Open(source)
	if err != nil {
		return err
	}
	defer sourceFile.Close()
	srpcClient := getSubClient()
	objClient := objclient.AttachObjectClient(srpcClient)
	defer objClient.Close()
	if err != nil {
		return err
	}
	hashVal, _, err := objClient.AddObject(sourceFile, uint64(sourceStat.Size),
		nil)
	if err != nil {
		return err
	}
	newRegularInode := &filesystem.RegularInode{
		Mode:             filesystem.FileMode(sourceStat.Mode),
		Uid:              sourceStat.Uid,
		Gid:              sourceStat.Gid,
		MtimeNanoSeconds: int32(sourceStat.Mtim.Nsec),
		MtimeSeconds:     sourceStat.Mtim.Sec,
		Size:             uint64(sourceStat.Size),
		Hash:             hashVal}
	newInode := sub.Inode{Name: dest, GenericInode: newRegularInode}
	var updateRequest sub.UpdateRequest
	var updateReply sub.UpdateResponse
	updateRequest.Wait = true
	updateRequest.InodesToMake = append(updateRequest.InodesToMake, newInode)
	if *triggersFile != "" {
		updateRequest.Triggers, err = triggers.Load(*triggersFile)
		if err != nil {
			return err
		}
	} else if *triggersString != "" {
		updateRequest.Triggers, err = triggers.Decode([]byte(*triggersString))
		if err != nil {
			return err
		}
	}
	startTime := showStart("Subd.Update()")
	err = client.CallUpdate(srpcClient, updateRequest, &updateReply)
	showTimeTaken(startTime)
	return err
}
<|endoftext|>


<commit_before>package venom

import (
	"sort"
	"strings"

	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

// Teach viper to search FOO_BAR for every --foo-bar key instead of
// the default FOO-BAR.
func AutomaticEnv(flags *pflag.FlagSet, viperMaybe ...*viper.Viper) {
	v := viper.GetViper()
	if len(viperMaybe) != 0 {
		v = viperMaybe[0]
	}

	replaceMap := make(map[string]string, flags.NFlag())
	flags.VisitAll(func(f *pflag.Flag) {
		name := strings.ToUpper(f.Name)
		replaceMap[name] = strings.Replace(name, "-", "_", -1)
	})

	keys := make([]string, 0, len(replaceMap))
	for k := range replaceMap {
		keys = append(keys, k)
	}

	// Reverse sort keys, this is to make sure foo-bar comes before foo. This is to prevent
	// foo being triggered when foo-bar is given to string replacer.
	sort.Sort(sort.Reverse(sort.StringSlice(keys)))

	values := make([]string, 0, 2*len(keys))
	for _, k := range keys {
		values = append(values, k)
		values = append(values, replaceMap[k])
	}

	v.SetEnvKeyReplacer(strings.NewReplacer(values...))
	v.AutomaticEnv()
}

func TwelveFactor(name string, flags *pflag.FlagSet, viperMaybe ...*viper.Viper) error {
	v := viper.GetViper()
	if len(viperMaybe) != 0 {
		v = viperMaybe[0]
	}

	// Bind flags and configuration keys 1-to-1
	err := v.BindPFlags(flags)
	if err != nil {
		return err
	}

	// Set env prefix
	v.SetEnvPrefix(strings.ToUpper(name))

	// Patch automatic env
	AutomaticEnv(flags, v)

	return nil
}
<commit_msg>Moar docs<commit_after>package venom

import (
	"sort"
	"strings"

	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

// Teach viper to search FOO_BAR for every --foo-bar key instead of
// the default FOO-BAR.
func AutomaticEnv(flags *pflag.FlagSet, viperMaybe ...*viper.Viper) {
	v := viper.GetViper()
	if len(viperMaybe) != 0 {
		v = viperMaybe[0]
	}

	replaceMap := make(map[string]string, flags.NFlag())
	flags.VisitAll(func(f *pflag.Flag) {
		name := strings.ToUpper(f.Name)
		replaceMap[name] = strings.Replace(name, "-", "_", -1)
	})

	keys := make([]string, 0, len(replaceMap))
	for k := range replaceMap {
		keys = append(keys, k)
	}

	// Reverse sort keys, this is to make sure foo-bar comes before foo. This is to prevent
	// foo being triggered when foo-bar is given to string replacer.
	sort.Sort(sort.Reverse(sort.StringSlice(keys)))

	values := make([]string, 0, 2*len(keys))
	for _, k := range keys {
		values = append(values, k)
		values = append(values, replaceMap[k])
	}

	v.SetEnvKeyReplacer(strings.NewReplacer(values...))
	v.AutomaticEnv()
}

// Configure viper to automatically check environment variables for all flags in the provided flags.
func TwelveFactor(name string, flags *pflag.FlagSet, viperMaybe ...*viper.Viper) error {
	v := viper.GetViper()
	if len(viperMaybe) != 0 {
		v = viperMaybe[0]
	}

	// Bind flags and configuration keys 1-to-1
	err := v.BindPFlags(flags)
	if err != nil {
		return err
	}

	// Set env prefix
	v.SetEnvPrefix(strings.ToUpper(name))

	// Patch automatic env
	AutomaticEnv(flags, v)

	return nil
}
<|endoftext|>


<commit_before>/*
The Venue package exposes the Avid™ VENUE VNC interface as a programmatic API.
*/
package venue

import (
	"flag"
	"fmt"
	"image"
	"log"
	"net"
	"strconv"
	"time"

	vnc "github.com/kward/go-vnc"
	"golang.org/x/net/context"
)

const (
	errPrefix = "Venue error."
	numInputs = 48
)

var (
	refresh = flag.Duration("venue_refresh", 1000*time.Millisecond, "framebuffer refresh period.")
	timeout = flag.Duration("venue_timeout", 10*time.Second, "timeout for Venue connection.")
	debug   = flag.Bool("venue_debug", false, "enable debugging output")
)

// Venue holds information representing the state of the VENUE backend.
type Venue struct {
	host string
	port uint
	cfg  *vnc.ClientConfig
	conn *vnc.ClientConn
	fb   *Framebuffer

	inputs    [numInputs]*Input
	currInput *Input

	outputs    map[string]*Output
	currOutput *Output

	Pages    VenuePages
	currPage int
}

// NewVenue returns a populated Venue struct.
func NewVenue(host string, port uint, passwd string) *Venue {
	cfg := vnc.NewClientConfig(passwd)
	return &Venue{host: host, port: port, cfg: cfg}
}

// Connect to a VENUE console.
func (v *Venue) Connect(ctx context.Context) error {
	if v.conn != nil {
		return fmt.Errorf("%v Already connected.", errPrefix)
	}

	log.Println("Connecting...")
	addr := v.host + ":" + strconv.FormatUint(uint64(v.port), 10)
	netConn, err := net.DialTimeout("tcp", addr, *timeout)
	if err != nil {
		return fmt.Errorf("%v Error connecting to host. %v", errPrefix, err)
	}

	var cancel context.CancelFunc
	if _, ok := ctx.Deadline(); ok {
		ctx, cancel = context.WithCancel(ctx)
	} else {
		ctx, cancel = context.WithTimeout(ctx, *timeout)
	}
	defer cancel()

	log.Println("Establishing...")
	ctx = context.WithValue(ctx, "debug", *debug)
	vncConn, err := vnc.Connect(ctx, netConn, v.cfg)
	if err != nil {
		return fmt.Errorf("%v Could not establish session. %v", errPrefix, err)
	}
	v.conn = vncConn
	return nil
}

// Close a connection to a VENUE console.
func (v *Venue) Close() error {
	return v.conn.Close()
}

// Initialize the in-memory state representation of a VENUE console.
func (v *Venue) Initialize() {
	// Create image to apply framebuffer updates to.
	v.fb = NewFramebuffer(int(v.conn.FramebufferWidth()), int(v.conn.FramebufferHeight()))

	// Setup channel to listen to server messages.
	v.cfg.ServerMessageCh = make(chan vnc.ServerMessage)

	// Initialize pages.
	v.Pages = VenuePages{}
	v.Pages[InputsPage] = NewInputsPage()
	v.Pages[OutputsPage] = NewOutputsPage()
	// Initialize inputs.
	for ch := 0; ch < numInputs; ch++ {
		input := NewInput(v, ch+1, Ichannel)
		v.inputs[ch] = input
	}

	// Choose something besides input page, so that later when the Inputs page is
	// selected, it shows first bank of channels.
	v.SetPage(OptionsPage)
	v.SetInput(1)

	// Clear solo.
	log.Println("Clearing solo.")
	vp := v.Pages[InputsPage]
	e := vp.Elements["solo_clear"]
	e.(*Switch).Update(v)
}

// ListenAndHandle VNC server messages.
func (v *Venue) ListenAndHandle() {
	log.Println("ListenAndHandle()")
	go v.conn.ListenAndHandle()
	for {
		msg := <-v.cfg.ServerMessageCh
		switch msg.Type() {
		case vnc.FramebufferUpdateMsg:
			log.Println("ListenAndHandle() FramebufferUpdateMessage")
			for i := uint16(0); i < msg.(*vnc.FramebufferUpdate).NumRect; i++ {
				var colors []vnc.Color
				rect := msg.(*vnc.FramebufferUpdate).Rects[i]
				switch rect.Enc.Type() {
				case vnc.Raw:
					colors = rect.Enc.(*vnc.RawEncoding).Colors
				}
				v.fb.Paint(v, rect, colors)
			}

		default:
			log.Printf("ListenAndHandle() unknown message type:%v msg:%v\n", msg.Type(), msg)
		}
	}
}

// FramebufferRefresh refreshes the local framebuffer image of the VNC server.
func (v *Venue) FramebufferRefresh() {
	screen := image.Rectangle{image.Point{0, 0}, image.Point{v.fb.Width, v.fb.Height}}
	for {
		if err := v.Snapshot(screen); err != nil {
			log.Fatal(err)
		}
		time.Sleep(*refresh)
	}
}

// Snapshot requests updated image info from the VNC server.
func (v *Venue) Snapshot(r image.Rectangle) error {
	log.Printf("Snapshot(%v)\n", r)
	w, h := uint16(r.Max.X-r.Min.X), uint16(r.Max.Y-r.Min.Y)
	if err := v.conn.FramebufferUpdateRequest(
		vnc.RFBTrue, uint16(r.Min.X), uint16(r.Min.Y), w, h); err != nil {
		log.Println("Snapshot() error:", err)
		return err
	}
	return nil
}

// abs returns the absolute value of an int.
func abs(x int) int {
	switch {
	case x < 0:
		return -x
	case x == 0:
		return 0
	}
	return x
}
<commit_msg>added Ping()<commit_after>/*
The Venue package exposes the Avid™ VENUE VNC interface as a programmatic API.
*/
package venue

import (
	"flag"
	"fmt"
	"image"
	"log"
	"net"
	"strconv"
	"time"

	vnc "github.com/kward/go-vnc"
	"golang.org/x/net/context"
)

const (
	errPrefix = "Venue error."
	numInputs = 48
)

var (
	refresh = flag.Duration("venue_refresh", 1000*time.Millisecond, "framebuffer refresh period.")
	timeout = flag.Duration("venue_timeout", 10*time.Second, "timeout for Venue connection.")
	debug   = flag.Bool("venue_debug", false, "enable debugging output")
)

// Venue holds information representing the state of the VENUE backend.
type Venue struct {
	host string
	port uint
	cfg  *vnc.ClientConfig
	conn *vnc.ClientConn
	fb   *Framebuffer

	inputs    [numInputs]*Input
	currInput *Input

	outputs    map[string]*Output
	currOutput *Output

	Pages    VenuePages
	currPage int
}

// NewVenue returns a populated Venue struct.
func NewVenue(host string, port uint, passwd string) *Venue {
	cfg := vnc.NewClientConfig(passwd)
	return &Venue{host: host, port: port, cfg: cfg}
}

// Connect to a VENUE console.
func (v *Venue) Connect(ctx context.Context) error {
	if v.conn != nil {
		return fmt.Errorf("%v Already connected.", errPrefix)
	}

	log.Println("Connecting...")
	addr := v.host + ":" + strconv.FormatUint(uint64(v.port), 10)
	netConn, err := net.DialTimeout("tcp", addr, *timeout)
	if err != nil {
		return fmt.Errorf("%v Error connecting to host. %v", errPrefix, err)
	}

	var cancel context.CancelFunc
	if _, ok := ctx.Deadline(); ok {
		ctx, cancel = context.WithCancel(ctx)
	} else {
		ctx, cancel = context.WithTimeout(ctx, *timeout)
	}
	defer cancel()

	log.Println("Establishing...")
	ctx = context.WithValue(ctx, "debug", *debug)
	vncConn, err := vnc.Connect(ctx, netConn, v.cfg)
	if err != nil {
		return fmt.Errorf("%v Could not establish session. %v", errPrefix, err)
	}
	v.conn = vncConn
	return nil
}

// Close a connection to a VENUE console.
func (v *Venue) Close() error {
	return v.conn.Close()
}

// Initialize the in-memory state representation of a VENUE console.
func (v *Venue) Initialize() {
	// Create image to apply framebuffer updates to.
	v.fb = NewFramebuffer(int(v.conn.FramebufferWidth()), int(v.conn.FramebufferHeight()))

	// Setup channel to listen to server messages.
	v.cfg.ServerMessageCh = make(chan vnc.ServerMessage)

	// Initialize pages.
	v.Pages = VenuePages{}
	v.Pages[InputsPage] = NewInputsPage()
	v.Pages[OutputsPage] = NewOutputsPage()
	// Initialize inputs.
	for ch := 0; ch < numInputs; ch++ {
		input := NewInput(v, ch+1, Ichannel)
		v.inputs[ch] = input
	}

	// Choose something besides input page, so that later when the Inputs page is
	// selected, it shows first bank of channels.
	v.SetPage(OptionsPage)
	v.SetInput(1)

	// Clear solo.
	log.Println("Clearing solo.")
	vp := v.Pages[InputsPage]
	e := vp.Elements["solo_clear"]
	e.(*Switch).Update(v)
}

// ListenAndHandle VNC server messages.
func (v *Venue) ListenAndHandle() {
	log.Println("ListenAndHandle()")
	go v.conn.ListenAndHandle()
	for {
		msg := <-v.cfg.ServerMessageCh
		switch msg.Type() {
		case vnc.FramebufferUpdateMsg:
			log.Println("ListenAndHandle() FramebufferUpdateMessage")
			for i := uint16(0); i < msg.(*vnc.FramebufferUpdate).NumRect; i++ {
				var colors []vnc.Color
				rect := msg.(*vnc.FramebufferUpdate).Rects[i]
				switch rect.Enc.Type() {
				case vnc.Raw:
					colors = rect.Enc.(*vnc.RawEncoding).Colors
				}
				v.fb.Paint(v, rect, colors)
			}

		default:
			log.Printf("ListenAndHandle() unknown message type:%v msg:%v\n", msg.Type(), msg)
		}
	}
}

// FramebufferRefresh refreshes the local framebuffer image of the VNC server.
func (v *Venue) FramebufferRefresh() {
	screen := image.Rectangle{image.Point{0, 0}, image.Point{v.fb.Width, v.fb.Height}}
	for {
		if err := v.Snapshot(screen); err != nil {
			log.Fatal(err)
		}
		time.Sleep(*refresh)
	}
}

// Snapshot requests updated image info from the VNC server.
func (v *Venue) Snapshot(r image.Rectangle) error {
	log.Printf("Snapshot(%v)\n", r)
	w, h := uint16(r.Max.X-r.Min.X), uint16(r.Max.Y-r.Min.Y)
	if err := v.conn.FramebufferUpdateRequest(
		vnc.RFBTrue, uint16(r.Min.X), uint16(r.Min.Y), w, h); err != nil {
		log.Println("Snapshot() error:", err)
		return err
	}
	return nil
}

// Ping can be called whenever an OSC ping is received.
func (v *Venue) Ping() {
	v.conn.DebugMetrics()
}

// abs returns the absolute value of an int.
func abs(x int) int {
	switch {
	case x < 0:
		return -x
	case x == 0:
		return 0
	}
	return x
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"strconv"
	"strings"
)

var cmdScale = &Command{
	Run:   runScale,
	Usage: "scale type=n...",
	Short: "change dyno counts",
	Long: `
Scale changes the number of dynos for each process type.

Example:

	$ hk scale web=2 worker=5
`,
}

// takes args of the form "web=1", "worker=3", etc
func runScale(cmd *Command, args []string) {
	todo := make(map[string]int)
	for _, arg := range args {
		i := strings.IndexRune(arg, '=')
		if i < 0 {
			cmd.printUsage()
			os.Exit(2)
		}
		val, err := strconv.Atoi(arg[i+1:])
		if err != nil {
			cmd.printUsage()
			os.Exit(2)
		}
		todo[arg[:i]] = val
	}

	ch := make(chan error)
	for ps, n := range todo {
		go scale(mustApp(), ps, n, ch)
	}
	for _ = range todo {
		if err := <-ch; err != nil {
			log.Println(err)
		}
	}
}

func scale(app, ps string, n int, ch chan error) {
	data := map[string]int{"quantity": n}
	ch <- Patch(nil, "/apps/"+app+"/formation/"+ps, data)
}
<commit_msg>use client in scale command<commit_after>package main

import (
	"github.com/bgentry/heroku-go"
	"log"
	"os"
	"strconv"
	"strings"
)

var cmdScale = &Command{
	Run:   runScale,
	Usage: "scale type=n...",
	Short: "change dyno counts",
	Long: `
Scale changes the number of dynos for each process type.

Example:

	$ hk scale web=2 worker=5
`,
}

// takes args of the form "web=1", "worker=3", etc
func runScale(cmd *Command, args []string) {
	todo := make(map[string]int)
	for _, arg := range args {
		i := strings.IndexRune(arg, '=')
		if i < 0 {
			cmd.printUsage()
			os.Exit(2)
		}
		val, err := strconv.Atoi(arg[i+1:])
		if err != nil {
			cmd.printUsage()
			os.Exit(2)
		}
		todo[arg[:i]] = val
	}

	ch := make(chan error)
	for ps, n := range todo {
		go scale(mustApp(), ps, n, ch)
	}
	for _ = range todo {
		if err := <-ch; err != nil {
			log.Println(err)
		}
	}
}

func scale(app, ps string, n int, ch chan error) {
	_, err := client.FormationUpdate(app, ps, heroku.FormationUpdateOpts{Quantity: &n})
	ch <- err
}
<|endoftext|>