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  }