Support preserving default ENTRYPOINT/CMD for containers used for Workspaces
MR: Add support for preserving container entrypoint... (!196482 - merged)
Description
With the initial work done in Startup scripts for Remote Development workspac... (&15602) to support the devfile
standard postStart
hooks, we can now provide support for not overriding the default ENTRYPOINT and CMD in containers which are used for Workspaces.
Once all of the existing internal script logic is moved to postStart
hooks, we will still be overriding the ENTRYPOINT/CMD to preserve existing behavior, but it will be overridden with a no-op keepalive command like tail -f /dev/null
.
We needed to keep this behavior as the default when moving the internal script logic to postStart hooks.
This is because each workspace container must have a non-exiting/daemonized process run as its Kubernetes command/args
. Otherwise, it will exit immediately after starting, and never reach the Running
status. Eclipse Che exhibits this same behavior.
Once support is added for users to add their own postStart hooks to their devfile, as the devfile standard supports, there will be a workaround for this: The users can replicate the Entrypoint/Cmd of each container in the corresponding postStart hooks for that Workspace container.
But it is very useful for users to not have to do this, and simply be able to use existing containers' Entrypoint/Cmd
behavior. In many cases, the user may not even know what Entrypoint/Cmd is being run by default by their container, nor have the technical/container expertise to find out what it is in order to copy it to the devfile postStart hook.
Thus, we want to provide a way for users to "opt-in" to preserving the existing containers' Entrypoint/Cmd.
This is already a first-class supported feature of the devcontainer
standard, through the "overrideCommand": false
attribute. See more details at &15602 (comment 2270811598), and [comment below for more context](See comment below for more context.
Acceptance Criteria
-
Add support for custom devfile attribute overrideCommand
. This should function the same as thedevcontainer
standard"overrideCommand"
attribute, when used with theArray
property type. See comment below for more context -
Update the fixture devfile that contains all of the possible devfile entries
Implementation Plan
There are a couple options here:
- Hack this in via a custom attribute in the devfile, in the
components.attributes
field. This will default tofalse
, which is the same as the devcontainer standard when using an image Dockerfile. For example:
components:
- name: tooling-container
attributes:
overrideCommand: false
- Propose it as an official addition to the devfile standard, to provide feature parity with the devcontainer standard's
"overrideCommand"
support. - Do both - support the custom attribute as a first iteration to get the support out ASAP, then coordinate with the devfile team to add it to the official devfile standard.
For the initial implementation, we will do Option 1, and then follow up with the devfile team on option 2, once we have a reference implementation to refer to.
Previous description
UPDATE 2024-11-25
- Part of this requirement will be implemented by Add support for devfile postStart event in Work... (#505988 - closed), as part of Startup scripts for Remote Development workspac... (&15602) (see "Current Plannd Solution" section)
- The preservation of the
ENTRYPOINT
/CMD
for workspace container(s) is NOT in scope for that issue/epic - see "Out-of-scope requirements for this solution" section. If we want to support this requirement, it should be considered as a separate epic, perhaps by promoting this issue to an epic and rewriting it accordingly.
Description
The current editor injector implementation specifies an entrypoint for the main container. This entrypoint starts the VS Code server.
In the future, we would like to support custom entrypoints and commands in the main Workspace containers
Vishal's description of the problem and suggested solution
make a local docker container work with local WebIDE server. (I'll use yours as an example, could you please post here a link?)
I'm not sure what you mean here. Are you referring to registry.gitlab.com/gitlab-com/create-stage/editor-poc/remote-development/gitlab-rd-web-ide-docker:0.1-alpha
?
have a look if I can find some editor injector example code that's licensed favourably
This is what we have to do, as I think -
Build a multi-architecture container image of our vscode-fork. This image would contain the package for that architecture and a script which will
- copy the contents of the said package to a location
- inject running the editor into the workspace's main container's entrypoint/command
e.g. We build multi-architecture container image for gitlab-web-ide-vscode-fork
with the command
docker buildx build \
--push \
--platform linux/arm64/v8,linux/amd64 \
--tag registry.gitlab.com/gitlab-org/gitlab-web-ide-vscode-fork \
.
To recap -
- The
ENTRYPOINT
specifies a command that will always be executed when the container starts. - The
CMD
specifies arguments that will be fed to theENTRYPOINT
.
Now imagine the user wants to run the docker/getting-started
container image. When we inspect the image, we get the following
docker inspect docker/getting-started
[
{
...
"ContainerConfig": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": null,
"Cmd": null,
"Image": "",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"DockerVersion": "",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.23.3",
"PKG_RELEASE=1",
"NJS_VERSION=0.7.9"
],
"Cmd": [
"nginx",
"-g",
"daemon off;"
],
"Image": "",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": [
"/docker-entrypoint.sh"
],
"OnBuild": null,
"Labels": {
"maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
},
"StopSignal": "SIGQUIT"
},
"Architecture": "arm64",
"Os": "linux",
...
}
]
Calculate the final Entrypoint
and Cmd
of the main container by inspecting it are
Entrypoint = first-not-null-value-of( .Config.Entrypoint, ["/bin/sh","-c"] )
Cmd = .Config.Cmd
In the case of running docker/getting-started
container image, the values we would from inspection are get are
Entrypoint = first-not-null-value-of( ["/docker-entrypoint.sh"], ["/bin/sh","-c"] ) = ["/docker-entrypoint.sh"]
Cmd = ["nginx","-g","daemon off;"]
Now create a script which copies the content binaries for running the editor, runs the editor and then runs the container's entrypoint/cmd. Example would be
# Let's call this script init.sh
# copy the editor to the mounted volume location
cp source destination
# run the editor
"./${VSCODE_REH_DIR}/bin/code-server-oss" --host "0.0.0.0"
# run the container's entrypoint/command
# "$Entrypoint" "$Command"
"/docker-entrypoint.sh" "nginx" "-g" "daemon off;"
The script would be available in the multi-architecture docker image we built earlier.
Now, we will set the workspace's ContainerConfig.Entrypoint
= ["/bin/sh","-c"]
and ContainerConfig.Cmd
= init.sh
try to create a file like this for the new docker image
Let's keep this on hold for now
P.S. - I feel like this now after writing this comment. I haven't tested it out. So let's pair on it if you'd like
After having slept on it, I think we should do the following
- Create multi-arch image which also has a script to copy the editor to a volume mount( say
init.sh
- We can use this image by setting the command of the main container of the workspace to this script (
init.sh
)
Once we have done that, let's figure out how Eclipse Che and DWO are taking of such scenarios - when docker image has a entrypoint and command and when that is overriden through pod-overrides
and container-overrides
in the devfile. Then decide how we want to do further improve it.
WDYT?
Chad's follow-up suggestion to support a `pre` and `post` hook (from this comment)
Add support in Remote Development to not completely override the default entrypoint/arg of the container (which are called command/args in Kuberntes) by doing something like this
This looks like a promising approach.
We could even make this more generic, by allowing an optional "pre" and "post" script to be run if they exist.
The script could look something like this:
# Run the pre-command hook only if it exists and is executable
if [ -f "${volume_path}/pre_command_hook.sh" ] && [ -x "${volume_path}/pre_command_hook.sh" ]; then
"${volume_path}/pre_command_hook.sh"
fi
# Run the default command, if any
if [ -n "$*" ]; then
exec "$@"
fi
# Run the post-command hook only if it exists and is executable
if [ -f "${volume_path}/post_command_hook.sh" ] && [ -x "${volume_path}/post_command_hook.sh" ]; then
"${volume_path}/post_command_hook.sh"
fi
We could also optionally make the names/locations of these scripts configurable somehow.
TASKS
-
Address the following suggestion from review (!105783 (comment 1374894393)): "I suggest looking into creating children of Config::Entry::Root to define the structure of the devfile in code. This is how CI yml config files are loaded and it provides a framework for parsing config files in a standardized and understandable way." -
Ideally, support a pre
andpost
script hook, with configurable paths for each.