Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option --group-id to specify numerical group id #1093

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions repo2docker/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,12 @@ def get_argparser():
"--user-name", help="Username of the primary user in the image"
)

argparser.add_argument(
"--group-id",
help="Group ID of the primary user in the image, defaults to same as user ID",
type=int,
)

# Process the environment options the same way that docker does, as
# they are passed directly to docker as the environment to use. This
# requires a custom action for argparse.
Expand Down Expand Up @@ -334,6 +340,9 @@ def make_r2d(argv=None):
"Please see repo2docker --help for more details.\n"
)
sys.exit(1)
# Check group-id not None because gid=0 is a valid value
if args.group_id is not None:
r2d.group_id = args.group_id

if args.build_memory_limit:
# if the string only contains numerals we assume it should be an int
Expand Down
20 changes: 20 additions & 0 deletions repo2docker/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,25 @@ def _user_name_default(self):
"""
return getpass.getuser()

group_id = Int(
help="""
GID of the user to create inside the built image.

Defaults to gid of currently running user, since that is the most
common case when running r2d manually.

Might not affect Dockerfile builds.
""",
config=True,
)

@default("group_id")
def _group_id_default(self):
"""
Default group_id to same as user id.
"""
return self._user_id_default()

appendix = Unicode(
config=True,
help="""
Expand Down Expand Up @@ -761,6 +780,7 @@ def build(self):
build_args = {
"NB_USER": self.user_name,
"NB_UID": str(self.user_id),
"NB_GID": str(self.group_id),
}
if self.target_repo_dir:
build_args["REPO_DIR"] = self.target_repo_dir
Expand Down
8 changes: 5 additions & 3 deletions repo2docker/buildpacks/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,18 @@
# Set up user
ARG NB_USER
ARG NB_UID
ARG NB_GID=${NB_UID}
ENV USER ${NB_USER}
ENV HOME /home/${NB_USER}

RUN groupadd \
--gid ${NB_UID} \
--gid ${NB_GID} \
--non-unique \
${NB_USER} && \
useradd \
--comment "Default user" \
--create-home \
--gid ${NB_UID} \
--gid ${NB_GID} \
--no-log-init \
--shell /bin/bash \
--uid ${NB_UID} \
Expand Down Expand Up @@ -572,7 +574,7 @@ def _filter_tar(tar):
tar.uname = ""
tar.gname = ""
tar.uid = int(build_args.get("NB_UID", DEFAULT_NB_UID))
tar.gid = int(build_args.get("NB_UID", DEFAULT_NB_UID))
tar.gid = int(build_args.get("NB_GID", DEFAULT_NB_UID))
return tar

for src in sorted(self.get_build_script_files()):
Expand Down
52 changes: 51 additions & 1 deletion tests/unit/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_user():
"--",
"/bin/bash",
"-c",
"id -u > id && pwd > pwd && whoami > name && echo -n $USER > env_user".format(
"id -u > id && id -g > grp && pwd > pwd && whoami > name && echo -n $USER > env_user".format(
ts
),
]
Expand All @@ -58,3 +58,53 @@ def test_user():
assert f.read().strip() == username
with open(os.path.join(tmpdir, "name")) as f:
assert f.read().strip() == username
# When group-id NOT specified, group id in container is user id
with open(os.path.join(tmpdir, "grp")) as f:
assert f.read().strip() == userid


def test_user_groups():
"""
Validate user id and name setting
"""
ts = str(time.time())
# FIXME: Use arbitrary login here, We need it now since we wanna put things to volume.
username = getuser()
userid = str(os.geteuid())
groupid = str(os.geteuid() + 1)
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir = os.path.realpath(tmpdir)
subprocess.check_call(
[
"repo2docker",
"-v",
"{}:/home/{}".format(tmpdir, username),
"--user-id",
userid,
"--user-name",
username,
"--group-id",
groupid,
tmpdir,
"--",
"/bin/bash",
"-c",
"id -u > id && id -g > grp && stat --format %u:%g grp > id_grp && pwd > pwd && whoami > name && echo -n $USER > env_user".format(
ts
),
]
)

with open(os.path.join(tmpdir, "id")) as f:
assert f.read().strip() == userid
with open(os.path.join(tmpdir, "pwd")) as f:
assert f.read().strip() == "/home/{}".format(username)
with open(os.path.join(tmpdir, "name")) as f:
assert f.read().strip() == username
with open(os.path.join(tmpdir, "name")) as f:
assert f.read().strip() == username
# When group-id specified, group id in container is same as specified
with open(os.path.join(tmpdir, "grp")) as f:
assert f.read().strip() == groupid
with open(os.path.join(tmpdir, "id_grp")) as f:
assert f.read().strip() == userid + ":" + groupid