github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/install/certresources_test.go (about)

     1  package install
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/golang/mock/gomock"
    10  	"github.com/google/go-cmp/cmp"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  	appsv1 "k8s.io/api/apps/v1"
    14  	corev1 "k8s.io/api/core/v1"
    15  	rbacv1 "k8s.io/api/rbac/v1"
    16  	"k8s.io/apimachinery/pkg/api/errors"
    17  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    18  	"k8s.io/apimachinery/pkg/runtime/schema"
    19  	corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
    20  	rbacv1ac "k8s.io/client-go/applyconfigurations/rbac/v1"
    21  
    22  	"github.com/operator-framework/api/pkg/operators/v1alpha1"
    23  	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/wrappers"
    24  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/certs"
    25  	listerfakes "github.com/operator-framework/operator-lifecycle-manager/pkg/fakes/client-go/listers"
    26  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient/operatorclientmocks"
    27  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister/operatorlisterfakes"
    28  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
    29  )
    30  
    31  func keyPair(t *testing.T, expiration time.Time) *certs.KeyPair {
    32  	p, err := certs.GenerateCA(expiration, Organization)
    33  	assert.NoError(t, err)
    34  	return p
    35  }
    36  
    37  func selector(t *testing.T, selector string) *metav1.LabelSelector {
    38  	s, err := metav1.ParseToLabelSelector(selector)
    39  	assert.NoError(t, err)
    40  	return s
    41  }
    42  
    43  var staticCerts *certs.KeyPair
    44  
    45  // staticCertGenerator replaces the CertGenerator to get consistent keys for testing
    46  func staticCertGenerator(notAfter time.Time, organization string, ca *certs.KeyPair, hosts []string) (*certs.KeyPair, error) {
    47  	if staticCerts != nil {
    48  		return staticCerts, nil
    49  	}
    50  	c, err := certs.CreateSignedServingPair(notAfter, organization, ca, hosts)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	staticCerts = c
    55  	return staticCerts, nil
    56  }
    57  
    58  type fakeState struct {
    59  	existingService *corev1.Service
    60  	getServiceError error
    61  
    62  	existingSecret *corev1.Secret
    63  	getSecretError error
    64  
    65  	existingRole *rbacv1.Role
    66  	getRoleError error
    67  
    68  	existingRoleBinding *rbacv1.RoleBinding
    69  	getRoleBindingError error
    70  
    71  	existingClusterRoleBinding *rbacv1.ClusterRoleBinding
    72  	getClusterRoleBindingError error
    73  }
    74  
    75  func newFakeLister(state fakeState) *operatorlisterfakes.FakeOperatorLister {
    76  	fakeLister := &operatorlisterfakes.FakeOperatorLister{}
    77  	fakeCoreV1Lister := &operatorlisterfakes.FakeCoreV1Lister{}
    78  	fakeRbacV1Lister := &operatorlisterfakes.FakeRbacV1Lister{}
    79  	fakeLister.CoreV1Returns(fakeCoreV1Lister)
    80  	fakeLister.RbacV1Returns(fakeRbacV1Lister)
    81  
    82  	fakeServiceLister := &listerfakes.FakeServiceLister{}
    83  	fakeCoreV1Lister.ServiceListerReturns(fakeServiceLister)
    84  	fakeServiceNamespacedLister := &listerfakes.FakeServiceNamespaceLister{}
    85  	fakeServiceLister.ServicesReturns(fakeServiceNamespacedLister)
    86  	fakeServiceNamespacedLister.GetReturns(state.existingService, state.getServiceError)
    87  
    88  	fakeSecretLister := &listerfakes.FakeSecretLister{}
    89  	fakeCoreV1Lister.SecretListerReturns(fakeSecretLister)
    90  	fakeSecretNamespacedLister := &listerfakes.FakeSecretNamespaceLister{}
    91  	fakeSecretLister.SecretsReturns(fakeSecretNamespacedLister)
    92  	fakeSecretNamespacedLister.GetReturns(state.existingSecret, state.getSecretError)
    93  
    94  	fakeRoleLister := &listerfakes.FakeRoleLister{}
    95  	fakeRbacV1Lister.RoleListerReturns(fakeRoleLister)
    96  	fakeRoleNamespacedLister := &listerfakes.FakeRoleNamespaceLister{}
    97  	fakeRoleLister.RolesReturns(fakeRoleNamespacedLister)
    98  	fakeRoleNamespacedLister.GetReturns(state.existingRole, state.getRoleError)
    99  
   100  	fakeRoleBindingLister := &listerfakes.FakeRoleBindingLister{}
   101  	fakeRbacV1Lister.RoleBindingListerReturns(fakeRoleBindingLister)
   102  	fakeRoleBindingNamespacedLister := &listerfakes.FakeRoleBindingNamespaceLister{}
   103  	fakeRoleBindingLister.RoleBindingsReturns(fakeRoleBindingNamespacedLister)
   104  	fakeRoleBindingNamespacedLister.GetReturns(state.existingRoleBinding, state.getRoleBindingError)
   105  
   106  	fakeClusterRoleBindingLister := &listerfakes.FakeClusterRoleBindingLister{}
   107  	fakeRbacV1Lister.ClusterRoleBindingListerReturns(fakeClusterRoleBindingLister)
   108  	fakeClusterRoleBindingLister.GetReturns(state.existingClusterRoleBinding, state.getClusterRoleBindingError)
   109  
   110  	return fakeLister
   111  }
   112  
   113  func TestInstallCertRequirementsForDeployment(t *testing.T) {
   114  	owner := ownerutil.Owner(&v1alpha1.ClusterServiceVersion{
   115  		TypeMeta: metav1.TypeMeta{
   116  			Kind:       v1alpha1.ClusterServiceVersionKind,
   117  			APIVersion: v1alpha1.ClusterServiceVersionAPIVersion,
   118  		},
   119  		ObjectMeta: metav1.ObjectMeta{
   120  			Name:      "owner",
   121  			Namespace: "test-namespace",
   122  			UID:       "123-uid",
   123  		},
   124  	})
   125  	ca := keyPair(t, time.Now().Add(time.Hour))
   126  	caPEM, _, err := ca.ToPEM()
   127  	assert.NoError(t, err)
   128  	caHash := certs.PEMSHA256(caPEM)
   129  	type fields struct {
   130  		owner                  ownerutil.Owner
   131  		previousStrategy       Strategy
   132  		templateAnnotations    map[string]string
   133  		initializers           DeploymentInitializerFuncChain
   134  		apiServiceDescriptions []certResource
   135  		webhookDescriptions    []certResource
   136  	}
   137  	type args struct {
   138  		deploymentName string
   139  		ca             *certs.KeyPair
   140  		rotateAt       time.Time
   141  		depSpec        appsv1.DeploymentSpec
   142  		ports          []corev1.ServicePort
   143  	}
   144  
   145  	type expectedExternalFunc func(clientInterface *operatorclientmocks.MockClientInterface, fakeLister *operatorlisterfakes.FakeOperatorLister, namespace string, args args)
   146  	tests := []struct {
   147  		name         string
   148  		mockExternal expectedExternalFunc
   149  		state        fakeState
   150  		fields       fields
   151  		args         args
   152  		want         *appsv1.DeploymentSpec
   153  		wantErr      bool
   154  	}{
   155  		{
   156  			name: "adds certs to deployment spec",
   157  			mockExternal: func(mockOpClient *operatorclientmocks.MockClientInterface, fakeLister *operatorlisterfakes.FakeOperatorLister, namespace string, args args) {
   158  				service := corev1.Service{
   159  					ObjectMeta: metav1.ObjectMeta{
   160  						Name:   "test-service",
   161  						Labels: map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   162  						OwnerReferences: []metav1.OwnerReference{
   163  							ownerutil.NonBlockingOwner(&v1alpha1.ClusterServiceVersion{}),
   164  						},
   165  					},
   166  					Spec: corev1.ServiceSpec{
   167  						Ports:    args.ports,
   168  						Selector: selector(t, "test=label").MatchLabels,
   169  					},
   170  				}
   171  
   172  				portsApplyConfig := []*corev1ac.ServicePortApplyConfiguration{}
   173  				for _, p := range args.ports {
   174  					ac := corev1ac.ServicePort().
   175  						WithName(p.Name).
   176  						WithPort(p.Port).
   177  						WithTargetPort(p.TargetPort)
   178  					portsApplyConfig = append(portsApplyConfig, ac)
   179  				}
   180  
   181  				svcApplyConfig := corev1ac.Service(service.Name, service.Namespace).
   182  					WithSpec(corev1ac.ServiceSpec().
   183  						WithPorts(portsApplyConfig...).
   184  						WithSelector(selector(t, "test=label").MatchLabels)).
   185  					WithOwnerReferences(ownerutil.NonBlockingOwnerApplyConfiguration(&v1alpha1.ClusterServiceVersion{})).
   186  					WithLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue})
   187  
   188  				mockOpClient.EXPECT().ApplyService(svcApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(&service, nil)
   189  
   190  				hosts := []string{
   191  					fmt.Sprintf("%s.%s", service.GetName(), namespace),
   192  					fmt.Sprintf("%s.%s.svc", service.GetName(), namespace),
   193  				}
   194  				servingPair, err := certGenerator.Generate(args.rotateAt, Organization, args.ca, hosts)
   195  				require.NoError(t, err)
   196  
   197  				// Create Secret for serving cert
   198  				certPEM, privPEM, err := servingPair.ToPEM()
   199  				require.NoError(t, err)
   200  
   201  				secret := &corev1.Secret{
   202  					ObjectMeta: metav1.ObjectMeta{
   203  						Name:        "test-service-cert",
   204  						Namespace:   namespace,
   205  						Annotations: map[string]string{OLMCAHashAnnotationKey: caHash},
   206  						Labels:      map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   207  					},
   208  					Data: map[string][]byte{
   209  						"tls.crt":   certPEM,
   210  						"tls.key":   privPEM,
   211  						OLMCAPEMKey: caPEM,
   212  					},
   213  					Type: corev1.SecretTypeTLS,
   214  				}
   215  				mockOpClient.EXPECT().UpdateSecret(secret).Return(secret, nil)
   216  
   217  				secretRole := &rbacv1.Role{
   218  					ObjectMeta: metav1.ObjectMeta{
   219  						Name:      secret.GetName(),
   220  						Namespace: namespace,
   221  						Labels:    map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   222  					},
   223  					Rules: []rbacv1.PolicyRule{
   224  						{
   225  							Verbs:         []string{"get"},
   226  							APIGroups:     []string{""},
   227  							Resources:     []string{"secrets"},
   228  							ResourceNames: []string{secret.GetName()},
   229  						},
   230  					},
   231  				}
   232  				mockOpClient.EXPECT().UpdateRole(secretRole).Return(secretRole, nil)
   233  
   234  				roleBinding := &rbacv1.RoleBinding{
   235  					ObjectMeta: metav1.ObjectMeta{
   236  						Name:      secret.GetName(),
   237  						Namespace: namespace,
   238  						Labels:    map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   239  					},
   240  					Subjects: []rbacv1.Subject{
   241  						{
   242  							Kind:      "ServiceAccount",
   243  							APIGroup:  "",
   244  							Name:      "test-sa",
   245  							Namespace: namespace,
   246  						},
   247  					},
   248  					RoleRef: rbacv1.RoleRef{
   249  						APIGroup: "rbac.authorization.k8s.io",
   250  						Kind:     "Role",
   251  						Name:     secretRole.GetName(),
   252  					},
   253  				}
   254  				mockOpClient.EXPECT().UpdateRoleBinding(roleBinding).Return(roleBinding, nil)
   255  
   256  				authDelegatorClusterRoleBinding := &rbacv1.ClusterRoleBinding{
   257  					ObjectMeta: metav1.ObjectMeta{
   258  						Name:   service.GetName() + "-system:auth-delegator",
   259  						Labels: map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   260  					},
   261  					Subjects: []rbacv1.Subject{
   262  						{
   263  							Kind:      "ServiceAccount",
   264  							APIGroup:  "",
   265  							Name:      "test-sa",
   266  							Namespace: namespace,
   267  						},
   268  					},
   269  					RoleRef: rbacv1.RoleRef{
   270  						APIGroup: "rbac.authorization.k8s.io",
   271  						Kind:     "ClusterRole",
   272  						Name:     "system:auth-delegator",
   273  					},
   274  				}
   275  
   276  				crbLabels := map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}
   277  				for key, val := range ownerutil.OwnerLabel(ownerutil.Owner(&v1alpha1.ClusterServiceVersion{}), owner.GetObjectKind().GroupVersionKind().Kind) {
   278  					crbLabels[key] = val
   279  				}
   280  				crbApplyConfig := rbacv1ac.ClusterRoleBinding(AuthDelegatorClusterRoleBindingName(service.GetName())).
   281  					WithSubjects(rbacv1ac.Subject().
   282  						WithKind("ServiceAccount").
   283  						WithAPIGroup("").
   284  						WithName(args.depSpec.Template.Spec.ServiceAccountName).
   285  						WithNamespace("")). // Empty owner with no namespace
   286  					WithRoleRef(rbacv1ac.RoleRef().
   287  						WithAPIGroup("rbac.authorization.k8s.io").
   288  						WithKind("ClusterRole").
   289  						WithName("system:auth-delegator")).
   290  					WithLabels(crbLabels)
   291  				mockOpClient.EXPECT().ApplyClusterRoleBinding(crbApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(authDelegatorClusterRoleBinding, nil)
   292  
   293  				authReaderRoleBinding := &rbacv1.RoleBinding{
   294  					Subjects: []rbacv1.Subject{
   295  						{
   296  							Kind:      "ServiceAccount",
   297  							APIGroup:  "",
   298  							Name:      args.depSpec.Template.Spec.ServiceAccountName,
   299  							Namespace: namespace,
   300  						},
   301  					},
   302  					RoleRef: rbacv1.RoleRef{
   303  						APIGroup: "rbac.authorization.k8s.io",
   304  						Kind:     "Role",
   305  						Name:     "extension-apiserver-authentication-reader",
   306  					},
   307  				}
   308  				authReaderRoleBinding.SetName(service.GetName() + "-auth-reader")
   309  				authReaderRoleBinding.SetNamespace(KubeSystem)
   310  				authReaderRoleBinding.SetLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue})
   311  
   312  				authReaderRoleBindingApplyConfig := rbacv1ac.RoleBinding(AuthReaderRoleBindingName(service.GetName()), KubeSystem).
   313  					WithLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}).
   314  					WithSubjects(rbacv1ac.Subject().
   315  						WithKind("ServiceAccount").
   316  						WithAPIGroup("").
   317  						WithName(args.depSpec.Template.Spec.ServiceAccountName).
   318  						WithNamespace(namespace)).
   319  					WithRoleRef(rbacv1ac.RoleRef().
   320  						WithAPIGroup("rbac.authorization.k8s.io").
   321  						WithKind("Role").
   322  						WithName("extension-apiserver-authentication-reader"))
   323  
   324  				mockOpClient.EXPECT().ApplyRoleBinding(authReaderRoleBindingApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(authReaderRoleBinding, nil)
   325  			},
   326  			state: fakeState{
   327  				existingService: &corev1.Service{
   328  					ObjectMeta: metav1.ObjectMeta{
   329  						OwnerReferences: []metav1.OwnerReference{
   330  							ownerutil.NonBlockingOwner(&v1alpha1.ClusterServiceVersion{}),
   331  						},
   332  					},
   333  				},
   334  				existingSecret: &corev1.Secret{
   335  					ObjectMeta: metav1.ObjectMeta{},
   336  				},
   337  				existingRole: &rbacv1.Role{
   338  					ObjectMeta: metav1.ObjectMeta{},
   339  				},
   340  				existingRoleBinding: &rbacv1.RoleBinding{
   341  					ObjectMeta: metav1.ObjectMeta{},
   342  				},
   343  				existingClusterRoleBinding: &rbacv1.ClusterRoleBinding{
   344  					ObjectMeta: metav1.ObjectMeta{},
   345  				},
   346  			},
   347  			fields: fields{
   348  				owner:                  &v1alpha1.ClusterServiceVersion{},
   349  				previousStrategy:       nil,
   350  				templateAnnotations:    nil,
   351  				initializers:           nil,
   352  				apiServiceDescriptions: []certResource{},
   353  				webhookDescriptions:    []certResource{},
   354  			},
   355  			args: args{
   356  				deploymentName: "test",
   357  				ca:             ca,
   358  				rotateAt:       time.Now().Add(time.Hour),
   359  				ports:          []corev1.ServicePort{},
   360  				depSpec: appsv1.DeploymentSpec{
   361  					Selector: selector(t, "test=label"),
   362  					Template: corev1.PodTemplateSpec{
   363  						Spec: corev1.PodSpec{
   364  							ServiceAccountName: "test-sa",
   365  						},
   366  						ObjectMeta: metav1.ObjectMeta{
   367  							Annotations: map[string]string{
   368  								"foo": "bar",
   369  							},
   370  						},
   371  					},
   372  				},
   373  			},
   374  			want: &appsv1.DeploymentSpec{
   375  				Selector: selector(t, "test=label"),
   376  				Template: corev1.PodTemplateSpec{
   377  					ObjectMeta: metav1.ObjectMeta{
   378  						Annotations: map[string]string{
   379  							"foo":                  "bar",
   380  							OLMCAHashAnnotationKey: caHash},
   381  					},
   382  					Spec: corev1.PodSpec{
   383  						ServiceAccountName: "test-sa",
   384  						Volumes: []corev1.Volume{
   385  							{
   386  								Name: "apiservice-cert",
   387  								VolumeSource: corev1.VolumeSource{
   388  									Secret: &corev1.SecretVolumeSource{
   389  										SecretName: "test-service-cert",
   390  										Items: []corev1.KeyToPath{
   391  											{
   392  												Key:  "tls.crt",
   393  												Path: "apiserver.crt",
   394  											},
   395  											{
   396  												Key:  "tls.key",
   397  												Path: "apiserver.key",
   398  											},
   399  										},
   400  									},
   401  								},
   402  							},
   403  							{
   404  								Name: "webhook-cert",
   405  								VolumeSource: corev1.VolumeSource{
   406  									Secret: &corev1.SecretVolumeSource{
   407  										SecretName: "test-service-cert",
   408  										Items: []corev1.KeyToPath{
   409  											{
   410  												Key:  "tls.crt",
   411  												Path: "tls.crt",
   412  											},
   413  											{
   414  												Key:  "tls.key",
   415  												Path: "tls.key",
   416  											},
   417  										},
   418  									},
   419  								},
   420  							},
   421  						},
   422  					},
   423  				},
   424  			},
   425  		},
   426  		{
   427  			name: "doesn't add duplicate service ownerrefs",
   428  			mockExternal: func(mockOpClient *operatorclientmocks.MockClientInterface, fakeLister *operatorlisterfakes.FakeOperatorLister, namespace string, args args) {
   429  				service := corev1.Service{
   430  					ObjectMeta: metav1.ObjectMeta{
   431  						Name:      "test-service",
   432  						Namespace: owner.GetNamespace(),
   433  						Labels:    map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   434  						OwnerReferences: []metav1.OwnerReference{
   435  							ownerutil.NonBlockingOwner(owner),
   436  						},
   437  					},
   438  					Spec: corev1.ServiceSpec{
   439  						Ports:    args.ports,
   440  						Selector: selector(t, "test=label").MatchLabels,
   441  					},
   442  				}
   443  				portsApplyConfig := []*corev1ac.ServicePortApplyConfiguration{}
   444  				for _, p := range args.ports {
   445  					ac := corev1ac.ServicePort().
   446  						WithName(p.Name).
   447  						WithPort(p.Port).
   448  						WithTargetPort(p.TargetPort)
   449  					portsApplyConfig = append(portsApplyConfig, ac)
   450  				}
   451  
   452  				svcApplyConfig := corev1ac.Service(service.Name, service.Namespace).
   453  					WithSpec(corev1ac.ServiceSpec().
   454  						WithPorts(portsApplyConfig...).
   455  						WithSelector(selector(t, "test=label").MatchLabels)).
   456  					WithOwnerReferences(ownerutil.NonBlockingOwnerApplyConfiguration(owner)).
   457  					WithLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue})
   458  
   459  				mockOpClient.EXPECT().ApplyService(svcApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(&service, nil)
   460  
   461  				hosts := []string{
   462  					fmt.Sprintf("%s.%s", service.GetName(), namespace),
   463  					fmt.Sprintf("%s.%s.svc", service.GetName(), namespace),
   464  				}
   465  				servingPair, err := certGenerator.Generate(args.rotateAt, Organization, args.ca, hosts)
   466  				require.NoError(t, err)
   467  
   468  				// Create Secret for serving cert
   469  				certPEM, privPEM, err := servingPair.ToPEM()
   470  				require.NoError(t, err)
   471  
   472  				secret := &corev1.Secret{
   473  					ObjectMeta: metav1.ObjectMeta{
   474  						Name:        "test-service-cert",
   475  						Namespace:   namespace,
   476  						Annotations: map[string]string{OLMCAHashAnnotationKey: caHash},
   477  						Labels:      map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   478  					},
   479  					Data: map[string][]byte{
   480  						"tls.crt":   certPEM,
   481  						"tls.key":   privPEM,
   482  						OLMCAPEMKey: caPEM,
   483  					},
   484  					Type: corev1.SecretTypeTLS,
   485  				}
   486  				mockOpClient.EXPECT().UpdateSecret(secret).Return(secret, nil)
   487  
   488  				secretRole := &rbacv1.Role{
   489  					ObjectMeta: metav1.ObjectMeta{
   490  						Name:      secret.GetName(),
   491  						Namespace: namespace,
   492  						Labels:    map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   493  					},
   494  					Rules: []rbacv1.PolicyRule{
   495  						{
   496  							Verbs:         []string{"get"},
   497  							APIGroups:     []string{""},
   498  							Resources:     []string{"secrets"},
   499  							ResourceNames: []string{secret.GetName()},
   500  						},
   501  					},
   502  				}
   503  				mockOpClient.EXPECT().UpdateRole(secretRole).Return(secretRole, nil)
   504  
   505  				roleBinding := &rbacv1.RoleBinding{
   506  					ObjectMeta: metav1.ObjectMeta{
   507  						Name:      secret.GetName(),
   508  						Namespace: namespace,
   509  						Labels:    map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   510  					},
   511  					Subjects: []rbacv1.Subject{
   512  						{
   513  							Kind:      "ServiceAccount",
   514  							APIGroup:  "",
   515  							Name:      "test-sa",
   516  							Namespace: namespace,
   517  						},
   518  					},
   519  					RoleRef: rbacv1.RoleRef{
   520  						APIGroup: "rbac.authorization.k8s.io",
   521  						Kind:     "Role",
   522  						Name:     secretRole.GetName(),
   523  					},
   524  				}
   525  				mockOpClient.EXPECT().UpdateRoleBinding(roleBinding).Return(roleBinding, nil)
   526  
   527  				authDelegatorClusterRoleBinding := &rbacv1.ClusterRoleBinding{
   528  					ObjectMeta: metav1.ObjectMeta{
   529  						Name:   service.GetName() + "-system:auth-delegator",
   530  						Labels: map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   531  					},
   532  					Subjects: []rbacv1.Subject{
   533  						{
   534  							Kind:      "ServiceAccount",
   535  							APIGroup:  "",
   536  							Name:      "test-sa",
   537  							Namespace: namespace,
   538  						},
   539  					},
   540  					RoleRef: rbacv1.RoleRef{
   541  						APIGroup: "rbac.authorization.k8s.io",
   542  						Kind:     "ClusterRole",
   543  						Name:     "system:auth-delegator",
   544  					},
   545  				}
   546  
   547  				crbLabels := map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}
   548  				for key, val := range ownerutil.OwnerLabel(owner, owner.GetObjectKind().GroupVersionKind().Kind) {
   549  					crbLabels[key] = val
   550  				}
   551  				crbApplyConfig := rbacv1ac.ClusterRoleBinding(service.GetName() + "-system:auth-delegator").
   552  					WithSubjects(rbacv1ac.Subject().
   553  						WithKind("ServiceAccount").
   554  						WithAPIGroup("").
   555  						WithName("test-sa").
   556  						WithNamespace(namespace)).
   557  					WithRoleRef(rbacv1ac.RoleRef().
   558  						WithAPIGroup("rbac.authorization.k8s.io").
   559  						WithKind("ClusterRole").
   560  						WithName("system:auth-delegator")).
   561  					WithLabels(crbLabels)
   562  
   563  				mockOpClient.EXPECT().ApplyClusterRoleBinding(crbApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(authDelegatorClusterRoleBinding, nil)
   564  
   565  				authReaderRoleBinding := &rbacv1.RoleBinding{
   566  					Subjects: []rbacv1.Subject{
   567  						{
   568  							Kind:      "ServiceAccount",
   569  							APIGroup:  "",
   570  							Name:      args.depSpec.Template.Spec.ServiceAccountName,
   571  							Namespace: namespace,
   572  						},
   573  					},
   574  					RoleRef: rbacv1.RoleRef{
   575  						APIGroup: "rbac.authorization.k8s.io",
   576  						Kind:     "Role",
   577  						Name:     "extension-apiserver-authentication-reader",
   578  					},
   579  				}
   580  				authReaderRoleBinding.SetName(service.GetName() + "-auth-reader")
   581  				authReaderRoleBinding.SetNamespace(KubeSystem)
   582  				authReaderRoleBinding.SetLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue})
   583  
   584  				authReaderRoleBindingApplyConfig := rbacv1ac.RoleBinding(AuthReaderRoleBindingName(service.GetName()), KubeSystem).
   585  					WithLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}).
   586  					WithSubjects(rbacv1ac.Subject().
   587  						WithKind("ServiceAccount").
   588  						WithAPIGroup("").
   589  						WithName(args.depSpec.Template.Spec.ServiceAccountName).
   590  						WithNamespace(namespace)).
   591  					WithRoleRef(rbacv1ac.RoleRef().
   592  						WithAPIGroup("rbac.authorization.k8s.io").
   593  						WithKind("Role").
   594  						WithName("extension-apiserver-authentication-reader"))
   595  
   596  				mockOpClient.EXPECT().ApplyRoleBinding(authReaderRoleBindingApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(authReaderRoleBinding, nil)
   597  			},
   598  			state: fakeState{
   599  				existingService: &corev1.Service{
   600  					ObjectMeta: metav1.ObjectMeta{
   601  						Namespace: owner.GetNamespace(),
   602  						OwnerReferences: []metav1.OwnerReference{
   603  							ownerutil.NonBlockingOwner(owner),
   604  						},
   605  					},
   606  				},
   607  				existingSecret: &corev1.Secret{
   608  					ObjectMeta: metav1.ObjectMeta{},
   609  				},
   610  				existingRole: &rbacv1.Role{
   611  					ObjectMeta: metav1.ObjectMeta{},
   612  				},
   613  				existingRoleBinding: &rbacv1.RoleBinding{
   614  					ObjectMeta: metav1.ObjectMeta{},
   615  				},
   616  				existingClusterRoleBinding: &rbacv1.ClusterRoleBinding{
   617  					ObjectMeta: metav1.ObjectMeta{},
   618  				},
   619  			},
   620  			fields: fields{
   621  				owner:                  owner,
   622  				previousStrategy:       nil,
   623  				templateAnnotations:    nil,
   624  				initializers:           nil,
   625  				apiServiceDescriptions: []certResource{},
   626  				webhookDescriptions:    []certResource{},
   627  			},
   628  			args: args{
   629  				deploymentName: "test",
   630  				ca:             ca,
   631  				rotateAt:       time.Now().Add(time.Hour),
   632  				ports:          []corev1.ServicePort{},
   633  				depSpec: appsv1.DeploymentSpec{
   634  					Selector: selector(t, "test=label"),
   635  					Template: corev1.PodTemplateSpec{
   636  						Spec: corev1.PodSpec{
   637  							ServiceAccountName: "test-sa",
   638  						},
   639  					},
   640  				},
   641  			},
   642  			want: &appsv1.DeploymentSpec{
   643  				Selector: selector(t, "test=label"),
   644  				Template: corev1.PodTemplateSpec{
   645  					ObjectMeta: metav1.ObjectMeta{
   646  						Annotations: map[string]string{OLMCAHashAnnotationKey: caHash},
   647  					},
   648  					Spec: corev1.PodSpec{
   649  						ServiceAccountName: "test-sa",
   650  						Volumes: []corev1.Volume{
   651  							{
   652  								Name: "apiservice-cert",
   653  								VolumeSource: corev1.VolumeSource{
   654  									Secret: &corev1.SecretVolumeSource{
   655  										SecretName: "test-service-cert",
   656  										Items: []corev1.KeyToPath{
   657  											{
   658  												Key:  "tls.crt",
   659  												Path: "apiserver.crt",
   660  											},
   661  											{
   662  												Key:  "tls.key",
   663  												Path: "apiserver.key",
   664  											},
   665  										},
   666  									},
   667  								},
   668  							},
   669  							{
   670  								Name: "webhook-cert",
   671  								VolumeSource: corev1.VolumeSource{
   672  									Secret: &corev1.SecretVolumeSource{
   673  										SecretName: "test-service-cert",
   674  										Items: []corev1.KeyToPath{
   675  											{
   676  												Key:  "tls.crt",
   677  												Path: "tls.crt",
   678  											},
   679  											{
   680  												Key:  "tls.key",
   681  												Path: "tls.key",
   682  											},
   683  										},
   684  									},
   685  								},
   686  							},
   687  						},
   688  					},
   689  				},
   690  			},
   691  		},
   692  		{
   693  			name: "labels an unlabelled secret if present; creates Service and ClusterRoleBinding if not existing",
   694  			mockExternal: func(mockOpClient *operatorclientmocks.MockClientInterface, fakeLister *operatorlisterfakes.FakeOperatorLister, namespace string, args args) {
   695  				service := corev1.Service{
   696  					ObjectMeta: metav1.ObjectMeta{
   697  						Name:   "test-service",
   698  						Labels: map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   699  						OwnerReferences: []metav1.OwnerReference{
   700  							ownerutil.NonBlockingOwner(&v1alpha1.ClusterServiceVersion{}),
   701  						},
   702  					},
   703  					Spec: corev1.ServiceSpec{
   704  						Ports:    args.ports,
   705  						Selector: selector(t, "test=label").MatchLabels,
   706  					},
   707  				}
   708  
   709  				portsApplyConfig := []*corev1ac.ServicePortApplyConfiguration{}
   710  				for _, p := range args.ports {
   711  					ac := corev1ac.ServicePort().
   712  						WithName(p.Name).
   713  						WithPort(p.Port).
   714  						WithTargetPort(p.TargetPort)
   715  					portsApplyConfig = append(portsApplyConfig, ac)
   716  				}
   717  
   718  				svcApplyConfig := corev1ac.Service(service.Name, service.Namespace).
   719  					WithSpec(corev1ac.ServiceSpec().
   720  						WithPorts(portsApplyConfig...).
   721  						WithSelector(selector(t, "test=label").MatchLabels)).
   722  					WithOwnerReferences(ownerutil.NonBlockingOwnerApplyConfiguration(&v1alpha1.ClusterServiceVersion{})).
   723  					WithLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue})
   724  
   725  				mockOpClient.EXPECT().ApplyService(svcApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(&service, nil)
   726  
   727  				hosts := []string{
   728  					fmt.Sprintf("%s.%s", service.GetName(), namespace),
   729  					fmt.Sprintf("%s.%s.svc", service.GetName(), namespace),
   730  				}
   731  				servingPair, err := certGenerator.Generate(args.rotateAt, Organization, args.ca, hosts)
   732  				require.NoError(t, err)
   733  
   734  				// Create Secret for serving cert
   735  				certPEM, privPEM, err := servingPair.ToPEM()
   736  				require.NoError(t, err)
   737  
   738  				secret := &corev1.Secret{
   739  					ObjectMeta: metav1.ObjectMeta{
   740  						Name:        "test-service-cert",
   741  						Namespace:   namespace,
   742  						Annotations: map[string]string{OLMCAHashAnnotationKey: caHash},
   743  						Labels:      map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   744  						OwnerReferences: []metav1.OwnerReference{
   745  							ownerutil.NonBlockingOwner(&v1alpha1.ClusterServiceVersion{}),
   746  						},
   747  					},
   748  					Data: map[string][]byte{
   749  						"tls.crt":   certPEM,
   750  						"tls.key":   privPEM,
   751  						OLMCAPEMKey: caPEM,
   752  					},
   753  					Type: corev1.SecretTypeTLS,
   754  				}
   755  				// secret already exists, but without label
   756  				mockOpClient.EXPECT().CreateSecret(secret).Return(nil, errors.NewAlreadyExists(schema.GroupResource{
   757  					Group:    "",
   758  					Resource: "secrets",
   759  				}, "test-service-cert"))
   760  
   761  				// update secret with label
   762  				mockOpClient.EXPECT().UpdateSecret(secret).Return(secret, nil)
   763  
   764  				secretRole := &rbacv1.Role{
   765  					ObjectMeta: metav1.ObjectMeta{
   766  						Name:      secret.GetName(),
   767  						Namespace: namespace,
   768  						Labels:    map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   769  					},
   770  					Rules: []rbacv1.PolicyRule{
   771  						{
   772  							Verbs:         []string{"get"},
   773  							APIGroups:     []string{""},
   774  							Resources:     []string{"secrets"},
   775  							ResourceNames: []string{secret.GetName()},
   776  						},
   777  					},
   778  				}
   779  				mockOpClient.EXPECT().UpdateRole(secretRole).Return(secretRole, nil)
   780  
   781  				roleBinding := &rbacv1.RoleBinding{
   782  					ObjectMeta: metav1.ObjectMeta{
   783  						Name:      secret.GetName(),
   784  						Namespace: namespace,
   785  						Labels:    map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   786  					},
   787  					Subjects: []rbacv1.Subject{
   788  						{
   789  							Kind:      "ServiceAccount",
   790  							APIGroup:  "",
   791  							Name:      "test-sa",
   792  							Namespace: namespace,
   793  						},
   794  					},
   795  					RoleRef: rbacv1.RoleRef{
   796  						APIGroup: "rbac.authorization.k8s.io",
   797  						Kind:     "Role",
   798  						Name:     secretRole.GetName(),
   799  					},
   800  				}
   801  				mockOpClient.EXPECT().UpdateRoleBinding(roleBinding).Return(roleBinding, nil)
   802  
   803  				authDelegatorClusterRoleBinding := &rbacv1.ClusterRoleBinding{
   804  					ObjectMeta: metav1.ObjectMeta{
   805  						Name:   service.GetName() + "-system:auth-delegator",
   806  						Labels: map[string]string{OLMManagedLabelKey: OLMManagedLabelValue},
   807  					},
   808  					Subjects: []rbacv1.Subject{
   809  						{
   810  							Kind:      "ServiceAccount",
   811  							APIGroup:  "",
   812  							Name:      "test-sa",
   813  							Namespace: namespace,
   814  						},
   815  					},
   816  					RoleRef: rbacv1.RoleRef{
   817  						APIGroup: "rbac.authorization.k8s.io",
   818  						Kind:     "ClusterRole",
   819  						Name:     "system:auth-delegator",
   820  					},
   821  				}
   822  				crbLabels := map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}
   823  				for key, val := range ownerutil.OwnerLabel(ownerutil.Owner(&v1alpha1.ClusterServiceVersion{}), owner.GetObjectKind().GroupVersionKind().Kind) {
   824  					crbLabels[key] = val
   825  				}
   826  				crbApplyConfig := rbacv1ac.ClusterRoleBinding(AuthDelegatorClusterRoleBindingName(service.GetName())).
   827  					WithSubjects(rbacv1ac.Subject().WithKind("ServiceAccount").
   828  						WithAPIGroup("").
   829  						WithName("test-sa").
   830  						WithNamespace(namespace)).
   831  					WithRoleRef(rbacv1ac.RoleRef().
   832  						WithAPIGroup("rbac.authorization.k8s.io").
   833  						WithKind("ClusterRole").
   834  						WithName("system:auth-delegator")).
   835  					WithLabels(crbLabels)
   836  
   837  				mockOpClient.EXPECT().ApplyClusterRoleBinding(crbApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(authDelegatorClusterRoleBinding, nil)
   838  
   839  				authReaderRoleBinding := &rbacv1.RoleBinding{
   840  					Subjects: []rbacv1.Subject{
   841  						{
   842  							Kind:      "ServiceAccount",
   843  							APIGroup:  "",
   844  							Name:      args.depSpec.Template.Spec.ServiceAccountName,
   845  							Namespace: namespace,
   846  						},
   847  					},
   848  					RoleRef: rbacv1.RoleRef{
   849  						APIGroup: "rbac.authorization.k8s.io",
   850  						Kind:     "Role",
   851  						Name:     "extension-apiserver-authentication-reader",
   852  					},
   853  				}
   854  				authReaderRoleBinding.SetName(service.GetName() + "-auth-reader")
   855  				authReaderRoleBinding.SetNamespace(KubeSystem)
   856  				authReaderRoleBinding.SetLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue})
   857  
   858  				authReaderRoleBindingApplyConfig := rbacv1ac.RoleBinding(AuthReaderRoleBindingName(service.GetName()), KubeSystem).
   859  					WithLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}).
   860  					WithSubjects(rbacv1ac.Subject().
   861  						WithKind("ServiceAccount").
   862  						WithAPIGroup("").
   863  						WithName(args.depSpec.Template.Spec.ServiceAccountName).
   864  						WithNamespace(namespace)).
   865  					WithRoleRef(rbacv1ac.RoleRef().
   866  						WithAPIGroup("rbac.authorization.k8s.io").
   867  						WithKind("Role").
   868  						WithName("extension-apiserver-authentication-reader"))
   869  
   870  				mockOpClient.EXPECT().ApplyRoleBinding(authReaderRoleBindingApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(authReaderRoleBinding, nil)
   871  			},
   872  			state: fakeState{
   873  				existingService: nil,
   874  				// unlabelled secret won't be in cache
   875  				getSecretError: errors.NewNotFound(schema.GroupResource{
   876  					Group:    "",
   877  					Resource: "Secret",
   878  				}, "nope"),
   879  				existingRole: &rbacv1.Role{
   880  					ObjectMeta: metav1.ObjectMeta{},
   881  				},
   882  				existingRoleBinding: &rbacv1.RoleBinding{
   883  					ObjectMeta: metav1.ObjectMeta{},
   884  				},
   885  				existingClusterRoleBinding: nil,
   886  			},
   887  			fields: fields{
   888  				owner:                  &v1alpha1.ClusterServiceVersion{},
   889  				previousStrategy:       nil,
   890  				templateAnnotations:    nil,
   891  				initializers:           nil,
   892  				apiServiceDescriptions: []certResource{},
   893  				webhookDescriptions:    []certResource{},
   894  			},
   895  			args: args{
   896  				deploymentName: "test",
   897  				ca:             ca,
   898  				rotateAt:       time.Now().Add(time.Hour),
   899  				ports:          []corev1.ServicePort{},
   900  				depSpec: appsv1.DeploymentSpec{
   901  					Selector: selector(t, "test=label"),
   902  					Template: corev1.PodTemplateSpec{
   903  						Spec: corev1.PodSpec{
   904  							ServiceAccountName: "test-sa",
   905  						},
   906  						ObjectMeta: metav1.ObjectMeta{
   907  							Annotations: map[string]string{
   908  								"foo": "bar",
   909  							},
   910  						},
   911  					},
   912  				},
   913  			},
   914  			want: &appsv1.DeploymentSpec{
   915  				Selector: selector(t, "test=label"),
   916  				Template: corev1.PodTemplateSpec{
   917  					ObjectMeta: metav1.ObjectMeta{
   918  						Annotations: map[string]string{
   919  							"foo":                  "bar",
   920  							OLMCAHashAnnotationKey: caHash},
   921  					},
   922  					Spec: corev1.PodSpec{
   923  						ServiceAccountName: "test-sa",
   924  						Volumes: []corev1.Volume{
   925  							{
   926  								Name: "apiservice-cert",
   927  								VolumeSource: corev1.VolumeSource{
   928  									Secret: &corev1.SecretVolumeSource{
   929  										SecretName: "test-service-cert",
   930  										Items: []corev1.KeyToPath{
   931  											{
   932  												Key:  "tls.crt",
   933  												Path: "apiserver.crt",
   934  											},
   935  											{
   936  												Key:  "tls.key",
   937  												Path: "apiserver.key",
   938  											},
   939  										},
   940  									},
   941  								},
   942  							},
   943  							{
   944  								Name: "webhook-cert",
   945  								VolumeSource: corev1.VolumeSource{
   946  									Secret: &corev1.SecretVolumeSource{
   947  										SecretName: "test-service-cert",
   948  										Items: []corev1.KeyToPath{
   949  											{
   950  												Key:  "tls.crt",
   951  												Path: "tls.crt",
   952  											},
   953  											{
   954  												Key:  "tls.key",
   955  												Path: "tls.key",
   956  											},
   957  										},
   958  									},
   959  								},
   960  							},
   961  						},
   962  					},
   963  				},
   964  			},
   965  		},
   966  	}
   967  	for _, tt := range tests {
   968  		t.Run(tt.name, func(t *testing.T) {
   969  			ctrl := gomock.NewController(t)
   970  			defer ctrl.Finish()
   971  
   972  			certGenerator = certs.CertGeneratorFunc(staticCertGenerator)
   973  
   974  			mockOpClient := operatorclientmocks.NewMockClientInterface(ctrl)
   975  			fakeLister := newFakeLister(tt.state)
   976  			tt.mockExternal(mockOpClient, fakeLister, tt.fields.owner.GetNamespace(), tt.args)
   977  
   978  			client := wrappers.NewInstallStrategyDeploymentClient(mockOpClient, fakeLister, tt.fields.owner.GetNamespace())
   979  			i := &StrategyDeploymentInstaller{
   980  				strategyClient:         client,
   981  				owner:                  tt.fields.owner,
   982  				previousStrategy:       tt.fields.previousStrategy,
   983  				templateAnnotations:    tt.fields.templateAnnotations,
   984  				initializers:           tt.fields.initializers,
   985  				apiServiceDescriptions: tt.fields.apiServiceDescriptions,
   986  				webhookDescriptions:    tt.fields.webhookDescriptions,
   987  			}
   988  			got, _, err := i.installCertRequirementsForDeployment(tt.args.deploymentName, tt.args.ca, tt.args.rotateAt, tt.args.depSpec, tt.args.ports)
   989  			if (err != nil) != tt.wantErr {
   990  				t.Errorf("installCertRequirementsForDeployment() error = %v, wantErr %v", err, tt.wantErr)
   991  				return
   992  			}
   993  			if !reflect.DeepEqual(got, tt.want) {
   994  				t.Errorf("installCertRequirementsForDeployment() \n got  = %v \n want = %v\n diff=%s\n", got, tt.want, cmp.Diff(got, tt.want))
   995  			}
   996  		})
   997  	}
   998  }