gopkg.in/docker/docker.v20@v20.10.27/distribution/registry.go (about)

     1  package distribution // import "github.com/docker/docker/distribution"
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"net/http"
     8  	"time"
     9  
    10  	"github.com/docker/distribution"
    11  	"github.com/docker/distribution/manifest/schema2"
    12  	"github.com/docker/distribution/reference"
    13  	"github.com/docker/distribution/registry/client"
    14  	"github.com/docker/distribution/registry/client/auth"
    15  	"github.com/docker/distribution/registry/client/transport"
    16  	"github.com/docker/docker/api/types"
    17  	"github.com/docker/docker/dockerversion"
    18  	"github.com/docker/docker/registry"
    19  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    20  )
    21  
    22  // supportedMediaTypes represents acceptable media-type(-prefixes)
    23  // we use this list to prevent obscure errors when trying to pull
    24  // OCI artifacts.
    25  var supportedMediaTypes = []string{
    26  	// valid prefixes
    27  	"application/vnd.oci.image",
    28  	"application/vnd.docker",
    29  
    30  	// these types may occur on old images, and are copied from
    31  	// ImageTypes below.
    32  	"application/octet-stream",
    33  	"application/json",
    34  	"text/html",
    35  	"",
    36  }
    37  
    38  // ImageTypes represents the schema2 config types for images
    39  var ImageTypes = []string{
    40  	schema2.MediaTypeImageConfig,
    41  	ocispec.MediaTypeImageConfig,
    42  	// Handle unexpected values from https://github.com/docker/distribution/issues/1621
    43  	// (see also https://github.com/docker/docker/issues/22378,
    44  	// https://github.com/docker/docker/issues/30083)
    45  	"application/octet-stream",
    46  	"application/json",
    47  	"text/html",
    48  	// Treat defaulted values as images, newer types cannot be implied
    49  	"",
    50  }
    51  
    52  // PluginTypes represents the schema2 config types for plugins
    53  var PluginTypes = []string{
    54  	schema2.MediaTypePluginConfig,
    55  }
    56  
    57  var mediaTypeClasses map[string]string
    58  
    59  func init() {
    60  	// initialize media type classes with all know types for
    61  	// plugin
    62  	mediaTypeClasses = map[string]string{}
    63  	for _, t := range ImageTypes {
    64  		mediaTypeClasses[t] = "image"
    65  	}
    66  	for _, t := range PluginTypes {
    67  		mediaTypeClasses[t] = "plugin"
    68  	}
    69  }
    70  
    71  // NewV2Repository returns a repository (v2 only). It creates an HTTP transport
    72  // providing timeout settings and authentication support, and also verifies the
    73  // remote API version.
    74  func NewV2Repository(
    75  	ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint,
    76  	metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string,
    77  ) (repo distribution.Repository, foundVersion bool, err error) {
    78  	repoName := repoInfo.Name.Name()
    79  	// If endpoint does not support CanonicalName, use the RemoteName instead
    80  	if endpoint.TrimHostname {
    81  		repoName = reference.Path(repoInfo.Name)
    82  	}
    83  
    84  	direct := &net.Dialer{
    85  		Timeout:   30 * time.Second,
    86  		KeepAlive: 30 * time.Second,
    87  		DualStack: true,
    88  	}
    89  
    90  	// TODO(dmcgowan): Call close idle connections when complete, use keep alive
    91  	base := &http.Transport{
    92  		Proxy:               http.ProxyFromEnvironment,
    93  		DialContext:         direct.DialContext,
    94  		TLSHandshakeTimeout: 10 * time.Second,
    95  		TLSClientConfig:     endpoint.TLSConfig,
    96  		// TODO(dmcgowan): Call close idle connections when complete and use keep alive
    97  		DisableKeepAlives: true,
    98  	}
    99  
   100  	modifiers := registry.Headers(dockerversion.DockerUserAgent(ctx), metaHeaders)
   101  	authTransport := transport.NewTransport(base, modifiers...)
   102  
   103  	challengeManager, foundVersion, err := registry.PingV2Registry(endpoint.URL, authTransport)
   104  	if err != nil {
   105  		transportOK := false
   106  		if responseErr, ok := err.(registry.PingResponseError); ok {
   107  			transportOK = true
   108  			err = responseErr.Err
   109  		}
   110  		return nil, foundVersion, fallbackError{
   111  			err:         err,
   112  			confirmedV2: foundVersion,
   113  			transportOK: transportOK,
   114  		}
   115  	}
   116  
   117  	if authConfig.RegistryToken != "" {
   118  		passThruTokenHandler := &existingTokenHandler{token: authConfig.RegistryToken}
   119  		modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler))
   120  	} else {
   121  		scope := auth.RepositoryScope{
   122  			Repository: repoName,
   123  			Actions:    actions,
   124  			Class:      repoInfo.Class,
   125  		}
   126  
   127  		creds := registry.NewStaticCredentialStore(authConfig)
   128  		tokenHandlerOptions := auth.TokenHandlerOptions{
   129  			Transport:   authTransport,
   130  			Credentials: creds,
   131  			Scopes:      []auth.Scope{scope},
   132  			ClientID:    registry.AuthClientID,
   133  		}
   134  		tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions)
   135  		basicHandler := auth.NewBasicHandler(creds)
   136  		modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
   137  	}
   138  	tr := transport.NewTransport(base, modifiers...)
   139  
   140  	repoNameRef, err := reference.WithName(repoName)
   141  	if err != nil {
   142  		return nil, foundVersion, fallbackError{
   143  			err:         err,
   144  			confirmedV2: foundVersion,
   145  			transportOK: true,
   146  		}
   147  	}
   148  
   149  	repo, err = client.NewRepository(repoNameRef, endpoint.URL.String(), tr)
   150  	if err != nil {
   151  		err = fallbackError{
   152  			err:         err,
   153  			confirmedV2: foundVersion,
   154  			transportOK: true,
   155  		}
   156  	}
   157  	return
   158  }
   159  
   160  type existingTokenHandler struct {
   161  	token string
   162  }
   163  
   164  func (th *existingTokenHandler) Scheme() string {
   165  	return "bearer"
   166  }
   167  
   168  func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
   169  	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token))
   170  	return nil
   171  }