github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/lib/scoped/syncer.go (about) 1 package scoped 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 8 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" 9 "github.com/sirupsen/logrus" 10 corev1 "k8s.io/api/core/v1" 11 apierrors "k8s.io/apimachinery/pkg/api/errors" 12 "k8s.io/apimachinery/pkg/api/meta" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 "k8s.io/apimachinery/pkg/runtime" 15 corev1applyconfigurations "k8s.io/client-go/applyconfigurations/core/v1" 16 "k8s.io/client-go/tools/reference" 17 "k8s.io/utils/clock" 18 19 v1 "github.com/operator-framework/api/pkg/operators/v1" 20 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" 21 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" 22 ) 23 24 // NewUserDefinedServiceAccountSyncer returns a new instance of UserDefinedServiceAccountSyncer. 25 func NewUserDefinedServiceAccountSyncer(logger *logrus.Logger, scheme *runtime.Scheme, client operatorclient.ClientInterface, versioned versioned.Interface) *UserDefinedServiceAccountSyncer { 26 return &UserDefinedServiceAccountSyncer{ 27 logger: logger, 28 versioned: versioned, 29 client: client, 30 clock: &clock.RealClock{}, 31 scheme: scheme, 32 } 33 } 34 35 // UserDefinedServiceAccountSyncer syncs an operator group appropriately when 36 // a user defined service account is specified. 37 type UserDefinedServiceAccountSyncer struct { 38 versioned versioned.Interface 39 client operatorclient.ClientInterface 40 logger *logrus.Logger 41 clock clock.Clock 42 scheme *runtime.Scheme 43 } 44 45 const ( 46 // All logs should in this package should have the following field to make 47 // it easy to comb through logs. 48 logFieldName = "mode" 49 logFieldValue = "scoped" 50 ) 51 52 // SyncOperatorGroup takes appropriate actions when a user specifies a service account. 53 func (s *UserDefinedServiceAccountSyncer) SyncOperatorGroup(in *v1.OperatorGroup) (out *v1.OperatorGroup, err error) { 54 out = in 55 namespace := in.GetNamespace() 56 serviceAccountName := in.Spec.ServiceAccountName 57 58 logger := s.logger.WithFields(logrus.Fields{ 59 "operatorGroup": in.GetName(), 60 "namespace": in.GetNamespace(), 61 logFieldName: logFieldValue, 62 }) 63 64 if serviceAccountName == "" { 65 if in.Status.ServiceAccountRef == nil { 66 return 67 } 68 69 // Remove ServiceAccount condition if existed 70 meta.RemoveStatusCondition(&in.Status.Conditions, v1.OperatorGroupServiceAccountCondition) 71 72 // User must have removed ServiceAccount from the spec. We need to 73 // rest Status to a nil reference. 74 out, err = s.update(in, nil) 75 if err != nil { 76 err = fmt.Errorf("failed to reset status.serviceAccount, sa=%s %v", serviceAccountName, err) 77 } 78 return 79 } 80 81 // A service account has been specified, we need to update the status. 82 sa, err := s.client.KubernetesInterface().CoreV1().ServiceAccounts(namespace).Get(context.TODO(), serviceAccountName, metav1.GetOptions{}) 83 if err != nil { 84 if apierrors.IsNotFound(err) { 85 // Set OG's status condition to indicate SA is not found 86 cond := metav1.Condition{ 87 Type: v1.OperatorGroupServiceAccountCondition, 88 Status: metav1.ConditionTrue, 89 Reason: v1.OperatorGroupServiceAccountReason, 90 Message: fmt.Sprintf("ServiceAccount %s not found", serviceAccountName), 91 } 92 93 meta.SetStatusCondition(&in.Status.Conditions, cond) 94 _, uerr := s.update(in, nil) 95 if uerr != nil { 96 logger.Warnf("fail to upgrade operator group status og=%s with condition %+v: %s", in.GetName(), cond, uerr.Error()) 97 } 98 } 99 err = fmt.Errorf("failed to get service account, sa=%s %v", serviceAccountName, err) 100 return 101 } 102 103 // A service account has been specified, but likely does not have the labels we require it to have to ensure it 104 // shows up in our listers, so let's add that and queue again later 105 config := corev1applyconfigurations.ServiceAccount(serviceAccountName, namespace) 106 config.Labels = map[string]string{install.OLMManagedLabelKey: install.OLMManagedLabelValue} 107 if _, err := s.client.KubernetesInterface().CoreV1().ServiceAccounts(namespace).Apply(context.TODO(), config, metav1.ApplyOptions{FieldManager: "operator-lifecycle-manager"}); err != nil { 108 return out, fmt.Errorf("failed to apply labels[%s]=%s to serviceaccount %s/%s: %w", install.OLMManagedLabelKey, install.OLMManagedLabelValue, namespace, serviceAccountName, err) 109 } 110 111 ref, err := reference.GetReference(s.scheme, sa) 112 if err != nil { 113 return 114 } 115 116 if reflect.DeepEqual(in.Status.ServiceAccountRef, ref) { 117 logger.Debugf("status.serviceAccount is in sync with spec sa=%s", serviceAccountName) 118 return 119 } 120 121 // Remove SA not found condition if found 122 if c := meta.FindStatusCondition(in.Status.Conditions, v1.OperatorGroupServiceAccountCondition); c != nil { 123 meta.RemoveStatusCondition(&in.Status.Conditions, v1.OperatorGroupServiceAccountCondition) 124 } 125 126 out, err = s.update(in, ref) 127 if err != nil { 128 err = fmt.Errorf("failed to set status.serviceAccount, sa=%s %v", serviceAccountName, err) 129 } 130 131 return 132 } 133 134 func (s *UserDefinedServiceAccountSyncer) update(in *v1.OperatorGroup, ref *corev1.ObjectReference) (out *v1.OperatorGroup, err error) { 135 out = in 136 137 status := out.Status.DeepCopy() 138 status.ServiceAccountRef = ref 139 now := metav1.NewTime(s.clock.Now()) 140 status.LastUpdated = &now 141 142 out.Status = *status 143 144 out, err = s.versioned.OperatorsV1().OperatorGroups(out.GetNamespace()).UpdateStatus(context.TODO(), out, metav1.UpdateOptions{}) 145 return 146 }