Skip to content
Open
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
2c75f08
Split PATCH /v3/service_instances logic into UPSI and Managed
ybykov-a9s Sep 10, 2025
32fc9d8
Add unit tests for managed services on PATCH /v3/service_instances
ybykov-a9s Sep 15, 2025
8661c74
Fix api/repositories/service_instance_repository.go due to suggestions
ybykov-a9s Oct 23, 2025
ea066a9
Merge branch 'cloudfoundry:main' into extend-update-service-instance-…
ybykov-a9s Dec 16, 2025
bc1663d
Cover controllers/services/osbapi with tests
ybykov-a9s Dec 22, 2025
3ffc150
[controllers][managed services] Use Status.PlanGUID in reconcilation …
ybykov-a9s Dec 30, 2025
2d1bd24
[controllers][managed services] update isFailed in order to use Updat…
ybykov-a9s Jan 23, 2026
6651e36
[controllers][services][osbapi] Fix broker statuscodes on Update call
ybykov-a9s Jan 23, 2026
4316eb6
Merge branch 'cloudfoundry:main' into extend-update-service-instance-…
ybykov-a9s Jan 29, 2026
5627bf1
Implement e2e tests for updating managed services
ybykov-a9s Jan 30, 2026
6080d7d
Merge branch 'cloudfoundry:main' into extend-update-service-instance-…
ybykov-a9s Feb 23, 2026
519c99e
Update managed service payloads instance checks
ybykov-a9s Feb 23, 2026
ab0a5e1
Improve managed service instance handler tests
ybykov-a9s Feb 23, 2026
10a5434
Cover repositories/PatchManagedServiceInstance with tests
ybykov-a9s Feb 23, 2026
0a1ac06
Update cfcerviceinstance_types comments for PlanGUID
ybykov-a9s Feb 23, 2026
5e0bd7a
Update cfcerviceinstance_types comments for PlanGUID
ybykov-a9s Feb 23, 2026
a3c00fc
Update managed service controller failing reason to ServiceInstanceFa…
ybykov-a9s Feb 23, 2026
309185d
Update provisioned status field for managed instance
ybykov-a9s Feb 24, 2026
b1a9a0d
Clean up setObservedGeneration method in managed service controller
ybykov-a9s Feb 25, 2026
c471fba
Managed controller allow consequent retries on UpdateFailedCondition
ybykov-a9s Feb 26, 2026
5bd8b84
Managed controller refine plan change test
ybykov-a9s Feb 26, 2026
cc66a02
Managed controller tests refine test names
ybykov-a9s Feb 26, 2026
ee03e3c
Managed controller tests remove extraneous ServicePlan2 creation check
ybykov-a9s Feb 26, 2026
7808778
Managed controller tests remove extraneous ObservedGeneration field c…
ybykov-a9s Feb 26, 2026
4e7355c
Managed controller tests refine test names
ybykov-a9s Feb 26, 2026
0f1e6db
Managed controller tests remove extraneous Ready condition check
ybykov-a9s Feb 26, 2026
ea3d8db
Managed controller tests refine test names
ybykov-a9s Feb 26, 2026
a6f9f03
revert unneeded newline change in webhook tests
ybykov-a9s Feb 26, 2026
df83936
revert unneeded newline change in webhook tests
ybykov-a9s Feb 26, 2026
e46a39b
e2e managed services update tests: move declaring serviceInstanceGUID…
ybykov-a9s Feb 27, 2026
6c99cb7
e2e add check OSB plans number assertion
ybykov-a9s Feb 27, 2026
2932729
combine some managed service update e2e plans
ybykov-a9s Feb 27, 2026
6afbcac
Revert some unneeded changes in api/payloads
ybykov-a9s Mar 2, 2026
afa5310
Simplify ToManagedSIPatchMessage method in api/payloads
ybykov-a9s Mar 2, 2026
735ea65
Revert some unneeded changes in api/handlers
ybykov-a9s Mar 2, 2026
b2db415
Inline tests api/repositories managed service instance creation
ybykov-a9s Mar 2, 2026
138672c
Split api/repositories patch service instance tests
ybykov-a9s Mar 2, 2026
b2aceec
Add api/repositories patch service instance api forbidden test
ybykov-a9s Mar 2, 2026
70adff8
Rename isFailed method in controllers/managed
ybykov-a9s Mar 2, 2026
8507a89
Simplify controllers/managed Reconciler code
ybykov-a9s Mar 2, 2026
73118cd
controllers/managed stop reconsiling on UpdateFailed
ybykov-a9s Mar 2, 2026
95d3b40
controllers/managed move servicePlan2 tests var to its scope
ybykov-a9s Mar 2, 2026
f1ee843
Rearrange e2e tests for updating managed services
ybykov-a9s Mar 2, 2026
9aa1147
Merge branch 'cloudfoundry:main' into extend-update-service-instance-…
ybykov-a9s Mar 3, 2026
97e98f4
Suppress linter errors in a managed controller
ybykov-a9s Mar 5, 2026
ee8aa6a
Fix repo service instance unit tests
ybykov-a9s Mar 5, 2026
1509139
Remove unneeded await check from repositories test
ybykov-a9s Mar 5, 2026
4a14aab
Apply proposed fixes for managed services and their tests
ybykov-a9s Mar 30, 2026
d506b76
Introduce a smoke-test for 'cf update-service' command
ybykov-a9s Mar 30, 2026
3aa15b0
Skip broker updates for metadata-only instance changes
ybykov-a9s Apr 20, 2026
27a56b6
Apply suggections to tests
ybykov-a9s Jun 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 120 additions & 39 deletions api/handlers/fake/cfservice_instance_repository.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions api/handlers/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const (
ServiceBrokerDeleteJobType = "service_broker.delete"
ManagedServiceInstanceDeleteJobType = "managed_service_instance.delete"
ManagedServiceInstanceCreateJobType = "managed_service_instance.create"
ManagedServiceInstanceUpdateJobType = "managed_service_instance.update"
ManagedServiceBindingCreateJobType = "managed_service_binding.create"
ManagedServiceBindingDeleteJobType = "managed_service_binding.delete"
JobTimeoutDuration = 120.0
Expand Down
20 changes: 16 additions & 4 deletions api/handlers/service_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ const (
type CFServiceInstanceRepository interface {
CreateUserProvidedServiceInstance(context.Context, authorization.Info, repositories.CreateUPSIMessage) (repositories.ServiceInstanceRecord, error)
CreateManagedServiceInstance(context.Context, authorization.Info, repositories.CreateManagedSIMessage) (repositories.ServiceInstanceRecord, error)
PatchServiceInstance(context.Context, authorization.Info, repositories.PatchServiceInstanceMessage) (repositories.ServiceInstanceRecord, error)
PatchUserProvidedServiceInstance(context.Context, authorization.Info, repositories.PatchUPSIMessage) (repositories.ServiceInstanceRecord, error)
PatchManagedServiceInstance(context.Context, authorization.Info, repositories.PatchManagedSIMessage) (repositories.ServiceInstanceRecord, error)
ListServiceInstances(context.Context, authorization.Info, repositories.ListServiceInstanceMessage) (repositories.ListResult[repositories.ServiceInstanceRecord], error)
GetServiceInstance(context.Context, authorization.Info, string) (repositories.ServiceInstanceRecord, error)
GetServiceInstanceCredentials(context.Context, authorization.Info, string) (map[string]any, error)
Expand Down Expand Up @@ -185,10 +186,21 @@ func (h *ServiceInstance) patch(r *http.Request) (*routing.Response, error) {
return nil, apierrors.LogAndReturn(logger, apierrors.ForbiddenAsNotFound(err), "failed to get service instance")
}

patchMessage := payload.ToServiceInstancePatchMessage(serviceInstance.SpaceGUID, serviceInstance.GUID)
serviceInstance, err = h.serviceInstanceRepo.PatchServiceInstance(r.Context(), authInfo, patchMessage)
if serviceInstance.Type == korifiv1alpha1.ManagedType {
patchMessage := payload.ToManagedSIPatchMessage(serviceInstance.SpaceGUID, serviceInstance.GUID)
serviceInstance, err = h.serviceInstanceRepo.PatchManagedServiceInstance(r.Context(), authInfo, patchMessage)
if err != nil {
return nil, apierrors.LogAndReturn(logger, err, "failed to patch managed service instance")
}

return routing.NewResponse(http.StatusAccepted).
WithHeader("Location", presenter.JobURLForRedirects(serviceInstance.GUID, presenter.ManagedServiceInstanceUpdateOperation, h.serverURL)), nil
}

patchMessage := payload.ToUPSIPatchMessage(serviceInstance.SpaceGUID, serviceInstance.GUID)
serviceInstance, err = h.serviceInstanceRepo.PatchUserProvidedServiceInstance(r.Context(), authInfo, patchMessage)
if err != nil {
return nil, apierrors.LogAndReturn(logger, err, "failed to patch service instance")
return nil, apierrors.LogAndReturn(logger, err, "failed to patch user provided service instance")
}

return routing.NewResponse(http.StatusOK).WithBody(presenter.ForServiceInstance(serviceInstance, h.serverURL)), nil
Expand Down
75 changes: 70 additions & 5 deletions api/handlers/service_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ var _ = Describe("ServiceInstance", func() {
},
})

serviceInstanceRepo.PatchServiceInstanceReturns(repositories.ServiceInstanceRecord{
serviceInstanceRepo.PatchUserProvidedServiceInstanceReturns(repositories.ServiceInstanceRecord{
Name: "new-name",
GUID: "service-instance-guid",
}, nil)
Expand All @@ -865,10 +865,10 @@ var _ = Describe("ServiceInstance", func() {
Expect(actualAuthInfo).To(Equal(authInfo))
Expect(actualGUID).To(Equal("service-instance-guid"))

Expect(serviceInstanceRepo.PatchServiceInstanceCallCount()).To(Equal(1))
_, actualAuthInfo, patchMessage := serviceInstanceRepo.PatchServiceInstanceArgsForCall(0)
Expect(serviceInstanceRepo.PatchUserProvidedServiceInstanceCallCount()).To(Equal(1))
_, actualAuthInfo, patchMessage := serviceInstanceRepo.PatchUserProvidedServiceInstanceArgsForCall(0)
Expect(actualAuthInfo).To(Equal(authInfo))
Expect(patchMessage).To(Equal(repositories.PatchServiceInstanceMessage{
Expect(patchMessage).To(Equal(repositories.PatchUPSIMessage{
GUID: "service-instance-guid",
SpaceGUID: "space-guid",
Name: tools.PtrTo("new-name"),
Expand Down Expand Up @@ -927,13 +927,78 @@ var _ = Describe("ServiceInstance", func() {

When("patching the service instances fails", func() {
BeforeEach(func() {
serviceInstanceRepo.PatchServiceInstanceReturns(repositories.ServiceInstanceRecord{}, errors.New("oops"))
serviceInstanceRepo.PatchUserProvidedServiceInstanceReturns(repositories.ServiceInstanceRecord{}, errors.New("oops"))
})

It("returns the error", func() {
expectUnknownError()
})
})
When("updating a managed service instance", func() {
BeforeEach(func() {
requestValidator.DecodeAndValidateJSONPayloadStub = decodeAndValidatePayloadStub(&payloads.ServiceInstancePatch{
Name: tools.PtrTo("new-name"),
Tags: &[]string{"alice", "bob"},
Credentials: &map[string]any{"foo": "bar"},
Relationships: &payloads.ServiceInstanceRelationships{
ServicePlan: &payloads.Relationship{
Data: &payloads.RelationshipData{
GUID: "plan-guid",
},
},
},
Metadata: payloads.MetadataPatch{
Annotations: map[string]*string{"ann2": tools.PtrTo("ann_val2")},
Labels: map[string]*string{"lab2": tools.PtrTo("lab_val2")},
},
})

serviceInstanceRepo.GetServiceInstanceReturns(repositories.ServiceInstanceRecord{
GUID: "service-instance-guid",
SpaceGUID: "space-guid",
Type: korifiv1alpha1.ManagedType,
}, nil)
serviceInstanceRepo.PatchManagedServiceInstanceReturns(repositories.ServiceInstanceRecord{
Name: "new-name",
GUID: "service-instance-guid",
}, nil)
})

It("patches the managed service instance", func() {
Expect(requestValidator.DecodeAndValidateJSONPayloadCallCount()).To(Equal(1))
actualReq, _ := requestValidator.DecodeAndValidateJSONPayloadArgsForCall(0)
Expect(bodyString(actualReq)).To(Equal("the-json-body"))

Expect(serviceInstanceRepo.PatchManagedServiceInstanceCallCount()).To(Equal(1))
_, actualAuthInfo, patchMessage := serviceInstanceRepo.PatchManagedServiceInstanceArgsForCall(0)
Expect(actualAuthInfo).To(Equal(authInfo))
Expect(patchMessage).To(Equal(repositories.PatchManagedSIMessage{
GUID: "service-instance-guid",
SpaceGUID: "space-guid",
PlanGUID: tools.PtrTo("plan-guid"),
Name: tools.PtrTo("new-name"),
Tags: &[]string{"alice", "bob"},
MetadataPatch: repositories.MetadataPatch{
Annotations: map[string]*string{"ann2": tools.PtrTo("ann_val2")},
Labels: map[string]*string{"lab2": tools.PtrTo("lab_val2")},
},
}))
})
It("returns HTTP 202 Accepted response", func() {
Expect(rr).To(HaveHTTPStatus(http.StatusAccepted))
Expect(rr).To(HaveHTTPHeaderWithValue("Location",
ContainSubstring("/v3/jobs/managed_service_instance.update~service-instance-guid")))
})
When("patching the managed service instances fails", func() {
BeforeEach(func() {
serviceInstanceRepo.PatchManagedServiceInstanceReturns(repositories.ServiceInstanceRecord{}, errors.New("oops"))
})

It("returns the error", func() {
expectUnknownError()
})
})
})
})

Describe("DELETE /v3/service_instances/:guid", func() {
Expand Down
1 change: 1 addition & 0 deletions api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ func main() {
handlers.ServiceBrokerCreateJobType: serviceBrokerRepo,
handlers.ServiceBrokerUpdateJobType: serviceBrokerRepo,
handlers.ManagedServiceInstanceCreateJobType: serviceInstanceRepo,
handlers.ManagedServiceInstanceUpdateJobType: serviceInstanceRepo,
handlers.ManagedServiceBindingCreateJobType: serviceBindingRepo,
},
routeRepo,
Expand Down
Loading