Skip to content

Commit b3280b3

Browse files
Merge pull request #1408 from awgreene/reconcile-namespace-og
Reconcile Namespace OG Labels in Namespace Syncer
2 parents d17f42c + 2b2cd91 commit b3280b3

File tree

3 files changed

+114
-105
lines changed

3 files changed

+114
-105
lines changed

pkg/api/apis/operators/v1/operatorgroup_types.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package v1
22

33
import (
4+
"fmt"
45
"sort"
56
"strings"
67

@@ -15,6 +16,9 @@ const (
1516
OperatorGroupProvidedAPIsAnnotationKey = "olm.providedAPIs"
1617

1718
OperatorGroupKind = "OperatorGroup"
19+
20+
operatorGroupLabelPrefix = "olm.operatorgroup/"
21+
operatorGroupLabelTemplate = operatorGroupLabelPrefix + "%s.%s"
1822
)
1923

2024
// OperatorGroupSpec is the spec for an OperatorGroup resource.
@@ -99,3 +103,14 @@ func (o *OperatorGroup) HasServiceAccountSynced() bool {
99103

100104
return false
101105
}
106+
107+
// getOperatorGroupLabel returns a label that is applied to Namespaces to signify that the
108+
// namespace is a part of the OperatorGroup using selectors.
109+
func (o *OperatorGroup) GetLabel() string {
110+
return fmt.Sprintf(operatorGroupLabelTemplate, o.GetNamespace(), o.GetName())
111+
}
112+
113+
// IsOperatorGroupLabel returns true if the label is an OperatorGroup label.
114+
func IsOperatorGroupLabel(label string) bool {
115+
return strings.HasPrefix(label, operatorGroupLabelPrefix)
116+
}

pkg/controller/operators/olm/operator.go

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ type Operator struct {
6868
csvCopyQueueSet *queueinformer.ResourceQueueSet
6969
csvGCQueueSet *queueinformer.ResourceQueueSet
7070
objGCQueueSet *queueinformer.ResourceQueueSet
71+
nsQueueSet workqueue.RateLimitingInterface
7172
apiServiceQueue workqueue.RateLimitingInterface
7273
csvIndexers map[string]cache.Indexer
7374
recorder record.EventRecorder
@@ -394,6 +395,7 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat
394395
// register namespace queueinformer
395396
namespaceInformer := k8sInformerFactory.Core().V1().Namespaces()
396397
op.lister.CoreV1().RegisterNamespaceLister(namespaceInformer.Lister())
398+
op.nsQueueSet = workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "resolver")
397399
namespaceInformer.Informer().AddEventHandler(
398400
&cache.ResourceEventHandlerFuncs{
399401
DeleteFunc: op.namespaceAddedOrRemoved,
@@ -403,8 +405,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat
403405
namespaceQueueInformer, err := queueinformer.NewQueueInformer(
404406
ctx,
405407
queueinformer.WithLogger(op.logger),
408+
queueinformer.WithQueue(op.nsQueueSet),
406409
queueinformer.WithInformer(namespaceInformer.Informer()),
407-
queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncObject).ToSyncer()),
410+
queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncNamespace).ToSyncer()),
408411
)
409412
if err != nil {
410413
return nil, err
@@ -736,6 +739,49 @@ func (a *Operator) namespaceAddedOrRemoved(obj interface{}) {
736739
return
737740
}
738741

742+
func (a *Operator) syncNamespace(obj interface{}) error {
743+
// Check to see if any operator groups are associated with this namespace
744+
namespace, ok := obj.(*corev1.Namespace)
745+
if !ok {
746+
a.logger.Debugf("wrong type: %#v\n", obj)
747+
return fmt.Errorf("casting Namespace failed")
748+
}
749+
750+
logger := a.logger.WithFields(logrus.Fields{
751+
"name": namespace.GetName(),
752+
})
753+
754+
// Remove existing OperatorGroup labels
755+
for label := range namespace.GetLabels() {
756+
if v1.IsOperatorGroupLabel(label) {
757+
delete(namespace.Labels, label)
758+
}
759+
}
760+
761+
operatorGroupList, err := a.lister.OperatorsV1().OperatorGroupLister().List(labels.Everything())
762+
if err != nil {
763+
logger.WithError(err).Warn("lister failed")
764+
return err
765+
}
766+
767+
for _, group := range operatorGroupList {
768+
namespaceSet := resolver.NewNamespaceSet(group.Status.Namespaces)
769+
770+
// Apply the label if not an All Namespaces OperatorGroup.
771+
if namespaceSet.Contains(namespace.GetName()) && !namespaceSet.IsAllNamespaces() {
772+
if namespace.Labels == nil {
773+
namespace.Labels = make(map[string]string, 1)
774+
}
775+
namespace.Labels[group.GetLabel()] = ""
776+
}
777+
}
778+
779+
// Update the Namespace
780+
_, err = a.opClient.KubernetesInterface().CoreV1().Namespaces().Update(namespace)
781+
782+
return err
783+
}
784+
739785
func (a *Operator) handleClusterServiceVersionDeletion(obj interface{}) {
740786
clusterServiceVersion, ok := obj.(*v1alpha1.ClusterServiceVersion)
741787
if !ok {
@@ -1893,3 +1939,12 @@ func (a *Operator) ensureLabels(in *v1alpha1.ClusterServiceVersion, labelSets ..
18931939
out, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(out.GetNamespace()).Update(out)
18941940
return out, err
18951941
}
1942+
1943+
func containsString(list []string, item string) bool {
1944+
for i := range list {
1945+
if list[i] == item {
1946+
return true
1947+
}
1948+
}
1949+
return false
1950+
}

pkg/controller/operators/olm/operatorgroup.go

Lines changed: 43 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ const (
2727
AdminSuffix = "admin"
2828
EditSuffix = "edit"
2929
ViewSuffix = "view"
30-
31-
operatorGroupLabelTemplate = "olm.operatorgroup/%s.%s"
3230
)
3331

3432
var (
@@ -100,9 +98,8 @@ func (a *Operator) syncOperatorGroups(obj interface{}) error {
10098
logger.WithField("targetNamespaces", targetNamespaces).Debug("updated target namespaces")
10199

102100
if namespacesChanged(targetNamespaces, op.Status.Namespaces) {
103-
// Labels are only applied to namespaces that are not empty strings.
104-
prunedStatusNamespaces := pruneEmptyStrings(op.Status.Namespaces)
105-
prunedTargetNamespaces := pruneEmptyStrings(targetNamespaces)
101+
logger.Debug("OperatorGroup namespaces change detected")
102+
outOfSyncNamespaces := namespacesAddedOrRemoved(op.Spec.TargetNamespaces, op.Status.Namespaces)
106103

107104
// Update operatorgroup target namespace selection
108105
logger.WithField("targets", targetNamespaces).Debug("namespace change detected")
@@ -116,25 +113,15 @@ func (a *Operator) syncOperatorGroups(obj interface{}) error {
116113
return err
117114
}
118115

119-
// Remove labels from targetNamespaces that were dropped.
120-
ogLabel := getOperatorGroupLabel(*op)
121-
for _, namespaceName := range setDifference(prunedStatusNamespaces, prunedTargetNamespaces) {
122-
err = a.removeNamespaceLabel(namespaceName, ogLabel)
123-
if err != nil {
124-
logger.WithError(err).Warnf("failed to remove operatorgroup label from namespace %s", namespaceName)
125-
return err
126-
}
127-
}
116+
logger.Debug("operatorgroup status updated")
128117

129-
// Add labels to targetNamespaces that were added.
130-
for _, namespaceName := range setDifference(prunedTargetNamespaces, prunedStatusNamespaces) {
131-
err = a.addNamespaceLabel(namespaceName, ogLabel, "")
132-
if err != nil {
133-
logger.WithError(err).Warnf("failed to add operatorgroup to from namespace %s", namespaceName)
134-
return err
135-
}
118+
// Requeueing out of sync namespaces
119+
logger.Debug("Requeueing out of sync namespaces")
120+
for _, ns := range outOfSyncNamespaces {
121+
logger.WithField("namespace", ns).Debug("requeueing")
122+
a.nsQueueSet.Add(ns)
136123
}
137-
logger.Debug("namespace change detected and operatorgroup status updated")
124+
138125
// CSV requeue is handled by the succeeding sync in `annotateCSVs`
139126
return nil
140127
}
@@ -185,43 +172,6 @@ func (a *Operator) syncOperatorGroups(obj interface{}) error {
185172
return nil
186173
}
187174

188-
// pruneEmptyStrings removes any items from a list that are empty
189-
func pruneEmptyStrings(strings []string) []string {
190-
prunedStrings := []string{}
191-
for _, str := range strings {
192-
if str != "" {
193-
prunedStrings = append(prunedStrings, str)
194-
}
195-
}
196-
return prunedStrings
197-
}
198-
199-
// setDifference implements Set Difference: A - B
200-
// https://en.wikipedia.org/wiki/Complement_(set_theory)#In_programming_languages
201-
func setDifference(a, b []string) (diff []string) {
202-
if len(a) == 0 {
203-
return diff
204-
}
205-
206-
if len(b) == 0 {
207-
return a
208-
}
209-
210-
// Find the Set Difference.
211-
m := make(map[string]bool)
212-
213-
for _, item := range b {
214-
m[item] = true
215-
}
216-
217-
for _, item := range a {
218-
if _, ok := m[item]; !ok {
219-
diff = append(diff, item)
220-
}
221-
}
222-
return diff
223-
}
224-
225175
func (a *Operator) operatorGroupDeleted(obj interface{}) {
226176
op, ok := obj.(*v1.OperatorGroup)
227177
if !ok {
@@ -246,52 +196,12 @@ func (a *Operator) operatorGroupDeleted(obj interface{}) {
246196
}
247197
}
248198

249-
// Remove OperatorGroup label from targeNamespaces.
250-
ogLabel := getOperatorGroupLabel(*op)
251-
for _, namespaceName := range op.Spec.TargetNamespaces {
252-
a.removeNamespaceLabel(namespaceName, ogLabel)
253-
if err != nil {
254-
logger.WithError(err).Error("failed to remove OperatorGroup Label from Namespace during garbage collection")
255-
}
256-
}
257-
}
258-
259-
func (a *Operator) updateNamespace(namespaceName string, namespaceUpdateFunc func(*corev1.Namespace)) error {
260-
namespace, err := a.opClient.KubernetesInterface().CoreV1().Namespaces().Get(namespaceName, metav1.GetOptions{})
261-
if err != nil {
262-
return err
263-
}
264-
265-
namespaceUpdateFunc(namespace)
266-
267-
_, err = a.opClient.KubernetesInterface().CoreV1().Namespaces().Update(namespace)
268-
if err != nil {
269-
return err
270-
}
271-
return nil
272-
}
273-
274-
func (a *Operator) removeNamespaceLabel(namespaceName, key string) error {
275-
namespaceUpdateFunc := func(namespace *corev1.Namespace) {
276-
delete(namespace.Labels, key)
277-
}
278-
return a.updateNamespace(namespaceName, namespaceUpdateFunc)
279-
}
280-
281-
func (a *Operator) addNamespaceLabel(namespaceName string, key, value string) error {
282-
namespaceUpdateFunc := func(namespace *corev1.Namespace) {
283-
if namespace.Labels == nil {
284-
namespace.Labels = make(map[string]string, 1)
285-
}
286-
namespace.Labels[key] = value
199+
// Trigger a sync on namespaces
200+
logger.Debug("OperatorGroup deleted, requeueing out of sync namespaces")
201+
for _, ns := range op.Status.Namespaces {
202+
logger.WithField("namespace", ns).Debug("requeueing")
203+
a.nsQueueSet.Add(ns)
287204
}
288-
return a.updateNamespace(namespaceName, namespaceUpdateFunc)
289-
}
290-
291-
// getOperatorGroupLabel returns a label that is applied to Namespaces to signify that the
292-
// namespace is a part of the OperatorGroup using selectors.
293-
func getOperatorGroupLabel(og v1.OperatorGroup) string {
294-
return fmt.Sprintf(operatorGroupLabelTemplate, og.GetNamespace(), og.GetName())
295205
}
296206

297207
func (a *Operator) annotateCSVs(group *v1.OperatorGroup, targetNamespaces []string, logger *logrus.Entry) error {
@@ -1075,3 +985,32 @@ func (a *Operator) findCSVsThatProvideAnyOf(provide resolver.APISet) ([]*v1alpha
1075985

1076986
return providers, nil
1077987
}
988+
989+
// namespacesAddedOrRemoved returns the union of:
990+
// - the set of elements in A but not in B
991+
// - the set of elements in B but not in A
992+
func namespacesAddedOrRemoved(a, b []string) []string {
993+
check := make(map[string]struct{})
994+
995+
for _, namespace := range a {
996+
check[namespace] = struct{}{}
997+
}
998+
999+
for _, namespace := range b {
1000+
if _, ok := check[namespace]; !ok {
1001+
check[namespace] = struct{}{}
1002+
} else {
1003+
delete(check, namespace)
1004+
}
1005+
}
1006+
1007+
// Remove global namespace name if added
1008+
delete(check, "")
1009+
1010+
var keys []string
1011+
for key := range check {
1012+
keys = append(keys, key)
1013+
}
1014+
1015+
return keys
1016+
}

0 commit comments

Comments
 (0)