2014-08-08 06:58:58 +00:00
|
|
|
package graph
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
|
2015-03-26 22:22:04 +00:00
|
|
|
"github.com/Sirupsen/logrus"
|
2015-04-22 12:06:58 +00:00
|
|
|
"github.com/docker/docker/cliconfig"
|
2015-03-18 02:18:41 +00:00
|
|
|
"github.com/docker/docker/pkg/streamformatter"
|
2014-08-08 06:58:58 +00:00
|
|
|
"github.com/docker/docker/registry"
|
|
|
|
)
|
|
|
|
|
2015-07-21 16:21:45 +00:00
|
|
|
// ImagePushConfig stores push configuration.
|
2015-04-21 03:16:25 +00:00
|
|
|
type ImagePushConfig struct {
|
2015-07-29 23:45:47 +00:00
|
|
|
// MetaHeaders store HTTP headers with metadata about the image
|
|
|
|
// (DockerHeaders with prefix X-Meta- in the request).
|
2015-04-21 03:16:25 +00:00
|
|
|
MetaHeaders map[string][]string
|
2015-07-29 23:45:47 +00:00
|
|
|
// AuthConfig holds authentication credentials for authenticating with
|
|
|
|
// the registry.
|
2015-07-21 16:21:45 +00:00
|
|
|
AuthConfig *cliconfig.AuthConfig
|
2015-07-29 23:45:47 +00:00
|
|
|
// Tag is the specific variant of the image to be pushed.
|
|
|
|
// If no tag is provided, all tags will be pushed.
|
2015-07-21 16:21:45 +00:00
|
|
|
Tag string
|
2015-07-29 23:45:47 +00:00
|
|
|
// OutStream is the output writer for showing the status of the push
|
|
|
|
// operation.
|
2015-07-21 16:21:45 +00:00
|
|
|
OutStream io.Writer
|
2015-04-21 03:16:25 +00:00
|
|
|
}
|
|
|
|
|
2015-07-29 23:45:47 +00:00
|
|
|
// Pusher is an interface that abstracts pushing for different API versions.
|
2015-02-12 18:23:22 +00:00
|
|
|
type Pusher interface {
|
|
|
|
// Push tries to push the image configured at the creation of Pusher.
|
|
|
|
// Push returns an error if any, as well as a boolean that determines whether to retry Push on the next configured endpoint.
|
|
|
|
//
|
|
|
|
// TODO(tiborvass): have Push() take a reference to repository + tag, so that the pusher itself is repository-agnostic.
|
|
|
|
Push() (fallback bool, err error)
|
2014-08-08 06:58:58 +00:00
|
|
|
}
|
|
|
|
|
2015-07-29 23:45:47 +00:00
|
|
|
// NewPusher creates a new Pusher interface that will push to either a v1 or v2
|
|
|
|
// registry. The endpoint argument contains a Version field that determines
|
|
|
|
// whether a v1 or v2 pusher will be created. The other parameters are passed
|
|
|
|
// through to the underlying pusher implementation for use during the actual
|
|
|
|
// push operation.
|
2015-02-12 18:23:22 +00:00
|
|
|
func (s *TagStore) NewPusher(endpoint registry.APIEndpoint, localRepo Repository, repoInfo *registry.RepositoryInfo, imagePushConfig *ImagePushConfig, sf *streamformatter.StreamFormatter) (Pusher, error) {
|
|
|
|
switch endpoint.Version {
|
|
|
|
case registry.APIVersion2:
|
|
|
|
return &v2Pusher{
|
2015-07-23 00:29:36 +00:00
|
|
|
TagStore: s,
|
|
|
|
endpoint: endpoint,
|
|
|
|
localRepo: localRepo,
|
|
|
|
repoInfo: repoInfo,
|
|
|
|
config: imagePushConfig,
|
|
|
|
sf: sf,
|
|
|
|
layersSeen: make(map[string]bool),
|
2015-02-12 18:23:22 +00:00
|
|
|
}, nil
|
|
|
|
case registry.APIVersion1:
|
|
|
|
return &v1Pusher{
|
|
|
|
TagStore: s,
|
|
|
|
endpoint: endpoint,
|
|
|
|
localRepo: localRepo,
|
|
|
|
repoInfo: repoInfo,
|
|
|
|
config: imagePushConfig,
|
|
|
|
sf: sf,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL)
|
2015-01-28 02:10:28 +00:00
|
|
|
}
|
|
|
|
|
2015-07-29 23:45:47 +00:00
|
|
|
// Push initiates a push operation on the repository named localName.
|
2015-04-21 03:16:25 +00:00
|
|
|
func (s *TagStore) Push(localName string, imagePushConfig *ImagePushConfig) error {
|
2015-07-29 23:45:47 +00:00
|
|
|
// FIXME: Allow to interrupt current push when new push of same image is done.
|
|
|
|
|
2015-02-12 18:23:22 +00:00
|
|
|
var sf = streamformatter.NewJSONStreamFormatter()
|
2014-08-08 06:58:58 +00:00
|
|
|
|
2014-10-07 01:54:52 +00:00
|
|
|
// Resolve the Repository name from fqn to RepositoryInfo
|
2015-03-31 23:21:37 +00:00
|
|
|
repoInfo, err := s.registryService.ResolveRepository(localName)
|
2014-10-07 01:54:52 +00:00
|
|
|
if err != nil {
|
2015-03-25 07:44:12 +00:00
|
|
|
return err
|
2014-10-07 01:54:52 +00:00
|
|
|
}
|
|
|
|
|
2015-08-06 21:41:59 +00:00
|
|
|
endpoints, err := s.registryService.LookupPushEndpoints(repoInfo.CanonicalName)
|
2015-02-10 23:46:42 +00:00
|
|
|
if err != nil {
|
2015-03-25 07:44:12 +00:00
|
|
|
return err
|
2014-08-08 06:58:58 +00:00
|
|
|
}
|
|
|
|
|
2015-02-10 23:46:42 +00:00
|
|
|
reposLen := 1
|
2015-04-21 03:16:25 +00:00
|
|
|
if imagePushConfig.Tag == "" {
|
2015-02-10 23:46:42 +00:00
|
|
|
reposLen = len(s.Repositories[repoInfo.LocalName])
|
2015-01-13 23:48:49 +00:00
|
|
|
}
|
2015-04-21 03:16:25 +00:00
|
|
|
|
|
|
|
imagePushConfig.OutStream.Write(sf.FormatStatus("", "The push refers to a repository [%s] (len: %d)", repoInfo.CanonicalName, reposLen))
|
|
|
|
|
2015-02-10 23:46:42 +00:00
|
|
|
// If it fails, try to get the repository
|
|
|
|
localRepo, exists := s.Repositories[repoInfo.LocalName]
|
|
|
|
if !exists {
|
2015-03-25 07:44:12 +00:00
|
|
|
return fmt.Errorf("Repository does not exist: %s", repoInfo.LocalName)
|
2015-02-10 23:46:42 +00:00
|
|
|
}
|
2015-03-04 20:05:17 +00:00
|
|
|
|
2015-02-12 18:23:22 +00:00
|
|
|
var lastErr error
|
|
|
|
for _, endpoint := range endpoints {
|
|
|
|
logrus.Debugf("Trying to push %s to %s %s", repoInfo.CanonicalName, endpoint.URL, endpoint.Version)
|
|
|
|
|
|
|
|
pusher, err := s.NewPusher(endpoint, localRepo, repoInfo, imagePushConfig, sf)
|
|
|
|
if err != nil {
|
|
|
|
lastErr = err
|
|
|
|
continue
|
2015-03-04 20:05:17 +00:00
|
|
|
}
|
2015-02-12 18:23:22 +00:00
|
|
|
if fallback, err := pusher.Push(); err != nil {
|
|
|
|
if fallback {
|
|
|
|
lastErr = err
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
logrus.Debugf("Not continuing with error: %v", err)
|
|
|
|
return err
|
2015-03-04 20:05:17 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-02-12 18:23:22 +00:00
|
|
|
s.eventsService.Log("push", repoInfo.LocalName, "")
|
|
|
|
return nil
|
2014-08-08 06:58:58 +00:00
|
|
|
}
|
2015-02-10 23:46:42 +00:00
|
|
|
|
2015-02-12 18:23:22 +00:00
|
|
|
if lastErr == nil {
|
|
|
|
lastErr = fmt.Errorf("no endpoints found for %s", repoInfo.CanonicalName)
|
|
|
|
}
|
|
|
|
return lastErr
|
2014-08-08 06:58:58 +00:00
|
|
|
}
|