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  }