From 3ed5617f68ca44215fba082b4e80116b0ac01de4 Mon Sep 17 00:00:00 2001 From: Yusmen Zabanov Date: Thu, 24 Oct 2024 15:27:21 +0000 Subject: [PATCH] Configure servicebinding.io bindings for managed services Also, annotate the `CFServiceBinding` object with the `korifi.cloudfoundry.org/service-instance-type` annotation. This helps for determining the type of the instance it references without looking up the instance. fixes #3294 Co-authored-by: Danail Branekov --- .../api/v1alpha1/cfservicebinding_types.go | 2 + .../services/bindings/controller.go | 29 +++--- .../services/bindings/controller_test.go | 89 +++++++++++++++++++ 3 files changed, 109 insertions(+), 11 deletions(-) diff --git a/controllers/api/v1alpha1/cfservicebinding_types.go b/controllers/api/v1alpha1/cfservicebinding_types.go index 612b664c8..9337cd613 100644 --- a/controllers/api/v1alpha1/cfservicebinding_types.go +++ b/controllers/api/v1alpha1/cfservicebinding_types.go @@ -26,6 +26,8 @@ import ( const ( BindingFailedCondition = "BindingFailed" BindingRequestedCondition = "BindingRequested" + + ServiceInstanceTypeAnnotationKey = "korifi.cloudfoundry.org/service-instance-type" ) // CFServiceBindingSpec defines the desired state of CFServiceBinding diff --git a/controllers/controllers/services/bindings/controller.go b/controllers/controllers/services/bindings/controller.go index f0d12153c..acd5323a6 100644 --- a/controllers/controllers/services/bindings/controller.go +++ b/controllers/controllers/services/bindings/controller.go @@ -154,6 +154,11 @@ func (r *Reconciler) ReconcileResource(ctx context.Context, cfServiceBinding *ko cfServiceBinding.Status.ObservedGeneration = cfServiceBinding.Generation log.V(1).Info("set observed generation", "generation", cfServiceBinding.Status.ObservedGeneration) + if cfServiceBinding.Annotations == nil { + cfServiceBinding.Annotations = map[string]string{} + } + cfServiceBinding.Annotations[korifiv1alpha1.ServiceInstanceTypeAnnotationKey] = string(cfServiceInstance.Spec.Type) + err = controllerutil.SetOwnerReference(cfServiceInstance, cfServiceBinding, r.scheme) if err != nil { log.Info("error when making the service instance owner of the service binding", "reason", err) @@ -216,14 +221,14 @@ func isSbServiceBindingReady(sbServiceBinding *servicebindingv1beta1.ServiceBind } func (r *Reconciler) reconcileSBServiceBinding(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding) (*servicebindingv1beta1.ServiceBinding, error) { - credentialsSecret := &corev1.Secret{ + bindingSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: cfServiceBinding.Name, + Name: cfServiceBinding.Status.Binding.Name, Namespace: cfServiceBinding.Namespace, }, } - err := r.k8sClient.Get(ctx, client.ObjectKeyFromObject(credentialsSecret), credentialsSecret) + err := r.k8sClient.Get(ctx, client.ObjectKeyFromObject(bindingSecret), bindingSecret) if err != nil { return nil, fmt.Errorf("failed to get service binding credentials secret %q: %w", cfServiceBinding.Status.Binding.Name, err) } @@ -233,14 +238,16 @@ func (r *Reconciler) reconcileSBServiceBinding(ctx context.Context, cfServiceBin _, err = controllerutil.CreateOrPatch(ctx, r.k8sClient, sbServiceBinding, func() error { sbServiceBinding.Spec.Name = getSBServiceBindingName(cfServiceBinding) - secretType, hasType := credentialsSecret.Data["type"] - if hasType && len(secretType) > 0 { - sbServiceBinding.Spec.Type = string(secretType) - } + if cfServiceBinding.Annotations[korifiv1alpha1.ServiceInstanceTypeAnnotationKey] == korifiv1alpha1.UserProvidedType { + secretType, hasType := bindingSecret.Data["type"] + if hasType && len(secretType) > 0 { + sbServiceBinding.Spec.Type = string(secretType) + } - secretProvider, hasProvider := credentialsSecret.Data["provider"] - if hasProvider { - sbServiceBinding.Spec.Provider = string(secretProvider) + secretProvider, hasProvider := bindingSecret.Data["provider"] + if hasProvider { + sbServiceBinding.Spec.Provider = string(secretProvider) + } } return controllerutil.SetControllerReference(cfServiceBinding, sbServiceBinding, r.scheme) }) @@ -263,7 +270,7 @@ func (r *Reconciler) toSBServiceBinding(cfServiceBinding *korifiv1alpha1.CFServi }, }, Spec: servicebindingv1beta1.ServiceBindingSpec{ - Type: "user-provided", + Type: cfServiceBinding.Annotations[korifiv1alpha1.ServiceInstanceTypeAnnotationKey], Workload: servicebindingv1beta1.ServiceBindingWorkloadReference{ APIVersion: "apps/v1", Kind: "StatefulSet", diff --git a/controllers/controllers/services/bindings/controller_test.go b/controllers/controllers/services/bindings/controller_test.go index 4a203fedd..4a2ba456a 100644 --- a/controllers/controllers/services/bindings/controller_test.go +++ b/controllers/controllers/services/bindings/controller_test.go @@ -137,6 +137,15 @@ var _ = Describe("CFServiceBinding", func() { }).Should(Succeed()) }) + It("sets the service-instance-type annotation to user-provided", func() { + Eventually(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(binding), binding)).To(Succeed()) + g.Expect(binding.Annotations).To(HaveKeyWithValue( + korifiv1alpha1.ServiceInstanceTypeAnnotationKey, "user-provided", + )) + }).Should(Succeed()) + }) + It("sets the binding status credentials name to the instance credentials secret", func() { Eventually(func(g Gomega) { g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(binding), binding)).To(Succeed()) @@ -184,6 +193,7 @@ var _ = Describe("CFServiceBinding", func() { }, } g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(sbServiceBinding), sbServiceBinding)).To(Succeed()) + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(binding), binding)).To(Succeed()) g.Expect(sbServiceBinding.Spec.Name).To(Equal(binding.Name)) g.Expect(sbServiceBinding.Spec.Type).To(Equal("user-provided")) @@ -627,6 +637,15 @@ var _ = Describe("CFServiceBinding", func() { }).Should(Succeed()) }) + It("sets the service-instance-type annotation to managed", func() { + Eventually(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(binding), binding)).To(Succeed()) + g.Expect(binding.Annotations).To(HaveKeyWithValue( + korifiv1alpha1.ServiceInstanceTypeAnnotationKey, "managed", + )) + }).Should(Succeed()) + }) + It("binds the service", func() { Eventually(func(g Gomega) { g.Expect(brokerClient.BindCallCount()).To(BeNumerically(">", 0)) @@ -680,6 +699,76 @@ var _ = Describe("CFServiceBinding", func() { }).Should(Succeed()) }) + It("creates a servicebinding.io ServiceBinding", func() { + Eventually(func(g Gomega) { + sbServiceBinding := &servicebindingv1beta1.ServiceBinding{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + Name: fmt.Sprintf("cf-binding-%s", binding.Name), + }, + } + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(sbServiceBinding), sbServiceBinding)).To(Succeed()) + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(binding), binding)).To(Succeed()) + + g.Expect(sbServiceBinding.Spec.Name).To(Equal(binding.Status.Binding.Name)) + g.Expect(sbServiceBinding.Spec.Type).To(Equal("managed")) + }).Should(Succeed()) + }) + + When("the credentials contain type key", func() { + BeforeEach(func() { + brokerClient.BindReturns(osbapi.BindResponse{ + Credentials: map[string]any{ + "foo": "bar", + "type": "please-ignore-me", + }, + Complete: true, + }, nil) + }) + + It("sets the servicebinding.io type to managed (ignoring the type credentials key)", func() { + Eventually(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(binding), binding)).To(Succeed()) + sbServiceBinding := &servicebindingv1beta1.ServiceBinding{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + Name: fmt.Sprintf("cf-binding-%s", binding.Name), + }, + } + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(sbServiceBinding), sbServiceBinding)).To(Succeed()) + + g.Expect(sbServiceBinding.Spec.Type).To(Equal("managed")) + }).Should(Succeed()) + }) + }) + + When("the credentials contain provider key", func() { + BeforeEach(func() { + brokerClient.BindReturns(osbapi.BindResponse{ + Credentials: map[string]any{ + "foo": "bar", + "provider": "please-ignore-me", + }, + Complete: true, + }, nil) + }) + + It("does not set the servicebinding.io provider type (ignoring the provider credentials key)", func() { + Eventually(func(g Gomega) { + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(binding), binding)).To(Succeed()) + sbServiceBinding := &servicebindingv1beta1.ServiceBinding{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + Name: fmt.Sprintf("cf-binding-%s", binding.Name), + }, + } + g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(sbServiceBinding), sbServiceBinding)).To(Succeed()) + + g.Expect(sbServiceBinding.Spec.Provider).To(BeEmpty()) + }).Should(Succeed()) + }) + }) + It("creates the servicebinding.io secret", func() { Eventually(func(g Gomega) { g.Expect(adminClient.Get(ctx, client.ObjectKeyFromObject(binding), binding)).To(Succeed())