github.com/operator-framework/operator-lifecycle-manager@v0.30.0/test/e2e/disabling_copied_csv_e2e_test.go (about)

     1  package e2e
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	. "github.com/onsi/ginkgo/v2"
     9  	. "github.com/onsi/gomega"
    10  	operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
    11  	operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
    12  	"github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx"
    13  	appsv1 "k8s.io/api/apps/v1"
    14  	corev1 "k8s.io/api/core/v1"
    15  	"k8s.io/apimachinery/pkg/api/meta"
    16  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  	"k8s.io/apimachinery/pkg/fields"
    18  	k8slabels "k8s.io/apimachinery/pkg/labels"
    19  	"k8s.io/apimachinery/pkg/selection"
    20  	apitypes "k8s.io/apimachinery/pkg/types"
    21  	"sigs.k8s.io/controller-runtime/pkg/client"
    22  )
    23  
    24  const (
    25  	olmDeploymentName                       = "olm-operator"
    26  	protectedCopiedCSVNamespacesRuntimeFlag = "--protectedCopiedCSVNamespaces"
    27  )
    28  
    29  var _ = Describe("Disabling copied CSVs", func() {
    30  	var (
    31  		generatedNamespace              corev1.Namespace
    32  		csv                             operatorsv1alpha1.ClusterServiceVersion
    33  		nonTerminatingNamespaceSelector = fields.ParseSelectorOrDie("status.phase!=Terminating")
    34  		protectedCopiedCSVNamespaces    = map[string]struct{}{}
    35  	)
    36  
    37  	BeforeEach(func() {
    38  		nsname := genName("disabling-copied-csv-e2e-")
    39  		og := operatorsv1.OperatorGroup{
    40  			ObjectMeta: metav1.ObjectMeta{
    41  				Name:      fmt.Sprintf("%s-operatorgroup", nsname),
    42  				Namespace: nsname,
    43  			},
    44  		}
    45  		generatedNamespace = SetupGeneratedTestNamespaceWithOperatorGroup(nsname, og)
    46  
    47  		csv = operatorsv1alpha1.ClusterServiceVersion{
    48  			ObjectMeta: metav1.ObjectMeta{
    49  				Name:      genName("csv-toggle-test-"),
    50  				Namespace: nsname,
    51  			},
    52  			Spec: operatorsv1alpha1.ClusterServiceVersionSpec{
    53  				InstallStrategy: newNginxInstallStrategy(genName("csv-toggle-test-"), nil, nil),
    54  				InstallModes: []operatorsv1alpha1.InstallMode{
    55  					{
    56  						Type:      operatorsv1alpha1.InstallModeTypeAllNamespaces,
    57  						Supported: true,
    58  					},
    59  				},
    60  			},
    61  		}
    62  		err := ctx.Ctx().Client().Create(context.Background(), &csv)
    63  		Expect(err).ShouldNot(HaveOccurred())
    64  	})
    65  
    66  	AfterEach(func() {
    67  		Eventually(func() error {
    68  			return client.IgnoreNotFound(ctx.Ctx().Client().Delete(context.Background(), &csv))
    69  		}).Should(Succeed())
    70  		TeardownNamespace(generatedNamespace.GetName())
    71  	})
    72  
    73  	When("an operator is installed in AllNamespace mode", func() {
    74  		It("should have Copied CSVs in all other namespaces", func() {
    75  			Eventually(func() error {
    76  				requirement, err := k8slabels.NewRequirement(operatorsv1alpha1.CopiedLabelKey, selection.Equals, []string{csv.GetNamespace()})
    77  				if err != nil {
    78  					return err
    79  				}
    80  
    81  				var copiedCSVs operatorsv1alpha1.ClusterServiceVersionList
    82  				err = ctx.Ctx().Client().List(context.TODO(), &copiedCSVs, &client.ListOptions{
    83  					LabelSelector: k8slabels.NewSelector().Add(*requirement),
    84  				})
    85  				if err != nil {
    86  					return err
    87  				}
    88  
    89  				var namespaces corev1.NamespaceList
    90  				if err := ctx.Ctx().Client().List(context.TODO(), &namespaces, &client.ListOptions{
    91  					FieldSelector: nonTerminatingNamespaceSelector,
    92  				}); err != nil {
    93  					return err
    94  				}
    95  
    96  				if len(namespaces.Items)-1 != len(copiedCSVs.Items) {
    97  					return fmt.Errorf("%d copied CSVs found, expected %d", len(copiedCSVs.Items), len(namespaces.Items)-1)
    98  				}
    99  
   100  				return nil
   101  			}).Should(Succeed())
   102  		})
   103  	})
   104  
   105  	When("Copied CSVs are disabled", func() {
   106  		BeforeEach(func() {
   107  			Eventually(func() error {
   108  				var olmConfig operatorsv1.OLMConfig
   109  				if err := ctx.Ctx().Client().Get(context.TODO(), apitypes.NamespacedName{Name: "cluster"}, &olmConfig); err != nil {
   110  					ctx.Ctx().Logf("Error getting olmConfig %v", err)
   111  					return err
   112  				}
   113  
   114  				By(`Exit early if copied CSVs are disabled.`)
   115  				if !olmConfig.CopiedCSVsAreEnabled() {
   116  					return nil
   117  				}
   118  
   119  				olmConfig.Spec = operatorsv1.OLMConfigSpec{
   120  					Features: &operatorsv1.Features{
   121  						DisableCopiedCSVs: getPointer(true),
   122  					},
   123  				}
   124  
   125  				if err := ctx.Ctx().Client().Update(context.TODO(), &olmConfig); err != nil {
   126  					ctx.Ctx().Logf("Error setting olmConfig %v", err)
   127  					return err
   128  				}
   129  
   130  				return nil
   131  			}).Should(Succeed())
   132  
   133  			Eventually(func() error {
   134  				return setProtectedCopiedCSVNamespaces(protectedCopiedCSVNamespaces)
   135  			}).Should(Succeed())
   136  		})
   137  
   138  		It("should not have any copied CSVs", func() {
   139  			Eventually(func() error {
   140  				requirement, err := k8slabels.NewRequirement(operatorsv1alpha1.CopiedLabelKey, selection.Equals, []string{csv.GetNamespace()})
   141  				if err != nil {
   142  					return err
   143  				}
   144  
   145  				var copiedCSVs operatorsv1alpha1.ClusterServiceVersionList
   146  				err = ctx.Ctx().Client().List(context.TODO(), &copiedCSVs, &client.ListOptions{
   147  					LabelSelector: k8slabels.NewSelector().Add(*requirement),
   148  				})
   149  				if err != nil {
   150  					return err
   151  				}
   152  
   153  				if numCSVs := len(copiedCSVs.Items); numCSVs != len(protectedCopiedCSVNamespaces) {
   154  					return fmt.Errorf("Found %d copied CSVs, should be %d", numCSVs, len(protectedCopiedCSVNamespaces))
   155  				}
   156  
   157  				for _, csv := range copiedCSVs.Items {
   158  					if _, ok := protectedCopiedCSVNamespaces[csv.GetNamespace()]; !ok {
   159  						return fmt.Errorf("copied CSV %s/%s should not exist in the given namespace", csv.GetNamespace(), csv.GetName())
   160  					}
   161  				}
   162  				return nil
   163  			}).Should(Succeed())
   164  		})
   165  
   166  		It("should be reflected in the olmConfig.Status.Condition array that the expected number of copied CSVs exist", func() {
   167  			Eventually(func() error {
   168  				var olmConfig operatorsv1.OLMConfig
   169  				if err := ctx.Ctx().Client().Get(context.TODO(), apitypes.NamespacedName{Name: "cluster"}, &olmConfig); err != nil {
   170  					return err
   171  				}
   172  
   173  				foundCondition := meta.FindStatusCondition(olmConfig.Status.Conditions, operatorsv1.DisabledCopiedCSVsConditionType)
   174  				if foundCondition == nil {
   175  					return fmt.Errorf("%s condition not found", operatorsv1.DisabledCopiedCSVsConditionType)
   176  				}
   177  
   178  				expectedCondition := metav1.Condition{
   179  					Reason:  "CopiedCSVsDisabled",
   180  					Message: "Copied CSVs are disabled and no unexpected copied CSVs were found for operators installed in AllNamespace mode",
   181  					Status:  metav1.ConditionTrue,
   182  				}
   183  
   184  				if foundCondition.Reason != expectedCondition.Reason ||
   185  					foundCondition.Message != expectedCondition.Message ||
   186  					foundCondition.Status != expectedCondition.Status {
   187  					return fmt.Errorf("condition does not have expected reason, message, and status. Expected %v, got %v", expectedCondition, foundCondition)
   188  				}
   189  
   190  				return nil
   191  			}).Should(Succeed())
   192  		})
   193  	})
   194  
   195  	When("Copied CSVs are toggled back on", func() {
   196  
   197  		BeforeEach(func() {
   198  			Eventually(func() error {
   199  				var olmConfig operatorsv1.OLMConfig
   200  				if err := ctx.Ctx().Client().Get(context.TODO(), apitypes.NamespacedName{Name: "cluster"}, &olmConfig); err != nil {
   201  					return err
   202  				}
   203  
   204  				By(`Exit early if copied CSVs are enabled.`)
   205  				if olmConfig.CopiedCSVsAreEnabled() {
   206  					return nil
   207  				}
   208  
   209  				olmConfig.Spec = operatorsv1.OLMConfigSpec{
   210  					Features: &operatorsv1.Features{
   211  						DisableCopiedCSVs: getPointer(false),
   212  					},
   213  				}
   214  
   215  				if err := ctx.Ctx().Client().Update(context.TODO(), &olmConfig); err != nil {
   216  					return err
   217  				}
   218  
   219  				return nil
   220  			}).Should(Succeed())
   221  		})
   222  
   223  		It("should have copied CSVs in all other Namespaces", func() {
   224  			Eventually(func() error {
   225  				By(`find copied csvs...`)
   226  				requirement, err := k8slabels.NewRequirement(operatorsv1alpha1.CopiedLabelKey, selection.Equals, []string{csv.GetNamespace()})
   227  				if err != nil {
   228  					return err
   229  				}
   230  
   231  				var copiedCSVs operatorsv1alpha1.ClusterServiceVersionList
   232  				err = ctx.Ctx().Client().List(context.TODO(), &copiedCSVs, &client.ListOptions{
   233  					LabelSelector: k8slabels.NewSelector().Add(*requirement),
   234  				})
   235  				if err != nil {
   236  					return err
   237  				}
   238  
   239  				var namespaces corev1.NamespaceList
   240  				if err := ctx.Ctx().Client().List(context.TODO(), &namespaces, &client.ListOptions{FieldSelector: nonTerminatingNamespaceSelector}); err != nil {
   241  					return err
   242  				}
   243  
   244  				if len(namespaces.Items)-1 != len(copiedCSVs.Items) {
   245  					return fmt.Errorf("%d copied CSVs found, expected %d", len(copiedCSVs.Items), len(namespaces.Items)-1)
   246  				}
   247  
   248  				return nil
   249  			}).Should(Succeed())
   250  		})
   251  
   252  		It("should be reflected in the olmConfig.Status.Condition array that the expected number of copied CSVs exist", func() {
   253  			Eventually(func() error {
   254  				var olmConfig operatorsv1.OLMConfig
   255  				if err := ctx.Ctx().Client().Get(context.TODO(), apitypes.NamespacedName{Name: "cluster"}, &olmConfig); err != nil {
   256  					return err
   257  				}
   258  				foundCondition := meta.FindStatusCondition(olmConfig.Status.Conditions, operatorsv1.DisabledCopiedCSVsConditionType)
   259  				if foundCondition == nil {
   260  					return fmt.Errorf("%s condition not found", operatorsv1.DisabledCopiedCSVsConditionType)
   261  				}
   262  
   263  				expectedCondition := metav1.Condition{
   264  					Reason:  "CopiedCSVsEnabled",
   265  					Message: "Copied CSVs are enabled and present across the cluster",
   266  					Status:  metav1.ConditionFalse,
   267  				}
   268  
   269  				if foundCondition.Reason != expectedCondition.Reason ||
   270  					foundCondition.Message != expectedCondition.Message ||
   271  					foundCondition.Status != expectedCondition.Status {
   272  					return fmt.Errorf("condition does not have expected reason, message, and status. Expected %v, got %v", expectedCondition, foundCondition)
   273  				}
   274  
   275  				return nil
   276  			}).Should(Succeed())
   277  		})
   278  	})
   279  })
   280  
   281  func setProtectedCopiedCSVNamespaces(protectedCopiedCSVNamespaces map[string]struct{}) error {
   282  	var olmDeployment appsv1.Deployment
   283  	if err := ctx.Ctx().Client().Get(context.TODO(), apitypes.NamespacedName{Name: olmDeploymentName, Namespace: operatorNamespace}, &olmDeployment); err != nil {
   284  		return err
   285  	}
   286  
   287  	if protectedNamespaceArgument := getRuntimeFlagValue(&olmDeployment, olmDeploymentName, protectedCopiedCSVNamespacesRuntimeFlag); protectedNamespaceArgument != "" {
   288  		for _, namespace := range strings.Split(protectedNamespaceArgument, ",") {
   289  			protectedCopiedCSVNamespaces[namespace] = struct{}{}
   290  		}
   291  	}
   292  
   293  	return nil
   294  }
   295  
   296  func getRuntimeFlagValue(deployment *appsv1.Deployment, containerName string, runtimeFlag string) string {
   297  	for _, container := range deployment.Spec.Template.Spec.Containers {
   298  		if container.Name == containerName {
   299  			for i := range container.Args {
   300  				if container.Args[i] == runtimeFlag && len(container.Args) > i+1 {
   301  					return container.Args[i+1]
   302  				}
   303  			}
   304  		}
   305  	}
   306  	return ""
   307  }