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

     1  package upgrade_cluster
     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.UpgradeClusterOperation, logger logrus.FieldLogger) (internal.UpgradeClusterOperation, time.Duration, error)
    21  }
    22  
    23  type StepCondition func(operation internal.Operation) 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, condition StepCondition) {
    52  	if weight <= 0 {
    53  		weight = 1
    54  	}
    55  	m.steps[weight] = append(m.steps[weight], StepWithCondition{Step: step, condition: condition})
    56  }
    57  
    58  func (m *Manager) runStep(step Step, operation internal.UpgradeClusterOperation, logger logrus.FieldLogger) (processedOperation internal.UpgradeClusterOperation, when time.Duration, err error) {
    59  	defer func() {
    60  		if pErr := recover(); pErr != nil {
    61  			logger.Println("panic in RunStep during cluster upgrade: ", pErr)
    62  			err = errors.New(fmt.Sprintf("%v", pErr))
    63  			om := process.NewUpgradeClusterOperationManager(m.operationStorage)
    64  			processedOperation, _, _ = om.OperationFailed(operation, "recovered from panic", err, m.log)
    65  		}
    66  	}()
    67  
    68  	start := time.Now()
    69  	processedOperation, when, err = step.Run(operation, logger)
    70  	m.publisher.Publish(context.TODO(), process.UpgradeClusterStepProcessed{
    71  		OldOperation: operation,
    72  		Operation:    processedOperation,
    73  		StepProcessed: process.StepProcessed{
    74  			StepName: step.Name(),
    75  			Duration: time.Since(start),
    76  			When:     when,
    77  			Error:    err,
    78  		},
    79  	})
    80  	return processedOperation, when, err
    81  }
    82  
    83  func (m *Manager) sortWeight() []int {
    84  	var weight []int
    85  	for w := range m.steps {
    86  		weight = append(weight, w)
    87  	}
    88  	sort.Ints(weight)
    89  
    90  	return weight
    91  }
    92  
    93  func (m *Manager) Execute(operationID string) (time.Duration, error) {
    94  	op, err := m.operationStorage.GetUpgradeClusterOperationByID(operationID)
    95  	if err != nil {
    96  		m.log.Errorf("Cannot fetch operation from storage: %s", err)
    97  		return 3 * time.Second, nil
    98  	}
    99  	operation := *op
   100  	if operation.IsFinished() {
   101  		return 0, nil
   102  	}
   103  
   104  	var when time.Duration
   105  	logOperation := m.log.WithFields(logrus.Fields{"operation": operationID, "instanceID": operation.InstanceID})
   106  
   107  	logOperation.Info("Start process operation steps")
   108  	for _, weightStep := range m.sortWeight() {
   109  		steps := m.steps[weightStep]
   110  		for _, step := range steps {
   111  			logStep := logOperation.WithField("step", step.Name())
   112  
   113  			if step.condition != nil && !step.condition(operation.Operation) {
   114  				logStep.Debugf("Skipping due to not met condition")
   115  				continue
   116  			}
   117  			logStep.Infof("Start step")
   118  
   119  			operation, when, err = m.runStep(step, operation, logStep)
   120  			if err != nil {
   121  				logStep.Errorf("Process operation failed: %s", err)
   122  				return 0, err
   123  			}
   124  			if operation.IsFinished() {
   125  				logStep.Infof("Operation %q got status %s. Process finished.", operation.Operation.ID, operation.State)
   126  				return 0, nil
   127  			}
   128  			if when == 0 {
   129  				logStep.Info("Process operation successful")
   130  				continue
   131  			}
   132  
   133  			logStep.Infof("Process operation will be repeated in %s ...", when)
   134  			return when, nil
   135  		}
   136  	}
   137  
   138  	logOperation.Infof("Operation %q got status %s. All steps finished.", operation.Operation.ID, operation.State)
   139  	return 0, nil
   140  }
   141  
   142  func (m Manager) Reschedule(operationID string, maintenanceWindowBegin, maintenanceWindowEnd time.Time) error {
   143  	op, err := m.operationStorage.GetUpgradeClusterOperationByID(operationID)
   144  	if err != nil {
   145  		m.log.Errorf("Cannot fetch operation %s from storage: %s", operationID, err)
   146  		return err
   147  	}
   148  	op.MaintenanceWindowBegin = maintenanceWindowBegin
   149  	op.MaintenanceWindowEnd = maintenanceWindowEnd
   150  	op, err = m.operationStorage.UpdateUpgradeClusterOperation(*op)
   151  	if err != nil {
   152  		m.log.Errorf("Cannot update (reschedule) operation %s in storage: %s", operationID, err)
   153  	}
   154  
   155  	return err
   156  }