Skip to content

Commit 601f48a

Browse files
committed
refactor: update namespace sync configuration to use label selector
- Changed `targetNamespaceLabelKey` to `targetNamespaceLabelSelector` in `values.yaml` for clarity. - Updated deployment template to reflect the new label selector usage. - Modified main application logic to validate and parse the label selector. - Removed deprecated `IsTargetNamespace` function in favor of a more flexible label selector approach. - Adjusted related tests and documentation accordingly.
1 parent 30a19db commit 601f48a

File tree

12 files changed

+116
-133
lines changed

12 files changed

+116
-133
lines changed

charts/cluster-api-runtime-extensions-nutanix/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ A Helm chart for cluster-api-runtime-extensions-nutanix
109109
| imagePullSecrets | list | `[]` | Optional secrets used for pulling the container image |
110110
| namespaceSync.enabled | bool | `true` | |
111111
| namespaceSync.sourceNamespace | string | `""` | |
112-
| namespaceSync.targetNamespaceLabelKey | string | `"caren.nutanix.com/namespace-sync"` | |
112+
| namespaceSync.targetNamespaceLabelSelector | string | `"caren.nutanix.com/namespace-sync"` | |
113113
| nodeSelector | object | `{}` | |
114114
| priorityClassName | string | `"system-cluster-critical"` | Priority class to be used for the pod. |
115115
| resources.limits.cpu | string | `"100m"` | |

charts/cluster-api-runtime-extensions-nutanix/templates/deployment.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ spec:
3232
- --defaults-namespace=$(POD_NAMESPACE)
3333
- --namespacesync-enabled={{ .Values.namespaceSync.enabled }}
3434
- --namespacesync-source-namespace={{ default .Release.Namespace .Values.namespaceSync.sourceNamespace }}
35-
- --namespacesync-target-namespace-label-key={{ .Values.namespaceSync.targetNamespaceLabelKey }}
35+
- --namespacesync-target-namespace-label-selector={{ .Values.namespaceSync.targetNamespaceLabelSelector }}
3636
- --enforce-clusterautoscaler-limits-enabled={{ .Values.enforceClusterAutoscalerLimits.enabled }}
3737
- --failure-domain-rollout-enabled={{ .Values.failureDomainRollout.enabled }}
3838
- --failure-domain-rollout-concurrency={{ .Values.failureDomainRollout.concurrency }}

charts/cluster-api-runtime-extensions-nutanix/values.schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@
683683
"sourceNamespace": {
684684
"type": "string"
685685
},
686-
"targetNamespaceLabelKey": {
686+
"targetNamespaceLabelSelector": {
687687
"type": "string"
688688
}
689689
}

charts/cluster-api-runtime-extensions-nutanix/values.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ deployDefaultClusterClasses: true
143143
# target namespace, i.e., every namespace that has a label with a matching key.
144144
namespaceSync:
145145
enabled: true
146-
targetNamespaceLabelKey: caren.nutanix.com/namespace-sync
146+
targetNamespaceLabelSelector: caren.nutanix.com/namespace-sync
147147
# By default, sourceNamespace is the helm release namespace.
148148
sourceNamespace: ""
149149

cmd/main.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010

1111
"github.com/spf13/pflag"
12+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1213
"k8s.io/apimachinery/pkg/runtime"
1314
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
1415
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
@@ -191,10 +192,32 @@ func main() {
191192

192193
if namespacesyncOptions.Enabled {
193194
if namespacesyncOptions.SourceNamespace == "" ||
194-
namespacesyncOptions.TargetNamespaceLabelKey == "" {
195+
namespacesyncOptions.TargetNamespaceLabelSelector == "" {
195196
setupLog.Error(
196197
nil,
197-
"Namespace Sync is enabled, but source namespace and/or target namespace label key are not configured.",
198+
"Namespace Sync is enabled, but source namespace and/or target namespace label selector are not configured.",
199+
)
200+
os.Exit(1)
201+
}
202+
203+
targetSelector, err := metav1.ParseToLabelSelector(namespacesyncOptions.TargetNamespaceLabelSelector)
204+
if err != nil {
205+
setupLog.Error(
206+
err,
207+
"unable to parse target namespace label selector",
208+
"selector",
209+
namespacesyncOptions.TargetNamespaceLabelSelector,
210+
)
211+
os.Exit(1)
212+
}
213+
214+
targetLabelSelector, err := metav1.LabelSelectorAsSelector(targetSelector)
215+
if err != nil {
216+
setupLog.Error(
217+
err,
218+
"unable to convert label selector",
219+
"selector",
220+
namespacesyncOptions.TargetNamespaceLabelSelector,
198221
)
199222
os.Exit(1)
200223
}
@@ -215,7 +238,7 @@ func main() {
215238
Client: mgr.GetClient(),
216239
UnstructuredCachingClient: unstructuredCachingClient,
217240
SourceClusterClassNamespace: namespacesyncOptions.SourceNamespace,
218-
IsTargetNamespace: namespacesync.NamespaceHasLabelKey(namespacesyncOptions.TargetNamespaceLabelKey),
241+
TargetNamespaceSelector: targetLabelSelector,
219242
}).SetupWithManager(
220243
mgr,
221244
&controller.Options{MaxConcurrentReconciles: namespacesyncOptions.Concurrency},

pkg/controllers/namespacesync/controller.go

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99

1010
corev1 "k8s.io/api/core/v1"
11+
"k8s.io/apimachinery/pkg/labels"
1112
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
1213
ctrl "sigs.k8s.io/controller-runtime"
1314
"sigs.k8s.io/controller-runtime/pkg/builder"
@@ -28,16 +29,17 @@ type Reconciler struct {
2829
// SourceClusterClassNamespace is the namespace from which ClusterClasses are copied.
2930
SourceClusterClassNamespace string
3031

31-
// IsTargetNamespace determines whether ClusterClasses should be copied to a given namespace.
32-
IsTargetNamespace func(ns *corev1.Namespace) bool
32+
// TargetNamespaceSelector is a label selector to determine which namespaces should receive
33+
// copies of ClusterClasses and Templates from the source namespace.
34+
TargetNamespaceSelector labels.Selector
3335
}
3436

3537
func (r *Reconciler) SetupWithManager(
3638
mgr ctrl.Manager,
3739
options *controller.Options,
3840
) error {
39-
if r.IsTargetNamespace == nil {
40-
return fmt.Errorf("define IsTargetNamespace function to use controller")
41+
if r.TargetNamespaceSelector == nil {
42+
return fmt.Errorf("TargetNamespaceSelector must be defined to use controller")
4143
}
4244

4345
err := ctrl.NewControllerManagedBy(mgr).
@@ -51,7 +53,7 @@ func (r *Reconciler) SetupWithManager(
5153
if !ok {
5254
return false
5355
}
54-
return r.IsTargetNamespace(ns)
56+
return r.TargetNamespaceSelector.Matches(labels.Set(ns.GetLabels()))
5557
},
5658
UpdateFunc: func(e event.UpdateEvent) bool {
5759
// Called when an object is already in the cache, and it is either updated,
@@ -66,7 +68,9 @@ func (r *Reconciler) SetupWithManager(
6668
}
6769
// Only reconcile the namespace if the answer to the question "Is this a
6870
// target namespace?" changed from no to yes.
69-
return !r.IsTargetNamespace(nsOld) && r.IsTargetNamespace(nsNew)
71+
matchesOld := r.TargetNamespaceSelector.Matches(labels.Set(nsOld.GetLabels()))
72+
matchesNew := r.TargetNamespaceSelector.Matches(labels.Set(nsNew.GetLabels()))
73+
return !matchesOld && matchesNew
7074
},
7175
DeleteFunc: func(e event.DeleteEvent) bool {
7276
// Ignore deletes.
@@ -93,22 +97,23 @@ func (r *Reconciler) SetupWithManager(
9397

9498
func (r *Reconciler) clusterClassToNamespaces(ctx context.Context, o client.Object) []ctrl.Request {
9599
namespaceList := &corev1.NamespaceList{}
96-
err := r.Client.List(ctx, namespaceList)
100+
err := r.Client.List(ctx, namespaceList, &client.ListOptions{
101+
LabelSelector: r.TargetNamespaceSelector,
102+
})
97103
if err != nil {
98104
// TODO Log the error, and record an Event.
99105
return nil
100106
}
101107

102-
rs := []ctrl.Request{}
108+
// Pre-allocate slice with exact capacity since we're using label selector
109+
rs := make([]ctrl.Request, 0, len(namespaceList.Items))
103110
for i := range namespaceList.Items {
104111
ns := &namespaceList.Items[i]
105-
if r.IsTargetNamespace(ns) {
106-
rs = append(rs,
107-
ctrl.Request{
108-
NamespacedName: client.ObjectKeyFromObject(ns),
109-
},
110-
)
111-
}
112+
rs = append(rs,
113+
ctrl.Request{
114+
NamespacedName: client.ObjectKeyFromObject(ns),
115+
},
116+
)
112117
}
113118
return rs
114119
}
@@ -130,22 +135,29 @@ func (r *Reconciler) Reconcile(
130135

131136
// TODO Consider running in parallel.
132137
for i := range sccs {
133-
scc := &sccs[i]
134-
err := copyClusterClassAndTemplates(
135-
ctx,
136-
r.Client,
137-
r.UnstructuredCachingClient,
138-
scc,
139-
namespace,
140-
)
141-
if err != nil {
142-
// TODO Record an Event.
143-
return ctrl.Result{}, fmt.Errorf(
144-
"failed to copy source ClusterClass %s or its referenced Templates to namespace %s: %w",
145-
client.ObjectKeyFromObject(scc),
138+
// Check for context cancellation to prevent goroutine leaks
139+
select {
140+
case <-ctx.Done():
141+
return ctrl.Result{}, ctx.Err()
142+
default:
143+
scc := &sccs[i]
144+
145+
err := copyClusterClassAndTemplates(
146+
ctx,
147+
r.Client,
148+
r.UnstructuredCachingClient,
149+
scc,
146150
namespace,
147-
err,
148151
)
152+
if err != nil {
153+
// TODO Record an Event.
154+
return ctrl.Result{}, fmt.Errorf(
155+
"failed to copy source ClusterClass %s or its referenced Templates to namespace %s: %w",
156+
client.ObjectKeyFromObject(scc),
157+
namespace,
158+
err,
159+
)
160+
}
149161
}
150162
}
151163

pkg/controllers/namespacesync/copy.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,6 @@ func copyObjectForCreate[T client.Object](src T, name, namespace string) T {
7171
dst.SetName(name)
7272
dst.SetNamespace(namespace)
7373

74-
// Zero out ManagedFields (clients will set them)
75-
dst.SetManagedFields(nil)
76-
// Zero out OwnerReferences (object is garbage-collected if
77-
// owners are not in the target namespace)
78-
dst.SetOwnerReferences(nil)
79-
8074
// Zero out fields that are ignored by the API server on create
8175
dst.SetCreationTimestamp(metav1.Time{})
8276
dst.SetDeletionGracePeriodSeconds(nil)

pkg/controllers/namespacesync/flags.go

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,48 @@ import (
88
)
99

1010
type Options struct {
11-
Enabled bool
12-
Concurrency int
13-
SourceNamespace string
14-
TargetNamespaceLabelKey string
11+
Enabled bool
12+
Concurrency int
13+
SourceNamespace string
14+
TargetNamespaceLabelSelector string
1515
}
1616

1717
func (o *Options) AddFlags(flags *pflag.FlagSet) {
18-
pflag.CommandLine.BoolVar(
18+
flags.BoolVar(
1919
&o.Enabled,
2020
"namespacesync-enabled",
2121
false,
2222
"Enable copying of ClusterClasses and Templates from a source namespace to one or more target namespaces.",
2323
)
2424

25-
pflag.CommandLine.IntVar(
25+
flags.IntVar(
2626
&o.Concurrency,
2727
"namespacesync-concurrency",
2828
10,
2929
"Number of target namespaces to sync concurrently.",
3030
)
3131

32-
pflag.CommandLine.StringVar(
32+
flags.StringVar(
3333
&o.SourceNamespace,
3434
"namespacesync-source-namespace",
3535
"",
3636
"Namespace from which ClusterClasses and Templates are copied.",
3737
)
3838

39-
pflag.CommandLine.StringVar(
40-
&o.TargetNamespaceLabelKey,
39+
flags.StringVar(
40+
&o.TargetNamespaceLabelSelector,
4141
"namespacesync-target-namespace-label-key",
4242
"",
4343
"Label key to determine if a namespace is a target. If a namespace has a label with this key, copy ClusterClasses and Templates to it from the source namespace.", //nolint:lll // Output will be wrapped.
4444
)
45+
_ = flags.MarkDeprecated(
46+
"namespacesync-target-namespace-label-key",
47+
"use namespacesync-target-namespace-label-selector instead",
48+
)
49+
flags.StringVar(
50+
&o.TargetNamespaceLabelSelector,
51+
"namespacesync-target-namespace-label-selector",
52+
"",
53+
"Label selector to determine target namespaces. Namespaces matching this selector will receive copies of ClusterClasses and Templates from the source namespace. Example: 'environment=production' or 'team in (platform,infrastructure)'.", //nolint:lll // Output will be wrapped.
54+
)
4555
}

pkg/controllers/namespacesync/label.go

Lines changed: 0 additions & 12 deletions
This file was deleted.

pkg/controllers/namespacesync/label_test.go

Lines changed: 0 additions & 61 deletions
This file was deleted.

0 commit comments

Comments
 (0)