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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 400 additions and 184 deletions

91
docs/advanced-features.md Normal file
View File

@ -0,0 +1,91 @@
## Advanced Features
- [Import Configuration Parameters into Helmfile](#import-configuration-parameters-into-helmfile)
### Import Configuration Parameters into Helmfile
Helmfile integrates [vals]() to import configuration parameters from following backends:
- AWS SSM Parameter Store
- AWS SecretsManager
- Vault
- SOPS
See [Vals "Suported Backends"](https://github.com/variantdev/vals#suported-backends) for the full list of available backends.
This feature was implemented in https://github.com/roboll/helmfile/pull/906.
If you're curious how it's designed and how it works, please consult the pull request.
### Deploy Kustomizations with Helmfile
You can deploy [kustomize](https://github.com/kubernetes-sigs/kustomize) "kustomization"s with Helmfile.
Most of Kustomize operations that is usually done with `kustomize edit` can be done declaratively via Helm values.yaml files.
Under the hood, Helmfile transforms the kustomization into a local chart in a temporary directory so that it can be `helm upgrade --install`ed.
The transformation is done by generating (1)a temporary kustomization from various options and (2)temporary chart from the temporary kustomization.
An example pseudo code for the transformation logic can be written as:
```console
$ TMPCHART=/tmp/sometmpdir
$ mkdir -p ${TMPCHART}/templates
$ somehow_generate_chart_yaml ${TMPCHART}/Chart.yaml
$ TMPKUSTOMIZATION=/tmp/sometmpdir2
$ somehow_generate_temp_kustomization_yaml ${TMPKUSTOMIZATION}/kustomization.yaml
$ kustomize build ${TMPKUSTOMIZATION}/kustomization.yaml > ${TMPCHART}/templates/all.yaml
```
Let's say you have a `helmfile.yaml` that looks like the below:
```yaml
releases:
- name: myapp
chart: mykustomization
values:
- values.yaml
```
Helmfile firstly generates a temporary `kustomization.yaml` that looks like:
```yaml
bases:
- $(ABS_PATH_TO_HELMFILE_YAML}/mykustomization
```
Followed by the below steps:
- Running `kustomize edit set image $IMAGE` for every `$IMAGE` generated from your values.yaml
- Running `kustomize edit set nameprefix $NAMEPREFIX` with the nameprefix specified in your values.yaml
- Running `kustomize edit set namesuffix $NAMESUFFIX` with the namesuffix specified in your values.yaml
- Running `kustomize edit set namespace $NS` with the namespace specified in your values.yaml
A `values.yaml` file for kustomization would look like the below:
```yaml
images:
# kustomize edit set image mysql=eu.gcr.io/my-project/mysql@canary
- name: mysql
newName: eu.gcr.io/my-project/mysql
newTag: canary
# kustomize edit set image myapp=my-registry/my-app@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
- name: myapp
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
newName: my-registry/my-app
# kustomize edit set nameprefix foo-
namePrefix: foo-
# kustomize edit set namesuffix -bar
nameSuffix: -bar
# kustomize edit set namespace myapp
namespace: myapp
```
At this point, Helmfile can generate a complete kustomization from the base kustomization you specified in `releases[].chart` of your helmfile.yaml and `values.yaml`,
which can be included in the temporary chart.
After all, Helmfile just installs the temporary chart like standard charts, which allows you to manage everything with Helmfile regardless of each app is declared using a Helm chart or a kustomization.

1
go.mod
View File

@ -23,6 +23,7 @@ require (
github.com/r3labs/diff v0.0.0-20190801153147-a71de73c46ad
github.com/tatsushid/go-prettytable v0.0.0-20141013043238-ed2d14c29939
github.com/urfave/cli v1.20.0
github.com/variantdev/chartify v0.3.0
github.com/variantdev/dag v0.0.0-20191028002400-bb0b3c785363
github.com/variantdev/vals v0.4.1-0.20200501114609-9cebe482281c
go.mozilla.org/sops v0.0.0-20190912205235-14a22d7a7060 // indirect

27
go.sum
View File

@ -278,6 +278,7 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-ldap/ldap v3.0.2+incompatible h1:kD5HQcAzlQ7yrhfn+h+MSABeAy/jAJhvIJ/QDllP44g=
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
@ -646,6 +647,12 @@ github.com/oracle/oci-go-sdk v7.0.0+incompatible h1:oj5ESjXwwkFRdhZSnPlShvLWYdt/
github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
github.com/ory/dockertest v3.3.4+incompatible h1:VrpM6Gqg7CrPm3bL4Wm1skO+zFWLbh7/Xb5kGEbJRh8=
github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
github.com/otiai10/copy v1.1.1 h1:PH7IFlRQ6Fv9vYmuXbDRLdgTHoP1w483kPNUP2bskpo=
github.com/otiai10/copy v1.1.1/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso=
github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@ -774,6 +781,20 @@ github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/variantdev/chartify v0.0.0-20200330123007-ddc79388188c h1:PiAOZUdKtf8tIfps1fLaEsP9BCTT8hR9FawTfAuLY5o=
github.com/variantdev/chartify v0.0.0-20200330123007-ddc79388188c/go.mod h1:DzFQ1XeNBpbLdbze39ai3auS4b+Xo/KzRluEY/LyqlM=
github.com/variantdev/chartify v0.1.0 h1:RHo/di1tXvydrJwwbHoWduvG3r9nU0oj7RvuxmQLCW0=
github.com/variantdev/chartify v0.1.0/go.mod h1:DzFQ1XeNBpbLdbze39ai3auS4b+Xo/KzRluEY/LyqlM=
github.com/variantdev/chartify v0.1.1 h1:4K+6XotrJ/U5BoQc29BTOWpNGnzsCBEDahApiOoPURc=
github.com/variantdev/chartify v0.1.1/go.mod h1:DzFQ1XeNBpbLdbze39ai3auS4b+Xo/KzRluEY/LyqlM=
github.com/variantdev/chartify v0.1.2 h1:BAaZawFXJfcgk6HVM3o+/Q9Xzyv1rkPd9m+YeWM7+dg=
github.com/variantdev/chartify v0.1.2/go.mod h1:DzFQ1XeNBpbLdbze39ai3auS4b+Xo/KzRluEY/LyqlM=
github.com/variantdev/chartify v0.1.3 h1:3WEaT1ZYLaivcVdlE2poxBCw2KxNEaOzHB9k4rj5zXk=
github.com/variantdev/chartify v0.1.3/go.mod h1:DzFQ1XeNBpbLdbze39ai3auS4b+Xo/KzRluEY/LyqlM=
github.com/variantdev/chartify v0.2.0 h1:oaG/o+juw1C5PTAbRlsxUTgGYPmIneoqnYI7KQyVJJU=
github.com/variantdev/chartify v0.2.0/go.mod h1:0tw+4doFHsNnhttYx7I9Pv/dsZ82BD4UuTV9saBOcfw=
github.com/variantdev/chartify v0.3.0 h1:vm9cY+Amj44g7scz/FVpa3s13XqzFvTJrdM6iufiKEM=
github.com/variantdev/chartify v0.3.0/go.mod h1:0tw+4doFHsNnhttYx7I9Pv/dsZ82BD4UuTV9saBOcfw=
github.com/variantdev/dag v0.0.0-20191028002400-bb0b3c785363 h1:KrfQBEUn+wEOQ/6UIfoqRDvn+Q/wZridQ7t0G1vQqKE=
github.com/variantdev/dag v0.0.0-20191028002400-bb0b3c785363/go.mod h1:pH1TQsNSLj2uxMo9NNl9zdGy01Wtn+/2MT96BrKmVyE=
github.com/variantdev/vals v0.4.0 h1:O1O7/sWhlvozcY2DjZBzlE1notxwVo6UBT1+w7HsO/k=
@ -1105,6 +1126,10 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 h1:0efs3hwEZhFKsCoP8l6dDB1AZWMgnEl3yWXWRZTOaEA=
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d h1:LCPbGQ34PMrwad11aMZ+dbz5SAsq/0ySjRwQ8I9Qwd8=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@ -1128,6 +1153,8 @@ k8s.io/apimachinery v0.0.0-20190409092423-760d1845f48b h1:fVkKJL9FIpA8LSJyHVM00M
k8s.io/apimachinery v0.0.0-20190409092423-760d1845f48b/go.mod h1:FW86P8YXVLsbuplGMZeb20J3jYHscrDqw4jELaFJvRU=
k8s.io/klog v0.0.0-20190306015804-8e90cee79f82 h1:SHucoAy7lRb+w5oC/hbXyZg+zX+Wftn6hD4tGzHCVqA=
k8s.io/klog v0.0.0-20190306015804-8e90cee79f82/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
layeh.com/radius v0.0.0-20190322222518-890bc1058917 h1:BDXFaFzUt5EIqe/4wrTc4AcYZWP6iC6Ult+jQWLh5eU=
layeh.com/radius v0.0.0-20190322222518-890bc1058917/go.mod h1:fywZKyu//X7iRzaxLgPWsvc0L26IUpVvE/aeIL2JtIQ=

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 {

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

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

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

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)...)

View File

@ -1,5 +1,10 @@
package state
import (
"github.com/variantdev/chartify"
"strings"
)
type Dependency struct {
Chart string `yaml:"chart"`
Version string `yaml:"version"`
@ -7,49 +12,73 @@ type Dependency struct {
}
func (st *HelmState) appendHelmXFlags(flags []string, release *ReleaseSpec) ([]string, error) {
for _, adopt := range release.Adopt {
flags = append(flags, "--adopt", adopt)
}
return flags, nil
}
func (st *HelmState) PrepareChartify(release *ReleaseSpec) (bool, *chartify.ChartifyOpts) {
var opts chartify.ChartifyOpts
var shouldRun bool
for _, d := range release.Dependencies {
var dep string
if d.Alias != "" {
dep += d.Alias + "="
} else {
a := strings.Split(d.Chart, "/")
chart := a[len(a)-1]
dep += chart + "="
}
dep += d.Chart
if d.Version != "" {
dep += ":" + d.Version
}
flags = append(flags, "--dependency", dep)
}
for _, adopt := range release.Adopt {
flags = append(flags, "--adopt", adopt)
opts.AdhocChartDependencies = append(opts.AdhocChartDependencies, dep)
shouldRun = true
}
jsonPatches := release.JSONPatches
if len(jsonPatches) > 0 {
generatedFiles, err := st.generateTemporaryValuesFiles(jsonPatches, release.MissingFileHandler)
if err != nil {
return nil, err
return false, nil
}
for _, f := range generatedFiles {
flags = append(flags, "--json-patch", f)
opts.JsonPatches = append(opts.JsonPatches, f)
}
release.generatedValues = append(release.generatedValues, generatedFiles...)
shouldRun = true
}
strategicMergePatches := release.StrategicMergePatches
if len(strategicMergePatches) > 0 {
generatedFiles, err := st.generateTemporaryValuesFiles(strategicMergePatches, release.MissingFileHandler)
if err != nil {
return nil, err
return false, nil
}
for _, f := range generatedFiles {
flags = append(flags, "--strategic-merge-patch", f)
opts.StrategicMergePatches = append(opts.StrategicMergePatches, f)
}
release.generatedValues = append(release.generatedValues, generatedFiles...)
shouldRun = true
}
return flags, nil
return shouldRun, &opts
}

View File

@ -5,6 +5,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"github.com/variantdev/chartify"
"io"
"io/ioutil"
"os"
@ -699,8 +700,20 @@ func (st *HelmState) getDeployedVersion(context helmexec.HelmContext, helm helme
}
}
// downloadCharts will download and untar charts for Lint and Template
func (st *HelmState) downloadCharts(helm helmexec.Interface, dir string, concurrency int, helmfileCommand string) (map[string]string, []error) {
// PrepareCharts creates temporary directories of charts.
//
// Each resulting "chart" can be one of the followings:
//
// (1) local chart
// (2) temporary local chart generated from kustomization or manifests
// (3) remote chart
//
// When running `helmfile lint` or `helmfile template`, PrepareCharts will download and untar charts for linting and templating.
//
// Otheriwse, if a chart is not a helm chart, it will call "chartify" to turn it into a chart.
//
// If exists, it will also patch resources by json patches, strategic-merge patches, and injectors.
func PrepareCharts(helm helmexec.Interface, st *HelmState, dir string, concurrency int, helmfileCommand string, forceDownload bool) (map[string]string, []error) {
temp := make(map[string]string, len(st.Releases))
type downloadResults struct {
releaseName string
@ -711,6 +724,12 @@ func (st *HelmState) downloadCharts(helm helmexec.Interface, dir string, concurr
jobQueue := make(chan *ReleaseSpec, len(st.Releases))
results := make(chan *downloadResults, len(st.Releases))
var helm3 bool
if helm != nil {
helm3 = helm.IsHelm3()
}
st.scatterGather(
concurrency,
len(st.Releases),
@ -722,8 +741,24 @@ func (st *HelmState) downloadCharts(helm helmexec.Interface, dir string, concurr
},
func(_ int) {
for release := range jobQueue {
chartPath := ""
if pathExists(normalizeChart(st.basePath, release.Chart)) {
var chartPath string
if shouldChartify, opts := st.PrepareChartify(release); shouldChartify {
c := chartify.New(
chartify.HelmBin(st.DefaultHelmBinary),
chartify.UseHelm3(helm3),
)
out, err := c.Chartify(release.Name, release.Chart, chartify.WithChartifyOpts(opts))
if err != nil {
errs = append(errs, err)
} else {
// TODO Chartify
chartPath = out
}
} else if !forceDownload {
chartPath = release.Chart
} else if pathExists(normalizeChart(st.basePath, release.Chart)) {
chartPath = normalizeChart(st.basePath, release.Chart)
} else {
fetchFlags := []string{}
@ -751,6 +786,7 @@ func (st *HelmState) downloadCharts(helm helmexec.Interface, dir string, concurr
chartPath = filepath.Dir(fullChartPath)
}
}
results <- &downloadResults{release.Name, chartPath}
}
},
@ -789,20 +825,6 @@ func (st *HelmState) TemplateReleases(helm helmexec.Interface, outputDir string,
helm.SetExtraArgs()
errs := []error{}
// Create tmp directory and bail immediately if it fails
dir, err := ioutil.TempDir("", "")
if err != nil {
errs = append(errs, err)
return errs
}
defer os.RemoveAll(dir)
temp, errs := st.downloadCharts(helm, dir, workerLimit, "template")
if errs != nil {
errs = append(errs, err)
return errs
}
if len(args) > 0 {
helm.SetExtraArgs(args...)
@ -856,7 +878,7 @@ func (st *HelmState) TemplateReleases(helm helmexec.Interface, outputDir string,
}
if len(errs) == 0 {
if err := helm.TemplateRelease(release.Name, temp[release.Name], flags...); err != nil {
if err := helm.TemplateRelease(release.Name, release.Chart, flags...); err != nil {
errs = append(errs, err)
}
}
@ -894,19 +916,6 @@ func (st *HelmState) LintReleases(helm helmexec.Interface, additionalValues []st
helm.SetExtraArgs()
errs := []error{}
// Create tmp directory and bail immediately if it fails
dir, err := ioutil.TempDir("", "")
if err != nil {
errs = append(errs, err)
return errs
}
defer os.RemoveAll(dir)
temp, errs := st.downloadCharts(helm, dir, workerLimit, "lint")
if errs != nil {
errs = append(errs, err)
return errs
}
if len(args) > 0 {
helm.SetExtraArgs(args...)
@ -942,7 +951,7 @@ func (st *HelmState) LintReleases(helm helmexec.Interface, additionalValues []st
}
if len(errs) == 0 {
if err := helm.Lint(release.Name, temp[release.Name], flags...); err != nil {
if err := helm.Lint(release.Name, release.Chart, flags...); err != nil {
errs = append(errs, err)
}
}

View File

@ -38,9 +38,7 @@ func (st *HelmState) scatterGather(concurrency int, items int, produceInputs fun
for w := 1; w <= concurrency; w++ {
go func(id int) {
st.logger.Debugf("worker %d/%d started", id, concurrency)
receiveInputsAndProduceIntermediates(id)
st.logger.Debugf("worker %d/%d finished", id, concurrency)
waitGroup.Done()
}(w)
}