Skip to content
This repository has been archived by the owner on Jun 13, 2021. It is now read-only.

Improve make -f docker.Makefile vendor #482

Merged
merged 4 commits into from
Mar 15, 2019

Conversation

simonferquel
Copy link
Contributor

- What I did

Initial work from @ijc brought a containerized vendoring which was great
but had a few issues:

  • did not work on Windows: dependencies containing symlinks could not be
    written by golang/dep on a cifs-mounted volume (wich is the case when
    mounting a windows dir in Docker Desktop)
  • slow: 2 reasons for that: doing the vendoring accross a cifs or osxfs
    bind mount was far from ideal due to FS latency and caching issues.
    There was no cache preservation between 2 calls.

This PR fixes those issues.

- How I did it

  • Not using bind mounts: we resolve the vendoring directly in the
    container RootFS, then copy back the vendor dir and the Gopkg.lock file
    (which might have been updated) to the host
  • Mounting a volume in which golang/dep will keep repo info and git
    clones in cache

Note that the dep-cache mount point and DEPCACHEDIR env var are
//dep-cache and not /dep-cache. From a Unix FS point of view both paths
are equivalent. But some windows shells (like the bash version shipped
with Git on Windows) tries to do clever conversions to win32 paths when detecting unix-style
paths in command line arguments, which makes docker trying to mount the
volume in "c:\Program Files\Git\bin..." which makes no sense in a Linux
container. Doubling the initial "/" disable this behavior.

- How to verify it

try make -f docker.Makefile on Windows

Initial work from @ijc brought a containerized vendoring which was great
but had a few issues:
- did not work on Windows: dependencies containing symlinks could not be
written by golang/dep on a cifs-mounted volume (wich is the case when
mounting a windows dir in Docker Desktop)
- slow: 2 reasons for that: doing the vendoring accross a cifs or osxfs
bind mount was far from ideal due to FS latency and caching issues.
There was no cache preservation between 2 calls.

This fix all those issues by:
- Not using bind mounts: we resolve the vendoring directly in the
container RootFS, then copy back the vendor dir and the Gopkg.lock file
(which might have been updated) to the host
- Mounting a volume in which golang/dep will keep repo info and git
clones in cache

Note that the dep-cache mount point and DEPCACHEDIR env var are
//dep-cache and not /dep-cache. From a Unix FS point of view both paths
are equivalent. But some windows shells (like the bash version shipped
with Git on Windows) tries to do clever conversions to win32 paths when detecting unix-style
paths in command line arguments, which makes docker trying to mount the
volume in "c:\Program Files\Git\bin\..." which makes no sense in a Linux
container. Doubling the initial "/" disable this behavior.

Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
Copy link
Contributor

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

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

LGTM 🐯
The only "flaw" here is that we may end up with a container still running at the end if something fails

@simonferquel
Copy link
Contributor Author

Yes, the exact reason why the target starts with an ugly docker rm -f docker-app-vendoring || echo ""
I suppose I should also add a target for cleaning the vendoring cache volume (in case of a dep crash / an untimely docker rm -f). I'll add a commit with that.

Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
@codecov
Copy link

codecov bot commented Mar 14, 2019

Codecov Report

Merging #482 into master will decrease coverage by 0.12%.
The diff coverage is 73.43%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master    #482      +/-   ##
=========================================
- Coverage   69.53%   69.4%   -0.13%     
=========================================
  Files          51      50       -1     
  Lines        2629    2553      -76     
=========================================
- Hits         1828    1772      -56     
+ Misses        569     545      -24     
- Partials      232     236       +4
Impacted Files Coverage Δ
internal/commands/inspect.go 78.37% <100%> (ø) ⬆️
internal/commands/uninstall.go 65.78% <50%> (-2.79%) ⬇️
internal/commands/status.go 71.42% <50%> (-3.58%) ⬇️
internal/commands/install.go 63.63% <50%> (-1.45%) ⬇️
internal/commands/upgrade.go 63.46% <70%> (-1.85%) ⬇️
internal/commands/cnab.go 68.99% <80.48%> (-10.21%) ⬇️
types/parameters/parameters.go 92.06% <0%> (-4.77%) ⬇️
internal/store/store.go 71.42% <0%> (-0.58%) ⬇️
internal/commands/dockerdesktop.go

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 622f5d9...2d70010. Read the comment docs.

docker.Makefile Outdated Show resolved Hide resolved
docker.Makefile Outdated
docker run --rm -v "$(CURDIR)":/go/src/github.com/docker/app/ $(DEV_IMAGE_NAME) make vendor
rm -rf ./vendor
docker rm -f docker-app-vendoring || echo ""
docker create --name docker-app-vendoring -v docker-app-vendor-cache://dep-cache -e DEPCACHEDIR=//dep-cache $(DEV_IMAGE_NAME) sh -c "rm -rf ./vendor && make vendor"
Copy link
Contributor

Choose a reason for hiding this comment

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

why the double //?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

mingw flavor of sh (which is used under the hood by make) tries to be clever and when calling a non-mingw executable (not sure how it does the distinction between vanilla win32 executable and those compiled in a mingw environment though), tries to expand any argument that looks like a unix path into the equivalent windows path. docker CLI is not a mingw executable, so it expands things like /dep-cache into c:\<path to mingw root\dep-cache. Using the double // disable this expansion mechanism.

Copy link
Contributor

Choose a reason for hiding this comment

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

😮, thanks for explaining!

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 rm -rf ./vendor needed? I see the makefile already removes;

app/Makefile

Line 109 in 151e1c5

$(call rmdir,vendor)

Copy link
Member

Choose a reason for hiding this comment

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

@simonferquel would be good to add a comment about the double slashes.

Copy link
Member

Choose a reason for hiding this comment

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

I think setting MSYS_NO_PATHCONV=1 disables the behavior

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

MSYS_NO_PATHCONV=1 disables this behavior with git bash. It does not seem to do it on my cygwin environment.
The only thing that seems to uniformally work (without causing problems for mounting), is the double "//".

I will comment on that

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the rm -rf ./vendor has to happen locally, so that we reset it with the equivalent coming from the container results (if we don't do it, we won't properly delete files that have been removed by the new vendoring)

Copy link
Contributor

Choose a reason for hiding this comment

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

It goes further than that:

$ touch vendor/github.com/docker/cli/cli/deleteme.go
$ dep ensure -v
# Bringing vendor into sync
(1/1) Wrote github.com/docker/cli (from https://github.com/chris-crone/cli)@d6bfd7e5592dad85969516c131d33910fa5ebd58: hash of vendored tree didn't match digest in Gopkg.lock

so if the package exists in vendor/ it insists that it is exactly what it wrote there previously (rather than, say, fixing it like you probably wanted it to!). I don't see any flag which changes this behaviour.

docker.Makefile Outdated Show resolved Hide resolved
docker create --name docker-app-vendoring -v docker-app-vendor-cache://dep-cache -e DEPCACHEDIR=//dep-cache $(DEV_IMAGE_NAME) sh -c "rm -rf ./vendor && make vendor"
docker start -i -a docker-app-vendoring
docker cp docker-app-vendoring:/go/src/github.com/docker/app/vendor .
docker cp docker-app-vendoring:/go/src/github.com/docker/app/Gopkg.lock .
Copy link
Contributor

Choose a reason for hiding this comment

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

Will it ever update the Gopkg.toml? I thought sometimes it did, I might be wrong though...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not the toml no, only the lock

docker.Makefile Outdated Show resolved Hide resolved
docker.Makefile Outdated
docker rm -f docker-app-vendoring || echo ""
docker create --name docker-app-vendoring -v docker-app-vendor-cache://dep-cache -e DEPCACHEDIR=//dep-cache $(DEV_IMAGE_NAME) sh -c "rm -rf ./vendor && make vendor"
docker start -i -a docker-app-vendoring
docker cp docker-app-vendoring:/go/src/github.com/docker/app/vendor .
docker cp docker-app-vendoring:/go/src/github.com/docker/app/Gopkg.lock .
docker rm -f docker-app-vendoring
git add Gopkg.lock vendor
Copy link
Contributor

Choose a reason for hiding this comment

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

The permissions changes on the vendored files (that I can't comment on directly so commenting here) are odd, I don't think they matter to us (we won't need to executre any of the scripts, and for one file the x bit looks spurious since it is a .go). My only concern is that it might be due to OS specific behaviour (i.e. Linux people might put these back), which only really matters because if the CI check (and I suppose we'll soon find out if that is a problem).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem here comes from git rmthat is slightly different that rm -rf.
On windows, when you modify an existing file, it will keep the mode of the original one and won't change it (as ntfs don't have a mode bitfield on files). git rm delete files and stage the deletion at the same time, which has the side effect of making a subsequent modification loose the previous version mode information.
I'll try to see if there is a flag for git rm that does not stage the deletion...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I reverted to rm -rf because I am worried at what can happen with symlinks as well.
I suggest we make a note about "you should use the make version that comes with mingw" for better script compatibility

Copy link
Contributor

Choose a reason for hiding this comment

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

WFM if it WFYou

Copy link
Contributor

@ijc ijc left a comment

Choose a reason for hiding this comment

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

In general I like the approach, I had a few questions/comments though...

Copy link
Contributor

@ijc ijc left a comment

Choose a reason for hiding this comment

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

I seem to have a mixture of direct and in-review comments, sorry about that. Here are the ones I thought I was posting just now...

@ijc
Copy link
Contributor

ijc commented Mar 14, 2019

Urk, I don't know wtf is going on with how those comments are presented (I did a per-commit review in different tags, but normally that DTRT, although I may have accidentally clicked "single comment" once or twice). Anyway, I think all of my comments have been captured in some form above.

Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
docker.Makefile Outdated
rm -rf ./vendor
docker rm -f docker-app-vendoring || true
docker create --name docker-app-vendoring -v docker-app-vendor-cache://dep-cache -e DEPCACHEDIR=//dep-cache $(DEV_IMAGE_NAME) sh -c "rm -rf ./vendor && make vendor"
docker start -i -a docker-app-vendoring
Copy link
Member

Choose a reason for hiding this comment

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

the container doesn't have to be running to use docker cp

Copy link
Member

Choose a reason for hiding this comment

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

Oh, nvm, this start triggers the actual vendoring, right?

An alternative would be to do the vendoring in docker build (in a build-stage). If you're using buildkit, you could even use a RUN --mount

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we aren't quite ready (in CI at least, if not on contributors machines) to mandate buildkit.

Copy link
Member

Choose a reason for hiding this comment

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

Hm, yeah. And with the legacy builder, the caching would be written to the container's filesystem (so likely slower).

Still curious why docker create and docker start are done separately; could just docker run the container (and have it exit afterwards)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I removed the create and collapsed everything in start (in upcoming commit)

docker.Makefile Outdated
docker run --rm -v "$(CURDIR)":/go/src/github.com/docker/app/ $(DEV_IMAGE_NAME) make vendor
rm -rf ./vendor
docker rm -f docker-app-vendoring || echo ""
docker create --name docker-app-vendoring -v docker-app-vendor-cache://dep-cache -e DEPCACHEDIR=//dep-cache $(DEV_IMAGE_NAME) sh -c "rm -rf ./vendor && make vendor"
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 rm -rf ./vendor needed? I see the makefile already removes;

app/Makefile

Line 109 in 151e1c5

$(call rmdir,vendor)

docker.Makefile Outdated
@@ -110,9 +110,19 @@ lint: ## run linter(s)

vendor: build_dev_image
$(info Update Vendoring...)
docker run --rm -v "$(CURDIR)":/go/src/github.com/docker/app/ $(DEV_IMAGE_NAME) make vendor
rm -rf ./vendor
Copy link
Member

Choose a reason for hiding this comment

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

Should this be put after the container completes vendoring? (i.e. in case the container fails, your local vendor is gone)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

docker.Makefile Outdated
rm -rf ./vendor
docker rm -f docker-app-vendoring || true
docker create --name docker-app-vendoring -v docker-app-vendor-cache://dep-cache -e DEPCACHEDIR=//dep-cache $(DEV_IMAGE_NAME) sh -c "rm -rf ./vendor && make vendor"
docker start -i -a docker-app-vendoring
Copy link
Member

Choose a reason for hiding this comment

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

Hm, yeah. And with the legacy builder, the caching would be written to the container's filesystem (so likely slower).

Still curious why docker create and docker start are done separately; could just docker run the container (and have it exit afterwards)

Signed-off-by: Simon Ferquel <simon.ferquel@docker.com>
@simonferquel
Copy link
Contributor Author

@ijc @thaJeztah, just updated the PR. PTAL.
I kept using the double "//" as the MSYS_* env var does not apply to my current mingw make environment...
Imerged create/start into a single run, and I added the possibility to pass arguments (usefull for things like -update golang/x/sys etc.)

@ijc ijc merged commit c22dc40 into docker:master Mar 15, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants