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 }