feat: update strategy for reinstall (#2019)
* feat: Add updateStrategy option in the state file with 'reinstall'/'reinstallIfForbidden' choices to uninstall and apply the specific release(s) (if forbidden to update) Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Fix unit tests related to the new updateStrategy feature Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Fix unit tests related to the new updateStrategy feature Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Resolve linter issue due to cognitive complexity Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Updated index.md to describe the possible values of updateStrategy Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Add validation of updateStrategy parameter and unit test Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Updated unit test Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Removed 'reinstall' update strategy option to only have reinstallIfForbidden, cleanup of pre-sync changes, adapted unit tests Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Display affected releases that were reinstalled Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Make sure to add --wait when deleting a release to be reinstalled due to reinstallIfForbidden Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> * Apply suggestions from Copilot code review Signed-off-by: Simon Bouchard <sbouchard@rbbn.com> --------- Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>
This commit is contained in:
		
							parent
							
								
									a45d681a08
								
							
						
					
					
						commit
						a6fab4dc75
					
				| 
						 | 
					@ -328,6 +328,9 @@ 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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -415,4 +415,28 @@ 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"}},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -220,6 +220,97 @@ 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": `
 | 
				
			||||||
| 
						 | 
					@ -3076,6 +3167,97 @@ 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
 | 
				
			||||||
		//
 | 
							//
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					@ -3772,7 +3954,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("releaes[%d].flags[%d]: got %v, want %v", relIdx, flagIdx, helm.Deleted[relIdx].Flags[flagIdx], wantDeletes[relIdx].Flags[flagIdx])
 | 
												t.Errorf("releases[%d].flags[%d]: got %v, want %v", relIdx, flagIdx, helm.Deleted[relIdx].Flags[flagIdx], wantDeletes[relIdx].Flags[flagIdx])
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"slices"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"dario.cat/mergo"
 | 
						"dario.cat/mergo"
 | 
				
			||||||
	"github.com/helmfile/vals"
 | 
						"github.com/helmfile/vals"
 | 
				
			||||||
| 
						 | 
					@ -285,6 +286,18 @@ 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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								pkg/app/testdata/app_diff_test/show_diff_on_changed_selected_release_with_reinstall
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										11
									
								
								pkg/app/testdata/app_diff_test/show_diff_on_changed_selected_release_with_reinstall
								
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					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"
 | 
				
			||||||
							
								
								
									
										35
									
								
								pkg/app/testdata/testapply/install-with-upgrade-with-reinstallifforbidden/log
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										35
									
								
								pkg/app/testdata/testapply/install-with-upgrade-with-reinstallifforbidden/log
								
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					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"
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					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"
 | 
				
			||||||
| 
						 | 
					@ -57,6 +57,7 @@ type Release struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Affected struct {
 | 
					type Affected struct {
 | 
				
			||||||
	Upgraded    []*Release
 | 
						Upgraded    []*Release
 | 
				
			||||||
 | 
						Reinstalled []*Release
 | 
				
			||||||
	Deleted     []*Release
 | 
						Deleted     []*Release
 | 
				
			||||||
	Failed      []*Release
 | 
						Failed      []*Release
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -107,7 +108,24 @@ 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, "error") {
 | 
						if strings.Contains(name, "forbidden") {
 | 
				
			||||||
 | 
							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,6 +26,8 @@ const (
 | 
				
			||||||
	DefaultHCLFileExtension = ".hcl"
 | 
						DefaultHCLFileExtension = ".hcl"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var ValidUpdateStrategyValues = []string{UpdateStrategyReinstallIfForbidden}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type StateLoadError struct {
 | 
					type StateLoadError struct {
 | 
				
			||||||
	Msg   string
 | 
						Msg   string
 | 
				
			||||||
	Cause error
 | 
						Cause error
 | 
				
			||||||
| 
						 | 
					@ -43,6 +45,14 @@ 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,6 +43,9 @@ 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
 | 
				
			||||||
| 
						 | 
					@ -277,6 +280,8 @@ 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
 | 
				
			||||||
| 
						 | 
					@ -467,6 +472,7 @@ 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
 | 
				
			||||||
| 
						 | 
					@ -1037,7 +1043,10 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
						m.Unlock()
 | 
											m.Unlock()
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				} else if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil {
 | 
									} else if release.UpdateStrategy == UpdateStrategyReinstallIfForbidden {
 | 
				
			||||||
 | 
										relErr = st.performSyncOrReinstallOfRelease(affectedReleases, helm, context, release, chart, m, flags...)
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil {
 | 
				
			||||||
						m.Lock()
 | 
											m.Lock()
 | 
				
			||||||
						affectedReleases.Failed = append(affectedReleases.Failed, release)
 | 
											affectedReleases.Failed = append(affectedReleases.Failed, release)
 | 
				
			||||||
						m.Unlock()
 | 
											m.Unlock()
 | 
				
			||||||
| 
						 | 
					@ -1053,6 +1062,7 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme
 | 
				
			||||||
							release.installedVersion = installedVersion
 | 
												release.installedVersion = installedVersion
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if _, err := st.triggerPostsyncEvent(release, relErr, "sync"); err != nil {
 | 
									if _, err := st.triggerPostsyncEvent(release, relErr, "sync"); err != nil {
 | 
				
			||||||
					if relErr == nil {
 | 
										if relErr == nil {
 | 
				
			||||||
| 
						 | 
					@ -1096,6 +1106,77 @@ 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 != "" {
 | 
				
			||||||
| 
						 | 
					@ -3735,6 +3816,28 @@ 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,6 +1640,112 @@ 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-7d454b9558",
 | 
							want:    "foo-values-67dc97cbcb",
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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-59c86d55bf",
 | 
							want:    "foo-values-75d7c4758c",
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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-6f87c5cd79",
 | 
							want:    "foo-values-685f8cf685",
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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-5dfd748475",
 | 
							want:    "foo-values-75597d9c57",
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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-858b9c55cc",
 | 
							want:    "bar-values-7b77df65ff",
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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-58dc9c6667",
 | 
							want:    "myns-foo-values-85f979545c",
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for id, n := range ids {
 | 
						for id, n := range ids {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue