diff --git a/apis/apps/v1/componentdefinition_types.go b/apis/apps/v1/componentdefinition_types.go index d13980a9e79..b7015ef4771 100644 --- a/apis/apps/v1/componentdefinition_types.go +++ b/apis/apps/v1/componentdefinition_types.go @@ -1751,9 +1751,9 @@ type ComponentLifecycleActions struct { // // The container executing this action has access to following variables: // - // - KB_ACCOUNT_NAME: The name of the system account to be created. + // - KB_ACCOUNT_NAME: The name of the system account to be manipulated. // - KB_ACCOUNT_PASSWORD: The password for the system account. - // - KB_ACCOUNT_STATEMENT: The statement used to create the system account. + // - KB_ACCOUNT_STATEMENT: The statement used to manipulate the system account. // // Note: This field is immutable once it has been set. // diff --git a/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml b/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml index ec919134eab..a3568885f25 100644 --- a/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml +++ b/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml @@ -4501,9 +4501,9 @@ spec: The container executing this action has access to following variables: - - KB_ACCOUNT_NAME: The name of the system account to be created. + - KB_ACCOUNT_NAME: The name of the system account to be manipulated. - KB_ACCOUNT_PASSWORD: The password for the system account. - - KB_ACCOUNT_STATEMENT: The statement used to create the system account. + - KB_ACCOUNT_STATEMENT: The statement used to manipulate the system account. Note: This field is immutable once it has been set. diff --git a/controllers/apps/componentdefinition_controller.go b/controllers/apps/componentdefinition_controller.go index 25040a3a4cd..6dc6d24d18d 100644 --- a/controllers/apps/componentdefinition_controller.go +++ b/controllers/apps/componentdefinition_controller.go @@ -342,19 +342,23 @@ func (r *ComponentDefinitionReconciler) validateConfigs(cli client.Client, rctx func (r *ComponentDefinitionReconciler) validateSystemAccounts(cli client.Client, rctx intctrlutil.RequestCtx, cmpd *appsv1.ComponentDefinition) error { - for _, v := range cmpd.Spec.SystemAccounts { - if !v.InitAccount && (cmpd.Spec.LifecycleActions == nil || cmpd.Spec.LifecycleActions.AccountProvision == nil) { - return fmt.Errorf(`the AccountProvision action is needed to provision system account %s`, v.Name) - } - } if !checkUniqueItemWithValue(cmpd.Spec.SystemAccounts, "Name", nil) { return fmt.Errorf("duplicate system accounts are not allowed") } + + hasNonInitAccount := false for _, account := range cmpd.Spec.SystemAccounts { - if !account.InitAccount && (account.Statement == nil || len(account.Statement.Create) == 0) { + if account.InitAccount { + continue + } + hasNonInitAccount = true + if account.Statement == nil || len(account.Statement.Create) == 0 { return fmt.Errorf("the create statement must be provided to provision system account: %s", account.Name) } } + if hasNonInitAccount && (cmpd.Spec.LifecycleActions == nil || cmpd.Spec.LifecycleActions.AccountProvision == nil) { + return fmt.Errorf("the AccountProvision action is needed to provision system accounts") + } return nil } diff --git a/controllers/apps/transformer_component_account_provision.go b/controllers/apps/transformer_component_account_provision.go index b3d5762f5b5..72974187426 100644 --- a/controllers/apps/transformer_component_account_provision.go +++ b/controllers/apps/transformer_component_account_provision.go @@ -85,7 +85,7 @@ func (t *componentAccountProvisionTransformer) Transform(ctx graph.TransformCont protoNameSet := sets.New(maps.Keys(secrets)...) cond := t.provisionCond(transCtx) - provisionedNameSet := sets.New(strings.Split(cond.Message, ",")...) + provisionedNameSet := t.getProvisionedAccounts(cond) createSet, deleteSet, updateSet := setDiff(provisionedNameSet, protoNameSet) if len(createSet) == 0 && len(deleteSet) == 0 && len(updateSet) == 0 { @@ -160,13 +160,17 @@ func (t *componentAccountProvisionTransformer) createAccount(transCtx *component if err == nil { // TODO: how about the password restored from backup? - t.addOrUpdateProvisionedAccount(cond, account.Name, secret.Annotations[systemAccountHashAnnotation]) + t.updateProvisionedAccount(cond, account.Name, secret.Annotations[systemAccountHashAnnotation]) } return err } func (t *componentAccountProvisionTransformer) deleteAccount(transCtx *componentTransformContext, lfa lifecycle.Lifecycle, cond *metav1.Condition, account synthesizedSystemAccount) error { + if account.Statement == nil || len(account.Statement.Delete) == 0 { + return fmt.Errorf("has no delete statement defined for system account: %s", account.Name) + } + err := lfa.AccountProvision(transCtx.Context, transCtx.Client, nil, account.Statement.Delete, account.Name, "") if lifecycle.IgnoreNotDefined(err) == nil { t.removeProvisionedAccount(cond, account.Name) @@ -176,16 +180,23 @@ func (t *componentAccountProvisionTransformer) deleteAccount(transCtx *component func (t *componentAccountProvisionTransformer) updateAccount(transCtx *componentTransformContext, lfa lifecycle.Lifecycle, cond *metav1.Condition, account synthesizedSystemAccount, secret *corev1.Secret) error { - hashedPassword := t.getHashedPasswordFromCond(cond, account.Name) + hashedPassword := t.hashedPasswordFromCond(cond, account.Name) + if hashedPassword == "" { + return nil // does not support password update? + } if verifySystemAccountPassword(secret, []byte(hashedPassword)) { return nil // the password is not changed } + if account.Statement == nil || len(account.Statement.Update) == 0 { + return fmt.Errorf("has no update statement defined for system account: %s", account.Name) + } + // TODO: how to notify other apps to update the new password? err := t.provision(transCtx, lfa, account.Statement.Update, secret) if err == nil { - t.addOrUpdateProvisionedAccount(cond, account.Name, secret.Annotations[systemAccountHashAnnotation]) + t.updateProvisionedAccount(cond, account.Name, secret.Annotations[systemAccountHashAnnotation]) } return err } @@ -225,11 +236,11 @@ func (t *componentAccountProvisionTransformer) provisionCondDone(transCtx *compo cond.Status = metav1.ConditionFalse // cond.Reason = err.Error() // TODO: error } - cond.ObservedGeneration = transCtx.Component.Generation if !reflect.DeepEqual(cond, condCopy) { cond.LastTransitionTime = metav1.Now() } + cond.ObservedGeneration = transCtx.Component.Generation conditions := transCtx.Component.Status.Conditions if conditions == nil { @@ -248,9 +259,26 @@ func (t *componentAccountProvisionTransformer) provisionCondDone(transCtx *compo transCtx.Component.Status.Conditions = conditions } -func (t *componentAccountProvisionTransformer) addOrUpdateProvisionedAccount(cond *metav1.Condition, account, hashedPassword string) { - accounts := strings.Split(cond.Message, ",") - idx := slices.Index(accounts, account) +func (t *componentAccountProvisionTransformer) getProvisionedAccounts(cond metav1.Condition) sets.Set[string] { + accounts := sets.New[string]() + if len(cond.Message) > 0 { + for _, e := range strings.Split(cond.Message, ",") { + if len(e) > 0 { + accounts.Insert(strings.Split(e, ":")[0]) + } + } + } + return accounts +} + +func (t *componentAccountProvisionTransformer) updateProvisionedAccount(cond *metav1.Condition, account, hashedPassword string) { + accounts := make([]string, 0) + if len(cond.Message) > 0 { + accounts = strings.Split(cond.Message, ",") + } + idx := slices.IndexFunc(accounts, func(s string) bool { + return strings.HasPrefix(s, fmt.Sprintf("%s:", account)) + }) if idx >= 0 { accounts[idx] = fmt.Sprintf("%s:%s", account, hashedPassword) } else { @@ -260,16 +288,24 @@ func (t *componentAccountProvisionTransformer) addOrUpdateProvisionedAccount(con } func (t *componentAccountProvisionTransformer) removeProvisionedAccount(cond *metav1.Condition, account string) { - accounts := strings.Split(cond.Message, ",") + accounts := make([]string, 0) + if len(cond.Message) > 0 { + accounts = strings.Split(cond.Message, ",") + } accounts = slices.DeleteFunc(accounts, func(s string) bool { - return s == account + return strings.HasPrefix(s, fmt.Sprintf("%s:", account)) }) cond.Message = strings.Join(accounts, ",") } -func (t *componentAccountProvisionTransformer) getHashedPasswordFromCond(cond *metav1.Condition, account string) string { - accounts := strings.Split(cond.Message, ",") - idx := slices.Index(accounts, account) +func (t *componentAccountProvisionTransformer) hashedPasswordFromCond(cond *metav1.Condition, account string) string { + accounts := make([]string, 0) + if len(cond.Message) > 0 { + accounts = strings.Split(cond.Message, ",") + } + idx := slices.IndexFunc(accounts, func(s string) bool { + return strings.HasPrefix(s, fmt.Sprintf("%s:", account)) + }) if idx >= 0 { val := strings.Split(accounts[idx], ":") if len(val) == 2 { diff --git a/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml b/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml index ec919134eab..a3568885f25 100644 --- a/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml +++ b/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml @@ -4501,9 +4501,9 @@ spec: The container executing this action has access to following variables: - - KB_ACCOUNT_NAME: The name of the system account to be created. + - KB_ACCOUNT_NAME: The name of the system account to be manipulated. - KB_ACCOUNT_PASSWORD: The password for the system account. - - KB_ACCOUNT_STATEMENT: The statement used to create the system account. + - KB_ACCOUNT_STATEMENT: The statement used to manipulate the system account. Note: This field is immutable once it has been set. diff --git a/docs/developer_docs/api-reference/cluster.md b/docs/developer_docs/api-reference/cluster.md index 744a765a7a9..11b4d786f1e 100644 --- a/docs/developer_docs/api-reference/cluster.md +++ b/docs/developer_docs/api-reference/cluster.md @@ -5770,9 +5770,9 @@ This action is designed to create system accounts that are utilized for replicat and other administrative tasks.

The container executing this action has access to following variables:

Note: This field is immutable once it has been set.

diff --git a/pkg/controller/component/lifecycle/lfa_account.go b/pkg/controller/component/lifecycle/lfa_account.go index bdf438c1eb8..a70b9893ca1 100644 --- a/pkg/controller/component/lifecycle/lfa_account.go +++ b/pkg/controller/component/lifecycle/lfa_account.go @@ -46,9 +46,9 @@ func (a *accountProvision) name() string { func (a *accountProvision) parameters(ctx context.Context, cli client.Reader) (map[string]string, error) { // The container executing this action has access to following variables: // - // - KB_ACCOUNT_NAME: The name of the system account to be created. + // - KB_ACCOUNT_NAME: The name of the system account to be manipulated. // - KB_ACCOUNT_PASSWORD: The password for the system account. - // - KB_ACCOUNT_STATEMENT: The statement used to create the system account. + // - KB_ACCOUNT_STATEMENT: The statement used to manipulate the system account. return map[string]string{ accountName: a.user, accountPassword: a.password,