github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/orchestration/manager/upgrade_kyma.go (about) 1 package manager 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 "time" 8 9 "sigs.k8s.io/controller-runtime/pkg/client" 10 11 "github.com/google/uuid" 12 "github.com/kyma-project/kyma-environment-broker/common/orchestration" 13 "github.com/kyma-project/kyma-environment-broker/internal" 14 "github.com/kyma-project/kyma-environment-broker/internal/notification" 15 internalOrchestration "github.com/kyma-project/kyma-environment-broker/internal/orchestration" 16 "github.com/kyma-project/kyma-environment-broker/internal/process" 17 "github.com/kyma-project/kyma-environment-broker/internal/storage" 18 "github.com/kyma-project/kyma-environment-broker/internal/storage/dbmodel" 19 "github.com/pivotal-cf/brokerapi/v8/domain" 20 "github.com/sirupsen/logrus" 21 ) 22 23 type upgradeKymaFactory struct { 24 operationStorage storage.Operations 25 defaultKymaVersion string 26 } 27 28 func NewUpgradeKymaManager(orchestrationStorage storage.Orchestrations, operationStorage storage.Operations, instanceStorage storage.Instances, 29 kymaUpgradeExecutor orchestration.OperationExecutor, resolver orchestration.RuntimeResolver, pollingInterval time.Duration, 30 log logrus.FieldLogger, cli client.Client, cfg *internalOrchestration.Config, bundleBuilder notification.BundleBuilder, speedFactor int) process.Executor { 31 return &orchestrationManager{ 32 orchestrationStorage: orchestrationStorage, 33 operationStorage: operationStorage, 34 instanceStorage: instanceStorage, 35 resolver: resolver, 36 factory: &upgradeKymaFactory{ 37 operationStorage: operationStorage, 38 defaultKymaVersion: cfg.KymaVersion, 39 }, 40 executor: kymaUpgradeExecutor, 41 pollingInterval: pollingInterval, 42 log: log, 43 k8sClient: cli, 44 configNamespace: cfg.Namespace, 45 configName: cfg.Name, 46 kymaVersion: cfg.KymaVersion, 47 kubernetesVersion: cfg.KubernetesVersion, 48 bundleBuilder: bundleBuilder, 49 speedFactor: speedFactor, 50 } 51 } 52 53 func (u *upgradeKymaFactory) NewOperation(o internal.Orchestration, r orchestration.Runtime, i internal.Instance, state domain.LastOperationState) (orchestration.RuntimeOperation, error) { 54 id := uuid.New().String() 55 details, err := i.GetInstanceDetails() 56 if err != nil { 57 return orchestration.RuntimeOperation{}, err 58 } 59 r.Region = i.ProviderRegion 60 op := internal.UpgradeKymaOperation{ 61 Operation: internal.Operation{ 62 ID: id, 63 Version: 0, 64 CreatedAt: time.Now(), 65 UpdatedAt: time.Now(), 66 Type: internal.OperationTypeUpgradeKyma, 67 InstanceID: r.InstanceID, 68 State: state, 69 Description: "Operation created", 70 OrchestrationID: o.OrchestrationID, 71 ProvisioningParameters: i.Parameters, 72 InstanceDetails: details, 73 RuntimeOperation: orchestration.RuntimeOperation{ 74 ID: id, 75 Runtime: r, 76 DryRun: o.Parameters.DryRun, 77 Notification: o.Parameters.Notification, 78 }, 79 }, 80 } 81 if o.Parameters.Kyma.Version != "" { 82 var majorVer int 83 var err error 84 85 majorVer, err = determineMajorVersion(o.Parameters.Kyma.Version, u.defaultKymaVersion) 86 if err != nil { 87 return orchestration.RuntimeOperation{}, fmt.Errorf("while determining Kyma's major version: %w", err) 88 } 89 90 op.RuntimeVersion = *internal.NewRuntimeVersionFromParameters(o.Parameters.Kyma.Version, majorVer) 91 } 92 93 err = u.operationStorage.InsertUpgradeKymaOperation(op) 94 return op.RuntimeOperation, err 95 } 96 97 func determineMajorVersion(version string, defaultVersion string) (int, error) { 98 if isCustomVersion(version) { 99 return extractMajorVersionNumberFromVersionString(defaultVersion) 100 } 101 return extractMajorVersionNumberFromVersionString(version) 102 } 103 104 func isCustomVersion(version string) bool { 105 return strings.HasPrefix(version, "PR-") || strings.HasPrefix(version, "main-") 106 } 107 108 func extractMajorVersionNumberFromVersionString(version string) (int, error) { 109 splitVer := strings.Split(version, ".") 110 majorVerNum, err := strconv.Atoi(splitVer[0]) 111 if err != nil { 112 return 0, fmt.Errorf("cannot convert major version to int") 113 } 114 return majorVerNum, nil 115 } 116 117 func (u *upgradeKymaFactory) ResumeOperations(orchestrationID string) ([]orchestration.RuntimeOperation, error) { 118 ops, _, _, err := u.operationStorage.ListUpgradeKymaOperationsByOrchestrationID(orchestrationID, dbmodel.OperationFilter{States: []string{orchestration.InProgress, orchestration.Retrying, orchestration.Pending}}) 119 if err != nil { 120 return nil, err 121 } 122 123 pending := make([]orchestration.RuntimeOperation, 0) 124 retrying := make([]orchestration.RuntimeOperation, 0) 125 inProgress := make([]orchestration.RuntimeOperation, 0) 126 for _, op := range ops { 127 if op.State == orchestration.Pending { 128 pending = append(pending, op.RuntimeOperation) 129 } 130 if op.State == orchestration.Retrying { 131 runtimeop, err := u.updateRetryingOperation(op) 132 if err != nil { 133 return nil, err 134 } 135 retrying = append(retrying, runtimeop) 136 } 137 if op.State == orchestration.InProgress { 138 inProgress = append(inProgress, op.RuntimeOperation) 139 } 140 } 141 142 return append(inProgress, append(retrying, pending...)...), nil 143 } 144 145 func (u *upgradeKymaFactory) CancelOperation(orchestrationID string, runtimeID string) error { 146 ops, _, _, err := u.operationStorage.ListUpgradeKymaOperationsByOrchestrationID(orchestrationID, dbmodel.OperationFilter{States: []string{orchestration.Pending}}) 147 if err != nil { 148 return fmt.Errorf("while listing upgrade kyma operations: %w", err) 149 } 150 for _, op := range ops { 151 if op.InstanceDetails.RuntimeID == runtimeID { 152 op.State = orchestration.Canceled 153 op.Description = "Operation was canceled" 154 _, err := u.operationStorage.UpdateUpgradeKymaOperation(op) 155 if err != nil { 156 return fmt.Errorf("while updating upgrade kyma operation: %w", err) 157 } 158 } 159 } 160 161 return nil 162 } 163 164 func (u *upgradeKymaFactory) CancelOperations(orchestrationID string) error { 165 ops, _, _, err := u.operationStorage.ListUpgradeKymaOperationsByOrchestrationID(orchestrationID, dbmodel.OperationFilter{States: []string{orchestration.Pending}}) 166 if err != nil { 167 return fmt.Errorf("while listing upgrade kyma operations: %w", err) 168 } 169 for _, op := range ops { 170 op.State = orchestration.Canceled 171 op.Description = "Operation was canceled" 172 _, err := u.operationStorage.UpdateUpgradeKymaOperation(op) 173 if err != nil { 174 return fmt.Errorf("while updating upgrade kyma operation: %w", err) 175 } 176 } 177 178 return nil 179 } 180 181 // get current retrying operations 182 func (u *upgradeKymaFactory) RetryOperations(retryOps []string) ([]orchestration.RuntimeOperation, error) { 183 result := []orchestration.RuntimeOperation{} 184 185 for _, opId := range retryOps { 186 runtimeop, err := u.operationStorage.GetUpgradeKymaOperationByID(opId) 187 if err != nil { 188 fmt.Errorf("while geting (retrying) upgrade kyma operation %s in storage: %w", opId, err) 189 } 190 191 result = append(result, runtimeop.RuntimeOperation) 192 } 193 194 return result, nil 195 } 196 197 // update storage in corresponding upgrade factory to avoid too many storage read and write 198 func (u *upgradeKymaFactory) updateRetryingOperation(op internal.UpgradeKymaOperation) (orchestration.RuntimeOperation, error) { 199 op.UpdatedAt = time.Now() 200 op.State = orchestration.Pending 201 op.Description = "Operation retry triggered" 202 op.ProvisionerOperationID = "" 203 204 opUpdated, err := u.operationStorage.UpdateUpgradeKymaOperation(op) 205 if err != nil { 206 return orchestration.RuntimeOperation{}, fmt.Errorf("while updating (retrying) upgrade kyma operation %s in storage: %w", op.Operation.ID, err) 207 } 208 209 return opUpdated.RuntimeOperation, nil 210 } 211 212 func (u *upgradeKymaFactory) QueryOperation(orchestrationID string, r orchestration.Runtime) (bool, orchestration.RuntimeOperation, error) { 213 ops, _, _, err := u.operationStorage.ListUpgradeKymaOperationsByOrchestrationID(orchestrationID, dbmodel.OperationFilter{States: []string{orchestration.Pending}}) 214 if err != nil { 215 return false, orchestration.RuntimeOperation{}, fmt.Errorf("while listing upgrade kyma operations: %w", err) 216 } 217 for _, op := range ops { 218 if op.InstanceDetails.RuntimeID == r.RuntimeID { 219 return true, op.RuntimeOperation, nil 220 } 221 } 222 223 return false, orchestration.RuntimeOperation{}, nil 224 } 225 226 func (u *upgradeKymaFactory) QueryOperations(orchestrationID string) ([]orchestration.RuntimeOperation, error) { 227 ops, _, _, err := u.operationStorage.ListUpgradeKymaOperationsByOrchestrationID(orchestrationID, dbmodel.OperationFilter{States: []string{orchestration.Pending}}) 228 if err != nil { 229 return []orchestration.RuntimeOperation{}, fmt.Errorf("while listing kyma cluster operations: %w", err) 230 } 231 result := []orchestration.RuntimeOperation{} 232 for _, op := range ops { 233 result = append(result, op.RuntimeOperation) 234 } 235 236 return result, nil 237 } 238 239 func (u *upgradeKymaFactory) NotifyOperation(orchestrationID string, runtimeID string, oState string, notifyState orchestration.NotificationStateType) error { 240 ops, _, _, err := u.operationStorage.ListUpgradeKymaOperationsByOrchestrationID(orchestrationID, dbmodel.OperationFilter{States: []string{oState}}) 241 if err != nil { 242 return fmt.Errorf("while listing upgrade kyma operations: %w", err) 243 } 244 for _, op := range ops { 245 if op.InstanceDetails.RuntimeID == runtimeID { 246 op.RuntimeOperation.NotificationState = notifyState 247 _, err := u.operationStorage.UpdateUpgradeKymaOperation(op) 248 if err != nil { 249 return fmt.Errorf("while updating pending upgrade kyma operation %s in storage: %w", op.Operation.ID, err) 250 } 251 } 252 } 253 return nil 254 }