From c44e7a3e632c3ea961cb8c12ba45371f54e6699c Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Fri, 18 Mar 2016 14:42:40 -0700 Subject: [PATCH] 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 --- api/client/cli.go | 6 +- api/client/trust.go | 3 +- api/server/router/build/backend.go | 6 +- api/server/router/build/build_routes.go | 2 +- api/server/router/image/backend.go | 4 +- api/server/router/image/image_routes.go | 4 +- api/server/router/system/backend.go | 3 +- api/server/router/system/system_routes.go | 2 +- builder/builder.go | 3 +- builder/dockerfile/builder.go | 15 ++-- builder/dockerfile/dispatchers.go | 2 +- daemon/daemon.go | 16 ++-- distribution/pull_v1.go | 4 +- distribution/push_v1.go | 4 +- distribution/registry.go | 4 +- dockerversion/useragent.go | 11 +-- .../docker_cli_registry_user_agent_test.go | 90 ++++++++++++------- 17 files changed, 109 insertions(+), 70 deletions(-) diff --git a/api/client/cli.go b/api/client/cli.go index e49c5351d5..6c673da42f 100644 --- a/api/client/cli.go +++ b/api/client/cli.go @@ -140,7 +140,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF if customHeaders == nil { customHeaders = map[string]string{} } - customHeaders["User-Agent"] = "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")" + customHeaders["User-Agent"] = clientUserAgent() verStr := api.DefaultVersion.String() if tmpStr := os.Getenv("DOCKER_API_VERSION"); tmpStr != "" { @@ -209,3 +209,7 @@ func newHTTPClient(host string, tlsOptions *tlsconfig.Options) (*http.Client, er Transport: tr, }, nil } + +func clientUserAgent() string { + return "Docker-Client/" + dockerversion.Version + " (" + runtime.GOOS + ")" +} diff --git a/api/client/trust.go b/api/client/trust.go index d897872ca3..fe23c2f9d3 100644 --- a/api/client/trust.go +++ b/api/client/trust.go @@ -23,7 +23,6 @@ import ( "github.com/docker/distribution/registry/client/transport" "github.com/docker/docker/cliconfig" "github.com/docker/docker/distribution" - "github.com/docker/docker/dockerversion" "github.com/docker/docker/pkg/jsonmessage" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/reference" @@ -152,7 +151,7 @@ func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, aut } // Skip configuration headers since request is not going to Docker daemon - modifiers := registry.DockerHeaders(dockerversion.DockerUserAgent(""), http.Header{}) + modifiers := registry.DockerHeaders(clientUserAgent(), http.Header{}) authTransport := transport.NewTransport(base, modifiers...) pingClient := &http.Client{ Transport: authTransport, diff --git a/api/server/router/build/backend.go b/api/server/router/build/backend.go index 6d8fca3c62..839f316088 100644 --- a/api/server/router/build/backend.go +++ b/api/server/router/build/backend.go @@ -1,9 +1,11 @@ package build import ( + "io" + "github.com/docker/docker/builder" "github.com/docker/engine-api/types" - "io" + "golang.org/x/net/context" ) // Backend abstracts an image builder whose only purpose is to build an image referenced by an imageID. @@ -14,5 +16,5 @@ type Backend interface { // by the caller. // // TODO: make this return a reference instead of string - Build(config *types.ImageBuildOptions, context builder.Context, stdout io.Writer, stderr io.Writer, out io.Writer, clientGone <-chan bool) (string, error) + Build(clientCtx context.Context, config *types.ImageBuildOptions, context builder.Context, stdout io.Writer, stderr io.Writer, out io.Writer, clientGone <-chan bool) (string, error) } diff --git a/api/server/router/build/build_routes.go b/api/server/router/build/build_routes.go index 0025c85a93..a6ab71f57d 100644 --- a/api/server/router/build/build_routes.go +++ b/api/server/router/build/build_routes.go @@ -171,7 +171,7 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r * closeNotifier = notifier.CloseNotify() } - imgID, err := br.backend.Build(buildOptions, + imgID, err := br.backend.Build(ctx, buildOptions, builder.DockerIgnoreContext{ModifiableContext: context}, stdout, stderr, out, closeNotifier) diff --git a/api/server/router/image/backend.go b/api/server/router/image/backend.go index c0bf3aeba5..dfb02a4df3 100644 --- a/api/server/router/image/backend.go +++ b/api/server/router/image/backend.go @@ -39,6 +39,6 @@ type importExportBackend interface { type registryBackend interface { PullImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error - PushImage(ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error - SearchRegistryForImages(term string, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error) + PushImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error + SearchRegistryForImages(ctx context.Context, term string, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error) } diff --git a/api/server/router/image/image_routes.go b/api/server/router/image/image_routes.go index aa68e115da..bd51abf3ba 100644 --- a/api/server/router/image/image_routes.go +++ b/api/server/router/image/image_routes.go @@ -228,7 +228,7 @@ func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, w.Header().Set("Content-Type", "application/json") - if err := s.backend.PushImage(ref, metaHeaders, authConfig, output); err != nil { + if err := s.backend.PushImage(ctx, ref, metaHeaders, authConfig, output); err != nil { if !output.Flushed() { return err } @@ -373,7 +373,7 @@ func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter headers[k] = v } } - query, err := s.backend.SearchRegistryForImages(r.Form.Get("term"), config, headers) + query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("term"), config, headers) if err != nil { return err } diff --git a/api/server/router/system/backend.go b/api/server/router/system/backend.go index c842ce68cc..e6284cd4ab 100644 --- a/api/server/router/system/backend.go +++ b/api/server/router/system/backend.go @@ -4,6 +4,7 @@ import ( "github.com/docker/engine-api/types" "github.com/docker/engine-api/types/events" "github.com/docker/engine-api/types/filters" + "golang.org/x/net/context" ) // Backend is the methods that need to be implemented to provide @@ -13,5 +14,5 @@ type Backend interface { SystemVersion() types.Version SubscribeToEvents(since, sinceNano int64, ef filters.Args) ([]events.Message, chan interface{}) UnsubscribeFromEvents(chan interface{}) - AuthenticateToRegistry(authConfig *types.AuthConfig) (string, string, error) + AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error) } diff --git a/api/server/router/system/system_routes.go b/api/server/router/system/system_routes.go index 1819747f7d..a994a46eb7 100644 --- a/api/server/router/system/system_routes.go +++ b/api/server/router/system/system_routes.go @@ -115,7 +115,7 @@ func (s *systemRouter) postAuth(ctx context.Context, w http.ResponseWriter, r *h if err != nil { return err } - status, token, err := s.backend.AuthenticateToRegistry(config) + status, token, err := s.backend.AuthenticateToRegistry(ctx, config) if err != nil { return err } diff --git a/builder/builder.go b/builder/builder.go index 43586a1e4f..6f8fdb8dc5 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -12,6 +12,7 @@ import ( "github.com/docker/docker/reference" "github.com/docker/engine-api/types" "github.com/docker/engine-api/types/container" + "golang.org/x/net/context" ) const ( @@ -109,7 +110,7 @@ type Backend interface { // Tag an image with newTag TagImage(newTag reference.Named, imageName string) error // Pull tells Docker to pull image referenced by `name`. - PullOnBuild(name string, authConfigs map[string]types.AuthConfig, output io.Writer) (Image, error) + PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (Image, error) // ContainerAttach attaches to container. ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool) error // ContainerCreate creates a new Docker container and returns potential warnings diff --git a/builder/dockerfile/builder.go b/builder/dockerfile/builder.go index 24b31a0f9b..0d42971beb 100644 --- a/builder/dockerfile/builder.go +++ b/builder/dockerfile/builder.go @@ -17,6 +17,7 @@ import ( "github.com/docker/docker/reference" "github.com/docker/engine-api/types" "github.com/docker/engine-api/types/container" + "golang.org/x/net/context" ) var validCommitCommands = map[string]bool{ @@ -52,8 +53,9 @@ type Builder struct { Stderr io.Writer Output io.Writer - docker builder.Backend - context builder.Context + docker builder.Backend + context builder.Context + clientCtx context.Context dockerfile *parser.Node runConfig *container.Config // runconfig for cmd, run, entrypoint etc. @@ -86,7 +88,7 @@ func NewBuildManager(b builder.Backend) (bm *BuildManager) { // NewBuilder creates a new Dockerfile builder from an optional dockerfile and a Config. // If dockerfile is nil, the Dockerfile specified by Config.DockerfileName, // will be read from the Context passed to Build(). -func NewBuilder(config *types.ImageBuildOptions, backend builder.Backend, context builder.Context, dockerfile io.ReadCloser) (b *Builder, err error) { +func NewBuilder(clientCtx context.Context, config *types.ImageBuildOptions, backend builder.Backend, context builder.Context, dockerfile io.ReadCloser) (b *Builder, err error) { if config == nil { config = new(types.ImageBuildOptions) } @@ -94,6 +96,7 @@ func NewBuilder(config *types.ImageBuildOptions, backend builder.Backend, contex config.BuildArgs = make(map[string]string) } b = &Builder{ + clientCtx: clientCtx, options: config, Stdout: os.Stdout, Stderr: os.Stderr, @@ -158,8 +161,8 @@ func sanitizeRepoAndTags(names []string) ([]reference.Named, error) { } // Build creates a NewBuilder, which builds the image. -func (bm *BuildManager) Build(config *types.ImageBuildOptions, context builder.Context, stdout io.Writer, stderr io.Writer, out io.Writer, clientGone <-chan bool) (string, error) { - b, err := NewBuilder(config, bm.backend, context, nil) +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) { + b, err := NewBuilder(clientCtx, config, bm.backend, context, nil) if err != nil { return "", err } @@ -291,7 +294,7 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con } } - b, err := NewBuilder(nil, nil, nil, nil) + b, err := NewBuilder(context.Background(), nil, nil, nil, nil) if err != nil { return nil, err } diff --git a/builder/dockerfile/dispatchers.go b/builder/dockerfile/dispatchers.go index 6323b75621..ac7c2b07e9 100644 --- a/builder/dockerfile/dispatchers.go +++ b/builder/dockerfile/dispatchers.go @@ -206,7 +206,7 @@ func from(b *Builder, args []string, attributes map[string]bool, original string // TODO: shouldn't we error out if error is different from "not found" ? } if image == nil { - image, err = b.docker.PullOnBuild(name, b.options.AuthConfigs, b.Output) + image, err = b.docker.PullOnBuild(b.clientCtx, name, b.options.AuthConfigs, b.Output) if err != nil { return err } diff --git a/daemon/daemon.go b/daemon/daemon.go index 974bf6e9d9..f04a417750 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -1030,7 +1030,7 @@ func (daemon *Daemon) PullImage(ctx context.Context, ref reference.Named, metaHe } // PullOnBuild tells Docker to pull image referenced by `name`. -func (daemon *Daemon) PullOnBuild(name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) { +func (daemon *Daemon) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) { ref, err := reference.ParseNamed(name) if err != nil { return nil, err @@ -1052,7 +1052,7 @@ func (daemon *Daemon) PullOnBuild(name string, authConfigs map[string]types.Auth pullRegistryAuth = &resolvedConfig } - if err := daemon.PullImage(context.Background(), ref, nil, pullRegistryAuth, output); err != nil { + if err := daemon.PullImage(ctx, ref, nil, pullRegistryAuth, output); err != nil { return nil, err } return daemon.GetImage(name) @@ -1069,14 +1069,14 @@ func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error { } // PushImage initiates a push operation on the repository named localName. -func (daemon *Daemon) PushImage(ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { +func (daemon *Daemon) PushImage(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { // Include a buffer so that slow client connections don't affect // transfer performance. progressChan := make(chan progress.Progress, 100) writesDone := make(chan struct{}) - ctx, cancelFunc := context.WithCancel(context.Background()) + ctx, cancelFunc := context.WithCancel(ctx) go func() { writeDistributionProgress(cancelFunc, outStream, progressChan) @@ -1502,16 +1502,16 @@ func configureVolumes(config *Config, rootUID, rootGID int) (*store.VolumeStore, } // AuthenticateToRegistry checks the validity of credentials in authConfig -func (daemon *Daemon) AuthenticateToRegistry(authConfig *types.AuthConfig) (string, string, error) { - return daemon.RegistryService.Auth(authConfig, dockerversion.DockerUserAgent("")) +func (daemon *Daemon) AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error) { + return daemon.RegistryService.Auth(authConfig, dockerversion.DockerUserAgent(ctx)) } // SearchRegistryForImages queries the registry for images matching // term. authConfig is used to login. -func (daemon *Daemon) SearchRegistryForImages(term string, +func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, term string, authConfig *types.AuthConfig, headers map[string][]string) (*registrytypes.SearchResults, error) { - return daemon.RegistryService.Search(term, authConfig, dockerversion.DockerUserAgent(""), headers) + return daemon.RegistryService.Search(term, authConfig, dockerversion.DockerUserAgent(ctx), headers) } // IsShuttingDown tells whether the daemon is shutting down or not diff --git a/distribution/pull_v1.go b/distribution/pull_v1.go index 9c23a676b8..a8ae624915 100644 --- a/distribution/pull_v1.go +++ b/distribution/pull_v1.go @@ -49,10 +49,10 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) error { tr := transport.NewTransport( // TODO(tiborvass): was ReceiveTimeout registry.NewTransport(tlsConfig), - registry.DockerHeaders(dockerversion.DockerUserAgent(""), p.config.MetaHeaders)..., + registry.DockerHeaders(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders)..., ) client := registry.HTTPClient(tr) - v1Endpoint, err := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(""), p.config.MetaHeaders) + v1Endpoint, err := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders) if err != nil { logrus.Debugf("Could not get v1 endpoint: %v", err) return fallbackError{err: err} diff --git a/distribution/push_v1.go b/distribution/push_v1.go index d82d120718..b6e4a13046 100644 --- a/distribution/push_v1.go +++ b/distribution/push_v1.go @@ -38,10 +38,10 @@ func (p *v1Pusher) Push(ctx context.Context) error { tr := transport.NewTransport( // TODO(tiborvass): was NoTimeout registry.NewTransport(tlsConfig), - registry.DockerHeaders(dockerversion.DockerUserAgent(""), p.config.MetaHeaders)..., + registry.DockerHeaders(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders)..., ) client := registry.HTTPClient(tr) - v1Endpoint, err := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(""), p.config.MetaHeaders) + v1Endpoint, err := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders) if err != nil { logrus.Debugf("Could not get v1 endpoint: %v", err) return fallbackError{err: err} diff --git a/distribution/registry.go b/distribution/registry.go index 54e314bbd6..f768a14d86 100644 --- a/distribution/registry.go +++ b/distribution/registry.go @@ -37,8 +37,6 @@ func (dcs dumbCredentialStore) SetRefreshToken(*url.URL, string, string) { // providing timeout settings and authentication support, and also verifies the // remote API version. 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) { - upstreamUA := dockerversion.GetUserAgentFromContext(ctx) - repoName := repoInfo.FullName() // If endpoint does not support CanonicalName, use the RemoteName instead if endpoint.TrimHostname { @@ -59,7 +57,7 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end DisableKeepAlives: true, } - modifiers := registry.DockerHeaders(dockerversion.DockerUserAgent(upstreamUA), metaHeaders) + modifiers := registry.DockerHeaders(dockerversion.DockerUserAgent(ctx), metaHeaders) authTransport := transport.NewTransport(base, modifiers...) challengeManager, foundVersion, err := registry.PingV2Registry(endpoint, authTransport) diff --git a/dockerversion/useragent.go b/dockerversion/useragent.go index abc0ed7f46..d2a891c4d6 100644 --- a/dockerversion/useragent.go +++ b/dockerversion/useragent.go @@ -13,7 +13,7 @@ import ( // DockerUserAgent is the User-Agent the Docker client uses to identify itself. // In accordance with RFC 7231 (5.5.3) is of the form: // [docker client's UA] UpstreamClient([upstream client's UA]) -func DockerUserAgent(upstreamUA string) string { +func DockerUserAgent(ctx context.Context) string { httpVersion := make([]useragent.VersionInfo, 0, 6) httpVersion = append(httpVersion, useragent.VersionInfo{Name: "docker", Version: Version}) httpVersion = append(httpVersion, useragent.VersionInfo{Name: "go", Version: runtime.Version()}) @@ -25,6 +25,7 @@ func DockerUserAgent(upstreamUA string) string { httpVersion = append(httpVersion, useragent.VersionInfo{Name: "arch", Version: runtime.GOARCH}) dockerUA := useragent.AppendVersions("", httpVersion...) + upstreamUA := getUserAgentFromContext(ctx) if len(upstreamUA) > 0 { ret := insertUpstreamUserAgent(upstreamUA, dockerUA) return ret @@ -32,8 +33,8 @@ func DockerUserAgent(upstreamUA string) string { return dockerUA } -// GetUserAgentFromContext returns the previously saved user-agent context stored in ctx, if one exists -func GetUserAgentFromContext(ctx context.Context) string { +// getUserAgentFromContext returns the previously saved user-agent context stored in ctx, if one exists +func getUserAgentFromContext(ctx context.Context) string { var upstreamUA string if ctx != nil { var ki interface{} = ctx.Value(httputils.UAStringKey) @@ -51,7 +52,7 @@ func escapeStr(s string, charsToEscape string) string { appended := false for _, escapeableRune := range charsToEscape { if currRune == escapeableRune { - ret += "\\" + string(currRune) + ret += `\` + string(currRune) appended = true break } @@ -67,7 +68,7 @@ func escapeStr(s string, charsToEscape string) string { // string of the form: // $dockerUA UpstreamClient($upstreamUA) func insertUpstreamUserAgent(upstreamUA string, dockerUA string) string { - charsToEscape := "();\\" //["\\", ";", "(", ")"]string + charsToEscape := `();\` upstreamUAEscaped := escapeStr(upstreamUA, charsToEscape) return fmt.Sprintf("%s UpstreamClient(%s)", dockerUA, upstreamUAEscaped) } diff --git a/integration-cli/docker_cli_registry_user_agent_test.go b/integration-cli/docker_cli_registry_user_agent_test.go index ebb2093bcb..67a950cabd 100644 --- a/integration-cli/docker_cli_registry_user_agent_test.go +++ b/integration-cli/docker_cli_registry_user_agent_test.go @@ -10,17 +10,17 @@ import ( // unescapeBackslashSemicolonParens unescapes \;() func unescapeBackslashSemicolonParens(s string) string { - re := regexp.MustCompile("\\\\;") + re := regexp.MustCompile(`\\;`) ret := re.ReplaceAll([]byte(s), []byte(";")) - re = regexp.MustCompile("\\\\\\(") + re = regexp.MustCompile(`\\\(`) ret = re.ReplaceAll([]byte(ret), []byte("(")) - re = regexp.MustCompile("\\\\\\)") + re = regexp.MustCompile(`\\\)`) ret = re.ReplaceAll([]byte(ret), []byte(")")) - re = regexp.MustCompile("\\\\\\\\") - ret = re.ReplaceAll([]byte(ret), []byte("\\")) + re = regexp.MustCompile(`\\\\`) + ret = re.ReplaceAll([]byte(ret), []byte(`\`)) return string(ret) } @@ -46,14 +46,7 @@ func regexpCheckUA(c *check.C, ua string) { c.Assert(bMatchUpstreamUA, check.Equals, true, check.Commentf("(Upstream) Docker Client User-Agent malformed")) } -// TestUserAgentPassThroughOnPull verifies that when an image is pulled from -// a registry, the registry should see a User-Agent string of the form -// [docker engine UA] UptreamClientSTREAM-CLIENT([client UA]) -func (s *DockerRegistrySuite) TestUserAgentPassThroughOnPull(c *check.C) { - reg, err := newTestRegistry(c) - c.Assert(err, check.IsNil) - expectUpstreamUA := false - +func registerUserAgentHandler(reg *testRegistry, result *string) { reg.registerHandler("/v2/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(404) var ua string @@ -62,29 +55,66 @@ func (s *DockerRegistrySuite) TestUserAgentPassThroughOnPull(c *check.C) { ua = v[0] } } - c.Assert(ua, check.Not(check.Equals), "", check.Commentf("No User-Agent found in request")) - if r.URL.Path == "/v2/busybox/manifests/latest" { - if expectUpstreamUA { - regexpCheckUA(c, ua) - } - } + *result = ua }) +} - repoName := fmt.Sprintf("%s/busybox", reg.hostport) - err = s.d.Start("--insecure-registry", reg.hostport, "--disable-legacy-registry=true") +// TestUserAgentPassThroughOnPull verifies that when an image is pulled from +// a registry, the registry should see a User-Agent string of the form +// [docker engine UA] UptreamClientSTREAM-CLIENT([client UA]) +func (s *DockerRegistrySuite) TestUserAgentPassThrough(c *check.C) { + var ( + buildUA string + pullUA string + pushUA string + loginUA string + ) + + buildReg, err := newTestRegistry(c) + c.Assert(err, check.IsNil) + registerUserAgentHandler(buildReg, &buildUA) + buildRepoName := fmt.Sprintf("%s/busybox", buildReg.hostport) + + pullReg, err := newTestRegistry(c) + c.Assert(err, check.IsNil) + registerUserAgentHandler(pullReg, &pullUA) + pullRepoName := fmt.Sprintf("%s/busybox", pullReg.hostport) + + pushReg, err := newTestRegistry(c) + c.Assert(err, check.IsNil) + registerUserAgentHandler(pushReg, &pushUA) + pushRepoName := fmt.Sprintf("%s/busybox", pushReg.hostport) + + loginReg, err := newTestRegistry(c) + c.Assert(err, check.IsNil) + registerUserAgentHandler(loginReg, &loginUA) + + err = s.d.Start( + "--insecure-registry", buildReg.hostport, + "--insecure-registry", pullReg.hostport, + "--insecure-registry", pushReg.hostport, + "--insecure-registry", loginReg.hostport, + "--disable-legacy-registry=true") c.Assert(err, check.IsNil) - dockerfileName, cleanup, err := makefile(fmt.Sprintf("FROM %s/busybox", reg.hostport)) + dockerfileName, cleanup1, err := makefile(fmt.Sprintf("FROM %s", buildRepoName)) c.Assert(err, check.IsNil, check.Commentf("Unable to create test dockerfile")) - defer cleanup() - + defer cleanup1() s.d.Cmd("build", "--file", dockerfileName, ".") + regexpCheckUA(c, buildUA) - s.d.Cmd("run", repoName) - s.d.Cmd("login", "-u", "richard", "-p", "testtest", "-e", "testuser@testdomain.com", reg.hostport) - s.d.Cmd("tag", "busybox", repoName) - s.d.Cmd("push", repoName) + s.d.Cmd("login", "-u", "richard", "-p", "testtest", "-e", "testuser@testdomain.com", loginReg.hostport) + regexpCheckUA(c, loginUA) - expectUpstreamUA = true - s.d.Cmd("pull", repoName) + s.d.Cmd("pull", pullRepoName) + regexpCheckUA(c, pullUA) + + dockerfileName, cleanup2, err := makefile(`FROM scratch + ENV foo bar`) + c.Assert(err, check.IsNil, check.Commentf("Unable to create test dockerfile")) + defer cleanup2() + s.d.Cmd("build", "-t", pushRepoName, "--file", dockerfileName, ".") + + s.d.Cmd("push", pushRepoName) + regexpCheckUA(c, pushUA) }