Merge pull request #21373 from aaronlehmann/client-user-agent-registry-operations
Pass upstream client's user agent through to registry on operations beyond pulls
This commit is contained in:
commit
9f327b4c28
17 changed files with 109 additions and 70 deletions
|
@ -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 + ")"
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue