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

Handle file paths base on target platform #3908

Merged
merged 12 commits into from
Jul 10, 2023

Conversation

gabriel-samfira
Copy link
Collaborator

This change properly handles paths on different platforms. In short, this change checks the target platform we're building an image for and applies normalization steps to make sure the file paths are valid. This makes buildkit properly handle paths on both *nix systems and on Windows.

Depends on:

Replaces:

Note: This branch will fail to build until dependencies are merged. Leaving the old branch up, as it contains a longer discussion related to the changes in these 3 PRs.

Here is a test run with all 3 branches merged: https://github.com/gabriel-samfira/buildkit/actions/runs/5091195907

CC: @crazy-max @jedevc

@gabriel-samfira
Copy link
Collaborator Author

Rebased, now that the dependencies have been merged. This should now be ready for review.

client/llb/fileop.go Outdated Show resolved Hide resolved
client/llb/fileop.go Outdated Show resolved Hide resolved
frontend/dockerfile/dockerfile2llb/convert.go Outdated Show resolved Hide resolved
solver/llbsolver/file/backend.go Outdated Show resolved Hide resolved
@@ -261,7 +262,7 @@ func PrepareMounts(ctx context.Context, mm *mounts.MountManager, cm cache.Manage
} else {
mws := mountWithSession(mountable, g)
dest := m.Dest
if !filepath.IsAbs(filepath.Clean(dest)) {
if !system.IsAbs(filepath.Clean(dest), platform) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This path is coming from LLB. How is it not already normalized?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is also called from NewContainer(). I can't remember for sure why I needed this here (first PR was proposed in November), but I can track it down and give you a more detailed answer.

On the other hand, I think that whenever we are dealing with paths that may be meant for a different OS than the one the code is running on, we should use the helper functions in util/system.

@gabriel-samfira
Copy link
Collaborator Author

@tonistiigi kind reminder. I would love to unblock the rest of the PRs. The executor work depends on this PR.

if c.Platform == nil {
c.Platform = &ocispecs.Platform{
OS: "linux",
Architecture: "amd64",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the architecture in here used anywhere? If not then I would prefer if the nil case is handled inside FileOp for OS only and it is clear that this only controls the path conversions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the Architecture.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant was to handle nil platform inside FileOp. If the logic is only based on OS then we shouldn't control it by passing full platform (or passing an invalid partial platform).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. Misread what you meant.

@@ -360,6 +360,7 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS
if d.stage.BaseName == emptyImageName {
d.state = llb.Scratch()
d.image = emptyImage(platformOpt.targetPlatform)
d.platform = &platformOpt.targetPlatform
Copy link
Member

@tonistiigi tonistiigi Jun 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this can be nil pointer dereference. In that nil case we should use DefaultSpec

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I am reading the code correctly, the buildPlatforms field defaults to DefaultSpec if it's not set via ConvertOpt and targetPlatform is set to &buildPlatforms[0] if it's nil, before returning.

platformOpt seems to be set at the beginning of this function using buildPlatformOpt() which always seems to return a non-nil instance of the platformOpt type.

Would you prefer I add a nil pointer check here, in case buildPlatformOpt() changes at some point?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I read it wrong. This is not even a pointer, so it can't be nil.

@@ -1135,6 +1142,11 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
a = a.Copy(st, f, dest, opts...)
}
} else {
src, err = system.NormalizePath("/", src, d.platform.OS, false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still confused why are doing these path transformations twice. If we already are always converting paths to UNIX in Dockerfile before passing to the LLB then why are we still carrying OS value in LLB and doing extra conversions there.

Copy link
Collaborator Author

@gabriel-samfira gabriel-samfira Jun 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can remove the calls to NormalizePath() from client/llb and just use the paths as they come in, if you wish.

The current code in the master branch calls normalizePath(). This code is now incorporated into system.NormalizePath(), with additional logic to take into account the input platform.

Would you prefer we do away with this and just use path.Join() in toProtoAction() (example: https://github.com/moby/buildkit/blob/master/client/llb/fileop.go#L340) ?

It's fine with me, either way 😄

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, in the client I can just hardcode "linux" as the platform sent into NormalizePath() and it would behave just like the current normalizePath() in client/llb, but I am not sure ignoring the input platform (if set) is worth it. In any case, let me know what you prefer and I will make the change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't need NormalizePath calls inside client/llb to make windows work then better to remove it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can revert the change to that file and bring back normalizePath() as it is in the master branch, because that is somewhat still needed. Although that code is now incorporated in system.NormalizePath().

Will change in the morning. I need to see if anything breaks (with a clear head).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reverted most of the changes to the client. Please take another look.

@tonistiigi tonistiigi added this to the v0.12.0 milestone Jun 29, 2023
This change properly handles paths on different platforms. In short, this
change checks the target platform we're building an image for and applies
normalization steps to make sure the file paths are valid. This makes buildkit
properly handle paths on both *nix systems and on Windows.

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
 * Set default platform for scratch images
 * Set default platform constraint for FileOps if none is set via
   ConstraintOpt.
 * Explicitly set platform ConstraintOpt in dockerfile frontend based
   on dispatch state platform.

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
In dockerfile2llb we ensure we use proper path separators before we send
them to LLB.

In LLB we default to linux/amd64 if no constraints are set by the
caller.

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
When running on Linux, filepath.ToSlash() is not suitable to convert paths
containing Windows path delimiter to Unix delimiters. To enable this, we
export system.ToSlash() that takes a platform flag, based on which we use
the proper file path separator, regardless of the platform we run on.

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
wd := c.Path
if !path.IsAbs(c.Path) {
wd = path.Join("/", d.image.Config.WorkingDir, wd)
wd, err := system.NormalizeWorkdir(d.image.Config.WorkingDir, c.Path, d.platform.OS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up: Naming is confusing here NormalizeWorkdir returns OS-specific paths and NormalizePath Unix-only paths. "Normalize" should mean the same thing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am terrible at naming things, and can't really think of a better name right now. I did make sure that the doc string for the function does state explicitly that for Windows, we convert path delimiter with \. If it's alright with you, I'd like to revisit the name at a later time. Or if you have any suggestions for a better name, I'll gladly change it.

util/system/path.go Outdated Show resolved Hide resolved
frontend/dockerfile/dockerfile2llb/convert.go Outdated Show resolved Hide resolved
frontend/dockerfile/dockerfile2llb/convert.go Outdated Show resolved Hide resolved
frontend/dockerfile/dockerfile2llb/convert.go Outdated Show resolved Hide resolved
solver/llbsolver/file/backend.go Outdated Show resolved Hide resolved
client/llb/fileop.go Outdated Show resolved Hide resolved
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
@tonistiigi tonistiigi merged commit 595bfa2 into moby:master Jul 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants