From 1a49735044fda3e0a2564d9e15c3d1babbe1ee29 Mon Sep 17 00:00:00 2001 From: Jacob Weinstock Date: Thu, 28 Sep 2023 20:09:37 -0600 Subject: [PATCH] Plumb bmc options through from cli to provider: Adds cli flags for all bmc options. The flags are hidden by default but the cli mechanism we use also makes them available as env vars. The dependencies factory is updated with a new func parameter for provider options that will allow future modifications without breaking the fn signature. the cmd/eksctl-anywhere/cmd/flags pkg was renamed to be singular. It was named similar to the pflags library but with "a" for anywhere. The csv reader was modified to take in bmc options so that the options can be added during a Read. Signed-off-by: Jacob Weinstock --- cmd/eksctl-anywhere/cmd/aflag/aflag.go | 44 ++++++++++++ .../cmd/{flags => aflag}/cluster.go | 2 +- .../cmd/{flags/require.go => aflag/marker.go} | 11 ++- .../require_test.go => aflag/marker_test.go} | 8 +-- cmd/eksctl-anywhere/cmd/aflag/tinkerbell.go | 68 +++++++++++++++++++ cmd/eksctl-anywhere/cmd/createcluster.go | 44 ++++++++++-- cmd/eksctl-anywhere/cmd/deletecluster.go | 15 +++- cmd/eksctl-anywhere/cmd/flags.go | 6 +- cmd/eksctl-anywhere/cmd/flags/flag.go | 23 ------- cmd/eksctl-anywhere/cmd/flags/tinkerbell.go | 8 --- .../generate_tinkerbell_template_config.go | 10 +-- .../cmd/generatebundleconfig.go | 17 +---- cmd/eksctl-anywhere/cmd/generatehardware.go | 28 +++++--- cmd/eksctl-anywhere/cmd/supportbundle.go | 16 +---- cmd/eksctl-anywhere/cmd/upgradecluster.go | 19 ++++-- cmd/eksctl-anywhere/cmd/upgradeplancluster.go | 16 +---- .../cmd/validatecreatecluster.go | 15 +++- .../thirdparty/tinkerbell/rufio/opts.go | 8 +-- .../tinkerbell/rufio/zz_generated.deepcopy.go | 24 +++++-- pkg/dependencies/factory.go | 18 ++++- pkg/dependencies/factory_test.go | 66 ++++++++++-------- pkg/providers/tinkerbell/create.go | 2 +- .../tinkerbell/hardware/catalogue_bmc.go | 18 ++--- .../tinkerbell/hardware/catalogue_bmc_test.go | 8 +-- pkg/providers/tinkerbell/hardware/csv.go | 20 ++++-- pkg/providers/tinkerbell/hardware/csv_test.go | 14 ++-- .../tinkerbell/hardware/validator.go | 14 ++-- pkg/providers/tinkerbell/tinkerbell.go | 2 + pkg/providers/tinkerbell/upgrade.go | 2 +- 29 files changed, 364 insertions(+), 182 deletions(-) create mode 100644 cmd/eksctl-anywhere/cmd/aflag/aflag.go rename cmd/eksctl-anywhere/cmd/{flags => aflag}/cluster.go (96%) rename cmd/eksctl-anywhere/cmd/{flags/require.go => aflag/marker.go} (55%) rename cmd/eksctl-anywhere/cmd/{flags/require_test.go => aflag/marker_test.go} (94%) create mode 100644 cmd/eksctl-anywhere/cmd/aflag/tinkerbell.go delete mode 100644 cmd/eksctl-anywhere/cmd/flags/flag.go delete mode 100644 cmd/eksctl-anywhere/cmd/flags/tinkerbell.go diff --git a/cmd/eksctl-anywhere/cmd/aflag/aflag.go b/cmd/eksctl-anywhere/cmd/aflag/aflag.go new file mode 100644 index 0000000000000..f7396ae891074 --- /dev/null +++ b/cmd/eksctl-anywhere/cmd/aflag/aflag.go @@ -0,0 +1,44 @@ +// Package aflag is the eks anywhere flag handling package. +package aflag + +import "github.com/spf13/pflag" + +// Flag defines a CLI flag. +type Flag[T any] struct { + Name string + Short string + Usage string + Default T +} + +// String applies f to fs and writes the value to dst. +func String(f Flag[string], dst *string, fs *pflag.FlagSet) { + switch { + // With short form + case f.Short != "": + fs.StringVarP(dst, f.Name, f.Short, f.Default, f.Usage) + // Without short form + default: + fs.StringVar(dst, f.Name, f.Default, f.Usage) + } +} + +// Bool applies f to fs and writes the value to dst. +func Bool(f Flag[bool], dst *bool, fs *pflag.FlagSet) { + switch { + case f.Short != "": + fs.BoolVarP(dst, f.Name, f.Short, f.Default, f.Usage) + default: + fs.BoolVar(dst, f.Name, f.Default, f.Usage) + } +} + +// StringSlice applies f to fs and writes the value to dst. +func StringSlice(f Flag[[]string], dst *[]string, fs *pflag.FlagSet) { + switch { + case f.Short != "": + fs.StringSliceVarP(dst, f.Name, f.Short, f.Default, f.Usage) + default: + fs.StringSliceVar(dst, f.Name, f.Default, f.Usage) + } +} diff --git a/cmd/eksctl-anywhere/cmd/flags/cluster.go b/cmd/eksctl-anywhere/cmd/aflag/cluster.go similarity index 96% rename from cmd/eksctl-anywhere/cmd/flags/cluster.go rename to cmd/eksctl-anywhere/cmd/aflag/cluster.go index 5d1a0c5f2b7e1..c1016306486bf 100644 --- a/cmd/eksctl-anywhere/cmd/flags/cluster.go +++ b/cmd/eksctl-anywhere/cmd/aflag/cluster.go @@ -1,4 +1,4 @@ -package flags +package aflag // ClusterConfig is the path to a cluster specification YAML. var ClusterConfig = Flag[string]{ diff --git a/cmd/eksctl-anywhere/cmd/flags/require.go b/cmd/eksctl-anywhere/cmd/aflag/marker.go similarity index 55% rename from cmd/eksctl-anywhere/cmd/flags/require.go rename to cmd/eksctl-anywhere/cmd/aflag/marker.go index aba7bbaba80e5..a22417ec9db9a 100644 --- a/cmd/eksctl-anywhere/cmd/flags/require.go +++ b/cmd/eksctl-anywhere/cmd/aflag/marker.go @@ -1,4 +1,4 @@ -package flags +package aflag import ( "github.com/spf13/cobra" @@ -13,3 +13,12 @@ func MarkRequired(set *pflag.FlagSet, flags ...string) { } } } + +// MarkHidden is a helper to mark flags hidden on cmd. If a flag does not exist, it panics. +func MarkHidden(set *pflag.FlagSet, flags ...string) { + for _, flag := range flags { + if err := set.MarkHidden(flag); err != nil { + panic(err) + } + } +} diff --git a/cmd/eksctl-anywhere/cmd/flags/require_test.go b/cmd/eksctl-anywhere/cmd/aflag/marker_test.go similarity index 94% rename from cmd/eksctl-anywhere/cmd/flags/require_test.go rename to cmd/eksctl-anywhere/cmd/aflag/marker_test.go index fa86fdd3c107b..41228219813d3 100644 --- a/cmd/eksctl-anywhere/cmd/flags/require_test.go +++ b/cmd/eksctl-anywhere/cmd/aflag/marker_test.go @@ -1,4 +1,4 @@ -package flags_test +package aflag_test import ( "testing" @@ -6,7 +6,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/flags" + "github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/aflag" ) func TestMarkRequired(t *testing.T) { @@ -77,7 +77,7 @@ func TestMarkRequired(t *testing.T) { // so we need to parse the args using the flag set before calling it. _ = cmd.Flags().Parse(tc.Args) - flags.MarkRequired(cmd.Flags(), required...) + aflag.MarkRequired(cmd.Flags(), required...) err := cmd.ValidateRequiredFlags() @@ -109,7 +109,7 @@ func TestMarkRequired_FlagDoesNotExist(t *testing.T) { }() flgs := pflag.NewFlagSet("", pflag.ContinueOnError) - flags.MarkRequired(flgs, "does-not-exist") + aflag.MarkRequired(flgs, "does-not-exist") } func nopPflag(name string) pflag.Flag { diff --git a/cmd/eksctl-anywhere/cmd/aflag/tinkerbell.go b/cmd/eksctl-anywhere/cmd/aflag/tinkerbell.go new file mode 100644 index 0000000000000..9b23115abcf8d --- /dev/null +++ b/cmd/eksctl-anywhere/cmd/aflag/tinkerbell.go @@ -0,0 +1,68 @@ +package aflag + +// TinkerbellBootstrapIP is used to override the Tinkerbell IP for serving a Tinkerbell stack +// from an admin machine. +var TinkerbellBootstrapIP = Flag[string]{ + Name: "tinkerbell-bootstrap-ip", + Usage: "The IP used to expose the Tinkerbell stack from the bootstrap cluster", +} + +// TinkerbellBMCConsumerURL is a Rufio RPC provider option. +var TinkerbellBMCConsumerURL = Flag[string]{ + Name: "tinkerbell-bmc-consumer-url", + Usage: "The URL used to expose the Tinkerbell BMC consumer from the bootstrap cluster", +} + +// TinkerbellBMCHTTPContentType is a Rufio RPC provider option. +var TinkerbellBMCHTTPContentType = Flag[string]{ + Name: "tinkerbell-bmc-http-content-type", + Usage: "The HTTP content type used to expose the Tinkerbell BMC consumer from the bootstrap cluster", +} + +// TinkerbellBMCHTTPMethod is a Rufio RPC provider option. +var TinkerbellBMCHTTPMethod = Flag[string]{ + Name: "tinkerbell-bmc-http-method", + Usage: "The HTTP method used to expose the Tinkerbell BMC consumer from the bootstrap cluster", +} + +// TinkerbellBMCTimestampHeader is a Rufio RPC provider option. +var TinkerbellBMCTimestampHeader = Flag[string]{ + Name: "tinkerbell-bmc-timestamp-header", + Usage: "The HTTP timestamp header used to expose the Tinkerbell BMC consumer from the bootstrap cluster", +} + +// TinkerbellBMCStaticHeaders is a Rufio RPC provider option. +var TinkerbellBMCStaticHeaders = Flag[string]{ + Name: "tinkerbell-bmc-static-headers", + Usage: "The HTTP static headers used to expose the Tinkerbell BMC consumer from the bootstrap cluster", +} + +// TinkerbellBMCHeaderName is a Rufio RPC provider option. +var TinkerbellBMCHeaderName = Flag[string]{ + Name: "tinkerbell-bmc-header-name", + Usage: "The HTTP header name used to expose the Tinkerbell BMC consumer from the bootstrap cluster", +} + +// TinkerbellBMCAppendAlgoToHeaderDisabled is a Rufio RPC provider option. +var TinkerbellBMCAppendAlgoToHeaderDisabled = Flag[bool]{ + Name: "tinkerbell-bmc-append-algo-to-header-disabled", + Usage: "The HTTP append algo to header disabled used to expose the Tinkerbell BMC consumer from the bootstrap cluster", +} + +// TinkerbellBMCIncludedPayloadHeaders is a Rufio RPC provider option. +var TinkerbellBMCIncludedPayloadHeaders = Flag[[]string]{ + Name: "tinkerbell-bmc-included-payload-headers", + Usage: "The HTTP included payload headers used to expose the Tinkerbell BMC consumer from the bootstrap cluster. If you specify a Timestamp header, it must be included here.", +} + +// TinkerbellBMCPrefixSigDisabled is a Rufio RPC provider option. +var TinkerbellBMCPrefixSigDisabled = Flag[bool]{ + Name: "tinkerbell-bmc-prefix-sig-disabled", + Usage: "The HTTP prefix sig disabled used to expose the Tinkerbell BMC consumer from the bootstrap cluster", +} + +// TinkerbellBMCWebhookSecrets is a Rufio RPC provider option. +var TinkerbellBMCWebhookSecrets = Flag[[]string]{ + Name: "tinkerbell-bmc-webhook-secrets", + Usage: "The webhook secrets used to expose the Tinkerbell BMC consumer from the bootstrap cluster", +} diff --git a/cmd/eksctl-anywhere/cmd/createcluster.go b/cmd/eksctl-anywhere/cmd/createcluster.go index dbc2f05640428..c3d0713d6c636 100644 --- a/cmd/eksctl-anywhere/cmd/createcluster.go +++ b/cmd/eksctl-anywhere/cmd/createcluster.go @@ -6,8 +6,9 @@ import ( "strings" "github.com/spf13/cobra" + "github.com/spf13/pflag" - "github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/flags" + "github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/aflag" "github.com/aws/eks-anywhere/pkg/api/v1alpha1" "github.com/aws/eks-anywhere/pkg/awsiamauth" "github.com/aws/eks-anywhere/pkg/clustermanager" @@ -16,6 +17,7 @@ import ( "github.com/aws/eks-anywhere/pkg/features" "github.com/aws/eks-anywhere/pkg/kubeconfig" "github.com/aws/eks-anywhere/pkg/logger" + "github.com/aws/eks-anywhere/pkg/providers/tinkerbell/hardware" "github.com/aws/eks-anywhere/pkg/types" "github.com/aws/eks-anywhere/pkg/validations" "github.com/aws/eks-anywhere/pkg/validations/createvalidations" @@ -32,9 +34,18 @@ type createClusterOptions struct { tinkerbellBootstrapIP string installPackages string skipValidations []string + providerOptions *dependencies.ProviderOptions } -var cc = &createClusterOptions{} +var cc = &createClusterOptions{ + providerOptions: &dependencies.ProviderOptions{ + Tinkerbell: &dependencies.TinkerbellOptions{ + BMCOptions: &hardware.BMCOptions{ + RPC: &hardware.RPCOpts{}, + }, + }, + }, +} var createClusterCmd = &cobra.Command{ Use: "cluster -f [flags]", @@ -50,14 +61,37 @@ func init() { applyClusterOptionFlags(createClusterCmd.Flags(), &cc.clusterOptions) applyTimeoutFlags(createClusterCmd.Flags(), &cc.timeoutOptions) applyTinkerbellHardwareFlag(createClusterCmd.Flags(), &cc.hardwareCSVPath) - flags.String(flags.TinkerbellBootstrapIP, &cc.tinkerbellBootstrapIP, createClusterCmd.Flags()) + aflag.String(aflag.TinkerbellBootstrapIP, &cc.tinkerbellBootstrapIP, createClusterCmd.Flags()) createClusterCmd.Flags().BoolVar(&cc.forceClean, "force-cleanup", false, "Force deletion of previously created bootstrap cluster") hideForceCleanup(createClusterCmd.Flags()) createClusterCmd.Flags().BoolVar(&cc.skipIpCheck, "skip-ip-check", false, "Skip check for whether cluster control plane ip is in use") createClusterCmd.Flags().StringVar(&cc.installPackages, "install-packages", "", "Location of curated packages configuration files to install to the cluster") createClusterCmd.Flags().StringArrayVar(&cc.skipValidations, "skip-validations", []string{}, fmt.Sprintf("Bypass create validations by name. Valid arguments you can pass are --skip-validations=%s", strings.Join(createvalidations.SkippableValidations[:], ","))) + tinkerbellFlags(createClusterCmd.Flags(), cc.providerOptions.Tinkerbell.BMCOptions.RPC) + + aflag.MarkRequired(createClusterCmd.Flags(), aflag.ClusterConfig.Name) +} - flags.MarkRequired(createClusterCmd.Flags(), flags.ClusterConfig.Name) +func tinkerbellFlags(fs *pflag.FlagSet, r *hardware.RPCOpts) { + aflag.String(aflag.TinkerbellBMCConsumerURL, &r.ConsumerURL, fs) + aflag.MarkHidden(fs, aflag.TinkerbellBMCConsumerURL.Name) + aflag.String(aflag.TinkerbellBMCHTTPContentType, &r.Request.HTTPContentType, fs) + aflag.MarkHidden(fs, aflag.TinkerbellBMCHTTPContentType.Name) + aflag.String(aflag.TinkerbellBMCHTTPMethod, &r.Request.HTTPMethod, fs) + aflag.MarkHidden(fs, aflag.TinkerbellBMCHTTPMethod.Name) + aflag.String(aflag.TinkerbellBMCTimestampHeader, &r.Request.TimestampHeader, fs) + aflag.MarkHidden(fs, aflag.TinkerbellBMCTimestampHeader.Name) + // add static headers here + aflag.String(aflag.TinkerbellBMCHeaderName, &r.Signature.HeaderName, fs) + aflag.MarkHidden(fs, aflag.TinkerbellBMCHeaderName.Name) + aflag.Bool(aflag.TinkerbellBMCAppendAlgoToHeaderDisabled, &r.Signature.AppendAlgoToHeaderDisabled, fs) + aflag.MarkHidden(fs, aflag.TinkerbellBMCAppendAlgoToHeaderDisabled.Name) + aflag.StringSlice(aflag.TinkerbellBMCIncludedPayloadHeaders, &r.Signature.IncludedPayloadHeaders, fs) + aflag.MarkHidden(fs, aflag.TinkerbellBMCIncludedPayloadHeaders.Name) + aflag.Bool(aflag.TinkerbellBMCPrefixSigDisabled, &r.HMAC.PrefixSigDisabled, fs) + aflag.MarkHidden(fs, aflag.TinkerbellBMCPrefixSigDisabled.Name) + aflag.StringSlice(aflag.TinkerbellBMCWebhookSecrets, &r.HMAC.Secrets, fs) + aflag.MarkHidden(fs, aflag.TinkerbellBMCWebhookSecrets.Name) } func (cc *createClusterOptions) createCluster(cmd *cobra.Command, _ []string) error { @@ -141,7 +175,7 @@ func (cc *createClusterOptions) createCluster(cmd *cobra.Command, _ []string) er WithBootstrapper(). WithCliConfig(cliConfig). WithClusterManager(clusterSpec.Cluster, clusterManagerTimeoutOpts). - WithProvider(cc.fileName, clusterSpec.Cluster, cc.skipIpCheck, cc.hardwareCSVPath, cc.forceClean, cc.tinkerbellBootstrapIP, skippedValidations). + WithProvider(cc.fileName, clusterSpec.Cluster, cc.skipIpCheck, cc.hardwareCSVPath, cc.forceClean, cc.tinkerbellBootstrapIP, skippedValidations, cc.providerOptions). WithGitOpsFlux(clusterSpec.Cluster, clusterSpec.FluxConfig, cliConfig). WithWriter(). WithEksdInstaller(). diff --git a/cmd/eksctl-anywhere/cmd/deletecluster.go b/cmd/eksctl-anywhere/cmd/deletecluster.go index f5c92afc83a6b..f653de634b443 100644 --- a/cmd/eksctl-anywhere/cmd/deletecluster.go +++ b/cmd/eksctl-anywhere/cmd/deletecluster.go @@ -10,6 +10,7 @@ import ( "github.com/aws/eks-anywhere/pkg/dependencies" "github.com/aws/eks-anywhere/pkg/kubeconfig" "github.com/aws/eks-anywhere/pkg/logger" + "github.com/aws/eks-anywhere/pkg/providers/tinkerbell/hardware" "github.com/aws/eks-anywhere/pkg/types" "github.com/aws/eks-anywhere/pkg/validations" "github.com/aws/eks-anywhere/pkg/workflows" @@ -21,9 +22,18 @@ type deleteClusterOptions struct { forceCleanup bool hardwareFileName string tinkerbellBootstrapIP string + providerOptions *dependencies.ProviderOptions } -var dc = &deleteClusterOptions{} +var dc = &deleteClusterOptions{ + providerOptions: &dependencies.ProviderOptions{ + Tinkerbell: &dependencies.TinkerbellOptions{ + BMCOptions: &hardware.BMCOptions{ + RPC: &hardware.RPCOpts{}, + }, + }, + }, +} var deleteClusterCmd = &cobra.Command{ Use: "cluster (|-f )", @@ -50,6 +60,7 @@ func init() { hideForceCleanup(deleteClusterCmd.Flags()) deleteClusterCmd.Flags().StringVar(&dc.managementKubeconfig, "kubeconfig", "", "kubeconfig file pointing to a management cluster") deleteClusterCmd.Flags().StringVar(&dc.bundlesOverride, "bundles-override", "", "Override default Bundles manifest (not recommended)") + tinkerbellFlags(deleteClusterCmd.Flags(), dc.providerOptions.Tinkerbell.BMCOptions.RPC) } func (dc *deleteClusterOptions) validate(ctx context.Context, args []string) error { @@ -101,7 +112,7 @@ func (dc *deleteClusterOptions) deleteCluster(ctx context.Context) error { WithBootstrapper(). WithCliConfig(cliConfig). WithClusterManager(clusterSpec.Cluster, nil). - WithProvider(dc.fileName, clusterSpec.Cluster, cc.skipIpCheck, dc.hardwareFileName, false, dc.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(dc.fileName, clusterSpec.Cluster, cc.skipIpCheck, dc.hardwareFileName, false, dc.tinkerbellBootstrapIP, map[string]bool{}, dc.providerOptions). WithGitOpsFlux(clusterSpec.Cluster, clusterSpec.FluxConfig, cliConfig). WithWriter(). Build(ctx) diff --git a/cmd/eksctl-anywhere/cmd/flags.go b/cmd/eksctl-anywhere/cmd/flags.go index 6ce557433489f..e7c7283f1ab6f 100644 --- a/cmd/eksctl-anywhere/cmd/flags.go +++ b/cmd/eksctl-anywhere/cmd/flags.go @@ -9,7 +9,7 @@ import ( "github.com/spf13/pflag" "github.com/spf13/viper" - "github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/flags" + "github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/aflag" "github.com/aws/eks-anywhere/pkg/validations" ) @@ -47,8 +47,8 @@ func bindFlagsToViper(cmd *cobra.Command, args []string) error { } func applyClusterOptionFlags(flagSet *pflag.FlagSet, clusterOpt *clusterOptions) { - flags.String(flags.ClusterConfig, &clusterOpt.fileName, flagSet) - flags.String(flags.BundleOverride, &clusterOpt.bundlesOverride, flagSet) + aflag.String(aflag.ClusterConfig, &clusterOpt.fileName, flagSet) + aflag.String(aflag.BundleOverride, &clusterOpt.bundlesOverride, flagSet) flagSet.StringVar(&clusterOpt.managementKubeconfig, "kubeconfig", "", "Management cluster kubeconfig file") } diff --git a/cmd/eksctl-anywhere/cmd/flags/flag.go b/cmd/eksctl-anywhere/cmd/flags/flag.go deleted file mode 100644 index 963acecac6283..0000000000000 --- a/cmd/eksctl-anywhere/cmd/flags/flag.go +++ /dev/null @@ -1,23 +0,0 @@ -package flags - -import "github.com/spf13/pflag" - -// Flag defines a CLI flag. -type Flag[T any] struct { - Name string - Short string - Usage string - Default T -} - -// String applies f to fs and writes the value to dst. -func String(f Flag[string], dst *string, fs *pflag.FlagSet) { - switch { - // With short form - case f.Short != "": - fs.StringVarP(dst, f.Name, f.Short, f.Default, f.Usage) - // Without short form - default: - fs.StringVar(dst, f.Name, f.Default, f.Usage) - } -} diff --git a/cmd/eksctl-anywhere/cmd/flags/tinkerbell.go b/cmd/eksctl-anywhere/cmd/flags/tinkerbell.go deleted file mode 100644 index 44c1d05dcd57d..0000000000000 --- a/cmd/eksctl-anywhere/cmd/flags/tinkerbell.go +++ /dev/null @@ -1,8 +0,0 @@ -package flags - -// TinkerbellBootstrapIP is used to override the Tinkerbell IP for serving a Tinkerbell stack -// from an admin machine. -var TinkerbellBootstrapIP = Flag[string]{ - Name: "tinkerbell-bootstrap-ip", - Usage: "The IP used to expose the Tinkerbell stack from the bootstrap cluster", -} diff --git a/cmd/eksctl-anywhere/cmd/generate_tinkerbell_template_config.go b/cmd/eksctl-anywhere/cmd/generate_tinkerbell_template_config.go index 058eb18265982..69061be2c0083 100644 --- a/cmd/eksctl-anywhere/cmd/generate_tinkerbell_template_config.go +++ b/cmd/eksctl-anywhere/cmd/generate_tinkerbell_template_config.go @@ -8,7 +8,7 @@ import ( "github.com/spf13/pflag" "github.com/spf13/viper" - "github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/flags" + "github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/aflag" "github.com/aws/eks-anywhere/pkg/api/v1alpha1" "github.com/aws/eks-anywhere/pkg/logger" "github.com/aws/eks-anywhere/pkg/networkutils" @@ -42,9 +42,9 @@ func NewGenerateTinkerbellTemplateConfig() *cobra.Command { // Configure the flagset. Some of these flags are duplicated from other parts of the cmd code // for consistency but their descriptions may vary because of the commands use-case. flgs := pflag.NewFlagSet("", pflag.ContinueOnError) - flags.String(flags.ClusterConfig, &opts.fileName, flgs) - flags.String(flags.BundleOverride, &opts.bundlesOverride, flgs) - flags.String(flags.TinkerbellBootstrapIP, &opts.BootstrapTinkerbellIP, flgs) + aflag.String(aflag.ClusterConfig, &opts.fileName, flgs) + aflag.String(aflag.BundleOverride, &opts.bundlesOverride, flgs) + aflag.String(aflag.TinkerbellBootstrapIP, &opts.BootstrapTinkerbellIP, flgs) cmd := &cobra.Command{ Use: "tinkerbelltemplateconfig", @@ -54,7 +54,7 @@ func NewGenerateTinkerbellTemplateConfig() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { // When the bootstrap IP is unspecified attempt to derive it from IPs assigned to the // primary interface. - if f := flgs.Lookup(flags.TinkerbellBootstrapIP.Name); !f.Changed { + if f := flgs.Lookup(aflag.TinkerbellBootstrapIP.Name); !f.Changed { bootstrapIP, err := networkutils.GetLocalIP() if err != nil { return fmt.Errorf("tinkerbell bootstrap ip: %v", err) diff --git a/cmd/eksctl-anywhere/cmd/generatebundleconfig.go b/cmd/eksctl-anywhere/cmd/generatebundleconfig.go index 8c42af4cb85f6..57ea017e02766 100644 --- a/cmd/eksctl-anywhere/cmd/generatebundleconfig.go +++ b/cmd/eksctl-anywhere/cmd/generatebundleconfig.go @@ -3,11 +3,8 @@ package cmd import ( "context" "fmt" - "log" "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" "github.com/aws/eks-anywhere/pkg/api/v1alpha1" "github.com/aws/eks-anywhere/pkg/dependencies" @@ -29,7 +26,7 @@ var generateBundleConfigCmd = &cobra.Command{ Use: "support-bundle-config", Short: "Generate support bundle config", Long: "This command is used to generate a default support bundle config yaml", - PreRunE: preRunGenerateBundleConfigCmd, + PreRunE: bindFlagsToViper, RunE: func(cmd *cobra.Command, args []string) error { err := gsbo.validateCmdInput() if err != nil { @@ -52,16 +49,6 @@ func init() { generateBundleConfigCmd.Flags().StringVarP(&gsbo.fileName, "filename", "f", "", "Filename that contains EKS-A cluster configuration") } -func preRunGenerateBundleConfigCmd(cmd *cobra.Command, args []string) error { - cmd.Flags().VisitAll(func(flag *pflag.Flag) { - err := viper.BindPFlag(flag.Name, flag) - if err != nil { - log.Fatalf("Error initializing flags: %v", err) - } - }) - return nil -} - func (gsbo *generateSupportBundleOptions) validateCmdInput() error { f := gsbo.fileName if f != "" { @@ -89,7 +76,7 @@ func (gsbo *generateSupportBundleOptions) generateBundleConfig(ctx context.Conte } deps, err := dependencies.ForSpec(ctx, clusterSpec). - WithProvider(clusterConfigPath, clusterSpec.Cluster, cc.skipIpCheck, gsbo.hardwareFileName, false, gsbo.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(clusterConfigPath, clusterSpec.Cluster, cc.skipIpCheck, gsbo.hardwareFileName, false, gsbo.tinkerbellBootstrapIP, map[string]bool{}, nil). WithDiagnosticBundleFactory(). Build(ctx) if err != nil { diff --git a/cmd/eksctl-anywhere/cmd/generatehardware.go b/cmd/eksctl-anywhere/cmd/generatehardware.go index 7ddb6f9af4246..0f565d8d38406 100644 --- a/cmd/eksctl-anywhere/cmd/generatehardware.go +++ b/cmd/eksctl-anywhere/cmd/generatehardware.go @@ -6,15 +6,25 @@ import ( "github.com/spf13/cobra" + "github.com/aws/eks-anywhere/pkg/dependencies" "github.com/aws/eks-anywhere/pkg/providers/tinkerbell/hardware" ) type hardwareOptions struct { - csvPath string - outputPath string + csvPath string + outputPath string + providerOptions *dependencies.ProviderOptions } -var hOpts = &hardwareOptions{} +var hOpts = &hardwareOptions{ + providerOptions: &dependencies.ProviderOptions{ + Tinkerbell: &dependencies.TinkerbellOptions{ + BMCOptions: &hardware.BMCOptions{ + RPC: &hardware.RPCOpts{}, + }, + }, + }, +} var generateHardwareCmd = &cobra.Command{ Use: "hardware", @@ -22,15 +32,16 @@ var generateHardwareCmd = &cobra.Command{ Long: ` Generate Kubernetes hardware YAML manifests for each Hardware entry in the source. `, - RunE: hOpts.generateHardware, + RunE: hOpts.generateHardware, + PreRunE: bindFlagsToViper, } func init() { generateCmd.AddCommand(generateHardwareCmd) - flags := generateHardwareCmd.Flags() - flags.StringVarP(&hOpts.outputPath, "output", "o", "", "Path to output hardware YAML.") - flags.StringVarP( + fset := generateHardwareCmd.Flags() + fset.StringVarP(&hOpts.outputPath, "output", "o", "", "Path to output hardware YAML.") + fset.StringVarP( &hOpts.csvPath, TinkerbellHardwareCSVFlagName, TinkerbellHardwareCSVFlagAlias, @@ -41,10 +52,11 @@ func init() { if err := generateHardwareCmd.MarkFlagRequired(TinkerbellHardwareCSVFlagName); err != nil { panic(err) } + tinkerbellFlags(fset, hOpts.providerOptions.Tinkerbell.BMCOptions.RPC) } func (hOpts *hardwareOptions) generateHardware(cmd *cobra.Command, args []string) error { - hardwareYaml, err := hardware.BuildHardwareYAML(hOpts.csvPath) + hardwareYaml, err := hardware.BuildHardwareYAML(hOpts.csvPath, hOpts.providerOptions.Tinkerbell.BMCOptions) if err != nil { return fmt.Errorf("building hardware yaml from csv: %v", err) } diff --git a/cmd/eksctl-anywhere/cmd/supportbundle.go b/cmd/eksctl-anywhere/cmd/supportbundle.go index 1916a376434b3..d576cfee876e3 100644 --- a/cmd/eksctl-anywhere/cmd/supportbundle.go +++ b/cmd/eksctl-anywhere/cmd/supportbundle.go @@ -7,8 +7,6 @@ import ( "time" "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" "github.com/aws/eks-anywhere/pkg/dependencies" "github.com/aws/eks-anywhere/pkg/diagnostics" @@ -32,7 +30,7 @@ var supportbundleCmd = &cobra.Command{ Use: "support-bundle -f my-cluster.yaml", Short: "Generate a support bundle", Long: "This command is used to create a support bundle to troubleshoot a cluster", - PreRunE: preRunSupportBundle, + PreRunE: bindFlagsToViper, SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { if err := csbo.validate(cmd.Context()); err != nil { @@ -72,16 +70,6 @@ func (csbo *createSupportBundleOptions) validate(ctx context.Context) error { return nil } -func preRunSupportBundle(cmd *cobra.Command, args []string) error { - cmd.Flags().VisitAll(func(flag *pflag.Flag) { - err := viper.BindPFlag(flag.Name, flag) - if err != nil { - log.Fatalf("Error initializing flags: %v", err) - } - }) - return nil -} - func (csbo *createSupportBundleOptions) createBundle(ctx context.Context, since, sinceTime, bundleConfig string) error { clusterSpec, err := readAndValidateClusterSpec(csbo.fileName, version.Get()) if err != nil { @@ -89,7 +77,7 @@ func (csbo *createSupportBundleOptions) createBundle(ctx context.Context, since, } deps, err := dependencies.ForSpec(ctx, clusterSpec). - WithProvider(csbo.fileName, clusterSpec.Cluster, cc.skipIpCheck, csbo.hardwareFileName, false, csbo.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(csbo.fileName, clusterSpec.Cluster, cc.skipIpCheck, csbo.hardwareFileName, false, csbo.tinkerbellBootstrapIP, map[string]bool{}, nil). WithDiagnosticBundleFactory(). Build(ctx) if err != nil { diff --git a/cmd/eksctl-anywhere/cmd/upgradecluster.go b/cmd/eksctl-anywhere/cmd/upgradecluster.go index 12e2098ab9531..26557d5837899 100644 --- a/cmd/eksctl-anywhere/cmd/upgradecluster.go +++ b/cmd/eksctl-anywhere/cmd/upgradecluster.go @@ -8,12 +8,13 @@ import ( "github.com/spf13/cobra" - "github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/flags" + "github.com/aws/eks-anywhere/cmd/eksctl-anywhere/cmd/aflag" "github.com/aws/eks-anywhere/pkg/api/v1alpha1" "github.com/aws/eks-anywhere/pkg/dependencies" "github.com/aws/eks-anywhere/pkg/features" "github.com/aws/eks-anywhere/pkg/kubeconfig" "github.com/aws/eks-anywhere/pkg/logger" + "github.com/aws/eks-anywhere/pkg/providers/tinkerbell/hardware" "github.com/aws/eks-anywhere/pkg/types" "github.com/aws/eks-anywhere/pkg/validations" "github.com/aws/eks-anywhere/pkg/validations/upgradevalidations" @@ -29,9 +30,18 @@ type upgradeClusterOptions struct { hardwareCSVPath string tinkerbellBootstrapIP string skipValidations []string + providerOptions *dependencies.ProviderOptions } -var uc = &upgradeClusterOptions{} +var uc = &upgradeClusterOptions{ + providerOptions: &dependencies.ProviderOptions{ + Tinkerbell: &dependencies.TinkerbellOptions{ + BMCOptions: &hardware.BMCOptions{ + RPC: &hardware.RPCOpts{}, + }, + }, + }, +} var upgradeClusterCmd = &cobra.Command{ Use: "cluster", @@ -62,7 +72,8 @@ func init() { hideForceCleanup(upgradeClusterCmd.Flags()) upgradeClusterCmd.Flags().StringArrayVar(&uc.skipValidations, "skip-validations", []string{}, fmt.Sprintf("Bypass upgrade validations by name. Valid arguments you can pass are --skip-validations=%s", strings.Join(upgradevalidations.SkippableValidations[:], ","))) - flags.MarkRequired(createClusterCmd.Flags(), flags.ClusterConfig.Name) + aflag.MarkRequired(createClusterCmd.Flags(), aflag.ClusterConfig.Name) + tinkerbellFlags(upgradeClusterCmd.Flags(), uc.providerOptions.Tinkerbell.BMCOptions.RPC) } func (uc *upgradeClusterOptions) upgradeCluster(cmd *cobra.Command) error { @@ -129,7 +140,7 @@ func (uc *upgradeClusterOptions) upgradeCluster(cmd *cobra.Command) error { WithCliConfig(cliConfig). WithClusterManager(clusterSpec.Cluster, clusterManagerTimeoutOpts). WithClusterApplier(). - WithProvider(uc.fileName, clusterSpec.Cluster, cc.skipIpCheck, uc.hardwareCSVPath, uc.forceClean, uc.tinkerbellBootstrapIP, skippedValidations). + WithProvider(uc.fileName, clusterSpec.Cluster, cc.skipIpCheck, uc.hardwareCSVPath, uc.forceClean, uc.tinkerbellBootstrapIP, skippedValidations, uc.providerOptions). WithGitOpsFlux(clusterSpec.Cluster, clusterSpec.FluxConfig, cliConfig). WithWriter(). WithCAPIManager(). diff --git a/cmd/eksctl-anywhere/cmd/upgradeplancluster.go b/cmd/eksctl-anywhere/cmd/upgradeplancluster.go index 9cc2b01d34bbc..0f163be9e4a35 100644 --- a/cmd/eksctl-anywhere/cmd/upgradeplancluster.go +++ b/cmd/eksctl-anywhere/cmd/upgradeplancluster.go @@ -9,8 +9,6 @@ import ( "text/tabwriter" "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" capiupgrader "github.com/aws/eks-anywhere/pkg/clusterapi" eksaupgrader "github.com/aws/eks-anywhere/pkg/clustermanager" @@ -34,7 +32,7 @@ var upgradePlanClusterCmd = &cobra.Command{ Use: "cluster", Short: "Provides new release versions for the next cluster upgrade", Long: "Provides a list of target versions for upgrading the core components in the workload cluster", - PreRunE: preRunUpgradePlanCluster, + PreRunE: bindFlagsToViper, SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { if err := uc.upgradePlanCluster(cmd.Context()); err != nil { @@ -44,16 +42,6 @@ var upgradePlanClusterCmd = &cobra.Command{ }, } -func preRunUpgradePlanCluster(cmd *cobra.Command, args []string) error { - cmd.Flags().VisitAll(func(flag *pflag.Flag) { - err := viper.BindPFlag(flag.Name, flag) - if err != nil { - log.Fatalf("Error initializing flags: %v", err) - } - }) - return nil -} - func init() { upgradePlanCmd.AddCommand(upgradePlanClusterCmd) upgradePlanClusterCmd.Flags().StringVarP(&uc.fileName, "filename", "f", "", "Filename that contains EKS-A cluster configuration") @@ -78,7 +66,7 @@ func (uc *upgradeClusterOptions) upgradePlanCluster(ctx context.Context) error { deps, err := dependencies.ForSpec(ctx, newClusterSpec). WithClusterManager(newClusterSpec.Cluster, nil). - WithProvider(uc.fileName, newClusterSpec.Cluster, false, uc.hardwareCSVPath, uc.forceClean, uc.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(uc.fileName, newClusterSpec.Cluster, false, uc.hardwareCSVPath, uc.forceClean, uc.tinkerbellBootstrapIP, map[string]bool{}, uc.providerOptions). WithGitOpsFlux(newClusterSpec.Cluster, newClusterSpec.FluxConfig, nil). WithCAPIManager(). Build(ctx) diff --git a/cmd/eksctl-anywhere/cmd/validatecreatecluster.go b/cmd/eksctl-anywhere/cmd/validatecreatecluster.go index de3bda127f4fb..c63855a8b2b81 100644 --- a/cmd/eksctl-anywhere/cmd/validatecreatecluster.go +++ b/cmd/eksctl-anywhere/cmd/validatecreatecluster.go @@ -9,6 +9,7 @@ import ( "github.com/aws/eks-anywhere/pkg/api/v1alpha1" "github.com/aws/eks-anywhere/pkg/dependencies" "github.com/aws/eks-anywhere/pkg/kubeconfig" + "github.com/aws/eks-anywhere/pkg/providers/tinkerbell/hardware" "github.com/aws/eks-anywhere/pkg/types" "github.com/aws/eks-anywhere/pkg/validations" "github.com/aws/eks-anywhere/pkg/validations/createcluster" @@ -20,9 +21,18 @@ type validateOptions struct { clusterOptions hardwareCSVPath string tinkerbellBootstrapIP string + providerOptions *dependencies.ProviderOptions } -var valOpt = &validateOptions{} +var valOpt = &validateOptions{ + providerOptions: &dependencies.ProviderOptions{ + Tinkerbell: &dependencies.TinkerbellOptions{ + BMCOptions: &hardware.BMCOptions{ + RPC: &hardware.RPCOpts{}, + }, + }, + }, +} var validateCreateClusterCmd = &cobra.Command{ Use: "cluster -f [flags]", @@ -42,6 +52,7 @@ func init() { if err := validateCreateClusterCmd.MarkFlagRequired("filename"); err != nil { log.Fatalf("Error marking flag as required: %v", err) } + tinkerbellFlags(validateCreateClusterCmd.Flags(), valOpt.providerOptions.Tinkerbell.BMCOptions.RPC) } func (valOpt *validateOptions) validateCreateCluster(cmd *cobra.Command, _ []string) error { @@ -73,7 +84,7 @@ func (valOpt *validateOptions) validateCreateCluster(cmd *cobra.Command, _ []str WithWriterFolder(tmpPath). WithDocker(). WithKubectl(). - WithProvider(valOpt.fileName, clusterSpec.Cluster, false, valOpt.hardwareCSVPath, true, valOpt.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(valOpt.fileName, clusterSpec.Cluster, false, valOpt.hardwareCSVPath, true, valOpt.tinkerbellBootstrapIP, map[string]bool{}, valOpt.providerOptions). WithGitOpsFlux(clusterSpec.Cluster, clusterSpec.FluxConfig, cliConfig). WithUnAuthKubeClient(). WithValidatorClients(). diff --git a/pkg/api/v1alpha1/thirdparty/tinkerbell/rufio/opts.go b/pkg/api/v1alpha1/thirdparty/tinkerbell/rufio/opts.go index faa2764f031ae..bd479dc69ea42 100644 --- a/pkg/api/v1alpha1/thirdparty/tinkerbell/rufio/opts.go +++ b/pkg/api/v1alpha1/thirdparty/tinkerbell/rufio/opts.go @@ -45,16 +45,16 @@ type RPCOptions struct { LogNotificationsDisabled bool `json:"logNotificationsDisabled"` // Request is the options used to create the rpc HTTP request. // +optional - Request RequestOpts `json:"request"` + Request *RequestOpts `json:"request"` // Signature is the options used for adding an HMAC signature to an HTTP request. // +optional - Signature SignatureOpts `json:"signature"` + Signature *SignatureOpts `json:"signature"` // HMAC is the options used to create a HMAC signature. // +optional - HMAC HMACOpts `json:"hmac"` + HMAC *HMACOpts `json:"hmac"` // Experimental options. // +optional - Experimental ExperimentalOpts `json:"experimental"` + Experimental *ExperimentalOpts `json:"experimental"` } // RequestOpts are the options used when creating an HTTP request. diff --git a/pkg/api/v1alpha1/thirdparty/tinkerbell/rufio/zz_generated.deepcopy.go b/pkg/api/v1alpha1/thirdparty/tinkerbell/rufio/zz_generated.deepcopy.go index e959b8e80fcd3..3d6d15284991e 100644 --- a/pkg/api/v1alpha1/thirdparty/tinkerbell/rufio/zz_generated.deepcopy.go +++ b/pkg/api/v1alpha1/thirdparty/tinkerbell/rufio/zz_generated.deepcopy.go @@ -301,10 +301,26 @@ func (in *ProviderOptions) DeepCopy() *ProviderOptions { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RPCOptions) DeepCopyInto(out *RPCOptions) { *out = *in - in.Request.DeepCopyInto(&out.Request) - in.Signature.DeepCopyInto(&out.Signature) - in.HMAC.DeepCopyInto(&out.HMAC) - out.Experimental = in.Experimental + if in.Request != nil { + in, out := &in.Request, &out.Request + *out = new(RequestOpts) + (*in).DeepCopyInto(*out) + } + if in.Signature != nil { + in, out := &in.Signature, &out.Signature + *out = new(SignatureOpts) + (*in).DeepCopyInto(*out) + } + if in.HMAC != nil { + in, out := &in.HMAC, &out.HMAC + *out = new(HMACOpts) + (*in).DeepCopyInto(*out) + } + if in.Experimental != nil { + in, out := &in.Experimental, &out.Experimental + *out = new(ExperimentalOpts) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RPCOptions. diff --git a/pkg/dependencies/factory.go b/pkg/dependencies/factory.go index 51c158080574e..d8494436cfad1 100644 --- a/pkg/dependencies/factory.go +++ b/pkg/dependencies/factory.go @@ -47,6 +47,7 @@ import ( "github.com/aws/eks-anywhere/pkg/providers/nutanix" "github.com/aws/eks-anywhere/pkg/providers/snow" "github.com/aws/eks-anywhere/pkg/providers/tinkerbell" + "github.com/aws/eks-anywhere/pkg/providers/tinkerbell/hardware" "github.com/aws/eks-anywhere/pkg/providers/validator" "github.com/aws/eks-anywhere/pkg/providers/vsphere" "github.com/aws/eks-anywhere/pkg/registrymirror" @@ -381,8 +382,20 @@ func (f *Factory) WithExecutableBuilder() *Factory { return f } +// ProviderOptions contains per provider options. +type ProviderOptions struct { + // Tinkerbell contains Tinkerbell specific options. + Tinkerbell *TinkerbellOptions +} + +// TinkerbellOptions contains Tinkerbell specific options. +type TinkerbellOptions struct { + // BMCOptions contains options for configuring BMC interactions. + BMCOptions *hardware.BMCOptions +} + // WithProvider initializes the provider dependency and adds to the build steps. -func (f *Factory) WithProvider(clusterConfigFile string, clusterConfig *v1alpha1.Cluster, skipIPCheck bool, hardwareCSVPath string, force bool, tinkerbellBootstrapIP string, skippedValidations map[string]bool) *Factory { // nolint:gocyclo +func (f *Factory) WithProvider(clusterConfigFile string, clusterConfig *v1alpha1.Cluster, skipIPCheck bool, hardwareCSVPath string, force bool, tinkerbellBootstrapIP string, skippedValidations map[string]bool, opts *ProviderOptions) *Factory { // nolint:gocyclo switch clusterConfig.Spec.DatacenterRef.Kind { case v1alpha1.VSphereDatacenterKind: f.WithKubectl().WithGovc().WithWriter().WithIPValidator() @@ -489,6 +502,9 @@ func (f *Factory) WithProvider(clusterConfigFile string, clusterConfig *v1alpha1 if err != nil { return err } + if opts != nil && opts.Tinkerbell != nil && opts.Tinkerbell.BMCOptions != nil { + provider.BMCOptions = opts.Tinkerbell.BMCOptions + } f.dependencies.Provider = provider diff --git a/pkg/dependencies/factory_test.go b/pkg/dependencies/factory_test.go index b419a17060987..b5d9e5c86241c 100644 --- a/pkg/dependencies/factory_test.go +++ b/pkg/dependencies/factory_test.go @@ -18,6 +18,7 @@ import ( "github.com/aws/eks-anywhere/pkg/dependencies" "github.com/aws/eks-anywhere/pkg/executables" "github.com/aws/eks-anywhere/pkg/providers/cloudstack/decoder" + "github.com/aws/eks-anywhere/pkg/providers/tinkerbell/hardware" "github.com/aws/eks-anywhere/pkg/registrymirror" "github.com/aws/eks-anywhere/pkg/version" "github.com/aws/eks-anywhere/release/api/v1alpha1" @@ -33,6 +34,7 @@ type factoryTest struct { cliConfig config.CliConfig createCLIConfig config.CreateClusterCLIConfig upgradeCLIConfig config.UpgradeClusterCLIConfig + providerOptions *dependencies.ProviderOptions } type provider string @@ -45,44 +47,50 @@ const ( ) func newTest(t *testing.T, p provider) *factoryTest { - var clusterConfigFile string + f := &factoryTest{ + WithT: NewGomegaWithT(t), + ctx: context.Background(), + createCLIConfig: config.CreateClusterCLIConfig{ + SkipCPIPCheck: false, + }, + upgradeCLIConfig: config.UpgradeClusterCLIConfig{ + NodeStartupTimeout: 5 * time.Minute, + UnhealthyMachineTimeout: 5 * time.Minute, + }, + } + switch p { case vsphere: - clusterConfigFile = "testdata/cluster_vsphere.yaml" + f.clusterConfigFile = "testdata/cluster_vsphere.yaml" case tinkerbell: - clusterConfigFile = "testdata/cluster_tinkerbell.yaml" + f.clusterConfigFile = "testdata/cluster_tinkerbell.yaml" + f.providerOptions = &dependencies.ProviderOptions{ + Tinkerbell: &dependencies.TinkerbellOptions{ + BMCOptions: &hardware.BMCOptions{ + RPC: &hardware.RPCOpts{ + ConsumerURL: "http://example.com", + }, + }, + }, + } case nutanix: - clusterConfigFile = "testdata/nutanix/cluster_nutanix.yaml" + f.clusterConfigFile = "testdata/nutanix/cluster_nutanix.yaml" case snow: - clusterConfigFile = "testdata/snow/cluster_snow.yaml" + f.clusterConfigFile = "testdata/snow/cluster_snow.yaml" default: t.Fatalf("Not a valid provider: %v", p) } - createCLIConfig := config.CreateClusterCLIConfig{ - SkipCPIPCheck: false, - } + f.clusterSpec = test.NewFullClusterSpec(t, f.clusterConfigFile) - upgradeCLIConfig := config.UpgradeClusterCLIConfig{ - NodeStartupTimeout: 5 * time.Minute, - UnhealthyMachineTimeout: 5 * time.Minute, - } - - return &factoryTest{ - WithT: NewGomegaWithT(t), - clusterConfigFile: clusterConfigFile, - clusterSpec: test.NewFullClusterSpec(t, clusterConfigFile), - ctx: context.Background(), - createCLIConfig: createCLIConfig, - upgradeCLIConfig: upgradeCLIConfig, - } + return f } func TestFactoryBuildWithProvidervSphere(t *testing.T) { tt := newTest(t, vsphere) deps, err := dependencies.NewFactory(). WithLocalExecutables(). - WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}, tt.providerOptions). Build(context.Background()) tt.Expect(err).To(BeNil()) @@ -94,7 +102,7 @@ func TestFactoryBuildWithProviderTinkerbell(t *testing.T) { tt := newTest(t, tinkerbell) deps, err := dependencies.NewFactory(). WithLocalExecutables(). - WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}, tt.providerOptions). Build(context.Background()) tt.Expect(err).To(BeNil()) @@ -110,7 +118,7 @@ func TestFactoryBuildWithProviderSnow(t *testing.T) { deps, err := dependencies.NewFactory(). WithLocalExecutables(). - WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}, tt.providerOptions). Build(context.Background()) tt.Expect(err).To(BeNil()) @@ -143,7 +151,7 @@ func TestFactoryBuildWithProviderNutanix(t *testing.T) { deps, err := dependencies.NewFactory(). WithLocalExecutables(). - WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}, tt.providerOptions). WithNutanixValidator(). Build(context.Background()) @@ -164,7 +172,7 @@ func TestFactoryBuildWithInvalidProvider(t *testing.T) { deps, err := dependencies.NewFactory(). WithLocalExecutables(). - WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}, tt.providerOptions). Build(context.Background()) tt.Expect(err).NotTo(BeNil()) @@ -209,7 +217,7 @@ func TestFactoryBuildWithMultipleDependencies(t *testing.T) { WithBootstrapper(). WithCliConfig(&tt.cliConfig). WithClusterManager(tt.clusterSpec.Cluster, timeoutOpts). - WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}, tt.providerOptions). WithGitOpsFlux(tt.clusterSpec.Cluster, tt.clusterSpec.FluxConfig, nil). WithWriter(). WithEksdInstaller(). @@ -525,7 +533,7 @@ func TestFactoryBuildWithCNIInstallerCilium(t *testing.T) { factory := dependencies.NewFactory() deps, err := factory. WithLocalExecutables(). - WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}, tt.providerOptions). Build(tt.ctx) tt.Expect(err).To(BeNil()) @@ -547,7 +555,7 @@ func TestFactoryBuildWithCNIInstallerKindnetd(t *testing.T) { factory := dependencies.NewFactory() deps, err := factory. WithLocalExecutables(). - WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}). + WithProvider(tt.clusterConfigFile, tt.clusterSpec.Cluster, false, tt.hardwareConfigFile, false, tt.tinkerbellBootstrapIP, map[string]bool{}, tt.providerOptions). Build(tt.ctx) tt.Expect(err).To(BeNil()) diff --git a/pkg/providers/tinkerbell/create.go b/pkg/providers/tinkerbell/create.go index 1d95a639ed6d2..d6207f97962a0 100644 --- a/pkg/providers/tinkerbell/create.go +++ b/pkg/providers/tinkerbell/create.go @@ -233,7 +233,7 @@ func (p *Provider) readCSVToCatalogue() error { // Translate all Machine instances from the p.machines source into Kubernetes object types. // The PostBootstrapSetup() call invoked elsewhere in the program serializes the catalogue // and submits it to the clsuter. - machines, err := hardware.NewNormalizedCSVReaderFromFile(p.hardwareCSVFile) + machines, err := hardware.NewNormalizedCSVReaderFromFile(p.hardwareCSVFile, p.BMCOptions) if err != nil { return err } diff --git a/pkg/providers/tinkerbell/hardware/catalogue_bmc.go b/pkg/providers/tinkerbell/hardware/catalogue_bmc.go index c87836eb4bd1d..d1cecacff45a5 100644 --- a/pkg/providers/tinkerbell/hardware/catalogue_bmc.go +++ b/pkg/providers/tinkerbell/hardware/catalogue_bmc.go @@ -100,7 +100,7 @@ func toRufioMachine(m Machine) *v1alpha1.Machine { } if m.BMCOptions != nil && m.BMCOptions.RPC.ConsumerURL != "" { conn.ProviderOptions = &v1alpha1.ProviderOptions{ - RPC: toRPCOptions(m.BMCOptions.RPC), + RPC: toRPCOptions(m.BMCOptions.RPC, m), } } return &v1alpha1.Machine{ @@ -115,21 +115,21 @@ func toRufioMachine(m Machine) *v1alpha1.Machine { } } -func toRPCOptions(r *RPCOpts) *v1alpha1.RPCOptions { +func toRPCOptions(r *RPCOpts, m Machine) *v1alpha1.RPCOptions { opts := &v1alpha1.RPCOptions{ ConsumerURL: r.ConsumerURL, } if req := toRequestOpts(r.Request); req != nil { - opts.Request = *req + opts.Request = req } if sig := toSignatureOpts(r.Signature); sig != nil { - opts.Signature = *sig + opts.Signature = sig } - if hmac := toHMACOpts(r.HMAC); hmac != nil { - opts.HMAC = *hmac + if hmac := toHMACOpts(r.HMAC, m); hmac != nil { + opts.HMAC = hmac } if exp := toExperimentalOpts(r.Experimental); exp != nil { - opts.Experimental = *exp + opts.Experimental = exp } return opts @@ -189,7 +189,7 @@ func toSignatureOpts(s SignatureOpts) *v1alpha1.SignatureOpts { return sig } -func toHMACOpts(h HMACOpts) *v1alpha1.HMACOpts { +func toHMACOpts(h HMACOpts, m Machine) *v1alpha1.HMACOpts { hmac := &v1alpha1.HMACOpts{} empty := true if h.PrefixSigDisabled { @@ -201,7 +201,7 @@ func toHMACOpts(h HMACOpts) *v1alpha1.HMACOpts { for idx := range h.Secrets { s := corev1.SecretReference{ // TODO(jacobweinstock): get hostname from the machine object. - Name: fmt.Sprintf("%v-%v", "bmc-localhost-auth", idx), + Name: fmt.Sprintf("%v-%v", formatBMCSecretRef(m), idx), Namespace: constants.EksaSystemNamespace, } hmac.Secrets[rufio.HMACAlgorithm("sha256")] = append(hmac.Secrets[rufio.HMACAlgorithm("sha256")], s) diff --git a/pkg/providers/tinkerbell/hardware/catalogue_bmc_test.go b/pkg/providers/tinkerbell/hardware/catalogue_bmc_test.go index 8d5548e380fb5..3c6154f8c33d8 100644 --- a/pkg/providers/tinkerbell/hardware/catalogue_bmc_test.go +++ b/pkg/providers/tinkerbell/hardware/catalogue_bmc_test.go @@ -100,19 +100,19 @@ func TestBMCMachineWithOptions(t *testing.T) { ProviderOptions: &v1alpha1.ProviderOptions{ RPC: &v1alpha1.RPCOptions{ ConsumerURL: "https://example.net", - Request: v1alpha1.RequestOpts{ + Request: &v1alpha1.RequestOpts{ HTTPContentType: "application/vnd.api+json", HTTPMethod: "POST", StaticHeaders: map[string][]string{"myheader": {"myvalue"}}, TimestampFormat: "2006-01-02T15:04:05Z07:00", // time.RFC3339 TimestampHeader: "X-Example-Timestamp", }, - Signature: v1alpha1.SignatureOpts{ + Signature: &v1alpha1.SignatureOpts{ HeaderName: "X-Example-Signature", AppendAlgoToHeaderDisabled: true, IncludedPayloadHeaders: []string{"X-Example-Timestamp"}, }, - HMAC: v1alpha1.HMACOpts{ + HMAC: &v1alpha1.HMACOpts{ PrefixSigDisabled: true, Secrets: map[v1alpha1.HMACAlgorithm][]v1.SecretReference{ v1alpha1.HMACAlgorithm("sha256"): { @@ -125,7 +125,7 @@ func TestBMCMachineWithOptions(t *testing.T) { }, }, }, - Experimental: v1alpha1.ExperimentalOpts{ + Experimental: &v1alpha1.ExperimentalOpts{ CustomRequestPayload: `{"data":{"type":"articles","id":"1","attributes":{"title": "Rails is Omakase"},"relationships":{"author":{"links":{"self":"/articles/1/relationships/author","related":"/articles/1/author"},"data":null}}}}`, DotPath: "data.relationships.author.data", }, diff --git a/pkg/providers/tinkerbell/hardware/csv.go b/pkg/providers/tinkerbell/hardware/csv.go index f21ac1fa8c122..4f57788c50dbd 100644 --- a/pkg/providers/tinkerbell/hardware/csv.go +++ b/pkg/providers/tinkerbell/hardware/csv.go @@ -18,11 +18,13 @@ import ( // the Machine is optional in the CSV. If unspecified, CSVReader will generate a UUID and apply it to the machine. type CSVReader struct { reader *csv.Unmarshaller + // BMCOptions used in a Machine that do not have a corresponding column in the CSV. + BMCOptions *BMCOptions } // NewCSVReader returns a new CSVReader instance that consumes csv data from r. r should return io.EOF when no more // records are available. -func NewCSVReader(r io.Reader) (CSVReader, error) { +func NewCSVReader(r io.Reader, opts *BMCOptions) (CSVReader, error) { stdreader := stdcsv.NewReader(r) reader, err := csv.NewUnmarshaller(stdreader, Machine{}) @@ -34,7 +36,7 @@ func NewCSVReader(r io.Reader) (CSVReader, error) { return CSVReader{}, err } - return CSVReader{reader: reader}, nil + return CSVReader{reader: reader, BMCOptions: opts}, nil } // Read reads a single entry from the CSV data source and returns a new Machine representation. @@ -43,19 +45,23 @@ func (cr CSVReader) Read() (Machine, error) { if err != nil { return Machine{}, err } + m := machine.(Machine) + if cr.BMCOptions != nil { + m.BMCOptions = cr.BMCOptions + } - return machine.(Machine), nil + return m, nil } // NewNormalizedCSVReaderFromFile creates a MachineReader instance backed by a CSVReader reading from path // that applies default normalizations to machines. -func NewNormalizedCSVReaderFromFile(path string) (MachineReader, error) { +func NewNormalizedCSVReaderFromFile(path string, opts *BMCOptions) (MachineReader, error) { fh, err := os.Open(path) if err != nil { return CSVReader{}, err } - reader, err := NewCSVReader(bufio.NewReader(fh)) + reader, err := NewCSVReader(bufio.NewReader(fh), opts) if err != nil { return nil, err } @@ -93,8 +99,8 @@ func ensureRequiredColumnsInCSV(unmatched []string) error { } // BuildHardwareYAML builds a hardware yaml from the csv at the provided path. -func BuildHardwareYAML(path string) ([]byte, error) { - reader, err := NewNormalizedCSVReaderFromFile(path) +func BuildHardwareYAML(path string, opts *BMCOptions) ([]byte, error) { + reader, err := NewNormalizedCSVReaderFromFile(path, opts) if err != nil { return nil, fmt.Errorf("reading csv: %v", err) } diff --git a/pkg/providers/tinkerbell/hardware/csv_test.go b/pkg/providers/tinkerbell/hardware/csv_test.go index 48d7fcad8ef40..91adb1741f2c4 100644 --- a/pkg/providers/tinkerbell/hardware/csv_test.go +++ b/pkg/providers/tinkerbell/hardware/csv_test.go @@ -25,7 +25,7 @@ func TestCSVReaderReads(t *testing.T) { err := csv.MarshalCSV([]hardware.Machine{expect}, buf) g.Expect(err).ToNot(gomega.HaveOccurred()) - reader, err := hardware.NewCSVReader(buf.Buffer) + reader, err := hardware.NewCSVReader(buf.Buffer, nil) g.Expect(err).ToNot(gomega.HaveOccurred()) machine, err := reader.Read() @@ -45,7 +45,7 @@ func TestCSVReaderWithMultipleLabels(t *testing.T) { err := csv.MarshalCSV([]hardware.Machine{expect}, buf) g.Expect(err).ToNot(gomega.HaveOccurred()) - reader, err := hardware.NewCSVReader(buf.Buffer) + reader, err := hardware.NewCSVReader(buf.Buffer, nil) g.Expect(err).ToNot(gomega.HaveOccurred()) machine, err := reader.Read() @@ -56,7 +56,7 @@ func TestCSVReaderWithMultipleLabels(t *testing.T) { func TestCSVReaderFromFile(t *testing.T) { g := gomega.NewWithT(t) - reader, err := hardware.NewNormalizedCSVReaderFromFile("./testdata/hardware.csv") + reader, err := hardware.NewNormalizedCSVReaderFromFile("./testdata/hardware.csv", nil) g.Expect(err).ToNot(gomega.HaveOccurred()) machine, err := reader.Read() @@ -83,7 +83,7 @@ func TestNewCSVReaderWithIOReaderError(t *testing.T) { expect := errors.New("read err") - _, err := hardware.NewCSVReader(iotest.ErrReader(expect)) + _, err := hardware.NewCSVReader(iotest.ErrReader(expect), nil) g.Expect(err).To(gomega.HaveOccurred()) g.Expect(err.Error()).To(gomega.ContainSubstring(expect.Error())) } @@ -91,7 +91,7 @@ func TestNewCSVReaderWithIOReaderError(t *testing.T) { func TestCSVReaderWithoutBMCHeaders(t *testing.T) { g := gomega.NewWithT(t) - reader, err := hardware.NewNormalizedCSVReaderFromFile("./testdata/hardware_no_bmc_headers.csv") + reader, err := hardware.NewNormalizedCSVReaderFromFile("./testdata/hardware_no_bmc_headers.csv", nil) g.Expect(err).ToNot(gomega.HaveOccurred()) machine, err := reader.Read() @@ -137,7 +137,7 @@ func TestCSVReaderWithMissingRequiredColumns(t *testing.T) { buf := bytes.NewBufferString(fmt.Sprintf("%v", strings.Join(included, ","))) g := gomega.NewWithT(t) - _, err := hardware.NewCSVReader(buf) + _, err := hardware.NewCSVReader(buf, nil) g.Expect(err).To(gomega.HaveOccurred()) g.Expect(err.Error()).To(gomega.ContainSubstring(missing)) }) @@ -147,7 +147,7 @@ func TestCSVReaderWithMissingRequiredColumns(t *testing.T) { func TestCSVBuildHardwareYamlFromCSV(t *testing.T) { g := gomega.NewWithT(t) - hardwareYaml, err := hardware.BuildHardwareYAML("./testdata/hardware.csv") + hardwareYaml, err := hardware.BuildHardwareYAML("./testdata/hardware.csv", nil) g.Expect(err).ToNot(gomega.HaveOccurred()) g.Expect(hardwareYaml).To(gomega.Equal([]byte(`apiVersion: tinkerbell.org/v1alpha1 kind: Hardware diff --git a/pkg/providers/tinkerbell/hardware/validator.go b/pkg/providers/tinkerbell/hardware/validator.go index 6c97e769155f2..e37d099f6a5d9 100644 --- a/pkg/providers/tinkerbell/hardware/validator.go +++ b/pkg/providers/tinkerbell/hardware/validator.go @@ -127,12 +127,14 @@ func StaticMachineAssertions() MachineAssertion { return fmt.Errorf("BMCIPAddress: %v", err) } - if m.BMCUsername == "" { - return newEmptyFieldError("BMCUsername") - } - - if m.BMCPassword == "" { - return newEmptyFieldError("BMCPassword") + if m.BMCOptions == nil || m.BMCOptions.RPC == nil { + if m.BMCUsername == "" { + return newEmptyFieldError("BMCUsername") + } + + if m.BMCPassword == "" { + return newEmptyFieldError("BMCPassword") + } } } diff --git a/pkg/providers/tinkerbell/tinkerbell.go b/pkg/providers/tinkerbell/tinkerbell.go index cdc5210104e55..d9cbf51478e3d 100644 --- a/pkg/providers/tinkerbell/tinkerbell.go +++ b/pkg/providers/tinkerbell/tinkerbell.go @@ -60,6 +60,8 @@ type Provider struct { hardwareCSVFile string catalogue *hardware.Catalogue tinkerbellIP string + // BMCOptions are Rufio BMC options that are used when creating Rufio machine CRDs. + BMCOptions *hardware.BMCOptions // TODO(chrisdoheryt4) Temporarily depend on the netclient until the validator can be injected. // This is already a dependency, just uncached, because we require it during the initializing diff --git a/pkg/providers/tinkerbell/upgrade.go b/pkg/providers/tinkerbell/upgrade.go index efebea19be7c7..46a8ab63a0cf0 100644 --- a/pkg/providers/tinkerbell/upgrade.go +++ b/pkg/providers/tinkerbell/upgrade.go @@ -68,7 +68,7 @@ func (p *Provider) SetupAndValidateUpgradeCluster(ctx context.Context, cluster * if p.hardwareCSVIsProvided() { machineCatalogueWriter := hardware.NewMachineCatalogueWriter(p.catalogue) - machines, err := hardware.NewNormalizedCSVReaderFromFile(p.hardwareCSVFile) + machines, err := hardware.NewNormalizedCSVReaderFromFile(p.hardwareCSVFile, p.BMCOptions) if err != nil { return err }