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

[Feature] Split & Unify Lifecycle management functionality (#821)

This commit is contained in:
Adam Janikowski 2021-10-25 01:09:40 +02:00 committed by GitHub
parent 57eb6e95de
commit a2a55530e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 497 additions and 349 deletions

View file

@ -1,6 +1,7 @@
# Change Log
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
- Split & Unify Lifecycle management functionality
## [1.2.4](https://github.com/arangodb/kube-arangodb/tree/1.2.4) (2021-10-22)
- Replace `beta.kubernetes.io/arch` Pod label with `kubernetes.io/arch` using Silent Rotation

View file

@ -47,7 +47,11 @@ var (
cmdLifecyclePreStop = &cobra.Command{
Use: "preStop",
Run: cmdLifecyclePreStopRun,
Hidden: true,
}
cmdLifecyclePreStopFinalizers = &cobra.Command{
Use: "finalizers",
Run: cmdLifecyclePreStopRunFinalizer,
Hidden: true,
}
cmdLifecycleCopy = &cobra.Command{
@ -63,6 +67,9 @@ var (
func init() {
cmdMain.AddCommand(cmdLifecycle)
cmdLifecyclePreStop.AddCommand(cmdLifecyclePreStopFinalizers)
cmdLifecycle.AddCommand(cmdLifecyclePreStop)
cmdLifecycle.AddCommand(cmdLifecycleCopy)
cmdLifecycle.AddCommand(cmdLifecycleProbe)
@ -71,7 +78,7 @@ func init() {
}
// Wait until all finalizers of the current pod have been removed.
func cmdLifecyclePreStopRun(cmd *cobra.Command, args []string) {
func cmdLifecyclePreStopRunFinalizer(cmd *cobra.Command, args []string) {
cliLog.Info().Msgf("Starting arangodb-operator (%s), lifecycle preStop, version %s build %s", version.GetVersionV1().Edition.Title(), version.GetVersionV1().Version, version.GetVersionV1().Build)

View file

@ -362,13 +362,12 @@ func newOperatorConfigAndDeps(id, namespace, name string) (operator.Config, oper
Namespace: namespace,
PodName: name,
ServiceAccount: serviceAccount,
LifecycleImage: image,
OperatorImage: image,
EnableDeployment: operatorOptions.enableDeployment,
EnableDeploymentReplication: operatorOptions.enableDeploymentReplication,
EnableStorage: operatorOptions.enableStorage,
EnableBackup: operatorOptions.enableBackup,
AllowChaos: chaosOptions.allowed,
MetricsExporterImage: operatorOptions.metricsExporterImage,
ArangoImage: operatorOptions.arangoImage,
SingleMode: operatorOptions.singleMode,
Scope: scope,

View file

@ -41,6 +41,7 @@ func (m MetricsMode) New() *MetricsMode {
return &m
}
// deprecated
func (m MetricsMode) GetMetricsEndpoint() string {
switch m {
case MetricsModeInternal:
@ -51,9 +52,12 @@ func (m MetricsMode) GetMetricsEndpoint() string {
}
const (
// deprecated
// MetricsModeExporter exporter mode for old exporter type
MetricsModeExporter MetricsMode = "exporter"
MetricsModeSidecar MetricsMode = "sidecar"
// deprecated
MetricsModeSidecar MetricsMode = "sidecar"
// deprecated
MetricsModeInternal MetricsMode = "internal"
)
@ -67,12 +71,14 @@ func (m *MetricsMode) Get() MetricsMode {
// MetricsSpec contains spec for arangodb exporter
type MetricsSpec struct {
Enabled *bool `json:"enabled,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
// deprecated
Image *string `json:"image,omitempty"`
Authentication MetricsAuthenticationSpec `json:"authentication,omitempty"`
Resources v1.ResourceRequirements `json:"resources,omitempty"`
Mode *MetricsMode `json:"mode,omitempty"`
TLS *bool `json:"tls,omitempty"`
// deprecated
Mode *MetricsMode `json:"mode,omitempty"`
TLS *bool `json:"tls,omitempty"`
ServiceMonitor *MetricsServiceMonitorSpec `json:"serviceMonitor,omitempty"`
@ -100,11 +106,13 @@ func (s *MetricsSpec) IsEnabled() bool {
return util.BoolOrDefault(s.Enabled, false)
}
// deprecated
// HasImage returns whether a image was specified or not
func (s *MetricsSpec) HasImage() bool {
return s.Image != nil
}
// deprecated
// GetImage returns the Image or empty string
func (s *MetricsSpec) GetImage() string {
return util.StringOrDefault(s.Image)

View file

@ -0,0 +1,35 @@
//
// DISCLAIMER
//
// Copyright 2016-2021 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 v1
const (
ServerGroupReservedContainerNameServer = "server"
ServerGroupReservedContainerNameExporter = "exporter"
)
func IsReservedServerGroupContainerName(name string) bool {
switch name {
case ServerGroupReservedContainerNameServer, ServerGroupReservedContainerNameExporter:
return true
default:
return false
}
}

View file

@ -115,14 +115,8 @@ func (d *Deployment) GetScope() scope.Scope {
return d.config.Scope
}
// GetLifecycleImage returns the image name containing the lifecycle helper (== name of operator image)
func (d *Deployment) GetLifecycleImage() string {
return d.config.LifecycleImage
}
// GetOperatorUUIDImage returns the image name containing the uuid helper (== name of operator image)
func (d *Deployment) GetOperatorUUIDImage() string {
return d.config.OperatorUUIDInitImage
func (d *Deployment) GetOperatorImage() string {
return d.config.OperatorImage
}
// GetNamespace returns the kubernetes namespace that contains
@ -591,10 +585,6 @@ func (d *Deployment) SelectImageForMember(spec api.DeploymentSpec, status api.De
return d.resources.SelectImageForMember(spec, status, member)
}
func (d *Deployment) GetMetricsExporterImage() string {
return d.config.MetricsExporterImage
}
func (d *Deployment) GetArangoImage() string {
return d.config.ArangoImage
}

View file

@ -69,13 +69,11 @@ import (
// Config holds configuration settings for a Deployment
type Config struct {
ServiceAccount string
AllowChaos bool
LifecycleImage string
OperatorUUIDInitImage string
MetricsExporterImage string
ArangoImage string
Scope scope.Scope
ServiceAccount string
AllowChaos bool
OperatorImage string
ArangoImage string
Scope scope.Scope
}
// Dependencies holds dependent services for a Deployment

View file

@ -33,7 +33,6 @@ import (
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/constants"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
)
@ -276,7 +275,7 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) {
},
},
config: Config{
OperatorUUIDInitImage: testImageOperatorUUIDInit,
OperatorImage: testImageOperator,
},
Helper: func(t *testing.T, deployment *Deployment, testCase *testCaseStruct) {
deployment.status.last = api.DeploymentStatus{
@ -505,7 +504,7 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) {
},
},
config: Config{
OperatorUUIDInitImage: testImageOperatorUUIDInit,
OperatorImage: testImageOperator,
},
Helper: func(t *testing.T, deployment *Deployment, testCase *testCaseStruct) {
deployment.status.last = api.DeploymentStatus{
@ -1090,7 +1089,7 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) {
testCase.ExpectedPod.ObjectMeta.Labels[k8sutil.LabelKeyArangoExporter] = testYes
},
config: Config{
LifecycleImage: testImageLifecycle,
OperatorImage: testImageOperator,
},
ExpectedEvent: "member dbserver is created",
ExpectedPod: core.Pod{
@ -1108,13 +1107,7 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) {
Name: k8sutil.ServerContainerName,
Image: testImage,
Command: createTestCommandForDBServer(firstDBServerStatus.ID, false, false, false),
Env: []core.EnvVar{
k8sutil.CreateEnvFieldPath(constants.EnvOperatorPodName, "metadata.name"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorPodNamespace, "metadata.namespace"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeName, "spec.nodeName"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeNameArango, "spec.nodeName"),
},
Ports: createTestPorts(),
Ports: createTestPorts(),
VolumeMounts: []core.VolumeMount{
k8sutil.ArangodVolumeMount(),
k8sutil.LifecycleVolumeMount(),
@ -1160,8 +1153,7 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) {
testCase.ExpectedPod.ObjectMeta.Labels[k8sutil.LabelKeyArangoExporter] = testYes
},
config: Config{
LifecycleImage: testImageLifecycle,
OperatorUUIDInitImage: testImageOperatorUUIDInit,
OperatorImage: testImageOperator,
},
ExpectedEvent: "member dbserver is created",
ExpectedPod: core.Pod{
@ -1169,7 +1161,6 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) {
Volumes: []core.Volume{
k8sutil.CreateVolumeEmptyDir(k8sutil.ArangodVolumeName),
k8sutil.CreateVolumeWithSecret(k8sutil.ExporterJWTVolumeName, testExporterToken),
k8sutil.LifecycleVolume(),
},
InitContainers: []core.Container{
createTestLifecycleContainer(emptyResources),
@ -1180,13 +1171,7 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) {
Name: k8sutil.ServerContainerName,
Image: testImage,
Command: createTestCommandForDBServer(firstDBServerStatus.ID, false, false, false),
Env: []core.EnvVar{
k8sutil.CreateEnvFieldPath(constants.EnvOperatorPodName, "metadata.name"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorPodNamespace, "metadata.namespace"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeName, "spec.nodeName"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeNameArango, "spec.nodeName"),
},
Ports: createTestPorts(),
Ports: createTestPorts(),
VolumeMounts: []core.VolumeMount{
k8sutil.ArangodVolumeMount(),
k8sutil.LifecycleVolumeMount(),
@ -1208,102 +1193,6 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) {
},
},
},
{
Name: "DBserver Pod with metrics exporter, lifecycle, tls, authentication, license, rocksDB encryption, secured liveness",
ArangoDeployment: &api.ArangoDeployment{
Spec: api.DeploymentSpec{
Image: util.NewString(testImage),
Authentication: authenticationSpec,
TLS: tlsSpec,
Metrics: metricsSpec,
RocksDB: rocksDBSpec,
Environment: api.NewEnvironment(api.EnvironmentProduction),
License: api.LicenseSpec{
SecretName: util.NewString(testLicense),
},
},
},
Helper: func(t *testing.T, deployment *Deployment, testCase *testCaseStruct) {
deployment.status.last = api.DeploymentStatus{
Members: api.DeploymentStatusMembers{
DBServers: api.MemberStatusList{
firstDBServerStatus,
},
},
Images: createTestImages(false),
}
testCase.createTestPodData(deployment, api.ServerGroupDBServers, firstDBServerStatus)
testCase.ExpectedPod.ObjectMeta.Labels[k8sutil.LabelKeyArangoExporter] = testYes
key := make([]byte, 32)
k8sutil.CreateEncryptionKeySecret(deployment.SecretsModInterface(), testRocksDBEncryptionKey, key)
authorization, err := createTestToken(deployment, testCase, []string{"/_api/version"})
require.NoError(t, err)
testCase.ExpectedPod.Spec.Containers[0].LivenessProbe = createTestLivenessProbe(httpProbe, true,
authorization, k8sutil.ArangoPort)
},
config: Config{
LifecycleImage: testImageLifecycle,
},
ExpectedEvent: "member dbserver is created",
ExpectedPod: core.Pod{
Spec: core.PodSpec{
Volumes: []core.Volume{
k8sutil.CreateVolumeEmptyDir(k8sutil.ArangodVolumeName),
createTestTLSVolume(api.ServerGroupDBServersString, firstDBServerStatus.ID),
k8sutil.CreateVolumeWithSecret(k8sutil.RocksdbEncryptionVolumeName, testRocksDBEncryptionKey),
k8sutil.CreateVolumeWithSecret(k8sutil.ExporterJWTVolumeName, testExporterToken),
k8sutil.CreateVolumeWithSecret(k8sutil.ClusterJWTSecretVolumeName, testJWTSecretName),
k8sutil.LifecycleVolume(),
},
InitContainers: []core.Container{
createTestLifecycleContainer(emptyResources),
},
Containers: []core.Container{
{
Name: k8sutil.ServerContainerName,
Image: testImage,
Command: createTestCommandForDBServer(firstDBServerStatus.ID, true, true, true),
Env: []core.EnvVar{
k8sutil.CreateEnvSecretKeySelector(constants.EnvArangoLicenseKey,
testLicense, constants.SecretKeyToken),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorPodName, "metadata.name"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorPodNamespace, "metadata.namespace"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeName, "spec.nodeName"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeNameArango, "spec.nodeName"),
},
Ports: createTestPorts(),
Lifecycle: createTestLifecycle(),
LivenessProbe: createTestLivenessProbe(httpProbe, false, "", k8sutil.ArangoPort),
ImagePullPolicy: core.PullIfNotPresent,
SecurityContext: securityContext.NewSecurityContext(),
VolumeMounts: []core.VolumeMount{
k8sutil.ArangodVolumeMount(),
k8sutil.LifecycleVolumeMount(),
k8sutil.TlsKeyfileVolumeMount(),
k8sutil.RocksdbEncryptionVolumeMount(),
k8sutil.ClusterJWTVolumeMount(),
},
Resources: emptyResources,
},
func() core.Container {
c := testArangodbInternalExporterContainer(true, emptyResources)
c.VolumeMounts = append(c.VolumeMounts, k8sutil.TlsKeyfileVolumeMount())
return c
}(),
},
RestartPolicy: core.RestartPolicyNever,
TerminationGracePeriodSeconds: &defaultDBServerTerminationTimeout,
Hostname: testDeploymentName + "-" + api.ServerGroupDBServersString + "-" + firstDBServerStatus.ID,
Subdomain: testDeploymentName + "-int",
Affinity: k8sutil.CreateAffinity(testDeploymentName, api.ServerGroupDBServersString,
true, ""),
},
},
},
{
Name: "Coordinator Pod with TLS and authentication and readiness and liveness",
ArangoDeployment: &api.ArangoDeployment{

View file

@ -32,7 +32,6 @@ import (
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/constants"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
)
@ -136,7 +135,7 @@ func TestEnsurePod_ArangoDB_Encryption(t *testing.T) {
authorization, k8sutil.ArangoPort)
},
config: Config{
LifecycleImage: testImageLifecycle,
OperatorImage: testImageOperator,
},
ExpectedEvent: "member dbserver is created",
ExpectedPod: core.Pod{
@ -154,17 +153,9 @@ func TestEnsurePod_ArangoDB_Encryption(t *testing.T) {
},
Containers: []core.Container{
{
Name: k8sutil.ServerContainerName,
Image: testImage,
Command: createTestCommandForDBServer(firstDBServerStatus.ID, true, true, true),
Env: []core.EnvVar{
k8sutil.CreateEnvSecretKeySelector(constants.EnvArangoLicenseKey,
testLicense, constants.SecretKeyToken),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorPodName, "metadata.name"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorPodNamespace, "metadata.namespace"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeName, "spec.nodeName"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeNameArango, "spec.nodeName"),
},
Name: k8sutil.ServerContainerName,
Image: testImage,
Command: createTestCommandForDBServer(firstDBServerStatus.ID, true, true, true),
Ports: createTestPorts(),
Lifecycle: createTestLifecycle(),
LivenessProbe: createTestLivenessProbe(httpProbe, false, "", k8sutil.ArangoPort),

View file

@ -197,6 +197,7 @@ func TestEnsurePod_Sync_Master(t *testing.T) {
"secrets \"" + testDeploymentName + "-sync-client-auth-ca\" not found"),
},
{
DropInit: true,
Name: "Sync Master Pod with authentication, monitoring, tls, service account, node selector, " +
"liveness probe, priority class name, resource requirements",
ArangoDeployment: &api.ArangoDeployment{
@ -237,11 +238,15 @@ func TestEnsurePod_Sync_Master(t *testing.T) {
ExpectedPod: core.Pod{
Spec: core.PodSpec{
Volumes: []core.Volume{
k8sutil.LifecycleVolume(),
createTestTLSVolume(api.ServerGroupSyncMastersString, firstSyncMaster.ID),
k8sutil.CreateVolumeWithSecret(k8sutil.ClientAuthCAVolumeName, "test-sync-client-auth-ca"),
k8sutil.CreateVolumeWithSecret(k8sutil.MasterJWTSecretVolumeName, "test-sync-jwt"),
k8sutil.CreateVolumeWithSecret(k8sutil.ClusterJWTSecretVolumeName, testJWTSecretName),
},
InitContainers: []core.Container{
createTestLifecycleContainer(emptyResources),
},
Containers: []core.Container{
{
Name: k8sutil.ServerContainerName,
@ -251,11 +256,17 @@ func TestEnsurePod_Sync_Master(t *testing.T) {
Env: []core.EnvVar{
k8sutil.CreateEnvSecretKeySelector(constants.EnvArangoSyncMonitoringToken,
testDeploymentName+"-sync-mt", constants.SecretKeyToken),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorPodName, "metadata.name"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorPodNamespace, "metadata.namespace"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeName, "spec.nodeName"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeNameArango, "spec.nodeName"),
},
ImagePullPolicy: core.PullIfNotPresent,
Lifecycle: createTestLifecycle(),
Resources: resourcesUnfiltered,
SecurityContext: securityContext.NewSecurityContext(),
VolumeMounts: []core.VolumeMount{
k8sutil.LifecycleVolumeMount(),
k8sutil.TlsKeyfileVolumeMount(),
k8sutil.ClientAuthCACertificateVolumeMount(),
k8sutil.MasterJWTVolumeMount(),
@ -277,9 +288,10 @@ func TestEnsurePod_Sync_Master(t *testing.T) {
},
},
{
Name: "Sync Master Pod with lifecycle, license, monitoring without authentication and alpine",
DropInit: true,
Name: "Sync Master Pod with lifecycle, license, monitoring without authentication and alpine",
config: Config{
LifecycleImage: testImageLifecycle,
OperatorImage: testImageOperator,
},
ArangoDeployment: &api.ArangoDeployment{
Spec: api.DeploymentSpec{
@ -372,10 +384,11 @@ func TestEnsurePod_Sync_Master(t *testing.T) {
func TestEnsurePod_Sync_Worker(t *testing.T) {
testCases := []testCaseStruct{
{
DropInit: true,
Name: "Sync Worker Pod with monitoring, service account, node selector, lifecycle, license " +
"liveness probe, priority class name, resource requirements without alpine",
config: Config{
LifecycleImage: testImageLifecycle,
OperatorImage: testImageOperator,
},
ArangoDeployment: &api.ArangoDeployment{
Spec: api.DeploymentSpec{
@ -440,8 +453,8 @@ func TestEnsurePod_Sync_Worker(t *testing.T) {
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeName, "spec.nodeName"),
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeNameArango, "spec.nodeName"),
},
Lifecycle: createTestLifecycle(),
ImagePullPolicy: core.PullIfNotPresent,
Lifecycle: createTestLifecycle(),
Resources: k8sutil.ExtractPodResourceRequirement(resourcesUnfiltered),
SecurityContext: securityContext.NewSecurityContext(),
VolumeMounts: []core.VolumeMount{

View file

@ -62,8 +62,14 @@ func runTestCases(t *testing.T, testCases ...testCaseStruct) {
func runTestCase(t *testing.T, testCase testCaseStruct) {
t.Run(testCase.Name, func(t *testing.T) {
// Arrange
if testCase.config.OperatorImage == "" {
testCase.config.OperatorImage = testImageOperator
}
d, eventRecorder := createTestDeployment(t, testCase.config, testCase.ArangoDeployment)
startDepl := d.status.last.DeepCopy()
errs := 0
for {
require.NoError(t, d.currentState.Refresh(context.Background()))
@ -89,6 +95,21 @@ func runTestCase(t *testing.T, testCase testCaseStruct) {
testCase.Helper(t, d, &testCase)
}
f := startDepl.Members.AsList()
if len(f) == 0 {
f = d.status.last.Members.AsList()
}
// Add Expected pod defaults
if !testCase.DropInit {
testCase.ExpectedPod = *defaultPodAppender(t, &testCase.ExpectedPod,
addLifecycle(f[0].Member.ID,
f[0].Group == api.ServerGroupDBServers && f[0].Member.IsInitialized,
testCase.ArangoDeployment.Spec.License.GetSecretName(),
f[0].Group),
podDataSort())
}
// Create custom resource in the fake kubernetes API
_, err := d.deps.DatabaseCRCli.DatabaseV1().ArangoDeployments(testNamespace).Create(context.Background(), d.apiObject, metav1.CreateOptions{})
require.NoError(t, err)

View file

@ -29,6 +29,8 @@ import (
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"testing"
"github.com/arangodb/kube-arangodb/pkg/deployment/resources/inspector"
@ -70,8 +72,7 @@ const (
testLicense = "testLicense"
testServiceAccountName = "testServiceAccountName"
testPriorityClassName = "testPriority"
testImageLifecycle = "arangodb/kube-arangodb:0.3.16"
testImageOperatorUUIDInit = "image/test-1234:3.7"
testImageOperator = "arangodb/kube-arangodb:0.3.16"
testYes = "yes"
)
@ -91,6 +92,7 @@ type testCaseStruct struct {
ExpectedEvent string
ExpectedPod core.Pod
Features testCaseFeatures
DropInit bool
}
func createTestTLSVolume(serverGroupString, ID string) core.Volume {
@ -536,7 +538,7 @@ func createTestLifecycleContainer(resources core.ResourceRequirements) core.Cont
return core.Container{
Name: "init-lifecycle",
Image: testImageLifecycle,
Image: testImageOperator,
Command: []string{binaryPath, "lifecycle", "copy", "--target", "/lifecycle/tools"},
VolumeMounts: []core.VolumeMount{
k8sutil.LifecycleVolumeMount(),
@ -550,7 +552,7 @@ func createTestLifecycleContainer(resources core.ResourceRequirements) core.Cont
func createTestAlpineContainer(name string, requireUUID bool) core.Container {
binaryPath, _ := os.Executable()
var securityContext api.ServerGroupSpecSecurityContext
return k8sutil.ArangodInitContainer("uuid", name, "rocksdb", binaryPath, testImageOperatorUUIDInit, requireUUID, securityContext.NewSecurityContext())
return k8sutil.ArangodInitContainer("uuid", name, "rocksdb", binaryPath, testImageOperator, requireUUID, securityContext.NewSecurityContext())
}
func (testCase *testCaseStruct) createTestPodData(deployment *Deployment, group api.ServerGroup,
@ -566,7 +568,7 @@ func (testCase *testCaseStruct) createTestPodData(deployment *Deployment, group
OwnerReferences: []metav1.OwnerReference{
testCase.ArangoDeployment.AsOwner(),
},
Finalizers: deployment.resources.CreatePodFinalizers(group),
Finalizers: finalizers(group),
}
groupSpec := testCase.ArangoDeployment.Spec.GetServerGroupSpec(group)
@ -579,3 +581,175 @@ func (testCase *testCaseStruct) createTestPodData(deployment *Deployment, group
deployment.apiObject.Status.Members.Update(member, group)
}
}
func finalizers(group api.ServerGroup) []string {
var finalizers []string
switch group {
case api.ServerGroupAgents:
finalizers = append(finalizers, constants.FinalizerPodAgencyServing)
case api.ServerGroupDBServers:
finalizers = append(finalizers, constants.FinalizerPodDrainDBServer)
}
return finalizers
}
func defaultPodAppender(t *testing.T, pod *core.Pod, f ...func(t *testing.T, p *core.Pod)) *core.Pod {
n := pod.DeepCopy()
for _, a := range f {
a(t, n)
}
return n
}
func podDataSort() func(t *testing.T, p *core.Pod) {
sortVolumes := map[string]int{
"rocksdb-encryption": -1,
"cluster-jwt": 1,
"tls-keyfile": -2,
"arangod-data": -3,
"exporter-jwt": 0,
"lifecycle": 2,
"uuid": 3,
"volume": 40,
"volume2": 40,
}
sortVolumeMounts := map[string]int{
"tls-keyfile": 1,
"arangod-data": -1,
"lifecycle": 0,
"cluster-jwt": 5,
"rocksdb-encryption": 4,
"volume": 40,
"volume2": 40,
}
sortInitContainers := map[string]int{
"init-lifecycle": 0,
"uuid": 1,
}
return func(t *testing.T, p *core.Pod) {
sort.Slice(p.Spec.Volumes, func(i, j int) bool {
av, ak := sortVolumes[p.Spec.Volumes[i].Name]
if strings.HasPrefix(p.Spec.Volumes[i].Name, "sni-") {
av = 100
ak = true
}
bv, bk := sortVolumes[p.Spec.Volumes[j].Name]
if strings.HasPrefix(p.Spec.Volumes[j].Name, "sni-") {
bv = 100
bk = true
}
if !ak && !bk {
return false
}
if !ak {
return true
}
if !bk {
return false
}
return av < bv
})
if len(p.Spec.Containers) > 0 {
sort.Slice(p.Spec.Containers[0].VolumeMounts, func(i, j int) bool {
av, ak := sortVolumeMounts[p.Spec.Containers[0].VolumeMounts[i].Name]
if strings.HasPrefix(p.Spec.Containers[0].VolumeMounts[i].Name, "sni-") {
av = 100
ak = true
}
bv, bk := sortVolumeMounts[p.Spec.Containers[0].VolumeMounts[j].Name]
if strings.HasPrefix(p.Spec.Containers[0].VolumeMounts[j].Name, "sni-") {
bv = 100
bk = true
}
if !ak && !bk {
return false
}
if !ak {
return true
}
if !bk {
return false
}
return av < bv
})
}
sort.Slice(p.Spec.InitContainers, func(i, j int) bool {
av, ak := sortInitContainers[p.Spec.InitContainers[i].Name]
bv, bk := sortInitContainers[p.Spec.InitContainers[j].Name]
if !ak && !bk {
return false
}
if !ak {
return true
}
if !bk {
return false
}
return av < bv
})
}
}
func addLifecycle(name string, uuidRequired bool, license string, group api.ServerGroup) func(t *testing.T, p *core.Pod) {
return func(t *testing.T, p *core.Pod) {
if group.IsArangosync() {
return
}
if len(p.Spec.Containers) > 0 {
p.Spec.Containers[0].Env = append(k8sutil.GetLifecycleEnv(), p.Spec.Containers[0].Env...)
if license != "" {
p.Spec.Containers[0].Env = append([]core.EnvVar{
k8sutil.CreateEnvSecretKeySelector(constants.EnvArangoLicenseKey,
license, constants.SecretKeyToken)}, p.Spec.Containers[0].Env...)
}
}
if _, ok := k8sutil.GetAnyVolumeByName(p.Spec.Volumes, k8sutil.LifecycleVolumeName); !ok {
p.Spec.Volumes = append([]core.Volume{k8sutil.LifecycleVolume()}, p.Spec.Volumes...)
}
if _, ok := k8sutil.GetAnyVolumeByName(p.Spec.Volumes, "arangod-data"); !ok {
p.Spec.Volumes = append([]core.Volume{k8sutil.LifecycleVolume()}, p.Spec.Volumes...)
}
if len(p.Spec.Containers) > 0 {
p.Spec.Containers[0].Lifecycle = createTestLifecycle()
}
if len(p.Spec.Containers) > 0 {
if _, ok := k8sutil.GetAnyVolumeMountByName(p.Spec.Containers[0].VolumeMounts, "lifecycle"); !ok {
p.Spec.Containers[0].VolumeMounts = append(p.Spec.Containers[0].VolumeMounts, k8sutil.LifecycleVolumeMount())
}
if _, ok := k8sutil.GetAnyContainerByName(p.Spec.InitContainers, "init-lifecycle"); !ok {
p.Spec.InitContainers = append([]core.Container{createTestLifecycleContainer(emptyResources)}, p.Spec.InitContainers...)
}
}
if _, ok := k8sutil.GetAnyContainerByName(p.Spec.InitContainers, "uuid"); !ok {
binaryPath, _ := os.Executable()
p.Spec.InitContainers = append([]core.Container{k8sutil.ArangodInitContainer("uuid", name, "rocksdb", binaryPath, testImageOperator, uuidRequired, securityContext.NewSecurityContext())}, p.Spec.InitContainers...)
}
}
}

View file

@ -41,6 +41,7 @@ var metricsExporter = &feature{
constValue: util.NewBool(true),
}
// deprecated
func MetricsExporter() Feature {
return metricsExporter
}

View file

@ -149,12 +149,8 @@ type Context interface {
// UpdateStatus replaces the status of the deployment with the given status and
// updates the resources in k8s.
UpdateStatus(ctx context.Context, status api.DeploymentStatus, lastVersion int32, force ...bool) error
// GetLifecycleImage returns the image name containing the lifecycle helper (== name of operator image)
GetLifecycleImage() string
// GetOperatorUUIDImage returns the image name containing the uuid helper (== name of operator image)
GetOperatorUUIDImage() string
// GetMetricsExporterImage returns the image name containing the default metrics exporter image
GetMetricsExporterImage() string
// GetOperatorImage returns the image name of operator image
GetOperatorImage() string
// GetArangoImage returns the image name containing the default arango image
GetArangoImage() string
// GetName returns the name of the deployment

View file

@ -94,34 +94,6 @@ func createInternalExporterArgs(spec api.DeploymentSpec, groupSpec api.ServerGro
return options.Sort().AsArgs()
}
func createExporterArgs(spec api.DeploymentSpec, groupSpec api.ServerGroupSpec) []string {
tokenpath := filepath.Join(k8sutil.ExporterJWTVolumeMountDir, constants.SecretKeyToken)
options := k8sutil.CreateOptionPairs(64)
options.Add("--arangodb.jwt-file", tokenpath)
if port := groupSpec.InternalPort; port == nil {
scheme := "http"
if spec.IsSecure() {
scheme = "https"
}
options.Addf("--arangodb.endpoint", "%s://localhost:%d", scheme, k8sutil.ArangoPort)
} else {
options.Addf("--arangodb.endpoint", "http://localhost:%d", *port)
}
keyPath := filepath.Join(k8sutil.TLSKeyfileVolumeMountDir, constants.SecretTLSKeyfile)
if spec.IsSecure() && spec.Metrics.IsTLS() {
options.Add("--ssl.keyfile", keyPath)
}
if port := spec.Metrics.GetPort(); port != k8sutil.ArangoExporterPort {
options.Addf("--server.address", ":%d", port)
}
return options.Sort().AsArgs()
}
func createExporterLivenessProbe(isSecure bool) *probes.HTTPProbeConfig {
probeCfg := &probes.HTTPProbeConfig{
LocalPath: "/",

View file

@ -250,23 +250,6 @@ func createArangoSyncArgs(apiObject meta.Object, spec api.DeploymentSpec, group
return args
}
// CreatePodFinalizers creates a list of finalizers for a pod created for the given group.
func (r *Resources) CreatePodFinalizers(group api.ServerGroup) []string {
var finalizers []string
if d := r.context.GetSpec().GetServerGroupSpec(group).ShutdownDelay; d != nil {
finalizers = append(finalizers, constants.FinalizerDelayPodTermination)
}
switch group {
case api.ServerGroupAgents:
finalizers = append(finalizers, constants.FinalizerPodAgencyServing)
case api.ServerGroupDBServers:
finalizers = append(finalizers, constants.FinalizerPodDrainDBServer)
}
return finalizers
}
// CreatePodTolerations creates a list of tolerations for a pod created for the given group.
func (r *Resources) CreatePodTolerations(group api.ServerGroup, groupSpec api.ServerGroupSpec) []core.Toleration {
notReadyDur := k8sutil.TolerationDuration{Forever: false, TimeSpan: time.Minute}

View file

@ -29,8 +29,6 @@ import (
"github.com/arangodb/kube-arangodb/pkg/deployment/topology"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/util/collection"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/interfaces"
@ -157,9 +155,7 @@ func (a *ArangoDContainer) GetEnvs() []core.EnvVar {
envs.Add(true, env)
}
if a.resources.context.GetLifecycleImage() != "" {
envs.Add(true, k8sutil.GetLifecycleEnv()...)
}
envs.Add(true, k8sutil.GetLifecycleEnv()...)
if a.groupSpec.Resources.Limits != nil {
if a.groupSpec.GetOverrideDetectedTotalMemory() {
@ -201,10 +197,7 @@ func (a *ArangoDContainer) GetResourceRequirements() core.ResourceRequirements {
}
func (a *ArangoDContainer) GetLifecycle() (*core.Lifecycle, error) {
if a.resources.context.GetLifecycleImage() != "" {
return k8sutil.NewLifecycle()
}
return nil, nil
return k8sutil.NewLifecycle()
}
func (a *ArangoDContainer) GetImagePullPolicy() core.PullPolicy {
@ -312,28 +305,11 @@ func (m *MemberArangoDPod) GetSidecars(pod *core.Pod) error {
if m.spec.Metrics.IsEnabled() {
var c *core.Container
if features.MetricsExporter().Enabled() {
pod.Labels[k8sutil.LabelKeyArangoExporter] = "yes"
if container, err := m.createMetricsExporterSidecarInternalExporter(); err != nil {
return err
} else {
c = container
}
pod.Labels[k8sutil.LabelKeyArangoExporter] = "yes"
if container, err := m.createMetricsExporterSidecarInternalExporter(); err != nil {
return err
} else {
switch m.spec.Metrics.Mode.Get() {
case api.MetricsModeExporter:
if !m.group.IsExportMetrics() {
break
}
fallthrough
case api.MetricsModeSidecar:
c = m.createMetricsExporterSidecarExternalExporter()
pod.Labels[k8sutil.LabelKeyArangoExporter] = "yes"
default:
pod.Labels[k8sutil.LabelKeyArangoExporter] = "yes"
}
c = container
}
if c != nil {
pod.Spec.Containers = append(pod.Spec.Containers, *c)
@ -354,9 +330,7 @@ func (m *MemberArangoDPod) GetVolumes() ([]core.Volume, []core.VolumeMount) {
volumes.AddVolumeMount(k8sutil.ArangodVolumeMount())
if m.resources.context.GetLifecycleImage() != "" {
volumes.AddVolumeMount(k8sutil.LifecycleVolumeMount())
}
volumes.AddVolumeMount(k8sutil.LifecycleVolumeMount())
if m.status.PersistentVolumeClaimName != "" {
vol := k8sutil.CreateVolumeWithPersitantVolumeClaim(k8sutil.ArangodVolumeName,
@ -377,34 +351,16 @@ func (m *MemberArangoDPod) GetVolumes() ([]core.Volume, []core.VolumeMount) {
volumes.Append(pod.Security(), m.AsInput())
if m.spec.Metrics.IsEnabled() {
if features.MetricsExporter().Enabled() {
token := m.spec.Metrics.GetJWTTokenSecretName()
if token != "" {
vol := k8sutil.CreateVolumeWithSecret(k8sutil.ExporterJWTVolumeName, token)
volumes.AddVolume(vol)
}
} else {
switch m.spec.Metrics.Mode.Get() {
case api.MetricsModeExporter:
if !m.group.IsExportMetrics() {
break
}
fallthrough
case api.MetricsModeSidecar:
token := m.spec.Metrics.GetJWTTokenSecretName()
if token != "" {
vol := k8sutil.CreateVolumeWithSecret(k8sutil.ExporterJWTVolumeName, token)
volumes.AddVolume(vol)
}
}
token := m.spec.Metrics.GetJWTTokenSecretName()
if token != "" {
vol := k8sutil.CreateVolumeWithSecret(k8sutil.ExporterJWTVolumeName, token)
volumes.AddVolume(vol)
}
}
volumes.Append(pod.JWT(), m.AsInput())
if m.resources.context.GetLifecycleImage() != "" {
volumes.AddVolume(k8sutil.LifecycleVolume())
}
volumes.AddVolume(k8sutil.LifecycleVolume())
// SNI
volumes.Append(pod.SNI(), m.AsInput())
@ -436,9 +392,8 @@ func (m *MemberArangoDPod) GetInitContainers(cachedStatus interfaces.Inspector)
return nil, err
}
lifecycleImage := m.resources.context.GetLifecycleImage()
if lifecycleImage != "" {
c, err := k8sutil.InitLifecycleContainer(lifecycleImage, &m.spec.Lifecycle.Resources,
{
c, err := k8sutil.InitLifecycleContainer(m.resources.context.GetOperatorImage(), &m.spec.Lifecycle.Resources,
m.groupSpec.SecurityContext.NewSecurityContext())
if err != nil {
return nil, err
@ -446,12 +401,11 @@ func (m *MemberArangoDPod) GetInitContainers(cachedStatus interfaces.Inspector)
initContainers = append(initContainers, c)
}
operatorUUIDImage := m.resources.context.GetOperatorUUIDImage()
if operatorUUIDImage != "" {
{
engine := m.spec.GetStorageEngine().AsArangoArgument()
requireUUID := m.group == api.ServerGroupDBServers && m.status.IsInitialized
c := k8sutil.ArangodInitContainer(api.ServerGroupReservedInitContainerNameUUID, m.status.ID, engine, executable, operatorUUIDImage, requireUUID,
c := k8sutil.ArangodInitContainer(api.ServerGroupReservedInitContainerNameUUID, m.status.ID, engine, executable, m.resources.context.GetOperatorImage(), requireUUID,
m.groupSpec.SecurityContext.NewSecurityContext())
initContainers = append(initContainers, c)
}
@ -509,7 +463,19 @@ func (m *MemberArangoDPod) GetInitContainers(cachedStatus interfaces.Inspector)
}
func (m *MemberArangoDPod) GetFinalizers() []string {
return m.resources.CreatePodFinalizers(m.group)
var finalizers []string
if d := m.spec.GetServerGroupSpec(m.group).ShutdownDelay; d != nil {
finalizers = append(finalizers, constants.FinalizerDelayPodTermination)
}
switch m.group {
case api.ServerGroupAgents:
finalizers = append(finalizers, constants.FinalizerPodAgencyServing)
case api.ServerGroupDBServers:
finalizers = append(finalizers, constants.FinalizerPodDrainDBServer)
}
return finalizers
}
func (m *MemberArangoDPod) GetTolerations() []core.Toleration {
@ -551,33 +517,6 @@ func (m *MemberArangoDPod) createMetricsExporterSidecarInternalExporter() (*core
return &c, nil
}
func (m *MemberArangoDPod) createMetricsExporterSidecarExternalExporter() *core.Container {
image := m.context.GetMetricsExporterImage()
if m.spec.Metrics.HasImage() {
image = m.spec.Metrics.GetImage()
}
args := createExporterArgs(m.spec, m.groupSpec)
if m.spec.Metrics.Mode.Get() == api.MetricsModeSidecar {
args = append(args, "--mode=passthru")
}
c := ArangodbExporterContainer(image, args,
createExporterLivenessProbe(m.spec.IsSecure() && m.spec.Metrics.IsTLS()), m.spec.Metrics.Resources,
m.groupSpec.SecurityContext.NewSecurityContext(),
m.spec)
if m.spec.Metrics.GetJWTTokenSecretName() != "" {
c.VolumeMounts = append(c.VolumeMounts, k8sutil.ExporterJWTVolumeMount())
}
if pod.IsTLSEnabled(m.AsInput()) {
c.VolumeMounts = append(c.VolumeMounts, k8sutil.TlsKeyfileVolumeMount())
}
return &c
}
func (m *MemberArangoDPod) ApplyPodSpec(p *core.PodSpec) error {
p.SecurityContext = m.groupSpec.SecurityContext.NewPodSecurityContext()

View file

@ -113,10 +113,7 @@ func (a *ArangoSyncContainer) GetResourceRequirements() core.ResourceRequirement
}
func (a *ArangoSyncContainer) GetLifecycle() (*core.Lifecycle, error) {
if a.resources.context.GetLifecycleImage() != "" {
return k8sutil.NewLifecycle()
}
return nil, nil
return k8sutil.NewLifecycle()
}
func (a *ArangoSyncContainer) GetImagePullPolicy() core.PullPolicy {
@ -144,9 +141,7 @@ func (a *ArangoSyncContainer) GetEnvs() []core.EnvVar {
envs.Add(true, env)
}
if a.resources.context.GetLifecycleImage() != "" {
envs.Add(true, k8sutil.GetLifecycleEnv()...)
}
envs.Add(true, k8sutil.GetLifecycleEnv()...)
if len(a.groupSpec.Envs) > 0 {
for _, env := range a.groupSpec.Envs {
@ -227,10 +222,8 @@ func (m *MemberSyncPod) GetVolumes() ([]core.Volume, []core.VolumeMount) {
var volumes []core.Volume
var volumeMounts []core.VolumeMount
if m.resources.context.GetLifecycleImage() != "" {
volumes = append(volumes, k8sutil.LifecycleVolume())
volumeMounts = append(volumeMounts, k8sutil.LifecycleVolumeMount())
}
volumes = append(volumes, k8sutil.LifecycleVolume())
volumeMounts = append(volumeMounts, k8sutil.LifecycleVolumeMount())
if m.tlsKeyfileSecretName != "" {
vol := k8sutil.CreateVolumeWithSecret(k8sutil.TlsKeyfileVolumeName, m.tlsKeyfileSecretName)
@ -273,9 +266,8 @@ func (m *MemberSyncPod) GetInitContainers(cachedStatus interfaces.Inspector) ([]
initContainers = append(initContainers, c...)
}
lifecycleImage := m.resources.context.GetLifecycleImage()
if lifecycleImage != "" {
c, err := k8sutil.InitLifecycleContainer(lifecycleImage, &m.spec.Lifecycle.Resources,
{
c, err := k8sutil.InitLifecycleContainer(m.resources.context.GetOperatorImage(), &m.spec.Lifecycle.Resources,
m.groupSpec.SecurityContext.NewSecurityContext())
if err != nil {
return nil, err

View file

@ -26,9 +26,10 @@ package rotation
import (
"strings"
"k8s.io/apimachinery/pkg/api/equality"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
core "k8s.io/api/core/v1"
)
@ -47,23 +48,29 @@ func containersCompare(_ api.DeploymentSpec, _ api.ServerGroup, spec, status *co
}
for id := range a {
if ac, bc := &a[id], &b[id]; ac.Name == k8sutil.ServerContainerName && ac.Name == bc.Name {
if !IsOnlyLogLevelChanged(ac.Command, bc.Command) {
continue
if ac, bc := &a[id], &b[id]; ac.Name == bc.Name {
if ac.Name == api.ServerGroupReservedContainerNameServer {
if !isOnlyLogLevelChanged(ac.Command, bc.Command) {
continue
}
plan = append(plan, builder.NewAction(api.ActionTypeRuntimeContainerArgsLogLevelUpdate).
AddParam(ContainerName, ac.Name))
bc.Command = ac.Command
mode = mode.And(InPlaceRotation)
} else {
if ac.Image != bc.Image {
// Image changed
plan = append(plan, builder.NewAction(api.ActionTypeRuntimeContainerImageUpdate).AddParam(ContainerName, ac.Name).AddParam(ContainerImage, ac.Image))
bc.Image = ac.Image
mode = mode.And(InPlaceRotation)
}
}
plan = append(plan, builder.NewAction(api.ActionTypeRuntimeContainerArgsLogLevelUpdate).
AddParam(ContainerName, ac.Name))
bc.Command = ac.Command
mode = mode.And(InPlaceRotation)
} else if ac.Name == bc.Name {
if ac.Image != bc.Image {
// Image changed
plan = append(plan, builder.NewAction(api.ActionTypeRuntimeContainerImageUpdate).AddParam(ContainerName, ac.Name).AddParam(ContainerImage, ac.Image))
bc.Image = ac.Image
mode = mode.And(InPlaceRotation)
if api.IsReservedServerGroupContainerName(ac.Name) {
mode = mode.And(internalContainerLifecycleCompare(ac, bc))
}
}
}
@ -116,9 +123,9 @@ func initContainersCompare(deploymentSpec api.DeploymentSpec, group api.ServerGr
}
}
// IsOnlyLogLevelChanged returns true when status and spec log level arguments are different.
// isOnlyLogLevelChanged returns true when status and spec log level arguments are different.
// If any other argument than --log.level is different false is returned.
func IsOnlyLogLevelChanged(specArgs, statusArgs []string) bool {
func isOnlyLogLevelChanged(specArgs, statusArgs []string) bool {
diff := util.DiffStrings(specArgs, statusArgs)
if len(diff) == 0 {
return false
@ -132,3 +139,26 @@ func IsOnlyLogLevelChanged(specArgs, statusArgs []string) bool {
return true
}
func internalContainerLifecycleCompare(spec, status *core.Container) Mode {
if spec.Lifecycle == nil && status.Lifecycle == nil {
return SkippedRotation
}
if spec.Lifecycle == nil {
status.Lifecycle = nil
return SilentRotation
}
if status.Lifecycle == nil {
status.Lifecycle = spec.Lifecycle
return SilentRotation
}
if !equality.Semantic.DeepEqual(spec.Lifecycle, status.Lifecycle) {
status.Lifecycle = spec.Lifecycle.DeepCopy()
return SilentRotation
}
return SkippedRotation
}

View file

@ -216,7 +216,7 @@ func TestIsOnlyLogLevelChanged(t *testing.T) {
for testName, testCase := range tests {
t.Run(testName, func(t *testing.T) {
got := IsOnlyLogLevelChanged(testCase.args.specArgs, testCase.args.statusArgs)
got := isOnlyLogLevelChanged(testCase.args.specArgs, testCase.args.statusArgs)
assert.Equal(t, testCase.want, got)
})

View file

@ -87,9 +87,8 @@ type Config struct {
Namespace string
PodName string
ServiceAccount string
LifecycleImage string
OperatorImage string
ArangoImage string
MetricsExporterImage string
EnableDeployment bool
EnableDeploymentReplication bool
EnableStorage bool

View file

@ -203,13 +203,11 @@ func (o *Operator) handleDeploymentEvent(event *Event) error {
// makeDeploymentConfigAndDeps creates a Config & Dependencies object for a new Deployment.
func (o *Operator) makeDeploymentConfigAndDeps(apiObject *api.ArangoDeployment) (deployment.Config, deployment.Dependencies) {
cfg := deployment.Config{
ServiceAccount: o.Config.ServiceAccount,
LifecycleImage: o.Config.LifecycleImage,
OperatorUUIDInitImage: o.Config.LifecycleImage,
MetricsExporterImage: o.MetricsExporterImage,
ArangoImage: o.ArangoImage,
AllowChaos: o.Config.AllowChaos,
Scope: o.Scope,
ServiceAccount: o.Config.ServiceAccount,
OperatorImage: o.Config.OperatorImage,
ArangoImage: o.ArangoImage,
AllowChaos: o.Config.AllowChaos,
Scope: o.Scope,
}
deps := deployment.Dependencies{
Log: o.Dependencies.LogService.MustGetLogger(logging.LoggerNameDeployment).With().

View file

@ -46,6 +46,17 @@ func GetContainerStatusByName(p *core.Pod, name string) (core.ContainerStatus, b
return core.ContainerStatus{}, false
}
// GetAnyContainerByName returns the container in the given containers with the given name.
// Returns false if not found.
func GetAnyContainerByName(containers []core.Container, name string) (core.Container, bool) {
for _, c := range containers {
if c.Name == name {
return c, true
}
}
return core.Container{}, false
}
// GetAnyContainerStatusByName returns the container status in the given ContainerStatus list with the given name.
// Returns false if not found.
func GetAnyContainerStatusByName(containers []core.ContainerStatus, name string) (core.ContainerStatus, bool) {

View file

@ -73,6 +73,28 @@ const (
ServerContainerConditionPrefix = "containers with unready status: "
)
// GetAnyVolumeByName returns the volume in the given volumes with the given name.
// Returns false if not found.
func GetAnyVolumeByName(volumes []core.Volume, name string) (core.Volume, bool) {
for _, c := range volumes {
if c.Name == name {
return c, true
}
}
return core.Volume{}, false
}
// GetAnyVolumeMountByName returns the volumemount in the given volumemountss with the given name.
// Returns false if not found.
func GetAnyVolumeMountByName(volumes []core.VolumeMount, name string) (core.VolumeMount, bool) {
for _, c := range volumes {
if c.Name == name {
return c, true
}
}
return core.VolumeMount{}, false
}
// IsPodReady returns true if the PodReady condition on
// the given pod is set to true.
func IsPodReady(pod *core.Pod) bool {

79
pkg/util/retry/timeout.go Normal file
View file

@ -0,0 +1,79 @@
//
// DISCLAIMER
//
// Copyright 2016-2021 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 retry
import "time"
func Interrput() error {
return interrupt{}
}
type interrupt struct {
}
func (i interrupt) Error() string {
return "interrupt"
}
func IsInterrupt(err error) bool {
_, ok := err.(interrupt)
return ok
}
type TimeoutError struct {
}
func (i TimeoutError) Error() string {
return "timeout"
}
func NewTimeout(t Timeout) Timeout {
return t
}
type Timeout func() error
func (t Timeout) Timeout(interval, timeout time.Duration) error {
timeoutI := time.NewTimer(timeout)
defer timeoutI.Stop()
intervalI := time.NewTicker(interval)
defer intervalI.Stop()
for {
err := t()
if err != nil {
if IsInterrupt(err) {
return nil
}
return err
}
select {
case <-timeoutI.C:
return TimeoutError{}
case <-intervalI.C:
continue
}
}
}