Skip to content

Commit f9019e0

Browse files
emilsapkerenlahav
andauthored
retry failed operations
Co-authored-by: kerenlahav <[email protected]> Co-authored-by: I501080 <[email protected]>
1 parent 98ebcc0 commit f9019e0

12 files changed

+224
-291
lines changed

client/sm/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ func (client *serviceManagerClient) getPlanInfo(planID string, serviceName strin
504504
}
505505

506506
if len(planID) > 0 {
507-
err = fmt.Errorf("the provided plan ID '%s' doesn't match the provided offering name '%s' and plan name '%s", planID, serviceName, planName)
507+
err = fmt.Errorf("the provided plan ID '%s' doesn't match the provided offering name '%s' and plan name '%s'", planID, serviceName, planName)
508508
} else {
509509
err = fmt.Errorf("ambiguity error: found more than one resource that matches the provided offering name '%s' and plan name '%s'. Please provide servicePlanID", serviceName, planName)
510510
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: services.cloud.sap.com/v1
2+
kind: ServiceInstance
3+
metadata:
4+
name: xsuaa-reference-instance
5+
spec:
6+
serviceOfferingName: xsuaa
7+
servicePlanName: reference-instance
8+
parameters:
9+
referenced_instance_id: xxx

config/samples/services_v1_servicebinding_cred_rotation.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apiVersion: services.cloud.sap.com/v1
22
kind: ServiceBinding
33
metadata:
44
name: sample-binding-rotate
5-
labels:
5+
annotations:
66
"services.cloud.sap.com/forceRotate": "true"
77
spec:
88
serviceInstanceName: sample-instance-1

controllers/servicebinding_controller.go

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,13 @@ func (r *ServiceBindingReconciler) Reconcile(ctx context.Context, req ctrl.Reque
196196

197197
smClient, err := r.GetSMClient(ctx, serviceInstance)
198198
if err != nil {
199-
return utils.MarkAsTransientError(ctx, r.Client, common.Unknown, err, serviceBinding)
199+
return utils.HandleOperationFailure(ctx, r.Client, serviceBinding, common.Unknown, err)
200200
}
201201

202202
smBinding, err := r.getBindingForRecovery(ctx, smClient, serviceBinding)
203203
if err != nil {
204204
log.Error(err, "failed to check binding recovery")
205-
return utils.MarkAsTransientError(ctx, r.Client, smClientTypes.CREATE, err, serviceBinding)
205+
return utils.HandleServiceManagerError(ctx, r.Client, serviceBinding, smClientTypes.CREATE, err)
206206
}
207207
if smBinding != nil {
208208
return r.recover(ctx, serviceBinding, smBinding)
@@ -230,7 +230,7 @@ func (r *ServiceBindingReconciler) createBinding(ctx context.Context, smClient s
230230
bindingParameters, _, err := utils.BuildSMRequestParameters(serviceBinding.Namespace, serviceBinding.Spec.Parameters, serviceBinding.Spec.ParametersFrom)
231231
if err != nil {
232232
log.Error(err, "failed to parse smBinding parameters")
233-
return utils.MarkAsNonTransientError(ctx, r.Client, smClientTypes.CREATE, err, serviceBinding)
233+
return utils.HandleOperationFailure(ctx, r.Client, serviceBinding, smClientTypes.CREATE, err)
234234
}
235235

236236
smBinding, operationURL, bindErr := smClient.Bind(&smClientTypes.ServiceBinding{
@@ -246,13 +246,13 @@ func (r *ServiceBindingReconciler) createBinding(ctx context.Context, smClient s
246246

247247
if bindErr != nil {
248248
log.Error(err, "failed to create service binding", "serviceInstanceID", serviceInstance.Status.InstanceID)
249-
return utils.HandleError(ctx, r.Client, smClientTypes.CREATE, bindErr, serviceBinding)
249+
return utils.HandleServiceManagerError(ctx, r.Client, serviceBinding, smClientTypes.CREATE, bindErr)
250250
}
251251

252252
if operationURL != "" {
253253
var bindingID string
254254
if bindingID = sm.ExtractBindingID(operationURL); len(bindingID) == 0 {
255-
return utils.MarkAsNonTransientError(ctx, r.Client, smClientTypes.CREATE, fmt.Errorf("failed to extract smBinding ID from operation URL %s", operationURL), serviceBinding)
255+
return utils.HandleOperationFailure(ctx, r.Client, serviceBinding, smClientTypes.CREATE, fmt.Errorf("failed to extract smBinding ID from operation URL %s", operationURL))
256256
}
257257
serviceBinding.Status.BindingID = bindingID
258258

@@ -264,7 +264,7 @@ func (r *ServiceBindingReconciler) createBinding(ctx context.Context, smClient s
264264
log.Error(err, "unable to update ServiceBinding status")
265265
return ctrl.Result{}, err
266266
}
267-
return ctrl.Result{Requeue: true, RequeueAfter: r.Config.PollInterval}, nil
267+
return ctrl.Result{RequeueAfter: r.Config.PollInterval}, nil
268268
}
269269

270270
log.Info("Binding created successfully")
@@ -292,14 +292,14 @@ func (r *ServiceBindingReconciler) delete(ctx context.Context, serviceBinding *v
292292
if controllerutil.ContainsFinalizer(serviceBinding, common.FinalizerName) {
293293
smClient, err := r.GetSMClient(ctx, serviceInstance)
294294
if err != nil {
295-
return utils.MarkAsTransientError(ctx, r.Client, common.Unknown, err, serviceBinding)
295+
return utils.HandleOperationFailure(ctx, r.Client, serviceBinding, smClientTypes.DELETE, err)
296296
}
297297

298298
if len(serviceBinding.Status.BindingID) == 0 {
299299
log.Info("No binding id found validating binding does not exists in SM before removing finalizer")
300300
smBinding, err := r.getBindingForRecovery(ctx, smClient, serviceBinding)
301301
if err != nil {
302-
return ctrl.Result{}, err
302+
return utils.HandleServiceManagerError(ctx, r.Client, serviceBinding, smClientTypes.DELETE, err)
303303
}
304304
if smBinding != nil {
305305
log.Info("binding exists in SM continue with deletion")
@@ -328,7 +328,7 @@ func (r *ServiceBindingReconciler) delete(ctx context.Context, serviceBinding *v
328328
log.Info(fmt.Sprintf("Deleting binding with id %v from SM", serviceBinding.Status.BindingID))
329329
operationURL, unbindErr := smClient.Unbind(serviceBinding.Status.BindingID, nil, utils.BuildUserInfo(ctx, serviceBinding.Spec.UserInfo))
330330
if unbindErr != nil {
331-
return utils.HandleDeleteError(ctx, r.Client, unbindErr, serviceBinding)
331+
return utils.HandleServiceManagerError(ctx, r.Client, serviceBinding, smClientTypes.DELETE, unbindErr)
332332
}
333333

334334
if operationURL != "" {
@@ -339,7 +339,7 @@ func (r *ServiceBindingReconciler) delete(ctx context.Context, serviceBinding *v
339339
if err := utils.UpdateStatus(ctx, r.Client, serviceBinding); err != nil {
340340
return ctrl.Result{}, err
341341
}
342-
return ctrl.Result{Requeue: true, RequeueAfter: r.Config.PollInterval}, nil
342+
return ctrl.Result{RequeueAfter: r.Config.PollInterval}, nil
343343
}
344344

345345
log.Info("Binding was deleted successfully")
@@ -354,7 +354,7 @@ func (r *ServiceBindingReconciler) poll(ctx context.Context, serviceBinding *v1.
354354

355355
smClient, err := r.GetSMClient(ctx, serviceInstance)
356356
if err != nil {
357-
return utils.MarkAsTransientError(ctx, r.Client, common.Unknown, err, serviceBinding)
357+
return utils.HandleOperationFailure(ctx, r.Client, serviceBinding, common.Unknown, err)
358358
}
359359

360360
status, statusErr := smClient.Status(serviceBinding.Status.OperationURL, nil)
@@ -375,7 +375,7 @@ func (r *ServiceBindingReconciler) poll(ctx context.Context, serviceBinding *v1.
375375
}
376376

377377
if status == nil {
378-
return utils.MarkAsTransientError(ctx, r.Client, serviceBinding.Status.OperationType, fmt.Errorf("failed to get last operation status of %s", serviceBinding.Name), serviceBinding)
378+
return utils.HandleOperationFailure(ctx, r.Client, serviceBinding, serviceBinding.Status.OperationType, fmt.Errorf("failed to get last operation status of %s", serviceBinding.Name))
379379
}
380380
switch status.State {
381381
case smClientTypes.INPROGRESS:
@@ -388,9 +388,9 @@ func (r *ServiceBindingReconciler) poll(ctx context.Context, serviceBinding *v1.
388388
return ctrl.Result{}, err
389389
}
390390
}
391-
return ctrl.Result{Requeue: true, RequeueAfter: r.Config.PollInterval}, nil
391+
return ctrl.Result{RequeueAfter: r.Config.PollInterval}, nil
392392
case smClientTypes.FAILED:
393-
// non transient error - should not retry
393+
// if async operation failed we should not retry
394394
utils.SetFailureConditions(status.Type, status.Description, serviceBinding, true)
395395
if serviceBinding.Status.OperationType == smClientTypes.DELETE {
396396
serviceBinding.Status.OperationURL = ""
@@ -822,10 +822,7 @@ func (r *ServiceBindingReconciler) validateSecretNameIsAvailable(ctx context.Con
822822
func (r *ServiceBindingReconciler) handleSecretError(ctx context.Context, op smClientTypes.OperationCategory, err error, binding *v1.ServiceBinding) (ctrl.Result, error) {
823823
log := utils.GetLogger(ctx)
824824
log.Error(err, fmt.Sprintf("failed to store secret %s for binding %s", binding.Spec.SecretName, binding.Name))
825-
if apierrors.ReasonForError(err) == metav1.StatusReasonUnknown {
826-
return utils.MarkAsNonTransientError(ctx, r.Client, op, err, binding)
827-
}
828-
return utils.MarkAsTransientError(ctx, r.Client, op, err, binding)
825+
return utils.HandleOperationFailure(ctx, r.Client, binding, op, err)
829826
}
830827

831828
func (r *ServiceBindingReconciler) getInstanceInfo(ctx context.Context, binding *v1.ServiceBinding) (map[string]string, error) {

controllers/servicebinding_controller_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ var _ = Describe("ServiceBinding controller", func() {
361361
It("should fail with the error returned from SM", func() {
362362
binding, err := createBindingWithoutAssertions(ctx, bindingName, bindingTestNamespace, instanceName, "", "binding-external-name", "", false)
363363
Expect(err).ToNot(HaveOccurred())
364-
waitForResourceCondition(ctx, binding, common.ConditionFailed, metav1.ConditionTrue, "", errorMessage)
364+
waitForResourceCondition(ctx, binding, common.ConditionSucceeded, metav1.ConditionFalse, "", errorMessage)
365365
})
366366
})
367367

@@ -394,7 +394,7 @@ var _ = Describe("ServiceBinding controller", func() {
394394
It("should fail", func() {
395395
binding, err := createBindingWithoutAssertions(ctx, bindingName, bindingTestNamespace, instanceName, "", "binding-external-name", "", false)
396396
Expect(err).ToNot(HaveOccurred())
397-
waitForResourceCondition(ctx, binding, common.ConditionSucceeded, metav1.ConditionFalse, common.CreateInProgress, errorMessage)
397+
waitForResourceCondition(ctx, binding, common.ConditionSucceeded, metav1.ConditionFalse, common.CreateFailed, errorMessage)
398398
})
399399
})
400400

@@ -434,7 +434,7 @@ var _ = Describe("ServiceBinding controller", func() {
434434
It("should detect the error as non-transient and fail", func() {
435435
binding, err := createBindingWithoutAssertions(ctx, bindingName, bindingTestNamespace, instanceName, "", "binding-external-name", "", false)
436436
Expect(err).ToNot(HaveOccurred())
437-
waitForResourceCondition(ctx, binding, common.ConditionSucceeded, metav1.ConditionFalse, common.CreateInProgress, errorMessage)
437+
waitForResourceCondition(ctx, binding, common.ConditionSucceeded, metav1.ConditionFalse, common.CreateFailed, errorMessage)
438438
})
439439
})
440440

@@ -447,7 +447,7 @@ var _ = Describe("ServiceBinding controller", func() {
447447

448448
It("creation will fail with appropriate message", func() {
449449
createdBinding, _ = createBindingWithoutAssertions(ctx, bindingName, bindingTestNamespace, instanceName, "", "", "", false)
450-
waitForResourceCondition(ctx, createdBinding, common.ConditionFailed, metav1.ConditionTrue, "CreateFailed", "failed to create secret")
450+
waitForResourceCondition(ctx, createdBinding, common.ConditionSucceeded, metav1.ConditionFalse, "", "failed to create secret")
451451
})
452452
})
453453
})
@@ -646,7 +646,7 @@ stringData:
646646
name: my-secret-name`)
647647
binding, err := createBindingWithoutAssertions(ctx, bindingName, bindingTestNamespace, instanceName, "", "", secretTemplate, false)
648648
Expect(err).ToNot(HaveOccurred())
649-
waitForResourceCondition(ctx, binding, common.ConditionFailed, metav1.ConditionTrue, "", "the Secret template is invalid: Secret's metadata field")
649+
waitForResourceCondition(ctx, binding, common.ConditionSucceeded, metav1.ConditionFalse, "", "the Secret template is invalid: Secret's metadata field")
650650
})
651651
It("should fail to create the secret if wrong template key in the spec.secretTemplate is provided", func() {
652652
ctx := context.Background()
@@ -664,7 +664,7 @@ stringData:
664664
return false
665665
}
666666
cond := meta.FindStatusCondition(binding.GetConditions(), common.ConditionSucceeded)
667-
return cond != nil && cond.Reason == "CreateFailed" && strings.Contains(cond.Message, "map has no entry for key \"non_existing_key\"")
667+
return cond != nil && cond.Reason == common.CreateFailed && strings.Contains(cond.Message, "map has no entry for key \"non_existing_key\"")
668668
}, timeout*2, interval).Should(BeTrue())
669669
})
670670
It("should fail to create the secret if secretTemplate is an unexpected type", func() {
@@ -674,7 +674,7 @@ stringData:
674674
kind: Pod`)
675675
binding, err := createBindingWithoutAssertions(ctx, bindingName, bindingTestNamespace, instanceName, "", "", secretTemplate, false)
676676
Expect(err).ToNot(HaveOccurred())
677-
waitForResourceCondition(ctx, binding, common.ConditionFailed, metav1.ConditionTrue, "", "but needs to be of kind 'Secret'")
677+
waitForResourceCondition(ctx, binding, common.ConditionSucceeded, metav1.ConditionFalse, "", "but needs to be of kind 'Secret'")
678678
})
679679
It("should succeed to create the secret- empty data", func() {
680680
ctx := context.Background()
@@ -884,7 +884,7 @@ stringData:
884884
err := k8sClient.Update(ctx, createdBinding)
885885
Expect(err).ToNot(HaveOccurred())
886886
By("Verify binding update failed")
887-
waitForResourceCondition(ctx, createdBinding, common.ConditionFailed, metav1.ConditionTrue, "UpdateFailed", "failed to create secret")
887+
waitForResourceCondition(ctx, createdBinding, common.ConditionSucceeded, metav1.ConditionFalse, "UpdateFailed", "failed to create secret from template")
888888

889889
secretTemplate = dedent.Dedent(
890890
`apiVersion: v1
@@ -979,8 +979,8 @@ stringData:
979979
if err != nil {
980980
return false
981981
}
982-
failedCond := meta.FindStatusCondition(createdBinding.GetConditions(), common.ConditionFailed)
983-
return failedCond != nil && strings.Contains(failedCond.Message, errorMessage)
982+
cond := meta.FindStatusCondition(createdBinding.GetConditions(), common.ConditionSucceeded)
983+
return cond != nil && strings.Contains(cond.Message, errorMessage)
984984
}, timeout, interval).Should(BeTrue())
985985
Expect(len(createdBinding.Finalizers)).To(Equal(1))
986986
getSecret(ctx, createdBinding.Spec.SecretName, createdBinding.Namespace, true)
@@ -1139,7 +1139,7 @@ stringData:
11391139
case smClientTypes.FAILED:
11401140
Expect(utils.IsFailed(createdBinding))
11411141
case smClientTypes.INPROGRESS:
1142-
Expect(utils.IsInProgress(createdBinding))
1142+
Expect(utils.ShouldRetryOperation(createdBinding))
11431143
case smClientTypes.SUCCEEDED:
11441144
Expect(isResourceReady(createdBinding))
11451145
}

0 commit comments

Comments
 (0)