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 }