Skip to content

Commit 83a8017

Browse files
authored
[Bugfix] Fix Maintenance grace period (#961)
1 parent f2776d7 commit 83a8017

File tree

12 files changed

+251
-83
lines changed

12 files changed

+251
-83
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- (Bugfix) Disable member removal in case of health failure
1212
- (Bugfix) Reorder Topology management plan steps
1313
- (Feature) UpdateInProgress & UpgradeInProgress Conditions
14+
- (Bugfix) Fix Maintenance switch and HotBackup race
1415

1516
## [1.2.9](https://github.com/arangodb/kube-arangodb/tree/1.2.9) (2022-03-30)
1617
- (Feature) Improve Kubernetes clientsets management

pkg/deployment/agency/state.go

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727

2828
"github.com/arangodb/go-driver"
2929
"github.com/arangodb/go-driver/agency"
30-
"github.com/arangodb/kube-arangodb/pkg/util"
3130
"github.com/arangodb/kube-arangodb/pkg/util/errors"
3231
)
3332

@@ -111,35 +110,7 @@ type StatePlan struct {
111110
}
112111

113112
type StateSupervision struct {
114-
Maintenance StateExists `json:"Maintenance,omitempty"`
115-
}
116-
117-
type StateExists []byte
118-
119-
func (d StateExists) Hash() string {
120-
if d == nil {
121-
return ""
122-
}
123-
124-
return util.SHA256(d)
125-
}
126-
127-
func (d StateExists) Exists() bool {
128-
return d != nil
129-
}
130-
131-
func (d *StateExists) UnmarshalJSON(bytes []byte) error {
132-
if bytes == nil {
133-
*d = nil
134-
return nil
135-
}
136-
137-
z := make([]byte, len(bytes))
138-
139-
copy(z, bytes)
140-
141-
*d = z
142-
return nil
113+
Maintenance StateTimestamp `json:"Maintenance,omitempty"`
143114
}
144115

145116
func (s State) CountShards() int {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2022 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+
21+
package agency
22+
23+
import "github.com/arangodb/kube-arangodb/pkg/util"
24+
25+
type StateExists []byte
26+
27+
func (d StateExists) Hash() string {
28+
if d == nil {
29+
return ""
30+
}
31+
32+
return util.SHA256(d)
33+
}
34+
35+
func (d StateExists) Exists() bool {
36+
return d != nil
37+
}
38+
39+
func (d *StateExists) UnmarshalJSON(bytes []byte) error {
40+
if bytes == nil {
41+
*d = nil
42+
return nil
43+
}
44+
45+
z := make([]byte, len(bytes))
46+
47+
copy(z, bytes)
48+
49+
*d = z
50+
return nil
51+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2022 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+
21+
package agency
22+
23+
import (
24+
"encoding/json"
25+
"time"
26+
27+
"github.com/arangodb/kube-arangodb/pkg/util"
28+
"github.com/arangodb/kube-arangodb/pkg/util/errors"
29+
)
30+
31+
type StateTimestamp struct {
32+
hash string
33+
data string
34+
time time.Time
35+
parsed bool
36+
exists bool
37+
}
38+
39+
func (s *StateTimestamp) Hash() string {
40+
if s == nil {
41+
return util.SHA256FromString("")
42+
}
43+
44+
return s.hash
45+
}
46+
47+
func (s *StateTimestamp) Exists() bool {
48+
if s == nil {
49+
return false
50+
}
51+
52+
return s.exists
53+
}
54+
55+
func (s *StateTimestamp) Time() (time.Time, bool) {
56+
if s == nil {
57+
return time.Time{}, false
58+
}
59+
60+
if !s.parsed || !s.exists {
61+
return time.Time{}, false
62+
}
63+
64+
return s.time, true
65+
}
66+
67+
func (s *StateTimestamp) UnmarshalJSON(bytes []byte) error {
68+
if s == nil {
69+
return errors.Newf("Object is nil")
70+
}
71+
72+
var t string
73+
74+
if err := json.Unmarshal(bytes, &t); err != nil {
75+
return err
76+
}
77+
78+
*s = unmarshalJSONStateTimestamp(t)
79+
80+
return nil
81+
}
82+
83+
func unmarshalJSONStateTimestamp(s string) StateTimestamp {
84+
var ts = StateTimestamp{
85+
hash: util.SHA256([]byte(s)),
86+
data: s,
87+
exists: true,
88+
}
89+
90+
t, ok := util.ParseAgencyTime(s)
91+
if !ok {
92+
ts.parsed = false
93+
return ts
94+
}
95+
96+
ts.time = t
97+
ts.parsed = true
98+
ts.hash = util.SHA256FromString(s)
99+
100+
return ts
101+
}

pkg/deployment/agency/target.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ type StateTarget struct {
2525
}
2626

2727
type StateTargetHotBackup struct {
28-
Create StateExists `json:"Create,omitempty"`
28+
Create StateTimestamp `json:"Create,omitempty"`
2929
}

pkg/deployment/agency/target_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ func Test_Target_HotBackup(t *testing.T) {
3333
require.NoError(t, json.Unmarshal(agencyDump39HotBackup, &s))
3434

3535
require.True(t, s.Agency.Arango.Target.HotBackup.Create.Exists())
36+
37+
t.Log(s.Agency.Arango.Target.HotBackup.Create.time.String())
3638
})
3739
t.Run("Does Not Exists", func(t *testing.T) {
3840
var s DumpState

pkg/deployment/deployment_inspector.go

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -404,23 +404,33 @@ func (d *Deployment) refreshMaintenanceTTL(ctx context.Context) {
404404
return
405405
}
406406

407-
condition, ok := d.status.last.Conditions.Get(api.ConditionTypeMaintenanceMode)
407+
agencyState, agencyOK := d.GetAgencyCache()
408+
if !agencyOK {
409+
return
410+
}
411+
412+
condition, ok := d.status.last.Conditions.Get(api.ConditionTypeMaintenance)
413+
maintenance := agencyState.Supervision.Maintenance
408414

409415
if !ok || !condition.IsTrue() {
410416
return
411417
}
412418

413419
// Check GracePeriod
414-
if condition.LastUpdateTime.Add(d.apiObject.Spec.Timeouts.GetMaintenanceGracePeriod()).Before(time.Now()) {
415-
if err := d.SetAgencyMaintenanceMode(ctx, true); err != nil {
416-
return
420+
if t, ok := maintenance.Time(); ok {
421+
if time.Until(t) < d.apiObject.Spec.Timeouts.GetMaintenanceGracePeriod() {
422+
if err := d.SetAgencyMaintenanceMode(ctx, true); err != nil {
423+
return
424+
}
425+
d.deps.Log.Info().Msgf("Refreshed maintenance lock")
417426
}
418-
if err := d.WithStatusUpdate(ctx, func(s *api.DeploymentStatus) bool {
419-
return s.Conditions.Touch(api.ConditionTypeMaintenanceMode)
420-
}); err != nil {
421-
return
427+
} else {
428+
if condition.LastUpdateTime.Add(d.apiObject.Spec.Timeouts.GetMaintenanceGracePeriod()).Before(time.Now()) {
429+
if err := d.SetAgencyMaintenanceMode(ctx, true); err != nil {
430+
return
431+
}
432+
d.deps.Log.Info().Msgf("Refreshed maintenance lock")
422433
}
423-
d.deps.Log.Info().Msgf("Refreshed maintenance lock")
424434
}
425435
}
426436

pkg/deployment/reconcile/action_maintenance_condition.go

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121
package reconcile
2222

2323
import (
24-
"context"
25-
2624
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
2725
"github.com/rs/zerolog"
2826
)
@@ -34,7 +32,7 @@ func init() {
3432
func newSetMaintenanceConditionAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
3533
a := &actionSetMaintenanceCondition{}
3634

37-
a.actionImpl = newActionImpl(log, action, actionCtx, &a.newMemberID)
35+
a.actionImpl = newActionImplDefRef(log, action, actionCtx)
3836

3937
return a
4038
}
@@ -43,33 +41,5 @@ type actionSetMaintenanceCondition struct {
4341
// actionImpl implement timeout and member id functions
4442
actionImpl
4543

46-
actionEmptyCheckProgress
47-
48-
newMemberID string
49-
}
50-
51-
func (a *actionSetMaintenanceCondition) Start(ctx context.Context) (bool, error) {
52-
switch a.actionCtx.GetMode() {
53-
case api.DeploymentModeSingle:
54-
return true, nil
55-
}
56-
57-
agencyState, agencyOK := a.actionCtx.GetAgencyCache()
58-
if !agencyOK {
59-
a.log.Error().Msgf("Unable to determine maintenance condition")
60-
} else {
61-
62-
if err := a.actionCtx.WithStatusUpdate(ctx, func(s *api.DeploymentStatus) bool {
63-
if agencyState.Supervision.Maintenance.Exists() {
64-
return s.Conditions.Update(api.ConditionTypeMaintenanceMode, true, "Maintenance", "Maintenance enabled")
65-
} else {
66-
return s.Conditions.Remove(api.ConditionTypeMaintenanceMode)
67-
}
68-
}); err != nil {
69-
a.log.Error().Err(err).Msgf("Unable to set maintenance condition")
70-
return true, nil
71-
}
72-
}
73-
74-
return true, nil
44+
actionEmpty
7545
}

pkg/deployment/reconcile/helper_wrap.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ func withMaintenanceStart(plan ...api.Action) api.Plan {
3939
}
4040

4141
return api.AsPlan(plan).Before(
42-
actions.NewClusterAction(api.ActionTypeEnableMaintenance, "Enable maintenance before actions"),
43-
actions.NewClusterAction(api.ActionTypeSetMaintenanceCondition, "Enable maintenance before actions"))
42+
actions.NewClusterAction(api.ActionTypeEnableMaintenance, "Enable maintenance before actions"))
4443
}
4544

4645
func withResignLeadership(group api.ServerGroup, member api.MemberStatus, reason string, plan ...api.Action) api.Plan {

0 commit comments

Comments
 (0)