diff --git a/src/cmd/common/utils.go b/src/cmd/common/utils.go index 26f2ce6707..1da7e456ee 100644 --- a/src/cmd/common/utils.go +++ b/src/cmd/common/utils.go @@ -4,13 +4,6 @@ // Package common handles command configuration across all commands package common -import ( - "context" - - "github.com/defenseunicorns/zarf/src/pkg/cluster" - "github.com/defenseunicorns/zarf/src/pkg/message" -) - // SetBaseDirectory sets the base directory. This is a directory with a zarf.yaml. func SetBaseDirectory(args []string) string { if len(args) > 0 { @@ -18,14 +11,3 @@ func SetBaseDirectory(args []string) string { } return "." } - -// NewClusterOrDie creates a new Cluster instance and waits for the cluster to be ready or throws a fatal error. -func NewClusterOrDie(ctx context.Context) *cluster.Cluster { - timeoutCtx, cancel := context.WithTimeout(ctx, cluster.DefaultTimeout) - defer cancel() - c, err := cluster.NewClusterWithWait(timeoutCtx) - if err != nil { - message.Fatalf(err, "Failed to connect to cluster") - } - return c -} diff --git a/src/cmd/connect.go b/src/cmd/connect.go index e2f3884183..7111abf813 100644 --- a/src/cmd/connect.go +++ b/src/cmd/connect.go @@ -5,10 +5,9 @@ package cmd import ( + "context" "fmt" - "os" - "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/cluster" "github.com/defenseunicorns/zarf/src/pkg/message" @@ -29,15 +28,16 @@ var ( Aliases: []string{"c"}, Short: lang.CmdConnectShort, Long: lang.CmdConnectLong, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { var target string if len(args) > 0 { target = args[0] } spinner := message.NewProgressSpinner(lang.CmdConnectPreparingTunnel, target) + defer spinner.Stop() c, err := cluster.NewCluster() if err != nil { - spinner.Fatalf(err, lang.CmdConnectErrCluster, err.Error()) + return err } ctx := cmd.Context() @@ -50,7 +50,7 @@ var ( tunnel, err = c.Connect(ctx, target) } if err != nil { - spinner.Fatalf(err, lang.CmdConnectErrService, err.Error()) + return fmt.Errorf("unable to connect to the service: %w", err) } defer tunnel.Close() @@ -74,9 +74,9 @@ var ( case <-ctx.Done(): spinner.Successf(lang.CmdConnectTunnelClosed, url) case err = <-tunnel.ErrChan(): - spinner.Fatalf(err, lang.CmdConnectErrService, err.Error()) + return fmt.Errorf("lost connection to the service: %w", err) } - os.Exit(0) + return nil }, } @@ -84,11 +84,18 @@ var ( Use: "list", Aliases: []string{"l"}, Short: lang.CmdConnectListShort, - Run: func(cmd *cobra.Command, _ []string) { - ctx := cmd.Context() - if err := common.NewClusterOrDie(ctx).PrintConnectTable(ctx); err != nil { - message.Fatal(err, err.Error()) + RunE: func(cmd *cobra.Command, _ []string) error { + timeoutCtx, cancel := context.WithTimeout(cmd.Context(), cluster.DefaultTimeout) + defer cancel() + c, err := cluster.NewClusterWithWait(timeoutCtx) + if err != nil { + return err + } + err = c.PrintConnectTable(cmd.Context()) + if err != nil { + return err } + return nil }, } ) diff --git a/src/cmd/destroy.go b/src/cmd/destroy.go index 0034186089..0faa1d5c3e 100644 --- a/src/cmd/destroy.go +++ b/src/cmd/destroy.go @@ -5,15 +5,17 @@ package cmd import ( + "context" "errors" + "fmt" "os" "regexp" "github.com/defenseunicorns/pkg/helpers/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/packager/helm" + "github.com/defenseunicorns/zarf/src/pkg/cluster" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/pkg/utils/exec" @@ -28,9 +30,14 @@ var destroyCmd = &cobra.Command{ Aliases: []string{"d"}, Short: lang.CmdDestroyShort, Long: lang.CmdDestroyLong, - Run: func(cmd *cobra.Command, _ []string) { + RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() - c := common.NewClusterOrDie(ctx) + timeoutCtx, cancel := context.WithTimeout(cmd.Context(), cluster.DefaultTimeout) + defer cancel() + c, err := cluster.NewClusterWithWait(timeoutCtx) + if err != nil { + return err + } // NOTE: If 'zarf init' failed to deploy the k3s component (or if we're looking at the wrong kubeconfig) // there will be no zarf-state to load and the struct will be empty. In these cases, if we can find @@ -45,7 +52,7 @@ var destroyCmd = &cobra.Command{ // Check if we have the scripts to destroy everything fileInfo, err := os.Stat(config.ZarfCleanupScriptsPath) if errors.Is(err, os.ErrNotExist) || !fileInfo.IsDir() { - message.Fatalf(lang.CmdDestroyErrNoScriptPath, config.ZarfCleanupScriptsPath) + return fmt.Errorf("unable to find the folder %s which has the scripts to cleanup the cluster. Please double-check you have the right kube-context", config.ZarfCleanupScriptsPath) } // Run all the scripts! @@ -73,12 +80,13 @@ var destroyCmd = &cobra.Command{ // If Zarf didn't deploy the cluster, only delete the ZarfNamespace if err := c.DeleteZarfNamespace(ctx); err != nil { - message.Fatal(err, err.Error()) + return err } // Remove zarf agent labels and secrets from namespaces Zarf doesn't manage c.StripZarfLabelsAndSecretsFromNamespaces(ctx) } + return nil }, } diff --git a/src/cmd/dev.go b/src/cmd/dev.go index 3714759c0d..7560b43651 100644 --- a/src/cmd/dev.go +++ b/src/cmd/dev.go @@ -5,6 +5,7 @@ package cmd import ( + "errors" "fmt" "io" "os" @@ -39,7 +40,7 @@ var devDeployCmd = &cobra.Command{ Args: cobra.MaximumNArgs(1), Short: lang.CmdDevDeployShort, Long: lang.CmdDevDeployLong, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { pkgConfig.CreateOpts.BaseDir = common.SetBaseDirectory(args) v := common.GetViper() @@ -53,8 +54,9 @@ var devDeployCmd = &cobra.Command{ defer pkgClient.ClearTempPaths() if err := pkgClient.DevDeploy(cmd.Context()); err != nil { - message.Fatalf(err, lang.CmdDevDeployErr, err.Error()) + return fmt.Errorf("failed to dev deploy: %w", err) } + return nil }, } @@ -64,7 +66,7 @@ var devGenerateCmd = &cobra.Command{ Args: cobra.ExactArgs(1), Short: lang.CmdDevGenerateShort, Example: lang.CmdDevGenerateExample, - Run: func(_ *cobra.Command, args []string) { + RunE: func(_ *cobra.Command, args []string) error { pkgConfig.GenerateOpts.Name = args[0] pkgConfig.CreateOpts.BaseDir = "." @@ -73,9 +75,11 @@ var devGenerateCmd = &cobra.Command{ pkgClient := packager.NewOrDie(&pkgConfig) defer pkgClient.ClearTempPaths() - if err := pkgClient.Generate(); err != nil { - message.Fatalf(err, err.Error()) + err := pkgClient.Generate() + if err != nil { + return err } + return nil }, } @@ -84,13 +88,13 @@ var devTransformGitLinksCmd = &cobra.Command{ Aliases: []string{"p"}, Short: lang.CmdDevPatchGitShort, Args: cobra.ExactArgs(2), - Run: func(_ *cobra.Command, args []string) { + RunE: func(_ *cobra.Command, args []string) error { host, fileName := args[0], args[1] // Read the contents of the given file content, err := os.ReadFile(fileName) if err != nil { - message.Fatalf(err, lang.CmdDevPatchGitFileReadErr, fileName) + return fmt.Errorf("unable to read the file %s: %w", fileName, err) } pkgConfig.InitOpts.GitServer.Address = host @@ -108,17 +112,17 @@ var devTransformGitLinksCmd = &cobra.Command{ Message: fmt.Sprintf(lang.CmdDevPatchGitOverwritePrompt, fileName), } if err := survey.AskOne(prompt, &confirm); err != nil { - message.Fatalf(nil, lang.CmdDevPatchGitOverwriteErr, err.Error()) + return fmt.Errorf("confirm overwrite canceled: %w", err) } if confirm { // Overwrite the file err = os.WriteFile(fileName, []byte(processedText), helpers.ReadAllWriteUser) if err != nil { - message.Fatal(err, lang.CmdDevPatchGitFileWriteErr) + return fmt.Errorf("unable to write the changes back to the file: %w", err) } } - + return nil }, } @@ -127,7 +131,9 @@ var devSha256SumCmd = &cobra.Command{ Aliases: []string{"s"}, Short: lang.CmdDevSha256sumShort, Args: cobra.ExactArgs(1), - Run: func(_ *cobra.Command, args []string) { + RunE: func(_ *cobra.Command, args []string) error { + hashErr := errors.New("unable to compute the SHA256SUM hash") + fileName := args[0] var tmp string @@ -139,7 +145,7 @@ var devSha256SumCmd = &cobra.Command{ fileBase, err := helpers.ExtractBasePathFromURL(fileName) if err != nil { - message.Fatalf(err, lang.CmdDevSha256sumHashErr, err.Error()) + return errors.Join(hashErr, err) } if fileBase == "" { @@ -148,13 +154,13 @@ var devSha256SumCmd = &cobra.Command{ tmp, err = utils.MakeTempDir(config.CommonOptions.TempDirectory) if err != nil { - message.Fatalf(err, lang.CmdDevSha256sumHashErr, err.Error()) + return errors.Join(hashErr, err) } downloadPath := filepath.Join(tmp, fileBase) err = utils.DownloadToFile(fileName, downloadPath, "") if err != nil { - message.Fatalf(err, lang.CmdDevSha256sumHashErr, err.Error()) + return errors.Join(hashErr, err) } fileName = downloadPath @@ -166,7 +172,7 @@ var devSha256SumCmd = &cobra.Command{ if tmp == "" { tmp, err = utils.MakeTempDir(config.CommonOptions.TempDirectory) if err != nil { - message.Fatalf(err, lang.CmdDevSha256sumHashErr, err.Error()) + return errors.Join(hashErr, err) } defer os.RemoveAll(tmp) } @@ -175,7 +181,7 @@ var devSha256SumCmd = &cobra.Command{ err = archiver.Extract(fileName, extractPath, tmp) if err != nil { - message.Fatalf(err, lang.CmdDevSha256sumHashErr, err.Error()) + return errors.Join(hashErr, err) } fileName = extractedFile @@ -183,17 +189,16 @@ var devSha256SumCmd = &cobra.Command{ data, err = os.Open(fileName) if err != nil { - message.Fatalf(err, lang.CmdDevSha256sumHashErr, err.Error()) + return errors.Join(hashErr, err) } defer data.Close() - var hash string - hash, err = helpers.GetSHA256Hash(data) + hash, err := helpers.GetSHA256Hash(data) if err != nil { - message.Fatalf(err, lang.CmdDevSha256sumHashErr, err.Error()) - } else { - fmt.Println(hash) + return errors.Join(hashErr, err) } + fmt.Println(hash) + return nil }, } @@ -203,7 +208,7 @@ var devFindImagesCmd = &cobra.Command{ Args: cobra.MaximumNArgs(1), Short: lang.CmdDevFindImagesShort, Long: lang.CmdDevFindImagesLong, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { pkgConfig.CreateOpts.BaseDir = common.SetBaseDirectory(args) v := common.GetViper() @@ -216,8 +221,9 @@ var devFindImagesCmd = &cobra.Command{ defer pkgClient.ClearTempPaths() if _, err := pkgClient.FindImages(cmd.Context()); err != nil { - message.Fatalf(err, lang.CmdDevFindImagesErr, err.Error()) + return fmt.Errorf("unable to find images: %w", err) } + return nil }, } @@ -227,18 +233,18 @@ var devGenConfigFileCmd = &cobra.Command{ Args: cobra.MaximumNArgs(1), Short: lang.CmdDevGenerateConfigShort, Long: lang.CmdDevGenerateConfigLong, - Run: func(_ *cobra.Command, args []string) { - fileName := "zarf-config.toml" - + RunE: func(_ *cobra.Command, args []string) error { // If a filename was provided, use that + fileName := "zarf-config.toml" if len(args) > 0 { fileName = args[0] } v := common.GetViper() if err := v.SafeWriteConfigAs(fileName); err != nil { - message.Fatalf(err, lang.CmdDevGenerateConfigErr, fileName) + return fmt.Errorf("unable to write the config file %s, make sure the file doesn't already exist: %w", fileName, err) } + return nil }, } @@ -285,14 +291,8 @@ func init() { // use the package create config for this and reset it here to avoid overwriting the config.CreateOptions.SetVariables devFindImagesCmd.Flags().StringToStringVar(&pkgConfig.CreateOpts.SetVariables, "set", v.GetStringMapString(common.VPkgCreateSet), lang.CmdDevFlagSet) - err := devFindImagesCmd.Flags().MarkDeprecated("set", "this field is replaced by create-set") - if err != nil { - message.Fatal(err, err.Error()) - } - err = devFindImagesCmd.Flags().MarkHidden("set") - if err != nil { - message.Fatal(err, err.Error()) - } + devFindImagesCmd.Flags().MarkDeprecated("set", "this field is replaced by create-set") + devFindImagesCmd.Flags().MarkHidden("set") devFindImagesCmd.Flags().StringVarP(&pkgConfig.CreateOpts.Flavor, "flavor", "f", v.GetString(common.VPkgCreateFlavor), lang.CmdPackageCreateFlagFlavor) devFindImagesCmd.Flags().StringToStringVar(&pkgConfig.CreateOpts.SetVariables, "create-set", v.GetStringMapString(common.VPkgCreateSet), lang.CmdDevFlagSet) devFindImagesCmd.Flags().StringToStringVar(&pkgConfig.PkgOpts.SetVariables, "deploy-set", v.GetStringMapString(common.VPkgDeploySet), lang.CmdPackageDeployFlagSet) @@ -340,16 +340,7 @@ func bindDevGenerateFlags(_ *viper.Viper) { generateFlags.StringVar(&pkgConfig.GenerateOpts.Output, "output-directory", "", "Output directory for the generated zarf.yaml") generateFlags.StringVar(&pkgConfig.FindImagesOpts.KubeVersionOverride, "kube-version", "", lang.CmdDevFlagKubeVersion) - err := devGenerateCmd.MarkFlagRequired("url") - if err != nil { - message.Fatal(err, err.Error()) - } - err = devGenerateCmd.MarkFlagRequired("version") - if err != nil { - message.Fatal(err, err.Error()) - } - err = devGenerateCmd.MarkFlagRequired("output-directory") - if err != nil { - message.Fatal(err, err.Error()) - } + devGenerateCmd.MarkFlagRequired("url") + devGenerateCmd.MarkFlagRequired("version") + devGenerateCmd.MarkFlagRequired("output-directory") } diff --git a/src/cmd/initialize.go b/src/cmd/initialize.go index 75a8a049e0..5a680b7836 100644 --- a/src/cmd/initialize.go +++ b/src/cmd/initialize.go @@ -36,12 +36,12 @@ var initCmd = &cobra.Command{ Short: lang.CmdInitShort, Long: lang.CmdInitLong, Example: lang.CmdInitExample, - Run: func(cmd *cobra.Command, _ []string) { + RunE: func(cmd *cobra.Command, _ []string) error { zarfLogo := message.GetLogo() _, _ = fmt.Fprintln(os.Stderr, zarfLogo) if err := validateInitFlags(); err != nil { - message.Fatal(err, lang.CmdInitErrFlags) + return fmt.Errorf("invalid command flags were provided: %w", err) } // Continue running package deploy for all components like any other package @@ -51,12 +51,12 @@ var initCmd = &cobra.Command{ // Try to use an init-package in the executable directory if none exist in current working directory var err error if pkgConfig.PkgOpts.PackageSource, err = findInitPackage(cmd.Context(), initPackageName); err != nil { - message.Fatal(err, err.Error()) + return err } src, err := sources.New(&pkgConfig.PkgOpts) if err != nil { - message.Fatal(err, err.Error()) + return err } v := common.GetViper() @@ -66,12 +66,11 @@ var initCmd = &cobra.Command{ pkgClient := packager.NewOrDie(&pkgConfig, packager.WithSource(src)) defer pkgClient.ClearTempPaths() - ctx := cmd.Context() - - err = pkgClient.Deploy(ctx) + err = pkgClient.Deploy(cmd.Context()) if err != nil { - message.Fatal(err, err.Error()) + return err } + return nil }, } @@ -94,7 +93,7 @@ func findInitPackage(ctx context.Context, initPackageName string) (string, error // Create the cache directory if it doesn't exist if helpers.InvalidPath(config.GetAbsCachePath()) { if err := helpers.CreateDirectory(config.GetAbsCachePath(), helpers.ReadExecuteAllWriteUser); err != nil { - message.Fatalf(err, lang.CmdInitErrUnableCreateCache, config.GetAbsCachePath()) + return "", fmt.Errorf("unable to create the cache directory %s: %w", config.GetAbsCachePath(), err) } } @@ -107,10 +106,9 @@ func findInitPackage(ctx context.Context, initPackageName string) (string, error downloadCacheTarget, err := downloadInitPackage(ctx, config.GetAbsCachePath()) if err != nil { if errors.Is(err, lang.ErrInitNotFound) { - message.Fatal(err, err.Error()) - } else { - message.Fatalf(err, lang.CmdInitErrDownload, err.Error()) + return "", err } + return "", fmt.Errorf("failed to download the init package: %w", err) } return downloadCacheTarget, nil } @@ -134,7 +132,7 @@ func downloadInitPackage(ctx context.Context, cacheDirectory string) (string, er Message: lang.CmdInitPullConfirm, } if err := survey.AskOne(prompt, &confirmDownload); err != nil { - return "", fmt.Errorf(lang.ErrConfirmCancel, err.Error()) + return "", fmt.Errorf("confirm download canceled: %w", err) } } diff --git a/src/cmd/internal.go b/src/cmd/internal.go index ceca2d612e..b385564151 100644 --- a/src/cmd/internal.go +++ b/src/cmd/internal.go @@ -5,6 +5,7 @@ package cmd import ( + "context" "encoding/json" "fmt" "os" @@ -16,6 +17,7 @@ import ( "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/internal/agent" "github.com/defenseunicorns/zarf/src/internal/packager/git" + "github.com/defenseunicorns/zarf/src/pkg/cluster" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/types" "github.com/invopop/jsonschema" @@ -55,7 +57,7 @@ var httpProxyCmd = &cobra.Command{ var genCLIDocs = &cobra.Command{ Use: "gen-cli-docs", Short: lang.CmdInternalGenerateCliDocsShort, - Run: func(_ *cobra.Command, _ []string) { + RunE: func(_ *cobra.Command, _ []string) error { // Don't include the datestamp in the output rootCmd.DisableAutoGenTag = true @@ -114,10 +116,10 @@ var genCLIDocs = &cobra.Command{ } if err := os.RemoveAll("./site/src/content/docs/commands"); err != nil { - message.Fatalf(lang.CmdInternalGenerateCliDocsErr, err.Error()) + return err } if err := os.Mkdir("./site/src/content/docs/commands", 0775); err != nil { - message.Fatalf(lang.CmdInternalGenerateCliDocsErr, err.Error()) + return err } var prependTitle = func(s string) string { @@ -147,10 +149,10 @@ tableOfContents: false } if err := doc.GenMarkdownTreeCustom(rootCmd, "./site/src/content/docs/commands", prependTitle, linkHandler); err != nil { - message.Fatalf(lang.CmdInternalGenerateCliDocsErr, err.Error()) - } else { - message.Success(lang.CmdInternalGenerateCliDocsSuccess) + return err } + message.Success(lang.CmdInternalGenerateCliDocsSuccess) + return nil }, } @@ -158,14 +160,15 @@ var genConfigSchemaCmd = &cobra.Command{ Use: "gen-config-schema", Aliases: []string{"gc"}, Short: lang.CmdInternalConfigSchemaShort, - Run: func(_ *cobra.Command, _ []string) { + RunE: func(_ *cobra.Command, _ []string) error { reflector := jsonschema.Reflector(jsonschema.Reflector{ExpandedStruct: true}) schema := reflector.Reflect(&types.ZarfPackage{}) output, err := json.MarshalIndent(schema, "", " ") if err != nil { - message.Fatal(err, lang.CmdInternalConfigSchemaErr) + return fmt.Errorf("unable to generate the Zarf config schema: %w", err) } fmt.Print(string(output) + "\n") + return nil }, } @@ -179,13 +182,14 @@ var genTypesSchemaCmd = &cobra.Command{ Use: "gen-types-schema", Aliases: []string{"gt"}, Short: lang.CmdInternalTypesSchemaShort, - Run: func(_ *cobra.Command, _ []string) { + RunE: func(_ *cobra.Command, _ []string) error { schema := jsonschema.Reflect(&zarfTypes{}) output, err := json.MarshalIndent(schema, "", " ") if err != nil { - message.Fatal(err, lang.CmdInternalTypesSchemaErr) + return fmt.Errorf("unable to generate the JSON schema for the Zarf types DeployedPackage, ZarfPackage, and ZarfState: %w", err) } fmt.Print(string(output) + "\n") + return nil }, } @@ -193,19 +197,23 @@ var createReadOnlyGiteaUser = &cobra.Command{ Use: "create-read-only-gitea-user", Short: lang.CmdInternalCreateReadOnlyGiteaUserShort, Long: lang.CmdInternalCreateReadOnlyGiteaUserLong, - Run: func(cmd *cobra.Command, _ []string) { - ctx := cmd.Context() - + RunE: func(cmd *cobra.Command, _ []string) error { + timeoutCtx, cancel := context.WithTimeout(cmd.Context(), cluster.DefaultTimeout) + defer cancel() + c, err := cluster.NewClusterWithWait(timeoutCtx) + if err != nil { + return err + } // Load the state so we can get the credentials for the admin git user - state, err := common.NewClusterOrDie(ctx).LoadZarfState(ctx) + state, err := c.LoadZarfState(cmd.Context()) if err != nil { message.WarnErr(err, lang.ErrLoadState) } - // Create the non-admin user - if err = git.New(state.GitServer).CreateReadOnlyUser(ctx); err != nil { + if err = git.New(state.GitServer).CreateReadOnlyUser(cmd.Context()); err != nil { message.WarnErr(err, lang.CmdInternalCreateReadOnlyGiteaUserErr) } + return nil }, } @@ -213,9 +221,15 @@ var createPackageRegistryToken = &cobra.Command{ Use: "create-artifact-registry-token", Short: lang.CmdInternalArtifactRegistryGiteaTokenShort, Long: lang.CmdInternalArtifactRegistryGiteaTokenLong, - Run: func(cmd *cobra.Command, _ []string) { + RunE: func(cmd *cobra.Command, _ []string) error { + timeoutCtx, cancel := context.WithTimeout(cmd.Context(), cluster.DefaultTimeout) + defer cancel() + c, err := cluster.NewClusterWithWait(timeoutCtx) + if err != nil { + return err + } + ctx := cmd.Context() - c := common.NewClusterOrDie(ctx) state, err := c.LoadZarfState(ctx) if err != nil { message.WarnErr(err, lang.ErrLoadState) @@ -231,9 +245,10 @@ var createPackageRegistryToken = &cobra.Command{ state.ArtifactServer.PushToken = token.Sha1 if err := c.SaveZarfState(ctx, state); err != nil { - message.Fatal(err, err.Error()) + return err } } + return nil }, } @@ -257,11 +272,12 @@ var updateGiteaPVC = &cobra.Command{ var isValidHostname = &cobra.Command{ Use: "is-valid-hostname", Short: lang.CmdInternalIsValidHostnameShort, - Run: func(_ *cobra.Command, _ []string) { + RunE: func(_ *cobra.Command, _ []string) error { if valid := helpers.IsValidHostName(); !valid { hostname, _ := os.Hostname() - message.Fatalf(nil, lang.CmdInternalIsValidHostnameErr, hostname) + return fmt.Errorf("the hostname %s is not valid. Ensure the hostname meets RFC1123 requirements https://www.rfc-editor.org/rfc/rfc1123.html", hostname) } + return nil }, } @@ -298,9 +314,6 @@ func addHiddenDummyFlag(cmd *cobra.Command, flagDummy string) { if cmd.PersistentFlags().Lookup(flagDummy) == nil { var dummyStr string cmd.PersistentFlags().StringVar(&dummyStr, flagDummy, "", "") - err := cmd.PersistentFlags().MarkHidden(flagDummy) - if err != nil { - message.Fatal(err, err.Error()) - } + cmd.PersistentFlags().MarkHidden(flagDummy) } } diff --git a/src/cmd/package.go b/src/cmd/package.go index b94fccee23..b5b28cff41 100644 --- a/src/cmd/package.go +++ b/src/cmd/package.go @@ -5,6 +5,8 @@ package cmd import ( + "context" + "errors" "fmt" "path/filepath" "regexp" @@ -39,7 +41,7 @@ var packageCreateCmd = &cobra.Command{ Args: cobra.MaximumNArgs(1), Short: lang.CmdPackageCreateShort, Long: lang.CmdPackageCreateLong, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { pkgConfig.CreateOpts.BaseDir = common.SetBaseDirectory(args) var isCleanPathRegex = regexp.MustCompile(`^[a-zA-Z0-9\_\-\/\.\~\\:]+$`) @@ -56,8 +58,9 @@ var packageCreateCmd = &cobra.Command{ defer pkgClient.ClearTempPaths() if err := pkgClient.Create(cmd.Context()); err != nil { - message.Fatalf(err, lang.CmdPackageCreateErr, err.Error()) + return fmt.Errorf("failed to create package: %w", err) } + return nil }, } @@ -67,8 +70,12 @@ var packageDeployCmd = &cobra.Command{ Short: lang.CmdPackageDeployShort, Long: lang.CmdPackageDeployLong, Args: cobra.MaximumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - pkgConfig.PkgOpts.PackageSource = choosePackage(args) + RunE: func(cmd *cobra.Command, args []string) error { + packageSource, err := choosePackage(args) + if err != nil { + return err + } + pkgConfig.PkgOpts.PackageSource = packageSource v := common.GetViper() pkgConfig.PkgOpts.SetVariables = helpers.TransformAndMergeMap( @@ -80,8 +87,9 @@ var packageDeployCmd = &cobra.Command{ ctx := cmd.Context() if err := pkgClient.Deploy(ctx); err != nil { - message.Fatalf(err, lang.CmdPackageDeployErr, err.Error()) + return fmt.Errorf("failed to deploy package: %w", err) } + return nil }, } @@ -92,17 +100,18 @@ var packageMirrorCmd = &cobra.Command{ Long: lang.CmdPackageMirrorLong, Example: lang.CmdPackageMirrorExample, Args: cobra.MaximumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - pkgConfig.PkgOpts.PackageSource = choosePackage(args) - + RunE: func(cmd *cobra.Command, args []string) error { + packageSource, err := choosePackage(args) + if err != nil { + return err + } + pkgConfig.PkgOpts.PackageSource = packageSource pkgClient := packager.NewOrDie(&pkgConfig) defer pkgClient.ClearTempPaths() - - ctx := cmd.Context() - - if err := pkgClient.Mirror(ctx); err != nil { - message.Fatalf(err, lang.CmdPackageDeployErr, err.Error()) + if err := pkgClient.Mirror(cmd.Context()); err != nil { + return fmt.Errorf("failed to mirror package: %w", err) } + return nil }, } @@ -112,17 +121,22 @@ var packageInspectCmd = &cobra.Command{ Short: lang.CmdPackageInspectShort, Long: lang.CmdPackageInspectLong, Args: cobra.MaximumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - pkgConfig.PkgOpts.PackageSource = choosePackage(args) - - src := identifyAndFallbackToClusterSource() - + RunE: func(cmd *cobra.Command, args []string) error { + packageSource, err := choosePackage(args) + if err != nil { + return err + } + pkgConfig.PkgOpts.PackageSource = packageSource + src, err := identifyAndFallbackToClusterSource() + if err != nil { + return err + } pkgClient := packager.NewOrDie(&pkgConfig, packager.WithSource(src)) defer pkgClient.ClearTempPaths() - if err := pkgClient.Inspect(cmd.Context()); err != nil { - message.Fatalf(err, lang.CmdPackageInspectErr, err.Error()) + return fmt.Errorf("failed to inspect package: %w", err) } + return nil }, ValidArgsFunction: getPackageCompletionArgs, } @@ -131,11 +145,18 @@ var packageListCmd = &cobra.Command{ Use: "list", Aliases: []string{"l", "ls"}, Short: lang.CmdPackageListShort, - Run: func(cmd *cobra.Command, _ []string) { + RunE: func(cmd *cobra.Command, _ []string) error { + timeoutCtx, cancel := context.WithTimeout(cmd.Context(), cluster.DefaultTimeout) + defer cancel() + c, err := cluster.NewClusterWithWait(timeoutCtx) + if err != nil { + return err + } + ctx := cmd.Context() - deployedZarfPackages, err := common.NewClusterOrDie(ctx).GetDeployedZarfPackages(ctx) + deployedZarfPackages, err := c.GetDeployedZarfPackages(ctx) if err != nil && len(deployedZarfPackages) == 0 { - message.Fatalf(err, lang.CmdPackageListNoPackageWarn) + return fmt.Errorf("unable to get the packages deployed to the cluster: %w", err) } // Populate a matrix of all the deployed packages @@ -158,8 +179,9 @@ var packageListCmd = &cobra.Command{ // Print out any unmarshalling errors if err != nil { - message.Fatalf(err, lang.CmdPackageListUnmarshalErr) + return fmt.Errorf("unable to read all of the packages deployed to the cluster: %w", err) } + return nil }, } @@ -168,19 +190,22 @@ var packageRemoveCmd = &cobra.Command{ Aliases: []string{"u", "rm"}, Args: cobra.MaximumNArgs(1), Short: lang.CmdPackageRemoveShort, - Run: func(cmd *cobra.Command, args []string) { - pkgConfig.PkgOpts.PackageSource = choosePackage(args) - - src := identifyAndFallbackToClusterSource() - + RunE: func(cmd *cobra.Command, args []string) error { + packageSource, err := choosePackage(args) + if err != nil { + return err + } + pkgConfig.PkgOpts.PackageSource = packageSource + src, err := identifyAndFallbackToClusterSource() + if err != nil { + return err + } pkgClient := packager.NewOrDie(&pkgConfig, packager.WithSource(src)) defer pkgClient.ClearTempPaths() - - ctx := cmd.Context() - - if err := pkgClient.Remove(ctx); err != nil { - message.Fatalf(err, lang.CmdPackageRemoveErr, err.Error()) + if err := pkgClient.Remove(cmd.Context()); err != nil { + return fmt.Errorf("unable to remove the package with an error of: %w", err) } + return nil }, ValidArgsFunction: getPackageCompletionArgs, } @@ -190,11 +215,11 @@ var packagePublishCmd = &cobra.Command{ Short: lang.CmdPackagePublishShort, Example: lang.CmdPackagePublishExample, Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { pkgConfig.PkgOpts.PackageSource = args[0] if !helpers.IsOCIURL(args[1]) { - message.Fatal(nil, lang.CmdPackageRegistryPrefixErr) + return errors.New("Registry must be prefixed with 'oci://'") } parts := strings.Split(strings.TrimPrefix(args[1], helpers.OCIURLPrefix), "/") ref := registry.Reference{ @@ -203,7 +228,7 @@ var packagePublishCmd = &cobra.Command{ } err := ref.ValidateRegistry() if err != nil { - message.Fatalf(nil, "%s", err.Error()) + return err } if helpers.IsDir(pkgConfig.PkgOpts.PackageSource) { @@ -217,8 +242,9 @@ var packagePublishCmd = &cobra.Command{ defer pkgClient.ClearTempPaths() if err := pkgClient.Publish(cmd.Context()); err != nil { - message.Fatalf(err, lang.CmdPackagePublishErr, err.Error()) + return fmt.Errorf("failed to publish package: %w", err) } + return nil }, } @@ -227,21 +253,20 @@ var packagePullCmd = &cobra.Command{ Short: lang.CmdPackagePullShort, Example: lang.CmdPackagePullExample, Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { pkgConfig.PkgOpts.PackageSource = args[0] - pkgClient := packager.NewOrDie(&pkgConfig) defer pkgClient.ClearTempPaths() - if err := pkgClient.Pull(cmd.Context()); err != nil { - message.Fatalf(err, lang.CmdPackagePullErr, err.Error()) + return fmt.Errorf("failed to pull package: %w", err) } + return nil }, } -func choosePackage(args []string) string { +func choosePackage(args []string) (string, error) { if len(args) > 0 { - return args[0] + return args[0], nil } var path string prompt := &survey.Input{ @@ -258,23 +283,24 @@ func choosePackage(args []string) string { } if err := survey.AskOne(prompt, &path, survey.WithValidator(survey.Required)); err != nil { - message.Fatalf(nil, lang.CmdPackageChooseErr, err.Error()) + return "", fmt.Errorf("package path selection canceled: %w", err) } - return path + return path, nil } -func identifyAndFallbackToClusterSource() (src sources.PackageSource) { - var err error +// TODO: This code does not seem to do what it was intended. +func identifyAndFallbackToClusterSource() (sources.PackageSource, error) { identifiedSrc := sources.Identify(pkgConfig.PkgOpts.PackageSource) if identifiedSrc == "" { message.Debugf(lang.CmdPackageClusterSourceFallback, pkgConfig.PkgOpts.PackageSource) - src, err = sources.NewClusterSource(&pkgConfig.PkgOpts) + src, err := sources.NewClusterSource(&pkgConfig.PkgOpts) if err != nil { - message.Fatalf(err, lang.CmdPackageInvalidSource, pkgConfig.PkgOpts.PackageSource, err.Error()) + return nil, fmt.Errorf("unable to identify source from %s: %w", pkgConfig.PkgOpts.PackageSource, err) } + return src, nil } - return src + return nil, nil } func getPackageCompletionArgs(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { @@ -356,18 +382,9 @@ func bindCreateFlags(v *viper.Viper) { createFlags.IntVar(&pkgConfig.PkgOpts.Retries, "retries", v.GetInt(common.VPkgRetries), lang.CmdPackageFlagRetries) - err := createFlags.MarkHidden("output-directory") - if err != nil { - message.Fatal(err, err.Error()) - } - err = createFlags.MarkHidden("key") - if err != nil { - message.Fatal(err, err.Error()) - } - err = createFlags.MarkHidden("key-pass") - if err != nil { - message.Fatal(err, err.Error()) - } + createFlags.MarkHidden("output-directory") + createFlags.MarkHidden("key") + createFlags.MarkHidden("key-pass") } func bindDeployFlags(v *viper.Viper) { @@ -387,10 +404,7 @@ func bindDeployFlags(v *viper.Viper) { deployFlags.StringVar(&pkgConfig.PkgOpts.Shasum, "shasum", v.GetString(common.VPkgDeployShasum), lang.CmdPackageDeployFlagShasum) deployFlags.StringVar(&pkgConfig.PkgOpts.SGetKeyPath, "sget", v.GetString(common.VPkgDeploySget), lang.CmdPackageDeployFlagSget) - err := deployFlags.MarkHidden("sget") - if err != nil { - message.Fatal(err, err.Error()) - } + deployFlags.MarkHidden("sget") } func bindMirrorFlags(v *viper.Viper) { diff --git a/src/cmd/tools/zarf.go b/src/cmd/tools/zarf.go index 84395d0e82..eafa9cbb47 100644 --- a/src/cmd/tools/zarf.go +++ b/src/cmd/tools/zarf.go @@ -5,6 +5,8 @@ package tools import ( + "context" + "errors" "fmt" "os" "slices" @@ -52,12 +54,23 @@ var getCredsCmd = &cobra.Command{ Example: lang.CmdToolsGetCredsExample, Aliases: []string{"gc"}, Args: cobra.MaximumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() - state, err := common.NewClusterOrDie(ctx).LoadZarfState(ctx) - if err != nil || state.Distro == "" { - // If no distro the zarf secret did not load properly - message.Fatalf(nil, lang.ErrLoadState) + + timeoutCtx, cancel := context.WithTimeout(ctx, cluster.DefaultTimeout) + defer cancel() + c, err := cluster.NewClusterWithWait(timeoutCtx) + if err != nil { + return err + } + + state, err := c.LoadZarfState(ctx) + if err != nil { + return err + } + // TODO: Determine if this is actually needed. + if state.Distro == "" { + return errors.New("Zarf state secret did not load properly") } if len(args) > 0 { @@ -66,6 +79,7 @@ var getCredsCmd = &cobra.Command{ } else { message.PrintCredentialTable(state, nil) } + return nil }, } @@ -76,27 +90,37 @@ var updateCredsCmd = &cobra.Command{ Example: lang.CmdToolsUpdateCredsExample, Aliases: []string{"uc"}, Args: cobra.MaximumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { validKeys := []string{message.RegistryKey, message.GitKey, message.ArtifactKey, message.AgentKey} if len(args) == 0 { args = validKeys } else { if !slices.Contains(validKeys, args[0]) { cmd.Help() - message.Fatalf(nil, lang.CmdToolsUpdateCredsInvalidServiceErr, message.RegistryKey, message.GitKey, message.ArtifactKey) + return fmt.Errorf("invalid service key specified, valid keys are: %s, %s, and %s", message.RegistryKey, message.GitKey, message.ArtifactKey) } } ctx := cmd.Context() - c := common.NewClusterOrDie(ctx) + + timeoutCtx, cancel := context.WithTimeout(ctx, cluster.DefaultTimeout) + defer cancel() + c, err := cluster.NewClusterWithWait(timeoutCtx) + if err != nil { + return err + } + oldState, err := c.LoadZarfState(ctx) - if err != nil || oldState.Distro == "" { - // If no distro the zarf secret did not load properly - message.Fatalf(nil, lang.ErrLoadState) + if err != nil { + return err + } + // TODO: Determine if this is actually needed. + if oldState.Distro == "" { + return errors.New("Zarf state secret did not load properly") } newState, err := cluster.MergeZarfState(oldState, updateCredsInitOpts, args) if err != nil { - message.Fatal(err, lang.CmdToolsUpdateCredsUnableUpdateCreds) + return fmt.Errorf("unable to update Zarf credentials: %w", err) } message.PrintCredentialUpdates(oldState, newState, args) @@ -110,7 +134,7 @@ var updateCredsCmd = &cobra.Command{ Message: lang.CmdToolsUpdateCredsConfirmContinue, } if err := survey.AskOne(prompt, &confirm); err != nil { - message.Fatalf(nil, lang.ErrConfirmCancel, err) + return fmt.Errorf("confirm selection canceled: %w", err) } } @@ -138,7 +162,7 @@ var updateCredsCmd = &cobra.Command{ // Save the final Zarf State err = c.SaveZarfState(ctx, newState) if err != nil { - message.Fatalf(err, lang.ErrSaveState) + return fmt.Errorf("failed to save the Zarf State to the cluster: %w", err) } // Update Zarf 'init' component Helm releases if present @@ -167,6 +191,7 @@ var updateCredsCmd = &cobra.Command{ } } } + return nil }, } @@ -174,32 +199,31 @@ var clearCacheCmd = &cobra.Command{ Use: "clear-cache", Aliases: []string{"c"}, Short: lang.CmdToolsClearCacheShort, - Run: func(_ *cobra.Command, _ []string) { + RunE: func(_ *cobra.Command, _ []string) error { message.Notef(lang.CmdToolsClearCacheDir, config.GetAbsCachePath()) if err := os.RemoveAll(config.GetAbsCachePath()); err != nil { - message.Fatalf(err, lang.CmdToolsClearCacheErr, config.GetAbsCachePath()) + return fmt.Errorf("unable to clear the cache directory %s: %w", config.GetAbsCachePath(), err) } message.Successf(lang.CmdToolsClearCacheSuccess, config.GetAbsCachePath()) + return nil }, } var downloadInitCmd = &cobra.Command{ Use: "download-init", Short: lang.CmdToolsDownloadInitShort, - Run: func(cmd *cobra.Command, _ []string) { + RunE: func(cmd *cobra.Command, _ []string) error { url := zoci.GetInitPackageURL(config.CLIVersion) - remote, err := zoci.NewRemote(url, oci.PlatformForArch(config.GetArch())) if err != nil { - message.Fatalf(err, lang.CmdToolsDownloadInitErr, err.Error()) + return fmt.Errorf("unable to download the init package: %w", err) } - source := &sources.OCISource{Remote: remote} - _, err = source.Collect(cmd.Context(), outputDirectory) if err != nil { - message.Fatalf(err, lang.CmdToolsDownloadInitErr, err.Error()) + return fmt.Errorf("unable to download the init package: %w", err) } + return nil }, } @@ -208,18 +232,19 @@ var generatePKICmd = &cobra.Command{ Aliases: []string{"pki"}, Short: lang.CmdToolsGenPkiShort, Args: cobra.ExactArgs(1), - Run: func(_ *cobra.Command, args []string) { + RunE: func(_ *cobra.Command, args []string) error { pki := pki.GeneratePKI(args[0], subAltNames...) if err := os.WriteFile("tls.ca", pki.CA, helpers.ReadAllWriteUser); err != nil { - message.Fatalf(err, lang.ErrWritingFile, "tls.ca", err.Error()) + return err } if err := os.WriteFile("tls.crt", pki.Cert, helpers.ReadAllWriteUser); err != nil { - message.Fatalf(err, lang.ErrWritingFile, "tls.crt", err.Error()) + return err } if err := os.WriteFile("tls.key", pki.Key, helpers.ReadWriteUser); err != nil { - message.Fatalf(err, lang.ErrWritingFile, "tls.key", err.Error()) + return err } message.Successf(lang.CmdToolsGenPkiSuccess, args[0]) + return nil }, } @@ -227,7 +252,7 @@ var generateKeyCmd = &cobra.Command{ Use: "gen-key", Aliases: []string{"key"}, Short: lang.CmdToolsGenKeyShort, - Run: func(_ *cobra.Command, _ []string) { + RunE: func(_ *cobra.Command, _ []string) error { // Utility function to prompt the user for the password to the private key passwordFunc := func(bool) ([]byte, error) { // perform the first prompt @@ -259,7 +284,7 @@ var generateKeyCmd = &cobra.Command{ // Use cosign to generate the keypair keyBytes, err := cosign.GenerateKeyPair(passwordFunc) if err != nil { - message.Fatalf(err, lang.CmdToolsGenKeyErrUnableToGenKeypair, err.Error()) + return fmt.Errorf("unable to generate key pair: %w", err) } prvKeyFileName := "cosign.key" @@ -275,23 +300,23 @@ var generateKeyCmd = &cobra.Command{ } err := survey.AskOne(confirmOverwritePrompt, &confirm) if err != nil { - message.Fatalf(err, lang.CmdToolsGenKeyErrNoConfirmOverwrite) + return err } - if !confirm { - message.Fatal(nil, lang.CmdToolsGenKeyErrNoConfirmOverwrite) + return errors.New("did not receive confirmation for overwriting key file(s)") } } // Write the key file contents to disk if err := os.WriteFile(prvKeyFileName, keyBytes.PrivateBytes, helpers.ReadWriteUser); err != nil { - message.Fatalf(err, lang.ErrWritingFile, prvKeyFileName, err.Error()) + return err } if err := os.WriteFile(pubKeyFileName, keyBytes.PublicBytes, helpers.ReadAllWriteUser); err != nil { - message.Fatalf(err, lang.ErrWritingFile, pubKeyFileName, err.Error()) + return err } message.Successf(lang.CmdToolsGenKeySuccess, prvKeyFileName, pubKeyFileName) + return nil }, } diff --git a/src/config/lang/english.go b/src/config/lang/english.go index d4f2e4d4d3..df39428f48 100644 --- a/src/config/lang/english.go +++ b/src/config/lang/english.go @@ -18,7 +18,6 @@ import ( // Include sprintf formatting directives in the string if needed. const ( ErrLoadState = "Failed to load the Zarf State from the cluster." - ErrSaveState = "Failed to save the Zarf State to the cluster." ErrUnmarshal = "failed to unmarshal file: %w" ErrWritingFile = "failed to write file %s: %s" ErrDownloading = "failed to download %s: %s" @@ -85,8 +84,6 @@ const ( CmdConnectFlagCliOnly = "Disable browser auto-open" CmdConnectPreparingTunnel = "Preparing a tunnel to connect to %s" - CmdConnectErrCluster = "Unable to connect to the cluster: %s" - CmdConnectErrService = "Unable to connect to the service: %s" CmdConnectEstablishedCLI = "Tunnel established at %s, waiting for user to interrupt (ctrl-c to end)" CmdConnectEstablishedWeb = "Tunnel established at %s, opening your default web browser (ctrl-c to end)" CmdConnectTunnelClosed = "Tunnel to %s successfully closed due to user interrupt" @@ -107,7 +104,6 @@ const ( CmdDestroyFlagConfirm = "REQUIRED. Confirm the destroy action to prevent accidental deletions" CmdDestroyFlagRemoveComponents = "Also remove any installed components outside the zarf namespace" - CmdDestroyErrNoScriptPath = "Unable to find the folder (%s) which has the scripts to cleanup the cluster. Please double-check you have the right kube-context" CmdDestroyErrScriptPermissionDenied = "Received 'permission denied' when trying to execute the script (%s). Please double-check you have the correct kube-context." // zarf init @@ -144,12 +140,9 @@ $ zarf init --artifact-push-password={PASSWORD} --artifact-push-username={USERNA # NOTE: Not specifying a pull username/password will use the push user for pulling as well. ` - CmdInitErrFlags = "Invalid command flags were provided." - CmdInitErrDownload = "failed to download the init package: %s" - CmdInitErrValidateGit = "the 'git-push-username' and 'git-push-password' flags must be provided if the 'git-url' flag is provided" - CmdInitErrValidateRegistry = "the 'registry-push-username' and 'registry-push-password' flags must be provided if the 'registry-url' flag is provided" - CmdInitErrValidateArtifact = "the 'artifact-push-username' and 'artifact-push-token' flags must be provided if the 'artifact-url' flag is provided" - CmdInitErrUnableCreateCache = "Unable to create the cache directory: %s" + CmdInitErrValidateGit = "the 'git-push-username' and 'git-push-password' flags must be provided if the 'git-url' flag is provided" + CmdInitErrValidateRegistry = "the 'registry-push-username' and 'registry-push-password' flags must be provided if the 'registry-url' flag is provided" + CmdInitErrValidateArtifact = "the 'artifact-push-username' and 'artifact-push-token' flags must be provided if the 'artifact-url' flag is provided" CmdInitPullAsk = "It seems the init package could not be found locally, but can be pulled from oci://%s" CmdInitPullNote = "Note: This will require an internet connection." @@ -195,13 +188,10 @@ $ zarf init --artifact-push-password={PASSWORD} --artifact-push-username={USERNA CmdInternalGenerateCliDocsShort = "Creates auto-generated markdown of all the commands for the CLI" CmdInternalGenerateCliDocsSuccess = "Successfully created the CLI documentation" - CmdInternalGenerateCliDocsErr = "Unable to generate the CLI documentation: %s" CmdInternalConfigSchemaShort = "Generates a JSON schema for the zarf.yaml configuration" - CmdInternalConfigSchemaErr = "Unable to generate the zarf config schema" CmdInternalTypesSchemaShort = "Generates a JSON schema for the Zarf types (DeployedPackage ZarfPackage ZarfState)" - CmdInternalTypesSchemaErr = "Unable to generate the JSON schema for the Zarf types (DeployedPackage ZarfPackage ZarfState)" CmdInternalCreateReadOnlyGiteaUserShort = "Creates a read-only user in Gitea" CmdInternalCreateReadOnlyGiteaUserLong = "Creates a read-only user in Gitea by using the Gitea API. " + @@ -220,7 +210,6 @@ $ zarf init --artifact-push-password={PASSWORD} --artifact-push-username={USERNA CmdInternalFlagUpdateGiteaPVCRollback = "Roll back previous Gitea persistent volume claim updates." CmdInternalIsValidHostnameShort = "Checks if the current machine's hostname is RFC1123 compliant" - CmdInternalIsValidHostnameErr = "The hostname '%s' is not valid. Ensure the hostname meets RFC1123 requirements https://www.rfc-editor.org/rfc/rfc1123.html." CmdInternalCrc32Short = "Generates a decimal CRC32 for the given text" @@ -267,7 +256,6 @@ $ zarf package mirror-resources \ CmdPackageListShort = "Lists out all of the packages that have been deployed to the cluster (runs offline)" CmdPackageListNoPackageWarn = "Unable to get the packages deployed to the cluster" - CmdPackageListUnmarshalErr = "Unable to read all of the packages deployed to the cluster" CmdPackageCreateFlagConfirm = "Confirm package creation without prompting" CmdPackageCreateFlagSet = "Specify package variables to set on the command line (KEY=value)" @@ -284,7 +272,6 @@ $ zarf package mirror-resources \ CmdPackageCreateFlagRegistryOverride = "Specify a map of domains to override on package create when pulling images (e.g. --registry-override docker.io=dockerio-reg.enterprise.intranet)" CmdPackageCreateFlagFlavor = "The flavor of components to include in the resulting package (i.e. have a matching or empty \"only.flavor\" key)" CmdPackageCreateCleanPathErr = "Invalid characters in Zarf cache path, defaulting to %s" - CmdPackageCreateErr = "Failed to create package: %s" CmdPackageDeployFlagConfirm = "Confirms package deployment without prompting. ONLY use with packages you trust. Skips prompts to review SBOM, configure variables, select optional components and review potential breaking changes." CmdPackageDeployFlagAdoptExistingResources = "Adopts any pre-existing K8s resources into the Helm charts managed by Zarf. ONLY use when you have existing deployments you want Zarf to takeover." @@ -297,7 +284,6 @@ $ zarf package mirror-resources \ CmdPackageDeployValidateArchitectureErr = "this package architecture is %s, but the target cluster only has the %s architecture(s). These architectures must be compatible when \"images\" are present" 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" CmdPackageDeployInvalidCLIVersionWarn = "CLIVersion is set to '%s' which can cause issues with package creation and deployment. To avoid such issues, please set the value to the valid semantic version for this version of Zarf." - CmdPackageDeployErr = "Failed to deploy package: %s" CmdPackageMirrorFlagComponents = "Comma-separated list of components to mirror. This list will be respected regardless of a component's 'required' or 'default' status. Globbing component names with '*' and deselecting components with a leading '-' are also supported." CmdPackageMirrorFlagNoChecksum = "Turns off the addition of a checksum to image tags (as would be used by the Zarf Agent) while mirroring images." @@ -305,14 +291,10 @@ $ zarf package mirror-resources \ CmdPackageInspectFlagSbom = "View SBOM contents while inspecting the package" CmdPackageInspectFlagSbomOut = "Specify an output directory for the SBOMs from the inspected Zarf package" CmdPackageInspectFlagListImages = "List images in the package (prints to stdout)" - CmdPackageInspectErr = "Failed to inspect package: %s" CmdPackageRemoveShort = "Removes a Zarf package that has been deployed already (runs offline)" CmdPackageRemoveFlagConfirm = "REQUIRED. Confirm the removal action to prevent accidental deletions" CmdPackageRemoveFlagComponents = "Comma-separated list of components to remove. This list will be respected regardless of a component's 'required' or 'default' status. Globbing component names with '*' and deselecting components with a leading '-' are also supported." - CmdPackageRemoveErr = "Unable to remove the package with an error of: %s" - - CmdPackageRegistryPrefixErr = "Registry must be prefixed with 'oci://'" CmdPackagePublishShort = "Publishes a Zarf package to a remote registry" CmdPackagePublishExample = ` @@ -324,7 +306,6 @@ $ zarf package publish ./path/to/dir oci://my-registry.com/my-namespace ` CmdPackagePublishFlagSigningKey = "Path to a private key file for signing or re-signing packages with a new key" CmdPackagePublishFlagSigningKeyPassword = "Password to the private key file used for publishing packages" - CmdPackagePublishErr = "Failed to publish package: %s" CmdPackagePullShort = "Pulls a Zarf package from a remote registry and save to the local file system" CmdPackagePullExample = ` @@ -337,10 +318,8 @@ $ zarf package pull oci://ghcr.io/defenseunicorns/packages/dos-games:1.0.0 -a ar # Pull a skeleton package $ zarf package pull oci://ghcr.io/defenseunicorns/packages/dos-games:1.0.0 -a skeleton` CmdPackagePullFlagOutputDirectory = "Specify the output directory for the pulled Zarf package" - CmdPackagePullErr = "Failed to pull package: %s" CmdPackageChoose = "Choose or type the package file" - CmdPackageChooseErr = "Package path selection canceled: %s" CmdPackageClusterSourceFallback = "%q does not satisfy any current sources, assuming it is a package deployed to a cluster" CmdPackageInvalidSource = "Unable to identify source from %q: %s" @@ -350,7 +329,6 @@ $ zarf package pull oci://ghcr.io/defenseunicorns/packages/dos-games:1.0.0 -a sk CmdDevDeployShort = "[beta] Creates and deploys a Zarf package from a given directory" CmdDevDeployLong = "[beta] Creates and deploys a Zarf package from a given directory, setting options like YOLO mode for faster iteration." CmdDevDeployFlagNoYolo = "Disable the YOLO mode default override and create / deploy the package as-defined" - CmdDevDeployErr = "Failed to dev deploy: %s" CmdDevGenerateShort = "[alpha] Creates a zarf.yaml automatically from a given remote (git) Helm chart" CmdDevGenerateExample = "zarf dev generate podinfo --url https://github.com/stefanprodan/podinfo.git --version 6.4.0 --gitPath charts/podinfo" @@ -358,25 +336,19 @@ $ zarf package pull oci://ghcr.io/defenseunicorns/packages/dos-games:1.0.0 -a sk CmdDevPatchGitShort = "Converts all .git URLs to the specified Zarf HOST and with the Zarf URL pattern in a given FILE. NOTE:\n" + "This should only be used for manifests that are not mutated by the Zarf Agent Mutating Webhook." CmdDevPatchGitOverwritePrompt = "Overwrite the file %s with these changes?" - CmdDevPatchGitOverwriteErr = "Confirm overwrite canceled: %s" - CmdDevPatchGitFileReadErr = "Unable to read the file %s" - CmdDevPatchGitFileWriteErr = "Unable to write the changes back to the file" CmdDevSha256sumShort = "Generates a SHA256SUM for the given file" CmdDevSha256sumRemoteWarning = "This is a remote source. If a published checksum is available you should use that rather than calculating it directly from the remote link." - CmdDevSha256sumHashErr = "Unable to compute the SHA256SUM hash: %s" CmdDevFindImagesShort = "Evaluates components in a Zarf file to identify images specified in their helm charts and manifests" CmdDevFindImagesLong = "Evaluates components in a Zarf file to identify images specified in their helm charts and manifests.\n\n" + "Components that have repos that host helm charts can be processed by providing the --repo-chart-path." - CmdDevFindImagesErr = "Unable to find images: %s" CmdDevGenerateConfigShort = "Generates a config file for Zarf" CmdDevGenerateConfigLong = "Generates a Zarf config file for controlling how the Zarf CLI operates. Optionally accepts a filename to write the config to.\n\n" + "The extension will determine the format of the config file, e.g. env-1.yaml, env-2.json, env-3.toml etc.\n" + "Accepted extensions are json, toml, yaml.\n\n" + "NOTE: This file must not already exist. If no filename is provided, the config will be written to the current working directory as zarf-config.toml." - CmdDevGenerateConfigErr = "Unable to write the config file %s, make sure the file doesn't already exist" CmdDevFlagExtractPath = `The path inside of an archive to use to calculate the sha256sum (i.e. for use with "files.extractPath")` CmdDevFlagSet = "Specify package variables to set on the command line (KEY=value). Note, if using a config file, this will be set by [package.create.set]." @@ -395,11 +367,7 @@ $ zarf package pull oci://ghcr.io/defenseunicorns/packages/dos-games:1.0.0 -a sk CmdToolsArchiverShort = "Compresses/Decompresses generic archives, including Zarf packages" CmdToolsArchiverCompressShort = "Compresses a collection of sources based off of the destination file extension." - CmdToolsArchiverCompressErr = "Unable to perform compression: %s" CmdToolsArchiverDecompressShort = "Decompresses an archive or Zarf package based off of the source file extension." - CmdToolsArchiverDecompressErr = "Unable to perform decompression: %s" - - CmdToolsArchiverUnarchiveAllErr = "Unable to unarchive all nested tarballs: %s" CmdToolsRegistryShort = "Tools for working with container registries using go-containertools" CmdToolsRegistryZarfState = "Retrieving registry information from Zarf state" @@ -461,11 +429,10 @@ $ zarf tools registry digest reg.example.com/stefanprodan/podinfo:6.4.0 CmdToolsRegistryPruneCalculate = "Calculating images to prune" CmdToolsRegistryPruneDelete = "Deleting unused images" - CmdToolsRegistryInvalidPlatformErr = "Invalid platform '%s': %s" - CmdToolsRegistryFlagVerbose = "Enable debug logs" - CmdToolsRegistryFlagInsecure = "Allow image references to be fetched without TLS" - CmdToolsRegistryFlagNonDist = "Allow pushing non-distributable (foreign) layers" - CmdToolsRegistryFlagPlatform = "Specifies the platform in the form os/arch[/variant][:osversion] (e.g. linux/amd64)." + CmdToolsRegistryFlagVerbose = "Enable debug logs" + CmdToolsRegistryFlagInsecure = "Allow image references to be fetched without TLS" + CmdToolsRegistryFlagNonDist = "Allow pushing non-distributable (foreign) layers" + CmdToolsRegistryFlagPlatform = "Specifies the platform in the form os/arch[/variant][:osversion] (e.g. linux/amd64)." 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." @@ -521,30 +488,25 @@ zarf tools yq e '.a.b = "cool"' -i file.yaml CmdToolsClearCacheShort = "Clears the configured git and image cache directory" CmdToolsClearCacheDir = "Cache directory set to: %s" - CmdToolsClearCacheErr = "Unable to clear the cache directory %s" CmdToolsClearCacheSuccess = "Successfully cleared the cache from %s" CmdToolsClearCacheFlagCachePath = "Specify the location of the Zarf artifact cache (images and git repositories)" - CmdToolsCraneNotEnoughArgumentsErr = "You do not have enough arguments specified for this command" CmdToolsCraneConnectedButBadStateErr = "Detected a K8s cluster but was unable to get Zarf state - continuing without state information: %s" CmdToolsDownloadInitShort = "Downloads the init package for the current Zarf version into the specified directory" CmdToolsDownloadInitFlagOutputDirectory = "Specify a directory to place the init package in." - CmdToolsDownloadInitErr = "Unable to download the init package: %s" CmdToolsGenPkiShort = "Generates a Certificate Authority and PKI chain of trust for the given host" CmdToolsGenPkiSuccess = "Successfully created a chain of trust for %s" CmdToolsGenPkiFlagAltName = "Specify Subject Alternative Names for the certificate" - CmdToolsGenKeyShort = "Generates a cosign public/private keypair that can be used to sign packages" - CmdToolsGenKeyPrompt = "Private key password (empty for no password): " - CmdToolsGenKeyPromptAgain = "Private key password again (empty for no password): " - CmdToolsGenKeyPromptExists = "File %s already exists. Overwrite? " - CmdToolsGenKeyErrUnableGetPassword = "unable to get password for private key: %s" - CmdToolsGenKeyErrPasswordsNotMatch = "passwords do not match" - CmdToolsGenKeyErrUnableToGenKeypair = "unable to generate key pair: %s" - CmdToolsGenKeyErrNoConfirmOverwrite = "did not receive confirmation for overwriting key file(s)" - CmdToolsGenKeySuccess = "Generated key pair and written to %s and %s" + CmdToolsGenKeyShort = "Generates a cosign public/private keypair that can be used to sign packages" + CmdToolsGenKeyPrompt = "Private key password (empty for no password): " + CmdToolsGenKeyPromptAgain = "Private key password again (empty for no password): " + CmdToolsGenKeyPromptExists = "File %s already exists. Overwrite? " + CmdToolsGenKeyErrUnableGetPassword = "unable to get password for private key: %s" + CmdToolsGenKeyErrPasswordsNotMatch = "passwords do not match" + CmdToolsGenKeySuccess = "Generated key pair and written to %s and %s" CmdToolsSbomShort = "Generates a Software Bill of Materials (SBOM) for the given package" @@ -620,7 +582,6 @@ $ zarf tools update-creds artifact --artifact-push-username={USERNAME} --artifac 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 values: %s" CmdToolsUpdateCredsUnableUpdateGit = "Unable to update Zarf Git Server values: %s" diff --git a/src/pkg/cluster/cluster.go b/src/pkg/cluster/cluster.go index 0aad1ad848..5d15550482 100644 --- a/src/pkg/cluster/cluster.go +++ b/src/pkg/cluster/cluster.go @@ -6,6 +6,7 @@ package cluster import ( "context" + "errors" "fmt" "time" @@ -55,13 +56,14 @@ func NewClusterWithWait(ctx context.Context) (*Cluster, error) { // NewCluster creates a new Cluster instance and validates connection to the cluster by fetching the Kubernetes version. func NewCluster() (*Cluster, error) { + clusterErr := errors.New("unable to connect to the cluster") clientset, config, err := pkgkubernetes.ClientAndConfig() if err != nil { - return nil, err + return nil, errors.Join(clusterErr, err) } watcher, err := pkgkubernetes.WatcherForConfig(config) if err != nil { - return nil, err + return nil, errors.Join(clusterErr, err) } c := &Cluster{ Clientset: clientset, @@ -71,7 +73,7 @@ func NewCluster() (*Cluster, error) { // Dogsled the version output. We just want to ensure no errors were returned to validate cluster connection. _, err = c.Clientset.Discovery().ServerVersion() if err != nil { - return nil, err + return nil, errors.Join(clusterErr, err) } return c, nil } diff --git a/src/pkg/cluster/state.go b/src/pkg/cluster/state.go index 5471628667..d194065125 100644 --- a/src/pkg/cluster/state.go +++ b/src/pkg/cluster/state.go @@ -206,19 +206,15 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO // LoadZarfState returns the current zarf/zarf-state secret data or an empty ZarfState. func (c *Cluster) LoadZarfState(ctx context.Context) (state *types.ZarfState, err error) { - // Set up the API connection secret, err := c.Clientset.CoreV1().Secrets(ZarfNamespaceName).Get(ctx, ZarfStateSecretName, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("%w. %s", err, message.ColorWrap("Did you remember to zarf init?", color.Bold)) } - err = json.Unmarshal(secret.Data[ZarfStateDataKey], &state) if err != nil { return nil, err } - c.debugPrintZarfState(state) - return state, nil } diff --git a/src/test/e2e/02_component_actions_test.go b/src/test/e2e/02_component_actions_test.go index 6eda343103..b709c0c267 100644 --- a/src/test/e2e/02_component_actions_test.go +++ b/src/test/e2e/02_component_actions_test.go @@ -149,7 +149,7 @@ func TestComponentActions(t *testing.T) { t.Parallel() stdOut, stdErr, err = e2e.Zarf("package", "deploy", path, "--components=on-deploy-immediate-failure", "--confirm") require.Error(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "Failed to deploy package") + require.Contains(t, stdErr, "failed to deploy package") // regression test to ensure that failed commands are not erroneously flagged as a timeout require.NotContains(t, stdErr, "timed out") }) diff --git a/src/test/e2e/09_component_compose_test.go b/src/test/e2e/09_component_compose_test.go index 0d5acf7c0e..c5edaeb682 100644 --- a/src/test/e2e/09_component_compose_test.go +++ b/src/test/e2e/09_component_compose_test.go @@ -185,7 +185,7 @@ func (suite *CompositionSuite) Test_2_ComposabilityBadLocalOS() { _, stdErr, err := e2e.Zarf("package", "create", composeTestBadLocalOS, "-o", "build", "--no-color", "--confirm") suite.Error(err) - suite.Contains(stdErr, "\"only.localOS\" \"linux\" cannot be\n redefined as \"windows\" during compose") + suite.Contains(stdErr, "\"only.localOS\" \"linux\" cannot be redefined as \"windows\" during compose") } func TestCompositionSuite(t *testing.T) { diff --git a/src/test/e2e/22_git_and_gitops_test.go b/src/test/e2e/22_git_and_gitops_test.go index 263ddccb55..8b53bacfaf 100644 --- a/src/test/e2e/22_git_and_gitops_test.go +++ b/src/test/e2e/22_git_and_gitops_test.go @@ -13,7 +13,6 @@ import ( "path/filepath" "testing" - "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/internal/packager/git" "github.com/defenseunicorns/zarf/src/pkg/cluster" "github.com/defenseunicorns/zarf/src/types" @@ -69,8 +68,13 @@ func testGitServerConnect(t *testing.T, gitURL string) { } func testGitServerReadOnly(ctx context.Context, t *testing.T, gitURL string) { + timeoutCtx, cancel := context.WithTimeout(ctx, cluster.DefaultTimeout) + defer cancel() + c, err := cluster.NewClusterWithWait(timeoutCtx) + require.NoError(t, err) + // Init the state variable - zarfState, err := common.NewClusterOrDie(ctx).LoadZarfState(ctx) + zarfState, err := c.LoadZarfState(ctx) require.NoError(t, err) gitCfg := git.New(zarfState.GitServer) @@ -92,8 +96,13 @@ func testGitServerReadOnly(ctx context.Context, t *testing.T, gitURL string) { } func testGitServerTagAndHash(ctx context.Context, t *testing.T, gitURL string) { + timeoutCtx, cancel := context.WithTimeout(ctx, cluster.DefaultTimeout) + defer cancel() + c, err := cluster.NewClusterWithWait(timeoutCtx) + require.NoError(t, err) + // Init the state variable - zarfState, err := common.NewClusterOrDie(ctx).LoadZarfState(ctx) + zarfState, err := c.LoadZarfState(ctx) require.NoError(t, err, "Failed to load Zarf state") repoName := "zarf-public-test-2469062884" @@ -121,7 +130,8 @@ func testGitServerTagAndHash(ctx context.Context, t *testing.T, gitURL string) { func waitFluxPodInfoDeployment(t *testing.T) { ctx := context.Background() - cluster := common.NewClusterOrDie(ctx) + cluster, err := cluster.NewClusterWithWait(ctx) + require.NoError(t, err) zarfState, err := cluster.LoadZarfState(ctx) require.NoError(t, err, "Failed to load Zarf state") registryAddress, err := cluster.GetServiceInfoFromRegistryAddress(ctx, zarfState.RegistryInfo.Address) diff --git a/src/test/e2e/31_checksum_and_signature_test.go b/src/test/e2e/31_checksum_and_signature_test.go index 08f7f2cdde..957deaeaa8 100644 --- a/src/test/e2e/31_checksum_and_signature_test.go +++ b/src/test/e2e/31_checksum_and_signature_test.go @@ -38,7 +38,7 @@ func TestChecksumAndSignature(t *testing.T) { // Test that we get an error when trying to deploy a package without providing the public key stdOut, stdErr, err = e2e.Zarf("package", "deploy", pkgName, "--confirm") require.Error(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "Failed to deploy package: unable to load the package: package is signed but no key was provided") + require.Contains(t, stdErr, "failed to deploy package: unable to load the package: package is signed but no key was provided - add a key with the --key flag or use the --insecure flag and run the command again") // Test that we don't get an error when we remember to provide the public key stdOut, stdErr, err = e2e.Zarf("package", "deploy", pkgName, publicKeyFlag, "--confirm")