Skip to content

Commit 841af83

Browse files
authored
[Feature] Allow Volume Shrink and DBServer replacement (#652)
1 parent 4cbf4e1 commit 841af83

12 files changed

+178
-8
lines changed

pkg/apis/deployment/annotations.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ const (
2626
ArangoDeploymentAnnotationPrefix = "deployment.arangodb.com"
2727
ArangoDeploymentPodMaintenanceAnnotation = ArangoDeploymentAnnotationPrefix + "/maintenance"
2828
ArangoDeploymentPodRotateAnnotation = ArangoDeploymentAnnotationPrefix + "/rotate"
29+
ArangoDeploymentPodReplaceAnnotation = ArangoDeploymentAnnotationPrefix + "/replace"
2930
)

pkg/apis/deployment/v1/conditions.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ const (
6161
ConditionTypeTerminating ConditionType = "Terminating"
6262
// ConditionTypeTerminating indicates that the deployment is up to date.
6363
ConditionTypeUpToDate ConditionType = "UpToDate"
64+
// ConditionTypeMarkedToRemove indicates that the member is marked to be removed.
65+
ConditionTypeMarkedToRemove ConditionType = "MarkedToRemove"
6466
)
6567

6668
// Condition represents one current condition of a deployment or deployment member.

pkg/apis/deployment/v1/member_status_list.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,12 @@ func (l *MemberStatusList) removeByID(id string) error {
142142
// Returns an error if the list is empty.
143143
func (l MemberStatusList) SelectMemberToRemove() (MemberStatus, error) {
144144
if len(l) > 0 {
145+
// Try to find member with phase to be removed
146+
for _, m := range l {
147+
if m.Conditions.IsTrue(ConditionTypeMarkedToRemove) {
148+
return m, nil
149+
}
150+
}
145151
// Try to find a not ready member
146152
for _, m := range l {
147153
if m.Phase == MemberPhaseNone {

pkg/apis/deployment/v1/plan.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ const (
4141
ActionTypeIdle ActionType = "Idle"
4242
// ActionTypeAddMember causes a member to be added.
4343
ActionTypeAddMember ActionType = "AddMember"
44+
// ActionTypeMarkToRemoveMember marks member to be removed.
45+
ActionTypeMarkToRemoveMember ActionType = "MarkToRemoveMember"
4446
// ActionTypeRemoveMember causes a member to be removed.
4547
ActionTypeRemoveMember ActionType = "RemoveMember"
4648
// ActionTypeRecreateMember recreates member. Used when member is still owner of some shards.

pkg/apis/deployment/v1/server_group_spec.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ type ServerGroupSpec struct {
8383
// VolumeClaimTemplate specifies a template for volume claims
8484
VolumeClaimTemplate *core.PersistentVolumeClaim `json:"volumeClaimTemplate,omitempty"`
8585
// VolumeResizeMode specified resize mode for pvc
86-
VolumeResizeMode *PVCResizeMode `json:"pvcResizeMode,omitempty"`
86+
VolumeResizeMode *PVCResizeMode `json:"pvcResizeMode,omitempty"`
87+
VolumeAllowShrink *bool `json:"volumeAllowShrink,omitempty"`
8788
// AntiAffinity specified additional antiAffinity settings in ArangoDB Pod definitions
8889
AntiAffinity *core.PodAntiAffinity `json:"antiAffinity,omitempty"`
8990
// Affinity specified additional affinity settings in ArangoDB Pod definitions
@@ -614,3 +615,11 @@ func (s ServerGroupSpec) ResetImmutableFields(group ServerGroup, fieldPrefix str
614615
}
615616
return resetFields
616617
}
618+
619+
func (s ServerGroupSpec) GetVolumeAllowShrink() bool {
620+
if s.VolumeAllowShrink == nil {
621+
return false // Default value
622+
}
623+
624+
return *s.VolumeAllowShrink
625+
}

pkg/deployment/reconcile/action_add_member.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,20 @@ func (a *actionAddMember) Start(ctx context.Context) (bool, error) {
6565
return false, maskAny(err)
6666
}
6767
a.newMemberID = newID
68+
69+
if _, ok := a.action.Params[api.ActionTypeWaitForMemberUp.String()]; ok {
70+
a.actionCtx.WithStatusUpdate(func(s *api.DeploymentStatus) bool {
71+
s.Plan = append(s.Plan, api.NewAction(api.ActionTypeWaitForMemberInSync, a.action.Group, newID, "Wait for member in sync after creation"))
72+
return true
73+
})
74+
}
75+
76+
if _, ok := a.action.Params[api.ActionTypeWaitForMemberInSync.String()]; ok {
77+
a.actionCtx.WithStatusUpdate(func(s *api.DeploymentStatus) bool {
78+
s.Plan = append(s.Plan, api.NewAction(api.ActionTypeWaitForMemberInSync, a.action.Group, newID, "Wait for member in sync after creation"))
79+
return true
80+
})
81+
}
82+
6883
return true, nil
6984
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2020 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Ewout Prangsma
21+
//
22+
23+
package reconcile
24+
25+
import (
26+
"context"
27+
28+
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
29+
"github.com/rs/zerolog"
30+
)
31+
32+
func init() {
33+
registerAction(api.ActionTypeMarkToRemoveMember, newMarkToRemoveMemberAction)
34+
}
35+
36+
func newMarkToRemoveMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
37+
a := &actionMarkToRemove{}
38+
39+
a.actionImpl = newActionImplDefRef(log, action, actionCtx, addMemberTimeout)
40+
41+
return a
42+
}
43+
44+
type actionMarkToRemove struct {
45+
// actionImpl implement timeout and member id functions
46+
actionImpl
47+
48+
// actionEmptyCheckProgress implement check progress with empty implementation
49+
actionEmptyCheckProgress
50+
}
51+
52+
func (a *actionMarkToRemove) Start(ctx context.Context) (bool, error) {
53+
if a.action.Group != api.ServerGroupDBServers {
54+
return true, nil
55+
}
56+
57+
return true, a.actionCtx.WithStatusUpdate(func(s *api.DeploymentStatus) bool {
58+
member, group, ok := s.Members.ElementByID(a.action.MemberID)
59+
if !ok {
60+
return false
61+
}
62+
63+
if group != a.action.Group {
64+
return false
65+
}
66+
67+
if !member.Conditions.Update(api.ConditionTypeMarkedToRemove, true, "Member marked to be removed", "") {
68+
return false
69+
}
70+
71+
if err := s.Members.Update(member, group); err != nil {
72+
a.log.Warn().Err(err).Str("Member", member.ID).Msgf("Unable to update member")
73+
return false
74+
}
75+
76+
return true
77+
})
78+
}

pkg/deployment/reconcile/action_pvc_resize.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,18 @@ func (a *actionPVCResize) Start(ctx context.Context) (bool, error) {
9898

9999
return false, nil
100100
} else if cmp > 0 {
101-
log.Error().Str("server-group", group.AsRole()).Str("pvc-storage-size", volumeSize.String()).Str("requested-size", requestedSize.String()).
102-
Msg("Volume size should not shrink")
103-
a.actionCtx.CreateEvent(k8sutil.NewCannotShrinkVolumeEvent(a.actionCtx.GetAPIObject(), pvc.Name))
101+
if groupSpec.GetVolumeAllowShrink() && group == api.ServerGroupDBServers {
102+
if err := a.actionCtx.WithStatusUpdate(func(s *api.DeploymentStatus) bool {
103+
s.Plan = append(s.Plan, api.NewAction(api.ActionTypeMarkToRemoveMember, group, m.ID))
104+
return true
105+
}); err != nil {
106+
log.Error().Err(err).Msg("Unable to mark instance to be replaced")
107+
}
108+
} else {
109+
log.Error().Str("server-group", group.AsRole()).Str("pvc-storage-size", volumeSize.String()).Str("requested-size", requestedSize.String()).
110+
Msg("Volume size should not shrink")
111+
a.actionCtx.CreateEvent(k8sutil.NewCannotShrinkVolumeEvent(a.actionCtx.GetAPIObject(), pvc.Name))
112+
}
104113
return false, nil
105114
}
106115
}

pkg/deployment/reconcile/plan_builder.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,12 @@ func createPlan(ctx context.Context, log zerolog.Logger, apiObject k8sutil.APIOb
214214

215215
// Check for scale up/down
216216
if plan.IsEmpty() {
217-
plan = pb.Apply(createScaleMemeberPlan)
217+
plan = pb.Apply(createScaleMemberPlan)
218+
}
219+
220+
// Check for members to be removed
221+
if plan.IsEmpty() {
222+
plan = pb.Apply(createReplaceMemberPlan)
218223
}
219224

220225
// Check for the need to rotate one or more members

pkg/deployment/reconcile/plan_builder_rotate_upgrade.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,14 @@ func createRotateOrUpgradePlanInternal(log zerolog.Logger, apiObject k8sutil.API
144144
}
145145

146146
if pod.Annotations != nil {
147+
if _, ok := pod.Annotations[deployment.ArangoDeploymentPodReplaceAnnotation]; ok && group == api.ServerGroupDBServers {
148+
newPlan = api.Plan{api.NewAction(api.ActionTypeMarkToRemoveMember, group, m.ID, "Replace flag present")}
149+
continue
150+
}
151+
147152
if _, ok := pod.Annotations[deployment.ArangoDeploymentPodRotateAnnotation]; ok {
148153
newPlan = createRotateMemberPlan(log, m, group, "Rotation flag present")
154+
continue
149155
}
150156
}
151157
}

0 commit comments

Comments
 (0)