/* Copyright 2020 The Flux authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package controllers import ( "context" "fmt" "net/http" "net/url" "os" "path" "path/filepath" "strings" "time" "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/gittestserver" "github.com/fluxcd/pkg/helmtestserver" "github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/storage/memory" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" helmchart "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/chartutil" corev1 "k8s.io/api/core/v1" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/yaml" sourcev1 "github.com/fluxcd/source-controller/api/v1beta2" ) var _ = Describe("HelmChartReconciler", func() { const ( timeout = time.Second * 30 interval = time.Second * 1 indexInterval = time.Second * 2 pullInterval = time.Second * 3 ) Context("HelmChart from HelmRepository", func() { var ( namespace *corev1.Namespace helmServer *helmtestserver.HelmServer err error ) BeforeEach(func() { namespace = &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "helm-chart-test-" + randStringRunes(5)}, } err = k8sClient.Create(context.Background(), namespace) Expect(err).NotTo(HaveOccurred(), "failed to create test namespace") helmServer, err = helmtestserver.NewTempHelmServer() Expect(err).To(Succeed()) helmServer.Start() }) AfterEach(func() { helmServer.Stop() os.RemoveAll(helmServer.Root()) err = k8sClient.Delete(context.Background(), namespace) Expect(err).NotTo(HaveOccurred(), "failed to delete test namespace") }) It("Creates artifacts for", func() { Expect(helmServer.PackageChart(path.Join("testdata/charts/helmchart"))).Should(Succeed()) Expect(helmServer.GenerateIndex()).Should(Succeed()) repositoryKey := types.NamespacedName{ Name: "helmrepository-sample-" + randStringRunes(5), Namespace: namespace.Name, } Expect(k8sClient.Create(context.Background(), &sourcev1.HelmRepository{ ObjectMeta: metav1.ObjectMeta{ Name: repositoryKey.Name, Namespace: repositoryKey.Namespace, }, Spec: sourcev1.HelmRepositorySpec{ URL: helmServer.URL(), Interval: metav1.Duration{Duration: indexInterval}, }, })).Should(Succeed()) key := types.NamespacedName{ Name: "helmchart-sample-" + randStringRunes(5), Namespace: namespace.Name, } created := &sourcev1.HelmChart{ ObjectMeta: metav1.ObjectMeta{ Name: key.Name, Namespace: key.Namespace, }, Spec: sourcev1.HelmChartSpec{ Chart: "helmchart", Version: "", SourceRef: sourcev1.LocalHelmChartSourceReference{ Kind: sourcev1.HelmRepositoryKind, Name: repositoryKey.Name, }, Interval: metav1.Duration{Duration: pullInterval}, }, } Expect(k8sClient.Create(context.Background(), created)).Should(Succeed()) By("Expecting artifact") got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact != nil && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeFalse()) By("Packaging a new chart version and regenerating the index") Expect(helmServer.PackageChartWithVersion(path.Join("testdata/charts/helmchart"), "0.2.0")).Should(Succeed()) Expect(helmServer.GenerateIndex()).Should(Succeed()) By("Expecting new artifact revision and GC") Eventually(func() bool { now := &sourcev1.HelmChart{} _ = k8sClient.Get(context.Background(), key, now) // Test revision change and garbage collection return now.Status.Artifact.Revision != got.Status.Artifact.Revision && !ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) When("Setting valid valuesFiles attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFiles = []string{ "values.yaml", "override.yaml", } Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) When("Setting invalid valuesFiles attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFiles = []string{ "values.yaml", "invalid.yaml", } Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) When("Setting valid valuesFiles and valuesFile attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFile = "values.yaml" updated.Spec.ValuesFiles = []string{ "override.yaml", } Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) When("Setting valid valuesFile attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFile = "override.yaml" updated.Spec.ValuesFiles = []string{} Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) _, exists := helmChart.Values["testDefault"] Expect(exists).To(BeFalse()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) When("Setting identical valuesFile attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFile = "duplicate.yaml" updated.Spec.ValuesFiles = []string{} Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeFalse()) }) When("Setting invalid valuesFile attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFile = "invalid.yaml" updated.Spec.ValuesFiles = []string{} Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeFalse()) }) By("Expecting missing HelmRepository error") updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).Should(Succeed()) updated.Spec.SourceRef.Name = "invalid" updated.Spec.ValuesFile = "" updated.Spec.ValuesFiles = []string{} Expect(k8sClient.Update(context.Background(), updated)).Should(Succeed()) Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, updated) for _, c := range updated.Status.Conditions { if c.Reason == sourcev1.ChartPullFailedReason && strings.Contains(c.Message, "failed to retrieve source") { return true } } return false }, timeout, interval).Should(BeTrue()) Expect(updated.Status.Artifact).ToNot(BeNil()) By("Expecting to delete successfully") got = &sourcev1.HelmChart{} Eventually(func() error { _ = k8sClient.Get(context.Background(), key, got) return k8sClient.Delete(context.Background(), got) }, timeout, interval).Should(Succeed()) By("Expecting delete to finish") Eventually(func() error { c := &sourcev1.HelmChart{} return k8sClient.Get(context.Background(), key, c) }, timeout, interval).ShouldNot(Succeed()) exists := func(path string) bool { // wait for tmp sync on macOS time.Sleep(time.Second) _, err := os.Stat(path) return err == nil } By("Expecting GC on delete") Eventually(exists(got.Status.Artifact.Path), timeout, interval).ShouldNot(BeTrue()) }) It("Filters versions", func() { versions := []string{"0.1.0", "0.1.1", "0.2.0", "0.3.0-rc.1", "1.0.0-alpha.1", "1.0.0"} for k := range versions { Expect(helmServer.PackageChartWithVersion(path.Join("testdata/charts/helmchart"), versions[k])).Should(Succeed()) } Expect(helmServer.GenerateIndex()).Should(Succeed()) repositoryKey := types.NamespacedName{ Name: "helmrepository-sample-" + randStringRunes(5), Namespace: namespace.Name, } repository := &sourcev1.HelmRepository{ ObjectMeta: metav1.ObjectMeta{ Name: repositoryKey.Name, Namespace: repositoryKey.Namespace, }, Spec: sourcev1.HelmRepositorySpec{ URL: helmServer.URL(), Interval: metav1.Duration{Duration: 1 * time.Hour}, }, } Expect(k8sClient.Create(context.Background(), repository)).Should(Succeed()) defer k8sClient.Delete(context.Background(), repository) key := types.NamespacedName{ Name: "helmchart-sample-" + randStringRunes(5), Namespace: namespace.Name, } chart := &sourcev1.HelmChart{ ObjectMeta: metav1.ObjectMeta{ Name: key.Name, Namespace: key.Namespace, }, Spec: sourcev1.HelmChartSpec{ Chart: "helmchart", Version: "*", SourceRef: sourcev1.LocalHelmChartSourceReference{ Kind: sourcev1.HelmRepositoryKind, Name: repositoryKey.Name, }, Interval: metav1.Duration{Duration: 1 * time.Hour}, }, } Expect(k8sClient.Create(context.Background(), chart)).Should(Succeed()) defer k8sClient.Delete(context.Background(), chart) Eventually(func() string { _ = k8sClient.Get(context.Background(), key, chart) if chart.Status.Artifact != nil { return chart.Status.Artifact.Revision } return "" }, timeout, interval).Should(Equal("1.0.0")) chart.Spec.Version = "<0.2.0" Expect(k8sClient.Update(context.Background(), chart)).Should(Succeed()) Eventually(func() string { _ = k8sClient.Get(context.Background(), key, chart) if chart.Status.Artifact != nil { return chart.Status.Artifact.Revision } return "" }, timeout, interval).Should(Equal("0.1.1")) chart.Spec.Version = "invalid" Expect(k8sClient.Update(context.Background(), chart)).Should(Succeed()) Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, chart) for _, c := range chart.Status.Conditions { if c.Reason == sourcev1.ChartPullFailedReason { return true } } return false }, timeout, interval).Should(BeTrue()) Expect(chart.GetArtifact()).NotTo(BeNil()) Expect(chart.Status.Artifact.Revision).Should(Equal("0.1.1")) }) It("Authenticates when credentials are provided", func() { helmServer.Stop() var username, password = "john", "doe" helmServer.WithMiddleware(func(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { u, p, ok := r.BasicAuth() if !ok || username != u || password != p { w.WriteHeader(401) return } handler.ServeHTTP(w, r) }) }) helmServer.Start() Expect(helmServer.PackageChartWithVersion(path.Join("testdata/charts/helmchart"), "0.1.0")).Should(Succeed()) Expect(helmServer.GenerateIndex()).Should(Succeed()) secretKey := types.NamespacedName{ Name: "helmrepository-auth-" + randStringRunes(5), Namespace: namespace.Name, } secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretKey.Name, Namespace: secretKey.Namespace, }, Data: map[string][]byte{ "username": []byte(username), "password": []byte(password), }, } Expect(k8sClient.Create(context.Background(), secret)).Should(Succeed()) By("Creating repository and waiting for artifact") repositoryKey := types.NamespacedName{ Name: "helmrepository-sample-" + randStringRunes(5), Namespace: namespace.Name, } repository := &sourcev1.HelmRepository{ ObjectMeta: metav1.ObjectMeta{ Name: repositoryKey.Name, Namespace: repositoryKey.Namespace, }, Spec: sourcev1.HelmRepositorySpec{ URL: helmServer.URL(), SecretRef: &meta.LocalObjectReference{ Name: secretKey.Name, }, Interval: metav1.Duration{Duration: pullInterval}, }, } Expect(k8sClient.Create(context.Background(), repository)).Should(Succeed()) defer k8sClient.Delete(context.Background(), repository) Eventually(func() bool { _ = k8sClient.Get(context.Background(), repositoryKey, repository) return repository.Status.Artifact != nil }, timeout, interval).Should(BeTrue()) By("Deleting secret before applying HelmChart") Expect(k8sClient.Delete(context.Background(), secret)).Should(Succeed()) By("Applying HelmChart") key := types.NamespacedName{ Name: "helmchart-sample-" + randStringRunes(5), Namespace: namespace.Name, } chart := &sourcev1.HelmChart{ ObjectMeta: metav1.ObjectMeta{ Name: key.Name, Namespace: key.Namespace, }, Spec: sourcev1.HelmChartSpec{ Chart: "helmchart", Version: "*", SourceRef: sourcev1.LocalHelmChartSourceReference{ Kind: sourcev1.HelmRepositoryKind, Name: repositoryKey.Name, }, Interval: metav1.Duration{Duration: pullInterval}, }, } Expect(k8sClient.Create(context.Background(), chart)).Should(Succeed()) defer k8sClient.Delete(context.Background(), chart) By("Expecting missing secret error") got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) for _, c := range got.Status.Conditions { if c.Reason == sourcev1.AuthenticationFailedReason && strings.Contains(c.Message, "auth secret error") { return true } } return false }, timeout, interval).Should(BeTrue()) By("Applying secret with missing keys") secret.ResourceVersion = "" secret.Data["username"] = []byte{} secret.Data["password"] = []byte{} Expect(k8sClient.Create(context.Background(), secret)).Should(Succeed()) By("Expecting 401") Eventually(func() bool { got := &sourcev1.HelmChart{} _ = k8sClient.Get(context.Background(), key, got) for _, c := range got.Status.Conditions { if c.Reason == sourcev1.ChartPullFailedReason && strings.Contains(c.Message, "401 Unauthorized") { return true } } return false }, timeout, interval).Should(BeTrue()) By("Adding username key") secret.Data["username"] = []byte(username) Expect(k8sClient.Update(context.Background(), secret)).Should(Succeed()) By("Expecting missing field error") Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) for _, c := range got.Status.Conditions { if c.Reason == sourcev1.AuthenticationFailedReason { return true } } return false }, timeout, interval).Should(BeTrue()) By("Adding password key") secret.Data["password"] = []byte(password) Expect(k8sClient.Update(context.Background(), secret)).Should(Succeed()) By("Expecting artifact") Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return apimeta.IsStatusConditionTrue(got.Status.Conditions, meta.ReadyCondition) }, timeout, interval).Should(BeTrue()) Expect(got.Status.Artifact).ToNot(BeNil()) }) }) Context("HelmChart from GitRepository", func() { var ( namespace *corev1.Namespace gitServer *gittestserver.GitServer err error ) BeforeEach(func() { namespace = &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "test-git-repository-" + randStringRunes(5)}, } err = k8sClient.Create(context.Background(), namespace) Expect(err).NotTo(HaveOccurred(), "failed to create test namespace") gitServer, err = gittestserver.NewTempGitServer() Expect(err).NotTo(HaveOccurred()) gitServer.AutoCreate() Expect(gitServer.StartHTTP()).To(Succeed()) }) AfterEach(func() { gitServer.StopHTTP() os.RemoveAll(gitServer.Root()) err = k8sClient.Delete(context.Background(), namespace) Expect(err).NotTo(HaveOccurred(), "failed to delete test namespace") }) It("Creates artifacts for", func() { fs := memfs.New() gitrepo, err := git.Init(memory.NewStorage(), fs) Expect(err).NotTo(HaveOccurred()) wt, err := gitrepo.Worktree() Expect(err).NotTo(HaveOccurred()) u, err := url.Parse(gitServer.HTTPAddress()) Expect(err).NotTo(HaveOccurred()) u.Path = path.Join(u.Path, fmt.Sprintf("repository-%s.git", randStringRunes(5))) _, err = gitrepo.CreateRemote(&config.RemoteConfig{ Name: "origin", URLs: []string{u.String()}, }) Expect(err).NotTo(HaveOccurred()) chartDir := "testdata/charts" Expect(filepath.Walk(chartDir, func(p string, fi os.FileInfo, err error) error { if err != nil { return err } switch { case fi.Mode().IsDir(): return fs.MkdirAll(p, os.ModeDir) case !fi.Mode().IsRegular(): return nil } b, err := os.ReadFile(p) if err != nil { return err } ff, err := fs.Create(p) if err != nil { return err } if _, err := ff.Write(b); err != nil { return err } _ = ff.Close() _, err = wt.Add(p) return err })).To(Succeed()) _, err = wt.Commit("Helm charts", &git.CommitOptions{Author: &object.Signature{ Name: "John Doe", Email: "john@example.com", When: time.Now(), }}) Expect(err).NotTo(HaveOccurred()) err = gitrepo.Push(&git.PushOptions{}) Expect(err).NotTo(HaveOccurred()) repositoryKey := types.NamespacedName{ Name: fmt.Sprintf("git-repository-sample-%s", randStringRunes(5)), Namespace: namespace.Name, } repository := &sourcev1.GitRepository{ ObjectMeta: metav1.ObjectMeta{ Name: repositoryKey.Name, Namespace: repositoryKey.Namespace, }, Spec: sourcev1.GitRepositorySpec{ URL: u.String(), Interval: metav1.Duration{Duration: indexInterval}, }, } Expect(k8sClient.Create(context.Background(), repository)).Should(Succeed()) defer k8sClient.Delete(context.Background(), repository) key := types.NamespacedName{ Name: "helmchart-sample-" + randStringRunes(5), Namespace: namespace.Name, } chart := &sourcev1.HelmChart{ ObjectMeta: metav1.ObjectMeta{ Name: key.Name, Namespace: key.Namespace, }, Spec: sourcev1.HelmChartSpec{ Chart: "testdata/charts/helmchartwithdeps", Version: "*", SourceRef: sourcev1.LocalHelmChartSourceReference{ Kind: sourcev1.GitRepositoryKind, Name: repositoryKey.Name, }, Interval: metav1.Duration{Duration: pullInterval}, }, } Expect(k8sClient.Create(context.Background(), chart)).Should(Succeed()) defer k8sClient.Delete(context.Background(), chart) By("Expecting artifact") got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact != nil && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) By("Committing a new version in the chart metadata") f, err := fs.OpenFile(fs.Join(chartDir, "helmchartwithdeps", chartutil.ChartfileName), os.O_RDWR, os.FileMode(0600)) Expect(err).NotTo(HaveOccurred()) b := make([]byte, 2048) n, err := f.Read(b) Expect(err).NotTo(HaveOccurred()) b = b[0:n] y := new(helmchart.Metadata) err = yaml.Unmarshal(b, y) Expect(err).NotTo(HaveOccurred()) y.Version = "0.2.0" b, err = yaml.Marshal(y) Expect(err).NotTo(HaveOccurred()) _, err = f.Write(b) Expect(err).NotTo(HaveOccurred()) err = f.Close() Expect(err).NotTo(HaveOccurred()) commit, err := wt.Commit("Chart version bump", &git.CommitOptions{ Author: &object.Signature{ Name: "John Doe", Email: "john@example.com", When: time.Now(), }, All: true, }) Expect(err).NotTo(HaveOccurred()) err = gitrepo.Push(&git.PushOptions{}) Expect(err).NotTo(HaveOccurred()) By("Expecting new artifact revision and GC") now := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, now) // Test revision change and garbage collection return now.Status.Artifact.Revision != got.Status.Artifact.Revision && !ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*now.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values).ToNot(BeNil()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeFalse()) When("Setting reconcileStrategy to Revision", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ReconcileStrategy = sourcev1.ReconcileStrategyRevision Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Revision != updated.Status.Artifact.Revision && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) Expect(got.Status.Artifact.Revision).To(ContainSubstring(updated.Status.Artifact.Revision)) Expect(got.Status.Artifact.Revision).To(ContainSubstring(commit.String()[0:12])) }) When("Setting valid valuesFiles attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFiles = []string{ "./testdata/charts/helmchart/values.yaml", "./testdata/charts/helmchart/override.yaml", } Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) When("Setting invalid valuesFiles attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFiles = []string{ "./testdata/charts/helmchart/values.yaml", "./testdata/charts/helmchart/invalid.yaml", } Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) When("Setting valid valuesFiles and valuesFile attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFile = "./testdata/charts/helmchart/values.yaml" updated.Spec.ValuesFiles = []string{ "./testdata/charts/helmchart/override.yaml", } Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) When("Setting valid valuesFile attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFile = "./testdata/charts/helmchart/override.yaml" updated.Spec.ValuesFiles = []string{} Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) // Since a lot of chart updates took place above, checking // artifact checksum isn't the most reliable way to find out // if the artifact was changed due to the current update. // Use status condition to be sure. for _, condn := range got.Status.Conditions { if strings.Contains(condn.Message, "with merged values files [./testdata/charts/helmchart/override.yaml]") && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) { return true } } return false }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) _, exists := helmChart.Values["testDefault"] Expect(exists).To(BeFalse()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) When("Setting invalid valuesFile attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFile = "./testdata/charts/helmchart/invalid.yaml" updated.Spec.ValuesFiles = []string{} Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) _, exists := helmChart.Values["testDefault"] Expect(exists).To(BeFalse()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) }) It("Creates artifacts with .tgz file", func() { fs := memfs.New() gitrepo, err := git.Init(memory.NewStorage(), fs) Expect(err).NotTo(HaveOccurred()) wt, err := gitrepo.Worktree() Expect(err).NotTo(HaveOccurred()) u, err := url.Parse(gitServer.HTTPAddress()) Expect(err).NotTo(HaveOccurred()) u.Path = path.Join(u.Path, fmt.Sprintf("repository-%s.git", randStringRunes(5))) _, err = gitrepo.CreateRemote(&config.RemoteConfig{ Name: "origin", URLs: []string{u.String()}, }) Expect(err).NotTo(HaveOccurred()) chartDir := "testdata/charts/helmchart" helmChart, err := loader.LoadDir(chartDir) Expect(err).NotTo(HaveOccurred()) chartPackagePath, err := os.MkdirTemp("", fmt.Sprintf("chartpackage-%s-%s", helmChart.Name(), randStringRunes(5))) Expect(err).NotTo(HaveOccurred()) defer os.RemoveAll(chartPackagePath) pkg, err := chartutil.Save(helmChart, chartPackagePath) Expect(err).NotTo(HaveOccurred()) b, err := os.ReadFile(pkg) Expect(err).NotTo(HaveOccurred()) tgz := filepath.Base(pkg) ff, err := fs.Create(tgz) Expect(err).NotTo(HaveOccurred()) _, err = ff.Write(b) Expect(err).NotTo(HaveOccurred()) ff.Close() _, err = wt.Add(tgz) Expect(err).NotTo(HaveOccurred()) _, err = wt.Commit("Helm chart", &git.CommitOptions{Author: &object.Signature{ Name: "John Doe", Email: "john@example.com", When: time.Now(), }}) Expect(err).NotTo(HaveOccurred()) err = gitrepo.Push(&git.PushOptions{}) Expect(err).NotTo(HaveOccurred()) repositoryKey := types.NamespacedName{ Name: fmt.Sprintf("git-repository-sample-%s", randStringRunes(5)), Namespace: namespace.Name, } repository := &sourcev1.GitRepository{ ObjectMeta: metav1.ObjectMeta{ Name: repositoryKey.Name, Namespace: repositoryKey.Namespace, }, Spec: sourcev1.GitRepositorySpec{ URL: u.String(), Interval: metav1.Duration{Duration: indexInterval}, }, } Expect(k8sClient.Create(context.Background(), repository)).Should(Succeed()) defer k8sClient.Delete(context.Background(), repository) key := types.NamespacedName{ Name: "helmchart-sample-" + randStringRunes(5), Namespace: namespace.Name, } chart := &sourcev1.HelmChart{ ObjectMeta: metav1.ObjectMeta{ Name: key.Name, Namespace: key.Namespace, }, Spec: sourcev1.HelmChartSpec{ Chart: tgz, Version: "*", SourceRef: sourcev1.LocalHelmChartSourceReference{ Kind: sourcev1.GitRepositoryKind, Name: repositoryKey.Name, }, Interval: metav1.Duration{Duration: pullInterval}, }, } Expect(k8sClient.Create(context.Background(), chart)).Should(Succeed()) defer k8sClient.Delete(context.Background(), chart) By("Expecting artifact") got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact != nil && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) }) }) Context("HelmChart from GitRepository with HelmRepository dependency", func() { var ( namespace *corev1.Namespace gitServer *gittestserver.GitServer helmServer *helmtestserver.HelmServer err error ) BeforeEach(func() { namespace = &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{Name: "test-git-repository-" + randStringRunes(5)}, } err = k8sClient.Create(context.Background(), namespace) Expect(err).NotTo(HaveOccurred(), "failed to create test namespace") gitServer, err = gittestserver.NewTempGitServer() Expect(err).NotTo(HaveOccurred()) gitServer.AutoCreate() Expect(gitServer.StartHTTP()).To(Succeed()) helmServer, err = helmtestserver.NewTempHelmServer() Expect(err).To(Succeed()) helmServer.Start() }) AfterEach(func() { gitServer.StopHTTP() os.RemoveAll(gitServer.Root()) helmServer.Stop() os.RemoveAll(helmServer.Root()) err = k8sClient.Delete(context.Background(), namespace) Expect(err).NotTo(HaveOccurred(), "failed to delete test namespace") }) It("Creates artifacts for", func() { helmServer.Stop() var username, password = "john", "doe" helmServer.WithMiddleware(func(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { u, p, ok := r.BasicAuth() if !ok || username != u || password != p { w.WriteHeader(401) return } handler.ServeHTTP(w, r) }) }) helmServer.Start() Expect(helmServer.PackageChart(path.Join("testdata/charts/helmchart"))).Should(Succeed()) Expect(helmServer.GenerateIndex()).Should(Succeed()) secretKey := types.NamespacedName{ Name: "helmrepository-auth-" + randStringRunes(5), Namespace: namespace.Name, } secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretKey.Name, Namespace: secretKey.Namespace, }, StringData: map[string]string{ "username": username, "password": password, }, } Expect(k8sClient.Create(context.Background(), secret)).Should(Succeed()) By("Creating repository and waiting for artifact") helmRepositoryKey := types.NamespacedName{ Name: "helmrepository-sample-" + randStringRunes(5), Namespace: namespace.Name, } helmRepository := &sourcev1.HelmRepository{ ObjectMeta: metav1.ObjectMeta{ Name: helmRepositoryKey.Name, Namespace: helmRepositoryKey.Namespace, }, Spec: sourcev1.HelmRepositorySpec{ URL: helmServer.URL(), SecretRef: &meta.LocalObjectReference{ Name: secretKey.Name, }, Interval: metav1.Duration{Duration: pullInterval}, }, } Expect(k8sClient.Create(context.Background(), helmRepository)).Should(Succeed()) defer k8sClient.Delete(context.Background(), helmRepository) Eventually(func() bool { _ = k8sClient.Get(context.Background(), helmRepositoryKey, helmRepository) return helmRepository.Status.Artifact != nil }, timeout, interval).Should(BeTrue()) fs := memfs.New() gitrepo, err := git.Init(memory.NewStorage(), fs) Expect(err).NotTo(HaveOccurred()) wt, err := gitrepo.Worktree() Expect(err).NotTo(HaveOccurred()) u, err := url.Parse(gitServer.HTTPAddress()) Expect(err).NotTo(HaveOccurred()) u.Path = path.Join(u.Path, fmt.Sprintf("repository-%s.git", randStringRunes(5))) _, err = gitrepo.CreateRemote(&config.RemoteConfig{ Name: "origin", URLs: []string{u.String()}, }) Expect(err).NotTo(HaveOccurred()) chartDir := "testdata/charts/helmchartwithdeps" Expect(filepath.Walk(chartDir, func(p string, fi os.FileInfo, err error) error { if err != nil { return err } switch { case fi.Mode().IsDir(): return fs.MkdirAll(p, os.ModeDir) case !fi.Mode().IsRegular(): return nil } b, err := os.ReadFile(p) if err != nil { return err } ff, err := fs.Create(p) if err != nil { return err } if _, err := ff.Write(b); err != nil { return err } _ = ff.Close() _, err = wt.Add(p) return err })).To(Succeed()) By("Configuring the chart dependency") filePath := fs.Join(chartDir, chartutil.ChartfileName) f, err := fs.OpenFile(filePath, os.O_RDWR, os.FileMode(0600)) Expect(err).NotTo(HaveOccurred()) b := make([]byte, 2048) n, err := f.Read(b) Expect(err).NotTo(HaveOccurred()) b = b[0:n] err = f.Close() Expect(err).NotTo(HaveOccurred()) y := new(helmchart.Metadata) err = yaml.Unmarshal(b, y) Expect(err).NotTo(HaveOccurred()) y.Dependencies = []*helmchart.Dependency{ { Name: "helmchart", Version: ">=0.1.0", Repository: helmRepository.Spec.URL, }, } b, err = yaml.Marshal(y) Expect(err).NotTo(HaveOccurred()) ff, err := fs.Create(filePath) Expect(err).NotTo(HaveOccurred()) _, err = ff.Write(b) Expect(err).NotTo(HaveOccurred()) err = ff.Close() Expect(err).NotTo(HaveOccurred()) _, err = wt.Commit("Helm charts", &git.CommitOptions{ Author: &object.Signature{ Name: "John Doe", Email: "john@example.com", When: time.Now(), }, All: true, }) Expect(err).NotTo(HaveOccurred()) err = gitrepo.Push(&git.PushOptions{}) Expect(err).NotTo(HaveOccurred()) repositoryKey := types.NamespacedName{ Name: fmt.Sprintf("git-repository-sample-%s", randStringRunes(5)), Namespace: namespace.Name, } repository := &sourcev1.GitRepository{ ObjectMeta: metav1.ObjectMeta{ Name: repositoryKey.Name, Namespace: repositoryKey.Namespace, }, Spec: sourcev1.GitRepositorySpec{ URL: u.String(), Interval: metav1.Duration{Duration: indexInterval}, }, } Expect(k8sClient.Create(context.Background(), repository)).Should(Succeed()) defer k8sClient.Delete(context.Background(), repository) key := types.NamespacedName{ Name: "helmchart-sample-" + randStringRunes(5), Namespace: namespace.Name, } chart := &sourcev1.HelmChart{ ObjectMeta: metav1.ObjectMeta{ Name: key.Name, Namespace: key.Namespace, }, Spec: sourcev1.HelmChartSpec{ Chart: "testdata/charts/helmchartwithdeps", Version: "*", SourceRef: sourcev1.LocalHelmChartSourceReference{ Kind: sourcev1.GitRepositoryKind, Name: repositoryKey.Name, }, Interval: metav1.Duration{Duration: pullInterval}, }, } Expect(k8sClient.Create(context.Background(), chart)).Should(Succeed()) defer k8sClient.Delete(context.Background(), chart) By("Expecting artifact") got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact != nil && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeFalse()) When("Setting valid valuesFiles attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFiles = []string{ "./testdata/charts/helmchartwithdeps/values.yaml", "./testdata/charts/helmchartwithdeps/override.yaml", } Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) When("Setting invalid valuesFiles attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFiles = []string{ "./testdata/charts/helmchartwithdeps/values.yaml", "./testdata/charts/helmchartwithdeps/invalid.yaml", } Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) When("Setting valid valuesFiles and valuesFile attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFile = "./testdata/charts/helmchartwithdeps/values.yaml" updated.Spec.ValuesFiles = []string{ "./testdata/charts/helmchartwithdeps/override.yaml", } Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(helmChart.Values["testDefault"]).To(BeTrue()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) When("Setting valid valuesFile attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFile = "./testdata/charts/helmchartwithdeps/override.yaml" updated.Spec.ValuesFiles = []string{} Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.Artifact.Checksum != updated.Status.Artifact.Checksum && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) _, exists := helmChart.Values["testDefault"] Expect(exists).To(BeFalse()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) When("Setting invalid valuesFile attribute", func() { updated := &sourcev1.HelmChart{} Expect(k8sClient.Get(context.Background(), key, updated)).To(Succeed()) updated.Spec.ValuesFile = "./testdata/charts/helmchartwithdeps/invalid.yaml" updated.Spec.ValuesFiles = []string{} Expect(k8sClient.Update(context.Background(), updated)).To(Succeed()) got := &sourcev1.HelmChart{} Eventually(func() bool { _ = k8sClient.Get(context.Background(), key, got) return got.Status.ObservedGeneration > updated.Status.ObservedGeneration && ginkgoTestStorage.ArtifactExist(*got.Status.Artifact) }, timeout, interval).Should(BeTrue()) f, err := os.Stat(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) Expect(f.Size()).To(BeNumerically(">", 0)) helmChart, err := loader.Load(ginkgoTestStorage.LocalPath(*got.Status.Artifact)) Expect(err).NotTo(HaveOccurred()) _, exists := helmChart.Values["testDefault"] Expect(exists).To(BeFalse()) Expect(helmChart.Values["testOverride"]).To(BeTrue()) }) }) }) })