Merge pull request #45086 from corhere/search-in-registry-service

Move filtered registry search out of the image service
This commit is contained in:
Bjorn Neergaard 2023-03-15 07:52:42 -06:00 committed by GitHub
commit 1c84f63a40
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 230 additions and 253 deletions

View file

@ -39,5 +39,8 @@ type importExportBackend interface {
type registryBackend interface {
PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *registry.AuthConfig, outStream io.Writer) error
PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *registry.AuthConfig, outStream io.Writer) error
SearchRegistryForImages(ctx context.Context, searchFilters filters.Args, term string, limit int, authConfig *registry.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
}
type Searcher interface {
Search(ctx context.Context, searchFilters filters.Args, term string, limit int, authConfig *registry.AuthConfig, metaHeaders map[string][]string) ([]registry.SearchResult, error)
}

View file

@ -10,6 +10,7 @@ import (
// imageRouter is a router to talk with the image controller
type imageRouter struct {
backend Backend
searcher Searcher
referenceBackend reference.Store
imageStore image.Store
layerStore layer.Store
@ -17,9 +18,10 @@ type imageRouter struct {
}
// NewRouter initializes a new image router
func NewRouter(backend Backend, referenceBackend reference.Store, imageStore image.Store, layerStore layer.Store) router.Router {
func NewRouter(backend Backend, searcher Searcher, referenceBackend reference.Store, imageStore image.Store, layerStore layer.Store) router.Router {
ir := &imageRouter{
backend: backend,
searcher: searcher,
referenceBackend: referenceBackend,
imageStore: imageStore,
layerStore: layerStore,

View file

@ -415,11 +415,11 @@ func (ir *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWrite
// For a search it is not an error if no auth was given. Ignore invalid
// AuthConfig to increase compatibility with the existing API.
authConfig, _ := registry.DecodeAuthConfig(r.Header.Get(registry.AuthHeader))
query, err := ir.backend.SearchRegistryForImages(ctx, searchFilters, r.Form.Get("term"), limit, authConfig, headers)
res, err := ir.searcher.Search(ctx, searchFilters, r.Form.Get("term"), limit, authConfig, headers)
if err != nil {
return err
}
return httputils.WriteJSON(w, http.StatusOK, query.Results)
return httputils.WriteJSON(w, http.StatusOK, res)
}
func (ir *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {

View file

@ -511,6 +511,7 @@ func initRouter(opts routerOptions) {
container.NewRouter(opts.daemon, decoder, opts.daemon.RawSysInfo().CgroupUnified),
image.NewRouter(
opts.daemon.ImageService(),
opts.daemon.RegistryService(),
opts.daemon.ReferenceStore,
opts.daemon.ImageService().DistributionServices().ImageStore,
opts.daemon.ImageService().DistributionServices().LayerStore,

View file

@ -1,19 +0,0 @@
package containerd
import (
"context"
"errors"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/errdefs"
)
// SearchRegistryForImages queries the registry for images matching
// term. authConfig is used to login.
//
// TODO: this could be implemented in a registry service instead of the image
// service.
func (i *ImageService) SearchRegistryForImages(ctx context.Context, searchFilters filters.Args, term string, limit int, authConfig *registry.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error) {
return nil, errdefs.NotImplemented(errors.New("not implemented"))
}

View file

@ -23,7 +23,7 @@ func (i *ImageService) newResolverFromAuthConfig(authConfig *registrytypes.AuthC
}), tracker
}
func hostsWrapper(hostsFn docker.RegistryHosts, authConfig *registrytypes.AuthConfig, regService registry.Service) docker.RegistryHosts {
func hostsWrapper(hostsFn docker.RegistryHosts, authConfig *registrytypes.AuthConfig, regService RegistryConfigProvider) docker.RegistryHosts {
return func(n string) ([]docker.RegistryHost, error) {
hosts, err := hostsFn(n)
if err != nil {

View file

@ -14,7 +14,6 @@ import (
"github.com/docker/docker/errdefs"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
"github.com/docker/docker/registry"
"github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/identity"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@ -27,15 +26,19 @@ type ImageService struct {
containers container.Store
snapshotter string
registryHosts RegistryHostsProvider
registryService registry.Service
registryService RegistryConfigProvider
}
type RegistryHostsProvider interface {
RegistryHosts() docker.RegistryHosts
}
type RegistryConfigProvider interface {
IsInsecureRegistry(host string) bool
}
// NewService creates a new ImageService.
func NewService(c *containerd.Client, containers container.Store, snapshotter string, hostsProvider RegistryHostsProvider, registry registry.Service) *ImageService {
func NewService(c *containerd.Client, containers container.Store, snapshotter string, hostsProvider RegistryHostsProvider, registry RegistryConfigProvider) *ImageService {
return &ImageService{
client: c,
containers: containers,

View file

@ -82,7 +82,7 @@ type Daemon struct {
configStore *config.Config
statsCollector *stats.Collector
defaultLogConfig containertypes.LogConfig
registryService registry.Service
registryService *registry.Service
EventsService *events.Events
netController *libnetwork.Controller
volumes *volumesservice.VolumesService
@ -1459,6 +1459,11 @@ func (daemon *Daemon) ImageService() ImageService {
return daemon.imageService
}
// RegistryService returns the Daemon's RegistryService
func (daemon *Daemon) RegistryService() *registry.Service {
return daemon.registryService
}
// BuilderBackend returns the backend used by builder
func (daemon *Daemon) BuilderBackend() builder.Backend {
return struct {

View file

@ -73,7 +73,6 @@ type ImageService interface {
// Other
GetRepository(ctx context.Context, ref reference.Named, authConfig *registry.AuthConfig) (distribution.Repository, error)
SearchRegistryForImages(ctx context.Context, searchFilters filters.Args, term string, limit int, authConfig *registry.AuthConfig, headers map[string][]string) (*registry.SearchResults, error)
DistributionServices() images.DistributionServices
Children(id image.ID) []image.ID
Cleanup() error

View file

@ -1,85 +0,0 @@
package images // import "github.com/docker/docker/daemon/images"
import (
"context"
"strconv"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/errdefs"
"github.com/pkg/errors"
)
var acceptedSearchFilterTags = map[string]bool{
"is-automated": true,
"is-official": true,
"stars": true,
}
// SearchRegistryForImages queries the registry for images matching
// term. authConfig is used to login.
//
// TODO: this could be implemented in a registry service instead of the image
// service.
func (i *ImageService) SearchRegistryForImages(ctx context.Context, searchFilters filters.Args, term string, limit int,
authConfig *registry.AuthConfig,
headers map[string][]string) (*registry.SearchResults, error) {
if err := searchFilters.Validate(acceptedSearchFilterTags); err != nil {
return nil, err
}
isAutomated, err := searchFilters.GetBoolOrDefault("is-automated", false)
if err != nil {
return nil, err
}
isOfficial, err := searchFilters.GetBoolOrDefault("is-official", false)
if err != nil {
return nil, err
}
hasStarFilter := 0
if searchFilters.Contains("stars") {
hasStars := searchFilters.Get("stars")
for _, hasStar := range hasStars {
iHasStar, err := strconv.Atoi(hasStar)
if err != nil {
return nil, errdefs.InvalidParameter(errors.Wrapf(err, "invalid filter 'stars=%s'", hasStar))
}
if iHasStar > hasStarFilter {
hasStarFilter = iHasStar
}
}
}
unfilteredResult, err := i.registryService.Search(ctx, term, limit, authConfig, dockerversion.DockerUserAgent(ctx), headers)
if err != nil {
return nil, err
}
filteredResults := []registry.SearchResult{}
for _, result := range unfilteredResult.Results {
if searchFilters.Contains("is-automated") {
if isAutomated != result.IsAutomated {
continue
}
}
if searchFilters.Contains("is-official") {
if isOfficial != result.IsOfficial {
continue
}
}
if searchFilters.Contains("stars") {
if result.StarCount < hasStarFilter {
continue
}
}
filteredResults = append(filteredResults, result)
}
return &registry.SearchResults{
Query: unfilteredResult.Query,
NumResults: len(filteredResults),
Results: filteredResults,
}, nil
}

View file

@ -8,12 +8,12 @@ import (
"github.com/containerd/containerd/leases"
"github.com/docker/docker/container"
daemonevents "github.com/docker/docker/daemon/events"
"github.com/docker/docker/distribution"
"github.com/docker/docker/distribution/metadata"
"github.com/docker/docker/distribution/xfer"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
dockerreference "github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)
@ -39,7 +39,7 @@ type ImageServiceConfig struct {
MaxConcurrentUploads int
MaxDownloadAttempts int
ReferenceStore dockerreference.Store
RegistryService registry.Service
RegistryService distribution.RegistryResolver
ContentStore content.Store
Leases leases.Manager
ContentNamespace string
@ -73,7 +73,7 @@ type ImageService struct {
layerStore layer.Store
pruneRunning int32
referenceStore dockerreference.Store
registryService registry.Service
registryService distribution.RegistryResolver
uploadManager *xfer.LayerUploadManager
leases leases.Manager
content content.Store

View file

@ -8,6 +8,7 @@ import (
"github.com/docker/distribution"
"github.com/docker/distribution/manifest/schema2"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/distribution/metadata"
"github.com/docker/docker/distribution/xfer"
@ -35,7 +36,7 @@ type Config struct {
ProgressOutput progress.Output
// RegistryService is the registry service to use for TLS configuration
// and endpoint lookup.
RegistryService registrypkg.Service
RegistryService RegistryResolver
// ImageEventLogger notifies events for a given image
ImageEventLogger func(id, name, action string)
// MetadataStore is the storage backend for distribution-specific
@ -75,6 +76,13 @@ type ImagePushConfig struct {
UploadManager *xfer.LayerUploadManager
}
// RegistryResolver is used for TLS configuration and endpoint lookup.
type RegistryResolver interface {
LookupPushEndpoints(hostname string) (endpoints []registrypkg.APIEndpoint, err error)
LookupPullEndpoints(hostname string) (endpoints []registrypkg.APIEndpoint, err error)
ResolveRepository(name reference.Named) (*registrypkg.RepositoryInfo, error)
}
// ImageConfigStore handles storing and getting image configurations
// by digest. Allows getting an image configurations rootfs from the
// configuration.

View file

@ -520,7 +520,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
if err != nil {
t.Fatal(err)
}
s := defaultService{config: cfg}
s := Service{config: cfg}
imageName, err := reference.WithName(IndexName + "/test/image")
if err != nil {

139
registry/search.go Normal file
View file

@ -0,0 +1,139 @@
package registry // import "github.com/docker/docker/registry"
import (
"context"
"net/http"
"strconv"
"strings"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/dockerversion"
"github.com/docker/docker/errdefs"
"github.com/docker/distribution/registry/client/auth"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
var acceptedSearchFilterTags = map[string]bool{
"is-automated": true,
"is-official": true,
"stars": true,
}
// Search queries the public registry for repositories matching the specified
// search term and filters.
func (s *Service) Search(ctx context.Context, searchFilters filters.Args, term string, limit int, authConfig *registry.AuthConfig, headers map[string][]string) ([]registry.SearchResult, error) {
if err := searchFilters.Validate(acceptedSearchFilterTags); err != nil {
return nil, err
}
isAutomated, err := searchFilters.GetBoolOrDefault("is-automated", false)
if err != nil {
return nil, err
}
isOfficial, err := searchFilters.GetBoolOrDefault("is-official", false)
if err != nil {
return nil, err
}
hasStarFilter := 0
if searchFilters.Contains("stars") {
hasStars := searchFilters.Get("stars")
for _, hasStar := range hasStars {
iHasStar, err := strconv.Atoi(hasStar)
if err != nil {
return nil, errdefs.InvalidParameter(errors.Wrapf(err, "invalid filter 'stars=%s'", hasStar))
}
if iHasStar > hasStarFilter {
hasStarFilter = iHasStar
}
}
}
unfilteredResult, err := s.searchUnfiltered(ctx, term, limit, authConfig, dockerversion.DockerUserAgent(ctx), headers)
if err != nil {
return nil, err
}
filteredResults := []registry.SearchResult{}
for _, result := range unfilteredResult.Results {
if searchFilters.Contains("is-automated") {
if isAutomated != result.IsAutomated {
continue
}
}
if searchFilters.Contains("is-official") {
if isOfficial != result.IsOfficial {
continue
}
}
if searchFilters.Contains("stars") {
if result.StarCount < hasStarFilter {
continue
}
}
filteredResults = append(filteredResults, result)
}
return filteredResults, nil
}
func (s *Service) searchUnfiltered(ctx context.Context, term string, limit int, authConfig *registry.AuthConfig, userAgent string, headers map[string][]string) (*registry.SearchResults, error) {
// TODO Use ctx when searching for repositories
if hasScheme(term) {
return nil, invalidParamf("invalid repository name: repository name (%s) should not have a scheme", term)
}
indexName, remoteName := splitReposSearchTerm(term)
// Search is a long-running operation, just lock s.config to avoid block others.
s.mu.RLock()
index, err := newIndexInfo(s.config, indexName)
s.mu.RUnlock()
if err != nil {
return nil, err
}
if index.Official {
// If pull "library/foo", it's stored locally under "foo"
remoteName = strings.TrimPrefix(remoteName, "library/")
}
endpoint, err := newV1Endpoint(index, userAgent, headers)
if err != nil {
return nil, err
}
var client *http.Client
if authConfig != nil && authConfig.IdentityToken != "" && authConfig.Username != "" {
creds := NewStaticCredentialStore(authConfig)
scopes := []auth.Scope{
auth.RegistryScope{
Name: "catalog",
Actions: []string{"search"},
},
}
modifiers := Headers(userAgent, nil)
v2Client, err := v2AuthHTTPClient(endpoint.URL, endpoint.client.Transport, modifiers, creds, scopes)
if err != nil {
return nil, err
}
// Copy non transport http client features
v2Client.Timeout = endpoint.client.Timeout
v2Client.CheckRedirect = endpoint.client.CheckRedirect
v2Client.Jar = endpoint.client.Jar
logrus.Debugf("using v2 client for search to %s", endpoint.URL)
client = v2Client
} else {
client = endpoint.client
if err := authorizeClient(client, authConfig, endpoint); err != nil {
return nil, err
}
}
return newSession(client, endpoint).searchRepositories(remoteName, limit)
}

View file

@ -1,44 +1,26 @@
package images // import "github.com/docker/docker/daemon/images"
package registry // import "github.com/docker/docker/registry"
import (
"context"
"errors"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/errdefs"
registrypkg "github.com/docker/docker/registry"
"gotest.tools/v3/assert"
)
type fakeService struct {
registrypkg.Service
shouldReturnError bool
term string
results []registry.SearchResult
}
func (s *fakeService) Search(ctx context.Context, term string, limit int, authConfig *registry.AuthConfig, userAgent string, headers map[string][]string) (*registry.SearchResults, error) {
if s.shouldReturnError {
return nil, errdefs.Unknown(errors.New("search unknown error"))
}
return &registry.SearchResults{
Query: s.term,
NumResults: len(s.results),
Results: s.results,
}, nil
}
func TestSearchRegistryForImagesErrors(t *testing.T) {
func TestSearchErrors(t *testing.T) {
errorCases := []struct {
filtersArgs filters.Args
shouldReturnError bool
expectedError string
}{
{
expectedError: "search unknown error",
expectedError: "Unexpected status code 500",
shouldReturnError: true,
},
{
@ -82,12 +64,20 @@ func TestSearchRegistryForImagesErrors(t *testing.T) {
for _, tc := range errorCases {
tc := tc
t.Run(tc.expectedError, func(t *testing.T) {
daemon := &ImageService{
registryService: &fakeService{
shouldReturnError: tc.shouldReturnError,
},
}
_, err := daemon.SearchRegistryForImages(context.Background(), tc.filtersArgs, "term", 0, nil, map[string][]string{})
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !tc.shouldReturnError {
t.Errorf("unexpected HTTP request")
}
http.Error(w, "no search for you", http.StatusInternalServerError)
}))
defer srv.Close()
// Construct the search term by cutting the 'http://' prefix off srv.URL.
term := srv.URL[7:] + "/term"
reg, err := NewService(ServiceOptions{})
assert.NilError(t, err)
_, err = reg.Search(context.Background(), tc.filtersArgs, term, 0, nil, map[string][]string{})
assert.ErrorContains(t, err, tc.expectedError)
if tc.shouldReturnError {
assert.Check(t, errdefs.IsUnknown(err), "got: %T: %v", err, err)
@ -98,8 +88,8 @@ func TestSearchRegistryForImagesErrors(t *testing.T) {
}
}
func TestSearchRegistryForImages(t *testing.T) {
term := "term"
func TestSearch(t *testing.T) {
const term = "term"
successCases := []struct {
name string
filtersArgs filters.Args
@ -348,17 +338,24 @@ func TestSearchRegistryForImages(t *testing.T) {
for _, tc := range successCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
daemon := &ImageService{
registryService: &fakeService{
term: term,
results: tc.registryResults,
},
}
results, err := daemon.SearchRegistryForImages(context.Background(), tc.filtersArgs, term, 0, nil, map[string][]string{})
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-type", "application/json")
json.NewEncoder(w).Encode(registry.SearchResults{
Query: term,
NumResults: len(tc.registryResults),
Results: tc.registryResults,
})
}))
defer srv.Close()
// Construct the search term by cutting the 'http://' prefix off srv.URL.
searchTerm := srv.URL[7:] + "/" + term
reg, err := NewService(ServiceOptions{})
assert.NilError(t, err)
assert.Equal(t, results.Query, term)
assert.Equal(t, results.NumResults, len(tc.expectedResults))
assert.DeepEqual(t, results.Results, tc.expectedResults)
results, err := reg.Search(context.Background(), tc.filtersArgs, searchTerm, 0, nil, map[string][]string{})
assert.NilError(t, err)
assert.DeepEqual(t, results, tc.expectedResults)
})
}
}

View file

@ -3,56 +3,40 @@ package registry // import "github.com/docker/docker/registry"
import (
"context"
"crypto/tls"
"net/http"
"net/url"
"strings"
"sync"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/client/auth"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/errdefs"
"github.com/sirupsen/logrus"
)
// Service is the interface defining what a registry service should implement.
type Service interface {
Auth(ctx context.Context, authConfig *registry.AuthConfig, userAgent string) (status, token string, err error)
LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error)
LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error)
ResolveRepository(name reference.Named) (*RepositoryInfo, error)
Search(ctx context.Context, term string, limit int, authConfig *registry.AuthConfig, userAgent string, headers map[string][]string) (*registry.SearchResults, error)
ServiceConfig() *registry.ServiceConfig
LoadAllowNondistributableArtifacts([]string) error
LoadMirrors([]string) error
LoadInsecureRegistries([]string) error
IsInsecureRegistry(string) bool
}
// defaultService is a registry service. It tracks configuration data such as a list
// Service is a registry service. It tracks configuration data such as a list
// of mirrors.
type defaultService struct {
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) {
func NewService(options ServiceOptions) (*Service, error) {
config, err := newServiceConfig(options)
return &defaultService{config: config}, err
return &Service{config: config}, err
}
// ServiceConfig returns a copy of the public registry service's configuration.
func (s *defaultService) ServiceConfig() *registry.ServiceConfig {
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 *defaultService) LoadAllowNondistributableArtifacts(registries []string) error {
func (s *Service) LoadAllowNondistributableArtifacts(registries []string) error {
s.mu.Lock()
defer s.mu.Unlock()
@ -60,7 +44,7 @@ func (s *defaultService) LoadAllowNondistributableArtifacts(registries []string)
}
// LoadMirrors loads registry mirrors for Service
func (s *defaultService) LoadMirrors(mirrors []string) error {
func (s *Service) LoadMirrors(mirrors []string) error {
s.mu.Lock()
defer s.mu.Unlock()
@ -68,7 +52,7 @@ func (s *defaultService) LoadMirrors(mirrors []string) error {
}
// LoadInsecureRegistries loads insecure registries for Service
func (s *defaultService) LoadInsecureRegistries(registries []string) error {
func (s *Service) LoadInsecureRegistries(registries []string) error {
s.mu.Lock()
defer s.mu.Unlock()
@ -78,7 +62,7 @@ func (s *defaultService) LoadInsecureRegistries(registries []string) error {
// 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 *defaultService) Auth(ctx context.Context, authConfig *registry.AuthConfig, userAgent string) (status, token string, err error) {
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
@ -129,69 +113,9 @@ func splitReposSearchTerm(reposName string) (string, string) {
return nameParts[0], nameParts[1]
}
// Search queries the public registry for images matching the specified
// search terms, and returns the results.
func (s *defaultService) Search(ctx context.Context, term string, limit int, authConfig *registry.AuthConfig, userAgent string, headers map[string][]string) (*registry.SearchResults, error) {
// TODO Use ctx when searching for repositories
if hasScheme(term) {
return nil, invalidParamf("invalid repository name: repository name (%s) should not have a scheme", term)
}
indexName, remoteName := splitReposSearchTerm(term)
// Search is a long-running operation, just lock s.config to avoid block others.
s.mu.RLock()
index, err := newIndexInfo(s.config, indexName)
s.mu.RUnlock()
if err != nil {
return nil, err
}
if index.Official {
// If pull "library/foo", it's stored locally under "foo"
remoteName = strings.TrimPrefix(remoteName, "library/")
}
endpoint, err := newV1Endpoint(index, userAgent, headers)
if err != nil {
return nil, err
}
var client *http.Client
if authConfig != nil && authConfig.IdentityToken != "" && authConfig.Username != "" {
creds := NewStaticCredentialStore(authConfig)
scopes := []auth.Scope{
auth.RegistryScope{
Name: "catalog",
Actions: []string{"search"},
},
}
modifiers := Headers(userAgent, nil)
v2Client, err := v2AuthHTTPClient(endpoint.URL, endpoint.client.Transport, modifiers, creds, scopes)
if err != nil {
return nil, err
}
// Copy non transport http client features
v2Client.Timeout = endpoint.client.Timeout
v2Client.CheckRedirect = endpoint.client.CheckRedirect
v2Client.Jar = endpoint.client.Jar
logrus.Debugf("using v2 client for search to %s", endpoint.URL)
client = v2Client
} else {
client = endpoint.client
if err := authorizeClient(client, authConfig, endpoint); err != nil {
return nil, err
}
}
return newSession(client, endpoint).searchRepositories(remoteName, limit)
}
// ResolveRepository splits a repository name into its components
// and configuration of the associated registry.
func (s *defaultService) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
s.mu.RLock()
defer s.mu.RUnlock()
return newRepositoryInfo(s.config, name)
@ -210,7 +134,7 @@ type APIEndpoint struct {
// 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 *defaultService) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
func (s *Service) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
s.mu.RLock()
defer s.mu.RUnlock()
@ -219,7 +143,7 @@ func (s *defaultService) LookupPullEndpoints(hostname string) (endpoints []APIEn
// 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 *defaultService) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
func (s *Service) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
s.mu.RLock()
defer s.mu.RUnlock()
@ -236,7 +160,7 @@ func (s *defaultService) LookupPushEndpoints(hostname string) (endpoints []APIEn
// IsInsecureRegistry returns true if the registry at given host is configured as
// insecure registry.
func (s *defaultService) IsInsecureRegistry(host string) bool {
func (s *Service) IsInsecureRegistry(host string) bool {
s.mu.RLock()
defer s.mu.RUnlock()
return !s.config.isSecureIndex(host)

View file

@ -7,7 +7,7 @@ import (
"github.com/docker/go-connections/tlsconfig"
)
func (s *defaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) {
func (s *Service) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) {
ana := s.config.allowNondistributableArtifacts(hostname)
if hostname == DefaultNamespace || hostname == IndexHostname {

View file

@ -206,10 +206,10 @@ func (r *session) searchRepositories(term string, limit int) (*registry.SearchRe
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, &jsonmessage.JSONError{
return nil, errdefs.Unknown(&jsonmessage.JSONError{
Message: fmt.Sprintf("Unexpected status code %d", res.StatusCode),
Code: res.StatusCode,
}
})
}
result := new(registry.SearchResults)
return result, errors.Wrap(json.NewDecoder(res.Body).Decode(result), "error decoding registry search results")