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  }