github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/plugin/distribution/push.go (about)

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