diff --git a/README.md b/README.md index 0b30d2b..0e07f2e 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,18 @@ Out of tree implementation of https://github.com/kubernetes/enhancements/tree/ma It allows users to use an ipam-controller that allocates IP ranges to Nodes, setting the node.spec.PodCIDRs fields. The ipam-controller is configured via CRDs +## Config + +* `apiserver` - The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. (Env: `IPAM_API_SERVER_URL`) +* `kubeconfig` - Path to a kubeconfig. Only required if out-of-cluster. (Env: `IPAM_KUBECONFIG`) +* `health-probe-address` - Specifies the TCP address for the health server to listen on. (Env: `IPAM_HEALTH_PROBE_ADDR`) +* `enable-leader-election` - Enable leader election for the controller manager. Ensures there is only one active controller manager. (Env: `IPAM_ENABLE_LEADER_ELECTION`) +* `leader-elect-lease-duration` - Duration that non-leader candidates will wait to force acquire leadership (duration string). (Env: `IPAM_LEASE_DURATION`) +* `leader-elect-renew-deadline` - Interval between attempts by the acting master to renew a leadership slot before it stops leading (duration string). (Env: `IPAM_RENEW_DEADLINE`) +* `leader-elect-retry-period` - Duration the clients should wait between attempting acquisition and renewal of a leadership (duration string). (Env: `IPAM_RESOURCE_LOCK`) +* `leader-elect-resource-lock` - The type of resource object that is used for locking. Supported options are 'leases', 'endpoints', 'configmaps'. (Env: `IPAM_RESOURCE_LOCK_NAME`) +* `leader-elect-resource-name` - The name of the resource object that is used for locking. (Env: `IPAM_RESOURCE_NAME`) + ## Build To build the binary for node-ipam-controller: @@ -56,3 +68,4 @@ Install node-ipam-controller in the cluster via helm: ```sh helm install node-ipam-controller ./charts/node-ipam-controller --create-namespace --namespace nodeipam --set image.tag=local ``` + diff --git a/go.mod b/go.mod index f35e0e2..bb97e68 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.21 require ( github.com/evanphx/json-patch v5.6.0+incompatible github.com/golangci/golangci-lint v1.54.2 + github.com/jessevdk/go-flags v1.6.1 github.com/onsi/ginkgo/v2 v2.13.2 github.com/onsi/gomega v1.30.0 github.com/stretchr/testify v1.8.4 @@ -210,7 +211,7 @@ require ( golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.14.0 // indirect + golang.org/x/sys v0.21.0 // indirect golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect diff --git a/go.sum b/go.sum index 715c922..0a1a605 100644 --- a/go.sum +++ b/go.sum @@ -337,6 +337,8 @@ github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= +github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/jgautheron/goconst v1.5.1 h1:HxVbL1MhydKs8R8n/HE5NPvzfaYmQJA3o879lE4+WcM= github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= @@ -824,6 +826,8 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/main.go b/main.go index 9716853..f731a4a 100644 --- a/main.go +++ b/main.go @@ -25,72 +25,64 @@ import ( "os" "time" - "sigs.k8s.io/node-ipam-controller/pkg/util/env" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/jessevdk/go-flags" + "k8s.io/client-go/tools/clientcmd" "k8s.io/component-base/logs" - logsapi "k8s.io/component-base/logs/api/v1" - clientset "sigs.k8s.io/node-ipam-controller/pkg/client/clientset/versioned" - informers "sigs.k8s.io/node-ipam-controller/pkg/client/informers/externalversions" - "sigs.k8s.io/node-ipam-controller/pkg/controller/ipam" + "sigs.k8s.io/node-ipam-controller/pkg/leaderelection" "sigs.k8s.io/node-ipam-controller/pkg/signals" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kubeinformers "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + logsapi "k8s.io/component-base/logs/api/v1" + clientset "sigs.k8s.io/node-ipam-controller/pkg/client/clientset/versioned" + informers "sigs.k8s.io/node-ipam-controller/pkg/client/informers/externalversions" + "sigs.k8s.io/node-ipam-controller/pkg/controller/ipam" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/client-go/tools/clientcmd" _ "k8s.io/component-base/logs/json/register" "k8s.io/klog/v2" ) type Config struct { - apiServerURL string - kubeconfig string - healthProbeAddr string - enableLeaderElection bool - leaseDuration time.Duration - renewDeadline time.Duration - retryPeriod time.Duration - resourceLock string - resourceName string + ApiServerURL string `long:"apiserver" description:"The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster." env:"IPAM_API_SERVER_URL"` + Kubeconfig string `long:"kubeconfig" description:"Path to a kubeconfig. Only required if out-of-cluster." env:"IPAM_KUBECONFIG"` + HealthProbeAddr string `long:"health-probe-address" default:":8081" description:"Specifies the TCP address for the health server to listen on." env:"IPAM_HEALTH_PROBE_ADDR"` + EnableLeaderElection bool `long:"enable-leader-election" description:"Enable leader election for the controller manager. Ensures there is only one active controller manager." env:"IPAM_ENABLE_LEADER_ELECTION"` + LeaseDuration time.Duration `long:"leader-elect-lease-duration" default:"15s" description:"Duration that non-leader candidates will wait to force acquire leadership (duration string)." env:"IPAM_LEASE_DURATION"` + RenewDeadline time.Duration `long:"leader-elect-renew-deadline" default:"10s" description:"Interval between attempts by the acting master to renew a leadership slot before it stops leading (duration string)." env:"IPAM_RENEW_DEADLINE"` + RetryPeriod time.Duration `long:"leader-elect-retry-period" default:"2s" description:"Duration the clients should wait between attempting acquisition and renewal of a leadership (duration string)." env:"IPAM_RESOURCE_LOCK"` + ResourceLock string `long:"leader-elect-resource-lock" default:"leases" description:"The type of resource object that is used for locking. Supported options are 'leases', 'endpoints', 'configmaps'." env:"IPAM_RESOURCE_LOCK_NAME"` + ResourceName string `long:"leader-elect-resource-name" default:"node-ipam-controller" description:"The name of the resource object that is used for locking." env:"IPAM_RESOURCE_NAME"` } -func (c *Config) Load() { - flag.StringVar(&c.kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") - flag.StringVar(&c.apiServerURL, "apiserver", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") - flag.StringVar(&c.healthProbeAddr, "health-probe-address", ":8081", "Specifies the TCP address for the health server to listen on.") - flag.BoolVar(&c.enableLeaderElection, "enable-leader-election", true, "Enable leader election for the controller manager. Ensures there is only one active controller manager.") - flag.DurationVar(&c.leaseDuration, "leader-elect-lease-duration", 15*time.Second, "Duration that non-leader candidates will wait to force acquire leadership (duration string).") - flag.DurationVar(&c.renewDeadline, "leader-elect-renew-deadline", 10*time.Second, "Interval between attempts by the acting master to renew a leadership slot before it stops leading (duration string).") - flag.DurationVar(&c.retryPeriod, "leader-elect-retry-period", 2*time.Second, "Duration the clients should wait between attempting acquisition and renewal of a leadership (duration string).") - flag.StringVar(&c.resourceLock, "leader-elect-resource-lock", "leases", "The type of resource object that is used for locking. Supported options are 'leases', 'endpoints', 'configmaps'.") - flag.StringVar(&c.resourceName, "leader-elect-resource-name", "node-ipam-controller", "The name of the resource object that is used for locking.") - flag.Parse() - - env.StringVar(&c.apiServerURL, "IPAM_API_SERVER_URL") - env.StringVar(&c.kubeconfig, "IPAM_KUBECONFIG") - env.StringVar(&c.healthProbeAddr, "IPAM_HEALTH_PROBE_ADDR") - env.BoolVar(&c.enableLeaderElection, "IPAM_ENABLE_LEADER_ELECTION") - env.DurationVar(&c.leaseDuration, "IPAM_LEASE_DURATION") - env.DurationVar(&c.renewDeadline, "IPAM_RENEW_DEADLINE") - env.DurationVar(&c.retryPeriod, "IPAM_RETRY_PERIOD") - env.StringVar(&c.resourceLock, "IPAM_RESOURCE_LOCK") - env.StringVar(&c.resourceName, "IPAM_RESOURCE_NAME") - - fmt.Println(c) +func (c *Config) Load() error { + // allows using true/false in the parameters + _, err := flags.NewParser(c, flags.Default|flags.AllowBoolValues).ParseArgs(os.Args) + if err != nil { + return err + } + return nil } func main() { c := logsapi.NewLoggingConfiguration() logsapi.AddGoFlags(c, flag.CommandLine) - config := &Config{} - config.Load() + config := Config{EnableLeaderElection: true} + err := config.Load() + if err != nil { + var flagError *flags.Error + errors.As(err, &flagError) + if flagError.Type == flags.ErrHelp { + os.Exit(0) + } + os.Exit(22) + } logs.InitLogs() if err := logsapi.ValidateAndApply(c, nil); err != nil { @@ -101,8 +93,8 @@ func main() { defer cancel() logger := klog.FromContext(ctx) - server := startHealthProbeServer(config.healthProbeAddr, logger) - cfg, err := clientcmd.BuildConfigFromFlags(config.apiServerURL, config.kubeconfig) + server := startHealthProbeServer(config.HealthProbeAddr, logger) + cfg, err := clientcmd.BuildConfigFromFlags(config.ApiServerURL, config.Kubeconfig) if err != nil { logger.Error(err, "failed to build kubeconfig") klog.FlushAndExit(klog.ExitFlushTimeout, 1) @@ -114,14 +106,14 @@ func main() { klog.FlushAndExit(klog.ExitFlushTimeout, 1) } - if config.enableLeaderElection { + if config.EnableLeaderElection { logger.Info("Leader election is enabled.") leaderelection.StartLeaderElection(ctx, kubeClient, cfg, logger, cancel, runControllers, leaderelection.Config{ - LeaseDuration: config.leaseDuration, - RenewDeadline: config.renewDeadline, - RetryPeriod: config.retryPeriod, - ResourceLock: config.resourceLock, - ResourceName: config.resourceName, + LeaseDuration: config.LeaseDuration, + RenewDeadline: config.RenewDeadline, + RetryPeriod: config.RetryPeriod, + ResourceLock: config.ResourceLock, + ResourceName: config.ResourceName, }) } else { logger.Info("Leader election is disabled.") diff --git a/pkg/util/env/env.go b/pkg/util/env/env.go deleted file mode 100644 index 0994b63..0000000 --- a/pkg/util/env/env.go +++ /dev/null @@ -1,39 +0,0 @@ -package env - -import ( - "os" - "strconv" - "time" -) - -func StringVar(val *string, name string) { - v := os.Getenv(name) - if v == "" { - return - } - *val = v -} - -func BoolVar(val *bool, name string) { - value := os.Getenv(name) - if value == "" { - return - } - v, err := strconv.ParseBool(value) - if err != nil { - return - } - *val = v -} - -func DurationVar(val *time.Duration, name string) { - value := os.Getenv(name) - if value == "" { - return - } - v, err := time.ParseDuration(value) - if err != nil { - return - } - *val = v -}