Skip to content

Commit 9af8c8d

Browse files
authored
[Feature] Change Init UUID logic (#559)
1 parent e41748d commit 9af8c8d

File tree

10 files changed

+214
-49
lines changed

10 files changed

+214
-49
lines changed

pkg/deployment/context_impl.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ func (d *Deployment) GetLifecycleImage() string {
7171
return d.config.LifecycleImage
7272
}
7373

74-
// GetAlpineImage returns the image name containing the alpine environment
75-
func (d *Deployment) GetAlpineImage() string {
76-
return d.config.AlpineImage
74+
// GetOperatorUUIDImage returns the image name containing the uuid helper (== name of operator image)
75+
func (d *Deployment) GetOperatorUUIDImage() string {
76+
return d.config.OperatorUUIDInitImage
7777
}
7878

7979
// GetNamespSecretsInterfaceace returns the kubernetes namespace that contains

pkg/deployment/deployment.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ import (
5252

5353
// Config holds configuration settings for a Deployment
5454
type Config struct {
55-
ServiceAccount string
56-
AllowChaos bool
57-
LifecycleImage string
58-
AlpineImage string
59-
MetricsExporterImage string
60-
ArangoImage string
55+
ServiceAccount string
56+
AllowChaos bool
57+
LifecycleImage string
58+
OperatorUUIDInitImage string
59+
MetricsExporterImage string
60+
ArangoImage string
6161
}
6262

6363
// Dependencies holds dependent services for a Deployment

pkg/deployment/deployment_core_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) {
276276
},
277277
},
278278
config: Config{
279-
AlpineImage: testImageAlpine,
279+
OperatorUUIDInitImage: testImageOperatorUUIDInit,
280280
},
281281
Helper: func(t *testing.T, deployment *Deployment, testCase *testCaseStruct) {
282282
deployment.status.last = api.DeploymentStatus{
@@ -502,7 +502,7 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) {
502502
},
503503
},
504504
config: Config{
505-
AlpineImage: testImageAlpine,
505+
OperatorUUIDInitImage: testImageOperatorUUIDInit,
506506
},
507507
Helper: func(t *testing.T, deployment *Deployment, testCase *testCaseStruct) {
508508
deployment.status.last = api.DeploymentStatus{
@@ -1155,8 +1155,8 @@ func TestEnsurePod_ArangoDB_Core(t *testing.T) {
11551155
testCase.ExpectedPod.ObjectMeta.Labels[k8sutil.LabelKeyArangoExporter] = testYes
11561156
},
11571157
config: Config{
1158-
LifecycleImage: testImageLifecycle,
1159-
AlpineImage: testImageAlpine,
1158+
LifecycleImage: testImageLifecycle,
1159+
OperatorUUIDInitImage: testImageOperatorUUIDInit,
11601160
},
11611161
ExpectedEvent: "member dbserver is created",
11621162
ExpectedPod: core.Pod{

pkg/deployment/deployment_pod_sync_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,6 @@ func TestEnsurePod_Sync_Master(t *testing.T) {
277277
Name: "Sync Master Pod with lifecycle, license, monitoring without authentication and alpine",
278278
config: Config{
279279
LifecycleImage: testImageLifecycle,
280-
AlpineImage: testImageAlpine,
281280
},
282281
ArangoDeployment: &api.ArangoDeployment{
283282
Spec: api.DeploymentSpec{
@@ -374,7 +373,6 @@ func TestEnsurePod_Sync_Worker(t *testing.T) {
374373
"liveness probe, priority class name, resource requirements without alpine",
375374
config: Config{
376375
LifecycleImage: testImageLifecycle,
377-
AlpineImage: testImageAlpine,
378376
},
379377
ArangoDeployment: &api.ArangoDeployment{
380378
Spec: api.DeploymentSpec{

pkg/deployment/deployment_suite_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const (
5858
testPriorityClassName = "testPriority"
5959
testImageLifecycle = "arangodb/kube-arangodb:0.3.16"
6060
testExporterImage = "arangodb/arangodb-exporter:0.1.6"
61-
testImageAlpine = "alpine:3.7"
61+
testImageOperatorUUIDInit = "image/test-1234:3.7"
6262

6363
testYes = "yes"
6464
)
@@ -469,8 +469,9 @@ func createTestLifecycleContainer(resources core.ResourceRequirements) core.Cont
469469
}
470470

471471
func createTestAlpineContainer(name string, requireUUID bool) core.Container {
472+
binaryPath, _ := os.Executable()
472473
var securityContext api.ServerGroupSpecSecurityContext
473-
return k8sutil.ArangodInitContainer("uuid", name, "rocksdb", testImageAlpine, requireUUID, securityContext.NewSecurityContext())
474+
return k8sutil.ArangodInitContainer("uuid", name, "rocksdb", binaryPath, testImageOperatorUUIDInit, requireUUID, securityContext.NewSecurityContext())
474475
}
475476

476477
func (testCase *testCaseStruct) createTestPodData(deployment *Deployment, group api.ServerGroup,

pkg/deployment/resources/context.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ type Context interface {
6161
GetKubeCli() kubernetes.Interface
6262
// GetLifecycleImage returns the image name containing the lifecycle helper (== name of operator image)
6363
GetLifecycleImage() string
64-
// GetAlpineImage returns the image name containing the alpine environment
65-
GetAlpineImage() string
64+
// GetOperatorUUIDImage returns the image name containing the uuid helper (== name of operator image)
65+
GetOperatorUUIDImage() string
6666
// GetMetricsExporterImage returns the image name containing the default metrics exporter image
6767
GetMetricsExporterImage() string
6868
// GetArangoImage returns the image name containing the default arango image

pkg/deployment/resources/pod_creator_arangod.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ package resources
2525
import (
2626
"fmt"
2727
"math"
28+
"os"
2829

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

@@ -384,6 +385,11 @@ func (m *MemberArangoDPod) IsDeploymentMode() bool {
384385
func (m *MemberArangoDPod) GetInitContainers() ([]core.Container, error) {
385386
var initContainers []core.Container
386387

388+
executable, err := os.Executable()
389+
if err != nil {
390+
return nil, err
391+
}
392+
387393
lifecycleImage := m.resources.context.GetLifecycleImage()
388394
if lifecycleImage != "" {
389395
c, err := k8sutil.InitLifecycleContainer(lifecycleImage, &m.spec.Lifecycle.Resources,
@@ -394,12 +400,12 @@ func (m *MemberArangoDPod) GetInitContainers() ([]core.Container, error) {
394400
initContainers = append(initContainers, c)
395401
}
396402

397-
alpineImage := m.resources.context.GetAlpineImage()
398-
if alpineImage != "" {
403+
operatorUUIDImage := m.resources.context.GetOperatorUUIDImage()
404+
if operatorUUIDImage != "" {
399405
engine := m.spec.GetStorageEngine().AsArangoArgument()
400406
requireUUID := m.group == api.ServerGroupDBServers && m.status.IsInitialized
401407

402-
c := k8sutil.ArangodInitContainer("uuid", m.status.ID, engine, alpineImage, requireUUID,
408+
c := k8sutil.ArangodInitContainer("uuid", m.status.ID, engine, executable, operatorUUIDImage, requireUUID,
403409
m.groupSpec.SecurityContext.NewSecurityContext())
404410
initContainers = append(initContainers, c)
405411
}

pkg/operator/operator_deployment.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,12 @@ func (o *Operator) handleDeploymentEvent(event *Event) error {
204204
// makeDeploymentConfigAndDeps creates a Config & Dependencies object for a new Deployment.
205205
func (o *Operator) makeDeploymentConfigAndDeps(apiObject *api.ArangoDeployment) (deployment.Config, deployment.Dependencies) {
206206
cfg := deployment.Config{
207-
ServiceAccount: o.Config.ServiceAccount,
208-
LifecycleImage: o.Config.LifecycleImage,
209-
AlpineImage: o.Config.AlpineImage,
210-
MetricsExporterImage: o.MetricsExporterImage,
211-
ArangoImage: o.ArangoImage,
212-
AllowChaos: o.Config.AllowChaos,
207+
ServiceAccount: o.Config.ServiceAccount,
208+
LifecycleImage: o.Config.LifecycleImage,
209+
OperatorUUIDInitImage: o.Config.LifecycleImage,
210+
MetricsExporterImage: o.MetricsExporterImage,
211+
ArangoImage: o.ArangoImage,
212+
AllowChaos: o.Config.AllowChaos,
213213
}
214214
deps := deployment.Dependencies{
215215
Log: o.Dependencies.LogService.MustGetLogger("deployment").With().

pkg/util/k8sutil/pods.go

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
"crypto/sha256"
2727
"fmt"
2828
"path/filepath"
29-
"strings"
3029
"time"
3130

3231
"k8s.io/apimachinery/pkg/types"
@@ -270,31 +269,28 @@ func RocksdbEncryptionVolumeMount() core.VolumeMount {
270269
}
271270

272271
// ArangodInitContainer creates a container configured to initalize a UUID file.
273-
func ArangodInitContainer(name, id, engine, alpineImage string, requireUUID bool, securityContext *core.SecurityContext) core.Container {
272+
func ArangodInitContainer(name, id, engine, executable, operatorImage string, requireUUID bool, securityContext *core.SecurityContext) core.Container {
274273
uuidFile := filepath.Join(ArangodVolumeMountDir, "UUID")
275274
engineFile := filepath.Join(ArangodVolumeMountDir, "ENGINE")
276-
var command string
275+
var command []string = []string{
276+
executable,
277+
"uuid",
278+
"--uuid-path",
279+
uuidFile,
280+
"--engine-path",
281+
engineFile,
282+
"--uuid",
283+
id,
284+
"--engine",
285+
engine,
286+
}
277287
if requireUUID {
278-
command = strings.Join([]string{
279-
// Files must exist
280-
fmt.Sprintf("test -f %s", uuidFile),
281-
fmt.Sprintf("test -f %s", engineFile),
282-
// Content must match
283-
fmt.Sprintf("grep -q %s %s", id, uuidFile),
284-
fmt.Sprintf("grep -q %s %s", engine, engineFile),
285-
}, " && ")
286-
287-
} else {
288-
command = fmt.Sprintf("test -f %s || echo '%s' > %s", uuidFile, id, uuidFile)
288+
command = append(command, "--require")
289289
}
290290
c := core.Container{
291-
Name: name,
292-
Image: alpineImage,
293-
Command: []string{
294-
"/bin/sh",
295-
"-c",
296-
command,
297-
},
291+
Name: name,
292+
Image: operatorImage,
293+
Command: command,
298294
Resources: core.ResourceRequirements{
299295
Requests: core.ResourceList{
300296
core.ResourceCPU: resource.MustParse("100m"),

uuid.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2020 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+
// Author Adam Janikowski
21+
//
22+
23+
package main
24+
25+
import (
26+
"fmt"
27+
"io/ioutil"
28+
"os"
29+
"strings"
30+
31+
"github.com/pkg/errors"
32+
"github.com/rs/zerolog/log"
33+
"github.com/spf13/cobra"
34+
)
35+
36+
type cmdUUIDInputStruct struct {
37+
uuidPath, uuid, engine, enginePath string
38+
require bool
39+
}
40+
41+
var (
42+
cmdUUID = &cobra.Command{
43+
Use: "uuid",
44+
RunE: cmdUUIDSave,
45+
Hidden: true,
46+
}
47+
48+
cmdUUIDInput cmdUUIDInputStruct
49+
)
50+
51+
func init() {
52+
cmdMain.AddCommand(cmdUUID)
53+
f := cmdUUID.Flags()
54+
f.StringVar(&cmdUUIDInput.uuidPath, "uuid-path", "", "Path to the UUID file")
55+
f.StringVar(&cmdUUIDInput.engine, "engine", "", "Path to the ENGINE file")
56+
f.StringVar(&cmdUUIDInput.uuid, "uuid", "", "UUID of server")
57+
f.StringVar(&cmdUUIDInput.enginePath, "engine-path", "", "ENGINE of server")
58+
f.BoolVar(&cmdUUIDInput.require, "require", false, "Ensure that UUID and ENGINE file is in place")
59+
}
60+
61+
func cmdUUIDSave(cmd *cobra.Command, args []string) error {
62+
if cmdUUIDInput.uuidPath == "" {
63+
return errors.Errorf("Path cannot be empty")
64+
}
65+
66+
if cmdUUIDInput.require {
67+
if cmdUUIDInput.enginePath == "" {
68+
return errors.Errorf("Path to the ENGINE cannot be empty")
69+
}
70+
71+
if cmdUUIDInput.engine == "" {
72+
return errors.Errorf("ENGINE cannot be empty")
73+
}
74+
}
75+
76+
if cmdUUIDInput.uuid == "" {
77+
return errors.Errorf("UUID cannot be empty")
78+
}
79+
80+
log.Info().Msg("Saving UUID in file")
81+
82+
if exists, err := fileExists(cmdUUIDInput.uuidPath); err != nil {
83+
log.Error().Err(err).Msg("Unable to get file info")
84+
return err
85+
} else if !exists {
86+
if cmdUUIDInput.require {
87+
log.Warn().Msg("Init phase is not defined, but file does not exists")
88+
89+
if exists, err := fileExists(cmdUUIDInput.uuidPath); err != nil {
90+
log.Error().Err(err).Msg("Unable to get ENGINE info")
91+
return err
92+
} else if !exists {
93+
log.Info().Msg("ENGINE file does not exist - able to proceed")
94+
} else {
95+
log.Error().Msg("ENGINE file found but UUID is missing - will not proceed")
96+
return errors.Errorf("ENGINE file found but UUID is missing - will not proceed")
97+
}
98+
}
99+
100+
fileContent := fmt.Sprintf("%s\n", cmdUUIDInput.uuid)
101+
if err := ioutil.WriteFile(cmdUUIDInput.uuidPath, []byte(fileContent), 0644); err != nil {
102+
log.Error().Err(err).Msg("Unable to save UUID")
103+
return err
104+
}
105+
106+
log.Info().Msg("UUID saved")
107+
return nil
108+
}
109+
110+
if equal, content, err := checkFileContent(cmdUUIDInput.uuidPath, cmdUUIDInput.uuid); err != nil {
111+
log.Error().Err(err).Msg("Unable to get UUID info")
112+
return err
113+
} else if !equal {
114+
log.Error().Str("expected", cmdUUIDInput.uuid).Str("actual", content).Msg("UUID does not match expected")
115+
return errors.Errorf("UUID mismatch")
116+
} else {
117+
log.Info().Msg("UUID is valid")
118+
}
119+
120+
if cmdUUIDInput.require {
121+
if exists, err := fileExists(cmdUUIDInput.enginePath); err != nil {
122+
log.Error().Err(err).Msg("Unable to get ENGINE info")
123+
return err
124+
} else if exists {
125+
if equal, content, err := checkFileContent(cmdUUIDInput.enginePath, cmdUUIDInput.engine); err != nil {
126+
log.Error().Err(err).Msg("Unable to get ENGINE info")
127+
return err
128+
} else if !equal {
129+
log.Error().Str("expected", cmdUUIDInput.engine).Str("actual", content).Msg("ENGINE does not match expected")
130+
return errors.Errorf("ENGINE mismatch")
131+
} else {
132+
log.Info().Msg("ENGINE is valid")
133+
}
134+
} else {
135+
log.Info().Msg("ENGINE file is missing, but UUID is in place - we can proceed")
136+
}
137+
}
138+
139+
log.Info().Msg("Init Phase is valid")
140+
return nil
141+
}
142+
143+
func fileExists(path string) (bool, error) {
144+
if _, err := os.Stat(path); err != nil {
145+
if os.IsNotExist(err) {
146+
return false, nil
147+
}
148+
149+
return false, err
150+
}
151+
152+
return true, nil
153+
}
154+
155+
func checkFileContent(path, expected string) (bool, string, error) {
156+
content, err := ioutil.ReadFile(path)
157+
if err != nil {
158+
return false, "", err
159+
}
160+
161+
contentString := strings.TrimSuffix(string(content), "\n")
162+
163+
return contentString == expected, contentString, nil
164+
}

0 commit comments

Comments
 (0)