github.com/docker/cnab-to-oci@v0.3.0-beta4/remotes/resolver.go (about)

     1  package remotes
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"fmt"
     7  	"net/http"
     8  
     9  	"github.com/containerd/containerd/remotes"
    10  	"github.com/containerd/containerd/remotes/docker"
    11  	"github.com/docker/cli/cli/config/configfile"
    12  	"github.com/docker/distribution/reference"
    13  	"github.com/docker/docker/registry"
    14  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    15  )
    16  
    17  type multiRegistryResolver struct {
    18  	plainHTTP           remotes.Resolver
    19  	secure              remotes.Resolver
    20  	skipTLS             remotes.Resolver
    21  	plainHTTPRegistries map[string]struct{}
    22  	skipTLSRegistries   map[string]struct{}
    23  }
    24  
    25  func (r *multiRegistryResolver) resolveImplementation(image string) (remotes.Resolver, error) {
    26  	ref, err := reference.ParseNormalizedNamed(image)
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  	repoInfo, err := registry.ParseRepositoryInfo(ref)
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  	if _, plainHTTP := r.plainHTTPRegistries[repoInfo.Index.Name]; plainHTTP {
    35  		return r.plainHTTP, nil
    36  	}
    37  	if _, skipTLS := r.skipTLSRegistries[repoInfo.Index.Name]; skipTLS {
    38  		return r.skipTLS, nil
    39  	}
    40  	return r.secure, nil
    41  }
    42  
    43  func (r *multiRegistryResolver) Resolve(ctx context.Context, ref string) (name string, desc ocispec.Descriptor, err error) {
    44  	impl, err := r.resolveImplementation(ref)
    45  	if err != nil {
    46  		return "", ocispec.Descriptor{}, err
    47  	}
    48  	return impl.Resolve(ctx, ref)
    49  }
    50  
    51  func (r *multiRegistryResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) {
    52  	impl, err := r.resolveImplementation(ref)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	return impl.Fetcher(ctx, ref)
    57  }
    58  
    59  func (r *multiRegistryResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) {
    60  	impl, err := r.resolveImplementation(ref)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	return impl.Pusher(ctx, ref)
    65  }
    66  
    67  // CreateResolver creates a docker registry resolver, using the local docker CLI credentials
    68  func CreateResolver(cfg *configfile.ConfigFile, plainHTTPRegistries ...string) remotes.Resolver {
    69  	authorizer := docker.NewAuthorizer(nil, func(hostName string) (string, string, error) {
    70  		if hostName == registry.DefaultV2Registry.Host {
    71  			hostName = registry.IndexServer
    72  		}
    73  		a, err := cfg.GetAuthConfig(hostName)
    74  		if err != nil {
    75  			return "", "", err
    76  		}
    77  		if a.IdentityToken != "" {
    78  			return "", a.IdentityToken, nil
    79  		}
    80  		return a.Username, a.Password, nil
    81  	})
    82  
    83  	clientSkipTLS := &http.Client{
    84  		Transport: &http.Transport{
    85  			TLSClientConfig: &tls.Config{
    86  				InsecureSkipVerify: true,
    87  			},
    88  		},
    89  	}
    90  
    91  	skipTLSAuthorizer := docker.NewAuthorizer(clientSkipTLS, func(hostName string) (string, string, error) {
    92  		if hostName == registry.DefaultV2Registry.Host {
    93  			hostName = registry.IndexServer
    94  		}
    95  		a, err := cfg.GetAuthConfig(hostName)
    96  		if err != nil {
    97  			return "", "", err
    98  		}
    99  		if a.IdentityToken != "" {
   100  			return "", a.IdentityToken, nil
   101  		}
   102  		return a.Username, a.Password, nil
   103  	})
   104  
   105  	result := &multiRegistryResolver{
   106  		plainHTTP: docker.NewResolver(docker.ResolverOptions{
   107  			Authorizer: authorizer,
   108  			PlainHTTP:  true,
   109  		}),
   110  		secure: docker.NewResolver(docker.ResolverOptions{
   111  			Authorizer: authorizer,
   112  			PlainHTTP:  false,
   113  		}),
   114  		skipTLS: docker.NewResolver(docker.ResolverOptions{
   115  			Authorizer: skipTLSAuthorizer,
   116  			PlainHTTP:  false,
   117  			Client:     clientSkipTLS,
   118  		}),
   119  		plainHTTPRegistries: make(map[string]struct{}),
   120  		skipTLSRegistries:   make(map[string]struct{}),
   121  	}
   122  
   123  	for _, r := range plainHTTPRegistries {
   124  		pingURL := fmt.Sprintf("https://%s/v2/", r)
   125  		resp, err := clientSkipTLS.Get(pingURL)
   126  		if err == nil {
   127  			resp.Body.Close()
   128  			result.skipTLSRegistries[r] = struct{}{}
   129  		} else {
   130  			result.plainHTTPRegistries[r] = struct{}{}
   131  		}
   132  	}
   133  
   134  	return result
   135  }