Skip to content

Commit 4a2f34b

Browse files
authored
Merge pull request #152 from arangodb/feature/service-account
Allow customization of serviceAccountName for pods
2 parents 659c5c9 + 1092164 commit 4a2f34b

File tree

9 files changed

+367
-13
lines changed

9 files changed

+367
-13
lines changed

docs/Manual/Deployment/Kubernetes/DeploymentResource.md

+8
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,14 @@ The default value is `8Gi`.
348348
This setting is not available for group `coordinators`, `syncmasters` & `syncworkers`
349349
because servers in these groups do not need persistent storage.
350350

351+
### `spec.<group>.serviceAccountName: string`
352+
353+
This setting specifies the `serviceAccountName` for the `Pods` created
354+
for each server of this group.
355+
356+
Using an alternative `ServiceAccount` is typically used to separate access rights.
357+
The ArangoDB deployments do not require any special rights.
358+
351359
### `spec.<group>.storageClassName: string`
352360

353361
This setting specifies the `storageClass` for the `PersistentVolume`s created

manifests/templates/test/rbac.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ rules:
1010
resources: ["nodes"]
1111
verbs: ["list"]
1212
- apiGroups: [""]
13-
resources: ["pods", "services", "persistentvolumes", "persistentvolumeclaims", "secrets"]
13+
resources: ["pods", "services", "persistentvolumes", "persistentvolumeclaims", "secrets", "serviceaccounts"]
1414
verbs: ["*"]
1515
- apiGroups: ["apps"]
1616
resources: ["daemonsets"]

pkg/apis/deployment/v1alpha/server_group_spec.go

+21
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"k8s.io/apimachinery/pkg/api/resource"
2929

3030
"github.com/arangodb/kube-arangodb/pkg/util"
31+
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
3132
)
3233

3334
// ServerGroupSpec contains the specification for all servers in a specific group (e.g. all agents)
@@ -42,6 +43,8 @@ type ServerGroupSpec struct {
4243
Resources v1.ResourceRequirements `json:"resources,omitempty"`
4344
// Tolerations specifies the tolerations added to Pods in this group.
4445
Tolerations []v1.Toleration `json:"tolerations,omitempty"`
46+
// ServiceAccountName specifies the name of the service account used for Pods in this group.
47+
ServiceAccountName *string `json:"serviceAccountName,omitempty"`
4548
}
4649

4750
// GetCount returns the value of count.
@@ -64,6 +67,11 @@ func (s ServerGroupSpec) GetTolerations() []v1.Toleration {
6467
return s.Tolerations
6568
}
6669

70+
// GetServiceAccountName returns the value of serviceAccountName.
71+
func (s ServerGroupSpec) GetServiceAccountName() string {
72+
return util.StringOrDefault(s.ServiceAccountName)
73+
}
74+
6775
// Validate the given group spec
6876
func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentMode, env Environment) error {
6977
if used {
@@ -86,6 +94,16 @@ func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentM
8694
if s.GetCount() > 1 && group == ServerGroupSingle && mode == DeploymentModeSingle {
8795
return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected 1", s.GetCount()))
8896
}
97+
if name := s.GetServiceAccountName(); name != "" {
98+
if err := k8sutil.ValidateOptionalResourceName(name); err != nil {
99+
return maskAny(errors.Wrapf(ValidationError, "Invalid serviceAccountName: %s", err))
100+
}
101+
}
102+
if name := s.GetStorageClassName(); name != "" {
103+
if err := k8sutil.ValidateOptionalResourceName(name); err != nil {
104+
return maskAny(errors.Wrapf(ValidationError, "Invalid storageClassName: %s", err))
105+
}
106+
}
89107
} else if s.GetCount() != 0 {
90108
return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d for un-used group. Expected 0", s.GetCount()))
91109
}
@@ -145,6 +163,9 @@ func (s *ServerGroupSpec) SetDefaultsFrom(source ServerGroupSpec) {
145163
if s.Tolerations == nil {
146164
s.Tolerations = source.Tolerations
147165
}
166+
if s.ServiceAccountName == nil {
167+
s.ServiceAccountName = util.NewStringOrNil(source.ServiceAccountName)
168+
}
148169
setDefaultsFromResourceList(&s.Resources.Limits, source.Resources.Limits)
149170
setDefaultsFromResourceList(&s.Resources.Requests, source.Resources.Requests)
150171
}

pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go

+9
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,15 @@ func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) {
597597
(*in)[i].DeepCopyInto(&(*out)[i])
598598
}
599599
}
600+
if in.ServiceAccountName != nil {
601+
in, out := &in.ServiceAccountName, &out.ServiceAccountName
602+
if *in == nil {
603+
*out = nil
604+
} else {
605+
*out = new(string)
606+
**out = **in
607+
}
608+
}
600609
return
601610
}
602611

pkg/deployment/images.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,10 @@ func (ib *imagesBuilder) fetchArangoDBImageIDAndVersion(ctx context.Context, ima
176176
tolerations = k8sutil.AddTolerationIfNotFound(tolerations, k8sutil.NewNoExecuteToleration(k8sutil.TolerationKeyNodeNotReady, shortDur))
177177
tolerations = k8sutil.AddTolerationIfNotFound(tolerations, k8sutil.NewNoExecuteToleration(k8sutil.TolerationKeyNodeUnreachable, shortDur))
178178
tolerations = k8sutil.AddTolerationIfNotFound(tolerations, k8sutil.NewNoExecuteToleration(k8sutil.TolerationKeyNodeAlphaUnreachable, shortDur))
179+
serviceAccountName := ""
179180

180-
if err := k8sutil.CreateArangodPod(ib.KubeCli, true, ib.APIObject, role, id, podName, "", image, "", ib.Spec.GetImagePullPolicy(), "", false, terminationGracePeriod, args, nil, nil, nil, nil, tolerations, "", ""); err != nil {
181+
if err := k8sutil.CreateArangodPod(ib.KubeCli, true, ib.APIObject, role, id, podName, "", image, "", ib.Spec.GetImagePullPolicy(), "", false, terminationGracePeriod, args, nil, nil, nil, nil,
182+
tolerations, serviceAccountName, "", ""); err != nil {
181183
log.Debug().Err(err).Msg("Failed to create image ID pod")
182184
return true, maskAny(err)
183185
}

pkg/deployment/reconcile/plan_builder.go

+8
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ func podNeedsUpgrading(p v1.Pod, spec api.DeploymentSpec, images api.ImageInfoLi
266266
// When true is returned, a reason for the rotation is already returned.
267267
func podNeedsRotation(p v1.Pod, apiObject metav1.Object, spec api.DeploymentSpec,
268268
group api.ServerGroup, agents api.MemberStatusList, id string) (bool, string) {
269+
groupSpec := spec.GetServerGroupSpec(group)
270+
269271
// Check image pull policy
270272
if c, found := k8sutil.GetContainerByName(&p, k8sutil.ServerContainerName); found {
271273
if c.ImagePullPolicy != spec.GetImagePullPolicy() {
@@ -274,6 +276,7 @@ func podNeedsRotation(p v1.Pod, apiObject metav1.Object, spec api.DeploymentSpec
274276
} else {
275277
return true, "Server container not found"
276278
}
279+
277280
// Check arguments
278281
/*expectedArgs := createArangodArgs(apiObject, spec, group, agents, id)
279282
if len(expectedArgs) != len(c.Args) {
@@ -285,6 +288,11 @@ func podNeedsRotation(p v1.Pod, apiObject metav1.Object, spec api.DeploymentSpec
285288
}
286289
}*/
287290

291+
// Check service account
292+
if p.Spec.ServiceAccountName != groupSpec.GetServiceAccountName() {
293+
return true, "ServiceAccountName changed"
294+
}
295+
288296
return false, ""
289297
}
290298

pkg/deployment/resources/pod_creator.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ func (r *Resources) createPodForMember(spec api.DeploymentSpec, group api.Server
438438
lifecycleImage := r.context.GetLifecycleImage()
439439
terminationGracePeriod := group.DefaultTerminationGracePeriod()
440440
tolerations := r.createPodTolerations(group, groupSpec)
441+
serviceAccountName := groupSpec.GetServiceAccountName()
441442

442443
// Update pod name
443444
role := group.AsRole()
@@ -500,7 +501,7 @@ func (r *Resources) createPodForMember(spec api.DeploymentSpec, group api.Server
500501
requireUUID := group == api.ServerGroupDBServers && m.IsInitialized
501502
finalizers := r.createPodFinalizers(group)
502503
if err := k8sutil.CreateArangodPod(kubecli, spec.IsDevelopment(), apiObject, role, m.ID, m.PodName, m.PersistentVolumeClaimName, imageInfo.ImageID, lifecycleImage, spec.GetImagePullPolicy(),
503-
engine, requireUUID, terminationGracePeriod, args, env, finalizers, livenessProbe, readinessProbe, tolerations, tlsKeyfileSecretName, rocksdbEncryptionSecretName); err != nil {
504+
engine, requireUUID, terminationGracePeriod, args, env, finalizers, livenessProbe, readinessProbe, tolerations, serviceAccountName, tlsKeyfileSecretName, rocksdbEncryptionSecretName); err != nil {
504505
return maskAny(err)
505506
}
506507
log.Debug().Str("pod-name", m.PodName).Msg("Created pod")
@@ -571,7 +572,7 @@ func (r *Resources) createPodForMember(spec api.DeploymentSpec, group api.Server
571572
affinityWithRole = api.ServerGroupDBServers.AsRole()
572573
}
573574
if err := k8sutil.CreateArangoSyncPod(kubecli, spec.IsDevelopment(), apiObject, role, m.ID, m.PodName, imageInfo.ImageID, lifecycleImage, spec.GetImagePullPolicy(), terminationGracePeriod, args, env,
574-
livenessProbe, tolerations, tlsKeyfileSecretName, clientAuthCASecretName, masterJWTSecretName, clusterJWTSecretName, affinityWithRole); err != nil {
575+
livenessProbe, tolerations, serviceAccountName, tlsKeyfileSecretName, clientAuthCASecretName, masterJWTSecretName, clusterJWTSecretName, affinityWithRole); err != nil {
575576
return maskAny(err)
576577
}
577578
log.Debug().Str("pod-name", m.PodName).Msg("Created pod")

pkg/util/k8sutil/pods.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ func initLifecycleContainer(image string) (v1.Container, error) {
390390
}
391391

392392
// newPod creates a basic Pod for given settings.
393-
func newPod(deploymentName, ns, role, id, podName string, finalizers []string, tolerations []v1.Toleration) v1.Pod {
393+
func newPod(deploymentName, ns, role, id, podName string, finalizers []string, tolerations []v1.Toleration, serviceAccountName string) v1.Pod {
394394
hostname := CreatePodHostName(deploymentName, role, id)
395395
p := v1.Pod{
396396
ObjectMeta: metav1.ObjectMeta{
@@ -399,10 +399,11 @@ func newPod(deploymentName, ns, role, id, podName string, finalizers []string, t
399399
Finalizers: finalizers,
400400
},
401401
Spec: v1.PodSpec{
402-
Hostname: hostname,
403-
Subdomain: CreateHeadlessServiceName(deploymentName),
404-
RestartPolicy: v1.RestartPolicyNever,
405-
Tolerations: tolerations,
402+
Hostname: hostname,
403+
Subdomain: CreateHeadlessServiceName(deploymentName),
404+
RestartPolicy: v1.RestartPolicyNever,
405+
Tolerations: tolerations,
406+
ServiceAccountName: serviceAccountName,
406407
},
407408
}
408409
return p
@@ -415,10 +416,10 @@ func CreateArangodPod(kubecli kubernetes.Interface, developmentMode bool, deploy
415416
role, id, podName, pvcName, image, lifecycleImage string, imagePullPolicy v1.PullPolicy,
416417
engine string, requireUUID bool, terminationGracePeriod time.Duration,
417418
args []string, env map[string]EnvValue, finalizers []string,
418-
livenessProbe *HTTPProbeConfig, readinessProbe *HTTPProbeConfig, tolerations []v1.Toleration,
419+
livenessProbe *HTTPProbeConfig, readinessProbe *HTTPProbeConfig, tolerations []v1.Toleration, serviceAccountName string,
419420
tlsKeyfileSecretName, rocksdbEncryptionSecretName string) error {
420421
// Prepare basic pod
421-
p := newPod(deployment.GetName(), deployment.GetNamespace(), role, id, podName, finalizers, tolerations)
422+
p := newPod(deployment.GetName(), deployment.GetNamespace(), role, id, podName, finalizers, tolerations, serviceAccountName)
422423
terminationGracePeriodSeconds := int64(math.Ceil(terminationGracePeriod.Seconds()))
423424
p.Spec.TerminationGracePeriodSeconds = &terminationGracePeriodSeconds
424425

@@ -516,10 +517,10 @@ func CreateArangodPod(kubecli kubernetes.Interface, developmentMode bool, deploy
516517
// If the pod already exists, nil is returned.
517518
// If another error occurs, that error is returned.
518519
func CreateArangoSyncPod(kubecli kubernetes.Interface, developmentMode bool, deployment APIObject, role, id, podName, image, lifecycleImage string, imagePullPolicy v1.PullPolicy,
519-
terminationGracePeriod time.Duration, args []string, env map[string]EnvValue, livenessProbe *HTTPProbeConfig, tolerations []v1.Toleration,
520+
terminationGracePeriod time.Duration, args []string, env map[string]EnvValue, livenessProbe *HTTPProbeConfig, tolerations []v1.Toleration, serviceAccountName string,
520521
tlsKeyfileSecretName, clientAuthCASecretName, masterJWTSecretName, clusterJWTSecretName, affinityWithRole string) error {
521522
// Prepare basic pod
522-
p := newPod(deployment.GetName(), deployment.GetNamespace(), role, id, podName, nil, tolerations)
523+
p := newPod(deployment.GetName(), deployment.GetNamespace(), role, id, podName, nil, tolerations, serviceAccountName)
523524
terminationGracePeriodSeconds := int64(math.Ceil(terminationGracePeriod.Seconds()))
524525
p.Spec.TerminationGracePeriodSeconds = &terminationGracePeriodSeconds
525526

0 commit comments

Comments
 (0)