diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 34fbcee2..f243351c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -59,6 +59,9 @@ jobs: - v3.7.2 - v3.8.2 - v3.9.0 + plugin-secrets-version: + - 3.15.0 + - 4.0.0 steps: - uses: actions/checkout@v2 - name: Cache libraries @@ -96,6 +99,7 @@ jobs: uses: medyagh/setup-minikube@master - name: Execute integration tests env: + HELM_SECRETS_VERSION: ${{ matrix.plugin-secrets-version }} HELMFILE_HELM3: 1 TERM: xterm run: make integration diff --git a/pkg/helmexec/exec.go b/pkg/helmexec/exec.go index a134f1fd..74a7b135 100644 --- a/pkg/helmexec/exec.go +++ b/pkg/helmexec/exec.go @@ -14,6 +14,8 @@ import ( "github.com/Masterminds/semver/v3" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/plugin" "github.com/helmfile/helmfile/pkg/envvar" ) @@ -84,6 +86,20 @@ func getHelmVersion(helmBinary string, runner Runner) (semver.Version, error) { return parseHelmVersion(string(outBytes)) } +func GetPluginVersion(name, pluginsDir string) (*semver.Version, error) { + plugins, err := plugin.FindPlugins(pluginsDir) + if err != nil { + return nil, err + } + for _, plugin := range plugins { + if plugin.Metadata.Name == name { + return semver.NewVersion(plugin.Metadata.Version) + } + } + + return nil, fmt.Errorf("plugin %s not installed", name) +} + func redactedURL(chart string) string { chartURL, err := url.ParseRequestURI(chart) if err != nil { @@ -281,7 +297,18 @@ func (helm *execer) DecryptSecret(context HelmContext, name string, flags ...str helm.logger.Infof("Decrypting secret %v", absPath) preArgs := context.GetTillerlessArgs(helm) env := context.getTillerlessEnv() - secretBytes, err := helm.exec(append(append(preArgs, "secrets", "view", absPath), flags...), env) + settings := cli.New() + pluginVersion, err := GetPluginVersion("secrets", settings.PluginsDirectory) + if err != nil { + secret.err = err + return "", err + } + secretArg := "view" + // helm secret view command. The helm secret decrypt command is a drop-in replacement in 4.0.0 version + if pluginVersion.Major() > 3 { + secretArg = "decrypt" + } + secretBytes, err := helm.exec(append(append(preArgs, "secrets", secretArg, absPath), flags...), env) if err != nil { secret.err = err return "", err diff --git a/pkg/helmexec/exec_test.go b/pkg/helmexec/exec_test.go index 3aafa4c5..7dbc19d0 100644 --- a/pkg/helmexec/exec_test.go +++ b/pkg/helmexec/exec_test.go @@ -353,6 +353,15 @@ exec: helm --kube-context dev dependency build ./chart/foo --verify } func Test_DecryptSecret(t *testing.T) { + // Set secrets plugin version to 4.0.0 + if err := os.Setenv("HELM_PLUGINS", "../../test/plugins/secrets/4.0.0"); err != nil { + t.Errorf("failed to set environment HELM_PLUGINS error: %s", err) + } + defer func() { + if err := os.Unsetenv("HELM_PLUGINS"); err != nil { + t.Errorf("failed to unset environment HELM_PLUGINS error: %s", err) + } + }() var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") helm := MockExecer(logger, "dev") @@ -375,7 +384,7 @@ func Test_DecryptSecret(t *testing.T) { expected := fmt.Sprintf(`Preparing to decrypt secret %v/secretName Decrypting secret %s/secretName -exec: helm --kube-context dev secrets view %s/secretName +exec: helm --kube-context dev secrets decrypt %s/secretName Decrypted %s/secretName into %s Preparing to decrypt secret %s/secretName Found secret in cache %s/secretName @@ -393,6 +402,15 @@ Decrypted %s/secretName into %s } func Test_DecryptSecretWithGotmpl(t *testing.T) { + // Set secrets plugin version to 4.0.0 + if err := os.Setenv("HELM_PLUGINS", "../../test/plugins/secrets/4.0.0"); err != nil { + t.Errorf("failed to set environment HELM_PLUGINS error: %s", err) + } + defer func() { + if err := os.Unsetenv("HELM_PLUGINS"); err != nil { + t.Errorf("failed to unset environment HELM_PLUGINS error: %s", err) + } + }() var buffer bytes.Buffer logger := NewLogger(&buffer, "debug") helm := MockExecer(logger, "dev") @@ -414,7 +432,7 @@ func Test_DecryptSecretWithGotmpl(t *testing.T) { expected := fmt.Sprintf(`Preparing to decrypt secret %v/secretName.yaml.gotmpl Decrypting secret %s/secretName.yaml.gotmpl -exec: helm --kube-context dev secrets view %s/secretName.yaml.gotmpl +exec: helm --kube-context dev secrets decrypt %s/secretName.yaml.gotmpl Decrypted %s/secretName.yaml.gotmpl into %s `, cwd, cwd, cwd, cwd, tmpFilePath) if err != nil { @@ -897,6 +915,29 @@ func Test_IsHelm3(t *testing.T) { } } +func Test_GetPluginVersion(t *testing.T) { + v3ExpectedVersion := "3.15.0" + v4ExpectedVersion := "4.0.0" + v3PluginDirPath := "../../test/plugins/secrets/3.15.0" + v4PluginDirPath := "../../test/plugins/secrets/4.0.0" + + v3SecretPluginVersion, err := GetPluginVersion("secrets", v3PluginDirPath) + if err != nil { + t.Errorf(err.Error()) + } + if v3SecretPluginVersion.String() != v3ExpectedVersion { + t.Errorf("secrets v3 plugin version is %v, expected %v", v3SecretPluginVersion.String(), v3ExpectedVersion) + } + + v4SecretPluginVersion, err := GetPluginVersion("secrets", v4PluginDirPath) + if err != nil { + t.Errorf(err.Error()) + } + if v4SecretPluginVersion.String() != v4ExpectedVersion { + t.Errorf("secrets v4 plugin version is %v, expected %v", v4SecretPluginVersion.String(), v4ExpectedVersion) + } +} + func Test_GetVersion(t *testing.T) { helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94\n")} helm := New("helm", NewLogger(os.Stdout, "info"), "dev", &helm2Runner) diff --git a/test/integration/run.sh b/test/integration/run.sh index fcc0c2b5..b1b25799 100755 --- a/test/integration/run.sh +++ b/test/integration/run.sh @@ -25,8 +25,9 @@ export HELM_DATA_HOME="${helm_dir}/data" export HELM_HOME="${HELM_DATA_HOME}" export HELM_PLUGINS="${HELM_DATA_HOME}/plugins" export HELM_CONFIG_HOME="${helm_dir}/config" -HELM_SECRETS_VERSION=3.5.0 HELM_DIFF_VERSION=3.3.1 +HELM_SECRETS_DEFAULT_VERSION=3.15.0 +HELM_SECRETS_VERSION="${HELM_SECRETS_VERSION:-$HELM_SECRETS_DEFAULT_VERSION}" export GNUPGHOME="${PWD}/${dir}/.gnupg" export SOPS_PGP_FP="B2D6D7BBEC03B2E66571C8C00AD18E16CFDEF700" @@ -194,7 +195,7 @@ if [[ helm_major_version -eq 3 ]]; then test_start "secretssops.2 - should succeed with secrets plugin" info "Ensure helm-secrets is installed" - ${helm} plugin install https://github.com/jkroepke/helm-secrets --version v3.5.0 + ${helm} plugin install https://github.com/jkroepke/helm-secrets --version v${HELM_SECRETS_VERSION} info "Ensure helmfile succeed when helm-secrets is installed" ${helmfile} -f ${dir}/secretssops.yaml -e direct build || fail "\"helmfile build\" shouldn't fail" diff --git a/test/plugins/secrets/3.15.0/helm-secrets/plugin.yaml b/test/plugins/secrets/3.15.0/helm-secrets/plugin.yaml new file mode 100644 index 00000000..55330f78 --- /dev/null +++ b/test/plugins/secrets/3.15.0/helm-secrets/plugin.yaml @@ -0,0 +1,21 @@ +name: "secrets" +version: "3.15.0" +usage: "Secrets encryption in Helm for Git storing" +description: |- + This plugin provides secrets values encryption for Helm charts secure storing +useTunnel: false +command: "$HELM_PLUGIN_DIR/scripts/run.sh" +platformCommand: + - os: windows + command: "cmd /c $HELM_PLUGIN_DIR\\scripts\\wrapper\\sh.cmd $HELM_PLUGIN_DIR\\scripts\\run.sh" + +downloaders: + - command: "scripts/run.sh downloader" + protocols: + - "sops" + - "secret" + - "secrets" + - "secrets+gpg-import" + - "secrets+gpg-import-kubernetes" + - "secrets+age-import" + - "secrets+age-import-kubernetes" diff --git a/test/plugins/secrets/4.0.0/helm-secrets/plugin.yaml b/test/plugins/secrets/4.0.0/helm-secrets/plugin.yaml new file mode 100644 index 00000000..23077185 --- /dev/null +++ b/test/plugins/secrets/4.0.0/helm-secrets/plugin.yaml @@ -0,0 +1,19 @@ +name: "secrets" +version: "4.0.0" +usage: "Secrets encryption in Helm for Git storing" +description: |- + This plugin provides secrets values encryption for Helm charts secure storing +useTunnel: false +command: "$HELM_PLUGIN_DIR/scripts/run.sh" +platformCommand: + - os: windows + command: "cmd /c $HELM_PLUGIN_DIR\\scripts\\wrapper\\sh.cmd $HELM_PLUGIN_DIR\\scripts\\run.sh" + +downloaders: + - command: "scripts/run.sh downloader" + protocols: + - "secrets" + - "secrets+gpg-import" + - "secrets+gpg-import-kubernetes" + - "secrets+age-import" + - "secrets+age-import-kubernetes"