diff --git a/.golangci.yml b/.golangci.yml index ee80ea33..b4b9d3b6 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -230,7 +230,7 @@ linters-settings: - ["call-chain", "loop", "method-call", "recover", "immediate-recover", "return"] - name: dot-imports arguments: - - { allowedPackages: ["github.com/onsi/ginkgo/v2","github.com/onsi/gomega"] } + - { allowedPackages: ["github.com/onsi/ginkgo/v2","github.com/onsi/gomega","sigs.k8s.io/controller-runtime/pkg/envtest/komega"] } - name: duplicated-imports - name: early-return - name: empty-block diff --git a/internal/controller/managedcluster_controller_test.go b/internal/controller/managedcluster_controller_test.go index f252e2e7..b423aa9e 100644 --- a/internal/controller/managedcluster_controller_test.go +++ b/internal/controller/managedcluster_controller_test.go @@ -19,6 +19,7 @@ import ( "time" hcv2 "github.com/fluxcd/helm-controller/api/v2" + sourcev1 "github.com/fluxcd/source-controller/api/v1" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" @@ -28,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" + . "sigs.k8s.io/controller-runtime/pkg/envtest/komega" "sigs.k8s.io/controller-runtime/pkg/reconcile" hmc "github.com/Mirantis/hmc/api/v1alpha1" @@ -42,6 +44,8 @@ var _ = Describe("ManagedCluster Controller", func() { templateName = "test-template" svcTemplateName = "test-svc-template" credentialName = "test-credential" + + helmChartURL = "http://source-controller.hmc-system.svc.cluster.local/helmchart/hmc-system/test-chart/0.1.0.tar.gz" ) ctx := context.Background() @@ -123,6 +127,11 @@ var _ = Describe("ManagedCluster Controller", func() { Expect(k8sClient.Create(ctx, svcTemplate)).To(Succeed()) svcTemplate.Status = hmc.ServiceTemplateStatus{ TemplateStatusCommon: hmc.TemplateStatusCommon{ + ChartRef: &hcv2.CrossNamespaceSourceReference{ + Kind: "HelmChart", + Name: "ref-test", + Namespace: "default", + }, TemplateValidationStatus: hmc.TemplateValidationStatus{ Valid: true, }, @@ -212,17 +221,102 @@ var _ = Describe("ManagedCluster Controller", func() { Expect(k8sClient.Delete(ctx, management)).To(Succeed()) Expect(k8sClient.Delete(ctx, namespace)).To(Succeed()) }) + It("should successfully reconcile the resource", func() { By("Reconciling the created resource") controllerReconciler := &ManagedClusterReconciler{ - Client: k8sClient, + Client: mgrClient, Config: &rest.Config{}, } + By("Ensure finalizer is added") _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ NamespacedName: typeNamespacedName, }) Expect(err).NotTo(HaveOccurred()) + Eventually(Object(managedCluster)).Should(SatisfyAll( + HaveField("Finalizers", ContainElement(hmc.ManagedClusterFinalizer)), + )) + + By("Reconciling resource with finalizer") + _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).To(HaveOccurred()) + Eventually(Object(managedCluster)).Should(SatisfyAll( + HaveField("Status.Conditions", ContainElement(SatisfyAll( + HaveField("Type", hmc.TemplateReadyCondition), + HaveField("Status", metav1.ConditionTrue), + HaveField("Reason", hmc.SucceededReason), + HaveField("Message", "Template is valid"), + ))), + )) + + By("Creating absent required resources: HelmChart, HelmRepository") + helmRepo := &sourcev1.HelmRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-repository", + Namespace: "default", + }, + Spec: sourcev1.HelmRepositorySpec{ + Insecure: true, + Interval: metav1.Duration{ + Duration: 10 * time.Minute, + }, + Provider: "generic", + Type: "oci", + URL: "oci://hmc-local-registry:5000/charts", + }, + } + Expect(k8sClient.Create(ctx, helmRepo)).To(Succeed()) + + helmChart := &sourcev1.HelmChart{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ref-test", + Namespace: "default", + }, + Spec: sourcev1.HelmChartSpec{ + Chart: "test", + Interval: metav1.Duration{ + Duration: 10 * time.Minute, + }, + ReconcileStrategy: sourcev1.ReconcileStrategyChartVersion, + SourceRef: sourcev1.LocalHelmChartSourceReference{ + Kind: "HelmRepository", + Name: helmRepo.Name, + }, + Version: "0.1.0", + }, + } + Expect(k8sClient.Create(ctx, helmChart)).To(Succeed()) + + _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).To(HaveOccurred()) + + By("Patching ClusterTemplate status") + Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(template), template)).Should(Succeed()) + template.Status.ChartRef = &hcv2.CrossNamespaceSourceReference{ + Kind: "HelmChart", + Name: "ref-test", + Namespace: "default", + } + Expect(k8sClient.Status().Update(ctx, template)).To(Succeed()) + + helmChart.Status.URL = helmChartURL + helmChart.Status.Artifact = &sourcev1.Artifact{ + URL: helmChartURL, + LastUpdateTime: metav1.Now(), + } + Expect(k8sClient.Status().Update(ctx, helmChart)).To(Succeed()) + + // todo: next error occurs due to dependency on helm library. The best way to mitigate this is to + // inject an interface into the reconciler struct that can be mocked out for testing. + _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).To(HaveOccurred()) }) }) }) diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index 18e71f83..a65cbcb9 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -40,6 +40,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" + . "sigs.k8s.io/controller-runtime/pkg/envtest/komega" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" @@ -147,6 +148,7 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) mgrClient = mgr.GetClient() Expect(mgrClient).NotTo(BeNil()) + SetClient(mgrClient) err = hmcmirantiscomv1alpha1.SetupIndexers(ctx, mgr) Expect(err).NotTo(HaveOccurred())