github.com/robertojrojas/docker@v1.9.1/graph/push.go (about)

     1  package graph
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  
     7  	"github.com/Sirupsen/logrus"
     8  	"github.com/docker/distribution/digest"
     9  	"github.com/docker/docker/cliconfig"
    10  	"github.com/docker/docker/pkg/streamformatter"
    11  	"github.com/docker/docker/registry"
    12  )
    13  
    14  // ImagePushConfig stores push configuration.
    15  type ImagePushConfig struct {
    16  	// MetaHeaders store HTTP headers with metadata about the image
    17  	// (DockerHeaders with prefix X-Meta- in the request).
    18  	MetaHeaders map[string][]string
    19  	// AuthConfig holds authentication credentials for authenticating with
    20  	// the registry.
    21  	AuthConfig *cliconfig.AuthConfig
    22  	// Tag is the specific variant of the image to be pushed.
    23  	// If no tag is provided, all tags will be pushed.
    24  	Tag string
    25  	// OutStream is the output writer for showing the status of the push
    26  	// operation.
    27  	OutStream io.Writer
    28  }
    29  
    30  // Pusher is an interface that abstracts pushing for different API versions.
    31  type Pusher interface {
    32  	// Push tries to push the image configured at the creation of Pusher.
    33  	// Push returns an error if any, as well as a boolean that determines whether to retry Push on the next configured endpoint.
    34  	//
    35  	// TODO(tiborvass): have Push() take a reference to repository + tag, so that the pusher itself is repository-agnostic.
    36  	Push() (fallback bool, err error)
    37  }
    38  
    39  // NewPusher creates a new Pusher interface that will push to either a v1 or v2
    40  // registry. The endpoint argument contains a Version field that determines
    41  // whether a v1 or v2 pusher will be created. The other parameters are passed
    42  // through to the underlying pusher implementation for use during the actual
    43  // push operation.
    44  func (s *TagStore) NewPusher(endpoint registry.APIEndpoint, localRepo Repository, repoInfo *registry.RepositoryInfo, imagePushConfig *ImagePushConfig, sf *streamformatter.StreamFormatter) (Pusher, error) {
    45  	switch endpoint.Version {
    46  	case registry.APIVersion2:
    47  		return &v2Pusher{
    48  			TagStore:     s,
    49  			endpoint:     endpoint,
    50  			localRepo:    localRepo,
    51  			repoInfo:     repoInfo,
    52  			config:       imagePushConfig,
    53  			sf:           sf,
    54  			layersPushed: make(map[digest.Digest]bool),
    55  		}, nil
    56  	case registry.APIVersion1:
    57  		return &v1Pusher{
    58  			TagStore:  s,
    59  			endpoint:  endpoint,
    60  			localRepo: localRepo,
    61  			repoInfo:  repoInfo,
    62  			config:    imagePushConfig,
    63  			sf:        sf,
    64  		}, nil
    65  	}
    66  	return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL)
    67  }
    68  
    69  // Push initiates a push operation on the repository named localName.
    70  func (s *TagStore) Push(localName string, imagePushConfig *ImagePushConfig) error {
    71  	// FIXME: Allow to interrupt current push when new push of same image is done.
    72  
    73  	var sf = streamformatter.NewJSONStreamFormatter()
    74  
    75  	// Resolve the Repository name from fqn to RepositoryInfo
    76  	repoInfo, err := s.registryService.ResolveRepository(localName)
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	endpoints, err := s.registryService.LookupPushEndpoints(repoInfo.CanonicalName)
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	reposLen := 1
    87  	if imagePushConfig.Tag == "" {
    88  		reposLen = len(s.Repositories[repoInfo.LocalName])
    89  	}
    90  
    91  	imagePushConfig.OutStream.Write(sf.FormatStatus("", "The push refers to a repository [%s] (len: %d)", repoInfo.CanonicalName, reposLen))
    92  
    93  	// If it fails, try to get the repository
    94  	localRepo, exists := s.Repositories[repoInfo.LocalName]
    95  	if !exists {
    96  		return fmt.Errorf("Repository does not exist: %s", repoInfo.LocalName)
    97  	}
    98  
    99  	var lastErr error
   100  	for _, endpoint := range endpoints {
   101  		logrus.Debugf("Trying to push %s to %s %s", repoInfo.CanonicalName, endpoint.URL, endpoint.Version)
   102  
   103  		pusher, err := s.NewPusher(endpoint, localRepo, repoInfo, imagePushConfig, sf)
   104  		if err != nil {
   105  			lastErr = err
   106  			continue
   107  		}
   108  		if fallback, err := pusher.Push(); err != nil {
   109  			if fallback {
   110  				lastErr = err
   111  				continue
   112  			}
   113  			logrus.Debugf("Not continuing with error: %v", err)
   114  			return err
   115  
   116  		}
   117  
   118  		s.eventsService.Log("push", repoInfo.LocalName, "")
   119  		return nil
   120  	}
   121  
   122  	if lastErr == nil {
   123  		lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.CanonicalName)
   124  	}
   125  	return lastErr
   126  }