From b5830a30117392650bfd5811f1b0b6c863d3c046 Mon Sep 17 00:00:00 2001 From: KUOKA Yusuke Date: Thu, 23 Jul 2020 00:30:16 +0900 Subject: [PATCH] Add ability to use go-getter for fetching remote manifests directory as chart (#1374) This, in combination with #1172, allows you to use `go-getter`-supported URL for K8s manifests on `chart`, so that Helmfile automatically fetches it and then turning it into a temporary local chart, which is then installed by Helmfile as similar as standard Helm charts. An example usecase of this is to install cert-manager CRDs which is distributed separately from the chart: ``` releases: - name: cert-manager-crds chart: git::http://github.com/jetstack/cert-manager.git@deploy/crds?ref=v0.15.2 ``` I'm adding this based on discussion with @lukasmrtvy. He was trying to install cert-manager and prometheus-opreator with Helmfile, and this combined with #1373 should do the job. Thanks for the input! --- pkg/app/app_test.go | 194 ++++++++++++++++++++++---------------------- pkg/state/helmx.go | 86 +++++++++++++++----- pkg/state/state.go | 32 ++++++-- 3 files changed, 189 insertions(+), 123 deletions(-) diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index 4a0f43f2..e3df3ce5 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -2373,16 +2373,16 @@ func TestTemplate_SingleStateFile(t *testing.T) { "/path/to/helmfile.yaml": ` releases: - name: myrelease1 - chart: mychart1 + chart: stable/mychart1 - name: myrelease2 - chart: mychart2 + chart: stable/mychart2 `, } var helm = &mockHelmExec{} var wantReleases = []mockTemplates{ - {name: "myrelease1", chart: "mychart1", flags: []string{"--namespace", "testNamespace", "--set", "foo=a", "--set", "bar=b", "--output-dir", "output/subdir/helmfile-[a-z0-9]{8}-myrelease1"}}, - {name: "myrelease2", chart: "mychart2", flags: []string{"--namespace", "testNamespace", "--set", "foo=a", "--set", "bar=b", "--output-dir", "output/subdir/helmfile-[a-z0-9]{8}-myrelease2"}}, + {name: "myrelease1", chart: "stable/mychart1", flags: []string{"--namespace", "testNamespace", "--set", "foo=a", "--set", "bar=b", "--output-dir", "output/subdir/helmfile-[a-z0-9]{8}-myrelease1"}}, + {name: "myrelease2", chart: "stable/mychart2", flags: []string{"--namespace", "testNamespace", "--set", "foo=a", "--set", "bar=b", "--output-dir", "output/subdir/helmfile-[a-z0-9]{8}-myrelease2"}}, } var buffer bytes.Buffer @@ -2407,7 +2407,9 @@ releases: valsRuntime: valsRuntime, }, files) - app.Template(configImpl{set: []string{"foo=a", "bar=b"}}) + if err := app.Template(configImpl{set: []string{"foo=a", "bar=b"}}); err != nil { + t.Fatalf("%v", err) + } for i := range wantReleases { if wantReleases[i].name != helm.templated[i].name { @@ -2438,13 +2440,13 @@ apiVersions: - helmfile.test/v2 releases: - name: myrelease1 - chart: mychart1 + chart: stable/mychart1 `, } var helm = &mockHelmExec{} var wantReleases = []mockTemplates{ - {name: "myrelease1", chart: "mychart1", flags: []string{"--api-versions", "helmfile.test/v1", "--api-versions", "helmfile.test/v2", "--namespace", "testNamespace", "--output-dir", "output/subdir/helmfile-[a-z0-9]{8}-myrelease1"}}, + {name: "myrelease1", chart: "stable/mychart1", flags: []string{"--api-versions", "helmfile.test/v1", "--api-versions", "helmfile.test/v2", "--namespace", "testNamespace", "--output-dir", "output/subdir/helmfile-[a-z0-9]{8}-myrelease1"}}, } var buffer bytes.Buffer @@ -2469,7 +2471,9 @@ releases: valsRuntime: valsRuntime, }, files) - app.Template(configImpl{}) + if err := app.Template(configImpl{}); err != nil { + t.Fatalf("%v", err) + } for i := range wantReleases { if wantReleases[i].name != helm.templated[i].name { @@ -2803,16 +2807,16 @@ backend-v1 "/path/to/helmfile.yaml": ` releases: - name: bar - chart: mychart2 + chart: stable/mychart2 - name: foo - chart: mychart1 + chart: stable/mychart1 installed: false needs: - bar `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: nil, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: nil, }, lists: map[exectest.ListKey]string{ exectest.ListKey{Filter: "^foo$", Flags: "--kube-contextdefault--deployed--failed--pending"}: ``, @@ -2833,19 +2837,19 @@ bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 defau "/path/to/helmfile.yaml": ` releases: - name: baz - chart: mychart3 + chart: stable/mychart3 - name: foo - chart: mychart1 + chart: stable/mychart1 needs: - bar - name: bar - chart: mychart2 + chart: stable/mychart2 `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "baz", Chart: "mychart3", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "baz", Chart: "stable/mychart3", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, lists: map[exectest.ListKey]string{}, upgraded: []exectest.Release{ @@ -2862,13 +2866,13 @@ first-pass rendering output of "helmfile.yaml.part.0": 0: 1: releases: 2: - name: baz - 3: chart: mychart3 + 3: chart: stable/mychart3 4: - name: foo - 5: chart: mychart1 + 5: chart: stable/mychart1 6: needs: 7: - bar 8: - name: bar - 9: chart: mychart2 + 9: chart: stable/mychart2 10: first-pass produced: &{default map[] map[]} @@ -2880,22 +2884,22 @@ second-pass rendering result of "helmfile.yaml.part.0": 0: 1: releases: 2: - name: baz - 3: chart: mychart3 + 3: chart: stable/mychart3 4: - name: foo - 5: chart: mychart1 + 5: chart: stable/mychart1 6: needs: 7: - bar 8: - name: bar - 9: chart: mychart2 + 9: chart: stable/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 + bar (stable/mychart2) UPDATED + baz (stable/mychart3) UPDATED + foo (stable/mychart1) UPDATED processing 2 groups of releases in this order: GROUP RELEASES @@ -2909,10 +2913,10 @@ processing releases in group 2/2: foo getting deployed release version failed:unexpected list key: {^foo$ --kube-contextdefault--deployed--failed--pending} UPDATED RELEASES: -NAME CHART VERSION -baz mychart3 -bar mychart2 -foo mychart1 +NAME CHART VERSION +baz stable/mychart3 +bar stable/mychart2 +foo stable/mychart1 `, }, @@ -2926,16 +2930,16 @@ foo mychart1 "/path/to/helmfile.yaml": ` releases: - name: bar - chart: mychart2 + chart: stable/mychart2 - name: foo - chart: mychart1 + chart: stable/mychart1 needs: - bar `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, upgraded: []exectest.Release{ {Name: "bar", Flags: []string{}}, @@ -2949,16 +2953,16 @@ releases: "/path/to/helmfile.yaml": ` releases: - name: foo - chart: mychart1 + chart: stable/mychart1 - name: bar - chart: mychart2 + chart: stable/mychart2 needs: - foo `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, upgraded: []exectest.Release{ {Name: "foo", Flags: []string{}}, @@ -2973,16 +2977,16 @@ releases: "/path/to/helmfile.yaml": ` releases: - name: bar - chart: mychart2 + chart: stable/mychart2 - name: foo - chart: mychart1 + chart: stable/mychart1 needs: - bar `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--namespacetestNamespace--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--namespacetestNamespace--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--namespacetestNamespace--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--namespacetestNamespace--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, upgraded: []exectest.Release{ {Name: "bar", Flags: []string{}}, @@ -2997,16 +3001,16 @@ releases: "/path/to/helmfile.yaml": ` releases: - name: foo - chart: mychart1 + chart: stable/mychart1 - name: bar - chart: mychart2 + chart: stable/mychart2 needs: - foo `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--namespacetestNamespace--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--namespacetestNamespace--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--namespacetestNamespace--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--namespacetestNamespace--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, upgraded: []exectest.Release{ {Name: "foo", Flags: []string{}}, @@ -3020,18 +3024,18 @@ releases: "/path/to/helmfile.yaml": ` releases: - name: foo - chart: mychart1 + chart: stable/mychart1 namespace: ns1 needs: - ns2/bar - name: bar - chart: mychart2 + chart: stable/mychart2 namespace: ns2 `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--namespacens2--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--namespacens2--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, upgraded: []exectest.Release{ {Name: "bar", Flags: []string{"--kube-context", "default", "--namespace", "ns2"}}, @@ -3045,18 +3049,18 @@ releases: "/path/to/helmfile.yaml": ` releases: - name: bar - chart: mychart2 + chart: stable/mychart2 namespace: ns2 needs: - ns1/foo - name: foo - chart: mychart1 + chart: stable/mychart1 namespace: ns1 `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--namespacens2--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--namespacens2--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, upgraded: []exectest.Release{ {Name: "foo", Flags: []string{"--kube-context", "default", "--namespace", "ns1"}}, @@ -3071,20 +3075,20 @@ releases: "/path/to/helmfile.yaml": ` releases: - name: foo - chart: mychart1 + chart: stable/mychart1 namespace: ns1 tillerNamespace: tns1 needs: - tns2/ns2/bar - name: bar - chart: mychart2 + chart: stable/mychart2 namespace: ns2 tillerNamespace: tns2 `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--tiller-namespacetns2--kube-contextdefault--namespacens2--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--tiller-namespacetns1--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--tiller-namespacetns2--kube-contextdefault--namespacens2--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--tiller-namespacetns1--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, upgraded: []exectest.Release{ {Name: "bar", Flags: []string{"--tiller-namespace", "tns2", "--kube-context", "default", "--namespace", "ns2"}}, @@ -3098,20 +3102,20 @@ releases: "/path/to/helmfile.yaml": ` releases: - name: bar - chart: mychart2 + chart: stable/mychart2 namespace: ns2 tillerNamespace: tns2 needs: - tns1/ns1/foo - name: foo - chart: mychart1 + chart: stable/mychart1 namespace: ns1 tillerNamespace: tns1 `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--tiller-namespacetns2--kube-contextdefault--namespacens2--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--tiller-namespacetns1--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--tiller-namespacetns2--kube-contextdefault--namespacens2--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--tiller-namespacetns1--kube-contextdefault--namespacens1--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, upgraded: []exectest.Release{ {Name: "foo", Flags: []string{"--tiller-namespace", "tns1", "--kube-context", "default", "--namespace", "ns1"}}, @@ -3126,13 +3130,13 @@ first-pass rendering output of "helmfile.yaml.part.0": 0: 1: releases: 2: - name: bar - 3: chart: mychart2 + 3: chart: stable/mychart2 4: namespace: ns2 5: tillerNamespace: tns2 6: needs: 7: - tns1/ns1/foo 8: - name: foo - 9: chart: mychart1 + 9: chart: stable/mychart1 10: namespace: ns1 11: tillerNamespace: tns1 12: @@ -3146,13 +3150,13 @@ second-pass rendering result of "helmfile.yaml.part.0": 0: 1: releases: 2: - name: bar - 3: chart: mychart2 + 3: chart: stable/mychart2 4: namespace: ns2 5: tillerNamespace: tns2 6: needs: 7: - tns1/ns1/foo 8: - name: foo - 9: chart: mychart1 + 9: chart: stable/mychart1 10: namespace: ns1 11: tillerNamespace: tns1 12: @@ -3161,8 +3165,8 @@ merged environment: &{default map[] map[]} 2 release(s) found in helmfile.yaml Affected releases are: - bar (mychart2) UPDATED - foo (mychart1) UPDATED + bar (stable/mychart2) UPDATED + foo (stable/mychart1) UPDATED processing 2 groups of releases in this order: GROUP RELEASES @@ -3175,9 +3179,9 @@ processing releases in group 2/2: tns2/ns2/bar getting deployed release version failed:unexpected list key: {^bar$ --tiller-namespacetns2--kube-contextdefault--deployed--failed--pending} UPDATED RELEASES: -NAME CHART VERSION -foo mychart1 -bar mychart2 +NAME CHART VERSION +foo stable/mychart1 +bar stable/mychart2 `, }, @@ -3191,18 +3195,18 @@ bar mychart2 "/path/to/helmfile.yaml": ` releases: - name: bar - chart: mychart2 + chart: stable/mychart2 installed: false - name: foo - chart: mychart1 + chart: stable/mychart1 installed: false needs: - bar `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, lists: map[exectest.ListKey]string{ exectest.ListKey{Filter: "^foo$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE @@ -3224,18 +3228,18 @@ bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 defau "/path/to/helmfile.yaml": ` releases: - name: bar - chart: mychart2 + chart: stable/mychart2 installed: false needs: - foo - name: foo - chart: mychart1 + chart: stable/mychart1 installed: false `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, lists: map[exectest.ListKey]string{ exectest.ListKey{Filter: "^foo$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE @@ -3260,17 +3264,17 @@ bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 defau "/path/to/helmfile.yaml": ` releases: - name: bar - chart: mychart2 + chart: stable/mychart2 - name: foo - chart: mychart1 + chart: stable/mychart1 installed: false needs: - bar `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, lists: map[exectest.ListKey]string{ exectest.ListKey{Filter: "^foo$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE @@ -3294,17 +3298,17 @@ bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 defau "/path/to/helmfile.yaml": ` releases: - name: bar - chart: mychart2 + chart: stable/mychart2 installed: false - name: foo - chart: mychart1 + chart: stable/mychart1 needs: - bar `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, lists: map[exectest.ListKey]string{ exectest.ListKey{Filter: "^foo$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE @@ -3328,17 +3332,17 @@ bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 defau "/path/to/helmfile.yaml": ` releases: - name: foo - chart: mychart1 + chart: stable/mychart1 installed: false - name: bar - chart: mychart2 + chart: stable/mychart2 needs: - foo `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, lists: map[exectest.ListKey]string{ exectest.ListKey{Filter: "^foo$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE @@ -3362,17 +3366,17 @@ bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 defau "/path/to/helmfile.yaml": ` releases: - name: foo - chart: mychart1 + chart: stable/mychart1 - name: bar - chart: mychart2 + chart: stable/mychart2 installed: false needs: - foo `, }, diffs: map[exectest.DiffKey]error{ - exectest.DiffKey{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, - exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "bar", Chart: "stable/mychart2", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, + exectest.DiffKey{Name: "foo", Chart: "stable/mychart1", Flags: "--kube-contextdefault--detailed-exitcode"}: helmexec.ExitError{Code: 2}, }, lists: map[exectest.ListKey]string{ exectest.ListKey{Filter: "^foo$", Flags: "--kube-contextdefault--deployed--failed--pending"}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE diff --git a/pkg/state/helmx.go b/pkg/state/helmx.go index adaac66a..b17674de 100644 --- a/pkg/state/helmx.go +++ b/pkg/state/helmx.go @@ -1,7 +1,9 @@ package state import ( + "fmt" "github.com/roboll/helmfile/pkg/helmexec" + "github.com/roboll/helmfile/pkg/remote" "github.com/variantdev/chartify" "os" "path/filepath" @@ -22,20 +24,66 @@ func (st *HelmState) appendHelmXFlags(flags []string, release *ReleaseSpec) ([]s return flags, nil } -func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) (bool, *chartify.ChartifyOpts, func(), error) { - var opts chartify.ChartifyOpts +func fileExistsAt(path string) bool { + fileInfo, err := os.Stat(path) + return err == nil && fileInfo.Mode().IsRegular() +} - opts.WorkaroundOutputDirIssue = true +func directoryExistsAt(path string) bool { + fileInfo, err := os.Stat(path) + return err == nil && fileInfo.Mode().IsDir() +} + +type Chartify struct { + Opts *chartify.ChartifyOpts + Chart string + Clean func() +} + +func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) (*Chartify, func(), error) { + chartify := &Chartify{ + Opts: &chartify.ChartifyOpts{ + WorkaroundOutputDirIssue: true, + EnableKustomizeAlphaPlugins: true, + ChartVersion: release.Version, + Namespace: release.Namespace, + }, + } + + var filesNeedCleaning []string + + clean := func() { + st.removeFiles(filesNeedCleaning) + } var shouldRun bool - opts.EnableKustomizeAlphaPlugins = true + chart := release.Chart + if release.Directory != "" && chart == "" { + chart = release.Directory + } - opts.ChartVersion = release.Version + _, err := remote.Parse(chart) + if err != nil { + if release.ForceGoGetter { + return nil, clean, fmt.Errorf("Parsing url from directory of release %q failed due to error %q.\nContinuing the process assuming this is a regular Helm chart or a local directory.", release.Name, err.Error()) + } + } else { + r := remote.NewRemote(st.logger, st.basePath, st.readFile, directoryExistsAt, fileExistsAt) - opts.Namespace = release.Namespace + fetchedDir, err := r.Fetch(chart) + if err != nil { + return nil, clean, fmt.Errorf("fetching %q: %v", chart, err) + } - dir := filepath.Join(st.basePath, release.Chart) + chart = fetchedDir + + filesNeedCleaning = append(filesNeedCleaning, fetchedDir) + } + + chartify.Chart = chart + + dir := filepath.Join(st.basePath, chart) if stat, _ := os.Stat(dir); stat != nil && stat.IsDir() { if exists, err := st.fileExists(filepath.Join(dir, "Chart.yaml")); err == nil && !exists { shouldRun = true @@ -61,28 +109,22 @@ func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSp dep += ":" + d.Version } - opts.AdhocChartDependencies = append(opts.AdhocChartDependencies, dep) + chartify.Opts.AdhocChartDependencies = append(chartify.Opts.AdhocChartDependencies, dep) shouldRun = true } - var filesNeedCleaning []string - - clean := func() { - st.removeFiles(filesNeedCleaning) - } - jsonPatches := release.JSONPatches if len(jsonPatches) > 0 { generatedFiles, err := st.generateTemporaryValuesFiles(jsonPatches, release.MissingFileHandler) if err != nil { - return false, nil, clean, err + return nil, clean, err } filesNeedCleaning = append(filesNeedCleaning, generatedFiles...) for _, f := range generatedFiles { - opts.JsonPatches = append(opts.JsonPatches, f) + chartify.Opts.JsonPatches = append(chartify.Opts.JsonPatches, f) } shouldRun = true @@ -92,11 +134,11 @@ func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSp if len(strategicMergePatches) > 0 { generatedFiles, err := st.generateTemporaryValuesFiles(strategicMergePatches, release.MissingFileHandler) if err != nil { - return false, nil, clean, err + return nil, clean, err } for _, f := range generatedFiles { - opts.StrategicMergePatches = append(opts.StrategicMergePatches, f) + chartify.Opts.StrategicMergePatches = append(chartify.Opts.StrategicMergePatches, f) } filesNeedCleaning = append(filesNeedCleaning, generatedFiles...) @@ -107,13 +149,15 @@ func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSp if shouldRun { generatedFiles, err := st.generateValuesFiles(helm, release, workerIndex) if err != nil { - return false, nil, clean, err + return nil, clean, err } filesNeedCleaning = append(filesNeedCleaning, generatedFiles...) - opts.ValuesFiles = generatedFiles + chartify.Opts.ValuesFiles = generatedFiles + + return chartify, clean, nil } - return shouldRun, &opts, clean, nil + return nil, clean, nil } diff --git a/pkg/state/state.go b/pkg/state/state.go index 68145473..47893a99 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -150,9 +150,15 @@ type RepositorySpec struct { // ReleaseSpec defines the structure of a helm release type ReleaseSpec struct { // Chart is the name of the chart being installed to create this release - Chart string `yaml:"chart,omitempty"` + Chart string `yaml:"chart,omitempty"` + // Directory is an alias to Chart which may be of more fit when you want to use a local/remote directory containing + // K8s manifests or Kustomization as a chart + Directory string `yaml:"directory,omitempty"` + // Version is the semver version or version constraint for the chart Version string `yaml:"version,omitempty"` - Verify *bool `yaml:"verify,omitempty"` + // Verify enables signature verification on fetched chart. + // Beware some (or many?) chart repositories and charts don't seem to support it. + Verify *bool `yaml:"verify,omitempty"` // Devel, when set to true, use development versions, too. Equivalent to version '>0.0.0-0' Devel *bool `yaml:"devel,omitempty"` // Wait, if set to true, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful @@ -237,6 +243,13 @@ type ReleaseSpec struct { //version of the chart that has really been installed cause desired version may be fuzzy (~2.0.0) installedVersion string + + // ForceGoGetter forces the use of go-getter for fetching remote directory as maniefsts/chart/kustomization + // by parsing the url from `chart` field of the release. + // This is handy when getting the go-getter url parsing error when it doesn't work as expected. + // Without this, any error in url parsing result in silently falling-back to normal process of treating `chart:` as the regular + // helm chart name. + ForceGoGetter bool `yaml:"forceGoGetter,omitempty"` } type Release struct { @@ -534,6 +547,7 @@ func (st *HelmState) DeleteReleasesForSync(affectedReleases *AffectedReleases, h } deletionFlags := st.appendConnectionFlags(args, release) m.Lock() + if err := helm.DeleteRelease(context, release.Name, deletionFlags...); err != nil { affectedReleases.Failed = append(affectedReleases.Failed, release) relErr = newReleaseFailedError(release, err) @@ -638,6 +652,7 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme } deletionFlags := st.appendConnectionFlags(args, release) m.Lock() + if err := helm.DeleteRelease(context, release.Name, deletionFlags...); err != nil { affectedReleases.Failed = append(affectedReleases.Failed, release) relErr = newReleaseFailedError(release, err) @@ -745,6 +760,7 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre chartPath string err error } + errs := []error{} jobQueue := make(chan *ReleaseSpec, len(st.Releases)) @@ -769,22 +785,23 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre for release := range jobQueue { var chartPath string - shouldChartify, opts, clean, err := st.PrepareChartify(helm, release, workerIndex) + chartification, clean, err := st.PrepareChartify(helm, release, workerIndex) defer clean() if err != nil { results <- &downloadResults{err: err} return } - if shouldChartify { + if chartification != nil { c := chartify.New( chartify.HelmBin(st.DefaultHelmBinary), chartify.UseHelm3(helm3), ) - out, err := c.Chartify(release.Name, release.Chart, chartify.WithChartifyOpts(opts)) + out, err := c.Chartify(release.Name, chartification.Chart, chartify.WithChartifyOpts(chartification.Opts)) if err != nil { - errs = append(errs, err) + results <- &downloadResults{err: err} + return } else { // TODO Chartify chartPath = out @@ -810,7 +827,8 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre if _, err := os.Stat(chartPath); os.IsNotExist(err) { fetchFlags = append(fetchFlags, "--untar", "--untardir", chartPath) if err := helm.Fetch(release.Chart, fetchFlags...); err != nil { - errs = append(errs, err) + results <- &downloadResults{err: err} + return } } // Set chartPath to be the path containing Chart.yaml, if found