Closed micahyoung closed 4 years ago
I feel like the best thing to do for now is keep using Config.Env.PATH
and existing CNB conventions for handling PATH
environment variable modification, and continue ignoring any Windows reg PATH
environment variable that might be present. Stack and buildpack authors already have plenty of tools from pack and lifecycle to modify PATH
if they need to.
Additionally, I'd still like to add more formal Window reg handling and I feel like it's premature to start relying on the existing Windows reg behavior too much until it's spec'd out.
So if we follow that opinion, there's these potential approaches:
Config.Env.PATH
(Dockerfile: ENV PATH c:\\Windows\\system32;C:\\Windows
just like Linux
exporter
)exporter
to always append ;c:\Windows\system32;C:\Windows
to the exported images Config.Env.PATH
launcher
to always append ;c:\Windows\system32;C:\Windows
to its own PATH
environment variable (when not already present) before executing any sub commands.
I wonder if this issue is related to another issue reported from @nebhale on slack.
docker run -it -p 8080:8080 --entrypoint web --env ALPHA="bravo" applications/jar
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"web\": executable file not found in $PATH": unknown.
Through the thread it was discovered that env vars are being duplicated:
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"CNB_LAYERS_DIR=/layers",
"CNB_APP_DIR=/workspace",
"CNB_PLATFORM_API=0.4",
"CNB_DEPRECATION_MODE=quiet",
"PATH=/cnb/process:/cnb/lifecycle:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
^ potentially related to #385???
It's exactly #385. This will be solved by the imgutil
changes.
I think this issue can be closed now since https://github.com/buildpacks/lifecycle/issues/385 was closed (https://github.com/buildpacks/imgutil/pull/56 and https://github.com/buildpacks/lifecycle/pull/387 were merged).
Hey @yaelharel. I'll check this morning that we can use stacks Windows stacks with a null Config.Env.PATH
. If so, we can close
I suspect that this is a slightly different issue. When PATH
is unset in a windows image config file I suspect it is either being set in /Hives
or there is some sort of OS-level defaulting behavior. We want to prepend /cnb/process
and /cnb/lifecycle
to the path without blowing away the original value. So we either need folks to set the original value in the place we exoect (Config.Env.PATH
) or find a way to grab the original value so that we can prepend to it.
Yeah, I just confirmed using https://github.com/buildpacks/samples/pull/85 but with the newest lifecycle appended to builders/nanoserver-1809/builder.toml
and with the explicit PATH
in the stack commented out, it fails with:
make build-windows build-windows-apps
...
docker run --rm sample-batch-script-app:nanoserver-1809
ERROR: failed to launch: cmd execute: exec: "cmd": executable file not found in %PATH%
@ekcasey I feel like changing lifecycle to read the Hives-PATH
from the run-image and prepending them to Config.Env.PATH
is the likely the right place to end up. But to start, would be ok requiring stack authors to manually duplicate whatever is in the Hives-PATH
into an explicit Config.Env.PATH
(aka ENV PATH ...
Dockerfile
directive) on their run-image to start with?
I feel like that works already today and I worry that parsing Hives/*_Delta
files is going to be tricky as there's no OSS tooling nor documentation for working with those files. They are much easier to work with in-container at runtime (using Windows Reg APIs) and thats what a lot of this wip spec is based on.
For now we could rely on the fact that, even though most stacks are currently defined in Dockerfiles
today, they are much less dynamic than any-old Dockerfile
in practice and if we had stack authors set an ENV PATH
once, it will be unlikely to get out of sync with the a Hives-PATH
, especially given the ENV
, FROM <base image>
, and RUN
directives that would alter the Hives-PATH
are in the same Dockerfile.
To start, what if we add some simple validation in creator
and launcher
to use the Windows reg APIs to compare the two PATH
values at runtime and if out of sync, print warnings for the exact values Config.Env.PATH
to use?
@micahyoung That all sounds reasonable to me. To resolve this issue we should read the Hives path if Config.Env.Path
is unset (seems like Config.Env.Path
take precedence) and prepend the lifecycle and process dirs to that path.
I think the remaining question is: if Config.Env.Path
is unset on the run-image, do we want to set the PATH
using Config.Env.Path
or do we want to contribute a Hive delta?
It's feeling like this will all need to be spec'd out but here's what I'm thinking:
We do everything in-container, in-process in creator
/launcher
executables at runtime: before executing any shell/sub-processes, if their own current env var PATH
does not contain the value of Windows reg HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment\PATH
, then they append that value to their own current, runtime env var PATH
(which would already have lifecycle/process or other dirs that Docker injected from Config.Env.PATH
). Then creator
/launcher
can find cmd
on any subcommands in the same process. This means Config.Env.PATH
on build/run/builder images would never contain c:\windows\system32;c:\windows
- those values would always come from the Windows reg at runtime.
Export continues to set Config.Env.PATH
as it does today with process/lifecycle variables, whether it's empty or not. If it's empty, Docker already automatically set's launcher
's env var PATH
from the registry, so launcher
reads the Windows reg value, compares it, and will not change it as describe above. If Config.Env.PATH
is empty, launcher
's env var PATH
will not yet contain the Windows reg value, so launcher
reads the Windows reg value, compares it, and appends it.
The alternative, having platforms or lifecycle read values from Hives/*_Delta
format from outside and appending to and image's Config.Env.PATH
or via new Hives/*_Delta
entries, would be tricky:
_Delta
format is unspeced with poor tooling and controlled by Microsoft, we'd have to figure it all out ourselves. We'd also have to aggregate the value from multiple _Delta
files across multiple stack image layers to get the final value. If instead we check via the Windows reg API, in-container at runtime, then it is already aggregated and accessible with Golang wrappers windows/registry.Config.Env.PATH
and we'd have that data in 2 places instead of just the Windows reg.creator
to have a proper env var PATH
, then a platform would have to "fix" the build image's Config.Env.PATH
ahead of time. If instead creator
fixes it's own env var PATH
from the build image's effective Windows reg at runtime, then a platform doesn't need to do anything ahead of time.I added a Draft PR of my previous thinking, basically "at runtime, the lifecycle/launcher merge their own in-memory environment variables with their Windows registry env vars, ignoring duplicates"
https://github.com/buildpacks/lifecycle/pull/402
Everything seems to work - ENV PATH...
is no longer need in build/run images. I'm wondering if this needs to be spec'd first though.
Although the solution proposed by @micahyoung would solve the PATH
problem for processes started by the launcher, images still would not have a sensible PATH
by default which may result in a bad experience if users want to the start a container without the launcher for any reason. Require windows stack authors (a smaller pool of more deeply engaged users) to set PATH
may result in a better experience all-around.
After discussion we have decided not to make this fix for now and instead propose specifying the requirement to set PATH
on windows run images.
Summary
Platform 0.4 exposed a bug with Windows images where an exported image with
ENTRYPOINT
set toc:\cnb\lifecycle\launcher.exe
(pack
's default) are unrunnable becauselauncher
tries to run subcommands but fails to findcmd
in thePATH
atc:\Windows\system32\cmd.exe
.Example app image:
micahyoung/sample-hello-world-windows-app:nanoserver-1809
Reproduction
Steps
make build-windows build-windows-app
from this samples PR commitCurrent behavior
attempt to run app
inspect
Expected
Context
Pack issue: https://github.com/buildpacks/pack/issues/800 Related moby issues/unimplemented PRs:
This appears to happen because the exported image config's
Config.Env.PATH
only has CNB paths (c:\cnb\process;c:\cnb\lifecycle
) and no default Windows paths (c:\Windows\system32;C:\Windows
), making it impossible forlifecycle.exe
to findcmd.exe
.When Docker Windows encounters and an empty
Config.Env.PATH
it loads the path from the image's Windows reg Hives atHKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment\PATH
docs) resulting inc:\Windows\system32;C:\Windows
+ any other hives entries added in subsequent layers.Whenever
Config.Env.PATH
is set, Docker Windows doesn't load the registry and only uses the value from the config, which is what causes this issue.Example showing typically empty
Config.Env
(includingConfig.Env.PATH
) on all windows base images.... whereas Linux images always set the full
PATH
lifecycle version
pack inspect-builder cnbs/sample-builder:nanoserver-1809
platform version(s)
pack report
docker info
anything else?
One temporary workaround workaround we found is to set
ENV PATH c:\\Windows\\system32;C:\\Windows
in the run image Dockerfile (or otherwise modify theconfig
on the run image), and this makeslauncher
able to run: