diff --git a/CHANGELOG.md b/CHANGELOG.md index ca4d30f33..23f005035 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - (Debug Package) Initial commit - (Feature) Detach PVC from deployment in Ordered indexing method - (Feature) OPS Alerts +- (Feature) ScaleDown Candidate ## [1.2.16](https://github.com/arangodb/kube-arangodb/tree/1.2.16) (2022-09-14) - (Feature) Add ArangoDeployment ServerGroupStatus diff --git a/pkg/apis/deployment/annotations.go b/pkg/apis/deployment/annotations.go index 3cc8c7d69..782a2d57c 100644 --- a/pkg/apis/deployment/annotations.go +++ b/pkg/apis/deployment/annotations.go @@ -21,10 +21,11 @@ package deployment const ( - ArangoDeploymentAnnotationPrefix = "deployment.arangodb.com" - ArangoDeploymentPodMaintenanceAnnotation = ArangoDeploymentAnnotationPrefix + "/maintenance" - ArangoDeploymentPodRotateAnnotation = ArangoDeploymentAnnotationPrefix + "/rotate" - ArangoDeploymentPodReplaceAnnotation = ArangoDeploymentAnnotationPrefix + "/replace" - ArangoDeploymentPodDeleteNow = ArangoDeploymentAnnotationPrefix + "/delete_now" - ArangoDeploymentPlanCleanAnnotation = "plan." + ArangoDeploymentAnnotationPrefix + "/clean" + ArangoDeploymentAnnotationPrefix = "deployment.arangodb.com" + ArangoDeploymentPodMaintenanceAnnotation = ArangoDeploymentAnnotationPrefix + "/maintenance" + ArangoDeploymentPodRotateAnnotation = ArangoDeploymentAnnotationPrefix + "/rotate" + ArangoDeploymentPodReplaceAnnotation = ArangoDeploymentAnnotationPrefix + "/replace" + ArangoDeploymentPodDeleteNow = ArangoDeploymentAnnotationPrefix + "/delete_now" + ArangoDeploymentPodScaleDownCandidateAnnotation = ArangoDeploymentAnnotationPrefix + "/scale_down_candidate" + ArangoDeploymentPlanCleanAnnotation = "plan." + ArangoDeploymentAnnotationPrefix + "/clean" ) diff --git a/pkg/apis/deployment/v1/conditions.go b/pkg/apis/deployment/v1/conditions.go index 918aac8f0..34168efd0 100644 --- a/pkg/apis/deployment/v1/conditions.go +++ b/pkg/apis/deployment/v1/conditions.go @@ -68,6 +68,8 @@ const ( ConditionTypeSpecAccepted ConditionType = "SpecAccepted" // ConditionTypeMarkedToRemove indicates that the member is marked to be removed. ConditionTypeMarkedToRemove ConditionType = "MarkedToRemove" + // ConditionTypeScaleDownCandidate indicates that the member will be picked in ScaleDown operaion. + ConditionTypeScaleDownCandidate ConditionType = "ScaleDownCandidate" // ConditionTypeUpgradeFailed indicates that upgrade failed ConditionTypeUpgradeFailed ConditionType = "UpgradeFailed" diff --git a/pkg/apis/deployment/v1/member_status_list.go b/pkg/apis/deployment/v1/member_status_list.go index 1ac5c5714..3f71c6fb3 100644 --- a/pkg/apis/deployment/v1/member_status_list.go +++ b/pkg/apis/deployment/v1/member_status_list.go @@ -149,6 +149,11 @@ func (l MemberStatusList) SelectMemberToRemove(selectors ...MemberToRemoveSelect return m, nil } } + for _, m := range l { + if m.Conditions.IsTrue(ConditionTypeScaleDownCandidate) { + return m, nil + } + } // Try to find a not ready member for _, m := range l { if m.Phase.IsPending() { diff --git a/pkg/apis/deployment/v2alpha1/conditions.go b/pkg/apis/deployment/v2alpha1/conditions.go index 4a01145a5..4ccd91fa2 100644 --- a/pkg/apis/deployment/v2alpha1/conditions.go +++ b/pkg/apis/deployment/v2alpha1/conditions.go @@ -68,6 +68,8 @@ const ( ConditionTypeSpecAccepted ConditionType = "SpecAccepted" // ConditionTypeMarkedToRemove indicates that the member is marked to be removed. ConditionTypeMarkedToRemove ConditionType = "MarkedToRemove" + // ConditionTypeScaleDownCandidate indicates that the member will be picked in ScaleDown operaion. + ConditionTypeScaleDownCandidate ConditionType = "ScaleDownCandidate" // ConditionTypeUpgradeFailed indicates that upgrade failed ConditionTypeUpgradeFailed ConditionType = "UpgradeFailed" diff --git a/pkg/apis/deployment/v2alpha1/member_status_list.go b/pkg/apis/deployment/v2alpha1/member_status_list.go index 0ce58bcfb..2dd4a5064 100644 --- a/pkg/apis/deployment/v2alpha1/member_status_list.go +++ b/pkg/apis/deployment/v2alpha1/member_status_list.go @@ -149,6 +149,11 @@ func (l MemberStatusList) SelectMemberToRemove(selectors ...MemberToRemoveSelect return m, nil } } + for _, m := range l { + if m.Conditions.IsTrue(ConditionTypeScaleDownCandidate) { + return m, nil + } + } // Try to find a not ready member for _, m := range l { if m.Phase.IsPending() { diff --git a/pkg/deployment/reconcile/plan_builder_high.go b/pkg/deployment/reconcile/plan_builder_high.go index 3af8cbbc8..632c74d32 100644 --- a/pkg/deployment/reconcile/plan_builder_high.go +++ b/pkg/deployment/reconcile/plan_builder_high.go @@ -58,6 +58,7 @@ func (r *Reconciler) createHighPlan(ctx context.Context, apiObject k8sutil.APIOb ApplyIfEmpty(r.createTopologyMemberConditionPlan). ApplyIfEmpty(r.createRebalancerCheckPlan). ApplyIfEmpty(r.createMemberFailedRestoreHighPlan). + ApplyIfEmpty(r.scaleDownCandidate). ApplyWithBackOff(BackOffCheck, time.Minute, r.emptyPlanBuilder)). ApplyIfEmptyWithBackOff(TimezoneCheck, time.Minute, r.createTimezoneUpdatePlan). Apply(r.createBackupInProgressConditionPlan). // Discover backups always diff --git a/pkg/deployment/reconcile/plan_builder_scale.go b/pkg/deployment/reconcile/plan_builder_scale.go index a78226330..4f6d98fc3 100644 --- a/pkg/deployment/reconcile/plan_builder_scale.go +++ b/pkg/deployment/reconcile/plan_builder_scale.go @@ -23,6 +23,7 @@ package reconcile import ( "context" + "github.com/arangodb/kube-arangodb/pkg/apis/deployment" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" "github.com/arangodb/kube-arangodb/pkg/deployment/actions" "github.com/arangodb/kube-arangodb/pkg/deployment/agency" @@ -173,3 +174,45 @@ func getCleanedServer(ctx reconciler.ArangoAgencyGet) api.MemberToRemoveSelector return "", nil } } + +func (r *Reconciler) scaleDownCandidate(ctx context.Context, apiObject k8sutil.APIObject, + spec api.DeploymentSpec, status api.DeploymentStatus, + context PlanBuilderContext) api.Plan { + var plan api.Plan + + for _, m := range status.Members.AsList() { + cache, ok := context.ACS().ClusterCache(m.Member.ClusterID) + if !ok { + continue + } + + annotationExists := false + + am, ok := cache.ArangoMember().V1().GetSimple(m.Member.ArangoMemberName(context.GetName(), m.Group)) + if !ok { + continue + } + + if _, ok := am.Annotations[deployment.ArangoDeploymentPodScaleDownCandidateAnnotation]; ok { + annotationExists = true + } + + if pod, ok := cache.Pod().V1().GetSimple(m.Member.Pod.GetName()); ok { + if _, ok := pod.Annotations[deployment.ArangoDeploymentPodScaleDownCandidateAnnotation]; ok { + annotationExists = true + } + } + + conditionExists := m.Member.Conditions.IsTrue(api.ConditionTypeScaleDownCandidate) + + if annotationExists != conditionExists { + if annotationExists { + plan = append(plan, updateMemberConditionActionV2("Marked as ScaleDownCandidate", api.ConditionTypeScaleDownCandidate, m.Group, m.Member.ID, true, "Marked as ScaleDownCandidate", "", "")) + } else { + plan = append(plan, removeMemberConditionActionV2("Unmarked as ScaleDownCandidate", api.ConditionTypeScaleDownCandidate, m.Group, m.Member.ID)) + } + } + } + + return plan +}