github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/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/Sirupsen/logrus" 11 "github.com/docker/docker/reference" 12 "github.com/docker/engine-api/types" 13 registrytypes "github.com/docker/engine-api/types/registry" 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 ServiceOptions) *Service { 25 return &Service{ 26 config: newServiceConfig(options), 27 } 28 } 29 30 // ServiceConfig returns the public registry service configuration. 31 func (s *Service) ServiceConfig() *registrytypes.ServiceConfig { 32 return &s.config.ServiceConfig 33 } 34 35 // Auth contacts the public registry with the provided credentials, 36 // and returns OK if authentication was successful. 37 // It can be used to verify the validity of a client's credentials. 38 func (s *Service) Auth(authConfig *types.AuthConfig, userAgent string) (status, token string, err error) { 39 serverAddress := authConfig.ServerAddress 40 if !strings.HasPrefix(serverAddress, "https://") && !strings.HasPrefix(serverAddress, "http://") { 41 serverAddress = "https://" + serverAddress 42 } 43 u, err := url.Parse(serverAddress) 44 if err != nil { 45 return "", "", fmt.Errorf("unable to parse server address: %v", err) 46 } 47 48 endpoints, err := s.LookupPushEndpoints(u.Host) 49 if err != nil { 50 return "", "", err 51 } 52 53 for _, endpoint := range endpoints { 54 login := loginV2 55 if endpoint.Version == APIVersion1 { 56 login = loginV1 57 } 58 59 status, token, err = login(authConfig, endpoint, userAgent) 60 if err == nil { 61 return 62 } 63 if fErr, ok := err.(fallbackError); ok { 64 err = fErr.err 65 logrus.Infof("Error logging in to %s endpoint, trying next endpoint: %v", endpoint.Version, err) 66 continue 67 } 68 return "", "", err 69 } 70 71 return "", "", err 72 } 73 74 // splitReposSearchTerm breaks a search term into an index name and remote name 75 func splitReposSearchTerm(reposName string) (string, string) { 76 nameParts := strings.SplitN(reposName, "/", 2) 77 var indexName, remoteName string 78 if len(nameParts) == 1 || (!strings.Contains(nameParts[0], ".") && 79 !strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost") { 80 // This is a Docker Index repos (ex: samalba/hipache or ubuntu) 81 // 'docker.io' 82 indexName = IndexName 83 remoteName = reposName 84 } else { 85 indexName = nameParts[0] 86 remoteName = nameParts[1] 87 } 88 return indexName, remoteName 89 } 90 91 // Search queries the public registry for images matching the specified 92 // search terms, and returns the results. 93 func (s *Service) Search(term string, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error) { 94 if err := validateNoScheme(term); err != nil { 95 return nil, err 96 } 97 98 indexName, remoteName := splitReposSearchTerm(term) 99 100 index, err := newIndexInfo(s.config, indexName) 101 if err != nil { 102 return nil, err 103 } 104 105 // *TODO: Search multiple indexes. 106 endpoint, err := NewV1Endpoint(index, userAgent, http.Header(headers)) 107 if err != nil { 108 return nil, err 109 } 110 111 r, err := NewSession(endpoint.client, authConfig, endpoint) 112 if err != nil { 113 return nil, err 114 } 115 116 if index.Official { 117 localName := remoteName 118 if strings.HasPrefix(localName, "library/") { 119 // If pull "library/foo", it's stored locally under "foo" 120 localName = strings.SplitN(localName, "/", 2)[1] 121 } 122 123 return r.SearchRepositories(localName) 124 } 125 return r.SearchRepositories(remoteName) 126 } 127 128 // ResolveRepository splits a repository name into its components 129 // and configuration of the associated registry. 130 func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, error) { 131 return newRepositoryInfo(s.config, name) 132 } 133 134 // ResolveIndex takes indexName and returns index info 135 func (s *Service) ResolveIndex(name string) (*registrytypes.IndexInfo, error) { 136 return newIndexInfo(s.config, name) 137 } 138 139 // APIEndpoint represents a remote API endpoint 140 type APIEndpoint struct { 141 Mirror bool 142 URL *url.URL 143 Version APIVersion 144 Official bool 145 TrimHostname bool 146 TLSConfig *tls.Config 147 } 148 149 // ToV1Endpoint returns a V1 API endpoint based on the APIEndpoint 150 func (e APIEndpoint) ToV1Endpoint(userAgent string, metaHeaders http.Header) (*V1Endpoint, error) { 151 return newV1Endpoint(*e.URL, e.TLSConfig, userAgent, metaHeaders) 152 } 153 154 // TLSConfig constructs a client TLS configuration based on server defaults 155 func (s *Service) TLSConfig(hostname string) (*tls.Config, error) { 156 return newTLSConfig(hostname, isSecureIndex(s.config, hostname)) 157 } 158 159 func (s *Service) tlsConfigForMirror(mirrorURL *url.URL) (*tls.Config, error) { 160 return s.TLSConfig(mirrorURL.Host) 161 } 162 163 // LookupPullEndpoints creates a list of endpoints to try to pull from, in order of preference. 164 // It gives preference to v2 endpoints over v1, mirrors over the actual 165 // registry, and HTTPS over plain HTTP. 166 func (s *Service) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) { 167 return s.lookupEndpoints(hostname) 168 } 169 170 // LookupPushEndpoints creates a list of endpoints to try to push to, in order of preference. 171 // It gives preference to v2 endpoints over v1, and HTTPS over plain HTTP. 172 // Mirrors are not included. 173 func (s *Service) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) { 174 allEndpoints, err := s.lookupEndpoints(hostname) 175 if err == nil { 176 for _, endpoint := range allEndpoints { 177 if !endpoint.Mirror { 178 endpoints = append(endpoints, endpoint) 179 } 180 } 181 } 182 return endpoints, err 183 } 184 185 func (s *Service) lookupEndpoints(hostname string) (endpoints []APIEndpoint, err error) { 186 endpoints, err = s.lookupV2Endpoints(hostname) 187 if err != nil { 188 return nil, err 189 } 190 191 if s.config.V2Only { 192 return endpoints, nil 193 } 194 195 legacyEndpoints, err := s.lookupV1Endpoints(hostname) 196 if err != nil { 197 return nil, err 198 } 199 endpoints = append(endpoints, legacyEndpoints...) 200 201 return endpoints, nil 202 }