| package registry // import "github.com/docker/docker/registry" | |
| import ( | |
| "context" | |
| "crypto/tls" | |
| "net/url" | |
| "strings" | |
| "sync" | |
| "github.com/containerd/log" | |
| "github.com/distribution/reference" | |
| "github.com/docker/docker/api/types/registry" | |
| "github.com/docker/docker/errdefs" | |
| ) | |
| // 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 [Service] 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() | |
| } | |
| // ReplaceConfig prepares a transaction which will atomically replace the | |
| // registry service's configuration when the returned commit function is called. | |
| func (s *Service) ReplaceConfig(options ServiceOptions) (commit func(), err error) { | |
| config, err := newServiceConfig(options) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return func() { | |
| s.mu.Lock() | |
| defer s.mu.Unlock() | |
| s.config = config | |
| }, nil | |
| } | |
| // 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 | |
| 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 | |
| } | |
| log.G(ctx).WithError(err).Infof("Error logging in to endpoint, trying next endpoint") | |
| } | |
| return "", "", err | |
| } | |
| // 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 // Deprecated: v1 registries are deprecated, and endpoints are always v2. | |
| 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) | |
| } |