github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/plugin/distribution/push.go (about)

     1  // +build experimental
     2  
     3  package distribution
     4  
     5  import (
     6  	"crypto/sha256"
     7  	"io"
     8  	"net/http"
     9  
    10  	"github.com/Sirupsen/logrus"
    11  	"github.com/docker/distribution"
    12  	"github.com/docker/distribution/digest"
    13  	"github.com/docker/distribution/manifest/schema2"
    14  	"github.com/docker/docker/api/types"
    15  	dockerdist "github.com/docker/docker/distribution"
    16  	"github.com/docker/docker/reference"
    17  	"github.com/docker/docker/registry"
    18  	"golang.org/x/net/context"
    19  )
    20  
    21  // Push pushes a plugin to a registry.
    22  func Push(name string, rs registry.Service, metaHeader http.Header, authConfig *types.AuthConfig, config io.ReadCloser, layers io.ReadCloser) (digest.Digest, error) {
    23  	ref, err := reference.ParseNamed(name)
    24  	if err != nil {
    25  		return "", err
    26  	}
    27  
    28  	repoInfo, err := rs.ResolveRepository(ref)
    29  	if err != nil {
    30  		return "", err
    31  	}
    32  
    33  	if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil {
    34  		return "", err
    35  	}
    36  
    37  	endpoints, err := rs.LookupPushEndpoints(repoInfo.Hostname())
    38  	if err != nil {
    39  		return "", err
    40  	}
    41  
    42  	var confirmedV2 bool
    43  	var repository distribution.Repository
    44  	for _, endpoint := range endpoints {
    45  		if confirmedV2 && endpoint.Version == registry.APIVersion1 {
    46  			logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL)
    47  			continue
    48  		}
    49  		repository, confirmedV2, err = dockerdist.NewV2Repository(context.Background(), repoInfo, endpoint, metaHeader, authConfig, "push", "pull")
    50  		if err != nil {
    51  			return "", err
    52  		}
    53  		if !confirmedV2 {
    54  			return "", ErrUnsupportedRegistry
    55  		}
    56  		logrus.Debugf("Trying to push %s to %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
    57  		// This means that we found an endpoint. and we are ready to push
    58  		break
    59  	}
    60  
    61  	// Returns a reference to the repository's blob service.
    62  	blobs := repository.Blobs(context.Background())
    63  
    64  	// Descriptor = {mediaType, size, digest}
    65  	var descs []distribution.Descriptor
    66  
    67  	for i, f := range []io.ReadCloser{config, layers} {
    68  		bw, err := blobs.Create(context.Background())
    69  		if err != nil {
    70  			logrus.Debugf("Error in blobs.Create: %v", err)
    71  			return "", err
    72  		}
    73  		h := sha256.New()
    74  		r := io.TeeReader(f, h)
    75  		_, err = io.Copy(bw, r)
    76  		if err != nil {
    77  			f.Close()
    78  			logrus.Debugf("Error in io.Copy: %v", err)
    79  			return "", err
    80  		}
    81  		f.Close()
    82  		mt := schema2.MediaTypeLayer
    83  		if i == 0 {
    84  			mt = schema2.MediaTypePluginConfig
    85  		}
    86  		// Commit completes the write process to the BlobService.
    87  		// The descriptor arg to Commit is called the "provisional" descriptor and
    88  		// used for validation.
    89  		// The returned descriptor should be the one used. Its called the "Canonical"
    90  		// descriptor.
    91  		desc, err := bw.Commit(context.Background(), distribution.Descriptor{
    92  			MediaType: mt,
    93  			// XXX: What about the Size?
    94  			Digest: digest.NewDigest("sha256", h),
    95  		})
    96  		if err != nil {
    97  			logrus.Debugf("Error in bw.Commit: %v", err)
    98  			return "", err
    99  		}
   100  		// The canonical descriptor is set the mediatype again, just in case.
   101  		// Don't touch the digest or the size here.
   102  		desc.MediaType = mt
   103  		logrus.Debugf("pushed blob: %s %s", desc.MediaType, desc.Digest)
   104  		descs = append(descs, desc)
   105  	}
   106  
   107  	// XXX: schema2.Versioned needs a MediaType as well.
   108  	// "application/vnd.docker.distribution.manifest.v2+json"
   109  	m, err := schema2.FromStruct(schema2.Manifest{Versioned: schema2.SchemaVersion, Config: descs[0], Layers: descs[1:]})
   110  	if err != nil {
   111  		logrus.Debugf("error in schema2.FromStruct: %v", err)
   112  		return "", err
   113  	}
   114  
   115  	msv, err := repository.Manifests(context.Background())
   116  	if err != nil {
   117  		logrus.Debugf("error in repository.Manifests: %v", err)
   118  		return "", err
   119  	}
   120  
   121  	_, pl, err := m.Payload()
   122  	if err != nil {
   123  		logrus.Debugf("error in m.Payload: %v", err)
   124  		return "", err
   125  	}
   126  
   127  	logrus.Debugf("Pushed manifest: %s", pl)
   128  
   129  	tag := DefaultTag
   130  	if tagged, ok := ref.(reference.NamedTagged); ok {
   131  		tag = tagged.Tag()
   132  	}
   133  
   134  	return msv.Put(context.Background(), m, distribution.WithTag(tag))
   135  }