github.com/crquan/docker@v1.8.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/docker/cliconfig"
     9  	"github.com/docker/docker/pkg/streamformatter"
    10  	"github.com/docker/docker/registry"
    11  )
    12  
    13  type ImagePushConfig struct {
    14  	MetaHeaders map[string][]string
    15  	AuthConfig  *cliconfig.AuthConfig
    16  	Tag         string
    17  	OutStream   io.Writer
    18  }
    19  
    20  type Pusher interface {
    21  	// Push tries to push the image configured at the creation of Pusher.
    22  	// Push returns an error if any, as well as a boolean that determines whether to retry Push on the next configured endpoint.
    23  	//
    24  	// TODO(tiborvass): have Push() take a reference to repository + tag, so that the pusher itself is repository-agnostic.
    25  	Push() (fallback bool, err error)
    26  }
    27  
    28  func (s *TagStore) NewPusher(endpoint registry.APIEndpoint, localRepo Repository, repoInfo *registry.RepositoryInfo, imagePushConfig *ImagePushConfig, sf *streamformatter.StreamFormatter) (Pusher, error) {
    29  	switch endpoint.Version {
    30  	case registry.APIVersion2:
    31  		return &v2Pusher{
    32  			TagStore:  s,
    33  			endpoint:  endpoint,
    34  			localRepo: localRepo,
    35  			repoInfo:  repoInfo,
    36  			config:    imagePushConfig,
    37  			sf:        sf,
    38  		}, nil
    39  	case registry.APIVersion1:
    40  		return &v1Pusher{
    41  			TagStore:  s,
    42  			endpoint:  endpoint,
    43  			localRepo: localRepo,
    44  			repoInfo:  repoInfo,
    45  			config:    imagePushConfig,
    46  			sf:        sf,
    47  		}, nil
    48  	}
    49  	return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL)
    50  }
    51  
    52  // FIXME: Allow to interrupt current push when new push of same image is done.
    53  func (s *TagStore) Push(localName string, imagePushConfig *ImagePushConfig) error {
    54  	var sf = streamformatter.NewJSONStreamFormatter()
    55  
    56  	// Resolve the Repository name from fqn to RepositoryInfo
    57  	repoInfo, err := s.registryService.ResolveRepository(localName)
    58  	if err != nil {
    59  		return err
    60  	}
    61  
    62  	endpoints, err := s.registryService.LookupPushEndpoints(repoInfo.CanonicalName)
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	reposLen := 1
    68  	if imagePushConfig.Tag == "" {
    69  		reposLen = len(s.Repositories[repoInfo.LocalName])
    70  	}
    71  
    72  	imagePushConfig.OutStream.Write(sf.FormatStatus("", "The push refers to a repository [%s] (len: %d)", repoInfo.CanonicalName, reposLen))
    73  
    74  	// If it fails, try to get the repository
    75  	localRepo, exists := s.Repositories[repoInfo.LocalName]
    76  	if !exists {
    77  		return fmt.Errorf("Repository does not exist: %s", repoInfo.LocalName)
    78  	}
    79  
    80  	var lastErr error
    81  	for _, endpoint := range endpoints {
    82  		logrus.Debugf("Trying to push %s to %s %s", repoInfo.CanonicalName, endpoint.URL, endpoint.Version)
    83  
    84  		pusher, err := s.NewPusher(endpoint, localRepo, repoInfo, imagePushConfig, sf)
    85  		if err != nil {
    86  			lastErr = err
    87  			continue
    88  		}
    89  		if fallback, err := pusher.Push(); err != nil {
    90  			if fallback {
    91  				lastErr = err
    92  				continue
    93  			}
    94  			logrus.Debugf("Not continuing with error: %v", err)
    95  			return err
    96  
    97  		}
    98  
    99  		s.eventsService.Log("push", repoInfo.LocalName, "")
   100  		return nil
   101  	}
   102  
   103  	if lastErr == nil {
   104  		lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.CanonicalName)
   105  	}
   106  	return lastErr
   107  }