Skip to content

fix: Follow-up fixes for #1172 #1273

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions docs/advanced-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,49 @@ At this point, Helmfile can generate a complete kustomization from the base kust
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.

Please also see [test/advanced/helmfile.yaml](https://github.com/roboll/helmfile/tree/master/test/advanced/helmfile.yaml) for an example of kustomization support and more.

## Adhoc Customization of Helm charts

You can add/update any Kubernetes resource field rendered from a Helm chart by specifying `releases[].strategicMergePatches`:

```
repositories:
- name: incubator
url: https://kubernetes-charts-incubator.storage.googleapis.com

releases:
- name: raw1
chart: incubator/raw
values:
- resources:
- apiVersion: v1
kind: ConfigMap
metadata:
name: raw1
namespace: default
data:
foo: FOO
strategicMergePatches:
- apiVersion: v1
kind: ConfigMap
metadata:
name: raw1
namespace: default
data:
bar: BAR
```

Running `helmfile template` on the above example results in a ConfigMap called `raw` whose `data` is:

```yaml
foo: FOO
bar: BAR
```

Please note that the second `data` field `bar` is coming from the strategic-merge patch defined in the above helmfile.yaml.

There's also `releases[].jsonPatches` that works similarly to `strategicMergePatches` but has additional capability to remove fields.

Please also see [test/advanced/helmfile.yaml](https://github.com/roboll/helmfile/tree/master/test/advanced/helmfile.yaml) for an example of patching support and more.
31 changes: 27 additions & 4 deletions pkg/state/helmx.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package state

import (
"github.com/roboll/helmfile/pkg/helmexec"
"github.com/variantdev/chartify"
"os"
"path/filepath"
"strings"
)

Expand All @@ -19,11 +22,22 @@ func (st *HelmState) appendHelmXFlags(flags []string, release *ReleaseSpec) ([]s
return flags, nil
}

func (st *HelmState) PrepareChartify(release *ReleaseSpec) (bool, *chartify.ChartifyOpts) {
func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) (bool, *chartify.ChartifyOpts, error) {
var opts chartify.ChartifyOpts

var shouldRun bool

opts.EnableKustomizeAlphaPlugins = true

opts.ChartVersion = release.Version

dir := filepath.Join(st.basePath, release.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
}
}

for _, d := range release.Dependencies {
var dep string

Expand Down Expand Up @@ -52,7 +66,7 @@ func (st *HelmState) PrepareChartify(release *ReleaseSpec) (bool, *chartify.Char
if len(jsonPatches) > 0 {
generatedFiles, err := st.generateTemporaryValuesFiles(jsonPatches, release.MissingFileHandler)
if err != nil {
return false, nil
return false, nil, err
}

for _, f := range generatedFiles {
Expand All @@ -68,7 +82,7 @@ func (st *HelmState) PrepareChartify(release *ReleaseSpec) (bool, *chartify.Char
if len(strategicMergePatches) > 0 {
generatedFiles, err := st.generateTemporaryValuesFiles(strategicMergePatches, release.MissingFileHandler)
if err != nil {
return false, nil
return false, nil, err
}

for _, f := range generatedFiles {
Expand All @@ -80,5 +94,14 @@ func (st *HelmState) PrepareChartify(release *ReleaseSpec) (bool, *chartify.Char
shouldRun = true
}

return shouldRun, &opts
if shouldRun {
generatedFiles, err := st.generateValuesFiles(helm, release, workerIndex)
if err != nil {
return false, nil, err
}

opts.ValuesFiles = generatedFiles
}

return shouldRun, &opts, nil
}
76 changes: 61 additions & 15 deletions pkg/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,7 @@ func PrepareCharts(helm helmexec.Interface, st *HelmState, dir string, concurren
type downloadResults struct {
releaseName string
chartPath string
err error
}
errs := []error{}

Expand All @@ -739,11 +740,17 @@ func PrepareCharts(helm helmexec.Interface, st *HelmState, dir string, concurren
}
close(jobQueue)
},
func(_ int) {
func(workerIndex int) {
for release := range jobQueue {
var chartPath string

if shouldChartify, opts := st.PrepareChartify(release); shouldChartify {
shouldChartify, opts, err := st.PrepareChartify(helm, release, workerIndex)
if err != nil {
results <- &downloadResults{err: err}
return
}

if shouldChartify {
c := chartify.New(
chartify.HelmBin(st.DefaultHelmBinary),
chartify.UseHelm3(helm3),
Expand Down Expand Up @@ -787,12 +794,18 @@ func PrepareCharts(helm helmexec.Interface, st *HelmState, dir string, concurren
}
}

results <- &downloadResults{release.Name, chartPath}
results <- &downloadResults{releaseName: release.Name, chartPath: chartPath}
}
},
func() {
for i := 0; i < len(st.Releases); i++ {
downloadRes := <-results

if downloadRes.err != nil {
errs = append(errs, downloadRes.err)

return
}
temp[downloadRes.releaseName] = downloadRes.chartPath
}
},
Expand Down Expand Up @@ -1886,12 +1899,7 @@ func (st *HelmState) generateTemporaryValuesFiles(values []interface{}, missingF
return generatedFiles, nil
}

func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) ([]string, error) {
flags := []string{}
if release.Namespace != "" {
flags = append(flags, "--namespace", release.Namespace)
}

func (st *HelmState) generateVanillaValuesFiles(release *ReleaseSpec) ([]string, error) {
values := []interface{}{}
for _, v := range release.Values {
switch typedValue := v.(type) {
Expand All @@ -1918,12 +1926,14 @@ func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *R
return nil, err
}

for _, f := range generatedFiles {
flags = append(flags, "--values", f)
}

release.generatedValues = append(release.generatedValues, generatedFiles...)

return generatedFiles, nil
}

func (st *HelmState) generateSecretValuesFiles(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) ([]string, error) {
var generatedFiles []string

for _, value := range release.Secrets {
paths, skip, err := st.storage().resolveFile(release.MissingFileHandler, "secrets", release.ValuesPathPrefix+value)
if err != nil {
Expand All @@ -1944,9 +1954,45 @@ func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *R
return nil, err
}

release.generatedValues = append(release.generatedValues, valfile)
flags = append(flags, "--values", valfile)
generatedFiles = append(generatedFiles, valfile)
}

release.generatedValues = append(release.generatedValues, generatedFiles...)

return generatedFiles, nil
}

func (st *HelmState) generateValuesFiles(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) ([]string, error) {
valuesFiles, err := st.generateVanillaValuesFiles(release)
if err != nil {
return nil, err
}

secretValuesFiles, err := st.generateSecretValuesFiles(helm, release, workerIndex)
if err != nil {
return nil, err
}

files := append(valuesFiles, secretValuesFiles...)

return files, nil
}

func (st *HelmState) namespaceAndValuesFlags(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) ([]string, error) {
flags := []string{}
if release.Namespace != "" {
flags = append(flags, "--namespace", release.Namespace)
}

generatedFiles, err := st.generateValuesFiles(helm, release, workerIndex)
if err != nil {
return nil, err
}

for _, f := range generatedFiles {
flags = append(flags, "--values", f)
}

if len(release.SetValues) > 0 {
for _, set := range release.SetValues {
if set.Value != "" {
Expand Down
49 changes: 49 additions & 0 deletions test/advanced/helmfile.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
repositories:
- name: incubator
url: https://kubernetes-charts-incubator.storage.googleapis.com

releases:
- name: kustomapp
chart: ./kustomapp
values:
- namePrefix: kustomapp-
- name: raw1
chart: incubator/raw
values:
- resources:
- apiVersion: v1
kind: ConfigMap
metadata:
name: raw1
namespace: default
data:
foo: FOO
strategicMergePatches:
- apiVersion: v1
kind: ConfigMap
metadata:
name: raw1
namespace: default
data:
bar: BAR
- name: raw2
chart: incubator/raw
values:
- resources:
- apiVersion: v1
kind: ConfigMap
metadata:
name: raw2
namespace: default
data:
foo: FOO
jsonPatches:
- target:
version: v1
kind: ConfigMap
name: raw2
namespace: default
patch:
- op: replace
path: /data/baz
value: "BAZ"
7 changes: 7 additions & 0 deletions test/advanced/kustomapp/all.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: kustomapp
namespace: default
data:
kustomappfoo: FOO
2 changes: 2 additions & 0 deletions test/advanced/kustomapp/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
resources:
- all.yaml
7 changes: 7 additions & 0 deletions test/advanced/kustomapp2/all.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: kustomapp2
namespace: default
data:
kustomappfoo: FOO
2 changes: 2 additions & 0 deletions test/advanced/kustomapp2/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
resources:
- all.yaml