Skip to content

Commit

Permalink
chore: add reconfigure status for configmap
Browse files Browse the repository at this point in the history
  • Loading branch information
sophon-zt committed Oct 19, 2023
1 parent 5a1d86b commit 37945f7
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 27 deletions.
62 changes: 55 additions & 7 deletions controllers/apps/configuration/config_annotation.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package configuration

import (
"encoding/json"
"fmt"
"strconv"

corev1 "k8s.io/api/core/v1"
Expand All @@ -34,6 +35,43 @@ import (
intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
)

type options = func(*intctrlutil.Result)

func reconciled(status ReturnedStatus, policy string, phase appsv1alpha1.ConfigurationPhase, options ...options) intctrlutil.Result {
result := intctrlutil.Result{
Policy: policy,
Phase: phase,
ExecResult: string(status.Status),
SucceedCount: status.SucceedCount,
ExpectedCount: status.ExpectedCount,
Retry: true,
}
for _, option := range options {
option(&result)
}
return result
}

func unReconciled(phase appsv1alpha1.ConfigurationPhase, revision string, message string) intctrlutil.Result {
return intctrlutil.Result{
Phase: phase,
Revision: revision,
Message: message,
Failed: false,
Retry: false,
}
}

func withFailed(err error, retry bool) options {
return func(result *intctrlutil.Result) {
result.Retry = retry
if err != nil {
result.Failed = true
result.Message = err.Error()
}
}
}

func checkEnableCfgUpgrade(object client.Object) bool {
// check user's upgrade switch
// config.kubeblocks.io/disable-reconfigure = "false"
Expand All @@ -51,7 +89,11 @@ func checkEnableCfgUpgrade(object client.Object) bool {
return true
}

func updateConfigPhase(cli client.Client, ctx intctrlutil.RequestCtx, config *corev1.ConfigMap, phase appsv1alpha1.ConfigurationPhase, failed bool, retry bool) (ctrl.Result, error) {
func updateConfigPhase(cli client.Client, ctx intctrlutil.RequestCtx, config *corev1.ConfigMap, phase appsv1alpha1.ConfigurationPhase, message string) (ctrl.Result, error) {
return updateConfigPhaseWithResult(cli, ctx, config, unReconciled(phase, "", message))
}

func updateConfigPhaseWithResult(cli client.Client, ctx intctrlutil.RequestCtx, config *corev1.ConfigMap, result intctrlutil.Result) (ctrl.Result, error) {
revision, ok := config.ObjectMeta.Annotations[constant.ConfigurationRevision]
if !ok || revision == "" {
return intctrlutil.Reconciled()
Expand All @@ -62,16 +104,18 @@ func updateConfigPhase(cli client.Client, ctx intctrlutil.RequestCtx, config *co
config.ObjectMeta.Annotations = map[string]string{}
}

if failed {
if result.Failed {
config.ObjectMeta.Annotations[constant.DisableUpgradeInsConfigurationAnnotationKey] = strconv.FormatBool(true)
}

GcConfigRevision(config)
config.ObjectMeta.Annotations[core.GenerateRevisionPhaseKey(revision)] = string(phase)
result.Revision = revision
b, _ := json.Marshal(result)
config.ObjectMeta.Annotations[core.GenerateRevisionPhaseKey(revision)] = string(b)
if err := cli.Patch(ctx.Ctx, config, patch); err != nil {
return intctrlutil.RequeueWithError(err, ctx.Log, "")
}
if retry {
if result.Retry {
return intctrlutil.RequeueAfter(ConfigReconcileInterval, ctx.Log, "")
}
return intctrlutil.Reconciled()
Expand All @@ -88,14 +132,14 @@ func checkAndApplyConfigsChanged(client client.Client, ctx intctrlutil.RequestCt

lastConfig, ok := annotations[constant.LastAppliedConfigAnnotationKey]
if !ok {
return updateAppliedConfigs(client, ctx, cm, configData, core.ReconfigureCreatedPhase)
return updateAppliedConfigs(client, ctx, cm, configData, core.ReconfigureCreatedPhase, nil)
}

return lastConfig == string(configData), nil
}

// updateAppliedConfigs updates hash label and last applied config
func updateAppliedConfigs(cli client.Client, ctx intctrlutil.RequestCtx, config *corev1.ConfigMap, configData []byte, reconfigurePhase string) (bool, error) {
func updateAppliedConfigs(cli client.Client, ctx intctrlutil.RequestCtx, config *corev1.ConfigMap, configData []byte, reconfigurePhase string, result *intctrlutil.Result) (bool, error) {

patch := client.MergeFrom(config.DeepCopy())
if config.ObjectMeta.Annotations == nil {
Expand All @@ -104,7 +148,11 @@ func updateAppliedConfigs(cli client.Client, ctx intctrlutil.RequestCtx, config

GcConfigRevision(config)
if revision, ok := config.ObjectMeta.Annotations[constant.ConfigurationRevision]; ok && revision != "" {
config.ObjectMeta.Annotations[core.GenerateRevisionPhaseKey(revision)] = string(appsv1alpha1.CFinishedPhase)
if result == nil {
result = util.ToPointer(unReconciled(appsv1alpha1.CFinishedPhase, "", fmt.Sprintf("phase: %s", reconfigurePhase)))
}
b, _ := json.Marshal(result)
config.ObjectMeta.Annotations[core.GenerateRevisionPhaseKey(revision)] = string(b)
}
config.ObjectMeta.Annotations[constant.LastAppliedConfigAnnotationKey] = string(configData)
hash, err := util.ComputeHash(config.Data)
Expand Down
7 changes: 4 additions & 3 deletions controllers/apps/configuration/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ func mockConfigResource() (*corev1.ConfigMap, *appsv1alpha1.ConfigConstraint) {
constant.CMConfigurationSpecProviderLabelKey, configSpecName,
constant.CMConfigurationTypeLabelKey, constant.ConfigInstanceType,
),
testapps.WithAnnotations(constant.KBParameterUpdateSourceAnnotationKey,
constant.ReconfigureManagerSource,
testapps.WithAnnotations(
constant.KBParameterUpdateSourceAnnotationKey, constant.ReconfigureManagerSource,
constant.ConfigurationRevision, "1",
constant.CMInsEnableRerenderTemplateKey, "true"))

By("Create a config constraint obj")
Expand Down Expand Up @@ -158,7 +159,7 @@ func initConfiguration(resourceCtx *intctrlutil.ResourceCtx, synthesizedComponen
}).
Prepare().
UpdateConfiguration(). // reconcile Configuration
Configuration(). // sync Configuration
Configuration(). // sync Configuration
CreateConfigTemplate().
UpdateConfigRelatedObject().
UpdateConfigurationStatus().
Expand Down
60 changes: 46 additions & 14 deletions controllers/apps/configuration/reconfigure_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ const (
ConfigReconcileInterval = time.Second * 1
)

const (
configurationNoChangedMessage = "the configuration file has not been modified, skip reconfigure"
configurationNotUsingMessage = "the configmap is not used by any container, skip reconfigure"
configurationNotRelatedComponentMessage = "related component does not found any configSpecs, skip reconfigure"
)

var ConfigurationRequiredLabels = []string{
constant.AppNameLabelKey,
constant.AppInstanceLabelKey,
Expand Down Expand Up @@ -102,7 +108,7 @@ func (r *ReconfigureReconciler) Reconcile(ctx context.Context, req ctrl.Request)
if err != nil {
return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "failed to check last-applied-configuration")
} else if isAppliedConfigs {
return updateConfigPhase(r.Client, reqCtx, config, appsv1alpha1.CFinishedPhase, false, false)
return updateConfigPhase(r.Client, reqCtx, config, appsv1alpha1.CFinishedPhase, configurationNoChangedMessage)
}

// process configuration without ConfigConstraints
Expand Down Expand Up @@ -163,7 +169,7 @@ func (r *ReconfigureReconciler) sync(reqCtx intctrlutil.RequestCtx, configMap *c
if configPatch != nil && !configPatch.IsModify {
reqCtx.Recorder.Eventf(configMap, corev1.EventTypeNormal, appsv1alpha1.ReasonReconfigureRunning,
"nothing changed, skip reconfigure")
return r.updateConfigCMStatus(reqCtx, configMap, core.ReconfigureNoChangeType)
return r.updateConfigCMStatus(reqCtx, configMap, core.ReconfigureNoChangeType, nil)
}

if configPatch != nil {
Expand Down Expand Up @@ -197,15 +203,17 @@ func (r *ReconfigureReconciler) sync(reqCtx intctrlutil.RequestCtx, configMap *c
}
if reconcileContext.ConfigSpec == nil {
reqCtx.Log.Info(fmt.Sprintf("not found configSpec[%s] in the component[%s].", configSpecName, componentName))
reqCtx.Recorder.Eventf(configMap, corev1.EventTypeWarning, appsv1alpha1.ReasonReconfigureFailed,
"related component does not have any configSpecs, skip reconfigure")
return updateConfigPhase(r.Client, reqCtx, configMap, appsv1alpha1.CFinishedPhase, false, false)
reqCtx.Recorder.Eventf(configMap,
corev1.EventTypeWarning,
appsv1alpha1.ReasonReconfigureFailed,
configurationNotRelatedComponentMessage)
return updateConfigPhase(r.Client, reqCtx, configMap, appsv1alpha1.CFinishedPhase, configurationNotRelatedComponentMessage)
}
if len(reconcileContext.StatefulSets) == 0 && len(reconcileContext.Deployments) == 0 {
reqCtx.Recorder.Eventf(configMap,
corev1.EventTypeWarning, appsv1alpha1.ReasonReconfigureFailed,
"the configmap is not used by any container, skip reconfigure")
return updateConfigPhase(r.Client, reqCtx, configMap, appsv1alpha1.CFinishedPhase, false, false)
return updateConfigPhase(r.Client, reqCtx, configMap, appsv1alpha1.CFinishedPhase, configurationNotUsingMessage)
}

return r.performUpgrade(reconfigureParams{
Expand All @@ -227,13 +235,13 @@ func (r *ReconfigureReconciler) sync(reqCtx intctrlutil.RequestCtx, configMap *c
})
}

func (r *ReconfigureReconciler) updateConfigCMStatus(reqCtx intctrlutil.RequestCtx, cfg *corev1.ConfigMap, reconfigureType string) (ctrl.Result, error) {
func (r *ReconfigureReconciler) updateConfigCMStatus(reqCtx intctrlutil.RequestCtx, cfg *corev1.ConfigMap, reconfigureType string, result *intctrlutil.Result) (ctrl.Result, error) {
configData, err := json.Marshal(cfg.Data)
if err != nil {
return intctrlutil.RequeueWithErrorAndRecordEvent(cfg, r.Recorder, err, reqCtx.Log)
}

if ok, err := updateAppliedConfigs(r.Client, reqCtx, cfg, configData, reconfigureType); err != nil || !ok {
if ok, err := updateAppliedConfigs(r.Client, reqCtx, cfg, configData, reconfigureType, result); err != nil || !ok {
return intctrlutil.RequeueAfter(ConfigReconcileInterval, reqCtx.Log, "failed to patch status and retry...", "error", err)
}

Expand Down Expand Up @@ -263,21 +271,45 @@ func (r *ReconfigureReconciler) performUpgrade(params reconfigureParams) (ctrl.R

switch returnedStatus.Status {
default:
return updateConfigPhase(params.Client, params.Ctx, params.ConfigMap, appsv1alpha1.CFailedAndPausePhase, false, false)
return updateConfigPhaseWithResult(
params.Client,
params.Ctx,
params.ConfigMap,
reconciled(returnedStatus, policy.GetPolicyName(), appsv1alpha1.CFailedAndPausePhase,
withFailed(core.MakeError("unknown status"), false)),
)
case ESFailedAndRetry:
return updateConfigPhase(params.Client, params.Ctx, params.ConfigMap, appsv1alpha1.CFailedPhase, false, true)
return updateConfigPhaseWithResult(
params.Client,
params.Ctx,
params.ConfigMap,
reconciled(returnedStatus, policy.GetPolicyName(), appsv1alpha1.CFailedPhase,
withFailed(err, true)),
)
case ESRetry:
return updateConfigPhase(params.Client, params.Ctx, params.ConfigMap, appsv1alpha1.CUpgradingPhase, false, true)
return updateConfigPhaseWithResult(
params.Client,
params.Ctx,
params.ConfigMap,
reconciled(returnedStatus, policy.GetPolicyName(), appsv1alpha1.CUpgradingPhase),
)
case ESFailed:
return updateConfigPhase(params.Client, params.Ctx, params.ConfigMap, appsv1alpha1.CFailedAndPausePhase, true, false)
return updateConfigPhaseWithResult(
params.Client,
params.Ctx,
params.ConfigMap,
reconciled(returnedStatus, policy.GetPolicyName(), appsv1alpha1.CFailedAndPausePhase),
)
case ESNone:
params.Ctx.Recorder.Eventf(params.ConfigMap,
params.Ctx.Recorder.Eventf(
params.ConfigMap,
corev1.EventTypeNormal,
appsv1alpha1.ReasonReconfigureSucceed,
"the reconfigure[%s] request[%s] has been processed successfully",
policy.GetPolicyName(),
getOpsRequestID(params.ConfigMap))
return r.updateConfigCMStatus(params.Ctx, params.ConfigMap, policy.GetPolicyName())
result := reconciled(returnedStatus, policy.GetPolicyName(), appsv1alpha1.CFinishedPhase)
return r.updateConfigCMStatus(params.Ctx, params.ConfigMap, policy.GetPolicyName(), &result)
}
}

Expand Down
24 changes: 22 additions & 2 deletions controllers/apps/configuration/revision.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package configuration

import (
"encoding/json"
"sort"
"strconv"
"strings"
Expand All @@ -29,12 +30,14 @@ import (
appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1"
"github.com/apecloud/kubeblocks/pkg/configuration/core"
"github.com/apecloud/kubeblocks/pkg/constant"
intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
)

type ConfigurationRevision struct {
Revision int64
StrRevision string
Phase appsv1alpha1.ConfigurationPhase
Result intctrlutil.Result
}

const revisionHistoryLimit = 10
Expand Down Expand Up @@ -78,18 +81,35 @@ func RetrieveRevision(annotations map[string]string) []ConfigurationRevision {
return revisions
}

func parseRevision(revision string, phase string) (ConfigurationRevision, error) {
func parseRevision(revision string, data string) (ConfigurationRevision, error) {
v, err := strconv.ParseInt(revision, 10, 64)
if err != nil {
return ConfigurationRevision{}, err
}
result := parseResult(data, revision)
return ConfigurationRevision{
StrRevision: revision,
Revision: v,
Phase: appsv1alpha1.ConfigurationPhase(phase),
Phase: result.Phase,
Result: result,
}, nil
}

func parseResult(data string, revision string) intctrlutil.Result {
result := intctrlutil.Result{
Revision: revision,
}
data = strings.TrimSpace(data)
if data == "" {
return result
}
err := json.Unmarshal([]byte(data), &result)
if err != nil {
result.Phase = appsv1alpha1.ConfigurationPhase(data)
}
return result
}

func GetCurrentRevision(annotations map[string]string) string {
if len(annotations) == 0 {
return ""
Expand Down
2 changes: 1 addition & 1 deletion controllers/apps/configuration/revision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestGcConfigRevision(t *testing.T) {
AddAnnotations(core.GenerateRevisionPhaseKey("9"), "finished").
AddAnnotations(core.GenerateRevisionPhaseKey("10"), "finished").
AddAnnotations(core.GenerateRevisionPhaseKey("11"), "finished").
AddAnnotations(core.GenerateRevisionPhaseKey("12"), "finished").
AddAnnotations(core.GenerateRevisionPhaseKey("12"), `{"Phase":"Finished","Revision":"12","Policy":"","ExecResult":"","SucceedCount":0,"ExpectedCount":0,"Retry":false,"Failed":false,"Message":"the configuration file has not been modified, skip reconfigure"}`).
GetObject()

assert.Equal(t, 12, len(RetrieveRevision(cm.GetAnnotations())))
Expand Down
14 changes: 14 additions & 0 deletions pkg/controllerutil/config_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ type ConfigEventContext struct {
PolicyStatus core.PolicyExecStatus
}

type Result struct {
Phase v1alpha1.ConfigurationPhase
Revision string
Policy string
ExecResult string

SucceedCount int32
ExpectedCount int32

Retry bool
Failed bool
Message string
}

type ConfigEventHandler interface {
Handle(eventContext ConfigEventContext, lastOpsRequest string, phase v1alpha1.OpsPhase, err error) error
}
Expand Down

0 comments on commit 37945f7

Please sign in to comment.