Skip to content

Commit f682b5c

Browse files
author
Mykhailo Pechkurov
committed
fix: Handle empty LastOperation state for user-provided services
User-provided Cloud Foundry services are created synchronously without async operations, resulting in an empty LastOperation object (empty Type and State fields). The Observe method was treating this as an unknown state and throwing an error, causing reconciliation to fail. This fix treats an empty LastOperation.State the same as succeeded state, since an existing user-provided service with no async operation is ready and available. Fixes #201
1 parent 63ae2b4 commit f682b5c

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

internal/controller/serviceinstance/controller.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,9 @@ func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
187187
ResourceExists: r.LastOperation.Type != v1alpha1.LastOperationCreate, // set to false when the last operation is create, hence the reconciler will retry create
188188
ResourceUpToDate: r.LastOperation.Type != v1alpha1.LastOperationUpdate, // set to false when the last operation is update, hence the reconciler will retry update
189189
}, nil
190-
case v1alpha1.LastOperationSucceeded:
190+
case v1alpha1.LastOperationSucceeded, "":
191191
// If the last operation succeeded, set the CR to available
192+
// Empty state is treated as succeeded (happens with user-provided services that have no async operations)
192193
cr.SetConditions(xpv1.Available())
193194
var credentialsUpToDate bool
194195
desiredCredentials, err := extractCredentialSpec(ctx, c.kube, cr.Spec.ForProvider)

internal/controller/serviceinstance/controller_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,39 @@ func TestObserve(t *testing.T) {
491491
)
492492
return m
493493
},
494+
},
495+
"UserProvidedService_EmptyLastOperation": {
496+
args: args{
497+
mg: serviceInstance("user-provided", withExternalName(guid), withSpace(spaceGUID), withCredentials(&jsonCredentials), withStatus(v1alpha1.ServiceInstanceObservation{Credentials: iSha256([]byte(jsonCredentials))})),
498+
},
499+
want: want{
500+
mg: serviceInstance("user-provided",
501+
withExternalName(guid),
502+
withSpace(spaceGUID),
503+
withCredentials(&jsonCredentials),
504+
withStatus(v1alpha1.ServiceInstanceObservation{ID: &guid, Credentials: iSha256([]byte(jsonCredentials))}),
505+
withConditions(xpv1.Available()),
506+
),
507+
obs: managed.ExternalObservation{ResourceExists: true, ResourceUpToDate: true},
508+
err: nil,
509+
},
510+
service: func() *fake.MockServiceInstance {
511+
m := &fake.MockServiceInstance{}
512+
// User-provided services have empty LastOperation
513+
m.On("Get", guid).Return(
514+
&fake.NewServiceInstance("user-provided").SetName(name).SetGUID(guid).SetLastOperation("", "").ServiceInstance,
515+
nil,
516+
)
517+
m.On("Single").Return(
518+
&fake.NewServiceInstance("user-provided").SetName(name).SetGUID(guid).SetLastOperation("", "").ServiceInstance,
519+
nil,
520+
)
521+
m.On("GetUserProvidedCredentials", guid).Return(
522+
fake.JSONRawMessage(jsonCredentials),
523+
nil,
524+
)
525+
return m
526+
},
494527
}}
495528

496529
for n, tc := range cases {

0 commit comments

Comments
 (0)