Skip to content

Commit c2bf8ac

Browse files
authored
fix(juggler): configurable labels (#124)
* fix(juggler): configurable labels On-behalf-of: @SAP christopher.junk@sap.com Signed-off-by: Christopher Junk <christopher.junk@sap.com> * refactor(utils): export labels for reuse On-behalf-of: @SAP christopher.junk@sap.com Signed-off-by: Christopher Junk <christopher.junk@sap.com> --------- Signed-off-by: Christopher Junk <christopher.junk@sap.com>
1 parent 26752f8 commit c2bf8ac

File tree

7 files changed

+197
-49
lines changed

7 files changed

+197
-49
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: 78 additions & 7 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"
@@ -21,7 +22,11 @@ import (
2122

2223
var errBoom = errors.New("boom")
2324

24-
const testLabelComponentName = "flux.juggler.test.io/component"
25+
const (
26+
testLabelComponentKey = "flux.juggler.test.io/component"
27+
testLabelManagedByKey = "flux.juggler.test.io/managedBy"
28+
testLabelManagedByValue = "flux.juggler.test.io/control-plane-operator"
29+
)
2530

2631
func TestNewFluxReconciler(t *testing.T) {
2732
fakeClient := fake.NewFakeClient()
@@ -60,7 +65,13 @@ func TestNewFluxReconciler(t *testing.T) {
6065
for _, tt := range tests {
6166
t.Run(tt.name, func(t *testing.T) {
6267
actual := NewFluxReconciler(tt.logger, tt.localClient, tt.remoteClient, "")
63-
if !assert.Equal(t, actual, tt.expected) {
68+
diff := cmp.Diff(actual, tt.expected, cmp.Comparer(func(a, b FluxReconciler) bool {
69+
return actual.localClient == tt.localClient &&
70+
actual.remoteClient == tt.remoteClient &&
71+
actual.logger == tt.logger &&
72+
actual.knownTypes.Equal(tt.expected.knownTypes)
73+
}))
74+
if !assert.Empty(t, diff) {
6475
t.Errorf("NewReconciler() = %v, want %v", actual, tt.expected)
6576
}
6677
})
@@ -364,7 +375,7 @@ func TestFluxReconciler_Observe(t *testing.T) {
364375
for _, tt := range tests {
365376
t.Run(tt.name, func(t *testing.T) {
366377

367-
r := NewFluxReconciler(logr.Logger{}, fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.localObjects...).Build(), nil, testLabelComponentName)
378+
r := NewFluxReconciler(logr.Logger{}, fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.localObjects...).Build(), nil, testLabelComponentKey)
368379
actualObservation, actualError := r.Observe(context.TODO(), tt.obj)
369380
if !assert.Equal(t, tt.expectedObservation, actualObservation) {
370381
t.Errorf("ObjectReconciler.Observe() = %v, want %v", actualObservation, tt.expectedObservation)
@@ -616,7 +627,7 @@ func TestFluxReconciler_Uninstall(t *testing.T) {
616627
}
617628
for _, tt := range tests {
618629
t.Run(tt.name, func(t *testing.T) {
619-
r := NewFluxReconciler(logr.Logger{}, fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.localObjects...).Build(), nil, testLabelComponentName)
630+
r := NewFluxReconciler(logr.Logger{}, fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.localObjects...).Build(), nil, testLabelComponentKey)
620631
actual := r.Uninstall(context.TODO(), tt.obj)
621632
if !errors.Is(actual, tt.expected) {
622633
t.Errorf("ObjectReconciler.Uninstall() = %v, want %v", actual, tt.expected)
@@ -630,6 +641,7 @@ func TestFluxReconciler_Install(t *testing.T) {
630641
name string
631642
obj juggler.Component
632643
validateFunc func(ctx context.Context, c client.Client, component juggler.Component) error
644+
labelFunc juggler.LabelFunc
633645
expected error
634646
}{
635647
{
@@ -675,7 +687,7 @@ func TestFluxReconciler_Install(t *testing.T) {
675687
}
676688
if !assert.Equal(t, helmRepo.GetLabels(), map[string]string{
677689
"app.kubernetes.io/managed-by": "control-plane-operator",
678-
testLabelComponentName: component.GetName(),
690+
testLabelComponentKey: component.GetName(),
679691
}) {
680692
return errors.New("labels not equal")
681693
}
@@ -720,19 +732,78 @@ func TestFluxReconciler_Install(t *testing.T) {
720732
}
721733
if !assert.Equal(t, helmRelease.GetLabels(), map[string]string{
722734
"app.kubernetes.io/managed-by": "control-plane-operator",
723-
testLabelComponentName: component.GetName(),
735+
testLabelComponentKey: component.GetName(),
724736
}) {
725737
return errors.New("labels not equal")
726738
}
727739
return nil
728740
},
729741
expected: nil,
730742
},
743+
{
744+
name: "FluxReconciler with custom label func - creation successful",
745+
labelFunc: func(comp juggler.Component) map[string]string {
746+
return map[string]string{
747+
testLabelComponentKey: comp.GetName(),
748+
testLabelManagedByKey: testLabelManagedByValue,
749+
}
750+
},
751+
obj: FakeFluxComponent{
752+
BuildSourceRepositoryFunc: func(ctx context.Context) (SourceAdapter, error) {
753+
return &HelmRepositoryAdapter{
754+
Source: &sourcev1.HelmRepository{
755+
ObjectMeta: metav1.ObjectMeta{
756+
Name: "test",
757+
Namespace: "default",
758+
},
759+
Spec: sourcev1.HelmRepositorySpec{
760+
URL: "test-url",
761+
},
762+
},
763+
}, nil
764+
},
765+
BuildManifestoFunc: func(ctx context.Context) (Manifesto, error) {
766+
return &HelmReleaseManifesto{
767+
Manifest: &helmv2.HelmRelease{
768+
ObjectMeta: metav1.ObjectMeta{
769+
Name: "test",
770+
Namespace: "default",
771+
},
772+
Spec: helmv2.HelmReleaseSpec{ReleaseName: "test-name"},
773+
},
774+
}, nil
775+
},
776+
GetNameFunc: "FakeFluxComponent",
777+
},
778+
validateFunc: func(ctx context.Context, c client.Client, component juggler.Component) error {
779+
expectedLabels := map[string]string{
780+
testLabelComponentKey: component.GetName(),
781+
testLabelManagedByKey: testLabelManagedByValue,
782+
}
783+
helmRepository := &sourcev1.HelmRepository{}
784+
if err := c.Get(ctx, client.ObjectKey{Name: "test", Namespace: "default"}, helmRepository); err != nil {
785+
return err
786+
}
787+
if !assert.Equal(t, helmRepository.GetLabels(), expectedLabels) {
788+
return errors.New("labels not equal")
789+
}
790+
helmRelease := &helmv2.HelmRelease{}
791+
if err := c.Get(ctx, client.ObjectKey{Name: "test", Namespace: "default"}, helmRelease); err != nil {
792+
return err
793+
}
794+
if !assert.Equal(t, helmRelease.GetLabels(), expectedLabels) {
795+
return errors.New("labels not equal")
796+
}
797+
return nil
798+
},
799+
expected: nil,
800+
},
731801
}
732802
for _, tt := range tests {
733803
t.Run(tt.name, func(t *testing.T) {
734804
fakeLocalClient := fake.NewClientBuilder().WithScheme(scheme).Build()
735-
r := NewFluxReconciler(logr.Logger{}, fakeLocalClient, nil, testLabelComponentName)
805+
r := NewFluxReconciler(logr.Logger{}, fakeLocalClient, nil, testLabelComponentKey).
806+
WithLabelFunc(tt.labelFunc)
736807
ctx := context.TODO()
737808
actual := r.Install(ctx, tt.obj)
738809

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: 54 additions & 8 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"
@@ -26,13 +27,18 @@ import (
2627

2728
var errBoom = errors.New("boom")
2829

29-
const testLabelComponentName = "object.juggler.test.io/component"
30+
const (
31+
testLabelComponentKey = "object.juggler.test.io/component"
32+
testLabelManagedByKey = "object.juggler.test.io/managedBy"
33+
testLabelManagedByValue = "object.juggler.test.io/control-plane-operator"
34+
)
3035

3136
func TestObjectReconciler_Install(t *testing.T) {
3237
tests := []struct {
3338
name string
3439
obj juggler.Component
3540
remoteObjects []client.Object
41+
labelFunc juggler.LabelFunc
3642
error error
3743
validateFunc func(ctx context.Context, c client.Client, comp juggler.Component) error
3844
}{
@@ -79,7 +85,7 @@ func TestObjectReconciler_Install(t *testing.T) {
7985
}
8086
if !assert.Equal(t, secret.GetLabels(), map[string]string{
8187
"app.kubernetes.io/managed-by": "control-plane-operator",
82-
testLabelComponentName: comp.GetName(),
88+
testLabelComponentKey: comp.GetName(),
8389
}) {
8490
return errors.New("labels not equal")
8591
}
@@ -119,7 +125,7 @@ func TestObjectReconciler_Install(t *testing.T) {
119125
}
120126
if !assert.Equal(t, secret.GetLabels(), map[string]string{
121127
"app.kubernetes.io/managed-by": "control-plane-operator",
122-
testLabelComponentName: comp.GetName(),
128+
testLabelComponentKey: comp.GetName(),
123129
}) {
124130
return errors.New("labels not equal")
125131
}
@@ -129,11 +135,46 @@ func TestObjectReconciler_Install(t *testing.T) {
129135
return nil
130136
},
131137
},
138+
{
139+
name: "ObjectReconciler with custom label func - creation successful",
140+
obj: FakeObjectComponent{
141+
BuildObjectToReconcileFunc: func(ctx context.Context) (client.Object, types.NamespacedName, error) {
142+
return &corev1.Secret{}, types.NamespacedName{
143+
Name: "test",
144+
Namespace: "default",
145+
}, nil
146+
},
147+
ReconcileObjectFunc: func(ctx context.Context, obj client.Object) error {
148+
return nil
149+
},
150+
name: "FakeObjectComponent",
151+
},
152+
labelFunc: func(comp juggler.Component) map[string]string {
153+
return map[string]string{
154+
testLabelComponentKey: comp.GetName(),
155+
testLabelManagedByKey: testLabelManagedByValue,
156+
}
157+
},
158+
validateFunc: func(ctx context.Context, c client.Client, comp juggler.Component) error {
159+
secret := &corev1.Secret{}
160+
if err := c.Get(ctx, client.ObjectKey{Name: "test", Namespace: "default"}, secret); err != nil {
161+
return err
162+
}
163+
if !assert.Equal(t, secret.GetLabels(), map[string]string{
164+
testLabelComponentKey: comp.GetName(),
165+
testLabelManagedByKey: testLabelManagedByValue,
166+
}) {
167+
return errors.New("labels not equal")
168+
}
169+
return nil
170+
},
171+
},
132172
}
133173
for _, tt := range tests {
134174
t.Run(tt.name, func(t *testing.T) {
135175
fakeRemoteClient := fake.NewClientBuilder().WithObjects(tt.remoteObjects...).Build()
136-
r := NewReconciler(logr.Logger{}, fakeRemoteClient, testLabelComponentName)
176+
r := NewReconciler(logr.Logger{}, fakeRemoteClient, testLabelComponentKey).
177+
WithLabelFunc(tt.labelFunc)
137178
ctx := context.TODO()
138179
actual := r.Install(ctx, tt.obj)
139180
if !errors.Is(actual, tt.error) {
@@ -370,7 +411,7 @@ func TestObjectReconciler_Uninstall(t *testing.T) {
370411
for _, tt := range tests {
371412
t.Run(tt.name, func(t *testing.T) {
372413
fakeClient := fake.NewClientBuilder().WithObjects(tt.remoteObjects...).Build()
373-
r := NewReconciler(logr.Logger{}, fakeClient, testLabelComponentName)
414+
r := NewReconciler(logr.Logger{}, fakeClient, testLabelComponentKey)
374415
ctx := context.TODO()
375416
actual := r.Uninstall(ctx, tt.obj)
376417
if !errors.Is(actual, tt.expected) {
@@ -417,7 +458,12 @@ func TestNewReconciler(t *testing.T) {
417458
for _, tt := range tests {
418459
t.Run(tt.name, func(t *testing.T) {
419460
actual := NewReconciler(tt.logger, tt.remoteClient, "")
420-
if !assert.Equal(t, tt.expected, actual) {
461+
diff := cmp.Diff(actual, tt.expected, cmp.Comparer(func(a, b ObjectReconciler) bool {
462+
return actual.remoteClient == tt.remoteClient &&
463+
actual.logger == tt.logger &&
464+
actual.knownTypes.Equal(tt.expected.knownTypes)
465+
}))
466+
if !assert.Empty(t, diff) {
421467
t.Errorf("NewReconciler() = %v, want %v", actual, tt.expected)
422468
}
423469
})
@@ -531,7 +577,7 @@ func TestObjectReconciler_Observe(t *testing.T) {
531577
}
532578
for _, tt := range tests {
533579
t.Run(tt.name, func(t *testing.T) {
534-
r := NewReconciler(logr.Logger{}, fake.NewClientBuilder().WithObjects(tt.remoteObjects...).Build(), testLabelComponentName)
580+
r := NewReconciler(logr.Logger{}, fake.NewClientBuilder().WithObjects(tt.remoteObjects...).Build(), testLabelComponentKey)
535581
ctx := context.TODO()
536582
actualObservation, actualError := r.Observe(ctx, tt.obj)
537583
if !assert.Equal(t, tt.expectedObservation, actualObservation) {
@@ -625,7 +671,7 @@ func Test_ObjectReconciler_DetectOrphanedComponents(t *testing.T) {
625671
for _, tC := range testCases {
626672
t.Run(tC.desc, func(t *testing.T) {
627673
fakeClient := fake.NewClientBuilder().WithObjects(tC.initObjs...).WithInterceptorFuncs(tC.interceptorFuncs).Build()
628-
r := NewReconciler(logr.Logger{}, fakeClient, testLabelComponentName)
674+
r := NewReconciler(logr.Logger{}, fakeClient, testLabelComponentKey)
629675
for _, cc := range tC.configuredComponents {
630676
r.RegisterType(cc.(ObjectComponent))
631677
}

0 commit comments

Comments
 (0)