diff --git a/pkg/controller/install/certresources.go b/pkg/controller/install/certresources.go index 45bc78d0c9..efa351a13c 100644 --- a/pkg/controller/install/certresources.go +++ b/pkg/controller/install/certresources.go @@ -12,6 +12,8 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + corev1ac "k8s.io/client-go/applyconfigurations/core/v1" + rbacv1ac "k8s.io/client-go/applyconfigurations/rbac/v1" "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/certs" @@ -241,43 +243,33 @@ func CalculateCertRotatesAt(certExpirationTime time.Time) time.Time { func (i *StrategyDeploymentInstaller) installCertRequirementsForDeployment(deploymentName string, ca *certs.KeyPair, expiration time.Time, depSpec appsv1.DeploymentSpec, ports []corev1.ServicePort) (*appsv1.DeploymentSpec, []byte, error) { logger := log.WithFields(log.Fields{}) - // Create a service for the deployment - service := &corev1.Service{ - Spec: corev1.ServiceSpec{ - Ports: ports, - Selector: depSpec.Selector.MatchLabels, - }, - } - service.SetName(ServiceName(deploymentName)) - service.SetNamespace(i.owner.GetNamespace()) - ownerutil.AddNonBlockingOwner(service, i.owner) - service.SetLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}) - - existingService, err := i.strategyClient.GetOpLister().CoreV1().ServiceLister().Services(i.owner.GetNamespace()).Get(service.GetName()) - if err == nil { - if !ownerutil.Adoptable(i.owner, existingService.GetOwnerReferences()) { - return nil, nil, fmt.Errorf("service %s not safe to replace: extraneous ownerreferences found", service.GetName()) - } - service.SetOwnerReferences(existingService.GetOwnerReferences()) - - // Delete the Service to replace - deleteErr := i.strategyClient.GetOpClient().DeleteService(service.GetNamespace(), service.GetName(), &metav1.DeleteOptions{}) - if deleteErr != nil && !apierrors.IsNotFound(deleteErr) { - return nil, nil, fmt.Errorf("could not delete existing service %s", service.GetName()) - } - } - - // Attempt to create the Service - _, err = i.strategyClient.GetOpClient().CreateService(service) - if err != nil { - logger.Warnf("could not create service %s", service.GetName()) - return nil, nil, fmt.Errorf("could not create service %s: %s", service.GetName(), err.Error()) + // apply Service + serviceName := ServiceName(deploymentName) + portsApplyConfig := []*corev1ac.ServicePortApplyConfiguration{} + for _, p := range ports { + ac := corev1ac.ServicePort(). + WithName(p.Name). + WithPort(p.Port). + WithTargetPort(p.TargetPort) + portsApplyConfig = append(portsApplyConfig, ac) + } + + svcApplyConfig := corev1ac.Service(serviceName, i.owner.GetNamespace()). + WithSpec(corev1ac.ServiceSpec(). + WithPorts(portsApplyConfig...). + WithSelector(depSpec.Selector.MatchLabels)). + WithOwnerReferences(ownerutil.NonBlockingOwnerApplyConfiguration(i.owner)). + WithLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}) + + if _, err := i.strategyClient.GetOpClient().ApplyService(svcApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}); err != nil { + log.Errorf("could not apply service %s: %s", *svcApplyConfig.Name, err.Error()) + return nil, nil, err } // Create signed serving cert hosts := []string{ - fmt.Sprintf("%s.%s", service.GetName(), i.owner.GetNamespace()), - fmt.Sprintf("%s.%s.svc", service.GetName(), i.owner.GetNamespace()), + fmt.Sprintf("%s.%s", serviceName, i.owner.GetNamespace()), + fmt.Sprintf("%s.%s.svc", serviceName, i.owner.GetNamespace()), } servingPair, err := certGenerator.Generate(expiration, Organization, ca, hosts) if err != nil { @@ -288,14 +280,14 @@ func (i *StrategyDeploymentInstaller) installCertRequirementsForDeployment(deplo // Create Secret for serving cert certPEM, privPEM, err := servingPair.ToPEM() if err != nil { - logger.Warnf("unable to convert serving certificate and private key to PEM format for Service %s", service.GetName()) + logger.Warnf("unable to convert serving certificate and private key to PEM format for Service %s", serviceName) return nil, nil, err } // Add olmcahash as a label to the caPEM caPEM, _, err := ca.ToPEM() if err != nil { - logger.Warnf("unable to convert CA certificate to PEM format for Service %s", service) + logger.Warnf("unable to convert CA certificate to PEM format for Service %s", serviceName) return nil, nil, err } caHash := certs.PEMSHA256(caPEM) @@ -308,7 +300,7 @@ func (i *StrategyDeploymentInstaller) installCertRequirementsForDeployment(deplo }, Type: corev1.SecretTypeTLS, } - secret.SetName(SecretName(service.GetName())) + secret.SetName(SecretName(serviceName)) secret.SetNamespace(i.owner.GetNamespace()) secret.SetAnnotations(map[string]string{OLMCAHashAnnotationKey: caHash}) secret.SetLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}) @@ -440,51 +432,25 @@ func (i *StrategyDeploymentInstaller) installCertRequirementsForDeployment(deplo return nil, nil, err } - // create ClusterRoleBinding to system:auth-delegator Role - authDelegatorClusterRoleBinding := &rbacv1.ClusterRoleBinding{ - Subjects: []rbacv1.Subject{ - { - Kind: "ServiceAccount", - APIGroup: "", - Name: depSpec.Template.Spec.ServiceAccountName, - Namespace: i.owner.GetNamespace(), - }, - }, - RoleRef: rbacv1.RoleRef{ - APIGroup: "rbac.authorization.k8s.io", - Kind: "ClusterRole", - Name: "system:auth-delegator", - }, - } - authDelegatorClusterRoleBinding.SetName(AuthDelegatorClusterRoleBindingName(service.GetName())) - authDelegatorClusterRoleBinding.SetLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}) - - existingAuthDelegatorClusterRoleBinding, err := i.strategyClient.GetOpLister().RbacV1().ClusterRoleBindingLister().Get(authDelegatorClusterRoleBinding.GetName()) - if err == nil { - // Check if the only owners are this CSV or in this CSV's replacement chain. - if ownerutil.AdoptableLabels(existingAuthDelegatorClusterRoleBinding.GetLabels(), true, i.owner) { - logger.WithFields(log.Fields{"obj": "authDelegatorCRB", "labels": existingAuthDelegatorClusterRoleBinding.GetLabels()}).Debug("adopting") - if err := ownerutil.AddOwnerLabels(authDelegatorClusterRoleBinding, i.owner); err != nil { - return nil, nil, err - } - } - - // Attempt an update. - if _, err := i.strategyClient.GetOpClient().UpdateClusterRoleBinding(authDelegatorClusterRoleBinding); err != nil { - logger.Warnf("could not update auth delegator clusterrolebinding %s", authDelegatorClusterRoleBinding.GetName()) - return nil, nil, err - } - } else if apierrors.IsNotFound(err) { - // Create the role. - if err := ownerutil.AddOwnerLabels(authDelegatorClusterRoleBinding, i.owner); err != nil { - return nil, nil, err - } - _, err = i.strategyClient.GetOpClient().CreateClusterRoleBinding(authDelegatorClusterRoleBinding) - if err != nil { - log.Warnf("could not create auth delegator clusterrolebinding %s", authDelegatorClusterRoleBinding.GetName()) - return nil, nil, err - } - } else { + // apply ClusterRoleBinding to system:auth-delegator Role + crbLabels := map[string]string{OLMManagedLabelKey: OLMManagedLabelValue} + for key, val := range ownerutil.OwnerLabel(i.owner, i.owner.GetObjectKind().GroupVersionKind().Kind) { + crbLabels[key] = val + } + crbApplyConfig := rbacv1ac.ClusterRoleBinding(AuthDelegatorClusterRoleBindingName(serviceName)). + WithSubjects(rbacv1ac.Subject(). + WithKind("ServiceAccount"). + WithAPIGroup(""). + WithName(depSpec.Template.Spec.ServiceAccountName). + WithNamespace(i.owner.GetNamespace())). + WithRoleRef(rbacv1ac.RoleRef(). + WithAPIGroup("rbac.authorization.k8s.io"). + WithKind("ClusterRole"). + WithName("system:auth-delegator")). + WithLabels(crbLabels) + + if _, err = i.strategyClient.GetOpClient().ApplyClusterRoleBinding(crbApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}); err != nil { + log.Errorf("could not apply auth delegator clusterrolebinding %s: %s", *crbApplyConfig.Name, err.Error()) return nil, nil, err } @@ -504,7 +470,7 @@ func (i *StrategyDeploymentInstaller) installCertRequirementsForDeployment(deplo Name: "extension-apiserver-authentication-reader", }, } - authReaderRoleBinding.SetName(AuthReaderRoleBindingName(service.GetName())) + authReaderRoleBinding.SetName(AuthReaderRoleBindingName(serviceName)) authReaderRoleBinding.SetNamespace(KubeSystem) authReaderRoleBinding.SetLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}) diff --git a/pkg/controller/install/certresources_test.go b/pkg/controller/install/certresources_test.go index ad8e1041b4..7e466fa38d 100644 --- a/pkg/controller/install/certresources_test.go +++ b/pkg/controller/install/certresources_test.go @@ -16,6 +16,8 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + corev1ac "k8s.io/client-go/applyconfigurations/core/v1" + rbacv1ac "k8s.io/client-go/applyconfigurations/rbac/v1" "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/wrappers" @@ -153,7 +155,6 @@ func TestInstallCertRequirementsForDeployment(t *testing.T) { { name: "adds certs to deployment spec", mockExternal: func(mockOpClient *operatorclientmocks.MockClientInterface, fakeLister *operatorlisterfakes.FakeOperatorLister, namespace string, args args) { - mockOpClient.EXPECT().DeleteService(namespace, "test-service", &metav1.DeleteOptions{}).Return(nil) service := corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test-service", @@ -167,7 +168,24 @@ func TestInstallCertRequirementsForDeployment(t *testing.T) { Selector: selector(t, "test=label").MatchLabels, }, } - mockOpClient.EXPECT().CreateService(&service).Return(&service, nil) + + portsApplyConfig := []*corev1ac.ServicePortApplyConfiguration{} + for _, p := range args.ports { + ac := corev1ac.ServicePort(). + WithName(p.Name). + WithPort(p.Port). + WithTargetPort(p.TargetPort) + portsApplyConfig = append(portsApplyConfig, ac) + } + + svcApplyConfig := corev1ac.Service(service.Name, service.Namespace). + WithSpec(corev1ac.ServiceSpec(). + WithPorts(portsApplyConfig...). + WithSelector(selector(t, "test=label").MatchLabels)). + WithOwnerReferences(ownerutil.NonBlockingOwnerApplyConfiguration(&v1alpha1.ClusterServiceVersion{})). + WithLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}) + + mockOpClient.EXPECT().ApplyService(svcApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(&service, nil) hosts := []string{ fmt.Sprintf("%s.%s", service.GetName(), namespace), @@ -255,7 +273,22 @@ func TestInstallCertRequirementsForDeployment(t *testing.T) { }, } - mockOpClient.EXPECT().UpdateClusterRoleBinding(authDelegatorClusterRoleBinding).Return(authDelegatorClusterRoleBinding, nil) + crbLabels := map[string]string{OLMManagedLabelKey: OLMManagedLabelValue} + for key, val := range ownerutil.OwnerLabel(ownerutil.Owner(&v1alpha1.ClusterServiceVersion{}), owner.GetObjectKind().GroupVersionKind().Kind) { + crbLabels[key] = val + } + crbApplyConfig := rbacv1ac.ClusterRoleBinding(AuthDelegatorClusterRoleBindingName(service.GetName())). + WithSubjects(rbacv1ac.Subject(). + WithKind("ServiceAccount"). + WithAPIGroup(""). + WithName(args.depSpec.Template.Spec.ServiceAccountName). + WithNamespace("")). // Empty owner with no namespace + WithRoleRef(rbacv1ac.RoleRef(). + WithAPIGroup("rbac.authorization.k8s.io"). + WithKind("ClusterRole"). + WithName("system:auth-delegator")). + WithLabels(crbLabels) + mockOpClient.EXPECT().ApplyClusterRoleBinding(crbApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(authDelegatorClusterRoleBinding, nil) authReaderRoleBinding := &rbacv1.RoleBinding{ Subjects: []rbacv1.Subject{ @@ -381,7 +414,6 @@ func TestInstallCertRequirementsForDeployment(t *testing.T) { { name: "doesn't add duplicate service ownerrefs", mockExternal: func(mockOpClient *operatorclientmocks.MockClientInterface, fakeLister *operatorlisterfakes.FakeOperatorLister, namespace string, args args) { - mockOpClient.EXPECT().DeleteService(namespace, "test-service", &metav1.DeleteOptions{}).Return(nil) service := corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test-service", @@ -396,7 +428,23 @@ func TestInstallCertRequirementsForDeployment(t *testing.T) { Selector: selector(t, "test=label").MatchLabels, }, } - mockOpClient.EXPECT().CreateService(&service).Return(&service, nil) + portsApplyConfig := []*corev1ac.ServicePortApplyConfiguration{} + for _, p := range args.ports { + ac := corev1ac.ServicePort(). + WithName(p.Name). + WithPort(p.Port). + WithTargetPort(p.TargetPort) + portsApplyConfig = append(portsApplyConfig, ac) + } + + svcApplyConfig := corev1ac.Service(service.Name, service.Namespace). + WithSpec(corev1ac.ServiceSpec(). + WithPorts(portsApplyConfig...). + WithSelector(selector(t, "test=label").MatchLabels)). + WithOwnerReferences(ownerutil.NonBlockingOwnerApplyConfiguration(owner)). + WithLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}) + + mockOpClient.EXPECT().ApplyService(svcApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(&service, nil) hosts := []string{ fmt.Sprintf("%s.%s", service.GetName(), namespace), @@ -484,7 +532,23 @@ func TestInstallCertRequirementsForDeployment(t *testing.T) { }, } - mockOpClient.EXPECT().UpdateClusterRoleBinding(authDelegatorClusterRoleBinding).Return(authDelegatorClusterRoleBinding, nil) + crbLabels := map[string]string{OLMManagedLabelKey: OLMManagedLabelValue} + for key, val := range ownerutil.OwnerLabel(owner, owner.GetObjectKind().GroupVersionKind().Kind) { + crbLabels[key] = val + } + crbApplyConfig := rbacv1ac.ClusterRoleBinding(service.GetName() + "-system:auth-delegator"). + WithSubjects(rbacv1ac.Subject(). + WithKind("ServiceAccount"). + WithAPIGroup(""). + WithName("test-sa"). + WithNamespace(namespace)). + WithRoleRef(rbacv1ac.RoleRef(). + WithAPIGroup("rbac.authorization.k8s.io"). + WithKind("ClusterRole"). + WithName("system:auth-delegator")). + WithLabels(crbLabels) + + mockOpClient.EXPECT().ApplyClusterRoleBinding(crbApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(authDelegatorClusterRoleBinding, nil) authReaderRoleBinding := &rbacv1.RoleBinding{ Subjects: []rbacv1.Subject{ @@ -602,9 +666,8 @@ func TestInstallCertRequirementsForDeployment(t *testing.T) { }, }, { - name: "labels an unlabelled secret if present", + name: "labels an unlabelled secret if present; creates Service and ClusterRoleBinding if not existing", mockExternal: func(mockOpClient *operatorclientmocks.MockClientInterface, fakeLister *operatorlisterfakes.FakeOperatorLister, namespace string, args args) { - mockOpClient.EXPECT().DeleteService(namespace, "test-service", &metav1.DeleteOptions{}).Return(nil) service := corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test-service", @@ -618,7 +681,24 @@ func TestInstallCertRequirementsForDeployment(t *testing.T) { Selector: selector(t, "test=label").MatchLabels, }, } - mockOpClient.EXPECT().CreateService(&service).Return(&service, nil) + + portsApplyConfig := []*corev1ac.ServicePortApplyConfiguration{} + for _, p := range args.ports { + ac := corev1ac.ServicePort(). + WithName(p.Name). + WithPort(p.Port). + WithTargetPort(p.TargetPort) + portsApplyConfig = append(portsApplyConfig, ac) + } + + svcApplyConfig := corev1ac.Service(service.Name, service.Namespace). + WithSpec(corev1ac.ServiceSpec(). + WithPorts(portsApplyConfig...). + WithSelector(selector(t, "test=label").MatchLabels)). + WithOwnerReferences(ownerutil.NonBlockingOwnerApplyConfiguration(&v1alpha1.ClusterServiceVersion{})). + WithLabels(map[string]string{OLMManagedLabelKey: OLMManagedLabelValue}) + + mockOpClient.EXPECT().ApplyService(svcApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(&service, nil) hosts := []string{ fmt.Sprintf("%s.%s", service.GetName(), namespace), @@ -715,8 +795,22 @@ func TestInstallCertRequirementsForDeployment(t *testing.T) { Name: "system:auth-delegator", }, } - - mockOpClient.EXPECT().UpdateClusterRoleBinding(authDelegatorClusterRoleBinding).Return(authDelegatorClusterRoleBinding, nil) + crbLabels := map[string]string{OLMManagedLabelKey: OLMManagedLabelValue} + for key, val := range ownerutil.OwnerLabel(ownerutil.Owner(&v1alpha1.ClusterServiceVersion{}), owner.GetObjectKind().GroupVersionKind().Kind) { + crbLabels[key] = val + } + crbApplyConfig := rbacv1ac.ClusterRoleBinding(AuthDelegatorClusterRoleBindingName(service.GetName())). + WithSubjects(rbacv1ac.Subject().WithKind("ServiceAccount"). + WithAPIGroup(""). + WithName("test-sa"). + WithNamespace(namespace)). + WithRoleRef(rbacv1ac.RoleRef(). + WithAPIGroup("rbac.authorization.k8s.io"). + WithKind("ClusterRole"). + WithName("system:auth-delegator")). + WithLabels(crbLabels) + + mockOpClient.EXPECT().ApplyClusterRoleBinding(crbApplyConfig, metav1.ApplyOptions{Force: true, FieldManager: "olm.install"}).Return(authDelegatorClusterRoleBinding, nil) authReaderRoleBinding := &rbacv1.RoleBinding{ Subjects: []rbacv1.Subject{ @@ -740,13 +834,7 @@ func TestInstallCertRequirementsForDeployment(t *testing.T) { mockOpClient.EXPECT().UpdateRoleBinding(authReaderRoleBinding).Return(authReaderRoleBinding, nil) }, state: fakeState{ - existingService: &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - OwnerReferences: []metav1.OwnerReference{ - ownerutil.NonBlockingOwner(&v1alpha1.ClusterServiceVersion{}), - }, - }, - }, + existingService: nil, // unlabelled secret won't be in cache getSecretError: errors.NewNotFound(schema.GroupResource{ Group: "", @@ -758,9 +846,7 @@ func TestInstallCertRequirementsForDeployment(t *testing.T) { existingRoleBinding: &rbacv1.RoleBinding{ ObjectMeta: metav1.ObjectMeta{}, }, - existingClusterRoleBinding: &rbacv1.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{}, - }, + existingClusterRoleBinding: nil, }, fields: fields{ owner: &v1alpha1.ClusterServiceVersion{}, diff --git a/pkg/controller/operators/olm/operator_test.go b/pkg/controller/operators/olm/operator_test.go index 538408db34..79cd22cc79 100644 --- a/pkg/controller/operators/olm/operator_test.go +++ b/pkg/controller/operators/olm/operator_test.go @@ -1507,6 +1507,13 @@ func TestTransitionCSV(t *testing.T) { v1alpha1.CSVPhaseInstallReady, ), defaultTemplateAnnotations), apis("a1.v1.a1Kind"), nil), }, + objs: []runtime.Object{ + // Note: Ideally we would not pre-create these objects, but fake client does not support + // creation through SSA, see issue here: https://github.com/kubernetes/kubernetes/issues/115598 + // Once resolved, these objects and others in this file may be removed. + service("a1-service", namespace, "a1", 80), + clusterRoleBinding("a1-service-system:auth-delegator", "system:auth-delegator", "sa", namespace), + }, clientObjs: []runtime.Object{addAnnotation(defaultOperatorGroup, operatorsv1.OperatorGroupProvidedAPIsAnnotationKey, "c1.v1.g1,a1Kind.v1.a1")}, crds: []runtime.Object{ crd("c1", "v1", "g1"), @@ -5978,6 +5985,11 @@ func TestCARotation(t *testing.T) { ), defaultTemplateAnnotations), apis("a1.v1.a1Kind"), nil), }, clientObjs: []runtime.Object{addAnnotation(defaultOperatorGroup, operatorsv1.OperatorGroupProvidedAPIsAnnotationKey, "c1.v1.g1,a1Kind.v1.a1")}, + // The service and clusterRoleBinding have been added here as a workaround to fake client not supporting SSA + objs: []runtime.Object{ + service("a1-service", namespace, "a1", 80, ownerReference), + clusterRoleBinding("a1-service-system:auth-delegator", "system:auth-delegator", "sa", namespace), + }, crds: []runtime.Object{ crd("c1", "v1", "g1"), }, @@ -6045,6 +6057,8 @@ func TestCARotation(t *testing.T) { Resources: []string{"subjectaccessreviews"}, }, }), + // The clusterRoleBinding has been added here as a workaround to fake client not supporting SSA + clusterRoleBinding("a1-service-system:auth-delegator", "system:auth-delegator", "sa", namespace), }, }, }, { @@ -6105,6 +6119,8 @@ func TestCARotation(t *testing.T) { Resources: []string{"subjectaccessreviews"}, }, }), + // The clusterRoleBinding has been added here as a workaround to fake client not supporting SSA + clusterRoleBinding("a1-service-system:auth-delegator", "system:auth-delegator", "sa", namespace), }, }, }, diff --git a/pkg/lib/operatorclient/client.go b/pkg/lib/operatorclient/client.go index 51778d24a1..a7c27ae07b 100644 --- a/pkg/lib/operatorclient/client.go +++ b/pkg/lib/operatorclient/client.go @@ -10,6 +10,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" + corev1ac "k8s.io/client-go/applyconfigurations/core/v1" + rbacv1ac "k8s.io/client-go/applyconfigurations/rbac/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -67,6 +69,7 @@ type SecretClient interface { // ServiceClient contains methods for manipulating Services type ServiceClient interface { + ApplyService(*corev1ac.ServiceApplyConfiguration, metav1.ApplyOptions) (*v1.Service, error) CreateService(*v1.Service) (*v1.Service, error) GetService(namespace, name string) (*v1.Service, error) UpdateService(modified *v1.Service) (*v1.Service, error) @@ -107,6 +110,7 @@ type ClusterRoleClient interface { // ClusterRoleBindingClient contains methods for manipulating ClusterRoleBindings. type ClusterRoleBindingClient interface { + ApplyClusterRoleBinding(applyConfig *rbacv1ac.ClusterRoleBindingApplyConfiguration, applyOptions metav1.ApplyOptions) (*rbacv1.ClusterRoleBinding, error) CreateClusterRoleBinding(*rbacv1.ClusterRoleBinding) (*rbacv1.ClusterRoleBinding, error) GetClusterRoleBinding(name string) (*rbacv1.ClusterRoleBinding, error) UpdateClusterRoleBinding(modified *rbacv1.ClusterRoleBinding) (*rbacv1.ClusterRoleBinding, error) diff --git a/pkg/lib/operatorclient/clusterrolebinding.go b/pkg/lib/operatorclient/clusterrolebinding.go index 896d25893b..0ab429accc 100644 --- a/pkg/lib/operatorclient/clusterrolebinding.go +++ b/pkg/lib/operatorclient/clusterrolebinding.go @@ -7,9 +7,15 @@ import ( rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + acv1 "k8s.io/client-go/applyconfigurations/rbac/v1" "k8s.io/klog" ) +// ApplyClusterRoleBinding applies the roleBinding. +func (c *Client) ApplyClusterRoleBinding(applyConfig *acv1.ClusterRoleBindingApplyConfiguration, applyOptions metav1.ApplyOptions) (*rbacv1.ClusterRoleBinding, error) { + return c.RbacV1().ClusterRoleBindings().Apply(context.TODO(), applyConfig, applyOptions) +} + // CreateRoleBinding creates the roleBinding. func (c *Client) CreateClusterRoleBinding(ig *rbacv1.ClusterRoleBinding) (*rbacv1.ClusterRoleBinding, error) { return c.RbacV1().ClusterRoleBindings().Create(context.TODO(), ig, metav1.CreateOptions{}) diff --git a/pkg/lib/operatorclient/operatorclientmocks/mock_client.go b/pkg/lib/operatorclient/operatorclientmocks/mock_client.go index 4e1c06093d..99ad7bc9df 100644 --- a/pkg/lib/operatorclient/operatorclientmocks/mock_client.go +++ b/pkg/lib/operatorclient/operatorclientmocks/mock_client.go @@ -16,8 +16,10 @@ import ( v12 "k8s.io/apimachinery/pkg/apis/meta/v1" unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" labels "k8s.io/apimachinery/pkg/labels" + v13 "k8s.io/client-go/applyconfigurations/core/v1" + v14 "k8s.io/client-go/applyconfigurations/rbac/v1" kubernetes "k8s.io/client-go/kubernetes" - v13 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" + v15 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" clientset0 "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset" ) @@ -72,6 +74,36 @@ func (mr *MockClientInterfaceMockRecorder) ApiregistrationV1Interface() *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApiregistrationV1Interface", reflect.TypeOf((*MockClientInterface)(nil).ApiregistrationV1Interface)) } +// ApplyClusterRoleBinding mocks base method. +func (m *MockClientInterface) ApplyClusterRoleBinding(applyConfig *v14.ClusterRoleBindingApplyConfiguration, applyOptions v12.ApplyOptions) (*v11.ClusterRoleBinding, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApplyClusterRoleBinding", applyConfig, applyOptions) + ret0, _ := ret[0].(*v11.ClusterRoleBinding) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ApplyClusterRoleBinding indicates an expected call of ApplyClusterRoleBinding. +func (mr *MockClientInterfaceMockRecorder) ApplyClusterRoleBinding(applyConfig, applyOptions interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyClusterRoleBinding", reflect.TypeOf((*MockClientInterface)(nil).ApplyClusterRoleBinding), applyConfig, applyOptions) +} + +// ApplyService mocks base method. +func (m *MockClientInterface) ApplyService(arg0 *v13.ServiceApplyConfiguration, arg1 v12.ApplyOptions) (*v10.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApplyService", arg0, arg1) + ret0, _ := ret[0].(*v10.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ApplyService indicates an expected call of ApplyService. +func (mr *MockClientInterfaceMockRecorder) ApplyService(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyService", reflect.TypeOf((*MockClientInterface)(nil).ApplyService), arg0, arg1) +} + // AtomicModifyCustomResource mocks base method. func (m *MockClientInterface) AtomicModifyCustomResource(apiGroup, version, namespace, resourceKind, resourceName string, f operatorclient.CustomResourceModifier, data interface{}) error { m.ctrl.T.Helper() @@ -87,10 +119,10 @@ func (mr *MockClientInterfaceMockRecorder) AtomicModifyCustomResource(apiGroup, } // CreateAPIService mocks base method. -func (m *MockClientInterface) CreateAPIService(arg0 *v13.APIService) (*v13.APIService, error) { +func (m *MockClientInterface) CreateAPIService(arg0 *v15.APIService) (*v15.APIService, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateAPIService", arg0) - ret0, _ := ret[0].(*v13.APIService) + ret0, _ := ret[0].(*v15.APIService) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -464,10 +496,10 @@ func (mr *MockClientInterfaceMockRecorder) DeleteServiceAccount(namespace, name, } // GetAPIService mocks base method. -func (m *MockClientInterface) GetAPIService(name string) (*v13.APIService, error) { +func (m *MockClientInterface) GetAPIService(name string) (*v15.APIService, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAPIService", name) - ret0, _ := ret[0].(*v13.APIService) + ret0, _ := ret[0].(*v15.APIService) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -768,10 +800,10 @@ func (mr *MockClientInterfaceMockRecorder) RollingUpdateDeploymentMigrations(nam } // UpdateAPIService mocks base method. -func (m *MockClientInterface) UpdateAPIService(modified *v13.APIService) (*v13.APIService, error) { +func (m *MockClientInterface) UpdateAPIService(modified *v15.APIService) (*v15.APIService, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAPIService", modified) - ret0, _ := ret[0].(*v13.APIService) + ret0, _ := ret[0].(*v15.APIService) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1151,10 +1183,10 @@ func (m *MockAPIServiceClient) EXPECT() *MockAPIServiceClientMockRecorder { } // CreateAPIService mocks base method. -func (m *MockAPIServiceClient) CreateAPIService(arg0 *v13.APIService) (*v13.APIService, error) { +func (m *MockAPIServiceClient) CreateAPIService(arg0 *v15.APIService) (*v15.APIService, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateAPIService", arg0) - ret0, _ := ret[0].(*v13.APIService) + ret0, _ := ret[0].(*v15.APIService) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1180,10 +1212,10 @@ func (mr *MockAPIServiceClientMockRecorder) DeleteAPIService(name, options inter } // GetAPIService mocks base method. -func (m *MockAPIServiceClient) GetAPIService(name string) (*v13.APIService, error) { +func (m *MockAPIServiceClient) GetAPIService(name string) (*v15.APIService, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAPIService", name) - ret0, _ := ret[0].(*v13.APIService) + ret0, _ := ret[0].(*v15.APIService) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1195,10 +1227,10 @@ func (mr *MockAPIServiceClientMockRecorder) GetAPIService(name interface{}) *gom } // UpdateAPIService mocks base method. -func (m *MockAPIServiceClient) UpdateAPIService(modified *v13.APIService) (*v13.APIService, error) { +func (m *MockAPIServiceClient) UpdateAPIService(modified *v15.APIService) (*v15.APIService, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateAPIService", modified) - ret0, _ := ret[0].(*v13.APIService) + ret0, _ := ret[0].(*v15.APIService) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -1314,6 +1346,21 @@ func (m *MockServiceClient) EXPECT() *MockServiceClientMockRecorder { return m.recorder } +// ApplyService mocks base method. +func (m *MockServiceClient) ApplyService(arg0 *v13.ServiceApplyConfiguration, arg1 v12.ApplyOptions) (*v10.Service, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApplyService", arg0, arg1) + ret0, _ := ret[0].(*v10.Service) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ApplyService indicates an expected call of ApplyService. +func (mr *MockServiceClientMockRecorder) ApplyService(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyService", reflect.TypeOf((*MockServiceClient)(nil).ApplyService), arg0, arg1) +} + // CreateService mocks base method. func (m *MockServiceClient) CreateService(arg0 *v10.Service) (*v10.Service, error) { m.ctrl.T.Helper() @@ -1724,6 +1771,21 @@ func (m *MockClusterRoleBindingClient) EXPECT() *MockClusterRoleBindingClientMoc return m.recorder } +// ApplyClusterRoleBinding mocks base method. +func (m *MockClusterRoleBindingClient) ApplyClusterRoleBinding(applyConfig *v14.ClusterRoleBindingApplyConfiguration, applyOptions v12.ApplyOptions) (*v11.ClusterRoleBinding, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApplyClusterRoleBinding", applyConfig, applyOptions) + ret0, _ := ret[0].(*v11.ClusterRoleBinding) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ApplyClusterRoleBinding indicates an expected call of ApplyClusterRoleBinding. +func (mr *MockClusterRoleBindingClientMockRecorder) ApplyClusterRoleBinding(applyConfig, applyOptions interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyClusterRoleBinding", reflect.TypeOf((*MockClusterRoleBindingClient)(nil).ApplyClusterRoleBinding), applyConfig, applyOptions) +} + // CreateClusterRoleBinding mocks base method. func (m *MockClusterRoleBindingClient) CreateClusterRoleBinding(arg0 *v11.ClusterRoleBinding) (*v11.ClusterRoleBinding, error) { m.ctrl.T.Helper() diff --git a/pkg/lib/operatorclient/service.go b/pkg/lib/operatorclient/service.go index 989ab847f3..89c593e67e 100644 --- a/pkg/lib/operatorclient/service.go +++ b/pkg/lib/operatorclient/service.go @@ -7,9 +7,15 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + acv1 "k8s.io/client-go/applyconfigurations/core/v1" "k8s.io/klog" ) +// ApplyService applies the Service. +func (c *Client) ApplyService(applyConfig *acv1.ServiceApplyConfiguration, applyOptions metav1.ApplyOptions) (*v1.Service, error) { + return c.CoreV1().Services(*applyConfig.Namespace).Apply(context.TODO(), applyConfig, applyOptions) +} + // CreateService creates the Service. func (c *Client) CreateService(ig *v1.Service) (*v1.Service, error) { return c.CoreV1().Services(ig.GetNamespace()).Create(context.TODO(), ig, metav1.CreateOptions{}) diff --git a/pkg/lib/ownerutil/util.go b/pkg/lib/ownerutil/util.go index ebdd73a315..8512d3b39e 100644 --- a/pkg/lib/ownerutil/util.go +++ b/pkg/lib/ownerutil/util.go @@ -9,6 +9,8 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1ac "k8s.io/client-go/applyconfigurations/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -197,6 +199,21 @@ func NonBlockingOwner(owner Owner) metav1.OwnerReference { } } +// NonBlockingOwnerApplyConfiguration returns an ownerrefence to be added to an ownerref list used in an SSA Configuration +func NonBlockingOwnerApplyConfiguration(owner Owner) *metav1ac.OwnerReferenceApplyConfiguration { + ownerRef := NonBlockingOwner(owner) + + ownerRefAC := metav1ac.OwnerReference(). + WithAPIVersion(ownerRef.APIVersion). + WithKind(ownerRef.Kind). + WithUID(ownerRef.UID). + WithName(ownerRef.Name). + WithBlockOwnerDeletion(*ownerRef.BlockOwnerDeletion). + WithController(*ownerRef.Controller) + + return ownerRefAC +} + // OwnerLabel returns a label added to generated objects for later querying func OwnerLabel(owner Owner, kind string) map[string]string { return map[string]string{