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

SAP / sap-btp-service-operator / 28498400538

01 Jul 2026 06:32AM UTC coverage: 77.745% (-0.3%) from 78.02%
28498400538

push

github

web-flow
bug fix - handle last operation response for delete operation is 404 (#652)

* bug fix - handle last operation response for delete operation is 404

* fix tests

* .

* handle binding 500 response

* log

15 of 32 new or added lines in 3 files covered. (46.88%)

4 existing lines in 1 file now uncovered.

2910 of 3743 relevant lines covered (77.75%)

0.88 hits per line

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

81.32
/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 {
1✔
192
                        return ctrl.Result{}, err
×
193
                }
×
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 {
2✔
295
                        log.Error(err, "unable to update ServiceBinding status")
1✔
296
                        return ctrl.Result{}, err
1✔
297
                }
1✔
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
                log.Info("status error is 'SM not found' for delete operation, return error and poll again")
1✔
418
                return utils.HandleServiceManagerError(ctx, r.Client, serviceBinding, serviceBinding.Status.OperationType, statusErr, true)
1✔
419
        }
420

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

594
func (r *ServiceBindingReconciler) getServiceInstanceForBinding(ctx context.Context, binding *v1.ServiceBinding) (*v1.ServiceInstance, error) {
1✔
595
        log := logutils.GetLogger(ctx)
1✔
596
        serviceInstance := &v1.ServiceInstance{}
1✔
597
        namespace := binding.Namespace
1✔
598
        if len(binding.Spec.ServiceInstanceNamespace) > 0 {
2✔
599
                namespace = binding.Spec.ServiceInstanceNamespace
1✔
600
        }
1✔
601
        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✔
602
        if err := r.Client.Get(ctx, types.NamespacedName{Name: binding.Spec.ServiceInstanceName, Namespace: namespace}, serviceInstance); err != nil {
2✔
603
                return serviceInstance, err
1✔
604
        }
1✔
605

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

963
        return metadata, nil
1✔
964
}
965

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1172
        return false
1✔
1173
}
1174

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

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

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

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

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

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

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

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

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

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

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

1245
        log.Info(fmt.Sprintf("trying to get from SM binding with id %s", bindingID))
1✔
1246
        if _, err := smClient.GetBindingByID(bindingID, nil); err != nil {
1✔
1247
                var smError *sm.ServiceManagerError
×
1248
                if ok := errors.As(err, &smError); ok {
×
1249
                        log.Error(smError, fmt.Sprintf("SM returned status code %d", smError.StatusCode))
×
1250
                        if smError.StatusCode == http.StatusNotFound {
×
1251
                                return false, nil
×
1252
                        }
×
1253
                }
1254
                return false, err
×
1255
        }
1256
        log.Info("binding found in SM")
1✔
1257
        return true, nil
1✔
1258
}
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