Skip to content

Commit a21f2ff

Browse files
committed
fix(juggler): configurable labels
On-behalf-of: @SAP [email protected] Signed-off-by: Christopher Junk <[email protected]>
1 parent 26752f8 commit a21f2ff

File tree

6 files changed

+172
-30
lines changed

6 files changed

+172
-30
lines changed

pkg/juggler/fluxcd/flux_reconciler.go

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,27 @@ var _ juggler.ComponentReconciler = &FluxReconciler{}
2525

2626
func NewFluxReconciler(logger logr.Logger, localClient client.Client, remoteClient client.Client, labelComponentName string) *FluxReconciler {
2727
return &FluxReconciler{
28-
logger: logger,
29-
localClient: localClient,
30-
remoteClient: remoteClient,
31-
knownTypes: sets.Set[reflect.Type]{},
32-
labelComponentName: labelComponentName,
28+
logger: logger,
29+
localClient: localClient,
30+
remoteClient: remoteClient,
31+
knownTypes: sets.Set[reflect.Type]{},
32+
labelFunc: juggler.DefaultLabelFunc(labelComponentName),
3333
}
3434
}
3535

36+
func (r *FluxReconciler) WithLabelFunc(fn juggler.LabelFunc) *FluxReconciler {
37+
if fn != nil {
38+
r.labelFunc = fn
39+
}
40+
return r
41+
}
42+
3643
type FluxReconciler struct {
37-
localClient client.Client
38-
remoteClient client.Client
39-
logger logr.Logger
40-
knownTypes sets.Set[reflect.Type]
41-
labelComponentName string
44+
localClient client.Client
45+
remoteClient client.Client
46+
logger logr.Logger
47+
knownTypes sets.Set[reflect.Type]
48+
labelFunc juggler.LabelFunc
4249
}
4350

4451
// KnownTypes implements juggler.ComponentReconciler.
@@ -214,9 +221,7 @@ func (r *FluxReconciler) installOrUpdateManifesto(ctx context.Context, fluxCompo
214221
if err := actual.Reconcile(desired); err != nil {
215222
return err
216223
}
217-
218-
utils.SetManagedBy(obj)
219-
utils.SetLabel(obj, r.labelComponentName, fluxComponent.GetName())
224+
utils.SetLabels(obj, r.labelFunc(fluxComponent))
220225
return nil
221226
})
222227

@@ -241,9 +246,7 @@ func (r *FluxReconciler) installOrUpdateSource(ctx context.Context, fluxComponen
241246
if err := actual.Reconcile(desired); err != nil {
242247
return err
243248
}
244-
245-
utils.SetManagedBy(obj)
246-
utils.SetLabel(obj, r.labelComponentName, fluxComponent.GetName())
249+
utils.SetLabels(obj, r.labelFunc(fluxComponent))
247250
return nil
248251
})
249252

pkg/juggler/fluxcd/flux_reconciler_test.go

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
helmv2 "github.com/fluxcd/helm-controller/api/v2"
1111
sourcev1 "github.com/fluxcd/source-controller/api/v1"
1212
"github.com/go-logr/logr"
13+
"github.com/google/go-cmp/cmp"
1314
"github.com/stretchr/testify/assert"
1415
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1516
"k8s.io/apimachinery/pkg/util/sets"
@@ -60,7 +61,13 @@ func TestNewFluxReconciler(t *testing.T) {
6061
for _, tt := range tests {
6162
t.Run(tt.name, func(t *testing.T) {
6263
actual := NewFluxReconciler(tt.logger, tt.localClient, tt.remoteClient, "")
63-
if !assert.Equal(t, actual, tt.expected) {
64+
diff := cmp.Diff(actual, tt.expected, cmp.Comparer(func(a, b FluxReconciler) bool {
65+
return actual.localClient == tt.localClient &&
66+
actual.remoteClient == tt.remoteClient &&
67+
actual.logger == tt.logger &&
68+
actual.knownTypes.Equal(tt.expected.knownTypes)
69+
}))
70+
if !assert.Empty(t, diff) {
6471
t.Errorf("NewReconciler() = %v, want %v", actual, tt.expected)
6572
}
6673
})
@@ -630,6 +637,7 @@ func TestFluxReconciler_Install(t *testing.T) {
630637
name string
631638
obj juggler.Component
632639
validateFunc func(ctx context.Context, c client.Client, component juggler.Component) error
640+
labelFunc juggler.LabelFunc
633641
expected error
634642
}{
635643
{
@@ -728,11 +736,72 @@ func TestFluxReconciler_Install(t *testing.T) {
728736
},
729737
expected: nil,
730738
},
739+
{
740+
name: "FluxReconciler with custom label func - creation successful",
741+
labelFunc: func(juggler.Component) map[string]string {
742+
return map[string]string{
743+
testLabelComponentName: "custom-name",
744+
"app.kubernetes.io/managed-by": "service-provider-crossplane",
745+
"custom-label": "custom-value",
746+
}
747+
},
748+
obj: FakeFluxComponent{
749+
BuildSourceRepositoryFunc: func(ctx context.Context) (SourceAdapter, error) {
750+
return &HelmRepositoryAdapter{
751+
Source: &sourcev1.HelmRepository{
752+
ObjectMeta: metav1.ObjectMeta{
753+
Name: "test",
754+
Namespace: "default",
755+
},
756+
Spec: sourcev1.HelmRepositorySpec{
757+
URL: "test-url",
758+
},
759+
},
760+
}, nil
761+
},
762+
BuildManifestoFunc: func(ctx context.Context) (Manifesto, error) {
763+
return &HelmReleaseManifesto{
764+
Manifest: &helmv2.HelmRelease{
765+
ObjectMeta: metav1.ObjectMeta{
766+
Name: "test",
767+
Namespace: "default",
768+
},
769+
Spec: helmv2.HelmReleaseSpec{ReleaseName: "test-name"},
770+
},
771+
}, nil
772+
},
773+
GetNameFunc: "FakeFluxComponent",
774+
},
775+
validateFunc: func(ctx context.Context, c client.Client, component juggler.Component) error {
776+
expectedLabels := map[string]string{
777+
"app.kubernetes.io/managed-by": "service-provider-crossplane",
778+
testLabelComponentName: "custom-name",
779+
"custom-label": "custom-value",
780+
}
781+
helmRepository := &sourcev1.HelmRepository{}
782+
if err := c.Get(ctx, client.ObjectKey{Name: "test", Namespace: "default"}, helmRepository); err != nil {
783+
return err
784+
}
785+
if !assert.Equal(t, helmRepository.GetLabels(), expectedLabels) {
786+
return errors.New("labels not equal")
787+
}
788+
helmRelease := &helmv2.HelmRelease{}
789+
if err := c.Get(ctx, client.ObjectKey{Name: "test", Namespace: "default"}, helmRelease); err != nil {
790+
return err
791+
}
792+
if !assert.Equal(t, helmRelease.GetLabels(), expectedLabels) {
793+
return errors.New("labels not equal")
794+
}
795+
return nil
796+
},
797+
expected: nil,
798+
},
731799
}
732800
for _, tt := range tests {
733801
t.Run(tt.name, func(t *testing.T) {
734802
fakeLocalClient := fake.NewClientBuilder().WithScheme(scheme).Build()
735-
r := NewFluxReconciler(logr.Logger{}, fakeLocalClient, nil, testLabelComponentName)
803+
r := NewFluxReconciler(logr.Logger{}, fakeLocalClient, nil, testLabelComponentName).
804+
WithLabelFunc(tt.labelFunc)
736805
ctx := context.TODO()
737806
actual := r.Install(ctx, tt.obj)
738807

pkg/juggler/object/object_reconciler.go

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,25 @@ var _ juggler.OrphanedComponentsDetector = &ObjectReconciler{}
2424

2525
func NewReconciler(logger logr.Logger, remoteClient client.Client, labelComponentName string) *ObjectReconciler {
2626
return &ObjectReconciler{
27-
logger: logger,
28-
remoteClient: remoteClient,
29-
knownTypes: sets.Set[reflect.Type]{},
30-
labelComponentName: labelComponentName,
27+
logger: logger,
28+
remoteClient: remoteClient,
29+
knownTypes: sets.Set[reflect.Type]{},
30+
labelFunc: juggler.DefaultLabelFunc(labelComponentName),
3131
}
3232
}
3333

34+
func (r *ObjectReconciler) WithLabelFunc(fn juggler.LabelFunc) *ObjectReconciler {
35+
if fn != nil {
36+
r.labelFunc = fn
37+
}
38+
return r
39+
}
40+
3441
type ObjectReconciler struct {
35-
logger logr.Logger
36-
remoteClient client.Client
37-
knownTypes sets.Set[reflect.Type]
38-
labelComponentName string
42+
logger logr.Logger
43+
remoteClient client.Client
44+
knownTypes sets.Set[reflect.Type]
45+
labelFunc juggler.LabelFunc
3946
}
4047

4148
// DetectOrphanedComponents implements juggler.OrphanedComponentsDetector.
@@ -197,8 +204,7 @@ func (r *ObjectReconciler) applyObject(ctx context.Context, component juggler.Co
197204
obj.SetNamespace(key.Namespace)
198205

199206
_, err = controllerutil.CreateOrUpdate(ctx, r.remoteClient, obj, func() error {
200-
utils.SetManagedBy(obj)
201-
utils.SetLabel(obj, r.labelComponentName, component.GetName())
207+
utils.SetLabels(obj, r.labelFunc(component))
202208
return objectComponent.ReconcileObject(ctx, obj)
203209
})
204210
return err

pkg/juggler/object/object_reconciler_test.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99

1010
"github.com/go-logr/logr"
11+
"github.com/google/go-cmp/cmp"
1112
"github.com/stretchr/testify/assert"
1213
corev1 "k8s.io/api/core/v1"
1314
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -33,6 +34,7 @@ func TestObjectReconciler_Install(t *testing.T) {
3334
name string
3435
obj juggler.Component
3536
remoteObjects []client.Object
37+
labelFunc juggler.LabelFunc
3638
error error
3739
validateFunc func(ctx context.Context, c client.Client, comp juggler.Component) error
3840
}{
@@ -129,11 +131,48 @@ func TestObjectReconciler_Install(t *testing.T) {
129131
return nil
130132
},
131133
},
134+
{
135+
name: "ObjectReconciler with custom label func - creation successful",
136+
obj: FakeObjectComponent{
137+
BuildObjectToReconcileFunc: func(ctx context.Context) (client.Object, types.NamespacedName, error) {
138+
return &corev1.Secret{}, types.NamespacedName{
139+
Name: "test",
140+
Namespace: "default",
141+
}, nil
142+
},
143+
ReconcileObjectFunc: func(ctx context.Context, obj client.Object) error {
144+
return nil
145+
},
146+
name: "FakeObjectComponent",
147+
},
148+
labelFunc: func(juggler.Component) map[string]string {
149+
return map[string]string{
150+
testLabelComponentName: "custom-name",
151+
"app.kubernetes.io/managed-by": "managed-by-value",
152+
"custom-label": "custom-value",
153+
}
154+
},
155+
validateFunc: func(ctx context.Context, c client.Client, comp juggler.Component) error {
156+
secret := &corev1.Secret{}
157+
if err := c.Get(ctx, client.ObjectKey{Name: "test", Namespace: "default"}, secret); err != nil {
158+
return err
159+
}
160+
if !assert.Equal(t, secret.GetLabels(), map[string]string{
161+
testLabelComponentName: "custom-name",
162+
"app.kubernetes.io/managed-by": "managed-by-value",
163+
"custom-label": "custom-value",
164+
}) {
165+
return errors.New("labels not equal")
166+
}
167+
return nil
168+
},
169+
},
132170
}
133171
for _, tt := range tests {
134172
t.Run(tt.name, func(t *testing.T) {
135173
fakeRemoteClient := fake.NewClientBuilder().WithObjects(tt.remoteObjects...).Build()
136-
r := NewReconciler(logr.Logger{}, fakeRemoteClient, testLabelComponentName)
174+
r := NewReconciler(logr.Logger{}, fakeRemoteClient, testLabelComponentName).
175+
WithLabelFunc(tt.labelFunc)
137176
ctx := context.TODO()
138177
actual := r.Install(ctx, tt.obj)
139178
if !errors.Is(actual, tt.error) {
@@ -417,7 +456,12 @@ func TestNewReconciler(t *testing.T) {
417456
for _, tt := range tests {
418457
t.Run(tt.name, func(t *testing.T) {
419458
actual := NewReconciler(tt.logger, tt.remoteClient, "")
420-
if !assert.Equal(t, tt.expected, actual) {
459+
diff := cmp.Diff(actual, tt.expected, cmp.Comparer(func(a, b ObjectReconciler) bool {
460+
return actual.remoteClient == tt.remoteClient &&
461+
actual.logger == tt.logger &&
462+
actual.knownTypes.Equal(tt.expected.knownTypes)
463+
}))
464+
if !assert.Empty(t, diff) {
421465
t.Errorf("NewReconciler() = %v, want %v", actual, tt.expected)
422466
}
423467
})

pkg/juggler/reconciler.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,17 @@ type OrphanedComponentsDetector interface {
4444
// Orphaned=Existing\Configured (set difference D=M\N).
4545
DetectOrphanedComponents(ctx context.Context, configuredComponents []Component) ([]Component, error)
4646
}
47+
48+
// LabelFunc defines the Kubernetes object labels to be set during component reconciliation
49+
type LabelFunc func(comp Component) map[string]string
50+
51+
// DefaultLabelFunc sets the `managedBy` label to `control-plane-operator`
52+
// and the `componentLabel` label to the name of the component
53+
func DefaultLabelFunc(componentLabel string) LabelFunc {
54+
return func(comp Component) map[string]string {
55+
return map[string]string{
56+
"app.kubernetes.io/managed-by": "control-plane-operator",
57+
componentLabel: comp.GetName(),
58+
}
59+
}
60+
}

pkg/utils/meta.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,9 @@ func IsManaged() client.MatchingLabels {
3131
func HasComponentLabel() client.ListOption {
3232
return client.HasLabels{LabelComponentName}
3333
}
34+
35+
func SetLabels(obj v1.Object, labels map[string]string) {
36+
for k, v := range labels {
37+
SetLabel(obj, k, v)
38+
}
39+
}

0 commit comments

Comments
 (0)