diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6c7eeede..9290e772 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -22,6 +22,7 @@ ## Bug Fixes * [#498](https://github.com/suse-edge/edge-image-builder/issues/498) - Fix kernelArgs issue with Leap Micro 6.0 +* [#481](https://github.com/suse-edge/edge-image-builder/issues/481) - Certain Helm charts fail when templated without specified API Versions --- diff --git a/pkg/helm/helm.go b/pkg/helm/helm.go index 202c0742..1d7ac285 100644 --- a/pkg/helm/helm.go +++ b/pkg/helm/helm.go @@ -210,7 +210,7 @@ func pullCommand(chart string, repo *image.HelmRepository, version, destDir, cer return cmd } -func (h *Helm) Template(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string) ([]map[string]any, error) { +func (h *Helm) Template(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string, apiVersions []string) ([]map[string]any, error) { logFile := filepath.Join(h.outputDir, templateLogFileName) file, err := os.OpenFile(logFile, outputFileFlags, fileio.NonExecutablePerms) @@ -224,7 +224,7 @@ func (h *Helm) Template(chart, repository, version, valuesFilePath, kubeVersion, }() chartContentsBuffer := new(strings.Builder) - cmd := templateCommand(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace, io.MultiWriter(file, chartContentsBuffer), file) + cmd := templateCommand(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace, apiVersions, io.MultiWriter(file, chartContentsBuffer), file) if _, err = fmt.Fprintf(file, "command: %s\n", cmd); err != nil { return nil, fmt.Errorf("writing command prefix to log file: %w", err) @@ -243,7 +243,7 @@ func (h *Helm) Template(chart, repository, version, valuesFilePath, kubeVersion, return resources, nil } -func templateCommand(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string, stdout, stderr io.Writer) *exec.Cmd { +func templateCommand(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string, apiVersions []string, stdout, stderr io.Writer) *exec.Cmd { var args []string args = append(args, "template", "--skip-crds", chart, repository) @@ -259,6 +259,10 @@ func templateCommand(chart, repository, version, valuesFilePath, kubeVersion, ta args = append(args, "-f", valuesFilePath) } + if len(apiVersions) != 0 { + args = append(args, "--api-versions", strings.Join(apiVersions, ",")) + } + args = append(args, "--kube-version", kubeVersion) cmd := exec.Command("helm", args...) diff --git a/pkg/helm/helm_test.go b/pkg/helm/helm_test.go index 6f4f7100..d35567bb 100644 --- a/pkg/helm/helm_test.go +++ b/pkg/helm/helm_test.go @@ -502,6 +502,7 @@ func TestTemplateCommand(t *testing.T) { repo string chart string version string + apiVersions []string kubeVersion string targetNamespace string valuesPath string @@ -512,6 +513,7 @@ func TestTemplateCommand(t *testing.T) { repo: "suse-edge/kubevirt", chart: "kubevirt", version: "0.2.1", + apiVersions: []string{"batch/v1", "apps/v1/Deployment"}, kubeVersion: "v1.29.0+rke2r1", targetNamespace: "kubevirt-ns", valuesPath: "/kubevirt/values.yaml", @@ -527,6 +529,8 @@ func TestTemplateCommand(t *testing.T) { "0.2.1", "-f", "/kubevirt/values.yaml", + "--api-versions", + "batch/v1,apps/v1/Deployment", "--kube-version", "v1.29.0+rke2r1", }, @@ -553,7 +557,7 @@ func TestTemplateCommand(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - cmd := templateCommand(test.chart, test.repo, test.version, test.valuesPath, test.kubeVersion, test.targetNamespace, &stdout, &stderr) + cmd := templateCommand(test.chart, test.repo, test.version, test.valuesPath, test.kubeVersion, test.targetNamespace, test.apiVersions, &stdout, &stderr) assert.Equal(t, test.expectedArgs, cmd.Args) assert.Equal(t, &stdout, cmd.Stdout) diff --git a/pkg/image/definition.go b/pkg/image/definition.go index 541ea11a..85aeddea 100644 --- a/pkg/image/definition.go +++ b/pkg/image/definition.go @@ -210,13 +210,14 @@ type Helm struct { } type HelmChart struct { - Name string `yaml:"name"` - RepositoryName string `yaml:"repositoryName"` - Version string `yaml:"version"` - TargetNamespace string `yaml:"targetNamespace"` - CreateNamespace bool `yaml:"createNamespace"` - InstallationNamespace string `yaml:"installationNamespace"` - ValuesFile string `yaml:"valuesFile"` + Name string `yaml:"name"` + RepositoryName string `yaml:"repositoryName"` + Version string `yaml:"version"` + TargetNamespace string `yaml:"targetNamespace"` + CreateNamespace bool `yaml:"createNamespace"` + InstallationNamespace string `yaml:"installationNamespace"` + ValuesFile string `yaml:"valuesFile"` + APIVersions []string `yaml:"apiVersions"` } type HelmRepository struct { diff --git a/pkg/image/definition_test.go b/pkg/image/definition_test.go index e032d517..0fa353da 100644 --- a/pkg/image/definition_test.go +++ b/pkg/image/definition_test.go @@ -190,6 +190,8 @@ func TestParse(t *testing.T) { assert.Equal(t, true, kubernetes.Helm.Charts[0].CreateNamespace) assert.Equal(t, "apache-system", kubernetes.Helm.Charts[0].InstallationNamespace) assert.Equal(t, "apache-values.yaml", kubernetes.Helm.Charts[0].ValuesFile) + assert.Equal(t, "batch/v1", kubernetes.Helm.Charts[0].APIVersions[0]) + assert.Equal(t, "apps/v1/Deployment", kubernetes.Helm.Charts[0].APIVersions[1]) assert.Equal(t, "metallb", kubernetes.Helm.Charts[1].Name) assert.Equal(t, "suse-edge", kubernetes.Helm.Charts[1].RepositoryName) diff --git a/pkg/image/testdata/full-valid-example.yaml b/pkg/image/testdata/full-valid-example.yaml index 9723e115..1c3cbe5d 100644 --- a/pkg/image/testdata/full-valid-example.yaml +++ b/pkg/image/testdata/full-valid-example.yaml @@ -107,6 +107,9 @@ kubernetes: createNamespace: true installationNamespace: apache-system valuesFile: apache-values.yaml + apiVersions: + - batch/v1 + - apps/v1/Deployment - name: metallb repositoryName: suse-edge version: 0.14.3 diff --git a/pkg/registry/helm.go b/pkg/registry/helm.go index ad687e4f..0e8a02f7 100644 --- a/pkg/registry/helm.go +++ b/pkg/registry/helm.go @@ -57,7 +57,7 @@ func (r *Registry) helmChartImages() ([]string, error) { } func (r *Registry) getChartContainerImages(chart *image.HelmChart, chartPath, valuesPath, kubeVersion string) ([]string, error) { - chartResources, err := r.helmClient.Template(chart.Name, chartPath, chart.Version, valuesPath, kubeVersion, chart.TargetNamespace) + chartResources, err := r.helmClient.Template(chart.Name, chartPath, chart.Version, valuesPath, kubeVersion, chart.TargetNamespace, chart.APIVersions) if err != nil { return nil, fmt.Errorf("templating chart: %w", err) } diff --git a/pkg/registry/helm_test.go b/pkg/registry/helm_test.go index 902c8b08..ab7a833e 100644 --- a/pkg/registry/helm_test.go +++ b/pkg/registry/helm_test.go @@ -16,7 +16,7 @@ type mockHelmClient struct { addRepoFunc func(repository *image.HelmRepository) error registryLoginFunc func(repository *image.HelmRepository) error pullFunc func(chart string, repository *image.HelmRepository, version, destDir string) (string, error) - templateFunc func(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string) ([]map[string]any, error) + templateFunc func(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string, apiVersions []string) ([]map[string]any, error) } func (m mockHelmClient) AddRepo(repository *image.HelmRepository) error { @@ -40,9 +40,9 @@ func (m mockHelmClient) Pull(chart string, repository *image.HelmRepository, ver panic("not implemented") } -func (m mockHelmClient) Template(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string) ([]map[string]any, error) { +func (m mockHelmClient) Template(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string, apiVersions []string) ([]map[string]any, error) { if m.templateFunc != nil { - return m.templateFunc(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace) + return m.templateFunc(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace, apiVersions) } panic("not implemented") } @@ -68,7 +68,7 @@ func TestRegistry_HelmChartImages_TemplateError(t *testing.T) { }, }, helmClient: mockHelmClient{ - templateFunc: func(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string) ([]map[string]any, error) { + templateFunc: func(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string, apiVersions []string) ([]map[string]any, error) { return nil, fmt.Errorf("failed templating") }, }, @@ -92,7 +92,7 @@ func TestRegistry_HelmChartImages(t *testing.T) { }, }, helmClient: mockHelmClient{ - templateFunc: func(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string) ([]map[string]any, error) { + templateFunc: func(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string, apiVersions []string) ([]map[string]any, error) { return []map[string]any{ { "kind": "Deployment", diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go index 9753f439..0f377a2f 100644 --- a/pkg/registry/registry.go +++ b/pkg/registry/registry.go @@ -20,7 +20,7 @@ type helmClient interface { AddRepo(repository *image.HelmRepository) error RegistryLogin(repository *image.HelmRepository) error Pull(chart string, repository *image.HelmRepository, version, destDir string) (string, error) - Template(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string) ([]map[string]any, error) + Template(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string, apiVersions []string) ([]map[string]any, error) } type helmChart struct { diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go index ab1beb0e..10878a34 100644 --- a/pkg/registry/registry_test.go +++ b/pkg/registry/registry_test.go @@ -62,7 +62,7 @@ func TestRegistry_ContainerImages(t *testing.T) { }, }, helmClient: mockHelmClient{ - templateFunc: func(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string) ([]map[string]any, error) { + templateFunc: func(chart, repository, version, valuesFilePath, kubeVersion, targetNamespace string, apiVersions []string) ([]map[string]any, error) { return []map[string]any{ { "kind": "Deployment",