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