diff --git a/controllers/operandrequestnoolm/operandrequestnoolm_controller_test.go b/controllers/operandrequestnoolm/operandrequestnoolm_controller_test.go new file mode 100644 index 00000000..07d8473f --- /dev/null +++ b/controllers/operandrequestnoolm/operandrequestnoolm_controller_test.go @@ -0,0 +1,222 @@ +// +// Copyright 2022 IBM Corporation +// +// 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 operandrequestnoolm + +import ( + "context" + "fmt" + "sync" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + operatorv1alpha1 "github.com/IBM/operand-deployment-lifecycle-manager/v4/api/v1alpha1" + "github.com/IBM/operand-deployment-lifecycle-manager/v4/controllers/constant" + deploy "github.com/IBM/operand-deployment-lifecycle-manager/v4/controllers/operator" + "github.com/IBM/operand-deployment-lifecycle-manager/v4/controllers/testutil" +) + +var _ = Describe("OperandRequestNoOLM controller", func() { + const ( + name1 = "ibm-cloudpak-name" + namespace = "ibm-cloudpak" + registryName = "common-service" + registryNamespace = "data-ns" + ) + + var ( + ctx context.Context + + namespaceName string + registryNamespaceName string + registry *operatorv1alpha1.OperandRegistry + config *operatorv1alpha1.OperandConfig + request *operatorv1alpha1.OperandRequest + requestKey types.NamespacedName + ) + + BeforeEach(func() { + ctx = context.Background() + namespaceName = testutil.CreateNSName(namespace) + registryNamespaceName = testutil.CreateNSName(registryNamespace) + registry = testutil.OperandRegistryObj(registryName, registryNamespaceName, registryNamespaceName) + config = testutil.OperandConfigObj(registryName, registryNamespaceName) + request = testutil.OperandRequestObj(registryName, registryNamespaceName, name1, namespaceName) + requestKey = types.NamespacedName{Name: name1, Namespace: namespaceName} + + By("Creating the Namespace") + Expect(k8sClient.Create(ctx, testutil.NamespaceObj(namespaceName))).Should(Succeed()) + Expect(k8sClient.Create(ctx, testutil.NamespaceObj(registryNamespaceName))).Should(Succeed()) + }) + + Context("Reconciling OperandRequest without OLM", func() { + It("Should add finalizer to OperandRequest", func() { + By("Creating the OperandRegistry") + Expect(k8sClient.Create(ctx, registry)).Should(Succeed()) + By("Creating the OperandConfig") + Expect(k8sClient.Create(ctx, config)).Should(Succeed()) + By("Creating the OperandRequest") + Expect(k8sClient.Create(ctx, request)).Should(Succeed()) + + By("Checking finalizer is added to the OperandRequest") + Eventually(func() bool { + instance := &operatorv1alpha1.OperandRequest{} + Expect(k8sClient.Get(ctx, requestKey, instance)).Should(Succeed()) + return len(instance.GetFinalizers()) > 0 + }, testutil.Timeout, testutil.Interval).Should(BeTrue()) + }) + + It("Should handle permissions check correctly", func() { + // Create a mock reconciler with a fake client + fakeClient := fake.NewClientBuilder().Build() + reconciler := &Reconciler{ + ODLMOperator: &deploy.ODLMOperator{ + Client: fakeClient, + }, + Mutex: sync.Mutex{}, + } + + // Test permission check + By("Verifying no-permission case") + hasPermission := reconciler.checkPermission(ctx, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "test-request", + Namespace: "test-namespace", + }, + }) + Expect(hasPermission).To(BeFalse()) + }) + + It("Should initialize request status", func() { + By("Creating the OperandRegistry") + Expect(k8sClient.Create(ctx, registry)).Should(Succeed()) + By("Creating the OperandConfig") + Expect(k8sClient.Create(ctx, config)).Should(Succeed()) + By("Creating the OperandRequest") + Expect(k8sClient.Create(ctx, request)).Should(Succeed()) + + By("Checking status is initialized") + Eventually(func() bool { + instance := &operatorv1alpha1.OperandRequest{} + Expect(k8sClient.Get(ctx, requestKey, instance)).Should(Succeed()) + return instance.Status.Phase != "" + }, testutil.Timeout, testutil.Interval).Should(BeTrue()) + }) + + It("Should handle deletion correctly", func() { + By("Creating the OperandRegistry") + Expect(k8sClient.Create(ctx, registry)).Should(Succeed()) + By("Creating the OperandConfig") + Expect(k8sClient.Create(ctx, config)).Should(Succeed()) + By("Creating the OperandRequest") + Expect(k8sClient.Create(ctx, request)).Should(Succeed()) + + By("Deleting the OperandRequest") + Expect(k8sClient.Delete(ctx, request)).Should(Succeed()) + + By("Verifying finalizer is removed") + Eventually(func() bool { + instance := &operatorv1alpha1.OperandRequest{} + err := k8sClient.Get(ctx, requestKey, instance) + return errors.IsNotFound(err) + }, testutil.Timeout, testutil.Interval).Should(BeTrue()) + }) + }) + + Context("Testing mapper functions", func() { + It("Should map OperandRegistry to OperandRequests", func() { + By("Creating the OperandRegistry") + Expect(k8sClient.Create(ctx, registry)).Should(Succeed()) + By("Creating the OperandConfig") + Expect(k8sClient.Create(ctx, config)).Should(Succeed()) + By("Creating the OperandRequest") + Expect(k8sClient.Create(ctx, request)).Should(Succeed()) + + reconciler := &Reconciler{ + ODLMOperator: &deploy.ODLMOperator{ + Client: k8sClient, + }, + } + + By("Verifying getRegistryToRequestMapper works") + mapper := reconciler.getRegistryToRequestMapper() + requests := mapper(registry) + Expect(len(requests)).To(BeNumerically(">", 0)) + }) + + It("Should map OperandConfig to OperandRequests", func() { + By("Creating the OperandRegistry") + Expect(k8sClient.Create(ctx, registry)).Should(Succeed()) + By("Creating the OperandConfig") + Expect(k8sClient.Create(ctx, config)).Should(Succeed()) + By("Creating the OperandRequest") + Expect(k8sClient.Create(ctx, request)).Should(Succeed()) + + reconciler := &Reconciler{ + ODLMOperator: &deploy.ODLMOperator{ + Client: k8sClient, + }, + } + + By("Verifying getConfigToRequestMapper works") + mapper := reconciler.getConfigToRequestMapper() + requests := mapper(config) + Expect(len(requests)).To(BeNumerically(">", 0)) + }) + + It("Should map references to OperandRequests", func() { + By("Creating the OperandRegistry") + Expect(k8sClient.Create(ctx, registry)).Should(Succeed()) + By("Creating the OperandConfig") + Expect(k8sClient.Create(ctx, config)).Should(Succeed()) + By("Creating the OperandRequest") + Expect(k8sClient.Create(ctx, request)).Should(Succeed()) + + By("Creating a ConfigMap with reference annotation") + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-configmap", + Namespace: registryNamespaceName, + Annotations: map[string]string{ + constant.ODLMReferenceAnnotation: fmt.Sprintf("OperandRegistry.%s.%s", registryNamespaceName, registryName), + }, + }, + Data: map[string]string{ + "test-key": "test-value", + }, + } + Expect(k8sClient.Create(ctx, cm)).Should(Succeed()) + + reconciler := &Reconciler{ + ODLMOperator: &deploy.ODLMOperator{ + Client: k8sClient, + }, + } + + By("Verifying getReferenceToRequestMapper works") + mapper := reconciler.getReferenceToRequestMapper() + requests := mapper(cm) + Expect(len(requests)).To(BeNumerically(">", 0)) + }) + }) +}) diff --git a/controllers/operandrequestnoolm/operandrequestnoolm_suite_test.go b/controllers/operandrequestnoolm/operandrequestnoolm_suite_test.go new file mode 100644 index 00000000..43acfcc1 --- /dev/null +++ b/controllers/operandrequestnoolm/operandrequestnoolm_suite_test.go @@ -0,0 +1,132 @@ +// +// Copyright 2022 IBM Corporation +// +// 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 operandrequestnoolm + +import ( + "os" + "path/filepath" + "testing" + "time" + + jaegerv1 "github.com/jaegertracing/jaeger-operator/apis/v1" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" + olmv1 "github.com/operator-framework/api/pkg/operators/v1" + olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" + operatorsv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + nssv1 "github.com/IBM/ibm-namespace-scope-operator/api/v1" + apiv1alpha1 "github.com/IBM/operand-deployment-lifecycle-manager/v4/api/v1alpha1" + deploy "github.com/IBM/operand-deployment-lifecycle-manager/v4/controllers/operator" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +const useExistingCluster = "USE_EXISTING_CLUSTER" + +var ( + cfg *rest.Config + k8sClient client.Client + testEnv *envtest.Environment +) + +func TestOperanRequestNoOLM(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, + "OperandRequest NoOLM Controller Suite") +} + +var _ = BeforeSuite(func(done Done) { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + UseExistingCluster: UseExistingCluster(), + CRDDirectoryPaths: []string{filepath.Join("../..", "config", "crd", "bases"), filepath.Join("../..", "testcrds")}, + } + + var err error + cfg, err = testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(cfg).ToNot(BeNil()) + + err = apiv1alpha1.AddToScheme(clientgoscheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + // +kubebuilder:scaffold:scheme + + err = nssv1.AddToScheme(clientgoscheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + err = olmv1alpha1.AddToScheme(clientgoscheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + err = olmv1.AddToScheme(clientgoscheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + err = jaegerv1.AddToScheme(clientgoscheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + err = operatorsv1.AddToScheme(clientgoscheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + k8sClient, err = client.New(cfg, client.Options{Scheme: clientgoscheme.Scheme}) + Expect(err).ToNot(HaveOccurred()) + Expect(k8sClient).ToNot(BeNil()) + + // Start your controllers test logic + k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: clientgoscheme.Scheme, + MetricsBindAddress: "0", + }) + Expect(err).ToNot(HaveOccurred()) + + // Setup Manager with OperandRequestNoOLM Controller + err = (&Reconciler{ + ODLMOperator: deploy.NewODLMOperator(k8sManager, "OperandRequestNoOLM"), + StepSize: 3, + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + + go func() { + err = k8sManager.Start(ctrl.SetupSignalHandler()) + Expect(err).ToNot(HaveOccurred()) + }() + + close(done) +}, 600) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + gexec.KillAndWait(5 * time.Second) + err := testEnv.Stop() + Expect(err).ToNot(HaveOccurred()) +}) + +func UseExistingCluster() *bool { + use := false + if os.Getenv(useExistingCluster) != "" && os.Getenv(useExistingCluster) == "true" { + use = true + } + return &use +}