Skip to content
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Change Log

## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
- Added annotation to rotate ArangoDeployment in secure way

## [1.0.0](https://github.com/arangodb/kube-arangodb/tree/1.0.0) (2020-03-03)
- Removal of v1alpha support for ArangoDeployment, ArangoDeploymentReplication, ArangoBackup
Expand Down
1 change: 1 addition & 0 deletions docs/design/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
- [Scaling](./scaling.md)
- [Status](./status.md)
- [Upgrading](./upgrading.md)
- [Rotating Pods](./rotating.md)
- [Maintenance](./maintenance.md)
13 changes: 13 additions & 0 deletions docs/design/rotating.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Rotation

## ArangoDeployment

Rotation of ArangoDeployment Pods can be triggered by Pod deletion or by annotation (safe way).

Using annotation Pods gonna be rotated one-by-one which will keep cluster alive.

Key: `deployment.arangodb.com/rotation`
Value: `true`

To rotate ArangoDeployment Pod kubectl command can be used:
`kubectl annotate pod arango-pod deployment.arangodb.com/rotation=true`
29 changes: 29 additions & 0 deletions pkg/apis/deployment/annotations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// 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 deployment

const (
ArangoDeploymentAnnotationPrefix = "deployment.arangodb.com"
ArangoDeploymentPodMaintenanceAnnotation = ArangoDeploymentAnnotationPrefix + "/maintenance"
ArangoDeploymentPodRotateAnnotation = ArangoDeploymentAnnotationPrefix + "/rotate"
)
8 changes: 3 additions & 5 deletions pkg/deployment/deployment_inspector.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"context"
"time"

"github.com/arangodb/kube-arangodb/pkg/apis/deployment"

api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/metrics"
"github.com/arangodb/kube-arangodb/pkg/util"
Expand All @@ -37,10 +39,6 @@ var (
inspectDeploymentDurationGauges = metrics.MustRegisterGaugeVec(metricsComponent, "inspect_deployment_duration", "Amount of time taken by a single inspection of a deployment (in sec)", metrics.DeploymentName)
)

const (
arangoDeploymentMaintenanceAnnotation = "deployment.arangodb.com/maintenance"
)

// inspectDeployment inspects the entire deployment, creates
// a plan to update if needed and inspects underlying resources.
// This function should be called when:
Expand Down Expand Up @@ -74,7 +72,7 @@ func (d *Deployment) inspectDeployment(lastInterval util.Interval) util.Interval
} else {
// Check if maintenance annotation is set
if updated != nil && updated.Annotations != nil {
if v, ok := updated.Annotations[arangoDeploymentMaintenanceAnnotation]; ok && v == "true" {
if v, ok := updated.Annotations[deployment.ArangoDeploymentPodMaintenanceAnnotation]; ok && v == "true" {
// Disable checks if we will enter maintenance mode
log.Info().Str("deployment", deploymentName).Msg("Deployment in maintenance mode")
return nextInterval
Expand Down
14 changes: 14 additions & 0 deletions pkg/deployment/reconcile/plan_builder_rotate_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import (
"reflect"
"strings"

"github.com/arangodb/kube-arangodb/pkg/apis/deployment"
"github.com/arangodb/kube-arangodb/pkg/deployment/pod"

"github.com/arangodb/kube-arangodb/pkg/deployment/pod"

"github.com/arangodb/go-driver"
Expand Down Expand Up @@ -87,6 +90,17 @@ func createRotateOrUpgradePlan(log zerolog.Logger, apiObject k8sutil.APIObject,
newPlan = createRotateMemberPlan(log, m, group, reason)
}
}

if !newPlan.IsEmpty() {
// Only rotate/upgrade 1 pod at a time
continue
}

if pod.Annotations != nil {
if _, ok := pod.Annotations[deployment.ArangoDeploymentPodRotateAnnotation]; ok {
newPlan = createRotateMemberPlan(log, m, group, "Rotation flag present")
}
}
}
return nil
})
Expand Down
36 changes: 34 additions & 2 deletions pkg/util/k8sutil/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@

package k8sutil

import "regexp"
import (
"regexp"

"github.com/arangodb/kube-arangodb/pkg/apis/deployment"
)

const (
kubernetesAnnotationMatch = ".*kubernetes\\.io/.*"
arangoAnnotationMatch = ".*arangodb\\.com/"
arangoAnnotationMatch = ".*arangodb\\.com/.*"
)

var (
Expand All @@ -50,6 +54,29 @@ func init() {
arangoAnnotationRegex = r
}

func isFilteredBlockedAnnotation(key string) bool {
switch key {
case deployment.ArangoDeploymentPodRotateAnnotation:
return true
default:
return false
}
}

func filterBlockedAnnotations(m map[string]string) map[string]string {
n := map[string]string{}

for key, value := range m {
if isFilteredBlockedAnnotation(key) {
continue
}

n[key] = value
}

return n
}

// MergeAnnotations into one annotations map
func MergeAnnotations(annotations ...map[string]string) map[string]string {
ret := map[string]string{}
Expand Down Expand Up @@ -114,7 +141,12 @@ func filterActualAnnotations(actual, expected map[string]string) map[string]stri

// CompareAnnotations will compare annotations, but will ignore secured annotations which are present in
// actual but not specified in expected map
// It will also filter out blocked annotations
func CompareAnnotations(actual, expected map[string]string) bool {
return compareAnnotations(filterBlockedAnnotations(actual), filterBlockedAnnotations(expected))
}

func compareAnnotations(actual, expected map[string]string) bool {
actualFiltered := filterActualAnnotations(actual, expected)

if actualFiltered == nil && expected == nil {
Expand Down