1
0
Fork 0
mirror of https://github.com/arangodb/kube-arangodb.git synced 2024-12-14 11:57:37 +00:00

[Feature] Single mode (#600)

This commit is contained in:
Adam Janikowski 2020-07-21 09:32:02 +02:00 committed by GitHub
parent 8fd654ca3d
commit f007ab0213
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 521 additions and 38 deletions

View file

@ -187,6 +187,11 @@ fmt:
@echo ">> Ensuring style of files"
@go run golang.org/x/tools/cmd/goimports -w $(SOURCES)
.PHONY: license
license:
@echo ">> Ensuring license of files"
@go run github.com/google/addlicense -f "./tools/codegen/boilerplate.go.txt" $(SOURCES)
.PHONY: fmt-verify
fmt-verify: license-verify
@echo ">> Verify files style"

View file

@ -85,6 +85,11 @@ spec:
- --operator.backup
{{- end }}
- --chaos.allowed={{ .Values.operator.allowChaos }}
{{- if .Values.operator.args }}
{{- range .Values.operator.args }}
- {{ . | quote }}
{{- end }}
{{- end }}
env:
- name: MY_POD_NAMESPACE
valueFrom:

View file

@ -5,6 +5,8 @@ operator:
imagePullPolicy: IfNotPresent
imagePullSecrets: []
args: []
service:
type: ClusterIP

View file

@ -31,6 +31,8 @@ import (
"strings"
"time"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/rs/zerolog/log"
deploymentApi "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
@ -105,6 +107,8 @@ var (
enableBackup bool // Run backup operator
alpineImage, metricsExporterImage, arangoImage string
singleMode bool
}
chaosOptions struct {
allowed bool
@ -133,7 +137,9 @@ func init() {
f.StringVar(&operatorOptions.metricsExporterImage, "operator.metrics-exporter-image", MetricsExporterImageEnv.GetOrDefault(defaultMetricsExporterImage), "Docker image used for metrics containers by default")
f.StringVar(&operatorOptions.arangoImage, "operator.arango-image", ArangoImageEnv.GetOrDefault(defaultArangoImage), "Docker image used for arango by default")
f.BoolVar(&chaosOptions.allowed, "chaos.allowed", false, "Set to allow chaos in deployments. Only activated when allowed and enabled in deployment")
f.BoolVar(&operatorOptions.singleMode, "mode.single", false, "Enable single mode in Operator. WARNING: There should be only one replica of Operator, otherwise Operator can take unexpected actions")
features.Init(&cmdMain)
}
func main() {
@ -300,6 +306,7 @@ func newOperatorConfigAndDeps(id, namespace, name string) (operator.Config, oper
AlpineImage: operatorOptions.alpineImage,
MetricsExporterImage: operatorOptions.metricsExporterImage,
ArangoImage: operatorOptions.arangoImage,
SingleMode: operatorOptions.singleMode,
}
deps := operator.Dependencies{
LogService: logService,

View file

@ -31,6 +31,8 @@ import (
"strconv"
"time"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/go-driver/http"
"github.com/arangodb/go-driver/jwt"
"github.com/arangodb/kube-arangodb/pkg/deployment/pod"
@ -245,7 +247,7 @@ func (d *Deployment) getAuth() (driver.Authentication, error) {
secrets := d.GetKubeCli().CoreV1().Secrets(d.apiObject.GetNamespace())
var secret string
if i := d.apiObject.Status.CurrentImage; i == nil || i.ArangoDBVersion.CompareTo("3.7.0") < 0 || !i.Enterprise {
if i := d.apiObject.Status.CurrentImage; i == nil || !features.JWTRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
s, err := secrets.Get(d.apiObject.Spec.Authentication.GetJWTSecretName(), meta.GetOptions{})
if err != nil {
return nil, goErrors.Errorf("JWT Secret is missing")

View file

@ -199,7 +199,7 @@ func TestEnsurePod_ArangoDB_Encryption(t *testing.T) {
},
},
{
Name: "Agent EE 3.7.0 Pod with encrypted rocksdb",
Name: "Agent EE 3.7.0 Pod with encrypted rocksdb, disabled feature",
ArangoDeployment: &api.ArangoDeployment{
Spec: api.DeploymentSpec{
Image: util.NewString(testImage),
@ -225,6 +225,70 @@ func TestEnsurePod_ArangoDB_Encryption(t *testing.T) {
k8sutil.CreateEncryptionKeySecret(secrets, testRocksDBEncryptionKey, key)
},
ExpectedEvent: "member agent is created",
ExpectedPod: core.Pod{
Spec: core.PodSpec{
Volumes: []core.Volume{
k8sutil.CreateVolumeEmptyDir(k8sutil.ArangodVolumeName),
k8sutil.CreateVolumeWithSecret(k8sutil.RocksdbEncryptionVolumeName, testRocksDBEncryptionKey),
},
Containers: []core.Container{
{
Name: k8sutil.ServerContainerName,
Image: testImage,
Command: BuildTestAgentArgs(t, firstAgentStatus.ID,
AgentArgsWithTLS(firstAgentStatus.ID, false),
ArgsWithAuth(false),
ArgsWithEncryptionKey()),
Ports: createTestPorts(),
VolumeMounts: []core.VolumeMount{
k8sutil.ArangodVolumeMount(),
k8sutil.RocksdbEncryptionVolumeMount(),
},
Resources: emptyResources,
LivenessProbe: createTestLivenessProbe(cmd, false, "", k8sutil.ArangoPort),
ImagePullPolicy: core.PullIfNotPresent,
SecurityContext: securityContext.NewSecurityContext(),
},
},
RestartPolicy: core.RestartPolicyNever,
TerminationGracePeriodSeconds: &defaultAgentTerminationTimeout,
Hostname: testDeploymentName + "-" + api.ServerGroupAgentsString + "-" + firstAgentStatus.ID,
Subdomain: testDeploymentName + "-int",
Affinity: k8sutil.CreateAffinity(testDeploymentName, api.ServerGroupAgentsString,
false, ""),
},
},
},
{
Name: "Agent EE 3.7.0 Pod with encrypted rocksdb, enabled feature",
ArangoDeployment: &api.ArangoDeployment{
Spec: api.DeploymentSpec{
Image: util.NewString(testImage),
Authentication: noAuthentication,
TLS: noTLS,
RocksDB: rocksDBSpec,
},
},
Features: testCaseFeatures{
EncryptionRotation: true,
},
Helper: func(t *testing.T, deployment *Deployment, testCase *testCaseStruct) {
deployment.status.last = api.DeploymentStatus{
Members: api.DeploymentStatusMembers{
Agents: api.MemberStatusList{
firstAgentStatus,
},
},
Images: createTestImagesWithVersion(true, "3.7.0"),
}
testCase.createTestPodData(deployment, api.ServerGroupAgents, firstAgentStatus)
secrets := deployment.GetKubeCli().CoreV1().Secrets(testNamespace)
key := make([]byte, 32)
k8sutil.CreateEncryptionKeySecret(secrets, testRocksDBEncryptionKey, key)
},
ExpectedEvent: "member agent is created",
ExpectedPod: core.Pod{
Spec: core.PodSpec{
Volumes: []core.Volume{

View file

@ -83,6 +83,9 @@ func TestEnsurePod_ArangoDB_TLS_SNI(t *testing.T) {
}(),
},
},
Features: testCaseFeatures{
TLSSNI: true,
},
Resources: func(t *testing.T, deployment *Deployment) {
createTLSSNISecret(t, deployment.GetKubeCli(), "sni1", deployment.Namespace(), constants.SecretTLSKeyfile, "")
createTLSSNISecret(t, deployment.GetKubeCli(), "sni2", deployment.Namespace(), constants.SecretTLSKeyfile, "")
@ -155,6 +158,9 @@ func TestEnsurePod_ArangoDB_TLS_SNI(t *testing.T) {
}(),
},
},
Features: testCaseFeatures{
TLSSNI: true,
},
Resources: func(t *testing.T, deployment *Deployment) {
createTLSSNISecret(t, deployment.GetKubeCli(), "sni1", deployment.Namespace(), constants.SecretTLSKeyfile, "")
createTLSSNISecret(t, deployment.GetKubeCli(), "sni2", deployment.Namespace(), constants.SecretTLSKeyfile, "")
@ -227,6 +233,9 @@ func TestEnsurePod_ArangoDB_TLS_SNI(t *testing.T) {
}(),
},
},
Features: testCaseFeatures{
TLSSNI: true,
},
Resources: func(t *testing.T, deployment *Deployment) {
createTLSSNISecret(t, deployment.GetKubeCli(), "sni1", deployment.Namespace(), constants.SecretTLSKeyfile, "")
createTLSSNISecret(t, deployment.GetKubeCli(), "sni2", deployment.Namespace(), constants.SecretTLSKeyfile, "")
@ -299,6 +308,9 @@ func TestEnsurePod_ArangoDB_TLS_SNI(t *testing.T) {
}(),
},
},
Features: testCaseFeatures{
TLSSNI: true,
},
Resources: func(t *testing.T, deployment *Deployment) {
createTLSSNISecret(t, deployment.GetKubeCli(), "sni1", deployment.Namespace(), constants.SecretTLSKeyfile, "")
createTLSSNISecret(t, deployment.GetKubeCli(), "sni2", deployment.Namespace(), constants.SecretTLSKeyfile, "")
@ -404,6 +416,9 @@ func TestEnsurePod_ArangoDB_TLS_SNI(t *testing.T) {
}(),
},
},
Features: testCaseFeatures{
TLSSNI: true,
},
Resources: func(t *testing.T, deployment *Deployment) {
createTLSSNISecret(t, deployment.GetKubeCli(), "sni1", deployment.Namespace(), constants.SecretTLSKeyfile, "")
createTLSSNISecret(t, deployment.GetKubeCli(), "sni2", deployment.Namespace(), constants.SecretTLSKeyfile, "")

View file

@ -27,6 +27,8 @@ import (
"fmt"
"testing"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/rs/zerolog/log"
"github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector"
@ -92,6 +94,15 @@ func runTestCase(t *testing.T, testCase testCaseStruct) {
testCase.Resources(t, d)
}
// Set features
{
*features.EncryptionRotation().EnabledPointer() = testCase.Features.EncryptionRotation
require.Equal(t, testCase.Features.EncryptionRotation, *features.EncryptionRotation().EnabledPointer())
*features.JWTRotation().EnabledPointer() = testCase.Features.JWTRotation
*features.TLSSNI().EnabledPointer() = testCase.Features.TLSSNI
*features.TLSRotation().EnabledPointer() = testCase.Features.TLSRotation
}
// Act
cache, err := inspector.NewInspector(d.GetKubeCli(), d.GetNamespace())
require.NoError(t, err)

View file

@ -68,6 +68,10 @@ const (
testYes = "yes"
)
type testCaseFeatures struct {
TLSSNI, TLSRotation, JWTRotation, EncryptionRotation bool
}
type testCaseStruct struct {
Name string
ArangoDeployment *api.ArangoDeployment
@ -78,6 +82,7 @@ type testCaseStruct struct {
ExpectedError error
ExpectedEvent string
ExpectedPod core.Pod
Features testCaseFeatures
}
func createTestTLSVolume(serverGroupString, ID string) core.Volume {

View file

@ -0,0 +1,37 @@
//
// 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
//
package features
func init() {
registerFeature(encryptionRotation)
}
var encryptionRotation = &feature{
name: "encryption-rotation",
description: "Encryption Key rotation in runtime",
version: "3.7.0",
enterpriseRequired: true,
enabledByDefault: false,
}
func EncryptionRotation() Feature {
return encryptionRotation
}

View file

@ -0,0 +1,74 @@
//
// 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
//
package features
import "github.com/arangodb/go-driver"
var _ Feature = &feature{}
type Feature interface {
Name() string
Description() string
Version() driver.Version
EnterpriseRequired() bool
EnabledByDefault() bool
Enabled() bool
EnabledPointer() *bool
Supported(v driver.Version, enterprise bool) bool
}
type feature struct {
name, description string
version driver.Version
enterpriseRequired, enabledByDefault, enabled bool
}
func (f feature) Supported(v driver.Version, enterprise bool) bool {
return Supported(&f, v, enterprise)
}
func (f feature) Enabled() bool {
return f.enabled
}
func (f *feature) EnabledPointer() *bool {
return &f.enabled
}
func (f feature) Version() driver.Version {
return f.version
}
func (f feature) EnterpriseRequired() bool {
return f.enterpriseRequired
}
func (f feature) EnabledByDefault() bool {
return f.enabledByDefault
}
func (f feature) Name() string {
return f.name
}
func (f feature) Description() string {
return f.description
}

View file

@ -0,0 +1,37 @@
//
// 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
//
package features
func init() {
registerFeature(jwtRotation)
}
var jwtRotation = &feature{
name: "jwt-rotation",
description: "JWT Token rotation in runtime",
version: "3.7.0",
enterpriseRequired: true,
enabledByDefault: false,
}
func JWTRotation() Feature {
return jwtRotation
}

View file

@ -0,0 +1,113 @@
//
// 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
//
package features
import (
"fmt"
"sync"
"github.com/arangodb/go-driver"
"github.com/spf13/cobra"
)
var features = map[string]Feature{}
var featuresLock sync.Mutex
var enableAll = false
func registerFeature(f Feature) {
featuresLock.Lock()
defer featuresLock.Unlock()
if f == nil {
panic("Feature cannot be nil")
}
if _, ok := features[f.Name()]; ok {
panic("Feature already registered")
}
features[f.Name()] = f
}
var internalCMD = &cobra.Command{
Use: "features",
Short: "Describe all operator features",
Run: cmdRun,
}
func Init(cmd *cobra.Command) {
featuresLock.Lock()
defer featuresLock.Unlock()
cmd.AddCommand(internalCMD)
f := cmd.Flags()
f.BoolVar(&enableAll, "deployment.feature.all", false, "Enable ALL Features")
for _, feature := range features {
z := ""
if v := feature.Version(); v != "" || feature.EnterpriseRequired() {
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)
} else if feature.EnterpriseRequired() {
z = fmt.Sprintf("%s - Required Enterprise Edition", feature.Description())
} else {
z = feature.Description()
}
}
f.BoolVar(feature.EnabledPointer(), fmt.Sprintf("deployment.feature.%s", feature.Name()), feature.EnabledByDefault(), z)
}
}
func cmdRun(cmd *cobra.Command, args []string) {
featuresLock.Lock()
defer featuresLock.Unlock()
for _, feature := range features {
println(fmt.Sprintf("Feature: %s", feature.Name()))
println(fmt.Sprintf("Description: %s", feature.Description()))
if feature.EnabledByDefault() {
println("Enabled: true")
} else {
println("Enabled: false")
}
if v := feature.Version(); v != "" {
println(fmt.Sprintf("ArangoDB Version Required: >= %s", v))
}
if feature.EnterpriseRequired() {
println(fmt.Sprintf("ArangoDB Edition Required: Enterprise"))
} else {
println(fmt.Sprintf("ArangoDB Edition Required: Community, Enterprise"))
}
println()
}
}
func Supported(f Feature, v driver.Version, enterprise bool) bool {
return f.Enabled() && ((f.EnterpriseRequired() && enterprise) || !f.EnterpriseRequired()) && v.CompareTo(f.Version()) >= 0
}

View file

@ -0,0 +1,50 @@
//
// 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
//
package features
func init() {
registerFeature(tlsRotation)
registerFeature(tlsSNI)
}
var tlsRotation Feature = &feature{
name: "tls-rotation",
description: "TLS Keyfile rotation in runtime",
version: "3.7.0",
enterpriseRequired: true,
enabledByDefault: false,
}
func TLSRotation() Feature {
return tlsRotation
}
var tlsSNI Feature = &feature{
name: "tls-sni",
description: "TLS SNI Support",
version: "3.7.0",
enterpriseRequired: true,
enabledByDefault: true,
}
func TLSSNI() Feature {
return tlsSNI
}

View file

@ -27,6 +27,8 @@ import (
"fmt"
"path/filepath"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
@ -106,7 +108,7 @@ func IsEncryptionEnabled(i Input) bool {
}
func MultiFileMode(i Input) bool {
return i.Enterprise && i.Version.CompareTo("3.7.0") >= 0
return features.EncryptionRotation().Supported(i.Version, i.Enterprise)
}
func Encryption() Builder {

View file

@ -26,6 +26,8 @@ import (
"fmt"
"path/filepath"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/go-driver"
"github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector"
"github.com/arangodb/kube-arangodb/pkg/util/constants"
@ -56,7 +58,7 @@ func JWTSecretFolder(name string) string {
}
func VersionHasJWTSecretKeyfolder(v driver.Version, enterprise bool) bool {
return enterprise && v.CompareTo("3.7.0") > 0
return features.JWTRotation().Supported(v, enterprise)
}
func JWT() Builder {

View file

@ -26,6 +26,8 @@ import (
"crypto/sha256"
"fmt"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
@ -66,7 +68,7 @@ func (s sni) isSupported(i Input) bool {
return false
}
if i.Version.CompareTo("3.7.0") < 0 || !i.Enterprise {
if !features.TLSSNI().Supported(i.Version, i.Enterprise) {
// We need 3.7.0+ and Enterprise to support this
return false
}

View file

@ -25,6 +25,8 @@ package pod
import (
"path/filepath"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector"
@ -35,8 +37,7 @@ import (
)
func IsRuntimeTLSKeyfileUpdateSupported(i Input) bool {
return IsTLSEnabled(i) && i.Enterprise &&
i.Version.CompareTo("3.7.0") >= 0 &&
return IsTLSEnabled(i) && features.TLSRotation().Supported(i.Version, i.Enterprise) &&
i.Deployment.TLS.Mode.Get() == api.TLSRotateModeInPlace
}

View file

@ -26,6 +26,8 @@ import (
"context"
"encoding/base64"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/deployment/patch"
"github.com/arangodb/kube-arangodb/pkg/deployment/pod"
"github.com/pkg/errors"
@ -43,11 +45,8 @@ func ensureEncryptionSupport(actionCtx ActionContext) error {
if image, ok := actionCtx.GetCurrentImageInfo(); !ok {
return errors.Errorf("Missing image info")
} else {
if !image.Enterprise {
return errors.Errorf("Supported only in enterprise")
}
if image.ArangoDBVersion.CompareTo("3.7.0") < 0 {
return errors.Errorf("Supported only in 3.7.0+")
if !features.EncryptionRotation().Supported(image.ArangoDBVersion, image.Enterprise) {
return errors.Errorf("Supported only in Enterprise Edition 3.7.0+")
}
}
return nil

View file

@ -27,6 +27,8 @@ import (
"fmt"
"sort"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/util/constants"
"github.com/pkg/errors"
@ -56,10 +58,7 @@ func ensureJWTFolderSupport(spec api.DeploymentSpec, status api.DeploymentStatus
if image := status.CurrentImage; image == nil {
return false, errors.Errorf("Missing image info")
} else {
if !image.Enterprise {
return false, nil
}
if image.ArangoDBVersion.CompareTo("3.7.0") < 0 {
if !features.JWTRotation().Supported(image.ArangoDBVersion, image.Enterprise) {
return false, nil
}
}

View file

@ -25,6 +25,8 @@ package reconcile
import (
"context"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
core "k8s.io/api/core/v1"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
@ -45,7 +47,7 @@ func skipEncryptionPlan(spec api.DeploymentSpec, status api.DeploymentStatus) bo
return true
}
if i := status.CurrentImage; i == nil || !i.Enterprise || i.ArangoDBVersion.CompareTo("3.7.0") < 0 {
if i := status.CurrentImage; i == nil || !features.EncryptionRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
return true
}
@ -275,7 +277,7 @@ func isEncryptionKeyUpToDate(ctx context.Context,
return false, true
}
if m.ArangoVersion.CompareTo("3.7.0") < 0 {
if i, ok := status.Images.GetByImageID(m.ImageID); !ok || !features.EncryptionRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
return false, false
}

View file

@ -27,6 +27,8 @@ import (
"fmt"
"sort"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/deployment/client"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
@ -70,7 +72,7 @@ func createJWTKeyUpdate(ctx context.Context,
jwtSha := util.SHA256(jwt)
if _, ok := folder.Data[jwtSha]; !ok {
return addJWTPropagatedPlanAction(status, api.NewAction(api.ActionTypeJWTAdd, api.ServerGroupUnknown, "", "Add JWT key").AddParam(checksum, jwtSha))
return addJWTPropagatedPlanAction(status, api.NewAction(api.ActionTypeJWTAdd, api.ServerGroupUnknown, "", "Add JWTRotation key").AddParam(checksum, jwtSha))
}
activeKey, ok := folder.Data[pod.ActiveJWTKey]
@ -246,7 +248,7 @@ func isJWTTokenUpToDate(ctx context.Context,
return false, true
}
if m.ArangoVersion.CompareTo("3.7.0") < 0 {
if i, ok := status.Images.GetByImageID(m.ImageID); !ok || !features.JWTRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
return false, false
}
@ -259,7 +261,7 @@ func isJWTTokenUpToDate(ctx context.Context,
}
if updateRequired, err := isMemberJWTTokenInvalid(ctx, client.NewClient(c.Connection()), folder.Data, false); err != nil {
mlog.Warn().Err(err).Msg("JET UpToDate Check failed")
mlog.Warn().Err(err).Msg("JWT UpToDate Check failed")
return false, true
} else if updateRequired {
return true, false

View file

@ -25,6 +25,8 @@ package reconcile
import (
"context"
"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"
@ -65,9 +67,11 @@ func createRestorePlan(ctx context.Context,
return p
}
if !status.Hashes.Encryption.Propagated {
log.Warn().Msg("Backup not able to be restored in non propagated state")
return nil
if i := status.CurrentImage; i != nil && features.EncryptionRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
if !status.Hashes.Encryption.Propagated {
log.Warn().Msg("Backup not able to be restored in non propagated state")
return nil
}
}
}
@ -85,8 +89,8 @@ func createRestorePlanEncryption(ctx context.Context, log zerolog.Logger, spec a
return true, nil
}
if i := status.CurrentImage; i == nil || !i.Enterprise || i.ArangoDBVersion.CompareTo("3.7.0") < 0 {
return true, nil
if i := status.CurrentImage; i == nil || !features.EncryptionRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
return false, nil
}
if !status.Hashes.Encryption.Propagated {

View file

@ -32,6 +32,8 @@ import (
"reflect"
"time"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/deployment/client"
"github.com/arangodb/kube-arangodb/pkg/util/constants"
@ -303,7 +305,7 @@ func createKeyfileRenewalPlanDefault(ctx context.Context,
return nil
}
if renew, recreate := keyfileRenewalRequired(ctx, log, apiObject, spec, status, cachedStatus, context, group, member, api.TLSRotateModeRecreate); renew {
log.Info().Msg("Renewal of keyfile required")
log.Info().Msg("Renewal of keyfile required - Recreate")
if recreate {
plan = append(plan, api.NewAction(api.ActionTypeCleanTLSKeyfileCertificate, group, member.ID, "Remove server keyfile and enforce renewal"))
}
@ -334,7 +336,7 @@ func createKeyfileRenewalPlanInPlace(ctx context.Context,
for _, member := range members {
if renew, recreate := keyfileRenewalRequired(ctx, log, apiObject, spec, status, cachedStatus, context, group, member, api.TLSRotateModeInPlace); renew {
log.Info().Msg("Renewal of keyfile required")
log.Info().Msg("Renewal of keyfile required - InPlace")
if recreate {
plan = append(plan, api.NewAction(api.ActionTypeCleanTLSKeyfileCertificate, group, member.ID, "Remove server keyfile and enforce renewal"))
}
@ -382,10 +384,10 @@ func createKeyfileRenewalPlanMode(
return nil
}
if i := status.CurrentImage; i == nil {
if i, ok := status.Images.GetByImageID(member.ImageID); !ok {
mode = api.TLSRotateModeRecreate
} else {
if !i.Enterprise || i.ArangoDBVersion.CompareTo("3.7.0") < 0 || i.ImageID != member.ImageID {
if !features.TLSRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
mode = api.TLSRotateModeRecreate
}
}

View file

@ -25,6 +25,8 @@ package reconcile
import (
"context"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
"github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector"
@ -75,7 +77,7 @@ func createRotateTLSServerSNIPlan(ctx context.Context,
continue
}
if m.ArangoVersion.CompareTo("3.7.0") < 0 {
if i, ok := status.Images.GetByImageID(m.ImageID); !ok || !features.EncryptionRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
continue
}

View file

@ -30,6 +30,8 @@ import (
"sort"
"strings"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/deployment/pod"
"github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector"
@ -138,7 +140,7 @@ func (r *Resources) ValidateSecretHashes(cachedStatus inspector.Inspector) error
}
if spec.IsAuthenticated() {
if image == nil || image.ArangoDBVersion.CompareTo("3.7.0") < 0 {
if image == nil || !features.JWTRotation().Supported(image.ArangoDBVersion, image.Enterprise) {
secretName := spec.Authentication.GetJWTSecretName()
getExpectedHash := func() string { return getHashes().AuthJWT }
setExpectedHash := func(h string) error {
@ -165,7 +167,7 @@ func (r *Resources) ValidateSecretHashes(cachedStatus inspector.Inspector) error
}
}
if spec.RocksDB.IsEncrypted() {
if image == nil || image.ArangoDBVersion.CompareTo("3.7.0") < 0 {
if image == nil || !features.EncryptionRotation().Supported(image.ArangoDBVersion, image.Enterprise) {
secretName := spec.RocksDB.Encryption.GetKeySecretName()
getExpectedHash := func() string { return getHashes().RocksDBEncryptionKey }
setExpectedHash := func(h string) error {

View file

@ -29,6 +29,7 @@ import (
"fmt"
"time"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/deployment/patch"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json"
@ -152,7 +153,7 @@ func (r *Resources) EnsureSecrets(log zerolog.Logger, cachedStatus inspector.Ins
}
}
if spec.RocksDB.IsEncrypted() {
if i := status.CurrentImage; i != nil && i.Enterprise && i.ArangoDBVersion.CompareTo("3.7.0") >= 0 {
if i := status.CurrentImage; i != nil && features.EncryptionRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
if err := r.refreshCache(cachedStatus, r.ensureEncryptionKeyfolderSecret(cachedStatus, secrets, spec.RocksDB.Encryption.GetKeySecretName(), pod.GetEncryptionFolderSecretName(deploymentName))); err != nil {
return maskAny(err)
}

View file

@ -92,6 +92,7 @@ type Config struct {
EnableStorage bool
EnableBackup bool
AllowChaos bool
SingleMode bool
}
type Dependencies struct {
@ -123,16 +124,32 @@ func NewOperator(config Config, deps Dependencies) (*Operator, error) {
// Run the operator
func (o *Operator) Run() {
if o.Config.EnableDeployment {
go o.runLeaderElection("arango-deployment-operator", constants.LabelRole, o.onStartDeployment, o.Dependencies.DeploymentProbe)
if !o.Config.SingleMode {
go o.runLeaderElection("arango-deployment-operator", constants.LabelRole, o.onStartDeployment, o.Dependencies.DeploymentProbe)
} else {
go o.runWithoutLeaderElection("arango-deployment-operator", constants.LabelRole, o.onStartDeployment, o.Dependencies.DeploymentProbe)
}
}
if o.Config.EnableDeploymentReplication {
go o.runLeaderElection("arango-deployment-replication-operator", constants.LabelRole, o.onStartDeploymentReplication, o.Dependencies.DeploymentReplicationProbe)
if !o.Config.SingleMode {
go o.runLeaderElection("arango-deployment-replication-operator", constants.LabelRole, o.onStartDeploymentReplication, o.Dependencies.DeploymentReplicationProbe)
} else {
go o.runWithoutLeaderElection("arango-deployment-replication-operator", constants.LabelRole, o.onStartDeploymentReplication, o.Dependencies.DeploymentReplicationProbe)
}
}
if o.Config.EnableStorage {
go o.runLeaderElection("arango-storage-operator", constants.LabelRole, o.onStartStorage, o.Dependencies.StorageProbe)
if !o.Config.SingleMode {
go o.runLeaderElection("arango-storage-operator", constants.LabelRole, o.onStartStorage, o.Dependencies.StorageProbe)
} else {
go o.runWithoutLeaderElection("arango-storage-operator", constants.LabelRole, o.onStartStorage, o.Dependencies.StorageProbe)
}
}
if o.Config.EnableBackup {
go o.runLeaderElection("arango-backup-operator", constants.BackupLabelRole, o.onStartBackup, o.Dependencies.BackupProbe)
if !o.Config.SingleMode {
go o.runLeaderElection("arango-backup-operator", constants.BackupLabelRole, o.onStartBackup, o.Dependencies.BackupProbe)
} else {
go o.runWithoutLeaderElection("arango-backup-operator", constants.BackupLabelRole, o.onStartBackup, o.Dependencies.BackupProbe)
}
}
// Wait until process terminates
<-context.TODO().Done()

View file

@ -99,6 +99,25 @@ func (o *Operator) runLeaderElection(lockName, label string, onStart func(stop <
})
}
func (o *Operator) runWithoutLeaderElection(lockName, label string, onStart func(stop <-chan struct{}), readyProbe *probe.ReadyProbe) {
log := o.log.With().Str("lock-name", lockName).Logger()
eventTarget := o.getLeaderElectionEventTarget(log)
recordEvent := func(reason, message string) {
if eventTarget != nil {
o.Dependencies.EventRecorder.Event(eventTarget, v1.EventTypeNormal, reason, message)
}
}
ctx := context.Background()
recordEvent("Leader Election Skipped", fmt.Sprintf("Pod %s is running as leader", o.Config.PodName))
readyProbe.SetReady()
if err := o.setRoleLabel(log, label, constants.LabelRoleLeader); err != nil {
log.Error().Msg("Cannot set leader role on Pod. Terminating process")
os.Exit(2)
}
onStart(ctx.Done())
}
// getLeaderElectionEventTarget returns the object that leader election related
// events will be added to.
func (o *Operator) getLeaderElectionEventTarget(log zerolog.Logger) runtime.Object {