registry/service.go
3d605683
 package registry
 
73823e5e
 import (
19515a7a
 	"crypto/tls"
 	"fmt"
73823e5e
 	"net/http"
cb57b256
 	"net/url"
19515a7a
 	"strings"
73823e5e
 
19515a7a
 	"github.com/docker/distribution/registry/client/auth"
73823e5e
 	"github.com/docker/docker/cliconfig"
19515a7a
 	"github.com/docker/docker/pkg/tlsconfig"
73823e5e
 )
bb9da6ba
 
4fcb9ac4
 // Service is a registry service. It tracks configuration data such as a list
 // of mirrors.
3d605683
 type Service struct {
568f86eb
 	Config *ServiceConfig
3d605683
 }
 
 // NewService returns a new instance of Service ready to be
4fcb9ac4
 // installed into an engine.
568f86eb
 func NewService(options *Options) *Service {
afade423
 	return &Service{
568f86eb
 		Config: NewServiceConfig(options),
afade423
 	}
3d605683
 }
 
 // Auth contacts the public registry with the provided credentials,
 // and returns OK if authentication was sucessful.
 // It can be used to verify the validity of a client's credentials.
bb9da6ba
 func (s *Service) Auth(authConfig *cliconfig.AuthConfig) (string, error) {
41e20cec
 	addr := authConfig.ServerAddress
 	if addr == "" {
 		// Use the official registry address if not specified.
4fcb9ac4
 		addr = IndexServer
3d605683
 	}
03d3d79b
 	index, err := s.ResolveIndex(addr)
 	if err != nil {
 		return "", err
3d605683
 	}
73823e5e
 	endpoint, err := NewEndpoint(index, nil)
03d3d79b
 	if err != nil {
 		return "", err
41e20cec
 	}
 	authConfig.ServerAddress = endpoint.String()
a01cc3ca
 	return Login(authConfig, endpoint)
3d605683
 }
c4089ad8
 
 // Search queries the public registry for images matching the specified
 // search terms, and returns the results.
bb9da6ba
 func (s *Service) Search(term string, authConfig *cliconfig.AuthConfig, headers map[string][]string) (*SearchResults, error) {
03d3d79b
 	repoInfo, err := s.ResolveRepository(term)
3231033a
 	if err != nil {
03d3d79b
 		return nil, err
3231033a
 	}
a01cc3ca
 
568f86eb
 	// *TODO: Search multiple indexes.
73823e5e
 	endpoint, err := repoInfo.GetEndpoint(http.Header(headers))
3231033a
 	if err != nil {
03d3d79b
 		return nil, err
c4089ad8
 	}
73823e5e
 	r, err := NewSession(endpoint.client, authConfig, endpoint)
c4089ad8
 	if err != nil {
03d3d79b
 		return nil, err
c4089ad8
 	}
03d3d79b
 	return r.SearchRepositories(repoInfo.GetSearchTerm())
c4089ad8
 }
568f86eb
 
 // ResolveRepository splits a repository name into its components
 // and configuration of the associated registry.
03d3d79b
 func (s *Service) ResolveRepository(name string) (*RepositoryInfo, error) {
 	return s.Config.NewRepositoryInfo(name)
568f86eb
 }
 
 // ResolveIndex takes indexName and returns index info
03d3d79b
 func (s *Service) ResolveIndex(name string) (*IndexInfo, error) {
 	return s.Config.NewIndexInfo(name)
568f86eb
 }
19515a7a
 
4fcb9ac4
 // APIEndpoint represents a remote API endpoint
19515a7a
 type APIEndpoint struct {
 	Mirror        bool
 	URL           string
 	Version       APIVersion
 	Official      bool
 	TrimHostname  bool
 	TLSConfig     *tls.Config
 	VersionHeader string
 	Versions      []auth.APIVersion
 }
 
4fcb9ac4
 // ToV1Endpoint returns a V1 API endpoint based on the APIEndpoint
19515a7a
 func (e APIEndpoint) ToV1Endpoint(metaHeaders http.Header) (*Endpoint, error) {
 	return newEndpoint(e.URL, e.TLSConfig, metaHeaders)
 }
 
4fcb9ac4
 // TLSConfig constructs a client TLS configuration based on server defaults
 func (s *Service) TLSConfig(hostname string) (*tls.Config, error) {
589922ad
 	return newTLSConfig(hostname, s.Config.isSecureIndex(hostname))
19515a7a
 }
 
cb57b256
 func (s *Service) tlsConfigForMirror(mirror string) (*tls.Config, error) {
4fcb9ac4
 	mirrorURL, err := url.Parse(mirror)
cb57b256
 	if err != nil {
 		return nil, err
 	}
4fcb9ac4
 	return s.TLSConfig(mirrorURL.Host)
cb57b256
 }
 
29ea36a8
 // LookupPullEndpoints creates an list of endpoints to try to pull from, in order of preference.
4fcb9ac4
 // It gives preference to v2 endpoints over v1, mirrors over the actual
 // registry, and HTTPS over plain HTTP.
29ea36a8
 func (s *Service) LookupPullEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
 	return s.lookupEndpoints(repoName, false)
 }
 
 // LookupPushEndpoints creates an list of endpoints to try to push to, in order of preference.
 // It gives preference to v2 endpoints over v1, and HTTPS over plain HTTP.
 // Mirrors are not included.
 func (s *Service) LookupPushEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
 	return s.lookupEndpoints(repoName, true)
 }
 
 func (s *Service) lookupEndpoints(repoName string, isPush bool) (endpoints []APIEndpoint, err error) {
19515a7a
 	var cfg = tlsconfig.ServerDefault
 	tlsConfig := &cfg
4fcb9ac4
 	if strings.HasPrefix(repoName, DefaultNamespace+"/") {
29ea36a8
 		if !isPush {
 			// v2 mirrors for pull only
 			for _, mirror := range s.Config.Mirrors {
 				mirrorTLSConfig, err := s.tlsConfigForMirror(mirror)
 				if err != nil {
 					return nil, err
 				}
 				endpoints = append(endpoints, APIEndpoint{
 					URL: mirror,
 					// guess mirrors are v2
 					Version:      APIVersion2,
 					Mirror:       true,
 					TrimHostname: true,
 					TLSConfig:    mirrorTLSConfig,
 				})
cb57b256
 			}
19515a7a
 		}
 		// v2 registry
 		endpoints = append(endpoints, APIEndpoint{
4fcb9ac4
 			URL:          DefaultV2Registry,
19515a7a
 			Version:      APIVersion2,
 			Official:     true,
 			TrimHostname: true,
 			TLSConfig:    tlsConfig,
 		})
 		// v1 registry
 		endpoints = append(endpoints, APIEndpoint{
4fcb9ac4
 			URL:          DefaultV1Registry,
19515a7a
 			Version:      APIVersion1,
 			Official:     true,
 			TrimHostname: true,
 			TLSConfig:    tlsConfig,
 		})
 		return endpoints, nil
 	}
 
 	slashIndex := strings.IndexRune(repoName, '/')
 	if slashIndex <= 0 {
 		return nil, fmt.Errorf("invalid repo name: missing '/':  %s", repoName)
 	}
 	hostname := repoName[:slashIndex]
 
4fcb9ac4
 	tlsConfig, err = s.TLSConfig(hostname)
19515a7a
 	if err != nil {
 		return nil, err
 	}
 	isSecure := !tlsConfig.InsecureSkipVerify
 
 	v2Versions := []auth.APIVersion{
 		{
 			Type:    "registry",
 			Version: "2.0",
 		},
 	}
 	endpoints = []APIEndpoint{
 		{
 			URL:           "https://" + hostname,
 			Version:       APIVersion2,
 			TrimHostname:  true,
 			TLSConfig:     tlsConfig,
4fcb9ac4
 			VersionHeader: DefaultRegistryVersionHeader,
19515a7a
 			Versions:      v2Versions,
 		},
 		{
 			URL:          "https://" + hostname,
 			Version:      APIVersion1,
 			TrimHostname: true,
 			TLSConfig:    tlsConfig,
 		},
 	}
 
 	if !isSecure {
 		endpoints = append(endpoints, APIEndpoint{
 			URL:          "http://" + hostname,
 			Version:      APIVersion2,
 			TrimHostname: true,
 			// used to check if supposed to be secure via InsecureSkipVerify
 			TLSConfig:     tlsConfig,
4fcb9ac4
 			VersionHeader: DefaultRegistryVersionHeader,
19515a7a
 			Versions:      v2Versions,
 		}, APIEndpoint{
 			URL:          "http://" + hostname,
 			Version:      APIVersion1,
 			TrimHostname: true,
 			// used to check if supposed to be secure via InsecureSkipVerify
 			TLSConfig: tlsConfig,
 		})
 	}
 
 	return endpoints, nil
 }