From 42e3a116f59e999ff6c81b1e84d1dd4492e87995 Mon Sep 17 00:00:00 2001 From: Robert Graeff Date: Wed, 22 Oct 2025 15:28:13 +0200 Subject: [PATCH] feat: exclude provider fields from status update --- internal/controllers/provider/controller.go | 20 ++++++++- .../controllers/provider/controller_test.go | 28 +++++++++++++ lib/utils/provider.go | 41 +++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 lib/utils/provider.go diff --git a/internal/controllers/provider/controller.go b/internal/controllers/provider/controller.go index 11acec96..0b2b9d62 100644 --- a/internal/controllers/provider/controller.go +++ b/internal/controllers/provider/controller.go @@ -79,7 +79,12 @@ func (r *ProviderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (r } } - // patch the status + // Patch the status - but exclude field status.resources as it is set by the provider. + if copyErr := CopyNestedSlice(providerOrig, provider, "status", "resources"); copyErr != nil { + err = errors.Join(err, copyErr) + return res, err + } + updateErr := r.PlatformClient.Status().Patch(ctx, provider, client.MergeFrom(providerOrig)) err = errors.Join(err, updateErr) return res, err @@ -234,3 +239,16 @@ func (r *ProviderReconciler) removeFinalizer(ctx context.Context, provider *unst } return nil } + +func CopyNestedSlice(source, target *unstructured.Unstructured, fieldPath ...string) error { + value, found, err := unstructured.NestedSlice(source.Object, fieldPath...) + if err != nil { + return fmt.Errorf("failed to get nested slice: %w", err) + } + if found { + if err := unstructured.SetNestedSlice(target.Object, value, fieldPath...); err != nil { + return fmt.Errorf("failed to set nested slice: %w", err) + } + } + return nil +} diff --git a/internal/controllers/provider/controller_test.go b/internal/controllers/provider/controller_test.go index 09e5f854..bd67f0ea 100644 --- a/internal/controllers/provider/controller_test.go +++ b/internal/controllers/provider/controller_test.go @@ -189,4 +189,32 @@ var _ = Describe("Deployment Controller", func() { Expect(provider2.Object).To(Equal(provider.Object)) }) }) + + Context("Copy field between unstructured", func() { + + sliceCopy := []any{"c", "o", "p", "y"} + sliceKeep := []any{"k", "e", "e", "p"} + fieldPathCopy := []string{"status", "resourcesCopy"} + fieldPathKeep := []string{"status", "resourcesKeep"} + + It("should copy a nested slice and keep another field", func() { + u1 := &unstructured.Unstructured{Object: map[string]any{}} + Expect(unstructured.SetNestedSlice(u1.Object, sliceCopy, fieldPathCopy...)).To(Succeed()) + + u2 := &unstructured.Unstructured{Object: map[string]any{}} + Expect(unstructured.SetNestedSlice(u2.Object, sliceKeep, fieldPathKeep...)).To(Succeed()) + + Expect(CopyNestedSlice(u1, u2, fieldPathCopy...)).To(Succeed()) + + value, found, err := unstructured.NestedSlice(u2.Object, fieldPathCopy...) + Expect(err).NotTo(HaveOccurred()) + Expect(found).To(BeTrue()) + Expect(value).To(Equal(sliceCopy)) + + value, found, err = unstructured.NestedSlice(u2.Object, fieldPathKeep...) + Expect(err).NotTo(HaveOccurred()) + Expect(found).To(BeTrue()) + Expect(value).To(Equal(sliceKeep)) + }) + }) }) diff --git a/lib/utils/provider.go b/lib/utils/provider.go new file mode 100644 index 00000000..1b033669 --- /dev/null +++ b/lib/utils/provider.go @@ -0,0 +1,41 @@ +package utils + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/openmcp-project/openmcp-operator/api/provider/v1alpha1" +) + +// GetServiceProviderResource retrieves the ServiceProvider resource with the given name using the provided platform client. +func GetServiceProviderResource(ctx context.Context, platformClient client.Client, providerName string) (*v1alpha1.ServiceProvider, error) { + serviceProvider := &v1alpha1.ServiceProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: providerName, + }, + } + if err := platformClient.Get(ctx, client.ObjectKeyFromObject(serviceProvider), serviceProvider); err != nil { + return nil, fmt.Errorf("failed to get service provider resource %s: %w", providerName, err) + } + return serviceProvider, nil +} + +// RegisterGVKsAtServiceProvider updates the status.resources field of the ServiceProvider resource with the provided GroupVersionKinds. +// This can be used by service providers, for example in their init job, to register the kinds of resources they manage. +func RegisterGVKsAtServiceProvider(ctx context.Context, platformClient client.Client, providerName string, gvks ...metav1.GroupVersionKind) error { + providerResource, err := GetServiceProviderResource(ctx, platformClient, providerName) + if err != nil { + return err + } + + providerResourceOld := providerResource.DeepCopy() + providerResource.Status.Resources = gvks + if err := platformClient.Status().Patch(ctx, providerResource, client.MergeFrom(providerResourceOld)); err != nil { + return fmt.Errorf("failed to patch platform service provider status: %w", err) + } + + return nil +}