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  }