| package registry // import "github.com/docker/docker/registry" | |
| import ( | |
| "context" | |
| "crypto/tls" | |
| "net/url" | |
| "strings" | |
| "sync" | |
| "github.com/docker/distribution/reference" | |
| "github.com/docker/docker/api/types/registry" | |
| "github.com/docker/docker/errdefs" | |
| "github.com/sirupsen/logrus" | |
| ) | |
| // Service is a registry service. It tracks configuration data such as a list | |
| // of mirrors. | |
| type Service struct { | |
| config *serviceConfig | |
| mu sync.RWMutex | |
| } | |
| // NewService returns a new instance of defaultService ready to be | |
| // installed into an engine. | |
| func NewService(options ServiceOptions) (*Service, error) { | |
| config, err := newServiceConfig(options) | |
| return &Service{config: config}, err | |
| } | |
| // ServiceConfig returns a copy of the public registry service's configuration. | |
| func (s *Service) ServiceConfig() *registry.ServiceConfig { | |
| s.mu.RLock() | |
| defer s.mu.RUnlock() | |
| return s.config.copy() | |
| } | |
| // LoadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries for Service. | |
| func (s *Service) LoadAllowNondistributableArtifacts(registries []string) error { | |
| s.mu.Lock() | |
| defer s.mu.Unlock() | |
| return s.config.loadAllowNondistributableArtifacts(registries) | |
| } | |
| // LoadMirrors loads registry mirrors for Service | |
| func (s *Service) LoadMirrors(mirrors []string) error { | |
| s.mu.Lock() | |
| defer s.mu.Unlock() | |
| return s.config.loadMirrors(mirrors) | |
| } | |
| // LoadInsecureRegistries loads insecure registries for Service | |
| func (s *Service) LoadInsecureRegistries(registries []string) error { | |
| s.mu.Lock() | |
| defer s.mu.Unlock() | |
| return s.config.loadInsecureRegistries(registries) | |
| } | |
| // Auth contacts the public registry with the provided credentials, | |
| // and returns OK if authentication was successful. | |
| // It can be used to verify the validity of a client's credentials. | |
| func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, userAgent string) (status, token string, err error) { | |
| // TODO Use ctx when searching for repositories | |
| var registryHostName = IndexHostname | |
| if authConfig.ServerAddress != "" { | |
| serverAddress := authConfig.ServerAddress | |
| if !strings.HasPrefix(serverAddress, "https://") && !strings.HasPrefix(serverAddress, "http://") { | |
| serverAddress = "https://" + serverAddress | |
| } | |
| u, err := url.Parse(serverAddress) | |
| if err != nil { | |
| return "", "", invalidParamWrapf(err, "unable to parse server address") | |
| } | |
| registryHostName = u.Host | |
| } | |
| // Lookup endpoints for authentication using "LookupPushEndpoints", which | |
| // excludes mirrors to prevent sending credentials of the upstream registry | |
| // to a mirror. | |
| endpoints, err := s.LookupPushEndpoints(registryHostName) | |
| if err != nil { | |
| return "", "", invalidParam(err) | |
| } | |
| for _, endpoint := range endpoints { | |
| status, token, err = loginV2(authConfig, endpoint, userAgent) | |
| if err == nil { | |
| return | |
| } | |
| if errdefs.IsUnauthorized(err) { | |
| // Failed to authenticate; don't continue with (non-TLS) endpoints. | |
| return status, token, err | |
| } | |
| logrus.WithError(err).Infof("Error logging in to endpoint, trying next endpoint") | |
| } | |
| return "", "", err | |
| } | |
| // splitReposSearchTerm breaks a search term into an index name and remote name | |
| func splitReposSearchTerm(reposName string) (string, string) { | |
| nameParts := strings.SplitN(reposName, "/", 2) | |
| if len(nameParts) == 1 || (!strings.Contains(nameParts[0], ".") && | |
| !strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost") { | |
| // This is a Docker Hub repository (ex: samalba/hipache or ubuntu), | |
| // use the default Docker Hub registry (docker.io) | |
| return IndexName, reposName | |
| } | |
| return nameParts[0], nameParts[1] | |
| } | |
| // ResolveRepository splits a repository name into its components | |
| // and configuration of the associated registry. | |
| func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, error) { | |
| s.mu.RLock() | |
| defer s.mu.RUnlock() | |
| return newRepositoryInfo(s.config, name) | |
| } | |
| // APIEndpoint represents a remote API endpoint | |
| type APIEndpoint struct { | |
| Mirror bool | |
| URL *url.URL | |
| Version APIVersion | |
| AllowNondistributableArtifacts bool | |
| Official bool | |
| TrimHostname bool | |
| TLSConfig *tls.Config | |
| } | |
| // LookupPullEndpoints creates a list of v2 endpoints to try to pull from, in order of preference. | |
| // It gives preference to mirrors over the actual registry, and HTTPS over plain HTTP. | |
| func (s *Service) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) { | |
| s.mu.RLock() | |
| defer s.mu.RUnlock() | |
| return s.lookupV2Endpoints(hostname) | |
| } | |
| // LookupPushEndpoints creates a list of v2 endpoints to try to push to, in order of preference. | |
| // It gives preference to HTTPS over plain HTTP. Mirrors are not included. | |
| func (s *Service) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) { | |
| s.mu.RLock() | |
| defer s.mu.RUnlock() | |
| allEndpoints, err := s.lookupV2Endpoints(hostname) | |
| if err == nil { | |
| for _, endpoint := range allEndpoints { | |
| if !endpoint.Mirror { | |
| endpoints = append(endpoints, endpoint) | |
| } | |
| } | |
| } | |
| return endpoints, err | |
| } | |
| // IsInsecureRegistry returns true if the registry at given host is configured as | |
| // insecure registry. | |
| func (s *Service) IsInsecureRegistry(host string) bool { | |
| s.mu.RLock() | |
| defer s.mu.RUnlock() | |
| return !s.config.isSecureIndex(host) | |
| } |