Skip to content

Commit f2776d7

Browse files
authored
[Feature] UpdateInProgress & UpgradeInProgress conditions (#959)
1 parent 710c551 commit f2776d7

File tree

4 files changed

+152
-92
lines changed

4 files changed

+152
-92
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- (Feature) Backup & Maintenance Conditions
1111
- (Bugfix) Disable member removal in case of health failure
1212
- (Bugfix) Reorder Topology management plan steps
13+
- (Feature) UpdateInProgress & UpgradeInProgress Conditions
1314

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

pkg/apis/deployment/v1/conditions.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ const (
9999

100100
// ConditionTypeBackupInProgress indicates that there is Backup in progress on cluster
101101
ConditionTypeBackupInProgress ConditionType = "BackupInProgress"
102+
// ConditionTypeUpgradeInProgress indicates that there is upgrade in progress on cluster
103+
ConditionTypeUpgradeInProgress ConditionType = "UpgradeInProgress"
104+
// ConditionTypeUpdateInProgress indicates that there is update in progress on cluster
105+
ConditionTypeUpdateInProgress ConditionType = "UpdateInProgress"
102106

103107
// ConditionTypeMaintenance indicates that maintenance is enabled on cluster
104108
ConditionTypeMaintenance ConditionType = "Maintenance"

pkg/apis/deployment/v2alpha1/conditions.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ const (
9999

100100
// ConditionTypeBackupInProgress indicates that there is Backup in progress on cluster
101101
ConditionTypeBackupInProgress ConditionType = "BackupInProgress"
102+
// ConditionTypeUpgradeInProgress indicates that there is upgrade in progress on cluster
103+
ConditionTypeUpgradeInProgress ConditionType = "UpgradeInProgress"
104+
// ConditionTypeUpdateInProgress indicates that there is update in progress on cluster
105+
ConditionTypeUpdateInProgress ConditionType = "UpdateInProgress"
102106

103107
// ConditionTypeMaintenance indicates that maintenance is enabled on cluster
104108
ConditionTypeMaintenance ConditionType = "Maintenance"

pkg/deployment/reconcile/plan_builder_rotate_upgrade.go

Lines changed: 143 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ package reconcile
2323
import (
2424
"context"
2525

26-
"github.com/arangodb/kube-arangodb/pkg/deployment/rotation"
27-
2826
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
2927

3028
"github.com/arangodb/kube-arangodb/pkg/deployment/resources"
@@ -37,6 +35,7 @@ import (
3735
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
3836
"github.com/arangodb/kube-arangodb/pkg/deployment/actions"
3937
"github.com/arangodb/kube-arangodb/pkg/deployment/agency"
38+
"github.com/arangodb/kube-arangodb/pkg/deployment/rotation"
4039
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
4140
inspectorInterface "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/inspector"
4241
"github.com/rs/zerolog"
@@ -123,118 +122,170 @@ func createRotateOrUpgradePlanInternal(log zerolog.Logger, apiObject k8sutil.API
123122
decision := createRotateOrUpgradeDecision(log, spec, status, context)
124123

125124
if decision.IsUpgrade() {
125+
return createUpgradePlanInternalCondition(log, apiObject, spec, status, context, decision)
126+
} else if decision.IsUpdate() {
127+
return createUpdatePlanInternalCondition(log, apiObject, spec, status, cachedStatus, decision)
128+
} else {
129+
upgradeCondition := status.Conditions.IsTrue(api.ConditionTypeUpgradeInProgress)
130+
updateCondition := status.Conditions.IsTrue(api.ConditionTypeUpdateInProgress)
126131

127-
for _, m := range status.Members.AsList() {
128-
// Pre-check
129-
d := decision[m.Member.ID]
130-
if !d.upgrade {
131-
continue
132-
}
132+
if upgradeCondition || updateCondition {
133+
p := make(api.Plan, 0, 2)
133134

134-
// We have member to upgrade
135-
if d.upgradeDecision.Hold {
136-
// Holding upgrade
137-
continue
135+
if upgradeCondition {
136+
p = append(p, removeConditionActionV2("Upgrade done", api.ConditionTypeUpgradeInProgress))
138137
}
139138

140-
if !d.upgradeDecision.UpgradeAllowed {
141-
context.CreateEvent(k8sutil.NewUpgradeNotAllowedEvent(apiObject, d.upgradeDecision.FromVersion, d.upgradeDecision.ToVersion, d.upgradeDecision.FromLicense, d.upgradeDecision.ToLicense))
142-
return nil, false
139+
if updateCondition {
140+
p = append(p, removeConditionActionV2("Update done", api.ConditionTypeUpdateInProgress))
143141
}
142+
143+
return p, false
144144
}
145+
}
145146

146-
// Upgrade phase
147-
// During upgrade always get first member which needs to be upgraded
148-
for _, m := range status.Members.AsList() {
149-
d := decision[m.Member.ID]
150-
if !d.upgrade {
151-
continue
152-
}
147+
return nil, false
148+
}
153149

154-
// We have member to upgrade
155-
if d.upgradeDecision.Hold {
156-
// Holding upgrade
157-
return nil, false
158-
}
150+
func createUpdatePlanInternalCondition(log zerolog.Logger, apiObject k8sutil.APIObject, spec api.DeploymentSpec, status api.DeploymentStatus, cachedStatus inspectorInterface.Inspector, decision updateUpgradeDecisionMap) (api.Plan, bool) {
151+
plan, idle := createUpdatePlanInternal(log, apiObject, spec, status, cachedStatus, decision)
159152

160-
if !d.upgradeDecision.UpgradeNeeded {
161-
// In upgrade scenario but upgrade is not needed
162-
return nil, false
163-
}
153+
if idle || len(plan) > 0 {
154+
if !status.Conditions.IsTrue(api.ConditionTypeUpdateInProgress) {
155+
plan = append(api.Plan{
156+
updateConditionActionV2("Update in progress", api.ConditionTypeUpdateInProgress, true, "", "", ""),
157+
}, plan...)
158+
}
159+
}
164160

165-
if !d.upgradeDecision.UpgradeAllowed {
166-
context.CreateEvent(k8sutil.NewUpgradeNotAllowedEvent(apiObject, d.upgradeDecision.FromVersion, d.upgradeDecision.ToVersion, d.upgradeDecision.FromLicense, d.upgradeDecision.ToLicense))
167-
return nil, false
168-
}
161+
return plan, idle
162+
}
169163

170-
if d.updateAllowed {
171-
// We are fine, group is alive so we can proceed
172-
log.Info().Str("member", m.Member.ID).Str("Reason", d.updateMessage).Msg("Upgrade allowed")
173-
return createUpgradeMemberPlan(log, m.Member, m.Group, "Version upgrade", spec, status, !d.upgradeDecision.AutoUpgradeNeeded), false
174-
} else if d.unsafeUpdateAllowed {
175-
log.Info().Str("member", m.Member.ID).Str("Reason", d.updateMessage).Msg("Pod needs upgrade but cluster is not ready. Either some shards are not in sync or some member is not ready, but unsafe upgrade is allowed")
176-
return createUpgradeMemberPlan(log, m.Member, m.Group, "Version upgrade", spec, status, !d.upgradeDecision.AutoUpgradeNeeded), false
177-
} else {
178-
log.Info().Str("member", m.Member.ID).Str("Reason", d.updateMessage).Msg("Pod needs upgrade but cluster is not ready. Either some shards are not in sync or some member is not ready.")
179-
return nil, true
180-
}
164+
func createUpdatePlanInternal(log zerolog.Logger, apiObject k8sutil.APIObject, spec api.DeploymentSpec, status api.DeploymentStatus, cachedStatus inspectorInterface.Inspector, decision updateUpgradeDecisionMap) (api.Plan, bool) {
165+
// Update phase
166+
for _, m := range status.Members.AsList() {
167+
d := decision[m.Member.ID]
168+
if !d.update {
169+
continue
181170
}
182171

183-
log.Warn().Msg("Pod upgrade plan has been made, but it has been dropped due to missing flag")
184-
return nil, false
185-
} else if decision.IsUpdate() {
186-
// Update phase
187-
for _, m := range status.Members.AsList() {
188-
d := decision[m.Member.ID]
189-
if !d.update {
172+
if !d.updateAllowed {
173+
// Update is not allowed due to constraint
174+
if !d.unsafeUpdateAllowed {
175+
log.Info().Str("member", m.Member.ID).Str("Reason", d.updateMessage).Msg("Pod needs restart but cluster is not ready. Either some shards are not in sync or some member is not ready.")
190176
continue
191177
}
178+
log.Info().Str("member", m.Member.ID).Str("Reason", d.updateMessage).Msg("Pod needs restart but cluster is not ready. Either some shards are not in sync or some member is not ready, but unsafe upgrade is allowed")
179+
}
192180

193-
if !d.updateAllowed {
194-
// Update is not allowed due to constraint
195-
if !d.unsafeUpdateAllowed {
196-
log.Info().Str("member", m.Member.ID).Str("Reason", d.updateMessage).Msg("Pod needs restart but cluster is not ready. Either some shards are not in sync or some member is not ready.")
197-
continue
198-
}
199-
log.Info().Str("member", m.Member.ID).Str("Reason", d.updateMessage).Msg("Pod needs restart but cluster is not ready. Either some shards are not in sync or some member is not ready, but unsafe upgrade is allowed")
200-
}
181+
if m.Member.Conditions.IsTrue(api.ConditionTypeRestart) {
182+
return createRotateMemberPlan(log, m.Member, m.Group, spec, "Restart flag present"), false
183+
}
184+
arangoMember, ok := cachedStatus.ArangoMember().V1().GetSimple(m.Member.ArangoMemberName(apiObject.GetName(), m.Group))
185+
if !ok {
186+
continue
187+
}
201188

202-
if m.Member.Conditions.IsTrue(api.ConditionTypeRestart) {
203-
return createRotateMemberPlan(log, m.Member, m.Group, spec, "Restart flag present"), false
204-
}
205-
arangoMember, ok := cachedStatus.ArangoMember().V1().GetSimple(m.Member.ArangoMemberName(apiObject.GetName(), m.Group))
206-
if !ok {
207-
continue
208-
}
189+
p, ok := cachedStatus.Pod().V1().GetSimple(m.Member.PodName)
190+
if !ok {
191+
p = nil
192+
}
209193

210-
p, ok := cachedStatus.Pod().V1().GetSimple(m.Member.PodName)
211-
if !ok {
212-
p = nil
213-
}
194+
if mode, p, reason, err := rotation.IsRotationRequired(log, cachedStatus, spec, m.Member, m.Group, p, arangoMember.Spec.Template, arangoMember.Status.Template); err != nil {
195+
log.Err(err).Str("member", m.Member.ID).Msgf("Error while generating update plan")
196+
continue
197+
} else if mode != rotation.InPlaceRotation {
198+
return api.Plan{actions.NewAction(api.ActionTypeSetMemberCondition, m.Group, m.Member, "Cleaning update").
199+
AddParam(api.ConditionTypePendingUpdate.String(), "").
200+
AddParam(api.ConditionTypeUpdating.String(), "T")}, false
201+
} else {
202+
p = p.After(
203+
actions.NewAction(api.ActionTypeWaitForMemberUp, m.Group, m.Member),
204+
actions.NewAction(api.ActionTypeWaitForMemberInSync, m.Group, m.Member))
205+
206+
p = p.Wrap(actions.NewAction(api.ActionTypeSetMemberCondition, m.Group, m.Member, reason).
207+
AddParam(api.ConditionTypePendingUpdate.String(), "").AddParam(api.ConditionTypeUpdating.String(), "T"),
208+
actions.NewAction(api.ActionTypeSetMemberCondition, m.Group, m.Member, reason).
209+
AddParam(api.ConditionTypeUpdating.String(), ""))
210+
211+
return p, false
212+
}
213+
}
214+
return nil, true
215+
}
214216

215-
if mode, p, reason, err := rotation.IsRotationRequired(log, cachedStatus, spec, m.Member, m.Group, p, arangoMember.Spec.Template, arangoMember.Status.Template); err != nil {
216-
log.Err(err).Str("member", m.Member.ID).Msgf("Error while generating update plan")
217-
continue
218-
} else if mode != rotation.InPlaceRotation {
219-
return api.Plan{actions.NewAction(api.ActionTypeSetMemberCondition, m.Group, m.Member, "Cleaning update").
220-
AddParam(api.ConditionTypePendingUpdate.String(), "").
221-
AddParam(api.ConditionTypeUpdating.String(), "T")}, false
222-
} else {
223-
p = p.After(
224-
actions.NewAction(api.ActionTypeWaitForMemberUp, m.Group, m.Member),
225-
actions.NewAction(api.ActionTypeWaitForMemberInSync, m.Group, m.Member))
226-
227-
p = p.Wrap(actions.NewAction(api.ActionTypeSetMemberCondition, m.Group, m.Member, reason).
228-
AddParam(api.ConditionTypePendingUpdate.String(), "").AddParam(api.ConditionTypeUpdating.String(), "T"),
229-
actions.NewAction(api.ActionTypeSetMemberCondition, m.Group, m.Member, reason).
230-
AddParam(api.ConditionTypeUpdating.String(), ""))
231-
232-
return p, false
233-
}
217+
func createUpgradePlanInternalCondition(log zerolog.Logger, apiObject k8sutil.APIObject, spec api.DeploymentSpec, status api.DeploymentStatus, context PlanBuilderContext, decision updateUpgradeDecisionMap) (api.Plan, bool) {
218+
plan, idle := createUpgradePlanInternal(log, apiObject, spec, status, context, decision)
219+
220+
if idle || len(plan) > 0 {
221+
if !status.Conditions.IsTrue(api.ConditionTypeUpgradeInProgress) {
222+
plan = append(api.Plan{
223+
updateConditionActionV2("Upgrade in progress", api.ConditionTypeUpgradeInProgress, true, "", "", ""),
224+
}, plan...)
225+
}
226+
}
227+
228+
return plan, idle
229+
}
230+
231+
func createUpgradePlanInternal(log zerolog.Logger, apiObject k8sutil.APIObject, spec api.DeploymentSpec, status api.DeploymentStatus, context PlanBuilderContext, decision updateUpgradeDecisionMap) (api.Plan, bool) {
232+
for _, m := range status.Members.AsList() {
233+
// Pre-check
234+
d := decision[m.Member.ID]
235+
if !d.upgrade {
236+
continue
237+
}
238+
239+
// We have member to upgrade
240+
if d.upgradeDecision.Hold {
241+
// Holding upgrade
242+
continue
243+
}
244+
245+
if !d.upgradeDecision.UpgradeAllowed {
246+
context.CreateEvent(k8sutil.NewUpgradeNotAllowedEvent(apiObject, d.upgradeDecision.FromVersion, d.upgradeDecision.ToVersion, d.upgradeDecision.FromLicense, d.upgradeDecision.ToLicense))
247+
return nil, false
248+
}
249+
}
250+
251+
// Upgrade phase
252+
// During upgrade always get first member which needs to be upgraded
253+
for _, m := range status.Members.AsList() {
254+
d := decision[m.Member.ID]
255+
if !d.upgrade {
256+
continue
257+
}
258+
259+
// We have member to upgrade
260+
if d.upgradeDecision.Hold {
261+
// Holding upgrade
262+
return nil, false
263+
}
264+
265+
if !d.upgradeDecision.UpgradeNeeded {
266+
// In upgrade scenario but upgrade is not needed
267+
return nil, false
268+
}
269+
270+
if !d.upgradeDecision.UpgradeAllowed {
271+
context.CreateEvent(k8sutil.NewUpgradeNotAllowedEvent(apiObject, d.upgradeDecision.FromVersion, d.upgradeDecision.ToVersion, d.upgradeDecision.FromLicense, d.upgradeDecision.ToLicense))
272+
return nil, false
273+
}
274+
275+
if d.updateAllowed {
276+
// We are fine, group is alive so we can proceed
277+
log.Info().Str("member", m.Member.ID).Str("Reason", d.updateMessage).Msg("Upgrade allowed")
278+
return createUpgradeMemberPlan(log, m.Member, m.Group, "Version upgrade", spec, status, !d.upgradeDecision.AutoUpgradeNeeded), false
279+
} else if d.unsafeUpdateAllowed {
280+
log.Info().Str("member", m.Member.ID).Str("Reason", d.updateMessage).Msg("Pod needs upgrade but cluster is not ready. Either some shards are not in sync or some member is not ready, but unsafe upgrade is allowed")
281+
return createUpgradeMemberPlan(log, m.Member, m.Group, "Version upgrade", spec, status, !d.upgradeDecision.AutoUpgradeNeeded), false
282+
} else {
283+
log.Info().Str("member", m.Member.ID).Str("Reason", d.updateMessage).Msg("Pod needs upgrade but cluster is not ready. Either some shards are not in sync or some member is not ready.")
284+
return nil, true
234285
}
235-
return nil, true
236286
}
237287

288+
log.Warn().Msg("Pod upgrade plan has been made, but it has been dropped due to missing flag")
238289
return nil, false
239290
}
240291

0 commit comments

Comments
 (0)