diff --git a/controllers/controllers/services/bindings/controller.go b/controllers/controllers/services/bindings/controller.go index f5a33a73a..4dc1d490b 100644 --- a/controllers/controllers/services/bindings/controller.go +++ b/controllers/controllers/services/bindings/controller.go @@ -21,6 +21,7 @@ import ( korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1" "code.cloudfoundry.org/korifi/controllers/controllers/shared" + "code.cloudfoundry.org/korifi/tools" "code.cloudfoundry.org/korifi/tools/k8s" "github.com/go-logr/logr" @@ -42,7 +43,7 @@ const ( ) type CredentialsReconciler interface { - ReconcileResource(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding) (ctrl.Result, error) + ReconcileResource(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding, cfServiceInstance *korifiv1alpha1.CFServiceInstance) (ctrl.Result, error) } type Reconciler struct { @@ -122,7 +123,7 @@ func (r *Reconciler) ReconcileResource(ctx context.Context, cfServiceBinding *ko return ctrl.Result{}, err } - r.setInstanceAnnotation(cfServiceInstance, cfServiceBinding) + setInstanceTypeAnnotation(cfServiceBinding, string(cfServiceInstance.Spec.Type)) err = controllerutil.SetOwnerReference(cfServiceInstance, cfServiceBinding, r.scheme) if err != nil { @@ -143,10 +144,10 @@ func (r *Reconciler) ReconcileResource(ctx context.Context, cfServiceBinding *ko func (r *Reconciler) reconcileByType(ctx context.Context, cfServiceInstance *korifiv1alpha1.CFServiceInstance, cfServiceBinding *korifiv1alpha1.CFServiceBinding) (ctrl.Result, error) { if cfServiceInstance.Spec.Type == korifiv1alpha1.UserProvidedType { - return r.upsiReconciler.ReconcileResource(ctx, cfServiceBinding) + return r.upsiReconciler.ReconcileResource(ctx, cfServiceBinding, cfServiceInstance) } - return r.managedReconciler.ReconcileResource(ctx, cfServiceBinding) + return r.managedReconciler.ReconcileResource(ctx, cfServiceBinding, cfServiceInstance) } func needsRequeue(res ctrl.Result, err error) bool { @@ -157,9 +158,6 @@ func needsRequeue(res ctrl.Result, err error) bool { return !res.IsZero() } -func (r *Reconciler) setInstanceAnnotation(cfServiceInstance *korifiv1alpha1.CFServiceInstance, cfServiceBinding *korifiv1alpha1.CFServiceBinding) { - if cfServiceBinding.Annotations == nil { - cfServiceBinding.Annotations = map[string]string{} - } - cfServiceBinding.Annotations[korifiv1alpha1.ServiceInstanceTypeAnnotationKey] = string(cfServiceInstance.Spec.Type) +func setInstanceTypeAnnotation(cfServiceBinding *korifiv1alpha1.CFServiceBinding, instanceType string) { + cfServiceBinding.Annotations = tools.SetMapValue(cfServiceBinding.Annotations, korifiv1alpha1.ServiceInstanceTypeAnnotationKey, instanceType) } diff --git a/controllers/controllers/services/bindings/managed/controller.go b/controllers/controllers/services/bindings/managed/controller.go index 99fe10253..0c0763813 100644 --- a/controllers/controllers/services/bindings/managed/controller.go +++ b/controllers/controllers/services/bindings/managed/controller.go @@ -2,9 +2,9 @@ package managed import ( "context" - "fmt" "time" + "code.cloudfoundry.org/korifi/controllers/controllers/services/bindings/sbio" "code.cloudfoundry.org/korifi/controllers/controllers/services/osbapi" "code.cloudfoundry.org/korifi/tools" "code.cloudfoundry.org/korifi/tools/k8s" @@ -18,7 +18,6 @@ import ( "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1" ctrl "sigs.k8s.io/controller-runtime" @@ -40,24 +39,14 @@ func NewReconciler(k8sClient client.Client, brokerClientFactory osbapi.BrokerCli } } -func (r *ManagedBindingsReconciler) ReconcileResource(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding) (ctrl.Result, error) { +func (r *ManagedBindingsReconciler) ReconcileResource(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding, cfServiceInstance *korifiv1alpha1.CFServiceInstance) (ctrl.Result, error) { log := logr.FromContextOrDiscard(ctx).WithName("reconcile-managed-service-binding") if !cfServiceBinding.GetDeletionTimestamp().IsZero() { return r.finalizeCFServiceBinding(ctx, cfServiceBinding) } - cfServiceInstance := new(korifiv1alpha1.CFServiceInstance) - err := r.k8sClient.Get(ctx, types.NamespacedName{Name: cfServiceBinding.Spec.Service.Name, Namespace: cfServiceBinding.Namespace}, cfServiceInstance) - if err != nil { - log.Info("service instance not found", "service-instance", cfServiceBinding.Spec.Service.Name, "error", err) - return ctrl.Result{}, err - } - - if cfServiceBinding.Labels == nil { - cfServiceBinding.Labels = map[string]string{} - } - cfServiceBinding.Labels[korifiv1alpha1.PlanGUIDLabelKey] = cfServiceInstance.Spec.PlanGUID + cfServiceBinding.Labels = tools.SetMapValue(cfServiceBinding.Labels, korifiv1alpha1.PlanGUIDLabelKey, cfServiceInstance.Spec.PlanGUID) assets, err := r.assets.GetServiceBindingAssets(ctx, cfServiceBinding) if err != nil { @@ -95,7 +84,7 @@ func (r *ManagedBindingsReconciler) ReconcileResource(ctx context.Context, cfSer return ctrl.Result{}, err } - if !isSbServiceBindingReady(sbServiceBinding) { + if !sbio.IsSbServiceBindingReady(sbServiceBinding) { return ctrl.Result{}, k8s.NewNotReadyError().WithReason("ServiceBindingNotReady") } @@ -280,11 +269,9 @@ func (r *ManagedBindingsReconciler) finalizeCFServiceBinding( } func (r *ManagedBindingsReconciler) reconcileSBServiceBinding(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding) (*servicebindingv1beta1.ServiceBinding, error) { - sbServiceBinding := r.toSBServiceBinding(cfServiceBinding) + sbServiceBinding := sbio.ToSBServiceBinding(cfServiceBinding, korifiv1alpha1.ManagedType) _, err := controllerutil.CreateOrPatch(ctx, r.k8sClient, sbServiceBinding, func() error { - sbServiceBinding.Spec.Name = getSBServiceBindingName(cfServiceBinding) - return controllerutil.SetControllerReference(cfServiceBinding, sbServiceBinding, r.scheme) }) if err != nil { @@ -294,58 +281,6 @@ func (r *ManagedBindingsReconciler) reconcileSBServiceBinding(ctx context.Contex return sbServiceBinding, nil } -func (r *ManagedBindingsReconciler) toSBServiceBinding(cfServiceBinding *korifiv1alpha1.CFServiceBinding) *servicebindingv1beta1.ServiceBinding { - return &servicebindingv1beta1.ServiceBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("cf-binding-%s", cfServiceBinding.Name), - Namespace: cfServiceBinding.Namespace, - Labels: map[string]string{ - korifiv1alpha1.ServiceBindingGUIDLabel: cfServiceBinding.Name, - korifiv1alpha1.CFAppGUIDLabelKey: cfServiceBinding.Spec.AppRef.Name, - korifiv1alpha1.ServiceCredentialBindingTypeLabel: "app", - }, - }, - Spec: servicebindingv1beta1.ServiceBindingSpec{ - Type: korifiv1alpha1.ManagedType, - Workload: servicebindingv1beta1.ServiceBindingWorkloadReference{ - APIVersion: "apps/v1", - Kind: "StatefulSet", - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - korifiv1alpha1.CFAppGUIDLabelKey: cfServiceBinding.Spec.AppRef.Name, - }, - }, - }, - Service: servicebindingv1beta1.ServiceBindingServiceReference{ - APIVersion: "korifi.cloudfoundry.org/v1alpha1", - Kind: "CFServiceBinding", - Name: cfServiceBinding.Name, - }, - }, - } -} - -func getSBServiceBindingName(cfServiceBinding *korifiv1alpha1.CFServiceBinding) string { - if cfServiceBinding.Spec.DisplayName != nil { - return *cfServiceBinding.Spec.DisplayName - } - - return cfServiceBinding.Status.Binding.Name -} - -func isSbServiceBindingReady(sbServiceBinding *servicebindingv1beta1.ServiceBinding) bool { - readyCondition := meta.FindStatusCondition(sbServiceBinding.Status.Conditions, "Ready") - if readyCondition == nil { - return false - } - - if readyCondition.Status != metav1.ConditionTrue { - return false - } - - return sbServiceBinding.Generation == sbServiceBinding.Status.ObservedGeneration -} - func isBindRequested(binding *korifiv1alpha1.CFServiceBinding) bool { return meta.IsStatusConditionTrue(binding.Status.Conditions, korifiv1alpha1.BindingRequestedCondition) } diff --git a/controllers/controllers/services/bindings/sbio/servicebinding.go b/controllers/controllers/services/bindings/sbio/servicebinding.go new file mode 100644 index 000000000..4c8f38218 --- /dev/null +++ b/controllers/controllers/services/bindings/sbio/servicebinding.go @@ -0,0 +1,64 @@ +package sbio + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/api/meta" + + korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1" + servicebindingv1beta1 "github.com/servicebinding/runtime/apis/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func ToSBServiceBinding(cfServiceBinding *korifiv1alpha1.CFServiceBinding, bindingType string) *servicebindingv1beta1.ServiceBinding { + return &servicebindingv1beta1.ServiceBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("cf-binding-%s", cfServiceBinding.Name), + Namespace: cfServiceBinding.Namespace, + Labels: map[string]string{ + korifiv1alpha1.ServiceBindingGUIDLabel: cfServiceBinding.Name, + korifiv1alpha1.CFAppGUIDLabelKey: cfServiceBinding.Spec.AppRef.Name, + korifiv1alpha1.ServiceCredentialBindingTypeLabel: "app", + }, + }, + Spec: servicebindingv1beta1.ServiceBindingSpec{ + Name: getSBServiceBindingName(cfServiceBinding), + Type: bindingType, + Workload: servicebindingv1beta1.ServiceBindingWorkloadReference{ + APIVersion: "apps/v1", + Kind: "StatefulSet", + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + korifiv1alpha1.CFAppGUIDLabelKey: cfServiceBinding.Spec.AppRef.Name, + }, + }, + }, + Service: servicebindingv1beta1.ServiceBindingServiceReference{ + APIVersion: "korifi.cloudfoundry.org/v1alpha1", + Kind: "CFServiceBinding", + Name: cfServiceBinding.Name, + }, + }, + } +} + +func getSBServiceBindingName(cfServiceBinding *korifiv1alpha1.CFServiceBinding) string { + if cfServiceBinding.Spec.DisplayName != nil { + return *cfServiceBinding.Spec.DisplayName + } + + return cfServiceBinding.Status.Binding.Name +} + +func IsSbServiceBindingReady(sbServiceBinding *servicebindingv1beta1.ServiceBinding) bool { + readyCondition := meta.FindStatusCondition(sbServiceBinding.Status.Conditions, "Ready") + if readyCondition == nil { + return false + } + + if readyCondition.Status != metav1.ConditionTrue { + return false + } + + return sbServiceBinding.Generation == sbServiceBinding.Status.ObservedGeneration +} diff --git a/controllers/controllers/services/bindings/upsi/controller.go b/controllers/controllers/services/bindings/upsi/controller.go index fb21bc3f4..65c45a04f 100644 --- a/controllers/controllers/services/bindings/upsi/controller.go +++ b/controllers/controllers/services/bindings/upsi/controller.go @@ -9,16 +9,15 @@ import ( "github.com/go-logr/logr" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1" + "code.cloudfoundry.org/korifi/controllers/controllers/services/bindings/sbio" "code.cloudfoundry.org/korifi/controllers/controllers/services/credentials" servicebindingv1beta1 "github.com/servicebinding/runtime/apis/v1beta1" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" ) @@ -35,7 +34,7 @@ func NewReconciler(k8sClient client.Client, scheme *runtime.Scheme) *UPSIBinding } } -func (r *UPSIBindingReconciler) ReconcileResource(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding) (ctrl.Result, error) { +func (r *UPSIBindingReconciler) ReconcileResource(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding, cfServiceInstance *korifiv1alpha1.CFServiceInstance) (ctrl.Result, error) { log := logr.FromContextOrDiscard(ctx) if !cfServiceBinding.GetDeletionTimestamp().IsZero() { @@ -46,13 +45,6 @@ func (r *UPSIBindingReconciler) ReconcileResource(ctx context.Context, cfService return ctrl.Result{}, nil } - cfServiceInstance := new(korifiv1alpha1.CFServiceInstance) - err := r.k8sClient.Get(ctx, types.NamespacedName{Name: cfServiceBinding.Spec.Service.Name, Namespace: cfServiceBinding.Namespace}, cfServiceInstance) - if err != nil { - log.Info("service instance not found", "service-instance", cfServiceBinding.Spec.Service.Name, "error", err) - return ctrl.Result{}, err - } - if cfServiceInstance.Status.Credentials.Name == "" { return ctrl.Result{}, k8s.NewNotReadyError(). WithReason("CredentialsSecretNotAvailable"). @@ -82,7 +74,7 @@ func (r *UPSIBindingReconciler) ReconcileResource(ctx context.Context, cfService return ctrl.Result{}, err } - if !isSbServiceBindingReady(sbServiceBinding) { + if !sbio.IsSbServiceBindingReady(sbServiceBinding) { return ctrl.Result{}, k8s.NewNotReadyError().WithReason("ServiceBindingNotReady") } @@ -132,11 +124,9 @@ func (r *UPSIBindingReconciler) reconcileCredentials(ctx context.Context, cfServ } func (r *UPSIBindingReconciler) reconcileSBServiceBinding(ctx context.Context, cfServiceBinding *korifiv1alpha1.CFServiceBinding, bindingSecret *corev1.Secret) (*servicebindingv1beta1.ServiceBinding, error) { - sbServiceBinding := r.toSBServiceBinding(cfServiceBinding) + sbServiceBinding := sbio.ToSBServiceBinding(cfServiceBinding, korifiv1alpha1.UserProvidedType) _, err := controllerutil.CreateOrPatch(ctx, r.k8sClient, sbServiceBinding, func() error { - sbServiceBinding.Spec.Name = getSBServiceBindingName(cfServiceBinding) - secretType, hasType := bindingSecret.Data["type"] if hasType && len(secretType) > 0 { sbServiceBinding.Spec.Type = string(secretType) @@ -154,55 +144,3 @@ func (r *UPSIBindingReconciler) reconcileSBServiceBinding(ctx context.Context, c return sbServiceBinding, nil } - -func (r *UPSIBindingReconciler) toSBServiceBinding(cfServiceBinding *korifiv1alpha1.CFServiceBinding) *servicebindingv1beta1.ServiceBinding { - return &servicebindingv1beta1.ServiceBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("cf-binding-%s", cfServiceBinding.Name), - Namespace: cfServiceBinding.Namespace, - Labels: map[string]string{ - korifiv1alpha1.ServiceBindingGUIDLabel: cfServiceBinding.Name, - korifiv1alpha1.CFAppGUIDLabelKey: cfServiceBinding.Spec.AppRef.Name, - korifiv1alpha1.ServiceCredentialBindingTypeLabel: "app", - }, - }, - Spec: servicebindingv1beta1.ServiceBindingSpec{ - Type: korifiv1alpha1.UserProvidedType, - Workload: servicebindingv1beta1.ServiceBindingWorkloadReference{ - APIVersion: "apps/v1", - Kind: "StatefulSet", - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - korifiv1alpha1.CFAppGUIDLabelKey: cfServiceBinding.Spec.AppRef.Name, - }, - }, - }, - Service: servicebindingv1beta1.ServiceBindingServiceReference{ - APIVersion: "korifi.cloudfoundry.org/v1alpha1", - Kind: "CFServiceBinding", - Name: cfServiceBinding.Name, - }, - }, - } -} - -func getSBServiceBindingName(cfServiceBinding *korifiv1alpha1.CFServiceBinding) string { - if cfServiceBinding.Spec.DisplayName != nil { - return *cfServiceBinding.Spec.DisplayName - } - - return cfServiceBinding.Status.Binding.Name -} - -func isSbServiceBindingReady(sbServiceBinding *servicebindingv1beta1.ServiceBinding) bool { - readyCondition := meta.FindStatusCondition(sbServiceBinding.Status.Conditions, "Ready") - if readyCondition == nil { - return false - } - - if readyCondition.Status != metav1.ConditionTrue { - return false - } - - return sbServiceBinding.Generation == sbServiceBinding.Status.ObservedGeneration -} diff --git a/tools/collections.go b/tools/collections.go index 76161b080..2276d7045 100644 --- a/tools/collections.go +++ b/tools/collections.go @@ -25,3 +25,12 @@ func NilOrEquals[E comparable](value *E, expectedValue E) bool { return expectedValue == *value } + +func SetMapValue[K comparable, V any](m map[K]V, key K, value V) map[K]V { + if m == nil { + m = map[K]V{} + } + m[key] = value + + return m +}