Skip to content

Pass upstream client's user agent through to registry on image pulls#21306

Merged
icecrime merged 1 commit intomasterfrom
unknown repository
Mar 21, 2016
Merged

Pass upstream client's user agent through to registry on image pulls#21306
icecrime merged 1 commit intomasterfrom
unknown repository

Conversation

@ghost
Copy link
Copy Markdown

@ghost ghost commented Mar 17, 2016

Summary: pass through to registry an upstream user-agent on image pull. Closes issue #20001.

What I did: People running Registry servers should be able to see what client initiated an image pull per issue #20001. For example, if Compose instructs Engine to pull from a local Registry, then the User-Agent header sent to that Registry server should be the following RFC 7231 compliant [1] string concatenation:

User-Agent:  [Docker Compose UA] upstream-ua/end; [Docker Engine UA]

upstream-ua/end; is just a delimiter. (Open to suggestions for a better one.)

How I did it: When Engine receives an image pull API request, I set a golang.org/x/net/context context inside the closure returned by NewUserAgentMiddleware() in api/server/middleware/user_agent.go. This context includes the upstream client's UA string (e.g., Compose's UA string). From there, I just plumbed the context variable all the way through the request processing chain until DockerUserAgent() is called. DockerUserAgent() was modified to accept an optional string argument, which is read back from the context by the caller.

How to verify it:

  • Run a Registry server on port 5000 of localhost:
docker run -d -p 5000:5000 --restart=always --name registry registry:2

See [2] for more information.

  • In a separate terminal, tail the registry server's logs:
docker logs -f [REGISTRY CONTAINER ID]
  • Put an image in your empty registry like this:
docker pull hello-world
docker tag hello-world localhost:5000/hello-world
docker push localhost:5000/hello-world
  • Do an image pull from your registry via Engine but using a custom User-Agent header:
curl -X POST -A "Goelzer/99.9 (Goelzer; en-us)" "http://127.0.0.1:2375/images/create?fromImage=localhost:5000/hello-world" -H "X-Registry-Auth: e30="
  • Observe this User-Agent string in the registry log:
172.17.42.1 - - [14/Mar/2016:23:09:09 +0000] "GET /v2/hello-world/manifests/latest HTTP/1.1" 200 708 "" "Goelzer/99.9 (Goelzer; en-us) upstream-ua/end; docker/1.11.0-dev go/go1.6 git-commit/xxxxxx-unsupported kernel/4.2.0-30-generic os/linux arch/amd64"

Refs:
[1] RFC 7231: http://tools.ietf.org/html/rfc7231#section-5.5.3
[2] https://github.com/docker/distribution/blob/master/docs/deploying.md

@calavera calavera changed the title Alternative to PR #21197 Pass upstream client's user agent through to registry on image pulls Mar 18, 2016
@calavera
Copy link
Copy Markdown
Contributor

Design LGTM

daemon/daemon.go Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should use context.Background() if we're not going to handle the user agent in this case.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed

@icecrime icecrime added this to the 1.11.0 milestone Mar 18, 2016
@icecrime
Copy link
Copy Markdown
Contributor

Thanks! I haven't tested, but from a rapid overview code LGTM (I do think the dependency graph in this PR puts us in a better position to split out the server-side API code out of the repo). 👍

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Could this check for the expected client UA? Perhaps with a permissing regexp so the test isn't tied to a particular client version?

@aaronlehmann
Copy link
Copy Markdown

I probably should have throught of this earlier, but wouldn't it make sense to pass through the client user agent on pushes, searches, and login operations as well? It feels like that would be a lot more consistent.

@ghost
Copy link
Copy Markdown
Author

ghost commented Mar 18, 2016

@aaronlehmann Yes, I want to do this in a future PR. This PR is intended as an incremental step to get to that point.

@aaronlehmann
Copy link
Copy Markdown

I understand the desire to make incremental progress, but wouldn't it be awkward for registry operators gathering these statistics if some Engine versions include the client user agent during all operations, and some only do it for pulls?

I'd be happy to help make these changes for the other operations if time is an issue.

@aaronlehmann
Copy link
Copy Markdown

I have a local branch that extends this to support push, login, and search: aaronlehmann@41f3d16. This also makes some improvements to the integration test.

I didn't go through the effort to support it in build. That's somewhat more complex because it requires plumbing a context all the way through the builder. One could argue, though, that the pull operation in this case isn't being triggered directly by a client, so it's reasonable to only include the daemon user agent.

Let's finalize this PR first (@mgoelzer, I know you're looking to tweak the User-Agent format), and then I can rebase my changes on top of what gets merged and open a separate PR.

@ghost
Copy link
Copy Markdown
Author

ghost commented Mar 18, 2016

I have updated the PR to use the following format (suggested to me by @stevvooe):

User-Agent:  [Docker Engine UA] UPSTREAM-CLIENT([Upstream Client UA])

where [Upstream Client UA] has backslash escaped each of these characters: ();

To parse it, one would presumably match on ".+ UPSTREAM-CLIENT([^)])", extract the right-hand side substring (stuff between the parens), unescape it and then process as needed.

Any feedback on the new format? @aaronlehmann @calavera

@anusha-ragunathan
Copy link
Copy Markdown
Contributor

docker run, similar to docker build can also trigger pulling images. If we dont plan to distinguish a user-initiated interaction with registry vs an indirect interaction with registry, these cases should be handled.

@ghost
Copy link
Copy Markdown
Author

ghost commented Mar 19, 2016

@anusha-ragunathan Good point, I had not thought of that but I agree we should handle it. I think if we merge this PR and then merge @aaronlehmann 's (as of yet uncreated) PR for push, login and search, then I can fix PullOnBuild() and the "pull on run" case in a subsequent PR. What do you think? I am hopeful about getting at least regular pull, push, login and search in for Docker 1.11 even if it's not perfect yet.

@ghost
Copy link
Copy Markdown
Author

ghost commented Mar 19, 2016

I just updated the PR with a better test given that the format of the UA "concatenation" has changed to a backslash-escaped, embedded string.

I know the test is not perfect and only looks at the beginning of each string, but I don't want to make the integration tests too brittle. Open to suggestions if anyone has them.

@aaronlehmann
@calavera
@anusha-ragunathan

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Typo: should be insertUpstreamUserAgent.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thank you, fixed.

@aaronlehmann
Copy link
Copy Markdown

I know the test is not perfect and only looks at the beginning of each string, but I don't want to make the integration tests too brittle. Open to suggestions if anyone has them.

The main problem that I see with the test is that it will succeed even if the mock handler is never hit for some reason. I've corrected this in my followup PR, so don't worry about it in this one.

@ghost
Copy link
Copy Markdown
Author

ghost commented Mar 19, 2016

Hi @aaronlehmann, I see how your branch improves the test significantly, so I'm going to defer to your PR on that.

@ghost
Copy link
Copy Markdown
Author

ghost commented Mar 20, 2016

win2lin failing because no space left on device

08:20:01 Step 1 : FROM docker:c51be1c
08:20:01  ---> 196dc052f06d
08:20:01 Step 2 : RUN hack/make.sh binary
08:20:01  ---> Running in 9ba5097ee5e9
08:20:02 Removing intermediate container 9ba5097ee5e9
08:20:02 Cannot start container 9ba5097ee5e98a0cadf1f66fff59ef01d5b027b3d33ad5421bae2908230dd526: [9] System error: mkdir /var/run/docker/execdriver/native/9ba5097ee5e98a0cadf1f66fff59ef01d5b027b3d33ad5421bae2908230dd526: no space left on device

windowsTP4 failing because HCSShim::StartComputeSystem failed in Win32

08:20:57 Step 5 : COPY . /go/src/github.com/docker/docker
08:21:12  ---> e047932207ea
08:21:12 Removing intermediate container 3773633fe385
08:21:12 Successfully built e047932207ea
08:21:12 + lastec=0
08:21:12 + set +x
08:21:12 INFO: Building the test binary...
08:21:12 + docker run --rm -v 'd:\CI\CI-9d11227:c:\target' docker sh -c 'cd /c/go/src/github.com/docker/docker; \
08:21:12    hack/make.sh binary; \
08:21:12    ec=$?; \
08:21:12    if [ $ec -eq 0 ]; then \
08:21:12        robocopy /c/go/src/github.com/docker/docker/bundles/$(cat VERSION)/binary /c/target/binary; \
08:21:12    fi; \
08:21:12    exit $ec'
08:21:13 C:\Windows\system32\docker.exe: Error response from daemon: Cannot start container 85a5523803a71e6e33c3c79f362b060ec6632d0e70d5a3578c332334c6635d56: HCSShim::StartComputeSystem failed in Win32: The object identifier does not represent a valid object. (0x10d8) id=85a5523803a71e6e33c3c79f362b060ec6632d0e70d5a3578c332334c6635d56.

All other tests passing.

Changes how the Engine interacts with Registry servers on image pull.
Previously, Engine sent a User-Agent string to the Registry server
that included only the Engine's version information.  This commit
appends to that string the fields from the User-Agent sent by the
client (e.g., Compose) of the Engine.  This allows Registry server
operators to understand what tools are actually generating pulls on
their registries.

Signed-off-by: Mike Goelzer <mgoelzer@docker.com>
@icecrime
Copy link
Copy Markdown
Contributor

@mgoelzer Sorry for this, the win2lin issue is known and being worked on, the windowsTP4 unfortunately has no solution but wait for a better TP release. I'll rebuild, we'll get it green.

@ghost
Copy link
Copy Markdown
Author

ghost commented Mar 21, 2016

@icecrime, no problem. I'm just posting the output of failing tests so we have a log of what's failing. But since it seems to be sporadic, there's probably no point.

@anusha-ragunathan
Copy link
Copy Markdown
Contributor

@mgoelzer : I'm okay with you fixing pull on build/run in a subsequent PR. It'd be nice to get that in 1.11 as well, but I understand if there are timing constraints.

@ghost
Copy link
Copy Markdown
Author

ghost commented Mar 21, 2016

@icecrime If we can get this merged today, @aaronlehmann has a second PR he will open that adds the User-Agent stuff to login, push and search. Can we just disregard the failing TP4 test?

@aaronlehmann
Copy link
Copy Markdown

LGTM

appended := false
for _, escapeableRune := range charsToEscape {
if currRune == escapeableRune {
ret += "\\" + string(currRune)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is this double escaped? If it encounters ;, it should emit \;. If it encounters \, it should emit \\.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Not sure what you mean by double escaped. The string literal "\" is a one-character string. So, encountering ; will cause it to append to ret \;, like you're saying.

EDIT: ironically, I can't say what I want because github also escapes backslashes :-)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rather, use a bactic literal here for clarity.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

To save CI churn, I'd be happy to make this change in my followup PR.

@anusha-ragunathan
Copy link
Copy Markdown
Contributor

LGTM

1 similar comment
@stevvooe
Copy link
Copy Markdown
Contributor

LGTM

icecrime pushed a commit that referenced this pull request Mar 21, 2016
Pass upstream client's user agent through to registry on image pulls
@icecrime icecrime merged commit 278d396 into moby:master Mar 21, 2016
@aaronlehmann
Copy link
Copy Markdown

Opened the companion PR to extend this to build, login, push, and search here: #21373

@rogaha
Copy link
Copy Markdown
Contributor

rogaha commented May 13, 2016

👍

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.

8 participants