Skip to content

Commit 0320652

Browse files
committed
Adding downtimeAllowed field
1 parent 97a46a6 commit 0320652

File tree

7 files changed

+84
-30
lines changed

7 files changed

+84
-30
lines changed

docs/Manual/Deployment/Kubernetes/DeploymentResource.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,23 @@ Possible values are:
107107

108108
This setting cannot be changed after the cluster has been created.
109109

110+
### `spec.downtimeAllowed: bool`
111+
112+
This setting is used to allow automatic reconciliation actions that yield
113+
some downtime of the ArangoDB deployment.
114+
When this setting is set to `false` (the default), no automatic action that
115+
may result in downtime is allowed.
116+
If the need for such an action is detected, an event is added to the `ArangoDeployment`.
117+
118+
Once this setting is set to `true`, the automatic action is executed.
119+
120+
Operations that may result in downtime are:
121+
122+
- Rotating TLS CA certificate
123+
124+
Note: It is still possible that there is some downtime when the Kubernetes
125+
cluster is down, or in a bad state, irrespective of the value of this setting.
126+
110127
### `spec.rocksdb.encryption.keySecretName`
111128

112129
This setting specifies the name of a kubernetes `Secret` that contains

pkg/apis/deployment/v1alpha/deployment_spec.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type DeploymentSpec struct {
5050
StorageEngine *StorageEngine `json:"storageEngine,omitempty"`
5151
Image *string `json:"image,omitempty"`
5252
ImagePullPolicy *v1.PullPolicy `json:"imagePullPolicy,omitempty"`
53+
DowntimeAllowed *bool `json:"downtimeAllowed,omitempty"`
5354

5455
ExternalAccess ExternalAccessSpec `json:"externalAccess"`
5556
RocksDB RocksDBSpec `json:"rocksdb"`
@@ -92,6 +93,11 @@ func (s DeploymentSpec) GetImagePullPolicy() v1.PullPolicy {
9293
return util.PullPolicyOrDefault(s.ImagePullPolicy)
9394
}
9495

96+
// IsDowntimeAllowed returns the value of downtimeAllowed.
97+
func (s DeploymentSpec) IsDowntimeAllowed() bool {
98+
return util.BoolOrDefault(s.DowntimeAllowed)
99+
}
100+
95101
// IsAuthenticated returns true when authentication is enabled
96102
func (s DeploymentSpec) IsAuthenticated() bool {
97103
return s.Authentication.IsAuthenticated()
@@ -171,6 +177,9 @@ func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) {
171177
if s.ImagePullPolicy == nil {
172178
s.ImagePullPolicy = util.NewPullPolicyOrNil(source.ImagePullPolicy)
173179
}
180+
if s.DowntimeAllowed == nil {
181+
s.DowntimeAllowed = util.NewBoolOrNil(source.DowntimeAllowed)
182+
}
174183
s.ExternalAccess.SetDefaultsFrom(source.ExternalAccess)
175184
s.RocksDB.SetDefaultsFrom(source.RocksDB)
176185
s.Authentication.SetDefaultsFrom(source.Authentication)

pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,15 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
253253
**out = **in
254254
}
255255
}
256+
if in.DowntimeAllowed != nil {
257+
in, out := &in.DowntimeAllowed, &out.DowntimeAllowed
258+
if *in == nil {
259+
*out = nil
260+
} else {
261+
*out = new(bool)
262+
**out = **in
263+
}
264+
}
256265
in.ExternalAccess.DeepCopyInto(&out.ExternalAccess)
257266
in.RocksDB.DeepCopyInto(&out.RocksDB)
258267
in.Authentication.DeepCopyInto(&out.Authentication)

pkg/deployment/reconcile/plan_builder.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func createPlan(log zerolog.Logger, apiObject k8sutil.APIObject,
8282
getTLSKeyfile func(group api.ServerGroup, member api.MemberStatus) (string, error),
8383
getTLSCA func(string) (string, string, bool, error),
8484
getPVC func(pvcName string) (*v1.PersistentVolumeClaim, error),
85-
createEvent func(evt *v1.Event)) (api.Plan, bool) {
85+
createEvent func(evt *k8sutil.Event)) (api.Plan, bool) {
8686
if len(currentPlan) > 0 {
8787
// Plan already exists, complete that first
8888
return currentPlan, false
@@ -189,7 +189,7 @@ func createPlan(log zerolog.Logger, apiObject k8sutil.APIObject,
189189

190190
// Check for the need to rotate TLS CA certificate and all members
191191
if len(plan) == 0 {
192-
plan = createRotateTLSCAPlan(log, spec, status, getTLSCA)
192+
plan = createRotateTLSCAPlan(log, apiObject, spec, status, getTLSCA, createEvent)
193193
}
194194

195195
// Return plan

pkg/deployment/reconcile/plan_builder_storage.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import (
3535
// different storage class or a difference in storage resource requirements.
3636
func createRotateServerStoragePlan(log zerolog.Logger, apiObject k8sutil.APIObject, spec api.DeploymentSpec, status api.DeploymentStatus,
3737
getPVC func(pvcName string) (*v1.PersistentVolumeClaim, error),
38-
createEvent func(evt *v1.Event)) api.Plan {
38+
createEvent func(evt *k8sutil.Event)) api.Plan {
3939
if spec.GetMode() == api.DeploymentModeSingle {
4040
// Storage cannot be changed in single server deployments
4141
return nil

pkg/deployment/reconcile/plan_builder_tls.go

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"time"
3030

3131
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
32+
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
3233
"github.com/rs/zerolog"
3334
)
3435

@@ -76,8 +77,10 @@ func createRotateTLSServerCertificatePlan(log zerolog.Logger, spec api.Deploymen
7677
}
7778

7879
// createRotateTLSCAPlan creates plan to replace a TLS CA and rotate all server.
79-
func createRotateTLSCAPlan(log zerolog.Logger, spec api.DeploymentSpec, status api.DeploymentStatus,
80-
getTLSCA func(string) (string, string, bool, error)) api.Plan {
80+
func createRotateTLSCAPlan(log zerolog.Logger, apiObject k8sutil.APIObject,
81+
spec api.DeploymentSpec, status api.DeploymentStatus,
82+
getTLSCA func(string) (string, string, bool, error),
83+
createEvent func(evt *k8sutil.Event)) api.Plan {
8184
if !spec.TLS.IsSecure() {
8285
return nil
8386
}
@@ -93,31 +96,37 @@ func createRotateTLSCAPlan(log zerolog.Logger, spec api.DeploymentSpec, status a
9396
}
9497
var plan api.Plan
9598
if renewalNeeded, reason := tlsCANeedsRenewal(log, cert, spec.TLS); renewalNeeded {
96-
var planSuffix api.Plan
97-
plan = append(plan,
98-
api.NewAction(api.ActionTypeRenewTLSCACertificate, 0, "", reason),
99-
)
100-
status.Members.ForeachServerGroup(func(group api.ServerGroup, members api.MemberStatusList) error {
101-
for _, m := range members {
102-
if m.Phase != api.MemberPhaseCreated {
103-
// Only make changes when phase is created
104-
continue
99+
if spec.IsDowntimeAllowed() {
100+
var planSuffix api.Plan
101+
plan = append(plan,
102+
api.NewAction(api.ActionTypeRenewTLSCACertificate, 0, "", reason),
103+
)
104+
status.Members.ForeachServerGroup(func(group api.ServerGroup, members api.MemberStatusList) error {
105+
for _, m := range members {
106+
if m.Phase != api.MemberPhaseCreated {
107+
// Only make changes when phase is created
108+
continue
109+
}
110+
if !group.IsArangod() {
111+
// Sync master/worker is not applicable here
112+
continue
113+
}
114+
plan = append(plan,
115+
api.NewAction(api.ActionTypeRenewTLSCertificate, group, m.ID),
116+
api.NewAction(api.ActionTypeRotateMember, group, m.ID, "TLS CA certificate changed"),
117+
)
118+
planSuffix = append(planSuffix,
119+
api.NewAction(api.ActionTypeWaitForMemberUp, group, m.ID, "TLS CA certificate changed"),
120+
)
105121
}
106-
if !group.IsArangod() {
107-
// Sync master/worker is not applicable here
108-
continue
109-
}
110-
plan = append(plan,
111-
api.NewAction(api.ActionTypeRenewTLSCertificate, group, m.ID),
112-
api.NewAction(api.ActionTypeRotateMember, group, m.ID, "TLS CA certificate changed"),
113-
)
114-
planSuffix = append(planSuffix,
115-
api.NewAction(api.ActionTypeWaitForMemberUp, group, m.ID, "TLS CA certificate changed"),
116-
)
117-
}
118-
return nil
119-
})
120-
plan = append(plan, planSuffix...)
122+
return nil
123+
})
124+
plan = append(plan, planSuffix...)
125+
} else {
126+
// Rotating the CA results in downtime.
127+
// That is currently not allowed.
128+
createEvent(k8sutil.NewDowntimeNotAllowedEvent(apiObject, "Rotate TLS CA"))
129+
}
121130
}
122131
return plan
123132
}

pkg/util/k8sutil/events.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,24 @@ func NewPlanAbortedEvent(apiObject APIObject, itemType, memberID, role string) *
173173

174174
// NewCannotChangeStorageClassEvent creates an event indicating that an item would need to use a different StorageClass,
175175
// but this is not possible for the given reason.
176-
func NewCannotChangeStorageClassEvent(apiObject APIObject, memberID, role, subReason string) *v1.Event {
176+
func NewCannotChangeStorageClassEvent(apiObject APIObject, memberID, role, subReason string) *Event {
177177
event := newDeploymentEvent(apiObject)
178178
event.Type = v1.EventTypeNormal
179179
event.Reason = fmt.Sprintf("%s Member StorageClass Cannot Change", strings.Title(role))
180180
event.Message = fmt.Sprintf("Member %s with role %s should use a different StorageClass, but is cannot because: %s", memberID, role, subReason)
181181
return event
182182
}
183183

184+
// NewDowntimeNotAllowedEvent creates an event indicating that an operation cannot be executed because downtime
185+
// is currently not allowed.
186+
func NewDowntimeNotAllowedEvent(apiObject APIObject, operation string) *Event {
187+
event := newDeploymentEvent(apiObject)
188+
event.Type = v1.EventTypeNormal
189+
event.Reason = "Downtime Operation Postponed"
190+
event.Message = fmt.Sprintf("The '%s' operation is postponed because downtime it not allowed. Set `spec.downtimeAllowed` to true to execute this operation", operation)
191+
return event
192+
}
193+
184194
// NewErrorEvent creates an even of type error.
185195
func NewErrorEvent(reason string, err error, apiObject APIObject) *Event {
186196
event := newDeploymentEvent(apiObject)

0 commit comments

Comments
 (0)