diff --git a/docs/user-guide.md b/docs/user-guide.md index 62818ea1..b0f415f7 100755 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -244,6 +244,19 @@ services: - "6379" ``` +- `kompose.service.accountname` defines the service account name to provide the credential info of the pod. + +For example: + +```yaml +version: '3.4' +services: + app: + image: python + labels: + kompose.service.accountname: "my-service" +``` + - `kompose.image-pull-secret` defines a kubernetes secret name for imagePullSecrets podspec field. This secret will be used for pulling private images. For example: diff --git a/pkg/loader/compose/utils.go b/pkg/loader/compose/utils.go index 516b5be7..840e1bf3 100644 --- a/pkg/loader/compose/utils.go +++ b/pkg/loader/compose/utils.go @@ -38,8 +38,10 @@ const ( LabelNodePortPort = "kompose.service.nodeport.port" // LabelServiceExpose defines if the service needs to be made accessible from outside the cluster or not LabelServiceExpose = "kompose.service.expose" - // LabelServiceExposeTLSSecret provides the name of the TLS secret to use with the Kubernetes ingress controller + // LabelServiceExposeTLSSecret provides the name of the TLS secret to use with the Kubernetes ingress controller LabelServiceExposeTLSSecret = "kompose.service.expose.tls-secret" + // LabelServiceAccountName defines the service account name to provide the credential info of the pod. + LabelServiceAccountName = "kompose.service.accountname" // LabelControllerType defines the type of controller to be created LabelControllerType = "kompose.controller.type" // LabelImagePullSecret defines a secret name for kubernetes ImagePullSecrets diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go index 3798b028..9c08c64f 100644 --- a/pkg/transformer/kubernetes/k8sutils.go +++ b/pkg/transformer/kubernetes/k8sutils.go @@ -670,6 +670,10 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic template.Spec.Subdomain = service.DomainName } + if serviceAccountName, ok := service.Labels[compose.LabelServiceAccountName]; ok { + template.Spec.ServiceAccountName = serviceAccountName + } + return nil } diff --git a/pkg/transformer/kubernetes/k8sutils_test.go b/pkg/transformer/kubernetes/k8sutils_test.go index 205dcfe2..134251ab 100644 --- a/pkg/transformer/kubernetes/k8sutils_test.go +++ b/pkg/transformer/kubernetes/k8sutils_test.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/kubernetes/kompose/pkg/kobject" + "github.com/kubernetes/kompose/pkg/loader/compose" "github.com/kubernetes/kompose/pkg/testutils" "github.com/pkg/errors" appsv1 "k8s.io/api/apps/v1" @@ -487,3 +488,31 @@ func TestDurationStrToSecondsInt(t *testing.T) { } } } + +func TestServiceWithServiceAccount(t *testing.T) { + assertServiceAccountName := "my-service" + + service := kobject.ServiceConfig{ + ContainerName: "name", + Image: "image", + Port: []kobject.Ports{{HostPort: 55555}}, + Labels: map[string]string{compose.LabelServiceAccountName: assertServiceAccountName}, + } + + komposeObject := kobject.KomposeObject{ + ServiceConfigs: map[string]kobject.ServiceConfig{"app": service}, + } + k := Kubernetes{} + + objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true}) + if err != nil { + t.Error(errors.Wrap(err, "k.Transform failed")) + } + for _, obj := range objects { + if deployment, ok := obj.(*appsv1.Deployment); ok { + if deployment.Spec.Template.Spec.ServiceAccountName != assertServiceAccountName { + t.Errorf("Expected %v returned, got %v", assertServiceAccountName, deployment.Spec.Template.Spec.ServiceAccountName) + } + } + } +} diff --git a/pkg/transformer/kubernetes/kubernetes.go b/pkg/transformer/kubernetes/kubernetes.go index 10912ffd..ff95ed54 100644 --- a/pkg/transformer/kubernetes/kubernetes.go +++ b/pkg/transformer/kubernetes/kubernetes.go @@ -1261,6 +1261,10 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject. TerminationGracePeriodSeconds(name, service), ) + if serviceAccountName, ok := service.Labels[compose.LabelServiceAccountName]; ok { + podSpec.Append(ServiceAccountName(serviceAccountName)) + } + err = k.UpdateKubernetesObjectsMultipleContainers(name, service, opt, &objects, podSpec) if err != nil { return nil, errors.Wrap(err, "Error transforming Kubernetes objects") diff --git a/pkg/transformer/kubernetes/kubernetes_test.go b/pkg/transformer/kubernetes/kubernetes_test.go index 7f077556..6434ed74 100644 --- a/pkg/transformer/kubernetes/kubernetes_test.go +++ b/pkg/transformer/kubernetes/kubernetes_test.go @@ -639,3 +639,60 @@ func TestMultipleContainersInPod(t *testing.T) { } } } + +func TestServiceAccountNameOnMultipleContainers(t *testing.T) { + groupName := "pod_group" + serviceAccountName := "my-service" + + createConfigs := func(labels map[string]string) map[string]kobject.ServiceConfig { + createConfig := func(name string) kobject.ServiceConfig { + config := newServiceConfig() + config.Labels = map[string]string{compose.LabelServiceGroup: groupName} + for k, v := range labels { + config.Labels[k] = v + } + config.Name = name + config.ContainerName = "" + config.Volumes = []kobject.Volumes{ + { + VolumeName: "mountVolume", + MountPath: "/data", + }, + } + return config + } + return map[string]kobject.ServiceConfig{"app1": createConfig("app1"), "app2": createConfig("app2")} + } + + testCases := map[string]struct { + komposeObject kobject.KomposeObject + expectedLabelNames []string + }{ + "Converted multiple containers with ServiceAccountName": { + kobject.KomposeObject{ + ServiceConfigs: createConfigs(map[string]string{compose.LabelServiceAccountName: serviceAccountName}), + }, []string{serviceAccountName}}, + } + + for name, test := range testCases { + t.Log("Test case:", name) + k := Kubernetes{} + // Run Transform + objs, err := k.Transform(test.komposeObject, kobject.ConvertOptions{MultipleContainerMode: true, CreateD: true}) + if err != nil { + t.Error(errors.Wrap(err, "k.Transform failed")) + } + + // Check results + for _, obj := range objs { + if deployment, ok := obj.(*appsv1.Deployment); ok { + if deployment.Name != groupName { + t.Errorf("Expected %v returned, got %v", groupName, deployment.Name) + } + if deployment.Spec.Template.Spec.ServiceAccountName != serviceAccountName { + t.Errorf("Expected %v returned, got %v", serviceAccountName, deployment.Spec.Template.Spec.ServiceAccountName) + } + } + } + } +} diff --git a/pkg/transformer/kubernetes/podspec.go b/pkg/transformer/kubernetes/podspec.go index 51238ed9..74def137 100644 --- a/pkg/transformer/kubernetes/podspec.go +++ b/pkg/transformer/kubernetes/podspec.go @@ -340,6 +340,12 @@ func ReadinessProbe(service kobject.ServiceConfig) PodSpecOption { } } +func ServiceAccountName(serviceAccountName string) PodSpecOption { + return func(podSpec *PodSpec) { + podSpec.ServiceAccountName = serviceAccountName + } +} + func (podSpec *PodSpec) Append(ops ...PodSpecOption) *PodSpec { for _, option := range ops { option(podSpec)