diff --git a/api/presenter/service_instance.go b/api/presenter/service_instance.go index a9d653e02..629fccdf7 100644 --- a/api/presenter/service_instance.go +++ b/api/presenter/service_instance.go @@ -45,11 +45,6 @@ type ServiceInstanceLinks struct { } func ForServiceInstance(serviceInstanceRecord repositories.ServiceInstanceRecord, baseURL url.URL, includes ...model.IncludedResource) ServiceInstanceResponse { - lastOperationType := "update" - if serviceInstanceRecord.UpdatedAt == nil || serviceInstanceRecord.CreatedAt.Equal(*serviceInstanceRecord.UpdatedAt) { - lastOperationType = "create" - } - return ServiceInstanceResponse{ Name: serviceInstanceRecord.Name, GUID: serviceInstanceRecord.GUID, @@ -58,9 +53,9 @@ func ForServiceInstance(serviceInstanceRecord repositories.ServiceInstanceRecord LastOperation: lastOperation{ CreatedAt: formatTimestamp(&serviceInstanceRecord.CreatedAt), UpdatedAt: formatTimestamp(serviceInstanceRecord.UpdatedAt), - Description: "Operation succeeded", - State: "succeeded", - Type: lastOperationType, + Description: serviceInstanceRecord.LastOperation.Description, + State: serviceInstanceRecord.LastOperation.State, + Type: serviceInstanceRecord.LastOperation.Type, }, CreatedAt: formatTimestamp(&serviceInstanceRecord.CreatedAt), UpdatedAt: formatTimestamp(serviceInstanceRecord.UpdatedAt), diff --git a/api/presenter/service_instance_test.go b/api/presenter/service_instance_test.go index 31880c6ba..8898e5606 100644 --- a/api/presenter/service_instance_test.go +++ b/api/presenter/service_instance_test.go @@ -7,6 +7,7 @@ import ( "code.cloudfoundry.org/korifi/api/presenter" "code.cloudfoundry.org/korifi/api/repositories" + "code.cloudfoundry.org/korifi/model/services" . "code.cloudfoundry.org/korifi/tests/matchers" "code.cloudfoundry.org/korifi/tools" . "github.com/onsi/ginkgo/v2" @@ -36,6 +37,11 @@ var _ = Describe("Service Instance", func() { Labels: map[string]string{ "foo": "bar", }, + LastOperation: services.LastOperation{ + Type: "update", + State: "succeeded", + Description: "Operation succeeded", + }, Annotations: map[string]string{ "one": "two", }, @@ -104,16 +110,6 @@ var _ = Describe("Service Instance", func() { }`)) }) - When("create and update times are the same", func() { - BeforeEach(func() { - record.UpdatedAt = &record.CreatedAt - }) - - It("sets last operation type to create", func() { - Expect(output).To(MatchJSONPath("$.last_operation.type", "create")) - }) - }) - When("labels is nil", func() { BeforeEach(func() { record.Labels = nil diff --git a/api/repositories/service_instance_repository.go b/api/repositories/service_instance_repository.go index dce09dc6f..12b08c405 100644 --- a/api/repositories/service_instance_repository.go +++ b/api/repositories/service_instance_repository.go @@ -14,6 +14,7 @@ import ( "code.cloudfoundry.org/korifi/api/repositories/compare" korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1" "code.cloudfoundry.org/korifi/model" + "code.cloudfoundry.org/korifi/model/services" "code.cloudfoundry.org/korifi/tools" "code.cloudfoundry.org/korifi/tools/k8s" @@ -169,19 +170,20 @@ type DeleteServiceInstanceMessage struct { } type ServiceInstanceRecord struct { - Name string - GUID string - SpaceGUID string - PlanGUID string - SecretName string - Tags []string - Type string - Labels map[string]string - Annotations map[string]string - CreatedAt time.Time - UpdatedAt *time.Time - DeletedAt *time.Time - Ready bool + Name string + GUID string + SpaceGUID string + PlanGUID string + SecretName string + Tags []string + Type string + Labels map[string]string + Annotations map[string]string + CreatedAt time.Time + UpdatedAt *time.Time + DeletedAt *time.Time + LastOperation services.LastOperation + Ready bool } func (r ServiceInstanceRecord) Relationships() map[string]string { @@ -545,19 +547,20 @@ func (r *ServiceInstanceRepo) removeBindingsFinalizer(ctx context.Context, userC func cfServiceInstanceToRecord(cfServiceInstance korifiv1alpha1.CFServiceInstance) ServiceInstanceRecord { return ServiceInstanceRecord{ - Name: cfServiceInstance.Spec.DisplayName, - GUID: cfServiceInstance.Name, - SpaceGUID: cfServiceInstance.Namespace, - PlanGUID: cfServiceInstance.Spec.PlanGUID, - SecretName: cfServiceInstance.Spec.SecretName, - Tags: cfServiceInstance.Spec.Tags, - Type: string(cfServiceInstance.Spec.Type), - Labels: cfServiceInstance.Labels, - Annotations: cfServiceInstance.Annotations, - CreatedAt: cfServiceInstance.CreationTimestamp.Time, - UpdatedAt: getLastUpdatedTime(&cfServiceInstance), - DeletedAt: golangTime(cfServiceInstance.DeletionTimestamp), - Ready: isInstanceReady(cfServiceInstance), + Name: cfServiceInstance.Spec.DisplayName, + GUID: cfServiceInstance.Name, + SpaceGUID: cfServiceInstance.Namespace, + PlanGUID: cfServiceInstance.Spec.PlanGUID, + SecretName: cfServiceInstance.Spec.SecretName, + Tags: cfServiceInstance.Spec.Tags, + Type: string(cfServiceInstance.Spec.Type), + Labels: cfServiceInstance.Labels, + Annotations: cfServiceInstance.Annotations, + CreatedAt: cfServiceInstance.CreationTimestamp.Time, + UpdatedAt: getLastUpdatedTime(&cfServiceInstance), + DeletedAt: golangTime(cfServiceInstance.DeletionTimestamp), + LastOperation: cfServiceInstance.Status.LastOperation, + Ready: isInstanceReady(cfServiceInstance), } } diff --git a/api/repositories/service_instance_repository_test.go b/api/repositories/service_instance_repository_test.go index 610741818..3ae36c2c4 100644 --- a/api/repositories/service_instance_repository_test.go +++ b/api/repositories/service_instance_repository_test.go @@ -13,6 +13,7 @@ import ( "code.cloudfoundry.org/korifi/api/repositories/fakeawaiter" korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1" "code.cloudfoundry.org/korifi/model" + "code.cloudfoundry.org/korifi/model/services" "code.cloudfoundry.org/korifi/tests/matchers" "code.cloudfoundry.org/korifi/tools" "code.cloudfoundry.org/korifi/tools/k8s" @@ -306,6 +307,50 @@ var _ = Describe("ServiceInstanceRepository", func() { }) }) + Describe("instance record last operation", func() { + var ( + cfServiceInstance *korifiv1alpha1.CFServiceInstance + serviceInstanceRecord repositories.ServiceInstanceRecord + ) + + BeforeEach(func() { + createRoleBinding(ctx, userName, spaceDeveloperRole.Name, space.Name) + + cfServiceInstance = &korifiv1alpha1.CFServiceInstance{ + ObjectMeta: metav1.ObjectMeta{ + Name: uuid.NewString(), + Namespace: space.Name, + }, + Spec: korifiv1alpha1.CFServiceInstanceSpec{ + Type: korifiv1alpha1.ManagedType, + }, + } + Expect(k8sClient.Create(ctx, cfServiceInstance)).To(Succeed()) + + Expect(k8s.Patch(ctx, k8sClient, cfServiceInstance, func() { + cfServiceInstance.Status.LastOperation = services.LastOperation{ + Type: "create", + State: "failed", + Description: "failed due to error", + } + })).To(Succeed()) + }) + + JustBeforeEach(func() { + var err error + serviceInstanceRecord, err = serviceInstanceRepo.GetServiceInstance(ctx, authInfo, cfServiceInstance.Name) + Expect(err).NotTo(HaveOccurred()) + }) + + It("returns last operation", func() { + Expect(serviceInstanceRecord.LastOperation).To(Equal(services.LastOperation{ + Type: "create", + State: "failed", + Description: "failed due to error", + })) + }) + }) + Describe("GetDeletedAt", func() { var ( cfServiceInstance *korifiv1alpha1.CFServiceInstance diff --git a/controllers/api/v1alpha1/cfserviceinstance_types.go b/controllers/api/v1alpha1/cfserviceinstance_types.go index 61d9197fe..56269457c 100644 --- a/controllers/api/v1alpha1/cfserviceinstance_types.go +++ b/controllers/api/v1alpha1/cfserviceinstance_types.go @@ -19,6 +19,7 @@ package v1alpha1 import ( "fmt" + "code.cloudfoundry.org/korifi/model/services" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" @@ -79,6 +80,9 @@ type CFServiceInstanceStatus struct { // This will ensure that interested contollers are notified on instance credentials change //+kubebuilder:validation:Optional CredentialsObservedVersion string `json:"credentialsObservedVersion,omitempty"` + + //+kubebuilder:validation:Optional + LastOperation services.LastOperation `json:"last_operation"` } //+kubebuilder:object:root=true diff --git a/controllers/api/v1alpha1/zz_generated.deepcopy.go b/controllers/api/v1alpha1/zz_generated.deepcopy.go index 59e84ec5e..fda821d7b 100644 --- a/controllers/api/v1alpha1/zz_generated.deepcopy.go +++ b/controllers/api/v1alpha1/zz_generated.deepcopy.go @@ -1479,6 +1479,7 @@ func (in *CFServiceInstanceStatus) DeepCopyInto(out *CFServiceInstanceStatus) { } } out.Credentials = in.Credentials + out.LastOperation = in.LastOperation } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CFServiceInstanceStatus. diff --git a/controllers/controllers/services/instances/managed/controller.go b/controllers/controllers/services/instances/managed/controller.go index 8bc48ca9b..a83194c83 100644 --- a/controllers/controllers/services/instances/managed/controller.go +++ b/controllers/controllers/services/instances/managed/controller.go @@ -26,6 +26,7 @@ import ( korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1" "code.cloudfoundry.org/korifi/controllers/controllers/services/osbapi" "code.cloudfoundry.org/korifi/controllers/controllers/shared" + "code.cloudfoundry.org/korifi/model/services" "code.cloudfoundry.org/korifi/tools" "code.cloudfoundry.org/korifi/tools/k8s" @@ -116,7 +117,7 @@ func (r *Reconciler) isManaged(object client.Object) bool { } //+kubebuilder:rbac:groups=korifi.cloudfoundry.org,resources=cfserviceinstances,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=korifi.cloudfoundry.org,resources=cfserviceinstances/status,verbs=get;update;atch +//+kubebuilder:rbac:groups=korifi.cloudfoundry.org,resources=cfserviceinstances/status,verbs=get;update;patch //+kubebuilder:rbac:groups=korifi.cloudfoundry.org,resources=cfserviceinstances/finalizers,verbs=update func (r *Reconciler) ReconcileResource(ctx context.Context, serviceInstance *korifiv1alpha1.CFServiceInstance) (ctrl.Result, error) { @@ -174,6 +175,7 @@ func (r *Reconciler) ReconcileResource(ctx context.Context, serviceInstance *kor return r.pollProvisionOperation(ctx, serviceInstance, serviceInstanceAssets, osbapiClient, provisionResponse.Operation) } + serviceInstance.Status.LastOperation.State = "succeeded" return ctrl.Result{}, nil } @@ -224,6 +226,11 @@ func (r *Reconciler) provisionServiceInstance( return osbapi.ServiceInstanceOperationResponse{}, err } + serviceInstance.Status.LastOperation = services.LastOperation{ + Type: "create", + State: "initial", + } + var provisionResponse osbapi.ServiceInstanceOperationResponse provisionResponse, err = osbapiClient.Provision(ctx, osbapi.InstanceProvisionPayload{ InstanceID: serviceInstance.Name, @@ -239,6 +246,7 @@ func (r *Reconciler) provisionServiceInstance( log.Error(err, "failed to provision service") if osbapi.IsUnrecoveralbeError(err) { + serviceInstance.Status.LastOperation.State = "failed" meta.SetStatusCondition(&serviceInstance.Status.Conditions, metav1.Condition{ Type: korifiv1alpha1.ProvisioningFailedCondition, Status: metav1.ConditionTrue, @@ -279,6 +287,9 @@ func (r *Reconciler) pollProvisionOperation( return ctrl.Result{}, k8s.NewNotReadyError().WithCause(err).WithReason("GetLastOperationFailed") } + serviceInstance.Status.LastOperation.State = lastOpResponse.State + serviceInstance.Status.LastOperation.Description = lastOpResponse.Description + if lastOpResponse.State == "in progress" { return ctrl.Result{}, k8s.NewNotReadyError().WithReason("ProvisionInProgress").WithRequeue() } diff --git a/controllers/controllers/services/instances/managed/controller_test.go b/controllers/controllers/services/instances/managed/controller_test.go index f1caba64c..a7d4d6be6 100644 --- a/controllers/controllers/services/instances/managed/controller_test.go +++ b/controllers/controllers/services/instances/managed/controller_test.go @@ -138,6 +138,7 @@ var _ = Describe("CFServiceInstance", func() { }, }, } + Expect(adminClient.Create(ctx, instance)).To(Succeed()) }) @@ -211,6 +212,17 @@ var _ = Describe("CFServiceInstance", func() { }).Should(Succeed()) }) + It("sets succeeded state in instance last operation", func() { + Eventually(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(instance), instance)).To(Succeed()) + g.Expect(brokerClient.ProvisionCallCount()).To(BeNumerically(">=", 1)) + g.Expect(instance.Status.LastOperation).To(Equal(services.LastOperation{ + Type: "create", + State: "succeeded", + })) + }).Should(Succeed()) + }) + When("the service instance parameters are not set", func() { BeforeEach(func() { Expect(k8s.PatchResource(ctx, adminClient, instance, func() { @@ -330,6 +342,17 @@ var _ = Describe("CFServiceInstance", func() { })) }).Should(Succeed()) }) + + It("sets initial state in instance last operation", func() { + Eventually(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(instance), instance)).To(Succeed()) + g.Expect(brokerClient.ProvisionCallCount()).To(BeNumerically(">=", 1)) + g.Expect(instance.Status.LastOperation).To(Equal(services.LastOperation{ + Type: "create", + State: "initial", + })) + }).Should(Succeed()) + }) }) When("service provisioning fails with unrecoverable error", func() { @@ -355,6 +378,17 @@ var _ = Describe("CFServiceInstance", func() { )) }).Should(Succeed()) }) + + It("sets failed state in instance last operation", func() { + Eventually(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(instance), instance)).To(Succeed()) + g.Expect(brokerClient.ProvisionCallCount()).To(BeNumerically(">=", 1)) + g.Expect(instance.Status.LastOperation).To(Equal(services.LastOperation{ + Type: "create", + State: "failed", + })) + }).Should(Succeed()) + }) }) When("getting service last operation fails", func() { @@ -392,6 +426,17 @@ var _ = Describe("CFServiceInstance", func() { }).Should(Succeed()) }) + It("sets in progress state in instance last operation", func() { + Eventually(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(instance), instance)).To(Succeed()) + g.Expect(brokerClient.ProvisionCallCount()).To(BeNumerically(">=", 1)) + g.Expect(instance.Status.LastOperation).To(Equal(services.LastOperation{ + Type: "create", + State: "in progress", + })) + }).Should(Succeed()) + }) + It("keeps checking last operation", func() { Eventually(func(g Gomega) { g.Expect(brokerClient.GetServiceInstanceLastOperationCallCount()).To(BeNumerically(">", 1)) @@ -436,6 +481,17 @@ var _ = Describe("CFServiceInstance", func() { ))) }).Should(Succeed()) }) + + It("sets failed state in instance last operation", func() { + Eventually(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(instance), instance)).To(Succeed()) + g.Expect(brokerClient.ProvisionCallCount()).To(BeNumerically(">=", 1)) + g.Expect(instance.Status.LastOperation).To(Equal(services.LastOperation{ + Type: "create", + State: "failed", + })) + }).Should(Succeed()) + }) }) When("the instance has become ready", func() { diff --git a/controllers/controllers/services/instances/upsi/controller.go b/controllers/controllers/services/instances/upsi/controller.go index 04068d89c..d016c00d9 100644 --- a/controllers/controllers/services/instances/upsi/controller.go +++ b/controllers/controllers/services/instances/upsi/controller.go @@ -23,6 +23,7 @@ import ( korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1" "code.cloudfoundry.org/korifi/controllers/controllers/shared" + "code.cloudfoundry.org/korifi/model/services" "code.cloudfoundry.org/korifi/tools" "code.cloudfoundry.org/korifi/tools/k8s" @@ -125,11 +126,18 @@ func (r *Reconciler) ReconcileResource(ctx context.Context, cfServiceInstance *k } if err = r.validateCredentials(credentialsSecret); err != nil { - return ctrl.Result{}, k8s.NewNotReadyError().WithCause(err).WithReason("SecretInvalid") + cfServiceInstance.Status.LastOperation = services.LastOperation{ + Type: "create", + State: "failed", + } + return ctrl.Result{}, k8s.NewNotReadyError().WithCause(err).WithReason("SecretInvalid").WithNoRequeue() } log.V(1).Info("credentials secret", "name", credentialsSecret.Name, "version", credentialsSecret.ResourceVersion) cfServiceInstance.Status.Credentials = corev1.LocalObjectReference{Name: credentialsSecret.Name} + + cfServiceInstance.Status.LastOperation = reconcileLastOperation(cfServiceInstance, credentialsSecret) + cfServiceInstance.Status.CredentialsObservedVersion = credentialsSecret.ResourceVersion return ctrl.Result{}, nil @@ -142,3 +150,19 @@ func (r *Reconciler) validateCredentials(credentialsSecret *corev1.Secret) error credentialsSecret.Name, ) } + +func reconcileLastOperation(cfServiceInstance *korifiv1alpha1.CFServiceInstance, credentialsSecret *corev1.Secret) services.LastOperation { + if cfServiceInstance.Status.CredentialsObservedVersion == "" { + return services.LastOperation{ + Type: "create", + State: "succeeded", + } + } + if cfServiceInstance.Status.CredentialsObservedVersion != credentialsSecret.ResourceVersion && cfServiceInstance.Status.CredentialsObservedVersion != "" { + return services.LastOperation{ + Type: "update", + State: "succeeded", + } + } + return services.LastOperation{} +} diff --git a/controllers/controllers/services/instances/upsi/controller_test.go b/controllers/controllers/services/instances/upsi/controller_test.go index 3ca2247bd..36a830107 100644 --- a/controllers/controllers/services/instances/upsi/controller_test.go +++ b/controllers/controllers/services/instances/upsi/controller_test.go @@ -5,6 +5,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1" + "code.cloudfoundry.org/korifi/model/services" . "code.cloudfoundry.org/korifi/tests/matchers" "code.cloudfoundry.org/korifi/tools" "code.cloudfoundry.org/korifi/tools/k8s" @@ -118,6 +119,26 @@ var _ = Describe("CFServiceInstance", func() { ))) }).Should(Succeed()) }) + + It("sets the instance last operation failed state", func() { + Eventually(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(instance), instance)).To(Succeed()) + g.Expect(instance.Status.LastOperation).To(Equal(services.LastOperation{ + Type: "create", + State: "failed", + })) + }).Should(Succeed()) + }) + }) + + It("sets the instance last operation succeed state", func() { + Eventually(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(instance), instance)).To(Succeed()) + g.Expect(instance.Status.LastOperation).To(Equal(services.LastOperation{ + Type: "create", + State: "succeeded", + })) + }).Should(Succeed()) }) When("the credentials secret changes", func() { @@ -170,30 +191,48 @@ var _ = Describe("CFServiceInstance", func() { }).Should(Succeed()) }) }) - }) - }) - When("the service instance is managed", func() { - BeforeEach(func() { - instance = &korifiv1alpha1.CFServiceInstance{ - ObjectMeta: metav1.ObjectMeta{ - Name: uuid.NewString(), - Namespace: testNamespace, - }, - Spec: korifiv1alpha1.CFServiceInstanceSpec{ - DisplayName: "service-instance-name", - Type: korifiv1alpha1.ManagedType, - Tags: []string{}, - }, - } - Expect(adminClient.Create(ctx, instance)).To(Succeed()) + When("credentials observed version is not equal to the secret version", func() { + BeforeEach(func() { + Expect(k8s.Patch(ctx, adminClient, instance, func() { + instance.Status.CredentialsObservedVersion = "invalid-version" + })).To(Succeed()) + }) + + It("sets the instance last operation update type", func() { + Eventually(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(instance), instance)).To(Succeed()) + g.Expect(instance.Status.LastOperation).To(Equal(services.LastOperation{ + Type: "update", + State: "succeeded", + })) + }).Should(Succeed()) + }) + }) }) - It("does not reconcile it", func() { - Consistently(func(g Gomega) { - g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(instance), instance)).To(Succeed()) - g.Expect(instance.Status).To(BeZero()) - }).Should(Succeed()) + When("the service instance is managed", func() { + BeforeEach(func() { + instance = &korifiv1alpha1.CFServiceInstance{ + ObjectMeta: metav1.ObjectMeta{ + Name: uuid.NewString(), + Namespace: testNamespace, + }, + Spec: korifiv1alpha1.CFServiceInstanceSpec{ + DisplayName: "service-instance-name", + Type: korifiv1alpha1.ManagedType, + Tags: []string{}, + }, + } + Expect(adminClient.Create(ctx, instance)).To(Succeed()) + }) + + It("does not reconcile it", func() { + Consistently(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(instance), instance)).To(Succeed()) + g.Expect(instance.Status).To(BeZero()) + }).Should(Succeed()) + }) }) }) }) diff --git a/helm/korifi/controllers/crds/korifi.cloudfoundry.org_cfserviceinstances.yaml b/helm/korifi/controllers/crds/korifi.cloudfoundry.org_cfserviceinstances.yaml index 47a37e0c6..02097acf0 100644 --- a/helm/korifi/controllers/crds/korifi.cloudfoundry.org_cfserviceinstances.yaml +++ b/helm/korifi/controllers/crds/korifi.cloudfoundry.org_cfserviceinstances.yaml @@ -163,6 +163,27 @@ spec: ObservedGeneration captures the latest version of the spec.secretName that has been reconciled This will ensure that interested contollers are notified on instance credentials change type: string + last_operation: + properties: + description: + type: string + state: + enum: + - initial + - in progress + - succeeded + - failed + type: string + type: + enum: + - create + - update + - delete + type: string + required: + - state + - type + type: object observedGeneration: description: ObservedGeneration captures the latest generation of the CFServiceInstance that has been reconciled diff --git a/helm/korifi/controllers/role.yaml b/helm/korifi/controllers/role.yaml index aa003ebb7..2f53e64e2 100644 --- a/helm/korifi/controllers/role.yaml +++ b/helm/korifi/controllers/role.yaml @@ -158,6 +158,7 @@ rules: - cfroutes/status - cfservicebindings/status - cfservicebrokers/status + - cfserviceinstances/status - cfspaces/status - cftasks/status verbs: @@ -195,15 +196,6 @@ rules: - list - patch - watch -- apiGroups: - - korifi.cloudfoundry.org - resources: - - cfserviceinstances/status - verbs: - - atch - - get - - patch - - update - apiGroups: - korifi.cloudfoundry.org resources: diff --git a/model/services/instances.go b/model/services/instances.go new file mode 100644 index 000000000..ec431c46f --- /dev/null +++ b/model/services/instances.go @@ -0,0 +1,11 @@ +package services + +type LastOperation struct { + // +kubebuilder:validation:Enum=create;update;delete + Type string `json:"type"` + // +kubebuilder:validation:Enum=initial;in progress;succeeded;failed + State string `json:"state"` + + //+kubebuilder:validation:Optional + Description string `json:"description"` +}