diff --git a/controllers/drupalsite_controller_test.go b/controllers/drupalsite_controller_test.go index 37301b69819a75054725423c0666913cb5be48ce..98ed0e631455250255c2a5bea87b55349b6e6baf 100644 --- a/controllers/drupalsite_controller_test.go +++ b/controllers/drupalsite_controller_test.go @@ -1,1346 +1,1349 @@ -/* -Copyright 2021 CERN. - -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" - "crypto/md5" - "encoding/hex" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - buildv1 "github.com/openshift/api/build/v1" - imagev1 "github.com/openshift/api/image/v1" - routev1 "github.com/openshift/api/route/v1" - "github.com/operator-framework/operator-lib/status" - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - dbodv1a1 "gitlab.cern.ch/drupal/paas/dbod-operator/api/v1alpha1" - drupalwebservicesv1alpha1 "gitlab.cern.ch/drupal/paas/drupalsite-operator/api/v1alpha1" - authz "gitlab.cern.ch/paas-tools/operators/authz-operator/api/v1alpha1" - appsv1 "k8s.io/api/apps/v1" - batchv1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" -) - -// Ginkgo makes it easy to write expressive specs that describe the behavior of your code in an organized manner. -// Describe and Context containers are used organize the tests and It is used to define the test result -// Describe is a top-level container that defines the test -// Context defines the circumstance i.e parameters or specifications -// It defines what the test result should be -// By defines smaller tests under a given it and in case of failure, the 'By' text is printed out -// Ref: http://onsi.github.io/ginkgo/ - -const dummySiteUrl = "testsite.webtest.cern.ch" - -var _ = Describe("DrupalSite controller", func() { - const ( - Name = "test" - Namespace = "default" - - veleroNamespace = "openshift-cern-drupal" - timeout = time.Second * 30 - duration = time.Second * 30 - interval = time.Millisecond * 250 - ) - var ( - drupalSiteObject = &drupalwebservicesv1alpha1.DrupalSite{} + /* + Copyright 2021 CERN. + + 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" + "crypto/md5" + "encoding/hex" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + buildv1 "github.com/openshift/api/build/v1" + imagev1 "github.com/openshift/api/image/v1" + routev1 "github.com/openshift/api/route/v1" + "github.com/operator-framework/operator-lib/status" + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + dbodv1a1 "gitlab.cern.ch/drupal/paas/dbod-operator/api/v1alpha1" + drupalwebservicesv1alpha1 "gitlab.cern.ch/drupal/paas/drupalsite-operator/api/v1alpha1" + authz "gitlab.cern.ch/paas-tools/operators/authz-operator/api/v1alpha1" + appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" ) - ctx := context.Background() - key := types.NamespacedName{ - Name: Name, - Namespace: Namespace, - } - - BeforeEach(func() { - drupalSiteObject = &drupalwebservicesv1alpha1.DrupalSite{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "drupal.webservices.cern.ch/v1alpha1", - Kind: "DrupalSite", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: key.Name, - Namespace: key.Namespace, - Labels: map[string]string{ - "drupal.webservices.cern.ch/isPrimary": "true", - }, - }, - Spec: drupalwebservicesv1alpha1.DrupalSiteSpec{ - Version: drupalwebservicesv1alpha1.Version{ - Name: "v9.3-2", - ReleaseSpec: "stable", - }, - Configuration: drupalwebservicesv1alpha1.Configuration{ - DiskSize: "10Gi", - QoSClass: drupalwebservicesv1alpha1.QoSStandard, - DatabaseClass: drupalwebservicesv1alpha1.DBODStandard, - }, - SiteURL: []drupalwebservicesv1alpha1.Url{ - "test-1.webtest.cern.ch", - "test-2.webtest.cern.ch", - "test-3.webtest.cern.ch", - }, - }, + // Ginkgo makes it easy to write expressive specs that describe the behavior of your code in an organized manner. + // Describe and Context containers are used organize the tests and It is used to define the test result + // Describe is a top-level container that defines the test + // Context defines the circumstance i.e parameters or specifications + // It defines what the test result should be + // By defines smaller tests under a given it and in case of failure, the 'By' text is printed out + // Ref: http://onsi.github.io/ginkgo/ + + const dummySiteUrl = "testsite.webtest.cern.ch" + + var _ = Describe("DrupalSite controller", func() { + const ( + Name = "test" + Namespace = "default" + + veleroNamespace = "openshift-cern-drupal" + timeout = time.Second * 30 + duration = time.Second * 30 + interval = time.Millisecond * 250 + ) + var ( + drupalSiteObject = &drupalwebservicesv1alpha1.DrupalSite{} + ) + + ctx := context.Background() + key := types.NamespacedName{ + Name: Name, + Namespace: Namespace, } - }) - Describe("Creating drupalSite object", func() { - Context("With basic spec", func() { - It("All dependent resources should be created", func() { - By("By creating a namespace for velero backup resources") - Eventually(func() error { - return k8sClient.Create(ctx, &corev1.Namespace{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Namespace", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: veleroNamespace, - }, - }) - }, timeout, interval).Should(Succeed()) - - By("By creating a new drupalSite") - Eventually(func() error { - return k8sClient.Create(ctx, drupalSiteObject) - }, timeout, interval).Should(Succeed()) - - By("Expecting drupalSite object created") - cr := drupalwebservicesv1alpha1.DrupalSite{} - Eventually(func() error { - return k8sClient.Get(ctx, key, &cr) - }, timeout, interval).Should(Succeed()) - trueVar := true - expectedOwnerReference := metav1.OwnerReference{ + BeforeEach(func() { + drupalSiteObject = &drupalwebservicesv1alpha1.DrupalSite{ + TypeMeta: metav1.TypeMeta{ APIVersion: "drupal.webservices.cern.ch/v1alpha1", Kind: "DrupalSite", - Name: Name, - UID: cr.UID, - Controller: &trueVar, - } - configmap := corev1.ConfigMap{} - svc := corev1.Service{} - pvc := corev1.PersistentVolumeClaim{} - job := batchv1.Job{} - deploy := appsv1.Deployment{} - dbod := dbodv1a1.Database{} - route := routev1.Route{} - oidcReturnUri := authz.OidcReturnURI{} - schedule := velerov1.Schedule{} - - // Check DBOD resource creation - By("Expecting Database resource created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &dbod) - return dbod.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - - // Update DBOD resource status field - By("Updating DBOD instance in Database resource status") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &dbod) - dbod.Status.DbodInstance = "test" - return k8sClient.Status().Update(ctx, &dbod) - }, timeout, interval).Should(Succeed()) - - //By("Expecting the drupal deployment to have the EnvFrom secret field set correctly") - By("Expecting the drupal deployment to have at least 2 containers") - Eventually(func() bool { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) - return len(deploy.Spec.Template.Spec.Containers) >= 2 - }, timeout, interval).Should(BeTrue()) - - // Check PHP-FPM configMap creation - By("Expecting PHP_FPM configmaps created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "php-fpm-" + key.Name, Namespace: key.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - - // Check Nginx configMap creation - By("Expecting Nginx configmaps created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "nginx-" + key.Name, Namespace: key.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - - // Check Site settings configMap creation - By("Expecting Site settings configmaps created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "site-settings-" + key.Name, Namespace: key.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - - // Check PHP Cli configMap creation - By("Expecting PHP Cli configmaps created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "php-cli-config-" + key.Name, Namespace: key.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - - // Check Drupal service - By("Expecting Drupal service created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &svc) - return svc.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - - // Check drupal persistentVolumeClaim - By("Expecting drupal persistentVolumeClaim created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "pv-claim-" + key.Name, Namespace: key.Namespace}, &pvc) - return pvc.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + }, + ObjectMeta: metav1.ObjectMeta{ + Name: key.Name, + Namespace: key.Namespace, + Labels: map[string]string{ + "drupal.webservices.cern.ch/isPrimary": "true", + }, + }, + Spec: drupalwebservicesv1alpha1.DrupalSiteSpec{ + Version: drupalwebservicesv1alpha1.Version{ + Name: "v9.3-2", + ReleaseSpec: "stable", + }, + Configuration: drupalwebservicesv1alpha1.Configuration{ + DiskSize: "10Gi", + QoSClass: drupalwebservicesv1alpha1.QoSStandard, + DatabaseClass: drupalwebservicesv1alpha1.DBODStandard, + }, + SiteURL: []drupalwebservicesv1alpha1.Url{ + "test-1.webtest.cern.ch", + "test-2.webtest.cern.ch", + "test-3.webtest.cern.ch", + }, + }, + } + }) - // Check Drupal deployments - By("Expecting Drupal deployments created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) - return deploy.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + Describe("Creating drupalSite object", func() { + Context("With basic spec", func() { + It("All dependent resources should be created", func() { + By("By creating a namespace for velero backup resources") + Eventually(func() error { + return k8sClient.Create(ctx, &corev1.Namespace{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: veleroNamespace, + }, + }) + }, timeout, interval).Should(Succeed()) - // Check Drush job - By("Expecting Drush job created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "ensure-site-install-" + key.Name, Namespace: key.Namespace}, &job) - return job.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + By("By creating a new drupalSite") + Eventually(func() error { + return k8sClient.Create(ctx, drupalSiteObject) + }, timeout, interval).Should(Succeed()) - // Update drupalSite custom resource status fields to allow route conditions - By("Updating 'initialized' status field in drupalSite resource") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) - cr.Status.Conditions.SetCondition(status.Condition{Type: "Initialized", Status: "True"}) - return k8sClient.Status().Update(ctx, &cr) - }, timeout, interval).Should(Succeed()) + By("Expecting drupalSite object created") + cr := drupalwebservicesv1alpha1.DrupalSite{} + Eventually(func() error { + return k8sClient.Get(ctx, key, &cr) + }, timeout, interval).Should(Succeed()) + trueVar := true + expectedOwnerReference := metav1.OwnerReference{ + APIVersion: "drupal.webservices.cern.ch/v1alpha1", + Kind: "DrupalSite", + Name: Name, + UID: cr.UID, + Controller: &trueVar, + } + configmap := corev1.ConfigMap{} + svc := corev1.Service{} + pvc := corev1.PersistentVolumeClaim{} + job := batchv1.Job{} + deploy := appsv1.Deployment{} + dbod := dbodv1a1.Database{} + route := routev1.Route{} + oidcReturnUri := authz.OidcReturnURI{} + schedule := velerov1.Schedule{} + + // Check DBOD resource creation + By("Expecting Database resource created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &dbod) + return dbod.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Update deployment status fields to allow 'ready' status field to be set on the drupalSite resource - By("Updating 'ReadyReplicas' and 'AvailableReplicas' status fields in deployment resource") - Eventually(func() error { - k8sClient.Get(ctx, key, &deploy) - deploy.Status.Replicas = 1 - deploy.Status.AvailableReplicas = 1 - deploy.Status.ReadyReplicas = 1 - return k8sClient.Status().Update(ctx, &deploy) - }, timeout, interval).Should(Succeed()) + // Update DBOD resource status field + By("Updating DBOD instance in Database resource status") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &dbod) + dbod.Status.DbodInstance = "test" + return k8sClient.Status().Update(ctx, &dbod) + }, timeout, interval).Should(Succeed()) - // Check if the Schedule resource is created - By("Expecting Schedule to be created") - Eventually(func() error { - return k8sClient.Get(ctx, types.NamespacedName{Name: generateScheduleName(key.Namespace, key.Name), Namespace: veleroNamespace}, &schedule) - }, timeout, interval).Should(Succeed()) + //By("Expecting the drupal deployment to have the EnvFrom secret field set correctly") + By("Expecting the drupal deployment to have at least 2 containers") + Eventually(func() bool { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) + return len(deploy.Spec.Template.Spec.Containers) >= 2 + }, timeout, interval).Should(BeTrue()) - // Check Routes - By("Expecting Drupal Route(s) to be created") - for _, url := range cr.Spec.SiteURL { - route = routev1.Route{} - hash := md5.Sum([]byte(url)) + // Check PHP-FPM configMap creation + By("Expecting PHP_FPM configmaps created") Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &route) - return route.ObjectMeta.OwnerReferences + k8sClient.Get(ctx, types.NamespacedName{Name: "php-fpm-" + key.Name, Namespace: key.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - } - // Check OidcReturnUris - By("Expecting OidcReturnURIs created") - for _, url := range cr.Spec.SiteURL { - oidcReturnUri = authz.OidcReturnURI{} - hash := md5.Sum([]byte(url)) + // Check Nginx configMap creation + By("Expecting Nginx configmaps created") Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &oidcReturnUri) - return oidcReturnUri.ObjectMeta.OwnerReferences + k8sClient.Get(ctx, types.NamespacedName{Name: "nginx-" + key.Name, Namespace: key.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - } - }) - }) - }) - Describe("Cloning a drupalSite object", func() { - Context("With cloneFrom", func() { - It("All dependent resources should be created", func() { - keyClone := types.NamespacedName{ - Name: Name + "-clone", - Namespace: Namespace, - } - drupalSiteObjectClone := &drupalwebservicesv1alpha1.DrupalSite{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "drupal.webservices.cern.ch/v1alpha1", - Kind: "DrupalSite", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: keyClone.Name, - Namespace: keyClone.Namespace, - Labels: map[string]string{ - "drupal.webservices.cern.ch/isPrimary": "true", - }, - }, - - Spec: drupalwebservicesv1alpha1.DrupalSiteSpec{ - Version: drupalwebservicesv1alpha1.Version{ - Name: "v9.3-2", - ReleaseSpec: "stable", - }, - Configuration: drupalwebservicesv1alpha1.Configuration{ - DiskSize: "6Gi", - QoSClass: drupalwebservicesv1alpha1.QoSStandard, - DatabaseClass: drupalwebservicesv1alpha1.DBODStandard, - CloneFrom: drupalwebservicesv1alpha1.CloneFrom(key.Name), - ScheduledBackups: "enabled", - }, - SiteURL: []drupalwebservicesv1alpha1.Url{dummySiteUrl}, - }, - } + // Check Site settings configMap creation + By("Expecting Site settings configmaps created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "site-settings-" + key.Name, Namespace: key.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - By("By creating a new drupalSite") - Eventually(func() error { - return k8sClient.Create(ctx, drupalSiteObjectClone) - }, timeout, interval).Should(Succeed()) + // Check PHP Cli configMap creation + By("Expecting PHP Cli configmaps created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "php-cli-config-" + key.Name, Namespace: key.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Create drupalSite object - By("Expecting drupalSite object created") - cr := drupalwebservicesv1alpha1.DrupalSite{} - Eventually(func() error { - return k8sClient.Get(ctx, keyClone, &cr) - }, timeout, interval).Should(Succeed()) + // Check Drupal service + By("Expecting Drupal service created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &svc) + return svc.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - trueVar := true - expectedOwnerReference := metav1.OwnerReference{ - APIVersion: "drupal.webservices.cern.ch/v1alpha1", - Kind: "DrupalSite", - Name: keyClone.Name, - UID: cr.UID, - Controller: &trueVar, - } - configmap := corev1.ConfigMap{} - svc := corev1.Service{} - pvc := corev1.PersistentVolumeClaim{} - job := batchv1.Job{} - deploy := appsv1.Deployment{} - dbod := dbodv1a1.Database{} - route := routev1.Route{} - oidcReturnUri := authz.OidcReturnURI{} - schedule := velerov1.Schedule{} + // Check drupal persistentVolumeClaim + By("Expecting drupal persistentVolumeClaim created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "pv-claim-" + key.Name, Namespace: key.Namespace}, &pvc) + return pvc.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Check DBOD resource creation - By("Expecting Database resource created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &dbod) - return dbod.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Check Drupal deployments + By("Expecting Drupal deployments created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) + return deploy.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Update DBOD resource status field - By("Updating the DBOD instance in Database resource status") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &dbod) - dbod.Status.DbodInstance = "test" - return k8sClient.Status().Update(ctx, &dbod) - }, timeout, interval).Should(Succeed()) + // Check Drush job + By("Expecting Drush job created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "ensure-site-install-" + key.Name, Namespace: key.Namespace}, &job) + return job.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - By("Expecting the drupal deployment to have at least 2 containers") - Eventually(func() bool { - k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &deploy) - return len(deploy.Spec.Template.Spec.Containers) >= 2 - }, timeout, interval).Should(BeTrue()) + // Update drupalSite custom resource status fields to allow route conditions + By("Updating 'initialized' status field in drupalSite resource") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) + cr.Status.Conditions.SetCondition(status.Condition{Type: "Initialized", Status: "True"}) + return k8sClient.Status().Update(ctx, &cr) + }, timeout, interval).Should(Succeed()) - // Check PHP-FPM configMap creation - By("Expecting PHP_FPM configmaps created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "php-fpm-" + keyClone.Name, Namespace: keyClone.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Update deployment status fields to allow 'ready' status field to be set on the drupalSite resource + By("Updating 'ReadyReplicas' and 'AvailableReplicas' status fields in deployment resource") + Eventually(func() error { + k8sClient.Get(ctx, key, &deploy) + deploy.Status.Replicas = 1 + deploy.Status.AvailableReplicas = 1 + deploy.Status.ReadyReplicas = 1 + return k8sClient.Status().Update(ctx, &deploy) + }, timeout, interval).Should(Succeed()) - // Check Nginx configMap creation - By("Expecting Nginx configmaps created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "nginx-" + keyClone.Name, Namespace: keyClone.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Check if the Schedule resource is created + By("Expecting Schedule to be created") + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{Name: generateScheduleName(key.Namespace, key.Name), Namespace: veleroNamespace}, &schedule) + }, timeout, interval).Should(Succeed()) - // Check Site settings configMap creation - By("Expecting Site settings configmaps created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "site-settings-" + keyClone.Name, Namespace: keyClone.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Check Routes + By("Expecting Drupal Route(s) to be created") + for _, url := range cr.Spec.SiteURL { + route = routev1.Route{} + hash := md5.Sum([]byte(url)) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &route) + return route.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + } - // Check PHP Cli configMap creation - By("Expecting PHP Cli configmaps created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "php-cli-config-" + keyClone.Name, Namespace: keyClone.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Check Drupal service - By("Expecting Drupal service created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &svc) - return svc.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Check OidcReturnUris + By("Expecting OidcReturnURIs created") + for _, url := range cr.Spec.SiteURL { + oidcReturnUri = authz.OidcReturnURI{} + hash := md5.Sum([]byte(url)) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &oidcReturnUri) + return oidcReturnUri.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + } + }) + }) + }) - // Check drupal persistentVolumeClaim - By("Expecting drupal persistentVolumeClaim created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "pv-claim-" + keyClone.Name, Namespace: keyClone.Namespace}, &pvc) - return pvc.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + Describe("Cloning a drupalSite object", func() { + Context("With cloneFrom", func() { + It("All dependent resources should be created", func() { + keyClone := types.NamespacedName{ + Name: Name + "-clone", + Namespace: Namespace, + } + drupalSiteObjectClone := &drupalwebservicesv1alpha1.DrupalSite{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "drupal.webservices.cern.ch/v1alpha1", + Kind: "DrupalSite", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: keyClone.Name, + Namespace: keyClone.Namespace, + Labels: map[string]string{ + "drupal.webservices.cern.ch/isPrimary": "true", + }, + }, + + Spec: drupalwebservicesv1alpha1.DrupalSiteSpec{ + Version: drupalwebservicesv1alpha1.Version{ + Name: "v9.3-2", + ReleaseSpec: "stable", + }, + Configuration: drupalwebservicesv1alpha1.Configuration{ + DiskSize: "6Gi", + QoSClass: drupalwebservicesv1alpha1.QoSStandard, + DatabaseClass: drupalwebservicesv1alpha1.DBODStandard, + CloneFrom: drupalwebservicesv1alpha1.CloneFrom(key.Name), + ScheduledBackups: "enabled", + }, + SiteURL: []drupalwebservicesv1alpha1.Url{dummySiteUrl}, + }, + } - // Check Drupal deployments - By("Expecting Drupal deployments created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &deploy) - return deploy.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + By("By creating a new drupalSite") + Eventually(func() error { + return k8sClient.Create(ctx, drupalSiteObjectClone) + }, timeout, interval).Should(Succeed()) - // // Check Drush job - By("Expecting Drush job created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "clone-" + keyClone.Name, Namespace: keyClone.Namespace}, &job) - return job.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Create drupalSite object + By("Expecting drupalSite object created") + cr := drupalwebservicesv1alpha1.DrupalSite{} + Eventually(func() error { + return k8sClient.Get(ctx, keyClone, &cr) + }, timeout, interval).Should(Succeed()) - // Update drupalSite custom resource status fields to allow route conditions - By("Updating 'initialized' status field in drupalSite resource") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &cr) - cr.Status.Conditions.SetCondition(status.Condition{Type: "Initialized", Status: "True"}) - return k8sClient.Status().Update(ctx, &cr) - }, timeout, interval).Should(Succeed()) + trueVar := true + expectedOwnerReference := metav1.OwnerReference{ + APIVersion: "drupal.webservices.cern.ch/v1alpha1", + Kind: "DrupalSite", + Name: keyClone.Name, + UID: cr.UID, + Controller: &trueVar, + } + configmap := corev1.ConfigMap{} + svc := corev1.Service{} + pvc := corev1.PersistentVolumeClaim{} + job := batchv1.Job{} + deploy := appsv1.Deployment{} + dbod := dbodv1a1.Database{} + route := routev1.Route{} + oidcReturnUri := authz.OidcReturnURI{} + schedule := velerov1.Schedule{} + + // Check DBOD resource creation + By("Expecting Database resource created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &dbod) + return dbod.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Update deployment status fields to allow 'ready' status field to be set on the drupalSite resource - By("Updating 'ReadyReplicas' and 'AvailableReplicas' status fields in deployment resource") - Eventually(func() error { - k8sClient.Get(ctx, keyClone, &deploy) - deploy.Status.Replicas = 1 - deploy.Status.AvailableReplicas = 1 - deploy.Status.ReadyReplicas = 1 - return k8sClient.Status().Update(ctx, &deploy) - }, timeout, interval).Should(Succeed()) + // Update DBOD resource status field + By("Updating the DBOD instance in Database resource status") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &dbod) + dbod.Status.DbodInstance = "test" + return k8sClient.Status().Update(ctx, &dbod) + }, timeout, interval).Should(Succeed()) - // Check if the Schedule resource is created - By("Expecting Schedule to be created") - Eventually(func() error { - return k8sClient.Get(ctx, types.NamespacedName{Name: generateScheduleName(keyClone.Namespace, keyClone.Name), Namespace: veleroNamespace}, &schedule) - }, timeout, interval).Should(Succeed()) + By("Expecting the drupal deployment to have at least 2 containers") + Eventually(func() bool { + k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &deploy) + return len(deploy.Spec.Template.Spec.Containers) >= 2 + }, timeout, interval).Should(BeTrue()) - // Check Routes - By("Expecting Drupal Route(s) to be created") - for _, url := range cr.Spec.SiteURL { - route = routev1.Route{} - hash := md5.Sum([]byte(url)) + // Check PHP-FPM configMap creation + By("Expecting PHP_FPM configmaps created") Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: keyClone.Namespace}, &route) - return route.ObjectMeta.OwnerReferences + k8sClient.Get(ctx, types.NamespacedName{Name: "php-fpm-" + keyClone.Name, Namespace: keyClone.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - } - // Check OidcReturnUris - By("Expecting OidcReturnURIs created") - for _, url := range cr.Spec.SiteURL { - oidcReturnUri = authz.OidcReturnURI{} - hash := md5.Sum([]byte(url)) + // Check Nginx configMap creation + By("Expecting Nginx configmaps created") Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: keyClone.Namespace}, &oidcReturnUri) - return oidcReturnUri.ObjectMeta.OwnerReferences + k8sClient.Get(ctx, types.NamespacedName{Name: "nginx-" + keyClone.Name, Namespace: keyClone.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - } - - // Check if the clone labels are set on the drupalSite - By("Expecting the clone labels to be set on the cloned site") - Eventually(func() bool { - k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &cr) - return (cr.Annotations["clonedFrom"] == key.Name && cr.Annotations["clonedOn"] != "") - }, timeout, interval).Should(BeTrue()) - - // Check if the cloneFrom field is reset on the drupalSite - By("Expecting the cloneFrom field is reset on the cloned site") - Eventually(func() bool { - k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &cr) - return (cr.Spec.Configuration.CloneFrom == "") - }, timeout, interval).Should(BeTrue()) - - // Check if the diskSize is set correctly - By("Expecting the clone site diskSize is set to the same as source site") - Eventually(func() bool { - cr_source := drupalwebservicesv1alpha1.DrupalSite{} - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr_source) - return (cr.Spec.Configuration.DiskSize == cr_source.Spec.Configuration.DiskSize) - }, timeout, interval).Should(BeTrue()) - - }) - }) - }) - - Describe("Blocking and unblocking the drupalsite object", func() { - Context("With basic spec", func() { - It("Should be blocked and unblocked successfully", func() { - key = types.NamespacedName{ - Name: Name, - Namespace: Namespace, - } - cr := drupalwebservicesv1alpha1.DrupalSite{} - namespace := corev1.Namespace{} - - By("Expecting drupalSite object created") - Eventually(func() error { - return k8sClient.Get(ctx, key, &cr) - }, timeout, interval).Should(Succeed()) - - By("Adding user-project label to namespace") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Namespace}, &namespace) - namespace.Labels = map[string]string{"okd.cern.ch/user-project": "true"} - return k8sClient.Update(ctx, &namespace) - }, timeout, interval).Should(Succeed()) + // Check Site settings configMap creation + By("Expecting Site settings configmaps created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "site-settings-" + keyClone.Name, Namespace: keyClone.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - By("Adding blocked label to namespace") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Namespace}, &namespace) - namespace.Labels["okd.cern.ch/project-blocked"] = "true" - return k8sClient.Update(ctx, &namespace) - }, timeout, interval).Should(Succeed()) + // Check PHP Cli configMap creation + By("Expecting PHP Cli configmaps created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "php-cli-config-" + keyClone.Name, Namespace: keyClone.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Check Drupal service + By("Expecting Drupal service created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &svc) + return svc.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - deploy := appsv1.Deployment{} - By("Expecting to set deployment replicas to 0") - Eventually(func() bool { - k8sClient.Get(ctx, key, &deploy) - return *deploy.Spec.Replicas == 0 - }, timeout, interval).Should(BeTrue()) + // Check drupal persistentVolumeClaim + By("Expecting drupal persistentVolumeClaim created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "pv-claim-" + keyClone.Name, Namespace: keyClone.Namespace}, &pvc) + return pvc.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - By("Removing blocked label from namespace") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Namespace}, &namespace) - delete(namespace.Labels, "okd.cern.ch/project-blocked") - return k8sClient.Update(ctx, &namespace) - }, timeout, interval).Should(Succeed()) + // Check Drupal deployments + By("Expecting Drupal deployments created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &deploy) + return deploy.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - By("Expecting to set deployment replicas to 1") - Eventually(func() bool { - k8sClient.Get(ctx, key, &deploy) - return *deploy.Spec.Replicas == 1 - }, timeout, interval).Should(BeTrue()) - }) - }) - }) + // // Check Drush job + By("Expecting Drush job created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "clone-" + keyClone.Name, Namespace: keyClone.Namespace}, &job) + return job.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - Describe("Creating a new backup resource", func() { - Context("for the basic drupalSite", func() { - It("New velero backups created for the site should reflect in the drupalSite Status", func() { - By("Expecting drupalSite object created") - cr := drupalwebservicesv1alpha1.DrupalSite{} - Eventually(func() error { - return k8sClient.Get(ctx, key, &cr) - }, timeout, interval).Should(Succeed()) + // Update drupalSite custom resource status fields to allow route conditions + By("Updating 'initialized' status field in drupalSite resource") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &cr) + cr.Status.Conditions.SetCondition(status.Condition{Type: "Initialized", Status: "True"}) + return k8sClient.Status().Update(ctx, &cr) + }, timeout, interval).Should(Succeed()) - // Create a backup resource for the drupalSite - hash := md5.Sum([]byte(key.Namespace)) - backup := velerov1.Backup{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "velero.io/v1", - Kind: "Backup", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: key.Name + "backup", - Namespace: veleroNamespace, - Labels: map[string]string{ - "drupal.webservices.cern.ch/projectHash": hex.EncodeToString(hash[:]), - "drupal.webservices.cern.ch/project": key.Namespace, - "drupal.webservices.cern.ch/drupalSite": key.Name, - }, - Annotations: map[string]string{ - "drupal.webservices.cern.ch/drupalSite": key.Name, - "drupal.webservices.cern.ch/project": key.Namespace, - }, - }, - Status: velerov1.BackupStatus{ - Phase: velerov1.BackupPhaseCompleted, - }, - } + // Update deployment status fields to allow 'ready' status field to be set on the drupalSite resource + By("Updating 'ReadyReplicas' and 'AvailableReplicas' status fields in deployment resource") + Eventually(func() error { + k8sClient.Get(ctx, keyClone, &deploy) + deploy.Status.Replicas = 1 + deploy.Status.AvailableReplicas = 1 + deploy.Status.ReadyReplicas = 1 + return k8sClient.Status().Update(ctx, &deploy) + }, timeout, interval).Should(Succeed()) - By("By creating a backup resource for the drupalSite") - Eventually(func() error { - return k8sClient.Create(ctx, &backup) - }, timeout, interval).Should(Succeed()) + // Check if the Schedule resource is created + By("Expecting Schedule to be created") + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{Name: generateScheduleName(keyClone.Namespace, keyClone.Name), Namespace: veleroNamespace}, &schedule) + }, timeout, interval).Should(Succeed()) - // Check if the backup resource is created - By("By creating a backup resource for the drupalSite") - backup1 := velerov1.Backup{} - Eventually(func() error { - return k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "backup", Namespace: veleroNamespace}, &backup1) - }, timeout, interval).Should(Succeed()) + // Check Routes + By("Expecting Drupal Route(s) to be created") + for _, url := range cr.Spec.SiteURL { + route = routev1.Route{} + hash := md5.Sum([]byte(url)) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: keyClone.Namespace}, &route) + return route.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + } - // Check for the Backup name in the Drupalsite Status - By("By checking for the Backup in the DrupalSite Status") - Eventually(func() bool { - k8sClient.Get(ctx, key, &cr) - return len(cr.Status.AvailableBackups) > 0 && cr.Status.AvailableBackups[0].BackupName == backup.Name - }, timeout, interval).Should(BeTrue()) + // Check OidcReturnUris + By("Expecting OidcReturnURIs created") + for _, url := range cr.Spec.SiteURL { + oidcReturnUri = authz.OidcReturnURI{} + hash := md5.Sum([]byte(url)) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: keyClone.Namespace}, &oidcReturnUri) + return oidcReturnUri.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + } + + // Check if the clone labels are set on the drupalSite + By("Expecting the clone labels to be set on the cloned site") + Eventually(func() bool { + k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &cr) + return (cr.Annotations["clonedFrom"] == key.Name && cr.Annotations["clonedOn"] != "") + }, timeout, interval).Should(BeTrue()) + + // Check if the cloneFrom field is reset on the drupalSite + By("Expecting the cloneFrom field is reset on the cloned site") + Eventually(func() bool { + k8sClient.Get(ctx, types.NamespacedName{Name: keyClone.Name, Namespace: keyClone.Namespace}, &cr) + return (cr.Spec.Configuration.CloneFrom == "") + }, timeout, interval).Should(BeTrue()) + + // Check if the diskSize is set correctly + By("Expecting the clone site diskSize is set to the same as source site") + Eventually(func() bool { + cr_source := drupalwebservicesv1alpha1.DrupalSite{} + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr_source) + return (cr.Spec.Configuration.DiskSize == cr_source.Spec.Configuration.DiskSize) + }, timeout, interval).Should(BeTrue()) + + }) }) }) - }) - Describe("Update the basic drupalsite object", func() { - Context("With a different drupal Version", func() { - It("Should be updated successfully", func() { - key = types.NamespacedName{ - Name: Name, - Namespace: Namespace, - } - newVersion := "v9.3-2" - newReleaseSpec := "new" + Describe("Blocking and unblocking the drupalsite object", func() { + Context("With basic spec", func() { + It("Should be blocked and unblocked successfully", func() { + key = types.NamespacedName{ + Name: Name, + Namespace: Namespace, + } - // Create drupalSite object - cr := drupalwebservicesv1alpha1.DrupalSite{} - By("Expecting drupalSite object created") - Eventually(func() error { - return k8sClient.Get(ctx, key, &cr) - }, timeout, interval).Should(Succeed()) + cr := drupalwebservicesv1alpha1.DrupalSite{} + namespace := corev1.Namespace{} - deploy := appsv1.Deployment{} + By("Expecting drupalSite object created") + Eventually(func() error { + return k8sClient.Get(ctx, key, &cr) + }, timeout, interval).Should(Succeed()) - By("Updating the version") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) - cr.Spec.Version.Name = newVersion - cr.Spec.Version.ReleaseSpec = newReleaseSpec - return k8sClient.Update(ctx, &cr) - }, timeout, interval).Should(Succeed()) + By("Adding user-project label to namespace") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Namespace}, &namespace) + namespace.Labels = map[string]string{"okd.cern.ch/user-project": "true"} + return k8sClient.Update(ctx, &namespace) + }, timeout, interval).Should(Succeed()) - // Check the annotation on the deployment - By("Expecting the new drupal Version on the pod annotation") - Eventually(func() bool { - k8sClient.Get(ctx, key, &deploy) - return deploy.Spec.Template.ObjectMeta.Annotations["releaseID"] == newVersion+"-"+newReleaseSpec - }, timeout, interval).Should(BeTrue()) + By("Adding blocked label to namespace") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Namespace}, &namespace) + namespace.Labels["okd.cern.ch/project-blocked"] = "true" + return k8sClient.Update(ctx, &namespace) + }, timeout, interval).Should(Succeed()) - pod := corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Pod", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: key.Name, - Namespace: key.Namespace, - Labels: map[string]string{"drupalSite": cr.Name, "app": "drupal"}, - Annotations: map[string]string{"releaseID": cr.Spec.Version.Name + "-" + cr.Spec.Version.ReleaseSpec}, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Image: "test-image", - Name: "test-image", - }}, - }, - } + deploy := appsv1.Deployment{} + By("Expecting to set deployment replicas to 0") + Eventually(func() bool { + k8sClient.Get(ctx, key, &deploy) + return *deploy.Spec.Replicas == 0 + }, timeout, interval).Should(BeTrue()) - // Create a pod to simulate a error in the update process - By("Expecting a new pod with required labels to be created") - Eventually(func() error { - return k8sClient.Create(ctx, &pod) - }, timeout, interval).Should(Succeed()) + By("Removing blocked label from namespace") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Namespace}, &namespace) + delete(namespace.Labels, "okd.cern.ch/project-blocked") + return k8sClient.Update(ctx, &namespace) + }, timeout, interval).Should(Succeed()) - // Set the pod status phase to 'Failed' to simulate a error in the update process - By("Expecting a new pod status to be updated") - Eventually(func() error { - k8sClient.Get(ctx, key, &pod) - pod.Status.Phase = corev1.PodFailed - return k8sClient.Status().Update(ctx, &pod) - }, timeout, interval).Should(Succeed()) + By("Expecting to set deployment replicas to 1") + Eventually(func() bool { + k8sClient.Get(ctx, key, &deploy) + return *deploy.Spec.Replicas == 1 + }, timeout, interval).Should(BeTrue()) + }) + }) + }) - // NOTE: Commenting this out temporarily. Refer to https://gitlab.cern.ch/drupal/paas/drupalsite-operator/-/issues/74 - // Check if the drupalSiteObject has 'codeUpdateFailed' status set - // By("Expecting 'codeUpdateFailed' status set on the drupalSiteObject") - // Eventually(func() bool { - // k8sClient.Get(ctx, key, &cr) - // return cr.ConditionTrue("CodeUpdateFailed") - // }, timeout, interval).Should(BeTrue()) + Describe("Creating a new backup resource", func() { + Context("for the basic drupalSite", func() { + It("New velero backups created for the site should reflect in the drupalSite Status", func() { + By("Expecting drupalSite object created") + cr := drupalwebservicesv1alpha1.DrupalSite{} + Eventually(func() error { + return k8sClient.Get(ctx, key, &cr) + }, timeout, interval).Should(Succeed()) - // Update drupalSite Failsafe status to simulate a successful upgrade - By("Updating 'Failsafe' status field in drupalSite resource") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) - cr.Status.Failsafe = newVersion + "-" + newReleaseSpec - return k8sClient.Status().Update(ctx, &cr) - }, timeout, interval).Should(Succeed()) + // Create a backup resource for the drupalSite + hash := md5.Sum([]byte(key.Namespace)) + backup := velerov1.Backup{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "velero.io/v1", + Kind: "Backup", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: key.Name + "backup", + Namespace: veleroNamespace, + Labels: map[string]string{ + "drupal.webservices.cern.ch/projectHash": hex.EncodeToString(hash[:]), + "drupal.webservices.cern.ch/project": key.Namespace, + "drupal.webservices.cern.ch/drupalSite": key.Name, + }, + Annotations: map[string]string{ + "drupal.webservices.cern.ch/drupalSite": key.Name, + "drupal.webservices.cern.ch/project": key.Namespace, + }, + }, + Status: velerov1.BackupStatus{ + Phase: velerov1.BackupPhaseCompleted, + }, + } - // Further tests need to be implemented, if we can bypass ExecErr with envtest + By("By creating a backup resource for the drupalSite") + Eventually(func() error { + return k8sClient.Create(ctx, &backup) + }, timeout, interval).Should(Succeed()) + + // Check if the backup resource is created + By("By creating a backup resource for the drupalSite") + backup1 := velerov1.Backup{} + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "backup", Namespace: veleroNamespace}, &backup1) + }, timeout, interval).Should(Succeed()) + + // Check for the Backup name in the Drupalsite Status + By("By checking for the Backup in the DrupalSite Status") + Eventually(func() bool { + k8sClient.Get(ctx, key, &cr) + return len(cr.Status.AvailableBackups) > 0 && cr.Status.AvailableBackups[0].BackupName == backup.Name + }, timeout, interval).Should(BeTrue()) + }) }) }) - }) - Describe("Deleting dependent objects", func() { - Context("Of the basic drupalSite", func() { - It("All dependent resources should be recreated successfully", func() { - By("Expecting drupalSite object created") - cr := drupalwebservicesv1alpha1.DrupalSite{} - Eventually(func() error { - return k8sClient.Get(ctx, key, &cr) - }, timeout, interval).Should(Succeed()) - trueVar := true - expectedOwnerReference := metav1.OwnerReference{ - APIVersion: "drupal.webservices.cern.ch/v1alpha1", - Kind: "DrupalSite", - Name: key.Name, - UID: cr.UID, - Controller: &trueVar, - } - configmap := corev1.ConfigMap{} - svc := corev1.Service{} - pvc := corev1.PersistentVolumeClaim{} - job := batchv1.Job{} - deploy := appsv1.Deployment{} - route := routev1.Route{} - oidcReturnUri := authz.OidcReturnURI{} + Describe("Update the basic drupalsite object", func() { + Context("With a different drupal Version", func() { + It("Should be updated successfully", func() { + key = types.NamespacedName{ + Name: Name, + Namespace: Namespace, + } + newVersion := "v9.3-2" + newReleaseSpec := "new" - // Check PHP-FPM configMap recreation - By("Expecting PHP_FPM configmaps recreated") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: "php-fpm-" + key.Name, Namespace: key.Namespace}, &configmap) - return k8sClient.Delete(ctx, &configmap) - }, timeout, interval).Should(Succeed()) - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "php-fpm-" + key.Name, Namespace: key.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Create drupalSite object + cr := drupalwebservicesv1alpha1.DrupalSite{} + By("Expecting drupalSite object created") + Eventually(func() error { + return k8sClient.Get(ctx, key, &cr) + }, timeout, interval).Should(Succeed()) - // Check Nginx configMap creation - By("Expecting Nginx configmaps recreated") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: "nginx-" + key.Name, Namespace: key.Namespace}, &configmap) - return k8sClient.Delete(ctx, &configmap) - }, timeout, interval).Should(Succeed()) - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "nginx-" + key.Name, Namespace: key.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + deploy := appsv1.Deployment{} - // Check Site settings configMap creation - By("Expecting Site settings configmaps recreated") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: "site-settings-" + key.Name, Namespace: key.Namespace}, &configmap) - return k8sClient.Delete(ctx, &configmap) - }, timeout, interval).Should(Succeed()) - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "site-settings-" + key.Name, Namespace: key.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + By("Updating the version") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) + cr.Spec.Version.Name = newVersion + cr.Spec.Version.ReleaseSpec = newReleaseSpec + return k8sClient.Update(ctx, &cr) + }, timeout, interval).Should(Succeed()) - // Check PHP Cli configMap creation - By("Expecting PHP Cli configmaps recreated") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: "php-cli-config-" + key.Name, Namespace: key.Namespace}, &configmap) - return k8sClient.Delete(ctx, &configmap) - }, timeout, interval).Should(Succeed()) - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "php-cli-config-" + key.Name, Namespace: key.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Check the annotation on the deployment + By("Expecting the new drupal Version on the pod annotation") + Eventually(func() bool { + k8sClient.Get(ctx, key, &deploy) + return deploy.Spec.Template.ObjectMeta.Annotations["releaseID"] == newVersion+"-"+newReleaseSpec + }, timeout, interval).Should(BeTrue()) - // Check Drupal service - By("Expecting Drupal service recreated") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &svc) - return k8sClient.Delete(ctx, &svc) - }, timeout, interval).Should(Succeed()) - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &svc) - return svc.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + pod := corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: key.Name, + Namespace: key.Namespace, + Labels: map[string]string{"drupalSite": cr.Name, "app": "drupal"}, + Annotations: map[string]string{"releaseID": cr.Spec.Version.Name + "-" + cr.Spec.Version.ReleaseSpec}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Image: "test-image", + Name: "test-image", + }}, + }, + } - // Check drupal persistentVolumeClaim - By("Expecting drupal persistentVolumeClaim recreated") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: "pv-claim-" + key.Name, Namespace: key.Namespace}, &pvc) - return k8sClient.Delete(ctx, &pvc) - }, timeout, interval).Should(Succeed()) - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "pv-claim-" + key.Name, Namespace: key.Namespace}, &pvc) - return pvc.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Create a pod to simulate a error in the update process + By("Expecting a new pod with required labels to be created") + Eventually(func() error { + return k8sClient.Create(ctx, &pod) + }, timeout, interval).Should(Succeed()) - // Check Drupal deployments - By("Expecting Drupal deployments recreated") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) - return k8sClient.Delete(ctx, &deploy) - }, timeout, interval).Should(Succeed()) - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) - return deploy.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Set the pod status phase to 'Failed' to simulate a error in the update process + By("Expecting a new pod status to be updated") + Eventually(func() error { + k8sClient.Get(ctx, key, &pod) + pod.Status.Phase = corev1.PodFailed + return k8sClient.Status().Update(ctx, &pod) + }, timeout, interval).Should(Succeed()) - // Check Drush job - By("Expecting Drush job recreated") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: "ensure-site-install-" + key.Name, Namespace: key.Namespace}, &job) - return k8sClient.Delete(ctx, &job) - }, timeout, interval).Should(Succeed()) - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "ensure-site-install-" + key.Name, Namespace: key.Namespace}, &job) - return job.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // NOTE: Commenting this out temporarily. Refer to https://gitlab.cern.ch/drupal/paas/drupalsite-operator/-/issues/74 + // Check if the drupalSiteObject has 'codeUpdateFailed' status set + // By("Expecting 'codeUpdateFailed' status set on the drupalSiteObject") + // Eventually(func() bool { + // k8sClient.Get(ctx, key, &cr) + // return cr.ConditionTrue("CodeUpdateFailed") + // }, timeout, interval).Should(BeTrue()) - // Check Routes - By("Expecting Drupal Route(s) recreated") - for _, url := range cr.Spec.SiteURL { - route = routev1.Route{} - hash := md5.Sum([]byte(url)) + // Update drupalSite Failsafe status to simulate a successful upgrade + By("Updating 'Failsafe' status field in drupalSite resource") Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &route) - return k8sClient.Delete(ctx, &route) + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) + cr.Status.Failsafe = newVersion + "-" + newReleaseSpec + return k8sClient.Status().Update(ctx, &cr) + }, timeout, interval).Should(Succeed()) + + // Further tests need to be implemented, if we can bypass ExecErr with envtest + }) + }) + }) + + Describe("Deleting dependent objects", func() { + Context("Of the basic drupalSite", func() { + It("All dependent resources should be recreated successfully", func() { + By("Expecting drupalSite object created") + cr := drupalwebservicesv1alpha1.DrupalSite{} + Eventually(func() error { + return k8sClient.Get(ctx, key, &cr) + }, timeout, interval).Should(Succeed()) + trueVar := true + expectedOwnerReference := metav1.OwnerReference{ + APIVersion: "drupal.webservices.cern.ch/v1alpha1", + Kind: "DrupalSite", + Name: key.Name, + UID: cr.UID, + Controller: &trueVar, + } + configmap := corev1.ConfigMap{} + svc := corev1.Service{} + pvc := corev1.PersistentVolumeClaim{} + job := batchv1.Job{} + deploy := appsv1.Deployment{} + route := routev1.Route{} + oidcReturnUri := authz.OidcReturnURI{} + + // Check PHP-FPM configMap recreation + By("Expecting PHP_FPM configmaps recreated") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: "php-fpm-" + key.Name, Namespace: key.Namespace}, &configmap) + return k8sClient.Delete(ctx, &configmap) }, timeout, interval).Should(Succeed()) Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &route) - return route.ObjectMeta.OwnerReferences + k8sClient.Get(ctx, types.NamespacedName{Name: "php-fpm-" + key.Name, Namespace: key.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - } - // Check OidcReturnUris - By("Expecting OidcReturnUri(s) recreated") - for _, url := range cr.Spec.SiteURL { - oidcReturnUri = authz.OidcReturnURI{} - hash := md5.Sum([]byte(url)) + // Check Nginx configMap creation + By("Expecting Nginx configmaps recreated") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: "nginx-" + key.Name, Namespace: key.Namespace}, &configmap) + return k8sClient.Delete(ctx, &configmap) + }, timeout, interval).Should(Succeed()) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "nginx-" + key.Name, Namespace: key.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + + // Check Site settings configMap creation + By("Expecting Site settings configmaps recreated") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: "site-settings-" + key.Name, Namespace: key.Namespace}, &configmap) + return k8sClient.Delete(ctx, &configmap) + }, timeout, interval).Should(Succeed()) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "site-settings-" + key.Name, Namespace: key.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + + // Check PHP Cli configMap creation + By("Expecting PHP Cli configmaps recreated") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: "php-cli-config-" + key.Name, Namespace: key.Namespace}, &configmap) + return k8sClient.Delete(ctx, &configmap) + }, timeout, interval).Should(Succeed()) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "php-cli-config-" + key.Name, Namespace: key.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + + // Check Drupal service + By("Expecting Drupal service recreated") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &svc) + return k8sClient.Delete(ctx, &svc) + }, timeout, interval).Should(Succeed()) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &svc) + return svc.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + + // Check drupal persistentVolumeClaim + By("Expecting drupal persistentVolumeClaim recreated") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: "pv-claim-" + key.Name, Namespace: key.Namespace}, &pvc) + return k8sClient.Delete(ctx, &pvc) + }, timeout, interval).Should(Succeed()) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "pv-claim-" + key.Name, Namespace: key.Namespace}, &pvc) + return pvc.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + + // Check Drupal deployments + By("Expecting Drupal deployments recreated") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) + return k8sClient.Delete(ctx, &deploy) + }, timeout, interval).Should(Succeed()) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) + return deploy.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + + // Check Drush job + By("Expecting Drush job recreated") Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &oidcReturnUri) - return k8sClient.Delete(ctx, &oidcReturnUri) + k8sClient.Get(ctx, types.NamespacedName{Name: "ensure-site-install-" + key.Name, Namespace: key.Namespace}, &job) + return k8sClient.Delete(ctx, &job) }, timeout, interval).Should(Succeed()) Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &oidcReturnUri) - return oidcReturnUri.ObjectMeta.OwnerReferences + k8sClient.Get(ctx, types.NamespacedName{Name: "ensure-site-install-" + key.Name, Namespace: key.Namespace}, &job) + return job.ObjectMeta.OwnerReferences }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - } + + // Check Routes + By("Expecting Drupal Route(s) recreated") + for _, url := range cr.Spec.SiteURL { + route = routev1.Route{} + hash := md5.Sum([]byte(url)) + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &route) + return k8sClient.Delete(ctx, &route) + }, timeout, interval).Should(Succeed()) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &route) + return route.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + } + + // Check OidcReturnUris + By("Expecting OidcReturnUri(s) recreated") + for _, url := range cr.Spec.SiteURL { + oidcReturnUri = authz.OidcReturnURI{} + hash := md5.Sum([]byte(url)) + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &oidcReturnUri) + return k8sClient.Delete(ctx, &oidcReturnUri) + }, timeout, interval).Should(Succeed()) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &oidcReturnUri) + return oidcReturnUri.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + } + }) }) }) - }) - Describe("Updating siteUrl Spec", func() { - Context("Of the basic drupalSite", func() { - It("Extra routes, oidcReturnUris should be removed and siteUrl list should be ensured", func() { - By("Expecting drupalSite object created") - cr := drupalwebservicesv1alpha1.DrupalSite{} - Eventually(func() error { - return k8sClient.Get(ctx, key, &cr) - }, timeout, interval).Should(Succeed()) - trueVar := true - expectedOwnerReference := metav1.OwnerReference{ - APIVersion: "drupal.webservices.cern.ch/v1alpha1", - Kind: "DrupalSite", - Name: key.Name, - UID: cr.UID, - Controller: &trueVar, - } - route := routev1.Route{} - oidcReturnUri := authz.OidcReturnURI{} + Describe("Updating siteUrl Spec", func() { + Context("Of the basic drupalSite", func() { + It("Extra routes, oidcReturnUris should be removed and siteUrl list should be ensured", func() { + By("Expecting drupalSite object created") + cr := drupalwebservicesv1alpha1.DrupalSite{} + Eventually(func() error { + return k8sClient.Get(ctx, key, &cr) + }, timeout, interval).Should(Succeed()) + trueVar := true + expectedOwnerReference := metav1.OwnerReference{ + APIVersion: "drupal.webservices.cern.ch/v1alpha1", + Kind: "DrupalSite", + Name: key.Name, + UID: cr.UID, + Controller: &trueVar, + } + route := routev1.Route{} + oidcReturnUri := authz.OidcReturnURI{} - By("Updating the siteUrl spec") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) - cr.Spec.SiteURL = cr.Spec.SiteURL[:len(cr.Spec.SiteURL)-1] - cr.Spec.SiteURL = append(cr.Spec.SiteURL, "test-4.webtest.cern.ch") - return k8sClient.Update(ctx, &cr) - }, timeout, interval).Should(Succeed()) + By("Updating the siteUrl spec") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) + cr.Spec.SiteURL = cr.Spec.SiteURL[:len(cr.Spec.SiteURL)-1] + cr.Spec.SiteURL = append(cr.Spec.SiteURL, "test-4.webtest.cern.ch") + return k8sClient.Update(ctx, &cr) + }, timeout, interval).Should(Succeed()) - // Check Routes - By("Expecting Drupal Route(s) created") - for _, url := range cr.Spec.SiteURL { - route = routev1.Route{} - hash := md5.Sum([]byte(url)) - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &route) - return route.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - } + // Check Routes + By("Expecting Drupal Route(s) created") + for _, url := range cr.Spec.SiteURL { + route = routev1.Route{} + hash := md5.Sum([]byte(url)) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &route) + return route.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + } - // Check OidcReturnUris - By("Expecting OidcReturnURIs created") - for _, url := range cr.Spec.SiteURL { - oidcReturnUri = authz.OidcReturnURI{} - hash := md5.Sum([]byte(url)) - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &oidcReturnUri) - return oidcReturnUri.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - } + // Check OidcReturnUris + By("Expecting OidcReturnURIs created") + for _, url := range cr.Spec.SiteURL { + oidcReturnUri = authz.OidcReturnURI{} + hash := md5.Sum([]byte(url)) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &oidcReturnUri) + return oidcReturnUri.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + } - // Check deleted entries - By("Expecting Drupal Route deleted") - hash := md5.Sum([]byte("test-3.webtest.cern.ch")) - Eventually(func() error { - return k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &route) - }, timeout, interval).ShouldNot(Succeed()) + // Check deleted entries + By("Expecting Drupal Route deleted") + hash := md5.Sum([]byte("test-3.webtest.cern.ch")) + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &route) + }, timeout, interval).ShouldNot(Succeed()) - By("Expecting oidcReturnURI deleted") - hash = md5.Sum([]byte("test-3.webtest.cern.ch")) - Eventually(func() error { - return k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &oidcReturnUri) - }, timeout, interval).ShouldNot(Succeed()) + By("Expecting oidcReturnURI deleted") + hash = md5.Sum([]byte("test-3.webtest.cern.ch")) + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &oidcReturnUri) + }, timeout, interval).ShouldNot(Succeed()) + }) }) }) - }) - Describe("Updating deployment object", func() { - Context("With debug annotations", func() { - It("Should not be updated successfully", func() { - By("Expecting drupalSite object created") - cr := drupalwebservicesv1alpha1.DrupalSite{} - Eventually(func() error { - return k8sClient.Get(ctx, key, &cr) - }, timeout, interval).Should(Succeed()) - - By("Updating the drupalSite with debug annotation") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) - if cr.Annotations == nil { - cr.Annotations = map[string]string{} - } - cr.Annotations[debugAnnotation] = "true" - return k8sClient.Update(ctx, &cr) - }, timeout, interval).Should(Succeed()) + Describe("Updating deployment object", func() { + Context("With debug annotations", func() { + It("Should not be updated successfully", func() { + By("Expecting drupalSite object created") + cr := drupalwebservicesv1alpha1.DrupalSite{} + Eventually(func() error { + return k8sClient.Get(ctx, key, &cr) + }, timeout, interval).Should(Succeed()) - By("By updating deployment object") - deploy := appsv1.Deployment{} - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) - deploy.Spec.Template.ObjectMeta.Annotations["pre.hook.backup.velero.io/container"] = "test-debug" - Eventually(func() error { - return k8sClient.Update(ctx, &deploy) - }, timeout, interval).Should(Succeed()) + By("Updating the drupalSite with debug annotation") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) + if cr.Annotations == nil { + cr.Annotations = map[string]string{} + } + cr.Annotations[debugAnnotation] = "true" + return k8sClient.Update(ctx, &cr) + }, timeout, interval).Should(Succeed()) - Eventually(func() bool { + By("By updating deployment object") + deploy := appsv1.Deployment{} k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) - return deploy.Spec.Template.GetAnnotations()["pre.hook.backup.velero.io/container"] == "test-debug" - }, timeout, interval).ShouldNot(BeTrue()) + deploy.Spec.Template.ObjectMeta.Annotations["pre.hook.backup.velero.io/container"] = "test-debug" + Eventually(func() error { + return k8sClient.Update(ctx, &deploy) + }, timeout, interval).Should(Succeed()) + + Eventually(func() bool { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) + return deploy.Spec.Template.GetAnnotations()["pre.hook.backup.velero.io/container"] == "test-debug" + }, timeout, interval).ShouldNot(BeTrue()) + }) }) }) - }) - Describe("Deleting the drupalsite object", func() { - Context("With basic spec", func() { - It("Should be deleted successfully", func() { - By("Expecting to delete successfully") - Eventually(func() error { - return k8sClient.Delete(context.Background(), drupalSiteObject) - }, timeout, interval).Should(Succeed()) + Describe("Deleting the drupalsite object", func() { + Context("With basic spec", func() { + It("Should be deleted successfully", func() { + By("Expecting to delete successfully") + Eventually(func() error { + return k8sClient.Delete(context.Background(), drupalSiteObject) + }, timeout, interval).Should(Succeed()) - By("Expecting to delete finish") - Eventually(func() error { - return k8sClient.Get(ctx, key, drupalSiteObject) - }, timeout, interval).ShouldNot(Succeed()) + By("Expecting to delete finish") + Eventually(func() error { + return k8sClient.Get(ctx, key, drupalSiteObject) + }, timeout, interval).ShouldNot(Succeed()) + }) }) }) - }) - Describe("Creating a drupalSite object", func() { - Context("With s2i source", func() { - It("All dependent resources should be created", func() { - key = types.NamespacedName{ - Name: Name + "-advanced", - Namespace: "advanced", - } - drupalSiteObject = &drupalwebservicesv1alpha1.DrupalSite{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "drupal.webservices.cern.ch/v1alpha1", - Kind: "DrupalSite", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: key.Name, - Namespace: key.Namespace, - }, - Spec: drupalwebservicesv1alpha1.DrupalSiteSpec{ - Version: drupalwebservicesv1alpha1.Version{ - Name: "v9.3-2", - ReleaseSpec: "stable", + Describe("Creating a drupalSite object", func() { + Context("With s2i source", func() { + It("All dependent resources should be created", func() { + key = types.NamespacedName{ + Name: Name + "-advanced", + Namespace: "advanced", + } + drupalSiteObject = &drupalwebservicesv1alpha1.DrupalSite{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "drupal.webservices.cern.ch/v1alpha1", + Kind: "DrupalSite", }, - Configuration: drupalwebservicesv1alpha1.Configuration{ - DiskSize: "10Gi", - QoSClass: drupalwebservicesv1alpha1.QoSStandard, - DatabaseClass: drupalwebservicesv1alpha1.DBODStandard, - ExtraConfigurationRepo: "https://gitlab.cern.ch/rvineetr/test-ravineet-d8-containers-buildconfig.git", + ObjectMeta: metav1.ObjectMeta{ + Name: key.Name, + Namespace: key.Namespace, + Labels: map[string]string{ + "drupal.webservices.cern.ch/isPrimary": "true", }, - SiteURL: []drupalwebservicesv1alpha1.Url{dummySiteUrl}, - }, - } - - By("By creating the testing namespace") - Eventually(func() error { - return k8sClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{ - Name: key.Namespace}, - }) - }, timeout, interval).Should(Succeed()) - - By("By creating a new drupalSite") - Eventually(func() error { - return k8sClient.Create(ctx, drupalSiteObject) - }, timeout, interval).Should(Succeed()) - - // Create drupalSite object - By("Expecting drupalSite object created") - cr := drupalwebservicesv1alpha1.DrupalSite{} - Eventually(func() error { - return k8sClient.Get(ctx, key, &cr) - }, timeout, interval).Should(Succeed()) - trueVar := true - expectedOwnerReference := metav1.OwnerReference{ - APIVersion: "drupal.webservices.cern.ch/v1alpha1", - Kind: "DrupalSite", - Name: key.Name, - UID: cr.UID, - Controller: &trueVar, - } - configmap := corev1.ConfigMap{} - svc := corev1.Service{} - pvc := corev1.PersistentVolumeClaim{} - job := batchv1.Job{} - deploy := appsv1.Deployment{} - is := imagev1.ImageStream{} - bc := buildv1.BuildConfig{} - dbod := dbodv1a1.Database{} - route := routev1.Route{} - oidcReturnUri := authz.OidcReturnURI{} - schedule := velerov1.Schedule{} - secret := corev1.Secret{} - - // Check DBOD resource creation - By("Expecting Database resource created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &dbod) - return dbod.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - - // Update DBOD resource status field - By("Updating the DBOD instance in Database resource status") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &dbod) - dbod.Status.DbodInstance = "test" - return k8sClient.Status().Update(ctx, &dbod) - }, timeout, interval).Should(Succeed()) - - By("Expecting the drupal deployment to have at least 2 containers") - Eventually(func() bool { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) - return len(deploy.Spec.Template.Spec.Containers) >= 2 - }, timeout, interval).Should(BeTrue()) - - // Check PHP-FPM configMap creation - By("Expecting PHP_FPM configmaps created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "php-fpm-" + key.Name, Namespace: key.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + }, + Spec: drupalwebservicesv1alpha1.DrupalSiteSpec{ + Version: drupalwebservicesv1alpha1.Version{ + Name: "v9.3-2", + ReleaseSpec: "stable", + }, + Configuration: drupalwebservicesv1alpha1.Configuration{ + DiskSize: "10Gi", + QoSClass: drupalwebservicesv1alpha1.QoSStandard, + DatabaseClass: drupalwebservicesv1alpha1.DBODStandard, + ExtraConfigurationRepo: "https://gitlab.cern.ch/rvineetr/test-ravineet-d8-containers-buildconfig.git", + }, + SiteURL: []drupalwebservicesv1alpha1.Url{dummySiteUrl}, + }, + } - // Check Nginx configMap creation - By("Expecting Nginx configmaps created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "nginx-" + key.Name, Namespace: key.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + By("By creating the testing namespace") + Eventually(func() error { + return k8sClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{ + Name: key.Namespace}, + }) + }, timeout, interval).Should(Succeed()) - // Check Site settings configMap creation - By("Expecting Site settings configmaps created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "site-settings-" + key.Name, Namespace: key.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + By("By creating a new drupalSite") + Eventually(func() error { + return k8sClient.Create(ctx, drupalSiteObject) + }, timeout, interval).Should(Succeed()) - // Check PHP Cli configMap creation - By("Expecting PHP Cli configmaps created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "php-cli-config-" + key.Name, Namespace: key.Namespace}, &configmap) - return configmap.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Check Drupal service - By("Expecting Drupal service created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &svc) - return svc.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Create drupalSite object + By("Expecting drupalSite object created") + cr := drupalwebservicesv1alpha1.DrupalSite{} + Eventually(func() error { + return k8sClient.Get(ctx, key, &cr) + }, timeout, interval).Should(Succeed()) + trueVar := true + expectedOwnerReference := metav1.OwnerReference{ + APIVersion: "drupal.webservices.cern.ch/v1alpha1", + Kind: "DrupalSite", + Name: key.Name, + UID: cr.UID, + Controller: &trueVar, + } + configmap := corev1.ConfigMap{} + svc := corev1.Service{} + pvc := corev1.PersistentVolumeClaim{} + job := batchv1.Job{} + deploy := appsv1.Deployment{} + is := imagev1.ImageStream{} + bc := buildv1.BuildConfig{} + dbod := dbodv1a1.Database{} + route := routev1.Route{} + oidcReturnUri := authz.OidcReturnURI{} + schedule := velerov1.Schedule{} + secret := corev1.Secret{} + + // Check DBOD resource creation + By("Expecting Database resource created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &dbod) + return dbod.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Check drupal persistentVolumeClaim - By("Expecting drupal persistentVolumeClaim created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "pv-claim-" + key.Name, Namespace: key.Namespace}, &pvc) - return pvc.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Update DBOD resource status field + By("Updating the DBOD instance in Database resource status") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &dbod) + dbod.Status.DbodInstance = "test" + return k8sClient.Status().Update(ctx, &dbod) + }, timeout, interval).Should(Succeed()) - // Check Drupal deployments - By("Expecting Drupal deployments created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) - return deploy.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + By("Expecting the drupal deployment to have at least 2 containers") + Eventually(func() bool { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) + return len(deploy.Spec.Template.Spec.Containers) >= 2 + }, timeout, interval).Should(BeTrue()) - // Check sitebuilder-s2i imageStream - By("Expecting sitebuilder-s2i imageStream created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "sitebuilder-s2i-" + key.Name, Namespace: key.Namespace}, &is) - return is.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Check PHP-FPM configMap creation + By("Expecting PHP_FPM configmaps created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "php-fpm-" + key.Name, Namespace: key.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Check Drush job - By("Expecting Drush job created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "ensure-site-install-" + key.Name, Namespace: key.Namespace}, &job) - return job.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Check Nginx configMap creation + By("Expecting Nginx configmaps created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "nginx-" + key.Name, Namespace: key.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Check S2I buildConfig - By("Expecting S2I buildConfig created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "sitebuilder-s2i-" + nameVersionHash(drupalSiteObject), Namespace: key.Namespace}, &bc) - return bc.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Check Site settings configMap creation + By("Expecting Site settings configmaps created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "site-settings-" + key.Name, Namespace: key.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Update drupalSite custom resource status fields to allow route conditions - By("Updating 'initialized' status field in drupalSite resource") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) - cr.Status.Conditions.SetCondition(status.Condition{Type: "Initialized", Status: "True"}) - return k8sClient.Status().Update(ctx, &cr) - }, timeout, interval).Should(Succeed()) + // Check PHP Cli configMap creation + By("Expecting PHP Cli configmaps created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "php-cli-config-" + key.Name, Namespace: key.Namespace}, &configmap) + return configmap.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Check Drupal service + By("Expecting Drupal service created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &svc) + return svc.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Update deployment status fields to allow 'ready' status field to be set on the drupalSite resource - By("Updating 'ReadyReplicas' and 'AvailableReplicas' status fields in deployment resource") - Eventually(func() error { - k8sClient.Get(ctx, key, &deploy) - deploy.Status.Replicas = 1 - deploy.Status.AvailableReplicas = 1 - deploy.Status.ReadyReplicas = 1 - return k8sClient.Status().Update(ctx, &deploy) - }, timeout, interval).Should(Succeed()) + // Check drupal persistentVolumeClaim + By("Expecting drupal persistentVolumeClaim created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "pv-claim-" + key.Name, Namespace: key.Namespace}, &pvc) + return pvc.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Check if the Schedule resource is created - By("Expecting Schedule to be created") - Eventually(func() error { - return k8sClient.Get(ctx, types.NamespacedName{Name: generateScheduleName(key.Namespace, key.Name), Namespace: veleroNamespace}, &schedule) - }, timeout, interval).Should(Succeed()) + // Check Drupal deployments + By("Expecting Drupal deployments created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &deploy) + return deploy.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Check Routes - By("Expecting Drupal Route(s) to be created") - for _, url := range cr.Spec.SiteURL { - route = routev1.Route{} - hash := md5.Sum([]byte(url)) + // Check sitebuilder-s2i imageStream + By("Expecting sitebuilder-s2i imageStream created") Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &route) - return route.ObjectMeta.OwnerReferences + k8sClient.Get(ctx, types.NamespacedName{Name: "sitebuilder-s2i-" + key.Name, Namespace: key.Namespace}, &is) + return is.ObjectMeta.OwnerReferences }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - } - // Check OidcReturnUris - By("Expecting OidcReturnURIs created") - for _, url := range cr.Spec.SiteURL { - oidcReturnUri = authz.OidcReturnURI{} - hash := md5.Sum([]byte(url)) + // Check Drush job + By("Expecting Drush job created") Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &oidcReturnUri) - return oidcReturnUri.ObjectMeta.OwnerReferences + k8sClient.Get(ctx, types.NamespacedName{Name: "ensure-site-install-" + key.Name, Namespace: key.Namespace}, &job) + return job.ObjectMeta.OwnerReferences }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - } - // Check gitlab webhook secret resource creation - By("Expecting Gitlab webhook secret created") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "gitlab-trigger-secret-" + key.Name, Namespace: key.Namespace}, &secret) - return secret.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + // Check S2I buildConfig + By("Expecting S2I buildConfig created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "sitebuilder-s2i-" + nameVersionHash(drupalSiteObject), Namespace: key.Namespace}, &bc) + return bc.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Check gitlab webhook URL updated on the drupalSite status - By("Expecting Gitlab webhook secret listed in the DrupalSite status") - Eventually(func() bool { - return cr.Status.GitlabWebhookURL == "https://api."+ClusterName+".okd.cern.ch:443/apis/build.openshift.io/v1/namespaces/"+drupalSiteObject.Namespace+"/buildconfigs/"+"sitebuilder-s2i-"+nameVersionHash(drupalSiteObject)+"/webhooks/"+string(secret.Name)+"/gitlab" - }, timeout, interval).Should(BeTrue()) + // Update drupalSite custom resource status fields to allow route conditions + By("Updating 'initialized' status field in drupalSite resource") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) + cr.Status.Conditions.SetCondition(status.Condition{Type: "Initialized", Status: "True"}) + return k8sClient.Status().Update(ctx, &cr) + }, timeout, interval).Should(Succeed()) + + // Update deployment status fields to allow 'ready' status field to be set on the drupalSite resource + By("Updating 'ReadyReplicas' and 'AvailableReplicas' status fields in deployment resource") + Eventually(func() error { + k8sClient.Get(ctx, key, &deploy) + deploy.Status.Replicas = 1 + deploy.Status.AvailableReplicas = 1 + deploy.Status.ReadyReplicas = 1 + return k8sClient.Status().Update(ctx, &deploy) + }, timeout, interval).Should(Succeed()) + + // Check if the Schedule resource is created + By("Expecting Schedule to be created") + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{Name: generateScheduleName(key.Namespace, key.Name), Namespace: veleroNamespace}, &schedule) + }, timeout, interval).Should(Succeed()) + + // Check Routes + By("Expecting Drupal Route(s) to be created") + for _, url := range cr.Spec.SiteURL { + route = routev1.Route{} + hash := md5.Sum([]byte(url)) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &route) + return route.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + } + + // Check OidcReturnUris + By("Expecting OidcReturnURIs created") + for _, url := range cr.Spec.SiteURL { + oidcReturnUri = authz.OidcReturnURI{} + hash := md5.Sum([]byte(url)) + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "-" + hex.EncodeToString(hash[0:4]), Namespace: key.Namespace}, &oidcReturnUri) + return oidcReturnUri.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + } + + // Check gitlab webhook secret resource creation + By("Expecting Gitlab webhook secret created") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "gitlab-trigger-secret-" + key.Name, Namespace: key.Namespace}, &secret) + return secret.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + + // Check gitlab webhook URL updated on the drupalSite status + By("Expecting Gitlab webhook secret listed in the DrupalSite status") + Eventually(func() bool { + return cr.Status.GitlabWebhookURL == "https://api."+ClusterName+".okd.cern.ch:443/apis/build.openshift.io/v1/namespaces/"+drupalSiteObject.Namespace+"/buildconfigs/"+"sitebuilder-s2i-"+nameVersionHash(drupalSiteObject)+"/webhooks/"+string(secret.Name)+"/gitlab" + }, timeout, interval).Should(BeTrue()) + }) }) }) - }) - - Describe("Creating a new backup resource", func() { - Context("for the drupalSite with s2i source", func() { - It("New velero backups created for the site should reflect in the drupalSite Status", func() { - By("Expecting drupalSite object created") - cr := drupalwebservicesv1alpha1.DrupalSite{} - Eventually(func() error { - return k8sClient.Get(ctx, key, &cr) - }, timeout, interval).Should(Succeed()) - // Create a backup resource for the drupalSite - hash := md5.Sum([]byte(key.Namespace)) - backup := velerov1.Backup{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "velero.io/v1", - Kind: "Backup", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: key.Name + "backup", - Namespace: veleroNamespace, + Describe("Creating a new backup resource", func() { + Context("for the drupalSite with s2i source", func() { + It("New velero backups created for the site should reflect in the drupalSite Status", func() { + By("Expecting drupalSite object created") + cr := drupalwebservicesv1alpha1.DrupalSite{} + Eventually(func() error { + return k8sClient.Get(ctx, key, &cr) + }, timeout, interval).Should(Succeed()) - Labels: map[string]string{ - "drupal.webservices.cern.ch/projectHash": hex.EncodeToString(hash[:]), - "drupal.webservices.cern.ch/project": key.Namespace, - "drupal.webservices.cern.ch/drupalSite": key.Name, + // Create a backup resource for the drupalSite + hash := md5.Sum([]byte(key.Namespace)) + backup := velerov1.Backup{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "velero.io/v1", + Kind: "Backup", }, - Annotations: map[string]string{ - "drupal.webservices.cern.ch/drupalSite": key.Name, - "drupal.webservices.cern.ch/project": key.Namespace, + ObjectMeta: metav1.ObjectMeta{ + Name: key.Name + "backup", + Namespace: veleroNamespace, + + Labels: map[string]string{ + "drupal.webservices.cern.ch/projectHash": hex.EncodeToString(hash[:]), + "drupal.webservices.cern.ch/project": key.Namespace, + "drupal.webservices.cern.ch/drupalSite": key.Name, + }, + Annotations: map[string]string{ + "drupal.webservices.cern.ch/drupalSite": key.Name, + "drupal.webservices.cern.ch/project": key.Namespace, + }, }, - }, - Status: velerov1.BackupStatus{ - Phase: velerov1.BackupPhaseCompleted, - }, - } + Status: velerov1.BackupStatus{ + Phase: velerov1.BackupPhaseCompleted, + }, + } - By("By creating a backup resource for the drupalSite") - Eventually(func() error { - return k8sClient.Create(ctx, &backup) - }, timeout, interval).Should(Succeed()) + By("By creating a backup resource for the drupalSite") + Eventually(func() error { + return k8sClient.Create(ctx, &backup) + }, timeout, interval).Should(Succeed()) - // Check if the backup resource is created - By("By creating a backup resource for the drupalSite") - backup1 := velerov1.Backup{} - Eventually(func() error { - return k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "backup", Namespace: veleroNamespace}, &backup1) - }, timeout, interval).Should(Succeed()) + // Check if the backup resource is created + By("By creating a backup resource for the drupalSite") + backup1 := velerov1.Backup{} + Eventually(func() error { + return k8sClient.Get(ctx, types.NamespacedName{Name: key.Name + "backup", Namespace: veleroNamespace}, &backup1) + }, timeout, interval).Should(Succeed()) - // Check for the Backup name in the Drupalsite Status - By("By checking for the Backup in the DrupalSite Status") - Eventually(func() bool { - k8sClient.Get(ctx, key, &cr) - return len(cr.Status.AvailableBackups) > 0 && cr.Status.AvailableBackups[0].BackupName == backup.Name - }, timeout, interval).Should(BeTrue()) + // Check for the Backup name in the Drupalsite Status + By("By checking for the Backup in the DrupalSite Status") + Eventually(func() bool { + k8sClient.Get(ctx, key, &cr) + return len(cr.Status.AvailableBackups) > 0 && cr.Status.AvailableBackups[0].BackupName == backup.Name + }, timeout, interval).Should(BeTrue()) + }) }) }) - }) - - Describe("Update the drupalsite object with s2i source", func() { - Context("With a different drupal Version", func() { - It("Should be updated successfully", func() { - key = types.NamespacedName{ - Name: Name + "-advanced", - Namespace: "advanced", - } - newVersion := "v9.3-2" - newReleaseSpec := "new" - // Create drupalSite object - cr := drupalwebservicesv1alpha1.DrupalSite{} - By("Expecting drupalSite object created") - Eventually(func() error { - return k8sClient.Get(ctx, key, &cr) - }, timeout, interval).Should(Succeed()) - - trueVar := true - expectedOwnerReference := metav1.OwnerReference{ - APIVersion: "drupal.webservices.cern.ch/v1alpha1", - Kind: "DrupalSite", - Name: key.Name, - UID: cr.UID, - Controller: &trueVar, - } - bc := buildv1.BuildConfig{} - deploy := appsv1.Deployment{} + Describe("Update the drupalsite object with s2i source", func() { + Context("With a different drupal Version", func() { + It("Should be updated successfully", func() { + key = types.NamespacedName{ + Name: Name + "-advanced", + Namespace: "advanced", + } + newVersion := "v9.3-2" + newReleaseSpec := "new" - By("Updating the version") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) - cr.Spec.Version.Name = newVersion - cr.Spec.Version.ReleaseSpec = newReleaseSpec - return k8sClient.Update(ctx, &cr) - }, timeout, interval).Should(Succeed()) + // Create drupalSite object + cr := drupalwebservicesv1alpha1.DrupalSite{} + By("Expecting drupalSite object created") + Eventually(func() error { + return k8sClient.Get(ctx, key, &cr) + }, timeout, interval).Should(Succeed()) - By("Expecting new S2I buildConfig to be updated") - Eventually(func() []metav1.OwnerReference { - k8sClient.Get(ctx, types.NamespacedName{Name: "sitebuilder-s2i-" + nameVersionHash(&cr), Namespace: key.Namespace}, &bc) - return bc.ObjectMeta.OwnerReferences - }, timeout, interval).Should(ContainElement(expectedOwnerReference)) + trueVar := true + expectedOwnerReference := metav1.OwnerReference{ + APIVersion: "drupal.webservices.cern.ch/v1alpha1", + Kind: "DrupalSite", + Name: key.Name, + UID: cr.UID, + Controller: &trueVar, + } + bc := buildv1.BuildConfig{} + deploy := appsv1.Deployment{} - // Check the annotation on the deployment - By("Expecting the new drupal Version on the pod annotation") - Eventually(func() bool { - k8sClient.Get(ctx, key, &deploy) - return deploy.Spec.Template.ObjectMeta.Annotations["releaseID"] == newVersion+"-"+newReleaseSpec - }, timeout, interval).Should(BeTrue()) + By("Updating the version") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) + cr.Spec.Version.Name = newVersion + cr.Spec.Version.ReleaseSpec = newReleaseSpec + return k8sClient.Update(ctx, &cr) + }, timeout, interval).Should(Succeed()) - pod := corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Pod", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: key.Name, - Namespace: key.Namespace, - Labels: map[string]string{"drupalSite": cr.Name, "app": "drupal"}, - Annotations: map[string]string{"releaseID": cr.Spec.Version.Name + "-" + cr.Spec.Version.ReleaseSpec}, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Image: "test-image", - Name: "test-image", - }}, - }, - } + By("Expecting new S2I buildConfig to be updated") + Eventually(func() []metav1.OwnerReference { + k8sClient.Get(ctx, types.NamespacedName{Name: "sitebuilder-s2i-" + nameVersionHash(&cr), Namespace: key.Namespace}, &bc) + return bc.ObjectMeta.OwnerReferences + }, timeout, interval).Should(ContainElement(expectedOwnerReference)) - // Since there is no deployment controller for creating pods. Create a pod manually to simulate a error in the update process - By("Expecting a new pod with required labels to be created") - Eventually(func() error { - return k8sClient.Create(ctx, &pod) - }, timeout, interval).Should(Succeed()) + // Check the annotation on the deployment + By("Expecting the new drupal Version on the pod annotation") + Eventually(func() bool { + k8sClient.Get(ctx, key, &deploy) + return deploy.Spec.Template.ObjectMeta.Annotations["releaseID"] == newVersion+"-"+newReleaseSpec + }, timeout, interval).Should(BeTrue()) - // Set the pod status phase to 'Failed' to simulate a error in the update process - By("Expecting a new pod status to be updated") - Eventually(func() error { - k8sClient.Get(ctx, key, &pod) - pod.Status.Phase = corev1.PodFailed - return k8sClient.Status().Update(ctx, &pod) - }, timeout, interval).Should(Succeed()) + pod := corev1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: key.Name, + Namespace: key.Namespace, + Labels: map[string]string{"drupalSite": cr.Name, "app": "drupal"}, + Annotations: map[string]string{"releaseID": cr.Spec.Version.Name + "-" + cr.Spec.Version.ReleaseSpec}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Image: "test-image", + Name: "test-image", + }}, + }, + } - // NOTE: Commenting this out temporarily. Refer to https://gitlab.cern.ch/drupal/paas/drupalsite-operator/-/issues/74 - // Check if the drupalSiteObject has 'codeUpdateFailed' status set - // By("Expecting 'codeUpdateFailed' status set on the drupalSiteObject") - // Eventually(func() bool { - // k8sClient.Get(ctx, key, &cr) - // return cr.ConditionTrue("CodeUpdateFailed") - // }, timeout, interval).Should(BeTrue()) + // Since there is no deployment controller for creating pods. Create a pod manually to simulate a error in the update process + By("Expecting a new pod with required labels to be created") + Eventually(func() error { + return k8sClient.Create(ctx, &pod) + }, timeout, interval).Should(Succeed()) - // NOTE: Commenting this out temporarily. Refer to https://gitlab.cern.ch/drupal/paas/drupalsite-operator/-/issues/74 - // Check if the drupalSiteObject has 'updateInProgress' annotation unset - // By("Expecting 'updateInProgress' annotation unset on the drupalSiteObject") - // Eventually(func() bool { - // k8sClient.Get(ctx, key, &cr) - // _, set := cr.Annotations["updateInProgress"] - // return set - // }, timeout, interval).Should(BeFalse()) + // Set the pod status phase to 'Failed' to simulate a error in the update process + By("Expecting a new pod status to be updated") + Eventually(func() error { + k8sClient.Get(ctx, key, &pod) + pod.Status.Phase = corev1.PodFailed + return k8sClient.Status().Update(ctx, &pod) + }, timeout, interval).Should(Succeed()) - // Update drupalSite Failsafe status to simulate a successful upgrade - By("Updating 'Failsafe' status field in drupalSite resource") - Eventually(func() error { - k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) - cr.Status.Failsafe = newVersion + "-" + newReleaseSpec - return k8sClient.Status().Update(ctx, &cr) - }, timeout, interval).Should(Succeed()) + // NOTE: Commenting this out temporarily. Refer to https://gitlab.cern.ch/drupal/paas/drupalsite-operator/-/issues/74 + // Check if the drupalSiteObject has 'codeUpdateFailed' status set + // By("Expecting 'codeUpdateFailed' status set on the drupalSiteObject") + // Eventually(func() bool { + // k8sClient.Get(ctx, key, &cr) + // return cr.ConditionTrue("CodeUpdateFailed") + // }, timeout, interval).Should(BeTrue()) + + // NOTE: Commenting this out temporarily. Refer to https://gitlab.cern.ch/drupal/paas/drupalsite-operator/-/issues/74 + // Check if the drupalSiteObject has 'updateInProgress' annotation unset + // By("Expecting 'updateInProgress' annotation unset on the drupalSiteObject") + // Eventually(func() bool { + // k8sClient.Get(ctx, key, &cr) + // _, set := cr.Annotations["updateInProgress"] + // return set + // }, timeout, interval).Should(BeFalse()) + + // Update drupalSite Failsafe status to simulate a successful upgrade + By("Updating 'Failsafe' status field in drupalSite resource") + Eventually(func() error { + k8sClient.Get(ctx, types.NamespacedName{Name: key.Name, Namespace: key.Namespace}, &cr) + cr.Status.Failsafe = newVersion + "-" + newReleaseSpec + return k8sClient.Status().Update(ctx, &cr) + }, timeout, interval).Should(Succeed()) - // Further tests need to be implemented, if we can bypass ExecErr with envtest + // Further tests need to be implemented, if we can bypass ExecErr with envtest + }) }) }) - }) - Describe("Deleting the drupalsite object", func() { - Context("With s2i spec", func() { - It("Should be deleted successfully", func() { - key = types.NamespacedName{ - Name: Name + "-advanced", - Namespace: "advanced", + Describe("Deleting the drupalsite object", func() { + Context("With s2i spec", func() { + It("Should be deleted successfully", func() { + key = types.NamespacedName{ + Name: Name + "-advanced", + Namespace: "advanced", } drupalSiteObject = &drupalwebservicesv1alpha1.DrupalSite{ TypeMeta: metav1.TypeMeta{ @@ -1350,6 +1353,9 @@ var _ = Describe("DrupalSite controller", func() { ObjectMeta: metav1.ObjectMeta{ Name: key.Name, Namespace: key.Namespace, + Labels: map[string]string{ + "drupal.webservices.cern.ch/isPrimary": "true", + }, }, Spec: drupalwebservicesv1alpha1.DrupalSiteSpec{ Version: drupalwebservicesv1alpha1.Version{ @@ -1416,6 +1422,9 @@ var _ = Describe("DrupalSite controller", func() { ObjectMeta: metav1.ObjectMeta{ Name: key.Name, Namespace: key.Namespace, + Labels: map[string]string{ + "drupal.webservices.cern.ch/isPrimary": "true", + }, }, Spec: drupalwebservicesv1alpha1.DrupalSiteSpec{ Version: drupalwebservicesv1alpha1.Version{ @@ -1707,6 +1716,9 @@ var _ = Describe("DrupalSite controller", func() { ObjectMeta: metav1.ObjectMeta{ Name: key.Name, Namespace: key.Namespace, + Labels: map[string]string{ + "drupal.webservices.cern.ch/isPrimary": "true", + }, }, Spec: drupalwebservicesv1alpha1.DrupalSiteSpec{ Version: drupalwebservicesv1alpha1.Version{