github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/process/operation_manager.go (about) 1 package process 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/kyma-project/kyma-environment-broker/common/orchestration" 8 "github.com/kyma-project/kyma-environment-broker/internal" 9 "github.com/kyma-project/kyma-environment-broker/internal/storage" 10 "github.com/kyma-project/kyma-environment-broker/internal/storage/dberr" 11 "github.com/pivotal-cf/brokerapi/v8/domain" 12 "github.com/sirupsen/logrus" 13 ) 14 15 type OperationManager struct { 16 storage storage.Operations 17 } 18 19 func NewOperationManager(storage storage.Operations) *OperationManager { 20 return &OperationManager{storage: storage} 21 } 22 23 // OperationSucceeded marks the operation as succeeded and returns status of the operation's update 24 func (om *OperationManager) OperationSucceeded(operation internal.Operation, description string, log logrus.FieldLogger) (internal.Operation, time.Duration, error) { 25 return om.update(operation, domain.Succeeded, description, log) 26 } 27 28 // OperationFailed marks the operation as failed and returns status of the operation's update 29 func (om *OperationManager) OperationFailed(operation internal.Operation, description string, err error, log logrus.FieldLogger) (internal.Operation, time.Duration, error) { 30 op, t, _ := om.update(operation, domain.Failed, description, log) 31 // repeat in case of storage error 32 if t != 0 { 33 return op, t, nil 34 } 35 36 var retErr error 37 if err == nil { 38 // no exact err passed in 39 retErr = fmt.Errorf(description) 40 } else { 41 // keep the original err object for error categorizer 42 retErr = fmt.Errorf("%s: %w", description, err) 43 } 44 operation.EventErrorf(err, "operation failed") 45 46 return op, 0, retErr 47 } 48 49 // OperationCanceled marks the operation as canceled and returns status of the operation's update 50 func (om *OperationManager) OperationCanceled(operation internal.Operation, description string, log logrus.FieldLogger) (internal.Operation, time.Duration, error) { 51 return om.update(operation, orchestration.Canceled, description, log) 52 } 53 54 // RetryOperation checks if operation should be retried or if it's the status should be marked as failed 55 func (om *OperationManager) RetryOperation(operation internal.Operation, errorMessage string, err error, retryInterval time.Duration, maxTime time.Duration, log logrus.FieldLogger) (internal.Operation, time.Duration, error) { 56 log.Infof("Retry Operation was triggered with message: %s", errorMessage) 57 log.Infof("Retrying for %s in %s steps", maxTime.String(), retryInterval.String()) 58 if time.Since(operation.UpdatedAt) < maxTime { 59 return operation, retryInterval, nil 60 } 61 log.Errorf("Aborting after %s of failing retries", maxTime.String()) 62 op, retry, err := om.OperationFailed(operation, errorMessage, err, log) 63 if err == nil { 64 err = fmt.Errorf("Too many retries") 65 } else { 66 err = fmt.Errorf("Failed to set status for operation after too many retries: %v", err) 67 } 68 return op, retry, err 69 } 70 71 // RetryOperationWithoutFail checks if operation should be retried or updates the status to InProgress, but omits setting the operation to failed if maxTime is reached 72 func (om *OperationManager) RetryOperationWithoutFail(operation internal.Operation, stepName string, description string, retryInterval, maxTime time.Duration, log logrus.FieldLogger) (internal.Operation, time.Duration, error) { 73 log.Infof("Retry Operation was triggered with message: %s", description) 74 log.Infof("Retrying for %s in %s steps", maxTime.String(), retryInterval.String()) 75 if time.Since(operation.UpdatedAt) < maxTime { 76 return operation, retryInterval, nil 77 } 78 // update description to track failed steps 79 op, repeat, err := om.UpdateOperation(operation, func(operation *internal.Operation) { 80 operation.State = domain.InProgress 81 operation.Description = description 82 operation.ExcutedButNotCompleted = append(operation.ExcutedButNotCompleted, stepName) 83 }, log) 84 if repeat != 0 { 85 return op, repeat, err 86 } 87 88 op.EventErrorf(fmt.Errorf(description), "step %s failed retries: operation continues", stepName) 89 log.Errorf("Omitting after %s of failing retries", maxTime.String()) 90 return op, 0, nil 91 } 92 93 // RetryOperationOnce retries the operation once and fails the operation when call second time 94 func (om *OperationManager) RetryOperationOnce(operation internal.Operation, errorMessage string, err error, wait time.Duration, log logrus.FieldLogger) (internal.Operation, time.Duration, error) { 95 return om.RetryOperation(operation, errorMessage, err, wait, wait+1, log) 96 } 97 98 // UpdateOperation updates a given operation and handles conflict situation 99 func (om *OperationManager) UpdateOperation(operation internal.Operation, update func(operation *internal.Operation), log logrus.FieldLogger) (internal.Operation, time.Duration, error) { 100 update(&operation) 101 op, err := om.storage.UpdateOperation(operation) 102 switch { 103 case dberr.IsConflict(err): 104 { 105 op, err = om.storage.GetOperationByID(operation.ID) 106 if err != nil { 107 log.Errorf("while getting operation: %v", err) 108 return operation, 1 * time.Minute, err 109 } 110 update(op) 111 op, err = om.storage.UpdateOperation(*op) 112 if err != nil { 113 log.Errorf("while updating operation after conflict: %v", err) 114 return operation, 1 * time.Minute, err 115 } 116 } 117 case err != nil: 118 log.Errorf("while updating operation: %v", err) 119 return operation, 1 * time.Minute, err 120 } 121 122 return *op, 0, nil 123 } 124 125 func (om *OperationManager) MarkStepAsExcutedButNotCompleted(operation internal.Operation, stepName string, msg string, log logrus.FieldLogger) (internal.Operation, time.Duration, error) { 126 op, repeat, err := om.UpdateOperation(operation, func(operation *internal.Operation) { 127 operation.ExcutedButNotCompleted = append(operation.ExcutedButNotCompleted, stepName) 128 }, log) 129 if repeat != 0 { 130 return op, repeat, err 131 } 132 133 op.EventErrorf(fmt.Errorf(msg), "step %s failed: operation continues", stepName) 134 log.Errorf(msg) 135 return op, 0, nil 136 } 137 138 func (om *OperationManager) update(operation internal.Operation, state domain.LastOperationState, description string, log logrus.FieldLogger) (internal.Operation, time.Duration, error) { 139 return om.UpdateOperation(operation, func(operation *internal.Operation) { 140 operation.State = state 141 operation.Description = description 142 }, log) 143 }