diff --git a/.github/workflows/parallel-tests.yaml b/.github/workflows/parallel-tests.yaml index 646d15c6..ae751420 100644 --- a/.github/workflows/parallel-tests.yaml +++ b/.github/workflows/parallel-tests.yaml @@ -55,7 +55,7 @@ jobs: with: suffix: ${{ matrix.test }} - test-dev-and-state: + test-dev: runs-on: ubuntu-latest needs: build steps: @@ -67,9 +67,9 @@ jobs: - name: setup-using-previous-job uses: ./.github/actions/setup-from-previous - - name: Run e2e dev and state tests + - name: Run e2e dev run: | - build/uds run test:dev-and-state + build/uds run test:dev - name: Save logs if: always() diff --git a/design-docs/0001-bundle-state.md b/design-docs/0001-bundle-state.md index bed0361b..c56a49e0 100644 --- a/design-docs/0001-bundle-state.md +++ b/design-docs/0001-bundle-state.md @@ -1,5 +1,13 @@ # Bundle State +### Update September 24th, 2024 + +After initial rollout we decided to remove state due to unforeseen issues. For +posterity this document is left intact, but the state feature described has been +removed. We will very likely revisit the idea in the future. + +------ + ## Context The following 2 issues provide context driving the need for a UDS state tracking mechanism: diff --git a/docs/command-reference/uds_deploy.md b/docs/command-reference/uds_deploy.md index b5e605d7..1c786231 100644 --- a/docs/command-reference/uds_deploy.md +++ b/docs/command-reference/uds_deploy.md @@ -17,7 +17,6 @@ uds deploy [BUNDLE_TARBALL|OCI_REF] [flags] -c, --confirm Confirms bundle deployment without prompting. ONLY use with bundles you trust -h, --help help for deploy -p, --packages stringArray Specify which zarf packages you would like to deploy from the bundle. By default all zarf packages in the bundle are deployed. - -P, --prune Forces the removal of packages that are no longer in the bundle -r, --resume Only deploys packages from the bundle which haven't already been deployed --retries int Specify the number of retries for package deployments (applies to all pkgs in a bundle) (default 3) --set stringToString Specify deployment variables to set on the command line (KEY=value) (default []) diff --git a/docs/command-reference/uds_dev_deploy.md b/docs/command-reference/uds_dev_deploy.md index 9f17e967..c5a7defe 100644 --- a/docs/command-reference/uds_dev_deploy.md +++ b/docs/command-reference/uds_dev_deploy.md @@ -22,7 +22,6 @@ uds dev deploy [BUNDLE_DIR|OCI_REF] [flags] --force-create [beta] For local bundles with local packages, specify whether to create a zarf package even if it already exists. -h, --help help for deploy -p, --packages stringArray Specify which zarf packages you would like to deploy from the bundle. By default all zarf packages in the bundle are deployed. - -P, --prune Forces the removal of packages that are no longer in the bundle -r, --ref stringToString Specify which zarf package ref you want to deploy. By default the ref set in the bundle yaml is used. (default []) --set stringToString Specify deployment variables to set on the command line (KEY=value) (default []) ``` diff --git a/docs/quickstart-and-usage.md b/docs/quickstart-and-usage.md index 132dbbae..ef5b6b07 100644 --- a/docs/quickstart-and-usage.md +++ b/docs/quickstart-and-usage.md @@ -101,16 +101,6 @@ As an example: `uds deploy uds-bundle-.tar.zst --resume` In the process of upgrading bundles, it's common to swap or remove packages from a `uds-bundle.yaml`. These packages can become `unreferenced`, meaning that they are still deployed to the cluster, but are no longer referenced by a bundle. To remove these packages from the cluster, you can use the `--prune` flag when deploying a bundle. -```bash -uds deploy --prune -``` - -This command will prompt and inform the user of any packages that are unreferenced and will be pruned from the cluster. To skip the prompt, you can add the `--confirm` flag to the command. - -{{% alert-note %}} -Currently, pruning will occur _after_ the bundle has been deployed. -{{% /alert-note %}} - #### Pre-Deploy View When `uds deploy` is executed, the bundle's metadata, along with a list of its packages and each package's overrides and Zarf variables, will be outputted to the terminal. Unlike [`inspect --list-variables`](#viewing-variables), this output will show the value set for each override or Zarf variable. Overrides and variables that have not been set will not be shown in the output. diff --git a/hack/validate-uds-core-state.sh b/hack/validate-uds-core-state.sh deleted file mode 100755 index ba7cf083..00000000 --- a/hack/validate-uds-core-state.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env sh - -# This script runs as part of the nightly UDS Core smoke tests and validates the state of the UDS Core bundle after deployment - -set -e - -echo "Validating UDS Core state" - -get_secret_data() { - kubectl get secret uds-bundle-k3d-core-slim-dev -n uds -o json | \ - jq -r '.data.data' | \ - base64 -d -} - -secret_data=$(get_secret_data) || { - echo "Error: Failed to retrieve secret data" - exit 1 -} - -echo "State data:" -echo "$secret_data" | jq '.' || { - echo "Error: Failed to parse secret data as JSON" - exit 1 -} - -all_statuses_success=$(echo "$secret_data" | \ - jq -r ' - if .status == "success" and (.packages | all(.status == "success")) then - "true" - else - "false" - end - ') || { - echo "Error: Failed to check statuses" - exit 1 -} - -if [ "$all_statuses_success" != "true" ]; then - echo "Error: Not all statuses are successful" - echo "Issues:" - echo "$secret_data" | jq -r ' - [ - if .status != "success" then "Top-level status is not success" else empty end, - (.packages[] | select(.status != "success") | "Package \(.name) status is not success") - ] | .[] - ' || echo "Error: Failed to list issues" - exit 1 -else - echo "All statuses are successful" -fi diff --git a/src/cmd/dev.go b/src/cmd/dev.go index 5158fb96..bf43df03 100644 --- a/src/cmd/dev.go +++ b/src/cmd/dev.go @@ -150,5 +150,4 @@ func init() { devDeployCmd.Flags().StringVarP(&bundleCfg.DevDeployOpts.FlavorInput, "flavor", "f", "", lang.CmdBundleCreateFlagFlavor) devDeployCmd.Flags().BoolVar(&bundleCfg.DevDeployOpts.ForceCreate, "force-create", false, lang.CmdBundleCreateForceCreate) devDeployCmd.Flags().StringToStringVar(&bundleCfg.DeployOpts.SetVariables, "set", nil, lang.CmdBundleDeployFlagSet) - devDeployCmd.Flags().BoolVarP(&bundleCfg.DeployOpts.Prune, "prune", "P", false, lang.CmdBundleDeployFlagPrune) } diff --git a/src/cmd/uds.go b/src/cmd/uds.go index 0d90654a..66aa2938 100644 --- a/src/cmd/uds.go +++ b/src/cmd/uds.go @@ -241,7 +241,6 @@ func init() { deployCmd.Flags().StringArrayVarP(&bundleCfg.DeployOpts.Packages, "packages", "p", []string{}, lang.CmdBundleDeployFlagPackages) deployCmd.Flags().BoolVarP(&bundleCfg.DeployOpts.Resume, "resume", "r", false, lang.CmdBundleDeployFlagResume) deployCmd.Flags().IntVar(&bundleCfg.DeployOpts.Retries, "retries", 3, lang.CmdBundleDeployFlagRetries) - deployCmd.Flags().BoolVarP(&bundleCfg.DeployOpts.Prune, "prune", "P", false, lang.CmdBundleDeployFlagPrune) // inspect cmd flags rootCmd.AddCommand(inspectCmd) diff --git a/src/config/config.go b/src/config/config.go index 0c3fc3a4..acb49bdb 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -118,8 +118,3 @@ var ( // BundleAlwaysPull is a list of paths that will always be pulled from the remote repository. BundleAlwaysPull = []string{BundleYAML, BundleYAMLSignature} ) - -// feature flag to enable/disable features -const ( - FF_STATE_ENABLED = false -) diff --git a/src/config/lang/lang.go b/src/config/lang/lang.go index 9d9adad4..744fd1b7 100644 --- a/src/config/lang/lang.go +++ b/src/config/lang/lang.go @@ -50,7 +50,6 @@ const ( CmdBundleDeployFlagSet = "Specify deployment variables to set on the command line (KEY=value)" CmdBundleDeployFlagRetries = "Specify the number of retries for package deployments (applies to all pkgs in a bundle)" CmdBundleDeployFlagRef = "Specify which zarf package ref you want to deploy. By default the ref set in the bundle yaml is used." - CmdBundleDeployFlagPrune = "Forces the removal of packages that are no longer in the bundle" // bundle inspect CmdBundleInspectShort = "Display the metadata of a bundle" diff --git a/src/pkg/bundle/common.go b/src/pkg/bundle/common.go index 1337123d..640e170d 100644 --- a/src/pkg/bundle/common.go +++ b/src/pkg/bundle/common.go @@ -21,6 +21,7 @@ import ( "github.com/defenseunicorns/uds-cli/src/types" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/zarf-dev/zarf/src/api/v1alpha1" + "github.com/zarf-dev/zarf/src/pkg/cluster" "github.com/zarf-dev/zarf/src/pkg/message" zarfUtils "github.com/zarf-dev/zarf/src/pkg/utils" "github.com/zarf-dev/zarf/src/pkg/zoci" @@ -361,3 +362,16 @@ func (b *Bundle) setPackageRef(pkg types.Package) (types.Package, error) { } return pkg, nil } + +// GetDeployedPackageNames returns the names of the packages that have been deployed +func GetDeployedPackageNames() []string { + var deployedPackageNames []string + c, _ := cluster.NewCluster() + if c != nil { + deployedPackages, _ := c.GetDeployedZarfPackages(context.TODO()) + for _, pkg := range deployedPackages { + deployedPackageNames = append(deployedPackageNames, pkg.Name) + } + } + return deployedPackageNames +} diff --git a/src/pkg/bundle/deploy.go b/src/pkg/bundle/deploy.go index 014e823a..b33d8fde 100644 --- a/src/pkg/bundle/deploy.go +++ b/src/pkg/bundle/deploy.go @@ -16,14 +16,12 @@ import ( "github.com/defenseunicorns/pkg/helpers/v2" "github.com/defenseunicorns/uds-cli/src/config" "github.com/defenseunicorns/uds-cli/src/pkg/sources" - "github.com/defenseunicorns/uds-cli/src/pkg/state" "github.com/defenseunicorns/uds-cli/src/types" "github.com/defenseunicorns/uds-cli/src/types/chartvariable" "github.com/defenseunicorns/uds-cli/src/types/valuesources" goyaml "github.com/goccy/go-yaml" "github.com/zarf-dev/zarf/src/api/v1alpha1" zarfConfig "github.com/zarf-dev/zarf/src/config" - "github.com/zarf-dev/zarf/src/pkg/cluster" "github.com/zarf-dev/zarf/src/pkg/layout" "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/pkg/packager" @@ -57,70 +55,23 @@ func (b *Bundle) Deploy() error { } } - // get bundle state - var sc *state.Client - var kc *cluster.Cluster - if config.FF_STATE_ENABLED { - message.Debugf("state management disabled, skipping bundle state management") - var err error - var enabledState bool - kc, err = cluster.NewCluster() - if err != nil { - // common scenario for Zarf actions run before cluster is available - enabledState = false - } - sc, err = state.NewClient(kc, enabledState) - if err != nil { - return err - } - - err = sc.InitBundleState(&b.bundle, state.Deploying) - if err != nil { - return err - } - } else { - sc = &state.Client{ - Enabled: false, - } - } - // if resume, filter for packages not yet deployed if b.cfg.DeployOpts.Resume { - deployedPackageNames := state.GetDeployedPackageNames() + deployedPackageNames := GetDeployedPackageNames() var notDeployed []types.Package for _, pkg := range packagesToDeploy { if !slices.Contains(deployedPackageNames, pkg.Name) { notDeployed = append(notDeployed, pkg) } + packagesToDeploy = notDeployed } - packagesToDeploy = notDeployed } - deployErr := deployPackages(sc, packagesToDeploy, b) - if deployErr != nil { - _ = sc.UpdateBundleState(&b.bundle, state.Failed) - return deployErr - } - - // update bundle state with success - err := sc.UpdateBundleState(&b.bundle, state.Success) - if err != nil { - return err - } - - // prune unreferenced packages - if b.cfg.DeployOpts.Prune { - err = b.handlePrune(sc, kc) - if err != nil { - return err - } - } - - return nil + return deployPackages(packagesToDeploy, b) } -func deployPackages(sc *state.Client, packagesToDeploy []types.Package, b *Bundle) error { +func deployPackages(packagesToDeploy []types.Package, b *Bundle) error { // map of Zarf pkgs and their vars bundleExportedVars := make(map[string]map[string]string) @@ -198,15 +149,7 @@ func deployPackages(sc *state.Client, packagesToDeploy []types.Package, b *Bundl return err } - if pkgDeployErr := pkgClient.Deploy(context.TODO()); pkgDeployErr != nil { - err = sc.UpdateBundlePkgState(&b.bundle, pkg, state.Failed) - if err != nil { - return err - } - return pkgDeployErr - } - err = sc.UpdateBundlePkgState(&b.bundle, pkg, state.Success) - if err != nil { + if err = pkgClient.Deploy(context.TODO()); err != nil { return err } @@ -223,31 +166,6 @@ func deployPackages(sc *state.Client, packagesToDeploy []types.Package, b *Bundl } bundleExportedVars[pkg.Name] = pkgExportedVars - // if state client is still disabled, check for cluster connection - if config.FF_STATE_ENABLED { - if !sc.Enabled { - kc, err := cluster.NewCluster() - if err != nil { - message.Debugf("not connected to cluster, skipping bundle state management") - } else { - message.Debugf("connected to cluster, enabling bundle state management") - sc.Client = kc.Clientset - sc.Enabled = true - err = sc.InitBundleState(&b.bundle, state.Deploying) - if err != nil { - return err - } - // got a cluster now! update UDS state with the pkgs that were deployed before the cluster was up - for j := 0; j <= i; j++ { - err = sc.UpdateBundlePkgState(&b.bundle, packagesToDeploy[j], state.Success) - if err != nil { - return err - } - } - } - } - } - } return nil } diff --git a/src/pkg/bundle/prune.go b/src/pkg/bundle/prune.go deleted file mode 100644 index f5ee3121..00000000 --- a/src/pkg/bundle/prune.go +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023-Present The UDS Authors - -// Package bundle contains functions for interacting with, managing and deploying UDS packages -package bundle - -import ( - "context" - "fmt" - "strings" - - "github.com/AlecAivazis/survey/v2" - "github.com/defenseunicorns/uds-cli/src/config" - "github.com/defenseunicorns/uds-cli/src/pkg/sources" - "github.com/defenseunicorns/uds-cli/src/pkg/state" - "github.com/defenseunicorns/uds-cli/src/types" - "github.com/fatih/color" - "github.com/zarf-dev/zarf/src/pkg/cluster" - "github.com/zarf-dev/zarf/src/pkg/message" - "github.com/zarf-dev/zarf/src/pkg/packager" - zarfUtils "github.com/zarf-dev/zarf/src/pkg/utils" - zarfTypes "github.com/zarf-dev/zarf/src/types" - "k8s.io/apimachinery/pkg/api/errors" -) - -func (b *Bundle) handlePrune(sc *state.Client, kc *cluster.Cluster) error { - // get any unreferenced pkgs - unreferencedPkgs, err := sc.GetUnreferencedPackages(&b.bundle) - if err != nil { - return err - } - if len(unreferencedPkgs) > 0 { - fmt.Println("\n", message.RuleLine) - message.HeaderInfof("🪓 PRUNING UNREFERENCED PACKAGES") - - // prompt user if no --confirm (noting dev deploy confirms automatically) - if !config.CommonOptions.Confirm { - err, cancel := b.prunePrompt(unreferencedPkgs) - if err != nil { - return err - } else if cancel { - return nil - } - } - - // remove unreferenced packages - for _, pkg := range unreferencedPkgs { - message.Infof("Removing unreferenced package: %v", pkg.Name) - - // set up Zarf pkg client - opts := zarfTypes.ZarfPackageOptions{ - PackageSource: b.cfg.RemoveOpts.Source, - } - pkgCfg := zarfTypes.PackagerConfig{ - PkgOpts: opts, - } - pkgTmp, err := zarfUtils.MakeTempDir(config.CommonOptions.TempDirectory) - if err != nil { - return err - } - - source, err := sources.NewFromZarfState(kc.Clientset, pkg.Name) - if err != nil { - if errors.IsNotFound(err) { - // handles case where Zarf pkg is not found in cluster, but exists in UDS state (ie. pkgs with only actions) - message.Debugf("Package %s state secret not found in cluster, updating UDS state", pkg.Name) - err = sc.RemovePackageFromState(&b.bundle, pkg.Name) - if err != nil { - return err - } - continue - } - return err - } - - pkgClient, err := packager.New(&pkgCfg, packager.WithSource(source), packager.WithTemp(pkgTmp)) - if err != nil { - return err - } - defer pkgClient.ClearTempPaths() - - // remove package - if removeErr := pkgClient.Remove(context.TODO()); removeErr != nil { - err = sc.UpdateBundlePkgState(&b.bundle, pkg, state.FailedRemove) - if err != nil { - return err - } - return removeErr - } - - err = sc.RemovePackageFromState(&b.bundle, pkg.Name) - if err != nil { - return err - } - message.Success("Package removed") - } - } - return nil -} - -func (b *Bundle) prunePrompt(unreferencedPkgs []types.Package) (error, bool) { - // format a list of pkg names and print prompt - unreferencedPkgNames := make([]string, 0) - for _, pkg := range unreferencedPkgs { - unreferencedPkgNames = append(unreferencedPkgNames, pkg.Name) - } - pkgList := strings.Join(unreferencedPkgNames, "\n - ") - cyan := color.New(color.FgCyan).SprintFunc() - styledBundleName := cyan(b.bundle.Metadata.Name) - promptMessage := fmt.Sprintf("The following packages are no longer referenced by the bundle %s:\n - %s\n\nAttempt removal of these packages?", styledBundleName, pkgList) - prompt := &survey.Confirm{ - Message: promptMessage, - } - if err := survey.AskOne(prompt, &config.CommonOptions.Confirm); err != nil { - return fmt.Errorf("failed to prompt user: %w", err), true - } - - if !config.CommonOptions.Confirm { - message.Info("Canceled prune operation") - return nil, true - } - - return nil, false -} diff --git a/src/pkg/bundle/remove.go b/src/pkg/bundle/remove.go index 360bee66..a5dfb528 100644 --- a/src/pkg/bundle/remove.go +++ b/src/pkg/bundle/remove.go @@ -11,10 +11,8 @@ import ( "github.com/defenseunicorns/uds-cli/src/config" "github.com/defenseunicorns/uds-cli/src/pkg/sources" - "github.com/defenseunicorns/uds-cli/src/pkg/state" "github.com/defenseunicorns/uds-cli/src/pkg/utils" "github.com/defenseunicorns/uds-cli/src/types" - "github.com/zarf-dev/zarf/src/pkg/cluster" "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/pkg/packager" zarfUtils "github.com/zarf-dev/zarf/src/pkg/utils" @@ -22,7 +20,7 @@ import ( "golang.org/x/exp/slices" ) -// Remove removes a bundle from the cluster +// Remove removes packages deployed from a bundle func (b *Bundle) Remove() error { // Check that provided oci source path is valid, and update it if it's missing the full path source, err := CheckOCISourcePath(b.cfg.RemoveOpts.Source) @@ -69,73 +67,19 @@ func (b *Bundle) Remove() error { if len(userSpecifiedPackages) != len(packagesToRemove) { return fmt.Errorf("invalid zarf packages specified by --packages") } - } else { - packagesToRemove = b.bundle.Packages + return removePackages(packagesToRemove, b) } - - // get bundle state - var sc *state.Client - var kc *cluster.Cluster - if config.FF_STATE_ENABLED { - var err error - kc, err = cluster.NewCluster() - sc, err = state.NewClient(kc, true) - if err != nil { - return err - } - - err = sc.InitBundleState(&b.bundle, state.Removing) - if err != nil { - return err - } - } else { - sc = &state.Client{ - Enabled: false, - } - } - - // remove packages - removeErr := removePackages(sc, packagesToRemove, b) - if removeErr != nil { - return removeErr - } - - // remove bundle state secret - err = sc.RemoveBundleState(&b.bundle) - if err != nil { - return err - } - - return nil + return removePackages(b.bundle.Packages, b) } -func removePackages(sc *state.Client, packagesToRemove []types.Package, b *Bundle) error { - // Get deployed packages from Zarf state - deployedPackageNames := state.GetDeployedPackageNames() - - bundleState, err := sc.GetBundleState(&b.bundle) - if err != nil { - return err - } +func removePackages(packagesToRemove []types.Package, b *Bundle) error { + // Get deployed packages + deployedPackageNames := GetDeployedPackageNames() for i := len(packagesToRemove) - 1; i >= 0; i-- { pkg := packagesToRemove[i] - if config.FF_STATE_ENABLED { - // check if disconnected from cluster - _, err = cluster.NewCluster() - if err != nil { - // cluster no longer available, disable state client (common scenario when running Zarf actions after cluster has been deleted) - sc.Enabled = false - } - } - if slices.Contains(deployedPackageNames, pkg.Name) { - err = sc.UpdateBundlePkgState(&b.bundle, pkg, state.Removing) - if err != nil { - return err - } - opts := zarfTypes.ZarfPackageOptions{ PackageSource: b.cfg.RemoveOpts.Source, } @@ -159,32 +103,11 @@ func removePackages(sc *state.Client, packagesToRemove []types.Package, b *Bundl } defer pkgClient.ClearTempPaths() - if removeErr := pkgClient.Remove(context.TODO()); removeErr != nil { - err = sc.UpdateBundlePkgState(&b.bundle, pkg, state.FailedRemove) - if err != nil { - return err - } - return removeErr - } - - err = sc.UpdateBundlePkgState(&b.bundle, pkg, state.Removed) - if err != nil { + if err := pkgClient.Remove(context.TODO()); err != nil { return err } } else { - if config.FF_STATE_ENABLED { - // update bundle state if exists in bundle but not in cluster (ie. simple Zarf pkgs with no artifacts) - for _, pkgState := range bundleState.PkgStatuses { - if pkgState.Name == pkg.Name { - err = sc.UpdateBundlePkgState(&b.bundle, pkg, state.Removed) - if err != nil { - return err - } - break - } - } - message.Debugf("Skipped removal of %s, package not found in Zarf or UDS state", pkg.Name) - } + message.Warnf("Skipping removal of %s. Package not deployed", pkg.Name) } } diff --git a/src/pkg/sources/new.go b/src/pkg/sources/new.go index 5297c103..c6082a6e 100644 --- a/src/pkg/sources/new.go +++ b/src/pkg/sources/new.go @@ -5,8 +5,6 @@ package sources import ( - "context" - "encoding/json" "fmt" "strings" @@ -17,8 +15,6 @@ import ( zarfSources "github.com/zarf-dev/zarf/src/pkg/packager/sources" "github.com/zarf-dev/zarf/src/pkg/zoci" zarfTypes "github.com/zarf-dev/zarf/src/types" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" ) // NewFromLocation creates a new package source based on pkgLocation @@ -65,23 +61,3 @@ func NewFromLocation(bundleCfg types.BundleConfig, pkg types.Package, opts zarfT } return source, nil } - -// NewFromZarfState creates a new ZarfState package source -// only used for remove operations on prune -func NewFromZarfState(client kubernetes.Interface, pkgName string) (*ZarfState, error) { - // get secret from K8s - secretName := fmt.Sprintf("zarf-package-%s", pkgName) - sec, err := client.CoreV1().Secrets("zarf").Get(context.TODO(), secretName, metav1.GetOptions{}) - if err != nil { - return nil, err - } - - // marshal secret to state - var state zarfTypes.DeployedPackage - err = json.Unmarshal(sec.Data["data"], &state) - if err != nil { - return nil, err - } - - return &ZarfState{pkgName: pkgName, state: &state}, nil -} diff --git a/src/pkg/sources/zarf-state.go b/src/pkg/sources/zarf-state.go deleted file mode 100644 index 736e8c5f..00000000 --- a/src/pkg/sources/zarf-state.go +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023-Present The UDS Authors - -// Package sources contains Zarf packager sources -package sources - -import ( - "context" - "fmt" - - "github.com/zarf-dev/zarf/src/api/v1alpha1" - "github.com/zarf-dev/zarf/src/pkg/layout" - "github.com/zarf-dev/zarf/src/pkg/packager/filters" - zarfTypes "github.com/zarf-dev/zarf/src/types" -) - -// ZarfState is a package source for Zarf packages that have already been deployed -type ZarfState struct { - pkgName string - state *zarfTypes.DeployedPackage -} - -func (z *ZarfState) LoadPackageMetadata(_ context.Context, _ *layout.PackagePaths, _ bool, _ bool) (pkg v1alpha1.ZarfPackage, warnings []string, err error) { - if z.state == nil { - return v1alpha1.ZarfPackage{}, nil, fmt.Errorf("missing metadata from deployed pkg: %s", z.pkgName) - } - return z.state.Data, nil, nil -} - -// LoadPackage doesn't need to be implemented because this source is only used for package removal -func (z *ZarfState) LoadPackage(_ context.Context, _ *layout.PackagePaths, _ filters.ComponentFilterStrategy, _ bool) (v1alpha1.ZarfPackage, []string, error) { - return v1alpha1.ZarfPackage{}, nil, fmt.Errorf("not implemented in %T", z) -} - -// Collect doesn't need to be implemented because this source is only used for package removal -func (z *ZarfState) Collect(_ context.Context, _ string) (string, error) { - return "", fmt.Errorf("not implemented in %T", z) -} diff --git a/src/pkg/state/state.go b/src/pkg/state/state.go deleted file mode 100644 index 56d19815..00000000 --- a/src/pkg/state/state.go +++ /dev/null @@ -1,426 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023-Present The UDS Authors - -package state - -import ( - "context" - "encoding/json" - "fmt" - "time" - - "github.com/defenseunicorns/uds-cli/src/types" - "github.com/zarf-dev/zarf/src/pkg/cluster" - "github.com/zarf-dev/zarf/src/pkg/message" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" -) - -type PkgStatus struct { - Name string `json:"name"` - Version string `json:"version"` - Status string `json:"status"` - DateUpdated time.Time `json:"date_updated"` -} - -type BundleState struct { - Name string `json:"name"` - Version string `json:"version"` - PkgStatuses []PkgStatus `json:"packages"` - Status string `json:"status"` - DateUpdated time.Time `json:"date_updated"` -} - -type Client struct { - Client kubernetes.Interface - Enabled bool -} - -const ( - Success = "success" // deployed successfully - Failed = "failed" // failed to deploy - Deploying = "deploying" // deployment in progress - NotDeployed = "not_deployed" // package is in the bundle but not deployed - Removing = "removing" // removal in progress - Removed = "removed" // package removed (does not apply to BundleState) - FailedRemove = "failed_remove" // package failed to be removed (does not apply to BundleState) - Unreferenced = "unreferenced" // package has been removed from the bundle but still exists in the cluster - stateNs = "uds" -) - -// NewClient creates a new state Client -func NewClient(kc *cluster.Cluster, enableState bool) (*Client, error) { - stateClient := &Client{ - Enabled: enableState, - } - if kc == nil { - stateClient.Client = nil - return stateClient, nil - } - stateClient.Client = kc.Clientset - return stateClient, nil -} - -// stateDisabled indicates to the caller that state is currently disabled -func (c *Client) stateDisabled() bool { - if !c.Enabled { - message.Debugf("state client is not enabled") - return true - } - return false -} - -// InitBundleState initializes the bundle state in the K8s cluster if it doesn't exist. -// This can safely be called multiple times -func (c *Client) InitBundleState(b *types.UDSBundle, status string) error { - if c.stateDisabled() { - return nil - } - err := c.ensureNamespace() - if err != nil { - return err - } - bundleState, isNewState, err := c.getOrCreateBundleState(b, status) - if err != nil { - return err - } else if isNewState { - message.Infof("Initialized bundle state for %s", b.Metadata.Name) - return nil - } - - // if existing state, update bundle state packages based on bundle YAML - // create map of bundled packages for easy lookup - bundledPkgs := make(map[string]types.Package, len(b.Packages)) - for _, pkg := range b.Packages { - bundledPkgs[pkg.Name] = pkg - } - - // create map of state packages for easy lookup - statePkgs := make(map[string]PkgStatus, len(bundleState.PkgStatuses)) - for _, pkg := range bundleState.PkgStatuses { - statePkgs[pkg.Name] = pkg - } - - // check for updates and dropped/unreferenced packages - for i, pkgStatus := range bundleState.PkgStatuses { - _, exists := bundledPkgs[pkgStatus.Name] // check if bundled pkg is in state - if exists { - // existing package, update version and timestamp - bundleState.PkgStatuses[i].Version = pkgStatus.Version - bundleState.PkgStatuses[i].DateUpdated = time.Now() - } else { - // package no longer in bundle - bundleState.PkgStatuses[i].Status = Unreferenced - bundleState.PkgStatuses[i].DateUpdated = time.Now() - } - } - - // add new packages to state - for _, pkg := range b.Packages { - if _, exists := statePkgs[pkg.Name]; !exists { - bundleState.PkgStatuses = append(bundleState.PkgStatuses, PkgStatus{ - Name: pkg.Name, - Version: pkg.Ref, - Status: NotDeployed, - DateUpdated: time.Now(), - }) - } - } - - // update state - bundleState.Version = b.Metadata.Version - bundleState.Status = status - bundleState.DateUpdated = time.Now() - err = c.saveBundleState(bundleState) - return err -} - -// ensureNamespace creates the uds namespace if it doesn't exist -func (c *Client) ensureNamespace() error { - _, err := c.Client.CoreV1().Namespaces().Get(context.TODO(), stateNs, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - ns := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: stateNs, - }, - } - _, err = c.Client.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{}) - if err != nil { - return fmt.Errorf("failed to create namespace: %w", err) - } - } else { - return fmt.Errorf("failed to get namespace: %w", err) - } - } - return nil -} - -// getOrCreateBundleState gets or creates the bundle state in the K8s cluster -func (c *Client) getOrCreateBundleState(b *types.UDSBundle, status string) (*BundleState, bool, error) { - var state *BundleState - isNewState := false - bundleName := b.Metadata.Name - version := b.Metadata.Version - stateSecretName := fmt.Sprintf("uds-bundle-%s", bundleName) - stateSecret, err := c.Client.CoreV1().Secrets(stateNs).Get(context.TODO(), stateSecretName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - var pkgStatuses []PkgStatus - isNewState = true - for _, pkg := range b.Packages { - pkgStatuses = append(pkgStatuses, PkgStatus{ - Name: pkg.Name, - Version: pkg.Ref, - Status: NotDeployed, - DateUpdated: time.Now(), - }) - } - - // init state and secret - state = &BundleState{ - Name: bundleName, - Version: version, - PkgStatuses: pkgStatuses, - DateUpdated: time.Now(), - Status: status, - } - - // marshal into K8s secret and save - jsonBundleState, err := json.Marshal(state) - if err != nil { - return nil, isNewState, fmt.Errorf("failed to marshal bundle state: %w", err) - } - stateSecret = &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: stateSecretName, - }, - Data: map[string][]byte{ - "data": jsonBundleState, - }, - } - _, err = c.Client.CoreV1().Secrets(stateNs).Create(context.TODO(), stateSecret, metav1.CreateOptions{}) - if err != nil { - return nil, isNewState, err - } - } else { - return nil, isNewState, err - } - } else { - state, err = c.unmarshalBundleState(stateSecret) - if err != nil { - return nil, isNewState, err - } - } - - return state, isNewState, nil -} - -// UpdateBundleState updates the bundle state in the K8s cluster -func (c *Client) UpdateBundleState(b *types.UDSBundle, status string) error { - if c.stateDisabled() { - return nil - } - stateSecret, err := c.Client.CoreV1().Secrets(stateNs).Get(context.TODO(), fmt.Sprintf("uds-bundle-%s", b.Metadata.Name), metav1.GetOptions{}) - if err != nil { - return fmt.Errorf("failed to get bundle state: %w", err) - } - bundleState, err := c.unmarshalBundleState(stateSecret) - if err != nil { - return err - } - - bundleState.Status = status - bundleState.Version = b.Metadata.Version - bundleState.DateUpdated = time.Now() - - // update state - err = c.saveBundleState(bundleState) - return err -} - -// GetBundleState gets the bundle state from the K8s cluster -func (c *Client) GetBundleState(b *types.UDSBundle) (*BundleState, error) { - if c.stateDisabled() { - return nil, nil - } - bundleName := b.Metadata.Name - stateSecret, err := c.Client.CoreV1().Secrets(stateNs).Get(context.TODO(), fmt.Sprintf("uds-bundle-%s", bundleName), metav1.GetOptions{}) - if err != nil { - return nil, fmt.Errorf("failed to get bundle state: %w", err) - } - return c.unmarshalBundleState(stateSecret) -} - -func (c *Client) unmarshalBundleState(secret *corev1.Secret) (*BundleState, error) { - var bundleState BundleState - if data, ok := secret.Data["data"]; ok { - err := json.Unmarshal(data, &bundleState) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal existing bundle state: %w", err) - } - } - return &bundleState, nil -} - -// saveBundleState saves the bundle state to the K8s cluster (but doesn't create a new state) -func (c *Client) saveBundleState(stateToSave *BundleState) error { - jsonBundleState, err := json.Marshal(stateToSave) - if err != nil { - return err - } - stateSecret, err := c.Client.CoreV1().Secrets(stateNs).Get(context.TODO(), fmt.Sprintf("uds-bundle-%s", stateToSave.Name), metav1.GetOptions{}) - if err != nil { - return err - } - stateSecret.Data["data"] = jsonBundleState - _, err = c.Client.CoreV1().Secrets(stateNs).Update(context.TODO(), stateSecret, metav1.UpdateOptions{}) - if err != nil { - return err - } - return nil -} - -func (c *Client) UpdateBundlePkgState(b *types.UDSBundle, bundledPkg types.Package, status string) error { - if c.stateDisabled() { - return nil - } - // get state - bundleName := b.Metadata.Name - stateSecret, err := c.Client.CoreV1().Secrets(stateNs).Get(context.TODO(), fmt.Sprintf("uds-bundle-%s", bundleName), metav1.GetOptions{}) - if err != nil { - return err - } - bundleState, err := c.unmarshalBundleState(stateSecret) - if err != nil { - return err - } - - // update pkg status - for i, pkg := range bundleState.PkgStatuses { - if pkg.Name == bundledPkg.Name { - bundleState.PkgStatuses[i].Status = status - bundleState.PkgStatuses[i].Version = bundledPkg.Ref - bundleState.PkgStatuses[i].DateUpdated = time.Now() - break - } - } - - // save state - bundleState.DateUpdated = time.Now() - err = c.saveBundleState(bundleState) - return err -} - -// GetBundlePkgState checks if a package exists in the bundle state -func (c *Client) GetBundlePkgState(b *types.UDSBundle, pkgName string) (*PkgStatus, error) { - if c.stateDisabled() { - return nil, nil - } - state, err := c.GetBundleState(b) - if err != nil { - return nil, err - } - - for _, pkg := range state.PkgStatuses { - if pkg.Name == pkgName { - return &pkg, nil - } - } - return nil, nil -} - -// RemoveBundleState removes the bundle state from the K8s cluster -func (c *Client) RemoveBundleState(b *types.UDSBundle) error { - if c.stateDisabled() { - return nil - } - // ensure all packages have been removed before deleting - bundleName := b.Metadata.Name - state, err := c.GetBundleState(b) - if err != nil { - return err - } - - partialRemoval := false - for _, pkg := range state.PkgStatuses { - if pkg.Status != Removed && pkg.Status != NotDeployed { - partialRemoval = true - message.Debugf("not removing state for bundle: %s, package %s still exists in state", bundleName, pkg.Name) - } - } - - if partialRemoval { - err = c.UpdateBundleState(b, Success) // not removing entire bundle, reset status - if err != nil { - return err - } - return nil - } - - // remove bundle state - err = c.Client.CoreV1().Secrets(stateNs).Delete(context.TODO(), - fmt.Sprintf("uds-bundle-%s", bundleName), metav1.DeleteOptions{}) - if err != nil { - return err - } - return nil -} - -func (c *Client) GetUnreferencedPackages(b *types.UDSBundle) ([]types.Package, error) { - if c.stateDisabled() { - return nil, nil - } - state, err := c.GetBundleState(b) - if err != nil { - return nil, err - } - - var unreferencedPkgs []types.Package - for _, p := range state.PkgStatuses { - if p.Status == Unreferenced { - unreferencedPkgs = append(unreferencedPkgs, types.Package{Name: p.Name, Ref: p.Version}) - } - } - - return unreferencedPkgs, nil -} - -func (c *Client) RemovePackageFromState(b *types.UDSBundle, pkgToRemove string) error { - if c.stateDisabled() { - return nil - } - state, err := c.GetBundleState(b) - if err != nil { - return err - } - - // remove pkg from list of statuses - var newPkgStatuses []PkgStatus - for _, p := range state.PkgStatuses { - if p.Name != pkgToRemove { - newPkgStatuses = append(newPkgStatuses, p) - } - } - - // update state - state.PkgStatuses = newPkgStatuses - state.DateUpdated = time.Now() - err = c.saveBundleState(state) - return err -} - -// GetDeployedPackageNames returns the names of the packages that have been deployed -func GetDeployedPackageNames() []string { - var deployedPackageNames []string - c, _ := cluster.NewCluster() - if c != nil { - deployedPackages, _ := c.GetDeployedZarfPackages(context.TODO()) - for _, pkg := range deployedPackages { - deployedPackageNames = append(deployedPackageNames, pkg.Name) - } - } - return deployedPackageNames -} diff --git a/src/test/bundles/16-state/prune/uds-bundle.yaml b/src/test/bundles/16-state/prune/uds-bundle.yaml deleted file mode 100644 index 0edf5e2e..00000000 --- a/src/test/bundles/16-state/prune/uds-bundle.yaml +++ /dev/null @@ -1,13 +0,0 @@ -kind: UDSBundle -metadata: - name: test-prune - description: - version: 0.0.1 - -packages: - - name: nginx - repository: ghcr.io/defenseunicorns/uds-cli/nginx - ref: 0.0.1 - - name: podinfo - path: "../../../packages/podinfo" - ref: 0.0.1 diff --git a/src/test/bundles/16-state/prune/updated/uds-bundle.yaml b/src/test/bundles/16-state/prune/updated/uds-bundle.yaml deleted file mode 100644 index fc7e3d21..00000000 --- a/src/test/bundles/16-state/prune/updated/uds-bundle.yaml +++ /dev/null @@ -1,13 +0,0 @@ -kind: UDSBundle -metadata: - name: test-prune - description: | - updated bundle to demonstrate pruning - - this is the "update" to the bundle in the 16-state/prune/uds-bundle.yaml - - noting that this bundle removes podinfo - version: 0.0.1 - -packages: - - name: nginx - repository: ghcr.io/defenseunicorns/uds-cli/nginx - ref: 0.0.1 diff --git a/src/test/bundles/16-state/uds-bundle.yaml b/src/test/bundles/16-state/uds-bundle.yaml deleted file mode 100644 index 54333f81..00000000 --- a/src/test/bundles/16-state/uds-bundle.yaml +++ /dev/null @@ -1,14 +0,0 @@ -kind: UDSBundle -metadata: - name: state - description: uses very simple packages to test state - version: 0.0.1 - -packages: - - name: output-var - path: ../../packages/no-cluster/output-var - ref: 0.0.1 - - - name: receive-var - path: ../../packages/no-cluster/receive-var - ref: 0.0.1 diff --git a/src/test/bundles/16-state/updated/uds-bundle.yaml b/src/test/bundles/16-state/updated/uds-bundle.yaml deleted file mode 100644 index 02a20785..00000000 --- a/src/test/bundles/16-state/updated/uds-bundle.yaml +++ /dev/null @@ -1,20 +0,0 @@ -kind: UDSBundle -metadata: - name: state - description: | - - this is the "update" to the bundle in the 16-state/uds-bundle.yaml - - noting that this bundle adds a new package and bumps the bundle version - version: 0.0.2 - -packages: - - name: output-var - path: ../../../packages/no-cluster/output-var - ref: 0.0.1 - - - name: receive-var - path: ../../../packages/no-cluster/receive-var - ref: 0.0.1 - - - name: real-simple - path: ../../../packages/no-cluster/real-simple - ref: 0.0.1 diff --git a/src/test/bundles/16-state/updated/updated-again/uds-bundle.yaml b/src/test/bundles/16-state/updated/updated-again/uds-bundle.yaml deleted file mode 100644 index a368dc36..00000000 --- a/src/test/bundles/16-state/updated/updated-again/uds-bundle.yaml +++ /dev/null @@ -1,20 +0,0 @@ -kind: UDSBundle -metadata: - name: state - description: | - - this is the "update" to the bundle in the 16-state/updated/uds-bundle.yaml - - noting that this bundle adds a new pkg, removes a pkg, and bumps the bundle version - version: 0.0.3 - -packages: - - name: output-var-collision - path: ../../../../packages/no-cluster/output-var-collision - ref: 0.0.1 - - - name: receive-var - path: ../../../../packages/no-cluster/receive-var - ref: 0.0.1 - - - name: real-simple - path: ../../../../packages/no-cluster/real-simple - ref: 0.0.1 diff --git a/src/test/e2e/bundle_deploy_flags_test.go b/src/test/e2e/bundle_deploy_flags_test.go index e6042d1c..c73b2923 100644 --- a/src/test/e2e/bundle_deploy_flags_test.go +++ b/src/test/e2e/bundle_deploy_flags_test.go @@ -69,8 +69,7 @@ func TestPackagesFlag(t *testing.T) { } func TestResumeFlag(t *testing.T) { - // delete nginx, podinfo, and uds (state) namespaces if they exist - // todo: add 'uds' to this list of namespaces once the state feature is turned on + // delete nginx, podinfo, and uds namespaces if they exist runCmdWithErr("zarf tools kubectl delete ns nginx podinfo") // intentionally not err checking this cmd deployZarfInit(t) e2e.CreateZarfPkg(t, "src/test/packages/podinfo", false) diff --git a/src/test/e2e/state_test.go b/src/test/e2e/state_test.go deleted file mode 100644 index 45309902..00000000 --- a/src/test/e2e/state_test.go +++ /dev/null @@ -1,248 +0,0 @@ -package test - -import ( - "context" - "encoding/json" - "fmt" - "testing" - - "github.com/defenseunicorns/uds-cli/src/pkg/state" - "github.com/stretchr/testify/require" - "github.com/zarf-dev/zarf/src/pkg/cluster" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestUDSStateOnDeploy(t *testing.T) { - // we are intentionally using no-cluster Zarf pkgs to this test super fast! - // even though we're using no-cluster packages, we still need a cluster to create the state secret - e2e.CreateZarfPkg(t, "src/test/packages/no-cluster/output-var", false) - e2e.CreateZarfPkg(t, "src/test/packages/no-cluster/receive-var", false) - e2e.CreateZarfPkg(t, "src/test/packages/no-cluster/real-simple", false) - e2e.CreateZarfPkg(t, "src/test/packages/no-cluster/output-var-collision", false) - - // deploy bundle - bundleName := "state" - bundlePath := "src/test/bundles/16-state/" - bundleTarball := fmt.Sprintf("uds-bundle-%s-%s-0.0.1.tar.zst", bundleName, e2e.Arch) - deployPath := fmt.Sprintf("%s/%s", bundlePath, bundleTarball) - cleanStateSecret(t, bundleName) - runCmd(t, fmt.Sprintf("create %s --confirm", bundlePath)) - runCmd(t, fmt.Sprintf("deploy %s --confirm", deployPath)) - - t.Run("on deploy + update/re-deploy", func(t *testing.T) { - bundleState := getStateSecret(t, bundleName) - originalDeployTimestamp := bundleState.DateUpdated - require.Equal(t, bundleName, bundleState.Name) - require.Equal(t, "0.0.1", bundleState.Version) - require.Equal(t, state.Success, bundleState.Status) - require.NotNil(t, bundleState.DateUpdated) - require.Len(t, bundleState.PkgStatuses, 2) - require.Contains(t, bundleState.PkgStatuses[0].Version, "0.0.1@") // using Contains because ref is mutated when bundle is created - require.Equal(t, state.Success, bundleState.PkgStatuses[0].Status) - require.NotNil(t, bundleState.PkgStatuses[0].DateUpdated) - - // update the bundle (same version and number of packages, effectively a re-deploy) - runCmd(t, fmt.Sprintf("deploy %s --confirm", deployPath)) - bundleState = getStateSecret(t, bundleName) - require.NotNil(t, bundleState.DateUpdated) - require.True(t, bundleState.DateUpdated.After(originalDeployTimestamp)) - }) - - t.Run("on deploy with --packages flag", func(t *testing.T) { - runCmd(t, fmt.Sprintf("deploy --packages=receive-var %s --confirm", deployPath)) - bundleState := getStateSecret(t, bundleName) - require.Equal(t, bundleName, bundleState.Name) - require.Equal(t, state.Success, bundleState.Status) - require.Len(t, bundleState.PkgStatuses, 2) - require.Equal(t, state.Success, bundleState.PkgStatuses[0].Status) - }) - - t.Run("on deploy with --resume flag", func(t *testing.T) { - cleanStateSecret(t, bundleName) // start with fresh state - runCmd(t, fmt.Sprintf("deploy --packages=output-var %s --confirm", deployPath)) - bundleState := getStateSecret(t, bundleName) - require.Equal(t, bundleName, bundleState.Name) - require.Equal(t, state.Success, bundleState.Status) - require.Len(t, bundleState.PkgStatuses, 2) - require.Equal(t, state.Success, bundleState.PkgStatuses[0].Status) - require.Equal(t, state.NotDeployed, bundleState.PkgStatuses[1].Status) - - runCmd(t, fmt.Sprintf("deploy --resume %s --confirm", deployPath)) - bundleState = getStateSecret(t, bundleName) - require.Equal(t, state.Success, bundleState.Status) - require.Len(t, bundleState.PkgStatuses, 2) - require.Equal(t, state.Success, bundleState.PkgStatuses[0].Status) - require.Equal(t, state.Success, bundleState.PkgStatuses[1].Status) - }) - - t.Run("deploy multiple updates", func(t *testing.T) { - // deploy bundle update: bumps version and adds a Zarf pkg - bundlePath = "src/test/bundles/16-state/updated" - bundleTarball = fmt.Sprintf("uds-bundle-%s-%s-0.0.2.tar.zst", bundleName, e2e.Arch) - deployPath = fmt.Sprintf("%s/%s", bundlePath, bundleTarball) - runCmd(t, fmt.Sprintf("create %s --confirm", bundlePath)) - runCmd(t, fmt.Sprintf("deploy %s --confirm", deployPath)) - - // ensure state got updated - bundleState := getStateSecret(t, bundleName) - require.Equal(t, "0.0.2", bundleState.Version) - require.Equal(t, state.Success, bundleState.Status) - require.Len(t, bundleState.PkgStatuses, 3) - for _, pkgStatus := range bundleState.PkgStatuses { - require.Equal(t, state.Success, pkgStatus.Status) - } - - // deploy another bundle update: bumps version and adds a Zarf pkg + removes a Zarf pkg - bundlePath = "src/test/bundles/16-state/updated/updated-again" - bundleTarball = fmt.Sprintf("uds-bundle-%s-%s-0.0.3.tar.zst", bundleName, e2e.Arch) - deployPath = fmt.Sprintf("%s/%s", bundlePath, bundleTarball) - runCmd(t, fmt.Sprintf("create %s --confirm", bundlePath)) - runCmd(t, fmt.Sprintf("deploy %s --confirm", deployPath)) - - // ensure state got updated - bundleState = getStateSecret(t, bundleName) - require.Equal(t, "0.0.3", bundleState.Version) - require.Equal(t, state.Success, bundleState.Status) - require.Len(t, bundleState.PkgStatuses, 4) // noting that we didn't call prune - checkedUnreferenced := false - successCount := 0 - for _, pkgStatus := range bundleState.PkgStatuses { - if pkgStatus.Name == "output-var" { - require.Equal(t, state.Unreferenced, pkgStatus.Status) - checkedUnreferenced = true - continue - } - require.Equal(t, state.Success, pkgStatus.Status) - successCount++ - } - require.True(t, checkedUnreferenced) - require.Equal(t, 3, successCount) - - // re-deploy latest update with prune and check state - runCmd(t, fmt.Sprintf("deploy --prune %s --confirm", deployPath)) - bundleState = getStateSecret(t, bundleName) - require.Len(t, bundleState.PkgStatuses, 3) - for _, pkgStatus := range bundleState.PkgStatuses { - require.Equal(t, state.Success, pkgStatus.Status) - } - }) - -} - -func TestUDSStateOnRemove(t *testing.T) { - // using dev deploy - removeZarfInit() - - e2e.CreateZarfPkg(t, "src/test/packages/no-cluster/output-var", false) - e2e.CreateZarfPkg(t, "src/test/packages/no-cluster/receive-var", false) - - bundleName := "state" - bundlePath := "src/test/bundles/16-state" - bundleTarball := fmt.Sprintf("uds-bundle-%s-%s-0.0.1.tar.zst", bundleName, e2e.Arch) - removePath := fmt.Sprintf("%s/%s", bundlePath, bundleTarball) - cleanStateSecret(t, bundleName) - - t.Run("on remove", func(t *testing.T) { - runCmd(t, fmt.Sprintf("dev deploy %s", bundlePath)) - runCmd(t, fmt.Sprintf("remove %s --confirm", removePath)) - expectNoSecret(t, bundleName) - }) - - t.Run("on remove with --packages flag", func(t *testing.T) { - runCmd(t, fmt.Sprintf("dev deploy %s", bundlePath)) - bundleState := getStateSecret(t, bundleName) - require.Len(t, bundleState.PkgStatuses, 2) - runCmd(t, fmt.Sprintf("remove %s --packages=output-var --confirm", removePath)) - bundleState = getStateSecret(t, bundleName) - require.Len(t, bundleState.PkgStatuses, 2) - require.Equal(t, state.Removed, bundleState.PkgStatuses[0].Status) - require.Equal(t, state.Success, bundleState.PkgStatuses[1].Status) - require.Equal(t, state.Success, bundleState.Status) - }) -} - -func TestPkgPruning(t *testing.T) { - // using dev deploy - removeZarfInit() - - // using podinfo and nginx packages so we can test is deployments get pruned - e2e.CreateZarfPkg(t, "src/test/packages/podinfo", false) - e2e.CreateZarfPkg(t, "src/test/packages/nginx", false) - - bundleName := "test-prune" - originalBundlePath := "src/test/bundles/16-state/prune" - updatedBundlePath := "src/test/bundles/16-state/prune/updated" - cleanStateSecret(t, bundleName) - - // deploy the original bundle and ensure deployments exist - runCmd(t, fmt.Sprintf("dev deploy %s", originalBundlePath)) - getDeploymentsCmd := "zarf tools kubectl get deployments -A -o=jsonpath='{.items[*].metadata.name}'" - deployments, _ := runCmd(t, getDeploymentsCmd) - require.Contains(t, deployments, "nginx") - require.Contains(t, deployments, "podinfo") - - t.Run("remove pkg from bundle and check status", func(t *testing.T) { - runCmd(t, fmt.Sprintf("dev deploy %s", updatedBundlePath)) - bundleState := getStateSecret(t, bundleName) - require.Len(t, bundleState.PkgStatuses, 2) - require.Equal(t, state.Success, bundleState.PkgStatuses[0].Status) - require.Equal(t, state.Success, bundleState.Status) - - // ensure podinfo pkg is marked as unreferenced - require.Equal(t, state.Unreferenced, bundleState.PkgStatuses[1].Status) - - // ensure podinfo still exists and wasn't pruned - deployments, _ = runCmd(t, getDeploymentsCmd) - require.Contains(t, deployments, "nginx") - require.Contains(t, deployments, "podinfo") - }) - - t.Run("remove pkg from bundle and prune", func(t *testing.T) { - runCmd(t, fmt.Sprintf("dev deploy --prune %s", updatedBundlePath)) - bundleState := getStateSecret(t, bundleName) - require.Len(t, bundleState.PkgStatuses, 1) - require.Equal(t, state.Success, bundleState.PkgStatuses[0].Status) - require.Equal(t, state.Success, bundleState.Status) - - // ensure podinfo deployment no longer exists - deployments, _ = runCmd(t, getDeploymentsCmd) - require.Contains(t, deployments, "nginx") - require.NotContains(t, deployments, "podinfo") - }) -} - -func cleanStateSecret(t *testing.T, bundleName string) { - kc, err := cluster.NewCluster() - require.NoError(t, err) - err = kc.Clientset.CoreV1().Secrets("uds"). - Delete(context.TODO(), fmt.Sprintf("uds-bundle-%s", bundleName), metav1.DeleteOptions{}) - if !errors.IsNotFound(err) { - require.NoError(t, err) - } -} - -func getStateSecret(t *testing.T, bundleName string) *state.BundleState { - kc, err := cluster.NewCluster() - require.NoError(t, err) - - // Get the secret - secret, err := kc.Clientset.CoreV1().Secrets("uds").Get(context.TODO(), fmt.Sprintf("uds-bundle-%s", bundleName), metav1.GetOptions{}) - require.NoError(t, err) - - // marshal into struct for easy assertions - var bundleState *state.BundleState - err = json.Unmarshal(secret.Data["data"], &bundleState) - require.NoError(t, err) - - return bundleState -} - -func expectNoSecret(t *testing.T, bundleName string) { - kc, err := cluster.NewCluster() - require.NoError(t, err) - - _, err = kc.Clientset.CoreV1().Secrets("uds").Get(context.TODO(), fmt.Sprintf("uds-bundle-%s", bundleName), metav1.GetOptions{}) - require.Error(t, err) - require.True(t, errors.IsNotFound(err)) -} diff --git a/src/types/options.go b/src/types/options.go index 69929db9..44477302 100644 --- a/src/types/options.go +++ b/src/types/options.go @@ -32,7 +32,6 @@ type BundleDeployOptions struct { Source string Config string Packages []string - Prune bool PublicKeyPath string SetVariables map[string]string `json:"setVariables" jsonschema:"description=Key-Value map of variable names and their corresponding values that will be used by Zarf packages in a bundle"` // Variables and SharedVariables are read in from uds-config.yaml diff --git a/tasks/tests.yaml b/tasks/tests.yaml index 4c590801..591162ac 100644 --- a/tasks/tests.yaml +++ b/tasks/tests.yaml @@ -35,7 +35,7 @@ tasks: actions: - cmd: cd src/test/e2e && go test -failfast -v -timeout 30m completion_test.go commands_test.go main_test.go - - name: dev-and-state + - name: dev description: only run tests in dev_test.go and state_test.go (grouped bc neither require zarf init) actions: # commenting out state tests for now as its behind a feature flag and not ready for CI @@ -85,5 +85,3 @@ tasks: build/uds zarf tools wait-for gateway admin-gateway -n istio-admin-gateway --timeout 10s build/uds zarf tools wait-for gateway tenant-gateway -n istio-tenant-gateway --timeout 10s build/uds zarf tools wait-for package keycloak -n keycloak --timeout 10s -# commenting out state tests until we re-enable the feature -# - cmd: ./hack/validate-uds-core-state.sh