From fda97879603d9df31ad150c419334a3e1be88a17 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Fri, 21 Jan 2022 14:08:27 +0000 Subject: [PATCH] [Feature] Add member replacement condition --- CHANGELOG.md | 1 + pkg/apis/deployment/v1/conditions.go | 2 + pkg/apis/deployment/v2alpha1/conditions.go | 2 + .../reconcile/condition_member_recreation.go | 78 +++++++++++++++++++ pkg/deployment/reconcile/plan_builder_high.go | 1 + .../reconcile/plan_builder_utils.go | 21 +++++ 6 files changed, 105 insertions(+) create mode 100644 pkg/deployment/reconcile/condition_member_recreation.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 161bd6176..a5dd470b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A) - Do not check License V2 on Community images - Add status.members.. +- Define MemberReplacementRequired condition ## [1.2.7](https://github.com/arangodb/kube-arangodb/tree/1.2.7) (2022-01-17) - Add Plan BackOff functionality diff --git a/pkg/apis/deployment/v1/conditions.go b/pkg/apis/deployment/v1/conditions.go index 15a00da54..2a7d9dc6b 100644 --- a/pkg/apis/deployment/v1/conditions.go +++ b/pkg/apis/deployment/v1/conditions.go @@ -73,6 +73,8 @@ const ( ConditionTypePendingRestart ConditionType = "PendingRestart" // ConditionTypeRestart indicates that restart will be started ConditionTypeRestart ConditionType = "Restart" + // MemberReplacementRequired indicates that the member requires a replacement to proceed with next actions. + MemberReplacementRequired ConditionType = "MemberReplacementRequired" // ConditionTypePendingTLSRotation indicates that TLS rotation is pending ConditionTypePendingTLSRotation ConditionType = "PendingTLSRotation" diff --git a/pkg/apis/deployment/v2alpha1/conditions.go b/pkg/apis/deployment/v2alpha1/conditions.go index 3a3dce052..c8c66a0b1 100644 --- a/pkg/apis/deployment/v2alpha1/conditions.go +++ b/pkg/apis/deployment/v2alpha1/conditions.go @@ -73,6 +73,8 @@ const ( ConditionTypePendingRestart ConditionType = "PendingRestart" // ConditionTypeRestart indicates that restart will be started ConditionTypeRestart ConditionType = "Restart" + // MemberReplacementRequired indicates that the member requires a replacement to proceed with next actions. + MemberReplacementRequired ConditionType = "MemberReplacementRequired" // ConditionTypePendingTLSRotation indicates that TLS rotation is pending ConditionTypePendingTLSRotation ConditionType = "PendingTLSRotation" diff --git a/pkg/deployment/reconcile/condition_member_recreation.go b/pkg/deployment/reconcile/condition_member_recreation.go new file mode 100644 index 000000000..6944e3f99 --- /dev/null +++ b/pkg/deployment/reconcile/condition_member_recreation.go @@ -0,0 +1,78 @@ +// +// DISCLAIMER +// +// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// + +package reconcile + +import ( + "context" + "strings" + + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector" + "github.com/rs/zerolog" +) + +func createMemberRecreationConditionsPlan(ctx context.Context, + log zerolog.Logger, apiObject k8sutil.APIObject, + spec api.DeploymentSpec, status api.DeploymentStatus, + cachedStatus inspectorInterface.Inspector, context PlanBuilderContext) api.Plan { + var p api.Plan + + for _, m := range status.Members.AsList() { + resp, recreate := EvaluateMemberRecreationCondition(ctx, log, apiObject, spec, status, m.Group, m.Member, cachedStatus, context) + + if !recreate { + if _, ok := m.Member.Conditions.Get(api.MemberReplacementRequired); ok { + // Unset condition + p = append(p, removeMemberConditionActionV2("Member replacement not required", api.MemberReplacementRequired, m.Group, m.Member.ID)) + } + } else { + if c, ok := m.Member.Conditions.Get(api.MemberReplacementRequired); !ok || !c.IsTrue() || c.Message != resp { + // Update condition + p = append(p, updateMemberConditionActionV2("Member replacement required", api.MemberReplacementRequired, m.Group, m.Member.ID, true, "Member replacement required", resp, "")) + } + } + } + + return p +} + +type MemberRecreationConditionEvaluator func(ctx context.Context, + log zerolog.Logger, apiObject k8sutil.APIObject, + spec api.DeploymentSpec, status api.DeploymentStatus, + group api.ServerGroup, member api.MemberStatus, + cachedStatus inspectorInterface.Inspector, context PlanBuilderContext) (string, bool) + +func EvaluateMemberRecreationCondition(ctx context.Context, + log zerolog.Logger, apiObject k8sutil.APIObject, + spec api.DeploymentSpec, status api.DeploymentStatus, + group api.ServerGroup, member api.MemberStatus, + cachedStatus inspectorInterface.Inspector, context PlanBuilderContext, evaluators ...MemberRecreationConditionEvaluator) (string, bool) { + args := make([]string, 0, len(evaluators)) + + for _, e := range evaluators { + if s, ok := e(ctx, log, apiObject, spec, status, group, member, cachedStatus, context); ok { + args = append(args, s) + } + } + + return strings.Join(args, ", "), len(args) > 0 +} diff --git a/pkg/deployment/reconcile/plan_builder_high.go b/pkg/deployment/reconcile/plan_builder_high.go index 59aeb50bb..4232772b0 100644 --- a/pkg/deployment/reconcile/plan_builder_high.go +++ b/pkg/deployment/reconcile/plan_builder_high.go @@ -51,6 +51,7 @@ func createHighPlan(ctx context.Context, log zerolog.Logger, apiObject k8sutil.A ApplyIfEmpty(createCleanOutPlan). ApplyIfEmpty(updateMemberUpdateConditionsPlan). ApplyIfEmpty(updateMemberRotationConditionsPlan). + ApplyIfEmpty(createMemberRecreationConditionsPlan). ApplyIfEmpty(createTopologyMemberUpdatePlan). ApplyIfEmptyWithBackOff(LicenseCheck, 30*time.Second, updateClusterLicense). ApplyIfEmpty(createTopologyMemberConditionPlan). diff --git a/pkg/deployment/reconcile/plan_builder_utils.go b/pkg/deployment/reconcile/plan_builder_utils.go index d7b0ac01d..fedb24874 100644 --- a/pkg/deployment/reconcile/plan_builder_utils.go +++ b/pkg/deployment/reconcile/plan_builder_utils.go @@ -78,3 +78,24 @@ func updateConditionActionV2(actionReason string, conditionType api.ConditionTyp AddParam(setConditionActionV2KeyMessage, message). AddParam(setConditionActionV2KeyHash, hash) } + +func removeMemberConditionActionV2(actionReason string, conditionType api.ConditionType, group api.ServerGroup, member string) api.Action { + return api.NewAction(api.ActionTypeSetMemberConditionV2, group, member, actionReason). + AddParam(setConditionActionV2KeyAction, string(conditionType)). + AddParam(setConditionActionV2KeyType, setConditionActionV2KeyTypeRemove) +} + +func updateMemberConditionActionV2(actionReason string, conditionType api.ConditionType, group api.ServerGroup, member string, status bool, reason, message, hash string) api.Action { + statusBool := core.ConditionTrue + if !status { + statusBool = core.ConditionFalse + } + + return api.NewAction(api.ActionTypeSetMemberConditionV2, group, member, actionReason). + AddParam(setConditionActionV2KeyAction, string(conditionType)). + AddParam(setConditionActionV2KeyType, setConditionActionV2KeyTypeAdd). + AddParam(setConditionActionV2KeyStatus, string(statusBool)). + AddParam(setConditionActionV2KeyReason, reason). + AddParam(setConditionActionV2KeyMessage, message). + AddParam(setConditionActionV2KeyHash, hash) +}