feat: GA of Kustomize and K8s manifests support (#1172)

This is the GA version of the helm-x integration #673 developed last year.

You get all the following benefits without an extra helm plugin:

- Ability to add ad-hoc chart dependencies/aliases, without forking the chart (Fixes #876 )
- Ability to patch resulting K8s resources before installing the helm chart
- Ability to install a kustomization as a chart (Requires `kustomize` binary to be available in `$PATH`
- Ability to install a directory of K8s manifests as a chart
- etc.
This commit is contained in:
KUOKA Yusuke
2020-05-27 11:42:43 +09:00
committed by GitHub
parent 969de1ebc9
commit 16288dfa7d
11 changed files with 400 additions and 184 deletions
+166 -46
View File
@@ -100,20 +100,44 @@ func Init(app *App) *App {
}
func (a *App) Deps(c DepsConfigProvider) error {
return a.ForEachStateFiltered(func(run *Run) []error {
return run.Deps(c)
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
err := run.withPreparedCharts(false, func() {
errs = run.Deps(c)
})
if err != nil {
errs = append(errs, err)
}
return
})
}
func (a *App) Repos(c ReposConfigProvider) error {
return a.ForEachStateFiltered(func(run *Run) []error {
return run.Repos(c)
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
err := run.withPreparedCharts(false, func() {
errs = run.Repos(c)
})
if err != nil {
errs = append(errs, err)
}
return
})
}
func (a *App) DeprecatedSyncCharts(c DeprecatedChartsConfigProvider) error {
return a.ForEachStateFiltered(func(run *Run) []error {
return run.DeprecatedSyncCharts(c)
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
err := run.withPreparedCharts(false, func() {
errs = run.DeprecatedSyncCharts(c)
})
if err != nil {
errs = append(errs, err)
}
return
})
}
@@ -125,12 +149,24 @@ func (a *App) Diff(c DiffConfigProvider) error {
err := a.ForEachState(func(run *Run) (bool, []error) {
var criticalErrs []error
msg, matched, affected, errs := run.Diff(c)
var msg *string
var matched, affected bool
var errs []error
err := run.withPreparedCharts(false, func() {
msg, matched, affected, errs = run.Diff(c)
})
if msg != nil {
a.Logger.Info(*msg)
}
if err != nil {
errs = append(errs, err)
}
affectedAny = affectedAny || affected
for i := range errs {
@@ -171,20 +207,44 @@ func (a *App) Diff(c DiffConfigProvider) error {
}
func (a *App) Template(c TemplateConfigProvider) error {
return a.ForEachState(func(run *Run) (bool, []error) {
return a.template(run, c)
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
err := run.withPreparedCharts(true, func() {
ok, errs = a.template(run, c)
})
if err != nil {
errs = append(errs, err)
}
return
})
}
func (a *App) Lint(c LintConfigProvider) error {
return a.ForEachStateFiltered(func(run *Run) []error {
return run.Lint(c)
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
err := run.withPreparedCharts(true, func() {
errs = run.Lint(c)
})
if err != nil {
errs = append(errs, err)
}
return
})
}
func (a *App) Sync(c SyncConfigProvider) error {
return a.ForEachState(func(run *Run) (bool, []error) {
return a.sync(run, c)
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
err := run.withPreparedCharts(false, func() {
ok, errs = a.sync(run, c)
})
if err != nil {
errs = append(errs, err)
}
return
})
}
@@ -197,14 +257,22 @@ func (a *App) Apply(c ApplyConfigProvider) error {
opts = append(opts, SetRetainValuesFiles(c.RetainValuesFiles()))
err := a.ForEachState(func(run *Run) (bool, []error) {
matched, updated, errs := a.apply(run, c)
err := a.ForEachState(func(run *Run) (ok bool, errs []error) {
err := run.withPreparedCharts(false, func() {
matched, updated, es := a.apply(run, c)
mut.Lock()
any = any || updated
mut.Unlock()
mut.Lock()
any = any || updated
mut.Unlock()
return matched, errs
ok, errs = matched, es
})
if err != nil {
errs = append(errs, err)
}
return
}, opts...)
if err != nil {
@@ -221,43 +289,85 @@ func (a *App) Apply(c ApplyConfigProvider) error {
}
func (a *App) Status(c StatusesConfigProvider) error {
return a.ForEachStateFiltered(func(run *Run) []error {
return run.Status(c)
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
err := run.withPreparedCharts(false, func() {
errs = run.Status(c)
})
if err != nil {
errs = append(errs, err)
}
return
})
}
func (a *App) Delete(c DeleteConfigProvider) error {
return a.ForEachState(func(run *Run) (bool, []error) {
return a.delete(run, c.Purge(), c)
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
err := run.withPreparedCharts(false, func() {
ok, errs = a.delete(run, c.Purge(), c)
})
if err != nil {
errs = append(errs, err)
}
return
}, SetReverse(true))
}
func (a *App) Destroy(c DestroyConfigProvider) error {
return a.ForEachState(func(run *Run) (bool, []error) {
return a.delete(run, true, c)
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
err := run.withPreparedCharts(false, func() {
ok, errs = a.delete(run, true, c)
})
if err != nil {
errs = append(errs, err)
}
return
}, SetReverse(true))
}
func (a *App) Test(c TestConfigProvider) error {
return a.ForEachStateFiltered(func(run *Run) []error {
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
if c.Cleanup() && run.helm.IsHelm3() {
a.Logger.Warnf("warn: requested cleanup will not be applied. " +
"To clean up test resources with Helm 3, you have to remove them manually " +
"or set helm.sh/hook-delete-policy\n")
}
return run.Test(c)
err := run.withPreparedCharts(false, func() {
errs = run.Test(c)
})
if err != nil {
errs = append(errs, err)
}
return
})
}
func (a *App) PrintState(c StateConfigProvider) error {
return a.VisitDesiredStatesWithReleasesFiltered(a.FileOrDir, func(st *state.HelmState) []error {
state, err := st.ToYaml()
return a.VisitDesiredStatesWithReleasesFiltered(a.FileOrDir, func(st *state.HelmState) (errs []error) {
err := NewRun(st, nil, NewContext()).withPreparedCharts(false, func() {
state, err := st.ToYaml()
if err != nil {
errs = []error{err}
return
}
fmt.Printf("---\n# Source: %s\n\n%+v", st.FilePath, state)
errs = []error{}
})
if err != nil {
return []error{err}
errs = append(errs, err)
}
fmt.Printf("---\n# Source: %s\n\n%+v", st.FilePath, state)
return []error{}
return
})
}
@@ -265,22 +375,32 @@ func (a *App) ListReleases(c ListConfigProvider) error {
var releases []*HelmRelease
err := a.VisitDesiredStatesWithReleasesFiltered(a.FileOrDir, func(st *state.HelmState) []error {
//var releases m
for _, r := range st.Releases {
labels := ""
for k, v := range r.Labels {
labels = fmt.Sprintf("%s,%s:%s", labels, k, v)
err := NewRun(st, nil, NewContext()).withPreparedCharts(false, func() {
//var releases m
for _, r := range st.Releases {
labels := ""
for k, v := range r.Labels {
labels = fmt.Sprintf("%s,%s:%s", labels, k, v)
}
labels = strings.Trim(labels, ",")
installed := r.Installed == nil || *r.Installed
releases = append(releases, &HelmRelease{
Name: r.Name,
Namespace: r.Namespace,
Enabled: installed,
Labels: labels,
})
}
labels = strings.Trim(labels, ",")
installed := r.Installed == nil || *r.Installed
releases = append(releases, &HelmRelease{
Name: r.Name,
Namespace: r.Namespace,
Enabled: installed,
Labels: labels,
})
})
var errs []error
if err != nil {
errs = append(errs, err)
}
return []error{}
return errs
})
if err != nil {
-68
View File
@@ -2715,10 +2715,6 @@ second-pass rendering result of "helmfile.yaml.part.0":
merged environment: &{default map[] map[]}
10 release(s) found in helmfile.yaml
worker 1/1 started
worker 1/1 finished
worker 1/1 started
worker 1/1 finished
Affected releases are:
anotherbackend (charts/anotherbackend) UPDATED
backend-v1 (charts/backend) DELETED
@@ -2739,11 +2735,7 @@ GROUP RELEASES
5 logging, front-proxy
processing releases in group 1/5: frontend-v1, frontend-v2, frontend-v3
worker 1/1 started
worker 1/1 finished
processing releases in group 2/5: backend-v1, backend-v2
worker 1/1 started
worker 1/1 finished
processing releases in group 3/5: anotherbackend
processing releases in group 4/5: database, servicemesh
processing releases in group 5/5: logging, front-proxy
@@ -2756,37 +2748,17 @@ GROUP RELEASES
5 frontend-v1, frontend-v2, frontend-v3
processing releases in group 1/5: logging, front-proxy
worker 1/1 started
worker 1/1 finished
worker 1/1 started
getting deployed release version failed:unexpected list key: {^logging$ --kube-contextdefault--deployed--failed--pending}
getting deployed release version failed:unexpected list key: {^front-proxy$ --kube-contextdefault--deployed--failed--pending}
worker 1/1 finished
processing releases in group 2/5: database, servicemesh
worker 1/1 started
worker 1/1 finished
worker 1/1 started
getting deployed release version failed:unexpected list key: {^database$ --kube-contextdefault--deployed--failed--pending}
getting deployed release version failed:unexpected list key: {^servicemesh$ --kube-contextdefault--deployed--failed--pending}
worker 1/1 finished
processing releases in group 3/5: anotherbackend
worker 1/1 started
worker 1/1 finished
worker 1/1 started
getting deployed release version failed:unexpected list key: {^anotherbackend$ --kube-contextdefault--deployed--failed--pending}
worker 1/1 finished
processing releases in group 4/5: backend-v1, backend-v2
worker 1/1 started
worker 1/1 finished
worker 1/1 started
getting deployed release version failed:unexpected list key: {^backend-v2$ --kube-contextdefault--deployed--failed--pending}
worker 1/1 finished
processing releases in group 5/5: frontend-v1, frontend-v2, frontend-v3
worker 1/1 started
worker 1/1 finished
worker 1/1 started
getting deployed release version failed:unexpected list key: {^frontend-v3$ --kube-contextdefault--deployed--failed--pending}
worker 1/1 finished
UPDATED RELEASES:
NAME CHART VERSION
@@ -2904,10 +2876,6 @@ second-pass rendering result of "helmfile.yaml.part.0":
merged environment: &{default map[] map[]}
3 release(s) found in helmfile.yaml
worker 1/1 started
worker 1/1 finished
worker 1/1 started
worker 1/1 finished
Affected releases are:
bar (mychart2) UPDATED
baz (mychart3) UPDATED
@@ -2919,18 +2887,10 @@ GROUP RELEASES
2 foo
processing releases in group 1/2: baz, bar
worker 1/1 started
worker 1/1 finished
worker 1/1 started
getting deployed release version failed:unexpected list key: {^baz$ --kube-contextdefault--deployed--failed--pending}
getting deployed release version failed:unexpected list key: {^bar$ --kube-contextdefault--deployed--failed--pending}
worker 1/1 finished
processing releases in group 2/2: foo
worker 1/1 started
worker 1/1 finished
worker 1/1 started
getting deployed release version failed:unexpected list key: {^foo$ --kube-contextdefault--deployed--failed--pending}
worker 1/1 finished
UPDATED RELEASES:
NAME CHART VERSION
@@ -3184,10 +3144,6 @@ second-pass rendering result of "helmfile.yaml.part.0":
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml
worker 1/1 started
worker 1/1 finished
worker 1/1 started
worker 1/1 finished
Affected releases are:
bar (mychart2) UPDATED
foo (mychart1) UPDATED
@@ -3198,17 +3154,9 @@ GROUP RELEASES
2 tns2/ns2/bar
processing releases in group 1/2: tns1/ns1/foo
worker 1/1 started
worker 1/1 finished
worker 1/1 started
getting deployed release version failed:unexpected list key: {^foo$ --tiller-namespacetns1--kube-contextdefault--deployed--failed--pending}
worker 1/1 finished
processing releases in group 2/2: tns2/ns2/bar
worker 1/1 started
worker 1/1 finished
worker 1/1 started
getting deployed release version failed:unexpected list key: {^bar$ --tiller-namespacetns2--kube-contextdefault--deployed--failed--pending}
worker 1/1 finished
UPDATED RELEASES:
NAME CHART VERSION
@@ -3532,10 +3480,6 @@ second-pass rendering result of "helmfile.yaml.part.0":
merged environment: &{default map[] map[]}
2 release(s) matching app=test found in helmfile.yaml
worker 1/1 started
worker 1/1 finished
worker 1/1 started
worker 1/1 finished
Affected releases are:
external-secrets (incubator/raw) UPDATED
my-release (incubator/raw) UPDATED
@@ -3552,19 +3496,11 @@ processing releases in group 1/3: kube-system/kubernetes-external-secrets
processing releases in group 2/3: default/external-secrets
1 release(s) matching app=test found in helmfile.yaml
worker 1/1 started
worker 1/1 finished
worker 1/1 started
getting deployed release version failed:unexpected list key: {^external-secrets$ --kube-contextdefault--deployed--failed--pending}
worker 1/1 finished
processing releases in group 3/3: default/my-release
1 release(s) matching app=test found in helmfile.yaml
worker 1/1 started
worker 1/1 finished
worker 1/1 started
getting deployed release version failed:unexpected list key: {^my-release$ --kube-contextdefault--deployed--failed--pending}
worker 1/1 finished
UPDATED RELEASES:
NAME CHART VERSION
@@ -3736,10 +3672,6 @@ second-pass rendering result of "helmfile.yaml.part.0":
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml
worker 1/1 started
worker 1/1 finished
worker 1/1 started
worker 1/1 finished
Affected releases are:
baz (mychart3) UPDATED
foo (mychart1) UPDATED
-10
View File
@@ -295,30 +295,20 @@ GROUP RELEASES
5 front-proxy, logging
processing releases in group 1/5: frontend-v3, frontend-v2, frontend-v1
worker 1/1 started
release "frontend-v3" processed
release "frontend-v2" processed
release "frontend-v1" processed
worker 1/1 finished
processing releases in group 2/5: backend-v2, backend-v1
worker 1/1 started
release "backend-v2" processed
release "backend-v1" processed
worker 1/1 finished
processing releases in group 3/5: anotherbackend
worker 1/1 started
release "anotherbackend" processed
worker 1/1 finished
processing releases in group 4/5: database, servicemesh
worker 1/1 started
release "database" processed
release "servicemesh" processed
worker 1/1 finished
processing releases in group 5/5: front-proxy, logging
worker 1/1 started
release "front-proxy" processed
release "logging" processed
worker 1/1 finished
DELETED RELEASES:
NAME
-16
View File
@@ -324,10 +324,6 @@ second-pass rendering result of "helmfile.yaml.part.0":
merged environment: &{default map[] map[]}
10 release(s) found in helmfile.yaml
worker 1/1 started
worker 1/1 finished
worker 1/1 started
worker 1/1 finished
Affected releases are:
anotherbackend (charts/anotherbackend) UPDATED
backend-v1 (charts/backend) DELETED
@@ -440,10 +436,6 @@ second-pass rendering result of "helmfile.yaml.part.0":
merged environment: &{default map[] map[]}
3 release(s) found in helmfile.yaml
worker 1/1 started
worker 1/1 finished
worker 1/1 started
worker 1/1 finished
Affected releases are:
bar (mychart2) UPDATED
baz (mychart3) UPDATED
@@ -687,10 +679,6 @@ second-pass rendering result of "helmfile.yaml.part.0":
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml
worker 1/1 started
worker 1/1 finished
worker 1/1 started
worker 1/1 finished
Affected releases are:
bar (mychart2) UPDATED
foo (mychart1) UPDATED
@@ -1001,10 +989,6 @@ second-pass rendering result of "helmfile.yaml.part.0":
merged environment: &{default map[] map[]}
2 release(s) matching app=test found in helmfile.yaml
worker 1/1 started
worker 1/1 finished
worker 1/1 started
worker 1/1 finished
Affected releases are:
external-secrets (incubator/raw) UPDATED
my-release (incubator/raw) UPDATED
+35
View File
@@ -5,6 +5,8 @@ import (
"github.com/roboll/helmfile/pkg/argparser"
"github.com/roboll/helmfile/pkg/helmexec"
"github.com/roboll/helmfile/pkg/state"
"io/ioutil"
"os"
"sort"
"strings"
)
@@ -14,6 +16,8 @@ type Run struct {
helm helmexec.Interface
ctx Context
ReleaseToChart map[string]string
Ask func(string) bool
}
@@ -28,6 +32,37 @@ func (r *Run) askForConfirmation(msg string) bool {
return AskForConfirmation(msg)
}
func (r *Run) withPreparedCharts(forceDownload bool, f func()) error {
if r.ReleaseToChart != nil {
panic("Run.PrepareCharts can be called only once")
}
// Create tmp directory and bail immediately if it fails
dir, err := ioutil.TempDir("", "")
if err != nil {
return err
}
defer os.RemoveAll(dir)
releaseToChart, errs := state.PrepareCharts(r.helm, r.state, dir, 2, "template", forceDownload)
if len(errs) > 0 {
return fmt.Errorf("%v", errs)
}
for i := range r.state.Releases {
rel := &r.state.Releases[i]
rel.Chart = releaseToChart[rel.Name]
}
r.ReleaseToChart = releaseToChart
f()
return nil
}
func (r *Run) Deps(c DepsConfigProvider) []error {
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)