Browse Source

Remove the use of dockerversion from the registry package

Signed-off-by: Daniel Nephin <dnephin@docker.com>
Daniel Nephin 9 năm trước cách đây
mục cha
commit
61a49bb6ba

+ 2 - 1
api/client/trust.go

@@ -21,6 +21,7 @@ import (
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/distribution"
 	"github.com/docker/docker/distribution"
+	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/pkg/jsonmessage"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/reference"
@@ -139,7 +140,7 @@ func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, aut
 	}
 	}
 
 
 	// Skip configuration headers since request is not going to Docker daemon
 	// Skip configuration headers since request is not going to Docker daemon
-	modifiers := registry.DockerHeaders(http.Header{})
+	modifiers := registry.DockerHeaders(dockerversion.DockerUserAgent(), http.Header{})
 	authTransport := transport.NewTransport(base, modifiers...)
 	authTransport := transport.NewTransport(base, modifiers...)
 	pingClient := &http.Client{
 	pingClient := &http.Client{
 		Transport: authTransport,
 		Transport: authTransport,

+ 3 - 2
daemon/daemon.go

@@ -40,6 +40,7 @@ import (
 	"github.com/docker/docker/distribution"
 	"github.com/docker/docker/distribution"
 	dmetadata "github.com/docker/docker/distribution/metadata"
 	dmetadata "github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/xfer"
 	"github.com/docker/docker/distribution/xfer"
+	"github.com/docker/docker/dockerversion"
 	derr "github.com/docker/docker/errors"
 	derr "github.com/docker/docker/errors"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image/tarexport"
 	"github.com/docker/docker/image/tarexport"
@@ -1452,7 +1453,7 @@ func configureVolumes(config *Config, rootUID, rootGID int) (*store.VolumeStore,
 
 
 // AuthenticateToRegistry checks the validity of credentials in authConfig
 // AuthenticateToRegistry checks the validity of credentials in authConfig
 func (daemon *Daemon) AuthenticateToRegistry(authConfig *types.AuthConfig) (string, error) {
 func (daemon *Daemon) AuthenticateToRegistry(authConfig *types.AuthConfig) (string, error) {
-	return daemon.RegistryService.Auth(authConfig)
+	return daemon.RegistryService.Auth(authConfig, dockerversion.DockerUserAgent())
 }
 }
 
 
 // SearchRegistryForImages queries the registry for images matching
 // SearchRegistryForImages queries the registry for images matching
@@ -1460,7 +1461,7 @@ func (daemon *Daemon) AuthenticateToRegistry(authConfig *types.AuthConfig) (stri
 func (daemon *Daemon) SearchRegistryForImages(term string,
 func (daemon *Daemon) SearchRegistryForImages(term string,
 	authConfig *types.AuthConfig,
 	authConfig *types.AuthConfig,
 	headers map[string][]string) (*registrytypes.SearchResults, error) {
 	headers map[string][]string) (*registrytypes.SearchResults, error) {
-	return daemon.RegistryService.Search(term, authConfig, headers)
+	return daemon.RegistryService.Search(term, authConfig, dockerversion.DockerUserAgent(), headers)
 }
 }
 
 
 // IsShuttingDown tells whether the daemon is shutting down or not
 // IsShuttingDown tells whether the daemon is shutting down or not

+ 0 - 1
distribution/pull.go

@@ -20,7 +20,6 @@ import (
 // ImagePullConfig stores pull configuration.
 // ImagePullConfig stores pull configuration.
 type ImagePullConfig struct {
 type ImagePullConfig struct {
 	// MetaHeaders stores HTTP headers with metadata about the image
 	// MetaHeaders stores HTTP headers with metadata about the image
-	// (DockerHeaders with prefix X-Meta- in the request).
 	MetaHeaders map[string][]string
 	MetaHeaders map[string][]string
 	// AuthConfig holds authentication credentials for authenticating with
 	// AuthConfig holds authentication credentials for authenticating with
 	// the registry.
 	// the registry.

+ 3 - 2
distribution/pull_v1.go

@@ -14,6 +14,7 @@ import (
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/xfer"
 	"github.com/docker/docker/distribution/xfer"
+	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image/v1"
 	"github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
@@ -47,10 +48,10 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) error {
 	tr := transport.NewTransport(
 	tr := transport.NewTransport(
 		// TODO(tiborvass): was ReceiveTimeout
 		// TODO(tiborvass): was ReceiveTimeout
 		registry.NewTransport(tlsConfig),
 		registry.NewTransport(tlsConfig),
-		registry.DockerHeaders(p.config.MetaHeaders)...,
+		registry.DockerHeaders(dockerversion.DockerUserAgent(), p.config.MetaHeaders)...,
 	)
 	)
 	client := registry.HTTPClient(tr)
 	client := registry.HTTPClient(tr)
-	v1Endpoint, err := p.endpoint.ToV1Endpoint(p.config.MetaHeaders)
+	v1Endpoint, err := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(), p.config.MetaHeaders)
 	if err != nil {
 	if err != nil {
 		logrus.Debugf("Could not get v1 endpoint: %v", err)
 		logrus.Debugf("Could not get v1 endpoint: %v", err)
 		return fallbackError{err: err}
 		return fallbackError{err: err}

+ 0 - 1
distribution/push.go

@@ -22,7 +22,6 @@ import (
 // ImagePushConfig stores push configuration.
 // ImagePushConfig stores push configuration.
 type ImagePushConfig struct {
 type ImagePushConfig struct {
 	// MetaHeaders store HTTP headers with metadata about the image
 	// MetaHeaders store HTTP headers with metadata about the image
-	// (DockerHeaders with prefix X-Meta- in the request).
 	MetaHeaders map[string][]string
 	MetaHeaders map[string][]string
 	// AuthConfig holds authentication credentials for authenticating with
 	// AuthConfig holds authentication credentials for authenticating with
 	// the registry.
 	// the registry.

+ 3 - 2
distribution/push_v1.go

@@ -8,6 +8,7 @@ import (
 	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/digest"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/distribution/metadata"
 	"github.com/docker/docker/distribution/metadata"
+	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image/v1"
 	"github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/layer"
@@ -38,10 +39,10 @@ func (p *v1Pusher) Push(ctx context.Context) error {
 	tr := transport.NewTransport(
 	tr := transport.NewTransport(
 		// TODO(tiborvass): was NoTimeout
 		// TODO(tiborvass): was NoTimeout
 		registry.NewTransport(tlsConfig),
 		registry.NewTransport(tlsConfig),
-		registry.DockerHeaders(p.config.MetaHeaders)...,
+		registry.DockerHeaders(dockerversion.DockerUserAgent(), p.config.MetaHeaders)...,
 	)
 	)
 	client := registry.HTTPClient(tr)
 	client := registry.HTTPClient(tr)
-	v1Endpoint, err := p.endpoint.ToV1Endpoint(p.config.MetaHeaders)
+	v1Endpoint, err := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(), p.config.MetaHeaders)
 	if err != nil {
 	if err != nil {
 		logrus.Debugf("Could not get v1 endpoint: %v", err)
 		logrus.Debugf("Could not get v1 endpoint: %v", err)
 		return fallbackError{err: err}
 		return fallbackError{err: err}

+ 2 - 1
distribution/registry.go

@@ -14,6 +14,7 @@ import (
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/auth"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/docker/distribution/xfer"
 	"github.com/docker/docker/distribution/xfer"
+	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"github.com/docker/engine-api/types"
 	"github.com/docker/engine-api/types"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
@@ -67,7 +68,7 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
 		DisableKeepAlives: true,
 		DisableKeepAlives: true,
 	}
 	}
 
 
-	modifiers := registry.DockerHeaders(metaHeaders)
+	modifiers := registry.DockerHeaders(dockerversion.DockerUserAgent(), metaHeaders)
 	authTransport := transport.NewTransport(base, modifiers...)
 	authTransport := transport.NewTransport(base, modifiers...)
 	pingClient := &http.Client{
 	pingClient := &http.Client{
 		Transport: authTransport,
 		Transport: authTransport,

+ 24 - 0
dockerversion/useragent.go

@@ -0,0 +1,24 @@
+package dockerversion
+
+import (
+	"runtime"
+
+	"github.com/docker/docker/pkg/parsers/kernel"
+	"github.com/docker/docker/pkg/useragent"
+)
+
+// DockerUserAgent is the User-Agent the Docker client uses to identify itself.
+// It is populated from version information of different components.
+func DockerUserAgent() 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()})
+	httpVersion = append(httpVersion, useragent.VersionInfo{Name: "git-commit", Version: GitCommit})
+	if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
+		httpVersion = append(httpVersion, useragent.VersionInfo{Name: "kernel", Version: kernelVersion.String()})
+	}
+	httpVersion = append(httpVersion, useragent.VersionInfo{Name: "os", Version: runtime.GOOS})
+	httpVersion = append(httpVersion, useragent.VersionInfo{Name: "arch", Version: runtime.GOARCH})
+
+	return useragent.AppendVersions("", httpVersion...)
+}

+ 4 - 4
registry/endpoint.go

@@ -45,12 +45,12 @@ func scanForAPIVersion(address string) (string, APIVersion) {
 
 
 // NewEndpoint parses the given address to return a registry endpoint.  v can be used to
 // NewEndpoint parses the given address to return a registry endpoint.  v can be used to
 // specify a specific endpoint version
 // specify a specific endpoint version
-func NewEndpoint(index *registrytypes.IndexInfo, metaHeaders http.Header, v APIVersion) (*Endpoint, error) {
+func NewEndpoint(index *registrytypes.IndexInfo, userAgent string, metaHeaders http.Header, v APIVersion) (*Endpoint, error) {
 	tlsConfig, err := newTLSConfig(index.Name, index.Secure)
 	tlsConfig, err := newTLSConfig(index.Name, index.Secure)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	endpoint, err := newEndpoint(GetAuthConfigKey(index), tlsConfig, metaHeaders)
+	endpoint, err := newEndpoint(GetAuthConfigKey(index), tlsConfig, userAgent, metaHeaders)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -91,7 +91,7 @@ func validateEndpoint(endpoint *Endpoint) error {
 	return nil
 	return nil
 }
 }
 
 
-func newEndpoint(address string, tlsConfig *tls.Config, metaHeaders http.Header) (*Endpoint, error) {
+func newEndpoint(address string, tlsConfig *tls.Config, userAgent string, metaHeaders http.Header) (*Endpoint, error) {
 	var (
 	var (
 		endpoint       = new(Endpoint)
 		endpoint       = new(Endpoint)
 		trimmedAddress string
 		trimmedAddress string
@@ -112,7 +112,7 @@ func newEndpoint(address string, tlsConfig *tls.Config, metaHeaders http.Header)
 
 
 	// TODO(tiborvass): make sure a ConnectTimeout transport is used
 	// TODO(tiborvass): make sure a ConnectTimeout transport is used
 	tr := NewTransport(tlsConfig)
 	tr := NewTransport(tlsConfig)
-	endpoint.client = HTTPClient(transport.NewTransport(tr, DockerHeaders(metaHeaders)...))
+	endpoint.client = HTTPClient(transport.NewTransport(tr, DockerHeaders(userAgent, metaHeaders)...))
 	return endpoint, nil
 	return endpoint, nil
 }
 }
 
 

+ 1 - 1
registry/endpoint_test.go

@@ -19,7 +19,7 @@ func TestEndpointParse(t *testing.T) {
 		{"0.0.0.0:5000", "https://0.0.0.0:5000/v0/"},
 		{"0.0.0.0:5000", "https://0.0.0.0:5000/v0/"},
 	}
 	}
 	for _, td := range testData {
 	for _, td := range testData {
-		e, err := newEndpoint(td.str, nil, nil)
+		e, err := newEndpoint(td.str, nil, "", nil)
 		if err != nil {
 		if err != nil {
 			t.Errorf("%q: %s", td.str, err)
 			t.Errorf("%q: %s", td.str, err)
 		}
 		}

+ 7 - 25
registry/registry.go

@@ -21,9 +21,6 @@ import (
 	"github.com/docker/distribution/registry/api/v2"
 	"github.com/docker/distribution/registry/api/v2"
 	"github.com/docker/distribution/registry/client"
 	"github.com/docker/distribution/registry/client"
 	"github.com/docker/distribution/registry/client/transport"
 	"github.com/docker/distribution/registry/client/transport"
-	"github.com/docker/docker/dockerversion"
-	"github.com/docker/docker/pkg/parsers/kernel"
-	"github.com/docker/docker/pkg/useragent"
 	"github.com/docker/go-connections/tlsconfig"
 	"github.com/docker/go-connections/tlsconfig"
 )
 )
 
 
@@ -34,23 +31,7 @@ var (
 	errLoginRequired = errors.New("Authentication is required.")
 	errLoginRequired = errors.New("Authentication is required.")
 )
 )
 
 
-// dockerUserAgent is the User-Agent the Docker client uses to identify itself.
-// It is populated on init(), comprising version information of different components.
-var dockerUserAgent string
-
 func init() {
 func init() {
-	httpVersion := make([]useragent.VersionInfo, 0, 6)
-	httpVersion = append(httpVersion, useragent.VersionInfo{Name: "docker", Version: dockerversion.Version})
-	httpVersion = append(httpVersion, useragent.VersionInfo{Name: "go", Version: runtime.Version()})
-	httpVersion = append(httpVersion, useragent.VersionInfo{Name: "git-commit", Version: dockerversion.GitCommit})
-	if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
-		httpVersion = append(httpVersion, useragent.VersionInfo{Name: "kernel", Version: kernelVersion.String()})
-	}
-	httpVersion = append(httpVersion, useragent.VersionInfo{Name: "os", Version: runtime.GOOS})
-	httpVersion = append(httpVersion, useragent.VersionInfo{Name: "arch", Version: runtime.GOARCH})
-
-	dockerUserAgent = useragent.AppendVersions("", httpVersion...)
-
 	if runtime.GOOS != "linux" {
 	if runtime.GOOS != "linux" {
 		V2Only = true
 		V2Only = true
 	}
 	}
@@ -130,12 +111,13 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
 	return nil
 	return nil
 }
 }
 
 
-// DockerHeaders returns request modifiers that ensure requests have
-// the User-Agent header set to dockerUserAgent and that metaHeaders
-// are added.
-func DockerHeaders(metaHeaders http.Header) []transport.RequestModifier {
-	modifiers := []transport.RequestModifier{
-		transport.NewHeaderRequestModifier(http.Header{"User-Agent": []string{dockerUserAgent}}),
+// DockerHeaders returns request modifiers with a User-Agent and metaHeaders
+func DockerHeaders(userAgent string, metaHeaders http.Header) []transport.RequestModifier {
+	modifiers := []transport.RequestModifier{}
+	if userAgent != "" {
+		modifiers = append(modifiers, transport.NewHeaderRequestModifier(http.Header{
+			"User-Agent": []string{userAgent},
+		}))
 	}
 	}
 	if metaHeaders != nil {
 	if metaHeaders != nil {
 		modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders))
 		modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders))

+ 8 - 7
registry/registry_test.go

@@ -25,12 +25,13 @@ const (
 
 
 func spawnTestRegistrySession(t *testing.T) *Session {
 func spawnTestRegistrySession(t *testing.T) *Session {
 	authConfig := &types.AuthConfig{}
 	authConfig := &types.AuthConfig{}
-	endpoint, err := NewEndpoint(makeIndex("/v1/"), nil, APIVersionUnknown)
+	endpoint, err := NewEndpoint(makeIndex("/v1/"), "", nil, APIVersionUnknown)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
+	userAgent := "docker test client"
 	var tr http.RoundTripper = debugTransport{NewTransport(nil), t.Log}
 	var tr http.RoundTripper = debugTransport{NewTransport(nil), t.Log}
-	tr = transport.NewTransport(AuthTransport(tr, authConfig, false), DockerHeaders(nil)...)
+	tr = transport.NewTransport(AuthTransport(tr, authConfig, false), DockerHeaders(userAgent, nil)...)
 	client := HTTPClient(tr)
 	client := HTTPClient(tr)
 	r, err := NewSession(client, authConfig, endpoint)
 	r, err := NewSession(client, authConfig, endpoint)
 	if err != nil {
 	if err != nil {
@@ -52,7 +53,7 @@ func spawnTestRegistrySession(t *testing.T) *Session {
 
 
 func TestPingRegistryEndpoint(t *testing.T) {
 func TestPingRegistryEndpoint(t *testing.T) {
 	testPing := func(index *registrytypes.IndexInfo, expectedStandalone bool, assertMessage string) {
 	testPing := func(index *registrytypes.IndexInfo, expectedStandalone bool, assertMessage string) {
-		ep, err := NewEndpoint(index, nil, APIVersionUnknown)
+		ep, err := NewEndpoint(index, "", nil, APIVersionUnknown)
 		if err != nil {
 		if err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
@@ -72,7 +73,7 @@ func TestPingRegistryEndpoint(t *testing.T) {
 func TestEndpoint(t *testing.T) {
 func TestEndpoint(t *testing.T) {
 	// Simple wrapper to fail test if err != nil
 	// Simple wrapper to fail test if err != nil
 	expandEndpoint := func(index *registrytypes.IndexInfo) *Endpoint {
 	expandEndpoint := func(index *registrytypes.IndexInfo) *Endpoint {
-		endpoint, err := NewEndpoint(index, nil, APIVersionUnknown)
+		endpoint, err := NewEndpoint(index, "", nil, APIVersionUnknown)
 		if err != nil {
 		if err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
@@ -81,7 +82,7 @@ func TestEndpoint(t *testing.T) {
 
 
 	assertInsecureIndex := func(index *registrytypes.IndexInfo) {
 	assertInsecureIndex := func(index *registrytypes.IndexInfo) {
 		index.Secure = true
 		index.Secure = true
-		_, err := NewEndpoint(index, nil, APIVersionUnknown)
+		_, err := NewEndpoint(index, "", nil, APIVersionUnknown)
 		assertNotEqual(t, err, nil, index.Name+": Expected error for insecure index")
 		assertNotEqual(t, err, nil, index.Name+": Expected error for insecure index")
 		assertEqual(t, strings.Contains(err.Error(), "insecure-registry"), true, index.Name+": Expected insecure-registry  error for insecure index")
 		assertEqual(t, strings.Contains(err.Error(), "insecure-registry"), true, index.Name+": Expected insecure-registry  error for insecure index")
 		index.Secure = false
 		index.Secure = false
@@ -89,7 +90,7 @@ func TestEndpoint(t *testing.T) {
 
 
 	assertSecureIndex := func(index *registrytypes.IndexInfo) {
 	assertSecureIndex := func(index *registrytypes.IndexInfo) {
 		index.Secure = true
 		index.Secure = true
-		_, err := NewEndpoint(index, nil, APIVersionUnknown)
+		_, err := NewEndpoint(index, "", nil, APIVersionUnknown)
 		assertNotEqual(t, err, nil, index.Name+": Expected cert error for secure index")
 		assertNotEqual(t, err, nil, index.Name+": Expected cert error for secure index")
 		assertEqual(t, strings.Contains(err.Error(), "certificate signed by unknown authority"), true, index.Name+": Expected cert error for secure index")
 		assertEqual(t, strings.Contains(err.Error(), "certificate signed by unknown authority"), true, index.Name+": Expected cert error for secure index")
 		index.Secure = false
 		index.Secure = false
@@ -155,7 +156,7 @@ func TestEndpoint(t *testing.T) {
 	}
 	}
 	for _, address := range badEndpoints {
 	for _, address := range badEndpoints {
 		index.Name = address
 		index.Name = address
-		_, err := NewEndpoint(index, nil, APIVersionUnknown)
+		_, err := NewEndpoint(index, "", nil, APIVersionUnknown)
 		checkNotEqual(t, err, nil, "Expected error while expanding bad endpoint")
 		checkNotEqual(t, err, nil, "Expected error while expanding bad endpoint")
 	}
 	}
 }
 }

+ 6 - 6
registry/service.go

@@ -28,7 +28,7 @@ func NewService(options *Options) *Service {
 // Auth contacts the public registry with the provided credentials,
 // Auth contacts the public registry with the provided credentials,
 // and returns OK if authentication was successful.
 // and returns OK if authentication was successful.
 // It can be used to verify the validity of a client's credentials.
 // It can be used to verify the validity of a client's credentials.
-func (s *Service) Auth(authConfig *types.AuthConfig) (string, error) {
+func (s *Service) Auth(authConfig *types.AuthConfig, userAgent string) (string, error) {
 	addr := authConfig.ServerAddress
 	addr := authConfig.ServerAddress
 	if addr == "" {
 	if addr == "" {
 		// Use the official registry address if not specified.
 		// Use the official registry address if not specified.
@@ -45,7 +45,7 @@ func (s *Service) Auth(authConfig *types.AuthConfig) (string, error) {
 		endpointVersion = APIVersion2
 		endpointVersion = APIVersion2
 	}
 	}
 
 
-	endpoint, err := NewEndpoint(index, nil, endpointVersion)
+	endpoint, err := NewEndpoint(index, userAgent, nil, endpointVersion)
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
@@ -72,7 +72,7 @@ func splitReposSearchTerm(reposName string) (string, string) {
 
 
 // Search queries the public registry for images matching the specified
 // Search queries the public registry for images matching the specified
 // search terms, and returns the results.
 // search terms, and returns the results.
-func (s *Service) Search(term string, authConfig *types.AuthConfig, headers map[string][]string) (*registrytypes.SearchResults, error) {
+func (s *Service) Search(term string, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error) {
 	if err := validateNoSchema(term); err != nil {
 	if err := validateNoSchema(term); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -85,7 +85,7 @@ func (s *Service) Search(term string, authConfig *types.AuthConfig, headers map[
 	}
 	}
 
 
 	// *TODO: Search multiple indexes.
 	// *TODO: Search multiple indexes.
-	endpoint, err := NewEndpoint(index, http.Header(headers), APIVersionUnknown)
+	endpoint, err := NewEndpoint(index, userAgent, http.Header(headers), APIVersionUnknown)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -129,8 +129,8 @@ type APIEndpoint struct {
 }
 }
 
 
 // ToV1Endpoint returns a V1 API endpoint based on the APIEndpoint
 // ToV1Endpoint returns a V1 API endpoint based on the APIEndpoint
-func (e APIEndpoint) ToV1Endpoint(metaHeaders http.Header) (*Endpoint, error) {
-	return newEndpoint(e.URL, e.TLSConfig, metaHeaders)
+func (e APIEndpoint) ToV1Endpoint(userAgent string, metaHeaders http.Header) (*Endpoint, error) {
+	return newEndpoint(e.URL, e.TLSConfig, userAgent, metaHeaders)
 }
 }
 
 
 // TLSConfig constructs a client TLS configuration based on server defaults
 // TLSConfig constructs a client TLS configuration based on server defaults