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 }