github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/apps/configuration/config_annotation.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 configuration 21 22 import ( 23 "encoding/json" 24 "fmt" 25 "strconv" 26 27 corev1 "k8s.io/api/core/v1" 28 ctrl "sigs.k8s.io/controller-runtime" 29 "sigs.k8s.io/controller-runtime/pkg/client" 30 31 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 32 "github.com/1aal/kubeblocks/pkg/configuration/core" 33 "github.com/1aal/kubeblocks/pkg/configuration/util" 34 "github.com/1aal/kubeblocks/pkg/constant" 35 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 36 ) 37 38 type options = func(*intctrlutil.Result) 39 40 func reconciled(status ReturnedStatus, policy string, phase appsv1alpha1.ConfigurationPhase, options ...options) intctrlutil.Result { 41 result := intctrlutil.Result{ 42 Policy: policy, 43 Phase: phase, 44 ExecResult: string(status.Status), 45 SucceedCount: status.SucceedCount, 46 ExpectedCount: status.ExpectedCount, 47 Retry: true, 48 } 49 for _, option := range options { 50 option(&result) 51 } 52 return result 53 } 54 55 func unReconciled(phase appsv1alpha1.ConfigurationPhase, revision string, message string) intctrlutil.Result { 56 return intctrlutil.Result{ 57 Phase: phase, 58 Revision: revision, 59 Message: message, 60 SucceedCount: core.NotStarted, 61 ExpectedCount: core.Unconfirmed, 62 Failed: false, 63 Retry: false, 64 } 65 } 66 67 func isReconciledResult(result intctrlutil.Result) bool { 68 return result.ExecResult != "" && result.Policy != "" 69 } 70 71 func withFailed(err error, retry bool) options { 72 return func(result *intctrlutil.Result) { 73 result.Retry = retry 74 if err != nil { 75 result.Failed = true 76 result.Message = err.Error() 77 } 78 } 79 } 80 81 func checkEnableCfgUpgrade(object client.Object) bool { 82 // check user's upgrade switch 83 // config.kubeblocks.io/disable-reconfigure = "false" 84 annotations := object.GetAnnotations() 85 value, ok := annotations[constant.DisableUpgradeInsConfigurationAnnotationKey] 86 if !ok { 87 return true 88 } 89 90 enable, err := strconv.ParseBool(value) 91 if err == nil && enable { 92 return false 93 } 94 95 return true 96 } 97 98 func updateConfigPhase(cli client.Client, ctx intctrlutil.RequestCtx, config *corev1.ConfigMap, phase appsv1alpha1.ConfigurationPhase, message string) (ctrl.Result, error) { 99 return updateConfigPhaseWithResult(cli, ctx, config, unReconciled(phase, "", message)) 100 } 101 102 func updateConfigPhaseWithResult(cli client.Client, ctx intctrlutil.RequestCtx, config *corev1.ConfigMap, result intctrlutil.Result) (ctrl.Result, error) { 103 revision, ok := config.ObjectMeta.Annotations[constant.ConfigurationRevision] 104 if !ok || revision == "" { 105 return intctrlutil.Reconciled() 106 } 107 108 patch := client.MergeFrom(config.DeepCopy()) 109 if config.ObjectMeta.Annotations == nil { 110 config.ObjectMeta.Annotations = map[string]string{} 111 } 112 113 if result.Failed && !result.Retry { 114 ctx.Log.Info(fmt.Sprintf("failed to reconcile and disable retry for configmap[%+v]", client.ObjectKeyFromObject(config))) 115 config.ObjectMeta.Annotations[constant.DisableUpgradeInsConfigurationAnnotationKey] = strconv.FormatBool(true) 116 } 117 118 GcConfigRevision(config) 119 if _, ok := config.ObjectMeta.Annotations[core.GenerateRevisionPhaseKey(revision)]; !ok || isReconciledResult(result) { 120 result.Revision = revision 121 b, _ := json.Marshal(result) 122 config.ObjectMeta.Annotations[core.GenerateRevisionPhaseKey(revision)] = string(b) 123 } 124 125 if err := cli.Patch(ctx.Ctx, config, patch); err != nil { 126 return intctrlutil.RequeueWithError(err, ctx.Log, "") 127 } 128 if result.Retry { 129 return intctrlutil.RequeueAfter(ConfigReconcileInterval, ctx.Log, "") 130 } 131 return intctrlutil.Reconciled() 132 } 133 134 // checkAndApplyConfigsChanged check if configs changed 135 func checkAndApplyConfigsChanged(client client.Client, ctx intctrlutil.RequestCtx, cm *corev1.ConfigMap) (bool, error) { 136 annotations := cm.GetAnnotations() 137 138 configData, err := json.Marshal(cm.Data) 139 if err != nil { 140 return false, err 141 } 142 143 lastConfig, ok := annotations[constant.LastAppliedConfigAnnotationKey] 144 if !ok { 145 return updateAppliedConfigs(client, ctx, cm, configData, core.ReconfigureCreatedPhase, nil) 146 } 147 148 return lastConfig == string(configData), nil 149 } 150 151 // updateAppliedConfigs updates hash label and last applied config 152 func updateAppliedConfigs(cli client.Client, ctx intctrlutil.RequestCtx, config *corev1.ConfigMap, configData []byte, reconfigurePhase string, result *intctrlutil.Result) (bool, error) { 153 154 patch := client.MergeFrom(config.DeepCopy()) 155 if config.ObjectMeta.Annotations == nil { 156 config.ObjectMeta.Annotations = map[string]string{} 157 } 158 159 GcConfigRevision(config) 160 if revision, ok := config.ObjectMeta.Annotations[constant.ConfigurationRevision]; ok && revision != "" { 161 if result == nil { 162 result = util.ToPointer(unReconciled(appsv1alpha1.CFinishedPhase, "", fmt.Sprintf("phase: %s", reconfigurePhase))) 163 } 164 result.Revision = revision 165 b, _ := json.Marshal(result) 166 config.ObjectMeta.Annotations[core.GenerateRevisionPhaseKey(revision)] = string(b) 167 } 168 config.ObjectMeta.Annotations[constant.LastAppliedConfigAnnotationKey] = string(configData) 169 hash, err := util.ComputeHash(config.Data) 170 if err != nil { 171 return false, err 172 } 173 config.ObjectMeta.Labels[constant.CMInsConfigurationHashLabelKey] = hash 174 175 newReconfigurePhase := config.ObjectMeta.Labels[constant.CMInsLastReconfigurePhaseKey] 176 if newReconfigurePhase == "" { 177 newReconfigurePhase = core.ReconfigureCreatedPhase 178 } 179 if core.ReconfigureNoChangeType != reconfigurePhase && !core.IsParametersUpdateFromManager(config) { 180 newReconfigurePhase = reconfigurePhase 181 } 182 config.ObjectMeta.Labels[constant.CMInsLastReconfigurePhaseKey] = newReconfigurePhase 183 184 // delete reconfigure-policy 185 delete(config.ObjectMeta.Annotations, constant.UpgradePolicyAnnotationKey) 186 if err := cli.Patch(ctx.Ctx, config, patch); err != nil { 187 return false, err 188 } 189 190 return true, nil 191 } 192 193 func getLastVersionConfig(cm *corev1.ConfigMap) (map[string]string, error) { 194 data := make(map[string]string, 0) 195 cfgContent, ok := cm.GetAnnotations()[constant.LastAppliedConfigAnnotationKey] 196 if !ok { 197 return data, nil 198 } 199 200 if err := json.Unmarshal([]byte(cfgContent), &data); err != nil { 201 return nil, err 202 } 203 204 return data, nil 205 } 206 207 func getUpgradePolicy(cm *corev1.ConfigMap) appsv1alpha1.UpgradePolicy { 208 const ( 209 DefaultUpgradePolicy = appsv1alpha1.NonePolicy 210 ) 211 212 annotations := cm.GetAnnotations() 213 value, ok := annotations[constant.UpgradePolicyAnnotationKey] 214 if !ok { 215 return DefaultUpgradePolicy 216 } 217 218 return appsv1alpha1.UpgradePolicy(value) 219 }