Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding downtimeAllowed field #194

Merged
merged 2 commits into from
Jun 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/Manual/Deployment/Kubernetes/DeploymentResource.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,23 @@ Possible values are:

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

### `spec.downtimeAllowed: bool`

This setting is used to allow automatic reconciliation actions that yield
some downtime of the ArangoDB deployment.
When this setting is set to `false` (the default), no automatic action that
may result in downtime is allowed.
If the need for such an action is detected, an event is added to the `ArangoDeployment`.

Once this setting is set to `true`, the automatic action is executed.

Operations that may result in downtime are:

- Rotating TLS CA certificate

Note: It is still possible that there is some downtime when the Kubernetes
cluster is down, or in a bad state, irrespective of the value of this setting.

### `spec.rocksdb.encryption.keySecretName`

This setting specifies the name of a kubernetes `Secret` that contains
Expand Down
9 changes: 9 additions & 0 deletions pkg/apis/deployment/v1alpha/deployment_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type DeploymentSpec struct {
StorageEngine *StorageEngine `json:"storageEngine,omitempty"`
Image *string `json:"image,omitempty"`
ImagePullPolicy *v1.PullPolicy `json:"imagePullPolicy,omitempty"`
DowntimeAllowed *bool `json:"downtimeAllowed,omitempty"`

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

// IsDowntimeAllowed returns the value of downtimeAllowed.
func (s DeploymentSpec) IsDowntimeAllowed() bool {
return util.BoolOrDefault(s.DowntimeAllowed)
}

// IsAuthenticated returns true when authentication is enabled
func (s DeploymentSpec) IsAuthenticated() bool {
return s.Authentication.IsAuthenticated()
Expand Down Expand Up @@ -171,6 +177,9 @@ func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) {
if s.ImagePullPolicy == nil {
s.ImagePullPolicy = util.NewPullPolicyOrNil(source.ImagePullPolicy)
}
if s.DowntimeAllowed == nil {
s.DowntimeAllowed = util.NewBoolOrNil(source.DowntimeAllowed)
}
s.ExternalAccess.SetDefaultsFrom(source.ExternalAccess)
s.RocksDB.SetDefaultsFrom(source.RocksDB)
s.Authentication.SetDefaultsFrom(source.Authentication)
Expand Down
9 changes: 9 additions & 0 deletions pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,15 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
**out = **in
}
}
if in.DowntimeAllowed != nil {
in, out := &in.DowntimeAllowed, &out.DowntimeAllowed
if *in == nil {
*out = nil
} else {
*out = new(bool)
**out = **in
}
}
in.ExternalAccess.DeepCopyInto(&out.ExternalAccess)
in.RocksDB.DeepCopyInto(&out.RocksDB)
in.Authentication.DeepCopyInto(&out.Authentication)
Expand Down
2 changes: 1 addition & 1 deletion pkg/deployment/reconcile/plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func createPlan(log zerolog.Logger, apiObject k8sutil.APIObject,

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

// Return plan
Expand Down
61 changes: 35 additions & 26 deletions pkg/deployment/reconcile/plan_builder_tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"time"

api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1alpha"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
"github.com/rs/zerolog"
)

Expand Down Expand Up @@ -76,8 +77,10 @@ func createRotateTLSServerCertificatePlan(log zerolog.Logger, spec api.Deploymen
}

// createRotateTLSCAPlan creates plan to replace a TLS CA and rotate all server.
func createRotateTLSCAPlan(log zerolog.Logger, spec api.DeploymentSpec, status api.DeploymentStatus,
getTLSCA func(string) (string, string, bool, error)) api.Plan {
func createRotateTLSCAPlan(log zerolog.Logger, apiObject k8sutil.APIObject,
spec api.DeploymentSpec, status api.DeploymentStatus,
getTLSCA func(string) (string, string, bool, error),
createEvent func(evt *k8sutil.Event)) api.Plan {
if !spec.TLS.IsSecure() {
return nil
}
Expand All @@ -93,31 +96,37 @@ func createRotateTLSCAPlan(log zerolog.Logger, spec api.DeploymentSpec, status a
}
var plan api.Plan
if renewalNeeded, reason := tlsCANeedsRenewal(log, cert, spec.TLS); renewalNeeded {
var planSuffix api.Plan
plan = append(plan,
api.NewAction(api.ActionTypeRenewTLSCACertificate, 0, "", reason),
)
status.Members.ForeachServerGroup(func(group api.ServerGroup, members api.MemberStatusList) error {
for _, m := range members {
if m.Phase != api.MemberPhaseCreated {
// Only make changes when phase is created
continue
if spec.IsDowntimeAllowed() {
var planSuffix api.Plan
plan = append(plan,
api.NewAction(api.ActionTypeRenewTLSCACertificate, 0, "", reason),
)
status.Members.ForeachServerGroup(func(group api.ServerGroup, members api.MemberStatusList) error {
for _, m := range members {
if m.Phase != api.MemberPhaseCreated {
// Only make changes when phase is created
continue
}
if !group.IsArangod() {
// Sync master/worker is not applicable here
continue
}
plan = append(plan,
api.NewAction(api.ActionTypeRenewTLSCertificate, group, m.ID),
api.NewAction(api.ActionTypeRotateMember, group, m.ID, "TLS CA certificate changed"),
)
planSuffix = append(planSuffix,
api.NewAction(api.ActionTypeWaitForMemberUp, group, m.ID, "TLS CA certificate changed"),
)
}
if !group.IsArangod() {
// Sync master/worker is not applicable here
continue
}
plan = append(plan,
api.NewAction(api.ActionTypeRenewTLSCertificate, group, m.ID),
api.NewAction(api.ActionTypeRotateMember, group, m.ID, "TLS CA certificate changed"),
)
planSuffix = append(planSuffix,
api.NewAction(api.ActionTypeWaitForMemberUp, group, m.ID, "TLS CA certificate changed"),
)
}
return nil
})
plan = append(plan, planSuffix...)
return nil
})
plan = append(plan, planSuffix...)
} else {
// Rotating the CA results in downtime.
// That is currently not allowed.
createEvent(k8sutil.NewDowntimeNotAllowedEvent(apiObject, "Rotate TLS CA"))
}
}
return plan
}
Expand Down
10 changes: 10 additions & 0 deletions pkg/util/k8sutil/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@ func NewCannotChangeStorageClassEvent(apiObject APIObject, memberID, role, subRe
return event
}

// NewDowntimeNotAllowedEvent creates an event indicating that an operation cannot be executed because downtime
// is currently not allowed.
func NewDowntimeNotAllowedEvent(apiObject APIObject, operation string) *Event {
event := newDeploymentEvent(apiObject)
event.Type = v1.EventTypeNormal
event.Reason = "Downtime Operation Postponed"
event.Message = fmt.Sprintf("The '%s' operation is postponed because downtime it not allowed. Set `spec.downtimeAllowed` to true to execute this operation", operation)
return event
}

// NewErrorEvent creates an even of type error.
func NewErrorEvent(reason string, err error, apiObject APIObject) *Event {
event := newDeploymentEvent(apiObject)
Expand Down