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  })