diff --git a/docs/2-the-zarf-cli/100-cli-commands/zarf_init.md b/docs/2-the-zarf-cli/100-cli-commands/zarf_init.md index 7452223bb9..7233c8a48f 100644 --- a/docs/2-the-zarf-cli/100-cli-commands/zarf_init.md +++ b/docs/2-the-zarf-cli/100-cli-commands/zarf_init.md @@ -32,7 +32,7 @@ $ zarf init --components=git-server $ zarf init --components=git-server,logging # Initializing w/ an internal registry but with a different nodeport: -$ zarf init --nodeport=30333 +$ zarf init --set REGISTRY_NODEPORT=30333 # Initializing w/ an external registry: $ zarf init --registry-push-password={PASSWORD} --registry-push-username={USERNAME} --registry-url={URL} @@ -61,7 +61,6 @@ $ zarf init --artifact-push-password={PASSWORD} --artifact-push-username={USERNA --git-push-username string Username to access to the git server Zarf is configured to use. User must be able to create repositories via 'git push' (default "zarf-git-user") --git-url string External git server url to use for this Zarf cluster -h, --help help for init - --nodeport int Nodeport to access a registry internal to the k8s cluster. Between [30000-32767] --registry-pull-password string Password for the pull-only user to access the registry --registry-pull-username string Username for pull-only access to the registry --registry-push-password string Password for the push-user to connect to the registry @@ -69,7 +68,6 @@ $ zarf init --artifact-push-password={PASSWORD} --artifact-push-username={USERNA --registry-secret string Registry secret value --registry-url string External registry url address to use for this Zarf cluster --set stringToString Specify deployment variables to set on the command line (KEY=value) (default []) - --storage-class string Specify the storage class to use for the registry and git server. E.g. --storage-class=standard ``` ## Options inherited from parent commands diff --git a/packages/gitea/gitea-values.yaml b/packages/gitea/gitea-values.yaml index d8b4aa8c22..1aad609f80 100644 --- a/packages/gitea/gitea-values.yaml +++ b/packages/gitea/gitea-values.yaml @@ -1,5 +1,5 @@ persistence: - storageClass: "###ZARF_STORAGE_CLASS###" + storageClass: "###ZARF_VAR_REGISTRY_STORAGE_CLASS###" existingClaim: "###ZARF_VAR_GIT_SERVER_EXISTING_PVC###" size: "###ZARF_VAR_GIT_SERVER_PVC_SIZE###" accessModes: diff --git a/packages/zarf-registry/registry-values.yaml b/packages/zarf-registry/registry-values.yaml index bfaf046aa1..ae49084414 100644 --- a/packages/zarf-registry/registry-values.yaml +++ b/packages/zarf-registry/registry-values.yaml @@ -1,6 +1,6 @@ persistence: enabled: ###ZARF_VAR_REGISTRY_PVC_ENABLED### - storageClass: "###ZARF_STORAGE_CLASS###" + storageClass: "###ZARF_VAR_REGISTRY_STORAGE_CLASS###" size: "###ZARF_VAR_REGISTRY_PVC_SIZE###" existingClaim: "###ZARF_VAR_REGISTRY_EXISTING_PVC###" accessMode: "###ZARF_VAR_REGISTRY_PVC_ACCESS_MODE###" @@ -19,7 +19,7 @@ secrets: secret: "###ZARF_REGISTRY_SECRET###" service: - nodePort: "###ZARF_NODEPORT###" + nodePort: "###ZARF_VAR_REGISTRY_NODEPORT###" resources: requests: diff --git a/packages/zarf-registry/zarf.yaml b/packages/zarf-registry/zarf.yaml index 38a13006c1..b8ad0c990d 100644 --- a/packages/zarf-registry/zarf.yaml +++ b/packages/zarf-registry/zarf.yaml @@ -58,6 +58,16 @@ variables: default: "" autoIndent: true + - name: REGISTRY_NODEPORT + description: NodePort for the registry + default: "31999" + autoIndent: true + + - name: REGISTRY_STORAGE_CLASS + description: StorageClass for the registry PVC + default: "" + autoIndent: true + constants: - name: REGISTRY_IMAGE value: "###ZARF_PKG_TMPL_REGISTRY_IMAGE###" diff --git a/src/cmd/initialize.go b/src/cmd/initialize.go index 193ba0f0d4..7ab42a2bfd 100644 --- a/src/cmd/initialize.go +++ b/src/cmd/initialize.go @@ -10,6 +10,7 @@ import ( "os" "path" "path/filepath" + "strconv" "strings" "github.com/AlecAivazis/survey/v2" @@ -26,6 +27,9 @@ import ( "github.com/spf13/cobra" ) +var nodePortArg = 0 +var storageClassArg = "" + // initCmd represents the init command. var initCmd = &cobra.Command{ Use: "init", @@ -61,6 +65,10 @@ var initCmd = &cobra.Command{ pkgConfig.PkgOpts.SetVariables = helpers.TransformAndMergeMap( v.GetStringMapString(common.VPkgDeploySet), pkgConfig.PkgOpts.SetVariables, strings.ToUpper) + // DEPRECATED_V1.0.0: these functions will need cleanup + setRegistryStorageClass() + setRegistryNodePort() + // Configure the packager pkgClient := packager.NewOrDie(&pkgConfig, packager.WithSource(src)) defer pkgClient.ClearTempPaths() @@ -73,6 +81,62 @@ var initCmd = &cobra.Command{ }, } +// DEPRECATED_V1.0.0: --nodeport should be removed from the cli in v1.0.0 +func setRegistryNodePort() { + configVar := "REGISTRY_NODEPORT" + + internalRegistry := pkgConfig.InitOpts.RegistryInfo.Address == "" + + // warn for deprecation + if nodePortArg != 0 { + message.Warn(lang.WarnNodePortDeprecated) + } + + // check the --set REGISTRY_NODEPORT first + configuredNodePort, err := strconv.Atoi(pkgConfig.PkgOpts.SetVariables[configVar]) + if err != nil { + configuredNodePort = 0 + } + + // check the old --nodeport flag second + if configuredNodePort == 0 { + configuredNodePort = nodePortArg + } + + // the user can't set both that this is an external registry and the nodeport stuff. + if !internalRegistry && configuredNodePort != 0 { + message.Fatal(nil, "both --registry-url and --nodeport are set, please only use one") + } + + if internalRegistry { + if configuredNodePort > 32767 || configuredNodePort < 30000 { + configuredNodePort = config.ZarfInClusterContainerRegistryNodePort + } + pkgConfig.PkgOpts.SetVariables[configVar] = strconv.Itoa(configuredNodePort) + pkgConfig.InitOpts.RegistryInfo.Address = fmt.Sprintf("%s:%d", helpers.IPV4Localhost, configuredNodePort) + pkgConfig.InitOpts.RegistryInfo.NodePort = configuredNodePort + } else { + // do not set the nodeport if this is an external registry + pkgConfig.PkgOpts.SetVariables[configVar] = "" + } + + pkgConfig.InitOpts.RegistryInfo.InternalRegistry = internalRegistry + + // TODO: test external registry and CLI. +} + +// DEPRECATED_V1.0.0: --storage-class should be removed from the CLI in v1.0.0 +func setRegistryStorageClass() { + configVar := "REGISTRY_STORAGE_CLASS" + // there is no validation if this storage class is valid + if storageClassArg != "" { + message.Warn(lang.WarnStorageClassDeprecated) + } + if pkgConfig.PkgOpts.SetVariables[configVar] == "" { + pkgConfig.PkgOpts.SetVariables[configVar] = storageClassArg + } +} + func findInitPackage(initPackageName string) (string, error) { // First, look for the init package in the current working directory if !utils.InvalidPath(initPackageName) { @@ -188,7 +252,10 @@ func init() { // Continue to require --confirm flag for init command to avoid accidental deployments initCmd.Flags().BoolVar(&config.CommonOptions.Confirm, "confirm", false, lang.CmdInitFlagConfirm) initCmd.Flags().StringVar(&pkgConfig.PkgOpts.OptionalComponents, "components", v.GetString(common.VInitComponents), lang.CmdInitFlagComponents) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.StorageClass, "storage-class", v.GetString(common.VInitStorageClass), lang.CmdInitFlagStorageClass) + + // [Deprecated] --storage-class is deprecated in favor of --set REGISTRY_STORAGE_CLASS (to be removed in v1.0.0) + initCmd.Flags().StringVar(&storageClassArg, "storage-class", v.GetString(common.VInitStorageClass), lang.CmdInitFlagStorageClass) + initCmd.Flags().MarkHidden("storage-class") // Flags for using an external Git server initCmd.Flags().StringVar(&pkgConfig.InitOpts.GitServer.Address, "git-url", v.GetString(common.VInitGitURL), lang.CmdInitFlagGitURL) @@ -199,7 +266,11 @@ func init() { // Flags for using an external registry initCmd.Flags().StringVar(&pkgConfig.InitOpts.RegistryInfo.Address, "registry-url", v.GetString(common.VInitRegistryURL), lang.CmdInitFlagRegURL) - initCmd.Flags().IntVar(&pkgConfig.InitOpts.RegistryInfo.NodePort, "nodeport", v.GetInt(common.VInitRegistryNodeport), lang.CmdInitFlagRegNodePort) + + // [Deprecated] --nodeport is deprecated in favor of --set REGISTRY_NODEPORT + initCmd.Flags().IntVar(&nodePortArg, "nodeport", v.GetInt(common.VInitRegistryNodeport), lang.CmdInitFlagRegNodePort) + initCmd.Flags().MarkHidden("nodeport") + initCmd.Flags().StringVar(&pkgConfig.InitOpts.RegistryInfo.PushUsername, "registry-push-username", v.GetString(common.VInitRegistryPushUser), lang.CmdInitFlagRegPushUser) initCmd.Flags().StringVar(&pkgConfig.InitOpts.RegistryInfo.PushPassword, "registry-push-password", v.GetString(common.VInitRegistryPushPass), lang.CmdInitFlagRegPushPass) initCmd.Flags().StringVar(&pkgConfig.InitOpts.RegistryInfo.PullUsername, "registry-pull-username", v.GetString(common.VInitRegistryPullUser), lang.CmdInitFlagRegPullUser) diff --git a/src/cmd/initialize_test.go b/src/cmd/initialize_test.go new file mode 100644 index 0000000000..72444fe7a5 --- /dev/null +++ b/src/cmd/initialize_test.go @@ -0,0 +1,33 @@ +// DEPRECATED_V1.0.0: do not check pkgConfig.InitOpts.RegistryInfo.NodePort, always overwrite it. +package cmd + +import ( + "testing" +) + +func TestSetRegistryStorageClass(t *testing.T) { + // Test case 1: storageClass is set + pkgConfig.PkgOpts.SetVariables = map[string]string{"REGISTRY_STORAGE_CLASS": "test-storage-class"} + storageClassArg = "" + setRegistryStorageClass() + if storageClassArg != "test-storage-class" { + t.Errorf("Expected storage class to be set to 'test-storage-class', but got '%s'", storageClassArg) + } + + // Test case 2: storageClass is not set, use old way + pkgConfig.PkgOpts.SetVariables = map[string]string{} + storageClassArg = "old-storage-class" + setRegistryStorageClass() + if pkgConfig.PkgOpts.SetVariables["REGISTRY_STORAGE_CLASS"] != "old-storage-class" { + t.Errorf("Expected storage class to be set to old way value, but got '%s'", pkgConfig.PkgOpts.SetVariables["REGISTRY_STORAGE_CLASS"]) + } + + // Test case 3: neither is set, should be empty + pkgConfig.PkgOpts.SetVariables = map[string]string{} + storageClassArg = "" + setRegistryStorageClass() + if pkgConfig.PkgOpts.SetVariables["REGISTRY_STORAGE_CLASS"] != "" { + t.Errorf("Expected storage class to be set to empty string, but got '%s'", pkgConfig.PkgOpts.SetVariables["REGISTRY_STORAGE_CLASS"]) + } + +} diff --git a/src/config/lang/english.go b/src/config/lang/english.go index 1957931fad..91c367acee 100644 --- a/src/config/lang/english.go +++ b/src/config/lang/english.go @@ -31,6 +31,13 @@ const ( ErrFileNameExtract = "failed to extract filename from URL %s: %s" ) +// Zarf CLI deprecation warnings +const ( + // remove deprecation in v1.0.0 + WarnStorageClassDeprecated = "--storage-class is deprecated, please use --set REGISTRY_STORAGE_CLASS=" + WarnNodePortDeprecated = "--nodeport is deprecated, please use --set REGISTRY_NODEPORT=" +) + // Zarf CLI commands. const ( // common command language @@ -128,7 +135,7 @@ $ zarf init --components=git-server $ zarf init --components=git-server,logging # Initializing w/ an internal registry but with a different nodeport: -$ zarf init --nodeport=30333 +$ zarf init --set REGISTRY_NODEPORT=30333 # Initializing w/ an external registry: $ zarf init --registry-push-password={PASSWORD} --registry-push-username={USERNAME} --registry-url={URL} @@ -158,7 +165,7 @@ $ zarf init --artifact-push-password={PASSWORD} --artifact-push-username={USERNA CmdInitFlagConfirm = "Confirms package deployment without prompting. ONLY use with packages you trust. Skips prompts to review SBOM, configure variables, select optional components and review potential breaking changes." CmdInitFlagComponents = "Specify which optional components to install. E.g. --components=git-server,logging" - CmdInitFlagStorageClass = "Specify the storage class to use for the registry and git server. E.g. --storage-class=standard" + CmdInitFlagStorageClass = "[DEPRECATED] This command has been replaced by 'zarf init --set REGISTRY_STORAGE_CLASS=' and will be removed in Zarf v1.0.0" CmdInitFlagGitURL = "External git server url to use for this Zarf cluster" CmdInitFlagGitPushUser = "Username to access to the git server Zarf is configured to use. User must be able to create repositories via 'git push'" @@ -167,7 +174,7 @@ $ zarf init --artifact-push-password={PASSWORD} --artifact-push-username={USERNA CmdInitFlagGitPullPass = "Password for the pull-only user to access the git server" CmdInitFlagRegURL = "External registry url address to use for this Zarf cluster" - CmdInitFlagRegNodePort = "Nodeport to access a registry internal to the k8s cluster. Between [30000-32767]" + CmdInitFlagRegNodePort = "[DEPRECATED] Nodeport to access a registry internal to the k8s cluster. Between [30000-32767]" CmdInitFlagRegPushUser = "Username to access to the registry Zarf is configured to use" CmdInitFlagRegPushPass = "Password for the push-user to connect to the registry" CmdInitFlagRegPullUser = "Username for pull-only access to the registry" diff --git a/src/internal/cluster/state.go b/src/internal/cluster/state.go index af13f4322f..774123c186 100644 --- a/src/internal/cluster/state.go +++ b/src/internal/cluster/state.go @@ -127,21 +127,6 @@ func (c *Cluster) InitZarfState(initOptions types.ZarfInitOptions) error { } } - switch state.Distro { - case k8s.DistroIsK3s, k8s.DistroIsK3d: - state.StorageClass = "local-path" - - case k8s.DistroIsKind, k8s.DistroIsGKE: - state.StorageClass = "standard" - - case k8s.DistroIsDockerDesktop: - state.StorageClass = "hostpath" - } - - if initOptions.StorageClass != "" { - state.StorageClass = initOptions.StorageClass - } - spinner.Success() // Save the state back to K8s @@ -249,13 +234,6 @@ func (c *Cluster) MergeZarfState(oldState *types.ZarfState, initOptions types.Za if slices.Contains(services, message.RegistryKey) { newState.RegistryInfo = helpers.MergeNonZero(newState.RegistryInfo, initOptions.RegistryInfo) - // Set the state of the internal registry if it has changed - if newState.RegistryInfo.Address == fmt.Sprintf("%s:%d", helpers.IPV4Localhost, newState.RegistryInfo.NodePort) { - newState.RegistryInfo.InternalRegistry = true - } else { - newState.RegistryInfo.InternalRegistry = false - } - // Set the new passwords if they should be autogenerated if newState.RegistryInfo.PushPassword == oldState.RegistryInfo.PushPassword && oldState.RegistryInfo.InternalRegistry { newState.RegistryInfo.PushPassword = utils.RandomString(config.ZarfGeneratedPasswordLen) @@ -310,12 +288,6 @@ func (c *Cluster) fillInEmptyContainerRegistryValues(containerRegistry types.Reg containerRegistry.NodePort = config.ZarfInClusterContainerRegistryNodePort } - // Set default url if an external registry was not provided - if containerRegistry.Address == "" { - containerRegistry.InternalRegistry = true - containerRegistry.Address = fmt.Sprintf("%s:%d", helpers.IPV4Localhost, containerRegistry.NodePort) - } - // Generate a push-user password if not provided by init flag if containerRegistry.PushPassword == "" { containerRegistry.PushPassword = utils.RandomString(config.ZarfGeneratedPasswordLen) diff --git a/src/internal/packager/template/template.go b/src/internal/packager/template/template.go index 7239947e8b..a69bcb8aba 100644 --- a/src/internal/packager/template/template.go +++ b/src/internal/packager/template/template.go @@ -85,12 +85,18 @@ func (values *Values) GetVariables(component types.ZarfComponent) (templateMap m regInfo := values.config.State.RegistryInfo gitInfo := values.config.State.GitServer - builtinMap := map[string]string{ - "STORAGE_CLASS": values.config.State.StorageClass, + if values.config.SetVariableMap != nil { + if regInfo.NodePort > 0 && values.config.SetVariableMap["REGISTRY_NODEPORT"] != nil { + values.config.SetVariableMap["REGISTRY_NODEPORT"].Value = fmt.Sprintf("%d", regInfo.NodePort) + } + if values.config.State.StorageClass != "" && values.config.SetVariableMap["REGISTRY_STORAGE_CLASS"] != nil { + values.config.SetVariableMap["REGISTRY_STORAGE_CLASS"].Value = values.config.State.StorageClass + } + } + builtinMap := map[string]string{ // Registry info "REGISTRY": values.registry, - "NODEPORT": fmt.Sprintf("%d", regInfo.NodePort), "REGISTRY_AUTH_PUSH": regInfo.PushPassword, "REGISTRY_AUTH_PULL": regInfo.PullPassword, diff --git a/src/pkg/k8s/info.go b/src/pkg/k8s/info.go index 61effabdaa..13d2074ca2 100644 --- a/src/pkg/k8s/info.go +++ b/src/pkg/k8s/info.go @@ -5,11 +5,14 @@ package k8s import ( + "context" "errors" "fmt" "regexp" "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // List of supported distros via distro detection. @@ -158,3 +161,19 @@ func MakeLabels(labels map[string]string) string { } return strings.Join(out, ",") } + +// GetDefaultStorageClass will query the cluster for the default storage class and return the name. +func (k *K8s) GetDefaultStorageClass() (string, error) { + storageV1Client := k.Clientset.StorageV1() + storageClasses, err := storageV1Client.StorageClasses().List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return "", fmt.Errorf("unable to get Kubernetes default storage class from the cluster : %w", err) + } + + for _, sc := range storageClasses.Items { + if sc.Annotations["storageclass.kubernetes.io/is-default-class"] == "true" { + return sc.Name, nil + } + } + return "", fmt.Errorf("unable to get Kubernetes default storage class from the cluster (no default storage class found)") +} diff --git a/src/pkg/packager/deploy.go b/src/pkg/packager/deploy.go index 7f8f8d30cb..e651c153c9 100644 --- a/src/pkg/packager/deploy.go +++ b/src/pkg/packager/deploy.go @@ -189,7 +189,6 @@ func (p *Packager) deployComponents() (deployedComponents []types.DeployedCompon } func (p *Packager) deployInitComponent(component types.ZarfComponent) (charts []types.InstalledChart, err error) { - hasExternalRegistry := p.cfg.InitOpts.RegistryInfo.Address != "" isSeedRegistry := component.Name == "zarf-seed-registry" isRegistry := component.Name == "zarf-registry" isInjector := component.Name == "zarf-injector" @@ -203,7 +202,7 @@ func (p *Packager) deployInitComponent(component types.ZarfComponent) (charts [] } } - if hasExternalRegistry && (isSeedRegistry || isInjector || isRegistry) { + if !p.cfg.InitOpts.RegistryInfo.InternalRegistry && (isSeedRegistry || isInjector || isRegistry) { message.Notef("Not deploying the component (%s) since external registry information was provided during `zarf init`", component.Name) return charts, nil } @@ -216,6 +215,18 @@ func (p *Packager) deployInitComponent(component types.ZarfComponent) (charts [] // Before deploying the seed registry, start the injector if isSeedRegistry { p.cluster.StartInjectionMadness(p.layout.Base, p.layout.Images.Base, component.Images) + + // Retrieve the default storage class from the cluster, it will be the default + if p.cfg.PkgOpts.SetVariables == nil { + p.cfg.PkgOpts.SetVariables = map[string]string{} + } + if c := p.cfg.PkgOpts.SetVariables["REGISTRY_STORAGE_CLASS"]; c == "" { + p.cfg.PkgOpts.SetVariables["REGISTRY_STORAGE_CLASS"], err = p.cluster.GetDefaultStorageClass() + } + + if err != nil { + message.WarnErr(err, "Unable to get the default storage class from the cluster") + } } charts, err = p.deployComponent(component, isAgent /* skip img checksum if isAgent */, isSeedRegistry /* skip image push if isSeedRegistry */) diff --git a/src/test/e2e/20_zarf_init_test.go b/src/test/e2e/20_zarf_init_test.go index 29213315aa..df4c555182 100644 --- a/src/test/e2e/20_zarf_init_test.go +++ b/src/test/e2e/20_zarf_init_test.go @@ -69,7 +69,7 @@ func TestZarfInit(t *testing.T) { } // run `zarf init` - _, initStdErr, err := e2e.Zarf("init", "--components="+initComponents, "--nodeport", "31337", "-l", "trace", "--confirm") + _, initStdErr, err := e2e.Zarf("init", "--components="+initComponents, "--set", "REGISTRY_NODEPORT=31337", "-l", "trace", "--confirm") require.NoError(t, err) require.Contains(t, initStdErr, "an inventory of all software contained in this package") diff --git a/src/test/e2e/30_config_file_test.go b/src/test/e2e/30_config_file_test.go index a03844fa5a..d92298b14d 100644 --- a/src/test/e2e/30_config_file_test.go +++ b/src/test/e2e/30_config_file_test.go @@ -107,13 +107,11 @@ func configFileDefaultTests(t *testing.T) { initFlags := []string{ "components: 359049b9", - "storage_class: 9cae917f", "git.pull_password: 8522ccca", "git.pull_username: 36646dbe", "git.push_password: ba00d92d", "git.push_username: eb76dca8", "git.url: 7c63c1b9", - "Between [30000-32767] (default 186282)", "registry.pull_password: b8152e38", "registry.pull_username: d0961a97", "registry.push_password: 8f58ca41", diff --git a/src/test/external/gitea-values.yaml b/src/test/external/gitea-values.yaml index 08f0393688..9dbc338a84 100644 --- a/src/test/external/gitea-values.yaml +++ b/src/test/external/gitea-values.yaml @@ -1,5 +1,5 @@ persistence: - storageClass: "local-path" # 'local-path' is for k3d, 'standard' is for kind + storageClass: "" gitea: admin: username: "git-user" diff --git a/src/test/zarf-config-test.toml b/src/test/zarf-config-test.toml index 3585a45f3f..c7e4df1770 100644 --- a/src/test/zarf-config-test.toml +++ b/src/test/zarf-config-test.toml @@ -8,7 +8,6 @@ insecure = true [init] components = 'components: 359049b9' -storage_class = 'storage_class: 9cae917f' [init.git] pull_password = 'git.pull_password: 8522ccca' diff --git a/src/types/runtime.go b/src/types/runtime.go index b9546c1658..a70d7b9d2b 100644 --- a/src/types/runtime.go +++ b/src/types/runtime.go @@ -81,7 +81,8 @@ type ZarfInitOptions struct { RegistryInfo RegistryInfo `json:"registryInfo" jsonschema:"description=Information about the container registry Zarf is going to be using"` ArtifactServer ArtifactServerInfo `json:"artifactServer" jsonschema:"description=Information about the artifact registry Zarf is going to be using"` - StorageClass string `json:"storageClass" jsonschema:"description=StorageClass of the k8s cluster Zarf is initializing"` + // removed + //StorageClass string `json:"storageClass" jsonschema:"description=StorageClass of the k8s cluster Zarf is initializing"` } // ZarfCreateOptions tracks the user-defined options used to create the package.