diff --git a/cmd/beekeeper/cmd/check.go b/cmd/beekeeper/cmd/check.go index eeda92ce1..683aef507 100644 --- a/cmd/beekeeper/cmd/check.go +++ b/cmd/beekeeper/cmd/check.go @@ -14,9 +14,10 @@ import ( "github.com/spf13/cobra" ) +var errMissingClusterName = fmt.Errorf("cluster name not provided") + func (c *command) initCheckCmd() (err error) { const ( - optionNameClusterName = "cluster-name" optionNameCreateCluster = "create-cluster" optionNameChecks = "checks" optionNameMetricsEnabled = "metrics-enabled" @@ -34,18 +35,23 @@ func (c *command) initCheckCmd() (err error) { ctx, cancel := context.WithTimeout(cmd.Context(), c.globalConfig.GetDuration(optionNameTimeout)) defer cancel() + checks := c.globalConfig.GetStringSlice(optionNameChecks) + if len(checks) == 0 { + return fmt.Errorf("no checks provided") + } + + clusterName := c.globalConfig.GetString(optionNameClusterName) + if clusterName == "" { + return errMissingClusterName + } + // set cluster config - cfgCluster, ok := c.config.Clusters[c.globalConfig.GetString(optionNameClusterName)] + cfgCluster, ok := c.config.Clusters[clusterName] if !ok { - return fmt.Errorf("cluster %s not defined", c.globalConfig.GetString(optionNameClusterName)) + return fmt.Errorf("cluster %s not defined", clusterName) } - // setup cluster - cluster, err := c.setupCluster(ctx, - c.globalConfig.GetString(optionNameClusterName), - c.config, - c.globalConfig.GetBool(optionNameCreateCluster), - ) + cluster, err := c.setupCluster(ctx, clusterName, c.globalConfig.GetBool(optionNameCreateCluster)) if err != nil { return fmt.Errorf("cluster setup: %w", err) } @@ -88,7 +94,7 @@ func (c *command) initCheckCmd() (err error) { } // run checks - for _, checkName := range c.globalConfig.GetStringSlice(optionNameChecks) { + for _, checkName := range checks { checkName = strings.TrimSpace(checkName) // get configuration checkConfig, ok := c.config.Checks[checkName] @@ -147,7 +153,7 @@ func (c *command) initCheckCmd() (err error) { PreRunE: c.preRunE, } - cmd.Flags().String(optionNameClusterName, "default", "cluster name") + cmd.Flags().String(optionNameClusterName, "", "cluster name. Required") cmd.Flags().String(optionNameMetricsPusherAddress, "pushgateway.staging.internal", "prometheus metrics pusher address") cmd.Flags().Bool(optionNameCreateCluster, false, "creates cluster before executing checks") cmd.Flags().StringSlice(optionNameChecks, []string{"pingpong"}, "list of checks to execute") diff --git a/cmd/beekeeper/cmd/cluster.go b/cmd/beekeeper/cmd/cluster.go index f7583492c..25a3ab12b 100644 --- a/cmd/beekeeper/cmd/cluster.go +++ b/cmd/beekeeper/cmd/cluster.go @@ -20,6 +20,10 @@ type nodeResult struct { } func (c *command) deleteCluster(ctx context.Context, clusterName string, cfg *config.Config, deleteStorage bool) (err error) { + if clusterName == "" { + return errMissingClusterName + } + clusterConfig, ok := cfg.Clusters[clusterName] if !ok { return fmt.Errorf("cluster %s not defined", clusterName) @@ -108,8 +112,12 @@ func (c *command) deleteCluster(ctx context.Context, clusterName string, cfg *co return } -func (c *command) setupCluster(ctx context.Context, clusterName string, cfg *config.Config, startCluster bool) (cluster orchestration.Cluster, err error) { - clusterConfig, ok := cfg.Clusters[clusterName] +func (c *command) setupCluster(ctx context.Context, clusterName string, startCluster bool) (cluster orchestration.Cluster, err error) { + if clusterName == "" { + return nil, errMissingClusterName + } + + clusterConfig, ok := c.config.Clusters[clusterName] if !ok { return nil, fmt.Errorf("cluster %s not defined", clusterName) } @@ -139,7 +147,7 @@ func (c *command) setupCluster(ctx context.Context, clusterName string, cfg *con inCluster := c.globalConfig.GetBool(optionNameInCluster) // setup bootnode node group - fundAddresses, bootnodes, err := setupNodes(ctx, clusterConfig, cfg, true, cluster, startCluster, inCluster, "", nodeResultChan) + fundAddresses, bootnodes, err := setupNodes(ctx, clusterConfig, c.config, true, cluster, startCluster, inCluster, "", nodeResultChan) if err != nil { return nil, fmt.Errorf("setup node group bootnode: %w", err) } @@ -153,7 +161,7 @@ func (c *command) setupCluster(ctx context.Context, clusterName string, cfg *con } // setup other node groups - fundAddresses, _, err = setupNodes(ctx, clusterConfig, cfg, false, cluster, startCluster, inCluster, bootnodes, nodeResultChan) + fundAddresses, _, err = setupNodes(ctx, clusterConfig, c.config, false, cluster, startCluster, inCluster, bootnodes, nodeResultChan) if err != nil { return nil, fmt.Errorf("setup other node groups: %w", err) } @@ -165,6 +173,7 @@ func (c *command) setupCluster(ctx context.Context, clusterName string, cfg *con } c.log.Infof("node groups funded") } + c.log.WithField("use-static-endpoints", clusterConfig.IsUsingStaticEndpoints()).Infof("cluster %s setup completed", clusterName) return cluster, nil diff --git a/cmd/beekeeper/cmd/cmd.go b/cmd/beekeeper/cmd/cmd.go index eaba878b4..47422e7a4 100644 --- a/cmd/beekeeper/cmd/cmd.go +++ b/cmd/beekeeper/cmd/cmd.go @@ -52,14 +52,10 @@ type command struct { globalConfig *viper.Viper globalConfigFile string homeDir string - // configuration - config *config.Config - // kubernetes client - k8sClient *k8s.Client - // swap client - swapClient swap.Client - // log - log logging.Logger + config *config.Config // beekeeper clusters configuration (config dir) + k8sClient *k8s.Client // kubernetes client + swapClient swap.Client + log logging.Logger } type option func(*command) @@ -72,7 +68,7 @@ func newCommand(opts ...option) (c *command, err error) { SilenceErrors: true, SilenceUsage: true, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - return c.initConfig() + return c.initConfig(cmd.Flags().Changed(optionNameClusterName)) }, }, } @@ -112,7 +108,7 @@ func newCommand(opts ...option) (c *command, err error) { return nil, err } - if err := c.initStampFunderCmd(); err != nil { + if err := c.initStamperCmd(); err != nil { return nil, err } @@ -143,6 +139,7 @@ func Execute() (err error) { if err != nil { return err } + return c.Execute() } @@ -167,58 +164,90 @@ func (c *command) initGlobalFlags() { globalFlags.String(optionNameKubeconfig, "~/.kube/config", "Path to the kubeconfig file") } -func (c *command) bindGlobalFlags() (err error) { - for _, flag := range []string{optionNameConfigDir, optionNameConfigGitRepo, optionNameConfigGitBranch, optionNameConfigGitDir, optionNameConfigGitUsername, optionNameConfigGitPassword, optionNameLogVerbosity, optionNameLokiEndpoint} { +func (c *command) bindGlobalFlags() error { + for _, flag := range []string{ + optionNameConfigDir, + optionNameConfigGitRepo, + optionNameConfigGitBranch, + optionNameConfigGitDir, + optionNameConfigGitUsername, + optionNameConfigGitPassword, + optionNameLogVerbosity, + optionNameLokiEndpoint, + } { if err := c.globalConfig.BindPFlag(flag, c.root.PersistentFlags().Lookup(flag)); err != nil { - return err + return fmt.Errorf("binding %s flag: %w", flag, err) } } - return + + return nil } -func (c *command) initConfig() (err error) { - // set global configuration +func (c *command) initConfig(loadConfigDir bool) error { + if err := c.initGlobalConfig(); err != nil { + return fmt.Errorf("initializing global configuration: %w", err) + } + + if err := c.initLogger(); err != nil { + return fmt.Errorf("initializing logger: %w", err) + } + + if !loadConfigDir { + c.log.Debugf("Skipping loading configuration directory as the cluster name is not set") + return nil + } + + if err := c.loadConfigDirectory(); err != nil { + return fmt.Errorf("loading configuration directory: %w", err) + } + + return nil +} + +func (c *command) initGlobalConfig() error { cfg := viper.New() cfgName := ".beekeeper" + if c.globalConfigFile != "" { - // Use config file from the flag. cfg.SetConfigFile(c.globalConfigFile) } else { - // Search config in home directory with name ".beekeeper" (without extension). cfg.AddConfigPath(c.homeDir) cfg.SetConfigName(cfgName) } - // environment cfg.SetEnvPrefix("beekeeper") - cfg.AutomaticEnv() // read in environment variables that match + cfg.AutomaticEnv() cfg.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) if c.homeDir != "" && c.globalConfigFile == "" { c.globalConfigFile = filepath.Join(c.homeDir, cfgName+".yaml") } - // if a config file is found, read it in. if err := cfg.ReadInConfig(); err != nil { - var e viper.ConfigFileNotFoundError - if !errors.As(err, &e) { + if !errors.As(err, &viper.ConfigFileNotFoundError{}) { return err } } c.globalConfig = cfg - if err := c.bindGlobalFlags(); err != nil { - return err - } - // init logger + return c.bindGlobalFlags() +} + +func (c *command) initLogger() error { verbosity := c.globalConfig.GetString(optionNameLogVerbosity) lokiEndpoint := c.globalConfig.GetString(optionNameLokiEndpoint) - c.log, err = newLogger(c.root, verbosity, lokiEndpoint) + + log, err := newLogger(c.root, verbosity, lokiEndpoint) if err != nil { return fmt.Errorf("new logger: %w", err) } + c.log = log + return nil +} + +func (c *command) loadConfigDirectory() error { if c.globalConfig.GetString(optionNameConfigGitRepo) != "" { c.log.Debugf("using configuration from Git repository %s, branch %s, directory %s", c.globalConfig.GetString(optionNameConfigGitRepo), c.globalConfig.GetString(optionNameConfigGitBranch), c.globalConfig.GetString(optionNameConfigGitDir)) // read configuration from git repo @@ -298,17 +327,19 @@ func (c *command) initConfig() (err error) { } } - return + return nil } -func (c *command) setHomeDir() (err error) { +func (c *command) setHomeDir() error { if c.homeDir != "" { - return + return nil } + dir, err := os.UserHomeDir() if err != nil { - return err + return fmt.Errorf("obtaining user's home dir: %w", err) } + c.homeDir = dir return nil } @@ -318,18 +349,17 @@ func (c *command) preRunE(cmd *cobra.Command, args []string) (err error) { return err } - // set Kubernetes client - if err := c.setK8S(); err != nil { + if err := c.setK8sClient(); err != nil { return err } - // set Swap client + if err := c.setSwapClient(); err != nil { return err } return nil } -func (c *command) setK8S() (err error) { +func (c *command) setK8sClient() (err error) { if c.globalConfig.GetBool(optionNameEnableK8S) { options := []k8s.ClientOption{ k8s.WithLogger(c.log), @@ -369,6 +399,7 @@ func newLogger(cmd *cobra.Command, verbosity, lokiEndpoint string) (logging.Logg logging.WithLokiOption(lokiEndpoint), logging.WithMetricsOption(), } + switch strings.ToLower(verbosity) { case "0", "silent": logger = logging.New(io.Discard, 0) @@ -385,5 +416,6 @@ func newLogger(cmd *cobra.Command, verbosity, lokiEndpoint string) (logging.Logg default: return nil, fmt.Errorf("unknown %s level %q, use help to check flag usage options", optionNameLogVerbosity, verbosity) } + return logger, nil } diff --git a/cmd/beekeeper/cmd/create.go b/cmd/beekeeper/cmd/create.go index 942bee5d5..17f28bf24 100644 --- a/cmd/beekeeper/cmd/create.go +++ b/cmd/beekeeper/cmd/create.go @@ -14,7 +14,7 @@ func (c *command) initCreateCmd() (err error) { }, } - cmd.AddCommand(c.initCreateK8SNamespace()) + cmd.AddCommand(c.initCreateK8sNamespace()) cmd.AddCommand(c.initCreateBeeCluster()) c.root.AddCommand(cmd) diff --git a/cmd/beekeeper/cmd/create_bee_cluster.go b/cmd/beekeeper/cmd/create_bee_cluster.go index abe9566db..2661b0649 100644 --- a/cmd/beekeeper/cmd/create_bee_cluster.go +++ b/cmd/beekeeper/cmd/create_bee_cluster.go @@ -8,10 +8,10 @@ import ( ) const ( - optionNameClusterName = "cluster-name" - optionNameChainNodeEndpoint = "geth-url" - optionNameWalletKey = "wallet-key" - optionNameTimeout = "timeout" + optionNameClusterName string = "cluster-name" + optionNameChainNodeEndpoint string = "geth-url" + optionNameWalletKey string = "wallet-key" + optionNameTimeout string = "timeout" ) func (c *command) initCreateBeeCluster() *cobra.Command { @@ -23,14 +23,14 @@ func (c *command) initCreateBeeCluster() *cobra.Command { ctx, cancel := context.WithTimeout(cmd.Context(), c.globalConfig.GetDuration(optionNameTimeout)) defer cancel() start := time.Now() - _, err = c.setupCluster(ctx, c.globalConfig.GetString(optionNameClusterName), c.config, true) + _, err = c.setupCluster(ctx, c.globalConfig.GetString(optionNameClusterName), true) c.log.Infof("cluster setup took %s", time.Since(start)) return err }, PreRunE: c.preRunE, } - cmd.Flags().String(optionNameClusterName, "default", "cluster name") + cmd.Flags().String(optionNameClusterName, "", "cluster name") cmd.Flags().String(optionNameChainNodeEndpoint, "", "Endpoint to chain node. Required.") cmd.Flags().String(optionNameWalletKey, "", "Hex-encoded private key for the Bee node wallet. Required.") cmd.Flags().Duration(optionNameTimeout, 30*time.Minute, "timeout") diff --git a/cmd/beekeeper/cmd/create_k8s_namespace.go b/cmd/beekeeper/cmd/create_k8s_namespace.go index 3a8dd0805..92b5839bd 100644 --- a/cmd/beekeeper/cmd/create_k8s_namespace.go +++ b/cmd/beekeeper/cmd/create_k8s_namespace.go @@ -6,15 +6,18 @@ import ( "github.com/spf13/cobra" ) -func (c *command) initCreateK8SNamespace() *cobra.Command { +const namespaceCmd string = "k8s-namespace" + +func (c *command) initCreateK8sNamespace() *cobra.Command { cmd := &cobra.Command{ - Use: "k8s-namespace", + Use: namespaceCmd, Short: "creates Kubernetes namespace", Long: `creates Kubernetes namespace.`, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return fmt.Errorf("requires exactly one argument representing name of the Kubernetes namespace") } + return nil }, RunE: func(cmd *cobra.Command, args []string) (err error) { @@ -28,12 +31,14 @@ func (c *command) initCreateK8SNamespace() *cobra.Command { return }, PreRunE: func(cmd *cobra.Command, args []string) error { - if err := c.setK8S(); err != nil { + if err := c.setK8sClient(); err != nil { return err } + if c.k8sClient == nil { return fmt.Errorf("k8s client not set") } + return nil }, } diff --git a/cmd/beekeeper/cmd/delete_bee_cluster.go b/cmd/beekeeper/cmd/delete_bee_cluster.go index 20f8dfd38..274fb7bdf 100644 --- a/cmd/beekeeper/cmd/delete_bee_cluster.go +++ b/cmd/beekeeper/cmd/delete_bee_cluster.go @@ -9,7 +9,6 @@ import ( func (c *command) initDeleteBeeCluster() *cobra.Command { const ( - optionNameClusterName = "cluster-name" optionNameWithStorage = "with-storage" optionNameTimeout = "timeout" ) @@ -27,7 +26,7 @@ func (c *command) initDeleteBeeCluster() *cobra.Command { PreRunE: c.preRunE, } - cmd.Flags().String(optionNameClusterName, "default", "cluster name") + cmd.Flags().String(optionNameClusterName, "", "cluster name") cmd.Flags().Bool(optionNameWithStorage, false, "delete storage") cmd.Flags().Duration(optionNameTimeout, 15*time.Minute, "timeout") diff --git a/cmd/beekeeper/cmd/delete_k8s_namespace.go b/cmd/beekeeper/cmd/delete_k8s_namespace.go index eb7c45c76..e09e54c58 100644 --- a/cmd/beekeeper/cmd/delete_k8s_namespace.go +++ b/cmd/beekeeper/cmd/delete_k8s_namespace.go @@ -8,13 +8,14 @@ import ( func (c *command) initDeleteK8SNamespace() *cobra.Command { cmd := &cobra.Command{ - Use: "k8s-namespace", + Use: namespaceCmd, Short: "deletes Kubernetes namespace", Long: `Deletes Kubernetes namespace.`, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return fmt.Errorf("requires exactly one argument representing name of the Kubernetes namespace") } + return nil }, RunE: func(cmd *cobra.Command, args []string) (err error) { @@ -28,12 +29,14 @@ func (c *command) initDeleteK8SNamespace() *cobra.Command { return }, PreRunE: func(cmd *cobra.Command, args []string) error { - if err := c.setK8S(); err != nil { + if err := c.setK8sClient(); err != nil { return err } + if c.k8sClient == nil { return fmt.Errorf("k8s client not set") } + return nil }, } diff --git a/cmd/beekeeper/cmd/node_funder.go b/cmd/beekeeper/cmd/node_funder.go index 0e66b77a4..17bd7b55a 100644 --- a/cmd/beekeeper/cmd/node_funder.go +++ b/cmd/beekeeper/cmd/node_funder.go @@ -6,19 +6,20 @@ import ( "fmt" "time" - "github.com/ethersphere/beekeeper/pkg/config" nodefunder "github.com/ethersphere/beekeeper/pkg/funder/node" "github.com/ethersphere/node-funder/pkg/funder" "github.com/spf13/cobra" ) -const nodeFunderLabelSelector string = "beekeeper.ethswarm.org/node-funder=true" +const ( + nodeFunderLabelSelector string = "beekeeper.ethswarm.org/node-funder=true" + nodeFunderCmd string = "node-funder" +) func (c *command) initNodeFunderCmd() (err error) { const ( optionNameAddresses = "addresses" optionNameNamespace = "namespace" - optionClusterName = "cluster-name" optionNameChainNodeEndpoint = "geth-url" optionNameWalletKey = "wallet-key" optionNameMinNative = "min-native" @@ -28,31 +29,15 @@ func (c *command) initNodeFunderCmd() (err error) { ) cmd := &cobra.Command{ - Use: "node-funder", + Use: nodeFunderCmd, Short: "funds bee nodes with ETH and BZZ", Long: `Fund makes BZZ tokens and ETH deposits to given Ethereum addresses. beekeeper node-funder`, RunE: func(cmd *cobra.Command, args []string) (err error) { - cfg := config.NodeFunder{} - - namespace := c.globalConfig.GetString(optionNameNamespace) - addresses := c.globalConfig.GetStringSlice(optionNameAddresses) - clusterName := c.globalConfig.GetString(optionClusterName) - - if len(addresses) > 0 { - cfg.Addresses = addresses - } else if namespace != "" { - cfg.Namespace = namespace - } else if clusterName != "" { - cluster, ok := c.config.Clusters[clusterName] - if !ok { - return fmt.Errorf("cluster %s not found", clusterName) - } - if cluster.Namespace == nil || *cluster.Namespace == "" { - return fmt.Errorf("cluster %s namespace not provided", clusterName) - } - cfg.Namespace = *cluster.Namespace - } else { - return errors.New("one of addresses, namespace, or valid cluster-name must be provided") + cfg := funder.Config{ + MinAmounts: funder.MinAmounts{ + NativeCoin: c.globalConfig.GetFloat64(optionNameMinNative), + SwarmToken: c.globalConfig.GetFloat64(optionNameMinSwarm), + }, } // chain node endpoint check @@ -65,44 +50,64 @@ func (c *command) initNodeFunderCmd() (err error) { return errors.New("wallet key not provided") } - cfg.MinAmounts.NativeCoin = c.globalConfig.GetFloat64(optionNameMinNative) - cfg.MinAmounts.SwarmToken = c.globalConfig.GetFloat64(optionNameMinSwarm) - // add timeout to node-funder ctx, cancel := context.WithTimeout(cmd.Context(), c.globalConfig.GetDuration(optionNameTimeout)) defer cancel() - defer c.log.Infof("node-funder done") + logger := funder.WithLoggerOption(c.log) + + addresses := c.globalConfig.GetStringSlice(optionNameAddresses) + if len(addresses) > 0 { + cfg.Addresses = addresses + return funder.Fund(ctx, cfg, nil, nil, logger) + } + + namespace := c.globalConfig.GetString(optionNameNamespace) + if namespace != "" { + label := c.globalConfig.GetString(optionNameLabelSelector) + funderClient := nodefunder.NewClient(c.k8sClient, c.globalConfig.GetBool(optionNameInCluster), label, c.log) + + cfg.Namespace = namespace + return funder.Fund(ctx, cfg, funderClient, nil, logger) + } + + clusterName := c.globalConfig.GetString(optionNameClusterName) + if clusterName != "" { + cluster, err := c.setupCluster(ctx, clusterName, false) + if err != nil { + return fmt.Errorf("setting up cluster %s: %w", clusterName, err) + } + + clients, err := cluster.NodesClients(ctx) + if err != nil { + return fmt.Errorf("failed to retrieve node clients: %w", err) + } + + for _, node := range clients { + addr, err := node.Addresses(ctx) + if err != nil { + return fmt.Errorf("error fetching addresses for node %s: %w", node.Name(), err) + } + cfg.Addresses = append(cfg.Addresses, addr.Ethereum) + } + + return funder.Fund(ctx, cfg, nil, nil, logger) + } + // NOTE: Swarm key address is the same as the nodeEndpoint/wallet walletAddress. // When setting up a bootnode, the swarmkey option is used to specify the existing swarm key. // However, for other nodes, the beekeeper automatically generates a new swarm key during cluster setup. // Once the swarm key is generated, beekeeper identifies the addresses that can be funded for each node. - var nodeLister funder.NodeLister - // if addresses are provided, use them, not k8s client to list nodes - if cfg.Namespace != "" { - label := c.globalConfig.GetString(optionNameLabelSelector) - nodeLister = nodefunder.NewClient(c.k8sClient, c.globalConfig.GetBool(optionNameInCluster), label, c.log) - } - - return funder.Fund(ctx, funder.Config{ - Namespace: cfg.Namespace, - Addresses: cfg.Addresses, - ChainNodeEndpoint: cfg.ChainNodeEndpoint, - WalletKey: cfg.WalletKey, - MinAmounts: funder.MinAmounts{ - NativeCoin: cfg.MinAmounts.NativeCoin, - SwarmToken: cfg.MinAmounts.SwarmToken, - }, - }, nodeLister, nil, funder.WithLoggerOption(c.log)) + return errors.New("one of addresses, namespace, or valid cluster-name must be provided") }, PreRunE: c.preRunE, } cmd.Flags().StringSliceP(optionNameAddresses, "a", nil, "Comma-separated list of Bee node addresses (must start with 0x). Overrides namespace and cluster name.") cmd.Flags().StringP(optionNameNamespace, "n", "", "Kubernetes namespace. Overrides cluster name if set.") - cmd.Flags().String(optionClusterName, "", "Name of the Beekeeper cluster to target. Ignored if a namespace is specified, in which case the namespace from the cluster configuration is used.") + cmd.Flags().String(optionNameClusterName, "", "Name of the Beekeeper cluster to target. Ignored if a namespace is specified.") cmd.Flags().String(optionNameChainNodeEndpoint, "", "Endpoint to chain node. Required.") cmd.Flags().String(optionNameWalletKey, "", "Hex-encoded private key for the Bee node wallet. Required.") cmd.Flags().Float64(optionNameMinNative, 0, "Minimum amount of chain native coins (xDAI) nodes should have.") diff --git a/cmd/beekeeper/cmd/operator.go b/cmd/beekeeper/cmd/operator.go index 6635bc311..d6ac26b75 100644 --- a/cmd/beekeeper/cmd/operator.go +++ b/cmd/beekeeper/cmd/operator.go @@ -5,11 +5,12 @@ import ( "errors" "time" - "github.com/ethersphere/beekeeper/pkg/config" "github.com/ethersphere/beekeeper/pkg/funder/operator" "github.com/spf13/cobra" ) +const nodeOperatorCmd string = "node-operator" + func (c *command) initOperatorCmd() (err error) { const ( optionNameNamespace = "namespace" @@ -22,30 +23,27 @@ func (c *command) initOperatorCmd() (err error) { ) cmd := &cobra.Command{ - Use: "node-operator", + Use: nodeOperatorCmd, Short: "scans for scheduled pods and funds them", Long: `Node operator scans for scheduled pods and funds them using node-funder. beekeeper node-operator`, RunE: func(cmd *cobra.Command, args []string) (err error) { - cfg := config.NodeFunder{} - var namespace string if namespace = c.globalConfig.GetString(optionNameNamespace); namespace == "" { return errors.New("namespace not provided") } // chain node endpoint check - if cfg.ChainNodeEndpoint = c.globalConfig.GetString(optionNameChainNodeEndpoint); cfg.ChainNodeEndpoint == "" { + var chainNodeEndpoint string + if chainNodeEndpoint = c.globalConfig.GetString(optionNameChainNodeEndpoint); chainNodeEndpoint == "" { return errors.New("chain node endpoint (geth-url) not provided") } // wallet key check - if cfg.WalletKey = c.globalConfig.GetString(optionNameWalletKey); cfg.WalletKey == "" { + var walletKey string + if walletKey = c.globalConfig.GetString(optionNameWalletKey); walletKey == "" { return errors.New("wallet key not provided") } - cfg.MinAmounts.NativeCoin = c.globalConfig.GetFloat64(optionNameMinNative) - cfg.MinAmounts.SwarmToken = c.globalConfig.GetFloat64(optionNameMinSwarm) - // add timeout to operator // if timeout is not set, operator will run infinitely var ctxNew context.Context @@ -63,9 +61,10 @@ func (c *command) initOperatorCmd() (err error) { return operator.NewClient(&operator.ClientConfig{ Log: c.log, Namespace: namespace, - WalletKey: cfg.WalletKey, - ChainNodeEndpoint: cfg.ChainNodeEndpoint, - MinAmounts: cfg.MinAmounts, + WalletKey: walletKey, + ChainNodeEndpoint: chainNodeEndpoint, + NativeToken: c.globalConfig.GetFloat64(optionNameMinNative), + SwarmToken: c.globalConfig.GetFloat64(optionNameMinSwarm), K8sClient: c.k8sClient, LabelSelector: c.globalConfig.GetString(optionNameLabelSelector), }).Run(ctxNew) diff --git a/cmd/beekeeper/cmd/print.go b/cmd/beekeeper/cmd/print.go index edbdfccda..5c7f5e99a 100644 --- a/cmd/beekeeper/cmd/print.go +++ b/cmd/beekeeper/cmd/print.go @@ -11,10 +11,7 @@ import ( ) func (c *command) initPrintCmd() (err error) { - const ( - optionNameClusterName = "cluster-name" - optionNameTimeout = "timeout" - ) + const optionNameTimeout = "timeout" cmd := &cobra.Command{ Use: "print", @@ -44,7 +41,7 @@ Requires exactly one argument from the following list: addresses, depths, nodes, ctx, cancel := context.WithTimeout(cmd.Context(), c.globalConfig.GetDuration(optionNameTimeout)) defer cancel() - cluster, err := c.setupCluster(ctx, c.globalConfig.GetString(optionNameClusterName), c.config, false) + cluster, err := c.setupCluster(ctx, c.globalConfig.GetString(optionNameClusterName), false) if err != nil { return fmt.Errorf("cluster setup: %w", err) } diff --git a/cmd/beekeeper/cmd/restart.go b/cmd/beekeeper/cmd/restart.go index e8004e80b..6c36d9fad 100644 --- a/cmd/beekeeper/cmd/restart.go +++ b/cmd/beekeeper/cmd/restart.go @@ -12,7 +12,6 @@ import ( func (c *command) initRestartCmd() (err error) { const ( - optionNameClusterName = "cluster-name" optionNameLabelSelector = "label-selector" optionNameNamespace = "namespace" optionNameImage = "image" @@ -43,7 +42,7 @@ func (c *command) initRestartCmd() (err error) { return fmt.Errorf("cluster config %s not defined", clusterName) } - cluster, err := c.setupCluster(ctx, clusterName, c.config, false) + cluster, err := c.setupCluster(ctx, clusterName, false) if err != nil { return fmt.Errorf("setting up cluster %s: %w", clusterName, err) } diff --git a/cmd/beekeeper/cmd/simulate.go b/cmd/beekeeper/cmd/simulate.go index 96afba68d..bf238f552 100644 --- a/cmd/beekeeper/cmd/simulate.go +++ b/cmd/beekeeper/cmd/simulate.go @@ -16,14 +16,12 @@ import ( func (c *command) initSimulateCmd() (err error) { const ( - optionNameClusterName = "cluster-name" optionNameCreateCluster = "create-cluster" optionNameSimulations = "simulations" optionNameMetricsEnabled = "metrics-enabled" optionNameSeed = "seed" optionNameTimeout = "timeout" optionNameMetricsPusherAddress = "metrics-pusher-address" - // TODO: optionNameStages = "stages" ) cmd := &cobra.Command{ @@ -34,18 +32,18 @@ func (c *command) initSimulateCmd() (err error) { ctx, cancel := context.WithTimeout(cmd.Context(), c.globalConfig.GetDuration(optionNameTimeout)) defer cancel() + clusterName := c.globalConfig.GetString(optionNameClusterName) + if clusterName == "" { + return errMissingClusterName + } + // set cluster config - cfgCluster, ok := c.config.Clusters[c.globalConfig.GetString(optionNameClusterName)] + cfgCluster, ok := c.config.Clusters[clusterName] if !ok { - return fmt.Errorf("cluster %s not defined", c.globalConfig.GetString(optionNameClusterName)) + return fmt.Errorf("cluster %s not defined", clusterName) } - // setup cluster - cluster, err := c.setupCluster(ctx, - c.globalConfig.GetString(optionNameClusterName), - c.config, - c.globalConfig.GetBool(optionNameCreateCluster), - ) + cluster, err := c.setupCluster(ctx, clusterName, c.globalConfig.GetBool(optionNameCreateCluster)) if err != nil { return fmt.Errorf("cluster setup: %w", err) } diff --git a/cmd/beekeeper/cmd/stamp_funder.go b/cmd/beekeeper/cmd/stamper.go similarity index 52% rename from cmd/beekeeper/cmd/stamp_funder.go rename to cmd/beekeeper/cmd/stamper.go index a1af085b8..fb4f329c3 100644 --- a/cmd/beekeeper/cmd/stamp_funder.go +++ b/cmd/beekeeper/cmd/stamper.go @@ -1,20 +1,15 @@ package cmd import ( - "context" - "errors" - "fmt" "time" - "github.com/ethersphere/beekeeper/pkg/config" - stampfunder "github.com/ethersphere/beekeeper/pkg/funder/stamp" + "github.com/ethersphere/beekeeper/pkg/stamper" "github.com/spf13/cobra" ) -func (c *command) initStampFunderCmd() (err error) { +func (c *command) initStamperCmd() (err error) { const ( optionNameNamespace = "namespace" - optionClusterName = "cluster-name" optionTTLTreshold = "ttl-treshold" optionTopUpTo = "topup-to" optionUsageThreshold = "usage-threshold" @@ -25,56 +20,29 @@ func (c *command) initStampFunderCmd() (err error) { ) cmd := &cobra.Command{ - Use: "stamp-funder", - Short: "funds stamp for nodes", - Long: `Funds stamp for nodes.`, + Use: "stamper", + Short: "Manage postage batches for nodes", + Long: `Use the stamper command to manage postage batches for nodes. Topup, dilution and creation of postage batches are supported.`, RunE: func(cmd *cobra.Command, args []string) (err error) { - cfg := config.NodeFunder{} - namespace := c.globalConfig.GetString(optionNameNamespace) - clusterName := c.globalConfig.GetString(optionClusterName) - - if namespace != "" { - cfg.Namespace = namespace - } else if clusterName != "" { - cluster, ok := c.config.Clusters[clusterName] - if !ok { - return fmt.Errorf("cluster %s not found", clusterName) - } - if cluster.Namespace == nil || *cluster.Namespace == "" { - return fmt.Errorf("cluster %s namespace not provided", clusterName) - } - cfg.Namespace = *cluster.Namespace - } else { - return errors.New("one of namespace, or valid cluster-name must be provided") - } - - // add timeout to stamp-funder - // if timeout is not set, operator will run infinitely - var ctxNew context.Context - var cancel context.CancelFunc - timeout := c.globalConfig.GetDuration(optionNameTimeout) - if timeout > 0 { - ctxNew, cancel = context.WithTimeout(cmd.Context(), timeout) - } else { - ctxNew = context.Background() - } - if cancel != nil { - defer cancel() - } + // clusterName := c.globalConfig.GetString(optionNameClusterName) - return stampfunder.NewClient(&stampfunder.ClientConfig{ + stamper := stamper.NewClient(&stamper.ClientConfig{ Log: c.log, Namespace: namespace, K8sClient: c.k8sClient, LabelSelector: c.globalConfig.GetString(optionNameLabelSelector), - }).Run(ctxNew) + }) + + _ = stamper + + return }, PreRunE: c.preRunE, } cmd.Flags().StringP(optionNameNamespace, "n", "", "Kubernetes namespace. Overrides cluster name if set.") - cmd.Flags().String(optionClusterName, "", "Name of the Beekeeper cluster to target. Ignored if a namespace is specified, in which case the namespace from the cluster configuration is used.") + cmd.Flags().String(optionNameClusterName, "", "Name of the Beekeeper cluster to target. Ignored if a namespace is specified, in which case the namespace from the cluster configuration is used.") cmd.Flags().Duration(optionTTLTreshold, 5*24*time.Hour, "Threshold for the remaining TTL of a stamp. Actions are triggered when TTL drops below this value.") cmd.Flags().Duration(optionTopUpTo, 30*24*time.Hour, "Duration to top up the TTL of a stamp to.") cmd.Flags().Float64(optionUsageThreshold, 90, "Percentage threshold for stamp utilization. Triggers dilution when usage exceeds this value.") diff --git a/pkg/config/node_funder.go b/pkg/config/node_funder.go deleted file mode 100644 index 5fd1ba750..000000000 --- a/pkg/config/node_funder.go +++ /dev/null @@ -1,14 +0,0 @@ -package config - -type NodeFunder struct { - Namespace string - Addresses []string - ChainNodeEndpoint string - WalletKey string // Hex encoded key - MinAmounts MinAmounts -} - -type MinAmounts struct { - NativeCoin float64 // on mainnet this is xDAI - SwarmToken float64 // on mainnet this is xBZZ -} diff --git a/pkg/funder/operator/operator.go b/pkg/funder/operator/operator.go index cb17cd31c..df06d2590 100644 --- a/pkg/funder/operator/operator.go +++ b/pkg/funder/operator/operator.go @@ -9,7 +9,6 @@ import ( "net/url" "github.com/ethersphere/beekeeper/pkg/bee" - "github.com/ethersphere/beekeeper/pkg/config" "github.com/ethersphere/beekeeper/pkg/k8s" "github.com/ethersphere/beekeeper/pkg/logging" "github.com/ethersphere/node-funder/pkg/funder" @@ -20,9 +19,10 @@ type ClientConfig struct { Namespace string WalletKey string ChainNodeEndpoint string - MinAmounts config.MinAmounts + NativeToken float64 + SwarmToken float64 K8sClient *k8s.Client - HTTPClient *http.Client // injected HTTP client + HTTPClient *http.Client LabelSelector string } @@ -83,8 +83,8 @@ func (c *Client) Run(ctx context.Context) error { ChainNodeEndpoint: c.ChainNodeEndpoint, WalletKey: c.WalletKey, MinAmounts: funder.MinAmounts{ - NativeCoin: c.MinAmounts.NativeCoin, - SwarmToken: c.MinAmounts.SwarmToken, + NativeCoin: c.NativeToken, + SwarmToken: c.SwarmToken, }, }, nil, nil, funder.WithLoggerOption(c.Log)) if err != nil { diff --git a/pkg/k8s/pod/client.go b/pkg/k8s/pod/client.go index 6d86153e3..adde3449c 100644 --- a/pkg/k8s/pod/client.go +++ b/pkg/k8s/pod/client.go @@ -100,6 +100,7 @@ func (c *Client) DeletePods(ctx context.Context, namespace, labelSelector string if len(deletionErrors) > 0 { return deletedCount, fmt.Errorf("some pods failed to delete: %v", deletionErrors) } + return deletedCount, nil } @@ -125,6 +126,7 @@ func (c *Client) WatchNewRunning(ctx context.Context, namespace, labelSelector s if !ok { return fmt.Errorf("watch channel closed") } + switch event.Type { // case watch.Added: // already running pods case watch.Modified: diff --git a/pkg/funder/stamp/stamp.go b/pkg/stamper/stamper.go similarity index 82% rename from pkg/funder/stamp/stamp.go rename to pkg/stamper/stamper.go index 36e6e6b26..632dc2a26 100644 --- a/pkg/funder/stamp/stamp.go +++ b/pkg/stamper/stamper.go @@ -1,7 +1,6 @@ -package stamp +package stamper import ( - "context" "io" "net/http" @@ -42,10 +41,3 @@ func NewClient(cfg *ClientConfig) *Client { ClientConfig: cfg, } } - -func (c *Client) Run(ctx context.Context) error { - c.Log.Infof("operator started") - defer c.Log.Infof("operator done") - - return nil -}