github.com/mheon/docker@v0.11.2-0.20150922122814-44f47903a831/registry/service.go (about)

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