Skip to content

Commit d8c9f5a

Browse files
authored
[Feature] Add customizable volumes (#533)
1 parent 2608863 commit d8c9f5a

25 files changed

+2978
-1210
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Change Log
22

33
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
4+
- Added Customizable Volumes and VolumeMounts for ArangoDB server container
5+
- Added MemoryOverride flag for ArangoDB >= 3.6.3
6+
- Improved Rotation discovery process
47
- Added annotation to rotate ArangoDeployment in secure way
58

69
## [1.0.0](https://github.com/arangodb/kube-arangodb/tree/1.0.0) (2020-03-03)

Makefile

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,11 @@ linter: fmt
177177
$(SOURCES_PACKAGES)
178178

179179
.PHONY: build
180-
build: docker docker-ubi manifests
180+
build: docker manifests
181+
182+
ifndef IGNORE_UBI
183+
build: docker-ubi
184+
endif
181185

182186
.PHONY: clean
183187
clean:
@@ -204,12 +208,19 @@ update-generated:
204208
@mkdir -p $(ORGDIR)
205209
@ln -s -f $(SCRIPTDIR) $(ORGDIR)/kube-arangodb
206210
GOPATH=$(GOBUILDDIR) $(VENDORDIR)/k8s.io/code-generator/generate-groups.sh \
207-
"all" \
208-
"github.com/arangodb/kube-arangodb/pkg/generated" \
209-
"github.com/arangodb/kube-arangodb/pkg/apis" \
210-
"deployment:v1 replication:v1 storage:v1alpha backup:v1" \
211-
--go-header-file "./tools/codegen/boilerplate.go.txt" \
212-
$(VERIFYARGS)
211+
"all" \
212+
"github.com/arangodb/kube-arangodb/pkg/generated" \
213+
"github.com/arangodb/kube-arangodb/pkg/apis" \
214+
"deployment:v1 replication:v1 storage:v1alpha backup:v1" \
215+
--go-header-file "./tools/codegen/boilerplate.go.txt" \
216+
$(VERIFYARGS)
217+
GOPATH=$(GOBUILDDIR) $(VENDORDIR)/k8s.io/code-generator/generate-groups.sh \
218+
"deepcopy" \
219+
"github.com/arangodb/kube-arangodb/pkg/generated" \
220+
"github.com/arangodb/kube-arangodb/pkg/apis" \
221+
"shared:v1" \
222+
--go-header-file "./tools/codegen/boilerplate.go.txt" \
223+
$(VERIFYARGS)
213224

214225
.PHONY: verify-generated
215226
verify-generated:
@@ -226,6 +237,9 @@ dashboard/assets.go: $(DASHBOARDSOURCES) $(DASHBOARDDIR)/Dockerfile.build
226237
$(DASHBOARDBUILDIMAGE)
227238
go run github.com/jessevdk/go-assets-builder -s /dashboard/build/ -o dashboard/assets.go -p dashboard dashboard/build
228239

240+
.PHONY: bin
241+
bin: $(BIN)
242+
229243
$(BIN): $(SOURCES) dashboard/assets.go VERSION
230244
@mkdir -p $(BINDIR)
231245
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -installsuffix netgo -ldflags "-X main.projectVersion=$(VERSION) -X main.projectBuild=$(COMMIT)" -o $(BIN) $(REPOPATH)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ require (
4444
golang.org/x/sys v0.0.0-20200116001909-b77594299b42
4545
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
4646
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
47+
gopkg.in/yaml.v2 v2.2.8
4748
k8s.io/api v0.17.3
4849
k8s.io/apiextensions-apiserver v0.17.3
4950
k8s.io/apimachinery v0.17.3

pkg/apis/deployment/v1/deployment.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ type ArangoDeploymentList struct {
4545
type ArangoDeployment struct {
4646
metav1.TypeMeta `json:",inline"`
4747
metav1.ObjectMeta `json:"metadata,omitempty"`
48-
Spec DeploymentSpec `json:"spec"`
49-
Status DeploymentStatus `json:"status"`
48+
Spec DeploymentSpec `json:"spec,omitempty"`
49+
Status DeploymentStatus `json:"status,omitempty"`
5050
}
5151

5252
type ServerGroupFunc func(ServerGroup, ServerGroupSpec, *MemberStatusList) error

pkg/apis/deployment/v1/deployment_status.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import (
2929
// DeploymentStatus contains the status part of a Cluster resource.
3030
type DeploymentStatus struct {
3131
// Phase holds the current lifetime phase of the deployment
32-
Phase DeploymentPhase `json:"phase"`
32+
Phase DeploymentPhase `json:"phase,omitempty"`
3333
// Reason contains a human readable reason for reaching the current state (can be empty)
3434
Reason string `json:"reason,omitempty"` // Reason for current state
3535

pkg/apis/deployment/v1/server_group_spec.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import (
2626
"math"
2727
"strings"
2828

29+
"github.com/arangodb/kube-arangodb/pkg/apis/shared"
30+
2931
"github.com/pkg/errors"
3032
v1 "k8s.io/api/core/v1"
3133
"k8s.io/apimachinery/pkg/api/resource"
@@ -72,6 +74,10 @@ type ServerGroupSpec struct {
7274
Sidecars []v1.Container `json:"sidecars,omitempty"`
7375
// SecurityContext specifies security context for group
7476
SecurityContext *ServerGroupSpecSecurityContext `json:"securityContext,omitempty"`
77+
// Volumes define list of volumes mounted to pod
78+
Volumes ServerGroupSpecVolumes `json:"volumes,omitempty"`
79+
// VolumeMounts define list of volume mounts mounted into server container
80+
VolumeMounts ServerGroupSpecVolumeMounts `json:"volumeMounts,omitempty"`
7581
}
7682

7783
// ServerGroupSpecSecurityContext contains specification for pod security context
@@ -372,12 +378,44 @@ func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentM
372378
}
373379
}
374380
}
381+
382+
if err := s.validate(); err != nil {
383+
return maskAny(err)
384+
}
375385
} else if s.GetCount() != 0 {
376386
return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d for un-used group. Expected 0", s.GetCount()))
377387
}
378388
return nil
379389
}
380390

391+
func (s *ServerGroupSpec) validate() error {
392+
if s == nil {
393+
return nil
394+
}
395+
396+
return shared.WithErrors(
397+
shared.PrefixResourceError("volumes", s.Volumes.Validate()),
398+
shared.PrefixResourceError("volumeMounts", s.VolumeMounts.Validate()),
399+
s.validateVolumes(),
400+
)
401+
}
402+
403+
func (s *ServerGroupSpec) validateVolumes() error {
404+
volumes := map[string]bool{}
405+
406+
for _, volume := range s.Volumes {
407+
volumes[volume.Name] = true
408+
}
409+
410+
for _, mount := range s.VolumeMounts {
411+
if _, ok := volumes[mount.Name]; !ok {
412+
return errors.Errorf("Volume %s is not defined, but required by mount", mount.Name)
413+
}
414+
}
415+
416+
return nil
417+
}
418+
381419
// SetDefaults fills in missing defaults
382420
func (s *ServerGroupSpec) SetDefaults(group ServerGroup, used bool, mode DeploymentMode) {
383421
if s.GetCount() == 0 && used {
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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 v1
24+
25+
import (
26+
"fmt"
27+
28+
"github.com/arangodb/kube-arangodb/pkg/apis/shared"
29+
sharedv1 "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1"
30+
31+
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
32+
"github.com/pkg/errors"
33+
core "k8s.io/api/core/v1"
34+
)
35+
36+
var (
37+
restrictedVolumeNames = []string{
38+
k8sutil.ArangodVolumeName,
39+
k8sutil.TlsKeyfileVolumeName,
40+
k8sutil.RocksdbEncryptionVolumeName,
41+
k8sutil.ExporterJWTVolumeName,
42+
k8sutil.ClusterJWTSecretVolumeName,
43+
"lifecycle",
44+
}
45+
)
46+
47+
// IsRestrictedVolumeName check of volume name is restricted, for example for originally mounted volumes
48+
func IsRestrictedVolumeName(name string) bool {
49+
for _, restrictedVolumeName := range restrictedVolumeNames {
50+
if restrictedVolumeName == name {
51+
return true
52+
}
53+
}
54+
55+
return false
56+
}
57+
58+
// ServerGroupSpecVolumes definition of volume list which need to be mounted to Pod
59+
type ServerGroupSpecVolumes []ServerGroupSpecVolume
60+
61+
// Validate if ServerGroupSpec volumes are valid and does not collide
62+
func (s ServerGroupSpecVolumes) Validate() error {
63+
var validationErrors []error
64+
65+
mappedVolumes := map[string]int{}
66+
67+
for id, volume := range s {
68+
if i, ok := mappedVolumes[volume.Name]; ok {
69+
mappedVolumes[volume.Name] = i + 1
70+
} else {
71+
mappedVolumes[volume.Name] = 1
72+
}
73+
74+
if err := volume.Validate(); err != nil {
75+
validationErrors = append(validationErrors, shared.PrefixResourceErrors(fmt.Sprintf("%d", id), err))
76+
}
77+
}
78+
79+
for volumeName, count := range mappedVolumes {
80+
if IsRestrictedVolumeName(volumeName) {
81+
validationErrors = append(validationErrors, errors.Errorf("volume with name %s is restricted", volumeName))
82+
}
83+
84+
if count == 1 {
85+
continue
86+
}
87+
88+
validationErrors = append(validationErrors, errors.Errorf("volume with name %s defined more than once: %d", volumeName, count))
89+
}
90+
91+
return shared.WithErrors(validationErrors...)
92+
}
93+
94+
// Volumes create volumes
95+
func (s ServerGroupSpecVolumes) Volumes() []core.Volume {
96+
volumes := make([]core.Volume, len(s))
97+
98+
for id, volume := range s {
99+
volumes[id] = volume.Volume()
100+
}
101+
102+
return volumes
103+
}
104+
105+
// ServerGroupSpecVolume definition of volume which need to be mounted to Pod
106+
type ServerGroupSpecVolume struct {
107+
// Name of volume
108+
Name string `json:"name"`
109+
110+
// Secret which should be mounted into pod
111+
Secret *ServerGroupSpecVolumeSecret `json:"secret,omitempty"`
112+
113+
// ConfigMap which should be mounted into pod
114+
ConfigMap *ServerGroupSpecVolumeConfigMap `json:"configMap,omitempty"`
115+
}
116+
117+
// Validate if ServerGroupSpec volume is valid
118+
func (s *ServerGroupSpecVolume) Validate() error {
119+
if s == nil {
120+
return nil
121+
}
122+
123+
return shared.WithErrors(
124+
shared.PrefixResourceErrors("name", sharedv1.AsKubernetesResourceName(&s.Name).Validate()),
125+
shared.PrefixResourceErrors("secret", s.Secret.Validate()),
126+
shared.PrefixResourceErrors("configMap", s.ConfigMap.Validate()),
127+
s.validate(),
128+
)
129+
}
130+
131+
// Volume create Pod Volume object
132+
func (s ServerGroupSpecVolume) Volume() core.Volume {
133+
return core.Volume{
134+
Name: s.Name,
135+
VolumeSource: core.VolumeSource{
136+
ConfigMap: (*core.ConfigMapVolumeSource)(s.ConfigMap),
137+
Secret: (*core.SecretVolumeSource)(s.Secret),
138+
},
139+
}
140+
}
141+
142+
func (s *ServerGroupSpecVolume) validate() error {
143+
if s.ConfigMap == nil && s.Secret == nil {
144+
return errors.Errorf("at least one option need to be defined: secret or configMap")
145+
}
146+
147+
if s.ConfigMap != nil && s.Secret != nil {
148+
return errors.Errorf("only one option can be defined: secret or configMap")
149+
}
150+
151+
return nil
152+
}
153+
154+
type ServerGroupSpecVolumeSecret core.SecretVolumeSource
155+
156+
func (s *ServerGroupSpecVolumeSecret) Validate() error {
157+
if s == nil {
158+
return nil
159+
}
160+
161+
return shared.WithErrors(
162+
shared.PrefixResourceError("secretName", sharedv1.AsKubernetesResourceName(&s.SecretName).Validate()),
163+
)
164+
}
165+
166+
type ServerGroupSpecVolumeConfigMap core.ConfigMapVolumeSource
167+
168+
func (s *ServerGroupSpecVolumeConfigMap) Validate() error {
169+
if s == nil {
170+
return nil
171+
}
172+
173+
return shared.WithErrors(
174+
shared.PrefixResourceError("name", sharedv1.AsKubernetesResourceName(&s.Name).Validate()),
175+
)
176+
}

0 commit comments

Comments
 (0)