moby / buildkit

concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit
https://github.com/moby/moby/issues/34227
Apache License 2.0
8.18k stars 1.16k forks source link

Divergent behavior when COPY tries to replace a symlink by a directory #2626

Open dereckson opened 2 years ago

dereckson commented 2 years ago

There are several other issues with symbolic links, but I'm not sure this one has already been reported.

An image provides a directory structure including a files/etc/service folder.

The /etc/service path already exists on the image at that step, but as a symbolic link.

The legacy build mode is happy to supersede the symlink by a directory, without any warning. BuildKit fails with a decent error message "cannot copy to non-directory" (decent enough it's easy to understand /etc/service existed, and not as a directory).

#11 [5/5] COPY files /
#11 sha256:335e11739def2fceb6f3b27b344e1634099fd311307c59064d902583cc5a15e1
#11 ERROR: cannot copy to non-directory: /var/lib/docker/overlay2/q4xakxgo6879bzw2t44ehm7j6/merged/etc/service
------
> [5/5] COPY files /:
------
error: failed to solve: rpc error: code = Unknown desc = cannot copy to non-directory: /var/lib/docker/overlay2/q4xakxgo6879bzw2t44ehm7j6/merged/etc/service
Build failed using Buildkit

On a legacy build, the image will be correctly built and the container has a folder:

$ file /etc/service
/etc/service: directory

A third scenario, not chosen by neither, could have been keep the symlink, follow it and populate target directory. That could lead to an image providing a full directory conf.d with the expectation it's only those files, to finally share them with previous existing files, because of the symlink.

Not sure the best approach on this one, could clearly be raise an error on legacy build mode instead of propagating a bug-as-a-feature into BuildKit.

rittneje commented 2 years ago

I feel the situation here is ambiguous. There are two possible things the user could mean:

  1. the incoming directory replaces the symlink itself
  2. the incoming directory is merged into the target of the symlink

Without a flag to COPY or something, there's no way to know what the intent was. This is similar to the issue I described in #1260. Curiously, in that case symlinks to files got followed, but that logic evidently is not applied to symlinks to directories.

A separate yet related issue is the matter of COPYing a directory onto a file (or vice versa), which buildkit does not allow.