Skip to content

Commit cf1ee0f

Browse files
Refactor binding controllers
The logic for servicebinding.io creation is moved to the respective controllers and some consants are moved to the korifiv1alpha1 package fixes #3576 Co-authored-by: Danail Branekov <[email protected]>
1 parent 8715b0b commit cf1ee0f

File tree

10 files changed

+340
-147
lines changed

10 files changed

+340
-147
lines changed

controllers/api/v1alpha1/cfservicebinding_types.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ const (
3131
ServiceInstanceTypeAnnotationKey = "korifi.cloudfoundry.org/service-instance-type"
3232
PlanGUIDLabelKey = "korifi.cloudfoundry.org/plan-guid"
3333

34-
CFServiceBindingFinalizerName = "cfServiceBinding.korifi.cloudfoundry.org"
34+
ServiceBindingGUIDLabel = "korifi.cloudfoundry.org/service-binding-guid"
35+
ServiceCredentialBindingTypeLabel = "korifi.cloudfoundry.org/service-credential-binding-type"
36+
CFServiceBindingFinalizerName = "cfServiceBinding.korifi.cloudfoundry.org"
3537
)
3638

3739
// CFServiceBindingSpec defines the desired state of CFServiceBinding

controllers/controllers/services/bindings/controller.go

Lines changed: 18 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,14 @@ package bindings
1818

1919
import (
2020
"context"
21-
"fmt"
2221

2322
korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1"
2423
"code.cloudfoundry.org/korifi/controllers/controllers/shared"
24+
"code.cloudfoundry.org/korifi/tools"
2525
"code.cloudfoundry.org/korifi/tools/k8s"
2626

2727
"github.com/go-logr/logr"
2828
servicebindingv1beta1 "github.com/servicebinding/runtime/apis/v1beta1"
29-
corev1 "k8s.io/api/core/v1"
30-
"k8s.io/apimachinery/pkg/api/meta"
31-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3229
"k8s.io/apimachinery/pkg/runtime"
3330
"k8s.io/apimachinery/pkg/types"
3431
ctrl "sigs.k8s.io/controller-runtime"
@@ -45,31 +42,31 @@ const (
4542
ServiceBindingSecretTypePrefix = "servicebinding.io/"
4643
)
4744

48-
type CredentialsReconciler interface {
49-
ReconcileResource(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding) (ctrl.Result, error)
45+
type DelegateReconciler interface {
46+
ReconcileResource(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding, cfServiceInstance *korifiv1alpha1.CFServiceInstance) (ctrl.Result, error)
5047
}
5148

5249
type Reconciler struct {
53-
k8sClient client.Client
54-
scheme *runtime.Scheme
55-
log logr.Logger
56-
upsiCredentialsReconciler CredentialsReconciler
57-
managedCredentialsReconciler CredentialsReconciler
50+
k8sClient client.Client
51+
scheme *runtime.Scheme
52+
log logr.Logger
53+
upsiReconciler DelegateReconciler
54+
managedReconciler DelegateReconciler
5855
}
5956

6057
func NewReconciler(
6158
k8sClient client.Client,
6259
scheme *runtime.Scheme,
6360
log logr.Logger,
64-
upsiCredentialsReconciler CredentialsReconciler,
65-
managedCredentialsReconciler CredentialsReconciler,
61+
upsiCredentialsReconciler DelegateReconciler,
62+
managedCredentialsReconciler DelegateReconciler,
6663
) *k8s.PatchingReconciler[korifiv1alpha1.CFServiceBinding, *korifiv1alpha1.CFServiceBinding] {
6764
cfBindingReconciler := &Reconciler{
68-
k8sClient: k8sClient,
69-
scheme: scheme,
70-
log: log,
71-
upsiCredentialsReconciler: upsiCredentialsReconciler,
72-
managedCredentialsReconciler: managedCredentialsReconciler,
65+
k8sClient: k8sClient,
66+
scheme: scheme,
67+
log: log,
68+
upsiReconciler: upsiCredentialsReconciler,
69+
managedReconciler: managedCredentialsReconciler,
7370
}
7471
return k8s.NewPatchingReconciler(log, k8sClient, cfBindingReconciler)
7572
}
@@ -126,10 +123,7 @@ func (r *Reconciler) ReconcileResource(ctx context.Context, cfServiceBinding *ko
126123
return ctrl.Result{}, err
127124
}
128125

129-
if cfServiceBinding.Annotations == nil {
130-
cfServiceBinding.Annotations = map[string]string{}
131-
}
132-
cfServiceBinding.Annotations[korifiv1alpha1.ServiceInstanceTypeAnnotationKey] = string(cfServiceInstance.Spec.Type)
126+
cfServiceBinding.Annotations = tools.SetMapValue(cfServiceBinding.Annotations, korifiv1alpha1.ServiceInstanceTypeAnnotationKey, string(cfServiceInstance.Spec.Type))
133127

134128
err = controllerutil.SetOwnerReference(cfServiceInstance, cfServiceBinding, r.scheme)
135129
if err != nil {
@@ -145,29 +139,15 @@ func (r *Reconciler) ReconcileResource(ctx context.Context, cfServiceBinding *ko
145139
return res, err
146140
}
147141

148-
sbServiceBinding, err := r.reconcileSBServiceBinding(ctx, cfServiceBinding)
149-
if err != nil {
150-
log.Info("error creating/updating servicebinding.io servicebinding", "reason", err)
151-
return ctrl.Result{}, err
152-
}
153-
154-
if !isSbServiceBindingReady(sbServiceBinding) {
155-
return ctrl.Result{}, k8s.NewNotReadyError().WithReason("ServiceBindingNotReady")
156-
}
157-
158142
return ctrl.Result{}, nil
159143
}
160144

161145
func (r *Reconciler) reconcileByType(ctx context.Context, cfServiceInstance *korifiv1alpha1.CFServiceInstance, cfServiceBinding *korifiv1alpha1.CFServiceBinding) (ctrl.Result, error) {
162146
if cfServiceInstance.Spec.Type == korifiv1alpha1.UserProvidedType {
163-
return r.upsiCredentialsReconciler.ReconcileResource(ctx, cfServiceBinding)
147+
return r.upsiReconciler.ReconcileResource(ctx, cfServiceBinding, cfServiceInstance)
164148
}
165149

166-
if cfServiceBinding.Labels == nil {
167-
cfServiceBinding.Labels = map[string]string{}
168-
}
169-
cfServiceBinding.Labels[korifiv1alpha1.PlanGUIDLabelKey] = cfServiceInstance.Spec.PlanGUID
170-
return r.managedCredentialsReconciler.ReconcileResource(ctx, cfServiceBinding)
150+
return r.managedReconciler.ReconcileResource(ctx, cfServiceBinding, cfServiceInstance)
171151
}
172152

173153
func needsRequeue(res ctrl.Result, err error) bool {
@@ -177,93 +157,3 @@ func needsRequeue(res ctrl.Result, err error) bool {
177157

178158
return !res.IsZero()
179159
}
180-
181-
func isSbServiceBindingReady(sbServiceBinding *servicebindingv1beta1.ServiceBinding) bool {
182-
readyCondition := meta.FindStatusCondition(sbServiceBinding.Status.Conditions, "Ready")
183-
if readyCondition == nil {
184-
return false
185-
}
186-
187-
if readyCondition.Status != metav1.ConditionTrue {
188-
return false
189-
}
190-
191-
return sbServiceBinding.Generation == sbServiceBinding.Status.ObservedGeneration
192-
}
193-
194-
func (r *Reconciler) reconcileSBServiceBinding(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding) (*servicebindingv1beta1.ServiceBinding, error) {
195-
bindingSecret := &corev1.Secret{
196-
ObjectMeta: metav1.ObjectMeta{
197-
Name: cfServiceBinding.Status.Binding.Name,
198-
Namespace: cfServiceBinding.Namespace,
199-
},
200-
}
201-
202-
err := r.k8sClient.Get(ctx, client.ObjectKeyFromObject(bindingSecret), bindingSecret)
203-
if err != nil {
204-
return nil, fmt.Errorf("failed to get service binding credentials secret %q: %w", cfServiceBinding.Status.Binding.Name, err)
205-
}
206-
207-
sbServiceBinding := r.toSBServiceBinding(cfServiceBinding)
208-
209-
_, err = controllerutil.CreateOrPatch(ctx, r.k8sClient, sbServiceBinding, func() error {
210-
sbServiceBinding.Spec.Name = getSBServiceBindingName(cfServiceBinding)
211-
212-
if cfServiceBinding.Annotations[korifiv1alpha1.ServiceInstanceTypeAnnotationKey] == korifiv1alpha1.UserProvidedType {
213-
secretType, hasType := bindingSecret.Data["type"]
214-
if hasType && len(secretType) > 0 {
215-
sbServiceBinding.Spec.Type = string(secretType)
216-
}
217-
218-
secretProvider, hasProvider := bindingSecret.Data["provider"]
219-
if hasProvider {
220-
sbServiceBinding.Spec.Provider = string(secretProvider)
221-
}
222-
}
223-
return controllerutil.SetControllerReference(cfServiceBinding, sbServiceBinding, r.scheme)
224-
})
225-
if err != nil {
226-
return nil, err
227-
}
228-
229-
return sbServiceBinding, nil
230-
}
231-
232-
func (r *Reconciler) toSBServiceBinding(cfServiceBinding *korifiv1alpha1.CFServiceBinding) *servicebindingv1beta1.ServiceBinding {
233-
return &servicebindingv1beta1.ServiceBinding{
234-
ObjectMeta: metav1.ObjectMeta{
235-
Name: fmt.Sprintf("cf-binding-%s", cfServiceBinding.Name),
236-
Namespace: cfServiceBinding.Namespace,
237-
Labels: map[string]string{
238-
ServiceBindingGUIDLabel: cfServiceBinding.Name,
239-
korifiv1alpha1.CFAppGUIDLabelKey: cfServiceBinding.Spec.AppRef.Name,
240-
ServiceCredentialBindingTypeLabel: "app",
241-
},
242-
},
243-
Spec: servicebindingv1beta1.ServiceBindingSpec{
244-
Type: cfServiceBinding.Annotations[korifiv1alpha1.ServiceInstanceTypeAnnotationKey],
245-
Workload: servicebindingv1beta1.ServiceBindingWorkloadReference{
246-
APIVersion: "apps/v1",
247-
Kind: "StatefulSet",
248-
Selector: &metav1.LabelSelector{
249-
MatchLabels: map[string]string{
250-
korifiv1alpha1.CFAppGUIDLabelKey: cfServiceBinding.Spec.AppRef.Name,
251-
},
252-
},
253-
},
254-
Service: servicebindingv1beta1.ServiceBindingServiceReference{
255-
APIVersion: "korifi.cloudfoundry.org/v1alpha1",
256-
Kind: "CFServiceBinding",
257-
Name: cfServiceBinding.Name,
258-
},
259-
},
260-
}
261-
}
262-
263-
func getSBServiceBindingName(cfServiceBinding *korifiv1alpha1.CFServiceBinding) string {
264-
if cfServiceBinding.Spec.DisplayName != nil {
265-
return *cfServiceBinding.Spec.DisplayName
266-
}
267-
268-
return cfServiceBinding.Status.Binding.Name
269-
}

controllers/controllers/services/bindings/controller_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77

88
korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1"
9-
"code.cloudfoundry.org/korifi/controllers/controllers/services/bindings"
109
"code.cloudfoundry.org/korifi/controllers/controllers/services/osbapi"
1110
"code.cloudfoundry.org/korifi/controllers/controllers/services/osbapi/fake"
1211
"code.cloudfoundry.org/korifi/model/services"
@@ -198,9 +197,9 @@ var _ = Describe("CFServiceBinding", func() {
198197
g.Expect(sbServiceBinding.Spec.Provider).To(BeEmpty())
199198

200199
g.Expect(sbServiceBinding.Labels).To(SatisfyAll(
201-
HaveKeyWithValue(bindings.ServiceBindingGUIDLabel, binding.Name),
200+
HaveKeyWithValue(korifiv1alpha1.ServiceBindingGUIDLabel, binding.Name),
202201
HaveKeyWithValue(korifiv1alpha1.CFAppGUIDLabelKey, cfAppGUID),
203-
HaveKeyWithValue(bindings.ServiceCredentialBindingTypeLabel, "app"),
202+
HaveKeyWithValue(korifiv1alpha1.ServiceCredentialBindingTypeLabel, "app"),
204203
))
205204

206205
g.Expect(sbServiceBinding.OwnerReferences).To(ConsistOf(MatchFields(IgnoreExtras, Fields{

controllers/controllers/services/bindings/managed/controller.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"time"
66

7+
"code.cloudfoundry.org/korifi/controllers/controllers/services/bindings/sbio"
78
"code.cloudfoundry.org/korifi/controllers/controllers/services/osbapi"
89
"code.cloudfoundry.org/korifi/tools"
910
"code.cloudfoundry.org/korifi/tools/k8s"
@@ -12,6 +13,7 @@ import (
1213
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1314

1415
"code.cloudfoundry.org/korifi/controllers/controllers/services/credentials"
16+
servicebindingv1beta1 "github.com/servicebinding/runtime/apis/v1beta1"
1517
corev1 "k8s.io/api/core/v1"
1618
"k8s.io/apimachinery/pkg/api/meta"
1719
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -37,13 +39,15 @@ func NewReconciler(k8sClient client.Client, brokerClientFactory osbapi.BrokerCli
3739
}
3840
}
3941

40-
func (r *ManagedBindingsReconciler) ReconcileResource(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding) (ctrl.Result, error) {
42+
func (r *ManagedBindingsReconciler) ReconcileResource(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding, cfServiceInstance *korifiv1alpha1.CFServiceInstance) (ctrl.Result, error) {
4143
log := logr.FromContextOrDiscard(ctx).WithName("reconcile-managed-service-binding")
4244

4345
if !cfServiceBinding.GetDeletionTimestamp().IsZero() {
4446
return r.finalizeCFServiceBinding(ctx, cfServiceBinding)
4547
}
4648

49+
cfServiceBinding.Labels = tools.SetMapValue(cfServiceBinding.Labels, korifiv1alpha1.PlanGUIDLabelKey, cfServiceInstance.Spec.PlanGUID)
50+
4751
assets, err := r.assets.GetServiceBindingAssets(ctx, cfServiceBinding)
4852
if err != nil {
4953
log.Error(err, "failed to get service binding assets")
@@ -74,6 +78,16 @@ func (r *ManagedBindingsReconciler) ReconcileResource(ctx context.Context, cfSer
7478
return ctrl.Result{}, err
7579
}
7680

81+
sbServiceBinding, err := r.reconcileSBServiceBinding(ctx, cfServiceBinding)
82+
if err != nil {
83+
log.Info("error creating/updating servicebinding.io servicebinding", "reason", err)
84+
return ctrl.Result{}, err
85+
}
86+
87+
if !sbio.IsSbServiceBindingReady(sbServiceBinding) {
88+
return ctrl.Result{}, k8s.NewNotReadyError().WithReason("ServiceBindingNotReady")
89+
}
90+
7791
return ctrl.Result{}, nil
7892
}
7993

@@ -254,6 +268,19 @@ func (r *ManagedBindingsReconciler) finalizeCFServiceBinding(
254268
return ctrl.Result{}, nil
255269
}
256270

271+
func (r *ManagedBindingsReconciler) reconcileSBServiceBinding(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding) (*servicebindingv1beta1.ServiceBinding, error) {
272+
sbServiceBinding := sbio.ToSBServiceBinding(cfServiceBinding, korifiv1alpha1.ManagedType)
273+
274+
_, err := controllerutil.CreateOrPatch(ctx, r.k8sClient, sbServiceBinding, func() error {
275+
return controllerutil.SetControllerReference(cfServiceBinding, sbServiceBinding, r.scheme)
276+
})
277+
if err != nil {
278+
return nil, err
279+
}
280+
281+
return sbServiceBinding, nil
282+
}
283+
257284
func isBindRequested(binding *korifiv1alpha1.CFServiceBinding) bool {
258285
return meta.IsStatusConditionTrue(binding.Status.Conditions, korifiv1alpha1.BindingRequestedCondition)
259286
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package sbio
2+
3+
import (
4+
"fmt"
5+
6+
"k8s.io/apimachinery/pkg/api/meta"
7+
8+
korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1"
9+
servicebindingv1beta1 "github.com/servicebinding/runtime/apis/v1beta1"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
)
12+
13+
func ToSBServiceBinding(cfServiceBinding *korifiv1alpha1.CFServiceBinding, bindingType string) *servicebindingv1beta1.ServiceBinding {
14+
return &servicebindingv1beta1.ServiceBinding{
15+
ObjectMeta: metav1.ObjectMeta{
16+
Name: fmt.Sprintf("cf-binding-%s", cfServiceBinding.Name),
17+
Namespace: cfServiceBinding.Namespace,
18+
Labels: map[string]string{
19+
korifiv1alpha1.ServiceBindingGUIDLabel: cfServiceBinding.Name,
20+
korifiv1alpha1.CFAppGUIDLabelKey: cfServiceBinding.Spec.AppRef.Name,
21+
korifiv1alpha1.ServiceCredentialBindingTypeLabel: "app",
22+
},
23+
},
24+
Spec: servicebindingv1beta1.ServiceBindingSpec{
25+
Name: getSBServiceBindingName(cfServiceBinding),
26+
Type: bindingType,
27+
Workload: servicebindingv1beta1.ServiceBindingWorkloadReference{
28+
APIVersion: "apps/v1",
29+
Kind: "StatefulSet",
30+
Selector: &metav1.LabelSelector{
31+
MatchLabels: map[string]string{
32+
korifiv1alpha1.CFAppGUIDLabelKey: cfServiceBinding.Spec.AppRef.Name,
33+
},
34+
},
35+
},
36+
Service: servicebindingv1beta1.ServiceBindingServiceReference{
37+
APIVersion: "korifi.cloudfoundry.org/v1alpha1",
38+
Kind: "CFServiceBinding",
39+
Name: cfServiceBinding.Name,
40+
},
41+
},
42+
}
43+
}
44+
45+
func getSBServiceBindingName(cfServiceBinding *korifiv1alpha1.CFServiceBinding) string {
46+
if cfServiceBinding.Spec.DisplayName != nil {
47+
return *cfServiceBinding.Spec.DisplayName
48+
}
49+
50+
return cfServiceBinding.Status.Binding.Name
51+
}
52+
53+
func IsSbServiceBindingReady(sbServiceBinding *servicebindingv1beta1.ServiceBinding) bool {
54+
if sbServiceBinding.Generation != sbServiceBinding.Status.ObservedGeneration {
55+
return false
56+
}
57+
58+
return meta.IsStatusConditionTrue(sbServiceBinding.Status.Conditions, korifiv1alpha1.StatusConditionReady)
59+
}

0 commit comments

Comments
 (0)