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