Skip to content

Commit

Permalink
Add deletion of managed service instances
Browse files Browse the repository at this point in the history
  • Loading branch information
zabanov-lab committed Sep 19, 2024
1 parent 8df6421 commit b5c5781
Show file tree
Hide file tree
Showing 20 changed files with 717 additions and 81 deletions.
1 change: 1 addition & 0 deletions api/handlers/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const (
ServiceBrokerCreateJobType = "service_broker.create"
ServiceBrokerUpdateJobType = "service_broker.update"
ServiceBrokerDeleteJobType = "service_broker.delete"
ManagedServiceInstanceDeleteJobType = "managed_service_instance.delete"
ManagedServiceInstanceCreateJobType = "managed_service_instance.create"
JobTimeoutDuration = 120.0
)
Expand Down
5 changes: 5 additions & 0 deletions api/handlers/service_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"code.cloudfoundry.org/korifi/api/routing"

"code.cloudfoundry.org/korifi/api/presenter"
korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1"

"code.cloudfoundry.org/korifi/api/repositories"

Expand Down Expand Up @@ -207,6 +208,10 @@ func (h *ServiceInstance) delete(r *http.Request) (*routing.Response, error) {
return nil, apierrors.LogAndReturn(logger, err, "error when deleting service instance", "guid", serviceInstanceGUID)
}

if serviceInstance.Type == korifiv1alpha1.ManagedType {
return routing.NewResponse(http.StatusAccepted).WithHeader("Location", presenter.JobURLForRedirects(serviceInstance.GUID, presenter.ManagedServiceInstanceDeleteOperation, h.serverURL)), nil
}

return routing.NewResponse(http.StatusNoContent), nil
}

Expand Down
24 changes: 24 additions & 0 deletions api/handlers/service_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ var _ = Describe("ServiceInstance", func() {
serviceInstanceRepo.GetServiceInstanceReturns(repositories.ServiceInstanceRecord{
GUID: "service-instance-guid",
SpaceGUID: "space-guid",
Type: korifiv1alpha1.UserProvidedType,
}, nil)

spaceRepo = new(fake.CFSpaceRepository)
Expand Down Expand Up @@ -566,6 +567,29 @@ var _ = Describe("ServiceInstance", func() {
Expect(rr).To(HaveHTTPStatus(http.StatusNoContent))
})

When("the service instance is managed", func() {
BeforeEach(func() {
serviceInstanceRepo.GetServiceInstanceReturns(repositories.ServiceInstanceRecord{
GUID: "service-instance-guid",
SpaceGUID: "space-guid",
Type: korifiv1alpha1.ManagedType,
}, nil)
})

It("deletes the service instance", func() {
Expect(serviceInstanceRepo.DeleteServiceInstanceCallCount()).To(Equal(1))
_, actualAuthInfo, message := serviceInstanceRepo.DeleteServiceInstanceArgsForCall(0)
Expect(actualAuthInfo).To(Equal(authInfo))
Expect(message.GUID).To(Equal("service-instance-guid"))
Expect(message.SpaceGUID).To(Equal("space-guid"))

Expect(rr).To(SatisfyAll(
HaveHTTPStatus(http.StatusAccepted),
HaveHTTPHeaderWithValue("Location", ContainSubstring("/v3/jobs/managed_service_instance.delete~service-instance-guid")),
))
})
})

When("getting the service instance fails with not found", func() {
BeforeEach(func() {
serviceInstanceRepo.GetServiceInstanceReturns(
Expand Down
15 changes: 8 additions & 7 deletions api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,13 +355,14 @@ func main() {
handlers.NewJob(
*serverURL,
map[string]handlers.DeletionRepository{
handlers.OrgDeleteJobType: orgRepo,
handlers.SpaceDeleteJobType: spaceRepo,
handlers.AppDeleteJobType: appRepo,
handlers.RouteDeleteJobType: routeRepo,
handlers.DomainDeleteJobType: domainRepo,
handlers.RoleDeleteJobType: roleRepo,
handlers.ServiceBrokerDeleteJobType: serviceBrokerRepo,
handlers.OrgDeleteJobType: orgRepo,
handlers.SpaceDeleteJobType: spaceRepo,
handlers.AppDeleteJobType: appRepo,
handlers.RouteDeleteJobType: routeRepo,
handlers.DomainDeleteJobType: domainRepo,
handlers.RoleDeleteJobType: roleRepo,
handlers.ServiceBrokerDeleteJobType: serviceBrokerRepo,
handlers.ManagedServiceInstanceDeleteJobType: serviceInstanceRepo,
},
map[string]handlers.StateRepository{
handlers.ServiceBrokerCreateJobType: serviceBrokerRepo,
Expand Down
1 change: 1 addition & 0 deletions api/presenter/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (
ServiceBrokerDeleteOperation = "service_broker.delete"
ServiceBrokerUpdateOperation = "service_broker.update"
ManagedServiceInstanceCreateOperation = "managed_service_instance.create"
ManagedServiceInstanceDeleteOperation = "managed_service_instance.delete"
)

var (
Expand Down
10 changes: 10 additions & 0 deletions api/repositories/service_instance_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ type ServiceInstanceRecord struct {
Annotations map[string]string
CreatedAt time.Time
UpdatedAt *time.Time
DeletedAt *time.Time
Ready bool
}

Expand Down Expand Up @@ -405,6 +406,14 @@ func (r *ServiceInstanceRepo) GetState(ctx context.Context, authInfo authorizati
return model.CFResourceStateUnknown, nil
}

func (r *ServiceInstanceRepo) GetDeletedAt(ctx context.Context, authInfo authorization.Info, instanceGUID string) (*time.Time, error) {
serviceInstance, err := r.GetServiceInstance(ctx, authInfo, instanceGUID)
if err != nil {
return nil, err
}
return serviceInstance.DeletedAt, nil
}

func cfServiceInstanceToRecord(cfServiceInstance korifiv1alpha1.CFServiceInstance) ServiceInstanceRecord {
return ServiceInstanceRecord{
Name: cfServiceInstance.Spec.DisplayName,
Expand All @@ -418,6 +427,7 @@ func cfServiceInstanceToRecord(cfServiceInstance korifiv1alpha1.CFServiceInstanc
Annotations: cfServiceInstance.Annotations,
CreatedAt: cfServiceInstance.CreationTimestamp.Time,
UpdatedAt: getLastUpdatedTime(&cfServiceInstance),
DeletedAt: golangTime(cfServiceInstance.DeletionTimestamp),
Ready: isReady(cfServiceInstance),
}
}
Expand Down
57 changes: 57 additions & 0 deletions api/repositories/service_instance_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,63 @@ var _ = Describe("ServiceInstanceRepository", func() {
})
})

Describe("GetDeletedAt", func() {
var (
cfServiceInstance *korifiv1alpha1.CFServiceInstance
deletionTime *time.Time
getErr error
)

BeforeEach(func() {
cfServiceInstance = &korifiv1alpha1.CFServiceInstance{
ObjectMeta: metav1.ObjectMeta{
Name: uuid.NewString(),
Namespace: space.Name,
},
Spec: korifiv1alpha1.CFServiceInstanceSpec{
Type: "managed",
},
}

Expect(k8sClient.Create(ctx, cfServiceInstance)).To(Succeed())
createRoleBinding(ctx, userName, spaceDeveloperRole.Name, space.Name)
})

JustBeforeEach(func() {
deletionTime, getErr = serviceInstanceRepo.GetDeletedAt(ctx, authInfo, cfServiceInstance.Name)
})

It("returns nil", func() {
Expect(getErr).NotTo(HaveOccurred())
Expect(deletionTime).To(BeNil())
})

When("the instance is being deleted", func() {
BeforeEach(func() {
Expect(k8s.PatchResource(ctx, k8sClient, cfServiceInstance, func() {
cfServiceInstance.Finalizers = append(cfServiceInstance.Finalizers, "foo")
})).To(Succeed())

Expect(k8sClient.Delete(ctx, cfServiceInstance)).To(Succeed())
})

It("returns the deletion time", func() {
Expect(getErr).NotTo(HaveOccurred())
Expect(deletionTime).To(PointTo(BeTemporally("~", time.Now(), time.Minute)))
})
})

When("the instance isn't found", func() {
BeforeEach(func() {
Expect(k8sClient.Delete(ctx, cfServiceInstance)).To(Succeed())
})

It("errors", func() {
Expect(getErr).To(matchers.WrapErrorAssignableToTypeOf(apierrors.NotFoundError{}))
})
})
})

Describe("GetState", func() {
var (
cfServiceInstance *korifiv1alpha1.CFServiceInstance
Expand Down
8 changes: 5 additions & 3 deletions controllers/api/v1alpha1/cfserviceinstance_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ const (

CFManagedServiceInstanceFinalizerName = "managed.cfServiceInstance.korifi.cloudfoundry.org"

ProvisionRequestedCondition = "ProvisionRequested"
ProvisioningFailedCondition = "ProvisioningFailed"
ProvisionRequestedCondition = "ProvisionRequested"
ProvisioningFailedCondition = "ProvisioningFailed"
DeprovisionRequestedCondition = "DeprovisionRequested"
)

// CFServiceInstanceSpec defines the desired state of CFServiceInstance
Expand Down Expand Up @@ -80,7 +81,8 @@ type CFServiceInstanceStatus struct {
//+kubebuilder:validation:Optional
CredentialsObservedVersion string `json:"credentialsObservedVersion,omitempty"`

ProvisionOperation string `json:"provisionOperation,omitempty"`
ProvisionOperation string `json:"provisionOperation,omitempty"`
DeprovisionOperation string `json:"deprovisionOperation,omitempty"`
}

//+kubebuilder:object:root=true
Expand Down
101 changes: 91 additions & 10 deletions controllers/controllers/services/brokers/fake/broker_client.go

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

Loading

0 comments on commit b5c5781

Please sign in to comment.