github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/apps/operations/stop.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 StopOpsHandler struct{}
    35  
    36  var _ OpsHandler = StopOpsHandler{}
    37  
    38  func init() {
    39  	stopBehaviour := OpsBehaviour{
    40  		FromClusterPhases:                  appsv1alpha1.GetClusterUpRunningPhases(),
    41  		ToClusterPhase:                     appsv1alpha1.StoppingClusterPhase,
    42  		OpsHandler:                         StopOpsHandler{},
    43  		ProcessingReasonInClusterCondition: ProcessingReasonStopping,
    44  	}
    45  
    46  	opsMgr := GetOpsManager()
    47  	opsMgr.RegisterOps(appsv1alpha1.StopType, stopBehaviour)
    48  }
    49  
    50  // ActionStartedCondition the started condition when handling the stop request.
    51  func (stop StopOpsHandler) ActionStartedCondition(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) (*metav1.Condition, error) {
    52  	return appsv1alpha1.NewStopCondition(opsRes.OpsRequest), nil
    53  }
    54  
    55  // Action modifies Cluster.spec.components[*].replicas from the opsRequest
    56  func (stop StopOpsHandler) Action(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) error {
    57  	var (
    58  		expectReplicas       = int32(0)
    59  		componentReplicasMap = map[string]int32{}
    60  		cluster              = opsRes.Cluster
    61  	)
    62  	if _, ok := cluster.Annotations[constant.SnapShotForStartAnnotationKey]; ok {
    63  		return nil
    64  	}
    65  	for i, v := range cluster.Spec.ComponentSpecs {
    66  		componentReplicasMap[v.Name] = v.Replicas
    67  		cluster.Spec.ComponentSpecs[i].Replicas = expectReplicas
    68  	}
    69  	componentReplicasSnapshot, err := json.Marshal(componentReplicasMap)
    70  	if err != nil {
    71  		return err
    72  	}
    73  	if cluster.Annotations == nil {
    74  		cluster.Annotations = map[string]string{}
    75  	}
    76  	// record the replicas snapshot of components to the annotations of cluster before stopping the cluster.
    77  	cluster.Annotations[constant.SnapShotForStartAnnotationKey] = string(componentReplicasSnapshot)
    78  	return cli.Update(reqCtx.Ctx, cluster)
    79  }
    80  
    81  // ReconcileAction will be performed when action is done and loops till OpsRequest.status.phase is Succeed/Failed.
    82  // the Reconcile function for stop opsRequest.
    83  func (stop StopOpsHandler) ReconcileAction(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) (appsv1alpha1.OpsPhase, time.Duration, error) {
    84  	getExpectReplicas := func(opsRequest *appsv1alpha1.OpsRequest, componentName string) *int32 {
    85  		expectReplicas := int32(0)
    86  		return &expectReplicas
    87  	}
    88  	handleComponentProgress := func(reqCtx intctrlutil.RequestCtx,
    89  		cli client.Client,
    90  		opsRes *OpsResource,
    91  		pgRes progressResource,
    92  		compStatus *appsv1alpha1.OpsRequestComponentStatus) (int32, int32, error) {
    93  		expectProgressCount, completedCount, err := handleComponentProgressForScalingReplicas(reqCtx, cli, opsRes, pgRes, compStatus, getExpectReplicas)
    94  		if err != nil {
    95  			return expectProgressCount, completedCount, err
    96  		}
    97  		return expectProgressCount, completedCount, nil
    98  	}
    99  	return reconcileActionWithComponentOps(reqCtx, cli, opsRes, "", handleComponentProgress)
   100  }
   101  
   102  // SaveLastConfiguration records last configuration to the OpsRequest.status.lastConfiguration
   103  func (stop StopOpsHandler) SaveLastConfiguration(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) error {
   104  	opsRequest := opsRes.OpsRequest
   105  	lastComponentInfo := map[string]appsv1alpha1.LastComponentConfiguration{}
   106  	for _, v := range opsRes.Cluster.Spec.ComponentSpecs {
   107  		if v.Replicas != 0 {
   108  			podNames, err := getCompPodNamesBeforeScaleDownReplicas(reqCtx, cli, *opsRes.Cluster, v.Name)
   109  			if err != nil {
   110  				return err
   111  			}
   112  			copyReplicas := v.Replicas
   113  			lastComponentInfo[v.Name] = appsv1alpha1.LastComponentConfiguration{
   114  				Replicas: &copyReplicas,
   115  				TargetResources: map[appsv1alpha1.ComponentResourceKey][]string{
   116  					appsv1alpha1.PodsCompResourceKey: podNames,
   117  				},
   118  			}
   119  		}
   120  	}
   121  	opsRequest.Status.LastConfiguration.Components = lastComponentInfo
   122  	return nil
   123  }