Skip to content

Commit

Permalink
Configure servicebinding.io bindings for managed services
Browse files Browse the repository at this point in the history
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 <danailster@gmail.com>
  • Loading branch information
zabanov-lab and danail-branekov committed Oct 24, 2024
1 parent dfc0b45 commit 3ed5617
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 11 deletions.
2 changes: 2 additions & 0 deletions controllers/api/v1alpha1/cfservicebinding_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
const (
BindingFailedCondition = "BindingFailed"
BindingRequestedCondition = "BindingRequested"

ServiceInstanceTypeAnnotationKey = "korifi.cloudfoundry.org/service-instance-type"
)

// CFServiceBindingSpec defines the desired state of CFServiceBinding
Expand Down
29 changes: 18 additions & 11 deletions controllers/controllers/services/bindings/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
})
Expand All @@ -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",
Expand Down
89 changes: 89 additions & 0 deletions controllers/controllers/services/bindings/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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"))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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())
Expand Down

0 comments on commit 3ed5617

Please sign in to comment.