From ffcca6051e390ddc200f22bc405e3c6615536dce Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Thu, 16 May 2024 15:30:27 -0600 Subject: [PATCH 01/15] add some config file validation --- src/cmd/uds.go | 2 +- src/test/e2e/bundle_test.go | 13 +++++++++++++ src/test/e2e/commands_test.go | 6 ++++++ src/types/options.go | 1 + 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/cmd/uds.go b/src/cmd/uds.go index 808dbea2..ac5decf8 100644 --- a/src/cmd/uds.go +++ b/src/cmd/uds.go @@ -216,7 +216,7 @@ func loadViperConfig() error { // read relevant config into DeployOpts.Variables // need to use goyaml because Viper doesn't preserve case: https://github.com/spf13/viper/issues/1014 - err = goyaml.Unmarshal(configFile, &bundleCfg.DeployOpts) + err = goyaml.UnmarshalWithOptions(configFile, &bundleCfg.DeployOpts, goyaml.Strict()) if err != nil { return err } diff --git a/src/test/e2e/bundle_test.go b/src/test/e2e/bundle_test.go index 271ef814..a966232b 100644 --- a/src/test/e2e/bundle_test.go +++ b/src/test/e2e/bundle_test.go @@ -601,3 +601,16 @@ func TestBundleTmpDir(t *testing.T) { err = os.RemoveAll("./customtmp") require.NoError(t, err) } + +func TestInvalidConfig(t *testing.T) { + os.Setenv("UDS_CONFIG", filepath.Join("src/test/bundles/07-helm-overrides", "uds-config-invalid.yaml")) + deployZarfInit(t) + zarfPkgPath := "src/test/packages/helm" + e2e.HelmDepUpdate(t, fmt.Sprintf("%s/unicorn-podinfo", zarfPkgPath)) + e2e.CreateZarfPkg(t, zarfPkgPath, false) + bundleDir := "src/test/bundles/07-helm-overrides" + createLocal(t, bundleDir, e2e.Arch) + bundlePath := filepath.Join(bundleDir, fmt.Sprintf("uds-bundle-helm-overrides-%s-0.0.1.tar.zst", e2e.Arch)) + _, stderr := deployWithError(t, bundlePath) + require.Contains(t, stderr, "unknown field") +} diff --git a/src/test/e2e/commands_test.go b/src/test/e2e/commands_test.go index bcc89fda..2ea01284 100644 --- a/src/test/e2e/commands_test.go +++ b/src/test/e2e/commands_test.go @@ -112,6 +112,12 @@ func deploy(t *testing.T, tarballPath string) (stdout string, stderr string) { return stdout, stderr } +func deployWithError(t *testing.T, tarballPath string) (stdout string, stderr string) { + cmd := strings.Split(fmt.Sprintf("deploy %s --retries 1 --confirm --no-tea", tarballPath), " ") + stdout, stderr, _ = e2e.UDS(cmd...) + return stdout, stderr +} + func deployWithTUI(t *testing.T, source string) (stdout string, stderr string) { cmd := strings.Split(fmt.Sprintf("deploy %s --confirm", source), " ") stdout, stderr, err := e2e.UDS(cmd...) diff --git a/src/types/options.go b/src/types/options.go index d2a5cbdc..9e3f15be 100644 --- a/src/types/options.go +++ b/src/types/options.go @@ -34,6 +34,7 @@ type BundleDeployOptions struct { Variables map[string]map[string]interface{} `yaml:"variables,omitempty"` SharedVariables map[string]interface{} `yaml:"shared,omitempty"` Retries int `yaml:"retries"` + Options map[string]interface{} `yaml:"options,omitempty"` } // BundleInspectOptions is the options for the bundler.Inspect() function From a80f56c43a1c3567b31cd56ab55fbbcc337838f7 Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Thu, 16 May 2024 15:34:34 -0600 Subject: [PATCH 02/15] add test config --- src/test/bundles/07-helm-overrides/uds-config-invalid.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/test/bundles/07-helm-overrides/uds-config-invalid.yaml diff --git a/src/test/bundles/07-helm-overrides/uds-config-invalid.yaml b/src/test/bundles/07-helm-overrides/uds-config-invalid.yaml new file mode 100644 index 00000000..e73644fb --- /dev/null +++ b/src/test/bundles/07-helm-overrides/uds-config-invalid.yaml @@ -0,0 +1,6 @@ +options: + log_level: debug + +# helm-overrides is an invalid top level key. Missing `variables` parent key. +helm-overrides: + ui_color: "orange" From 8ad38538cb538ebb6b3c15e0a747138b4f71a287 Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Thu, 16 May 2024 16:16:41 -0600 Subject: [PATCH 03/15] unset test env --- src/test/e2e/bundle_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/e2e/bundle_test.go b/src/test/e2e/bundle_test.go index a966232b..a7d27fd7 100644 --- a/src/test/e2e/bundle_test.go +++ b/src/test/e2e/bundle_test.go @@ -613,4 +613,5 @@ func TestInvalidConfig(t *testing.T) { bundlePath := filepath.Join(bundleDir, fmt.Sprintf("uds-bundle-helm-overrides-%s-0.0.1.tar.zst", e2e.Arch)) _, stderr := deployWithError(t, bundlePath) require.Contains(t, stderr, "unknown field") + os.Unsetenv("UDS_CONFIG") } From 30a91b33e10c08678b8545dc058a6c99385dfb83 Mon Sep 17 00:00:00 2001 From: decleaver <85503726+decleaver@users.noreply.github.com> Date: Tue, 21 May 2024 09:02:45 -0600 Subject: [PATCH 04/15] Update src/test/e2e/commands_test.go Co-authored-by: UncleGedd <42304551+UncleGedd@users.noreply.github.com> --- src/test/e2e/commands_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/e2e/commands_test.go b/src/test/e2e/commands_test.go index 2ea01284..44a00972 100644 --- a/src/test/e2e/commands_test.go +++ b/src/test/e2e/commands_test.go @@ -112,7 +112,7 @@ func deploy(t *testing.T, tarballPath string) (stdout string, stderr string) { return stdout, stderr } -func deployWithError(t *testing.T, tarballPath string) (stdout string, stderr string) { +func deployWithError(_ *testing.T, tarballPath string) (stdout string, stderr string) { cmd := strings.Split(fmt.Sprintf("deploy %s --retries 1 --confirm --no-tea", tarballPath), " ") stdout, stderr, _ = e2e.UDS(cmd...) return stdout, stderr From f34084c05942d4fa09c56338e95a6286179c7d83 Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Fri, 24 May 2024 16:25:48 -0600 Subject: [PATCH 05/15] updates to validate config file options values --- src/cmd/uds.go | 21 ++++++- src/cmd/uds_test.go | 61 +++++++++++++++++++ .../07-helm-overrides/uds-config-invalid.yaml | 7 ++- src/test/e2e/bundle_test.go | 3 +- src/types/options.go | 22 +++++++ 5 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 src/cmd/uds_test.go diff --git a/src/cmd/uds.go b/src/cmd/uds.go index 3d3dbebc..9043225f 100644 --- a/src/cmd/uds.go +++ b/src/cmd/uds.go @@ -16,6 +16,7 @@ import ( "github.com/defenseunicorns/uds-cli/src/config" "github.com/defenseunicorns/uds-cli/src/config/lang" "github.com/defenseunicorns/uds-cli/src/pkg/bundle" + "github.com/defenseunicorns/uds-cli/src/types" "github.com/defenseunicorns/zarf/src/pkg/message" goyaml "github.com/goccy/go-yaml" "github.com/spf13/cobra" @@ -193,9 +194,7 @@ func loadViperConfig() error { return err } - // read relevant config into DeployOpts.Variables - // need to use goyaml because Viper doesn't preserve case: https://github.com/spf13/viper/issues/1014 - err = goyaml.UnmarshalWithOptions(configFile, &bundleCfg.DeployOpts, goyaml.Strict()) + err = UnmarshalAndValidateConfig(configFile, &bundleCfg) if err != nil { return err } @@ -287,3 +286,19 @@ func chooseBundle(args []string) string { return path } + +func UnmarshalAndValidateConfig(configFile []byte, bundleCfg *types.BundleConfig) error { + // read relevant config into DeployOpts.Variables + // need to use goyaml because Viper doesn't preserve case: https://github.com/spf13/viper/issues/1014 + err := goyaml.UnmarshalWithOptions(configFile, &bundleCfg.DeployOpts, goyaml.Strict()) + if err != nil { + return err + } + // validate config options + for optionName := range bundleCfg.DeployOpts.Options { + if !types.IsValidConfigOption(optionName) { + return fmt.Errorf("invalid config option: %s", optionName) + } + } + return nil +} diff --git a/src/cmd/uds_test.go b/src/cmd/uds_test.go new file mode 100644 index 00000000..c2957d89 --- /dev/null +++ b/src/cmd/uds_test.go @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023-Present The UDS Authors + +// Package cmd contains the CLI commands for UDS. +package cmd + +import ( + "testing" + + "github.com/defenseunicorns/uds-cli/src/types" + "github.com/stretchr/testify/require" +) + +func TestUnmarshalAndValidateConfig(t *testing.T) { + type args struct { + configFile []byte + bundleCfg *types.BundleConfig + } + tests := []struct { + name string + args args + wantErr bool + errContains string + }{ + { + name: "Invalid option key", + args: args{ + configFile: []byte(` +options: + log_levelx: debug +`), + bundleCfg: &types.BundleConfig{}, + }, + wantErr: true, + errContains: "invalid config option: log_levelx", + }, + { + name: "Option typo", + args: args{ + configFile: []byte(` +optionx: + log_level: debug +`), + bundleCfg: &types.BundleConfig{}, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := UnmarshalAndValidateConfig(tt.args.configFile, tt.args.bundleCfg) + if tt.wantErr { + require.NotNil(t, err, "Expected error") + require.Contains(t, err.Error(), tt.errContains, "Error message should contain the expected string") + } else { + require.Nil(t, err, "Expected no error") + } + }) + } +} diff --git a/src/test/bundles/07-helm-overrides/uds-config-invalid.yaml b/src/test/bundles/07-helm-overrides/uds-config-invalid.yaml index e73644fb..492f50e4 100644 --- a/src/test/bundles/07-helm-overrides/uds-config-invalid.yaml +++ b/src/test/bundles/07-helm-overrides/uds-config-invalid.yaml @@ -1,6 +1,7 @@ options: - log_level: debug + log_levelx: debug # helm-overrides is an invalid top level key. Missing `variables` parent key. -helm-overrides: - ui_color: "orange" +variables: + helm-overrides: + ui_color: "orange" diff --git a/src/test/e2e/bundle_test.go b/src/test/e2e/bundle_test.go index a7d27fd7..bb59e1c1 100644 --- a/src/test/e2e/bundle_test.go +++ b/src/test/e2e/bundle_test.go @@ -604,7 +604,6 @@ func TestBundleTmpDir(t *testing.T) { func TestInvalidConfig(t *testing.T) { os.Setenv("UDS_CONFIG", filepath.Join("src/test/bundles/07-helm-overrides", "uds-config-invalid.yaml")) - deployZarfInit(t) zarfPkgPath := "src/test/packages/helm" e2e.HelmDepUpdate(t, fmt.Sprintf("%s/unicorn-podinfo", zarfPkgPath)) e2e.CreateZarfPkg(t, zarfPkgPath, false) @@ -612,6 +611,6 @@ func TestInvalidConfig(t *testing.T) { createLocal(t, bundleDir, e2e.Arch) bundlePath := filepath.Join(bundleDir, fmt.Sprintf("uds-bundle-helm-overrides-%s-0.0.1.tar.zst", e2e.Arch)) _, stderr := deployWithError(t, bundlePath) - require.Contains(t, stderr, "unknown field") + require.Contains(t, stderr, "invalid config option: log_levelx") os.Unsetenv("UDS_CONFIG") } diff --git a/src/types/options.go b/src/types/options.go index 7a3ef415..aa8c024b 100644 --- a/src/types/options.go +++ b/src/types/options.go @@ -37,6 +37,28 @@ type BundleDeployOptions struct { Options map[string]interface{} `yaml:"options,omitempty"` } +type ConfigOption string + +const ( + Confirm ConfigOption = "confirm" + Insecure ConfigOption = "insecure" + CachePath ConfigOption = "uds_cache" + TempDirectory ConfigOption = "tmp_dir" + LogLevel ConfigOption = "log_level" + Architecture ConfigOption = "architecture" + NoLogFile ConfigOption = "no_log_file" + NoProgress ConfigOption = "no_progress" +) + +func IsValidConfigOption(str string) bool { + switch ConfigOption(str) { + case Confirm, Insecure, CachePath, TempDirectory, LogLevel, Architecture, NoLogFile, NoProgress: + return true + default: + return false + } +} + // BundleInspectOptions is the options for the bundler.Inspect() function type BundleInspectOptions struct { PublicKeyPath string From 8c2aa3a082dcd41c200f38f4d0a2af7dffaf454b Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Tue, 28 May 2024 13:30:20 -0600 Subject: [PATCH 06/15] make config options and unmarshal fn private --- src/cmd/common.go | 24 ++++++++++++++++++++++++ src/cmd/uds.go | 6 +++--- src/cmd/uds_test.go | 2 +- src/types/options.go | 22 ---------------------- 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/cmd/common.go b/src/cmd/common.go index 12c498b9..f7ef3ef9 100644 --- a/src/cmd/common.go +++ b/src/cmd/common.go @@ -20,6 +20,30 @@ import ( "github.com/spf13/cobra" ) +type ConfigOption string + +// Valid values for options in uds_config.yaml +const ( + Confirm ConfigOption = "confirm" + Insecure ConfigOption = "insecure" + CachePath ConfigOption = "uds_cache" + TempDirectory ConfigOption = "tmp_dir" + LogLevel ConfigOption = "log_level" + Architecture ConfigOption = "architecture" + NoLogFile ConfigOption = "no_log_file" + NoProgress ConfigOption = "no_progress" +) + +// isValidConfigOption checks if a string is a valid config option +func isValidConfigOption(str string) bool { + switch ConfigOption(str) { + case Confirm, Insecure, CachePath, TempDirectory, LogLevel, Architecture, NoLogFile, NoProgress: + return true + default: + return false + } +} + // deploy performs validation, confirmation and deployment of a bundle func deploy(bndlClient *bundle.Bundle) { _, _, _, err := bndlClient.PreDeployValidation() diff --git a/src/cmd/uds.go b/src/cmd/uds.go index 9043225f..ffbfdadb 100644 --- a/src/cmd/uds.go +++ b/src/cmd/uds.go @@ -194,7 +194,7 @@ func loadViperConfig() error { return err } - err = UnmarshalAndValidateConfig(configFile, &bundleCfg) + err = unmarshalAndValidateConfig(configFile, &bundleCfg) if err != nil { return err } @@ -287,7 +287,7 @@ func chooseBundle(args []string) string { return path } -func UnmarshalAndValidateConfig(configFile []byte, bundleCfg *types.BundleConfig) error { +func unmarshalAndValidateConfig(configFile []byte, bundleCfg *types.BundleConfig) error { // read relevant config into DeployOpts.Variables // need to use goyaml because Viper doesn't preserve case: https://github.com/spf13/viper/issues/1014 err := goyaml.UnmarshalWithOptions(configFile, &bundleCfg.DeployOpts, goyaml.Strict()) @@ -296,7 +296,7 @@ func UnmarshalAndValidateConfig(configFile []byte, bundleCfg *types.BundleConfig } // validate config options for optionName := range bundleCfg.DeployOpts.Options { - if !types.IsValidConfigOption(optionName) { + if !isValidConfigOption(optionName) { return fmt.Errorf("invalid config option: %s", optionName) } } diff --git a/src/cmd/uds_test.go b/src/cmd/uds_test.go index c2957d89..ef0b8659 100644 --- a/src/cmd/uds_test.go +++ b/src/cmd/uds_test.go @@ -49,7 +49,7 @@ optionx: for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := UnmarshalAndValidateConfig(tt.args.configFile, tt.args.bundleCfg) + err := unmarshalAndValidateConfig(tt.args.configFile, tt.args.bundleCfg) if tt.wantErr { require.NotNil(t, err, "Expected error") require.Contains(t, err.Error(), tt.errContains, "Error message should contain the expected string") diff --git a/src/types/options.go b/src/types/options.go index aa8c024b..7a3ef415 100644 --- a/src/types/options.go +++ b/src/types/options.go @@ -37,28 +37,6 @@ type BundleDeployOptions struct { Options map[string]interface{} `yaml:"options,omitempty"` } -type ConfigOption string - -const ( - Confirm ConfigOption = "confirm" - Insecure ConfigOption = "insecure" - CachePath ConfigOption = "uds_cache" - TempDirectory ConfigOption = "tmp_dir" - LogLevel ConfigOption = "log_level" - Architecture ConfigOption = "architecture" - NoLogFile ConfigOption = "no_log_file" - NoProgress ConfigOption = "no_progress" -) - -func IsValidConfigOption(str string) bool { - switch ConfigOption(str) { - case Confirm, Insecure, CachePath, TempDirectory, LogLevel, Architecture, NoLogFile, NoProgress: - return true - default: - return false - } -} - // BundleInspectOptions is the options for the bundler.Inspect() function type BundleInspectOptions struct { PublicKeyPath string From f303bde255de8c228418efa22f53aa0010e3952a Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Tue, 28 May 2024 14:04:14 -0600 Subject: [PATCH 07/15] make configOption private --- src/cmd/common.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cmd/common.go b/src/cmd/common.go index f7ef3ef9..fc35d86f 100644 --- a/src/cmd/common.go +++ b/src/cmd/common.go @@ -20,24 +20,24 @@ import ( "github.com/spf13/cobra" ) -type ConfigOption string +type configOption string // Valid values for options in uds_config.yaml const ( - Confirm ConfigOption = "confirm" - Insecure ConfigOption = "insecure" - CachePath ConfigOption = "uds_cache" - TempDirectory ConfigOption = "tmp_dir" - LogLevel ConfigOption = "log_level" - Architecture ConfigOption = "architecture" - NoLogFile ConfigOption = "no_log_file" - NoProgress ConfigOption = "no_progress" + confirm configOption = "confirm" + insecure configOption = "insecure" + cachePath configOption = "uds_cache" + tempDirectory configOption = "tmp_dir" + logLevelOption configOption = "log_level" + architecture configOption = "architecture" + noLogFile configOption = "no_log_file" + noProgress configOption = "no_progress" ) // isValidConfigOption checks if a string is a valid config option func isValidConfigOption(str string) bool { - switch ConfigOption(str) { - case Confirm, Insecure, CachePath, TempDirectory, LogLevel, Architecture, NoLogFile, NoProgress: + switch configOption(str) { + case confirm, insecure, cachePath, tempDirectory, logLevelOption, architecture, noLogFile, noProgress: return true default: return false From 894a99504f519c79a3762b23934c5147e37144ce Mon Sep 17 00:00:00 2001 From: decleaver <85503726+decleaver@users.noreply.github.com> Date: Tue, 28 May 2024 14:31:31 -0600 Subject: [PATCH 08/15] Update src/test/bundles/07-helm-overrides/uds-config-invalid.yaml old comment. thx Co-authored-by: UncleGedd <42304551+UncleGedd@users.noreply.github.com> --- src/test/bundles/07-helm-overrides/uds-config-invalid.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/bundles/07-helm-overrides/uds-config-invalid.yaml b/src/test/bundles/07-helm-overrides/uds-config-invalid.yaml index 492f50e4..a93de69f 100644 --- a/src/test/bundles/07-helm-overrides/uds-config-invalid.yaml +++ b/src/test/bundles/07-helm-overrides/uds-config-invalid.yaml @@ -1,7 +1,6 @@ options: log_levelx: debug -# helm-overrides is an invalid top level key. Missing `variables` parent key. variables: helm-overrides: ui_color: "orange" From abc1d8e45a8641d242d22321c53f81a9d1a6cdd7 Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Tue, 28 May 2024 14:53:39 -0600 Subject: [PATCH 09/15] move config file check to root init() so it applies to all cmds --- src/cmd/root.go | 8 ++++++++ src/cmd/uds.go | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cmd/root.go b/src/cmd/root.go index 3b1e0b7d..e07e8e53 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -86,4 +86,12 @@ func init() { rootCmd.PersistentFlags().StringVar(&config.CommonOptions.TempDirectory, "tmpdir", v.GetString(V_TMP_DIR), lang.RootCmdFlagTempDir) rootCmd.PersistentFlags().BoolVar(&config.CommonOptions.Insecure, "insecure", v.GetBool(V_INSECURE), lang.RootCmdFlagInsecure) rootCmd.PersistentFlags().IntVar(&config.CommonOptions.OCIConcurrency, "oci-concurrency", v.GetInt(V_BNDL_OCI_CONCURRENCY), lang.CmdBundleFlagConcurrency) + + // load uds-config if it exists + if v.ConfigFileUsed() != "" { + if err := loadViperConfig(); err != nil { + message.Fatalf(err, "Failed to load uds-config: %s", err.Error()) + return + } + } } diff --git a/src/cmd/uds.go b/src/cmd/uds.go index ffbfdadb..e314a17d 100644 --- a/src/cmd/uds.go +++ b/src/cmd/uds.go @@ -59,14 +59,6 @@ var deployCmd = &cobra.Command{ bundleCfg.DeployOpts.Source = chooseBundle(args) configureZarf() - // load uds-config if it exists - if v.ConfigFileUsed() != "" { - if err := loadViperConfig(); err != nil { - message.Fatalf(err, "Failed to load uds-config: %s", err.Error()) - return - } - } - // create new bundle client and deploy bndlClient := bundle.NewOrDie(&bundleCfg) defer bndlClient.ClearPaths() From bd13f12df0fb82891ca2f7274e5717296f6175d9 Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Tue, 28 May 2024 15:19:25 -0600 Subject: [PATCH 10/15] move config validation functions to root.go --- src/cmd/root.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ src/cmd/uds.go | 51 ------------------------------------------------- 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/cmd/root.go b/src/cmd/root.go index e07e8e53..a9a75484 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -7,12 +7,14 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/defenseunicorns/uds-cli/src/config" "github.com/defenseunicorns/uds-cli/src/config/lang" "github.com/defenseunicorns/uds-cli/src/types" zarfCommon "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/pkg/message" + goyaml "github.com/goccy/go-yaml" "github.com/spf13/cobra" ) @@ -95,3 +97,51 @@ func init() { } } } + +// loadViperConfig reads the config file and unmarshals the relevant config into DeployOpts.Variables +func loadViperConfig() error { + // get config file from Viper + configFile, err := os.ReadFile(v.ConfigFileUsed()) + if err != nil { + return err + } + + err = unmarshalAndValidateConfig(configFile, &bundleCfg) + if err != nil { + return err + } + + // ensure the DeployOpts.Variables pkg vars are uppercase + for pkgName, pkgVar := range bundleCfg.DeployOpts.Variables { + for varName, varValue := range pkgVar { + // delete the lowercase var and replace with uppercase + delete(bundleCfg.DeployOpts.Variables[pkgName], varName) + bundleCfg.DeployOpts.Variables[pkgName][strings.ToUpper(varName)] = varValue + } + } + + // ensure the DeployOpts.SharedVariables vars are uppercase + for varName, varValue := range bundleCfg.DeployOpts.SharedVariables { + // delete the lowercase var and replace with uppercase + delete(bundleCfg.DeployOpts.SharedVariables, varName) + bundleCfg.DeployOpts.SharedVariables[strings.ToUpper(varName)] = varValue + } + + return nil +} + +func unmarshalAndValidateConfig(configFile []byte, bundleCfg *types.BundleConfig) error { + // read relevant config into DeployOpts.Variables + // need to use goyaml because Viper doesn't preserve case: https://github.com/spf13/viper/issues/1014 + err := goyaml.UnmarshalWithOptions(configFile, &bundleCfg.DeployOpts, goyaml.Strict()) + if err != nil { + return err + } + // validate config options + for optionName := range bundleCfg.DeployOpts.Options { + if !isValidConfigOption(optionName) { + return fmt.Errorf("invalid config option: %s", optionName) + } + } + return nil +} diff --git a/src/cmd/uds.go b/src/cmd/uds.go index e314a17d..396c4a8e 100644 --- a/src/cmd/uds.go +++ b/src/cmd/uds.go @@ -10,15 +10,12 @@ import ( "io" "os" "path/filepath" - "strings" "github.com/AlecAivazis/survey/v2" "github.com/defenseunicorns/uds-cli/src/config" "github.com/defenseunicorns/uds-cli/src/config/lang" "github.com/defenseunicorns/uds-cli/src/pkg/bundle" - "github.com/defenseunicorns/uds-cli/src/types" "github.com/defenseunicorns/zarf/src/pkg/message" - goyaml "github.com/goccy/go-yaml" "github.com/spf13/cobra" ) @@ -178,38 +175,6 @@ var logsCmd = &cobra.Command{ }, } -// loadViperConfig reads the config file and unmarshals the relevant config into DeployOpts.Variables -func loadViperConfig() error { - // get config file from Viper - configFile, err := os.ReadFile(v.ConfigFileUsed()) - if err != nil { - return err - } - - err = unmarshalAndValidateConfig(configFile, &bundleCfg) - if err != nil { - return err - } - - // ensure the DeployOpts.Variables pkg vars are uppercase - for pkgName, pkgVar := range bundleCfg.DeployOpts.Variables { - for varName, varValue := range pkgVar { - // delete the lowercase var and replace with uppercase - delete(bundleCfg.DeployOpts.Variables[pkgName], varName) - bundleCfg.DeployOpts.Variables[pkgName][strings.ToUpper(varName)] = varValue - } - } - - // ensure the DeployOpts.SharedVariables vars are uppercase - for varName, varValue := range bundleCfg.DeployOpts.SharedVariables { - // delete the lowercase var and replace with uppercase - delete(bundleCfg.DeployOpts.SharedVariables, varName) - bundleCfg.DeployOpts.SharedVariables[strings.ToUpper(varName)] = varValue - } - - return nil -} - func init() { initViper() @@ -278,19 +243,3 @@ func chooseBundle(args []string) string { return path } - -func unmarshalAndValidateConfig(configFile []byte, bundleCfg *types.BundleConfig) error { - // read relevant config into DeployOpts.Variables - // need to use goyaml because Viper doesn't preserve case: https://github.com/spf13/viper/issues/1014 - err := goyaml.UnmarshalWithOptions(configFile, &bundleCfg.DeployOpts, goyaml.Strict()) - if err != nil { - return err - } - // validate config options - for optionName := range bundleCfg.DeployOpts.Options { - if !isValidConfigOption(optionName) { - return fmt.Errorf("invalid config option: %s", optionName) - } - } - return nil -} From 849395b9c35c8c794cf7b32a12673a6258dccf1c Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Tue, 28 May 2024 15:27:51 -0600 Subject: [PATCH 11/15] add documentation --- src/cmd/root.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cmd/root.go b/src/cmd/root.go index a9a75484..a2f300a6 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -133,6 +133,7 @@ func loadViperConfig() error { func unmarshalAndValidateConfig(configFile []byte, bundleCfg *types.BundleConfig) error { // read relevant config into DeployOpts.Variables // need to use goyaml because Viper doesn't preserve case: https://github.com/spf13/viper/issues/1014 + // unmarshalling into DeployOpts because we want to check all of the top level config keys which are currently defined in DeployOpts err := goyaml.UnmarshalWithOptions(configFile, &bundleCfg.DeployOpts, goyaml.Strict()) if err != nil { return err From 95c73de20258d70682b27bca1acb8a3b4af06f31 Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Tue, 28 May 2024 15:41:01 -0600 Subject: [PATCH 12/15] fix config tests --- src/test/e2e/bundle_test.go | 4 +--- src/test/e2e/commands_test.go | 6 ------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/test/e2e/bundle_test.go b/src/test/e2e/bundle_test.go index 8bfeb2a3..17975b11 100644 --- a/src/test/e2e/bundle_test.go +++ b/src/test/e2e/bundle_test.go @@ -608,9 +608,7 @@ func TestInvalidConfig(t *testing.T) { e2e.HelmDepUpdate(t, fmt.Sprintf("%s/unicorn-podinfo", zarfPkgPath)) e2e.CreateZarfPkg(t, zarfPkgPath, false) bundleDir := "src/test/bundles/07-helm-overrides" - createLocal(t, bundleDir, e2e.Arch) - bundlePath := filepath.Join(bundleDir, fmt.Sprintf("uds-bundle-helm-overrides-%s-0.0.1.tar.zst", e2e.Arch)) - _, stderr := deployWithError(t, bundlePath) + stderr := createLocalError(bundleDir, e2e.Arch) require.Contains(t, stderr, "invalid config option: log_levelx") os.Unsetenv("UDS_CONFIG") } diff --git a/src/test/e2e/commands_test.go b/src/test/e2e/commands_test.go index 5585f94c..b840f9cf 100644 --- a/src/test/e2e/commands_test.go +++ b/src/test/e2e/commands_test.go @@ -112,12 +112,6 @@ func deploy(t *testing.T, tarballPath string) (stdout string, stderr string) { return stdout, stderr } -func deployWithError(_ *testing.T, tarballPath string) (stdout string, stderr string) { - cmd := strings.Split(fmt.Sprintf("deploy %s --retries 1 --confirm", tarballPath), " ") - stdout, stderr, _ = e2e.UDS(cmd...) - return stdout, stderr -} - func devDeploy(t *testing.T, bundlePath string) (stdout string, stderr string) { cmd := strings.Split(fmt.Sprintf("dev deploy %s --confirm", bundlePath), " ") stdout, stderr, err := e2e.UDS(cmd...) From c3bd79d81f247c15d25f68b54038b997f3f69306 Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Tue, 28 May 2024 15:45:02 -0600 Subject: [PATCH 13/15] remove extra space --- src/cmd/root.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cmd/root.go b/src/cmd/root.go index a2f300a6..31184f5a 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -69,6 +69,14 @@ func init() { initViper() + // load uds-config if it exists + if v.ConfigFileUsed() != "" { + if err := loadViperConfig(); err != nil { + message.Fatalf(err, "Failed to load uds-config: %s", err.Error()) + return + } + } + v.SetDefault(V_LOG_LEVEL, "info") v.SetDefault(V_ARCHITECTURE, "") v.SetDefault(V_NO_LOG_FILE, false) @@ -88,14 +96,6 @@ func init() { rootCmd.PersistentFlags().StringVar(&config.CommonOptions.TempDirectory, "tmpdir", v.GetString(V_TMP_DIR), lang.RootCmdFlagTempDir) rootCmd.PersistentFlags().BoolVar(&config.CommonOptions.Insecure, "insecure", v.GetBool(V_INSECURE), lang.RootCmdFlagInsecure) rootCmd.PersistentFlags().IntVar(&config.CommonOptions.OCIConcurrency, "oci-concurrency", v.GetInt(V_BNDL_OCI_CONCURRENCY), lang.CmdBundleFlagConcurrency) - - // load uds-config if it exists - if v.ConfigFileUsed() != "" { - if err := loadViperConfig(); err != nil { - message.Fatalf(err, "Failed to load uds-config: %s", err.Error()) - return - } - } } // loadViperConfig reads the config file and unmarshals the relevant config into DeployOpts.Variables From 73724fc092e4d8beb07a7a302ddb642d309dd0ae Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Tue, 28 May 2024 16:21:10 -0600 Subject: [PATCH 14/15] test fix --- src/test/e2e/bundle_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/test/e2e/bundle_test.go b/src/test/e2e/bundle_test.go index 17975b11..2fb7a6bf 100644 --- a/src/test/e2e/bundle_test.go +++ b/src/test/e2e/bundle_test.go @@ -606,11 +606,10 @@ func TestInvalidConfig(t *testing.T) { os.Setenv("UDS_CONFIG", filepath.Join("src/test/bundles/07-helm-overrides", "uds-config-invalid.yaml")) zarfPkgPath := "src/test/packages/helm" e2e.HelmDepUpdate(t, fmt.Sprintf("%s/unicorn-podinfo", zarfPkgPath)) - e2e.CreateZarfPkg(t, zarfPkgPath, false) - bundleDir := "src/test/bundles/07-helm-overrides" - stderr := createLocalError(bundleDir, e2e.Arch) - require.Contains(t, stderr, "invalid config option: log_levelx") - os.Unsetenv("UDS_CONFIG") + args := strings.Split(fmt.Sprintf("zarf package create %s -o %s --confirm", zarfPkgPath, zarfPkgPath), " ") + _, stdErr, err := e2e.UDS(args...) + require.Error(t, err) + require.Contains(t, stdErr, "invalid config option: log_levelx") } func TestInvalidBundle(t *testing.T) { From 7a12366d3910486f8ae91713e9716eec0971bc6b Mon Sep 17 00:00:00 2001 From: Darcy Cleaver Date: Tue, 28 May 2024 16:40:39 -0600 Subject: [PATCH 15/15] unset config env from previous test --- src/test/e2e/bundle_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/e2e/bundle_test.go b/src/test/e2e/bundle_test.go index 2fb7a6bf..0238b3e5 100644 --- a/src/test/e2e/bundle_test.go +++ b/src/test/e2e/bundle_test.go @@ -610,6 +610,7 @@ func TestInvalidConfig(t *testing.T) { _, stdErr, err := e2e.UDS(args...) require.Error(t, err) require.Contains(t, stdErr, "invalid config option: log_levelx") + os.Unsetenv("UDS_CONFIG") } func TestInvalidBundle(t *testing.T) {