Make a few helmfile sub-commands consistently support needs-related flags (#78)

* Make a few helmfile sub-commands to consistently support needs-related flags

* helmfile-diff adds support for --include-transitive-needs
* helmfile-template adds support for --skip-needs
* helmfile-lint adds support for --skip-needs, --include-needs, and --include-transitive-needs

Ref https://github.com/roboll/helmfile/issues/2055

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Fix a few helmfile-lint needs related bugs and add tests

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Is include-transitive-needs realy working as intended? 🤔

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Confirm that it does fail on unselected need by default

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Add missing testdata

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Test helmfile-template for include/skip needs support

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Fix a few terms

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Add more tests to better know the current helmfile-diff behavior around needs

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Fix failing tests

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Fix helmfile-diff to consistently handle skip/include-needs

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Extract testhelper.RequireLog for reusing

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Fix all bugs and test cases for TestDiff and TestDiff_2

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Fix TestDiff_2

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Fix TestDiff

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Fix TestDiffWithNeeds

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Unify behavior on including disabled releases as needs for lint and template

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>

* Fix bug that --include-transitive-needs does not imply include-needs

Signed-off-by: Yusuke Kuoka <ykuoka@gmail.com>
This commit is contained in:
Yusuke Kuoka
2022-06-20 07:19:39 +09:00
committed by GitHub
parent 3e60f72d1f
commit fc306ec3d1
118 changed files with 9728 additions and 1235 deletions
+178 -399
View File
@@ -17,28 +17,29 @@ import (
)
type diffConfig struct {
args string
values []string
retainValuesFiles bool
set []string
validate bool
skipCRDs bool
skipDeps bool
includeTests bool
includeNeeds bool
skipNeeds bool
suppress []string
suppressSecrets bool
showSecrets bool
suppressDiff bool
noColor bool
context int
diffOutput string
concurrency int
detailedExitcode bool
interactive bool
skipDiffOnInstall bool
logger *zap.SugaredLogger
args string
values []string
retainValuesFiles bool
set []string
validate bool
skipCRDs bool
skipDeps bool
includeTests bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
suppress []string
suppressSecrets bool
showSecrets bool
suppressDiff bool
noColor bool
context int
diffOutput string
concurrency int
detailedExitcode bool
interactive bool
skipDiffOnInstall bool
logger *zap.SugaredLogger
}
func (a diffConfig) Args() string {
@@ -69,12 +70,16 @@ func (a diffConfig) IncludeTests() bool {
return a.includeTests
}
func (a diffConfig) SkipNeeds() bool {
return a.skipNeeds
}
func (a diffConfig) IncludeNeeds() bool {
return a.includeNeeds
}
func (a diffConfig) SkipNeeds() bool {
return a.skipNeeds
func (a diffConfig) IncludeTransitiveNeeds() bool {
return a.includeTransitiveNeeds
}
func (a diffConfig) Suppress() []string {
@@ -135,7 +140,8 @@ func (a diffConfig) RetainValuesFiles() bool {
func TestDiff(t *testing.T) {
type flags struct {
skipNeeds bool
skipNeeds bool
includeNeeds bool
}
testcases := []struct {
@@ -246,145 +252,6 @@ backend-v1 4 Fri Nov 1 08:40:07 2019 DEPLOYED backend-3.1.0 3.1.0
concurrency: 1,
upgraded: []exectest.Release{},
deleted: []exectest.Release{},
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: database
3: chart: charts/mysql
4: needs:
5: - logging
6: - name: frontend-v1
7: chart: charts/frontend
8: installed: false
9: needs:
10: - servicemesh
11: - logging
12: - backend-v1
13: - name: frontend-v2
14: chart: charts/frontend
15: needs:
16: - servicemesh
17: - logging
18: - backend-v2
19: - name: frontend-v3
20: chart: charts/frontend
21: needs:
22: - servicemesh
23: - logging
24: - backend-v2
25: - name: backend-v1
26: chart: charts/backend
27: installed: false
28: needs:
29: - servicemesh
30: - logging
31: - database
32: - anotherbackend
33: - name: backend-v2
34: chart: charts/backend
35: needs:
36: - servicemesh
37: - logging
38: - database
39: - anotherbackend
40: - name: anotherbackend
41: chart: charts/anotherbackend
42: needs:
43: - servicemesh
44: - logging
45: - database
46: - name: servicemesh
47: chart: charts/istio
48: needs:
49: - logging
50: - name: logging
51: chart: charts/fluent-bit
52: - name: front-proxy
53: chart: stable/envoy
54:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: database
3: chart: charts/mysql
4: needs:
5: - logging
6: - name: frontend-v1
7: chart: charts/frontend
8: installed: false
9: needs:
10: - servicemesh
11: - logging
12: - backend-v1
13: - name: frontend-v2
14: chart: charts/frontend
15: needs:
16: - servicemesh
17: - logging
18: - backend-v2
19: - name: frontend-v3
20: chart: charts/frontend
21: needs:
22: - servicemesh
23: - logging
24: - backend-v2
25: - name: backend-v1
26: chart: charts/backend
27: installed: false
28: needs:
29: - servicemesh
30: - logging
31: - database
32: - anotherbackend
33: - name: backend-v2
34: chart: charts/backend
35: needs:
36: - servicemesh
37: - logging
38: - database
39: - anotherbackend
40: - name: anotherbackend
41: chart: charts/anotherbackend
42: needs:
43: - servicemesh
44: - logging
45: - database
46: - name: servicemesh
47: chart: charts/istio
48: needs:
49: - logging
50: - name: logging
51: chart: charts/fluent-bit
52: - name: front-proxy
53: chart: stable/envoy
54:
merged environment: &{default map[] map[]}
10 release(s) found in helmfile.yaml
Affected releases are:
anotherbackend (charts/anotherbackend) UPDATED
backend-v1 (charts/backend) DELETED
backend-v2 (charts/backend) UPDATED
database (charts/mysql) UPDATED
front-proxy (stable/envoy) UPDATED
frontend-v1 (charts/frontend) DELETED
frontend-v3 (charts/frontend) UPDATED
logging (charts/fluent-bit) UPDATED
servicemesh (charts/istio) UPDATED
changing working directory back to "/path/to"
`,
},
//
// noop: no changes
@@ -448,51 +315,6 @@ releases:
upgraded: []exectest.Release{},
deleted: []exectest.Release{},
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: baz
3: chart: mychart3
4: - name: foo
5: chart: mychart1
6: needs:
7: - bar
8: - name: bar
9: chart: mychart2
10:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: baz
3: chart: mychart3
4: - name: foo
5: chart: mychart1
6: needs:
7: - bar
8: - name: bar
9: chart: mychart2
10:
merged environment: &{default map[] map[]}
3 release(s) found in helmfile.yaml
Affected releases are:
bar (mychart2) UPDATED
baz (mychart3) UPDATED
foo (mychart1) UPDATED
changing working directory back to "/path/to"
`,
},
//
// upgrades
@@ -612,31 +434,7 @@ releases:
upgraded: []exectest.Release{},
},
{
name: "helm3: upgrade when ns2/bar needs ns1/foo",
loc: location(),
files: map[string]string{
"/path/to/helmfile.yaml": `
releases:
- name: bar
chart: mychart2
namespace: ns2
needs:
- ns1/foo
- name: foo
chart: mychart1
namespace: ns1
`,
},
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--namespacens2--detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
{
name: "upgrade when tns1/ns1/foo needs tns2/ns2/bar",
name: "upgrade when tns1 ns1 foo needs tns2 ns2 bar",
loc: location(),
files: map[string]string{
@@ -663,7 +461,7 @@ releases:
upgraded: []exectest.Release{},
},
{
name: "helm2: upgrade when tns2/bar needs tns1/foo",
name: "helm2 upgrade when tns2 bar needs tns1 foo",
loc: location(),
files: map[string]string{
"/path/to/helmfile.yaml": `
@@ -689,57 +487,9 @@ releases:
upgraded: []exectest.Release{},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: namespace: ns2
5: tillerNamespace: tns2
6: needs:
7: - tns1/foo
8: - name: foo
9: chart: mychart1
10: namespace: ns1
11: tillerNamespace: tns1
12:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: namespace: ns2
5: tillerNamespace: tns2
6: needs:
7: - tns1/foo
8: - name: foo
9: chart: mychart1
10: namespace: ns1
11: tillerNamespace: tns1
12:
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml
Affected releases are:
bar (mychart2) UPDATED
foo (mychart1) UPDATED
changing working directory back to "/path/to"
`,
},
{
name: "helm3: upgrade when ns2/bar needs ns1/foo",
name: "helm3 upgrade when ns2 bar needs ns1 foo",
loc: location(),
files: map[string]string{
"/path/to/helmfile.yaml": `
@@ -763,50 +513,6 @@ releases:
upgraded: []exectest.Release{},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: namespace: ns2
5: needs:
6: - ns1/foo
7: - name: foo
8: chart: mychart1
9: namespace: ns1
10:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: namespace: ns2
5: needs:
6: - ns1/foo
7: - name: foo
8: chart: mychart1
9: namespace: ns1
10:
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml
Affected releases are:
bar (mychart2) UPDATED
foo (mychart1) UPDATED
changing working directory back to "/path/to"
`,
},
//
// deletes: deleting all releases in the correct order
@@ -926,7 +632,77 @@ releases:
`,
},
detailedExitcode: true,
error: "Identified at least one change",
error: `in ./helmfile.yaml: release "default//foo" depends on "default//bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV2ListFlags}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
foo 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart1-3.1.0 3.1.0 default
`,
{Filter: "^bar$", Flags: helmV2ListFlags}: `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
`,
},
upgraded: []exectest.Release{},
deleted: []exectest.Release{},
},
{
name: "delete bar when foo needs bar with include-needs",
loc: location(),
files: map[string]string{
"/path/to/helmfile.yaml": `
releases:
- name: bar
chart: mychart2
installed: false
- name: foo
chart: mychart1
needs:
- bar
`,
},
detailedExitcode: true,
flags: flags{
includeNeeds: true,
},
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV2ListFlags}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
foo 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart1-3.1.0 3.1.0 default
`,
{Filter: "^bar$", Flags: helmV2ListFlags}: `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
`,
},
upgraded: []exectest.Release{},
deleted: []exectest.Release{},
},
{
name: "delete bar when foo needs bar with skip-needs",
loc: location(),
files: map[string]string{
"/path/to/helmfile.yaml": `
releases:
- name: bar
chart: mychart2
installed: false
- name: foo
chart: mychart1
needs:
- bar
`,
},
detailedExitcode: true,
flags: flags{
skipNeeds: true,
},
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
@@ -958,6 +734,76 @@ releases:
`,
},
detailedExitcode: true,
error: `in ./helmfile.yaml: release "default//bar" depends on "default//foo" which does not match the selectors. Please add a selector like "--selector name=foo", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV2ListFlags}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
foo 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart1-3.1.0 3.1.0 default
`,
{Filter: "^bar$", Flags: helmV2ListFlags}: `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
`,
},
upgraded: []exectest.Release{},
deleted: []exectest.Release{},
},
{
name: "delete foo when bar needs foo with include-needs",
loc: location(),
files: map[string]string{
"/path/to/helmfile.yaml": `
releases:
- name: foo
chart: mychart1
installed: false
- name: bar
chart: mychart2
needs:
- foo
`,
},
flags: flags{
includeNeeds: true,
},
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV2ListFlags}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
foo 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart1-3.1.0 3.1.0 default
`,
{Filter: "^bar$", Flags: helmV2ListFlags}: `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
`,
},
upgraded: []exectest.Release{},
deleted: []exectest.Release{},
},
{
name: "delete foo when bar needs foo with skip-needs",
loc: location(),
files: map[string]string{
"/path/to/helmfile.yaml": `
releases:
- name: foo
chart: mychart1
installed: false
- name: bar
chart: mychart2
needs:
- foo
`,
},
flags: flags{
skipNeeds: true,
},
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2},
@@ -1050,76 +896,6 @@ releases:
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
error: "Identified at least one change",
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
merged environment: &{default map[] map[]}
2 release(s) matching app=test found in helmfile.yaml
Affected releases are:
external-secrets (incubator/raw) UPDATED
my-release (incubator/raw) UPDATED
changing working directory back to "/path/to"
`,
},
{
name: "upgrades with good selector with --skip-needs=false",
@@ -1542,6 +1318,7 @@ changing working directory back to "/path/to"
logger: logger,
detailedExitcode: tc.detailedExitcode,
skipNeeds: tc.flags.skipNeeds,
includeNeeds: tc.flags.includeNeeds,
})
var diffErrStr string
@@ -1591,6 +1368,8 @@ changing working directory back to "/path/to"
if exists {
t.Errorf("unexpected log for data defined %s:\nDIFF\n%s\nEOD", tc.loc, diff)
}
} else {
testhelper.RequireLog(t, "app_diff_test_1", bs)
}
})
}