diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e42c1aa247..19f4ed328b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -102,7 +102,6 @@ jobs: - name: Build examples and run tests run: | sudo env "PATH=$PATH" CI=true APPLIANCE_MODE=true make test-e2e ARCH=amd64 - sudo chown $USER /tmp/zarf-*.log - name: Save logs if: always() diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 5220f18698..f54411c534 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -96,11 +96,9 @@ jobs: # NOTE: "PATH=$PATH" preserves the default user $PATH. This is needed to maintain the version of go installed # in a previous step. This test run will use Zarf to create a K3s cluster, and a brand new cluster will be # used for each test - # chown the logs since they were originally created as root run: | chmod +x build/zarf sudo env "PATH=$PATH" CI=true APPLIANCE_MODE=true make test-e2e ARCH=amd64 - sudo chown $USER /tmp/zarf-*.log - name: Save logs if: always() diff --git a/.github/workflows/test-upgrade.yml b/.github/workflows/test-upgrade.yml index d4319fc2e9..78b47e5bd6 100644 --- a/.github/workflows/test-upgrade.yml +++ b/.github/workflows/test-upgrade.yml @@ -73,9 +73,8 @@ jobs: - name: Initialize the cluster with the release version # NOTE: "PATH=$PATH" preserves the default user $PATH. This is needed to maintain the version of zarf installed # in a previous step. This test run will the current release to create a K3s cluster. - # chown the logs since they were originally created as root run: | - sudo env "PATH=$PATH" CI=true zarf init --components k3s,git-server,logging --confirm + sudo env "PATH=$PATH" CI=true zarf init --components k3s,git-server,logging --nodeport 31337 --confirm # Before we run the regular tests we need to aggressively cleanup files to reduce disk pressure - name: Cleanup files @@ -84,7 +83,6 @@ jobs: - name: Create and deploy the upgrade test packages # NOTE: "PATH=$PATH" preserves the default user $PATH. This is needed to maintain the version of zarf installed # in a previous step. This test run will the current release to create a K3s cluster. - # chown the logs since they were originally created as root run: | zarf package create src/test/upgrade --set PODINFO_VERSION=6.3.3 --confirm sudo env "PATH=$PATH" CI=true zarf package deploy zarf-package-test-upgrade-package-amd64-6.3.3.tar.zst --confirm @@ -94,7 +92,6 @@ jobs: - name: Run tests # NOTE: "PATH=$PATH" preserves the default user $PATH. This is needed to maintain the version of go installed # in a previous step. This test run will use this PR's Zarf to create a K3s cluster. - # chown the logs since they were originally created as root run: | sudo env "PATH=$PATH" CI=true APPLIANCE_MODE=true APPLIANCE_MODE_KEEP=true make test-e2e ARCH=amd64 @@ -112,7 +109,6 @@ jobs: - name: Run the upgrade tests # NOTE: "PATH=$PATH" preserves the default user $PATH. This is needed to maintain the version of zarf installed # in a previous step. This test run will the current release to create a K3s cluster. - # chown the logs since they were originally created as root run: | sudo env "PATH=$PATH" CI=true zarf tools kubectl describe nodes diff --git a/Makefile b/Makefile index 128daee40b..d82bf23151 100644 --- a/Makefile +++ b/Makefile @@ -170,7 +170,6 @@ test-external: ## Run the Zarf CLI E2E tests for an external registry and cluste @test -s $(ZARF_BIN) || $(MAKE) build-cli @test -s ./build/zarf-init-$(ARCH)-$(CLI_VERSION).tar.zst || $(MAKE) init-package @test -s ./build/zarf-package-podinfo-flux-$(ARCH).tar.zst || $(ZARF_BIN) package create examples/podinfo-flux -o build -a $(ARCH) --confirm - @test -s ./build/zarf-package-argocd-$(ARCH).tar.zst || $(ZARF_BIN) package create examples/argocd -o build -a $(ARCH) --confirm cd src/test/external && go test -failfast -v -timeout 30m ## NOTE: Requires an existing cluster and 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 8f0f94ba61..3aabfdac50 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 @@ -40,6 +40,11 @@ zarf init [flags] # Initializing w/ an external git server: zarf init --git-push-password={PASSWORD} --git-push-username={USERNAME} --git-url={URL} + # Initializing w/ an external artifact server: + zarf init --artifact-push-password={PASSWORD} --artifact-push-username={USERNAME} --artifact-url={URL} + + # NOTE: Not specifying a pull username/password will use the push user for pulling as well. + ``` ## Options diff --git a/docs/2-the-zarf-cli/100-cli-commands/zarf_package_deploy.md b/docs/2-the-zarf-cli/100-cli-commands/zarf_package_deploy.md index a2baee7d41..43f643b258 100644 --- a/docs/2-the-zarf-cli/100-cli-commands/zarf_package_deploy.md +++ b/docs/2-the-zarf-cli/100-cli-commands/zarf_package_deploy.md @@ -21,7 +21,6 @@ zarf package deploy [ PACKAGE ] [flags] -h, --help help for deploy -k, --key string Path to public key file for validating signed packages --set stringToString Specify deployment variables to set on the command line (KEY=value) (default []) - --sget string [Deprecated] Path to public sget key file for remote packages signed via cosign. This flag will be removed in v0.31.0 please use the --key flag instead. --shasum string Shasum of the package to deploy. Required if deploying a remote package and "--insecure" is not provided ``` diff --git a/docs/2-the-zarf-cli/100-cli-commands/zarf_tools.md b/docs/2-the-zarf-cli/100-cli-commands/zarf_tools.md index 921517a39f..0b5caa210a 100644 --- a/docs/2-the-zarf-cli/100-cli-commands/zarf_tools.md +++ b/docs/2-the-zarf-cli/100-cli-commands/zarf_tools.md @@ -30,9 +30,10 @@ Collection of additional tools to make airgap easier * [zarf tools download-init](zarf_tools_download-init.md) - Downloads the init package for the current Zarf version into the specified directory * [zarf tools gen-key](zarf_tools_gen-key.md) - Generates a cosign public/private keypair that can be used to sign packages * [zarf tools gen-pki](zarf_tools_gen-pki.md) - Generates a Certificate Authority and PKI chain of trust for the given host -* [zarf tools get-creds](zarf_tools_get-creds.md) - Displays a Table of credentials for deployed components. Pass a component name to get a single credential +* [zarf tools get-creds](zarf_tools_get-creds.md) - Displays a table of credentials for deployed Zarf services. Pass a service key to get a single credential * [zarf tools kubectl](zarf_tools_kubectl.md) - Kubectl command. See https://kubernetes.io/docs/reference/kubectl/overview/ for more information. * [zarf tools monitor](zarf_tools_monitor.md) - Launches a terminal UI to monitor the connected cluster using K9s. * [zarf tools registry](zarf_tools_registry.md) - Tools for working with container registries using go-containertools * [zarf tools sbom](zarf_tools_sbom.md) - Generates a Software Bill of Materials (SBOM) for the given package +* [zarf tools update-creds](zarf_tools_update-creds.md) - Updates the credentials for deployed Zarf services. Pass a service key to update credentials for a single service * [zarf tools wait-for](zarf_tools_wait-for.md) - Waits for a given Kubernetes resource to be ready diff --git a/docs/2-the-zarf-cli/100-cli-commands/zarf_tools_get-creds.md b/docs/2-the-zarf-cli/100-cli-commands/zarf_tools_get-creds.md index 71b547ebeb..4cc33b76b3 100644 --- a/docs/2-the-zarf-cli/100-cli-commands/zarf_tools_get-creds.md +++ b/docs/2-the-zarf-cli/100-cli-commands/zarf_tools_get-creds.md @@ -1,16 +1,33 @@ # zarf tools get-creds -Displays a Table of credentials for deployed components. Pass a component name to get a single credential +Displays a table of credentials for deployed Zarf services. Pass a service key to get a single credential ## Synopsis -Display a Table of credentials for deployed components. Pass a component name to get a single credential. i.e. 'zarf tools get-creds registry' +Display a table of credentials for deployed Zarf services. Pass a service key to get a single credential. i.e. 'zarf tools get-creds registry' ``` zarf tools get-creds [flags] ``` +## Examples + +``` + + # Print all Zarf credentials: + zarf tools get-creds + + # Get specific Zarf credentials: + zarf tools get-creds registry + zarf tools get-creds registry-readonly + zarf tools get-creds git + zarf tools get-creds git-readonly + zarf tools get-creds artifact + zarf tools get-creds logging + +``` + ## Options ``` diff --git a/docs/2-the-zarf-cli/100-cli-commands/zarf_tools_update-creds.md b/docs/2-the-zarf-cli/100-cli-commands/zarf_tools_update-creds.md new file mode 100644 index 0000000000..4176ec237b --- /dev/null +++ b/docs/2-the-zarf-cli/100-cli-commands/zarf_tools_update-creds.md @@ -0,0 +1,80 @@ +# zarf tools update-creds + + +Updates the credentials for deployed Zarf services. Pass a service key to update credentials for a single service + +## Synopsis + +Updates the credentials for deployed Zarf services. Pass a service key to update credentials for a single service. i.e. 'zarf tools update-creds registry' + +``` +zarf tools update-creds [flags] +``` + +## Examples + +``` + + # Autogenerate all Zarf credentials at once: + zarf tools update-creds + + # Autogenerate specific Zarf service credentials: + zarf tools update-creds registry + zarf tools update-creds git + zarf tools update-creds artifact + zarf tools update-creds logging + + # Update all Zarf credentials w/external services at once: + zarf tools update-creds \ + --registry-push-username={USERNAME} --registry-push-password={PASSWORD} \ + --git-push-username={USERNAME} --git-push-password={PASSWORD} \ + --artifact-push-username={USERNAME} --artifact-push-token={PASSWORD} + + # NOTE: Any credentials omitted from flags without a service key specified will be autogenerated - URLs will only change if specified. + # Config options can also be set with the 'init' section of a Zarf config file. + + # Update specific Zarf credentials w/external services: + zarf tools update-creds registry --registry-push-username={USERNAME} --registry-push-password={PASSWORD} + zarf tools update-creds git --git-push-username={USERNAME} --git-push-password={PASSWORD} + zarf tools update-creds artifact --artifact-push-username={USERNAME} --artifact-push-token={PASSWORD} + + # NOTE: Not specifying a pull username/password will keep the previous pull username/password. + +``` + +## Options + +``` + --artifact-push-token string [alpha] API Token for the push-user to access the artifact registry + --artifact-push-username string [alpha] Username to access to the artifact registry Zarf is configured to use. User must be able to upload package artifacts. + --artifact-url string [alpha] External artifact registry url to use for this Zarf cluster + --confirm Confirm updating credentials without prompting + --git-pull-password string Password for the pull-only user to access the git server + --git-pull-username string Username for pull-only access to the git server + --git-push-password string Password for the push-user to access the git server + --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' + --git-url string External git server url to use for this Zarf cluster + -h, --help help for update-creds + --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 + --registry-push-username string Username to access to the registry Zarf is configured to use + --registry-url string External registry url address to use for this Zarf cluster +``` + +## Options inherited from parent commands + +``` + -a, --architecture string Architecture for OCI images and Zarf packages + --insecure Allow access to insecure registries and disable other recommended security enforcements such as package checksum and signature validation. This flag should only be used if you have a specific reason and accept the reduced security posture. + -l, --log-level string Log level when running Zarf. Valid options are: warn, info, debug, trace (default "info") + --no-color Disable colors in output + --no-log-file Disable log file creation + --no-progress Disable fancy UI progress bars, spinners, logos, etc + --tmpdir string Specify the temporary directory to use for intermediate files + --zarf-cache string Specify the location of the Zarf cache directory (default "~/.zarf-cache") +``` + +## SEE ALSO + +* [zarf tools](zarf_tools.md) - Collection of additional tools to make airgap easier diff --git a/docs/2-the-zarf-cli/100-cli-commands/zarf_tools_wait-for.md b/docs/2-the-zarf-cli/100-cli-commands/zarf_tools_wait-for.md index f36a81ee21..717a0a9441 100644 --- a/docs/2-the-zarf-cli/100-cli-commands/zarf_tools_wait-for.md +++ b/docs/2-the-zarf-cli/100-cli-commands/zarf_tools_wait-for.md @@ -19,22 +19,22 @@ zarf tools wait-for { KIND | PROTOCOL } { NAME | SELECTOR | URI } { CONDITION | ``` - Wait for Kubernetes resources: - zarf tools wait-for pod my-pod-name ready -n default # wait for pod my-pod-name in namespace default to be ready - zarf tools wait-for p cool-pod-name ready -n cool # wait for pod (using p alias) cool-pod-name in namespace cool to be ready - zarf tools wait-for deployment podinfo available -n podinfo # wait for deployment podinfo in namespace podinfo to be available - zarf tools wait-for pod app=podinfo ready -n podinfo # wait for pod with label app=podinfo in namespace podinfo to be ready - zarf tools wait-for svc zarf-docker-registry exists -n zarf # wait for service zarf-docker-registry in namespace zarf to exist - zarf tools wait-for svc zarf-docker-registry -n zarf # same as above, except exists is the default condition - zarf tools wait-for crd addons.k3s.cattle.io # wait for crd addons.k3s.cattle.io to exist - zarf tools wait-for sts test-sts '{.status.availableReplicas}'=23 # wait for statefulset test-sts to have 23 available replicas - - Wait for network endpoints: - zarf tools wait-for http localhost:8080 200 # wait for a 200 response from http://localhost:8080 - zarf tools wait-for tcp localhost:8080 # wait for a connection to be established on localhost:8080 - zarf tools wait-for https 1.1.1.1 200 # wait for a 200 response from https://1.1.1.1 - zarf tools wait-for http google.com # wait for any 2xx response from http://google.com - zarf tools wait-for http google.com success # wait for any 2xx response from http://google.com + # Wait for Kubernetes resources: + zarf tools wait-for pod my-pod-name ready -n default # wait for pod my-pod-name in namespace default to be ready + zarf tools wait-for p cool-pod-name ready -n cool # wait for pod (using p alias) cool-pod-name in namespace cool to be ready + zarf tools wait-for deployment podinfo available -n podinfo # wait for deployment podinfo in namespace podinfo to be available + zarf tools wait-for pod app=podinfo ready -n podinfo # wait for pod with label app=podinfo in namespace podinfo to be ready + zarf tools wait-for svc zarf-docker-registry exists -n zarf # wait for service zarf-docker-registry in namespace zarf to exist + zarf tools wait-for svc zarf-docker-registry -n zarf # same as above, except exists is the default condition + zarf tools wait-for crd addons.k3s.cattle.io # wait for crd addons.k3s.cattle.io to exist + zarf tools wait-for sts test-sts '{.status.availableReplicas}'=23 # wait for statefulset test-sts to have 23 available replicas + + # Wait for network endpoints: + zarf tools wait-for http localhost:8080 200 # wait for a 200 response from http://localhost:8080 + zarf tools wait-for tcp localhost:8080 # wait for a connection to be established on localhost:8080 + zarf tools wait-for https 1.1.1.1 200 # wait for a 200 response from https://1.1.1.1 + zarf tools wait-for http google.com # wait for any 2xx response from http://google.com + zarf tools wait-for http google.com success # wait for any 2xx response from http://google.com ``` diff --git a/examples/git-data/zarf.yaml b/examples/git-data/zarf.yaml index 939a8aee25..ca61b0d252 100644 --- a/examples/git-data/zarf.yaml +++ b/examples/git-data/zarf.yaml @@ -11,7 +11,7 @@ components: # The following performs a full Git Repo Mirror with `go-git` (internal to Zarf) - https://github.com/defenseunicorns/zarf-public-test.git # The following performs a full Git Repo Mirror forcing a fallback to host `git` - - https://racer159.visualstudio.com/zarf-public-test/_git/zarf-public-test + - https://dev.azure.com/defenseunicorns/zarf-public-test/_git/zarf-public-test - name: specific-tag required: true @@ -21,7 +21,7 @@ components: # The following performs a refspec tag Git Repo Mirror with `go-git` - https://github.com/defenseunicorns/zarf-public-test.git@refs/tags/v0.0.1 # The following performs a tag Git Repo Mirror forcing a fallback to host `git` - - https://racer159.visualstudio.com/zarf-public-test/_git/zarf-public-test@v0.0.1 + - https://dev.azure.com/defenseunicorns/zarf-public-test/_git/zarf-public-test@v0.0.1 - name: specific-branch required: true @@ -29,7 +29,7 @@ components: # The following performs a branch Git Repo Mirror with `go-git` (internal to Zarf) - https://github.com/defenseunicorns/zarf-public-test.git@refs/heads/dragons # The following performs a branch Git Repo Mirror forcing a fallback to host `git` - - https://racer159.visualstudio.com/zarf-public-test/_git/zarf-public-test@refs/heads/dragons + - https://dev.azure.com/defenseunicorns/zarf-public-test/_git/zarf-public-test@refs/heads/dragons - name: specific-hash required: true @@ -37,5 +37,5 @@ components: # The following performs a SHA Git Repo Mirror with `go-git` (internal to Zarf) - https://github.com/defenseunicorns/zarf-public-test.git@01a23218923f24194133b5eb11268cf8d73ff1bb # The following performs a SHA Git Repo Mirror forcing a fallback to host `git` - - https://racer159.visualstudio.com/zarf-public-test/_git/zarf-public-test@01a23218923f24194133b5eb11268cf8d73ff1bb + - https://dev.azure.com/defenseunicorns/zarf-public-test/_git/zarf-public-test@01a23218923f24194133b5eb11268cf8d73ff1bb diff --git a/packages/gitea/gitea-values.yaml b/packages/gitea/gitea-values.yaml index 5d3814b74a..3ac892ba27 100644 --- a/packages/gitea/gitea-values.yaml +++ b/packages/gitea/gitea-values.yaml @@ -2,6 +2,10 @@ persistence: storageClass: "###ZARF_STORAGE_CLASS###" existingClaim: "###ZARF_VAR_GIT_SERVER_EXISTING_PVC###" size: "###ZARF_VAR_GIT_SERVER_PVC_SIZE###" + accessModes: + - "###ZARF_VAR_GIT_SERVER_PVC_ACCESS_MODE###" + +replicaCount: "###ZARF_VAR_GIT_SERVER_REPLICA_COUNT###" gitea: admin: diff --git a/packages/gitea/zarf.yaml b/packages/gitea/zarf.yaml index e750ef3e92..70894dc271 100644 --- a/packages/gitea/zarf.yaml +++ b/packages/gitea/zarf.yaml @@ -8,9 +8,13 @@ variables: default: "" - name: GIT_SERVER_PVC_SIZE - description: The size of the persistent volume claim for git server + description: The size of the persistent volume claim for the git server default: 10Gi + - name: GIT_SERVER_PVC_ACCESS_MODE + description: The access mode of the persistent volume claim for the git server + default: ReadWriteOnce + - name: GIT_SERVER_CPU_REQ description: The CPU request for git server default: 200m @@ -27,6 +31,10 @@ variables: description: The memory limit for git server default: 2Gi + - name: GIT_SERVER_REPLICA_COUNT + description: The number of git server replicas to deploy + default: "1" + components: - name: git-server description: | diff --git a/packages/zarf-registry/chart/templates/deployment.yaml b/packages/zarf-registry/chart/templates/deployment.yaml index 33fa922e51..d91faeeeaf 100644 --- a/packages/zarf-registry/chart/templates/deployment.yaml +++ b/packages/zarf-registry/chart/templates/deployment.yaml @@ -73,7 +73,11 @@ spec: - name: config mountPath: "/etc/docker/registry" affinity: +{{- if (eq "ReadWriteMany" .Values.persistence.accessMode) }} + podAntiAffinity: +{{- else }} podAffinity: +{{- end }} preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: diff --git a/src/cmd/common/setup.go b/src/cmd/common/setup.go new file mode 100644 index 0000000000..8bde89e640 --- /dev/null +++ b/src/cmd/common/setup.go @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +// Package common handles command configuration across all commands +package common + +import ( + "os" + + "github.com/defenseunicorns/zarf/src/config" + "github.com/defenseunicorns/zarf/src/config/lang" + "github.com/defenseunicorns/zarf/src/pkg/message" + "github.com/defenseunicorns/zarf/src/pkg/utils/exec" +) + +// LogLevelCLI holds the log level as input from a command +var LogLevelCLI string + +// SetupCLI sets up the CLI logging, interrupt functions, and more +func SetupCLI() { + exec.ExitOnInterrupt() + + match := map[string]message.LogLevel{ + "warn": message.WarnLevel, + "info": message.InfoLevel, + "debug": message.DebugLevel, + "trace": message.TraceLevel, + } + + if config.NoColor { + message.DisableColor() + } + + printViperConfigUsed() + + // No log level set, so use the default + if LogLevelCLI != "" { + if lvl, ok := match[LogLevelCLI]; ok { + message.SetLogLevel(lvl) + message.Debug("Log level set to " + LogLevelCLI) + } else { + message.Warn(lang.RootCmdErrInvalidLogLevel) + } + } + + // Disable progress bars for CI envs + if os.Getenv("CI") == "true" { + message.Debug("CI environment detected, disabling progress bars") + message.NoProgress = true + } + + if !config.SkipLogFile { + message.UseLogFile() + } +} diff --git a/src/cmd/common/vendor.go b/src/cmd/common/vendor.go new file mode 100644 index 0000000000..53cb1cca5c --- /dev/null +++ b/src/cmd/common/vendor.go @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +// Package common handles command configuration across all commands +package common + +import ( + "os" + "strings" + + "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" + "github.com/spf13/cobra" +) + +var vendorCmds = []string{ + "kubectl", + "k", + "syft", + "sbom", + "s", + "k9s", + "monitor", + "wait-for", + "wait", + "w", + "crane", + "registry", + "r", +} + +// CheckVendorOnlyFromArgs checks if the command being run is a vendor-only command +func CheckVendorOnlyFromArgs() bool { + // Check for "zarf tools|t " where is in the vendorCmd list + return IsVendorCmd(os.Args, vendorCmds) +} + +// CheckVendorOnlyFromPath checks if the cobra command is a vendor-only command +func CheckVendorOnlyFromPath(cmd *cobra.Command) bool { + args := strings.Split(cmd.CommandPath(), " ") + // Check for "zarf tools|t " where is in the vendorCmd list + return IsVendorCmd(args, vendorCmds) +} + +// IsVendorCmd checks if the command is a vendor command. +func IsVendorCmd(args []string, vendoredCmds []string) bool { + if len(args) > 2 { + if args[1] == "tools" || args[1] == "t" { + if helpers.SliceContains(vendoredCmds, args[2]) { + return true + } + } + } + + return false +} diff --git a/src/cmd/common/viper.go b/src/cmd/common/viper.go new file mode 100644 index 0000000000..7ab6b890fc --- /dev/null +++ b/src/cmd/common/viper.go @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +// Package common handles command configuration across all commands +package common + +import ( + "os" + "strings" + + "github.com/defenseunicorns/zarf/src/config/lang" + "github.com/defenseunicorns/zarf/src/pkg/message" + "github.com/spf13/viper" +) + +// Constants for use when loading configurations from viper config files +const ( + + // Root config keys + + VLogLevel = "log_level" + VArchitecture = "architecture" + VNoLogFile = "no_log_file" + VNoProgress = "no_progress" + VNoColor = "no_color" + VZarfCache = "zarf_cache" + VTmpDir = "tmp_dir" + VInsecure = "insecure" + + // Init config keys + + VInitComponents = "init.components" + VInitStorageClass = "init.storage_class" + + // Init Git config keys + + VInitGitURL = "init.git.url" + VInitGitPushUser = "init.git.push_username" + VInitGitPushPass = "init.git.push_password" + VInitGitPullUser = "init.git.pull_username" + VInitGitPullPass = "init.git.pull_password" + + // Init Registry config keys + + VInitRegistryURL = "init.registry.url" + VInitRegistryNodeport = "init.registry.nodeport" + VInitRegistrySecret = "init.registry.secret" + VInitRegistryPushUser = "init.registry.push_username" + VInitRegistryPushPass = "init.registry.push_password" + VInitRegistryPullUser = "init.registry.pull_username" + VInitRegistryPullPass = "init.registry.pull_password" + + // Init Package config keys + + VInitArtifactURL = "init.artifact.url" + VInitArtifactPushUser = "init.artifact.push_username" + VInitArtifactPushToken = "init.artifact.push_token" + + // Package config keys + + VPkgOCIConcurrency = "package.oci_concurrency" + + // Package create config keys + + VPkgCreateSet = "package.create.set" + VPkgCreateOutput = "package.create.output" + VPkgCreateSbom = "package.create.sbom" + VPkgCreateSbomOutput = "package.create.sbom_output" + VPkgCreateSkipSbom = "package.create.skip_sbom" + VPkgCreateMaxPackageSize = "package.create.max_package_size" + VPkgCreateSigningKey = "package.create.signing_key" + VPkgCreateSigningKeyPassword = "package.create.signing_key_password" + VPkgCreateDifferential = "package.create.differential" + VPkgCreateRegistryOverride = "package.create.registry_override" + + // Package deploy config keys + + VPkgDeploySet = "package.deploy.set" + VPkgDeployComponents = "package.deploy.components" + VPkgDeployShasum = "package.deploy.shasum" + VPkgDeploySget = "package.deploy.sget" + VPkgDeployPublicKey = "package.deploy.public_key" + + // Package publish config keys + + VPkgPublishSigningKey = "package.publish.signing_key" + VPkgPublishSigningKeyPassword = "package.publish.signing_key_password" + + // Package pull config keys + + VPkgPullOutputDir = "package.pull.output_directory" + VPkgPullPublicKey = "package.pull.public_key" +) + +var ( + // Viper instance used by commands + v *viper.Viper + + // Viper configuration error + vConfigError error +) + +// InitViper initializes the viper singleton for the CLI +func InitViper() *viper.Viper { + // Already initialized by some other command + if v != nil { + return v + } + + v = viper.New() + + // Skip for vendor-only commands or the version command + if CheckVendorOnlyFromArgs() || isVersionCmd() { + return v + } + + // Specify an alternate config file + cfgFile := os.Getenv("ZARF_CONFIG") + + // Don't forget to read config either from cfgFile or from home directory! + if cfgFile != "" { + // Use config file from the flag. + v.SetConfigFile(cfgFile) + } else { + // Search config paths in the current directory and $HOME/.zarf. + v.AddConfigPath(".") + v.AddConfigPath("$HOME/.zarf") + v.SetConfigName("zarf-config") + } + + // E.g. ZARF_LOG_LEVEL=debug + v.SetEnvPrefix("zarf") + v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + v.AutomaticEnv() + + // Optional, so ignore errors + vConfigError = v.ReadInConfig() + + return v +} + +// GetViper returns the viper singleton +func GetViper() *viper.Viper { + return v +} + +func isVersionCmd() bool { + args := os.Args + return len(args) > 1 && (args[1] == "version" || args[1] == "v") +} + +func printViperConfigUsed() { + // Only print config info if viper is initialized. + vInitialized := v != nil + if !vInitialized { + return + } + + // Optional, so ignore file not found errors + if vConfigError != nil { + // Config file not found; ignore + if _, ok := vConfigError.(viper.ConfigFileNotFoundError); !ok { + message.WarnErrorf(vConfigError, lang.CmdViperErrLoadingConfigFile, vConfigError.Error()) + } + } else { + message.Notef(lang.CmdViperInfoUsingConfigFile, v.ConfigFileUsed()) + } +} diff --git a/src/cmd/initialize.go b/src/cmd/initialize.go index cc0593878b..27b0e202ec 100644 --- a/src/cmd/initialize.go +++ b/src/cmd/initialize.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/AlecAivazis/survey/v2" + "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/message" @@ -51,8 +52,9 @@ var initCmd = &cobra.Command{ pkgConfig.PkgSource = pkgConfig.PkgOpts.PackagePath // Ensure uppercase keys from viper - viperConfig := helpers.TransformMapKeys(v.GetStringMapString(V_PKG_DEPLOY_SET), strings.ToUpper) - pkgConfig.PkgOpts.SetVariables = helpers.MergeMap(viperConfig, pkgConfig.PkgOpts.SetVariables) + v := common.GetViper() + pkgConfig.PkgOpts.SetVariables = helpers.TransformAndMergeMap( + v.GetStringMapString(common.VPkgDeploySet), pkgConfig.PkgOpts.SetVariables, strings.ToUpper) // Configure the packager pkgClient := packager.NewOrDie(&pkgConfig) @@ -125,7 +127,7 @@ func downloadInitPackage(downloadCacheTarget string) error { Message: lang.CmdInitDownloadConfirm, } if err := survey.AskOne(prompt, &confirmDownload); err != nil { - return fmt.Errorf(lang.CmdInitDownloadErrCancel, err.Error()) + return fmt.Errorf(lang.ErrConfirmCancel, err.Error()) } } @@ -162,58 +164,42 @@ func validateInitFlags() error { } func init() { - initViper() + v := common.InitViper() rootCmd.AddCommand(initCmd) - // Init package variables - v.SetDefault(V_PKG_DEPLOY_SET, map[string]string{}) - - v.SetDefault(V_INIT_COMPONENTS, "") - v.SetDefault(V_INIT_STORAGE_CLASS, "") - - v.SetDefault(V_INIT_GIT_URL, "") - v.SetDefault(V_INIT_GIT_PUSH_USER, config.ZarfGitPushUser) - v.SetDefault(V_INIT_GIT_PUSH_PASS, "") - v.SetDefault(V_INIT_GIT_PULL_USER, "") - v.SetDefault(V_INIT_GIT_PULL_PASS, "") - - v.SetDefault(V_INIT_REGISTRY_URL, "") - v.SetDefault(V_INIT_REGISTRY_NODEPORT, 0) - v.SetDefault(V_INIT_REGISTRY_SECRET, "") - v.SetDefault(V_INIT_REGISTRY_PUSH_USER, config.ZarfRegistryPushUser) - v.SetDefault(V_INIT_REGISTRY_PUSH_PASS, "") - v.SetDefault(V_INIT_REGISTRY_PULL_USER, "") - v.SetDefault(V_INIT_REGISTRY_PULL_PASS, "") + // Init package variable defaults that are non-zero values + v.SetDefault(common.VInitGitPushUser, config.ZarfGitPushUser) + v.SetDefault(common.VInitRegistryPushUser, config.ZarfRegistryPushUser) // Init package set variable flags - initCmd.Flags().StringToStringVar(&pkgConfig.PkgOpts.SetVariables, "set", v.GetStringMapString(V_PKG_DEPLOY_SET), lang.CmdInitFlagSet) + initCmd.Flags().StringToStringVar(&pkgConfig.PkgOpts.SetVariables, "set", v.GetStringMapString(common.VPkgDeploySet), lang.CmdInitFlagSet) // 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(V_INIT_COMPONENTS), lang.CmdInitFlagComponents) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.StorageClass, "storage-class", v.GetString(V_INIT_STORAGE_CLASS), lang.CmdInitFlagStorageClass) + 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) // Flags for using an external Git server - initCmd.Flags().StringVar(&pkgConfig.InitOpts.GitServer.Address, "git-url", v.GetString(V_INIT_GIT_URL), lang.CmdInitFlagGitURL) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.GitServer.PushUsername, "git-push-username", v.GetString(V_INIT_GIT_PUSH_USER), lang.CmdInitFlagGitPushUser) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.GitServer.PushPassword, "git-push-password", v.GetString(V_INIT_GIT_PUSH_PASS), lang.CmdInitFlagGitPushPass) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.GitServer.PullUsername, "git-pull-username", v.GetString(V_INIT_GIT_PULL_USER), lang.CmdInitFlagGitPullUser) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.GitServer.PullPassword, "git-pull-password", v.GetString(V_INIT_GIT_PULL_PASS), lang.CmdInitFlagGitPullPass) + initCmd.Flags().StringVar(&pkgConfig.InitOpts.GitServer.Address, "git-url", v.GetString(common.VInitGitURL), lang.CmdInitFlagGitURL) + initCmd.Flags().StringVar(&pkgConfig.InitOpts.GitServer.PushUsername, "git-push-username", v.GetString(common.VInitGitPushUser), lang.CmdInitFlagGitPushUser) + initCmd.Flags().StringVar(&pkgConfig.InitOpts.GitServer.PushPassword, "git-push-password", v.GetString(common.VInitGitPushPass), lang.CmdInitFlagGitPushPass) + initCmd.Flags().StringVar(&pkgConfig.InitOpts.GitServer.PullUsername, "git-pull-username", v.GetString(common.VInitGitPullUser), lang.CmdInitFlagGitPullUser) + initCmd.Flags().StringVar(&pkgConfig.InitOpts.GitServer.PullPassword, "git-pull-password", v.GetString(common.VInitGitPullPass), lang.CmdInitFlagGitPullPass) // Flags for using an external registry - initCmd.Flags().StringVar(&pkgConfig.InitOpts.RegistryInfo.Address, "registry-url", v.GetString(V_INIT_REGISTRY_URL), lang.CmdInitFlagRegURL) - initCmd.Flags().IntVar(&pkgConfig.InitOpts.RegistryInfo.NodePort, "nodeport", v.GetInt(V_INIT_REGISTRY_NODEPORT), lang.CmdInitFlagRegNodePort) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.RegistryInfo.PushUsername, "registry-push-username", v.GetString(V_INIT_REGISTRY_PUSH_USER), lang.CmdInitFlagRegPushUser) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.RegistryInfo.PushPassword, "registry-push-password", v.GetString(V_INIT_REGISTRY_PUSH_PASS), lang.CmdInitFlagRegPushPass) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.RegistryInfo.PullUsername, "registry-pull-username", v.GetString(V_INIT_REGISTRY_PULL_USER), lang.CmdInitFlagRegPullUser) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.RegistryInfo.PullPassword, "registry-pull-password", v.GetString(V_INIT_REGISTRY_PULL_PASS), lang.CmdInitFlagRegPullPass) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.RegistryInfo.Secret, "registry-secret", v.GetString(V_INIT_REGISTRY_SECRET), lang.CmdInitFlagRegSecret) + 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) + 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) + initCmd.Flags().StringVar(&pkgConfig.InitOpts.RegistryInfo.PullPassword, "registry-pull-password", v.GetString(common.VInitRegistryPullPass), lang.CmdInitFlagRegPullPass) + initCmd.Flags().StringVar(&pkgConfig.InitOpts.RegistryInfo.Secret, "registry-secret", v.GetString(common.VInitRegistrySecret), lang.CmdInitFlagRegSecret) // Flags for using an external artifact server - initCmd.Flags().StringVar(&pkgConfig.InitOpts.ArtifactServer.Address, "artifact-url", v.GetString(V_INIT_ARTIFACT_URL), lang.CmdInitFlagArtifactURL) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.ArtifactServer.PushUsername, "artifact-push-username", v.GetString(V_INIT_ARTIFACT_PUSH_USER), lang.CmdInitFlagArtifactPushUser) - initCmd.Flags().StringVar(&pkgConfig.InitOpts.ArtifactServer.PushToken, "artifact-push-token", v.GetString(V_INIT_ARTIFACT_PUSH_TOKEN), lang.CmdInitFlagArtifactPushToken) + initCmd.Flags().StringVar(&pkgConfig.InitOpts.ArtifactServer.Address, "artifact-url", v.GetString(common.VInitArtifactURL), lang.CmdInitFlagArtifactURL) + initCmd.Flags().StringVar(&pkgConfig.InitOpts.ArtifactServer.PushUsername, "artifact-push-username", v.GetString(common.VInitArtifactPushUser), lang.CmdInitFlagArtifactPushUser) + initCmd.Flags().StringVar(&pkgConfig.InitOpts.ArtifactServer.PushToken, "artifact-push-token", v.GetString(common.VInitArtifactPushToken), lang.CmdInitFlagArtifactPushToken) initCmd.Flags().SortFlags = true } diff --git a/src/cmd/internal.go b/src/cmd/internal.go index b1a8d73e7d..f93848f001 100644 --- a/src/cmd/internal.go +++ b/src/cmd/internal.go @@ -10,7 +10,7 @@ import ( "os" "github.com/alecthomas/jsonschema" - "github.com/defenseunicorns/zarf/src/cmd/tools" + "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/internal/agent" "github.com/defenseunicorns/zarf/src/internal/api" @@ -60,7 +60,7 @@ var generateCLIDocs = &cobra.Command{ if cmd.Use == "tools" { for _, toolCmd := range cmd.Commands() { // If the command is a vendored command, add a dummy flag to hide root flags from the docs - if tools.CheckVendorOnlyFromPath(toolCmd) { + if common.CheckVendorOnlyFromPath(toolCmd) { addHiddenDummyFlag(toolCmd, "log-level") addHiddenDummyFlag(toolCmd, "architecture") addHiddenDummyFlag(toolCmd, "no-log-file") diff --git a/src/cmd/package.go b/src/cmd/package.go index cb7b5e5ec0..7848ffd1df 100644 --- a/src/cmd/package.go +++ b/src/cmd/package.go @@ -10,6 +10,7 @@ import ( "regexp" "strings" + "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/message" @@ -22,6 +23,7 @@ import ( "github.com/defenseunicorns/zarf/src/pkg/packager" "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var includeInspectSBOM bool @@ -56,8 +58,9 @@ var packageCreateCmd = &cobra.Command{ } // Ensure uppercase keys from viper - viperConfig := helpers.TransformMapKeys(v.GetStringMapString(V_PKG_CREATE_SET), strings.ToUpper) - pkgConfig.CreateOpts.SetVariables = helpers.MergeMap(viperConfig, pkgConfig.CreateOpts.SetVariables) + v := common.GetViper() + pkgConfig.CreateOpts.SetVariables = helpers.TransformAndMergeMap( + v.GetStringMapString(common.VPkgCreateSet), pkgConfig.CreateOpts.SetVariables, strings.ToUpper) // Configure the packager pkgClient := packager.NewOrDie(&pkgConfig) @@ -80,11 +83,11 @@ var packageDeployCmd = &cobra.Command{ pkgConfig.PkgOpts.PackagePath = choosePackage(args) // Ensure uppercase keys from viper and CLI --set - viperConfigSetVariables := helpers.TransformMapKeys(v.GetStringMapString(V_PKG_DEPLOY_SET), strings.ToUpper) - pkgConfig.PkgOpts.SetVariables = helpers.TransformMapKeys(pkgConfig.PkgOpts.SetVariables, strings.ToUpper) + v := common.GetViper() // Merge the viper config file variables and provided CLI flag variables (CLI takes precedence)) - pkgConfig.PkgOpts.SetVariables = helpers.MergeMap(viperConfigSetVariables, pkgConfig.PkgOpts.SetVariables) + pkgConfig.PkgOpts.SetVariables = helpers.TransformAndMergeMap( + v.GetStringMapString(common.VPkgDeploySet), pkgConfig.PkgOpts.SetVariables, strings.ToUpper) pkgConfig.PkgSource = pkgConfig.PkgOpts.PackagePath @@ -260,7 +263,7 @@ func choosePackage(args []string) string { } func init() { - initViper() + v := common.InitViper() rootCmd.AddCommand(packageCmd) packageCmd.AddCommand(packageCreateCmd) @@ -271,57 +274,49 @@ func init() { packageCmd.AddCommand(packagePublishCmd) packageCmd.AddCommand(packagePullCmd) - bindPackageFlags() - bindCreateFlags() - bindDeployFlags() - bindInspectFlags() - bindRemoveFlags() - bindPublishFlags() - bindPullFlags() + bindPackageFlags(v) + bindCreateFlags(v) + bindDeployFlags(v) + bindInspectFlags(v) + bindRemoveFlags(v) + bindPublishFlags(v) + bindPullFlags(v) } -func bindPackageFlags() { +func bindPackageFlags(v *viper.Viper) { packageFlags := packageCmd.PersistentFlags() - v.SetDefault(V_PKG_OCI_CONCURRENCY, 3) - packageFlags.IntVar(&config.CommonOptions.OCIConcurrency, "oci-concurrency", v.GetInt(V_PKG_OCI_CONCURRENCY), lang.CmdPackageFlagConcurrency) + v.SetDefault(common.VPkgOCIConcurrency, 3) + packageFlags.IntVar(&config.CommonOptions.OCIConcurrency, "oci-concurrency", v.GetInt(common.VPkgOCIConcurrency), lang.CmdPackageFlagConcurrency) } -func bindCreateFlags() { +func bindCreateFlags(v *viper.Viper) { createFlags := packageCreateCmd.Flags() // Always require confirm flag (no viper) createFlags.BoolVar(&config.CommonOptions.Confirm, "confirm", false, lang.CmdPackageCreateFlagConfirm) - v.SetDefault(V_PKG_CREATE_SET, map[string]string{}) - v.SetDefault(V_PKG_CREATE_OUTPUT, "") - v.SetDefault(V_PKG_CREATE_SBOM, false) - v.SetDefault(V_PKG_CREATE_SBOM_OUTPUT, "") - v.SetDefault(V_PKG_CREATE_SKIP_SBOM, false) - v.SetDefault(V_PKG_CREATE_MAX_PACKAGE_SIZE, 0) - v.SetDefault(V_PKG_CREATE_SIGNING_KEY, "") - outputDirectory := v.GetString("package.create.output_directory") - output := v.GetString(V_PKG_CREATE_OUTPUT) + output := v.GetString(common.VPkgCreateOutput) if outputDirectory != "" && output == "" { - v.Set(V_PKG_CREATE_OUTPUT, outputDirectory) + v.Set(common.VPkgCreateOutput, outputDirectory) } createFlags.StringVar(&pkgConfig.CreateOpts.Output, "output-directory", v.GetString("package.create.output_directory"), lang.CmdPackageCreateFlagOutput) - createFlags.StringVarP(&pkgConfig.CreateOpts.Output, "output", "o", v.GetString(V_PKG_CREATE_OUTPUT), lang.CmdPackageCreateFlagOutput) - - createFlags.StringVar(&pkgConfig.CreateOpts.DifferentialData.DifferentialPackagePath, "differential", v.GetString(V_PKG_CREATE_DIFFERENTIAL), lang.CmdPackageCreateFlagDifferential) - createFlags.StringToStringVar(&pkgConfig.CreateOpts.SetVariables, "set", v.GetStringMapString(V_PKG_CREATE_SET), lang.CmdPackageCreateFlagSet) - createFlags.BoolVarP(&pkgConfig.CreateOpts.ViewSBOM, "sbom", "s", v.GetBool(V_PKG_CREATE_SBOM), lang.CmdPackageCreateFlagSbom) - createFlags.StringVar(&pkgConfig.CreateOpts.SBOMOutputDir, "sbom-out", v.GetString(V_PKG_CREATE_SBOM_OUTPUT), lang.CmdPackageCreateFlagSbomOut) - createFlags.BoolVar(&pkgConfig.CreateOpts.SkipSBOM, "skip-sbom", v.GetBool(V_PKG_CREATE_SKIP_SBOM), lang.CmdPackageCreateFlagSkipSbom) - createFlags.IntVarP(&pkgConfig.CreateOpts.MaxPackageSizeMB, "max-package-size", "m", v.GetInt(V_PKG_CREATE_MAX_PACKAGE_SIZE), lang.CmdPackageCreateFlagMaxPackageSize) - createFlags.StringVarP(&pkgConfig.CreateOpts.SigningKeyPath, "key", "k", v.GetString(V_PKG_CREATE_SIGNING_KEY), lang.CmdPackageCreateFlagSigningKey) - createFlags.StringVar(&pkgConfig.CreateOpts.SigningKeyPassword, "key-pass", v.GetString(V_PKG_CREATE_SIGNING_KEY_PASSWORD), lang.CmdPackageCreateFlagSigningKeyPassword) - createFlags.StringToStringVar(&pkgConfig.CreateOpts.RegistryOverrides, "registry-override", v.GetStringMapString(V_PKG_CREATE_REGISTRY_OVERRIDE), lang.CmdPackageCreateFlagRegistryOverride) + createFlags.StringVarP(&pkgConfig.CreateOpts.Output, "output", "o", v.GetString(common.VPkgCreateOutput), lang.CmdPackageCreateFlagOutput) + + createFlags.StringVar(&pkgConfig.CreateOpts.DifferentialData.DifferentialPackagePath, "differential", v.GetString(common.VPkgCreateDifferential), lang.CmdPackageCreateFlagDifferential) + createFlags.StringToStringVar(&pkgConfig.CreateOpts.SetVariables, "set", v.GetStringMapString(common.VPkgCreateSet), lang.CmdPackageCreateFlagSet) + createFlags.BoolVarP(&pkgConfig.CreateOpts.ViewSBOM, "sbom", "s", v.GetBool(common.VPkgCreateSbom), lang.CmdPackageCreateFlagSbom) + createFlags.StringVar(&pkgConfig.CreateOpts.SBOMOutputDir, "sbom-out", v.GetString(common.VPkgCreateSbomOutput), lang.CmdPackageCreateFlagSbomOut) + createFlags.BoolVar(&pkgConfig.CreateOpts.SkipSBOM, "skip-sbom", v.GetBool(common.VPkgCreateSkipSbom), lang.CmdPackageCreateFlagSkipSbom) + createFlags.IntVarP(&pkgConfig.CreateOpts.MaxPackageSizeMB, "max-package-size", "m", v.GetInt(common.VPkgCreateMaxPackageSize), lang.CmdPackageCreateFlagMaxPackageSize) + createFlags.StringVarP(&pkgConfig.CreateOpts.SigningKeyPath, "key", "k", v.GetString(common.VPkgCreateSigningKey), lang.CmdPackageCreateFlagSigningKey) + createFlags.StringVar(&pkgConfig.CreateOpts.SigningKeyPassword, "key-pass", v.GetString(common.VPkgCreateSigningKeyPassword), lang.CmdPackageCreateFlagSigningKeyPassword) + createFlags.StringToStringVar(&pkgConfig.CreateOpts.RegistryOverrides, "registry-override", v.GetStringMapString(common.VPkgCreateRegistryOverride), lang.CmdPackageCreateFlagRegistryOverride) createFlags.MarkHidden("output-directory") } -func bindDeployFlags() { +func bindDeployFlags(v *viper.Viper) { deployFlags := packageDeployCmd.Flags() // Always require confirm flag (no viper) @@ -330,42 +325,38 @@ func bindDeployFlags() { // Always require adopt-existing-resources flag (no viper) deployFlags.BoolVar(&pkgConfig.DeployOpts.AdoptExistingResources, "adopt-existing-resources", false, lang.CmdPackageDeployFlagAdoptExistingResources) - v.SetDefault(V_PKG_DEPLOY_SET, map[string]string{}) - v.SetDefault(V_PKG_DEPLOY_COMPONENTS, "") - v.SetDefault(V_PKG_DEPLOY_SHASUM, "") - v.SetDefault(V_PKG_DEPLOY_SGET, "") - v.SetDefault(V_PKG_DEPLOY_PUBLIC_KEY, "") - - deployFlags.StringToStringVar(&pkgConfig.PkgOpts.SetVariables, "set", v.GetStringMapString(V_PKG_DEPLOY_SET), lang.CmdPackageDeployFlagSet) - deployFlags.StringVar(&pkgConfig.PkgOpts.OptionalComponents, "components", v.GetString(V_PKG_DEPLOY_COMPONENTS), lang.CmdPackageDeployFlagComponents) - deployFlags.StringVar(&pkgConfig.PkgOpts.Shasum, "shasum", v.GetString(V_PKG_DEPLOY_SHASUM), lang.CmdPackageDeployFlagShasum) - deployFlags.StringVar(&pkgConfig.PkgOpts.SGetKeyPath, "sget", v.GetString(V_PKG_DEPLOY_SGET), lang.CmdPackageDeployFlagSget) - deployFlags.StringVarP(&pkgConfig.PkgOpts.PublicKeyPath, "key", "k", v.GetString(V_PKG_DEPLOY_PUBLIC_KEY), lang.CmdPackageDeployFlagPublicKey) + deployFlags.StringToStringVar(&pkgConfig.PkgOpts.SetVariables, "set", v.GetStringMapString(common.VPkgDeploySet), lang.CmdPackageDeployFlagSet) + deployFlags.StringVar(&pkgConfig.PkgOpts.OptionalComponents, "components", v.GetString(common.VPkgDeployComponents), lang.CmdPackageDeployFlagComponents) + deployFlags.StringVar(&pkgConfig.PkgOpts.Shasum, "shasum", v.GetString(common.VPkgDeployShasum), lang.CmdPackageDeployFlagShasum) + deployFlags.StringVar(&pkgConfig.PkgOpts.SGetKeyPath, "sget", v.GetString(common.VPkgDeploySget), lang.CmdPackageDeployFlagSget) + deployFlags.StringVarP(&pkgConfig.PkgOpts.PublicKeyPath, "key", "k", v.GetString(common.VPkgDeployPublicKey), lang.CmdPackageDeployFlagPublicKey) + + deployFlags.MarkHidden("sget") } -func bindInspectFlags() { +func bindInspectFlags(v *viper.Viper) { inspectFlags := packageInspectCmd.Flags() inspectFlags.BoolVarP(&includeInspectSBOM, "sbom", "s", false, lang.CmdPackageInspectFlagSbom) inspectFlags.StringVar(&outputInspectSBOM, "sbom-out", "", lang.CmdPackageInspectFlagSbomOut) - inspectFlags.StringVarP(&inspectPublicKey, "key", "k", v.GetString(V_PKG_DEPLOY_PUBLIC_KEY), lang.CmdPackageInspectFlagPublicKey) + inspectFlags.StringVarP(&inspectPublicKey, "key", "k", v.GetString(common.VPkgDeployPublicKey), lang.CmdPackageInspectFlagPublicKey) } -func bindRemoveFlags() { +func bindRemoveFlags(v *viper.Viper) { removeFlags := packageRemoveCmd.Flags() removeFlags.BoolVar(&config.CommonOptions.Confirm, "confirm", false, lang.CmdPackageRemoveFlagConfirm) - removeFlags.StringVar(&pkgConfig.PkgOpts.OptionalComponents, "components", v.GetString(V_PKG_DEPLOY_COMPONENTS), lang.CmdPackageRemoveFlagComponents) + removeFlags.StringVar(&pkgConfig.PkgOpts.OptionalComponents, "components", v.GetString(common.VPkgDeployComponents), lang.CmdPackageRemoveFlagComponents) + _ = packageRemoveCmd.MarkFlagRequired("confirm") } -func bindPublishFlags() { +func bindPublishFlags(v *viper.Viper) { publishFlags := packagePublishCmd.Flags() - publishFlags.StringVarP(&pkgConfig.PublishOpts.SigningKeyPath, "key", "k", v.GetString(V_PKG_PUBLISH_SIGNING_KEY), lang.CmdPackagePublishFlagSigningKey) - publishFlags.StringVar(&pkgConfig.PublishOpts.SigningKeyPassword, "key-pass", v.GetString(V_PKG_PUBLISH_SIGNING_KEY_PASSWORD), lang.CmdPackagePublishFlagSigningKeyPassword) + publishFlags.StringVarP(&pkgConfig.PublishOpts.SigningKeyPath, "key", "k", v.GetString(common.VPkgPublishSigningKey), lang.CmdPackagePublishFlagSigningKey) + publishFlags.StringVar(&pkgConfig.PublishOpts.SigningKeyPassword, "key-pass", v.GetString(common.VPkgPublishSigningKeyPassword), lang.CmdPackagePublishFlagSigningKeyPassword) } -func bindPullFlags() { +func bindPullFlags(v *viper.Viper) { pullFlags := packagePullCmd.Flags() - v.SetDefault(V_PKG_PULL_OUTPUT_DIR, "") - pullFlags.StringVarP(&pkgConfig.PullOpts.OutputDirectory, "output-directory", "o", v.GetString(V_PKG_PULL_OUTPUT_DIR), lang.CmdPackagePullFlagOutputDirectory) - pullFlags.StringVarP(&pkgConfig.PullOpts.PublicKeyPath, "key", "k", v.GetString(V_PKG_PULL_PUBLIC_KEY), lang.CmdPackagePullFlagPublicKey) + pullFlags.StringVarP(&pkgConfig.PullOpts.OutputDirectory, "output-directory", "o", v.GetString(common.VPkgPullOutputDir), lang.CmdPackagePullFlagOutputDirectory) + pullFlags.StringVarP(&pkgConfig.PullOpts.PublicKeyPath, "key", "k", v.GetString(common.VPkgPullPublicKey), lang.CmdPackagePullFlagPublicKey) } diff --git a/src/cmd/prepare.go b/src/cmd/prepare.go index 9bf8d1aad3..3b5cd1f221 100644 --- a/src/cmd/prepare.go +++ b/src/cmd/prepare.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/AlecAivazis/survey/v2" + "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/message" @@ -102,8 +103,9 @@ var prepareFindImages = &cobra.Command{ } // Ensure uppercase keys from viper - viperConfig := helpers.TransformMapKeys(v.GetStringMapString(V_PKG_CREATE_SET), strings.ToUpper) - pkgConfig.CreateOpts.SetVariables = helpers.MergeMap(viperConfig, pkgConfig.CreateOpts.SetVariables) + v := common.GetViper() + pkgConfig.CreateOpts.SetVariables = helpers.TransformAndMergeMap( + v.GetStringMapString(common.VPkgCreateSet), pkgConfig.CreateOpts.SetVariables, strings.ToUpper) // Configure the packager pkgClient := packager.NewOrDie(&pkgConfig) @@ -130,6 +132,7 @@ var prepareGenerateConfigFile = &cobra.Command{ fileName = args[0] } + v := common.GetViper() if err := v.SafeWriteConfigAs(fileName); err != nil { message.Fatalf(err, lang.CmdPrepareGenerateConfigErr, fileName) } @@ -137,7 +140,7 @@ var prepareGenerateConfigFile = &cobra.Command{ } func init() { - initViper() + v := common.InitViper() rootCmd.AddCommand(prepareCmd) prepareCmd.AddCommand(prepareTransformGitLinks) @@ -145,11 +148,11 @@ func init() { prepareCmd.AddCommand(prepareFindImages) prepareCmd.AddCommand(prepareGenerateConfigFile) - v.SetDefault(V_PKG_CREATE_SET, map[string]string{}) + v.SetDefault(common.VPkgCreateSet, map[string]string{}) prepareFindImages.Flags().StringVarP(&repoHelmChartPath, "repo-chart-path", "p", "", lang.CmdPrepareFlagRepoChartPath) // use the package create config for this and reset it here to avoid overwriting the config.CreateOptions.SetVariables - prepareFindImages.Flags().StringToStringVar(&pkgConfig.CreateOpts.SetVariables, "set", v.GetStringMapString(V_PKG_CREATE_SET), lang.CmdPrepareFlagSet) + prepareFindImages.Flags().StringToStringVar(&pkgConfig.CreateOpts.SetVariables, "set", v.GetStringMapString(common.VPkgCreateSet), lang.CmdPrepareFlagSet) // allow for the override of the default helm KubeVersion prepareFindImages.Flags().StringVar(&kubeVersionOverride, "kube-version", "", lang.CmdPrepareFlagKubeVersion) diff --git a/src/cmd/root.go b/src/cmd/root.go index 62f73f8db6..92053be023 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -9,45 +9,34 @@ import ( "os" "strings" + "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/cmd/tools" "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/message" - "github.com/defenseunicorns/zarf/src/pkg/utils/exec" "github.com/defenseunicorns/zarf/src/types" - "github.com/pterm/pterm" "github.com/spf13/cobra" - "github.com/spf13/viper" ) var ( - logLevel string - // Default global config for the packager pkgConfig = types.PackagerConfig{} - - // Viper instance used by the cmd package - v *viper.Viper - - // holds any error from reading in Viper config - vConfigError error ) var rootCmd = &cobra.Command{ Use: "zarf COMMAND", PersistentPreRun: func(cmd *cobra.Command, args []string) { // Skip for vendor-only commands - if tools.CheckVendorOnlyFromPath(cmd) { + if common.CheckVendorOnlyFromPath(cmd) { return } - exec.ExitOnInterrupt() - // Don't log the help command if cmd.Parent() == nil { config.SkipLogFile = true } - cliSetup() + + common.SetupCLI() }, Short: lang.RootCmdShort, Long: lang.RootCmdLong, @@ -59,10 +48,10 @@ var rootCmd = &cobra.Command{ if len(args) > 0 { if strings.Contains(args[0], config.ZarfPackagePrefix) || strings.Contains(args[0], "zarf-init") { - pterm.FgYellow.Printfln("\n"+lang.RootCmdDeprecatedDeploy, args[0]) + message.Warnf(lang.RootCmdDeprecatedDeploy, args[0]) } if args[0] == config.ZarfYAML { - pterm.FgYellow.Printfln("\n" + lang.RootCmdDeprecatedCreate) + message.Warn(lang.RootCmdDeprecatedCreate) } } }, @@ -78,62 +67,21 @@ func init() { tools.Include(rootCmd) // Skip for vendor-only commands - if tools.CheckVendorOnlyFromArgs() { + if common.CheckVendorOnlyFromArgs() { return } - initViper() - - v.SetDefault(V_LOG_LEVEL, "info") - v.SetDefault(V_ARCHITECTURE, "") - v.SetDefault(V_NO_LOG_FILE, false) - v.SetDefault(V_NO_PROGRESS, false) - v.SetDefault(V_NO_COLOR, false) - v.SetDefault(V_INSECURE, false) - v.SetDefault(V_ZARF_CACHE, config.ZarfDefaultCachePath) - v.SetDefault(V_TMP_DIR, "") - - rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "l", v.GetString(V_LOG_LEVEL), lang.RootCmdFlagLogLevel) - rootCmd.PersistentFlags().StringVarP(&config.CLIArch, "architecture", "a", v.GetString(V_ARCHITECTURE), lang.RootCmdFlagArch) - rootCmd.PersistentFlags().BoolVar(&config.SkipLogFile, "no-log-file", v.GetBool(V_NO_LOG_FILE), lang.RootCmdFlagSkipLogFile) - rootCmd.PersistentFlags().BoolVar(&message.NoProgress, "no-progress", v.GetBool(V_NO_PROGRESS), lang.RootCmdFlagNoProgress) - rootCmd.PersistentFlags().BoolVar(&config.NoColor, "no-color", v.GetBool(V_NO_COLOR), lang.RootCmdFlagNoColor) - rootCmd.PersistentFlags().StringVar(&config.CommonOptions.CachePath, "zarf-cache", v.GetString(V_ZARF_CACHE), lang.RootCmdFlagCachePath) - rootCmd.PersistentFlags().StringVar(&config.CommonOptions.TempDirectory, "tmpdir", v.GetString(V_TMP_DIR), lang.RootCmdFlagTempDir) - rootCmd.PersistentFlags().BoolVar(&config.CommonOptions.Insecure, "insecure", v.GetBool(V_INSECURE), lang.RootCmdFlagInsecure) -} - -func cliSetup() { - match := map[string]message.LogLevel{ - "warn": message.WarnLevel, - "info": message.InfoLevel, - "debug": message.DebugLevel, - "trace": message.TraceLevel, - } - - if config.NoColor { - message.DisableColor() - } + v := common.InitViper() - printViperConfigUsed() + v.SetDefault(common.VLogLevel, "info") + v.SetDefault(common.VZarfCache, config.ZarfDefaultCachePath) - // No log level set, so use the default - if logLevel != "" { - if lvl, ok := match[logLevel]; ok { - message.SetLogLevel(lvl) - message.Debug("Log level set to " + logLevel) - } else { - message.Warn(lang.RootCmdErrInvalidLogLevel) - } - } - - // Disable progress bars for CI envs - if os.Getenv("CI") == "true" { - message.Debug("CI environment detected, disabling progress bars") - message.NoProgress = true - } - - if !config.SkipLogFile { - message.UseLogFile() - } + rootCmd.PersistentFlags().StringVarP(&common.LogLevelCLI, "log-level", "l", v.GetString(common.VLogLevel), lang.RootCmdFlagLogLevel) + rootCmd.PersistentFlags().StringVarP(&config.CLIArch, "architecture", "a", v.GetString(common.VArchitecture), lang.RootCmdFlagArch) + rootCmd.PersistentFlags().BoolVar(&config.SkipLogFile, "no-log-file", v.GetBool(common.VNoLogFile), lang.RootCmdFlagSkipLogFile) + rootCmd.PersistentFlags().BoolVar(&message.NoProgress, "no-progress", v.GetBool(common.VNoProgress), lang.RootCmdFlagNoProgress) + rootCmd.PersistentFlags().BoolVar(&config.NoColor, "no-color", v.GetBool(common.VNoColor), lang.RootCmdFlagNoColor) + rootCmd.PersistentFlags().StringVar(&config.CommonOptions.CachePath, "zarf-cache", v.GetString(common.VZarfCache), lang.RootCmdFlagCachePath) + rootCmd.PersistentFlags().StringVar(&config.CommonOptions.TempDirectory, "tmpdir", v.GetString(common.VTmpDir), lang.RootCmdFlagTempDir) + rootCmd.PersistentFlags().BoolVar(&config.CommonOptions.Insecure, "insecure", v.GetBool(common.VInsecure), lang.RootCmdFlagInsecure) } diff --git a/src/cmd/tools/common.go b/src/cmd/tools/common.go index 18f5da8c7c..da1f6725c1 100644 --- a/src/cmd/tools/common.go +++ b/src/cmd/tools/common.go @@ -5,38 +5,18 @@ package tools import ( - "os" - "strings" - + "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" - "github.com/defenseunicorns/zarf/src/pkg/utils/exec" - "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" "github.com/spf13/cobra" ) -var vendorCmds = []string{ - "kubectl", - "k", - "syft", - "sbom", - "s", - "k9s", - "monitor", - "wait-for", - "wait", - "w", - "crane", - "registry", - "r", -} - var toolsCmd = &cobra.Command{ Use: "tools", Aliases: []string{"t"}, PersistentPreRun: func(cmd *cobra.Command, args []string) { config.SkipLogFile = true - exec.ExitOnInterrupt() + common.SetupCLI() }, Short: lang.CmdToolsShort, } @@ -45,29 +25,3 @@ var toolsCmd = &cobra.Command{ func Include(rootCmd *cobra.Command) { rootCmd.AddCommand(toolsCmd) } - -// CheckVendorOnlyFromArgs checks if the command being run is a vendor-only command -func CheckVendorOnlyFromArgs() bool { - // Check for "zarf tools|t " where is in the vendorCmd list - return isVendorCmd(os.Args, vendorCmds) -} - -// CheckVendorOnlyFromPath checks if the cobra command is a vendor-only command -func CheckVendorOnlyFromPath(cmd *cobra.Command) bool { - args := strings.Split(cmd.CommandPath(), " ") - // Check for "zarf tools|t " where is in the vendorCmd list - return isVendorCmd(args, vendorCmds) -} - -// isVendorCmd checks if the command is a vendor command. -func isVendorCmd(args []string, vendoredCmds []string) bool { - if len(args) > 2 { - if args[1] == "tools" || args[1] == "t" { - if helpers.SliceContains(vendoredCmds, args[2]) { - return true - } - } - } - - return false -} diff --git a/src/cmd/tools/kubectl.go b/src/cmd/tools/kubectl.go index 15783ac58e..ab9f16e920 100644 --- a/src/cmd/tools/kubectl.go +++ b/src/cmd/tools/kubectl.go @@ -7,6 +7,7 @@ package tools import ( "os" + "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/spf13/cobra" @@ -25,7 +26,7 @@ func init() { } // Only load this command if it is being called directly. - if isVendorCmd(os.Args, []string{"kubectl", "k"}) { + if common.IsVendorCmd(os.Args, []string{"kubectl", "k"}) { // Add the kubectl command to the tools command. kubectlCmd = kubeCmd.NewDefaultKubectlCommand() diff --git a/src/cmd/tools/zarf.go b/src/cmd/tools/zarf.go index 0243105e96..b09922da98 100644 --- a/src/cmd/tools/zarf.go +++ b/src/cmd/tools/zarf.go @@ -10,182 +10,301 @@ import ( "path/filepath" "github.com/AlecAivazis/survey/v2" + "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/internal/cluster" + "github.com/defenseunicorns/zarf/src/internal/packager/git" + "github.com/defenseunicorns/zarf/src/internal/packager/helm" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/pkg/packager" "github.com/defenseunicorns/zarf/src/pkg/pki" "github.com/defenseunicorns/zarf/src/pkg/utils" + "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" + "github.com/defenseunicorns/zarf/src/types" "github.com/sigstore/cosign/pkg/cosign" "github.com/spf13/cobra" ) -func init() { - var subAltNames []string - var outputDirectory string - - readCredsCmd := &cobra.Command{ - Use: "get-git-password", - Hidden: true, - Short: lang.CmdToolsGetGitPasswdShort, - Long: lang.CmdToolsGetGitPasswdLong, - Run: func(cmd *cobra.Command, args []string) { - state, err := cluster.NewClusterOrDie().LoadZarfState() - if err != nil || state.Distro == "" { - // If no distro the zarf secret did not load properly - message.Fatalf(nil, lang.ErrLoadState) - } - - message.Note(lang.CmdToolsGetGitPasswdInfo) - message.Warn(lang.CmdToolsGetGitPasswdDeprecation) - utils.PrintComponentCredential(state, "git") - }, - } - - readAllCredsCmd := &cobra.Command{ - Use: "get-creds", - Short: lang.CmdToolsGetCredsShort, - Long: lang.CmdToolsGetCredsLong, - Aliases: []string{"gc"}, - Args: cobra.MaximumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - state, err := cluster.NewClusterOrDie().LoadZarfState() - if err != nil || state.Distro == "" { - // If no distro the zarf secret did not load properly - message.Fatalf(nil, lang.ErrLoadState) - } - - if len(args) > 0 { - // If a component name is provided, only show that component's credentials - utils.PrintComponentCredential(state, args[0]) - } else { - utils.PrintCredentialTable(state, nil) - } - }, - } - - clearCacheCmd := &cobra.Command{ - Use: "clear-cache", - Aliases: []string{"c"}, - Short: lang.CmdToolsClearCacheShort, - Run: func(cmd *cobra.Command, args []string) { - message.Notef(lang.CmdToolsClearCacheDir, config.GetAbsCachePath()) - if err := os.RemoveAll(config.GetAbsCachePath()); err != nil { - message.Fatalf(err, lang.CmdToolsClearCacheErr, config.GetAbsCachePath()) - } - message.Successf(lang.CmdToolsClearCacheSuccess, config.GetAbsCachePath()) - }, - } - - downloadInitCmd := &cobra.Command{ - Use: "download-init", - Short: lang.CmdToolsDownloadInitShort, - Run: func(cmd *cobra.Command, args []string) { - initPackageName := packager.GetInitPackageName("") - target := filepath.Join(outputDirectory, initPackageName) - url := packager.GetInitPackageRemote("") - err := utils.DownloadToFile(url, target, "") - if err != nil { - message.Fatalf(err, lang.CmdToolsDownloadInitErr, err.Error()) - } - }, - } - - generatePKICmd := &cobra.Command{ - Use: "gen-pki HOST", - Aliases: []string{"pki"}, - Short: lang.CmdToolsGenPkiShort, - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - pki := pki.GeneratePKI(args[0], subAltNames...) - if err := os.WriteFile("tls.ca", pki.CA, 0644); err != nil { - message.Fatalf(err, lang.ErrWritingFile, "tls.ca", err.Error()) - } - if err := os.WriteFile("tls.crt", pki.Cert, 0644); err != nil { - message.Fatalf(err, lang.ErrWritingFile, "tls.crt", err.Error()) - } - if err := os.WriteFile("tls.key", pki.Key, 0600); err != nil { - message.Fatalf(err, lang.ErrWritingFile, "tls.key", err.Error()) - } - message.Successf(lang.CmdToolsGenPkiSuccess, args[0]) - }, - } - - generateKeyCmd := &cobra.Command{ - Use: "gen-key", - Aliases: []string{"key"}, - Short: lang.CmdToolsGenKeyShort, - Run: func(cmd *cobra.Command, args []string) { - // Utility function to prompt the user for the password to the private key - passwordFunc := func(bool) ([]byte, error) { - // perform the first prompt - var password string - prompt := &survey.Password{ - Message: lang.CmdToolsGenKeyPrompt, - } - if err := survey.AskOne(prompt, &password); err != nil { - return nil, fmt.Errorf(lang.CmdToolsGenKeyErrUnableGetPassword, err.Error()) - } +var subAltNames []string +var outputDirectory string +var updateCredsInitOpts types.ZarfInitOptions - // perform the second prompt - var doubleCheck string - rePrompt := &survey.Password{ - Message: lang.CmdToolsGenKeyPromptAgain, - } - if err := survey.AskOne(rePrompt, &doubleCheck); err != nil { - return nil, fmt.Errorf(lang.CmdToolsGenKeyErrUnableGetPassword, err.Error()) - } +var deprecatedGetGitCredsCmd = &cobra.Command{ + Use: "get-git-password", + Hidden: true, + Short: lang.CmdToolsGetGitPasswdShort, + Long: lang.CmdToolsGetGitPasswdLong, + Run: func(cmd *cobra.Command, args []string) { + message.Warn(lang.CmdToolsGetGitPasswdDeprecation) + getCredsCmd.Run(getCredsCmd, []string{"git"}) + }, +} - // check if the passwords match - if password != doubleCheck { - return nil, fmt.Errorf(lang.CmdToolsGenKeyErrPasswordsNotMatch) - } +var getCredsCmd = &cobra.Command{ + Use: "get-creds", + Short: lang.CmdToolsGetCredsShort, + Long: lang.CmdToolsGetCredsLong, + Example: lang.CmdToolsGetCredsExample, + Aliases: []string{"gc"}, + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + state, err := cluster.NewClusterOrDie().LoadZarfState() + if err != nil || state.Distro == "" { + // If no distro the zarf secret did not load properly + message.Fatalf(nil, lang.ErrLoadState) + } - return []byte(password), nil + if len(args) > 0 { + // If a component name is provided, only show that component's credentials + message.PrintComponentCredential(state, args[0]) + } else { + message.PrintCredentialTable(state, nil) + } + }, +} + +var updateCredsCmd = &cobra.Command{ + Use: "update-creds", + Short: lang.CmdToolsUpdateCredsShort, + Long: lang.CmdToolsUpdateCredsLong, + Example: lang.CmdToolsUpdateCredsExample, + Aliases: []string{"uc"}, + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + validKeys := []string{message.RegistryKey, message.GitKey, message.ArtifactKey} + if len(args) == 0 { + args = validKeys + } else { + if !helpers.SliceContains(validKeys, args[0]) { + cmd.Help() + message.Fatalf(nil, lang.CmdToolsUpdateCredsInvalidServiceErr, message.RegistryKey, message.GitKey, message.ArtifactKey) } + } + + c := cluster.NewClusterOrDie() + oldState, err := c.LoadZarfState() + if err != nil || oldState.Distro == "" { + // If no distro the zarf secret did not load properly + message.Fatalf(nil, lang.ErrLoadState) + } + + newState := c.MergeZarfState(oldState, updateCredsInitOpts, args) - // Use cosign to generate the keypair - keyBytes, err := cosign.GenerateKeyPair(passwordFunc) + message.PrintCredentialUpdates(oldState, newState, args) + + confirm := config.CommonOptions.Confirm + + if confirm { + message.Note(lang.CmdToolsUpdateCredsConfirmProvided) + } else { + prompt := &survey.Confirm{ + Message: lang.CmdToolsUpdateCredsConfirmContinue, + } + if err := survey.AskOne(prompt, &confirm); err != nil { + message.Fatalf(nil, lang.ErrConfirmCancel, err) + } + } + + if confirm { + // Update registry and git pull secrets + if helpers.SliceContains(args, message.RegistryKey) { + c.UpdateZarfManagedImageSecrets(newState) + } + if helpers.SliceContains(args, message.GitKey) { + c.UpdateZarfManagedGitSecrets(newState) + } + + // Update artifact token (if internal) + if helpers.SliceContains(args, message.ArtifactKey) && newState.ArtifactServer.PushToken == "" && newState.ArtifactServer.InternalServer { + g := git.New(oldState.GitServer) + tokenResponse, err := g.CreatePackageRegistryToken() + if err != nil { + message.Fatalf(nil, lang.CmdToolsUpdateCredsUnableCreateToken, err.Error()) + } + newState.ArtifactServer.PushToken = tokenResponse.Sha1 + } + + // Save the final Zarf State + err = c.SaveZarfState(newState) if err != nil { - message.Fatalf(err, lang.CmdToolsGenKeyErrUnableToGenKeypair, err.Error()) + message.Fatalf(err, lang.ErrSaveState) } - prvKeyFileName := "cosign.key" - pubKeyFileName := "cosign.pub" + // Update Zarf 'init' component Helm releases if present + h := helm.Helm{ + Cluster: c, + Cfg: &types.PackagerConfig{ + State: newState, + }, + } - // Check if we are about to overwrite existing key files - _, prvKeyExistsErr := os.Stat(prvKeyFileName) - _, pubKeyExistsErr := os.Stat(pubKeyFileName) - if prvKeyExistsErr == nil || pubKeyExistsErr == nil { - var confirm bool - confirmOverwritePrompt := &survey.Confirm{ - Message: fmt.Sprintf(lang.CmdToolsGenKeyPromptExists, prvKeyFileName), + if helpers.SliceContains(args, message.RegistryKey) && newState.RegistryInfo.InternalRegistry { + err = h.UpdateZarfRegistryValues() + if err != nil { + message.Fatalf(err, lang.CmdToolsUpdateCredsUnableUpdateRegistry, err.Error()) } - err := survey.AskOne(confirmOverwritePrompt, &confirm) + } + if helpers.SliceContains(args, message.GitKey) && newState.GitServer.InternalServer { + err = h.UpdateZarfGiteaValues() if err != nil { - message.Fatalf(err, lang.CmdToolsGenKeyErrNoConfirmOverwrite) + message.Fatalf(err, lang.CmdToolsUpdateCredsUnableUpdateGit, err.Error()) } + } + } + }, +} - if !confirm { - message.Fatal(nil, lang.CmdToolsGenKeyErrNoConfirmOverwrite) - } +var clearCacheCmd = &cobra.Command{ + Use: "clear-cache", + Aliases: []string{"c"}, + Short: lang.CmdToolsClearCacheShort, + Run: func(cmd *cobra.Command, args []string) { + message.Notef(lang.CmdToolsClearCacheDir, config.GetAbsCachePath()) + if err := os.RemoveAll(config.GetAbsCachePath()); err != nil { + message.Fatalf(err, lang.CmdToolsClearCacheErr, config.GetAbsCachePath()) + } + message.Successf(lang.CmdToolsClearCacheSuccess, config.GetAbsCachePath()) + }, +} + +var downloadInitCmd = &cobra.Command{ + Use: "download-init", + Short: lang.CmdToolsDownloadInitShort, + Run: func(cmd *cobra.Command, args []string) { + initPackageName := packager.GetInitPackageName("") + target := filepath.Join(outputDirectory, initPackageName) + url := packager.GetInitPackageRemote("") + err := utils.DownloadToFile(url, target, "") + if err != nil { + message.Fatalf(err, lang.CmdToolsDownloadInitErr, err.Error()) + } + }, +} + +var generatePKICmd = &cobra.Command{ + Use: "gen-pki HOST", + Aliases: []string{"pki"}, + Short: lang.CmdToolsGenPkiShort, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + pki := pki.GeneratePKI(args[0], subAltNames...) + if err := os.WriteFile("tls.ca", pki.CA, 0644); err != nil { + message.Fatalf(err, lang.ErrWritingFile, "tls.ca", err.Error()) + } + if err := os.WriteFile("tls.crt", pki.Cert, 0644); err != nil { + message.Fatalf(err, lang.ErrWritingFile, "tls.crt", err.Error()) + } + if err := os.WriteFile("tls.key", pki.Key, 0600); err != nil { + message.Fatalf(err, lang.ErrWritingFile, "tls.key", err.Error()) + } + message.Successf(lang.CmdToolsGenPkiSuccess, args[0]) + }, +} + +var generateKeyCmd = &cobra.Command{ + Use: "gen-key", + Aliases: []string{"key"}, + Short: lang.CmdToolsGenKeyShort, + Run: func(cmd *cobra.Command, args []string) { + // Utility function to prompt the user for the password to the private key + passwordFunc := func(bool) ([]byte, error) { + // perform the first prompt + var password string + prompt := &survey.Password{ + Message: lang.CmdToolsGenKeyPrompt, + } + if err := survey.AskOne(prompt, &password); err != nil { + return nil, fmt.Errorf(lang.CmdToolsGenKeyErrUnableGetPassword, err.Error()) } - // Write the key file contents to disk - if err := os.WriteFile(prvKeyFileName, keyBytes.PrivateBytes, 0600); err != nil { - message.Fatalf(err, lang.ErrWritingFile, prvKeyFileName, err.Error()) + // perform the second prompt + var doubleCheck string + rePrompt := &survey.Password{ + Message: lang.CmdToolsGenKeyPromptAgain, } - if err := os.WriteFile(pubKeyFileName, keyBytes.PublicBytes, 0644); err != nil { - message.Fatalf(err, lang.ErrWritingFile, pubKeyFileName, err.Error()) + if err := survey.AskOne(rePrompt, &doubleCheck); err != nil { + return nil, fmt.Errorf(lang.CmdToolsGenKeyErrUnableGetPassword, err.Error()) + } + + // check if the passwords match + if password != doubleCheck { + return nil, fmt.Errorf(lang.CmdToolsGenKeyErrPasswordsNotMatch) + } + + return []byte(password), nil + } + + // Use cosign to generate the keypair + keyBytes, err := cosign.GenerateKeyPair(passwordFunc) + if err != nil { + message.Fatalf(err, lang.CmdToolsGenKeyErrUnableToGenKeypair, err.Error()) + } + + prvKeyFileName := "cosign.key" + pubKeyFileName := "cosign.pub" + + // Check if we are about to overwrite existing key files + _, prvKeyExistsErr := os.Stat(prvKeyFileName) + _, pubKeyExistsErr := os.Stat(pubKeyFileName) + if prvKeyExistsErr == nil || pubKeyExistsErr == nil { + var confirm bool + confirmOverwritePrompt := &survey.Confirm{ + Message: fmt.Sprintf(lang.CmdToolsGenKeyPromptExists, prvKeyFileName), } + err := survey.AskOne(confirmOverwritePrompt, &confirm) + if err != nil { + message.Fatalf(err, lang.CmdToolsGenKeyErrNoConfirmOverwrite) + } + + if !confirm { + message.Fatal(nil, lang.CmdToolsGenKeyErrNoConfirmOverwrite) + } + } + + // Write the key file contents to disk + if err := os.WriteFile(prvKeyFileName, keyBytes.PrivateBytes, 0600); err != nil { + message.Fatalf(err, lang.ErrWritingFile, prvKeyFileName, err.Error()) + } + if err := os.WriteFile(pubKeyFileName, keyBytes.PublicBytes, 0644); err != nil { + message.Fatalf(err, lang.ErrWritingFile, pubKeyFileName, err.Error()) + } + + message.Successf(lang.CmdToolsGenKeySuccess, prvKeyFileName, pubKeyFileName) + }, +} + +func init() { + v := common.InitViper() + + toolsCmd.AddCommand(deprecatedGetGitCredsCmd) + toolsCmd.AddCommand(getCredsCmd) + + toolsCmd.AddCommand(updateCredsCmd) + + // Always require confirm flag (no viper) + updateCredsCmd.Flags().BoolVar(&config.CommonOptions.Confirm, "confirm", false, lang.CmdToolsUpdateCredsConfirmFlag) + + // Flags for using an external Git server + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.GitServer.Address, "git-url", v.GetString(common.VInitGitURL), lang.CmdInitFlagGitURL) + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.GitServer.PushUsername, "git-push-username", v.GetString(common.VInitGitPushUser), lang.CmdInitFlagGitPushUser) + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.GitServer.PushPassword, "git-push-password", v.GetString(common.VInitGitPushPass), lang.CmdInitFlagGitPushPass) + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.GitServer.PullUsername, "git-pull-username", v.GetString(common.VInitGitPullUser), lang.CmdInitFlagGitPullUser) + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.GitServer.PullPassword, "git-pull-password", v.GetString(common.VInitGitPullPass), lang.CmdInitFlagGitPullPass) + + // Flags for using an external registry + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.RegistryInfo.Address, "registry-url", v.GetString(common.VInitRegistryURL), lang.CmdInitFlagRegURL) + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.RegistryInfo.PushUsername, "registry-push-username", v.GetString(common.VInitRegistryPushUser), lang.CmdInitFlagRegPushUser) + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.RegistryInfo.PushPassword, "registry-push-password", v.GetString(common.VInitRegistryPushPass), lang.CmdInitFlagRegPushPass) + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.RegistryInfo.PullUsername, "registry-pull-username", v.GetString(common.VInitRegistryPullUser), lang.CmdInitFlagRegPullUser) + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.RegistryInfo.PullPassword, "registry-pull-password", v.GetString(common.VInitRegistryPullPass), lang.CmdInitFlagRegPullPass) - message.Successf(lang.CmdToolsGenKeySuccess, prvKeyFileName, pubKeyFileName) - }, - } + // Flags for using an external artifact server + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.ArtifactServer.Address, "artifact-url", v.GetString(common.VInitArtifactURL), lang.CmdInitFlagArtifactURL) + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.ArtifactServer.PushUsername, "artifact-push-username", v.GetString(common.VInitArtifactPushUser), lang.CmdInitFlagArtifactPushUser) + updateCredsCmd.Flags().StringVar(&updateCredsInitOpts.ArtifactServer.PushToken, "artifact-push-token", v.GetString(common.VInitArtifactPushToken), lang.CmdInitFlagArtifactPushToken) - toolsCmd.AddCommand(readCredsCmd) - toolsCmd.AddCommand(readAllCredsCmd) + updateCredsCmd.Flags().SortFlags = true toolsCmd.AddCommand(clearCacheCmd) clearCacheCmd.Flags().StringVar(&config.CommonOptions.CachePath, "zarf-cache", config.ZarfDefaultCachePath, lang.CmdToolsClearCacheFlagCachePath) diff --git a/src/cmd/version.go b/src/cmd/version.go index 16ed44b3c0..18984a6e82 100644 --- a/src/cmd/version.go +++ b/src/cmd/version.go @@ -7,7 +7,6 @@ package cmd import ( "encoding/json" "fmt" - "os" "runtime" "github.com/Masterminds/semver/v3" @@ -76,11 +75,6 @@ var versionCmd = &cobra.Command{ }, } -func isVersionCmd() bool { - args := os.Args - return len(args) > 1 && (args[1] == "version" || args[1] == "v") -} - func init() { versionCmd.Flags().StringVarP(&outputFormat, "output", "o", "", "Output format (yaml|json)") rootCmd.AddCommand(versionCmd) diff --git a/src/cmd/viper.go b/src/cmd/viper.go deleted file mode 100644 index a8dedcc996..0000000000 --- a/src/cmd/viper.go +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2021-Present The Zarf Authors - -// Package cmd contains the CLI commands for Zarf. -package cmd - -import ( - "os" - "strings" - - "github.com/defenseunicorns/zarf/src/cmd/tools" - "github.com/defenseunicorns/zarf/src/config/lang" - "github.com/defenseunicorns/zarf/src/pkg/message" - "github.com/spf13/viper" -) - -const ( - // Root config keys - V_LOG_LEVEL = "log_level" - V_ARCHITECTURE = "architecture" - V_NO_LOG_FILE = "no_log_file" - V_NO_PROGRESS = "no_progress" - V_NO_COLOR = "no_color" - V_ZARF_CACHE = "zarf_cache" - V_TMP_DIR = "tmp_dir" - V_INSECURE = "insecure" - - // Init config keys - V_INIT_COMPONENTS = "init.components" - V_INIT_STORAGE_CLASS = "init.storage_class" - - // Init Git config keys - V_INIT_GIT_URL = "init.git.url" - V_INIT_GIT_PUSH_USER = "init.git.push_username" - V_INIT_GIT_PUSH_PASS = "init.git.push_password" - V_INIT_GIT_PULL_USER = "init.git.pull_username" - V_INIT_GIT_PULL_PASS = "init.git.pull_password" - - // Init Registry config keys - V_INIT_REGISTRY_URL = "init.registry.url" - V_INIT_REGISTRY_NODEPORT = "init.registry.nodeport" - V_INIT_REGISTRY_SECRET = "init.registry.secret" - V_INIT_REGISTRY_PUSH_USER = "init.registry.push_username" - V_INIT_REGISTRY_PUSH_PASS = "init.registry.push_password" - V_INIT_REGISTRY_PULL_USER = "init.registry.pull_username" - V_INIT_REGISTRY_PULL_PASS = "init.registry.pull_password" - - // Init Package config keys - V_INIT_ARTIFACT_URL = "init.artifact.url" - V_INIT_ARTIFACT_PUSH_USER = "init.artifact.push_username" - V_INIT_ARTIFACT_PUSH_TOKEN = "init.artifact.push_token" - - // Package config keys - V_PKG_OCI_CONCURRENCY = "package.oci_concurrency" - - // Package create config keys - V_PKG_CREATE_SET = "package.create.set" - V_PKG_CREATE_OUTPUT = "package.create.output" - V_PKG_CREATE_SBOM = "package.create.sbom" - V_PKG_CREATE_SBOM_OUTPUT = "package.create.sbom_output" - V_PKG_CREATE_SKIP_SBOM = "package.create.skip_sbom" - V_PKG_CREATE_MAX_PACKAGE_SIZE = "package.create.max_package_size" - V_PKG_CREATE_SIGNING_KEY = "package.create.signing_key" - V_PKG_CREATE_SIGNING_KEY_PASSWORD = "package.create.signing_key_password" - V_PKG_CREATE_DIFFERENTIAL = "package.create.differential" - V_PKG_CREATE_REGISTRY_OVERRIDE = "package.create.registry_override" - - // Package deploy config keys - V_PKG_DEPLOY_SET = "package.deploy.set" - V_PKG_DEPLOY_COMPONENTS = "package.deploy.components" - V_PKG_DEPLOY_SHASUM = "package.deploy.shasum" - V_PKG_DEPLOY_SGET = "package.deploy.sget" - V_PKG_DEPLOY_PUBLIC_KEY = "package.deploy.public_key" - - // Package publish config keys - V_PKG_PUBLISH_SIGNING_KEY = "package.publish.signing_key" - V_PKG_PUBLISH_SIGNING_KEY_PASSWORD = "package.publish.signing_key_password" - - // Package pull config keys - V_PKG_PULL_OUTPUT_DIR = "package.pull.output_directory" - V_PKG_PULL_PUBLIC_KEY = "package.pull.public_key" -) - -func initViper() { - // Already initialized by some other command - if v != nil { - return - } - - v = viper.New() - - // Skip for vendor-only commands - if tools.CheckVendorOnlyFromArgs() { - return - } - - // Skip for the version command - if isVersionCmd() { - return - } - - // Specify an alternate config file - cfgFile := os.Getenv("ZARF_CONFIG") - - // Don't forget to read config either from cfgFile or from home directory! - if cfgFile != "" { - // Use config file from the flag. - v.SetConfigFile(cfgFile) - } else { - // Search config paths in the current directory and $HOME/.zarf. - v.AddConfigPath(".") - v.AddConfigPath("$HOME/.zarf") - v.SetConfigName("zarf-config") - } - - // E.g. ZARF_LOG_LEVEL=debug - v.SetEnvPrefix("zarf") - v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - v.AutomaticEnv() - - // Optional, but capture any error - vConfigError = v.ReadInConfig() -} - -func printViperConfigUsed() { - // Optional, so ignore file not found errors - if vConfigError != nil { - // Config file not found; ignore - if _, ok := vConfigError.(viper.ConfigFileNotFoundError); !ok { - message.WarnErrorf(vConfigError, lang.CmdViperErrLoadingConfigFile, vConfigError.Error()) - } - } else { - message.Notef(lang.CmdViperInfoUsingConfigFile, v.ConfigFileUsed()) - } -} diff --git a/src/config/config.go b/src/config/config.go index 593dbcbd8c..9bc9cfaf1c 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -72,6 +72,8 @@ const ( ZarfInClusterGitServiceURL = "http://zarf-gitea-http.zarf.svc.cluster.local:3000" ZarfInClusterArtifactServiceURL = ZarfInClusterGitServiceURL + "/api/packages/" + ZarfGitPushUser + + ZarfLoggingUser = "zarf-admin" ) // Zarf Global Configuration Variables. diff --git a/src/config/lang/english.go b/src/config/lang/english.go index 88c48337d6..9bfe870250 100644 --- a/src/config/lang/english.go +++ b/src/config/lang/english.go @@ -17,6 +17,7 @@ import "errors" const ( ErrLoadingConfig = "failed to load config: %w" ErrLoadState = "Failed to load the Zarf State from the Kubernetes cluster." + ErrSaveState = "Failed to save the Zarf State to the Kubernetes cluster." ErrLoadPackageSecret = "Failed to load %s's secret from the Kubernetes cluster" ErrMarshal = "failed to marshal file: %w" ErrNoClusterConnection = "Failed to connect to the Kubernetes cluster." @@ -50,8 +51,8 @@ const ( RootCmdFlagTempDir = "Specify the temporary directory to use for intermediate files" RootCmdFlagInsecure = "Allow access to insecure registries and disable other recommended security enforcements such as package checksum and signature validation. This flag should only be used if you have a specific reason and accept the reduced security posture." - RootCmdDeprecatedDeploy = "Please use \"zarf package deploy %s\" to deploy this package." - RootCmdDeprecatedCreate = "Please use \"zarf package create\" to create this package." + RootCmdDeprecatedDeploy = "Deprecated: Please use \"zarf package deploy %s\" to deploy this package. This warning will be removed in Zarf v1.0.0." + RootCmdDeprecatedCreate = "Deprecated: Please use \"zarf package create\" to create this package. This warning will be removed in Zarf v1.0.0." RootCmdErrInvalidLogLevel = "Invalid log level. Valid options are: warn, info, debug, trace." @@ -127,6 +128,11 @@ const ( # Initializing w/ an external git server: zarf init --git-push-password={PASSWORD} --git-push-username={USERNAME} --git-url={URL} + + # Initializing w/ an external artifact server: + zarf init --artifact-push-password={PASSWORD} --artifact-push-username={USERNAME} --artifact-url={URL} + + # NOTE: Not specifying a pull username/password will use the push user for pulling as well. ` CmdInitErrFlags = "Invalid command flags were provided." @@ -139,7 +145,6 @@ const ( CmdInitDownloadAsk = "It seems the init package could not be found locally, but can be downloaded from %s" CmdInitDownloadNote = "Note: This will require an internet connection." CmdInitDownloadConfirm = "Do you want to download this init package?" - CmdInitDownloadErrCancel = "confirm selection canceled: %s" CmdInitDownloadErrManual = "download the init package manually and place it in the current working directory" CmdInitFlagSet = "Specify deployment variables to set on the command line (KEY=value)" @@ -247,7 +252,7 @@ const ( CmdPackageDeployFlagSet = "Specify deployment variables to set on the command line (KEY=value)" CmdPackageDeployFlagComponents = "Comma-separated list of components to install. Adding this flag will skip the init prompts for which components to install" CmdPackageDeployFlagShasum = "Shasum of the package to deploy. Required if deploying a remote package and \"--insecure\" is not provided" - CmdPackageDeployFlagSget = "[Deprecated] Path to public sget key file for remote packages signed via cosign. This flag will be removed in v0.31.0 please use the --key flag instead." + CmdPackageDeployFlagSget = "[Deprecated] Path to public sget key file for remote packages signed via cosign. This flag will be removed in v1.0.0 please use the --key flag instead." CmdPackageDeployFlagPublicKey = "Path to public key file for validating signed packages" CmdPackageDeployValidateArchitectureErr = "this package architecture is %s, but the target cluster has the %s architecture. These architectures must be the same" CmdPackageDeployValidateLastNonBreakingVersionWarn = "the version of this Zarf binary '%s' is less than the LastNonBreakingVersion of '%s'. You may need to upgrade your Zarf version to at least '%s' to deploy this package" @@ -390,10 +395,9 @@ $ zarf tools registry digest reg.example.com/stefanprodan/podinfo:6.4.0 CmdToolsRegistryFlagNonDist = "Allow pushing non-distributable (foreign) layers" CmdToolsRegistryFlagPlatform = "Specifies the platform in the form os/arch[/variant][:osversion] (e.g. linux/amd64)." - CmdToolsGetGitPasswdShort = "Returns the push user's password for the Git server" - CmdToolsGetGitPasswdLong = "Reads the password for a user with push access to the configured Git server from the zarf-state secret in the zarf namespace" - CmdToolsGetGitPasswdInfo = "Git Server Push Password: " - CmdToolsGetGitPasswdDeprecation = "Deprecated: This command has been replaced by 'zarf tools get-creds git' and will be removed in a future release." + CmdToolsGetGitPasswdShort = "Deprecated: Returns the push user's password for the Git server" + CmdToolsGetGitPasswdLong = "Deprecated: Reads the password for a user with push access to the configured Git server in Zarf State. Note that this command has been replaced by 'zarf tools get-creds git' and will be removed in Zarf v1.0.0." + CmdToolsGetGitPasswdDeprecation = "Deprecated: This command has been replaced by 'zarf tools get-creds git' and will be removed in Zarf v1.0.0." CmdToolsMonitorShort = "Launches a terminal UI to monitor the connected cluster using K9s." @@ -425,29 +429,29 @@ $ zarf tools registry digest reg.example.com/stefanprodan/podinfo:6.4.0 CmdToolsGenKeySuccess = "Generated key pair and written to %s and %s" CmdToolsSbomShort = "Generates a Software Bill of Materials (SBOM) for the given package" - CmdToolsSbomErr = "Unable to create sbom (syft) CLI" + CmdToolsSbomErr = "Unable to create SBOM (Syft) CLI" CmdToolsWaitForShort = "Waits for a given Kubernetes resource to be ready" CmdToolsWaitForLong = "By default Zarf will wait for all Kubernetes resources to be ready before completion of a component during a deployment.\n" + "This command can be used to wait for a Kubernetes resources to exist and be ready that may be created by a Gitops tool or a Kubernetes operator.\n" + "You can also wait for arbitrary network endpoints using REST or TCP checks.\n\n" CmdToolsWaitForExample = ` - Wait for Kubernetes resources: - zarf tools wait-for pod my-pod-name ready -n default # wait for pod my-pod-name in namespace default to be ready - zarf tools wait-for p cool-pod-name ready -n cool # wait for pod (using p alias) cool-pod-name in namespace cool to be ready - zarf tools wait-for deployment podinfo available -n podinfo # wait for deployment podinfo in namespace podinfo to be available - zarf tools wait-for pod app=podinfo ready -n podinfo # wait for pod with label app=podinfo in namespace podinfo to be ready - zarf tools wait-for svc zarf-docker-registry exists -n zarf # wait for service zarf-docker-registry in namespace zarf to exist - zarf tools wait-for svc zarf-docker-registry -n zarf # same as above, except exists is the default condition - zarf tools wait-for crd addons.k3s.cattle.io # wait for crd addons.k3s.cattle.io to exist - zarf tools wait-for sts test-sts '{.status.availableReplicas}'=23 # wait for statefulset test-sts to have 23 available replicas - - Wait for network endpoints: - zarf tools wait-for http localhost:8080 200 # wait for a 200 response from http://localhost:8080 - zarf tools wait-for tcp localhost:8080 # wait for a connection to be established on localhost:8080 - zarf tools wait-for https 1.1.1.1 200 # wait for a 200 response from https://1.1.1.1 - zarf tools wait-for http google.com # wait for any 2xx response from http://google.com - zarf tools wait-for http google.com success # wait for any 2xx response from http://google.com + # Wait for Kubernetes resources: + zarf tools wait-for pod my-pod-name ready -n default # wait for pod my-pod-name in namespace default to be ready + zarf tools wait-for p cool-pod-name ready -n cool # wait for pod (using p alias) cool-pod-name in namespace cool to be ready + zarf tools wait-for deployment podinfo available -n podinfo # wait for deployment podinfo in namespace podinfo to be available + zarf tools wait-for pod app=podinfo ready -n podinfo # wait for pod with label app=podinfo in namespace podinfo to be ready + zarf tools wait-for svc zarf-docker-registry exists -n zarf # wait for service zarf-docker-registry in namespace zarf to exist + zarf tools wait-for svc zarf-docker-registry -n zarf # same as above, except exists is the default condition + zarf tools wait-for crd addons.k3s.cattle.io # wait for crd addons.k3s.cattle.io to exist + zarf tools wait-for sts test-sts '{.status.availableReplicas}'=23 # wait for statefulset test-sts to have 23 available replicas + + # Wait for network endpoints: + zarf tools wait-for http localhost:8080 200 # wait for a 200 response from http://localhost:8080 + zarf tools wait-for tcp localhost:8080 # wait for a connection to be established on localhost:8080 + zarf tools wait-for https 1.1.1.1 200 # wait for a 200 response from https://1.1.1.1 + zarf tools wait-for http google.com # wait for any 2xx response from http://google.com + zarf tools wait-for http google.com success # wait for any 2xx response from http://google.com ` CmdToolsWaitForFlagTimeout = "Specify the timeout duration for the wait command." CmdToolsWaitForErrTimeoutString = "Invalid timeout duration '%s'. Please use a valid duration string (e.g. 1s, 2m, 3h)." @@ -458,8 +462,56 @@ $ zarf tools registry digest reg.example.com/stefanprodan/podinfo:6.4.0 CmdToolsKubectlDocs = "Kubectl command. See https://kubernetes.io/docs/reference/kubectl/overview/ for more information." - CmdToolsGetCredsShort = "Displays a Table of credentials for deployed components. Pass a component name to get a single credential" - CmdToolsGetCredsLong = "Display a Table of credentials for deployed components. Pass a component name to get a single credential. i.e. 'zarf tools get-creds registry'" + CmdToolsGetCredsShort = "Displays a table of credentials for deployed Zarf services. Pass a service key to get a single credential" + CmdToolsGetCredsLong = "Display a table of credentials for deployed Zarf services. Pass a service key to get a single credential. i.e. 'zarf tools get-creds registry'" + CmdToolsGetCredsExample = ` + # Print all Zarf credentials: + zarf tools get-creds + + # Get specific Zarf credentials: + zarf tools get-creds registry + zarf tools get-creds registry-readonly + zarf tools get-creds git + zarf tools get-creds git-readonly + zarf tools get-creds artifact + zarf tools get-creds logging +` + + CmdToolsUpdateCredsShort = "Updates the credentials for deployed Zarf services. Pass a service key to update credentials for a single service" + CmdToolsUpdateCredsLong = "Updates the credentials for deployed Zarf services. Pass a service key to update credentials for a single service. i.e. 'zarf tools update-creds registry'" + CmdToolsUpdateCredsExample = ` + # Autogenerate all Zarf credentials at once: + zarf tools update-creds + + # Autogenerate specific Zarf service credentials: + zarf tools update-creds registry + zarf tools update-creds git + zarf tools update-creds artifact + zarf tools update-creds logging + + # Update all Zarf credentials w/external services at once: + zarf tools update-creds \ + --registry-push-username={USERNAME} --registry-push-password={PASSWORD} \ + --git-push-username={USERNAME} --git-push-password={PASSWORD} \ + --artifact-push-username={USERNAME} --artifact-push-token={PASSWORD} + + # NOTE: Any credentials omitted from flags without a service key specified will be autogenerated - URLs will only change if specified. + # Config options can also be set with the 'init' section of a Zarf config file. + + # Update specific Zarf credentials w/external services: + zarf tools update-creds registry --registry-push-username={USERNAME} --registry-push-password={PASSWORD} + zarf tools update-creds git --git-push-username={USERNAME} --git-push-password={PASSWORD} + zarf tools update-creds artifact --artifact-push-username={USERNAME} --artifact-push-token={PASSWORD} + + # NOTE: Not specifying a pull username/password will keep the previous pull username/password. +` + CmdToolsUpdateCredsConfirmFlag = "Confirm updating credentials without prompting" + CmdToolsUpdateCredsConfirmProvided = "Confirm flag specified, continuing without prompting." + CmdToolsUpdateCredsConfirmContinue = "Continue with these changes?" + CmdToolsUpdateCredsInvalidServiceErr = "Invalid service key specified - valid keys are: %s, %s, and %s" + CmdToolsUpdateCredsUnableCreateToken = "Unable to create the new Gitea artifact token: %s" + CmdToolsUpdateCredsUnableUpdateRegistry = "Unable to update Zarf registry: %s" + CmdToolsUpdateCredsUnableUpdateGit = "Unable to update Zarf git server: %s" // zarf version CmdVersionShort = "Shows the version of the running Zarf binary" @@ -501,7 +553,7 @@ const ( // src/internal/packager/validate. const ( - PkgValidateTemplateDeprecation = "Package template '%s' is using the deprecated syntax ###ZARF_PKG_VAR_%s###. This will be removed in a future Zarf version. Please update to ###ZARF_PKG_TMPL_%s###." + PkgValidateTemplateDeprecation = "Package template '%s' is using the deprecated syntax ###ZARF_PKG_VAR_%s###. This will be removed in Zarf v1.0.0. Please update to ###ZARF_PKG_TMPL_%s###." PkgValidateMustBeUppercase = "variable name '%s' must be all uppercase and contain no special characters except _" PkgValidateErrAction = "invalid action: %w" PkgValidateErrActionVariables = "component %s cannot contain setVariables outside of onDeploy in actions" @@ -548,5 +600,5 @@ var ( // Collection of reusable warn messages. var ( - WarnSGetDeprecation = "Using sget to download resources is being deprecated and will removed in the v0.31.0 release of Zarf. Please publish the packages as OCI artifacts instead." + WarnSGetDeprecation = "Using sget to download resources is being deprecated and will removed in the v1.0.0 release of Zarf. Please publish the packages as OCI artifacts instead." ) diff --git a/src/internal/agent/hooks/argocd-application.go b/src/internal/agent/hooks/argocd-application.go index 986631e837..2f4d4b6dc0 100644 --- a/src/internal/agent/hooks/argocd-application.go +++ b/src/internal/agent/hooks/argocd-application.go @@ -28,7 +28,7 @@ type ArgoApplication struct { Spec struct { Source Source `json:"source"` Sources []Source `json:"sources"` - } + } `json:"spec"` } var ( diff --git a/src/internal/agent/hooks/flux.go b/src/internal/agent/hooks/flux.go index af4a057673..2594524238 100644 --- a/src/internal/agent/hooks/flux.go +++ b/src/internal/agent/hooks/flux.go @@ -29,7 +29,7 @@ type GenericGitRepo struct { Spec struct { URL string `json:"url"` SecretRef SecretRef `json:"secretRef,omitempty"` - } + } `json:"spec"` } // NewGitRepositoryMutationHook creates a new instance of the git repo mutation hook. diff --git a/src/internal/cluster/secrets.go b/src/internal/cluster/secrets.go index 489c2bcb08..cc29b6259c 100644 --- a/src/internal/cluster/secrets.go +++ b/src/internal/cluster/secrets.go @@ -7,9 +7,13 @@ package cluster import ( "encoding/base64" "encoding/json" - "fmt" + "reflect" corev1 "k8s.io/api/core/v1" + + "github.com/defenseunicorns/zarf/src/config" + "github.com/defenseunicorns/zarf/src/pkg/message" + "github.com/defenseunicorns/zarf/src/types" ) // DockerConfig contains the authentication information from the machine's docker config. @@ -26,24 +30,14 @@ type DockerConfigEntryWithAuth struct { } // GenerateRegistryPullCreds generates a secret containing the registry credentials. -func (c *Cluster) GenerateRegistryPullCreds(namespace, name string) (*corev1.Secret, error) { +func (c *Cluster) GenerateRegistryPullCreds(namespace, name string, registryInfo types.RegistryInfo) *corev1.Secret { secretDockerConfig := c.GenerateSecret(namespace, name, corev1.SecretTypeDockerConfigJson) - // Get the registry credentials from the ZarfState secret - zarfState, err := c.LoadZarfState() - if err != nil { - return nil, err - } - credential := zarfState.RegistryInfo.PullPassword - if credential == "" { - return nil, fmt.Errorf("generating pull credential failed") - } - // Auth field must be username:password and base64 encoded - fieldValue := zarfState.RegistryInfo.PullUsername + ":" + credential + fieldValue := registryInfo.PullUsername + ":" + registryInfo.PullPassword authEncodedValue := base64.StdEncoding.EncodeToString([]byte(fieldValue)) - registry := zarfState.RegistryInfo.Address + registry := registryInfo.Address // Create the expected structure for the dockerconfigjson dockerConfigJSON := DockerConfig{ Auths: DockerConfigEntry{ @@ -56,11 +50,92 @@ func (c *Cluster) GenerateRegistryPullCreds(namespace, name string) (*corev1.Sec // Convert to JSON dockerConfigData, err := json.Marshal(dockerConfigJSON) if err != nil { - return nil, err + message.WarnErrorf(err, "Unable to marshal the .dockerconfigjson secret data for the image pull secret") } // Add to the secret data secretDockerConfig.Data[".dockerconfigjson"] = dockerConfigData - return secretDockerConfig, nil + return secretDockerConfig +} + +// GenerateGitPullCreds generates a secret containing the git credentials. +func (c *Cluster) GenerateGitPullCreds(namespace, name string, gitServerInfo types.GitServerInfo) *corev1.Secret { + message.Debugf("k8s.GenerateGitPullCreds(%s, %s, gitServerInfo)", namespace, name) + + gitServerSecret := c.GenerateSecret(namespace, name, corev1.SecretTypeOpaque) + gitServerSecret.StringData = map[string]string{ + "username": gitServerInfo.PullUsername, + "password": gitServerInfo.PullPassword, + } + + return gitServerSecret +} + +// UpdateZarfManagedImageSecrets updates all Zarf-managed image secrets in all namespaces based on state +func (c *Cluster) UpdateZarfManagedImageSecrets(state *types.ZarfState) { + spinner := message.NewProgressSpinner("Updating existing Zarf-managed image secrets") + defer spinner.Stop() + + if namespaces, err := c.GetNamespaces(); err != nil { + spinner.Errorf(err, "Unable to get k8s namespaces") + } else { + // Update all image pull secrets + for _, namespace := range namespaces.Items { + currentRegistrySecret, err := c.GetSecret(namespace.Name, config.ZarfImagePullSecretName) + if err != nil { + continue + } + + // Check if this is a Zarf managed secret or is in a namespace the Zarf agent will take action in + if currentRegistrySecret.Labels[config.ZarfManagedByLabel] == "zarf" || + (namespace.Labels[agentLabel] != "skip" && namespace.Labels[agentLabel] != "ignore") { + spinner.Updatef("Updating existing Zarf-managed image secret for namespace: '%s'", namespace.Name) + + // Create the secret + newRegistrySecret := c.GenerateRegistryPullCreds(namespace.Name, config.ZarfImagePullSecretName, state.RegistryInfo) + if !reflect.DeepEqual(currentRegistrySecret.Data, newRegistrySecret.Data) { + // Create or update the zarf registry secret + if err := c.CreateOrUpdateSecret(newRegistrySecret); err != nil { + message.WarnErrorf(err, "Problem creating registry secret for the %s namespace", namespace.Name) + } + } + } + } + spinner.Success() + } +} + +// UpdateZarfManagedGitSecrets updates all Zarf-managed git secrets in all namespaces based on state +func (c *Cluster) UpdateZarfManagedGitSecrets(state *types.ZarfState) { + spinner := message.NewProgressSpinner("Updating existing Zarf-managed git secrets") + defer spinner.Stop() + + if namespaces, err := c.GetNamespaces(); err != nil { + spinner.Errorf(err, "Unable to get k8s namespaces") + } else { + // Update all git pull secrets + for _, namespace := range namespaces.Items { + currentGitSecret, err := c.GetSecret(namespace.Name, config.ZarfGitServerSecretName) + if err != nil { + continue + } + + // Check if this is a Zarf managed secret or is in a namespace the Zarf agent will take action in + if currentGitSecret.Labels[config.ZarfManagedByLabel] == "zarf" || + (namespace.Labels[agentLabel] != "skip" && namespace.Labels[agentLabel] != "ignore") { + spinner.Updatef("Updating existing Zarf-managed git secret for namespace: '%s'", namespace.Name) + + // Create the secret + newGitSecret := c.GenerateGitPullCreds(namespace.Name, config.ZarfGitServerSecretName, state.GitServer) + if !reflect.DeepEqual(currentGitSecret.StringData, newGitSecret.StringData) { + // Create or update the zarf git secret + if err := c.CreateOrUpdateSecret(newGitSecret); err != nil { + message.WarnErrorf(err, "Problem creating git server secret for the %s namespace", namespace.Name) + } + } + } + } + spinner.Success() + } } diff --git a/src/internal/cluster/state.go b/src/internal/cluster/state.go index 94c5f4ac66..a18745b37a 100644 --- a/src/internal/cluster/state.go +++ b/src/internal/cluster/state.go @@ -16,6 +16,7 @@ import ( "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/pkg/pki" "github.com/defenseunicorns/zarf/src/pkg/utils" + "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -112,6 +113,23 @@ func (c *Cluster) InitZarfState(initOptions types.ZarfInitOptions) error { if _, err := c.WaitForServiceAccount(ZarfNamespaceName, "default", 2*time.Minute); err != nil { return fmt.Errorf("unable get default Zarf service account: %w", err) } + + state.GitServer = c.fillInEmptyGitServerValues(initOptions.GitServer) + state.RegistryInfo = c.fillInEmptyContainerRegistryValues(initOptions.RegistryInfo) + state.ArtifactServer = c.fillInEmptyArtifactServerValues(initOptions.ArtifactServer) + } else { + if helpers.IsNotZeroAndNotEqual(initOptions.GitServer, state.GitServer) { + message.Warn("Detected a change in Git Server init options on a re-init. Ignoring... To update run:") + message.ZarfCommand("tools update-creds git") + } + if helpers.IsNotZeroAndNotEqual(initOptions.RegistryInfo, state.RegistryInfo) { + message.Warn("Detected a change in Image Registry init options on a re-init. Ignoring... To update run:") + message.ZarfCommand("tools update-creds registry") + } + if helpers.IsNotZeroAndNotEqual(initOptions.ArtifactServer, state.ArtifactServer) { + message.Warn("Detected a change in Artifact Server init options on a re-init. Ignoring... To update run:") + message.ZarfCommand("tools update-creds artifact") + } } if clusterArch != state.Architecture { @@ -133,10 +151,6 @@ func (c *Cluster) InitZarfState(initOptions types.ZarfInitOptions) error { state.StorageClass = initOptions.StorageClass } - state.GitServer = c.fillInEmptyGitServerValues(initOptions.GitServer) - state.RegistryInfo = c.fillInEmptyContainerRegistryValues(initOptions.RegistryInfo) - state.ArtifactServer = c.fillInEmptyArtifactServerValues(initOptions.ArtifactServer) - spinner.Success() // Save the state back to K8s @@ -238,6 +252,64 @@ func (c *Cluster) SaveZarfState(state *types.ZarfState) error { return nil } +// MergeZarfState merges init options for provided services into the provided state to create a new state struct +func (c *Cluster) MergeZarfState(oldState *types.ZarfState, initOptions types.ZarfInitOptions, services []string) *types.ZarfState { + newState := *oldState + + if helpers.SliceContains(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", config.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) + } + if newState.RegistryInfo.PullPassword == oldState.RegistryInfo.PullPassword && oldState.RegistryInfo.InternalRegistry { + newState.RegistryInfo.PullPassword = utils.RandomString(config.ZarfGeneratedPasswordLen) + } + } + if helpers.SliceContains(services, message.GitKey) { + newState.GitServer = helpers.MergeNonZero(newState.GitServer, initOptions.GitServer) + + // Set the state of the internal git server if it has changed + if newState.GitServer.Address == config.ZarfInClusterGitServiceURL { + newState.GitServer.InternalServer = true + } else { + newState.GitServer.InternalServer = false + } + + // Set the new passwords if they should be autogenerated + if newState.GitServer.PushPassword == oldState.GitServer.PushPassword && oldState.GitServer.InternalServer { + newState.GitServer.PushPassword = utils.RandomString(config.ZarfGeneratedPasswordLen) + } + if newState.GitServer.PullPassword == oldState.GitServer.PullPassword && oldState.GitServer.InternalServer { + newState.GitServer.PullPassword = utils.RandomString(config.ZarfGeneratedPasswordLen) + } + } + if helpers.SliceContains(services, message.ArtifactKey) { + newState.ArtifactServer = helpers.MergeNonZero(newState.ArtifactServer, initOptions.ArtifactServer) + + // Set the state of the internal artifact server if it has changed + if newState.ArtifactServer.Address == config.ZarfInClusterArtifactServiceURL { + newState.ArtifactServer.InternalServer = true + } else { + newState.ArtifactServer.InternalServer = false + } + + // Set an empty token if it should be autogenerated + if newState.ArtifactServer.PushToken == oldState.ArtifactServer.PushToken && oldState.ArtifactServer.InternalServer { + newState.ArtifactServer.PushToken = "" + } + } + + return &newState +} + func (c *Cluster) fillInEmptyContainerRegistryValues(containerRegistry types.RegistryInfo) types.RegistryInfo { // Set default NodePort if none was provided if containerRegistry.NodePort == 0 { diff --git a/src/internal/cluster/zarf.go b/src/internal/cluster/zarf.go index 925107864c..a37e04ca66 100644 --- a/src/internal/cluster/zarf.go +++ b/src/internal/cluster/zarf.go @@ -86,14 +86,12 @@ func (c *Cluster) StripZarfLabelsAndSecretsFromNamespaces() { } } - for _, namespace := range namespaces.Items { - spinner.Updatef("Removing Zarf secrets for namespace %s", namespace.Name) - err := c.Clientset.CoreV1(). - Secrets(namespace.Name). - DeleteCollection(context.TODO(), deleteOptions, listOptions) - if err != nil { - spinner.Errorf(err, "Unable to delete secrets from namespace %s", namespace.Name) - } + spinner.Updatef("Removing Zarf secrets for namespace %s", namespace.Name) + err := c.Clientset.CoreV1(). + Secrets(namespace.Name). + DeleteCollection(context.TODO(), deleteOptions, listOptions) + if err != nil { + spinner.Errorf(err, "Unable to delete secrets from namespace %s", namespace.Name) } } } diff --git a/src/internal/packager/helm/chart.go b/src/internal/packager/helm/chart.go index 1e1e36266c..8921ce5ad5 100644 --- a/src/internal/packager/helm/chart.go +++ b/src/internal/packager/helm/chart.go @@ -275,6 +275,62 @@ func (h *Helm) RemoveChart(namespace string, name string, spinner *message.Spinn return err } +// UpdateReleaseValues updates values for a given chart release +// (note: this only works on single-deep charts, charts with dependencies (like loki-stack) will not work) +func (h *Helm) UpdateReleaseValues(updatedValues map[string]interface{}) error { + spinner := message.NewProgressSpinner("Updating values for helm release %s", h.ReleaseName) + defer spinner.Stop() + + err := h.createActionConfig(h.Chart.Namespace, spinner) + if err != nil { + return fmt.Errorf("unable to initialize the K8s client: %w", err) + } + + postRender, err := h.newRenderer() + if err != nil { + return fmt.Errorf("unable to create helm renderer: %w", err) + } + + histClient := action.NewHistory(h.actionConfig) + histClient.Max = 1 + releases, histErr := histClient.Run(h.ReleaseName) + if histErr == nil && len(releases) > 0 { + lastRelease := releases[len(releases)-1] + + // Setup a new upgrade action + client := action.NewUpgrade(h.actionConfig) + + // Let each chart run for the default timeout. + client.Timeout = defaultClientTimeout + + client.SkipCRDs = true + + // Namespace must be specified. + client.Namespace = h.Chart.Namespace + + // Post-processing our manifests for reasons.... + client.PostRenderer = postRender + + // Set reuse values to only override the values we are explicitly given + client.ReuseValues = true + + // Wait for the update operation to successfully complete + client.Wait = true + + // Perform the loadedChart upgrade. + _, err = client.Run(h.ReleaseName, lastRelease.Chart, updatedValues) + if err != nil { + return err + } + + spinner.Success() + + return nil + } + + return fmt.Errorf("unable to find the %s helm release", h.ReleaseName) +} + func (h *Helm) installChart(postRender *renderer) (*release.Release, error) { // Bind the helm action. client := action.NewInstall(h.actionConfig) @@ -420,7 +476,7 @@ func (h *Helm) migrateDeprecatedAPIs(latestRelease *release.Release) error { return fmt.Errorf("failed to unmarshal manifest: %#v", err) } - rawData, manifestModified, err := h.Cluster.HandleDeprecations(rawData, *kubeGitVersion) + rawData, manifestModified, _ := h.Cluster.HandleDeprecations(rawData, *kubeGitVersion) manifestContent, err := yaml.Marshal(rawData) if err != nil { return fmt.Errorf("failed to marshal raw manifest after deprecation check: %#v", err) diff --git a/src/internal/packager/helm/post-render.go b/src/internal/packager/helm/post-render.go index e42cff3a13..31e1e866e8 100644 --- a/src/internal/packager/helm/post-render.go +++ b/src/internal/packager/helm/post-render.go @@ -203,32 +203,24 @@ func (r *renderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) { } // Create the secret - validSecret, err := c.GenerateRegistryPullCreds(name, config.ZarfImagePullSecretName) - if err != nil { - return nil, fmt.Errorf("unable to generate the registry pull secret for namespace %s", name) - } + validRegistrySecret := c.GenerateRegistryPullCreds(name, config.ZarfImagePullSecretName, r.options.Cfg.State.RegistryInfo) // Try to get a valid existing secret - currentSecret, _ := c.GetSecret(name, config.ZarfImagePullSecretName) - if currentSecret.Name != config.ZarfImagePullSecretName || !reflect.DeepEqual(currentSecret.Data, validSecret.Data) { - // Create or update the missing zarf registry secret - if err := c.CreateOrUpdateSecret(validSecret); err != nil { + currentRegistrySecret, _ := c.GetSecret(name, config.ZarfImagePullSecretName) + if currentRegistrySecret.Name != config.ZarfImagePullSecretName || !reflect.DeepEqual(currentRegistrySecret.Data, validRegistrySecret.Data) { + // Create or update the zarf registry secret + if err := c.CreateOrUpdateSecret(validRegistrySecret); err != nil { message.WarnErrorf(err, "Problem creating registry secret for the %s namespace", name) } // Generate the git server secret - gitServerSecret := c.GenerateSecret(name, config.ZarfGitServerSecretName, corev1.SecretTypeOpaque) - gitServerSecret.StringData = map[string]string{ - "username": r.options.Cfg.State.GitServer.PullUsername, - "password": r.options.Cfg.State.GitServer.PullPassword, - } + gitServerSecret := c.GenerateGitPullCreds(name, config.ZarfGitServerSecretName, r.options.Cfg.State.GitServer) - // Create or update the git server secret + // Create or update the zarf git server secret if err := c.CreateOrUpdateSecret(gitServerSecret); err != nil { message.WarnErrorf(err, "Problem creating git server secret for the %s namespace", name) } } - } // Send the bytes back to helm diff --git a/src/internal/packager/helm/zarf.go b/src/internal/packager/helm/zarf.go new file mode 100644 index 0000000000..8722b39f47 --- /dev/null +++ b/src/internal/packager/helm/zarf.go @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +// Package helm contains operations for working with helm charts. +package helm + +import ( + "fmt" + + "github.com/defenseunicorns/zarf/src/internal/packager/git" + "github.com/defenseunicorns/zarf/src/pkg/utils" + "github.com/defenseunicorns/zarf/src/types" +) + +// UpdateZarfRegistryValues updates the Zarf registry deployment with the new state values +func (h *Helm) UpdateZarfRegistryValues() error { + pushUser, err := utils.GetHtpasswdString(h.Cfg.State.RegistryInfo.PushUsername, h.Cfg.State.RegistryInfo.PushPassword) + if err != nil { + return fmt.Errorf("error generating htpasswd string: %w", err) + } + + pullUser, err := utils.GetHtpasswdString(h.Cfg.State.RegistryInfo.PullUsername, h.Cfg.State.RegistryInfo.PullPassword) + if err != nil { + return fmt.Errorf("error generating htpasswd string: %w", err) + } + + registryValues := map[string]interface{}{ + "secrets": map[string]interface{}{ + "htpasswd": fmt.Sprintf("%s\n%s", pushUser, pullUser), + }, + } + + h.Chart = types.ZarfChart{ + Namespace: "zarf", + } + h.ReleaseName = "zarf-docker-registry" + + err = h.UpdateReleaseValues(registryValues) + if err != nil { + return fmt.Errorf("error updating the release values: %w", err) + } + + return nil +} + +// UpdateZarfGiteaValues updates the Zarf git server deployment with the new state values +func (h *Helm) UpdateZarfGiteaValues() error { + giteaValues := map[string]interface{}{ + "gitea": map[string]interface{}{ + "admin": map[string]interface{}{ + "username": h.Cfg.State.GitServer.PushUsername, + "password": h.Cfg.State.GitServer.PushPassword, + }, + }, + } + + h.Chart = types.ZarfChart{ + Namespace: "zarf", + } + h.ReleaseName = "zarf-gitea" + + err := h.UpdateReleaseValues(giteaValues) + if err != nil { + return fmt.Errorf("error updating the release values: %w", err) + } + + g := git.New(h.Cfg.State.GitServer) + err = g.CreateReadOnlyUser() + if err != nil { + return fmt.Errorf("unable to create the new Gitea read only user: %w", err) + } + + return nil +} diff --git a/src/pkg/message/credentials.go b/src/pkg/message/credentials.go new file mode 100644 index 0000000000..b1f8ccbe8d --- /dev/null +++ b/src/pkg/message/credentials.go @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +// Package message provides a rich set of functions for displaying messages to the user. +package message + +import ( + "fmt" + "os" + "strings" + + "github.com/defenseunicorns/zarf/src/config" + "github.com/defenseunicorns/zarf/src/types" + "github.com/pterm/pterm" +) + +// Common constants for printing credentials +const ( + RegistryKey = "registry" + RegistryReadKey = "registry-readonly" + GitKey = "git" + GitReadKey = "git-readonly" + ArtifactKey = "artifact" + LoggingKey = "logging" +) + +// PrintCredentialTable displays credentials in a table +func PrintCredentialTable(state *types.ZarfState, componentsToDeploy []types.DeployedComponent) { + if len(componentsToDeploy) == 0 { + componentsToDeploy = []types.DeployedComponent{{Name: "logging"}, {Name: "git-server"}} + } + + // Set output to os.Stderr to avoid creds being printed in logs + pterm.SetDefaultOutput(os.Stderr) + + pterm.Println() + loginTableHeader := pterm.TableData{ + {" Application", "Username", "Password", "Connect", "Get-Creds Key"}, + } + + loginTable := pterm.TableData{} + if state.RegistryInfo.InternalRegistry { + loginTable = append(loginTable, pterm.TableData{ + {" Registry", state.RegistryInfo.PushUsername, state.RegistryInfo.PushPassword, "zarf connect registry", RegistryKey}, + {" Registry (read-only)", state.RegistryInfo.PullUsername, state.RegistryInfo.PullPassword, "zarf connect registry", RegistryReadKey}, + }...) + } + + for _, component := range componentsToDeploy { + // Show message if including logging stack + if component.Name == "logging" { + loginTable = append(loginTable, pterm.TableData{{" Logging", config.ZarfLoggingUser, state.LoggingSecret, "zarf connect logging", LoggingKey}}...) + } + // Show message if including git-server + if component.Name == "git-server" { + loginTable = append(loginTable, pterm.TableData{ + {" Git", state.GitServer.PushUsername, state.GitServer.PushPassword, "zarf connect git", GitKey}, + {" Git (read-only)", state.GitServer.PullUsername, state.GitServer.PullPassword, "zarf connect git", GitReadKey}, + {" Artifact Token", state.ArtifactServer.PushUsername, state.ArtifactServer.PushToken, "zarf connect git", ArtifactKey}, + }...) + } + } + + if len(loginTable) > 0 { + loginTable = append(loginTableHeader, loginTable...) + _ = pterm.DefaultTable.WithHasHeader().WithData(loginTable).Render() + } + + // Restore the log file if it was specified + if !config.SkipLogFile { + UseLogFile() + } +} + +// PrintComponentCredential displays credentials for a single component +func PrintComponentCredential(state *types.ZarfState, componentName string) { + switch strings.ToLower(componentName) { + case LoggingKey: + Notef("Logging credentials (username: %s):", config.ZarfLoggingUser) + fmt.Println(state.LoggingSecret) + case GitKey: + Notef("Git Server push password (username: %s):", state.GitServer.PushUsername) + fmt.Println(state.GitServer.PushPassword) + case GitReadKey: + Notef("Git Server (read-only) password (username: %s):", state.GitServer.PullUsername) + fmt.Println(state.GitServer.PullPassword) + case ArtifactKey: + Notef("Artifact Server token (username: %s):", state.ArtifactServer.PushUsername) + fmt.Println(state.ArtifactServer.PushToken) + case RegistryKey: + Notef("Image Registry password (username: %s):", state.RegistryInfo.PushUsername) + fmt.Println(state.RegistryInfo.PushPassword) + case RegistryReadKey: + Notef("Image Registry (read-only) password (username: %s):", state.RegistryInfo.PullUsername) + fmt.Println(state.RegistryInfo.PullPassword) + default: + Warn("Unknown component: " + componentName) + } +} + +// PrintCredentialUpdates displays credentials that will be updated +func PrintCredentialUpdates(oldState *types.ZarfState, newState *types.ZarfState, services []string) { + // Set output to os.Stderr to avoid creds being printed in logs + pterm.SetDefaultOutput(os.Stderr) + + for _, service := range services { + + HorizontalRule() + + switch service { + case RegistryKey: + oR := oldState.RegistryInfo + nR := newState.RegistryInfo + Title("Registry", "the information used to interact with Zarf's container image registry") + pterm.Println() + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("URL Address"), compareStrings(oR.Address, nR.Address, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Username"), compareStrings(oR.PushUsername, nR.PushUsername, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Password"), compareStrings(oR.PushPassword, nR.PushPassword, true)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Pull Username"), compareStrings(oR.PullUsername, nR.PullUsername, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Pull Password"), compareStrings(oR.PullPassword, nR.PullPassword, true)) + case GitKey: + oG := oldState.GitServer + nG := newState.GitServer + Title("Git Server", "the information used to interact with Zarf's GitOps Git Server") + pterm.Println() + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("URL Address"), compareStrings(oG.Address, nG.Address, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Username"), compareStrings(oG.PushUsername, nG.PushUsername, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Password"), compareStrings(oG.PushPassword, nG.PushPassword, true)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Pull Username"), compareStrings(oG.PullUsername, nG.PullUsername, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Pull Password"), compareStrings(oG.PullPassword, nG.PullPassword, true)) + case ArtifactKey: + oA := oldState.ArtifactServer + nA := newState.ArtifactServer + Title("Artifact Server", "the information used to interact with Zarf's Artifact Server") + pterm.Println() + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("URL Address"), compareStrings(oA.Address, nA.Address, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Username"), compareStrings(oA.PushUsername, nA.PushUsername, false)) + pterm.Printfln(" %s: %s", pterm.Bold.Sprint("Push Token"), compareStrings(oA.PushToken, nA.PushToken, true)) + } + } + + pterm.Println() + + // Restore the log file if it was specified + if !config.SkipLogFile { + UseLogFile() + } +} + +func compareStrings(old string, new string, secret bool) string { + if new == old { + if secret { + return "**sanitized** (unchanged)" + } + return fmt.Sprintf("%s (unchanged)", old) + } + if secret { + return fmt.Sprintf("%s -> %s", pterm.FgRed.Sprint("**existing (sanitized)**"), pterm.FgGreen.Sprint("**replacement (sanitized)**")) + } + return fmt.Sprintf("%s -> %s", pterm.FgRed.Sprint(old), pterm.FgGreen.Sprint(new)) +} diff --git a/src/pkg/message/message.go b/src/pkg/message/message.go index 48bd3c33f2..32151f7a68 100644 --- a/src/pkg/message/message.go +++ b/src/pkg/message/message.go @@ -45,11 +45,13 @@ var RuleLine = strings.Repeat("━", TermWidth) // LogWriter is the stream to write logs to. var LogWriter io.Writer = os.Stderr +// logLevel holds the pterm compatible log level integer var logLevel = InfoLevel -// Write logs to stderr and a buffer for logFile generation. +// logFile acts as a buffer for logFile generation var logFile *os.File +// useLogFile controls whether to use the log file or not var useLogFile bool // DebugWriter represents a writer interface that writes to message.Debug diff --git a/src/pkg/packager/common_test.go b/src/pkg/packager/common_test.go index 5b40c94caa..9665864b15 100644 --- a/src/pkg/packager/common_test.go +++ b/src/pkg/packager/common_test.go @@ -181,11 +181,7 @@ func TestValidateLastNonBreakingVersion(t *testing.T) { } for _, testCase := range testCases { - testCase := testCase - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - config.CLIVersion = testCase.cliVersion p := &Packager{ diff --git a/src/pkg/packager/compose.go b/src/pkg/packager/compose.go index 8264596c0a..c325e0d083 100644 --- a/src/pkg/packager/compose.go +++ b/src/pkg/packager/compose.go @@ -266,9 +266,9 @@ func (p *Packager) fixComposedFilepaths(pathAncestry string, child types.ZarfCom child.Actions.OnCreate.Defaults.Dir = composedDefaultDir } - if child.CosignKeyPath != "" { - composed := p.getComposedFilePath(pathAncestry, child.CosignKeyPath) - child.CosignKeyPath = composed + if child.DeprecatedCosignKeyPath != "" { + composed := p.getComposedFilePath(pathAncestry, child.DeprecatedCosignKeyPath) + child.DeprecatedCosignKeyPath = composed } child = p.composeExtensions(pathAncestry, child) @@ -300,8 +300,8 @@ func (p *Packager) mergeComponentOverrides(target *types.ZarfComponent, override } // Override cosign key path if it was provided. - if override.CosignKeyPath != "" { - target.CosignKeyPath = override.CosignKeyPath + if override.DeprecatedCosignKeyPath != "" { + target.DeprecatedCosignKeyPath = override.DeprecatedCosignKeyPath } // Append slices where they exist. diff --git a/src/pkg/packager/create.go b/src/pkg/packager/create.go index dc96b0698e..ef1d712a00 100755 --- a/src/pkg/packager/create.go +++ b/src/pkg/packager/create.go @@ -316,13 +316,13 @@ func (p *Packager) addComponent(index int, component types.ZarfComponent, isSkel return fmt.Errorf("unable to create the component paths: %s", err.Error()) } - if isSkeleton && component.CosignKeyPath != "" { + if isSkeleton && component.DeprecatedCosignKeyPath != "" { dst := filepath.Join(componentPath.Base, "cosign.pub") - err := utils.CreatePathAndCopy(component.CosignKeyPath, dst) + err := utils.CreatePathAndCopy(component.DeprecatedCosignKeyPath, dst) if err != nil { return err } - p.cfg.Pkg.Components[index].CosignKeyPath = "cosign.pub" + p.cfg.Pkg.Components[index].DeprecatedCosignKeyPath = "cosign.pub" } onCreate := component.Actions.OnCreate @@ -365,7 +365,7 @@ func (p *Packager) addComponent(index int, component types.ZarfComponent, isSkel if isSkeleton { continue } - if err := utils.DownloadToFile(path, dst, component.CosignKeyPath); err != nil { + if err := utils.DownloadToFile(path, dst, component.DeprecatedCosignKeyPath); err != nil { return fmt.Errorf(lang.ErrDownloading, path, err.Error()) } } else { @@ -389,7 +389,7 @@ func (p *Packager) addComponent(index int, component types.ZarfComponent, isSkel if isSkeleton { continue } - if err := utils.DownloadToFile(file.Source, dst, component.CosignKeyPath); err != nil { + if err := utils.DownloadToFile(file.Source, dst, component.DeprecatedCosignKeyPath); err != nil { return fmt.Errorf(lang.ErrDownloading, file.Source, err.Error()) } } else { @@ -429,7 +429,7 @@ func (p *Packager) addComponent(index int, component types.ZarfComponent, isSkel if isSkeleton { continue } - if err := utils.DownloadToFile(data.Source, dst, component.CosignKeyPath); err != nil { + if err := utils.DownloadToFile(data.Source, dst, component.DeprecatedCosignKeyPath); err != nil { return fmt.Errorf(lang.ErrDownloading, data.Source, err.Error()) } } else { @@ -468,7 +468,7 @@ func (p *Packager) addComponent(index int, component types.ZarfComponent, isSkel if isSkeleton { continue } - if err := utils.DownloadToFile(path, dst, component.CosignKeyPath); err != nil { + if err := utils.DownloadToFile(path, dst, component.DeprecatedCosignKeyPath); err != nil { return fmt.Errorf(lang.ErrDownloading, path, err.Error()) } } else { diff --git a/src/pkg/packager/deploy.go b/src/pkg/packager/deploy.go index 7e6e452c03..00eea92d9e 100644 --- a/src/pkg/packager/deploy.go +++ b/src/pkg/packager/deploy.go @@ -584,7 +584,12 @@ func (p *Packager) printTablesForDeployment(componentsToDeploy []types.DeployedC if !p.cfg.IsInitConfig { message.PrintConnectStringTable(p.connectStrings) } else { + // Grab a fresh copy of the state (if we are able) to print the most up-to-date version of the creds + freshState, err := p.cluster.LoadZarfState() + if err != nil { + freshState = p.cfg.State + } // otherwise, print the init config connection and passwords - utils.PrintCredentialTable(p.cfg.State, componentsToDeploy) + message.PrintCredentialTable(freshState, componentsToDeploy) } } diff --git a/src/pkg/packager/deprecated/pluralize-set-variable.go b/src/pkg/packager/deprecated/pluralize-set-variable.go index 92970e1f00..9835c80a64 100644 --- a/src/pkg/packager/deprecated/pluralize-set-variable.go +++ b/src/pkg/packager/deprecated/pluralize-set-variable.go @@ -49,7 +49,7 @@ func migrateSetVariableToSetVariables(c types.ZarfComponent) (types.ZarfComponen // Leave deprecated setVariable in place, but warn users if hasSetVariable { - return c, fmt.Sprintf("Component '%s' is using setVariable in actions which will be removed in a future version of Zarf. Please migrate to the list form of setVariables.", c.Name) + return c, fmt.Sprintf("Component '%s' is using setVariable in actions which will be removed in Zarf v1.0.0. Please migrate to the list form of setVariables.", c.Name) } return c, "" diff --git a/src/pkg/packager/deprecated/scripts-to-actions.go b/src/pkg/packager/deprecated/scripts-to-actions.go index 747a8a18bb..2040e7eb90 100644 --- a/src/pkg/packager/deprecated/scripts-to-actions.go +++ b/src/pkg/packager/deprecated/scripts-to-actions.go @@ -63,7 +63,7 @@ func migrateScriptsToActions(c types.ZarfComponent) (types.ZarfComponent, string // Leave deprecated scripts in place, but warn users if hasScripts { - return c, fmt.Sprintf("Component '%s' is using scripts which will be removed in a future version of Zarf. Please migrate to actions.", c.Name) + return c, fmt.Sprintf("Component '%s' is using scripts which will be removed in Zarf v1.0.0. Please migrate to actions.", c.Name) } return c, "" diff --git a/src/pkg/packager/prepare.go b/src/pkg/packager/prepare.go index 8c60034bb5..9ef120a3da 100644 --- a/src/pkg/packager/prepare.go +++ b/src/pkg/packager/prepare.go @@ -133,7 +133,7 @@ func (p *Packager) FindImages(baseDir, repoHelmChartPath string, kubeVersionOver for idx, path := range chart.ValuesFiles { dst := helm.StandardName(componentPath.Values, chart) + "-" + strconv.Itoa(idx) if helpers.IsURL(path) { - if err := utils.DownloadToFile(path, dst, component.CosignKeyPath); err != nil { + if err := utils.DownloadToFile(path, dst, component.DeprecatedCosignKeyPath); err != nil { return nil, fmt.Errorf(lang.ErrDownloading, path, err.Error()) } } else { @@ -199,7 +199,7 @@ func (p *Packager) FindImages(baseDir, repoHelmChartPath string, kubeVersionOver if helpers.IsURL(f) { mname := fmt.Sprintf("manifest-%s-%d.yaml", manifest.Name, idx) destination := filepath.Join(componentPath.Manifests, mname) - if err := utils.DownloadToFile(f, destination, component.CosignKeyPath); err != nil { + if err := utils.DownloadToFile(f, destination, component.DeprecatedCosignKeyPath); err != nil { return nil, fmt.Errorf(lang.ErrDownloading, f, err.Error()) } f = destination diff --git a/src/pkg/packager/variables.go b/src/pkg/packager/variables.go index d3948e422a..b36379b485 100644 --- a/src/pkg/packager/variables.go +++ b/src/pkg/packager/variables.go @@ -6,13 +6,11 @@ package packager import ( "fmt" - "strings" "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/interactive" "github.com/defenseunicorns/zarf/src/pkg/utils" - "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" "github.com/defenseunicorns/zarf/src/types" ) @@ -21,9 +19,6 @@ func (p *Packager) fillActiveTemplate() error { templateMap := map[string]string{} promptAndSetTemplate := func(templatePrefix string, deprecated bool) error { - // Ensure uppercase keys - setFromCLIConfig := helpers.TransformMapKeys(p.cfg.CreateOpts.SetVariables, strings.ToUpper) - yamlTemplates, err := utils.FindYamlTemplates(&p.cfg.Pkg, templatePrefix, "###") if err != nil { return err @@ -34,14 +29,14 @@ func (p *Packager) fillActiveTemplate() error { p.warnings = append(p.warnings, fmt.Sprintf(lang.PkgValidateTemplateDeprecation, key, key, key)) } - _, present := setFromCLIConfig[key] + _, present := p.cfg.CreateOpts.SetVariables[key] if !present && !config.CommonOptions.Confirm { setVal, err := interactive.PromptVariable(types.ZarfPackageVariable{ Name: key, }) if err == nil { - setFromCLIConfig[key] = setVal + p.cfg.CreateOpts.SetVariables[key] = setVal } else { return err } @@ -50,7 +45,7 @@ func (p *Packager) fillActiveTemplate() error { } } - for key, value := range setFromCLIConfig { + for key, value := range p.cfg.CreateOpts.SetVariables { templateMap[fmt.Sprintf("%s%s###", templatePrefix, key)] = value } @@ -79,9 +74,7 @@ func (p *Packager) fillActiveTemplate() error { // setVariableMapInConfig handles setting the active variables used to template component files. func (p *Packager) setVariableMapInConfig() error { - // Ensure uppercase keys - setVariableValues := helpers.TransformMapKeys(p.cfg.PkgOpts.SetVariables, strings.ToUpper) - for name, value := range setVariableValues { + for name, value := range p.cfg.PkgOpts.SetVariables { p.setVariableInConfig(name, value, false, false, "") } diff --git a/src/pkg/utils/credentials.go b/src/pkg/utils/credentials.go deleted file mode 100644 index b526929a7e..0000000000 --- a/src/pkg/utils/credentials.go +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2021-Present The Zarf Authors - -// Package utils provides generic utility functions. -package utils - -import ( - "fmt" - "os" - "strings" - - "github.com/defenseunicorns/zarf/src/config" - "github.com/defenseunicorns/zarf/src/pkg/message" - "github.com/defenseunicorns/zarf/src/types" - "github.com/pterm/pterm" -) - -// PrintCredentialTable displays credentials in a table -func PrintCredentialTable(state *types.ZarfState, componentsToDeploy []types.DeployedComponent) { - if len(componentsToDeploy) == 0 { - componentsToDeploy = []types.DeployedComponent{{Name: "logging"}, {Name: "git-server"}} - } - - // Set output to os.Stderr to avoid creds being printed in logs - pterm.SetDefaultOutput(os.Stderr) - - pterm.Println() - loginTableHeader := pterm.TableData{ - {" Application", "Username", "Password", "Connect"}, - } - - loginTable := pterm.TableData{} - if state.RegistryInfo.InternalRegistry { - loginTable = append(loginTable, pterm.TableData{{" Registry", state.RegistryInfo.PushUsername, state.RegistryInfo.PushPassword, "zarf connect registry"}}...) - } - - for _, component := range componentsToDeploy { - // Show message if including logging stack - if component.Name == "logging" { - loginTable = append(loginTable, pterm.TableData{{" Logging", "zarf-admin", state.LoggingSecret, "zarf connect logging"}}...) - } - // Show message if including git-server - if component.Name == "git-server" { - loginTable = append(loginTable, pterm.TableData{ - {" Git", state.GitServer.PushUsername, state.GitServer.PushPassword, "zarf connect git"}, - {" Git (read-only)", state.GitServer.PullUsername, state.GitServer.PullPassword, "zarf connect git"}, - }...) - } - } - - if len(loginTable) > 0 { - loginTable = append(loginTableHeader, loginTable...) - _ = pterm.DefaultTable.WithHasHeader().WithData(loginTable).Render() - } - - // Restore the log file if it was specified - if !config.SkipLogFile { - message.UseLogFile() - } -} - -// PrintComponentCredential displays credentials for a single component -func PrintComponentCredential(state *types.ZarfState, componentName string) { - switch strings.ToLower(componentName) { - case "logging": - message.Note("Logging credentials (username: zarf-admin):") - fmt.Println(state.LoggingSecret) - case "git": - message.Note("Git Server push password (username: " + state.GitServer.PushUsername + "):") - fmt.Println(state.GitServer.PushPassword) - case "git-readonly": - message.Note("Git Server (read-only) password (username: " + state.GitServer.PullUsername + "):") - fmt.Println(state.GitServer.PullPassword) - case "registry": - message.Note("Image Registry password (username: " + state.RegistryInfo.PushUsername + "):") - fmt.Println(state.RegistryInfo.PushPassword) - default: - message.Warn("Unknown component: " + componentName) - } -} diff --git a/src/pkg/utils/helpers/misc.go b/src/pkg/utils/helpers/misc.go index f3c328630a..2f6292c419 100644 --- a/src/pkg/utils/helpers/misc.go +++ b/src/pkg/utils/helpers/misc.go @@ -6,6 +6,7 @@ package helpers import ( "fmt" + "reflect" "regexp" "time" ) @@ -109,6 +110,15 @@ func MergeMap[T any](m1, m2 map[string]T) (r map[string]T) { return r } +// TransformAndMergeMap transforms keys in both maps then merges map m2 with m1 overwriting common values with m2's values. +func TransformAndMergeMap[T any](m1, m2 map[string]T, transform func(string) string) (r map[string]T) { + mt1 := TransformMapKeys(m1, transform) + mt2 := TransformMapKeys(m2, transform) + r = MergeMap(mt1, mt2) + + return r +} + // MergeMapRecursive recursively (nestedly) merges map m2 with m1 overwriting common values with m2's values. func MergeMapRecursive(m1, m2 map[string]interface{}) (r map[string]interface{}) { r = map[string]interface{}{} @@ -159,3 +169,33 @@ func MatchRegex(regex *regexp.Regexp, str string) (func(string) string, error) { return get, nil } + +// IsNotZeroAndNotEqual is used to test if a struct has zero values or is equal values with another struct +func IsNotZeroAndNotEqual[T any](given T, equal T) bool { + givenValue := reflect.ValueOf(given) + equalValue := reflect.ValueOf(equal) + + if givenValue.NumField() != equalValue.NumField() { + return true + } + + for i := 0; i < givenValue.NumField(); i++ { + if !givenValue.Field(i).IsZero() && givenValue.Field(i).Interface() != equalValue.Field(i).Interface() { + return true + } + } + return false +} + +// MergeNonZero is used to merge non-zero overrides from one struct into another of the same type +func MergeNonZero[T any](original T, overrides T) T { + originalValue := reflect.ValueOf(original) + overridesValue := reflect.ValueOf(overrides) + + for i := 0; i < originalValue.NumField(); i++ { + if !overridesValue.Field(i).IsZero() { + originalValue.Field(i).Set(overridesValue.Field(i)) + } + } + return originalValue.Interface().(T) +} diff --git a/src/pkg/utils/network.go b/src/pkg/utils/network.go index 89d6b47a68..889964fc96 100644 --- a/src/pkg/utils/network.go +++ b/src/pkg/utils/network.go @@ -92,7 +92,7 @@ func DownloadToFile(src string, dst string, cosignKeyPath string) (err error) { if parsed.Scheme == SGETURLScheme { err = Sget(context.TODO(), src, cosignKeyPath, file) if err != nil { - return fmt.Errorf("unable to download file with sget: %s", src) + return fmt.Errorf("unable to download file with sget: %s: %w", src, err) } if err != nil { return err diff --git a/src/test/e2e/07_create_git_test.go b/src/test/e2e/07_create_git_test.go index 8d2ef68521..115558ace9 100644 --- a/src/test/e2e/07_create_git_test.go +++ b/src/test/e2e/07_create_git_test.go @@ -32,7 +32,7 @@ func TestCreateGit(t *testing.T) { "v0.0.1\n", " dragons\n* main\n") // Verify the full-repo component fallback - gitDir = fmt.Sprintf("%s/components/full-repo/repos/zarf-public-test-1651489007/.git", extractDir) + gitDir = fmt.Sprintf("%s/components/full-repo/repos/zarf-public-test-410141584/.git", extractDir) verifyGitRepo(t, gitDir, "0a6b587", "(HEAD -> main, online-upstream/main, online-upstream/HEAD)", "Adjust dragon spacing", "v0.0.1\n", " dragons\n* main\n") @@ -50,7 +50,7 @@ func TestCreateGit(t *testing.T) { "v0.0.1\n", "* zarf-ref-v0.0.1\n") // Verify specific tag component tag fallback - gitDir = fmt.Sprintf("%s/components/specific-tag/repos/zarf-public-test-308170788/.git", extractDir) + gitDir = fmt.Sprintf("%s/components/specific-tag/repos/zarf-public-test-3956869879/.git", extractDir) verifyGitRepo(t, gitDir, "5249809", "(HEAD -> zarf-ref-v0.0.1, tag: v0.0.1)", "Added README.md", "v0.0.1\n", "* zarf-ref-v0.0.1\n") @@ -62,7 +62,7 @@ func TestCreateGit(t *testing.T) { "", "* dragons\n") // Verify specific branch component fallback - gitDir = fmt.Sprintf("%s/components/specific-branch/repos/zarf-public-test-1204519508/.git", extractDir) + gitDir = fmt.Sprintf("%s/components/specific-branch/repos/zarf-public-test-3363080017/.git", extractDir) verifyGitRepo(t, gitDir, "01a2321", "(HEAD -> dragons, online-upstream/dragons)", "Explain what this repo does", "", "* dragons\n") @@ -74,7 +74,7 @@ func TestCreateGit(t *testing.T) { "v0.0.1\n", " main\n* zarf-ref-01a23218923f24194133b5eb11268cf8d73ff1bb\n") // Verify specific hash component fallback - gitDir = fmt.Sprintf("%s/components/specific-hash/repos/zarf-public-test-2793472375/.git", extractDir) + gitDir = fmt.Sprintf("%s/components/specific-hash/repos/zarf-public-test-1425142831/.git", extractDir) verifyGitRepo(t, gitDir, "01a2321", "(HEAD -> zarf-ref-01a23218923f24194133b5eb11268cf8d73ff1bb, online-upstream/dragons)", "Explain what this repo does", "v0.0.1\n", " main\n* zarf-ref-01a23218923f24194133b5eb11268cf8d73ff1bb\n") diff --git a/src/test/e2e/20_zarf_init_test.go b/src/test/e2e/20_zarf_init_test.go index a37884ed26..48df836a9c 100644 --- a/src/test/e2e/20_zarf_init_test.go +++ b/src/test/e2e/20_zarf_init_test.go @@ -63,7 +63,7 @@ func TestZarfInit(t *testing.T) { if err == nil { oldStateJSON, err := base64.StdEncoding.DecodeString(base64State) require.NoError(t, err) - err = json.Unmarshal(oldStateJSON, &oldState) + json.Unmarshal(oldStateJSON, &oldState) } // run `zarf init` diff --git a/src/test/e2e/21_connect_test.go b/src/test/e2e/21_connect_creds_test.go similarity index 67% rename from src/test/e2e/21_connect_test.go rename to src/test/e2e/21_connect_creds_test.go index adbc2cf763..bfe8f314e0 100644 --- a/src/test/e2e/21_connect_test.go +++ b/src/test/e2e/21_connect_creds_test.go @@ -6,7 +6,8 @@ package test import ( "crypto/tls" - "io/ioutil" + "fmt" + "io" "net/http" "strings" "testing" @@ -19,43 +20,16 @@ type RegistryResponse struct { Repositories []string `json:"repositories"` } -func TestConnect(t *testing.T) { +func TestConnectAndCreds(t *testing.T) { t.Log("E2E: Connect") e2e.SetupWithCluster(t) - // Make the Registry contains the images we expect - stdOut, stdErr, err := e2e.Zarf("tools", "registry", "catalog") - require.NoError(t, err, stdOut, stdErr) - registryList := strings.Split(strings.Trim(stdOut, "\n "), "\n") - - // We assert greater than or equal to since the base init has 12 images - // HOWEVER during an upgrade we could have mismatched versions/names resulting in more images - require.GreaterOrEqual(t, len(registryList), 7) - require.Contains(t, stdOut, "gitea/gitea") - - // Connect to Gitea - tunnelGit, err := cluster.NewZarfTunnel() - require.NoError(t, err) - err = tunnelGit.Connect(cluster.ZarfGit, false) - require.NoError(t, err) - defer tunnelGit.Close() - - // Make sure Gitea comes up cleanly - respGit, err := http.Get(tunnelGit.HTTPEndpoint()) - require.NoError(t, err) - require.Equal(t, 200, respGit.StatusCode) + connectToZarfServices(t) - // Connect to the Logging Stack - tunnelLog, err := cluster.NewZarfTunnel() - require.NoError(t, err) - err = tunnelLog.Connect(cluster.ZarfLogging, false) - require.NoError(t, err) - defer tunnelLog.Close() + stdOut, stdErr, err := e2e.Zarf("tools", "update-creds", "--confirm") + require.NoError(t, err, stdOut, stdErr) - // Make sure Grafana comes up cleanly - respLog, err := http.Get(tunnelLog.HTTPEndpoint()) - require.NoError(t, err) - require.Equal(t, 200, respLog.StatusCode) + connectToZarfServices(t) stdOut, stdErr, err = e2e.Zarf("package", "remove", "init", "--components=logging", "--confirm") require.NoError(t, err, stdOut, stdErr) @@ -104,7 +78,7 @@ func TestMetrics(t *testing.T) { defer resp.Body.Close() // Read the response body - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { t.Fatal(err) } @@ -113,5 +87,66 @@ func TestMetrics(t *testing.T) { require.Equal(t, true, strings.Contains(string(body), desiredString)) require.NoError(t, err, resp) require.Equal(t, 200, resp.StatusCode) +} +func connectToZarfServices(t *testing.T) { + // Make the Registry contains the images we expect + stdOut, stdErr, err := e2e.Zarf("tools", "registry", "catalog") + require.NoError(t, err, stdOut, stdErr) + registryList := strings.Split(strings.Trim(stdOut, "\n "), "\n") + + // We assert greater than or equal to since the base init has 12 images + // HOWEVER during an upgrade we could have mismatched versions/names resulting in more images + require.GreaterOrEqual(t, len(registryList), 7) + require.Contains(t, stdOut, "defenseunicorns/zarf/agent") + require.Contains(t, stdOut, "gitea/gitea") + require.Contains(t, stdOut, "grafana/grafana") + require.Contains(t, stdOut, "grafana/loki") + require.Contains(t, stdOut, "grafana/promtail") + require.Contains(t, stdOut, "kiwigrid/k8s-sidecar") + require.Contains(t, stdOut, "library/registry") + + // Get the git credentials + stdOut, stdErr, err = e2e.Zarf("tools", "get-creds", "git") + require.NoError(t, err, stdOut, stdErr) + gitPushPassword := strings.TrimSpace(stdOut) + stdOut, stdErr, err = e2e.Zarf("tools", "get-creds", "git-readonly") + require.NoError(t, err, stdOut, stdErr) + gitPullPassword := strings.TrimSpace(stdOut) + stdOut, stdErr, err = e2e.Zarf("tools", "get-creds", "artifact") + require.NoError(t, err, stdOut, stdErr) + gitArtifactToken := strings.TrimSpace(stdOut) + + // Connect to Gitea + tunnelGit, err := cluster.NewZarfTunnel() + require.NoError(t, err) + err = tunnelGit.Connect(cluster.ZarfGit, false) + require.NoError(t, err) + defer tunnelGit.Close() + + // Make sure Gitea comes up cleanly + gitPushURL := fmt.Sprintf("http://zarf-git-user:%s@%s/api/v1/user", gitPushPassword, tunnelGit.Endpoint()) + respGit, err := http.Get(gitPushURL) + require.NoError(t, err) + require.Equal(t, 200, respGit.StatusCode) + gitPullURL := fmt.Sprintf("http://zarf-git-read-user:%s@%s/api/v1/user", gitPullPassword, tunnelGit.Endpoint()) + respGit, err = http.Get(gitPullURL) + require.NoError(t, err) + require.Equal(t, 200, respGit.StatusCode) + gitArtifactURL := fmt.Sprintf("http://zarf-git-user:%s@%s/api/v1/user", gitArtifactToken, tunnelGit.Endpoint()) + respGit, err = http.Get(gitArtifactURL) + require.NoError(t, err) + require.Equal(t, 200, respGit.StatusCode) + + // Connect to the Logging Stack + tunnelLog, err := cluster.NewZarfTunnel() + require.NoError(t, err) + err = tunnelLog.Connect(cluster.ZarfLogging, false) + require.NoError(t, err) + defer tunnelLog.Close() + + // Make sure Grafana comes up cleanly + respLog, err := http.Get(tunnelLog.HTTPEndpoint()) + require.NoError(t, err) + require.Equal(t, 200, respLog.StatusCode) } diff --git a/src/test/e2e/30_config_file_test.go b/src/test/e2e/30_config_file_test.go index 4ed4b4e45a..3c8189a146 100644 --- a/src/test/e2e/30_config_file_test.go +++ b/src/test/e2e/30_config_file_test.go @@ -131,7 +131,6 @@ func configFileDefaultTests(t *testing.T) { packageDeployFlags := []string{ "deploy.components: 8d6fde37", - "deploy.sget: ee7905de", "deploy.shasum: 7606fe19", "[thing2=2b3c4d5e]", } diff --git a/src/test/e2e/51_oci_compose_test.go b/src/test/e2e/51_oci_compose_test.go index f02f96c4e0..a916d23997 100644 --- a/src/test/e2e/51_oci_compose_test.go +++ b/src/test/e2e/51_oci_compose_test.go @@ -202,8 +202,8 @@ func (suite *SkeletonSuite) verifyComponentPaths(unpackedPath string, components Values: filepath.Join(base, types.ValuesFolder), } - if isSkeleton && component.CosignKeyPath != "" { - suite.FileExists(filepath.Join(base, component.CosignKeyPath)) + if isSkeleton && component.DeprecatedCosignKeyPath != "" { + suite.FileExists(filepath.Join(base, component.DeprecatedCosignKeyPath)) } if isSkeleton && component.Extensions.BigBang != nil { diff --git a/src/test/packages/22-git-data/zarf.yaml b/src/test/packages/22-git-data/zarf.yaml index 34ff5fdb98..3ebe431ea3 100644 --- a/src/test/packages/22-git-data/zarf.yaml +++ b/src/test/packages/22-git-data/zarf.yaml @@ -11,7 +11,7 @@ components: # Do a full Git Repo Mirror - https://github.com/defenseunicorns/zarf-public-test.git # The following performs a full Git Repo Mirror forcing a fallback to host `git` - - https://racer159.visualstudio.com/zarf-public-test/_git/zarf-public-test + - https://dev.azure.com/defenseunicorns/zarf-public-test/_git/zarf-public-test # Perform a full repo mirror of a simple repository with a single branch - (this causes an "already up to date" error in go-git) - https://github.com/defenseunicorns/golang-tekton-hello-world.git @@ -23,7 +23,7 @@ components: # The following performs a refspec tag Git Repo Mirror with `go-git` - https://github.com/defenseunicorns/zarf-public-test.git@refs/tags/v0.0.1 # The following performs a tag Git Repo Mirror forcing a fallback to host `git` - - https://racer159.visualstudio.com/zarf-public-test/_git/zarf-public-test@v0.0.1 + - https://dev.azure.com/defenseunicorns/zarf-public-test/_git/zarf-public-test@v0.0.1 actions: onDeploy: before: @@ -38,7 +38,7 @@ components: # The following performs a branch Git Repo Mirror with `go-git` (internal to Zarf) - https://github.com/defenseunicorns/zarf-public-test.git@refs/heads/dragons # The following performs a branch Git Repo Mirror forcing a fallback to host `git` - - https://racer159.visualstudio.com/zarf-public-test/_git/zarf-public-test@refs/heads/dragons + - https://dev.azure.com/defenseunicorns/zarf-public-test/_git/zarf-public-test@refs/heads/dragons actions: onDeploy: before: @@ -53,7 +53,7 @@ components: # The following performs a SHA Git Repo Mirror with `go-git` (internal to Zarf) - https://github.com/defenseunicorns/zarf-public-test.git@01a23218923f24194133b5eb11268cf8d73ff1bb # The following performs a SHA Git Repo Mirror forcing a fallback to host `git` - - https://racer159.visualstudio.com/zarf-public-test/_git/zarf-public-test@01a23218923f24194133b5eb11268cf8d73ff1bb + - https://dev.azure.com/defenseunicorns/zarf-public-test/_git/zarf-public-test@01a23218923f24194133b5eb11268cf8d73ff1bb actions: onDeploy: before: diff --git a/src/test/upgrade/previously_built_test.go b/src/test/upgrade/previously_built_test.go index e823b7a8ce..179bad0432 100644 --- a/src/test/upgrade/previously_built_test.go +++ b/src/test/upgrade/previously_built_test.go @@ -35,6 +35,15 @@ func TestPreviouslyBuiltZarfPackage(t *testing.T) { kubectlOut, _, _ = kubectl("-n=podinfo-upgrade", "get", "deployment", "podinfo-upgrade", "-o=jsonpath={.metadata.labels}}") require.Contains(t, kubectlOut, "6.3.3") + // Verify that the private-registry secret and private-git-server secret in the podinfo-upgrade namespace are the same after re-init + // This tests that `zarf tools update-creds` successfully updated the other namespace + zarfRegistrySecret, _, _ := kubectl("-n=zarf", "get", "secret", "private-registry", "-o", "jsonpath={.data}") + podinfoRegistrySecret, _, _ := kubectl("-n=podinfo-upgrade", "get", "secret", "private-registry", "-o", "jsonpath={.data}") + require.Equal(t, zarfRegistrySecret, podinfoRegistrySecret, "the zarf registry secret and podinfo-upgrade registry secret did not match") + zarfGitServerSecret, _, _ := kubectl("-n=zarf", "get", "secret", "private-git-server", "-o", "jsonpath={.data}") + podinfoGitServerSecret, _, _ := kubectl("-n=podinfo-upgrade", "get", "secret", "private-git-server", "-o", "jsonpath={.data}") + require.Equal(t, zarfGitServerSecret, podinfoGitServerSecret, "the zarf git server secret and podinfo-upgrade git server secret did not match") + // We also expect a 6.3.4 package to have been previously built previouslyBuiltPackage := "../../../zarf-package-test-upgrade-package-amd64-6.3.4.tar.zst" diff --git a/src/test/zarf-config-test.toml b/src/test/zarf-config-test.toml index a81de98fe9..3585a45f3f 100644 --- a/src/test/zarf-config-test.toml +++ b/src/test/zarf-config-test.toml @@ -37,7 +37,6 @@ thing1 = '1a2b3c4d' [package.deploy] components = 'deploy.components: 8d6fde37' -sget = 'deploy.sget: ee7905de' shasum = 'deploy.shasum: 7606fe19' [package.deploy.set] diff --git a/src/types/component.go b/src/types/component.go index 62e399e9b0..b6297925e5 100644 --- a/src/types/component.go +++ b/src/types/component.go @@ -31,14 +31,14 @@ type ZarfComponent struct { // Note: ignores default and required flags Group string `json:"group,omitempty" jsonschema:"description=Create a user selector field based on all components in the same group"` - //Path to cosign publickey for signed online resources - CosignKeyPath string `json:"cosignKeyPath,omitempty" jsonschema:"description=[Deprecated] Specify a path to a public key to validate signed online resources"` + // (Deprecated) Path to cosign public key for signed online resources + DeprecatedCosignKeyPath string `json:"cosignKeyPath,omitempty" jsonschema:"description=[Deprecated] Specify a path to a public key to validate signed online resources. This will be removed in Zarf v1.0.0.,deprecated=true"` // Import refers to another zarf.yaml package component. Import ZarfComponentImport `json:"import,omitempty" jsonschema:"description=Import a component from another Zarf package"` // (Deprecated) DeprecatedScripts are custom commands that run before or after package deployment - DeprecatedScripts DeprecatedZarfComponentScripts `json:"scripts,omitempty" jsonschema:"description=[Deprecated] (replaced by actions) Custom commands to run before or after package deployment,deprecated=true"` + DeprecatedScripts DeprecatedZarfComponentScripts `json:"scripts,omitempty" jsonschema:"description=[Deprecated] (replaced by actions) Custom commands to run before or after package deployment. This will be removed in Zarf v1.0.0.,deprecated=true"` // Files are files to place on disk during deploy Files []ZarfFile `json:"files,omitempty" jsonschema:"description=Files or folders to place on disk during package deployment"` @@ -119,7 +119,7 @@ type DeprecatedZarfComponentScripts struct { After []string `json:"after,omitempty" jsonschema:"description=Scripts to run after the component successfully deploys"` } -// ZarfComponentActions are actionsets that map to different zarf package operations +// ZarfComponentActions are ActionSets that map to different zarf package operations type ZarfComponentActions struct { OnCreate ZarfComponentActionSet `json:"onCreate,omitempty" jsonschema:"description=Actions to run during package creation"` OnDeploy ZarfComponentActionSet `json:"onDeploy,omitempty" jsonschema:"description=Actions to run during package deployment"` @@ -161,7 +161,7 @@ type ZarfComponentAction struct { Env []string `json:"env,omitempty" jsonschema:"description=Additional environment variables to set for the command"` Cmd string `json:"cmd,omitempty" jsonschema:"description=The command to run. Must specify either cmd or wait for the action to do anything."` Shell *ZarfComponentActionShell `json:"shell,omitempty" jsonschema:"description=(cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems"` - DeprecatedSetVariable string `json:"setVariable,omitempty" jsonschema:"description=[Deprecated] (replaced by setVariables) (onDeploy/cmd only) The name of a variable to update with the output of the command. This variable will be available to all remaining actions and components in the package.,pattern=^[A-Z0-9_]+$"` + DeprecatedSetVariable string `json:"setVariable,omitempty" jsonschema:"description=[Deprecated] (replaced by setVariables) (onDeploy/cmd only) The name of a variable to update with the output of the command. This variable will be available to all remaining actions and components in the package. This will be removed in Zarf v1.0.0,pattern=^[A-Z0-9_]+$"` SetVariables []ZarfComponentActionSetVariable `json:"setVariables,omitempty" jsonschema:"description=(onDeploy/cmd only) An array of variables to update with the output of the command. These variables will be available to all remaining actions and components in the package."` Description string `json:"description,omitempty" jsonschema:"description=Description of the action to be displayed during package execution instead of the command"` Wait *ZarfComponentActionWait `json:"wait,omitempty" jsonschema:"description=Wait for a condition to be met before continuing. Must specify either cmd or wait for the action. See the 'zarf tools wait-for' command for more info."` diff --git a/src/ui/lib/api-types.ts b/src/ui/lib/api-types.ts index 6d52e7c2bc..56780c6c9e 100644 --- a/src/ui/lib/api-types.ts +++ b/src/ui/lib/api-types.ts @@ -285,7 +285,8 @@ export interface ZarfComponent { */ charts?: ZarfChart[]; /** - * [Deprecated] Specify a path to a public key to validate signed online resources + * [Deprecated] Specify a path to a public key to validate signed online resources. This + * will be removed in Zarf v1.0.0. */ cosignKeyPath?: string; /** @@ -342,7 +343,7 @@ export interface ZarfComponent { required?: boolean; /** * [Deprecated] (replaced by actions) Custom commands to run before or after package - * deployment + * deployment. This will be removed in Zarf v1.0.0. */ scripts?: DeprecatedZarfComponentScripts; } @@ -427,7 +428,7 @@ export interface ZarfComponentAction { /** * [Deprecated] (replaced by setVariables) (onDeploy/cmd only) The name of a variable to * update with the output of the command. This variable will be available to all remaining - * actions and components in the package. + * actions and components in the package. This will be removed in Zarf v1.0.0 */ setVariable?: string; /** @@ -830,7 +831,7 @@ export enum LocalOS { /** * [Deprecated] (replaced by actions) Custom commands to run before or after package - * deployment + * deployment. This will be removed in Zarf v1.0.0. */ export interface DeprecatedZarfComponentScripts { /** diff --git a/zarf.schema.json b/zarf.schema.json index cf6972d35b..d05e36f41b 100644 --- a/zarf.schema.json +++ b/zarf.schema.json @@ -254,7 +254,7 @@ }, "cosignKeyPath": { "type": "string", - "description": "[Deprecated] Specify a path to a public key to validate signed online resources" + "description": "[Deprecated] Specify a path to a public key to validate signed online resources. This will be removed in Zarf v1.0.0." }, "import": { "$schema": "http://json-schema.org/draft-04/schema#", @@ -264,7 +264,7 @@ "scripts": { "$schema": "http://json-schema.org/draft-04/schema#", "$ref": "#/definitions/DeprecatedZarfComponentScripts", - "description": "[Deprecated] (replaced by actions) Custom commands to run before or after package deployment" + "description": "[Deprecated] (replaced by actions) Custom commands to run before or after package deployment. This will be removed in Zarf v1.0.0." }, "files": { "items": { @@ -362,7 +362,7 @@ "setVariable": { "pattern": "^[A-Z0-9_]+$", "type": "string", - "description": "[Deprecated] (replaced by setVariables) (onDeploy/cmd only) The name of a variable to update with the output of the command. This variable will be available to all remaining actions and components in the package." + "description": "[Deprecated] (replaced by setVariables) (onDeploy/cmd only) The name of a variable to update with the output of the command. This variable will be available to all remaining actions and components in the package. This will be removed in Zarf v1.0.0" }, "setVariables": { "items": {