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

     1  package upgrade_kyma
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sort"
     7  	"time"
     8  
     9  	"github.com/pkg/errors"
    10  
    11  	"github.com/kyma-project/kyma-environment-broker/internal"
    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/sirupsen/logrus"
    16  )
    17  
    18  type Step interface {
    19  	Name() string
    20  	Run(operation internal.UpgradeKymaOperation, logger logrus.FieldLogger) (internal.UpgradeKymaOperation, time.Duration, error)
    21  }
    22  
    23  type StepCondition func(operation internal.UpgradeKymaOperation) bool
    24  
    25  type StepWithCondition struct {
    26  	Step
    27  	condition StepCondition
    28  }
    29  
    30  type Manager struct {
    31  	log              logrus.FieldLogger
    32  	steps            map[int][]StepWithCondition
    33  	operationStorage storage.Operations
    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  		steps:            make(map[int][]StepWithCondition, 0),
    42  		operationStorage: storage,
    43  		publisher:        pub,
    44  	}
    45  }
    46  
    47  func (m *Manager) InitStep(step Step) {
    48  	m.AddStep(0, step, nil)
    49  }
    50  
    51  func (m *Manager) AddStep(weight int, step Step, cnd StepCondition) {
    52  	if weight <= 0 {
    53  		weight = 1
    54  	}
    55  	m.steps[weight] = append(m.steps[weight], StepWithCondition{
    56  		Step:      step,
    57  		condition: cnd,
    58  	})
    59  }
    60  
    61  func (m *Manager) runStep(step Step, operation internal.UpgradeKymaOperation, logger logrus.FieldLogger) (processedOperation internal.UpgradeKymaOperation, when time.Duration, err error) {
    62  
    63  	defer func() {
    64  		if pErr := recover(); pErr != nil {
    65  			logger.Println("panic in RunStep during Kyma upgrade: ", pErr)
    66  			err = errors.New(fmt.Sprintf("%v", pErr))
    67  			om := process.NewUpgradeKymaOperationManager(m.operationStorage)
    68  			processedOperation, _, _ = om.OperationFailed(operation, "recovered from panic", err, m.log)
    69  		}
    70  	}()
    71  
    72  	start := time.Now()
    73  	processedOperation, when, err = step.Run(operation, logger)
    74  	m.publisher.Publish(context.TODO(), process.UpgradeKymaStepProcessed{
    75  		OldOperation: operation,
    76  		Operation:    processedOperation,
    77  		StepProcessed: process.StepProcessed{
    78  			StepName: step.Name(),
    79  			Duration: time.Since(start),
    80  			When:     when,
    81  			Error:    err,
    82  		},
    83  	})
    84  	return processedOperation, when, err
    85  }
    86  
    87  func (m *Manager) Execute(operationID string) (time.Duration, error) {
    88  
    89  	op, err := m.operationStorage.GetUpgradeKymaOperationByID(operationID)
    90  	if err != nil {
    91  		m.log.Errorf("Cannot fetch operation from storage: %s", err)
    92  		return 3 * time.Second, nil
    93  	}
    94  	operation := *op
    95  	if operation.IsFinished() {
    96  		return 0, nil
    97  	}
    98  
    99  	var when time.Duration
   100  	logOperation := m.log.WithFields(logrus.Fields{"operation": operationID, "instanceID": operation.InstanceID})
   101  
   102  	logOperation.Info("Start process operation steps")
   103  	for _, weightStep := range m.sortWeight() {
   104  		steps := m.steps[weightStep]
   105  		for _, step := range steps {
   106  			if step.condition != nil && !step.condition(operation) {
   107  				continue
   108  			}
   109  			logStep := logOperation.WithField("step", step.Name())
   110  			logStep.Infof("Start step")
   111  
   112  			operation, when, err = m.runStep(step, operation, logStep)
   113  			if err != nil {
   114  				logStep.Errorf("Process operation failed: %s", err)
   115  				return 0, err
   116  			}
   117  			if operation.IsFinished() {
   118  				logStep.Infof("Operation %q got status %s. Process finished.", operation.Operation.ID, operation.State)
   119  				return 0, nil
   120  			}
   121  			if when == 0 {
   122  				logStep.Info("Process operation successful")
   123  				continue
   124  			}
   125  
   126  			logStep.Infof("Process operation will be repeated in %s ...", when)
   127  			return when, nil
   128  		}
   129  	}
   130  
   131  	logOperation.Infof("Operation %q got status %s. All steps finished.", operation.Operation.ID, operation.State)
   132  	return 0, nil
   133  }
   134  
   135  func (m Manager) Reschedule(operationID string, maintenanceWindowBegin, maintenanceWindowEnd time.Time) error {
   136  	op, err := m.operationStorage.GetUpgradeKymaOperationByID(operationID)
   137  	if err != nil {
   138  		m.log.Errorf("Cannot fetch operation %s from storage: %s", operationID, err)
   139  		return err
   140  	}
   141  	op.MaintenanceWindowBegin = maintenanceWindowBegin
   142  	op.MaintenanceWindowEnd = maintenanceWindowEnd
   143  	op, err = m.operationStorage.UpdateUpgradeKymaOperation(*op)
   144  	if err != nil {
   145  		m.log.Errorf("Cannot update (reschedule) operation %s in storage: %s", operationID, err)
   146  	}
   147  
   148  	return err
   149  }
   150  
   151  func (m *Manager) sortWeight() []int {
   152  	var weight []int
   153  	for w := range m.steps {
   154  		weight = append(weight, w)
   155  	}
   156  	sort.Ints(weight)
   157  
   158  	return weight
   159  }