Compare commits
	
		
			No commits in common. "main" and "v1.1.8" have entirely different histories.
		
	
	
		|  | @ -37,7 +37,7 @@ jobs: | ||||||
|         run: make check test |         run: make check test | ||||||
|       - name: Archive built binaries |       - name: Archive built binaries | ||||||
|         run: tar -cvf built-binaries.tar helmfile diff-yamls dyff |         run: tar -cvf built-binaries.tar helmfile diff-yamls dyff | ||||||
|       - uses: actions/upload-artifact@v5 |       - uses: actions/upload-artifact@v4 | ||||||
|         with: |         with: | ||||||
|           name: built-binaries-${{ github.run_id }} |           name: built-binaries-${{ github.run_id }} | ||||||
|           path: built-binaries.tar |           path: built-binaries.tar | ||||||
|  | @ -92,7 +92,7 @@ jobs: | ||||||
|         with: |         with: | ||||||
|           go-version-file: go.mod |           go-version-file: go.mod | ||||||
| 
 | 
 | ||||||
|       - uses: actions/download-artifact@v6 |       - uses: actions/download-artifact@v5 | ||||||
|         with: |         with: | ||||||
|           name: built-binaries-${{ github.run_id }} |           name: built-binaries-${{ github.run_id }} | ||||||
|       - name: install semver |       - name: install semver | ||||||
|  | @ -127,7 +127,7 @@ jobs: | ||||||
|       - uses: actions/checkout@v5 |       - uses: actions/checkout@v5 | ||||||
|         with: |         with: | ||||||
|           fetch-depth: 0 |           fetch-depth: 0 | ||||||
|       - uses: actions/download-artifact@v6 |       - uses: actions/download-artifact@v5 | ||||||
|         with: |         with: | ||||||
|           name: built-binaries-${{ github.run_id }} |           name: built-binaries-${{ github.run_id }} | ||||||
|       - name: Extract tar to get built binaries |       - name: Extract tar to get built binaries | ||||||
|  |  | ||||||
|  | @ -328,9 +328,6 @@ releases: | ||||||
|     reuseValues: false |     reuseValues: false | ||||||
|     # set `false` to uninstall this release on sync.  (default true) |     # set `false` to uninstall this release on sync.  (default true) | ||||||
|     installed: true |     installed: true | ||||||
|     # Defines the strategy to use when updating. Possible value is: |  | ||||||
|     # - "reinstallIfForbidden": Performs an uninstall before the update only if the update is forbidden (e.g., due to permission issues or conflicts). |  | ||||||
|     updateStrategy: "" |  | ||||||
|     # restores previous state in case of failed release (default false) |     # restores previous state in case of failed release (default false) | ||||||
|     atomic: true |     atomic: true | ||||||
|     # when true, cleans up any new resources created during a failed release (default false) |     # when true, cleans up any new resources created during a failed release (default false) | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										2
									
								
								go.mod
								
								
								
								
							|  | @ -7,7 +7,7 @@ require ( | ||||||
| 	github.com/Masterminds/semver/v3 v3.4.0 | 	github.com/Masterminds/semver/v3 v3.4.0 | ||||||
| 	github.com/Masterminds/sprig/v3 v3.3.0 | 	github.com/Masterminds/sprig/v3 v3.3.0 | ||||||
| 	github.com/aws/aws-sdk-go-v2/config v1.31.15 | 	github.com/aws/aws-sdk-go-v2/config v1.31.15 | ||||||
| 	github.com/aws/aws-sdk-go-v2/service/s3 v1.89.0 | 	github.com/aws/aws-sdk-go-v2/service/s3 v1.88.7 | ||||||
| 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc | 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc | ||||||
| 	github.com/go-test/deep v1.1.1 | 	github.com/go-test/deep v1.1.1 | ||||||
| 	github.com/golang/mock v1.6.0 | 	github.com/golang/mock v1.6.0 | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										4
									
								
								go.sum
								
								
								
								
							|  | @ -170,8 +170,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.11 h1:weapBOuuFIBEQ | ||||||
| github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.11/go.mod h1:3C1gN4FmIVLwYSh8etngUS+f1viY6nLCDVtZmrFbDy0= | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.11/go.mod h1:3C1gN4FmIVLwYSh8etngUS+f1viY6nLCDVtZmrFbDy0= | ||||||
| github.com/aws/aws-sdk-go-v2/service/kms v1.45.6 h1:Br3kil4j7RPW+7LoLVkYt8SuhIWlg6ylmbmzXJ7PgXY= | github.com/aws/aws-sdk-go-v2/service/kms v1.45.6 h1:Br3kil4j7RPW+7LoLVkYt8SuhIWlg6ylmbmzXJ7PgXY= | ||||||
| github.com/aws/aws-sdk-go-v2/service/kms v1.45.6/go.mod h1:FKXkHzw1fJZtg1P1qoAIiwen5thz/cDRTTDCIu8ljxc= | github.com/aws/aws-sdk-go-v2/service/kms v1.45.6/go.mod h1:FKXkHzw1fJZtg1P1qoAIiwen5thz/cDRTTDCIu8ljxc= | ||||||
| github.com/aws/aws-sdk-go-v2/service/s3 v1.89.0 h1:JbCUlVDEjmhpvpIgXP9QN+/jW61WWWj99cGmxMC49hM= | github.com/aws/aws-sdk-go-v2/service/s3 v1.88.7 h1:Wer3W0GuaedWT7dv/PiWNZGSQFSTcBY2rZpbiUp5xcA= | ||||||
| github.com/aws/aws-sdk-go-v2/service/s3 v1.89.0/go.mod h1:UHKgcRSx8PVtvsc1Poxb/Co3PD3wL7P+f49P0+cWtuY= | github.com/aws/aws-sdk-go-v2/service/s3 v1.88.7/go.mod h1:UHKgcRSx8PVtvsc1Poxb/Co3PD3wL7P+f49P0+cWtuY= | ||||||
| github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.6 h1:9PWl450XOG+m5lKv+qg5BXso1eLxpsZLqq7VPug5km0= | github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.6 h1:9PWl450XOG+m5lKv+qg5BXso1eLxpsZLqq7VPug5km0= | ||||||
| github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.6/go.mod h1:hwt7auGsDcaNQ8pzLgE2kCNyIWouYlAKSjuUu5Dqr7I= | github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.6/go.mod h1:hwt7auGsDcaNQ8pzLgE2kCNyIWouYlAKSjuUu5Dqr7I= | ||||||
| github.com/aws/aws-sdk-go-v2/service/ssm v1.65.1 h1:TFg6XiS7EsHN0/jpV3eVNczZi/sPIVP5jxIs+euIESQ= | github.com/aws/aws-sdk-go-v2/service/ssm v1.65.1 h1:TFg6XiS7EsHN0/jpV3eVNczZi/sPIVP5jxIs+euIESQ= | ||||||
|  |  | ||||||
|  | @ -793,9 +793,6 @@ func (a *App) getHelm(st *state.HelmState) (helmexec.Interface, error) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	bin := st.DefaultHelmBinary | 	bin := st.DefaultHelmBinary | ||||||
| 	if bin == "" { |  | ||||||
| 		bin = state.DefaultHelmBinary |  | ||||||
| 	} |  | ||||||
| 	kubeconfig := a.Kubeconfig | 	kubeconfig := a.Kubeconfig | ||||||
| 	kubectx := st.HelmDefaults.KubeContext | 	kubectx := st.HelmDefaults.KubeContext | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -415,28 +415,4 @@ releases: | ||||||
| 			}, | 			}, | ||||||
| 		}) | 		}) | ||||||
| 	}) | 	}) | ||||||
| 
 |  | ||||||
| 	t.Run("show diff on changed selected release with reinstall", func(t *testing.T) { |  | ||||||
| 		check(t, testcase{ |  | ||||||
| 			helmfile: ` |  | ||||||
| releases: |  | ||||||
| - name: a |  | ||||||
|   chart: incubator/raw |  | ||||||
|   namespace: default |  | ||||||
|   updateStrategy: reinstallIfForbidden |  | ||||||
| - name: b |  | ||||||
|   chart: incubator/raw |  | ||||||
|   namespace: default |  | ||||||
| `, |  | ||||||
| 			selectors: []string{"name=a"}, |  | ||||||
| 			lists: map[exectest.ListKey]string{ |  | ||||||
| 				{Filter: "^a$", Flags: listFlags("default", "default")}: `NAME	REVISION	UPDATED                 	STATUS  	CHART        	APP VERSION	NAMESPACE |  | ||||||
| foo 	4       	Fri Nov  1 08:40:07 2019	DEPLOYED	raw-3.1.0	3.1.0      	default |  | ||||||
| `, |  | ||||||
| 			}, |  | ||||||
| 			diffed: []exectest.Release{ |  | ||||||
| 				{Name: "a", Flags: []string{"--kube-context", "default", "--namespace", "default", "--reset-values"}}, |  | ||||||
| 			}, |  | ||||||
| 		}) |  | ||||||
| 	}) |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,51 +0,0 @@ | ||||||
| package app |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	goContext "context" |  | ||||||
| 	"testing" |  | ||||||
| 
 |  | ||||||
| 	"github.com/stretchr/testify/require" |  | ||||||
| 
 |  | ||||||
| 	"github.com/helmfile/helmfile/pkg/state" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // TestGetHelmWithEmptyDefaultHelmBinary tests that getHelm properly defaults to "helm"
 |  | ||||||
| // when st.DefaultHelmBinary is empty. This addresses the issue where base files with
 |  | ||||||
| // environment secrets would fail with "exec: no command" error.
 |  | ||||||
| //
 |  | ||||||
| // Background: When a base file has environment secrets but doesn't specify helmBinary,
 |  | ||||||
| // the state.DefaultHelmBinary would be empty, causing helmexec.New to be called with
 |  | ||||||
| // an empty string, which results in "error determining helm version: exec: no command".
 |  | ||||||
| //
 |  | ||||||
| // The fix in app.getHelm() ensures that when st.DefaultHelmBinary is empty, it defaults
 |  | ||||||
| // to state.DefaultHelmBinary ("helm").
 |  | ||||||
| func TestGetHelmWithEmptyDefaultHelmBinary(t *testing.T) { |  | ||||||
| 	// Test that app.getHelm() handles empty DefaultHelmBinary correctly by applying a default
 |  | ||||||
| 	st := &state.HelmState{ |  | ||||||
| 		ReleaseSetSpec: state.ReleaseSetSpec{ |  | ||||||
| 			DefaultHelmBinary: "", // Empty, as would be the case for base files
 |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	logger := newAppTestLogger() |  | ||||||
| 	app := &App{ |  | ||||||
| 		OverrideHelmBinary:  "", |  | ||||||
| 		OverrideKubeContext: "", |  | ||||||
| 		Logger:              logger, |  | ||||||
| 		Env:                 "default", |  | ||||||
| 		ctx:                 goContext.Background(), |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// This should NOT fail because app.getHelm() defaults empty DefaultHelmBinary to "helm"
 |  | ||||||
| 	helm, err := app.getHelm(st) |  | ||||||
| 
 |  | ||||||
| 	// Verify that no error occurred - the fix in app.getHelm() prevents the "exec: no command" error
 |  | ||||||
| 	require.NoError(t, err, "getHelm should not fail when DefaultHelmBinary is empty (fix should apply default)") |  | ||||||
| 
 |  | ||||||
| 	// Verify that a valid helm execer was returned
 |  | ||||||
| 	require.NotNil(t, helm, "getHelm should return a valid helm execer") |  | ||||||
| 
 |  | ||||||
| 	// Verify that the helm version is accessible (confirms the helm binary is valid)
 |  | ||||||
| 	version := helm.GetVersion() |  | ||||||
| 	require.NotNil(t, version, "helm version should be accessible") |  | ||||||
| } |  | ||||||
|  | @ -220,97 +220,6 @@ releases: | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestUpdateStrategyParamValidation(t *testing.T) { |  | ||||||
| 	cases := []struct { |  | ||||||
| 		files          map[string]string |  | ||||||
| 		updateStrategy string |  | ||||||
| 		isValid        bool |  | ||||||
| 	}{ |  | ||||||
| 		{map[string]string{ |  | ||||||
| 			"/path/to/helmfile.yaml": `releases: |  | ||||||
| - name: zipkin |  | ||||||
|   chart: stable/zipkin |  | ||||||
|   updateStrategy: reinstallIfForbidden |  | ||||||
| `}, |  | ||||||
| 			"reinstallIfForbidden", |  | ||||||
| 			true}, |  | ||||||
| 		{map[string]string{ |  | ||||||
| 			"/path/to/helmfile.yaml": `releases: |  | ||||||
| - name: zipkin |  | ||||||
|   chart: stable/zipkin |  | ||||||
|   updateStrategy: reinstallIfForbidden |  | ||||||
| `}, |  | ||||||
| 			"reinstallIfForbidden", |  | ||||||
| 			true}, |  | ||||||
| 		{map[string]string{ |  | ||||||
| 			"/path/to/helmfile.yaml": `releases: |  | ||||||
| - name: zipkin |  | ||||||
|   chart: stable/zipkin |  | ||||||
|   updateStrategy: |  | ||||||
| `}, |  | ||||||
| 			"", |  | ||||||
| 			true}, |  | ||||||
| 		{map[string]string{ |  | ||||||
| 			"/path/to/helmfile.yaml": `releases: |  | ||||||
| - name: zipkin |  | ||||||
|   chart: stable/zipkin |  | ||||||
|   updateStrategy: foo |  | ||||||
| `}, |  | ||||||
| 			"foo", |  | ||||||
| 			false}, |  | ||||||
| 		{map[string]string{ |  | ||||||
| 			"/path/to/helmfile.yaml": `releases: |  | ||||||
| - name: zipkin |  | ||||||
|   chart: stable/zipkin |  | ||||||
|   updateStrategy: reinstal |  | ||||||
| `}, |  | ||||||
| 			"reinstal", |  | ||||||
| 			false}, |  | ||||||
| 		{map[string]string{ |  | ||||||
| 			"/path/to/helmfile.yaml": `releases: |  | ||||||
| - name: zipkin |  | ||||||
|   chart: stable/zipkin |  | ||||||
|   updateStrategy: reinstall1 |  | ||||||
| `}, |  | ||||||
| 			"reinstall1", |  | ||||||
| 			false}, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for idx, c := range cases { |  | ||||||
| 		fs := testhelper.NewTestFs(c.files) |  | ||||||
| 		app := &App{ |  | ||||||
| 			OverrideHelmBinary:  DefaultHelmBinary, |  | ||||||
| 			OverrideKubeContext: "default", |  | ||||||
| 			Logger:              newAppTestLogger(), |  | ||||||
| 			Namespace:           "", |  | ||||||
| 			Env:                 "default", |  | ||||||
| 			FileOrDir:           "helmfile.yaml", |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		expectNoCallsToHelm(app) |  | ||||||
| 
 |  | ||||||
| 		app = injectFs(app, fs) |  | ||||||
| 
 |  | ||||||
| 		err := app.ForEachState( |  | ||||||
| 			Noop, |  | ||||||
| 			false, |  | ||||||
| 			SetFilter(true), |  | ||||||
| 		) |  | ||||||
| 
 |  | ||||||
| 		if c.isValid && err != nil { |  | ||||||
| 			t.Errorf("[case: %d] Unexpected error for valid case: %v", idx, err) |  | ||||||
| 		} else if !c.isValid { |  | ||||||
| 			var invalidUpdateStrategy state.InvalidUpdateStrategyError |  | ||||||
| 			invalidUpdateStrategy.UpdateStrategy = c.updateStrategy |  | ||||||
| 			if err == nil { |  | ||||||
| 				t.Errorf("[case: %d] Expected error for invalid case", idx) |  | ||||||
| 			} else if !strings.Contains(err.Error(), invalidUpdateStrategy.Error()) { |  | ||||||
| 				t.Errorf("[case: %d] Unexpected error returned for invalid case\ngot: %v\nexpected underlying error: %s", idx, err, invalidUpdateStrategy.Error()) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestVisitDesiredStatesWithReleasesFiltered_Issue1008_MissingNonDefaultEnvInBase(t *testing.T) { | func TestVisitDesiredStatesWithReleasesFiltered_Issue1008_MissingNonDefaultEnvInBase(t *testing.T) { | ||||||
| 	files := map[string]string{ | 	files := map[string]string{ | ||||||
| 		"/path/to/base.yaml": ` | 		"/path/to/base.yaml": ` | ||||||
|  | @ -3167,97 +3076,6 @@ baz 	4       	Fri Nov  1 08:40:07 2019	DEPLOYED	mychart3-3.1.0	3.1.0      	defau | ||||||
| 			concurrency: 1, | 			concurrency: 1, | ||||||
| 		}, | 		}, | ||||||
| 		//
 | 		//
 | ||||||
| 		// install with upgrade with reinstallIfForbidden
 |  | ||||||
| 		//
 |  | ||||||
| 		{ |  | ||||||
| 			name: "install-with-upgrade-with-reinstallIfForbidden", |  | ||||||
| 			loc:  location(), |  | ||||||
| 			files: map[string]string{ |  | ||||||
| 				"/path/to/helmfile.yaml": ` |  | ||||||
| releases: |  | ||||||
| - name: baz |  | ||||||
|   chart: stable/mychart3 |  | ||||||
|   disableValidationOnInstall: true |  | ||||||
|   updateStrategy: reinstallIfForbidden |  | ||||||
| - name: foo |  | ||||||
|   chart: stable/mychart1 |  | ||||||
|   disableValidationOnInstall: true |  | ||||||
|   needs: |  | ||||||
|   - bar |  | ||||||
| - name: bar |  | ||||||
|   chart: stable/mychart2 |  | ||||||
|   disableValidation: true |  | ||||||
|   updateStrategy: reinstallIfForbidden |  | ||||||
| `, |  | ||||||
| 			}, |  | ||||||
| 			diffs: map[exectest.DiffKey]error{ |  | ||||||
| 				{Name: "baz", Chart: "stable/mychart3", Flags: "--kube-context default --reset-values --detailed-exitcode"}:                      helmexec.ExitError{Code: 2}, |  | ||||||
| 				{Name: "foo", Chart: "stable/mychart1", Flags: "--disable-validation --kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2}, |  | ||||||
| 				{Name: "bar", Chart: "stable/mychart2", Flags: "--disable-validation --kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2}, |  | ||||||
| 			}, |  | ||||||
| 			lists: map[exectest.ListKey]string{ |  | ||||||
| 				{Filter: "^foo$", Flags: listFlags("", "default")}: ``, |  | ||||||
| 				{Filter: "^bar$", Flags: listFlags("", "default")}: `NAME	REVISION	UPDATED                 	STATUS  	CHART        	APP VERSION	NAMESPACE |  | ||||||
| bar 	4       	Fri Nov  1 08:40:07 2019	DEPLOYED	mychart2-3.1.0	3.1.0      	default |  | ||||||
| `, |  | ||||||
| 				{Filter: "^baz$", Flags: listFlags("", "default")}: `NAME	REVISION	UPDATED                 	STATUS  	CHART        	APP VERSION	NAMESPACE |  | ||||||
| baz 	4       	Fri Nov  1 08:40:07 2019	DEPLOYED	mychart3-3.1.0	3.1.0      	default |  | ||||||
| `, |  | ||||||
| 			}, |  | ||||||
| 			upgraded: []exectest.Release{ |  | ||||||
| 				{Name: "baz", Flags: []string{"--kube-context", "default"}}, |  | ||||||
| 				{Name: "bar", Flags: []string{"--kube-context", "default"}}, |  | ||||||
| 				{Name: "foo", Flags: []string{"--kube-context", "default"}}, |  | ||||||
| 			}, |  | ||||||
| 			deleted:     []exectest.Release{}, |  | ||||||
| 			concurrency: 1, |  | ||||||
| 		}, |  | ||||||
| 		//
 |  | ||||||
| 		// install with upgrade and --skip-diff-on-install with reinstallIfForbidden
 |  | ||||||
| 		//
 |  | ||||||
| 		{ |  | ||||||
| 			name:              "install-with-upgrade-with-skip-diff-on-install-with-reinstallIfForbidden", |  | ||||||
| 			loc:               location(), |  | ||||||
| 			skipDiffOnInstall: true, |  | ||||||
| 			files: map[string]string{ |  | ||||||
| 				"/path/to/helmfile.yaml": ` |  | ||||||
| releases: |  | ||||||
| - name: baz |  | ||||||
|   chart: stable/mychart3 |  | ||||||
|   disableValidationOnInstall: true |  | ||||||
|   updateStrategy: reinstallIfForbidden |  | ||||||
| - name: foo |  | ||||||
|   chart: stable/mychart1 |  | ||||||
|   disableValidationOnInstall: true |  | ||||||
|   needs: |  | ||||||
|   - bar |  | ||||||
| - name: bar |  | ||||||
|   chart: stable/mychart2 |  | ||||||
|   disableValidation: true |  | ||||||
|   updateStrategy: reinstallIfForbidden |  | ||||||
| `, |  | ||||||
| 			}, |  | ||||||
| 			diffs: map[exectest.DiffKey]error{ |  | ||||||
| 				{Name: "baz", Chart: "stable/mychart3", Flags: "--kube-context default --reset-values --detailed-exitcode"}:                      helmexec.ExitError{Code: 2}, |  | ||||||
| 				{Name: "bar", Chart: "stable/mychart2", Flags: "--disable-validation --kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2}, |  | ||||||
| 			}, |  | ||||||
| 			lists: map[exectest.ListKey]string{ |  | ||||||
| 				{Filter: "^foo$", Flags: listFlags("", "default")}: ``, |  | ||||||
| 				{Filter: "^bar$", Flags: listFlags("", "default")}: `NAME	REVISION	UPDATED                 	STATUS  	CHART        	APP VERSION	NAMESPACE |  | ||||||
| bar 	4       	Fri Nov  1 08:40:07 2019	DEPLOYED	mychart2-3.1.0	3.1.0      	default |  | ||||||
| `, |  | ||||||
| 				{Filter: "^baz$", Flags: listFlags("", "default")}: `NAME	REVISION	UPDATED                 	STATUS  	CHART        	APP VERSION	NAMESPACE |  | ||||||
| baz 	4       	Fri Nov  1 08:40:07 2019	DEPLOYED	mychart3-3.1.0	3.1.0      	default |  | ||||||
| `, |  | ||||||
| 			}, |  | ||||||
| 			upgraded: []exectest.Release{ |  | ||||||
| 				{Name: "baz", Flags: []string{"--kube-context", "default"}}, |  | ||||||
| 				{Name: "bar", Flags: []string{"--kube-context", "default"}}, |  | ||||||
| 				{Name: "foo", Flags: []string{"--kube-context", "default"}}, |  | ||||||
| 			}, |  | ||||||
| 			concurrency: 1, |  | ||||||
| 		}, |  | ||||||
| 		//
 |  | ||||||
| 		// upgrades
 | 		// upgrades
 | ||||||
| 		//
 | 		//
 | ||||||
| 		{ | 		{ | ||||||
|  | @ -3954,7 +3772,7 @@ releases: | ||||||
| 					} | 					} | ||||||
| 					for flagIdx := range wantDeletes[relIdx].Flags { | 					for flagIdx := range wantDeletes[relIdx].Flags { | ||||||
| 						if wantDeletes[relIdx].Flags[flagIdx] != helm.Deleted[relIdx].Flags[flagIdx] { | 						if wantDeletes[relIdx].Flags[flagIdx] != helm.Deleted[relIdx].Flags[flagIdx] { | ||||||
| 							t.Errorf("releases[%d].flags[%d]: got %v, want %v", relIdx, flagIdx, helm.Deleted[relIdx].Flags[flagIdx], wantDeletes[relIdx].Flags[flagIdx]) | 							t.Errorf("releaes[%d].flags[%d]: got %v, want %v", relIdx, flagIdx, helm.Deleted[relIdx].Flags[flagIdx], wantDeletes[relIdx].Flags[flagIdx]) | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"slices" |  | ||||||
| 
 | 
 | ||||||
| 	"dario.cat/mergo" | 	"dario.cat/mergo" | ||||||
| 	"github.com/helmfile/vals" | 	"github.com/helmfile/vals" | ||||||
|  | @ -286,18 +285,6 @@ func (ld *desiredStateLoader) load(env, overrodeEnv *environment.Environment, ba | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Validate updateStrategy value if set in the releases
 |  | ||||||
| 	for i := range finalState.Releases { |  | ||||||
| 		if finalState.Releases[i].UpdateStrategy != "" { |  | ||||||
| 			if !slices.Contains(state.ValidUpdateStrategyValues, finalState.Releases[i].UpdateStrategy) { |  | ||||||
| 				return nil, &state.StateLoadError{ |  | ||||||
| 					Msg:   fmt.Sprintf("failed to read %s", finalState.FilePath), |  | ||||||
| 					Cause: &state.InvalidUpdateStrategyError{UpdateStrategy: finalState.Releases[i].UpdateStrategy}, |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	finalState.OrginReleases = finalState.Releases | 	finalState.OrginReleases = finalState.Releases | ||||||
| 	return finalState, nil | 	return finalState, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,11 +0,0 @@ | ||||||
| processing file "helmfile.yaml" in directory "." |  | ||||||
| changing working directory to "/path/to" |  | ||||||
| merged environment: &{default  map[] map[]} |  | ||||||
| 1 release(s) matching name=a found in helmfile.yaml |  | ||||||
| 
 |  | ||||||
| processing 1 groups of releases in this order: |  | ||||||
| GROUP RELEASES |  | ||||||
| 1     default/default/a |  | ||||||
| 
 |  | ||||||
| processing releases in group 1/1: default/default/a |  | ||||||
| changing working directory back to "/path/to" |  | ||||||
|  | @ -1,35 +0,0 @@ | ||||||
| processing file "helmfile.yaml" in directory "." |  | ||||||
| changing working directory to "/path/to" |  | ||||||
| merged environment: &{default  map[] map[]} |  | ||||||
| 3 release(s) found in helmfile.yaml |  | ||||||
| 
 |  | ||||||
| Affected releases are: |  | ||||||
|   bar (stable/mychart2) UPDATED |  | ||||||
|   baz (stable/mychart3) UPDATED |  | ||||||
|   foo (stable/mychart1) UPDATED |  | ||||||
| 
 |  | ||||||
| invoking preapply hooks for 2 groups of releases in this order: |  | ||||||
| GROUP RELEASES |  | ||||||
| 1     default//foo |  | ||||||
| 2     default//baz, default//bar |  | ||||||
| 
 |  | ||||||
| invoking preapply hooks for releases in group 1/2: default//foo |  | ||||||
| invoking preapply hooks for releases in group 2/2: default//baz, default//bar |  | ||||||
| processing 2 groups of releases in this order: |  | ||||||
| GROUP RELEASES |  | ||||||
| 1     default//baz, default//bar |  | ||||||
| 2     default//foo |  | ||||||
| 
 |  | ||||||
| processing releases in group 1/2: default//baz, default//bar |  | ||||||
| update strategy - sync success |  | ||||||
| update strategy - sync success |  | ||||||
| processing releases in group 2/2: default//foo |  | ||||||
| getting deployed release version failed: Failed to get the version for: mychart1 |  | ||||||
| 
 |  | ||||||
| UPDATED RELEASES: |  | ||||||
| NAME   NAMESPACE   CHART             VERSION   DURATION |  | ||||||
| baz                stable/mychart3   3.1.0           0s |  | ||||||
| bar                stable/mychart2   3.1.0           0s |  | ||||||
| foo                stable/mychart1                   0s |  | ||||||
| 
 |  | ||||||
| changing working directory back to "/path/to" |  | ||||||
|  | @ -1,35 +0,0 @@ | ||||||
| processing file "helmfile.yaml" in directory "." |  | ||||||
| changing working directory to "/path/to" |  | ||||||
| merged environment: &{default  map[] map[]} |  | ||||||
| 3 release(s) found in helmfile.yaml |  | ||||||
| 
 |  | ||||||
| Affected releases are: |  | ||||||
|   bar (stable/mychart2) UPDATED |  | ||||||
|   baz (stable/mychart3) UPDATED |  | ||||||
|   foo (stable/mychart1) UPDATED |  | ||||||
| 
 |  | ||||||
| invoking preapply hooks for 2 groups of releases in this order: |  | ||||||
| GROUP RELEASES |  | ||||||
| 1     default//foo |  | ||||||
| 2     default//baz, default//bar |  | ||||||
| 
 |  | ||||||
| invoking preapply hooks for releases in group 1/2: default//foo |  | ||||||
| invoking preapply hooks for releases in group 2/2: default//baz, default//bar |  | ||||||
| processing 2 groups of releases in this order: |  | ||||||
| GROUP RELEASES |  | ||||||
| 1     default//baz, default//bar |  | ||||||
| 2     default//foo |  | ||||||
| 
 |  | ||||||
| processing releases in group 1/2: default//baz, default//bar |  | ||||||
| update strategy - sync success |  | ||||||
| update strategy - sync success |  | ||||||
| processing releases in group 2/2: default//foo |  | ||||||
| getting deployed release version failed: Failed to get the version for: mychart1 |  | ||||||
| 
 |  | ||||||
| UPDATED RELEASES: |  | ||||||
| NAME   NAMESPACE   CHART             VERSION   DURATION |  | ||||||
| baz                stable/mychart3   3.1.0           0s |  | ||||||
| bar                stable/mychart2   3.1.0           0s |  | ||||||
| foo                stable/mychart1                   0s |  | ||||||
| 
 |  | ||||||
| changing working directory back to "/path/to" |  | ||||||
|  | @ -56,10 +56,9 @@ type Release struct { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Affected struct { | type Affected struct { | ||||||
| 	Upgraded    []*Release | 	Upgraded []*Release | ||||||
| 	Reinstalled []*Release | 	Deleted  []*Release | ||||||
| 	Deleted     []*Release | 	Failed   []*Release | ||||||
| 	Failed      []*Release |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (helm *Helm) UpdateDeps(chart string) error { | func (helm *Helm) UpdateDeps(chart string) error { | ||||||
|  | @ -108,24 +107,7 @@ func (helm *Helm) RegistryLogin(name, username, password, caFile, certFile, keyF | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| func (helm *Helm) SyncRelease(context helmexec.HelmContext, name, chart, namespace string, flags ...string) error { | func (helm *Helm) SyncRelease(context helmexec.HelmContext, name, chart, namespace string, flags ...string) error { | ||||||
| 	if strings.Contains(name, "forbidden") { | 	if strings.Contains(name, "error") { | ||||||
| 		releaseExists := false |  | ||||||
| 		for _, release := range helm.Releases { |  | ||||||
| 			if release.Name == name { |  | ||||||
| 				releaseExists = true |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		releaseDeleted := false |  | ||||||
| 		for _, release := range helm.Deleted { |  | ||||||
| 			if release.Name == name { |  | ||||||
| 				releaseDeleted = true |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		// Only fail if the release is present in the helm.Releases to simulate a forbidden update if it exists
 |  | ||||||
| 		if releaseExists && !releaseDeleted { |  | ||||||
| 			return fmt.Errorf("cannot patch %q with kind StatefulSet: StatefulSet.apps %q is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'ordinals', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden", name, name) |  | ||||||
| 		} |  | ||||||
| 	} else if strings.Contains(name, "error") { |  | ||||||
| 		return errors.New("error") | 		return errors.New("error") | ||||||
| 	} | 	} | ||||||
| 	helm.sync(helm.ReleasesMutex, func() { | 	helm.sync(helm.ReleasesMutex, func() { | ||||||
|  |  | ||||||
|  | @ -26,8 +26,6 @@ const ( | ||||||
| 	DefaultHCLFileExtension = ".hcl" | 	DefaultHCLFileExtension = ".hcl" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ValidUpdateStrategyValues = []string{UpdateStrategyReinstallIfForbidden} |  | ||||||
| 
 |  | ||||||
| type StateLoadError struct { | type StateLoadError struct { | ||||||
| 	Msg   string | 	Msg   string | ||||||
| 	Cause error | 	Cause error | ||||||
|  | @ -45,14 +43,6 @@ func (e *UndefinedEnvError) Error() string { | ||||||
| 	return fmt.Sprintf("environment \"%s\" is not defined", e.Env) | 	return fmt.Sprintf("environment \"%s\" is not defined", e.Env) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type InvalidUpdateStrategyError struct { |  | ||||||
| 	UpdateStrategy string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (e *InvalidUpdateStrategyError) Error() string { |  | ||||||
| 	return fmt.Sprintf("updateStrategy %q is invalid, valid values are: %s or not set", e.UpdateStrategy, strings.Join(ValidUpdateStrategyValues, ", ")) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type StateCreator struct { | type StateCreator struct { | ||||||
| 	logger *zap.SugaredLogger | 	logger *zap.SugaredLogger | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -43,9 +43,6 @@ const ( | ||||||
| 	// This is used by an interim solution to make the urfave/cli command report to the helmfile internal about that the
 | 	// This is used by an interim solution to make the urfave/cli command report to the helmfile internal about that the
 | ||||||
| 	// --timeout flag is missingl
 | 	// --timeout flag is missingl
 | ||||||
| 	EmptyTimeout = -1 | 	EmptyTimeout = -1 | ||||||
| 
 |  | ||||||
| 	// Valid enum for updateStrategy values
 |  | ||||||
| 	UpdateStrategyReinstallIfForbidden = "reinstallIfForbidden" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // ReleaseSetSpec is release set spec
 | // ReleaseSetSpec is release set spec
 | ||||||
|  | @ -280,8 +277,6 @@ type ReleaseSpec struct { | ||||||
| 	Force *bool `yaml:"force,omitempty"` | 	Force *bool `yaml:"force,omitempty"` | ||||||
| 	// Installed, when set to true, `delete --purge` the release
 | 	// Installed, when set to true, `delete --purge` the release
 | ||||||
| 	Installed *bool `yaml:"installed,omitempty"` | 	Installed *bool `yaml:"installed,omitempty"` | ||||||
| 	// UpdateStrategy, when set, indicate the strategy to use to update the release
 |  | ||||||
| 	UpdateStrategy string `yaml:"updateStrategy,omitempty"` |  | ||||||
| 	// Atomic, when set to true, restore previous state in case of a failed install/upgrade attempt
 | 	// Atomic, when set to true, restore previous state in case of a failed install/upgrade attempt
 | ||||||
| 	Atomic *bool `yaml:"atomic,omitempty"` | 	Atomic *bool `yaml:"atomic,omitempty"` | ||||||
| 	// CleanupOnFail, when set to true, the --cleanup-on-fail helm flag is passed to the upgrade command
 | 	// CleanupOnFail, when set to true, the --cleanup-on-fail helm flag is passed to the upgrade command
 | ||||||
|  | @ -472,7 +467,6 @@ type SetValue struct { | ||||||
| // AffectedReleases hold the list of released that where updated, deleted, or in error
 | // AffectedReleases hold the list of released that where updated, deleted, or in error
 | ||||||
| type AffectedReleases struct { | type AffectedReleases struct { | ||||||
| 	Upgraded     []*ReleaseSpec | 	Upgraded     []*ReleaseSpec | ||||||
| 	Reinstalled  []*ReleaseSpec |  | ||||||
| 	Deleted      []*ReleaseSpec | 	Deleted      []*ReleaseSpec | ||||||
| 	Failed       []*ReleaseSpec | 	Failed       []*ReleaseSpec | ||||||
| 	DeleteFailed []*ReleaseSpec | 	DeleteFailed []*ReleaseSpec | ||||||
|  | @ -1043,24 +1037,20 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme | ||||||
| 						} | 						} | ||||||
| 						m.Unlock() | 						m.Unlock() | ||||||
| 					} | 					} | ||||||
| 				} else if release.UpdateStrategy == UpdateStrategyReinstallIfForbidden { | 				} else if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil { | ||||||
| 					relErr = st.performSyncOrReinstallOfRelease(affectedReleases, helm, context, release, chart, m, flags...) | 					m.Lock() | ||||||
|  | 					affectedReleases.Failed = append(affectedReleases.Failed, release) | ||||||
|  | 					m.Unlock() | ||||||
|  | 					relErr = newReleaseFailedError(release, err) | ||||||
| 				} else { | 				} else { | ||||||
| 					if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil { | 					m.Lock() | ||||||
| 						m.Lock() | 					affectedReleases.Upgraded = append(affectedReleases.Upgraded, release) | ||||||
| 						affectedReleases.Failed = append(affectedReleases.Failed, release) | 					m.Unlock() | ||||||
| 						m.Unlock() | 					installedVersion, err := st.getDeployedVersion(context, helm, release) | ||||||
| 						relErr = newReleaseFailedError(release, err) | 					if err != nil { // err is not really impacting so just log it
 | ||||||
|  | 						st.logger.Debugf("getting deployed release version failed: %v", err) | ||||||
| 					} else { | 					} else { | ||||||
| 						m.Lock() | 						release.installedVersion = installedVersion | ||||||
| 						affectedReleases.Upgraded = append(affectedReleases.Upgraded, release) |  | ||||||
| 						m.Unlock() |  | ||||||
| 						installedVersion, err := st.getDeployedVersion(context, helm, release) |  | ||||||
| 						if err != nil { // err is not really impacting so just log it
 |  | ||||||
| 							st.logger.Debugf("getting deployed release version failed: %v", err) |  | ||||||
| 						} else { |  | ||||||
| 							release.installedVersion = installedVersion |  | ||||||
| 						} |  | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
|  | @ -1106,77 +1096,6 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (st *HelmState) performSyncOrReinstallOfRelease(affectedReleases *AffectedReleases, helm helmexec.Interface, context helmexec.HelmContext, release *ReleaseSpec, chart string, m *sync.Mutex, flags ...string) *ReleaseError { |  | ||||||
| 	if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil { |  | ||||||
| 		st.logger.Debugf("update strategy - sync failed: %s", err.Error()) |  | ||||||
| 		// Only fail if a different error than forbidden updates
 |  | ||||||
| 		if !strings.Contains(err.Error(), "Forbidden: updates") { |  | ||||||
| 			st.logger.Debugf("update strategy - sync failed not due to Forbidden updates") |  | ||||||
| 			m.Lock() |  | ||||||
| 			affectedReleases.Failed = append(affectedReleases.Failed, release) |  | ||||||
| 			m.Unlock() |  | ||||||
| 			return newReleaseFailedError(release, err) |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		st.logger.Debugf("update strategy - sync success") |  | ||||||
| 		m.Lock() |  | ||||||
| 		affectedReleases.Upgraded = append(affectedReleases.Upgraded, release) |  | ||||||
| 		m.Unlock() |  | ||||||
| 		installedVersion, err := st.getDeployedVersion(context, helm, release) |  | ||||||
| 		if err != nil { // err is not really impacting so just log it
 |  | ||||||
| 			st.logger.Debugf("update strategy - getting deployed release version failed: %v", err) |  | ||||||
| 		} else { |  | ||||||
| 			release.installedVersion = installedVersion |  | ||||||
| 		} |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	st.logger.Infof("Failed to sync due to forbidden updates, attempting to reinstall %q allowed by update strategy", release.Name) |  | ||||||
| 	installed, err := st.isReleaseInstalled(context, helm, *release) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return newReleaseFailedError(release, err) |  | ||||||
| 	} |  | ||||||
| 	if installed { |  | ||||||
| 		var args []string |  | ||||||
| 		if release.Namespace != "" { |  | ||||||
| 			args = append(args, "--namespace", release.Namespace) |  | ||||||
| 		} |  | ||||||
| 		deleteWaitFlag := true |  | ||||||
| 		release.DeleteWait = &deleteWaitFlag |  | ||||||
| 		args = st.appendDeleteWaitFlags(args, release) |  | ||||||
| 		deletionFlags := st.appendConnectionFlags(args, release) |  | ||||||
| 		m.Lock() |  | ||||||
| 		if _, err := st.triggerReleaseEvent("preuninstall", nil, release, "sync"); err != nil { |  | ||||||
| 			affectedReleases.Failed = append(affectedReleases.Failed, release) |  | ||||||
| 			return newReleaseFailedError(release, err) |  | ||||||
| 		} else if err := helm.DeleteRelease(context, release.Name, deletionFlags...); err != nil { |  | ||||||
| 			affectedReleases.Failed = append(affectedReleases.Failed, release) |  | ||||||
| 			return newReleaseFailedError(release, err) |  | ||||||
| 		} else if _, err := st.triggerReleaseEvent("postuninstall", nil, release, "sync"); err != nil { |  | ||||||
| 			affectedReleases.Failed = append(affectedReleases.Failed, release) |  | ||||||
| 			return newReleaseFailedError(release, err) |  | ||||||
| 		} |  | ||||||
| 		m.Unlock() |  | ||||||
| 	} |  | ||||||
| 	if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil { |  | ||||||
| 		m.Lock() |  | ||||||
| 		affectedReleases.Failed = append(affectedReleases.Failed, release) |  | ||||||
| 		m.Unlock() |  | ||||||
| 		return newReleaseFailedError(release, err) |  | ||||||
| 	} else { |  | ||||||
| 		m.Lock() |  | ||||||
| 		affectedReleases.Reinstalled = append(affectedReleases.Reinstalled, release) |  | ||||||
| 		m.Unlock() |  | ||||||
| 		installedVersion, err := st.getDeployedVersion(context, helm, release) |  | ||||||
| 		if err != nil { // err is not really impacting so just log it
 |  | ||||||
| 			st.logger.Debugf("update strategy - getting deployed release version failed: %v", err) |  | ||||||
| 		} else { |  | ||||||
| 			release.installedVersion = installedVersion |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (st *HelmState) listReleases(context helmexec.HelmContext, helm helmexec.Interface, release *ReleaseSpec) (string, error) { | func (st *HelmState) listReleases(context helmexec.HelmContext, helm helmexec.Interface, release *ReleaseSpec) (string, error) { | ||||||
| 	flags := st.kubeConnectionFlags(release) | 	flags := st.kubeConnectionFlags(release) | ||||||
| 	if release.Namespace != "" { | 	if release.Namespace != "" { | ||||||
|  | @ -3816,28 +3735,6 @@ func (ar *AffectedReleases) DisplayAffectedReleases(logger *zap.SugaredLogger) { | ||||||
| 		} | 		} | ||||||
| 		logger.Info(tbl.String()) | 		logger.Info(tbl.String()) | ||||||
| 	} | 	} | ||||||
| 	if len(ar.Reinstalled) > 0 { |  | ||||||
| 		logger.Info("\nREINSTALLED RELEASES:") |  | ||||||
| 		tbl, _ := prettytable.NewTable(prettytable.Column{Header: "NAME"}, |  | ||||||
| 			prettytable.Column{Header: "NAMESPACE", MinWidth: 6}, |  | ||||||
| 			prettytable.Column{Header: "CHART", MinWidth: 6}, |  | ||||||
| 			prettytable.Column{Header: "VERSION", MinWidth: 6}, |  | ||||||
| 			prettytable.Column{Header: "DURATION", AlignRight: true}, |  | ||||||
| 		) |  | ||||||
| 		tbl.Separator = "   " |  | ||||||
| 		for _, release := range ar.Reinstalled { |  | ||||||
| 			modifiedChart, modErr := hideChartCredentials(release.Chart) |  | ||||||
| 			if modErr != nil { |  | ||||||
| 				logger.Warn("Could not modify chart credentials, %v", modErr) |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			err := tbl.AddRow(release.Name, release.Namespace, modifiedChart, release.installedVersion, release.duration.Round(time.Second)) |  | ||||||
| 			if err != nil { |  | ||||||
| 				logger.Warn("Could not add row, %v", err) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		logger.Info(tbl.String()) |  | ||||||
| 	} |  | ||||||
| 	if len(ar.Deleted) > 0 { | 	if len(ar.Deleted) > 0 { | ||||||
| 		logger.Info("\nDELETED RELEASES:") | 		logger.Info("\nDELETED RELEASES:") | ||||||
| 		tbl, _ := prettytable.NewTable(prettytable.Column{Header: "NAME"}, | 		tbl, _ := prettytable.NewTable(prettytable.Column{Header: "NAME"}, | ||||||
|  |  | ||||||
|  | @ -1640,112 +1640,6 @@ func TestHelmState_SyncReleasesAffectedRealeases(t *testing.T) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestHelmState_SyncReleasesAffectedReleasesWithReinstallIfForbidden(t *testing.T) { |  | ||||||
| 	no := false |  | ||||||
| 	tests := []struct { |  | ||||||
| 		name         string |  | ||||||
| 		releases     []ReleaseSpec |  | ||||||
| 		installed    []bool |  | ||||||
| 		wantAffected exectest.Affected |  | ||||||
| 	}{ |  | ||||||
| 		{ |  | ||||||
| 			name: "2 new", |  | ||||||
| 			releases: []ReleaseSpec{ |  | ||||||
| 				{ |  | ||||||
| 					Name:           "releaseNameFoo-forbidden", |  | ||||||
| 					Chart:          "foo", |  | ||||||
| 					UpdateStrategy: "reinstallIfForbidden", |  | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					Name:           "releaseNameBar-forbidden", |  | ||||||
| 					Chart:          "foo", |  | ||||||
| 					UpdateStrategy: "reinstallIfForbidden", |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 			wantAffected: exectest.Affected{ |  | ||||||
| 				Upgraded: []*exectest.Release{ |  | ||||||
| 					{Name: "releaseNameFoo-forbidden", Flags: []string{}}, |  | ||||||
| 					{Name: "releaseNameBar-forbidden", Flags: []string{}}, |  | ||||||
| 				}, |  | ||||||
| 				Reinstalled: nil, |  | ||||||
| 				Deleted:     nil, |  | ||||||
| 				Failed:      nil, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "1 removed, 1 new, 1 reinstalled first new", |  | ||||||
| 			releases: []ReleaseSpec{ |  | ||||||
| 				{ |  | ||||||
| 					Name:           "releaseNameFoo-forbidden", |  | ||||||
| 					Chart:          "foo", |  | ||||||
| 					UpdateStrategy: "reinstallIfForbidden", |  | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					Name:           "releaseNameBar", |  | ||||||
| 					Chart:          "foo", |  | ||||||
| 					UpdateStrategy: "reinstallIfForbidden", |  | ||||||
| 					Installed:      &no, |  | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					Name:           "releaseNameFoo-forbidden", |  | ||||||
| 					Chart:          "foo", |  | ||||||
| 					UpdateStrategy: "reinstallIfForbidden", |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 			installed: []bool{true, true, true}, |  | ||||||
| 			wantAffected: exectest.Affected{ |  | ||||||
| 				Upgraded: []*exectest.Release{ |  | ||||||
| 					{Name: "releaseNameFoo-forbidden", Flags: []string{}}, |  | ||||||
| 				}, |  | ||||||
| 				Reinstalled: []*exectest.Release{ |  | ||||||
| 					{Name: "releaseNameFoo-forbidden", Flags: []string{}}, |  | ||||||
| 				}, |  | ||||||
| 				Deleted: []*exectest.Release{ |  | ||||||
| 					{Name: "releaseNameBar", Flags: []string{}}, |  | ||||||
| 				}, |  | ||||||
| 				Failed: nil, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	for _, tt := range tests { |  | ||||||
| 		t.Run(tt.name, func(t *testing.T) { |  | ||||||
| 			state := &HelmState{ |  | ||||||
| 				ReleaseSetSpec: ReleaseSetSpec{ |  | ||||||
| 					Releases: tt.releases, |  | ||||||
| 				}, |  | ||||||
| 				logger:         logger, |  | ||||||
| 				valsRuntime:    valsRuntime, |  | ||||||
| 				RenderedValues: map[string]any{}, |  | ||||||
| 			} |  | ||||||
| 			helm := &exectest.Helm{ |  | ||||||
| 				Lists: map[exectest.ListKey]string{}, |  | ||||||
| 			} |  | ||||||
| 			//simulate the release is already installed
 |  | ||||||
| 			for i, release := range tt.releases { |  | ||||||
| 				if tt.installed != nil && tt.installed[i] { |  | ||||||
| 					helm.Lists[exectest.ListKey{Filter: "^" + release.Name + "$", Flags: "--uninstalling --deployed --failed --pending"}] = release.Name |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			affectedReleases := AffectedReleases{} |  | ||||||
| 			if err := state.SyncReleases(&affectedReleases, helm, []string{}, 1); err != nil { |  | ||||||
| 				if !testEq(affectedReleases.Failed, tt.wantAffected.Failed) { |  | ||||||
| 					t.Errorf("HelmState.SyncReleases() error failed for [%s] = %v, want %v", tt.name, affectedReleases.Failed, tt.wantAffected.Failed) |  | ||||||
| 				} //else expected error
 |  | ||||||
| 			} |  | ||||||
| 			if !testEq(affectedReleases.Upgraded, tt.wantAffected.Upgraded) { |  | ||||||
| 				t.Errorf("HelmState.SyncReleases() upgrade failed for [%s] = %v, want %v", tt.name, affectedReleases.Upgraded, tt.wantAffected.Upgraded) |  | ||||||
| 			} |  | ||||||
| 			if !testEq(affectedReleases.Reinstalled, tt.wantAffected.Reinstalled) { |  | ||||||
| 				t.Errorf("HelmState.SyncReleases() reinstalled failed for [%s] = %v, want %v", tt.name, affectedReleases.Reinstalled, tt.wantAffected.Reinstalled) |  | ||||||
| 			} |  | ||||||
| 			if !testEq(affectedReleases.Deleted, tt.wantAffected.Deleted) { |  | ||||||
| 				t.Errorf("HelmState.SyncReleases() deleted failed for [%s] = %v, want %v", tt.name, affectedReleases.Deleted, tt.wantAffected.Deleted) |  | ||||||
| 			} |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func testEq(a []*ReleaseSpec, b []*exectest.Release) bool { | func testEq(a []*ReleaseSpec, b []*exectest.Release) bool { | ||||||
| 	// If one is nil, the other must also be nil.
 | 	// If one is nil, the other must also be nil.
 | ||||||
| 	if (a == nil) != (b == nil) { | 	if (a == nil) != (b == nil) { | ||||||
|  |  | ||||||
|  | @ -38,39 +38,39 @@ func TestGenerateID(t *testing.T) { | ||||||
| 	run(testcase{ | 	run(testcase{ | ||||||
| 		subject: "baseline", | 		subject: "baseline", | ||||||
| 		release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"}, | 		release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"}, | ||||||
| 		want:    "foo-values-67dc97cbcb", | 		want:    "foo-values-7d454b9558", | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	run(testcase{ | 	run(testcase{ | ||||||
| 		subject: "different bytes content", | 		subject: "different bytes content", | ||||||
| 		release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"}, | 		release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"}, | ||||||
| 		data:    []byte(`{"k":"v"}`), | 		data:    []byte(`{"k":"v"}`), | ||||||
| 		want:    "foo-values-75d7c4758c", | 		want:    "foo-values-59c86d55bf", | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	run(testcase{ | 	run(testcase{ | ||||||
| 		subject: "different map content", | 		subject: "different map content", | ||||||
| 		release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"}, | 		release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"}, | ||||||
| 		data:    map[string]any{"k": "v"}, | 		data:    map[string]any{"k": "v"}, | ||||||
| 		want:    "foo-values-685f8cf685", | 		want:    "foo-values-6f87c5cd79", | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	run(testcase{ | 	run(testcase{ | ||||||
| 		subject: "different chart", | 		subject: "different chart", | ||||||
| 		release: ReleaseSpec{Name: "foo", Chart: "stable/envoy"}, | 		release: ReleaseSpec{Name: "foo", Chart: "stable/envoy"}, | ||||||
| 		want:    "foo-values-75597d9c57", | 		want:    "foo-values-5dfd748475", | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	run(testcase{ | 	run(testcase{ | ||||||
| 		subject: "different name", | 		subject: "different name", | ||||||
| 		release: ReleaseSpec{Name: "bar", Chart: "incubator/raw"}, | 		release: ReleaseSpec{Name: "bar", Chart: "incubator/raw"}, | ||||||
| 		want:    "bar-values-7b77df65ff", | 		want:    "bar-values-858b9c55cc", | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	run(testcase{ | 	run(testcase{ | ||||||
| 		subject: "specific ns", | 		subject: "specific ns", | ||||||
| 		release: ReleaseSpec{Name: "foo", Chart: "incubator/raw", Namespace: "myns"}, | 		release: ReleaseSpec{Name: "foo", Chart: "incubator/raw", Namespace: "myns"}, | ||||||
| 		want:    "myns-foo-values-85f979545c", | 		want:    "myns-foo-values-58dc9c6667", | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	for id, n := range ids { | 	for id, n := range ids { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue