github.com/simonferquel/app@v0.6.1-0.20181012141724-68b7cccf26ac/pkg/resto/registry.go (about)

     1  package resto
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"net"
     7  	"net/http"
     8  	"net/url"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/docker/distribution"
    13  	"github.com/docker/distribution/registry/client/auth"
    14  	"github.com/docker/distribution/registry/client/transport"
    15  	"github.com/docker/docker/api/types"
    16  	dd "github.com/docker/docker/distribution"
    17  	"github.com/docker/docker/registry"
    18  	digest "github.com/opencontainers/go-digest"
    19  )
    20  
    21  type myreference string
    22  
    23  func (m myreference) String() string {
    24  	return string(m)
    25  }
    26  
    27  func (m myreference) Name() string {
    28  	return string(m)
    29  }
    30  
    31  // MediaTypeConfig is the media type used for configuration files.
    32  const MediaTypeConfig = "application/vndr.docker.config"
    33  
    34  // ConfigManifest is a Manifest type holding arbitrary data.
    35  type ConfigManifest struct {
    36  	mediaType string
    37  	payload   []byte
    38  }
    39  
    40  // References returns the objects this Manifest refers to.
    41  func (c *ConfigManifest) References() []distribution.Descriptor {
    42  	return nil
    43  }
    44  
    45  // Payload returns the mediatype and payload of this manifest.
    46  func (c *ConfigManifest) Payload() (string, []byte, error) {
    47  	return c.mediaType, c.payload, nil
    48  }
    49  
    50  // NewConfigManifest creates and returns an new ConfigManifest.
    51  func NewConfigManifest(mediaType string, payload []byte) *ConfigManifest {
    52  	return &ConfigManifest{mediaType, payload}
    53  }
    54  
    55  func init() {
    56  	distribution.RegisterManifestSchema(MediaTypeConfig, func(b []byte) (distribution.Manifest, distribution.Descriptor, error) {
    57  		return &ConfigManifest{
    58  				mediaType: MediaTypeConfig,
    59  				payload:   b,
    60  			},
    61  			distribution.Descriptor{
    62  				MediaType: MediaTypeConfig,
    63  				Size:      int64(len(b)),
    64  				Digest:    digest.SHA256.FromBytes(b),
    65  			}, nil
    66  	})
    67  }
    68  
    69  // NewRepository instantiates a distribution.Repository pointing to the given target, with credentials
    70  func NewRepository(ctx context.Context, endpoint string, repository string, opts RegistryOptions) (distribution.Repository, error) {
    71  	named := myreference(repository)
    72  	authConfig := &types.AuthConfig{
    73  		Username: opts.Username,
    74  		Password: opts.Password,
    75  	}
    76  	url, err := url.Parse(endpoint)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	apiendpoint := registry.APIEndpoint{
    81  		Mirror:    false,
    82  		URL:       url,
    83  		Version:   2,
    84  		TLSConfig: &tls.Config{InsecureSkipVerify: opts.Insecure},
    85  	}
    86  	repoInfo, err := registry.ParseRepositoryInfo(named)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	repo, _, err := dd.NewV2Repository(ctx, repoInfo, apiendpoint, nil, authConfig, "push", "pull")
    91  	if err == nil {
    92  		return repo, nil
    93  	}
    94  	if !strings.Contains(err.Error(), "HTTP response to HTTPS client") {
    95  		return nil, err
    96  	}
    97  	if !opts.CleartextCredentials {
    98  		// Don't use credentials over insecure connection unless instnucted to
    99  		authConfig.Username = ""
   100  		authConfig.Password = ""
   101  	}
   102  	endpointHTTP := strings.Replace(endpoint, "https://", "http://", 1)
   103  	urlHTTP, err := url.Parse(endpointHTTP)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	apiendpoint.URL = urlHTTP
   108  	repo, _, err = dd.NewV2Repository(ctx, repoInfo, apiendpoint, nil, authConfig, "push", "pull")
   109  	return repo, err
   110  }
   111  
   112  // NewTransportCatalog returns a transport suitable for a catalog operation
   113  func NewTransportCatalog(endpoint string, opts RegistryOptions) (http.RoundTripper, error) {
   114  	// taken from docker/distribution/registry.go
   115  	direct := &net.Dialer{
   116  		Timeout:   30 * time.Second,
   117  		KeepAlive: 30 * time.Second,
   118  		DualStack: true,
   119  	}
   120  	base := &http.Transport{
   121  		Proxy:               http.ProxyFromEnvironment,
   122  		Dial:                direct.Dial,
   123  		TLSHandshakeTimeout: 10 * time.Second,
   124  		TLSClientConfig:     &tls.Config{InsecureSkipVerify: opts.Insecure},
   125  		DisableKeepAlives:   true,
   126  	}
   127  
   128  	authTransport := transport.NewTransport(base)
   129  	endpointURL, err := url.Parse(endpoint)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	challengeManager, _, err := registry.PingV2Registry(endpointURL, authTransport)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	scope := auth.RegistryScope{
   138  		Name:    "catalog",
   139  		Actions: []string{"*"},
   140  	}
   141  	authConfig := &types.AuthConfig{
   142  		Username: opts.Username,
   143  		Password: opts.Password,
   144  	}
   145  	creds := registry.NewStaticCredentialStore(authConfig)
   146  	tokenHandlerOptions := auth.TokenHandlerOptions{
   147  		Transport:   authTransport,
   148  		Credentials: creds,
   149  		Scopes:      []auth.Scope{scope},
   150  		ClientID:    registry.AuthClientID,
   151  	}
   152  	tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions)
   153  	basicHandler := auth.NewBasicHandler(creds)
   154  	tr := transport.NewTransport(base, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
   155  	return tr, nil
   156  }