diff --git a/Dockerfile b/Dockerfile index 48e5fbba..cb0e8865 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,5 +8,4 @@ EXPOSE 80 COPY filebrowser /filebrowser -ENTRYPOINT [ "/filebrowser", "--database", "/database.db" ] -CMD [ "--scope /srv", "--port 80" ] +ENTRYPOINT [ "/filebrowser"] diff --git a/cmd/config_init.go b/cmd/config_init.go index cec455a0..0f7cb043 100644 --- a/cmd/config_init.go +++ b/cmd/config_init.go @@ -9,6 +9,7 @@ import ( "github.com/asdine/storm" "github.com/filebrowser/filebrowser/v2/settings" "github.com/spf13/cobra" + v "github.com/spf13/viper" ) func init() { @@ -28,6 +29,7 @@ to the defaults when creating new users and you don't override the options.`, Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { + databasePath := v.GetString("database") if _, err := os.Stat(databasePath); err == nil { panic(errors.New(databasePath + " already exists")) } diff --git a/cmd/import.go b/cmd/import.go index 2dd495cd..3465b64b 100644 --- a/cmd/import.go +++ b/cmd/import.go @@ -3,6 +3,7 @@ package cmd import ( "github.com/filebrowser/filebrowser/v2/storage/bolt/importer" "github.com/spf13/cobra" + v "github.com/spf13/viper" ) func init() { @@ -24,7 +25,7 @@ this version.`, oldDB := mustGetString(cmd, "old.database") oldConf := mustGetString(cmd, "old.config") - err := importer.Import(oldDB, oldConf, databasePath) + err := importer.Import(oldDB, oldConf, v.GetString("database")) checkErr(err) }, } diff --git a/cmd/root.go b/cmd/root.go index 9444dc4c..a267abd6 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -3,13 +3,13 @@ package cmd import ( "crypto/rand" "crypto/tls" - "errors" "io/ioutil" "log" "net" "net/http" "os" "strconv" + "strings" "github.com/asdine/storm" "github.com/filebrowser/filebrowser/v2/auth" @@ -18,26 +18,13 @@ import ( "github.com/filebrowser/filebrowser/v2/users" fbhttp "github.com/filebrowser/filebrowser/v2/http" + homedir "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" - "github.com/spf13/pflag" + // "github.com/spf13/pflag" + v "github.com/spf13/viper" lumberjack "gopkg.in/natefinch/lumberjack.v2" ) -var ( - databasePath string -) - -func init() { - rootCmd.PersistentFlags().StringVarP(&databasePath, "database", "d", "./filebrowser.db", "path to the database") - - rootCmd.Flags().StringP("address", "a", "", "address to listen on (default comes from database)") - rootCmd.Flags().StringP("log", "l", "", "log output (default comes from database)") - rootCmd.Flags().IntP("port", "p", 0, "port to listen on (default comes from database)") - rootCmd.Flags().StringP("cert", "c", "", "tls certificate (default comes from database)") - rootCmd.Flags().StringP("key", "k", "", "tls key (default comes from database)") - rootCmd.Flags().StringP("scope", "s", "", "scope for users") -} - var rootCmd = &cobra.Command{ Use: "filebrowser", Short: "A stylish web-based file browser", @@ -56,17 +43,211 @@ Use the available flags to override the database/default options. These flags values won't be persisted to the database. To persist configuration to the database use the command 'filebrowser config set'.`, Run: func(cmd *cobra.Command, args []string) { - if _, err := os.Stat(databasePath); os.IsNotExist(err) { - quickSetup(cmd) - } - db := getDB() defer db.Close() st := getStorage(db) - startServer(cmd, st) + startServer(st) }, } +var ( + cfgFile string +) + +// POSSIBLE WORKAROUND TO IDENTIFY WHEN DEFAULT VALUES ARE BEING USED +var defaults = struct { + database string + address string + log string + port int + scope string + admin string +}{ + "./filebrowser.db", + "127.0.0.1", + "stderr", + 80, + "/srv", + "admin", +} + +func init() { + cobra.OnInitialize(initConfig) + //rootCmd.SetVersionTemplate("File Browser {{printf \"version %s\" .Version}}\n") + + f := rootCmd.Flags() + pf := rootCmd.PersistentFlags() + + pf.StringVarP(&cfgFile, "config", "c", "", "config file (defaults are './.filebrowser[ext]', '$HOME/.filebrowser[ext]' or '/etc/filebrowser/.filebrowser[ext]')") + + vaddP(pf, "database", "d", "./filebrowser.db", "path to the database") + + vaddP(f, "address", "a", defaults.address, "address to listen on") + vaddP(f, "log", "l", defaults.log, "log output") + vaddP(f, "port", "p", defaults.port, "port to listen on") + vaddP(f, "cert", "t", "", "tls certificate (default comes from database)") + vaddP(f, "key", "k", "", "tls key (default comes from database)") + vaddP(f, "scope", "s", defaults.scope, "scope for users") + vaddP(f, "force", "f", false, "overwrite DB config with runtime params") + vaddP(f, "admin", "f", defaults.admin, "first username") + vaddP(f, "passwd", "f", "", "first username password hash") + vaddP(f, "baseurl", "b", "", "base URL") + + // Bind the full flag sets to the configuration + if err := v.BindPFlags(f); err != nil { + panic(err) + } + if err := v.BindPFlags(pf); err != nil { + panic(err) + } +} + +// initConfig reads in config file and ENV variables if set. +func initConfig() { + if cfgFile == "" { + // Find home directory. + home, err := homedir.Dir() + if err != nil { + panic(err) + } + v.AddConfigPath(".") + v.AddConfigPath(home) + v.AddConfigPath("/etc/filebrowser/") + v.SetConfigName(".filebrowser") + } else { + // Use config file from the flag. + v.SetConfigFile(cfgFile) + } + + v.SetEnvPrefix("FB") + v.AutomaticEnv() + v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + + if err := v.ReadInConfig(); err != nil { + if _, ok := err.(v.ConfigParseError); ok { + panic(err) + } + log.Println("No config file provided") + } else { + log.Println("Using config file:", v.ConfigFileUsed()) + } + + log.Println("FORCE:", v.GetBool("force")) + + /* + if DB exists + if force false + database has highest priority, if undefined in DB use config params + else + config params overwrite existing and non-existing params in DB + else + (quick)Setup with provided config params + */ + + /* + DISPLAY WARNINGS WHEN DEFAULT VALUES ARE USED + + This allows to know if a CLI flag was provided: + + log.Println(rootCmd.Flags().Changed("database")) + + However, that is not enough in order to know if a value came from a config file or from envvars. + This should allow so. But it seems not to work as expected (see spf13/viper#323): + + log.Println(v.IsSet("database")) + */ + + if _, err := os.Stat(v.GetString("database")); os.IsNotExist(err) { + quickSetup() + } + +} + +/* +func serverVisitAndReplace(s *settings.Settings) { + rootCmd.Flags().Visit(func(flag *pflag.Flag) { + switch flag.Name { + case "log": + s.Log = v.GetString(flag.Name) + case "address": + s.Server.Address = v.GetString(flag.Name) + case "port": + s.Server.Port = v.GetInt(flag.Name) + case "cert": + s.Server.TLSCert = v.GetString(flag.Name) + case "key": + s.Server.TLSKey = v.GetString(flag.Name) + } + }) +} +*/ + +func quickSetup() { + scope := v.GetString("scope") + if scope == defaults.scope { + log.Println("[WARN] Using default value '/srv' as param 'scope'") + } + + db, err := storm.Open(v.GetString("database")) + checkErr(err) + defer db.Close() + + set := &settings.Settings{ + Key: generateRandomBytes(64), // 256 bit + BaseURL: v.GetString("baseurl"), + Log: v.GetString("log"), + Signup: false, + AuthMethod: auth.MethodJSONAuth, + Server: settings.Server{ + Port: v.GetInt("port"), + Address: v.GetString("address"), + TLSCert: v.GetString("cert"), + TLSKey: v.GetString("key"), + }, + Defaults: settings.UserDefaults{ + Scope: scope, + Locale: "en", + Perm: users.Permissions{ + Admin: false, + Execute: true, + Create: true, + Rename: true, + Modify: true, + Delete: true, + Share: true, + Download: true, + }, + }, + } + + // serverVisitAndReplace(set) + st := getStorage(db) + + err = st.Settings.Save(set) + checkErr(err) + + err = st.Auth.Save(&auth.JSONAuth{}) + checkErr(err) + + password := v.GetString("password") + if password == "" { + password, err = users.HashPwd("admin") + checkErr(err) + } + + user := &users.User{ + Username: v.GetString("admin"), + Password: password, + LockPassword: false, + } + + set.Defaults.Apply(user) + user.Perm.Admin = true + + err = st.Users.Save(user) + checkErr(err) +} + func setupLogger(s *settings.Settings) { switch s.Log { case "stdout": @@ -85,91 +266,11 @@ func setupLogger(s *settings.Settings) { } } -func serverVisitAndReplace(cmd *cobra.Command, s *settings.Settings) { - cmd.Flags().Visit(func(flag *pflag.Flag) { - switch flag.Name { - case "log": - s.Log = mustGetString(cmd, flag.Name) - case "address": - s.Server.Address = mustGetString(cmd, flag.Name) - case "port": - s.Server.Port = mustGetInt(cmd, flag.Name) - case "cert": - s.Server.TLSCert = mustGetString(cmd, flag.Name) - case "key": - s.Server.TLSKey = mustGetString(cmd, flag.Name) - } - }) -} - -func quickSetup(cmd *cobra.Command) { - scope := mustGetString(cmd, "scope") - if scope == "" { - panic(errors.New("scope flag must be set for quick setup")) - } - - db, err := storm.Open(databasePath) - checkErr(err) - defer db.Close() - - set := &settings.Settings{ - Key: generateRandomBytes(64), // 256 bit - BaseURL: "", - Log: "stderr", - Signup: false, - AuthMethod: auth.MethodJSONAuth, - Server: settings.Server{ - Port: 0, - Address: "127.0.0.1", - TLSCert: mustGetString(cmd, "cert"), - TLSKey: mustGetString(cmd, "key"), - }, - Defaults: settings.UserDefaults{ - Scope: scope, - Locale: "en", - Perm: users.Permissions{ - Admin: false, - Execute: true, - Create: true, - Rename: true, - Modify: true, - Delete: true, - Share: true, - Download: true, - }, - }, - } - - serverVisitAndReplace(cmd, set) - st := getStorage(db) - - err = st.Settings.Save(set) - checkErr(err) - - err = st.Auth.Save(&auth.JSONAuth{}) - checkErr(err) - - password, err := users.HashPwd("admin") - checkErr(err) - - user := &users.User{ - Username: "admin", - Password: password, - LockPassword: false, - } - - set.Defaults.Apply(user) - user.Perm.Admin = true - - err = st.Users.Save(user) - checkErr(err) -} - -func startServer(cmd *cobra.Command, st *storage.Storage) { +func startServer(st *storage.Storage) { settings, err := st.Settings.Get() checkErr(err) - serverVisitAndReplace(cmd, settings) + // serverVisitAndReplace(settings) setupLogger(settings) handler, err := fbhttp.NewHandler(st) diff --git a/cmd/utils.go b/cmd/utils.go index 8b2520dd..689912de 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -8,8 +8,34 @@ import ( "github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/storage/bolt" "github.com/spf13/cobra" + "github.com/spf13/pflag" + v "github.com/spf13/viper" ) +func vaddP(f *pflag.FlagSet, k, p string, i interface{}, u string) { + switch y := i.(type) { + case bool: + f.BoolP(k, p, y, u) + case int: + f.IntP(k, p, y, u) + case string: + f.StringP(k, p, y, u) + } + v.SetDefault(k, i) +} + +func vadd(f *pflag.FlagSet, k string, i interface{}, u string) { + switch y := i.(type) { + case bool: + f.Bool(k, y, u) + case int: + f.Int(k, y, u) + case string: + f.String(k, y, u) + } + v.SetDefault(k, i) +} + func checkErr(err error) { if err != nil { panic(err) @@ -41,6 +67,7 @@ func mustGetUint(cmd *cobra.Command, flag string) uint { } func getDB() *storm.DB { + databasePath := v.GetString("database") if _, err := os.Stat(databasePath); err != nil { panic(errors.New(databasePath + " does not exist. Please run 'filebrowser init' first.")) } diff --git a/go.mod b/go.mod index 08b1a00c..cb9c2a2b 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/asdine/storm v2.1.2+incompatible github.com/boltdb/bolt v1.3.1 // indirect github.com/daaku/go.zipexe v0.0.0-20150329023125-a5fe2436ffcb // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dsnet/compress v0.0.0-20171208185109-cc9eb1d7ad76 // indirect github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect @@ -24,14 +23,14 @@ require ( github.com/maruel/natural v0.0.0-20180416170133-dbcb3e2e8cf1 github.com/mholt/archiver v3.1.0+incompatible github.com/mholt/caddy v0.11.1 + github.com/mitchellh/go-homedir v1.0.0 github.com/nwaples/rardecode v1.0.0 // indirect github.com/pelletier/go-toml v1.2.0 github.com/pierrec/lz4 v2.0.5+incompatible // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/afero v1.1.2 github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.3 - github.com/stretchr/testify v1.2.2 // indirect + github.com/spf13/viper v1.3.1 github.com/ulikunitz/xz v0.5.5 // indirect github.com/vmihailenco/msgpack v4.0.1+incompatible // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect @@ -39,7 +38,6 @@ require ( golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd // indirect golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect - golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a // indirect google.golang.org/appengine v1.3.0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 diff --git a/go.sum b/go.sum index 7b355bef..c6b3e08d 100644 --- a/go.sum +++ b/go.sum @@ -6,10 +6,14 @@ github.com/GeertJohan/go.rice v0.0.0-20170420135705-c02ca9a983da h1:UVU3a9pRUyLd github.com/GeertJohan/go.rice v0.0.0-20170420135705-c02ca9a983da/go.mod h1:DgrzXonpdQbfN3uYaGz1EG4Sbhyum/MMIn6Cphlh2bw= github.com/Sereal/Sereal v0.0.0-20180905114147-563b78806e28 h1:KjLSBawWQq6I0p9VRX8RtHIuttTYvUCGfMgNoBBFxYs= github.com/Sereal/Sereal v0.0.0-20180905114147-563b78806e28/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asdine/storm v2.1.2+incompatible h1:dczuIkyqwY2LrtXPz8ixMrU/OFgZp71kbKTHGrXYt/Q= github.com/asdine/storm v2.1.2+incompatible/go.mod h1:RarYDc9hq1UPLImuiXK3BIWPJLdIygvV3PsInK0FbVQ= github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/daaku/go.zipexe v0.0.0-20150329023125-a5fe2436ffcb h1:tUf55Po0vzOendQ7NWytcdK0VuzQmfAgvGBUOQvN0WA= github.com/daaku/go.zipexe v0.0.0-20150329023125-a5fe2436ffcb/go.mod h1:U0vRfAucUOohvdCxt5MWLF+TePIL0xbCkbKIiV8TQCE= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -20,6 +24,8 @@ github.com/dsnet/compress v0.0.0-20171208185109-cc9eb1d7ad76 h1:eX+pdPPlD279OWgd github.com/dsnet/compress v0.0.0-20171208185109-cc9eb1d7ad76/go.mod h1:KjxHHirfLaw19iGT70HvVjHQsL1vq1SRQB4yOsAfy2s= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= @@ -34,6 +40,8 @@ github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/hacdias/fileutils v0.0.0-20171121222743-76b1c6ab9067 h1:K2ugN3B7NOrATI7GfXRrwtbyg0OYVR9oNcm1XeTIyY4= github.com/hacdias/fileutils v0.0.0-20171121222743-76b1c6ab9067/go.mod h1:lwnswzFVSy7B/k81M5rOLUU0fOBKHrDRIkPIBZd7PBo= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro= @@ -43,12 +51,18 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/maruel/natural v0.0.0-20180416170133-dbcb3e2e8cf1 h1:PEhRT94KBTY4E0KdCYmhvDGWjSFBxc68j2M6PMRix8U= github.com/maruel/natural v0.0.0-20180416170133-dbcb3e2e8cf1/go.mod h1:wI697HNhDFM/vBruYM3ckbszQ2+DOIeH9qdBKMdf288= github.com/mholt/archiver v3.1.0+incompatible h1:S1rFZ7umHtN6cG+6cusrfoXTMPqp6u/R89iKxBYJd4w= github.com/mholt/archiver v3.1.0+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU= github.com/mholt/caddy v0.11.1 h1:oNfejqftVesLoFxw53Gh17aBPNbTxQ9xJw1pn4IiAPk= github.com/mholt/caddy v0.11.1/go.mod h1:Wb1PlT4DAYSqOEd03MsqkdkXnTxA8v9pKjdpxbqM1kY= +github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs= github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= @@ -59,18 +73,26 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.1 h1:5+8j8FTpnFV4nEImW/ofkzEt8VoOiLXxdYIDsB73T38= +github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU= github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.0 h1:oY10fI923Q5pVCVt1GBTZMn8LHo5M+RCInFpeMnV4QI= go.etcd.io/bbolt v1.3.0/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=