github.com/campoy/docker@v1.8.0-rc1/graph/push_v2.go (about)

     1  package graph
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  
     8  	"github.com/Sirupsen/logrus"
     9  	"github.com/docker/distribution"
    10  	"github.com/docker/distribution/digest"
    11  	"github.com/docker/distribution/manifest"
    12  	"github.com/docker/docker/image"
    13  	"github.com/docker/docker/pkg/progressreader"
    14  	"github.com/docker/docker/pkg/streamformatter"
    15  	"github.com/docker/docker/pkg/stringid"
    16  	"github.com/docker/docker/registry"
    17  	"github.com/docker/docker/runconfig"
    18  	"github.com/docker/docker/utils"
    19  	"golang.org/x/net/context"
    20  )
    21  
    22  type v2Pusher struct {
    23  	*TagStore
    24  	endpoint  registry.APIEndpoint
    25  	localRepo Repository
    26  	repoInfo  *registry.RepositoryInfo
    27  	config    *ImagePushConfig
    28  	sf        *streamformatter.StreamFormatter
    29  	repo      distribution.Repository
    30  
    31  	// layersSeen is the set of layers known to exist on the remote side.
    32  	// This avoids redundant queries when pushing multiple tags that
    33  	// involve the same layers.
    34  	layersSeen map[string]bool
    35  }
    36  
    37  func (p *v2Pusher) Push() (fallback bool, err error) {
    38  	p.repo, err = NewV2Repository(p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig)
    39  	if err != nil {
    40  		logrus.Debugf("Error getting v2 registry: %v", err)
    41  		return true, err
    42  	}
    43  	return false, p.pushV2Repository(p.config.Tag)
    44  }
    45  
    46  func (p *v2Pusher) getImageTags(askedTag string) ([]string, error) {
    47  	logrus.Debugf("Checking %q against %#v", askedTag, p.localRepo)
    48  	if len(askedTag) > 0 {
    49  		if _, ok := p.localRepo[askedTag]; !ok || utils.DigestReference(askedTag) {
    50  			return nil, fmt.Errorf("Tag does not exist for %s", askedTag)
    51  		}
    52  		return []string{askedTag}, nil
    53  	}
    54  	var tags []string
    55  	for tag := range p.localRepo {
    56  		if !utils.DigestReference(tag) {
    57  			tags = append(tags, tag)
    58  		}
    59  	}
    60  	return tags, nil
    61  }
    62  
    63  func (p *v2Pusher) pushV2Repository(tag string) error {
    64  	localName := p.repoInfo.LocalName
    65  	if _, err := p.poolAdd("push", localName); err != nil {
    66  		return err
    67  	}
    68  	defer p.poolRemove("push", localName)
    69  
    70  	tags, err := p.getImageTags(tag)
    71  	if err != nil {
    72  		return fmt.Errorf("error getting tags for %s: %s", localName, err)
    73  	}
    74  	if len(tags) == 0 {
    75  		return fmt.Errorf("no tags to push for %s", localName)
    76  	}
    77  
    78  	for _, tag := range tags {
    79  		if err := p.pushV2Tag(tag); err != nil {
    80  			return err
    81  		}
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  func (p *v2Pusher) pushV2Tag(tag string) error {
    88  	logrus.Debugf("Pushing repository: %s:%s", p.repo.Name(), tag)
    89  
    90  	layerId, exists := p.localRepo[tag]
    91  	if !exists {
    92  		return fmt.Errorf("tag does not exist: %s", tag)
    93  	}
    94  
    95  	layer, err := p.graph.Get(layerId)
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	m := &manifest.Manifest{
   101  		Versioned: manifest.Versioned{
   102  			SchemaVersion: 1,
   103  		},
   104  		Name:         p.repo.Name(),
   105  		Tag:          tag,
   106  		Architecture: layer.Architecture,
   107  		FSLayers:     []manifest.FSLayer{},
   108  		History:      []manifest.History{},
   109  	}
   110  
   111  	var metadata runconfig.Config
   112  	if layer != nil && layer.Config != nil {
   113  		metadata = *layer.Config
   114  	}
   115  
   116  	out := p.config.OutStream
   117  
   118  	for ; layer != nil; layer, err = p.graph.GetParent(layer) {
   119  		if err != nil {
   120  			return err
   121  		}
   122  
   123  		if p.layersSeen[layer.ID] {
   124  			break
   125  		}
   126  
   127  		logrus.Debugf("Pushing layer: %s", layer.ID)
   128  
   129  		if layer.Config != nil && metadata.Image != layer.ID {
   130  			if err := runconfig.Merge(&metadata, layer.Config); err != nil {
   131  				return err
   132  			}
   133  		}
   134  
   135  		jsonData, err := p.graph.RawJSON(layer.ID)
   136  		if err != nil {
   137  			return fmt.Errorf("cannot retrieve the path for %s: %s", layer.ID, err)
   138  		}
   139  
   140  		var exists bool
   141  		dgst, err := p.graph.GetDigest(layer.ID)
   142  		switch err {
   143  		case nil:
   144  			_, err := p.repo.Blobs(nil).Stat(nil, dgst)
   145  			switch err {
   146  			case nil:
   147  				exists = true
   148  				out.Write(p.sf.FormatProgress(stringid.TruncateID(layer.ID), "Image already exists", nil))
   149  			case distribution.ErrBlobUnknown:
   150  				// nop
   151  			default:
   152  				out.Write(p.sf.FormatProgress(stringid.TruncateID(layer.ID), "Image push failed", nil))
   153  				return err
   154  			}
   155  		case ErrDigestNotSet:
   156  			// nop
   157  		case digest.ErrDigestInvalidFormat, digest.ErrDigestUnsupported:
   158  			return fmt.Errorf("error getting image checksum: %v", err)
   159  		}
   160  
   161  		// if digest was empty or not saved, or if blob does not exist on the remote repository,
   162  		// then fetch it.
   163  		if !exists {
   164  			if pushDigest, err := p.pushV2Image(p.repo.Blobs(nil), layer); err != nil {
   165  				return err
   166  			} else if pushDigest != dgst {
   167  				// Cache new checksum
   168  				if err := p.graph.SetDigest(layer.ID, pushDigest); err != nil {
   169  					return err
   170  				}
   171  				dgst = pushDigest
   172  			}
   173  		}
   174  
   175  		m.FSLayers = append(m.FSLayers, manifest.FSLayer{BlobSum: dgst})
   176  		m.History = append(m.History, manifest.History{V1Compatibility: string(jsonData)})
   177  
   178  		p.layersSeen[layer.ID] = true
   179  	}
   180  
   181  	logrus.Infof("Signed manifest for %s:%s using daemon's key: %s", p.repo.Name(), tag, p.trustKey.KeyID())
   182  	signed, err := manifest.Sign(m, p.trustKey)
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	manifestDigest, manifestSize, err := digestFromManifest(signed, p.repo.Name())
   188  	if err != nil {
   189  		return err
   190  	}
   191  	if manifestDigest != "" {
   192  		out.Write(p.sf.FormatStatus("", "%s: digest: %s size: %d", tag, manifestDigest, manifestSize))
   193  	}
   194  
   195  	manSvc, err := p.repo.Manifests(context.Background())
   196  	if err != nil {
   197  		return err
   198  	}
   199  	return manSvc.Put(signed)
   200  }
   201  
   202  func (p *v2Pusher) pushV2Image(bs distribution.BlobService, img *image.Image) (digest.Digest, error) {
   203  	out := p.config.OutStream
   204  
   205  	out.Write(p.sf.FormatProgress(stringid.TruncateID(img.ID), "Buffering to Disk", nil))
   206  
   207  	image, err := p.graph.Get(img.ID)
   208  	if err != nil {
   209  		return "", err
   210  	}
   211  	arch, err := p.graph.TarLayer(image)
   212  	if err != nil {
   213  		return "", err
   214  	}
   215  
   216  	tf, err := p.graph.newTempFile()
   217  	if err != nil {
   218  		return "", err
   219  	}
   220  	defer func() {
   221  		tf.Close()
   222  		os.Remove(tf.Name())
   223  	}()
   224  
   225  	size, dgst, err := bufferToFile(tf, arch)
   226  	if err != nil {
   227  		return "", err
   228  	}
   229  
   230  	// Send the layer
   231  	logrus.Debugf("rendered layer for %s of [%d] size", img.ID, size)
   232  	layerUpload, err := bs.Create(nil)
   233  	if err != nil {
   234  		return "", err
   235  	}
   236  	defer layerUpload.Close()
   237  
   238  	reader := progressreader.New(progressreader.Config{
   239  		In:        ioutil.NopCloser(tf),
   240  		Out:       out,
   241  		Formatter: p.sf,
   242  		Size:      int(size),
   243  		NewLines:  false,
   244  		ID:        stringid.TruncateID(img.ID),
   245  		Action:    "Pushing",
   246  	})
   247  	n, err := layerUpload.ReadFrom(reader)
   248  	if err != nil {
   249  		return "", err
   250  	}
   251  	if n != size {
   252  		return "", fmt.Errorf("short upload: only wrote %d of %d", n, size)
   253  	}
   254  
   255  	desc := distribution.Descriptor{Digest: dgst}
   256  	if _, err := layerUpload.Commit(nil, desc); err != nil {
   257  		return "", err
   258  	}
   259  
   260  	out.Write(p.sf.FormatProgress(stringid.TruncateID(img.ID), "Image successfully pushed", nil))
   261  
   262  	return dgst, nil
   263  }