feat: Option to pass apiVersions to `helm diff` and `helm template` (#1046)

This makes it possible to pass the API Capabilities to helmfile when executing a task that does not render against an actual cluster (diff, template, apply).

Resolves #1014
This commit is contained in:
a-hat 2019-12-27 00:30:39 +01:00 committed by KUOKA Yusuke
parent fb7cc60afe
commit 9cf6b59cd8
3 changed files with 101 additions and 1 deletions

View File

@ -95,7 +95,6 @@ helmDefaults:
# limit the maximum number of revisions saved per release. Use 0 for no limit (default 10)
historyMax: 10
# The desired states of Helm releases.
#
# Helmfile runs various helm commands to converge the current state in the live cluster to the desired state defined here.
@ -267,6 +266,18 @@ bases:
- environments.yaml
- defaults.yaml
- templates.yaml
#
# Advanced Configuration: API Capabilities
#
# Some helmfile tasks render releases locally without querying an actual cluster (diff, apply, template),
# and in this case `.Capabilities.APIVersions` cannot be populated.
# When a chart queries for a specific CRD, this can lead to unexpected results.
#
# Configure a fixed list of api versions to pass to helm via the --api-versions flag:
apiVersions:
- example/v1
```
## Templating

View File

@ -2092,6 +2092,64 @@ releases:
}
}
func TestTemplate_ApiVersions(t *testing.T) {
files := map[string]string{
"/path/to/helmfile.yaml": `
apiVersions:
- helmfile.test/v1
- helmfile.test/v2
releases:
- name: myrelease1
chart: 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"}},
}
var buffer bytes.Buffer
logger := helmexec.NewLogger(&buffer, "debug")
valsRuntime, err := vals.New(vals.Options{CacheSize: 32})
if err != nil {
t.Errorf("unexpected error creating vals runtime: %v", err)
}
app := appWithFs(&App{
glob: filepath.Glob,
abs: filepath.Abs,
KubeContext: "default",
Env: "default",
Logger: logger,
helmExecer: helm,
Namespace: "testNamespace",
valsRuntime: valsRuntime,
}, files)
app.Template(configImpl{})
for i := range wantReleases {
if wantReleases[i].name != helm.templated[i].name {
t.Errorf("name = [%v], want %v", helm.templated[i].name, wantReleases[i].name)
}
if !strings.Contains(helm.templated[i].chart, wantReleases[i].chart) {
t.Errorf("chart = [%v], want %v", helm.templated[i].chart, wantReleases[i].chart)
}
for j := range wantReleases[i].flags {
if j == 7 {
matched, _ := regexp.Match(wantReleases[i].flags[j], []byte(helm.templated[i].flags[j]))
if !matched {
t.Errorf("HelmState.TemplateReleases() = [%v], want %v", helm.templated[i].flags[j], wantReleases[i].flags[j])
}
} else if wantReleases[i].flags[j] != helm.templated[i].flags[j] {
t.Errorf("HelmState.TemplateReleases() = [%v], want %v", helm.templated[i].flags[j], wantReleases[i].flags[j])
}
}
}
}
func TestApply(t *testing.T) {
testcases := []struct {
name string
@ -3351,6 +3409,25 @@ Affected releases are:
err: "foo" has dependency to inexistent release "bar"
`,
},
{
name: "pass apiVersions to helm diff",
loc: location(),
files: map[string]string{
"/path/to/helmfile.yaml": `
apiVersions:
- xxx/v1
releases:
- name: foo
chart: mychart1
`,
},
diffs: map[exectest.DiffKey]error{
exectest.DiffKey{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--api-versionsxxx/v1--detailed-exitcode"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{
{Name: "foo", Flags: []string{"--kube-context", "default"}},
},
},
}
for i := range testcases {

View File

@ -47,6 +47,7 @@ type HelmState struct {
Repositories []RepositorySpec `yaml:"repositories,omitempty"`
Releases []ReleaseSpec `yaml:"releases,omitempty"`
Selectors []string `yaml:"-"`
ApiVersions []string `yaml:"apiVersions,omitempty"`
Templates map[string]TemplateSpec `yaml:"templates"`
@ -1590,6 +1591,8 @@ func (st *HelmState) flagsForTemplate(helm helmexec.Interface, release *ReleaseS
return nil, err
}
flags = st.appendApiVersionsFlags(flags)
common, err := st.namespaceAndValuesFlags(helm, release, workerIndex)
if err != nil {
return nil, err
@ -1615,6 +1618,8 @@ func (st *HelmState) flagsForDiff(helm helmexec.Interface, release *ReleaseSpec,
return nil, err
}
flags = st.appendApiVersionsFlags(flags)
common, err := st.namespaceAndValuesFlags(helm, release, workerIndex)
if err != nil {
return nil, err
@ -1622,6 +1627,13 @@ func (st *HelmState) flagsForDiff(helm helmexec.Interface, release *ReleaseSpec,
return append(flags, common...), nil
}
func (st *HelmState) appendApiVersionsFlags(flags []string) []string {
for _, a := range st.ApiVersions {
flags = append(flags, "--api-versions", a)
}
return flags
}
func (st *HelmState) isDevelopment(release *ReleaseSpec) bool {
result := st.HelmDefaults.Devel
if release.Devel != nil {