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 }