Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
Don't set a default PATH for Windows #3158
base: master
Are you sure you want to change the base?
Don't set a default PATH for Windows #3158
Changes from all commits
14f751d
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand where that "must be set by the image" is coming from. Yes, in wcow environment variables can be part of the rootfs tars because of the registry files, but they can also be in image configuration.
I don't see any reason to add these exceptions to the codebase where they need to be understood by the devs and also by people writing the Dockerfiles. To have a reasonable behavior that works with
ENV
in Dockerfiles the images should definePATH
in the config and users who wish to change it should do it withENV
command, the same way as the Linux users do. Yes, there is another method to do it but it doesn't mean it needs to be used.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Windows is... different.
First of all, Windows doesn't support
FROM scratch
, so every Windows image will have a "Windows" flavour as base image. It's up to that base image to define thePATH
(in whatever form it does).So unless your creating a base image (which means you're either Working for Microsoft, or violating your Windows License), the
PATH
should be defined by the base image, so the "default" is determined by the base image.Of course, as an image author you may override that default, but BuildKit itself should not try to come up with a default, because there isn't any.
To be fair, I don't think we should've set a default for non-Windows images either (and also have let it up to the base image (if any)), but that ship sailed. Changing that would be a breaking change, but so is #1747, which changed the default to be different from any container system created in the past 7 Years).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that the base image should set the
PATH
. The default we have should only be a fallback that is almost never used(it is almost never used in Linux as well). People who create Windows images should set thePATH
in the image config the same way people who create Linux images do, not ask tools to add and maintain a bunch of exceptions for them. This is also confusing for the users whenENV PATH=$PATH
has unexpected behavior.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is that (but don't ask me about details, @TBBle or @kevpar may be able to explain it better), that setting a
PATH
breaks things on Windows.For example, take the image config for
mcr.microsoft.com/windows/servercore:ltsc2022
does NOT have aPATH
;When I tried to update the classic builder to use the default
PATH
from this repository, things break. Not exactly sure, but I somewhat expect that in this case eithercmd
orPowerShell
define the path, but when setting aPATH
as environment variable, that overrides that default, rendering the image broken;I guess this is where the (LONG ) discussions we had about moby/moby#29048 come to play; the
PATH
environment variable cannot be read from the environment, but must be read back from the process that's running (e.g.PowerShell
orcmd.exe
).So probably the only options for the builder are;
PATH
and acknowledge that "we don't know"GETENV
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The breakage above happens because PowerShell lives in
C:\WINDOWS\System32\WindowsPowerShell\v1.0\
which is in-the path on systems with PowerShell present (and hence not on nanoserver). See also microsoft/terminal#7418 for other env-var traps in process launching (again, with PowerShell).moby/moby#31525 (comment) seems to be the best reference for how we got here. The
ENV
in Dockerfile and the registry-held persistent env-vars (both machine and user) are parallel sources of truth, and the simplest workaround is to just not useENV PATH
in Windows Dockerfiles, in favour ofRUN cmd /c setx /m PATH=${PATH};...
. (OrRUN reg <options that append to a multi-string value>
to save you using${PATH}
, etc.)This is most-compatible with installers that add paths to the registry as well. Once you've
ENV PATH
'd once, you then need toENV PATH
after each installer executes too as you lose access to all past and future registry-based env-path changes.That's not a great workaround, as it puts extra load on the Dockerfile writer, and requires
RUN
, which blocks non-executable-environment layer-editing tools. That's also true of GETENV as described, although if some brave soul wants to use a hive-loading library, e.g., https://github.com/gabriel-samfira/go-hivex, to implement GETENV (or really env-substitution) without booting a container, that option exists and I think it less-awful than it might seem at-first. (Still not great though).Along those lines, making Windows-platform builders transform Dockerfile
ENV
into aRUN setx
instead of writing to the container image config would resolve down to a single source of truth, with the same limitations as above and with extra build-time cost of launching another container for theENV
command, which would be surprising to long-time users.As I understand, there's no good way to tell a Windows process being launched to append an env-var to the registry-provided values, since the Windows process launch takes a single set of env-vars, and it's up to the launching process to assemble them from either copying the existing environment, or requesting a new environment block from the OS and hence using only the registry (basically only explorer and similar process launchers do the latter, and we can't do it outside a running container anyway). This is where Windows's approach differs from Linux, where if you need to append to the path, you add an
export PATH=${PATH};
to the end of .bashrc (user) or /etc/environment (system), and hence it's trivially additive across container image layers as those are processed after the Dockerfile ENV, while on Windows, DockerfileENV
hides the system and user env-vars.A more esoteric solution would be some wild inversion of the above, which I am not currently volunteering to write: At the end of every
RUN
, the Windows registry env-vars are exported into the container configENV
block (matching the merge/override behaviours as they are processed in Windows, perhaps with a secretRUN env
where we don't keep the scratch layer, or using a hive-reading library). That way Dockerfile ENV is the canonical way to make changes, but changes by installers orsetx /m
are correctly captured as well.(Year-later edit: This last idea is me being 6 years late to the table with GETENV as mentioned in the previous comment, and it's already been rejected for performance reasons).
The similar issue around stale env-vars in Windows Terminal tabs has been around for years without successful resolution. It's slightly more complex because its two sources of truth overlap (one contains an old version of the other, so there's a three-way merge in the mix), where here a fairly simple merge is sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding my 2 cents:
There is also the annoying fact that a potential installer may set just the system
PATH
or the userPATH
...or both. So thePATH
will differ based on what we set in theUSER
stanza.For example:
The two
RUN
commands above may yield different results, and that may be something that is expected by the consumers of the image.Not setting the
PATH
as an env var is, I think, desirable on Windows.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You really don't want to do
setx /m PATH=${PATH};...
either, because that takes expanded value of PATH from the current process environment (where it includes appended per-user values, expanded substitutions of any REG_EXPAND_SZ%OTHER%
references, etc) and puts that whole string back intoHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
. So your per-user stuff has leaked over to other users, any references are now copied-by-value, your own user values will be appended a second time (and thus become duplicated ifsetx
is used like this a second time...).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Marked this as deprecated, because there is no default to be set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And updated this function to return a bool to indicate whether there is a default; also tried to capture some of this in the function's GoDoc, and links to relevant discussions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like doing so broke tests; is
PATH
already set somehow? (but to a linux default path?)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, hm, right, so test is running a
busybox
image on Linux;buildkit/client/build_test.go
Lines 1370 to 1371 in 6a0cd7c
And checks the output of
echo $PATH
, with the expectation that that is changed because of thewindows
argument 🤔;buildkit/client/build_test.go
Lines 1418 to 1421 in 6a0cd7c
Not sure what should be tested there; at most, we could test if the
$PATH
matches the originalPATH
env from the image's config (if set)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, yeah. Seems like an inappropriate test to me, since we don't actually support
RUN
steps for Windows images on non-Windows AFAIK. In fact, why doesn'tc.NewContainer
orctr.Start
fail here? It seems weird to me that this ever got this far, but maybe the "we don't support this" or "inconsistent request for Windows platform on a Linux-annotated image" failure-handling is at a higher level.We will probably want Windows-side tests to ensure that we aren't breaking
PATH
(including coveringsetx /M
andsetx
across a couple of users to ensure we aren't leakingPATH
changes across users. Since we don't run the integration test suite on Windows yet, we can probably defer adding that test, and just drop the "windows path" branch of this one.