github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/operators/adoption_controller_test.go (about) 1 package operators 2 3 import ( 4 "context" 5 "fmt" 6 7 . "github.com/onsi/ginkgo/v2" 8 . "github.com/onsi/gomega" 9 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" 10 appsv1 "k8s.io/api/apps/v1" 11 corev1 "k8s.io/api/core/v1" 12 rbacv1 "k8s.io/api/rbac/v1" 13 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 14 apierrors "k8s.io/apimachinery/pkg/api/errors" 15 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 "k8s.io/client-go/tools/reference" 17 apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" 18 "sigs.k8s.io/controller-runtime/pkg/client" 19 20 operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" 21 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/decorators" 22 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil" 23 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/testobj" 24 ) 25 26 var _ = Describe("Adoption Controller", func() { 27 var ( 28 ctx context.Context 29 ) 30 31 BeforeEach(func() { 32 ctx = context.Background() 33 }) 34 35 Describe("Component label generation", func() { 36 var ( 37 created []client.Object 38 ) 39 40 BeforeEach(func() { 41 created = []client.Object{} 42 }) 43 44 JustAfterEach(func() { 45 for _, obj := range created { 46 Eventually(func() error { 47 err := k8sClient.Delete(ctx, obj) 48 if apierrors.IsNotFound(err) { 49 return nil 50 } 51 52 return err 53 }).Should(Succeed()) 54 } 55 }) 56 57 Context("a subscription", func() { 58 var ( 59 ns *corev1.Namespace 60 sub *operatorsv1alpha1.Subscription 61 ) 62 63 BeforeEach(func() { 64 ns = &corev1.Namespace{} 65 ns.SetName(genName("operators-")) 66 67 Eventually(func() error { 68 return k8sClient.Create(ctx, ns) 69 }, timeout, interval).Should(Succeed()) 70 created = append(created, ns) 71 }) 72 73 Context("with a package", func() { 74 var ( 75 componentLabelKey string 76 installed *operatorsv1alpha1.ClusterServiceVersion 77 ) 78 79 BeforeEach(func() { 80 sub = &operatorsv1alpha1.Subscription{ 81 Spec: &operatorsv1alpha1.SubscriptionSpec{ 82 Package: "poultry", 83 }, 84 } 85 sub.SetNamespace(ns.GetName()) 86 sub.SetName(sub.Spec.Package) 87 88 Eventually(func() error { 89 return k8sClient.Create(ctx, sub) 90 }, timeout, interval).Should(Succeed()) 91 created = append(created, sub) 92 93 // Set the Subscription's status separately 94 Eventually(func() error { 95 if err := k8sClient.Get(ctx, testobj.NamespacedName(sub), sub); err != nil { 96 return err 97 } 98 sub.Status = operatorsv1alpha1.SubscriptionStatus{ 99 InstalledCSV: "turkey", 100 LastUpdated: metav1.Now(), 101 } 102 return k8sClient.Status().Update(ctx, sub) 103 }, timeout, interval).Should(Succeed()) 104 105 componentLabelKey = fmt.Sprintf("%s%s.%s", decorators.ComponentLabelKeyPrefix, sub.Spec.Package, sub.GetNamespace()) 106 }) 107 108 Context("that references an existing installplan", func() { 109 var ( 110 ip *operatorsv1alpha1.InstallPlan 111 ) 112 113 BeforeEach(func() { 114 ip = fixtures.Fill(&operatorsv1alpha1.InstallPlan{}).(*operatorsv1alpha1.InstallPlan) 115 ip.SetNamespace(sub.GetNamespace()) 116 ip.SetName(genName("poultry-")) 117 118 Eventually(func() error { 119 owned := testobj.WithOwner(sub, ip) 120 return k8sClient.Create(ctx, owned) 121 }).Should(Succeed()) 122 created = append(created, ip) 123 124 ref, err := reference.GetReference(scheme, ip) 125 Expect(err).ToNot(HaveOccurred()) 126 127 // Set the Subscription's status separately 128 status := sub.DeepCopy().Status 129 status.InstallPlanRef = ref 130 Eventually(func() error { 131 if err := k8sClient.Get(ctx, testobj.NamespacedName(sub), sub); err != nil { 132 return err 133 } 134 sub.Status = status 135 136 return k8sClient.Status().Update(ctx, sub) 137 }).Should(Succeed()) 138 }) 139 140 Context("and has other, non-latest, adopted installplans", func() { 141 var ( 142 ips []*operatorsv1alpha1.InstallPlan 143 ) 144 145 BeforeEach(func() { 146 for i := 0; i < 4; i++ { 147 ip := fixtures.Fill(&operatorsv1alpha1.InstallPlan{}).(*operatorsv1alpha1.InstallPlan) 148 ip.SetNamespace(sub.GetNamespace()) 149 ip.SetName(genName("")) 150 ip.SetLabels(map[string]string{ 151 componentLabelKey: "", 152 }) 153 154 Eventually(func() error { 155 return k8sClient.Create(ctx, ip) 156 }).Should(Succeed()) 157 158 created = append(created, ip) 159 ips = append(ips, ip) 160 } 161 162 }) 163 164 Specify("correct component labels", func() { 165 installPlan := ip.DeepCopy() 166 Eventually(func() (map[string]string, error) { 167 err := k8sClient.Get(ctx, testobj.NamespacedName(ip), installPlan) 168 return installPlan.GetLabels(), err 169 }).Should(HaveKey(componentLabelKey)) 170 171 for _, ip := range ips { 172 Eventually(func() (map[string]string, error) { 173 err := k8sClient.Get(ctx, testobj.NamespacedName(ip), ip) 174 return ip.GetLabels(), err 175 }, timeout, interval).ShouldNot(HaveKey(componentLabelKey)) 176 } 177 178 }) 179 180 }) 181 }) 182 183 Context("that has an existing installed csv", func() { 184 var ( 185 providedCRD *apiextensionsv1.CustomResourceDefinition 186 ) 187 188 BeforeEach(func() { 189 providedCRD = fixtures.Fill(&apiextensionsv1.CustomResourceDefinition{}).(*apiextensionsv1.CustomResourceDefinition) 190 installed = &operatorsv1alpha1.ClusterServiceVersion{ 191 Spec: operatorsv1alpha1.ClusterServiceVersionSpec{ 192 CustomResourceDefinitions: operatorsv1alpha1.CustomResourceDefinitions{ 193 Owned: []operatorsv1alpha1.CRDDescription{ 194 { 195 Name: providedCRD.GetName(), 196 Kind: providedCRD.Spec.Names.Kind, 197 Version: providedCRD.Spec.Versions[0].Name, 198 }, 199 }, 200 }, 201 InstallStrategy: operatorsv1alpha1.NamedInstallStrategy{ 202 StrategyName: operatorsv1alpha1.InstallStrategyNameDeployment, 203 StrategySpec: operatorsv1alpha1.StrategyDetailsDeployment{ 204 DeploymentSpecs: []operatorsv1alpha1.StrategyDeploymentSpec{}, 205 }, 206 }, 207 }, 208 } 209 installed.SetNamespace(ns.GetName()) 210 installed.SetName(sub.Status.InstalledCSV) 211 212 Eventually(func() error { 213 return k8sClient.Create(ctx, installed) 214 }, timeout, interval).Should(Succeed()) 215 created = append(created, installed) 216 }) 217 218 Context("that has no resources owned by the installed csv", func() { 219 Specify("a component label", func() { 220 Eventually(func() (map[string]string, error) { 221 latest := &operatorsv1alpha1.ClusterServiceVersion{} 222 err := k8sClient.Get(ctx, testobj.NamespacedName(installed), latest) 223 224 return latest.GetLabels(), err 225 }, timeout, interval).Should(HaveKey(componentLabelKey)) 226 }) 227 }) 228 229 Context("with an existing provided CRD", func() { 230 231 BeforeEach(func() { 232 Eventually(func() error { 233 return k8sClient.Create(ctx, providedCRD) 234 }, timeout, interval).Should(Succeed()) 235 created = append(created, providedCRD) 236 237 Eventually(func() (map[string]string, error) { 238 latest := &apiextensionsv1.CustomResourceDefinition{} 239 err := k8sClient.Get(ctx, testobj.NamespacedName(providedCRD), latest) 240 241 return latest.GetLabels(), err 242 }, timeout, interval).Should(HaveKey(componentLabelKey)) 243 }) 244 245 Context("when its component label is removed", func() { 246 247 BeforeEach(func() { 248 Eventually(func() error { 249 latest := &apiextensionsv1.CustomResourceDefinition{} 250 if err := k8sClient.Get(ctx, testobj.NamespacedName(providedCRD), latest); err != nil { 251 return err 252 } 253 254 if len(latest.GetLabels()) == 0 { 255 return nil 256 } 257 delete(latest.GetLabels(), componentLabelKey) 258 259 return k8sClient.Update(ctx, latest) 260 }, timeout, interval).Should(Succeed()) 261 }) 262 263 It("should be relabelled", func() { 264 Eventually(func() (map[string]string, error) { 265 latest := &apiextensionsv1.CustomResourceDefinition{} 266 err := k8sClient.Get(ctx, testobj.NamespacedName(providedCRD), latest) 267 268 return latest.GetLabels(), err 269 }, timeout, interval).Should(HaveKey(componentLabelKey)) 270 }) 271 }) 272 }) 273 274 Context("that has resources owned by the installed csv", func() { 275 var ( 276 components []testobj.RuntimeMetaObject 277 ) 278 279 BeforeEach(func() { 280 Eventually(func() error { 281 return k8sClient.Get(ctx, testobj.NamespacedName(installed), installed) 282 }, timeout, interval).Should(Succeed()) 283 284 namespace := installed.GetNamespace() 285 ownerLabels := map[string]string{ 286 ownerutil.OwnerKind: operatorsv1alpha1.ClusterServiceVersionKind, 287 ownerutil.OwnerNamespaceKey: namespace, 288 ownerutil.OwnerKey: installed.GetName(), 289 } 290 components = []testobj.RuntimeMetaObject{ 291 testobj.WithOwner( 292 installed, 293 testobj.WithNamespace( 294 namespace, 295 fixtures.Fill(&appsv1.Deployment{}), 296 ), 297 ), 298 testobj.WithOwner( 299 installed, 300 testobj.WithNamespace( 301 namespace, 302 fixtures.Fill(&corev1.Service{}), 303 ), 304 ), 305 testobj.WithOwner( 306 installed, 307 testobj.WithNamespace( 308 namespace, 309 fixtures.Fill(&corev1.ServiceAccount{}), 310 ), 311 ), 312 testobj.WithOwner( 313 installed, 314 testobj.WithNamespace( 315 namespace, 316 fixtures.Fill(&corev1.Secret{}), 317 ), 318 ), 319 testobj.WithOwner( 320 installed, 321 testobj.WithNamespace( 322 namespace, 323 fixtures.Fill(&corev1.ConfigMap{}), 324 ), 325 ), 326 testobj.WithOwner( 327 installed, 328 testobj.WithNamespace( 329 namespace, 330 fixtures.Fill(&rbacv1.Role{}), 331 ), 332 ), 333 testobj.WithOwner( 334 installed, 335 testobj.WithNamespace( 336 namespace, 337 fixtures.Fill(&rbacv1.RoleBinding{}), 338 ), 339 ), 340 testobj.WithLabels( 341 ownerLabels, 342 fixtures.Fill(&rbacv1.ClusterRole{}), 343 ), 344 testobj.WithLabels( 345 ownerLabels, 346 fixtures.Fill(&rbacv1.ClusterRoleBinding{}), 347 ), 348 testobj.WithLabels( 349 ownerLabels, 350 fixtures.Fill(&apiregistrationv1.APIService{}), 351 ), 352 } 353 for _, component := range components { 354 labels := component.GetLabels() 355 if labels == nil { 356 labels = map[string]string{} 357 } 358 labels[install.OLMManagedLabelKey] = install.OLMManagedLabelValue 359 component.SetLabels(labels) 360 Eventually(func() error { 361 return k8sClient.Create(ctx, component) 362 }, timeout, interval).Should(Succeed()) 363 created = append(created, component) 364 } 365 }) 366 367 Specify("a component label", func() { 368 for _, component := range components { 369 Eventually(func() (map[string]string, error) { 370 err := k8sClient.Get(ctx, testobj.NamespacedName(component), component) 371 return component.GetLabels(), err 372 }, timeout, interval).Should(HaveKey(componentLabelKey)) 373 } 374 }) 375 376 }) 377 378 }) 379 }) 380 381 }) 382 }) 383 })