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 }