github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/operators/olm/requirements_test.go (about)

     1  package olm
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
     9  	"github.com/stretchr/testify/require"
    10  	corev1 "k8s.io/api/core/v1"
    11  	rbacv1 "k8s.io/api/rbac/v1"
    12  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    13  	"k8s.io/apimachinery/pkg/api/errors"
    14  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  	"k8s.io/apimachinery/pkg/runtime"
    16  	"k8s.io/apimachinery/pkg/runtime/schema"
    17  	"k8s.io/apimachinery/pkg/types"
    18  
    19  	"github.com/operator-framework/api/pkg/operators/v1alpha1"
    20  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/alongside"
    21  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister/operatorlisterfakes"
    22  	"github.com/stretchr/testify/assert"
    23  )
    24  
    25  func TestRequirementAndPermissionStatus(t *testing.T) {
    26  	namespace := "ns"
    27  	type gvkn struct {
    28  		group   string
    29  		version string
    30  		kind    string
    31  		name    string
    32  	}
    33  	tests := []struct {
    34  		description                 string
    35  		csv                         *v1alpha1.ClusterServiceVersion
    36  		existingObjs                []runtime.Object
    37  		existingExtObjs             []runtime.Object
    38  		met                         bool
    39  		expectedRequirementStatuses map[gvkn]v1alpha1.RequirementStatus
    40  		expectedError               error
    41  	}{
    42  		{
    43  			description: "AllPermissionsMet",
    44  			csv: csvWithUID(csv("csv1",
    45  				namespace,
    46  				"0.0.0",
    47  				"",
    48  				installStrategy(
    49  					"csv1-dep",
    50  					[]v1alpha1.StrategyDeploymentPermissions{
    51  						{
    52  							ServiceAccountName: "sa",
    53  							Rules: []rbacv1.PolicyRule{
    54  								{
    55  									APIGroups: []string{""},
    56  									Verbs:     []string{"*"},
    57  									Resources: []string{"donuts"},
    58  								},
    59  							},
    60  						},
    61  					},
    62  					[]v1alpha1.StrategyDeploymentPermissions{
    63  						{
    64  							ServiceAccountName: "sa",
    65  							Rules: []rbacv1.PolicyRule{
    66  								{
    67  									Verbs:           []string{"get"},
    68  									NonResourceURLs: []string{"/osbs"},
    69  								},
    70  							},
    71  						},
    72  					},
    73  				),
    74  				nil,
    75  				nil,
    76  				v1alpha1.CSVPhasePending,
    77  			), types.UID("csv-uid")),
    78  			existingObjs: []runtime.Object{
    79  				&corev1.ServiceAccount{
    80  					ObjectMeta: metav1.ObjectMeta{
    81  						Name:      "sa",
    82  						Namespace: namespace,
    83  						UID:       types.UID("sa"),
    84  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
    85  						OwnerReferences: []metav1.OwnerReference{
    86  							{
    87  								Kind: v1alpha1.ClusterServiceVersionKind,
    88  								UID:  "csv-uid",
    89  							},
    90  						},
    91  					},
    92  				},
    93  				&rbacv1.Role{
    94  					ObjectMeta: metav1.ObjectMeta{
    95  						Name:      "role",
    96  						Namespace: namespace,
    97  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
    98  					},
    99  					Rules: []rbacv1.PolicyRule{
   100  						{
   101  							APIGroups: []string{""},
   102  							Verbs:     []string{"*"},
   103  							Resources: []string{"donuts"},
   104  						},
   105  					},
   106  				},
   107  				&rbacv1.RoleBinding{
   108  					ObjectMeta: metav1.ObjectMeta{
   109  						Name:      "roleBinding",
   110  						Namespace: namespace,
   111  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   112  					},
   113  					Subjects: []rbacv1.Subject{
   114  						{
   115  							Kind:      "ServiceAccount",
   116  							APIGroup:  "",
   117  							Name:      "sa",
   118  							Namespace: namespace,
   119  						},
   120  					},
   121  					RoleRef: rbacv1.RoleRef{
   122  						APIGroup: "rbac.authorization.k8s.io",
   123  						Kind:     "Role",
   124  						Name:     "role",
   125  					},
   126  				},
   127  				&rbacv1.ClusterRole{
   128  					ObjectMeta: metav1.ObjectMeta{
   129  						Name:   "clusterRole",
   130  						Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   131  					},
   132  					Rules: []rbacv1.PolicyRule{
   133  						{
   134  							Verbs:           []string{"get"},
   135  							NonResourceURLs: []string{"/osbs"},
   136  						},
   137  					},
   138  				},
   139  				&rbacv1.ClusterRoleBinding{
   140  					ObjectMeta: metav1.ObjectMeta{
   141  						Name:   "clusterRoleBinding",
   142  						Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   143  					},
   144  					Subjects: []rbacv1.Subject{
   145  						{
   146  							Kind:      "ServiceAccount",
   147  							APIGroup:  "",
   148  							Name:      "sa",
   149  							Namespace: namespace,
   150  						},
   151  					},
   152  					RoleRef: rbacv1.RoleRef{
   153  						APIGroup: "rbac.authorization.k8s.io",
   154  						Kind:     "ClusterRole",
   155  						Name:     "clusterRole",
   156  					},
   157  				},
   158  			},
   159  			existingExtObjs: nil,
   160  			met:             true,
   161  			expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{
   162  				{"", "v1", "ServiceAccount", "sa"}: {
   163  					Group:   "",
   164  					Version: "v1",
   165  					Kind:    "ServiceAccount",
   166  					Name:    "sa",
   167  					Status:  v1alpha1.RequirementStatusReasonPresent,
   168  					Dependents: []v1alpha1.DependentStatus{
   169  						{
   170  							Group:   "rbac.authorization.k8s.io",
   171  							Kind:    "PolicyRule",
   172  							Version: "v1",
   173  						},
   174  						{
   175  							Group:   "rbac.authorization.k8s.io",
   176  							Kind:    "PolicyRule",
   177  							Version: "v1",
   178  						},
   179  					},
   180  				},
   181  				{"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: {
   182  					Group:   "operators.coreos.com",
   183  					Version: "v1alpha1",
   184  					Kind:    "ClusterServiceVersion",
   185  					Name:    "csv1",
   186  					Status:  v1alpha1.RequirementStatusReasonPresent,
   187  				},
   188  			},
   189  			expectedError: nil,
   190  		},
   191  		{
   192  			description: "OnePermissionNotMet",
   193  			csv: csvWithUID(csv("csv1",
   194  				namespace,
   195  				"0.0.0",
   196  				"",
   197  				installStrategy(
   198  					"csv1-dep",
   199  					[]v1alpha1.StrategyDeploymentPermissions{
   200  						{
   201  							ServiceAccountName: "sa",
   202  							Rules: []rbacv1.PolicyRule{
   203  								{
   204  									APIGroups: []string{""},
   205  									Verbs:     []string{"*"},
   206  									Resources: []string{"donuts"},
   207  								},
   208  							},
   209  						},
   210  					},
   211  					[]v1alpha1.StrategyDeploymentPermissions{
   212  						{
   213  							ServiceAccountName: "sa",
   214  							Rules: []rbacv1.PolicyRule{
   215  								{
   216  									Verbs:           []string{"get"},
   217  									NonResourceURLs: []string{"/osbs"},
   218  								},
   219  							},
   220  						},
   221  					},
   222  				),
   223  				nil,
   224  				nil,
   225  				v1alpha1.CSVPhasePending,
   226  			), types.UID("csv-uid")),
   227  			existingObjs: []runtime.Object{
   228  				&corev1.ServiceAccount{
   229  					ObjectMeta: metav1.ObjectMeta{
   230  						Name:      "sa",
   231  						Namespace: namespace,
   232  						UID:       types.UID("sa"),
   233  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   234  						OwnerReferences: []metav1.OwnerReference{
   235  							{
   236  								Kind: v1alpha1.ClusterServiceVersionKind,
   237  								UID:  "csv-uid",
   238  							},
   239  						},
   240  					},
   241  				},
   242  				&rbacv1.Role{
   243  					ObjectMeta: metav1.ObjectMeta{
   244  						Name:      "role",
   245  						Namespace: namespace,
   246  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   247  					},
   248  					Rules: []rbacv1.PolicyRule{
   249  						{
   250  							APIGroups: []string{""},
   251  							Verbs:     []string{"*"},
   252  							Resources: []string{"donuts"},
   253  						},
   254  					},
   255  				},
   256  				&rbacv1.RoleBinding{
   257  					ObjectMeta: metav1.ObjectMeta{
   258  						Name:      "roleBinding",
   259  						Namespace: namespace,
   260  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   261  					},
   262  					Subjects: []rbacv1.Subject{
   263  						{
   264  							Kind:      "ServiceAccount",
   265  							APIGroup:  "",
   266  							Name:      "sa",
   267  							Namespace: namespace,
   268  						},
   269  					},
   270  					RoleRef: rbacv1.RoleRef{
   271  						APIGroup: "rbac.authorization.k8s.io",
   272  						Kind:     "Role",
   273  						Name:     "role",
   274  					},
   275  				},
   276  				&rbacv1.ClusterRole{
   277  					ObjectMeta: metav1.ObjectMeta{
   278  						Name:   "clusterRole",
   279  						Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   280  					},
   281  					Rules: []rbacv1.PolicyRule{
   282  						{
   283  							Verbs:           []string{"get"},
   284  							NonResourceURLs: []string{"/osbs/*"},
   285  						},
   286  					},
   287  				},
   288  				&rbacv1.ClusterRoleBinding{
   289  					ObjectMeta: metav1.ObjectMeta{
   290  						Name:   "clusterRoleBinding",
   291  						Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   292  					},
   293  					Subjects: []rbacv1.Subject{
   294  						{
   295  							Kind:      "ServiceAccount",
   296  							APIGroup:  "",
   297  							Name:      "sa",
   298  							Namespace: namespace,
   299  						},
   300  					},
   301  					RoleRef: rbacv1.RoleRef{
   302  						APIGroup: "rbac.authorization.k8s.io",
   303  						Kind:     "ClusterRole",
   304  						Name:     "clusterRole",
   305  					},
   306  				},
   307  			},
   308  			existingExtObjs: nil,
   309  			met:             false,
   310  			expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{
   311  				{"", "v1", "ServiceAccount", "sa"}: {
   312  					Group:   "",
   313  					Version: "v1",
   314  					Kind:    "ServiceAccount",
   315  					Name:    "sa",
   316  					Status:  v1alpha1.RequirementStatusReasonPresentNotSatisfied,
   317  					Dependents: []v1alpha1.DependentStatus{
   318  						{
   319  							Group:   "rbac.authorization.k8s.io",
   320  							Kind:    "PolicyRule",
   321  							Version: "v1",
   322  						},
   323  						{
   324  							Group:   "rbac.authorization.k8s.io",
   325  							Kind:    "PolicyRule",
   326  							Version: "v1",
   327  						},
   328  					},
   329  				},
   330  				{"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: {
   331  					Group:   "operators.coreos.com",
   332  					Version: "v1alpha1",
   333  					Kind:    "ClusterServiceVersion",
   334  					Name:    "csv1",
   335  					Status:  v1alpha1.RequirementStatusReasonPresent,
   336  				},
   337  			},
   338  			expectedError: nil,
   339  		},
   340  		{
   341  			description: "RequirementNotMet/ServiceAccountOwnerConflict",
   342  			csv: csvWithUID(csv("csv1",
   343  				namespace,
   344  				"0.0.0",
   345  				"",
   346  				installStrategy(
   347  					"csv1-dep",
   348  					[]v1alpha1.StrategyDeploymentPermissions{
   349  						{
   350  							ServiceAccountName: "sa",
   351  							Rules: []rbacv1.PolicyRule{
   352  								{
   353  									APIGroups: []string{""},
   354  									Verbs:     []string{"*"},
   355  									Resources: []string{"donuts"},
   356  								},
   357  							},
   358  						},
   359  					},
   360  					[]v1alpha1.StrategyDeploymentPermissions{
   361  						{
   362  							ServiceAccountName: "sa",
   363  							Rules: []rbacv1.PolicyRule{
   364  								{
   365  									Verbs:           []string{"get"},
   366  									NonResourceURLs: []string{"/osbs"},
   367  								},
   368  							},
   369  						},
   370  					},
   371  				),
   372  				nil,
   373  				nil,
   374  				v1alpha1.CSVPhasePending,
   375  			), types.UID("csv-uid")),
   376  			existingObjs: []runtime.Object{
   377  				&corev1.ServiceAccount{
   378  					ObjectMeta: metav1.ObjectMeta{
   379  						Name:      "sa",
   380  						Namespace: namespace,
   381  						UID:       types.UID("sa"),
   382  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   383  						OwnerReferences: []metav1.OwnerReference{
   384  							{
   385  								Kind: v1alpha1.ClusterServiceVersionKind,
   386  								UID:  "csv-uid-other",
   387  							},
   388  						},
   389  					},
   390  				},
   391  				&rbacv1.Role{
   392  					ObjectMeta: metav1.ObjectMeta{
   393  						Name:      "role",
   394  						Namespace: namespace,
   395  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   396  					},
   397  					Rules: []rbacv1.PolicyRule{
   398  						{
   399  							APIGroups: []string{""},
   400  							Verbs:     []string{"*"},
   401  							Resources: []string{"donuts"},
   402  						},
   403  					},
   404  				},
   405  				&rbacv1.RoleBinding{
   406  					ObjectMeta: metav1.ObjectMeta{
   407  						Name:      "roleBinding",
   408  						Namespace: namespace,
   409  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   410  					},
   411  					Subjects: []rbacv1.Subject{
   412  						{
   413  							Kind:      "ServiceAccount",
   414  							APIGroup:  "",
   415  							Name:      "sa",
   416  							Namespace: namespace,
   417  						},
   418  					},
   419  					RoleRef: rbacv1.RoleRef{
   420  						APIGroup: "rbac.authorization.k8s.io",
   421  						Kind:     "Role",
   422  						Name:     "role",
   423  					},
   424  				},
   425  				&rbacv1.ClusterRole{
   426  					ObjectMeta: metav1.ObjectMeta{
   427  						Name:   "clusterRole",
   428  						Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   429  					},
   430  					Rules: []rbacv1.PolicyRule{
   431  						{
   432  							Verbs:           []string{"get"},
   433  							NonResourceURLs: []string{"/osbs"},
   434  						},
   435  					},
   436  				},
   437  				&rbacv1.ClusterRoleBinding{
   438  					ObjectMeta: metav1.ObjectMeta{
   439  						Name:   "clusterRoleBinding",
   440  						Labels: map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   441  					},
   442  					Subjects: []rbacv1.Subject{
   443  						{
   444  							Kind:      "ServiceAccount",
   445  							APIGroup:  "",
   446  							Name:      "sa",
   447  							Namespace: namespace,
   448  						},
   449  					},
   450  					RoleRef: rbacv1.RoleRef{
   451  						APIGroup: "rbac.authorization.k8s.io",
   452  						Kind:     "ClusterRole",
   453  						Name:     "clusterRole",
   454  					},
   455  				},
   456  			},
   457  			existingExtObjs: nil,
   458  			met:             false,
   459  			expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{
   460  				{"", "v1", "ServiceAccount", "sa"}: {
   461  					Group:      "",
   462  					Version:    "v1",
   463  					Kind:       "ServiceAccount",
   464  					Name:       "sa",
   465  					Status:     v1alpha1.RequirementStatusReasonPresentNotSatisfied,
   466  					Dependents: []v1alpha1.DependentStatus{},
   467  				},
   468  				{"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: {
   469  					Group:   "operators.coreos.com",
   470  					Version: "v1alpha1",
   471  					Kind:    "ClusterServiceVersion",
   472  					Name:    "csv1",
   473  					Status:  v1alpha1.RequirementStatusReasonPresent,
   474  				},
   475  			},
   476  			expectedError: nil,
   477  		},
   478  		{
   479  			description: "AllRequirementsMet",
   480  			csv: csvWithUID(csv("csv1",
   481  				namespace,
   482  				"0.0.0",
   483  				"",
   484  				installStrategy(
   485  					"csv1-dep",
   486  					[]v1alpha1.StrategyDeploymentPermissions{
   487  						{
   488  							ServiceAccountName: "sa",
   489  							Rules: []rbacv1.PolicyRule{
   490  								{
   491  									APIGroups: []string{""},
   492  									Verbs:     []string{"*"},
   493  									Resources: []string{"donuts"},
   494  								},
   495  							},
   496  						},
   497  					},
   498  					nil,
   499  				),
   500  				[]*apiextensionsv1.CustomResourceDefinition{crd("c1", "v1", "g1")},
   501  				[]*apiextensionsv1.CustomResourceDefinition{crd("c2", "v1", "g2")},
   502  				v1alpha1.CSVPhasePending,
   503  			), types.UID("csv-uid")),
   504  			existingObjs: []runtime.Object{
   505  				&corev1.ServiceAccount{
   506  					ObjectMeta: metav1.ObjectMeta{
   507  						Name:      "sa",
   508  						Namespace: namespace,
   509  						UID:       types.UID("sa"),
   510  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   511  						OwnerReferences: []metav1.OwnerReference{
   512  							{
   513  								Kind: v1alpha1.ClusterServiceVersionKind,
   514  								UID:  "csv-uid",
   515  							},
   516  						},
   517  					},
   518  				},
   519  				&rbacv1.Role{
   520  					ObjectMeta: metav1.ObjectMeta{
   521  						Name:      "role",
   522  						Namespace: namespace,
   523  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   524  					},
   525  					Rules: []rbacv1.PolicyRule{
   526  						{
   527  							APIGroups: []string{""},
   528  							Verbs:     []string{"*"},
   529  							Resources: []string{"donuts"},
   530  						},
   531  					},
   532  				},
   533  				&rbacv1.RoleBinding{
   534  					ObjectMeta: metav1.ObjectMeta{
   535  						Name:      "roleBinding",
   536  						Namespace: namespace,
   537  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   538  					},
   539  					Subjects: []rbacv1.Subject{
   540  						{
   541  							Kind:      "ServiceAccount",
   542  							APIGroup:  "",
   543  							Name:      "sa",
   544  							Namespace: namespace,
   545  						},
   546  					},
   547  					RoleRef: rbacv1.RoleRef{
   548  						APIGroup: "rbac.authorization.k8s.io",
   549  						Kind:     "Role",
   550  						Name:     "role",
   551  					},
   552  				},
   553  			},
   554  			existingExtObjs: []runtime.Object{
   555  				crd("c1", "v1", "g1"),
   556  				crd("c2", "v1", "g2"),
   557  			},
   558  			met: true,
   559  			expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{
   560  				{"", "v1", "ServiceAccount", "sa"}: {
   561  					Group:   "",
   562  					Version: "v1",
   563  					Kind:    "ServiceAccount",
   564  					Name:    "sa",
   565  					Status:  v1alpha1.RequirementStatusReasonPresent,
   566  					Dependents: []v1alpha1.DependentStatus{
   567  						{
   568  							Group:   "rbac.authorization.k8s.io",
   569  							Kind:    "PolicyRule",
   570  							Version: "v1",
   571  						},
   572  					},
   573  				},
   574  				{"", "v1", "ServiceAccount", "sa"}: {
   575  					Group:   "",
   576  					Version: "v1",
   577  					Kind:    "ServiceAccount",
   578  					Name:    "sa",
   579  					Status:  v1alpha1.RequirementStatusReasonPresent,
   580  					Dependents: []v1alpha1.DependentStatus{
   581  						{
   582  							Group:   "rbac.authorization.k8s.io",
   583  							Kind:    "PolicyRule",
   584  							Version: "v1",
   585  						},
   586  					},
   587  				},
   588  				{"apiextensions.k8s.io", "v1", "CustomResourceDefinition", "c1.g1"}: {
   589  					Group:   "apiextensions.k8s.io",
   590  					Version: "v1",
   591  					Kind:    "CustomResourceDefinition",
   592  					Name:    "c1.g1",
   593  					Status:  v1alpha1.RequirementStatusReasonPresent,
   594  				},
   595  				{"apiextensions.k8s.io", "v1", "CustomResourceDefinition", "c2.g2"}: {
   596  					Group:   "apiextensions.k8s.io",
   597  					Version: "v1",
   598  					Kind:    "CustomResourceDefinition",
   599  					Name:    "c2.g2",
   600  					Status:  v1alpha1.RequirementStatusReasonPresent,
   601  				},
   602  				{"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: {
   603  					Group:   "operators.coreos.com",
   604  					Version: "v1alpha1",
   605  					Kind:    "ClusterServiceVersion",
   606  					Name:    "csv1",
   607  					Status:  v1alpha1.RequirementStatusReasonPresent,
   608  				},
   609  			},
   610  			expectedError: nil,
   611  		},
   612  		{
   613  			description: "RequirementNotMet/NonServedCRDVersion",
   614  			csv: csv("csv1",
   615  				namespace,
   616  				"0.0.0",
   617  				"",
   618  				installStrategy("csv1-dep", nil, nil),
   619  				[]*apiextensionsv1.CustomResourceDefinition{crd("c1", "v2", "g1")},
   620  				nil,
   621  				v1alpha1.CSVPhasePending,
   622  			),
   623  			existingObjs: nil,
   624  			existingExtObjs: []runtime.Object{
   625  				crd("c1", "v1", "g1"),
   626  			},
   627  			met: false,
   628  			expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{
   629  				{"apiextensions.k8s.io", "v1", "CustomResourceDefinition", "c1.g1"}: {
   630  					Group:   "apiextensions.k8s.io",
   631  					Version: "v1",
   632  					Kind:    "CustomResourceDefinition",
   633  					Name:    "c1.g1",
   634  					Status:  v1alpha1.RequirementStatusReasonNotPresent,
   635  				},
   636  				{"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: {
   637  					Group:   "operators.coreos.com",
   638  					Version: "v1alpha1",
   639  					Kind:    "ClusterServiceVersion",
   640  					Name:    "csv1",
   641  					Status:  v1alpha1.RequirementStatusReasonPresent,
   642  				},
   643  			},
   644  			expectedError: nil,
   645  		},
   646  		{
   647  			description: "RequirementNotMet/NotEstablishedCRDVersion",
   648  			csv: csv("csv1",
   649  				namespace,
   650  				"0.0.0",
   651  				"",
   652  				installStrategy("csv1-dep", nil, nil),
   653  				[]*apiextensionsv1.CustomResourceDefinition{crd("c1", "version-not-found", "g1")},
   654  				nil,
   655  				v1alpha1.CSVPhasePending,
   656  			),
   657  			existingObjs: nil,
   658  			existingExtObjs: []runtime.Object{
   659  				crd("c1", "v2", "g1"),
   660  			},
   661  			met: false,
   662  			expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{
   663  				{"apiextensions.k8s.io", "v1", "CustomResourceDefinition", "c1.g1"}: {
   664  					Group:   "apiextensions.k8s.io",
   665  					Version: "v1",
   666  					Kind:    "CustomResourceDefinition",
   667  					Name:    "c1.g1",
   668  					Status:  v1alpha1.RequirementStatusReasonNotPresent,
   669  				},
   670  				{"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: {
   671  					Group:   "operators.coreos.com",
   672  					Version: "v1alpha1",
   673  					Kind:    "ClusterServiceVersion",
   674  					Name:    "csv1",
   675  					Status:  v1alpha1.RequirementStatusReasonPresent,
   676  				},
   677  			},
   678  			expectedError: nil,
   679  		},
   680  		{
   681  			description: "RequirementNotMet/NamesConflictedCRD",
   682  			csv: csv("csv1",
   683  				namespace,
   684  				"0.0.0",
   685  				"",
   686  				installStrategy("csv1-dep", nil, nil),
   687  				[]*apiextensionsv1.CustomResourceDefinition{crd("c1", "v2", "g1")},
   688  				nil,
   689  				v1alpha1.CSVPhasePending,
   690  			),
   691  			existingObjs: nil,
   692  			existingExtObjs: []runtime.Object{
   693  				func() *apiextensionsv1.CustomResourceDefinition {
   694  					newCRD := crd("c1", "v2", "g1")
   695  					// condition order: established, name accepted
   696  					newCRD.Status.Conditions[0].Status = apiextensionsv1.ConditionTrue
   697  					newCRD.Status.Conditions[1].Status = apiextensionsv1.ConditionFalse
   698  					return newCRD
   699  				}(),
   700  			},
   701  			met: false,
   702  			expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{
   703  				{"apiextensions.k8s.io", "v1", "CustomResourceDefinition", "c1.g1"}: {
   704  					Group:   "apiextensions.k8s.io",
   705  					Version: "v1",
   706  					Kind:    "CustomResourceDefinition",
   707  					Name:    "c1.g1",
   708  					Status:  v1alpha1.RequirementStatusReasonNotAvailable,
   709  				},
   710  				{"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: {
   711  					Group:   "operators.coreos.com",
   712  					Version: "v1alpha1",
   713  					Kind:    "ClusterServiceVersion",
   714  					Name:    "csv1",
   715  					Status:  v1alpha1.RequirementStatusReasonPresent,
   716  				},
   717  			},
   718  			expectedError: nil,
   719  		},
   720  		{
   721  			description: "RequirementNotMet/CRDResourceInactive",
   722  			csv: csv("csv1",
   723  				namespace,
   724  				"0.0.0",
   725  				"",
   726  				installStrategy("csv1-dep", nil, nil),
   727  				[]*apiextensionsv1.CustomResourceDefinition{crd("c1", "v2", "g1")},
   728  				nil,
   729  				v1alpha1.CSVPhasePending,
   730  			),
   731  			existingObjs: nil,
   732  			existingExtObjs: []runtime.Object{
   733  				func() *apiextensionsv1.CustomResourceDefinition {
   734  					newCRD := crd("c1", "v2", "g1")
   735  					// condition order: established, name accepted
   736  					newCRD.Status.Conditions[0].Status = apiextensionsv1.ConditionFalse
   737  					newCRD.Status.Conditions[1].Status = apiextensionsv1.ConditionTrue
   738  					return newCRD
   739  				}(),
   740  			},
   741  			met: false,
   742  			expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{
   743  				{"apiextensions.k8s.io", "v1", "CustomResourceDefinition", "c1.g1"}: {
   744  					Group:   "apiextensions.k8s.io",
   745  					Version: "v1",
   746  					Kind:    "CustomResourceDefinition",
   747  					Name:    "c1.g1",
   748  					Status:  v1alpha1.RequirementStatusReasonNotAvailable,
   749  				},
   750  				{"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: {
   751  					Group:   "operators.coreos.com",
   752  					Version: "v1alpha1",
   753  					Kind:    "ClusterServiceVersion",
   754  					Name:    "csv1",
   755  					Status:  v1alpha1.RequirementStatusReasonPresent,
   756  				},
   757  			},
   758  			expectedError: nil,
   759  		},
   760  		{
   761  			description: "RequirementNotMet/StaleServiceAccount",
   762  			csv: csvWithUID(csv("csv1",
   763  				namespace,
   764  				"0.0.0",
   765  				"",
   766  				installStrategy(
   767  					"csv1-dep",
   768  					[]v1alpha1.StrategyDeploymentPermissions{
   769  						{
   770  							ServiceAccountName: "sa",
   771  							Rules: []rbacv1.PolicyRule{
   772  								{
   773  									APIGroups: []string{""},
   774  									Verbs:     []string{"*"},
   775  									Resources: []string{"donuts"},
   776  								},
   777  							},
   778  						},
   779  					},
   780  					nil,
   781  				),
   782  				nil,
   783  				nil,
   784  				v1alpha1.CSVPhasePending,
   785  			), types.UID("csv-uid")),
   786  			existingObjs: []runtime.Object{
   787  				&corev1.ServiceAccount{
   788  					ObjectMeta: metav1.ObjectMeta{
   789  						Name:      "sa",
   790  						Namespace: namespace,
   791  						UID:       types.UID("sa"),
   792  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   793  						OwnerReferences: []metav1.OwnerReference{
   794  							{
   795  								Kind: v1alpha1.ClusterServiceVersionKind,
   796  								UID:  "csv-wrong",
   797  							},
   798  						},
   799  					},
   800  				},
   801  			},
   802  			existingExtObjs: nil,
   803  			met:             false,
   804  			expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{
   805  				{"", "v1", "ServiceAccount", "sa"}: {
   806  					Version:    "v1",
   807  					Kind:       "ServiceAccount",
   808  					Name:       "sa",
   809  					Status:     v1alpha1.RequirementStatusReasonPresentNotSatisfied,
   810  					Dependents: []v1alpha1.DependentStatus{},
   811  				},
   812  				{"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: {
   813  					Group:   "operators.coreos.com",
   814  					Version: "v1alpha1",
   815  					Kind:    "ClusterServiceVersion",
   816  					Name:    "csv1",
   817  					Status:  v1alpha1.RequirementStatusReasonPresent,
   818  				},
   819  			},
   820  			expectedError: nil,
   821  		},
   822  		{
   823  			description: "RequirementMet/ServiceAccountOwnedByNonCSV",
   824  			csv: csvWithUID(csv("csv",
   825  				namespace,
   826  				"0.0.0",
   827  				"",
   828  				installStrategy(
   829  					"csv-dep",
   830  					[]v1alpha1.StrategyDeploymentPermissions{
   831  						{
   832  							ServiceAccountName: "sa",
   833  						},
   834  					},
   835  					nil,
   836  				),
   837  				nil,
   838  				nil,
   839  				v1alpha1.CSVPhasePending,
   840  			), types.UID("csv-uid")),
   841  			existingObjs: []runtime.Object{
   842  				&corev1.ServiceAccount{
   843  					ObjectMeta: metav1.ObjectMeta{
   844  						Name:      "sa",
   845  						Namespace: namespace,
   846  						UID:       types.UID("sa"),
   847  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   848  						OwnerReferences: []metav1.OwnerReference{
   849  							{
   850  								Kind: v1alpha1.SubscriptionKind, // arbitrary non-CSV kind
   851  								UID:  "non-csv",
   852  							},
   853  						},
   854  					},
   855  				},
   856  			},
   857  			existingExtObjs: nil,
   858  			met:             true,
   859  			expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{
   860  				{"", "v1", "ServiceAccount", "sa"}: {
   861  					Version:    "v1",
   862  					Kind:       "ServiceAccount",
   863  					Name:       "sa",
   864  					Status:     v1alpha1.RequirementStatusReasonPresent,
   865  					Dependents: []v1alpha1.DependentStatus{},
   866  				},
   867  				{"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv"}: {
   868  					Group:   "operators.coreos.com",
   869  					Version: "v1alpha1",
   870  					Kind:    "ClusterServiceVersion",
   871  					Name:    "csv",
   872  					Status:  v1alpha1.RequirementStatusReasonPresent,
   873  				},
   874  			},
   875  			expectedError: nil,
   876  		},
   877  		{
   878  			description: "RequirementMet/ServiceAccountHasNoOwner",
   879  			csv: csvWithUID(csv("csv",
   880  				namespace,
   881  				"0.0.0",
   882  				"",
   883  				installStrategy(
   884  					"csv-dep",
   885  					[]v1alpha1.StrategyDeploymentPermissions{
   886  						{
   887  							ServiceAccountName: "sa",
   888  						},
   889  					},
   890  					nil,
   891  				),
   892  				nil,
   893  				nil,
   894  				v1alpha1.CSVPhasePending,
   895  			), types.UID("csv-uid")),
   896  			existingObjs: []runtime.Object{
   897  				&corev1.ServiceAccount{
   898  					ObjectMeta: metav1.ObjectMeta{
   899  						Name:      "sa",
   900  						Namespace: namespace,
   901  						Labels:    map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue},
   902  						UID:       types.UID("sa"),
   903  					},
   904  				},
   905  			},
   906  			existingExtObjs: nil,
   907  			met:             true,
   908  			expectedRequirementStatuses: map[gvkn]v1alpha1.RequirementStatus{
   909  				{"", "v1", "ServiceAccount", "sa"}: {
   910  					Version:    "v1",
   911  					Kind:       "ServiceAccount",
   912  					Name:       "sa",
   913  					Status:     v1alpha1.RequirementStatusReasonPresent,
   914  					Dependents: []v1alpha1.DependentStatus{},
   915  				},
   916  				{"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv"}: {
   917  					Group:   "operators.coreos.com",
   918  					Version: "v1alpha1",
   919  					Kind:    "ClusterServiceVersion",
   920  					Name:    "csv",
   921  					Status:  v1alpha1.RequirementStatusReasonPresent,
   922  				},
   923  			},
   924  			expectedError: nil,
   925  		},
   926  	}
   927  
   928  	for _, test := range tests {
   929  		t.Run(test.description, func(t *testing.T) {
   930  			ctx, cancel := context.WithCancel(context.TODO())
   931  			defer cancel()
   932  			op, err := NewFakeOperator(ctx, withNamespaces(namespace), withOperatorNamespace(namespace), withClientObjs(test.csv), withK8sObjs(test.existingObjs...), withExtObjs(test.existingExtObjs...))
   933  			require.NoError(t, err)
   934  
   935  			// Get the permission status
   936  			met, statuses, err := op.requirementAndPermissionStatus(test.csv)
   937  
   938  			if test.expectedError != nil {
   939  				require.Error(t, err)
   940  				require.EqualError(t, test.expectedError, err.Error())
   941  			}
   942  			assert := assert.New(t)
   943  			assert.Equal(test.met, met)
   944  
   945  			for _, status := range statuses {
   946  				key := gvkn{
   947  					group:   status.Group,
   948  					version: status.Version,
   949  					kind:    status.Kind,
   950  					name:    status.Name,
   951  				}
   952  
   953  				expected, ok := test.expectedRequirementStatuses[key]
   954  				assert.True(ok, fmt.Sprintf("permission requirement status %+v found but not expected", key))
   955  				assert.Equal(expected.Status, status.Status)
   956  				assert.Len(status.Dependents, len(expected.Dependents), "number of dependents is not what was expected")
   957  
   958  				// Delete the requirement status to mark as found
   959  				delete(test.expectedRequirementStatuses, key)
   960  			}
   961  
   962  			assert.Len(test.expectedRequirementStatuses, 0, "not all expected permission requirement statuses were found")
   963  		})
   964  	}
   965  }
   966  
   967  func TestMinKubeVersionStatus(t *testing.T) {
   968  	namespace := "ns"
   969  	csv := csv("csv1",
   970  		namespace,
   971  		"0.0.0",
   972  		"",
   973  		v1alpha1.NamedInstallStrategy{StrategyName: "deployment", StrategySpec: v1alpha1.StrategyDetailsDeployment{}},
   974  		nil,
   975  		nil,
   976  		v1alpha1.CSVPhasePending,
   977  	)
   978  
   979  	tests := []struct {
   980  		description                 string
   981  		csvName                     string
   982  		minKubeVersion              string
   983  		expectedMet                 bool
   984  		expectedRequirementStatuses []v1alpha1.RequirementStatus
   985  	}{
   986  		{
   987  			description:                 "minKubeVersion is not specfied",
   988  			csvName:                     "test1",
   989  			minKubeVersion:              "",
   990  			expectedMet:                 true,
   991  			expectedRequirementStatuses: []v1alpha1.RequirementStatus{},
   992  		},
   993  		{
   994  			description:    "minKubeVersion is met",
   995  			csvName:        "test2",
   996  			minKubeVersion: "0.0.0",
   997  			expectedMet:    true,
   998  			expectedRequirementStatuses: []v1alpha1.RequirementStatus{
   999  				{
  1000  					Status:  v1alpha1.RequirementStatusReasonPresent,
  1001  					Message: fmt.Sprintf("CSV minKubeVersion (%s) less than server version", "0.0.0"),
  1002  					Group:   "operators.coreos.com",
  1003  					Version: "v1alpha1",
  1004  					Kind:    "ClusterServiceVersion",
  1005  					Name:    "test2",
  1006  				},
  1007  			},
  1008  		},
  1009  		{
  1010  			description:    "minKubeVersion is unmet",
  1011  			csvName:        "test3",
  1012  			minKubeVersion: "999.999.999",
  1013  			expectedMet:    false,
  1014  			expectedRequirementStatuses: []v1alpha1.RequirementStatus{
  1015  				{
  1016  					Status:  v1alpha1.RequirementStatusReasonPresentNotSatisfied,
  1017  					Message: fmt.Sprintf("CSV version requirement not met: minKubeVersion (%s)", "999.999.999"),
  1018  					Group:   "operators.coreos.com",
  1019  					Version: "v1alpha1",
  1020  					Kind:    "ClusterServiceVersion",
  1021  					Name:    "test3",
  1022  				},
  1023  			},
  1024  		},
  1025  		{
  1026  			description:    "minKubeVersion is invalid",
  1027  			csvName:        "test4",
  1028  			minKubeVersion: "a.b.c",
  1029  			expectedMet:    false,
  1030  			expectedRequirementStatuses: []v1alpha1.RequirementStatus{
  1031  				{
  1032  					Status:  v1alpha1.RequirementStatusReasonPresentNotSatisfied,
  1033  					Message: "CSV version parsing error",
  1034  					Group:   "operators.coreos.com",
  1035  					Version: "v1alpha1",
  1036  					Kind:    "ClusterServiceVersion",
  1037  					Name:    "test4",
  1038  				},
  1039  			},
  1040  		},
  1041  	}
  1042  
  1043  	for _, test := range tests {
  1044  		t.Run(test.description, func(t *testing.T) {
  1045  			ctx, cancel := context.WithCancel(context.TODO())
  1046  			defer cancel()
  1047  			op, err := NewFakeOperator(ctx, withNamespaces(namespace), withOperatorNamespace(namespace), withClientObjs(csv))
  1048  			require.NoError(t, err)
  1049  
  1050  			// Get the permission status
  1051  			met, status := op.minKubeVersionStatus(test.csvName, test.minKubeVersion)
  1052  			require.Equal(t, test.expectedMet, met)
  1053  			if len(test.expectedRequirementStatuses) > 0 {
  1054  				require.Equal(t, status[0].Status, test.expectedRequirementStatuses[0].Status)
  1055  				require.Equal(t, status[0].Kind, test.expectedRequirementStatuses[0].Kind)
  1056  				require.Equal(t, status[0].Name, test.expectedRequirementStatuses[0].Name)
  1057  				require.Contains(t, status[0].Message, test.expectedRequirementStatuses[0].Message)
  1058  			} else {
  1059  				require.Equal(t, status, []v1alpha1.RequirementStatus(nil))
  1060  			}
  1061  		})
  1062  	}
  1063  }
  1064  
  1065  func TestOthersInstalledAlongside(t *testing.T) {
  1066  	for _, tc := range []struct {
  1067  		Name        string
  1068  		All         []alongside.NamespacedName
  1069  		Target      v1alpha1.ClusterServiceVersion
  1070  		InNamespace []v1alpha1.ClusterServiceVersion
  1071  		Expected    []string
  1072  	}{
  1073  		{
  1074  			Name: "csv in different namespace excluded",
  1075  			All: []alongside.NamespacedName{
  1076  				{Namespace: "namespace-2", Name: "a"},
  1077  			},
  1078  			Target: v1alpha1.ClusterServiceVersion{
  1079  				ObjectMeta: metav1.ObjectMeta{
  1080  					Name:      "b",
  1081  					Namespace: "namespace-1",
  1082  				},
  1083  			},
  1084  			InNamespace: []v1alpha1.ClusterServiceVersion{
  1085  				{
  1086  					ObjectMeta: metav1.ObjectMeta{
  1087  						Name: "a",
  1088  					},
  1089  				},
  1090  			},
  1091  			Expected: nil,
  1092  		},
  1093  		{
  1094  			Name: "given csv excluded",
  1095  			All: []alongside.NamespacedName{
  1096  				{Namespace: "namespace", Name: "a"},
  1097  			},
  1098  			Target: v1alpha1.ClusterServiceVersion{
  1099  				ObjectMeta: metav1.ObjectMeta{
  1100  					Name:      "a",
  1101  					Namespace: "namespace",
  1102  				},
  1103  			},
  1104  			Expected: nil,
  1105  		},
  1106  		{
  1107  			Name: "returns nil if given csv is included",
  1108  			All: []alongside.NamespacedName{
  1109  				{Namespace: "namespace", Name: "a"},
  1110  				{Namespace: "namespace", Name: "b"},
  1111  			},
  1112  			Target: v1alpha1.ClusterServiceVersion{
  1113  				ObjectMeta: metav1.ObjectMeta{
  1114  					Name:      "a",
  1115  					Namespace: "namespace",
  1116  				},
  1117  				Spec: v1alpha1.ClusterServiceVersionSpec{
  1118  					Replaces: "b",
  1119  				},
  1120  			},
  1121  			InNamespace: []v1alpha1.ClusterServiceVersion{
  1122  				{
  1123  					ObjectMeta: metav1.ObjectMeta{
  1124  						Name: "b",
  1125  					},
  1126  				},
  1127  			},
  1128  			Expected: nil,
  1129  		},
  1130  		{
  1131  			Name: "copied csv excluded",
  1132  			All: []alongside.NamespacedName{
  1133  				{Namespace: "namespace", Name: "b"},
  1134  			},
  1135  			Target: v1alpha1.ClusterServiceVersion{
  1136  				ObjectMeta: metav1.ObjectMeta{
  1137  					Name:      "a",
  1138  					Namespace: "namespace",
  1139  				},
  1140  			},
  1141  			InNamespace: []v1alpha1.ClusterServiceVersion{
  1142  				{
  1143  					ObjectMeta: metav1.ObjectMeta{
  1144  						Name: "b",
  1145  					},
  1146  					Status: v1alpha1.ClusterServiceVersionStatus{
  1147  						Reason: v1alpha1.CSVReasonCopied,
  1148  					},
  1149  				},
  1150  			},
  1151  			Expected: nil,
  1152  		},
  1153  		{
  1154  			Name: "non-ancestor csv excluded",
  1155  			All: []alongside.NamespacedName{
  1156  				{Namespace: "namespace", Name: "b"},
  1157  			},
  1158  			Target: v1alpha1.ClusterServiceVersion{
  1159  				ObjectMeta: metav1.ObjectMeta{
  1160  					Name:      "a",
  1161  					Namespace: "namespace",
  1162  				},
  1163  			},
  1164  			InNamespace: []v1alpha1.ClusterServiceVersion{
  1165  				{
  1166  					ObjectMeta: metav1.ObjectMeta{
  1167  						Name: "b",
  1168  					},
  1169  				},
  1170  			},
  1171  			Expected: nil,
  1172  		},
  1173  		{
  1174  			Name: "ancestor csvs included",
  1175  			All: []alongside.NamespacedName{
  1176  				{Namespace: "namespace", Name: "b"},
  1177  				{Namespace: "namespace", Name: "c"},
  1178  			},
  1179  			Target: v1alpha1.ClusterServiceVersion{
  1180  				ObjectMeta: metav1.ObjectMeta{
  1181  					Name:      "a",
  1182  					Namespace: "namespace",
  1183  				},
  1184  				Spec: v1alpha1.ClusterServiceVersionSpec{
  1185  					Replaces: "b",
  1186  				},
  1187  			},
  1188  			InNamespace: []v1alpha1.ClusterServiceVersion{
  1189  				{
  1190  					ObjectMeta: metav1.ObjectMeta{
  1191  						Name: "b",
  1192  					},
  1193  					Spec: v1alpha1.ClusterServiceVersionSpec{
  1194  						Replaces: "c",
  1195  					},
  1196  				},
  1197  				{
  1198  					ObjectMeta: metav1.ObjectMeta{
  1199  						Name: "c",
  1200  					},
  1201  				},
  1202  			},
  1203  			Expected: []string{"b", "c"},
  1204  		},
  1205  		{
  1206  			Name: "descendant csvs excluded",
  1207  			All: []alongside.NamespacedName{
  1208  				{Namespace: "namespace", Name: "b"},
  1209  				{Namespace: "namespace", Name: "c"},
  1210  			},
  1211  			Target: v1alpha1.ClusterServiceVersion{
  1212  				ObjectMeta: metav1.ObjectMeta{
  1213  					Name:      "a",
  1214  					Namespace: "namespace",
  1215  				},
  1216  			},
  1217  			InNamespace: []v1alpha1.ClusterServiceVersion{
  1218  				{
  1219  					ObjectMeta: metav1.ObjectMeta{
  1220  						Name: "c",
  1221  					},
  1222  					Spec: v1alpha1.ClusterServiceVersionSpec{
  1223  						Replaces: "b",
  1224  					},
  1225  				},
  1226  				{
  1227  					ObjectMeta: metav1.ObjectMeta{
  1228  						Name: "b",
  1229  					},
  1230  					Spec: v1alpha1.ClusterServiceVersionSpec{
  1231  						Replaces: "a",
  1232  					},
  1233  				},
  1234  			},
  1235  			Expected: nil,
  1236  		},
  1237  		{
  1238  			Name: "ancestor csvs included with cycle",
  1239  			All: []alongside.NamespacedName{
  1240  				{Namespace: "namespace", Name: "b"},
  1241  				{Namespace: "namespace", Name: "c"},
  1242  			},
  1243  			Target: v1alpha1.ClusterServiceVersion{
  1244  				ObjectMeta: metav1.ObjectMeta{
  1245  					Name:      "a",
  1246  					Namespace: "namespace",
  1247  				},
  1248  				Spec: v1alpha1.ClusterServiceVersionSpec{
  1249  					Replaces: "b",
  1250  				},
  1251  			},
  1252  			InNamespace: []v1alpha1.ClusterServiceVersion{
  1253  				{
  1254  					ObjectMeta: metav1.ObjectMeta{
  1255  						Name: "b",
  1256  					},
  1257  					Spec: v1alpha1.ClusterServiceVersionSpec{
  1258  						Replaces: "c",
  1259  					},
  1260  				},
  1261  				{
  1262  					ObjectMeta: metav1.ObjectMeta{
  1263  						Name: "c",
  1264  					},
  1265  					Spec: v1alpha1.ClusterServiceVersionSpec{
  1266  						Replaces: "a",
  1267  					},
  1268  				},
  1269  			},
  1270  			Expected: []string{"b", "c"},
  1271  		},
  1272  	} {
  1273  		t.Run(tc.Name, func(t *testing.T) {
  1274  			var (
  1275  				o        metav1.ObjectMeta
  1276  				a        alongside.Annotator
  1277  				nslister operatorlisterfakes.FakeClusterServiceVersionNamespaceLister
  1278  			)
  1279  
  1280  			nslister.GetCalls(func(name string) (*v1alpha1.ClusterServiceVersion, error) {
  1281  				if name == tc.Target.GetName() {
  1282  					return tc.Target.DeepCopy(), nil
  1283  				}
  1284  
  1285  				for _, csv := range tc.InNamespace {
  1286  					if csv.GetName() == name {
  1287  						return csv.DeepCopy(), nil
  1288  					}
  1289  				}
  1290  				return nil, errors.NewNotFound(schema.GroupResource{}, name)
  1291  			})
  1292  
  1293  			a.ToObject(&o, tc.All)
  1294  			actual := othersInstalledAlongside(&o, tc.Target.DeepCopy(), &nslister)
  1295  			assert.ElementsMatch(t, actual, tc.Expected)
  1296  		})
  1297  	}
  1298  }