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 }