Skip to content

Commit 5c5fda4

Browse files
[Feature] Add --deployment.feature.init-containers-copy-resources (default enabled) - GT-480 (#1404)
1 parent 0e79a30 commit 5c5fda4

File tree

12 files changed

+355
-39
lines changed

12 files changed

+355
-39
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
- (Improvement) Remove PodSchedulingFailure condition instead of setting to false, restart pod if it could not be scheduled
2121
- (Feature) Add ArangoMember overrides
2222
- (Feature) ArangoMember Removal Priority
23+
- (Feature) Add --deployment.feature.init-containers-copy-resources (default enabled)
2324

2425
## [1.2.32](https://github.com/arangodb/kube-arangodb/tree/1.2.32) (2023-08-07)
2526
- (Feature) Backup lifetime - remove Backup once its lifetime has been reached

README.md

Lines changed: 26 additions & 25 deletions
Large diffs are not rendered by default.

internal/features.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,10 @@ features:
213213
state: Alpha
214214
- operatorVersion: 1.2.25
215215
state: Production
216+
- name: Copy resources spec to init containers
217+
enabled: true
218+
remarks: Copy resources spec to built-in init containers if they are not specified
219+
flag: --deployment.feature.init-containers-copy-resources
220+
releases:
221+
- operatorVersion: 1.2.33
222+
state: Production

pkg/deployment/deployment_pod_resources_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,69 @@ func TestEnsurePod_ArangoDB_Resources(t *testing.T) {
120120
testCase.createTestPodData(deployment, api.ServerGroupDBServers, firstDBServerStatus)
121121
},
122122
ExpectedEvent: "member dbserver is created",
123+
ExpectedPod: core.Pod{
124+
Spec: core.PodSpec{
125+
Volumes: []core.Volume{
126+
k8sutil.CreateVolumeEmptyDir(shared.ArangodVolumeName),
127+
},
128+
InitContainers: []core.Container{
129+
createTestLifecycleContainer(emptyResources),
130+
},
131+
Containers: []core.Container{
132+
{
133+
Name: shared.ServerContainerName,
134+
Image: testImage,
135+
Command: createTestCommandForDBServer(firstDBServerStatus.ID, false, false, false),
136+
Ports: createTestPorts(api.ServerGroupAgents),
137+
Resources: k8sutil.ExtractPodResourceRequirement(resourcesUnfiltered),
138+
VolumeMounts: []core.VolumeMount{
139+
k8sutil.ArangodVolumeMount(),
140+
},
141+
LivenessProbe: createTestLivenessProbe(httpProbe, false, "", shared.ServerPortName),
142+
ImagePullPolicy: core.PullIfNotPresent,
143+
SecurityContext: securityContext.NewSecurityContext(),
144+
Env: withDefaultEnvs(t, resourcesUnfiltered),
145+
},
146+
},
147+
RestartPolicy: core.RestartPolicyNever,
148+
TerminationGracePeriodSeconds: &defaultDBServerTerminationTimeout,
149+
Hostname: testDeploymentName + "-" + api.ServerGroupDBServersString + "-" +
150+
firstDBServerStatus.ID,
151+
Subdomain: testDeploymentName + "-int",
152+
Affinity: k8sutil.CreateAffinity(testDeploymentName, api.ServerGroupDBServersString,
153+
false, ""),
154+
},
155+
},
156+
},
157+
{
158+
Name: "DBserver POD with resource requirements, init-container-copy-resources feature false",
159+
ArangoDeployment: &api.ArangoDeployment{
160+
Spec: api.DeploymentSpec{
161+
Image: util.NewType[string](testImage),
162+
Authentication: noAuthentication,
163+
TLS: noTLS,
164+
DBServers: api.ServerGroupSpec{
165+
Resources: resourcesUnfiltered,
166+
},
167+
},
168+
},
169+
Helper: func(t *testing.T, deployment *Deployment, testCase *testCaseStruct) {
170+
deployment.currentObjectStatus = &api.DeploymentStatus{
171+
Members: api.DeploymentStatusMembers{
172+
DBServers: api.MemberStatusList{
173+
firstDBServerStatus,
174+
},
175+
},
176+
Images: createTestImages(false),
177+
}
178+
deployment.currentObjectStatus.Members.DBServers[0].IsInitialized = true
179+
180+
testCase.createTestPodData(deployment, api.ServerGroupDBServers, firstDBServerStatus)
181+
},
182+
ExpectedEvent: "member dbserver is created",
183+
Features: testCaseFeatures{
184+
InitContainersCopyResources: util.NewType(false),
185+
},
123186
ExpectedPod: core.Pod{
124187
Spec: core.PodSpec{
125188
Volumes: []core.Volume{

pkg/deployment/deployment_pod_sync_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,101 @@ func TestEnsurePod_Sync_Worker(t *testing.T) {
13251325
},
13261326
},
13271327
},
1328+
{
1329+
DropInit: true,
1330+
Name: "Sync Worker Pod with monitoring, service account, node selector, lifecycle, license " +
1331+
"liveness probe, priority class name, resource requirements without alpine, with init-containers-copy-resources feature off",
1332+
Features: testCaseFeatures{
1333+
InitContainersCopyResources: util.NewType(false),
1334+
},
1335+
config: Config{
1336+
OperatorImage: testImageOperator,
1337+
},
1338+
ArangoDeployment: &api.ArangoDeployment{
1339+
Spec: api.DeploymentSpec{
1340+
Image: util.NewType[string](testImage),
1341+
Authentication: noAuthentication,
1342+
Sync: api.SyncSpec{
1343+
Enabled: util.NewType[bool](true),
1344+
},
1345+
SyncWorkers: api.ServerGroupSpec{
1346+
ServiceAccountName: util.NewType[string](testServiceAccountName),
1347+
NodeSelector: nodeSelectorTest,
1348+
PriorityClassName: testPriorityClassName,
1349+
Resources: resourcesUnfiltered,
1350+
},
1351+
License: api.LicenseSpec{
1352+
SecretName: util.NewType[string](testLicense),
1353+
},
1354+
},
1355+
},
1356+
Helper: func(t *testing.T, deployment *Deployment, testCase *testCaseStruct) {
1357+
deployment.currentObjectStatus = &api.DeploymentStatus{
1358+
Members: api.DeploymentStatusMembers{
1359+
SyncWorkers: api.MemberStatusList{
1360+
firstSyncWorker,
1361+
},
1362+
},
1363+
Images: createTestImages(true),
1364+
}
1365+
1366+
testCase.createTestPodData(deployment, api.ServerGroupSyncWorkers, firstSyncWorker)
1367+
1368+
name := testCase.ArangoDeployment.Spec.Sync.Monitoring.GetTokenSecretName()
1369+
auth, err := k8sutil.GetTokenSecret(context.Background(), deployment.GetCachedStatus().Secret().V1().Read(), name)
1370+
require.NoError(t, err)
1371+
1372+
testCase.ExpectedPod.Spec.Containers[0].LivenessProbe = createTestLivenessProbe("", true, "bearer "+auth, shared.ServerPortName)
1373+
},
1374+
ExpectedEvent: "member syncworker is created",
1375+
ExpectedPod: core.Pod{
1376+
Spec: core.PodSpec{
1377+
Volumes: []core.Volume{
1378+
k8sutil.LifecycleVolume(),
1379+
k8sutil.CreateVolumeWithSecret(shared.MasterJWTSecretVolumeName, testDeploymentName+"-sync-jwt"),
1380+
},
1381+
InitContainers: []core.Container{
1382+
createTestLifecycleContainer(emptyResources),
1383+
},
1384+
Containers: []core.Container{
1385+
{
1386+
Name: shared.ServerContainerName,
1387+
Image: testImage,
1388+
Command: createTestCommandForSyncWorker(firstSyncWorker.ID, true, true),
1389+
Ports: createTestPorts(api.ServerGroupSyncWorkers),
1390+
Env: []core.EnvVar{
1391+
k8sutil.CreateEnvSecretKeySelector(constants.EnvArangoSyncMonitoringToken,
1392+
testDeploymentName+"-sync-mt", constants.SecretKeyToken),
1393+
k8sutil.CreateEnvSecretKeySelector(constants.EnvArangoLicenseKey,
1394+
testLicense, constants.SecretKeyToken),
1395+
k8sutil.CreateEnvFieldPath(constants.EnvOperatorPodName, "metadata.name"),
1396+
k8sutil.CreateEnvFieldPath(constants.EnvOperatorPodNamespace, "metadata.namespace"),
1397+
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeName, "spec.nodeName"),
1398+
k8sutil.CreateEnvFieldPath(constants.EnvOperatorNodeNameArango, "spec.nodeName"),
1399+
},
1400+
ImagePullPolicy: core.PullIfNotPresent,
1401+
Lifecycle: createTestLifecycle(api.ServerGroupSyncMasters),
1402+
Resources: k8sutil.ExtractPodResourceRequirement(resourcesUnfiltered),
1403+
SecurityContext: securityContext.NewSecurityContext(),
1404+
VolumeMounts: []core.VolumeMount{
1405+
k8sutil.LifecycleVolumeMount(),
1406+
k8sutil.MasterJWTVolumeMount(),
1407+
},
1408+
},
1409+
},
1410+
PriorityClassName: testPriorityClassName,
1411+
RestartPolicy: core.RestartPolicyNever,
1412+
ServiceAccountName: testServiceAccountName,
1413+
NodeSelector: nodeSelectorTest,
1414+
TerminationGracePeriodSeconds: &defaultSyncWorkerTerminationTimeout,
1415+
Hostname: testDeploymentName + "-" + api.ServerGroupSyncWorkersString + "-" +
1416+
firstSyncWorker.ID,
1417+
Subdomain: testDeploymentName + "-int",
1418+
Affinity: k8sutil.CreateAffinity(testDeploymentName, api.ServerGroupSyncWorkersString,
1419+
false, api.ServerGroupDBServersString),
1420+
},
1421+
},
1422+
},
13281423
}
13291424

13301425
runTestCases(t, testCases...)

pkg/deployment/deployment_run_test.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import (
4141
)
4242

4343
func runTestCases(t *testing.T, testCases ...testCaseStruct) {
44-
// This esure idempotency in generated outputs
44+
// This ensures idempotency in generated outputs
4545
for i := 0; i < 25; i++ {
4646
t.Run(fmt.Sprintf("Iteration %d", i), func(t *testing.T) {
4747
for _, testCase := range testCases {
@@ -101,6 +101,22 @@ func runTestCase(t *testing.T, testCase testCaseStruct) {
101101
f[0].Group),
102102
podDataSort())
103103
}
104+
if util.TypeOrDefault(testCase.Features.InitContainersCopyResources, features.InitContainerCopyResources().EnabledByDefault()) {
105+
pSpec := &testCase.ExpectedPod.Spec
106+
// ensure all "built-in" init containers have resources set
107+
for i, c := range pSpec.InitContainers {
108+
if !api.IsReservedServerGroupInitContainerName(c.Name) {
109+
continue
110+
}
111+
mainContainer := pSpec.Containers[0]
112+
if len(c.Resources.Limits) == 0 {
113+
pSpec.InitContainers[i].Resources.Limits = mainContainer.Resources.Limits.DeepCopy()
114+
}
115+
if len(c.Resources.Requests) == 0 {
116+
pSpec.InitContainers[i].Resources.Requests = mainContainer.Resources.Requests.DeepCopy()
117+
}
118+
}
119+
}
104120

105121
// Create custom resource in the fake kubernetes API
106122
_, err := d.deps.Client.Arango().DatabaseV1().ArangoDeployments(testNamespace).Create(context.Background(), d.currentObject, meta.CreateOptions{})
@@ -117,12 +133,17 @@ func runTestCase(t *testing.T, testCase testCaseStruct) {
117133
require.Equal(t, testCase.Features.EncryptionRotation, *features.EncryptionRotation().EnabledPointer())
118134
*features.JWTRotation().EnabledPointer() = testCase.Features.JWTRotation
119135
*features.TLSSNI().EnabledPointer() = testCase.Features.TLSSNI
120-
if g := testCase.Features.Graceful; g != nil {
121-
*features.GracefulShutdown().EnabledPointer() = *g
122-
} else {
123-
*features.GracefulShutdown().EnabledPointer() = features.GracefulShutdown().EnabledByDefault()
124-
}
125136
*features.TLSRotation().EnabledPointer() = testCase.Features.TLSRotation
137+
138+
fromPtr := func(f features.Feature, b *bool) {
139+
if b != nil {
140+
*f.EnabledPointer() = *b
141+
} else {
142+
*f.EnabledPointer() = f.EnabledByDefault()
143+
}
144+
}
145+
fromPtr(features.GracefulShutdown(), testCase.Features.Graceful)
146+
fromPtr(features.InitContainerCopyResources(), testCase.Features.InitContainersCopyResources)
126147
}
127148

128149
// Set Pending phase

pkg/deployment/deployment_suite_test.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ const (
7979

8080
type testCaseFeatures struct {
8181
TLSSNI, TLSRotation, JWTRotation, EncryptionRotation, Version310 bool
82-
Graceful *bool
82+
Graceful, InitContainersCopyResources *bool
8383
}
8484

8585
type testCaseStruct struct {
@@ -864,7 +864,6 @@ func podDataSort() func(t *testing.T, p *core.Pod) {
864864
func addLifecycle(name string, uuidRequired bool, license string, group api.ServerGroup) func(t *testing.T, p *core.Pod) {
865865
return func(t *testing.T, p *core.Pod) {
866866
if group.IsArangosync() {
867-
868867
return
869868
}
870869

@@ -894,15 +893,21 @@ func addLifecycle(name string, uuidRequired bool, license string, group api.Serv
894893
}
895894

896895
if _, ok := k8sutil.GetAnyContainerByName(p.Spec.InitContainers, "init-lifecycle"); !ok {
897-
p.Spec.InitContainers = append([]core.Container{createTestLifecycleContainer(emptyResources)}, p.Spec.InitContainers...)
898-
896+
p.Spec.InitContainers = append(
897+
[]core.Container{createTestLifecycleContainer(emptyResources)},
898+
p.Spec.InitContainers...,
899+
)
899900
}
900901
}
901902

902903
if _, ok := k8sutil.GetAnyContainerByName(p.Spec.InitContainers, "uuid"); !ok {
903904
binaryPath, _ := os.Executable()
904-
p.Spec.InitContainers = append([]core.Container{k8sutil.ArangodInitContainer("uuid", name, "rocksdb", binaryPath, testImageOperator, uuidRequired, securityContext.NewSecurityContext())}, p.Spec.InitContainers...)
905-
905+
p.Spec.InitContainers = append(
906+
[]core.Container{
907+
k8sutil.ArangodInitContainer("uuid", name, "rocksdb", binaryPath, testImageOperator, uuidRequired, securityContext.NewSecurityContext()),
908+
},
909+
p.Spec.InitContainers...,
910+
)
906911
}
907912
}
908913
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2023 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package features
22+
23+
func init() {
24+
registerFeature(initContainerCopyResources)
25+
}
26+
27+
var initContainerCopyResources = &feature{
28+
name: "init-containers-copy-resources",
29+
description: "Copy resources spec to built-in init containers if they are not specified",
30+
version: "3.6.0",
31+
enterpriseRequired: false,
32+
enabledByDefault: true,
33+
hidden: false,
34+
}
35+
36+
func InitContainerCopyResources() Feature {
37+
return initContainerCopyResources
38+
}

pkg/deployment/resources/pod_creator_arangod.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ func (m *MemberArangoDPod) GetInitContainers(cachedStatus interfaces.Inspector)
513513
}
514514
}
515515

516-
return initContainers, nil
516+
return applyInitContainersResourceResources(initContainers, &m.groupSpec.Resources), nil
517517
}
518518

519519
func (m *MemberArangoDPod) GetFinalizers() []string {

pkg/deployment/resources/pod_creator_sync.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ func (m *MemberSyncPod) GetInitContainers(cachedStatus interfaces.Inspector) ([]
300300
initContainers = append(initContainers, c)
301301
}
302302

303-
return initContainers, nil
303+
return applyInitContainersResourceResources(initContainers, &m.groupSpec.Resources), nil
304304
}
305305

306306
func (m *MemberSyncPod) GetFinalizers() []string {

0 commit comments

Comments
 (0)