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:
parent
8fd654ca3d
commit
f007ab0213
29 changed files with 521 additions and 38 deletions
5
Makefile
5
Makefile
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -5,6 +5,8 @@ operator:
|
|||
imagePullPolicy: IfNotPresent
|
||||
imagePullSecrets: []
|
||||
|
||||
args: []
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
|
||||
|
|
7
main.go
7
main.go
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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, "")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
37
pkg/deployment/features/encryption.go
Normal file
37
pkg/deployment/features/encryption.go
Normal 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
|
||||
}
|
74
pkg/deployment/features/features.go
Normal file
74
pkg/deployment/features/features.go
Normal 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
|
||||
}
|
37
pkg/deployment/features/jwt.go
Normal file
37
pkg/deployment/features/jwt.go
Normal 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
|
||||
}
|
113
pkg/deployment/features/local.go
Normal file
113
pkg/deployment/features/local.go
Normal 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
|
||||
}
|
50
pkg/deployment/features/tls.go
Normal file
50
pkg/deployment/features/tls.go
Normal 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
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue