diff --git a/controllers/helmchart_controller.go b/controllers/helmchart_controller.go index b47bbce8..b726ff36 100644 --- a/controllers/helmchart_controller.go +++ b/controllers/helmchart_controller.go @@ -82,7 +82,6 @@ func (r *HelmChartReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { chart = sourcev1.HelmChartNotReady(*chart.DeepCopy(), sourcev1.ChartPullFailedReason, err.Error()) if err := r.Status().Update(ctx, &chart); err != nil { log.Error(err, "unable to update HelmChart status") - return ctrl.Result{Requeue: true}, err } return ctrl.Result{Requeue: true}, err } @@ -97,6 +96,10 @@ func (r *HelmChartReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { pulledChart, err := r.sync(repository, *chart.DeepCopy()) if err != nil { log.Error(err, "Helm chart sync failed") + if err := r.Status().Update(ctx, &pulledChart); err != nil { + log.Error(err, "unable to update HelmChart status") + } + return ctrl.Result{Requeue: true}, err } // update status @@ -156,8 +159,6 @@ func (r *HelmChartReconciler) sync(repository sourcev1.HelmRepository, chart sou return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err } - // TODO(hidde): auth options from Helm repository probably need to be - // substituted here c, err := r.Getters.ByScheme(u.Scheme) if err != nil { return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err @@ -268,7 +269,7 @@ func (r *HelmChartReconciler) shouldResetStatus(chart sourcev1.HelmChart) (bool, } // set initial status - if len(chart.Status.Conditions) == 0 || resetStatus { + if len(chart.Status.Conditions) == 0 { resetStatus = true } diff --git a/controllers/helmchart_controller_test.go b/controllers/helmchart_controller_test.go new file mode 100644 index 00000000..72ae653e --- /dev/null +++ b/controllers/helmchart_controller_test.go @@ -0,0 +1,367 @@ +/* +Copyright 2020 The Flux CD contributors. + +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" + "net/http" + "os" + "path" + "strings" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + + sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1" + "github.com/fluxcd/source-controller/internal/testserver" +) + +var _ = Describe("HelmChartReconciler", func() { + + const ( + timeout = time.Second * 30 + interval = time.Second * 1 + indexInterval = time.Second * 2 + pullInterval = time.Second * 3 + ) + + Context("HelmChart", func() { + var ( + namespace *corev1.Namespace + helmServer *testserver.Helm + 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 = testserver.NewTempHelmServer() + Expect(err).To(Succeed()) + helmServer.Start() + }) + + AfterEach(func() { + os.RemoveAll(helmServer.Root()) + helmServer.Stop() + + 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/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{ + Name: "helmchart", + Version: "*", + HelmRepositoryRef: corev1.LocalObjectReference{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 && storage.ArtifactExist(*got.Status.Artifact) + }, timeout, interval).Should(BeTrue()) + + By("Packaging a new chart version and regenerating the index") + Expect(helmServer.PackageChartWithVersion(path.Join("testdata/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 && + !storage.ArtifactExist(*got.Status.Artifact) + }, timeout, interval).Should(BeTrue()) + + By("Expecting missing HelmRepository error") + updated := &sourcev1.HelmChart{} + Expect(k8sClient.Get(context.Background(), key, updated)).Should(Succeed()) + updated.Spec.HelmRepositoryRef.Name = "invalid" + 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 get HelmRepository") { + 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) + }).ShouldNot(Succeed()) + + By("Expecting GC on delete") + Eventually(storage.ArtifactExist(*got.Status.Artifact), 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/helmchart"), versions[k])).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: 1 * time.Hour}, + }, + })).Should(Succeed()) + + 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{ + Name: "helmchart", + Version: "*", + HelmRepositoryRef: corev1.LocalObjectReference{Name: repositoryKey.Name}, + Interval: metav1.Duration{Duration: 1 * time.Hour}, + }, + } + Expect(k8sClient.Create(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("1.0.0")) + + chart.Spec.Version = "~0.1.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.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.PackageChart(path.Join("testdata/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, + }, + Data: map[string][]byte{ + "username": []byte(username), + "password": []byte(password), + }, + } + Expect(k8sClient.Create(context.Background(), secret)).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(), + SecretRef: &corev1.LocalObjectReference{ + Name: secretKey.Name, + }, + Interval: metav1.Duration{Duration: time.Hour * 1}, + }, + } + Expect(k8sClient.Create(context.Background(), repository)).Should(Succeed()) + + 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{ + Name: "helmchart", + Version: "*", + HelmRepositoryRef: corev1.LocalObjectReference{Name: repositoryKey.Name}, + Interval: metav1.Duration{Duration: pullInterval}, + }, + } + Expect(k8sClient.Create(context.Background(), chart)).Should(Succeed()) + + By("Expecting artifact") + Expect(k8sClient.Update(context.Background(), secret)).Should(Succeed()) + Eventually(func() bool { + got := &sourcev1.HelmChart{} + _ = k8sClient.Get(context.Background(), key, got) + return got.Status.Artifact != nil && + storage.ArtifactExist(*got.Status.Artifact) + }, timeout, interval).Should(BeTrue()) + + delete(secret.Data, "username") + Expect(k8sClient.Update(context.Background(), secret)).Should(Succeed()) + + By("Expecting missing field error") + delete(secret.Data, "username") + Expect(k8sClient.Update(context.Background(), secret)).Should(Succeed()) + got := &sourcev1.HelmChart{} + 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()) + Expect(got.Status.Artifact).ToNot(BeNil()) + + delete(secret.Data, "password") + Expect(k8sClient.Update(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("Expecting missing secret error") + Expect(k8sClient.Delete(context.Background(), secret)).Should(Succeed()) + 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()) + Expect(got.Status.Artifact).ShouldNot(BeNil()) + }) + }) +}) diff --git a/controllers/helmrepository_controller.go b/controllers/helmrepository_controller.go index b263de68..15f24ec2 100644 --- a/controllers/helmrepository_controller.go +++ b/controllers/helmrepository_controller.go @@ -160,6 +160,7 @@ func (r *HelmRepositoryReconciler) sync(repository sourcev1.HelmRepository) (sou if err := yaml.Unmarshal(data, i); err != nil { return sourcev1.HelmRepositoryNotReady(repository, sourcev1.IndexationFailedReason, err.Error()), err } + i.SortEntries() index, err := yaml.Marshal(i) if err != nil { diff --git a/controllers/helmrepository_controller_test.go b/controllers/helmrepository_controller_test.go index e53d8ed6..3e7bab12 100644 --- a/controllers/helmrepository_controller_test.go +++ b/controllers/helmrepository_controller_test.go @@ -37,35 +37,39 @@ import ( var _ = Describe("HelmRepositoryReconciler", func() { const ( - timeout = time.Second * 30 - interval = time.Second * 1 + timeout = time.Second * 30 + interval = time.Second * 1 + indexInterval = time.Second * 2 ) - var ( - namespace *corev1.Namespace - helmServer *testserver.Helm - err error - ) - - BeforeEach(func() { - namespace = &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: "helm-repository-" + randStringRunes(5)}, - } - err = k8sClient.Create(context.Background(), namespace) - Expect(err).NotTo(HaveOccurred(), "failed to create test namespace") - }) - - AfterEach(func() { - err = k8sClient.Delete(context.Background(), namespace) - Expect(err).NotTo(HaveOccurred(), "failed to delete test namespace") - }) - Context("HelmRepository", func() { - It("Creates artifacts for", func() { + var ( + namespace *corev1.Namespace + helmServer *testserver.Helm + err error + ) + + BeforeEach(func() { + namespace = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: "helm-repository-" + randStringRunes(5)}, + } + err = k8sClient.Create(context.Background(), namespace) + Expect(err).NotTo(HaveOccurred(), "failed to create test namespace") + helmServer, err = testserver.NewTempHelmServer() Expect(err).To(Succeed()) - defer os.RemoveAll(helmServer.Root()) - defer helmServer.Stop() + }) + + AfterEach(func() { + os.RemoveAll(helmServer.Root()) + helmServer.Stop() + + Eventually(func() error { + return k8sClient.Delete(context.Background(), namespace) + }, timeout, interval).Should(Succeed(), "failed to delete test namespace") + }) + + It("Creates artifacts for", func() { helmServer.Start() Expect(helmServer.PackageChart(path.Join("testdata/helmchart"))).Should(Succeed()) @@ -82,7 +86,7 @@ var _ = Describe("HelmRepositoryReconciler", func() { }, Spec: sourcev1.HelmRepositorySpec{ URL: helmServer.URL(), - Interval: metav1.Duration{Duration: interval}, + Interval: metav1.Duration{Duration: indexInterval}, }, } Expect(k8sClient.Create(context.Background(), created)).Should(Succeed()) @@ -97,6 +101,8 @@ var _ = Describe("HelmRepositoryReconciler", func() { By("Updating the chart index") // Regenerating the index is sufficient to make the revision change Expect(helmServer.GenerateIndex()).Should(Succeed()) + + By("Expecting revision change and GC") Eventually(func() bool { now := &sourcev1.HelmRepository{} _ = k8sClient.Get(context.Background(), key, now) @@ -132,6 +138,8 @@ var _ = Describe("HelmRepositoryReconciler", func() { r := &sourcev1.HelmRepository{} return k8sClient.Get(context.Background(), key, r) }).ShouldNot(Succeed()) + + By("Expecting GC after delete") Eventually(storage.ArtifactExist(*got.Status.Artifact), timeout, interval).ShouldNot(BeTrue()) }) @@ -184,7 +192,7 @@ var _ = Describe("HelmRepositoryReconciler", func() { SecretRef: &corev1.LocalObjectReference{ Name: secretKey.Name, }, - Interval: metav1.Duration{Duration: interval}, + Interval: metav1.Duration{Duration: indexInterval}, }, } Expect(k8sClient.Create(context.Background(), created)).Should(Succeed()) @@ -240,101 +248,97 @@ var _ = Describe("HelmRepositoryReconciler", func() { }, timeout, interval).Should(BeTrue()) Expect(got.Status.Artifact).ShouldNot(BeNil()) }) - }) - It("Authenticates when TLS credentials are provided", func() { - helmServer, err = testserver.NewTempHelmServer() - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(helmServer.Root()) - defer helmServer.Stop() - err = helmServer.StartTLS(examplePublicKey, examplePrivateKey, exampleCA) - Expect(err).NotTo(HaveOccurred()) + It("Authenticates when TLS credentials are provided", func() { + err = helmServer.StartTLS(examplePublicKey, examplePrivateKey, exampleCA) + Expect(err).NotTo(HaveOccurred()) - Expect(helmServer.PackageChart(path.Join("testdata/helmchart"))).Should(Succeed()) - Expect(helmServer.GenerateIndex()).Should(Succeed()) + Expect(helmServer.PackageChart(path.Join("testdata/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, - }, - Data: map[string][]byte{}, - } - Expect(k8sClient.Create(context.Background(), secret)).Should(Succeed()) - - key := types.NamespacedName{ - Name: "helmrepository-sample-" + randStringRunes(5), - Namespace: namespace.Name, - } - created := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: key.Name, - Namespace: key.Namespace, - }, - Spec: sourcev1.HelmRepositorySpec{ - URL: helmServer.URL(), - SecretRef: &corev1.LocalObjectReference{ - Name: secretKey.Name, + secretKey := types.NamespacedName{ + Name: "helmrepository-auth-" + randStringRunes(5), + Namespace: namespace.Name, + } + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretKey.Name, + Namespace: secretKey.Namespace, }, - Interval: metav1.Duration{Duration: interval}, - }, - } - Expect(k8sClient.Create(context.Background(), created)).Should(Succeed()) - - By("Expecting unknown authority error") - Eventually(func() bool { - got := &sourcev1.HelmRepository{} - _ = k8sClient.Get(context.Background(), key, got) - for _, c := range got.Status.Conditions { - if c.Reason == sourcev1.IndexationFailedReason && - strings.Contains(c.Message, "certificate signed by unknown authority") { - return true - } + Data: map[string][]byte{}, } - return false - }, timeout, interval).Should(BeTrue()) + Expect(k8sClient.Create(context.Background(), secret)).Should(Succeed()) - By("Expecting missing field error") - secret.Data["certFile"] = examplePublicKey - secret.Data["keyFile"] = examplePrivateKey - Expect(k8sClient.Update(context.Background(), secret)).Should(Succeed()) - Eventually(func() bool { - got := &sourcev1.HelmRepository{} - _ = k8sClient.Get(context.Background(), key, got) - for _, c := range got.Status.Conditions { - if c.Reason == sourcev1.AuthenticationFailedReason { - return true - } + key := types.NamespacedName{ + Name: "helmrepository-sample-" + randStringRunes(5), + Namespace: namespace.Name, } - return false - }, timeout, interval).Should(BeTrue()) - - By("Expecting artifact") - secret.Data["caFile"] = exampleCA - Expect(k8sClient.Update(context.Background(), secret)).Should(Succeed()) - Eventually(func() bool { - got := &sourcev1.HelmRepository{} - _ = k8sClient.Get(context.Background(), key, got) - return got.Status.Artifact != nil && - storage.ArtifactExist(*got.Status.Artifact) - }, timeout, interval).Should(BeTrue()) - - By("Expecting missing secret error") - Expect(k8sClient.Delete(context.Background(), secret)).Should(Succeed()) - got := &sourcev1.HelmRepository{} - Eventually(func() bool { - _ = k8sClient.Get(context.Background(), key, got) - for _, c := range got.Status.Conditions { - if c.Reason == sourcev1.AuthenticationFailedReason { - return true - } + created := &sourcev1.HelmRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: key.Name, + Namespace: key.Namespace, + }, + Spec: sourcev1.HelmRepositorySpec{ + URL: helmServer.URL(), + SecretRef: &corev1.LocalObjectReference{ + Name: secretKey.Name, + }, + Interval: metav1.Duration{Duration: indexInterval}, + }, } - return false - }, timeout, interval).Should(BeTrue()) - Expect(got.Status.Artifact).ShouldNot(BeNil()) + Expect(k8sClient.Create(context.Background(), created)).Should(Succeed()) + + By("Expecting unknown authority error") + Eventually(func() bool { + got := &sourcev1.HelmRepository{} + _ = k8sClient.Get(context.Background(), key, got) + for _, c := range got.Status.Conditions { + if c.Reason == sourcev1.IndexationFailedReason && + strings.Contains(c.Message, "certificate signed by unknown authority") { + return true + } + } + return false + }, timeout, interval).Should(BeTrue()) + + By("Expecting missing field error") + secret.Data["certFile"] = examplePublicKey + secret.Data["keyFile"] = examplePrivateKey + Expect(k8sClient.Update(context.Background(), secret)).Should(Succeed()) + Eventually(func() bool { + got := &sourcev1.HelmRepository{} + _ = 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("Expecting artifact") + secret.Data["caFile"] = exampleCA + Expect(k8sClient.Update(context.Background(), secret)).Should(Succeed()) + Eventually(func() bool { + got := &sourcev1.HelmRepository{} + _ = k8sClient.Get(context.Background(), key, got) + return got.Status.Artifact != nil && + storage.ArtifactExist(*got.Status.Artifact) + }, timeout, interval).Should(BeTrue()) + + By("Expecting missing secret error") + Expect(k8sClient.Delete(context.Background(), secret)).Should(Succeed()) + got := &sourcev1.HelmRepository{} + 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()) + Expect(got.Status.Artifact).ShouldNot(BeNil()) + }) }) }) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 4fa3bfd6..943ea157 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -117,6 +117,18 @@ var _ = BeforeSuite(func(done Done) { }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred(), "failed to setup HelmRepositoryReconciler") + err = (&HelmChartReconciler{ + Client: k8sManager.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("HelmChart"), + Scheme: scheme.Scheme, + Storage: storage, + Getters: getter.Providers{getter.Provider{ + Schemes: []string{"http", "https"}, + New: getter.NewHTTPGetter, + }}, + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred(), "failed to setup HelmChartReconciler") + go func() { err = k8sManager.Start(ctrl.SetupSignalHandler()) Expect(err).ToNot(HaveOccurred()) diff --git a/internal/testserver/helm.go b/internal/testserver/helm.go index 9ffc4055..db0ddf5e 100644 --- a/internal/testserver/helm.go +++ b/internal/testserver/helm.go @@ -36,8 +36,13 @@ func (s *Helm) GenerateIndex() error { } func (s *Helm) PackageChart(path string) error { + return s.PackageChartWithVersion(path, "") +} + +func (s *Helm) PackageChartWithVersion(path, version string) error { pkg := action.NewPackage() pkg.Destination = s.HTTP.docroot + pkg.Version = version _, err := pkg.Run(path, nil) return err } diff --git a/internal/testserver/http.go b/internal/testserver/http.go index 865820f4..95f68dee 100644 --- a/internal/testserver/http.go +++ b/internal/testserver/http.go @@ -89,6 +89,7 @@ func (s *HTTP) Stop() { func (s *HTTP) Root() string { return s.docroot } + func (s *HTTP) URL() string { if s.server != nil { return s.server.URL