Skip to content

Commit c44e7a3

Browse files
committed
Pass upstream client's user agent through to registry on operations beyond pulls
This adds support for the passthrough on build, push, login, and search. Revamp the integration test to cover these cases and make it more robust. Use backticks instead of quoted strings for backslash-heavy string contstands. Signed-off-by: Aaron Lehmann <[email protected]>
1 parent 278d396 commit c44e7a3

File tree

17 files changed

+109
-70
lines changed

17 files changed

+109
-70
lines changed

api/client/cli.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
140140
if customHeaders == nil {
141141
customHeaders = map[string]string{}
142142
}
143-
customHeaders["User-Agent"] = "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")"
143+
customHeaders["User-Agent"] = clientUserAgent()
144144

145145
verStr := api.DefaultVersion.String()
146146
if tmpStr := os.Getenv("DOCKER_API_VERSION"); tmpStr != "" {
@@ -209,3 +209,7 @@ func newHTTPClient(host string, tlsOptions *tlsconfig.Options) (*http.Client, er
209209
Transport: tr,
210210
}, nil
211211
}
212+
213+
func clientUserAgent() string {
214+
return "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")"
215+
}

api/client/trust.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"github.com/docker/distribution/registry/client/transport"
2424
"github.com/docker/docker/cliconfig"
2525
"github.com/docker/docker/distribution"
26-
"github.com/docker/docker/dockerversion"
2726
"github.com/docker/docker/pkg/jsonmessage"
2827
flag "github.com/docker/docker/pkg/mflag"
2928
"github.com/docker/docker/reference"
@@ -152,7 +151,7 @@ func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, aut
152151
}
153152

154153
// Skip configuration headers since request is not going to Docker daemon
155-
modifiers := registry.DockerHeaders(dockerversion.DockerUserAgent(""), http.Header{})
154+
modifiers := registry.DockerHeaders(clientUserAgent(), http.Header{})
156155
authTransport := transport.NewTransport(base, modifiers...)
157156
pingClient := &http.Client{
158157
Transport: authTransport,

api/server/router/build/backend.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package build
22

33
import (
4+
"io"
5+
46
"github.com/docker/docker/builder"
57
"github.com/docker/engine-api/types"
6-
"io"
8+
"golang.org/x/net/context"
79
)
810

911
// Backend abstracts an image builder whose only purpose is to build an image referenced by an imageID.
@@ -14,5 +16,5 @@ type Backend interface {
1416
// by the caller.
1517
//
1618
// TODO: make this return a reference instead of string
17-
Build(config *types.ImageBuildOptions, context builder.Context, stdout io.Writer, stderr io.Writer, out io.Writer, clientGone <-chan bool) (string, error)
19+
Build(clientCtx context.Context, config *types.ImageBuildOptions, context builder.Context, stdout io.Writer, stderr io.Writer, out io.Writer, clientGone <-chan bool) (string, error)
1820
}

api/server/router/build/build_routes.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *
171171
closeNotifier = notifier.CloseNotify()
172172
}
173173

174-
imgID, err := br.backend.Build(buildOptions,
174+
imgID, err := br.backend.Build(ctx, buildOptions,
175175
builder.DockerIgnoreContext{ModifiableContext: context},
176176
stdout, stderr, out,
177177
closeNotifier)

api/server/router/image/backend.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ type importExportBackend interface {
3939

4040
type registryBackend interface {
4141
PullImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
42-
PushImage(ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
43-
SearchRegistryForImages(term string, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
42+
PushImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
43+
SearchRegistryForImages(ctx context.Context, term string, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
4444
}

api/server/router/image/image_routes.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter,
228228

229229
w.Header().Set("Content-Type", "application/json")
230230

231-
if err := s.backend.PushImage(ref, metaHeaders, authConfig, output); err != nil {
231+
if err := s.backend.PushImage(ctx, ref, metaHeaders, authConfig, output); err != nil {
232232
if !output.Flushed() {
233233
return err
234234
}
@@ -373,7 +373,7 @@ func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter
373373
headers[k] = v
374374
}
375375
}
376-
query, err := s.backend.SearchRegistryForImages(r.Form.Get("term"), config, headers)
376+
query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("term"), config, headers)
377377
if err != nil {
378378
return err
379379
}

api/server/router/system/backend.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/docker/engine-api/types"
55
"github.com/docker/engine-api/types/events"
66
"github.com/docker/engine-api/types/filters"
7+
"golang.org/x/net/context"
78
)
89

910
// Backend is the methods that need to be implemented to provide
@@ -13,5 +14,5 @@ type Backend interface {
1314
SystemVersion() types.Version
1415
SubscribeToEvents(since, sinceNano int64, ef filters.Args) ([]events.Message, chan interface{})
1516
UnsubscribeFromEvents(chan interface{})
16-
AuthenticateToRegistry(authConfig *types.AuthConfig) (string, string, error)
17+
AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error)
1718
}

api/server/router/system/system_routes.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func (s *systemRouter) postAuth(ctx context.Context, w http.ResponseWriter, r *h
115115
if err != nil {
116116
return err
117117
}
118-
status, token, err := s.backend.AuthenticateToRegistry(config)
118+
status, token, err := s.backend.AuthenticateToRegistry(ctx, config)
119119
if err != nil {
120120
return err
121121
}

builder/builder.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/docker/docker/reference"
1313
"github.com/docker/engine-api/types"
1414
"github.com/docker/engine-api/types/container"
15+
"golang.org/x/net/context"
1516
)
1617

1718
const (
@@ -109,7 +110,7 @@ type Backend interface {
109110
// Tag an image with newTag
110111
TagImage(newTag reference.Named, imageName string) error
111112
// Pull tells Docker to pull image referenced by `name`.
112-
PullOnBuild(name string, authConfigs map[string]types.AuthConfig, output io.Writer) (Image, error)
113+
PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (Image, error)
113114
// ContainerAttach attaches to container.
114115
ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error
115116
// ContainerCreate creates a new Docker container and returns potential warnings

builder/dockerfile/builder.go

+9-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/docker/docker/reference"
1818
"github.com/docker/engine-api/types"
1919
"github.com/docker/engine-api/types/container"
20+
"golang.org/x/net/context"
2021
)
2122

2223
var validCommitCommands = map[string]bool{
@@ -52,8 +53,9 @@ type Builder struct {
5253
Stderr io.Writer
5354
Output io.Writer
5455

55-
docker builder.Backend
56-
context builder.Context
56+
docker builder.Backend
57+
context builder.Context
58+
clientCtx context.Context
5759

5860
dockerfile *parser.Node
5961
runConfig *container.Config // runconfig for cmd, run, entrypoint etc.
@@ -86,14 +88,15 @@ func NewBuildManager(b builder.Backend) (bm *BuildManager) {
8688
// NewBuilder creates a new Dockerfile builder from an optional dockerfile and a Config.
8789
// If dockerfile is nil, the Dockerfile specified by Config.DockerfileName,
8890
// will be read from the Context passed to Build().
89-
func NewBuilder(config *types.ImageBuildOptions, backend builder.Backend, context builder.Context, dockerfile io.ReadCloser) (b *Builder, err error) {
91+
func NewBuilder(clientCtx context.Context, config *types.ImageBuildOptions, backend builder.Backend, context builder.Context, dockerfile io.ReadCloser) (b *Builder, err error) {
9092
if config == nil {
9193
config = new(types.ImageBuildOptions)
9294
}
9395
if config.BuildArgs == nil {
9496
config.BuildArgs = make(map[string]string)
9597
}
9698
b = &Builder{
99+
clientCtx: clientCtx,
97100
options: config,
98101
Stdout: os.Stdout,
99102
Stderr: os.Stderr,
@@ -158,8 +161,8 @@ func sanitizeRepoAndTags(names []string) ([]reference.Named, error) {
158161
}
159162

160163
// Build creates a NewBuilder, which builds the image.
161-
func (bm *BuildManager) Build(config *types.ImageBuildOptions, context builder.Context, stdout io.Writer, stderr io.Writer, out io.Writer, clientGone <-chan bool) (string, error) {
162-
b, err := NewBuilder(config, bm.backend, context, nil)
164+
func (bm *BuildManager) Build(clientCtx context.Context, config *types.ImageBuildOptions, context builder.Context, stdout io.Writer, stderr io.Writer, out io.Writer, clientGone <-chan bool) (string, error) {
165+
b, err := NewBuilder(clientCtx, config, bm.backend, context, nil)
163166
if err != nil {
164167
return "", err
165168
}
@@ -291,7 +294,7 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con
291294
}
292295
}
293296

294-
b, err := NewBuilder(nil, nil, nil, nil)
297+
b, err := NewBuilder(context.Background(), nil, nil, nil, nil)
295298
if err != nil {
296299
return nil, err
297300
}

builder/dockerfile/dispatchers.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ func from(b *Builder, args []string, attributes map[string]bool, original string
206206
// TODO: shouldn't we error out if error is different from "not found" ?
207207
}
208208
if image == nil {
209-
image, err = b.docker.PullOnBuild(name, b.options.AuthConfigs, b.Output)
209+
image, err = b.docker.PullOnBuild(b.clientCtx, name, b.options.AuthConfigs, b.Output)
210210
if err != nil {
211211
return err
212212
}

daemon/daemon.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -1030,7 +1030,7 @@ func (daemon *Daemon) PullImage(ctx context.Context, ref reference.Named, metaHe
10301030
}
10311031

10321032
// PullOnBuild tells Docker to pull image referenced by `name`.
1033-
func (daemon *Daemon) PullOnBuild(name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
1033+
func (daemon *Daemon) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
10341034
ref, err := reference.ParseNamed(name)
10351035
if err != nil {
10361036
return nil, err
@@ -1052,7 +1052,7 @@ func (daemon *Daemon) PullOnBuild(name string, authConfigs map[string]types.Auth
10521052
pullRegistryAuth = &resolvedConfig
10531053
}
10541054

1055-
if err := daemon.PullImage(context.Background(), ref, nil, pullRegistryAuth, output); err != nil {
1055+
if err := daemon.PullImage(ctx, ref, nil, pullRegistryAuth, output); err != nil {
10561056
return nil, err
10571057
}
10581058
return daemon.GetImage(name)
@@ -1069,14 +1069,14 @@ func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
10691069
}
10701070

10711071
// PushImage initiates a push operation on the repository named localName.
1072-
func (daemon *Daemon) PushImage(ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
1072+
func (daemon *Daemon) PushImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
10731073
// Include a buffer so that slow client connections don't affect
10741074
// transfer performance.
10751075
progressChan := make(chan progress.Progress, 100)
10761076

10771077
writesDone := make(chan struct{})
10781078

1079-
ctx, cancelFunc := context.WithCancel(context.Background())
1079+
ctx, cancelFunc := context.WithCancel(ctx)
10801080

10811081
go func() {
10821082
writeDistributionProgress(cancelFunc, outStream, progressChan)
@@ -1502,16 +1502,16 @@ func configureVolumes(config *Config, rootUID, rootGID int) (*store.VolumeStore,
15021502
}
15031503

15041504
// AuthenticateToRegistry checks the validity of credentials in authConfig
1505-
func (daemon *Daemon) AuthenticateToRegistry(authConfig *types.AuthConfig) (string, string, error) {
1506-
return daemon.RegistryService.Auth(authConfig, dockerversion.DockerUserAgent(""))
1505+
func (daemon *Daemon) AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error) {
1506+
return daemon.RegistryService.Auth(authConfig, dockerversion.DockerUserAgent(ctx))
15071507
}
15081508

15091509
// SearchRegistryForImages queries the registry for images matching
15101510
// term. authConfig is used to login.
1511-
func (daemon *Daemon) SearchRegistryForImages(term string,
1511+
func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, term string,
15121512
authConfig *types.AuthConfig,
15131513
headers map[string][]string) (*registrytypes.SearchResults, error) {
1514-
return daemon.RegistryService.Search(term, authConfig, dockerversion.DockerUserAgent(""), headers)
1514+
return daemon.RegistryService.Search(term, authConfig, dockerversion.DockerUserAgent(ctx), headers)
15151515
}
15161516

15171517
// IsShuttingDown tells whether the daemon is shutting down or not

distribution/pull_v1.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) error {
4949
tr := transport.NewTransport(
5050
// TODO(tiborvass): was ReceiveTimeout
5151
registry.NewTransport(tlsConfig),
52-
registry.DockerHeaders(dockerversion.DockerUserAgent(""), p.config.MetaHeaders)...,
52+
registry.DockerHeaders(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders)...,
5353
)
5454
client := registry.HTTPClient(tr)
55-
v1Endpoint, err := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(""), p.config.MetaHeaders)
55+
v1Endpoint, err := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders)
5656
if err != nil {
5757
logrus.Debugf("Could not get v1 endpoint: %v", err)
5858
return fallbackError{err: err}

distribution/push_v1.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ func (p *v1Pusher) Push(ctx context.Context) error {
3838
tr := transport.NewTransport(
3939
// TODO(tiborvass): was NoTimeout
4040
registry.NewTransport(tlsConfig),
41-
registry.DockerHeaders(dockerversion.DockerUserAgent(""), p.config.MetaHeaders)...,
41+
registry.DockerHeaders(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders)...,
4242
)
4343
client := registry.HTTPClient(tr)
44-
v1Endpoint, err := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(""), p.config.MetaHeaders)
44+
v1Endpoint, err := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders)
4545
if err != nil {
4646
logrus.Debugf("Could not get v1 endpoint: %v", err)
4747
return fallbackError{err: err}

distribution/registry.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ func (dcs dumbCredentialStore) SetRefreshToken(*url.URL, string, string) {
3737
// providing timeout settings and authentication support, and also verifies the
3838
// remote API version.
3939
func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string) (repo distribution.Repository, foundVersion bool, err error) {
40-
upstreamUA := dockerversion.GetUserAgentFromContext(ctx)
41-
4240
repoName := repoInfo.FullName()
4341
// If endpoint does not support CanonicalName, use the RemoteName instead
4442
if endpoint.TrimHostname {
@@ -59,7 +57,7 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
5957
DisableKeepAlives: true,
6058
}
6159

62-
modifiers := registry.DockerHeaders(dockerversion.DockerUserAgent(upstreamUA), metaHeaders)
60+
modifiers := registry.DockerHeaders(dockerversion.DockerUserAgent(ctx), metaHeaders)
6361
authTransport := transport.NewTransport(base, modifiers...)
6462

6563
challengeManager, foundVersion, err := registry.PingV2Registry(endpoint, authTransport)

dockerversion/useragent.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
// DockerUserAgent is the User-Agent the Docker client uses to identify itself.
1414
// In accordance with RFC 7231 (5.5.3) is of the form:
1515
// [docker client's UA] UpstreamClient([upstream client's UA])
16-
func DockerUserAgent(upstreamUA string) string {
16+
func DockerUserAgent(ctx context.Context) string {
1717
httpVersion := make([]useragent.VersionInfo, 0, 6)
1818
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "docker", Version: Version})
1919
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "go", Version: runtime.Version()})
@@ -25,15 +25,16 @@ func DockerUserAgent(upstreamUA string) string {
2525
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "arch", Version: runtime.GOARCH})
2626

2727
dockerUA := useragent.AppendVersions("", httpVersion...)
28+
upstreamUA := getUserAgentFromContext(ctx)
2829
if len(upstreamUA) > 0 {
2930
ret := insertUpstreamUserAgent(upstreamUA, dockerUA)
3031
return ret
3132
}
3233
return dockerUA
3334
}
3435

35-
// GetUserAgentFromContext returns the previously saved user-agent context stored in ctx, if one exists
36-
func GetUserAgentFromContext(ctx context.Context) string {
36+
// getUserAgentFromContext returns the previously saved user-agent context stored in ctx, if one exists
37+
func getUserAgentFromContext(ctx context.Context) string {
3738
var upstreamUA string
3839
if ctx != nil {
3940
var ki interface{} = ctx.Value(httputils.UAStringKey)
@@ -51,7 +52,7 @@ func escapeStr(s string, charsToEscape string) string {
5152
appended := false
5253
for _, escapeableRune := range charsToEscape {
5354
if currRune == escapeableRune {
54-
ret += "\\" + string(currRune)
55+
ret += `\` + string(currRune)
5556
appended = true
5657
break
5758
}
@@ -67,7 +68,7 @@ func escapeStr(s string, charsToEscape string) string {
6768
// string of the form:
6869
// $dockerUA UpstreamClient($upstreamUA)
6970
func insertUpstreamUserAgent(upstreamUA string, dockerUA string) string {
70-
charsToEscape := "();\\" //["\\", ";", "(", ")"]string
71+
charsToEscape := `();\`
7172
upstreamUAEscaped := escapeStr(upstreamUA, charsToEscape)
7273
return fmt.Sprintf("%s UpstreamClient(%s)", dockerUA, upstreamUAEscaped)
7374
}

0 commit comments

Comments
 (0)