github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/apps/operations/start.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 "encoding/json" 24 "time" 25 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "sigs.k8s.io/controller-runtime/pkg/client" 28 29 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 30 "github.com/1aal/kubeblocks/pkg/constant" 31 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 32 ) 33 34 type StartOpsHandler struct{} 35 36 var _ OpsHandler = StartOpsHandler{} 37 38 func init() { 39 stopBehaviour := OpsBehaviour{ 40 FromClusterPhases: []appsv1alpha1.ClusterPhase{appsv1alpha1.StoppedClusterPhase}, 41 ToClusterPhase: appsv1alpha1.UpdatingClusterPhase, // appsv1alpha1.StartingPhase, 42 OpsHandler: StartOpsHandler{}, 43 ProcessingReasonInClusterCondition: ProcessingReasonStarting, 44 } 45 46 opsMgr := GetOpsManager() 47 opsMgr.RegisterOps(appsv1alpha1.StartType, stopBehaviour) 48 } 49 50 // ActionStartedCondition the started condition when handling the start request. 51 func (start StartOpsHandler) ActionStartedCondition(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) (*metav1.Condition, error) { 52 return appsv1alpha1.NewStartCondition(opsRes.OpsRequest), nil 53 } 54 55 // Action modifies Cluster.spec.components[*].replicas from the opsRequest 56 func (start StartOpsHandler) Action(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) error { 57 cluster := opsRes.Cluster 58 componentReplicasMap, err := start.getComponentReplicasSnapshot(cluster.Annotations) 59 if err != nil { 60 return err 61 } 62 for i, v := range cluster.Spec.ComponentSpecs { 63 replicasOfSnapshot := componentReplicasMap[v.Name] 64 if replicasOfSnapshot == 0 { 65 continue 66 } 67 // only reset the component whose replicas number is 0 68 if v.Replicas == 0 { 69 cluster.Spec.ComponentSpecs[i].Replicas = replicasOfSnapshot 70 } 71 } 72 // delete the replicas snapshot of components from the cluster. 73 delete(cluster.Annotations, constant.SnapShotForStartAnnotationKey) 74 return cli.Update(reqCtx.Ctx, cluster) 75 } 76 77 // ReconcileAction will be performed when action is done and loops till OpsRequest.status.phase is Succeed/Failed. 78 // the Reconcile function for start opsRequest. 79 func (start StartOpsHandler) ReconcileAction(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) (appsv1alpha1.OpsPhase, time.Duration, error) { 80 getExpectReplicas := func(opsRequest *appsv1alpha1.OpsRequest, componentName string) *int32 { 81 componentReplicasMap, _ := start.getComponentReplicasSnapshot(opsRequest.Annotations) 82 replicas, ok := componentReplicasMap[componentName] 83 if !ok { 84 return nil 85 } 86 return &replicas 87 } 88 89 handleComponentProgress := func(reqCtx intctrlutil.RequestCtx, 90 cli client.Client, 91 opsRes *OpsResource, 92 pgRes progressResource, 93 compStatus *appsv1alpha1.OpsRequestComponentStatus) (int32, int32, error) { 94 return handleComponentProgressForScalingReplicas(reqCtx, cli, opsRes, pgRes, compStatus, getExpectReplicas) 95 } 96 return reconcileActionWithComponentOps(reqCtx, cli, opsRes, "", handleComponentProgress) 97 } 98 99 // SaveLastConfiguration records last configuration to the OpsRequest.status.lastConfiguration 100 func (start StartOpsHandler) SaveLastConfiguration(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) error { 101 opsRequest := opsRes.OpsRequest 102 lastComponentInfo := map[string]appsv1alpha1.LastComponentConfiguration{} 103 componentReplicasMap, err := start.getComponentReplicasSnapshot(opsRes.Cluster.Annotations) 104 if err != nil { 105 return err 106 } 107 if err = start.setOpsAnnotation(reqCtx, cli, opsRes, componentReplicasMap); err != nil { 108 return err 109 } 110 for _, v := range opsRes.Cluster.Spec.ComponentSpecs { 111 replicasOfSnapshot := componentReplicasMap[v.Name] 112 if replicasOfSnapshot == 0 { 113 continue 114 } 115 if v.Replicas == 0 { 116 copyReplicas := v.Replicas 117 lastComponentInfo[v.Name] = appsv1alpha1.LastComponentConfiguration{ 118 Replicas: ©Replicas, 119 } 120 } 121 } 122 opsRequest.Status.LastConfiguration.Components = lastComponentInfo 123 return nil 124 } 125 126 // setOpsAnnotation sets the replicas snapshot of components before stopping the cluster to the annotations of this opsRequest. 127 func (start StartOpsHandler) setOpsAnnotation(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource, componentReplicasMap map[string]int32) error { 128 annotations := opsRes.OpsRequest.Annotations 129 if annotations == nil { 130 annotations = map[string]string{} 131 } 132 componentReplicasSnapshot, err := json.Marshal(componentReplicasMap) 133 if err != nil { 134 return err 135 } 136 if _, ok := opsRes.OpsRequest.Annotations[constant.SnapShotForStartAnnotationKey]; !ok { 137 patch := client.MergeFrom(opsRes.OpsRequest.DeepCopy()) 138 annotations[constant.SnapShotForStartAnnotationKey] = string(componentReplicasSnapshot) 139 opsRes.OpsRequest.Annotations = annotations 140 return cli.Patch(reqCtx.Ctx, opsRes.OpsRequest, patch) 141 } 142 return nil 143 } 144 145 // getComponentReplicasSnapshot gets the replicas snapshot of components from annotations. 146 func (start StartOpsHandler) getComponentReplicasSnapshot(annotations map[string]string) (map[string]int32, error) { 147 componentReplicasMap := map[string]int32{} 148 snapshotForStart := annotations[constant.SnapShotForStartAnnotationKey] 149 if len(snapshotForStart) != 0 { 150 if err := json.Unmarshal([]byte(snapshotForStart), &componentReplicasMap); err != nil { 151 return componentReplicasMap, err 152 } 153 } 154 return componentReplicasMap, nil 155 }