github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/process/deprovisioning/manager.go (about)

     1  package deprovisioning
     2  
     3  import (
     4  	"context"
     5  	"sort"
     6  	"time"
     7  
     8  	"github.com/kyma-project/kyma-environment-broker/common/orchestration"
     9  
    10  	"github.com/kyma-project/kyma-environment-broker/internal"
    11  	kebError "github.com/kyma-project/kyma-environment-broker/internal/error"
    12  	"github.com/kyma-project/kyma-environment-broker/internal/event"
    13  	"github.com/kyma-project/kyma-environment-broker/internal/process"
    14  	"github.com/kyma-project/kyma-environment-broker/internal/storage"
    15  	"github.com/kyma-project/kyma-environment-broker/internal/storage/dberr"
    16  	"github.com/pivotal-cf/brokerapi/v8/domain"
    17  	"github.com/sirupsen/logrus"
    18  )
    19  
    20  const (
    21  	retryAfterTime = 1 * time.Minute
    22  )
    23  
    24  type Step interface {
    25  	Name() string
    26  	Run(operation internal.DeprovisioningOperation, logger logrus.FieldLogger) (internal.DeprovisioningOperation, time.Duration, error)
    27  }
    28  
    29  type Manager struct {
    30  	log              logrus.FieldLogger
    31  	steps            map[int][]Step
    32  	operationStorage storage.Operations
    33  	operationManager *process.DeprovisionOperationManager
    34  
    35  	publisher event.Publisher
    36  }
    37  
    38  func NewManager(storage storage.Operations, pub event.Publisher, logger logrus.FieldLogger) *Manager {
    39  	return &Manager{
    40  		log:              logger,
    41  		operationStorage: storage,
    42  		steps:            make(map[int][]Step, 0),
    43  		publisher:        pub,
    44  		operationManager: process.NewDeprovisionOperationManager(storage),
    45  	}
    46  }
    47  
    48  func (m *Manager) InitStep(step Step) {
    49  	m.AddStep(0, step)
    50  }
    51  
    52  func (m *Manager) AddStep(weight int, step Step) {
    53  	if weight <= 0 {
    54  		weight = 1
    55  	}
    56  	m.steps[weight] = append(m.steps[weight], step)
    57  }
    58  
    59  func (m *Manager) runStep(step Step, operation internal.DeprovisioningOperation, logger logrus.FieldLogger) (internal.DeprovisioningOperation, time.Duration, error) {
    60  	start := time.Now()
    61  	processedOperation, when, err := step.Run(operation, logger)
    62  	if err != nil {
    63  		processedOperation.LastError = kebError.ReasonForError(err)
    64  		log := logger.WithFields(logrus.Fields{"error_component": processedOperation.LastError.Component(), "error_reason": processedOperation.LastError.Reason()})
    65  		log.Errorf("Last error: %s", processedOperation.LastError.Error())
    66  		// no saving to storage for deprovisioning
    67  	}
    68  
    69  	m.publisher.Publish(context.TODO(), process.DeprovisioningStepProcessed{
    70  		StepProcessed: process.StepProcessed{
    71  			StepName: step.Name(),
    72  			Duration: time.Since(start),
    73  			When:     when,
    74  			Error:    err,
    75  		},
    76  		OldOperation: operation,
    77  		Operation:    processedOperation,
    78  	})
    79  	return processedOperation, when, err
    80  }
    81  
    82  func (m *Manager) Execute(operationID string) (time.Duration, error) {
    83  	op, err := m.operationStorage.GetDeprovisioningOperationByID(operationID)
    84  	if err != nil {
    85  		m.log.Errorf("Cannot fetch DeprovisioningOperation from storage: %s", err)
    86  		return retryAfterTime, nil
    87  	}
    88  	operation := *op
    89  
    90  	logOperation := m.log.WithFields(logrus.Fields{"operation": operationID, "instanceID": operation.InstanceID})
    91  
    92  	provisioningOp, err := m.operationStorage.GetProvisioningOperationByInstanceID(op.InstanceID)
    93  	if err != nil {
    94  		m.log.Errorf("Cannot fetch ProvisioningOperation for instanceID %s from storage: %s", op.InstanceID, err)
    95  
    96  		operation.LastError = kebError.ReasonForError(err)
    97  		log := logOperation.WithFields(logrus.Fields{"error_component": operation.LastError.Component(), "error_reason": operation.LastError.Reason()})
    98  		log.Errorf("Last error: %s", operation.LastError.Error())
    99  		m.publisher.Publish(context.TODO(), process.DeprovisioningStepProcessed{
   100  			StepProcessed: process.StepProcessed{
   101  				Error: err,
   102  			},
   103  			Operation: operation,
   104  		})
   105  
   106  		if dberr.IsNotFound(err) {
   107  			_, duration, err := m.operationManager.OperationFailed(operation, "Error retrieving provisioning operation - operation not found", err, log)
   108  			return duration, err
   109  		}
   110  
   111  		return retryAfterTime, nil
   112  	}
   113  
   114  	logOperation = logOperation.WithField("planID", provisioningOp.ProvisioningParameters.PlanID)
   115  
   116  	var when time.Duration
   117  	logOperation.Info("Start process operation steps")
   118  
   119  	for _, weightStep := range m.sortWeight() {
   120  		steps := m.steps[weightStep]
   121  		for _, step := range steps {
   122  			logStep := logOperation.WithField("step", step.Name())
   123  			logStep.Infof("Start step")
   124  
   125  			operation, when, err = m.runStep(step, operation, logStep)
   126  			if err != nil {
   127  				logStep.Errorf("Process operation failed: %s", err)
   128  				return 0, err
   129  			}
   130  			if operation.State != domain.InProgress && operation.State != orchestration.Pending {
   131  				if operation.RuntimeID == "" && operation.State == domain.Succeeded {
   132  					logStep.Infof("Operation %q has no runtime ID. Process finished.", operation.ID)
   133  					return when, nil
   134  				}
   135  				logStep.Infof("Operation %q got status %s. Process finished.", operation.ID, operation.State)
   136  				return 0, nil
   137  			}
   138  			if when == 0 {
   139  				logStep.Info("Process operation successful")
   140  				continue
   141  			}
   142  
   143  			logStep.Infof("Process operation will be repeated in %s ...", when)
   144  			return when, nil
   145  		}
   146  	}
   147  
   148  	logOperation.Infof("Operation %q got status %s. All steps finished.", operation.ID, operation.State)
   149  	return 0, nil
   150  }
   151  
   152  func (m *Manager) sortWeight() []int {
   153  	var weight []int
   154  	for w := range m.steps {
   155  		weight = append(weight, w)
   156  	}
   157  	sort.Ints(weight)
   158  
   159  	return weight
   160  }