• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SAP / sap-btp-service-operator / 28439683328

30 Jun 2026 11:05AM UTC coverage: 77.712% (-0.3%) from 78.02%
28439683328

Pull #652

github

kerenlahav
handle binding 500 response
Pull Request #652: bug fix - handle last operation response for delete operation is 404

14 of 31 new or added lines in 3 files covered. (45.16%)

4 existing lines in 1 file now uncovered.

2908 of 3742 relevant lines covered (77.71%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.2
/controllers/servicebinding_controller.go
1
/*
2

3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package controllers
18

19
import (
20
        "context"
21
        "encoding/json"
22
        "net/http"
23
        "strings"
24
        "time"
25

26
        commonutils "github.com/SAP/sap-btp-service-operator/api/common/utils"
27
        "github.com/SAP/sap-btp-service-operator/internal/utils/logutils"
28
        "sigs.k8s.io/controller-runtime/pkg/reconcile"
29

30
        "github.com/pkg/errors"
31

32
        "github.com/SAP/sap-btp-service-operator/api/common"
33
        "github.com/SAP/sap-btp-service-operator/internal/config"
34
        "github.com/SAP/sap-btp-service-operator/internal/utils"
35
        "github.com/go-logr/logr"
36
        "k8s.io/apimachinery/pkg/runtime"
37
        "k8s.io/client-go/tools/events"
38

39
        "fmt"
40

41
        "k8s.io/client-go/util/workqueue"
42
        "sigs.k8s.io/controller-runtime/pkg/controller"
43

44
        v1 "github.com/SAP/sap-btp-service-operator/api/v1"
45

46
        "k8s.io/apimachinery/pkg/api/meta"
47
        "k8s.io/apimachinery/pkg/runtime/schema"
48

49
        "github.com/google/uuid"
50

51
        "github.com/SAP/sap-btp-service-operator/client/sm"
52

53
        smClientTypes "github.com/SAP/sap-btp-service-operator/client/sm/types"
54

55
        corev1 "k8s.io/api/core/v1"
56
        apierrors "k8s.io/apimachinery/pkg/api/errors"
57
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
58
        "k8s.io/apimachinery/pkg/types"
59
        ctrl "sigs.k8s.io/controller-runtime"
60
        "sigs.k8s.io/controller-runtime/pkg/client"
61
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
62
)
63

64
const (
65
        secretNameTakenErrorFormat    = "the specified secret name '%s' is already taken. Choose another name and try again"
66
        secretAlreadyOwnedErrorFormat = "secret %s belongs to another binding %s, choose a different name"
67
)
68

69
// ServiceBindingReconciler reconciles a ServiceBinding object
70
type ServiceBindingReconciler struct {
71
        client.Client
72
        Log         logr.Logger
73
        Scheme      *runtime.Scheme
74
        GetSMClient func(ctx context.Context, instance *v1.ServiceInstance) (sm.Client, error)
75
        Config      config.Config
76
        Recorder    events.EventRecorder
77
        Retries     *utils.RetryStore
78
}
79

80
// +kubebuilder:rbac:groups=services.cloud.sap.com,resources=servicebindings,verbs=get;list;watch;create;update;patch;delete
81
// +kubebuilder:rbac:groups=services.cloud.sap.com,resources=servicebindings/status,verbs=get;update;patch
82
// +kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete
83
// +kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;update;patch;delete
84
// +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;create;update
85

86
func (r *ServiceBindingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
1✔
87
        correlationID := uuid.New().String()
1✔
88
        retry := r.Retries.Get(req.NamespacedName)
1✔
89
        if retry != nil {
2✔
90
                correlationID = retry.CorrelationID
1✔
91
        }
1✔
92
        log := r.Log.WithValues("servicebinding", req.NamespacedName).WithValues("correlation_id", correlationID, req.Name, req.Namespace)
1✔
93
        if retry != nil && time.Now().Before(retry.NextRetry) {
2✔
94
                remaining := time.Until(retry.NextRetry)
1✔
95
                log.Info(fmt.Sprintf("skipping binding reconcile due to backoff. attempts=%d retryIn=%s", retry.Attempts, remaining))
1✔
96
                return ctrl.Result{RequeueAfter: remaining}, nil
1✔
97
        }
1✔
98
        ctx = context.WithValue(ctx, logutils.LogKey, log)
1✔
99
        ctx = context.WithValue(ctx, logutils.CorrelationIDKey, correlationID)
1✔
100

1✔
101
        serviceBinding := &v1.ServiceBinding{}
1✔
102
        if err := r.Client.Get(ctx, req.NamespacedName, serviceBinding); err != nil {
2✔
103
                if !apierrors.IsNotFound(err) {
1✔
104
                        log.Error(err, "unable to fetch ServiceBinding")
×
105
                }
×
106
                return ctrl.Result{}, client.IgnoreNotFound(err)
1✔
107
        }
108

109
        log.Info(fmt.Sprintf("*** staring reconcile of ServiceBinding %s/%s ***", serviceBinding.Namespace, serviceBinding.Name))
1✔
110

1✔
111
        serviceBinding = serviceBinding.DeepCopy()
1✔
112
        log.Info(fmt.Sprintf("Current generation is %v and observed is %v", serviceBinding.Generation, common.GetObservedGeneration(serviceBinding)))
1✔
113

1✔
114
        if len(serviceBinding.GetConditions()) == 0 {
2✔
115
                if err := utils.InitConditions(ctx, r.Client, serviceBinding); err != nil {
1✔
116
                        return ctrl.Result{}, err
×
117
                }
×
118
        }
119

120
        serviceInstance, instanceErr := r.getServiceInstanceForBinding(ctx, serviceBinding)
1✔
121
        if instanceErr != nil {
2✔
122
                if !apierrors.IsNotFound(instanceErr) {
1✔
123
                        log.Error(instanceErr, "failed to get service instance for binding")
×
124
                        return ctrl.Result{}, instanceErr
×
125
                }
×
126
                if !utils.IsMarkedForDeletion(serviceBinding.ObjectMeta) {
2✔
127
                        //instance is not found and binding is not marked for deletion
1✔
128
                        return r.handleInstanceForBindingNotFound(ctx, serviceBinding)
1✔
129
                }
1✔
130
                if len(serviceBinding.Status.BindingID) == 0 {
2✔
131
                        log.Info("service instance not found, binding is marked for deletion and has no binding id, removing finalizer if exists")
1✔
132
                        if err := r.deleteBindingSecret(ctx, serviceBinding); err != nil {
1✔
133
                                return ctrl.Result{}, err
×
134
                        }
×
135
                        return ctrl.Result{}, utils.RemoveFinalizer(ctx, r.Client, serviceBinding, common.FinalizerName)
1✔
136
                }
137
        }
138

139
        smClient, err := r.GetSMClient(ctx, serviceInstance)
1✔
140
        if err != nil {
1✔
141
                return utils.HandleOperationFailure(ctx, r.Client, serviceBinding, common.Unknown, err)
×
142
        }
×
143

144
        // poll only if delete sm operation is in progress or there is create/update ongoing operation and instance is not marked for deletion
145
        // if marked for deletion we should trigger the sm delete and ignore the current operation url
146
        if len(serviceBinding.Status.OperationURL) > 0 &&
1✔
147
                (serviceBinding.Status.OperationType == smClientTypes.DELETE || !utils.IsMarkedForDeletion(serviceBinding.ObjectMeta)) {
2✔
148
                return r.poll(ctx, smClient, serviceBinding)
1✔
149
        }
1✔
150

151
        if utils.IsMarkedForDeletion(serviceBinding.ObjectMeta) {
2✔
152
                return r.delete(ctx, smClient, serviceBinding)
1✔
153
        }
1✔
154

155
        if len(serviceBinding.Status.BindingID) > 0 {
2✔
156
                if bindingExist, err := isBindingExistInSM(smClient, serviceInstance, serviceBinding.Status.BindingID, log); err != nil {
1✔
157
                        log.Error(err, "failed to check if binding exist in sm due to unknown error")
×
158
                        return utils.HandleServiceManagerError(ctx, r.Client, serviceBinding, common.Unknown, err, false)
×
159
                } else if !bindingExist {
2✔
160
                        log.Info("binding not found in SM for this operator, updating status")
1✔
161
                        condition := metav1.Condition{
1✔
162
                                Type:               common.ConditionReady,
1✔
163
                                Status:             metav1.ConditionFalse,
1✔
164
                                ObservedGeneration: serviceBinding.Generation,
1✔
165
                                LastTransitionTime: metav1.NewTime(time.Now()),
1✔
166
                                Reason:             common.ResourceNotFound,
1✔
167
                                Message:            fmt.Sprintf(common.ResourceNotFoundMessageFormat, "binding", serviceBinding.Status.BindingID),
1✔
168
                        }
1✔
169
                        serviceBinding.Status.Conditions = []metav1.Condition{condition}
1✔
170
                        serviceBinding.Status.Ready = metav1.ConditionFalse
1✔
171
                        return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceBinding)
1✔
172
                }
1✔
173
        }
174

175
        if controllerutil.AddFinalizer(serviceBinding, common.FinalizerName) {
2✔
176
                log.Info(fmt.Sprintf("added finalizer '%s' to service binding", common.FinalizerName))
1✔
177
                if err := r.Client.Update(ctx, serviceBinding); err != nil {
1✔
178
                        return ctrl.Result{}, err
×
179
                }
×
180
        }
181

182
        if utils.IsMarkedForDeletion(serviceInstance.ObjectMeta) {
2✔
183
                log.Info(fmt.Sprintf("service instance name: %s namespace: %s is marked for deletion, unable to create binding", serviceInstance.Name, serviceInstance.Namespace))
1✔
184
                utils.SetBlockedCondition(ctx, "instance is in deletion process", serviceBinding)
1✔
185
                return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceBinding)
1✔
186
        }
1✔
187

188
        if !serviceInstanceReady(serviceInstance) {
2✔
189
                log.Info(fmt.Sprintf("service instance name: %s namespace: %s is not ready, unable to create binding", serviceInstance.Name, serviceInstance.Namespace))
1✔
190
                utils.SetBlockedCondition(ctx, "service instance is not ready", serviceBinding)
1✔
191
                if err := utils.UpdateStatus(ctx, r.Client, serviceBinding); err != nil {
2✔
192
                        return ctrl.Result{}, err
1✔
193
                }
1✔
194
                return ctrl.Result{}, errors.New("ServiceInstance is not ready")
1✔
195
        }
196

197
        // should rotate creds
198
        if meta.IsStatusConditionTrue(serviceBinding.Status.Conditions, common.ConditionCredRotationInProgress) {
2✔
199
                log.Info("rotating credentials")
1✔
200
                if shouldUpdateStatus, err := r.rotateCredentials(ctx, serviceBinding, serviceInstance); err != nil {
2✔
201
                        if !shouldUpdateStatus {
2✔
202
                                log.Error(err, "internal error occurred during cred rotation, requeuing binding")
1✔
203
                                return ctrl.Result{}, err
1✔
204
                        }
1✔
205
                        return utils.HandleCredRotationError(ctx, r.Client, serviceBinding, err)
×
206
                }
207
        }
208

209
        // is binding ready
210
        if meta.IsStatusConditionTrue(serviceBinding.Status.Conditions, common.ConditionReady) {
2✔
211
                if isStaleServiceBinding(serviceBinding) {
2✔
212
                        log.Info("binding is stale, handling")
1✔
213
                        return r.handleStaleServiceBinding(ctx, serviceBinding)
1✔
214
                }
1✔
215

216
                if initCredRotationIfRequired(serviceBinding) {
2✔
217
                        log.Info("cred rotation required, updating status")
1✔
218
                        return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceBinding)
1✔
219
                }
1✔
220

221
                log.Info("binding in final state, maintaining secret")
1✔
222
                return r.maintain(ctx, smClient, serviceBinding)
1✔
223
        }
224

225
        if serviceBinding.Status.BindingID == "" {
2✔
226
                if err := r.validateSecretNameIsAvailable(ctx, serviceBinding); err != nil {
2✔
227
                        log.Error(err, "secret validation failed")
1✔
228
                        utils.SetBlockedCondition(ctx, err.Error(), serviceBinding)
1✔
229
                        return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceBinding)
1✔
230
                }
1✔
231

232
                smBinding, err := r.getBindingForRecovery(ctx, smClient, serviceBinding)
1✔
233
                if err != nil {
1✔
234
                        log.Error(err, "failed to check binding recovery")
×
235
                        return utils.HandleServiceManagerError(ctx, r.Client, serviceBinding, smClientTypes.CREATE, err, true)
×
236
                }
×
237
                if smBinding != nil {
2✔
238
                        return r.recover(ctx, serviceBinding, smBinding)
1✔
239
                }
1✔
240

241
                return r.createBinding(ctx, smClient, serviceInstance, serviceBinding)
1✔
242
        }
243

244
        log.Info("nothing to do for this binding")
1✔
245
        return ctrl.Result{}, nil
1✔
246
}
247

248
func (r *ServiceBindingReconciler) SetupWithManager(mgr ctrl.Manager) error {
1✔
249

1✔
250
        return ctrl.NewControllerManagedBy(mgr).
1✔
251
                For(&v1.ServiceBinding{}).
1✔
252
                WithOptions(controller.Options{RateLimiter: workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](r.Config.RetryBaseDelay, r.Config.RetryMaxDelay)}).
1✔
253
                Complete(r)
1✔
254
}
1✔
255

256
func (r *ServiceBindingReconciler) createBinding(ctx context.Context, smClient sm.Client, serviceInstance *v1.ServiceInstance, serviceBinding *v1.ServiceBinding) (ctrl.Result, error) {
1✔
257
        log := logutils.GetLogger(ctx)
1✔
258
        log.Info("Creating smBinding in SM")
1✔
259
        serviceBinding.Status.InstanceID = serviceInstance.Status.InstanceID
1✔
260
        bindingParameters, _, err := utils.BuildSMRequestParameters(serviceBinding.Namespace, serviceBinding.Spec.Parameters, serviceBinding.Spec.ParametersFrom)
1✔
261
        if err != nil {
1✔
262
                log.Error(err, "failed to parse smBinding parameters")
×
263
                return utils.HandleOperationFailure(ctx, r.Client, serviceBinding, smClientTypes.CREATE, err)
×
264
        }
×
265

266
        smBinding, operationURL, bindErr := smClient.Bind(&smClientTypes.ServiceBinding{
1✔
267
                Name: serviceBinding.Spec.ExternalName,
1✔
268
                Labels: smClientTypes.Labels{
1✔
269
                        common.NamespaceLabel: []string{serviceBinding.Namespace},
1✔
270
                        common.K8sNameLabel:   []string{serviceBinding.Name},
1✔
271
                        common.ClusterIDLabel: []string{r.Config.ClusterID},
1✔
272
                },
1✔
273
                ServiceInstanceID: serviceInstance.Status.InstanceID,
1✔
274
                Parameters:        bindingParameters,
1✔
275
        }, nil, utils.BuildUserInfo(ctx, serviceBinding.Spec.UserInfo))
1✔
276

1✔
277
        if bindErr != nil {
2✔
278
                log.Error(err, "failed to create service binding", "serviceInstanceID", serviceInstance.Status.InstanceID)
1✔
279
                return utils.HandleServiceManagerError(ctx, r.Client, serviceBinding, smClientTypes.CREATE, bindErr, true)
1✔
280
        }
1✔
281

282
        if operationURL != "" {
2✔
283
                var bindingID string
1✔
284
                if bindingID = sm.ExtractBindingID(operationURL); len(bindingID) == 0 {
1✔
285
                        return utils.HandleOperationFailure(ctx, r.Client, serviceBinding, smClientTypes.CREATE, fmt.Errorf("failed to extract smBinding ID from operation URL %s", operationURL))
×
286
                }
×
287
                log.Info(fmt.Sprintf("binding is being created async, bindingID=%s", bindingID))
1✔
288
                serviceBinding.Status.BindingID = bindingID
1✔
289

1✔
290
                log.Info("Create smBinding request is async")
1✔
291
                serviceBinding.Status.OperationURL = operationURL
1✔
292
                serviceBinding.Status.OperationType = smClientTypes.CREATE
1✔
293
                utils.SetInProgressConditions(ctx, smClientTypes.CREATE, "", serviceBinding, false)
1✔
294
                if err := utils.UpdateStatus(ctx, r.Client, serviceBinding); err != nil {
1✔
295
                        log.Error(err, "unable to update ServiceBinding status")
×
296
                        return ctrl.Result{}, err
×
297
                }
×
298
                return ctrl.Result{RequeueAfter: r.Config.PollInterval}, nil
1✔
299
        }
300

301
        log.Info("Binding created successfully")
1✔
302

1✔
303
        if err := r.storeBindingSecret(ctx, serviceBinding, smBinding); err != nil {
2✔
304
                return r.handleSecretError(ctx, smClientTypes.CREATE, err, serviceBinding)
1✔
305
        }
1✔
306

307
        subaccountID := ""
1✔
308
        if len(smBinding.Labels["subaccount_id"]) > 0 {
1✔
309
                subaccountID = smBinding.Labels["subaccount_id"][0]
×
310
        }
×
311

312
        serviceBinding.Status.BindingID = smBinding.ID
1✔
313
        serviceBinding.Status.SubaccountID = subaccountID
1✔
314
        serviceBinding.Status.Ready = metav1.ConditionTrue
1✔
315
        r.Retries.Reset(types.NamespacedName{Name: serviceBinding.Name, Namespace: serviceBinding.Namespace})
1✔
316
        utils.SetSuccessConditions(smClientTypes.CREATE, serviceBinding, false)
1✔
317
        log.Info("Updating binding", "bindingID", smBinding.ID)
1✔
318

1✔
319
        return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceBinding)
1✔
320
}
321

322
func (r *ServiceBindingReconciler) delete(ctx context.Context, smClient sm.Client, serviceBinding *v1.ServiceBinding) (ctrl.Result, error) {
1✔
323
        log := logutils.GetLogger(ctx)
1✔
324
        log.Info(fmt.Sprintf("binding in delete phase, marked for deletion=%v, bindingID=%s, ready=%s", utils.IsMarkedForDeletion(serviceBinding.ObjectMeta), serviceBinding.Status.BindingID, serviceBinding.Status.Ready))
1✔
325
        if controllerutil.ContainsFinalizer(serviceBinding, common.FinalizerName) {
2✔
326
                if len(serviceBinding.Status.BindingID) == 0 {
2✔
327
                        log.Info("No binding id found validating binding does not exists in SM before removing finalizer")
1✔
328
                        smBinding, err := r.getBindingForRecovery(ctx, smClient, serviceBinding)
1✔
329
                        if err != nil {
1✔
330
                                return utils.HandleServiceManagerError(ctx, r.Client, serviceBinding, smClientTypes.DELETE, err, true)
×
331
                        }
×
332
                        if smBinding != nil {
2✔
333
                                log.Info("binding exists in SM continue with deletion")
1✔
334
                                serviceBinding.Status.BindingID = smBinding.ID
1✔
335
                                utils.SetInProgressConditions(ctx, smClientTypes.DELETE, "delete after recovery", serviceBinding, false)
1✔
336
                                return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceBinding)
1✔
337
                        }
1✔
338

339
                        // make sure there's no secret stored for the binding
340
                        if err := r.deleteBindingSecret(ctx, serviceBinding); err != nil {
1✔
341
                                return ctrl.Result{}, err
×
342
                        }
×
343

344
                        log.Info("Binding does not exists in SM, removing finalizer")
1✔
345
                        if err := utils.RemoveFinalizer(ctx, r.Client, serviceBinding, common.FinalizerName); err != nil {
2✔
346
                                return ctrl.Result{}, err
1✔
347
                        }
1✔
348
                        return ctrl.Result{}, nil
1✔
349
                }
350

351
                if len(serviceBinding.Status.OperationURL) > 0 && serviceBinding.Status.OperationType == smClientTypes.DELETE {
1✔
352
                        // ongoing delete operation - poll status from SM
×
353
                        return r.poll(ctx, smClient, serviceBinding)
×
354
                }
×
355

356
                log.Info(fmt.Sprintf("Deleting binding with id %v from SM, resourceMarkedForDeletions=%v", serviceBinding.Status.BindingID, utils.IsMarkedForDeletion(serviceBinding.ObjectMeta)))
1✔
357
                operationURL, unbindErr := smClient.Unbind(serviceBinding.Status.BindingID, nil, utils.BuildUserInfo(ctx, serviceBinding.Spec.UserInfo))
1✔
358
                if unbindErr != nil {
2✔
359
                        return utils.HandleServiceManagerError(ctx, r.Client, serviceBinding, smClientTypes.DELETE, unbindErr, true)
1✔
360
                }
1✔
361

362
                if operationURL != "" {
2✔
363
                        log.Info("Deleting binding async")
1✔
364
                        serviceBinding.Status.OperationURL = operationURL
1✔
365
                        serviceBinding.Status.OperationType = smClientTypes.DELETE
1✔
366
                        utils.SetInProgressConditions(ctx, smClientTypes.DELETE, "", serviceBinding, false)
1✔
367
                        if err := utils.UpdateStatus(ctx, r.Client, serviceBinding); err != nil {
1✔
368
                                return ctrl.Result{}, err
×
369
                        }
×
370
                        return ctrl.Result{RequeueAfter: r.Config.PollInterval}, nil
1✔
371
                }
372

373
                log.Info("reset binding id after successful sync delete operation")
1✔
374
                serviceBinding.Status.BindingID = ""
1✔
375
                if err := utils.UpdateStatus(ctx, r.Client, serviceBinding); err != nil {
1✔
376
                        log.Error(err, "unable to update ServiceBinding status after deletion")
×
377
                        return ctrl.Result{}, err
×
378
                }
×
379
                log.Info("Binding was deleted successfully")
1✔
380
                return r.deleteSecretAndRemoveFinalizer(ctx, serviceBinding)
1✔
381
        }
382
        return ctrl.Result{}, nil
×
383
}
384

385
func (r *ServiceBindingReconciler) poll(ctx context.Context, smClient sm.Client, serviceBinding *v1.ServiceBinding) (ctrl.Result, error) {
1✔
386
        log := logutils.GetLogger(ctx)
1✔
387
        log.Info(fmt.Sprintf("binding resource is in progress, found operation url %s", serviceBinding.Status.OperationURL))
1✔
388

1✔
389
        status, statusErr := smClient.Status(serviceBinding.Status.OperationURL, serviceBinding.Status.OperationType, nil)
1✔
390
        if statusErr != nil {
2✔
391
                log.Info(fmt.Sprintf("failed to fetch operation, got error from SM: %s", statusErr.Error()), "operationURL", serviceBinding.Status.OperationURL, "operationType", serviceBinding.Status.OperationType)
1✔
392
                if serviceBinding.Status.OperationType == smClientTypes.DELETE {
2✔
393
                        var smError *sm.ServiceManagerError
1✔
394
                        if ok := errors.As(statusErr, &smError); ok && smError.StatusCode == http.StatusInternalServerError {
1✔
NEW
395
                                log.Info("sm returned 500 for polling delete operation, checking if binding still exist")
×
NEW
396
                                if _, err := smClient.GetBindingByID(serviceBinding.Status.BindingID, nil); err != nil {
×
NEW
397
                                        if ok = errors.As(err, &smError); ok {
×
NEW
398
                                                log.Error(smError, fmt.Sprintf("SM returned status code %d", smError.StatusCode))
×
NEW
399
                                                if smError.StatusCode == http.StatusNotFound {
×
NEW
400
                                                        log.Info("binding does not exist in SM after deletion, deleting binding secret")
×
NEW
401
                                                        if _, err := r.deleteSecretAndRemoveFinalizer(ctx, serviceBinding); err != nil {
×
NEW
402
                                                                log.Error(err, "failed to delete binding secret and remove finalizer after delete operation completed")
×
NEW
403
                                                                return ctrl.Result{}, err
×
NEW
404
                                                        }
×
NEW
405
                                                        serviceBinding.Status.BindingID = ""
×
NEW
406
                                                        serviceBinding.Status.OperationURL = ""
×
NEW
407
                                                        serviceBinding.Status.OperationType = ""
×
NEW
408
                                                        r.Retries.Reset(types.NamespacedName{Name: serviceBinding.Name, Namespace: serviceBinding.Namespace})
×
NEW
409
                                                        return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceBinding)
×
410
                                                }
411
                                        }
NEW
412
                                        log.Info("status error is not 'SM not found', return error and poll again")
×
NEW
413
                                        return ctrl.Result{}, statusErr
×
414
                                }
415
                        }
416
                }
417
                return utils.HandleServiceManagerError(ctx, r.Client, serviceBinding, serviceBinding.Status.OperationType, statusErr, true)
1✔
418
        }
419

420
        if status == nil {
1✔
421
                return utils.HandleOperationFailure(ctx, r.Client, serviceBinding, serviceBinding.Status.OperationType, fmt.Errorf("failed to get last operation status of %s", serviceBinding.Name))
×
422
        }
×
423
        switch status.State {
1✔
424
        case smClientTypes.INPROGRESS:
1✔
425
                fallthrough
1✔
426
        case smClientTypes.PENDING:
1✔
427
                log.Info(fmt.Sprintf("%s is still in progress", serviceBinding.Status.OperationURL))
1✔
428
                if len(status.Description) != 0 {
1✔
429
                        utils.SetInProgressConditions(ctx, status.Type, status.Description, serviceBinding, true)
×
430
                        if err := utils.UpdateStatus(ctx, r.Client, serviceBinding); err != nil {
×
431
                                log.Error(err, "unable to update ServiceBinding polling description")
×
432
                                return ctrl.Result{}, err
×
433
                        }
×
434
                }
435
                return ctrl.Result{RequeueAfter: r.Config.PollInterval}, nil
1✔
436
        case smClientTypes.FAILED:
1✔
437
                log.Info(fmt.Sprintf("%s ended with failure", serviceBinding.Status.OperationURL))
1✔
438
                utils.SetFailureConditions(status.Type, status.Description, serviceBinding, true)
1✔
439
                if serviceBinding.Status.OperationType == smClientTypes.CREATE ||
1✔
440
                        (serviceBinding.Status.OperationType == smClientTypes.DELETE && !utils.IsMarkedForDeletion(serviceBinding.ObjectMeta)) {
2✔
441
                        errMsg := getErrorMsgFromLastOperation(status)
1✔
442
                        log.Info(fmt.Sprintf("async binding failed for binding id %s, error: %s", serviceBinding.Status.BindingID, errMsg))
1✔
443
                        key := types.NamespacedName{Namespace: serviceBinding.GetNamespace(), Name: serviceBinding.GetName()}
1✔
444
                        newState := r.Retries.RegisterFailure(key, logutils.GetCorrelationID(ctx))
1✔
445
                        log.Info(fmt.Sprintf("async binding failed. attempts=%d nextRetry=%s currrent error=%s\n", newState.Attempts, newState.NextRetry.Format(time.RFC3339), errMsg))
1✔
446
                        return r.handleFailedAsyncBinding(ctx, smClient, serviceBinding)
1✔
447
                }
1✔
448
                serviceBinding.Status.OperationURL = ""
1✔
449
                serviceBinding.Status.OperationType = ""
1✔
450
                if err := utils.UpdateStatus(ctx, r.Client, serviceBinding); err != nil {
1✔
451
                        log.Error(err, "unable to update ServiceBinding status")
×
452
                        return ctrl.Result{}, err
×
453
                }
×
454
                errMsg := fmt.Sprintf("Async binding %s operation failed", serviceBinding.Status.OperationType)
1✔
455
                if status.Errors != nil {
1✔
456
                        errMsg = fmt.Sprintf("Async unbind operation failed, errors: %s", string(status.Errors))
×
457
                }
×
458
                return ctrl.Result{}, errors.New(errMsg)
1✔
459
        case smClientTypes.SUCCEEDED:
1✔
460
                log.Info(fmt.Sprintf("%s completed successfully", serviceBinding.Status.OperationURL))
1✔
461
                switch serviceBinding.Status.OperationType {
1✔
462
                case smClientTypes.CREATE:
1✔
463
                        smBinding, err := smClient.GetBindingByID(serviceBinding.Status.BindingID, nil)
1✔
464
                        if err != nil || smBinding == nil {
2✔
465
                                log.Error(err, fmt.Sprintf("binding %s succeeded but could not fetch it from SM", serviceBinding.Status.BindingID))
1✔
466
                                return ctrl.Result{}, err
1✔
467
                        }
1✔
468
                        if len(smBinding.Labels["subaccount_id"]) > 0 {
1✔
469
                                serviceBinding.Status.SubaccountID = smBinding.Labels["subaccount_id"][0]
×
470
                        }
×
471

472
                        if err := r.storeBindingSecret(ctx, serviceBinding, smBinding); err != nil {
1✔
473
                                return r.handleSecretError(ctx, smClientTypes.CREATE, err, serviceBinding)
×
474
                        }
×
475
                        utils.SetSuccessConditions(status.Type, serviceBinding, false)
1✔
476
                case smClientTypes.DELETE:
1✔
477
                        _, err := r.deleteSecretAndRemoveFinalizer(ctx, serviceBinding)
1✔
478
                        if err != nil {
1✔
479
                                log.Error(err, "failed to delete binding secret and remove finalizer after delete operation completed")
×
480
                                return ctrl.Result{}, err
×
481
                        }
×
482

483
                        log.Info("reset binding id after successful async delete operation")
1✔
484
                        serviceBinding.Status.BindingID = ""
1✔
485
                }
486
        }
487

488
        log.Info(fmt.Sprintf("finished polling operation %s '%s'", serviceBinding.Status.OperationType, serviceBinding.Status.OperationURL))
1✔
489
        serviceBinding.Status.OperationURL = ""
1✔
490
        serviceBinding.Status.OperationType = ""
1✔
491
        r.Retries.Reset(types.NamespacedName{Name: serviceBinding.Name, Namespace: serviceBinding.Namespace})
1✔
492

1✔
493
        return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceBinding)
1✔
494
}
495

496
func (r *ServiceBindingReconciler) handleFailedAsyncBinding(ctx context.Context, smClient sm.Client, serviceBinding *v1.ServiceBinding) (ctrl.Result, error) {
1✔
497
        log := logutils.GetLogger(ctx)
1✔
498
        log.Info(fmt.Sprintf("handleFailedAsyncBinding deleting binding id %s that failed from SM", serviceBinding.Status.BindingID))
1✔
499
        operationURL, unbindErr := smClient.Unbind(serviceBinding.Status.BindingID, nil, utils.BuildUserInfo(ctx, serviceBinding.Spec.UserInfo))
1✔
500
        if unbindErr != nil {
1✔
501
                log.Error(unbindErr, fmt.Sprintf("handleFailedAsyncBinding unbind binding with id %s failed", serviceBinding.Status.BindingID))
×
502
                return utils.HandleServiceManagerError(ctx, r.Client, serviceBinding, smClientTypes.DELETE, unbindErr, false)
×
503
        }
×
504

505
        if operationURL != "" {
1✔
506
                log.Info(fmt.Sprintf("handleFailedAsyncBinding unbind is async, operation url %s", operationURL))
×
507
                serviceBinding.Status.OperationURL = operationURL
×
508
                serviceBinding.Status.OperationType = smClientTypes.DELETE
×
509
                return ctrl.Result{RequeueAfter: r.Config.PollInterval}, utils.UpdateStatus(ctx, r.Client, serviceBinding)
×
510
        }
×
511

512
        log.Info(fmt.Sprintf("handleFailedAsyncBinding binding %s deleted successfully", serviceBinding.Status.BindingID))
1✔
513
        serviceBinding.Status.OperationURL = ""
1✔
514
        serviceBinding.Status.OperationType = ""
1✔
515
        serviceBinding.Status.BindingID = ""
1✔
516
        if err := r.Client.Status().Update(ctx, serviceBinding); err != nil {
1✔
517
                log.Error(err, "handleFailedAsyncBinding failed to update service binding status after deletion")
×
518
                return ctrl.Result{}, err
×
519
        }
×
520
        return ctrl.Result{RequeueAfter: r.Config.PollInterval}, nil
1✔
521
}
522

523
func (r *ServiceBindingReconciler) getBindingForRecovery(ctx context.Context, smClient sm.Client, serviceBinding *v1.ServiceBinding) (*smClientTypes.ServiceBinding, error) {
1✔
524
        log := logutils.GetLogger(ctx)
1✔
525
        nameQuery := fmt.Sprintf("name eq '%s'", serviceBinding.Spec.ExternalName)
1✔
526
        clusterIDQuery := fmt.Sprintf("context/clusterid eq '%s'", r.Config.ClusterID)
1✔
527
        namespaceQuery := fmt.Sprintf("context/namespace eq '%s'", serviceBinding.Namespace)
1✔
528
        k8sNameQuery := fmt.Sprintf("%s eq '%s'", common.K8sNameLabel, serviceBinding.Name)
1✔
529
        parameters := sm.Parameters{
1✔
530
                FieldQuery:    []string{nameQuery, clusterIDQuery, namespaceQuery},
1✔
531
                LabelQuery:    []string{k8sNameQuery},
1✔
532
                GeneralParams: []string{"attach_last_operations=true"},
1✔
533
        }
1✔
534
        log.Info(fmt.Sprintf("binding recovery query params: %s, %s, %s, %s", nameQuery, clusterIDQuery, namespaceQuery, k8sNameQuery))
1✔
535

1✔
536
        bindings, err := smClient.ListBindings(&parameters)
1✔
537
        if err != nil {
1✔
538
                log.Error(err, "failed to list bindings in SM")
×
539
                return nil, err
×
540
        }
×
541
        if bindings != nil {
2✔
542
                log.Info(fmt.Sprintf("found %d bindings", len(bindings.ServiceBindings)))
1✔
543
                if len(bindings.ServiceBindings) == 1 {
2✔
544
                        return &bindings.ServiceBindings[0], nil
1✔
545
                }
1✔
546
        }
547
        return nil, nil
1✔
548
}
549

550
func (r *ServiceBindingReconciler) maintain(ctx context.Context, smClient sm.Client, binding *v1.ServiceBinding) (ctrl.Result, error) {
1✔
551
        log := logutils.GetLogger(ctx)
1✔
552
        if err := r.maintainSecret(ctx, smClient, binding); err != nil {
2✔
553
                log.Error(err, "failed to maintain secret")
1✔
554
                return r.handleSecretError(ctx, smClientTypes.UPDATE, err, binding)
1✔
555
        }
1✔
556

557
        log.Info("maintain finished successfully")
1✔
558
        return ctrl.Result{}, nil
1✔
559
}
560

561
func (r *ServiceBindingReconciler) maintainSecret(ctx context.Context, smClient sm.Client, serviceBinding *v1.ServiceBinding) error {
1✔
562
        log := logutils.GetLogger(ctx)
1✔
563
        if common.GetObservedGeneration(serviceBinding) == serviceBinding.Generation {
2✔
564
                log.Info("observed generation is up to date, checking if secret exists")
1✔
565
                if _, err := r.getSecret(ctx, serviceBinding.Namespace, serviceBinding.Spec.SecretName); err == nil {
2✔
566
                        log.Info("secret exists, no need to maintain secret")
1✔
567
                        return nil
1✔
568
                }
1✔
569

570
                log.Info("binding's secret was not found")
1✔
571
                r.Recorder.Eventf(serviceBinding, nil, corev1.EventTypeWarning, "SecretDeleted", "SecretDeleted", "SecretDeleted")
1✔
572
        }
573

574
        log.Info("maintaining binding's secret")
1✔
575
        smBinding, err := smClient.GetBindingByID(serviceBinding.Status.BindingID, nil)
1✔
576
        if err != nil {
1✔
577
                log.Error(err, "failed to get binding for update secret")
×
578
                return err
×
579
        }
×
580
        if smBinding != nil {
2✔
581
                if smBinding.Credentials != nil {
2✔
582
                        if err = r.storeBindingSecret(ctx, serviceBinding, smBinding); err != nil {
2✔
583
                                return err
1✔
584
                        }
1✔
585
                        log.Info("Updating binding", "bindingID", smBinding.ID)
1✔
586
                        utils.SetSuccessConditions(smClientTypes.UPDATE, serviceBinding, false)
1✔
587
                }
588
        }
589

590
        return utils.UpdateStatus(ctx, r.Client, serviceBinding)
1✔
591
}
592

593
func (r *ServiceBindingReconciler) getServiceInstanceForBinding(ctx context.Context, binding *v1.ServiceBinding) (*v1.ServiceInstance, error) {
1✔
594
        log := logutils.GetLogger(ctx)
1✔
595
        serviceInstance := &v1.ServiceInstance{}
1✔
596
        namespace := binding.Namespace
1✔
597
        if len(binding.Spec.ServiceInstanceNamespace) > 0 {
2✔
598
                namespace = binding.Spec.ServiceInstanceNamespace
1✔
599
        }
1✔
600
        log.Info(fmt.Sprintf("getting service instance named %s in namespace %s for binding %s in namespace %s", binding.Spec.ServiceInstanceName, namespace, binding.Name, binding.Namespace))
1✔
601
        if err := r.Client.Get(ctx, types.NamespacedName{Name: binding.Spec.ServiceInstanceName, Namespace: namespace}, serviceInstance); err != nil {
2✔
602
                return serviceInstance, err
1✔
603
        }
1✔
604

605
        return serviceInstance.DeepCopy(), nil
1✔
606
}
607

608
func (r *ServiceBindingReconciler) resyncBindingStatus(ctx context.Context, k8sBinding *v1.ServiceBinding, smBinding *smClientTypes.ServiceBinding) {
1✔
609
        k8sBinding.Status.BindingID = smBinding.ID
1✔
610
        k8sBinding.Status.InstanceID = smBinding.ServiceInstanceID
1✔
611
        k8sBinding.Status.OperationURL = ""
1✔
612
        k8sBinding.Status.OperationType = ""
1✔
613

1✔
614
        bindingStatus := smClientTypes.SUCCEEDED
1✔
615
        operationType := smClientTypes.CREATE
1✔
616
        description := ""
1✔
617
        if smBinding.LastOperation != nil {
2✔
618
                bindingStatus = smBinding.LastOperation.State
1✔
619
                operationType = smBinding.LastOperation.Type
1✔
620
                description = smBinding.LastOperation.Description
1✔
621
        } else if !smBinding.Ready {
3✔
622
                bindingStatus = smClientTypes.FAILED
1✔
623
        }
1✔
624
        switch bindingStatus {
1✔
625
        case smClientTypes.PENDING:
×
626
                fallthrough
×
627
        case smClientTypes.INPROGRESS:
1✔
628
                k8sBinding.Status.OperationURL = sm.BuildOperationURL(smBinding.LastOperation.ID, smBinding.ID, smClientTypes.ServiceBindingsURL)
1✔
629
                k8sBinding.Status.OperationType = smBinding.LastOperation.Type
1✔
630
                utils.SetInProgressConditions(ctx, smBinding.LastOperation.Type, smBinding.LastOperation.Description, k8sBinding, false)
1✔
631
        case smClientTypes.SUCCEEDED:
1✔
632
                utils.SetSuccessConditions(operationType, k8sBinding, false)
1✔
633
        case smClientTypes.FAILED:
1✔
634
                utils.SetFailureConditions(operationType, description, k8sBinding, false)
1✔
635
        }
636
}
637

638
func (r *ServiceBindingReconciler) storeBindingSecret(ctx context.Context, k8sBinding *v1.ServiceBinding, smBinding *smClientTypes.ServiceBinding) error {
1✔
639
        log := logutils.GetLogger(ctx)
1✔
640
        logger := log.WithValues("bindingName", k8sBinding.Name, "secretName", k8sBinding.Spec.SecretName)
1✔
641

1✔
642
        var secret *corev1.Secret
1✔
643
        var err error
1✔
644

1✔
645
        if k8sBinding.Spec.SecretTemplate != "" {
2✔
646
                secret, err = r.createBindingSecretFromSecretTemplate(ctx, k8sBinding, smBinding)
1✔
647
        } else {
2✔
648
                secret, err = r.createBindingSecret(ctx, k8sBinding, smBinding)
1✔
649
        }
1✔
650

651
        if err != nil {
2✔
652
                return err
1✔
653
        }
1✔
654
        if err = controllerutil.SetControllerReference(k8sBinding, secret, r.Scheme); err != nil {
1✔
655
                logger.Error(err, "Failed to set secret owner")
×
656
                return err
×
657
        }
×
658

659
        if secret.Labels == nil {
1✔
660
                secret.Labels = map[string]string{}
×
661
        }
×
662
        secret.Labels[common.ManagedByBTPOperatorLabel] = "true"
1✔
663
        if len(k8sBinding.Labels) > 0 && len(k8sBinding.Labels[common.StaleBindingIDLabel]) > 0 {
2✔
664
                secret.Labels[common.StaleBindingIDLabel] = k8sBinding.Labels[common.StaleBindingIDLabel]
1✔
665
        }
1✔
666

667
        if secret.Annotations == nil {
1✔
668
                secret.Annotations = map[string]string{}
×
669
        }
×
670
        secret.Annotations["binding"] = k8sBinding.Name
1✔
671

1✔
672
        return r.createOrUpdateBindingSecret(ctx, k8sBinding, secret)
1✔
673
}
674

675
func (r *ServiceBindingReconciler) createBindingSecret(ctx context.Context, k8sBinding *v1.ServiceBinding, smBinding *smClientTypes.ServiceBinding) (*corev1.Secret, error) {
1✔
676
        credentialsMap, err := r.getSecretDefaultData(ctx, k8sBinding, smBinding)
1✔
677
        if err != nil {
2✔
678
                return nil, err
1✔
679
        }
1✔
680

681
        secret := &corev1.Secret{
1✔
682
                ObjectMeta: metav1.ObjectMeta{
1✔
683
                        Name:        k8sBinding.Spec.SecretName,
1✔
684
                        Annotations: map[string]string{"binding": k8sBinding.Name},
1✔
685
                        Labels:      map[string]string{common.ManagedByBTPOperatorLabel: "true"},
1✔
686
                        Namespace:   k8sBinding.Namespace,
1✔
687
                },
1✔
688
                Data: credentialsMap,
1✔
689
        }
1✔
690
        return secret, nil
1✔
691
}
692

693
func (r *ServiceBindingReconciler) getSecretDefaultData(ctx context.Context, k8sBinding *v1.ServiceBinding, smBinding *smClientTypes.ServiceBinding) (map[string][]byte, error) {
1✔
694
        log := logutils.GetLogger(ctx).WithValues("bindingName", k8sBinding.Name, "secretName", k8sBinding.Spec.SecretName)
1✔
695

1✔
696
        var credentialsMap map[string][]byte
1✔
697
        var credentialProperties []utils.SecretMetadataProperty
1✔
698

1✔
699
        if len(smBinding.Credentials) == 0 {
2✔
700
                log.Info("Binding credentials are empty")
1✔
701
                credentialsMap = make(map[string][]byte)
1✔
702
        } else if k8sBinding.Spec.SecretKey != nil {
3✔
703
                credentialsMap = map[string][]byte{
1✔
704
                        *k8sBinding.Spec.SecretKey: smBinding.Credentials,
1✔
705
                }
1✔
706
                credentialProperties = []utils.SecretMetadataProperty{
1✔
707
                        {
1✔
708
                                Name:      *k8sBinding.Spec.SecretKey,
1✔
709
                                Format:    string(utils.JSON),
1✔
710
                                Container: true,
1✔
711
                        },
1✔
712
                }
1✔
713
        } else {
2✔
714
                var err error
1✔
715
                credentialsMap, credentialProperties, err = utils.NormalizeCredentials(smBinding.Credentials)
1✔
716
                if err != nil {
2✔
717
                        log.Error(err, "Failed to store binding secret")
1✔
718
                        return nil, fmt.Errorf("failed to create secret. Error: %v", err.Error())
1✔
719
                }
1✔
720
        }
721

722
        metaDataProperties, err := r.addInstanceInfo(ctx, k8sBinding, credentialsMap)
1✔
723
        if err != nil {
1✔
724
                log.Error(err, "failed to enrich binding with service instance info")
×
725
        }
×
726

727
        if k8sBinding.Spec.SecretRootKey != nil {
2✔
728
                var err error
1✔
729
                credentialsMap, err = singleKeyMap(credentialsMap, *k8sBinding.Spec.SecretRootKey)
1✔
730
                if err != nil {
1✔
731
                        return nil, err
×
732
                }
×
733
        } else {
1✔
734
                metadata := map[string][]utils.SecretMetadataProperty{
1✔
735
                        "metaDataProperties":   metaDataProperties,
1✔
736
                        "credentialProperties": credentialProperties,
1✔
737
                }
1✔
738
                metadataByte, err := json.Marshal(metadata)
1✔
739
                if err != nil {
1✔
740
                        log.Error(err, "failed to enrich binding with metadata")
×
741
                } else {
1✔
742
                        credentialsMap[".metadata"] = metadataByte
1✔
743
                }
1✔
744
        }
745
        return credentialsMap, nil
1✔
746
}
747

748
func (r *ServiceBindingReconciler) createBindingSecretFromSecretTemplate(ctx context.Context, k8sBinding *v1.ServiceBinding, smBinding *smClientTypes.ServiceBinding) (*corev1.Secret, error) {
1✔
749
        log := logutils.GetLogger(ctx)
1✔
750
        logger := log.WithValues("bindingName", k8sBinding.Name, "secretName", k8sBinding.Spec.SecretName)
1✔
751

1✔
752
        logger.Info("Create Object using SecretTemplate from ServiceBinding Specs")
1✔
753
        inputSmCredentials := smBinding.Credentials
1✔
754
        smBindingCredentials := make(map[string]interface{})
1✔
755
        if inputSmCredentials != nil {
2✔
756
                err := json.Unmarshal(inputSmCredentials, &smBindingCredentials)
1✔
757
                if err != nil {
1✔
758
                        logger.Error(err, "failed to unmarshal given service binding credentials")
×
759
                        return nil, errors.Wrap(err, "failed to unmarshal given service binding credentials")
×
760
                }
×
761
        }
762

763
        instanceInfos, err := r.getInstanceInfo(ctx, k8sBinding)
1✔
764
        if err != nil {
1✔
765
                logger.Error(err, "failed to addInstanceInfo")
×
766
                return nil, errors.Wrap(err, "failed to add service instance info")
×
767
        }
×
768

769
        parameters := commonutils.GetSecretDataForTemplate(smBindingCredentials, instanceInfos)
1✔
770
        templateName := fmt.Sprintf("%s/%s", k8sBinding.Namespace, k8sBinding.Name)
1✔
771
        secret, err := commonutils.CreateSecretFromTemplate(templateName, k8sBinding.Spec.SecretTemplate, "missingkey=error", parameters)
1✔
772
        if err != nil {
2✔
773
                logger.Error(err, "failed to create secret from template")
1✔
774
                return nil, errors.Wrap(err, "failed to create secret from template")
1✔
775
        }
1✔
776
        secret.SetNamespace(k8sBinding.Namespace)
1✔
777
        secret.SetName(k8sBinding.Spec.SecretName)
1✔
778
        if secret.Labels == nil {
1✔
779
                secret.Labels = map[string]string{}
×
780
        }
×
781
        secret.Labels[common.ManagedByBTPOperatorLabel] = "true"
1✔
782

1✔
783
        // if no data provided use the default data
1✔
784
        if len(secret.Data) == 0 && len(secret.StringData) == 0 {
2✔
785
                credentialsMap, err := r.getSecretDefaultData(ctx, k8sBinding, smBinding)
1✔
786
                if err != nil {
1✔
787
                        return nil, err
×
788
                }
×
789
                secret.Data = credentialsMap
1✔
790
        }
791
        return secret, nil
1✔
792
}
793

794
func (r *ServiceBindingReconciler) createOrUpdateBindingSecret(ctx context.Context, binding *v1.ServiceBinding, secret *corev1.Secret) error {
1✔
795
        log := logutils.GetLogger(ctx)
1✔
796
        dbSecret := &corev1.Secret{}
1✔
797
        create := false
1✔
798
        if err := r.Client.Get(ctx, types.NamespacedName{Name: binding.Spec.SecretName, Namespace: binding.Namespace}, dbSecret); err != nil {
2✔
799
                if !apierrors.IsNotFound(err) {
1✔
800
                        return err
×
801
                }
×
802
                create = true
1✔
803
        }
804

805
        if create {
2✔
806
                log.Info("Creating binding secret", "name", secret.Name)
1✔
807
                if err := r.Client.Create(ctx, secret); err != nil {
1✔
808
                        if !apierrors.IsAlreadyExists(err) {
×
809
                                return err
×
810
                        }
×
811
                        return nil
×
812
                }
813
                r.Recorder.Eventf(binding, nil, corev1.EventTypeNormal, "SecretCreated", "SecretCreated", "SecretCreated")
1✔
814
                return nil
1✔
815
        }
816

817
        log.Info("Updating existing binding secret", "name", secret.Name)
1✔
818
        dbSecret.Data = secret.Data
1✔
819
        dbSecret.StringData = secret.StringData
1✔
820
        dbSecret.Labels = secret.Labels
1✔
821
        dbSecret.Annotations = secret.Annotations
1✔
822
        return r.Client.Update(ctx, dbSecret)
1✔
823
}
824

825
func (r *ServiceBindingReconciler) deleteBindingSecret(ctx context.Context, binding *v1.ServiceBinding) error {
1✔
826
        log := logutils.GetLogger(ctx)
1✔
827
        log.Info("Deleting binding secret")
1✔
828
        bindingSecret := &corev1.Secret{}
1✔
829
        if err := r.Client.Get(ctx, types.NamespacedName{
1✔
830
                Namespace: binding.Namespace,
1✔
831
                Name:      binding.Spec.SecretName,
1✔
832
        }, bindingSecret); err != nil {
2✔
833
                if !apierrors.IsNotFound(err) {
1✔
834
                        log.Error(err, "unable to fetch binding secret")
×
835
                        return err
×
836
                }
×
837

838
                // secret not found, nothing more to do
839
                log.Info("secret was deleted successfully")
1✔
840
                return nil
1✔
841
        }
842
        bindingSecret = bindingSecret.DeepCopy()
1✔
843

1✔
844
        if err := r.Client.Delete(ctx, bindingSecret); err != nil {
1✔
845
                log.Error(err, "Failed to delete binding secret")
×
846
                return err
×
847
        }
×
848

849
        log.Info("secret was deleted successfully")
1✔
850
        return nil
1✔
851
}
852

853
func (r *ServiceBindingReconciler) deleteSecretAndRemoveFinalizer(ctx context.Context, serviceBinding *v1.ServiceBinding) (ctrl.Result, error) {
1✔
854
        // delete binding secret if exist
1✔
855
        if err := r.deleteBindingSecret(ctx, serviceBinding); err != nil {
1✔
856
                return ctrl.Result{}, err
×
857
        }
×
858

859
        return ctrl.Result{}, utils.RemoveFinalizer(ctx, r.Client, serviceBinding, common.FinalizerName)
1✔
860
}
861

862
func (r *ServiceBindingReconciler) getSecret(ctx context.Context, namespace string, name string) (*corev1.Secret, error) {
1✔
863
        secret := &corev1.Secret{}
1✔
864
        err := utils.GetSecretWithFallback(ctx, types.NamespacedName{Namespace: namespace, Name: name}, secret)
1✔
865
        return secret, err
1✔
866
}
1✔
867

868
func (r *ServiceBindingReconciler) validateSecretNameIsAvailable(ctx context.Context, binding *v1.ServiceBinding) error {
1✔
869
        currentSecret, err := r.getSecret(ctx, binding.Namespace, binding.Spec.SecretName)
1✔
870
        if err != nil {
2✔
871
                return client.IgnoreNotFound(err)
1✔
872
        }
1✔
873

874
        if metav1.IsControlledBy(currentSecret, binding) {
2✔
875
                return nil
1✔
876
        }
1✔
877

878
        ownerRef := metav1.GetControllerOf(currentSecret)
1✔
879
        if ownerRef != nil {
2✔
880
                owner, err := schema.ParseGroupVersion(ownerRef.APIVersion)
1✔
881
                if err != nil {
1✔
882
                        return err
×
883
                }
×
884

885
                if owner.Group == binding.GroupVersionKind().Group && ownerRef.Kind == binding.Kind {
2✔
886
                        return fmt.Errorf(secretAlreadyOwnedErrorFormat, binding.Spec.SecretName, ownerRef.Name)
1✔
887
                }
1✔
888
        }
889

890
        return fmt.Errorf(secretNameTakenErrorFormat, binding.Spec.SecretName)
1✔
891
}
892

893
func (r *ServiceBindingReconciler) handleSecretError(ctx context.Context, op smClientTypes.OperationCategory, err error, binding *v1.ServiceBinding) (ctrl.Result, error) {
1✔
894
        log := logutils.GetLogger(ctx)
1✔
895
        log.Error(err, fmt.Sprintf("failed to store secret %s for binding %s", binding.Spec.SecretName, binding.Name))
1✔
896
        return utils.HandleOperationFailure(ctx, r.Client, binding, op, err)
1✔
897
}
1✔
898

899
func (r *ServiceBindingReconciler) getInstanceInfo(ctx context.Context, binding *v1.ServiceBinding) (map[string]string, error) {
1✔
900
        instance, err := r.getServiceInstanceForBinding(ctx, binding)
1✔
901
        if err != nil {
1✔
902
                return nil, err
×
903
        }
×
904
        instanceInfos := make(map[string]string)
1✔
905
        instanceInfos["instance_name"] = string(getInstanceNameForSecretCredentials(instance))
1✔
906
        instanceInfos["instance_guid"] = instance.Status.InstanceID
1✔
907
        instanceInfos["plan"] = instance.Spec.ServicePlanName
1✔
908
        instanceInfos["label"] = instance.Spec.ServiceOfferingName
1✔
909
        instanceInfos["type"] = instance.Spec.ServiceOfferingName
1✔
910
        if len(instance.Status.Tags) > 0 || len(instance.Spec.CustomTags) > 0 {
2✔
911
                tags := mergeInstanceTags(instance.Status.Tags, instance.Spec.CustomTags)
1✔
912
                instanceInfos["tags"] = strings.Join(tags, ",")
1✔
913
        }
1✔
914
        return instanceInfos, nil
1✔
915
}
916

917
func (r *ServiceBindingReconciler) addInstanceInfo(ctx context.Context, binding *v1.ServiceBinding, credentialsMap map[string][]byte) ([]utils.SecretMetadataProperty, error) {
1✔
918
        instance, err := r.getServiceInstanceForBinding(ctx, binding)
1✔
919
        if err != nil {
1✔
920
                return nil, err
×
921
        }
×
922

923
        credentialsMap["instance_name"] = getInstanceNameForSecretCredentials(instance)
1✔
924
        credentialsMap["instance_guid"] = []byte(instance.Status.InstanceID)
1✔
925
        credentialsMap["plan"] = []byte(instance.Spec.ServicePlanName)
1✔
926
        credentialsMap["label"] = []byte(instance.Spec.ServiceOfferingName)
1✔
927
        credentialsMap["type"] = []byte(instance.Spec.ServiceOfferingName)
1✔
928
        if len(instance.Status.Tags) > 0 || len(instance.Spec.CustomTags) > 0 {
2✔
929
                tagsBytes, err := json.Marshal(mergeInstanceTags(instance.Status.Tags, instance.Spec.CustomTags))
1✔
930
                if err != nil {
1✔
931
                        return nil, err
×
932
                }
×
933
                credentialsMap["tags"] = tagsBytes
1✔
934
        }
935

936
        metadata := []utils.SecretMetadataProperty{
1✔
937
                {
1✔
938
                        Name:   "instance_name",
1✔
939
                        Format: string(utils.TEXT),
1✔
940
                },
1✔
941
                {
1✔
942
                        Name:   "instance_guid",
1✔
943
                        Format: string(utils.TEXT),
1✔
944
                },
1✔
945
                {
1✔
946
                        Name:   "plan",
1✔
947
                        Format: string(utils.TEXT),
1✔
948
                },
1✔
949
                {
1✔
950
                        Name:   "label",
1✔
951
                        Format: string(utils.TEXT),
1✔
952
                },
1✔
953
                {
1✔
954
                        Name:   "type",
1✔
955
                        Format: string(utils.TEXT),
1✔
956
                },
1✔
957
        }
1✔
958
        if _, ok := credentialsMap["tags"]; ok {
2✔
959
                metadata = append(metadata, utils.SecretMetadataProperty{Name: "tags", Format: string(utils.JSON)})
1✔
960
        }
1✔
961

962
        return metadata, nil
1✔
963
}
964

965
func (r *ServiceBindingReconciler) rotateCredentials(ctx context.Context, binding *v1.ServiceBinding, serviceInstance *v1.ServiceInstance) (bool, error) {
1✔
966
        log := logutils.GetLogger(ctx)
1✔
967
        if err := r.removeForceRotateAnnotationIfNeeded(ctx, binding, log); err != nil {
1✔
968
                log.Info("Credentials rotation - failed to delete force rotate annotation")
×
969
                return false, err
×
970
        }
×
971

972
        credInProgressCondition := meta.FindStatusCondition(binding.GetConditions(), common.ConditionCredRotationInProgress)
1✔
973
        if credInProgressCondition.Reason == common.CredRotating {
2✔
974
                if len(binding.Status.BindingID) > 0 && binding.Status.Ready == metav1.ConditionTrue {
2✔
975
                        log.Info("Credentials rotation - finished successfully")
1✔
976
                        now := metav1.NewTime(time.Now())
1✔
977
                        binding.Status.LastCredentialsRotationTime = &now
1✔
978
                        return false, r.stopRotation(ctx, binding)
1✔
979
                }
1✔
980
                log.Info("Credentials rotation - waiting to finish")
1✔
981
                return false, nil
1✔
982
        }
983

984
        if len(binding.Status.BindingID) == 0 {
1✔
985
                log.Info("Credentials rotation - no binding id found nothing to do")
×
986
                return false, r.stopRotation(ctx, binding)
×
987
        }
×
988

989
        bindings := &v1.ServiceBindingList{}
1✔
990
        err := r.Client.List(ctx, bindings, client.MatchingLabels{common.StaleBindingIDLabel: binding.Status.BindingID}, client.InNamespace(binding.Namespace))
1✔
991
        if err != nil {
1✔
992
                return false, err
×
993
        }
×
994

995
        if len(bindings.Items) == 0 {
2✔
996
                // create the backup binding
1✔
997
                smClient, err := r.GetSMClient(ctx, serviceInstance)
1✔
998
                if err != nil {
1✔
999
                        return false, err
×
1000
                }
×
1001

1002
                // rename current binding
1003
                suffix := "-" + utils.RandStringRunes(6)
1✔
1004
                log.Info("Credentials rotation - renaming binding to old in SM", "current", binding.Spec.ExternalName)
1✔
1005
                if _, errRenaming := smClient.RenameBinding(binding.Status.BindingID, binding.Spec.ExternalName+suffix, binding.Name+suffix); errRenaming != nil {
1✔
1006
                        log.Error(errRenaming, "Credentials rotation - failed renaming binding to old in SM", "binding", binding.Spec.ExternalName)
×
1007
                        return true, errRenaming
×
1008
                }
×
1009

1010
                log.Info("Credentials rotation - backing up old binding in K8S", "name", binding.Name+suffix)
1✔
1011
                if err := r.createOldBinding(ctx, suffix, binding); err != nil {
1✔
1012
                        log.Error(err, "Credentials rotation - failed to back up old binding in K8S")
×
1013
                        return true, err
×
1014
                }
×
1015
        }
1016

1017
        log.Info("reset binding id after successful rotation")
1✔
1018
        binding.Status.BindingID = ""
1✔
1019
        binding.Status.Ready = metav1.ConditionFalse
1✔
1020
        utils.SetInProgressConditions(ctx, smClientTypes.CREATE, "rotating binding credentials", binding, false)
1✔
1021
        utils.SetCredRotationInProgressConditions(common.CredRotating, "", binding)
1✔
1022
        return false, utils.UpdateStatus(ctx, r.Client, binding)
1✔
1023
}
1024

1025
func (r *ServiceBindingReconciler) removeForceRotateAnnotationIfNeeded(ctx context.Context, binding *v1.ServiceBinding, log logr.Logger) error {
1✔
1026
        if binding.Annotations != nil {
2✔
1027
                if _, ok := binding.Annotations[common.ForceRotateAnnotation]; ok {
2✔
1028
                        log.Info("Credentials rotation - deleting force rotate annotation")
1✔
1029
                        delete(binding.Annotations, common.ForceRotateAnnotation)
1✔
1030
                        return r.Client.Update(ctx, binding)
1✔
1031
                }
1✔
1032
        }
1033
        return nil
1✔
1034
}
1035

1036
func (r *ServiceBindingReconciler) stopRotation(ctx context.Context, binding *v1.ServiceBinding) error {
1✔
1037
        conditions := binding.GetConditions()
1✔
1038
        meta.RemoveStatusCondition(&conditions, common.ConditionCredRotationInProgress)
1✔
1039
        binding.Status.Conditions = conditions
1✔
1040
        return utils.UpdateStatus(ctx, r.Client, binding)
1✔
1041
}
1✔
1042

1043
func (r *ServiceBindingReconciler) createOldBinding(ctx context.Context, suffix string, binding *v1.ServiceBinding) error {
1✔
1044
        oldBinding := newBindingObject(binding.Name+suffix, binding.Namespace)
1✔
1045
        err := controllerutil.SetControllerReference(binding, oldBinding, r.Scheme)
1✔
1046
        if err != nil {
1✔
1047
                return err
×
1048
        }
×
1049
        oldBinding.Labels = map[string]string{
1✔
1050
                common.StaleBindingIDLabel:         binding.Status.BindingID,
1✔
1051
                common.StaleBindingRotationOfLabel: truncateString(binding.Name, 63),
1✔
1052
        }
1✔
1053
        oldBinding.Annotations = map[string]string{
1✔
1054
                common.StaleBindingOrigBindingNameAnnotation: binding.Name,
1✔
1055
        }
1✔
1056
        spec := binding.Spec.DeepCopy()
1✔
1057
        spec.CredRotationPolicy.Enabled = false
1✔
1058
        spec.SecretName = spec.SecretName + suffix
1✔
1059
        spec.ExternalName = spec.ExternalName + suffix
1✔
1060
        oldBinding.Spec = *spec
1✔
1061
        return r.Client.Create(ctx, oldBinding)
1✔
1062
}
1063

1064
func (r *ServiceBindingReconciler) handleStaleServiceBinding(ctx context.Context, serviceBinding *v1.ServiceBinding) (ctrl.Result, error) {
1✔
1065
        log := logutils.GetLogger(ctx)
1✔
1066
        originalBindingName, ok := serviceBinding.Annotations[common.StaleBindingOrigBindingNameAnnotation]
1✔
1067
        if !ok {
2✔
1068
                //if the user removed the "OrigBindingName" annotation and rotationOf label not exist as well
1✔
1069
                //the stale binding should be deleted otherwise it will remain forever
1✔
1070
                if originalBindingName, ok = serviceBinding.Labels[common.StaleBindingRotationOfLabel]; !ok {
2✔
1071
                        log.Info("missing rotationOf label/annotation, unable to fetch original binding, deleting stale")
1✔
1072
                        return ctrl.Result{}, r.Client.Delete(ctx, serviceBinding)
1✔
1073
                }
1✔
1074
        }
1075
        origBinding := &v1.ServiceBinding{}
1✔
1076
        if err := r.Client.Get(ctx, types.NamespacedName{Namespace: serviceBinding.Namespace, Name: originalBindingName}, origBinding); err != nil {
1✔
UNCOV
1077
                if apierrors.IsNotFound(err) {
×
UNCOV
1078
                        log.Info("original binding not found, deleting stale binding")
×
UNCOV
1079
                        return ctrl.Result{}, r.Client.Delete(ctx, serviceBinding)
×
UNCOV
1080
                }
×
1081
                return ctrl.Result{}, err
×
1082
        }
1083
        if meta.IsStatusConditionTrue(origBinding.Status.Conditions, common.ConditionReady) {
2✔
1084
                return ctrl.Result{}, r.Client.Delete(ctx, serviceBinding)
1✔
1085
        }
1✔
1086

1087
        log.Info("not deleting stale binding since original binding is not ready")
1✔
1088
        if !meta.IsStatusConditionPresentAndEqual(serviceBinding.Status.Conditions, common.ConditionPendingTermination, metav1.ConditionTrue) {
2✔
1089
                pendingTerminationCondition := metav1.Condition{
1✔
1090
                        Type:               common.ConditionPendingTermination,
1✔
1091
                        Status:             metav1.ConditionTrue,
1✔
1092
                        Reason:             common.ConditionPendingTermination,
1✔
1093
                        Message:            "waiting for new credentials to be ready",
1✔
1094
                        ObservedGeneration: serviceBinding.GetGeneration(),
1✔
1095
                }
1✔
1096
                meta.SetStatusCondition(&serviceBinding.Status.Conditions, pendingTerminationCondition)
1✔
1097
                return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceBinding)
1✔
1098
        }
1✔
1099
        return ctrl.Result{}, nil
1✔
1100
}
1101

1102
func (r *ServiceBindingReconciler) recover(ctx context.Context, serviceBinding *v1.ServiceBinding, smBinding *smClientTypes.ServiceBinding) (ctrl.Result, error) {
1✔
1103
        log := logutils.GetLogger(ctx)
1✔
1104
        log.Info(fmt.Sprintf("found existing smBinding in SM with id %s, updating status", smBinding.ID))
1✔
1105

1✔
1106
        if smBinding.Credentials != nil {
2✔
1107
                if err := r.storeBindingSecret(ctx, serviceBinding, smBinding); err != nil {
1✔
1108
                        operationType := smClientTypes.CREATE
×
1109
                        if smBinding.LastOperation != nil {
×
1110
                                operationType = smBinding.LastOperation.Type
×
1111
                        }
×
1112
                        return r.handleSecretError(ctx, operationType, err, serviceBinding)
×
1113
                }
1114
        }
1115
        r.resyncBindingStatus(ctx, serviceBinding, smBinding)
1✔
1116

1✔
1117
        return ctrl.Result{}, utils.UpdateStatus(ctx, r.Client, serviceBinding)
1✔
1118
}
1119

1120
func (r *ServiceBindingReconciler) handleInstanceForBindingNotFound(ctx context.Context, serviceBinding *v1.ServiceBinding) (ctrl.Result, error) {
1✔
1121
        log := logutils.GetLogger(ctx)
1✔
1122
        instanceNamespace := serviceBinding.Namespace
1✔
1123
        if len(serviceBinding.Spec.ServiceInstanceNamespace) > 0 {
1✔
1124
                instanceNamespace = serviceBinding.Spec.ServiceInstanceNamespace
×
1125
        }
×
1126
        errMsg := fmt.Sprintf("couldn't find the service instance '%s' in namespace '%s'", serviceBinding.Spec.ServiceInstanceName, instanceNamespace)
1✔
1127
        log.Info(errMsg)
1✔
1128
        utils.SetBlockedCondition(ctx, errMsg, serviceBinding)
1✔
1129
        if updateErr := utils.UpdateStatus(ctx, r.Client, serviceBinding); updateErr != nil {
2✔
1130
                return ctrl.Result{}, updateErr
1✔
1131
        }
1✔
1132
        return ctrl.Result{}, fmt.Errorf("instance %s not found in namespace %s", serviceBinding.Spec.ServiceInstanceName, instanceNamespace)
1✔
1133
}
1134

1135
func isStaleServiceBinding(binding *v1.ServiceBinding) bool {
1✔
1136
        if utils.IsMarkedForDeletion(binding.ObjectMeta) {
1✔
1137
                return false
×
1138
        }
×
1139

1140
        if binding.Labels != nil {
2✔
1141
                if _, ok := binding.Labels[common.StaleBindingIDLabel]; ok {
2✔
1142
                        if binding.Spec.CredRotationPolicy != nil {
2✔
1143
                                keepFor, _ := time.ParseDuration(binding.Spec.CredRotationPolicy.RotatedBindingTTL)
1✔
1144
                                if time.Since(binding.CreationTimestamp.Time) > keepFor {
2✔
1145
                                        return true
1✔
1146
                                }
1✔
1147
                        }
1148
                }
1149
        }
1150
        return false
1✔
1151
}
1152

1153
func initCredRotationIfRequired(binding *v1.ServiceBinding) bool {
1✔
1154
        if utils.IsFailed(binding) || !credRotationEnabled(binding) {
2✔
1155
                return false
1✔
1156
        }
1✔
1157
        _, forceRotate := binding.Annotations[common.ForceRotateAnnotation]
1✔
1158

1✔
1159
        lastCredentialRotationTime := binding.Status.LastCredentialsRotationTime
1✔
1160
        if lastCredentialRotationTime == nil {
2✔
1161
                ts := metav1.NewTime(binding.CreationTimestamp.Time)
1✔
1162
                lastCredentialRotationTime = &ts
1✔
1163
        }
1✔
1164

1165
        rotationInterval, _ := time.ParseDuration(binding.Spec.CredRotationPolicy.RotationFrequency)
1✔
1166
        if time.Since(lastCredentialRotationTime.Time) > rotationInterval || forceRotate {
2✔
1167
                utils.SetCredRotationInProgressConditions(common.CredPreparing, "", binding)
1✔
1168
                return true
1✔
1169
        }
1✔
1170

1171
        return false
1✔
1172
}
1173

1174
func credRotationEnabled(binding *v1.ServiceBinding) bool {
1✔
1175
        return binding.Spec.CredRotationPolicy != nil && binding.Spec.CredRotationPolicy.Enabled
1✔
1176
}
1✔
1177

1178
func mergeInstanceTags(offeringTags, customTags []string) []string {
1✔
1179
        var tags []string
1✔
1180

1✔
1181
        for _, tag := range append(offeringTags, customTags...) {
2✔
1182
                if !utils.SliceContains(tags, tag) {
2✔
1183
                        tags = append(tags, tag)
1✔
1184
                }
1✔
1185
        }
1186
        return tags
1✔
1187
}
1188

1189
func newBindingObject(name, namespace string) *v1.ServiceBinding {
1✔
1190
        return &v1.ServiceBinding{
1✔
1191
                TypeMeta: metav1.TypeMeta{
1✔
1192
                        APIVersion: v1.GroupVersion.String(),
1✔
1193
                        Kind:       "ServiceBinding",
1✔
1194
                },
1✔
1195
                ObjectMeta: metav1.ObjectMeta{
1✔
1196
                        Name:      name,
1✔
1197
                        Namespace: namespace,
1✔
1198
                },
1✔
1199
        }
1✔
1200
}
1✔
1201

1202
func serviceInstanceReady(instance *v1.ServiceInstance) bool {
1✔
1203
        return instance.Status.Ready == metav1.ConditionTrue
1✔
1204
}
1✔
1205

1206
func getInstanceNameForSecretCredentials(instance *v1.ServiceInstance) []byte {
1✔
1207
        if useMetaName, ok := instance.Annotations[common.UseInstanceMetadataNameInSecret]; ok && useMetaName == "true" {
2✔
1208
                return []byte(instance.Name)
1✔
1209
        }
1✔
1210
        return []byte(instance.Spec.ExternalName)
1✔
1211
}
1212

1213
func singleKeyMap(credentialsMap map[string][]byte, key string) (map[string][]byte, error) {
1✔
1214
        stringCredentialsMap := make(map[string]string)
1✔
1215
        for k, v := range credentialsMap {
2✔
1216
                stringCredentialsMap[k] = string(v)
1✔
1217
        }
1✔
1218

1219
        credBytes, err := json.Marshal(stringCredentialsMap)
1✔
1220
        if err != nil {
1✔
1221
                return nil, err
×
1222
        }
×
1223

1224
        return map[string][]byte{
1✔
1225
                key: credBytes,
1✔
1226
        }, nil
1✔
1227
}
1228

1229
func truncateString(str string, length int) string {
1✔
1230
        if len(str) > length {
2✔
1231
                return str[:length]
1✔
1232
        }
1✔
1233
        return str
1✔
1234
}
1235

1236
func isBindingExistInSM(smClient sm.Client, instance *v1.ServiceInstance, bindingID string, log logr.Logger) (bool, error) {
1✔
1237
        log.Info("checking if k8s instance status is NotFound")
1✔
1238
        instanceReadyCond := meta.FindStatusCondition(instance.GetConditions(), common.ConditionReady)
1✔
1239
        if instanceReadyCond != nil && instanceReadyCond.Reason == common.ResourceNotFound {
2✔
1240
                log.Info("k8s instance is in NotFound state -> invalid binding")
1✔
1241
                return false, nil
1✔
1242
        }
1✔
1243

1244
        log.Info(fmt.Sprintf("trying to get from SM binding with id %s", bindingID))
1✔
1245
        if _, err := smClient.GetBindingByID(bindingID, nil); err != nil {
1✔
1246
                var smError *sm.ServiceManagerError
×
1247
                if ok := errors.As(err, &smError); ok {
×
1248
                        log.Error(smError, fmt.Sprintf("SM returned status code %d", smError.StatusCode))
×
1249
                        if smError.StatusCode == http.StatusNotFound {
×
1250
                                return false, nil
×
1251
                        }
×
1252
                }
1253
                return false, err
×
1254
        }
1255
        log.Info("binding found in SM")
1✔
1256
        return true, nil
1✔
1257
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc