github.com/yuanlv/docker@v1.8.1/registry/service.go (about)

     1  package registry
     2  
     3  import (
     4  	"crypto/tls"
     5  	"fmt"
     6  	"net/http"
     7  	"net/url"
     8  	"strings"
     9  
    10  	"github.com/docker/distribution/registry/client/auth"
    11  	"github.com/docker/docker/cliconfig"
    12  	"github.com/docker/docker/pkg/tlsconfig"
    13  )
    14  
    15  // Service is a registry service. It tracks configuration data such as a list
    16  // of mirrors.
    17  type Service struct {
    18  	Config *ServiceConfig
    19  }
    20  
    21  // NewService returns a new instance of Service ready to be
    22  // installed into an engine.
    23  func NewService(options *Options) *Service {
    24  	return &Service{
    25  		Config: NewServiceConfig(options),
    26  	}
    27  }
    28  
    29  // Auth contacts the public registry with the provided credentials,
    30  // and returns OK if authentication was sucessful.
    31  // It can be used to verify the validity of a client's credentials.
    32  func (s *Service) Auth(authConfig *cliconfig.AuthConfig) (string, error) {
    33  	addr := authConfig.ServerAddress
    34  	if addr == "" {
    35  		// Use the official registry address if not specified.
    36  		addr = IndexServer
    37  	}
    38  	index, err := s.ResolveIndex(addr)
    39  	if err != nil {
    40  		return "", err
    41  	}
    42  	endpoint, err := NewEndpoint(index, nil)
    43  	if err != nil {
    44  		return "", err
    45  	}
    46  	authConfig.ServerAddress = endpoint.String()
    47  	return Login(authConfig, endpoint)
    48  }
    49  
    50  // Search queries the public registry for images matching the specified
    51  // search terms, and returns the results.
    52  func (s *Service) Search(term string, authConfig *cliconfig.AuthConfig, headers map[string][]string) (*SearchResults, error) {
    53  	repoInfo, err := s.ResolveRepository(term)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	// *TODO: Search multiple indexes.
    59  	endpoint, err := repoInfo.GetEndpoint(http.Header(headers))
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	r, err := NewSession(endpoint.client, authConfig, endpoint)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	return r.SearchRepositories(repoInfo.GetSearchTerm())
    68  }
    69  
    70  // ResolveRepository splits a repository name into its components
    71  // and configuration of the associated registry.
    72  func (s *Service) ResolveRepository(name string) (*RepositoryInfo, error) {
    73  	return s.Config.NewRepositoryInfo(name)
    74  }
    75  
    76  // ResolveIndex takes indexName and returns index info
    77  func (s *Service) ResolveIndex(name string) (*IndexInfo, error) {
    78  	return s.Config.NewIndexInfo(name)
    79  }
    80  
    81  // APIEndpoint represents a remote API endpoint
    82  type APIEndpoint struct {
    83  	Mirror        bool
    84  	URL           string
    85  	Version       APIVersion
    86  	Official      bool
    87  	TrimHostname  bool
    88  	TLSConfig     *tls.Config
    89  	VersionHeader string
    90  	Versions      []auth.APIVersion
    91  }
    92  
    93  // ToV1Endpoint returns a V1 API endpoint based on the APIEndpoint
    94  func (e APIEndpoint) ToV1Endpoint(metaHeaders http.Header) (*Endpoint, error) {
    95  	return newEndpoint(e.URL, e.TLSConfig, metaHeaders)
    96  }
    97  
    98  // TLSConfig constructs a client TLS configuration based on server defaults
    99  func (s *Service) TLSConfig(hostname string) (*tls.Config, error) {
   100  	return newTLSConfig(hostname, s.Config.isSecureIndex(hostname))
   101  }
   102  
   103  func (s *Service) tlsConfigForMirror(mirror string) (*tls.Config, error) {
   104  	mirrorURL, err := url.Parse(mirror)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	return s.TLSConfig(mirrorURL.Host)
   109  }
   110  
   111  // LookupPullEndpoints creates an list of endpoints to try to pull from, in order of preference.
   112  // It gives preference to v2 endpoints over v1, mirrors over the actual
   113  // registry, and HTTPS over plain HTTP.
   114  func (s *Service) LookupPullEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
   115  	return s.lookupEndpoints(repoName, false)
   116  }
   117  
   118  // LookupPushEndpoints creates an list of endpoints to try to push to, in order of preference.
   119  // It gives preference to v2 endpoints over v1, and HTTPS over plain HTTP.
   120  // Mirrors are not included.
   121  func (s *Service) LookupPushEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
   122  	return s.lookupEndpoints(repoName, true)
   123  }
   124  
   125  func (s *Service) lookupEndpoints(repoName string, isPush bool) (endpoints []APIEndpoint, err error) {
   126  	var cfg = tlsconfig.ServerDefault
   127  	tlsConfig := &cfg
   128  	if strings.HasPrefix(repoName, DefaultNamespace+"/") {
   129  		if !isPush {
   130  			// v2 mirrors for pull only
   131  			for _, mirror := range s.Config.Mirrors {
   132  				mirrorTLSConfig, err := s.tlsConfigForMirror(mirror)
   133  				if err != nil {
   134  					return nil, err
   135  				}
   136  				endpoints = append(endpoints, APIEndpoint{
   137  					URL: mirror,
   138  					// guess mirrors are v2
   139  					Version:      APIVersion2,
   140  					Mirror:       true,
   141  					TrimHostname: true,
   142  					TLSConfig:    mirrorTLSConfig,
   143  				})
   144  			}
   145  		}
   146  		// v2 registry
   147  		endpoints = append(endpoints, APIEndpoint{
   148  			URL:          DefaultV2Registry,
   149  			Version:      APIVersion2,
   150  			Official:     true,
   151  			TrimHostname: true,
   152  			TLSConfig:    tlsConfig,
   153  		})
   154  		// v1 registry
   155  		endpoints = append(endpoints, APIEndpoint{
   156  			URL:          DefaultV1Registry,
   157  			Version:      APIVersion1,
   158  			Official:     true,
   159  			TrimHostname: true,
   160  			TLSConfig:    tlsConfig,
   161  		})
   162  		return endpoints, nil
   163  	}
   164  
   165  	slashIndex := strings.IndexRune(repoName, '/')
   166  	if slashIndex <= 0 {
   167  		return nil, fmt.Errorf("invalid repo name: missing '/':  %s", repoName)
   168  	}
   169  	hostname := repoName[:slashIndex]
   170  
   171  	tlsConfig, err = s.TLSConfig(hostname)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	isSecure := !tlsConfig.InsecureSkipVerify
   176  
   177  	v2Versions := []auth.APIVersion{
   178  		{
   179  			Type:    "registry",
   180  			Version: "2.0",
   181  		},
   182  	}
   183  	endpoints = []APIEndpoint{
   184  		{
   185  			URL:           "https://" + hostname,
   186  			Version:       APIVersion2,
   187  			TrimHostname:  true,
   188  			TLSConfig:     tlsConfig,
   189  			VersionHeader: DefaultRegistryVersionHeader,
   190  			Versions:      v2Versions,
   191  		},
   192  		{
   193  			URL:          "https://" + hostname,
   194  			Version:      APIVersion1,
   195  			TrimHostname: true,
   196  			TLSConfig:    tlsConfig,
   197  		},
   198  	}
   199  
   200  	if !isSecure {
   201  		endpoints = append(endpoints, APIEndpoint{
   202  			URL:          "http://" + hostname,
   203  			Version:      APIVersion2,
   204  			TrimHostname: true,
   205  			// used to check if supposed to be secure via InsecureSkipVerify
   206  			TLSConfig:     tlsConfig,
   207  			VersionHeader: DefaultRegistryVersionHeader,
   208  			Versions:      v2Versions,
   209  		}, APIEndpoint{
   210  			URL:          "http://" + hostname,
   211  			Version:      APIVersion1,
   212  			TrimHostname: true,
   213  			// used to check if supposed to be secure via InsecureSkipVerify
   214  			TLSConfig: tlsConfig,
   215  		})
   216  	}
   217  
   218  	return endpoints, nil
   219  }