github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/process/provision_operation.go (about) 1 package process 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/kyma-project/kyma-environment-broker/internal/storage/dberr" 8 9 "github.com/sirupsen/logrus" 10 11 "github.com/kyma-project/kyma-environment-broker/internal" 12 "github.com/kyma-project/kyma-environment-broker/internal/storage" 13 14 "github.com/pivotal-cf/brokerapi/v8/domain" 15 ) 16 17 type ProvisionOperationManager struct { 18 storage storage.Provisioning 19 } 20 21 func NewProvisionOperationManager(storage storage.Operations) *ProvisionOperationManager { 22 return &ProvisionOperationManager{storage: storage} 23 } 24 25 // OperationSucceeded marks the operation as succeeded and only repeats it if there is a storage error 26 func (om *ProvisionOperationManager) OperationSucceeded(operation internal.ProvisioningOperation, description string, log logrus.FieldLogger) (internal.ProvisioningOperation, time.Duration, error) { 27 updatedOperation, repeat, _ := om.update(operation, domain.Succeeded, description, log) 28 // repeat in case of storage error 29 if repeat != 0 { 30 return updatedOperation, repeat, nil 31 } 32 33 return updatedOperation, 0, nil 34 } 35 36 // OperationFailed marks the operation as failed and only repeats it if there is a storage error 37 func (om *ProvisionOperationManager) OperationFailed(operation internal.ProvisioningOperation, description string, err error, log logrus.FieldLogger) (internal.ProvisioningOperation, time.Duration, error) { 38 updatedOperation, repeat, _ := om.update(operation, domain.Failed, description, log) 39 // repeat in case of storage error 40 if repeat != 0 { 41 return updatedOperation, repeat, nil 42 } 43 44 var retErr error 45 if err == nil { 46 // no exact err passed in 47 retErr = fmt.Errorf(description) 48 } else { 49 // keep the original err object for error categorizer 50 retErr = fmt.Errorf("%s: %w", description, err) 51 } 52 53 return updatedOperation, 0, retErr 54 } 55 56 // UpdateOperation updates a given operation and handles conflict situation 57 func (om *ProvisionOperationManager) UpdateOperation(operation internal.ProvisioningOperation, update func(operation *internal.ProvisioningOperation), log logrus.FieldLogger) (internal.ProvisioningOperation, time.Duration, error) { 58 update(&operation) 59 updatedOperation, err := om.storage.UpdateProvisioningOperation(operation) 60 switch { 61 case dberr.IsConflict(err): 62 { 63 op, err := om.storage.GetProvisioningOperationByID(operation.ID) 64 if err != nil { 65 log.Errorf("while getting operation: %v", err) 66 return operation, 1 * time.Minute, err 67 } 68 update(op) 69 updatedOperation, err = om.storage.UpdateProvisioningOperation(*op) 70 if err != nil { 71 log.Errorf("while updating operation after conflict: %v", err) 72 return operation, 1 * time.Minute, err 73 } 74 } 75 case err != nil: 76 log.Errorf("while updating operation: %v", err) 77 return operation, 1 * time.Minute, err 78 } 79 return *updatedOperation, 0, nil 80 } 81 82 // Deprecated: SimpleUpdateOperation updates a given operation without handling conflicts. Should be used when operation's data mutations are not clear 83 func (om *ProvisionOperationManager) SimpleUpdateOperation(operation internal.ProvisioningOperation) (internal.ProvisioningOperation, time.Duration) { 84 updatedOperation, err := om.storage.UpdateProvisioningOperation(operation) 85 if err != nil { 86 logrus.WithField("operation", operation.ID). 87 WithField("instanceID", operation.InstanceID). 88 Errorf("Update provisioning operation failed: %s", err.Error()) 89 return operation, 1 * time.Minute 90 } 91 return *updatedOperation, 0 92 } 93 94 // RetryOperationOnce retries the operation once and fails the operation when call second time 95 func (om *ProvisionOperationManager) RetryOperationOnce(operation internal.ProvisioningOperation, errorMessage string, err error, wait time.Duration, log logrus.FieldLogger) (internal.ProvisioningOperation, time.Duration, error) { 96 return om.RetryOperation(operation, errorMessage, err, wait, wait+1, log) 97 } 98 99 // RetryOperation retries an operation for at maxTime in retryInterval steps and fails the operation if retrying failed 100 func (om *ProvisionOperationManager) RetryOperation(operation internal.ProvisioningOperation, errorMessage string, err error, retryInterval time.Duration, maxTime time.Duration, log logrus.FieldLogger) (internal.ProvisioningOperation, time.Duration, error) { 101 since := time.Since(operation.UpdatedAt) 102 103 log.Infof("Retry Operation was triggered with message: %s", errorMessage) 104 log.Infof("Retrying for %s in %s steps", maxTime.String(), retryInterval.String()) 105 if since < maxTime { 106 return operation, retryInterval, nil 107 } 108 log.Errorf("Aborting after %s of failing retries", maxTime.String()) 109 return om.OperationFailed(operation, errorMessage, err, log) 110 } 111 112 func (om *ProvisionOperationManager) HandleError(operation internal.ProvisioningOperation, err error, log logrus.FieldLogger, msg string) (internal.ProvisioningOperation, time.Duration, error) { 113 log.Errorf("%s: %s", msg, err) 114 return om.OperationFailed(operation, msg, err, log) 115 } 116 117 func (om *ProvisionOperationManager) update(operation internal.ProvisioningOperation, state domain.LastOperationState, description string, log logrus.FieldLogger) (internal.ProvisioningOperation, time.Duration, error) { 118 return om.UpdateOperation(operation, func(operation *internal.ProvisioningOperation) { 119 operation.State = state 120 operation.Description = fmt.Sprintf("%s : %s", operation.Description, description) 121 }, log) 122 }