cmd/hc, cmd/gw, cmd/captplanet: simplify setup/run commands (#178)
also allows much more customization of services within captain planet, such as reconfiguring the overlay service to use redis
This commit is contained in:
parent
3fa7a30b31
commit
79d2639ba1
@ -4,11 +4,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
||||
"storj.io/storj/pkg/process"
|
||||
)
|
||||
@ -25,7 +23,10 @@ var (
|
||||
)
|
||||
|
||||
func main() {
|
||||
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
|
||||
process.ExecuteWithConfig(rootCmd,
|
||||
filepath.Join(defaultConfDir, "config.yaml"))
|
||||
// process.Exec will load this for this command.
|
||||
runCmd.Flags().String("config",
|
||||
filepath.Join(defaultConfDir, "config.yaml"), "path to configuration")
|
||||
setupCmd.Flags().String("config",
|
||||
filepath.Join(defaultConfDir, "setup.yaml"), "path to configuration")
|
||||
process.Exec(rootCmd)
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@ -20,122 +18,76 @@ import (
|
||||
"storj.io/storj/pkg/provider"
|
||||
)
|
||||
|
||||
const (
|
||||
farmerCount = 40
|
||||
)
|
||||
|
||||
type HeavyClient struct {
|
||||
Identity provider.IdentityConfig
|
||||
Kademlia kademlia.Config
|
||||
PointerDB pointerdb.Config
|
||||
Overlay overlay.Config
|
||||
}
|
||||
|
||||
type Farmer struct {
|
||||
Identity provider.IdentityConfig
|
||||
Kademlia kademlia.Config
|
||||
Storage psservice.Config
|
||||
}
|
||||
|
||||
var (
|
||||
runCmd = &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "Run all providers",
|
||||
RunE: cmdRun,
|
||||
}
|
||||
runCfg Config
|
||||
|
||||
runCfg struct {
|
||||
HeavyClient HeavyClient
|
||||
Farmers [farmerCount]Farmer
|
||||
Gateway miniogw.Config
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(runCmd)
|
||||
cfgstruct.Bind(runCmd.Flags(), &runCfg,
|
||||
cfgstruct.ConfDir(defaultConfDir),
|
||||
)
|
||||
cfgstruct.Bind(runCmd.Flags(), &runCfg, cfgstruct.ConfDir(defaultConfDir))
|
||||
}
|
||||
|
||||
func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
ctx := process.Ctx(cmd)
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
startingPort := runCfg.StartingPort
|
||||
|
||||
errch := make(chan error, runCfg.FarmerCount+2)
|
||||
|
||||
// define heavy client config programmatically
|
||||
type HeavyClient struct {
|
||||
Identity provider.IdentityConfig
|
||||
Kademlia kademlia.Config
|
||||
PointerDB pointerdb.Config
|
||||
Overlay overlay.Config
|
||||
}
|
||||
|
||||
hc := HeavyClient{
|
||||
Identity: provider.IdentityConfig{
|
||||
CertPath: filepath.Join(runCfg.BasePath, "hc", "ident.leaf.cert"),
|
||||
KeyPath: filepath.Join(runCfg.BasePath, "hc", "ident.leaf.key"),
|
||||
Address: joinHostPort(runCfg.ListenHost, startingPort+1),
|
||||
},
|
||||
Kademlia: kademlia.Config{
|
||||
TODOListenAddr: joinHostPort(runCfg.ListenHost, startingPort+2),
|
||||
BootstrapAddr: joinHostPort(runCfg.ListenHost, startingPort+4),
|
||||
},
|
||||
PointerDB: pointerdb.Config{
|
||||
DatabaseURL: "bolt://" + filepath.Join(
|
||||
runCfg.BasePath, "hc", "pointerdb.db"),
|
||||
},
|
||||
Overlay: overlay.Config{
|
||||
DatabaseURL: "bolt://" + filepath.Join(
|
||||
runCfg.BasePath, "hc", "overlay.db"),
|
||||
},
|
||||
}
|
||||
errch := make(chan error, len(runCfg.Farmers)+2)
|
||||
|
||||
// start heavy client
|
||||
go func() {
|
||||
_, _ = fmt.Printf("starting heavy client on %s\n", hc.Identity.Address)
|
||||
errch <- hc.Identity.Run(ctx, hc.Kademlia, hc.PointerDB, hc.Overlay)
|
||||
_, _ = fmt.Printf("starting heavy client on %s\n",
|
||||
runCfg.HeavyClient.Identity.Address)
|
||||
errch <- runCfg.HeavyClient.Identity.Run(ctx,
|
||||
runCfg.HeavyClient.Kademlia,
|
||||
runCfg.HeavyClient.PointerDB,
|
||||
runCfg.HeavyClient.Overlay)
|
||||
}()
|
||||
|
||||
// define and start a bunch of farmers programmatically
|
||||
type Farmer struct {
|
||||
Identity provider.IdentityConfig
|
||||
Kademlia kademlia.Config
|
||||
Storage psservice.Config
|
||||
}
|
||||
|
||||
for i := 0; i < runCfg.FarmerCount; i++ {
|
||||
basepath := filepath.Join(runCfg.BasePath, fmt.Sprintf("f%d", i))
|
||||
farmer := Farmer{
|
||||
Identity: provider.IdentityConfig{
|
||||
CertPath: filepath.Join(basepath, "ident.leaf.cert"),
|
||||
KeyPath: filepath.Join(basepath, "ident.leaf.key"),
|
||||
Address: joinHostPort(runCfg.ListenHost, startingPort+i*2+3),
|
||||
},
|
||||
Kademlia: kademlia.Config{
|
||||
TODOListenAddr: joinHostPort(runCfg.ListenHost, startingPort+i*2+4),
|
||||
BootstrapAddr: joinHostPort(runCfg.ListenHost, startingPort+1),
|
||||
},
|
||||
Storage: psservice.Config{
|
||||
Path: filepath.Join(basepath, "data"),
|
||||
},
|
||||
}
|
||||
// start the farmers
|
||||
for i := 0; i < len(runCfg.Farmers); i++ {
|
||||
go func(i int) {
|
||||
_, _ = fmt.Printf("starting farmer %d grpc on %s, kad on %s\n",
|
||||
i, farmer.Identity.Address, farmer.Kademlia.TODOListenAddr)
|
||||
errch <- farmer.Identity.Run(ctx, farmer.Kademlia, farmer.Storage)
|
||||
_, _ = fmt.Printf("starting farmer %d grpc on %s, kad on %s\n", i,
|
||||
runCfg.Farmers[i].Identity.Address,
|
||||
runCfg.Farmers[i].Kademlia.TODOListenAddr)
|
||||
errch <- runCfg.Farmers[i].Identity.Run(ctx,
|
||||
runCfg.Farmers[i].Kademlia,
|
||||
runCfg.Farmers[i].Storage)
|
||||
}(i)
|
||||
}
|
||||
|
||||
// start s3 gateway
|
||||
gw := miniogw.Config{
|
||||
IdentityConfig: provider.IdentityConfig{
|
||||
CertPath: filepath.Join(runCfg.BasePath, "gw", "ident.leaf.cert"),
|
||||
KeyPath: filepath.Join(runCfg.BasePath, "gw", "ident.leaf.key"),
|
||||
Address: joinHostPort(runCfg.ListenHost, startingPort),
|
||||
},
|
||||
MinioConfig: runCfg.MinioConfig,
|
||||
ClientConfig: miniogw.ClientConfig{
|
||||
OverlayAddr: joinHostPort(
|
||||
runCfg.ListenHost, startingPort+1),
|
||||
PointerDBAddr: joinHostPort(
|
||||
runCfg.ListenHost, startingPort+1),
|
||||
},
|
||||
RSConfig: runCfg.RSConfig,
|
||||
}
|
||||
gw.MinioConfig.MinioDir = filepath.Join(runCfg.BasePath, "gw", "minio")
|
||||
|
||||
// start s3 gateway
|
||||
go func() {
|
||||
_, _ = fmt.Printf("starting minio gateway on %s\n",
|
||||
gw.IdentityConfig.Address)
|
||||
errch <- gw.Run(ctx)
|
||||
runCfg.Gateway.IdentityConfig.Address)
|
||||
errch <- runCfg.Gateway.Run(ctx)
|
||||
}()
|
||||
|
||||
return <-errch
|
||||
}
|
||||
|
||||
func joinHostPort(host string, port int) string {
|
||||
return net.JoinHostPort(host, fmt.Sprint(port))
|
||||
}
|
||||
|
@ -5,25 +5,22 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"storj.io/storj/pkg/cfgstruct"
|
||||
"storj.io/storj/pkg/miniogw"
|
||||
"storj.io/storj/pkg/peertls"
|
||||
"storj.io/storj/pkg/process"
|
||||
)
|
||||
|
||||
// Config defines broad Captain Planet configuration
|
||||
type Config struct {
|
||||
FarmerCount int `help:"number of farmers to run" default:"20"`
|
||||
BasePath string `help:"base path for captain planet storage" default:"$CONFDIR"`
|
||||
ListenHost string `help:"the host for providers to listen on" default:"127.0.0.1"`
|
||||
StartingPort int `help:"all providers will listen on ports consecutively starting with this one" default:"7777"`
|
||||
miniogw.RSConfig
|
||||
miniogw.MinioConfig
|
||||
}
|
||||
|
||||
var (
|
||||
@ -54,7 +51,7 @@ func cmdSetup(cmd *cobra.Command, args []string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < setupCfg.FarmerCount; i++ {
|
||||
for i := 0; i < len(runCfg.Farmers); i++ {
|
||||
farmerPath := filepath.Join(setupCfg.BasePath, fmt.Sprintf("f%d", i))
|
||||
err = os.MkdirAll(farmerPath, 0700)
|
||||
if err != nil {
|
||||
@ -78,5 +75,58 @@ func cmdSetup(cmd *cobra.Command, args []string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
return process.SaveConfig(cmd)
|
||||
startingPort := setupCfg.StartingPort
|
||||
|
||||
overrides := map[string]interface{}{
|
||||
"heavy-client.identity.cert-path": filepath.Join(
|
||||
setupCfg.BasePath, "hc", "ident.leaf.cert"),
|
||||
"heavy-client.identity.key-path": filepath.Join(
|
||||
setupCfg.BasePath, "hc", "ident.leaf.key"),
|
||||
"heavy-client.identity.address": joinHostPort(
|
||||
setupCfg.ListenHost, startingPort+1),
|
||||
"heavy-client.kademlia.todo-listen-addr": joinHostPort(
|
||||
setupCfg.ListenHost, startingPort+2),
|
||||
"heavy-client.kademlia.bootstrap-addr": joinHostPort(
|
||||
setupCfg.ListenHost, startingPort+4),
|
||||
"heavy-client.pointer-db.database-url": "bolt://" + filepath.Join(
|
||||
setupCfg.BasePath, "hc", "pointerdb.db"),
|
||||
"heavy-client.overlay.database-url": "bolt://" + filepath.Join(
|
||||
setupCfg.BasePath, "hc", "overlay.db"),
|
||||
"gateway.cert-path": filepath.Join(
|
||||
setupCfg.BasePath, "gw", "ident.leaf.cert"),
|
||||
"gateway.key-path": filepath.Join(
|
||||
setupCfg.BasePath, "gw", "ident.leaf.key"),
|
||||
"gateway.address": joinHostPort(
|
||||
setupCfg.ListenHost, startingPort),
|
||||
"gateway.overlay-addr": joinHostPort(
|
||||
setupCfg.ListenHost, startingPort+1),
|
||||
"gateway.pointer-db-addr": joinHostPort(
|
||||
setupCfg.ListenHost, startingPort+1),
|
||||
"gateway.minio-dir": filepath.Join(
|
||||
setupCfg.BasePath, "gw", "minio"),
|
||||
"pointer-db.auth.api-key": "abc123",
|
||||
}
|
||||
|
||||
for i := 0; i < len(runCfg.Farmers); i++ {
|
||||
basepath := filepath.Join(setupCfg.BasePath, fmt.Sprintf("f%d", i))
|
||||
farmer := fmt.Sprintf("farmers.%02d.", i)
|
||||
overrides[farmer+"identity.cert-path"] = filepath.Join(
|
||||
basepath, "ident.leaf.cert")
|
||||
overrides[farmer+"identity.key-path"] = filepath.Join(
|
||||
basepath, "ident.leaf.key")
|
||||
overrides[farmer+"identity.address"] = joinHostPort(
|
||||
setupCfg.ListenHost, startingPort+i*2+3)
|
||||
overrides[farmer+"kademlia.todo-listen-addr"] = joinHostPort(
|
||||
setupCfg.ListenHost, startingPort+i*2+4)
|
||||
overrides[farmer+"kademlia.bootstrap-addr"] = joinHostPort(
|
||||
setupCfg.ListenHost, startingPort+1)
|
||||
overrides[farmer+"storage.path"] = filepath.Join(basepath, "data")
|
||||
}
|
||||
|
||||
return process.SaveConfig(runCmd.Flags(),
|
||||
filepath.Join(setupCfg.BasePath, "config.yaml"), overrides)
|
||||
}
|
||||
|
||||
func joinHostPort(host string, port int) string {
|
||||
return net.JoinHostPort(host, fmt.Sprint(port))
|
||||
}
|
||||
|
@ -4,10 +4,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@ -22,67 +20,54 @@ var (
|
||||
Use: "gw",
|
||||
Short: "Gateway",
|
||||
}
|
||||
runCmd = &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "Run the gateway",
|
||||
RunE: cmdRun,
|
||||
}
|
||||
setupCmd = &cobra.Command{
|
||||
Use: "setup",
|
||||
Short: "Create config files",
|
||||
RunE: cmdSetup,
|
||||
}
|
||||
|
||||
cfg miniogw.Config
|
||||
runCfg miniogw.Config
|
||||
setupCfg struct {
|
||||
BasePath string `default:"$CONFDIR" help:"base path for setup"`
|
||||
}
|
||||
|
||||
defaultConfDir = "$HOME/.storj/gw"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(&cobra.Command{
|
||||
Use: "run",
|
||||
Short: "Run the gateway",
|
||||
RunE: cmdRun,
|
||||
})
|
||||
rootCmd.AddCommand(&cobra.Command{
|
||||
Use: "setup",
|
||||
Short: "Create config files",
|
||||
RunE: cmdSetup,
|
||||
})
|
||||
cfgstruct.Bind(rootCmd.PersistentFlags(), &cfg,
|
||||
cfgstruct.ConfDir(defaultConfDir))
|
||||
rootCmd.AddCommand(runCmd)
|
||||
rootCmd.AddCommand(setupCmd)
|
||||
cfgstruct.Bind(runCmd.Flags(), &runCfg, cfgstruct.ConfDir(defaultConfDir))
|
||||
cfgstruct.Bind(setupCmd.Flags(), &setupCfg, cfgstruct.ConfDir(defaultConfDir))
|
||||
}
|
||||
|
||||
func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
return cfg.Run(process.Ctx(cmd))
|
||||
return runCfg.Run(process.Ctx(cmd))
|
||||
}
|
||||
|
||||
func cmdSetup(cmd *cobra.Command, args []string) (err error) {
|
||||
ctx := process.Ctx(cmd)
|
||||
|
||||
// TODO: clean this up somehow?
|
||||
if !strings.HasSuffix(cfg.IdentityConfig.CertPath, ".leaf.cert") {
|
||||
return fmt.Errorf("certificate path should end with .leaf.cert")
|
||||
}
|
||||
certpath := strings.TrimSuffix(cfg.IdentityConfig.CertPath, ".leaf.cert")
|
||||
if !strings.HasSuffix(cfg.IdentityConfig.KeyPath, ".leaf.key") {
|
||||
return fmt.Errorf("key path should end with .leaf.key")
|
||||
}
|
||||
keypath := strings.TrimSuffix(cfg.IdentityConfig.KeyPath, ".leaf.key")
|
||||
|
||||
err = os.MkdirAll(filepath.Dir(certpath), 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(filepath.Dir(keypath), 0700)
|
||||
err = os.MkdirAll(setupCfg.BasePath, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = peertls.NewTLSFileOptions(certpath, keypath, true, false)
|
||||
identityPath := filepath.Join(setupCfg.BasePath, "identity")
|
||||
_, err = peertls.NewTLSFileOptions(identityPath, identityPath, true, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.MkdirAll(filepath.Dir(process.CfgPath(ctx)), 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return process.SaveConfigAs(cmd, process.CfgPath(ctx))
|
||||
return process.SaveConfig(runCmd.Flags(),
|
||||
filepath.Join(setupCfg.BasePath, "config.yaml"), nil)
|
||||
}
|
||||
|
||||
func main() {
|
||||
process.ExecuteWithConfig(rootCmd,
|
||||
filepath.Join(defaultConfDir, "config.yaml"))
|
||||
runCmd.Flags().String("config",
|
||||
filepath.Join(defaultConfDir, "config.yaml"), "path to configuration")
|
||||
process.Exec(rootCmd)
|
||||
}
|
||||
|
@ -4,10 +4,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@ -20,80 +18,65 @@ import (
|
||||
"storj.io/storj/pkg/provider"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultConfFolder = "$HOME/.storj/hc"
|
||||
)
|
||||
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "hc",
|
||||
Short: "Heavy client",
|
||||
}
|
||||
runCmd = &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "Run the heavy client",
|
||||
RunE: cmdRun,
|
||||
}
|
||||
setupCmd = &cobra.Command{
|
||||
Use: "setup",
|
||||
Short: "Create config files",
|
||||
RunE: cmdSetup,
|
||||
}
|
||||
|
||||
cfg struct {
|
||||
runCfg struct {
|
||||
Identity provider.IdentityConfig
|
||||
Kademlia kademlia.Config
|
||||
PointerDB pointerdb.Config
|
||||
Overlay overlay.Config
|
||||
}
|
||||
setupCfg struct {
|
||||
BasePath string `default:"$CONFDIR" help:"base path for setup"`
|
||||
}
|
||||
|
||||
defaultConfDir = "$HOME/.storj/hc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(&cobra.Command{
|
||||
Use: "run",
|
||||
Short: "Run the heavy client",
|
||||
RunE: cmdRun,
|
||||
})
|
||||
rootCmd.AddCommand(&cobra.Command{
|
||||
Use: "setup",
|
||||
Short: "Create config files",
|
||||
RunE: cmdSetup,
|
||||
})
|
||||
cfgstruct.Bind(rootCmd.PersistentFlags(), &cfg,
|
||||
cfgstruct.ConfDir(defaultConfFolder))
|
||||
rootCmd.AddCommand(runCmd)
|
||||
rootCmd.AddCommand(setupCmd)
|
||||
cfgstruct.Bind(runCmd.Flags(), &runCfg, cfgstruct.ConfDir(defaultConfDir))
|
||||
cfgstruct.Bind(setupCmd.Flags(), &setupCfg, cfgstruct.ConfDir(defaultConfDir))
|
||||
}
|
||||
|
||||
func cmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
return cfg.Identity.Run(process.Ctx(cmd),
|
||||
cfg.Kademlia, cfg.PointerDB, cfg.Overlay)
|
||||
return runCfg.Identity.Run(process.Ctx(cmd),
|
||||
runCfg.Kademlia, runCfg.PointerDB, runCfg.Overlay)
|
||||
}
|
||||
|
||||
func cmdSetup(cmd *cobra.Command, args []string) (err error) {
|
||||
ctx := process.Ctx(cmd)
|
||||
|
||||
// TODO: clean this up somehow?
|
||||
if !strings.HasSuffix(cfg.Identity.CertPath, ".leaf.cert") {
|
||||
return fmt.Errorf("certificate path should end with .leaf.cert")
|
||||
}
|
||||
certpath := strings.TrimSuffix(cfg.Identity.CertPath, ".leaf.cert")
|
||||
if !strings.HasSuffix(cfg.Identity.KeyPath, ".leaf.key") {
|
||||
return fmt.Errorf("key path should end with .leaf.key")
|
||||
}
|
||||
keypath := strings.TrimSuffix(cfg.Identity.KeyPath, ".leaf.key")
|
||||
|
||||
err = os.MkdirAll(filepath.Dir(certpath), 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(filepath.Dir(keypath), 0700)
|
||||
err = os.MkdirAll(setupCfg.BasePath, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = peertls.NewTLSFileOptions(certpath, keypath, true, false)
|
||||
identityPath := filepath.Join(setupCfg.BasePath, "identity")
|
||||
_, err = peertls.NewTLSFileOptions(identityPath, identityPath, true, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.MkdirAll(filepath.Dir(process.CfgPath(ctx)), 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return process.SaveConfigAs(cmd, process.CfgPath(ctx))
|
||||
return process.SaveConfig(runCmd.Flags(),
|
||||
filepath.Join(setupCfg.BasePath, "config.yaml"), nil)
|
||||
}
|
||||
|
||||
func main() {
|
||||
process.ExecuteWithConfig(rootCmd,
|
||||
filepath.Join(defaultConfFolder, "config.yaml"))
|
||||
runCmd.Flags().String("config",
|
||||
filepath.Join(defaultConfDir, "config.yaml"), "path to configuration")
|
||||
process.Exec(rootCmd)
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@ -23,9 +22,15 @@ import (
|
||||
|
||||
// ExecuteWithConfig runs a Cobra command with the provided default config
|
||||
func ExecuteWithConfig(cmd *cobra.Command, defaultConfig string) {
|
||||
cfgFile := flag.String("config", os.ExpandEnv(defaultConfig), "config file")
|
||||
flag.String("config", os.ExpandEnv(defaultConfig), "config file")
|
||||
Exec(cmd)
|
||||
}
|
||||
|
||||
// Exec runs a Cobra command. If a "config" flag is defined it will be parsed
|
||||
// and loaded using viper.
|
||||
func Exec(cmd *cobra.Command) {
|
||||
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
|
||||
cleanup(cmd, cfgFile)
|
||||
cleanup(cmd)
|
||||
_ = cmd.Execute()
|
||||
}
|
||||
|
||||
@ -36,6 +41,39 @@ var (
|
||||
contexts = map[*cobra.Command]context.Context{}
|
||||
)
|
||||
|
||||
// SaveConfig will save all flags with default values to outfilewith specific
|
||||
// values specified in 'overrides' overridden.
|
||||
func SaveConfig(flagset *pflag.FlagSet, outfile string,
|
||||
overrides map[string]interface{}) error {
|
||||
|
||||
vip := viper.New()
|
||||
err := vip.BindPFlags(pflag.CommandLine)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
flagset.VisitAll(func(f *pflag.Flag) {
|
||||
// stop processing if we hit an error on a BindPFlag call
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if f.Name == "config" {
|
||||
return
|
||||
}
|
||||
err = vip.BindPFlag(f.Name, f)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if overrides != nil {
|
||||
for key, val := range overrides {
|
||||
vip.Set(key, val)
|
||||
}
|
||||
}
|
||||
|
||||
return vip.WriteConfigAs(os.ExpandEnv(outfile))
|
||||
}
|
||||
|
||||
// Ctx returns the appropriate context.Context for ExecuteWithConfig commands
|
||||
func Ctx(cmd *cobra.Command) context.Context {
|
||||
contextMtx.Lock()
|
||||
@ -47,44 +85,9 @@ func Ctx(cmd *cobra.Command) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
type ctxKey int
|
||||
|
||||
const (
|
||||
ctxKeyVip ctxKey = iota
|
||||
ctxKeyCfg
|
||||
)
|
||||
|
||||
// SaveConfig outputs the configuration to the configured (or default) config
|
||||
// file given to ExecuteWithConfig
|
||||
func SaveConfig(cmd *cobra.Command) error {
|
||||
ctx := Ctx(cmd)
|
||||
return getViper(ctx).WriteConfigAs(CfgPath(ctx))
|
||||
}
|
||||
|
||||
// SaveConfigAs outputs the configuration to the provided path assuming the
|
||||
// command was executed with ExecuteWithConfig
|
||||
func SaveConfigAs(cmd *cobra.Command, path string) error {
|
||||
return getViper(Ctx(cmd)).WriteConfigAs(path)
|
||||
}
|
||||
|
||||
func getViper(ctx context.Context) *viper.Viper {
|
||||
if v, ok := ctx.Value(ctxKeyVip).(*viper.Viper); ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CfgPath returns the configuration path used with ExecuteWithConfig
|
||||
func CfgPath(ctx context.Context) string {
|
||||
if v, ok := ctx.Value(ctxKeyCfg).(string); ok {
|
||||
return v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func cleanup(cmd *cobra.Command, cfgFile *string) {
|
||||
func cleanup(cmd *cobra.Command) {
|
||||
for _, ccmd := range cmd.Commands() {
|
||||
cleanup(ccmd, cfgFile)
|
||||
cleanup(ccmd)
|
||||
}
|
||||
if cmd.Run != nil {
|
||||
panic("Please use cobra's RunE instead of Run")
|
||||
@ -104,16 +107,22 @@ func cleanup(cmd *cobra.Command, cfgFile *string) {
|
||||
}
|
||||
vip.SetEnvPrefix("storj")
|
||||
vip.AutomaticEnv()
|
||||
if *cfgFile != "" && fileExists(*cfgFile) {
|
||||
vip.SetConfigFile(*cfgFile)
|
||||
err = vip.ReadInConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
cfgFlag := cmd.Flags().Lookup("config")
|
||||
if cfgFlag != nil && cfgFlag.Value.String() != "" {
|
||||
path := os.ExpandEnv(cfgFlag.Value.String())
|
||||
if cfgFlag.Changed || fileExists(path) {
|
||||
vip.SetConfigFile(path)
|
||||
err = vip.ReadInConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// go back and propagate changed config values to appropriate flags
|
||||
var brokenKeys []string
|
||||
var brokenVals []string
|
||||
for _, key := range vip.AllKeys() {
|
||||
if cmd.Flags().Lookup(key) == nil {
|
||||
// flag couldn't be found
|
||||
@ -122,14 +131,11 @@ func cleanup(cmd *cobra.Command, cfgFile *string) {
|
||||
err := cmd.Flags().Set(key, vip.GetString(key))
|
||||
if err != nil {
|
||||
// flag couldn't be set
|
||||
brokenKeys = append(brokenKeys, key)
|
||||
brokenVals = append(brokenVals, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, ctxKeyVip, vip)
|
||||
ctx = context.WithValue(ctx, ctxKeyCfg, *cfgFile)
|
||||
|
||||
logger, err := utils.NewLogger(*logDisposition)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -139,12 +145,11 @@ func cleanup(cmd *cobra.Command, cfgFile *string) {
|
||||
defer zap.RedirectStdLog(logger)()
|
||||
|
||||
// okay now that logging is working, inform about the broken keys
|
||||
// these keys are almost certainly broken because they have capital
|
||||
// letters
|
||||
if len(brokenKeys) > 0 {
|
||||
logger.Sugar().Infof("TODO: these flags are not configurable via "+
|
||||
"config file, probably due to having uppercase letters: %s",
|
||||
strings.Join(brokenKeys, ", "))
|
||||
for _, key := range brokenKeys {
|
||||
logger.Sugar().Infof("Invalid configuration file key: %s", key)
|
||||
}
|
||||
for _, key := range brokenVals {
|
||||
logger.Sugar().Infof("Invalid configuration file value for key: %s", key)
|
||||
}
|
||||
|
||||
err = initMetrics(ctx, monkit.Default,
|
||||
|
Loading…
Reference in New Issue
Block a user