Skip to content

Commit

Permalink
Merge pull request #152 from arangodb/feature/service-account
Browse files Browse the repository at this point in the history
Allow customization of serviceAccountName for pods
  • Loading branch information
ewoutp authored Jun 6, 2018
2 parents 659c5c9 + 1092164 commit 4a2f34b
Show file tree
Hide file tree
Showing 9 changed files with 367 additions and 13 deletions.
8 changes: 8 additions & 0 deletions docs/Manual/Deployment/Kubernetes/DeploymentResource.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,14 @@ The default value is `8Gi`.
This setting is not available for group `coordinators`, `syncmasters` & `syncworkers`
because servers in these groups do not need persistent storage.

### `spec.<group>.serviceAccountName: string`

This setting specifies the `serviceAccountName` for the `Pods` created
for each server of this group.

Using an alternative `ServiceAccount` is typically used to separate access rights.
The ArangoDB deployments do not require any special rights.

### `spec.<group>.storageClassName: string`

This setting specifies the `storageClass` for the `PersistentVolume`s created
Expand Down
2 changes: 1 addition & 1 deletion manifests/templates/test/rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ rules:
resources: ["nodes"]
verbs: ["list"]
- apiGroups: [""]
resources: ["pods", "services", "persistentvolumes", "persistentvolumeclaims", "secrets"]
resources: ["pods", "services", "persistentvolumes", "persistentvolumeclaims", "secrets", "serviceaccounts"]
verbs: ["*"]
- apiGroups: ["apps"]
resources: ["daemonsets"]
Expand Down
21 changes: 21 additions & 0 deletions pkg/apis/deployment/v1alpha/server_group_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/api/resource"

"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
)

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

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

// GetServiceAccountName returns the value of serviceAccountName.
func (s ServerGroupSpec) GetServiceAccountName() string {
return util.StringOrDefault(s.ServiceAccountName)
}

// Validate the given group spec
func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentMode, env Environment) error {
if used {
Expand All @@ -86,6 +94,16 @@ func (s ServerGroupSpec) Validate(group ServerGroup, used bool, mode DeploymentM
if s.GetCount() > 1 && group == ServerGroupSingle && mode == DeploymentModeSingle {
return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d. Expected 1", s.GetCount()))
}
if name := s.GetServiceAccountName(); name != "" {
if err := k8sutil.ValidateOptionalResourceName(name); err != nil {
return maskAny(errors.Wrapf(ValidationError, "Invalid serviceAccountName: %s", err))
}
}
if name := s.GetStorageClassName(); name != "" {
if err := k8sutil.ValidateOptionalResourceName(name); err != nil {
return maskAny(errors.Wrapf(ValidationError, "Invalid storageClassName: %s", err))
}
}
} else if s.GetCount() != 0 {
return maskAny(errors.Wrapf(ValidationError, "Invalid count value %d for un-used group. Expected 0", s.GetCount()))
}
Expand Down Expand Up @@ -145,6 +163,9 @@ func (s *ServerGroupSpec) SetDefaultsFrom(source ServerGroupSpec) {
if s.Tolerations == nil {
s.Tolerations = source.Tolerations
}
if s.ServiceAccountName == nil {
s.ServiceAccountName = util.NewStringOrNil(source.ServiceAccountName)
}
setDefaultsFromResourceList(&s.Resources.Limits, source.Resources.Limits)
setDefaultsFromResourceList(&s.Resources.Requests, source.Resources.Requests)
}
Expand Down
9 changes: 9 additions & 0 deletions pkg/apis/deployment/v1alpha/zz_generated.deepcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,15 @@ func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.ServiceAccountName != nil {
in, out := &in.ServiceAccountName, &out.ServiceAccountName
if *in == nil {
*out = nil
} else {
*out = new(string)
**out = **in
}
}
return
}

Expand Down
4 changes: 3 additions & 1 deletion pkg/deployment/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,10 @@ func (ib *imagesBuilder) fetchArangoDBImageIDAndVersion(ctx context.Context, ima
tolerations = k8sutil.AddTolerationIfNotFound(tolerations, k8sutil.NewNoExecuteToleration(k8sutil.TolerationKeyNodeNotReady, shortDur))
tolerations = k8sutil.AddTolerationIfNotFound(tolerations, k8sutil.NewNoExecuteToleration(k8sutil.TolerationKeyNodeUnreachable, shortDur))
tolerations = k8sutil.AddTolerationIfNotFound(tolerations, k8sutil.NewNoExecuteToleration(k8sutil.TolerationKeyNodeAlphaUnreachable, shortDur))
serviceAccountName := ""

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 {
if err := k8sutil.CreateArangodPod(ib.KubeCli, true, ib.APIObject, role, id, podName, "", image, "", ib.Spec.GetImagePullPolicy(), "", false, terminationGracePeriod, args, nil, nil, nil, nil,
tolerations, serviceAccountName, "", ""); err != nil {
log.Debug().Err(err).Msg("Failed to create image ID pod")
return true, maskAny(err)
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/deployment/reconcile/plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ func podNeedsUpgrading(p v1.Pod, spec api.DeploymentSpec, images api.ImageInfoLi
// When true is returned, a reason for the rotation is already returned.
func podNeedsRotation(p v1.Pod, apiObject metav1.Object, spec api.DeploymentSpec,
group api.ServerGroup, agents api.MemberStatusList, id string) (bool, string) {
groupSpec := spec.GetServerGroupSpec(group)

// Check image pull policy
if c, found := k8sutil.GetContainerByName(&p, k8sutil.ServerContainerName); found {
if c.ImagePullPolicy != spec.GetImagePullPolicy() {
Expand All @@ -274,6 +276,7 @@ func podNeedsRotation(p v1.Pod, apiObject metav1.Object, spec api.DeploymentSpec
} else {
return true, "Server container not found"
}

// Check arguments
/*expectedArgs := createArangodArgs(apiObject, spec, group, agents, id)
if len(expectedArgs) != len(c.Args) {
Expand All @@ -285,6 +288,11 @@ func podNeedsRotation(p v1.Pod, apiObject metav1.Object, spec api.DeploymentSpec
}
}*/

// Check service account
if p.Spec.ServiceAccountName != groupSpec.GetServiceAccountName() {
return true, "ServiceAccountName changed"
}

return false, ""
}

Expand Down
5 changes: 3 additions & 2 deletions pkg/deployment/resources/pod_creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ func (r *Resources) createPodForMember(spec api.DeploymentSpec, group api.Server
lifecycleImage := r.context.GetLifecycleImage()
terminationGracePeriod := group.DefaultTerminationGracePeriod()
tolerations := r.createPodTolerations(group, groupSpec)
serviceAccountName := groupSpec.GetServiceAccountName()

// Update pod name
role := group.AsRole()
Expand Down Expand Up @@ -500,7 +501,7 @@ func (r *Resources) createPodForMember(spec api.DeploymentSpec, group api.Server
requireUUID := group == api.ServerGroupDBServers && m.IsInitialized
finalizers := r.createPodFinalizers(group)
if err := k8sutil.CreateArangodPod(kubecli, spec.IsDevelopment(), apiObject, role, m.ID, m.PodName, m.PersistentVolumeClaimName, imageInfo.ImageID, lifecycleImage, spec.GetImagePullPolicy(),
engine, requireUUID, terminationGracePeriod, args, env, finalizers, livenessProbe, readinessProbe, tolerations, tlsKeyfileSecretName, rocksdbEncryptionSecretName); err != nil {
engine, requireUUID, terminationGracePeriod, args, env, finalizers, livenessProbe, readinessProbe, tolerations, serviceAccountName, tlsKeyfileSecretName, rocksdbEncryptionSecretName); err != nil {
return maskAny(err)
}
log.Debug().Str("pod-name", m.PodName).Msg("Created pod")
Expand Down Expand Up @@ -571,7 +572,7 @@ func (r *Resources) createPodForMember(spec api.DeploymentSpec, group api.Server
affinityWithRole = api.ServerGroupDBServers.AsRole()
}
if err := k8sutil.CreateArangoSyncPod(kubecli, spec.IsDevelopment(), apiObject, role, m.ID, m.PodName, imageInfo.ImageID, lifecycleImage, spec.GetImagePullPolicy(), terminationGracePeriod, args, env,
livenessProbe, tolerations, tlsKeyfileSecretName, clientAuthCASecretName, masterJWTSecretName, clusterJWTSecretName, affinityWithRole); err != nil {
livenessProbe, tolerations, serviceAccountName, tlsKeyfileSecretName, clientAuthCASecretName, masterJWTSecretName, clusterJWTSecretName, affinityWithRole); err != nil {
return maskAny(err)
}
log.Debug().Str("pod-name", m.PodName).Msg("Created pod")
Expand Down
19 changes: 10 additions & 9 deletions pkg/util/k8sutil/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ func initLifecycleContainer(image string) (v1.Container, error) {
}

// newPod creates a basic Pod for given settings.
func newPod(deploymentName, ns, role, id, podName string, finalizers []string, tolerations []v1.Toleration) v1.Pod {
func newPod(deploymentName, ns, role, id, podName string, finalizers []string, tolerations []v1.Toleration, serviceAccountName string) v1.Pod {
hostname := CreatePodHostName(deploymentName, role, id)
p := v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -399,10 +399,11 @@ func newPod(deploymentName, ns, role, id, podName string, finalizers []string, t
Finalizers: finalizers,
},
Spec: v1.PodSpec{
Hostname: hostname,
Subdomain: CreateHeadlessServiceName(deploymentName),
RestartPolicy: v1.RestartPolicyNever,
Tolerations: tolerations,
Hostname: hostname,
Subdomain: CreateHeadlessServiceName(deploymentName),
RestartPolicy: v1.RestartPolicyNever,
Tolerations: tolerations,
ServiceAccountName: serviceAccountName,
},
}
return p
Expand All @@ -415,10 +416,10 @@ func CreateArangodPod(kubecli kubernetes.Interface, developmentMode bool, deploy
role, id, podName, pvcName, image, lifecycleImage string, imagePullPolicy v1.PullPolicy,
engine string, requireUUID bool, terminationGracePeriod time.Duration,
args []string, env map[string]EnvValue, finalizers []string,
livenessProbe *HTTPProbeConfig, readinessProbe *HTTPProbeConfig, tolerations []v1.Toleration,
livenessProbe *HTTPProbeConfig, readinessProbe *HTTPProbeConfig, tolerations []v1.Toleration, serviceAccountName string,
tlsKeyfileSecretName, rocksdbEncryptionSecretName string) error {
// Prepare basic pod
p := newPod(deployment.GetName(), deployment.GetNamespace(), role, id, podName, finalizers, tolerations)
p := newPod(deployment.GetName(), deployment.GetNamespace(), role, id, podName, finalizers, tolerations, serviceAccountName)
terminationGracePeriodSeconds := int64(math.Ceil(terminationGracePeriod.Seconds()))
p.Spec.TerminationGracePeriodSeconds = &terminationGracePeriodSeconds

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

Expand Down
Loading

0 comments on commit 4a2f34b

Please sign in to comment.