From 06fda6a9dc20d6b74ab1804685f5c5e1bbdf4d93 Mon Sep 17 00:00:00 2001 From: Lennard Rother Date: Wed, 15 Jul 2026 09:42:54 +0200 Subject: [PATCH 1/3] feat(servicecredentialbinding): expose name in status --- apis/resources/v1alpha1/servicecredentialbinding_types.go | 2 ++ internal/clients/servicecredentialbinding/keyrotator.go | 2 ++ .../servicecredentialbinding/servicecredentialbinding.go | 2 ++ .../controller/servicecredentialbinding/controller_test.go | 1 + ...loudfoundry.crossplane.io_servicecredentialbindings.yaml | 6 ++++++ 5 files changed, 13 insertions(+) diff --git a/apis/resources/v1alpha1/servicecredentialbinding_types.go b/apis/resources/v1alpha1/servicecredentialbinding_types.go index 080e442e..ad8dc23f 100644 --- a/apis/resources/v1alpha1/servicecredentialbinding_types.go +++ b/apis/resources/v1alpha1/servicecredentialbinding_types.go @@ -109,6 +109,8 @@ type ServiceCredentialBindingStatus struct { type SCBResource struct { // The GUID of the Cloud Foundry resource GUID string `json:"guid,omitempty"` + // The name of the Cloud Foundry resource + Name string `json:"name,omitempty"` // The date and time when the resource was created. CreatedAt *metav1.Time `json:"createdAt,omitempty"` } diff --git a/internal/clients/servicecredentialbinding/keyrotator.go b/internal/clients/servicecredentialbinding/keyrotator.go index 89672920..bfc76bb3 100644 --- a/internal/clients/servicecredentialbinding/keyrotator.go +++ b/internal/clients/servicecredentialbinding/keyrotator.go @@ -9,6 +9,7 @@ import ( cfresource "github.com/cloudfoundry/go-cfclient/v3/resource" "github.com/crossplane/crossplane-runtime/v2/pkg/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" "github.com/SAP/crossplane-provider-cloudfoundry/apis/resources/v1alpha1" ) @@ -55,6 +56,7 @@ func (r *SCBKeyRotator) RetireBinding(cr *v1alpha1.ServiceCredentialBinding, ser } cr.Status.AtProvider.RetiredKeys = append(cr.Status.AtProvider.RetiredKeys, &v1alpha1.SCBResource{ GUID: serviceBinding.GUID, + Name: ptr.Deref(serviceBinding.Name, ""), CreatedAt: &metav1.Time{Time: serviceBinding.CreatedAt}, }) return true diff --git a/internal/clients/servicecredentialbinding/servicecredentialbinding.go b/internal/clients/servicecredentialbinding/servicecredentialbinding.go index d8df6d01..05b21be7 100644 --- a/internal/clients/servicecredentialbinding/servicecredentialbinding.go +++ b/internal/clients/servicecredentialbinding/servicecredentialbinding.go @@ -14,6 +14,7 @@ import ( "github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed" xpresource "github.com/crossplane/crossplane-runtime/v2/pkg/resource" "github.com/google/uuid" + "k8s.io/utils/ptr" "github.com/SAP/crossplane-provider-cloudfoundry/apis/resources/v1alpha1" "github.com/SAP/crossplane-provider-cloudfoundry/internal/clients/job" @@ -222,6 +223,7 @@ func newUpdateOption(mg xpresource.Managed, forProvider v1alpha1.ServiceCredenti // UpdateObservation updates the CR's AtProvider status from the observed resource func UpdateObservation(observation *v1alpha1.ServiceCredentialBindingObservation, r *resource.ServiceCredentialBinding) { observation.GUID = r.GUID + observation.Name = ptr.Deref(r.Name, "") observation.LastOperation = &v1alpha1.LastOperation{ Type: r.LastOperation.Type, State: r.LastOperation.State, diff --git a/internal/controller/servicecredentialbinding/controller_test.go b/internal/controller/servicecredentialbinding/controller_test.go index 1b9b22bf..35705525 100644 --- a/internal/controller/servicecredentialbinding/controller_test.go +++ b/internal/controller/servicecredentialbinding/controller_test.go @@ -81,6 +81,7 @@ func withObservedLabels(labels map[string]*string) modifier { func withObservation(guid string, lastOp *v1alpha1.LastOperation) modifier { return func(r *v1alpha1.ServiceCredentialBinding) { r.Status.AtProvider.GUID = guid + r.Status.AtProvider.Name = name r.Status.AtProvider.CreatedAt = &metav1.Time{} r.Status.AtProvider.LastOperation = lastOp } diff --git a/package/crds/cloudfoundry.crossplane.io_servicecredentialbindings.yaml b/package/crds/cloudfoundry.crossplane.io_servicecredentialbindings.yaml index 635841c8..d4fc475e 100644 --- a/package/crds/cloudfoundry.crossplane.io_servicecredentialbindings.yaml +++ b/package/crds/cloudfoundry.crossplane.io_servicecredentialbindings.yaml @@ -456,6 +456,9 @@ spec: was updated in RFC3339 format. type: string type: object + name: + description: The name of the Cloud Foundry resource + type: string retiredKeys: description: If the binding is rotated, `retiredBindings` stores resources that have been rotated out but are still transitionally @@ -469,6 +472,9 @@ spec: guid: description: The GUID of the Cloud Foundry resource type: string + name: + description: The name of the Cloud Foundry resource + type: string type: object type: array type: object From 51a42d64a23eb616d5361f278d5062aef2905811 Mon Sep 17 00:00:00 2001 From: Lennard Rother Date: Wed, 15 Jul 2026 11:36:04 +0200 Subject: [PATCH 2/3] test(servicecredentialbinding): add name to unit test --- .../keyrotator_test.go | 6 ++ .../servicecredentialbinding_test.go | 66 +++++++++++++------ 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/internal/clients/servicecredentialbinding/keyrotator_test.go b/internal/clients/servicecredentialbinding/keyrotator_test.go index eaf99383..17697fdb 100644 --- a/internal/clients/servicecredentialbinding/keyrotator_test.go +++ b/internal/clients/servicecredentialbinding/keyrotator_test.go @@ -9,6 +9,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/mock" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" cfresource "github.com/cloudfoundry/go-cfclient/v3/resource" @@ -84,6 +85,7 @@ func TestSCBKeyRotator_RetireBinding(t *testing.T) { serviceBindingResource := &cfresource.ServiceCredentialBinding{ Resource: cfresource.Resource{GUID: "test-guid"}, + Name: ptr.To("my-binding-abc12"), } cases := map[string]struct { @@ -134,6 +136,10 @@ func TestSCBKeyRotator_RetireBinding(t *testing.T) { for _, retiredKey := range tc.args.cr.Status.AtProvider.RetiredKeys { if retiredKey.GUID == serviceBindingResource.GUID { found = true + if retiredKey.Name != ptr.Deref(serviceBindingResource.Name, "") { + t.Errorf("RetireBinding(...): Name mismatch in retired key, want %q, got %q", + ptr.Deref(serviceBindingResource.Name, ""), retiredKey.Name) + } break } } diff --git a/internal/clients/servicecredentialbinding/servicecredentialbinding_test.go b/internal/clients/servicecredentialbinding/servicecredentialbinding_test.go index 6b6abb7d..3d62f28f 100644 --- a/internal/clients/servicecredentialbinding/servicecredentialbinding_test.go +++ b/internal/clients/servicecredentialbinding/servicecredentialbinding_test.go @@ -376,30 +376,58 @@ func TestNewCreateOption(t *testing.T) { } func TestUpdateObservation(t *testing.T) { - observation := &v1alpha1.ServiceCredentialBindingObservation{} - now := time.Now() - resource := &cfresource.ServiceCredentialBinding{ - Resource: cfresource.Resource{GUID: testGUID}, - LastOperation: cfresource.LastOperation{ - Type: v1alpha1.LastOperationCreate, - State: v1alpha1.LastOperationSucceeded, - Description: "Create succeeded", - UpdatedAt: now, - CreatedAt: now, + + cases := map[string]struct { + resource *cfresource.ServiceCredentialBinding + wantName string + }{ + "WithName": { + resource: &cfresource.ServiceCredentialBinding{ + Resource: cfresource.Resource{GUID: testGUID}, + Name: ptr.To("my-binding-abc12"), + LastOperation: cfresource.LastOperation{ + Type: v1alpha1.LastOperationCreate, + State: v1alpha1.LastOperationSucceeded, + UpdatedAt: now, + CreatedAt: now, + }, + }, + wantName: "my-binding-abc12", + }, + "WithoutName": { + resource: &cfresource.ServiceCredentialBinding{ + Resource: cfresource.Resource{GUID: testGUID}, + Name: nil, + LastOperation: cfresource.LastOperation{ + Type: v1alpha1.LastOperationCreate, + State: v1alpha1.LastOperationSucceeded, + UpdatedAt: now, + CreatedAt: now, + }, + }, + wantName: "", }, } - UpdateObservation(observation, resource) + for n, tc := range cases { + t.Run(n, func(t *testing.T) { + observation := &v1alpha1.ServiceCredentialBindingObservation{} + UpdateObservation(observation, tc.resource) - if observation.GUID != testGUID { - t.Errorf("UpdateObservation(...): GUID mismatch, want %s, got %s", testGUID, observation.GUID) - } - if observation.LastOperation.Type != v1alpha1.LastOperationCreate { - t.Errorf("UpdateObservation(...): LastOperation.Type mismatch") - } - if observation.LastOperation.State != v1alpha1.LastOperationSucceeded { - t.Errorf("UpdateObservation(...): LastOperation.State mismatch") + if observation.GUID != testGUID { + t.Errorf("UpdateObservation(...): GUID mismatch, want %s, got %s", testGUID, observation.GUID) + } + if observation.Name != tc.wantName { + t.Errorf("UpdateObservation(...): Name mismatch, want %q, got %q", tc.wantName, observation.Name) + } + if observation.LastOperation.Type != v1alpha1.LastOperationCreate { + t.Errorf("UpdateObservation(...): LastOperation.Type mismatch") + } + if observation.LastOperation.State != v1alpha1.LastOperationSucceeded { + t.Errorf("UpdateObservation(...): LastOperation.State mismatch") + } + }) } } From 64e04dc137f1d013561061a8e5b1d7fb90741ee6 Mon Sep 17 00:00:00 2001 From: Lennard Rother Date: Wed, 15 Jul 2026 13:32:49 +0200 Subject: [PATCH 3/3] docs(servicecredentialbinding): add comment that name is only set during retirement --- internal/clients/servicecredentialbinding/keyrotator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/clients/servicecredentialbinding/keyrotator.go b/internal/clients/servicecredentialbinding/keyrotator.go index bfc76bb3..0355f3dc 100644 --- a/internal/clients/servicecredentialbinding/keyrotator.go +++ b/internal/clients/servicecredentialbinding/keyrotator.go @@ -56,7 +56,7 @@ func (r *SCBKeyRotator) RetireBinding(cr *v1alpha1.ServiceCredentialBinding, ser } cr.Status.AtProvider.RetiredKeys = append(cr.Status.AtProvider.RetiredKeys, &v1alpha1.SCBResource{ GUID: serviceBinding.GUID, - Name: ptr.Deref(serviceBinding.Name, ""), + Name: ptr.Deref(serviceBinding.Name, ""), // only set during retirement; already retired keys are not backfilled CreatedAt: &metav1.Time{Time: serviceBinding.CreatedAt}, }) return true