diff --git a/pkg/controller/operators/olm/operator.go b/pkg/controller/operators/olm/operator.go index bb0246b292c..ce0d9e0bea5 100644 --- a/pkg/controller/operators/olm/operator.go +++ b/pkg/controller/operators/olm/operator.go @@ -295,9 +295,7 @@ func (a *Operator) syncRBAC(obj interface{}) (syncError error) { } func namespacesChanged(clusterNamespaces []corev1.Namespace, statusNamespaces []corev1.Namespace) bool { - nsCount := len(clusterNamespaces) - - if len(statusNamespaces) != nsCount { + if len(clusterNamespaces) != len(statusNamespaces) { return true } @@ -329,11 +327,16 @@ func (a *Operator) updateDeploymentAnnotation(op *v1alpha2.OperatorGroup) (error if !namespacesChanged(namespaceList.Items, op.Status.Namespaces) { // status is current with correct namespaces, so no further updates required - log.Debugf("No namespace changes detected, found: %v", namespaceList.Items) return nil, namespaceList.Items } - op.Status.Namespaces = namespaceList.Items + log.Debugf("Namespace change detected, found: %v", namespaceList.Items) + op.Status.Namespaces = make([]corev1.Namespace, len(namespaceList.Items)) + copy(op.Status.Namespaces, namespaceList.Items) op.Status.LastUpdated = timeNow() + _, err = a.client.OperatorsV1alpha2().OperatorGroups(op.Namespace).UpdateStatus(op) + if err != nil { + return err, namespaceList.Items + } currentNamespace := op.GetNamespace() csvsInNamespace := a.csvsInNamespace(currentNamespace) @@ -350,13 +353,14 @@ func (a *Operator) updateDeploymentAnnotation(op *v1alpha2.OperatorGroup) (error apiEditPolicyRules = append(apiEditPolicyRules, rbacv1.PolicyRule{Verbs: []string{"create", "update", "patch", "delete"}, APIGroups: []string{owned.Name}, Resources: []string{owned.Kind}}) apiViewPolicyRules = append(apiViewPolicyRules, rbacv1.PolicyRule{Verbs: []string{"get", "list", "watch"}, APIGroups: []string{owned.Name}, Resources: []string{owned.Kind}}) } - for _, owned := range csv.Spec.APIServiceDefinitions.Owned { - resourceNames := []string{} - for _, resource := range owned.Resources { - resourceNames = append(resourceNames, resource.Name) - } - managerPolicyRules = append(managerPolicyRules, rbacv1.PolicyRule{Verbs: []string{"*"}, APIGroups: []string{owned.Name}, Resources: resourceNames}) - } + // TODO: definitely remove? + // for _, owned := range csv.Spec.APIServiceDefinitions.Owned { + // resourceNames := []string{} + // for _, resource := range owned.Resources { + // resourceNames = append(resourceNames, resource.Name) + // } + // managerPolicyRules = append(managerPolicyRules, rbacv1.PolicyRule{Verbs: []string{"*"}, APIGroups: []string{owned.Name}, Resources: resourceNames}) + // } clusterRole := &rbacv1.ClusterRole{ Rules: managerPolicyRules, } @@ -398,9 +402,7 @@ func (a *Operator) updateDeploymentAnnotation(op *v1alpha2.OperatorGroup) (error // write above namespaces to watch in every deployment for _, ns := range nsList { - //deploymentList, err := a.OpClient.KubernetesInterface().AppsV1().Deployments(ns).List(metav1.ListOptions{}) deploymentList, err := a.deploymentLister[ns].List(labels.Everything()) - log.Debugf("JPEELER: looking at ns %v deployments:%v\n", ns, deploymentList) if err != nil { return err, namespaceList.Items } @@ -447,23 +449,35 @@ func (a *Operator) syncOperatorGroups(obj interface{}) error { return err } - for _, ns := range targetedNamespaces { - csvsInNamespace := a.csvsInNamespace(ns.Name) - for _, csv := range csvsInNamespace { - if csv.Status.Phase == v1alpha1.CSVPhaseSucceeded { - newCSV := csv.DeepCopy() - newCSV.Status = v1alpha1.ClusterServiceVersionStatus{ - Message: "CSV copied to target namespace", - Reason: v1alpha1.CSVReasonCopied, - LastUpdateTime: timeNow(), - } - metav1.SetMetaDataAnnotation(&newCSV.ObjectMeta, "olm.originalCSV", fmt.Sprintf("%v/%v", csv.GetNamespace(), csv.GetName())) - ownerutil.AddNonBlockingOwner(newCSV, csv) - if newCSV.GetNamespace() != ns.Name { - _, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(newCSV.GetNamespace()).Create(newCSV) - if err != nil { - return err - } + csvsInNamespace := a.csvsInNamespace(op.Namespace) + for _, csv := range csvsInNamespace { + // TODO: handle CSV copying in a different place + // if csv.Status.Phase != v1alpha1.CSVPhaseSucceeded { + // log.Debugf("JPEELER: continuing on, skipping CSV %v\n", csv.Name) + // continue + // } + + // create new CSV instead of DeepCopy as namespace and resource version (and status) will be different + newCSV := v1alpha1.ClusterServiceVersion{ + ObjectMeta: metav1.ObjectMeta{ + Name: csv.Name, + }, + Spec: *csv.Spec.DeepCopy(), + Status: v1alpha1.ClusterServiceVersionStatus{ + Message: "CSV copied to target namespace", + Reason: v1alpha1.CSVReasonCopied, + LastUpdateTime: timeNow(), + }, + } + + metav1.SetMetaDataAnnotation(&newCSV.ObjectMeta, "olm.originalCSV", fmt.Sprintf("%v/%v", csv.GetNamespace(), csv.GetName())) + ownerutil.AddNonBlockingOwner(&newCSV, csv) + for _, ns := range targetedNamespaces { + newCSV.SetNamespace(ns.Name) + if ns.Name != op.Namespace { + _, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(newCSV.GetNamespace()).Create(&newCSV) + if err != nil { + return err } } } diff --git a/pkg/controller/operators/olm/operator_test.go b/pkg/controller/operators/olm/operator_test.go index b19dc6e7638..de459a9e03e 100644 --- a/pkg/controller/operators/olm/operator_test.go +++ b/pkg/controller/operators/olm/operator_test.go @@ -1031,7 +1031,7 @@ func TestTransitionCSV(t *testing.T) { } func TestSyncOperatorGroups(t *testing.T) { - log.SetLevel(log.DebugLevel) + //log.SetLevel(log.DebugLevel) nowTime := metav1.Date(2006, time.January, 2, 15, 4, 5, 0, time.FixedZone("MST", -7*3600)) timeNow = func() metav1.Time { return nowTime } @@ -1076,7 +1076,6 @@ func TestSyncOperatorGroups(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "operator-group-1", Namespace: testNS, - Labels: map[string]string{"app": "matchLabel"}, }, Spec: v1alpha2.OperatorGroupSpec{ Selector: metav1.LabelSelector{ @@ -1240,6 +1239,9 @@ func TestSyncOperatorGroups(t *testing.T) { informerFactory.Start(stopCh) informerFactory.WaitForCacheSync(stopCh) + // Could not put this in initialObjs - got "no kind is registered for the type v1alpha2.OperatorGroup" + op.client.OperatorsV1alpha2().OperatorGroups(tc.inputGroup.Namespace).Create(&tc.inputGroup) + err = op.syncOperatorGroups(&tc.inputGroup) require.NoError(t, err) assert.Equal(t, tc.expectedStatus, tc.inputGroup.Status) diff --git a/test/e2e/operator_groups_e2e_test.go b/test/e2e/operator_groups_e2e_test.go new file mode 100644 index 00000000000..e8df796e3f3 --- /dev/null +++ b/test/e2e/operator_groups_e2e_test.go @@ -0,0 +1,97 @@ +package e2e + +import ( + "testing" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/util/wait" + + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha2" + + "github.com/coreos/go-semver/semver" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// TODO: make tests for: +// RBAC +// Deployment annotation verification + +// func NoTestCreateOperatorGroupWithMatchingNamespace(t *testing.T) { +// // Create namespace with specific label +// // Create deployment in namespace +// // Create operator group that watches namespace and uses specific label +// // Verify operator group status contains correct status +// // Verify deployments have correct namespace annotation + +// // c := newKubeClient(t) + +// // deployment := appsv1. + +// // c.CreateDeployment() + +// } + +func TestCreateOperatorCSVCopy(t *testing.T) { + // create operator namespace + // create operator group in OLM namespace + // create CSV in OLM namespace + // verify CSV is copied to operator namespace + + log.SetLevel(log.DebugLevel) + c := newKubeClient(t) + crc := newCRClient(t) + operatorNamespaceName := testNamespace + "-operator" + csvName := "acsv-that-is-unique" // must be lowercase for DNS-1123 validation + matchingLabel := map[string]string{"app": "matchLabel"} + + operatorNamespace := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: operatorNamespaceName, + Labels: matchingLabel, + }, + } + _, err := c.KubernetesInterface().CoreV1().Namespaces().Create(&operatorNamespace) + require.NoError(t, err) + + operatorGroup := v1alpha2.OperatorGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: "e2e-operator-group", + Namespace: testNamespace, + }, + Spec: v1alpha2.OperatorGroupSpec{ + Selector: metav1.LabelSelector{ + MatchLabels: matchingLabel, + }, + }, + } + _, err = crc.OperatorsV1alpha2().OperatorGroups(testNamespace).Create(&operatorGroup) + require.NoError(t, err) + + aCSV := newCSV(csvName, testNamespace, "", *semver.New("0.0.0"), nil, nil, newNginxInstallStrategy("aspec", nil, nil)) + createdCSV, err := crc.OperatorsV1alpha1().ClusterServiceVersions(testNamespace).Create(&aCSV) + require.NoError(t, err) + + var csvCopy *v1alpha1.ClusterServiceVersion + err = wait.Poll(pollInterval, pollDuration, func() (bool, error) { + csvCopy, err = crc.OperatorsV1alpha1().ClusterServiceVersions(operatorNamespaceName).Get(csvName, metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + return false, nil + } + return false, err + } + return true, nil + }) + require.Equal(t, createdCSV.Name, csvCopy.Name) + require.Equal(t, createdCSV.Spec, csvCopy.Spec) + + // clean up + err = c.KubernetesInterface().CoreV1().Namespaces().Delete(operatorNamespaceName, &metav1.DeleteOptions{}) + if err != nil { + t.Errorf("Operator namespace cleanup failed: %v\n", err) + } +}