Repository: kubernetes/kompose
Pull request #1929 (2024-08-28 13:07:50)

feature: support UID:GID in the user key
#### What type of PR is this?

/kind feature

#### What this PR does / why we need it:

With this PR, Kompose can convert `UID:GID` to both `RunAsUser` and `RunAsGroup`. Previously, it cannot understand the `:GID` part.

#### Which issue(s) this PR fixes:

None

#### Special notes for your reviewer:

I modified the existing test case to check `uid:gid`. It does not check the simpler case `uid`. Also, one of the file does not seem to have existing tests (?).

linux-foundation-easycla[bot]:  <a href="https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/18706487/62088377/1929/#/?version=2"><img src="https://s3.amazonaws.com/cla-project-logo-prod/cla-not-signed.svg" alt="CLA Not Signed" align="left" height="28" width="328"></a><br/><br /><ul><li><a href='https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/18706487/62088377/1929/#/?version=2' target='_blank'>:x:</a> - login: @favonia / name: favonia . The commit (cb6312941b11bd4d0609698549e509fdf47054b9) is not authorized under a signed CLA. <a href='https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/18706487/62088377/1929/#/?version=2' target='_blank'>Please click here to be authorized</a>. For further assistance with EasyCLA, <a href='https://jira.linuxfoundation.org/servicedesk/customer/portal/4' target='_blank'>please submit a support request ticket</a>.</li></ul><!-- Date Modified: 2024-08-28 13:07:57.049541 -->
k8s-ci-robot: Welcome @favonia! <br><br>It looks like this is your first PR to <a href='https://github.com/kubernetes/kompose'>kubernetes/kompose</a> 🎉. Please refer to our [pull request process documentation](https://git.k8s.io/community/contributors/guide/pull-requests.md) to help your PR have a smooth ride to approval. <br><br>You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. [Here is the bot commands documentation](https://go.k8s.io/bot-commands). <br><br>You can also check if kubernetes/kompose has [its own contribution guidelines](https://github.com/kubernetes/kompose/tree/master/CONTRIBUTING.md). <br><br>You may want to refer to our [testing guide](https://git.k8s.io/community/contributors/devel/sig-testing/testing.md) if you run into trouble with your tests not passing. <br><br>If you are having difficulty getting your pull request seen, please follow the [recommended escalation practices](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#why-is-my-pull-request-not-getting-reviewed). Also, for tips and tricks in the contribution process you may want to read the [Kubernetes contributor cheat sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md). We want to make sure your contribution gets all the attention it needs! <br><br>Thank you, and welcome to Kubernetes. :smiley:
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes/kompose/pull/1929#" title="Author self-approved">favonia</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign [tessaio](https://github.com/tessaio) for approval. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes%2Fkompose).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes/kompose/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["tessaio"]} -->

Patch:
diff --git a/pkg/transformer/kubernetes/k8sutils.go b/pkg/transformer/kubernetes/k8sutils.go
index 76eb214b..3029356f 100644
--- a/pkg/transformer/kubernetes/k8sutils.go
+++ b/pkg/transformer/kubernetes/k8sutils.go
@@ -641,11 +641,30 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
 			securityContext.Privileged = &service.Privileged
 		}
 		if service.User != "" {
-			uid, err := strconv.ParseInt(service.User, 10, 64)
-			if err != nil {
-				log.Warn("Ignoring user directive. User to be specified as a UID (numeric).")
-			} else {
-				securityContext.RunAsUser = &uid
+			switch userparts := strings.Split(service.User, ":"); len(userparts) {
+			default:
+				log.Warn("Ignoring ill-formed user directive. Must be in format UID or UID:GID.")
+			case 1:
+				uid, err := strconv.ParseInt(userparts[0], 10, 64)
+				if err != nil {
+					log.Warn("Ignoring user directive. User to be specified as a UID (numeric).")
+				} else {
+					securityContext.RunAsUser = &uid
+				}
+			case 2:
+				uid, err := strconv.ParseInt(userparts[0], 10, 64)
+				if err != nil {
+					log.Warn("Ignoring user name in user directive. User to be specified as a UID (numeric).")
+				} else {
+					securityContext.RunAsUser = &uid
+				}
+
+				gid, err := strconv.ParseInt(userparts[1], 10, 64)
+				if err != nil {
+					log.Warn("Ignoring group name in user directive. Group to be specified as a GID (numeric).")
+				} else {
+					securityContext.RunAsGroup = &gid
+				}
 			}
 		}
 
diff --git a/pkg/transformer/kubernetes/podspec.go b/pkg/transformer/kubernetes/podspec.go
index 48afdf07..420b078b 100644
--- a/pkg/transformer/kubernetes/podspec.go
+++ b/pkg/transformer/kubernetes/podspec.go
@@ -3,6 +3,7 @@ package kubernetes
 import (
 	"reflect"
 	"strconv"
+	"strings"
 
 	mapset "github.com/deckarep/golang-set"
 	"github.com/kubernetes/kompose/pkg/kobject"
@@ -143,11 +144,30 @@ func SecurityContext(name string, service kobject.ServiceConfig) PodSpecOption {
 			securityContext.Privileged = &service.Privileged
 		}
 		if service.User != "" {
-			uid, err := strconv.ParseInt(service.User, 10, 64)
-			if err != nil {
-				log.Warn("Ignoring user directive. User to be specified as a UID (numeric).")
-			} else {
-				securityContext.RunAsUser = &uid
+			switch userparts := strings.Split(service.User, ":"); len(userparts) {
+			default:
+				log.Warn("Ignoring ill-formed user directive. Must be in format UID or UID:GID.")
+			case 1:
+				uid, err := strconv.ParseInt(userparts[0], 10, 64)
+				if err != nil {
+					log.Warn("Ignoring user directive. User to be specified as a UID (numeric).")
+				} else {
+					securityContext.RunAsUser = &uid
+				}
+			case 2:
+				uid, err := strconv.ParseInt(userparts[0], 10, 64)
+				if err != nil {
+					log.Warn("Ignoring user name in user directive. User to be specified as a UID (numeric).")
+				} else {
+					securityContext.RunAsUser = &uid
+				}
+
+				gid, err := strconv.ParseInt(userparts[1], 10, 64)
+				if err != nil {
+					log.Warn("Ignoring group name in user directive. Group to be specified as a GID (numeric).")
+				} else {
+					securityContext.RunAsGroup = &gid
+				}
 			}
 		}
 


Test patch:
diff --git a/pkg/transformer/kubernetes/k8sutils_test.go b/pkg/transformer/kubernetes/k8sutils_test.go
index 6392338e..933de5dd 100644
--- a/pkg/transformer/kubernetes/k8sutils_test.go
+++ b/pkg/transformer/kubernetes/k8sutils_test.go
@@ -22,7 +22,6 @@ import (
 	"path/filepath"
 	"reflect"
 	"sort"
-	"strconv"
 	"testing"
 
 	"github.com/kubernetes/kompose/pkg/kobject"
@@ -208,7 +207,7 @@ func TestCreateServiceWithServiceUser(t *testing.T) {
 		Expose:        []string{"expose"},   // not supported
 		Privileged:    true,
 		Restart:       "always",
-		User:          "1234",
+		User:          "1234:5678",
 	}
 
 	komposeObject := kobject.KomposeObject{
@@ -224,8 +223,9 @@ func TestCreateServiceWithServiceUser(t *testing.T) {
 	for _, obj := range objects {
 		if deploy, ok := obj.(*appsv1.Deployment); ok {
 			uid := *deploy.Spec.Template.Spec.Containers[0].SecurityContext.RunAsUser
-			if strconv.FormatInt(uid, 10) != service.User {
-				t.Errorf("User in ServiceConfig is not matching user in PodSpec")
+			gid := *deploy.Spec.Template.Spec.Containers[0].SecurityContext.RunAsGroup
+			if fmt.Sprintf("%d:%d", uid, gid) != service.User {
+				t.Errorf("User and group in ServiceConfig is not matching user in PodSpec")
 			}
 		}
 	}