github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/apps/operations/ops_manager.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 "sync" 24 "time" 25 26 ctrl "sigs.k8s.io/controller-runtime" 27 "sigs.k8s.io/controller-runtime/pkg/client" 28 29 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 30 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 31 ) 32 33 var ( 34 opsManagerOnce sync.Once 35 opsManager *OpsManager 36 ) 37 38 // RegisterOps registers operation with OpsType and OpsBehaviour 39 func (opsMgr *OpsManager) RegisterOps(opsType appsv1alpha1.OpsType, opsBehaviour OpsBehaviour) { 40 opsManager.OpsMap[opsType] = opsBehaviour 41 appsv1alpha1.OpsRequestBehaviourMapper[opsType] = appsv1alpha1.OpsRequestBehaviour{ 42 FromClusterPhases: opsBehaviour.FromClusterPhases, 43 ToClusterPhase: opsBehaviour.ToClusterPhase, 44 ProcessingReasonInClusterCondition: opsBehaviour.ProcessingReasonInClusterCondition, 45 } 46 } 47 48 // Do the entry function for handling OpsRequest 49 func (opsMgr *OpsManager) Do(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) (*ctrl.Result, error) { 50 var ( 51 opsBehaviour OpsBehaviour 52 err error 53 ok bool 54 opsRequest = opsRes.OpsRequest 55 ) 56 if opsBehaviour, ok = opsMgr.OpsMap[opsRequest.Spec.Type]; !ok || opsBehaviour.OpsHandler == nil { 57 return nil, patchOpsHandlerNotSupported(reqCtx.Ctx, cli, opsRes) 58 } 59 // validate OpsRequest.spec 60 if err = opsRequest.Validate(reqCtx.Ctx, cli, opsRes.Cluster, true); err != nil { 61 if patchErr := patchValidateErrorCondition(reqCtx.Ctx, cli, opsRes, err.Error()); patchErr != nil { 62 return nil, patchErr 63 } 64 return nil, err 65 } 66 // validate entry condition for OpsRequest 67 if opsRequest.Status.Phase == appsv1alpha1.OpsPendingPhase { 68 if err = validateOpsWaitingPhase(opsRes.Cluster, opsRequest, opsBehaviour); err != nil { 69 // check if the error is caused by WaitForClusterPhaseErr error 70 if _, ok := err.(*WaitForClusterPhaseErr); ok { 71 return intctrlutil.ResultToP(intctrlutil.RequeueAfter(time.Second, reqCtx.Log, "")) 72 } 73 if patchErr := patchValidateErrorCondition(reqCtx.Ctx, cli, opsRes, err.Error()); patchErr != nil { 74 return nil, patchErr 75 } 76 return nil, err 77 } 78 } 79 80 if opsRequest.Status.Phase != appsv1alpha1.OpsCreatingPhase { 81 // If the operation causes the cluster phase to change, the cluster needs to be locked. 82 // At the same time, only one operation is running if these operations are mutually exclusive(exist opsBehaviour.ToClusterPhase). 83 if err = addOpsRequestAnnotationToCluster(reqCtx.Ctx, cli, opsRes, opsBehaviour); err != nil { 84 return nil, err 85 } 86 87 opsDeepCopy := opsRequest.DeepCopy() 88 // save last configuration into status.lastConfiguration 89 if err = opsBehaviour.OpsHandler.SaveLastConfiguration(reqCtx, cli, opsRes); err != nil { 90 return nil, err 91 } 92 93 return &ctrl.Result{}, patchOpsRequestToCreating(reqCtx, cli, opsRes, opsDeepCopy, opsBehaviour.OpsHandler) 94 } 95 96 if err = opsBehaviour.OpsHandler.Action(reqCtx, cli, opsRes); err != nil { 97 // patch the status.phase to Failed when the error is FastFaileError, which means the operation is failed and there is no need to retry 98 if _, ok := err.(*FastFaileError); ok { 99 if patchErr := patchFastFailErrorCondition(reqCtx.Ctx, cli, opsRes, err); patchErr != nil { 100 return nil, patchErr 101 } 102 } 103 return nil, err 104 } 105 106 return nil, nil 107 } 108 109 // Reconcile entry function when OpsRequest.status.phase is Running. 110 // loops till the operation is completed. 111 func (opsMgr *OpsManager) Reconcile(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource) (time.Duration, error) { 112 var ( 113 opsBehaviour OpsBehaviour 114 ok bool 115 err error 116 requeueAfter time.Duration 117 opsRequestPhase appsv1alpha1.OpsPhase 118 opsRequest = opsRes.OpsRequest 119 ) 120 121 if opsBehaviour, ok = opsMgr.OpsMap[opsRes.OpsRequest.Spec.Type]; !ok || opsBehaviour.OpsHandler == nil { 122 return 0, patchOpsHandlerNotSupported(reqCtx.Ctx, cli, opsRes) 123 } 124 opsRes.ToClusterPhase = opsBehaviour.ToClusterPhase 125 if opsRequestPhase, requeueAfter, err = opsBehaviour.OpsHandler.ReconcileAction(reqCtx, cli, opsRes); err != nil && 126 !isOpsRequestFailedPhase(opsRequestPhase) { 127 // if the opsRequest phase is not failed, skipped 128 return requeueAfter, err 129 } 130 switch opsRequestPhase { 131 case appsv1alpha1.OpsSucceedPhase: 132 if opsRequest.Status.Phase == appsv1alpha1.OpsCancellingPhase { 133 return 0, PatchOpsStatus(reqCtx.Ctx, cli, opsRes, appsv1alpha1.OpsCancelledPhase, appsv1alpha1.NewCancelSucceedCondition(opsRequest.Name)) 134 } 135 return 0, PatchOpsStatus(reqCtx.Ctx, cli, opsRes, opsRequestPhase, appsv1alpha1.NewSucceedCondition(opsRequest)) 136 case appsv1alpha1.OpsFailedPhase: 137 if opsRequest.Status.Phase == appsv1alpha1.OpsCancellingPhase { 138 return 0, PatchOpsStatus(reqCtx.Ctx, cli, opsRes, appsv1alpha1.OpsCancelledPhase, appsv1alpha1.NewCancelFailedCondition(opsRequest, err)) 139 } 140 return 0, PatchOpsStatus(reqCtx.Ctx, cli, opsRes, opsRequestPhase, appsv1alpha1.NewFailedCondition(opsRequest, err)) 141 default: 142 return requeueAfter, nil 143 } 144 } 145 146 func GetOpsManager() *OpsManager { 147 opsManagerOnce.Do(func() { 148 opsManager = &OpsManager{OpsMap: make(map[appsv1alpha1.OpsType]OpsBehaviour)} 149 }) 150 return opsManager 151 }