diff --git a/CHANGELOG.md b/CHANGELOG.md index cf2161ddc..9092d7b80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Change Log ## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A) +- Add Operator Maintenance Management feature ## [1.0.6](https://github.com/arangodb/kube-arangodb/tree/1.0.6) (2020-08-19) - Add Operator Namespaced mode (Alpha) diff --git a/README.md b/README.md index 7188cb59a..0aabb7021 100644 --- a/README.md +++ b/README.md @@ -60,25 +60,26 @@ covers individual newer features separately. Feature-wise production readiness table: -| Feature | Operator Version | ArangoDB Version | ArangoDB Edition | State | Enabled | Flag | Remarks | -|---------------------------------|------------------|------------------|-----------------------|------------|---------|------------------------------------------|--------------------------------------------------------------------------| -| Pod Disruption Budgets | 0.3.10 | Any | Community, Enterprise | Alpha | True | N/A | N/A | -| Pod Disruption Budgets | 0.3.11 | Any | Community, Enterprise | Production | True | N/A | N/A | -| Volume Resizing | 0.3.10 | Any | Community, Enterprise | Alpha | True | N/A | N/A | -| Volume Resizing | 0.3.11 | Any | Community, Enterprise | Production | True | N/A | N/A | -| Disabling of liveness probes | 0.3.10 | Any | Community, Enterprise | Alpha | True | N/A | N/A | -| Disabling of liveness probes | 0.3.11 | Any | Community, Enterprise | Production | True | N/A | N/A | -| Volume Claim Templates | 0.3.11 | Any | Community, Enterprise | Alpha | True | N/A | N/A | -| Volume Claim Templates | 1.0.0 | Any | Community, Enterprise | Production | True | N/A | N/A | -| Prometheus Metrics Exporter | 0.3.11 | Any | Community, Enterprise | Alpha | True | N/A | Prometheus required | -| Prometheus Metrics Exporter | 1.0.0 | Any | Community, Enterprise | Production | True | N/A | Prometheus required | -| Sidecar Containers | 0.3.11 | Any | Community, Enterprise | Alpha | True | N/A | N/A | -| Sidecar Containers | 1.0.0 | Any | Community, Enterprise | Production | True | N/A | N/A | -| Operator Single Mode | 1.0.4 | Any | Community, Enterprise | Production | False | --mode.single | Only 1 instance of Operator allowed in namespace when feature is enabled | -| TLS SNI Support | 1.0.3 | >= 3.7.0 | Enterprise | Production | True | --deployment.feature.tls-sni | N/A | -| TLS Runtime Rotation Support | 1.0.4 | > 3.7.0 | Enterprise | Alpha | False | --deployment.feature.tls-rotation | N/A | -| JWT Rotation Support | 1.0.4 | > 3.7.0 | Enterprise | Alpha | False | --deployment.feature.jwt-rotation | N/A | -| Encryption Key Rotation Support | 1.0.4 | > 3.7.0 | Enterprise | Alpha | False | --deployment.feature.encryption-rotation | N/A | +| Feature | Operator Version | ArangoDB Version | ArangoDB Edition | State | Enabled | Flag | Remarks | +|-----------------------------------------|------------------|------------------|-----------------------|------------|---------|------------------------------------------|--------------------------------------------------------------------------| +| Pod Disruption Budgets | 0.3.10 | Any | Community, Enterprise | Alpha | True | N/A | N/A | +| Pod Disruption Budgets | 0.3.11 | Any | Community, Enterprise | Production | True | N/A | N/A | +| Volume Resizing | 0.3.10 | Any | Community, Enterprise | Alpha | True | N/A | N/A | +| Volume Resizing | 0.3.11 | Any | Community, Enterprise | Production | True | N/A | N/A | +| Disabling of liveness probes | 0.3.10 | Any | Community, Enterprise | Alpha | True | N/A | N/A | +| Disabling of liveness probes | 0.3.11 | Any | Community, Enterprise | Production | True | N/A | N/A | +| Volume Claim Templates | 0.3.11 | Any | Community, Enterprise | Alpha | True | N/A | N/A | +| Volume Claim Templates | 1.0.0 | Any | Community, Enterprise | Production | True | N/A | N/A | +| Prometheus Metrics Exporter | 0.3.11 | Any | Community, Enterprise | Alpha | True | N/A | Prometheus required | +| Prometheus Metrics Exporter | 1.0.0 | Any | Community, Enterprise | Production | True | N/A | Prometheus required | +| Sidecar Containers | 0.3.11 | Any | Community, Enterprise | Alpha | True | N/A | N/A | +| Sidecar Containers | 1.0.0 | Any | Community, Enterprise | Production | True | N/A | N/A | +| Operator Single Mode | 1.0.4 | Any | Community, Enterprise | Production | False | --mode.single | Only 1 instance of Operator allowed in namespace when feature is enabled | +| TLS SNI Support | 1.0.3 | >= 3.7.0 | Enterprise | Production | True | --deployment.feature.tls-sni | N/A | +| TLS Runtime Rotation Support | 1.0.4 | > 3.7.0 | Enterprise | Alpha | False | --deployment.feature.tls-rotation | N/A | +| JWT Rotation Support | 1.0.4 | > 3.7.0 | Enterprise | Alpha | False | --deployment.feature.jwt-rotation | N/A | +| Encryption Key Rotation Support | 1.0.4 | > 3.7.0 | Enterprise | Alpha | False | --deployment.feature.encryption-rotation | N/A | +| Operator Maintenance Management Support | 1.0.7 | >= 3.5.0 | Community, Enterprise | Alpha | False | --deployment.feature.maintenance | N/A | ## Release notes for 0.3.16 diff --git a/pkg/apis/deployment/v1/database_spec.go b/pkg/apis/deployment/v1/database_spec.go new file mode 100644 index 000000000..2a070daa7 --- /dev/null +++ b/pkg/apis/deployment/v1/database_spec.go @@ -0,0 +1,35 @@ +// +// DISCLAIMER +// +// Copyright 2020 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// +// Author Adam Janikowski +// + +package v1 + +type DatabaseSpec struct { + Maintenance *bool `json:"maintenance,omitempty"` +} + +func (m *DatabaseSpec) GetMaintenance() bool { + if m == nil || m.Maintenance == nil { + return false + } + + return *m.Maintenance +} diff --git a/pkg/apis/deployment/v1/deployment_spec.go b/pkg/apis/deployment/v1/deployment_spec.go index b44e8467e..da794dcbf 100644 --- a/pkg/apis/deployment/v1/deployment_spec.go +++ b/pkg/apis/deployment/v1/deployment_spec.go @@ -95,6 +95,8 @@ type DeploymentSpec struct { ID *ServerIDGroupSpec `json:"id,omitempty"` + Database *DatabaseSpec `json:"database,omitempty"` + Single ServerGroupSpec `json:"single"` Agents ServerGroupSpec `json:"agents"` DBServers ServerGroupSpec `json:"dbservers"` @@ -295,6 +297,9 @@ func (s *DeploymentSpec) SetDefaultsFrom(source DeploymentSpec) { if s.AllowUnsafeUpgrade == nil { s.AllowUnsafeUpgrade = util.NewBoolOrNil(source.AllowUnsafeUpgrade) } + if s.Database == nil { + s.Database = source.Database.DeepCopy() + } s.License.SetDefaultsFrom(source.License) s.ExternalAccess.SetDefaultsFrom(source.ExternalAccess) diff --git a/pkg/apis/deployment/v1/plan.go b/pkg/apis/deployment/v1/plan.go index 2cd886c8c..aecd51382 100644 --- a/pkg/apis/deployment/v1/plan.go +++ b/pkg/apis/deployment/v1/plan.go @@ -119,6 +119,10 @@ const ( ActionTypeJWTPropagated ActionType = "JWTPropagated" // ActionTypeClusterMemberCleanup removes member from cluster ActionTypeClusterMemberCleanup ActionType = "ClusterMemberCleanup" + // ActionTypeEnableMaintenance enables maintenance on cluster. + ActionTypeEnableMaintenance ActionType = "EnableMaintenance" + // ActionTypeEnableMaintenance disables maintenance on cluster. + ActionTypeDisableMaintenance ActionType = "DisableMaintenance" ) const ( diff --git a/pkg/apis/deployment/v1/zz_generated.deepcopy.go b/pkg/apis/deployment/v1/zz_generated.deepcopy.go index c9cd1dfc8..b47b52df5 100644 --- a/pkg/apis/deployment/v1/zz_generated.deepcopy.go +++ b/pkg/apis/deployment/v1/zz_generated.deepcopy.go @@ -258,6 +258,27 @@ func (in ConditionList) DeepCopy() ConditionList { return *out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseSpec) DeepCopyInto(out *DatabaseSpec) { + *out = *in + if in.Maintenance != nil { + in, out := &in.Maintenance, &out.Maintenance + *out = new(bool) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseSpec. +func (in *DatabaseSpec) DeepCopy() *DatabaseSpec { + if in == nil { + return nil + } + out := new(DatabaseSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeploymentFeatures) DeepCopyInto(out *DeploymentFeatures) { *out = *in @@ -415,6 +436,11 @@ func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) { *out = new(ServerIDGroupSpec) (*in).DeepCopyInto(*out) } + if in.Database != nil { + in, out := &in.Database, &out.Database + *out = new(DatabaseSpec) + (*in).DeepCopyInto(*out) + } in.Single.DeepCopyInto(&out.Single) in.Agents.DeepCopyInto(&out.Agents) in.DBServers.DeepCopyInto(&out.DBServers) diff --git a/pkg/deployment/agency/agency.go b/pkg/deployment/agency/agency.go index 83870ce5b..87eae4b98 100644 --- a/pkg/deployment/agency/agency.go +++ b/pkg/deployment/agency/agency.go @@ -22,41 +22,25 @@ package agency -type ArangoPlanDatabases map[string]ArangoPlanCollections - -func (a ArangoPlanDatabases) IsDBServerInDatabases(name string) bool { - for _, collections := range a { - if collections.IsDBServerInCollections(name) { - return true - } - } - return false -} - -type ArangoPlanCollections map[string]ArangoPlanCollection - -func (a ArangoPlanCollections) IsDBServerInCollections(name string) bool { - for _, collection := range a { - if collection.IsDBServerInShards(name) { - return true +import ( + "context" + + "github.com/arangodb/go-driver/agency" + "github.com/pkg/errors" +) + +type Fetcher func(ctx context.Context, i interface{}, keyParts ...string) error + +func NewFetcher(a agency.Agency) Fetcher { + return func(ctx context.Context, i interface{}, keyParts ...string) error { + if err := a.ReadKey(ctx, []string{ + ArangoKey, + PlanKey, + PlanCollectionsKey, + }, i); err != nil { + return errors.WithStack(err) } - } - return false -} - -type ArangoPlanCollection struct { - Shards ArangoPlanShard `json:"shards"` -} -func (a ArangoPlanCollection) IsDBServerInShards(name string) bool { - for _, dbservers := range a.Shards { - for _, dbserver := range dbservers { - if dbserver == name { - return true - } - } + return nil } - return false } - -type ArangoPlanShard map[string][]string diff --git a/pkg/deployment/agency/database.go b/pkg/deployment/agency/database.go new file mode 100644 index 000000000..f4a3f79a3 --- /dev/null +++ b/pkg/deployment/agency/database.go @@ -0,0 +1,78 @@ +// +// DISCLAIMER +// +// Copyright 2020 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// +// Author Adam Janikowski +// + +package agency + +import ( + "context" + + "github.com/pkg/errors" +) + +func GetAgencyCollections(ctx context.Context, f Fetcher) (*ArangoPlanDatabases, error) { + ret := &ArangoPlanDatabases{} + + if err := f(ctx, ret, ArangoKey, PlanKey, PlanCollectionsKey); err != nil { + return nil, errors.WithStack(err) + } + + return ret, nil +} + +type ArangoPlanDatabases map[string]ArangoPlanCollections + +func (a ArangoPlanDatabases) IsDBServerInDatabases(name string) bool { + for _, collections := range a { + if collections.IsDBServerInCollections(name) { + return true + } + } + return false +} + +type ArangoPlanCollections map[string]ArangoPlanCollection + +func (a ArangoPlanCollections) IsDBServerInCollections(name string) bool { + for _, collection := range a { + if collection.IsDBServerInShards(name) { + return true + } + } + return false +} + +type ArangoPlanCollection struct { + Shards ArangoPlanShard `json:"shards"` +} + +func (a ArangoPlanCollection) IsDBServerInShards(name string) bool { + for _, dbservers := range a.Shards { + for _, dbserver := range dbservers { + if dbserver == name { + return true + } + } + } + return false +} + +type ArangoPlanShard map[string][]string diff --git a/pkg/deployment/agency/maintenance.go b/pkg/deployment/agency/maintenance.go new file mode 100644 index 000000000..933d65f67 --- /dev/null +++ b/pkg/deployment/agency/maintenance.go @@ -0,0 +1,91 @@ +// +// DISCLAIMER +// +// Copyright 2020 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// +// Author Adam Janikowski +// + +package agency + +import ( + "context" + "net/http" + + "github.com/arangodb/go-driver" +) + +type Maintenance struct { + Result string `json:"result"` +} + +func (m Maintenance) Enabled() bool { + return m.Result == "Maintenance" +} + +func GetMaintenanceMode(ctx context.Context, client driver.Client) (Maintenance, error) { + conn := client.Connection() + r, err := conn.NewRequest(http.MethodGet, "/_admin/cluster/maintenance") + if err != nil { + return Maintenance{}, err + } + + resp, err := conn.Do(ctx, r) + if err != nil { + return Maintenance{}, err + } + + if err := resp.CheckStatus(http.StatusOK); err != nil { + return Maintenance{}, err + } + + var m Maintenance + + if err := resp.ParseBody("", &m); err != nil { + return Maintenance{}, err + } + + return m, nil +} + +func SetMaintenanceMode(ctx context.Context, client driver.Client, enabled bool) error { + data := "on" + if !enabled { + data = "off" + } + + conn := client.Connection() + r, err := conn.NewRequest(http.MethodPut, "/_admin/cluster/maintenance") + if err != nil { + return err + } + + if _, err := r.SetBody(data); err != nil { + return err + } + + resp, err := conn.Do(ctx, r) + if err != nil { + return err + } + + if err := resp.CheckStatus(http.StatusOK); err != nil { + return err + } + + return nil +} diff --git a/pkg/deployment/features/encryption.go b/pkg/deployment/features/encryption.go index 97a9882f2..1310fe2f0 100644 --- a/pkg/deployment/features/encryption.go +++ b/pkg/deployment/features/encryption.go @@ -17,6 +17,8 @@ // // Copyright holder is ArangoDB GmbH, Cologne, Germany // +// Author Adam Janikowski +// package features diff --git a/pkg/deployment/features/features.go b/pkg/deployment/features/features.go index 726dff2ec..cb5ae6e91 100644 --- a/pkg/deployment/features/features.go +++ b/pkg/deployment/features/features.go @@ -17,6 +17,8 @@ // // Copyright holder is ArangoDB GmbH, Cologne, Germany // +// Author Adam Janikowski +// package features diff --git a/pkg/deployment/features/jwt.go b/pkg/deployment/features/jwt.go index df6d32431..4d7d8445b 100644 --- a/pkg/deployment/features/jwt.go +++ b/pkg/deployment/features/jwt.go @@ -17,6 +17,8 @@ // // Copyright holder is ArangoDB GmbH, Cologne, Germany // +// Author Adam Janikowski +// package features diff --git a/pkg/deployment/features/local.go b/pkg/deployment/features/local.go index d20abd183..4080b3472 100644 --- a/pkg/deployment/features/local.go +++ b/pkg/deployment/features/local.go @@ -17,6 +17,8 @@ // // Copyright holder is ArangoDB GmbH, Cologne, Germany // +// Author Adam Janikowski +// package features @@ -70,7 +72,7 @@ func Init(cmd *cobra.Command) { if v != "" && feature.EnterpriseRequired() { z = fmt.Sprintf("%s - Required version %s and Enterprise Edition", feature.Description(), v) } else if v != "" { - z = fmt.Sprintf("%s. Required version %s", feature.Description(), v) + z = fmt.Sprintf("%s - Required version %s", feature.Description(), v) } else if feature.EnterpriseRequired() { z = fmt.Sprintf("%s - Required Enterprise Edition", feature.Description()) } else { diff --git a/pkg/deployment/features/maintenance.go b/pkg/deployment/features/maintenance.go new file mode 100644 index 000000000..d3ff243c1 --- /dev/null +++ b/pkg/deployment/features/maintenance.go @@ -0,0 +1,39 @@ +// +// DISCLAIMER +// +// Copyright 2020 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// +// Author Adam Janikowski +// + +package features + +func init() { + registerFeature(maintenance) +} + +var maintenance = &feature{ + name: "maintenance", + description: "Database maintenance mode management", + version: "3.5.0", + enterpriseRequired: false, + enabledByDefault: false, +} + +func Maintenance() Feature { + return maintenance +} diff --git a/pkg/deployment/features/tls.go b/pkg/deployment/features/tls.go index 3a17a5d7a..de49d9b4c 100644 --- a/pkg/deployment/features/tls.go +++ b/pkg/deployment/features/tls.go @@ -17,6 +17,8 @@ // // Copyright holder is ArangoDB GmbH, Cologne, Germany // +// Author Adam Janikowski +// package features diff --git a/pkg/deployment/reconcile/action_disable_scaling_cluster.go b/pkg/deployment/reconcile/action_disable_scaling_cluster.go index b54f9691c..d2c8094a6 100644 --- a/pkg/deployment/reconcile/action_disable_scaling_cluster.go +++ b/pkg/deployment/reconcile/action_disable_scaling_cluster.go @@ -17,6 +17,8 @@ // // Copyright holder is ArangoDB GmbH, Cologne, Germany // +// Author Adam Janikowski +// package reconcile diff --git a/pkg/deployment/reconcile/action_enable_scaling_cluster.go b/pkg/deployment/reconcile/action_enable_scaling_cluster.go index feb539862..5f05ca763 100644 --- a/pkg/deployment/reconcile/action_enable_scaling_cluster.go +++ b/pkg/deployment/reconcile/action_enable_scaling_cluster.go @@ -17,6 +17,8 @@ // // Copyright holder is ArangoDB GmbH, Cologne, Germany // +// Author Adam Janikowski +// package reconcile diff --git a/pkg/deployment/reconcile/action_maintenance_disable.go b/pkg/deployment/reconcile/action_maintenance_disable.go new file mode 100644 index 000000000..4e7e53ce9 --- /dev/null +++ b/pkg/deployment/reconcile/action_maintenance_disable.go @@ -0,0 +1,72 @@ +// +// DISCLAIMER +// +// Copyright 2020 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// +// Author Adam Janikowski +// + +package reconcile + +import ( + "context" + + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + "github.com/arangodb/kube-arangodb/pkg/deployment/agency" + "github.com/rs/zerolog" +) + +func init() { + registerAction(api.ActionTypeDisableMaintenance, newDisableMaintenanceAction) +} + +func newDisableMaintenanceAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { + a := &actionDisableMaintenance{} + + a.actionImpl = newActionImpl(log, action, actionCtx, addMemberTimeout, &a.newMemberID) + + return a +} + +type actionDisableMaintenance struct { + // actionImpl implement timeout and member id functions + actionImpl + + actionEmptyCheckProgress + + newMemberID string +} + +func (a *actionDisableMaintenance) Start(ctx context.Context) (bool, error) { + switch a.actionCtx.GetMode() { + case api.DeploymentModeSingle: + return true, nil + } + + client, err := a.actionCtx.GetDatabaseClient(ctx) + if err != nil { + a.log.Error().Err(err).Msgf("Unable to get agency client") + return true, nil + } + + if err := agency.SetMaintenanceMode(ctx, client, false); err != nil { + a.log.Error().Err(err).Msgf("Unable to disable maintenance") + return true, nil + } + + return true, nil +} diff --git a/pkg/deployment/reconcile/action_maintenance_enable.go b/pkg/deployment/reconcile/action_maintenance_enable.go new file mode 100644 index 000000000..ceb3d1b68 --- /dev/null +++ b/pkg/deployment/reconcile/action_maintenance_enable.go @@ -0,0 +1,72 @@ +// +// DISCLAIMER +// +// Copyright 2020 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// +// Author Adam Janikowski +// + +package reconcile + +import ( + "context" + + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + "github.com/arangodb/kube-arangodb/pkg/deployment/agency" + "github.com/rs/zerolog" +) + +func init() { + registerAction(api.ActionTypeEnableMaintenance, newEnableMaintenanceAction) +} + +func newEnableMaintenanceAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { + a := &actionEnableMaintenance{} + + a.actionImpl = newActionImpl(log, action, actionCtx, addMemberTimeout, &a.newMemberID) + + return a +} + +type actionEnableMaintenance struct { + // actionImpl implement timeout and member id functions + actionImpl + + actionEmptyCheckProgress + + newMemberID string +} + +func (a *actionEnableMaintenance) Start(ctx context.Context) (bool, error) { + switch a.actionCtx.GetMode() { + case api.DeploymentModeSingle: + return true, nil + } + + client, err := a.actionCtx.GetDatabaseClient(ctx) + if err != nil { + a.log.Error().Err(err).Msgf("Unable to get agency client") + return true, nil + } + + if err := agency.SetMaintenanceMode(ctx, client, true); err != nil { + a.log.Error().Err(err).Msgf("Unable to set maintenance") + return true, nil + } + + return true, nil +} diff --git a/pkg/deployment/reconcile/helper_maintenance.go b/pkg/deployment/reconcile/helper_maintenance.go new file mode 100644 index 000000000..f5153884c --- /dev/null +++ b/pkg/deployment/reconcile/helper_maintenance.go @@ -0,0 +1,44 @@ +// +// DISCLAIMER +// +// Copyright 2020 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// +// Author Adam Janikowski +// + +package reconcile + +import ( + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + "github.com/arangodb/kube-arangodb/pkg/deployment/features" +) + +func withMaintenance(plan ...api.Action) api.Plan { + if !features.Maintenance().Enabled() { + return plan + } + + var newPlan api.Plan + + newPlan = append(newPlan, api.NewAction(api.ActionTypeEnableMaintenance, api.ServerGroupUnknown, "", "Enable maintenance before actions")) + + newPlan = append(newPlan, plan...) + + newPlan = append(newPlan, api.NewAction(api.ActionTypeDisableMaintenance, api.ServerGroupUnknown, "", "Disable maintenance after actions")) + + return newPlan +} diff --git a/pkg/deployment/reconcile/plan_builder.go b/pkg/deployment/reconcile/plan_builder.go index 8bda7d708..3b6fc18de 100644 --- a/pkg/deployment/reconcile/plan_builder.go +++ b/pkg/deployment/reconcile/plan_builder.go @@ -97,13 +97,7 @@ func fetchAgency(ctx context.Context, log zerolog.Logger, agencyCtx, agencyCancel := goContext.WithTimeout(ctx, time.Minute) defer agencyCancel() - ret := &agency.ArangoPlanDatabases{} - - if err := context.GetAgencyData(agencyCtx, cache, agency.ArangoKey, agency.PlanKey, agency.PlanCollectionsKey); err != nil { - return nil, err - } - - return ret, nil + return agency.GetAgencyCollections(agencyCtx, context.GetAgencyData) } else { return nil, fmt.Errorf("not able to read from agency when agency is down") } @@ -214,6 +208,10 @@ func createPlan(ctx context.Context, log zerolog.Logger, apiObject k8sutil.APIOb plan = pb.Apply(createJWTStatusUpdate) } + if plan.IsEmpty() { + plan = pb.Apply(createMaintenanceManagementPlan) + } + // Check for scale up/down if plan.IsEmpty() { plan = pb.Apply(createScaleMemeberPlan) diff --git a/pkg/deployment/reconcile/plan_builder_common.go b/pkg/deployment/reconcile/plan_builder_common.go new file mode 100644 index 000000000..9c55f4bd2 --- /dev/null +++ b/pkg/deployment/reconcile/plan_builder_common.go @@ -0,0 +1,72 @@ +// +// DISCLAIMER +// +// Copyright 2020 ArangoDB GmbH, Cologne, Germany +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// +// Author Adam Janikowski +// + +package reconcile + +import ( + "context" + + api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" + "github.com/arangodb/kube-arangodb/pkg/deployment/agency" + "github.com/arangodb/kube-arangodb/pkg/deployment/features" + "github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" + "github.com/rs/zerolog" +) + +func createMaintenanceManagementPlan(ctx context.Context, + log zerolog.Logger, apiObject k8sutil.APIObject, + spec api.DeploymentSpec, status api.DeploymentStatus, + cachedStatus inspector.Inspector, context PlanBuilderContext) api.Plan { + if spec.Mode.Get() == api.DeploymentModeSingle { + return nil + } + + if !features.Maintenance().Enabled() { + // Maintenance feature is not enabled + return nil + } + + client, err := context.GetDatabaseClient(ctx) + if err != nil { + log.Error().Err(err).Msgf("Unable to get agency client") + return nil + } + + m, err := agency.GetMaintenanceMode(ctx, client) + if err != nil { + log.Error().Err(err).Msgf("Unable to get agency maintenance mode") + return nil + } + + if !m.Enabled() && spec.Database.GetMaintenance() { + log.Info().Msgf("Enabling maintenance mode") + return api.Plan{api.NewAction(api.ActionTypeEnableMaintenance, api.ServerGroupUnknown, "")} + } + + if m.Enabled() && !spec.Database.GetMaintenance() { + log.Info().Msgf("Disabling maintenance mode") + return api.Plan{api.NewAction(api.ActionTypeEnableMaintenance, api.ServerGroupUnknown, "")} + } + + return nil +} diff --git a/pkg/deployment/reconcile/plan_builder_context.go b/pkg/deployment/reconcile/plan_builder_context.go index 18126fa97..cc4a3afb9 100644 --- a/pkg/deployment/reconcile/plan_builder_context.go +++ b/pkg/deployment/reconcile/plan_builder_context.go @@ -25,6 +25,8 @@ package reconcile import ( "context" + "github.com/arangodb/go-driver/agency" + "github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector" backupApi "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1" @@ -69,6 +71,8 @@ type PlanBuilderContext interface { GetBackup(backup string) (*backupApi.ArangoBackup, error) // GetName receives deployment name GetName() string + // GetAgency returns a connection to the entire agency. + GetAgency(ctx context.Context) (agency.Agency, error) } // newPlanBuilderContext creates a PlanBuilderContext from the given context diff --git a/pkg/deployment/reconcile/plan_builder_restore.go b/pkg/deployment/reconcile/plan_builder_restore.go index 28e5e4cac..5dd367828 100644 --- a/pkg/deployment/reconcile/plan_builder_restore.go +++ b/pkg/deployment/reconcile/plan_builder_restore.go @@ -75,14 +75,25 @@ func createRestorePlan(ctx context.Context, } } - return api.Plan{ - api.NewAction(api.ActionTypeBackupRestore, api.ServerGroupUnknown, ""), - } + return restorePlan(spec.Mode.Get()) } return nil } +func restorePlan(mode api.DeploymentMode) api.Plan { + p := api.Plan{ + api.NewAction(api.ActionTypeBackupRestore, api.ServerGroupUnknown, ""), + } + + switch mode { + case api.DeploymentModeActiveFailover: + p = withMaintenance(p...) + } + + return p +} + func createRestorePlanEncryption(ctx context.Context, log zerolog.Logger, spec api.DeploymentSpec, status api.DeploymentStatus, builderCtx PlanBuilderContext, backup *backupv1.ArangoBackup) (bool, api.Plan) { if spec.RestoreEncryptionSecret != nil { if !spec.RocksDB.IsEncrypted() { diff --git a/pkg/deployment/resources/license.go b/pkg/deployment/resources/license.go index 22bebc23b..4acb49de8 100644 --- a/pkg/deployment/resources/license.go +++ b/pkg/deployment/resources/license.go @@ -17,6 +17,8 @@ // // Copyright holder is ArangoDB GmbH, Cologne, Germany // +// Author Adam Janikowski +// package resources diff --git a/pkg/deployment/resources/pdbs.go b/pkg/deployment/resources/pdbs.go index 2dce488fc..be391a388 100644 --- a/pkg/deployment/resources/pdbs.go +++ b/pkg/deployment/resources/pdbs.go @@ -17,6 +17,8 @@ // // Copyright holder is ArangoDB GmbH, Cologne, Germany // +// Author Adam Janikowski +// package resources