github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/apps/operations/upgrade.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package operations
    21  
    22  import (
    23  	"context"
    24  	"reflect"
    25  	"time"
    26  
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"sigs.k8s.io/controller-runtime/pkg/client"
    29  
    30  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    31  	intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil"
    32  )
    33  
    34  type upgradeOpsHandler struct{}
    35  
    36  var _ OpsHandler = upgradeOpsHandler{}
    37  
    38  func init() {
    39  	upgradeBehaviour := OpsBehaviour{
    40  		// if cluster is Abnormal or Failed, new opsRequest may can repair it.
    41  		// TODO: we should add "force" flag for these opsRequest.
    42  		FromClusterPhases:                  appsv1alpha1.GetClusterUpRunningPhases(),
    43  		ToClusterPhase:                     appsv1alpha1.UpdatingClusterPhase,
    44  		OpsHandler:                         upgradeOpsHandler{},
    45  		ProcessingReasonInClusterCondition: ProcessingReasonVersionUpgrading,
    46  	}
    47  
    48  	opsMgr := GetOpsManager()
    49  	opsMgr.RegisterOps(appsv1alpha1.UpgradeType, upgradeBehaviour)
    50  }
    51  
    52  // ActionStartedCondition the started condition when handle the upgrade request.
    53  func (u upgradeOpsHandler) ActionStartedCondition(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) (*metav1.Condition, error) {
    54  	return appsv1alpha1.NewHorizontalScalingCondition(opsRes.OpsRequest), nil
    55  }
    56  
    57  // Action modifies Cluster.spec.clusterVersionRef with opsRequest.spec.upgrade.clusterVersionRef
    58  func (u upgradeOpsHandler) Action(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) error {
    59  	opsRes.Cluster.Spec.ClusterVersionRef = opsRes.OpsRequest.Spec.Upgrade.ClusterVersionRef
    60  	return cli.Update(reqCtx.Ctx, opsRes.Cluster)
    61  }
    62  
    63  // ReconcileAction will be performed when action is done and loops till OpsRequest.status.phase is Succeed/Failed.
    64  // the Reconcile function for upgrade opsRequest.
    65  func (u upgradeOpsHandler) ReconcileAction(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) (appsv1alpha1.OpsPhase, time.Duration, error) {
    66  	return reconcileActionWithComponentOps(reqCtx, cli, opsRes, "upgrade", handleComponentStatusProgress)
    67  }
    68  
    69  // SaveLastConfiguration records last configuration to the OpsRequest.status.lastConfiguration
    70  func (u upgradeOpsHandler) SaveLastConfiguration(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) error {
    71  	compsStatus, err := u.getUpgradeComponentsStatus(reqCtx, cli, opsRes)
    72  	if err != nil {
    73  		return err
    74  	}
    75  	opsRes.OpsRequest.Status.LastConfiguration.ClusterVersionRef = opsRes.Cluster.Spec.ClusterVersionRef
    76  	opsRes.OpsRequest.Status.Components = compsStatus
    77  	return nil
    78  }
    79  
    80  // getUpgradeComponentsStatus compares the ClusterVersions before and after upgrade, and get the changed components map.
    81  func (u upgradeOpsHandler) getUpgradeComponentsStatus(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) (map[string]appsv1alpha1.OpsRequestComponentStatus, error) {
    82  	lastComponents, err := u.getClusterComponentVersionMap(reqCtx.Ctx, cli,
    83  		opsRes.Cluster.Spec.ClusterVersionRef)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	components, err := u.getClusterComponentVersionMap(reqCtx.Ctx, cli,
    88  		opsRes.OpsRequest.Spec.Upgrade.ClusterVersionRef)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	// get the changed components map
    93  	changedComponentMap := map[string]struct{}{}
    94  	for k, v := range components {
    95  		lastComp := lastComponents[k]
    96  		if !reflect.DeepEqual(v, lastComp) {
    97  			changedComponentMap[k] = struct{}{}
    98  		}
    99  	}
   100  	// get the changed components name map, and record the components infos to OpsRequest.status.
   101  	compStatusMap := map[string]appsv1alpha1.OpsRequestComponentStatus{}
   102  	for _, comp := range opsRes.Cluster.Spec.ComponentSpecs {
   103  		if _, ok := changedComponentMap[comp.ComponentDefRef]; !ok {
   104  			continue
   105  		}
   106  		compStatusMap[comp.Name] = appsv1alpha1.OpsRequestComponentStatus{
   107  			Phase: appsv1alpha1.UpdatingClusterCompPhase,
   108  		}
   109  	}
   110  	return compStatusMap, nil
   111  }
   112  
   113  // getClusterComponentVersionMap gets the components of ClusterVersion and converts the component list to map.
   114  func (u upgradeOpsHandler) getClusterComponentVersionMap(ctx context.Context,
   115  	cli client.Client, clusterVersionName string) (map[string]appsv1alpha1.ClusterComponentVersion, error) {
   116  	clusterVersion := &appsv1alpha1.ClusterVersion{}
   117  	if err := cli.Get(ctx, client.ObjectKey{Name: clusterVersionName}, clusterVersion); err != nil {
   118  		return nil, client.IgnoreNotFound(err)
   119  	}
   120  	components := map[string]appsv1alpha1.ClusterComponentVersion{}
   121  	for _, v := range clusterVersion.Spec.ComponentVersions {
   122  		components[v.ComponentDefRef] = v
   123  	}
   124  	return components, nil
   125  }