Skip to content

Commit 078fe09

Browse files
committed
Merged in master
2 parents 93ff3e6 + 91c65dc commit 078fe09

23 files changed

+644
-119
lines changed

docs/design/testing.md

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Testing
2+
3+
## Scenario's
4+
5+
The following test scenario's must be covered by automated tests:
6+
7+
- Creating 1 deployment (all modes, all environments, all storage engines)
8+
- Creating multiple deployments (all modes, all environments, all storage engines),
9+
controlling each individually
10+
- Creating deployment with/without authentication
11+
- Creating deployment with/without TLS
12+
13+
- Updating deployment wrt:
14+
- Number of servers (scaling, up/down)
15+
- Image version (upgrading, downgrading within same minor version range (e.g. 3.2.x))
16+
- Immutable fields (should be reset automatically)
17+
18+
- Resilience:
19+
- Delete individual pods
20+
- Delete individual PVCs
21+
- Delete individual Services
22+
- Delete Node
23+
- Restart Node
24+
- API server unavailable
25+
26+
## Test environments
27+
28+
- Kubernetes clusters
29+
- Single node
30+
- Multi node
31+
- Access control mode (RBAC, ...)
32+
- Persistent volumes ...

docs/user/custom_resource.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ an encryption key that is exactly 32 bytes long.
126126
This setting specifies the name of a kubernetes `Secret` that contains
127127
the JWT token used for accessing all ArangoDB servers.
128128
When no name is specified, it defaults to `<deployment-name>-jwt`.
129-
To disable authentication, set this value to `-`.
129+
To disable authentication, set this value to `None`.
130130

131131
If you specify a name of a `Secret`, that secret must have the token
132132
in a data field named `token`.

examples/setup-rbac.sh

+37-32
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
#!/bin/bash
22

3+
ROLE_NAME="${ROLE_NAME:-arangodb-operator}"
4+
ROLE_BINDING_NAME="${ROLE_BINDING_NAME:-arangodb-operator}"
5+
NAMESPACE="${NAMESPACE:-default}"
6+
37
function usage {
4-
echo "$(basename "$0") - Create Kubernetes RBAC role and bindings for ArangoDB operator
8+
echo "$(basename "$0") - Create Kubernetes RBAC role and bindings for ArangoDB operator
59
Usage: $(basename "$0") [options...]
610
Options:
711
--role-name=STRING Name of ClusterRole to create
@@ -13,12 +17,8 @@ Options:
1317
" >&2
1418
}
1519

16-
ROLE_NAME="${ROLE_NAME:-arangodb-operator}"
17-
ROLE_BINDING_NAME="${ROLE_BINDING_NAME:-arangodb-operator}"
18-
NAMESPACE="${NAMESPACE:-default}"
19-
2020
function setupRole {
21-
yaml=$(cat << EOYAML
21+
kubectl apply -f - << EOYAML
2222
apiVersion: rbac.authorization.k8s.io/v1beta1
2323
kind: ClusterRole
2424
metadata:
@@ -54,12 +54,15 @@ rules:
5454
verbs:
5555
- "*"
5656
EOYAML
57-
)
58-
echo "$yaml" | kubectl apply -f -
57+
58+
local code=$?
59+
if (code != 0); then
60+
exit $code
61+
fi
5962
}
6063

6164
function setupRoleBinding {
62-
yaml=$(cat << EOYAML
65+
kubectl apply -f - << EOYAML
6366
apiVersion: rbac.authorization.k8s.io/v1beta1
6467
kind: ClusterRoleBinding
6568
metadata:
@@ -73,31 +76,33 @@ subjects:
7376
name: default
7477
namespace: ${NAMESPACE}
7578
EOYAML
76-
)
77-
echo "$yaml" | kubectl apply -f -
79+
80+
local code=$?
81+
if (code != 0); then
82+
exit $code
83+
fi
7884
}
7985

80-
for i in "$@"
81-
do
82-
case $i in
83-
--role-name=*)
84-
ROLE_NAME="${i#*=}"
85-
;;
86-
--role-binding-name=*)
87-
ROLE_BINDING_NAME="${i#*=}"
88-
;;
89-
--namespace=*)
90-
NAMESPACE="${i#*=}"
91-
;;
92-
-h|--help)
93-
usage
94-
exit 0
95-
;;
96-
*)
97-
usage
98-
exit 1
99-
;;
100-
esac
86+
for i in "$@"; do
87+
case $i in
88+
--role-name=*)
89+
ROLE_NAME="${i#*=}"
90+
;;
91+
--role-binding-name=*)
92+
ROLE_BINDING_NAME="${i#*=}"
93+
;;
94+
--namespace=*)
95+
NAMESPACE="${i#*=}"
96+
;;
97+
-h|--help)
98+
usage
99+
exit 0
100+
;;
101+
*)
102+
usage
103+
exit 1
104+
;;
105+
esac
101106
done
102107

103108
setupRole

examples/single-server-no-auth.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: "database.arangodb.com/v1alpha"
2+
kind: "ArangoDeployment"
3+
metadata:
4+
name: "example-simple-single-no-auth"
5+
spec:
6+
mode: single
7+
auth:
8+
jwtSecretName: None
9+

pkg/apis/arangodb/v1alpha/deployment_spec.go

+39-27
Original file line numberDiff line numberDiff line change
@@ -179,23 +179,35 @@ type AuthenticationSpec struct {
179179
JWTSecretName string `json:"jwtSecretName,omitempty"`
180180
}
181181

182+
const (
183+
// JWTSecretNameDisabled is the value of JWTSecretName to use for disabling authentication.
184+
JWTSecretNameDisabled = "None"
185+
)
186+
187+
// IsAuthenticated returns true if authentication is enabled.
188+
// Returns false other (when JWTSecretName == "None").
189+
func (s AuthenticationSpec) IsAuthenticated() bool {
190+
return s.JWTSecretName != JWTSecretNameDisabled
191+
}
192+
182193
// Validate the given spec
183-
func (s AuthenticationSpec) Validate(required, allowed bool) error {
184-
if required && s.JWTSecretName == "" {
185-
return maskAny(errors.Wrap(ValidationError, "Missing JWT secret"))
194+
func (s AuthenticationSpec) Validate(required bool) error {
195+
if required && !s.IsAuthenticated() {
196+
return maskAny(errors.Wrap(ValidationError, "JWT secret is required"))
186197
}
187-
if !allowed && s.JWTSecretName != "" {
188-
return maskAny(errors.Wrap(ValidationError, "Non-empty JWT secret name is not allowed"))
189-
}
190-
if err := k8sutil.ValidateOptionalResourceName(s.JWTSecretName); err != nil {
191-
return maskAny(err)
198+
if s.IsAuthenticated() {
199+
if err := k8sutil.ValidateResourceName(s.JWTSecretName); err != nil {
200+
return maskAny(err)
201+
}
192202
}
193203
return nil
194204
}
195205

196206
// SetDefaults fills in missing defaults
197-
func (s *AuthenticationSpec) SetDefaults() {
198-
// Nothing needed
207+
func (s *AuthenticationSpec) SetDefaults(defaultJWTSecretName string) {
208+
if s.JWTSecretName == "" {
209+
s.JWTSecretName = defaultJWTSecretName
210+
}
199211
}
200212

201213
// SSLSpec holds SSL specific configuration settings
@@ -256,7 +268,7 @@ func (s SyncSpec) Validate(mode DeploymentMode) error {
256268
if s.Image == "" {
257269
return maskAny(errors.Wrapf(ValidationError, "image must be set"))
258270
}
259-
if err := s.Authentication.Validate(s.Enabled, s.Enabled); err != nil {
271+
if err := s.Authentication.Validate(s.Enabled); err != nil {
260272
return maskAny(err)
261273
}
262274
if err := s.Monitoring.Validate(); err != nil {
@@ -266,14 +278,14 @@ func (s SyncSpec) Validate(mode DeploymentMode) error {
266278
}
267279

268280
// SetDefaults fills in missing defaults
269-
func (s *SyncSpec) SetDefaults(defaultImage string, defaulPullPolicy v1.PullPolicy) {
281+
func (s *SyncSpec) SetDefaults(defaultImage string, defaulPullPolicy v1.PullPolicy, defaultJWTSecretName string) {
270282
if s.Image == "" {
271283
s.Image = defaultImage
272284
}
273285
if s.ImagePullPolicy == "" {
274286
s.ImagePullPolicy = defaulPullPolicy
275287
}
276-
s.Authentication.SetDefaults()
288+
s.Authentication.SetDefaults(defaultJWTSecretName)
277289
s.Monitoring.SetDefaults()
278290
}
279291

@@ -420,7 +432,7 @@ type DeploymentSpec struct {
420432

421433
// IsAuthenticated returns true when authentication is enabled
422434
func (s DeploymentSpec) IsAuthenticated() bool {
423-
return s.Authentication.JWTSecretName != ""
435+
return s.Authentication.IsAuthenticated()
424436
}
425437

426438
// IsSecure returns true when SSL is enabled
@@ -429,7 +441,7 @@ func (s DeploymentSpec) IsSecure() bool {
429441
}
430442

431443
// SetDefaults fills in default values when a field is not specified.
432-
func (s *DeploymentSpec) SetDefaults() {
444+
func (s *DeploymentSpec) SetDefaults(deploymentName string) {
433445
if s.Mode == "" {
434446
s.Mode = DeploymentModeCluster
435447
}
@@ -446,9 +458,9 @@ func (s *DeploymentSpec) SetDefaults() {
446458
s.ImagePullPolicy = v1.PullIfNotPresent
447459
}
448460
s.RocksDB.SetDefaults()
449-
s.Authentication.SetDefaults()
461+
s.Authentication.SetDefaults(deploymentName + "-jwt")
450462
s.SSL.SetDefaults()
451-
s.Sync.SetDefaults(s.Image, s.ImagePullPolicy)
463+
s.Sync.SetDefaults(s.Image, s.ImagePullPolicy, deploymentName+"-sync-jwt")
452464
s.Single.SetDefaults(ServerGroupSingle, s.Mode.HasSingleServers(), s.Mode)
453465
s.Agents.SetDefaults(ServerGroupAgents, s.Mode.HasAgents(), s.Mode)
454466
s.DBServers.SetDefaults(ServerGroupDBServers, s.Mode.HasDBServers(), s.Mode)
@@ -461,31 +473,31 @@ func (s *DeploymentSpec) SetDefaults() {
461473
// Return errors when validation fails, nil on success.
462474
func (s *DeploymentSpec) Validate() error {
463475
if err := s.Mode.Validate(); err != nil {
464-
return maskAny(err)
476+
return maskAny(errors.Wrap(err, "spec.mode"))
465477
}
466478
if err := s.Environment.Validate(); err != nil {
467-
return maskAny(err)
479+
return maskAny(errors.Wrap(err, "spec.environment"))
468480
}
469481
if err := s.StorageEngine.Validate(); err != nil {
470-
return maskAny(err)
482+
return maskAny(errors.Wrap(err, "spec.storageEngine"))
471483
}
472484
if err := validatePullPolicy(s.ImagePullPolicy); err != nil {
473-
return maskAny(err)
485+
return maskAny(errors.Wrap(err, "spec.imagePullPolicy"))
474486
}
475487
if s.Image == "" {
476-
return maskAny(errors.Wrapf(ValidationError, "image must be set"))
488+
return maskAny(errors.Wrapf(ValidationError, "spec.image must be set"))
477489
}
478490
if err := s.RocksDB.Validate(); err != nil {
479-
return maskAny(err)
491+
return maskAny(errors.Wrap(err, "spec.rocksdb"))
480492
}
481-
if err := s.Authentication.Validate(false, true); err != nil {
482-
return maskAny(err)
493+
if err := s.Authentication.Validate(false); err != nil {
494+
return maskAny(errors.Wrap(err, "spec.auth"))
483495
}
484496
if err := s.SSL.Validate(); err != nil {
485-
return maskAny(err)
497+
return maskAny(errors.Wrap(err, "spec.ssl"))
486498
}
487499
if err := s.Sync.Validate(s.Mode); err != nil {
488-
return maskAny(err)
500+
return maskAny(errors.Wrap(err, "spec.sync"))
489501
}
490502
if err := s.Single.Validate(ServerGroupSingle, s.Mode.HasSingleServers(), s.Mode); err != nil {
491503
return maskAny(err)

pkg/controller/controller.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ func (c *Controller) handleDeploymentEvent(event *Event) error {
217217
}
218218

219219
// Fill in defaults
220-
apiObject.Spec.SetDefaults()
220+
apiObject.Spec.SetDefaults(apiObject.GetName())
221221
// Validate deployment spec
222222
if err := apiObject.Spec.Validate(); err != nil {
223223
return maskAny(errors.Wrapf(err, "invalid deployment spec. please fix the following problem with the deployment spec: %v", err))

pkg/deployment/client_cache.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
package deployment
2424

2525
import (
26+
"context"
2627
"fmt"
2728
"sync"
2829

@@ -52,7 +53,7 @@ func newClientCache(kubecli kubernetes.Interface, apiObject *api.ArangoDeploymen
5253

5354
// Get a cached client for the given ID in the given group, creating one
5455
// if needed.
55-
func (cc *clientCache) Get(group api.ServerGroup, id string) (driver.Client, error) {
56+
func (cc *clientCache) Get(ctx context.Context, group api.ServerGroup, id string) (driver.Client, error) {
5657
cc.mutex.Lock()
5758
defer cc.mutex.Unlock()
5859

@@ -63,7 +64,7 @@ func (cc *clientCache) Get(group api.ServerGroup, id string) (driver.Client, err
6364
}
6465

6566
// Not found, create a new client
66-
c, err := arangod.CreateArangodClient(cc.kubecli, cc.apiObject, group, id)
67+
c, err := arangod.CreateArangodClient(ctx, cc.kubecli, cc.apiObject, group, id)
6768
if err != nil {
6869
return nil, maskAny(err)
6970
}
@@ -73,7 +74,7 @@ func (cc *clientCache) Get(group api.ServerGroup, id string) (driver.Client, err
7374

7475
// GetDatabase returns a cached client for the entire database (cluster coordinators or single server),
7576
// creating one if needed.
76-
func (cc *clientCache) GetDatabase() (driver.Client, error) {
77+
func (cc *clientCache) GetDatabase(ctx context.Context) (driver.Client, error) {
7778
cc.mutex.Lock()
7879
defer cc.mutex.Unlock()
7980

@@ -82,7 +83,7 @@ func (cc *clientCache) GetDatabase() (driver.Client, error) {
8283
}
8384

8485
// Not found, create a new client
85-
c, err := arangod.CreateArangodDatabaseClient(cc.kubecli, cc.apiObject)
86+
c, err := arangod.CreateArangodDatabaseClient(ctx, cc.kubecli, cc.apiObject)
8687
if err != nil {
8788
return nil, maskAny(err)
8889
}

pkg/deployment/deployment.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ func (d *Deployment) run() {
153153
log := d.deps.Log
154154

155155
if d.status.State == api.DeploymentStateNone {
156+
// Create secrets
157+
if err := d.createSecrets(d.apiObject); err != nil {
158+
d.failOnError(err, "Failed to create secrets")
159+
return
160+
}
161+
156162
// Create services
157163
if err := d.createServices(d.apiObject); err != nil {
158164
d.failOnError(err, "Failed to create services")
@@ -272,7 +278,7 @@ func (d *Deployment) handleArangoDeploymentUpdatedEvent(event *deploymentEvent)
272278
}
273279

274280
newAPIObject := current.DeepCopy()
275-
newAPIObject.Spec.SetDefaults()
281+
newAPIObject.Spec.SetDefaults(newAPIObject.GetName())
276282
newAPIObject.Status = d.status
277283
resetFields := d.apiObject.Spec.ResetImmutableFields(&newAPIObject.Spec)
278284
if len(resetFields) > 0 {

0 commit comments

Comments
 (0)