Skip to content

Commit 6ab800b

Browse files
authored
[Feature] Fix UpToDate Condition (#552)
1 parent aaa7042 commit 6ab800b

File tree

7 files changed

+127
-33
lines changed

7 files changed

+127
-33
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Change Log
22

33
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
4+
- Added additional checks in UpToDate condition
45
- Added extended Rotation check for Cluster mode
56
- Removed old rotation logic (rotation of ArangoDeployment may be enforced after Operator upgrade)
67
- Added UpToDate condition in ArangoDeployment Status

pkg/apis/deployment/v1/plan.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import (
3333
type ActionType string
3434

3535
const (
36+
// ActionTypeIdle causes a plan to be recalculated.
37+
ActionTypeIdle ActionType = "Idle"
3638
// ActionTypeAddMember causes a member to be added.
3739
ActionTypeAddMember ActionType = "AddMember"
3840
// ActionTypeRemoveMember causes a member to be removed.

pkg/deployment/deployment_inspector.go

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,8 @@ func (d *Deployment) inspectDeploymentWithError(ctx context.Context, lastInterva
119119
return minInspectionInterval, errors.Wrapf(err, "Calculation of spec failed")
120120
} else {
121121
condition, exists := status.Conditions.Get(api.ConditionTypeUpToDate)
122-
if (checksum != status.AppliedVersion && (!exists || condition.IsTrue())) ||
123-
(checksum == status.AppliedVersion && (!exists || !condition.IsTrue())) {
124-
if err = d.WithStatusUpdate(func(s *api.DeploymentStatus) bool {
125-
if checksum == status.AppliedVersion {
126-
return s.Conditions.Update(api.ConditionTypeUpToDate, true, "Everything is UpToDate", "Spec applied")
127-
}
128-
return s.Conditions.Update(api.ConditionTypeUpToDate, false, "Spec Changed", "Spec Object changed. Waiting until plan will be applied")
129-
}); err != nil {
122+
if checksum != status.AppliedVersion && (!exists || condition.IsTrue()) {
123+
if err = d.updateCondition(api.ConditionTypeUpToDate, false, "Spec Changed", "Spec Object changed. Waiting until plan will be applied"); err != nil {
130124
return minInspectionInterval, errors.Wrapf(err, "Unable to update UpToDate condition")
131125
}
132126

@@ -185,8 +179,37 @@ func (d *Deployment) inspectDeploymentWithError(ctx context.Context, lastInterva
185179
}
186180

187181
// Create scale/update plan
188-
if err := d.reconciler.CreatePlan(); err != nil {
182+
if err, updated := d.reconciler.CreatePlan(); err != nil {
189183
return minInspectionInterval, errors.Wrapf(err, "Plan creation failed")
184+
} else if updated {
185+
return minInspectionInterval, nil
186+
}
187+
188+
if d.apiObject.Status.Plan.IsEmpty() && status.AppliedVersion != checksum {
189+
if err := d.WithStatusUpdate(func(s *api.DeploymentStatus) bool {
190+
s.AppliedVersion = checksum
191+
return true
192+
}); err != nil {
193+
return minInspectionInterval, errors.Wrapf(err, "Unable to update UpToDate condition")
194+
}
195+
196+
return minInspectionInterval, nil
197+
} else if status.AppliedVersion == checksum {
198+
if !status.Plan.IsEmpty() && status.Conditions.IsTrue(api.ConditionTypeUpToDate) {
199+
if err = d.updateCondition(api.ConditionTypeUpToDate, false, "Plan is not empty", "There are pending operations in plan"); err != nil {
200+
return minInspectionInterval, errors.Wrapf(err, "Unable to update UpToDate condition")
201+
}
202+
203+
return minInspectionInterval, nil
204+
}
205+
206+
if status.Plan.IsEmpty() && !status.Conditions.IsTrue(api.ConditionTypeUpToDate) {
207+
if err = d.updateCondition(api.ConditionTypeUpToDate, true, "Spec is Up To Date", "Spec is Up To Date"); err != nil {
208+
return minInspectionInterval, errors.Wrapf(err, "Unable to update UpToDate condition")
209+
}
210+
211+
return minInspectionInterval, nil
212+
}
190213
}
191214

192215
// Execute current step of scale/update plan
@@ -196,18 +219,6 @@ func (d *Deployment) inspectDeploymentWithError(ctx context.Context, lastInterva
196219
}
197220
if retrySoon {
198221
nextInterval = minInspectionInterval
199-
} else {
200-
// Do not retry - so plan is empty
201-
if status.AppliedVersion != checksum {
202-
if err := d.WithStatusUpdate(func(s *api.DeploymentStatus) bool {
203-
s.AppliedVersion = checksum
204-
return true
205-
}); err != nil {
206-
return minInspectionInterval, errors.Wrapf(err, "Unable to update UpToDate condition")
207-
}
208-
209-
return minInspectionInterval, nil
210-
}
211222
}
212223

213224
// Create access packages
@@ -279,3 +290,14 @@ func (d *Deployment) triggerInspection() {
279290
func (d *Deployment) triggerCRDInspection() {
280291
d.inspectCRDTrigger.Trigger()
281292
}
293+
294+
func (d *Deployment) updateCondition(conditionType api.ConditionType, status bool, reason, message string) error {
295+
d.deps.Log.Info().Str("condition", string(conditionType)).Bool("status", status).Str("reason", reason).Str("message", message).Msg("Updated condition")
296+
if err := d.WithStatusUpdate(func(s *api.DeploymentStatus) bool {
297+
return s.Conditions.Update(conditionType, status, reason, message)
298+
}); err != nil {
299+
return errors.Wrapf(err, "Unable to update condition")
300+
}
301+
302+
return nil
303+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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 Adam Janikowski
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.ActionTypeIdle, newIdleAction)
34+
}
35+
36+
// newIdleAction creates a new Action that implements the given
37+
// planned Idle action.
38+
func newIdleAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
39+
a := &actionIdle{}
40+
41+
a.actionImpl = newActionImplDefRef(log, action, actionCtx, addMemberTimeout)
42+
43+
return a
44+
}
45+
46+
// actionIdle implements an Idle.
47+
type actionIdle struct {
48+
// actionImpl implement timeout and member id functions
49+
actionImpl
50+
51+
// actionEmptyCheckProgress implement check progress with empty implementation
52+
actionEmptyCheckProgress
53+
}
54+
55+
// Start performs the start of the action.
56+
// Returns true if the action is completely finished, false in case
57+
// the start time needs to be recorded and a ready condition needs to be checked.
58+
func (a *actionIdle) Start(ctx context.Context) (bool, error) {
59+
return true, nil
60+
}

pkg/deployment/reconcile/plan_builder.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ type upgradeDecision struct {
5252
// CreatePlan considers the current specification & status of the deployment creates a plan to
5353
// get the status in line with the specification.
5454
// If a plan already exists, nothing is done.
55-
func (d *Reconciler) CreatePlan() error {
55+
func (d *Reconciler) CreatePlan() (error, bool) {
5656
// Get all current pods
5757
pods, err := d.context.GetOwnedPods()
5858
if err != nil {
5959
d.log.Debug().Err(err).Msg("Failed to get owned pods")
60-
return maskAny(err)
60+
return maskAny(err), false
6161
}
6262

6363
// Create plan
@@ -69,19 +69,19 @@ func (d *Reconciler) CreatePlan() error {
6969

7070
// If not change, we're done
7171
if !changed {
72-
return nil
72+
return nil, false
7373
}
7474

7575
// Save plan
7676
if len(newPlan) == 0 {
7777
// Nothing to do
78-
return nil
78+
return nil, false
7979
}
8080
status.Plan = newPlan
8181
if err := d.context.UpdateStatus(status, lastVersion); err != nil {
82-
return maskAny(err)
82+
return maskAny(err), false
8383
}
84-
return nil
84+
return nil, true
8585
}
8686

8787
func fetchAgency(log zerolog.Logger,
@@ -112,6 +112,7 @@ func createPlan(log zerolog.Logger, apiObject k8sutil.APIObject,
112112
currentPlan api.Plan, spec api.DeploymentSpec,
113113
status api.DeploymentStatus, pods []v1.Pod,
114114
context PlanBuilderContext) (api.Plan, bool) {
115+
115116
if !currentPlan.IsEmpty() {
116117
// Plan already exists, complete that first
117118
return currentPlan, false
@@ -176,7 +177,8 @@ func createPlan(log zerolog.Logger, apiObject k8sutil.APIObject,
176177
// Ensure that we were able to get agency info
177178
if len(plan) == 0 && agencyErr != nil {
178179
log.Err(agencyErr).Msg("unable to build further plan without access to agency")
179-
return plan, false
180+
return append(plan,
181+
api.NewAction(api.ActionTypeIdle, api.ServerGroupUnknown, "")), true
180182
}
181183

182184
// Check for cleaned out dbserver in created state
@@ -200,7 +202,13 @@ func createPlan(log zerolog.Logger, apiObject k8sutil.APIObject,
200202

201203
// Check for the need to rotate one or more members
202204
if plan.IsEmpty() {
203-
plan = createRotateOrUpgradePlan(log, apiObject, spec, status, context, pods)
205+
newPlan, idle := createRotateOrUpgradePlan(log, apiObject, spec, status, context, pods)
206+
if idle {
207+
plan = append(plan,
208+
api.NewAction(api.ActionTypeIdle, api.ServerGroupUnknown, ""))
209+
} else {
210+
plan = append(plan, newPlan...)
211+
}
204212
}
205213

206214
// Check for the need to rotate TLS certificate of a members

pkg/deployment/reconcile/plan_builder_rotate_upgrade.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import (
3535

3636
// createRotateOrUpgradePlan goes over all pods to check if an upgrade or rotate is needed.
3737
func createRotateOrUpgradePlan(log zerolog.Logger, apiObject k8sutil.APIObject, spec api.DeploymentSpec,
38-
status api.DeploymentStatus, context PlanBuilderContext, pods []core.Pod) api.Plan {
38+
status api.DeploymentStatus, context PlanBuilderContext, pods []core.Pod) (api.Plan, bool) {
3939

4040
var newPlan api.Plan
4141
var upgradeNotAllowed bool
@@ -103,12 +103,13 @@ func createRotateOrUpgradePlan(log zerolog.Logger, apiObject k8sutil.APIObject,
103103
} else if !newPlan.IsEmpty() {
104104
if clusterReadyForUpgrade(context) {
105105
// Use the new plan
106-
return newPlan
106+
return newPlan, false
107107
} else {
108108
log.Info().Msg("Pod needs upgrade but cluster is not ready. Either some shards are not in sync or some member is not ready.")
109+
return nil, true
109110
}
110111
}
111-
return nil
112+
return nil, false
112113
}
113114

114115
// podNeedsUpgrading decides if an upgrade of the pod is needed (to comply with

pkg/deployment/reconcile/plan_builder_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,7 @@ func TestCreatePlan(t *testing.T) {
776776
if testCase.Helper != nil {
777777
testCase.Helper(testCase.context.ArangoDeployment)
778778
}
779-
err := r.CreatePlan()
779+
err, _ := r.CreatePlan()
780780

781781
// Assert
782782
if testCase.ExpectedEvent != nil {

0 commit comments

Comments
 (0)