6856a6b1 |
package graph
import (
"fmt"
"io"
|
6f4d8470 |
"github.com/Sirupsen/logrus" |
bb9da6ba |
"github.com/docker/docker/cliconfig" |
0cd6c05d |
"github.com/docker/docker/pkg/streamformatter" |
6856a6b1 |
"github.com/docker/docker/registry"
"github.com/docker/docker/utils"
)
|
6e38a53f |
type ImagePullConfig struct {
MetaHeaders map[string][]string |
bb9da6ba |
AuthConfig *cliconfig.AuthConfig |
6e38a53f |
OutStream io.Writer
}
|
19515a7a |
type Puller interface {
// Pull tries to pull the image referenced by `tag`
// Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint.
//
// TODO(tiborvass): have Pull() take a reference to repository + tag, so that the puller itself is repository-agnostic.
Pull(tag string) (fallback bool, err error)
}
func NewPuller(s *TagStore, endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, imagePullConfig *ImagePullConfig, sf *streamformatter.StreamFormatter) (Puller, error) {
switch endpoint.Version {
case registry.APIVersion2:
return &v2Puller{
TagStore: s,
endpoint: endpoint,
config: imagePullConfig,
sf: sf,
repoInfo: repoInfo,
}, nil
case registry.APIVersion1:
return &v1Puller{
TagStore: s,
endpoint: endpoint,
config: imagePullConfig,
sf: sf,
repoInfo: repoInfo,
}, nil
}
return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL)
}
|
a2f74aa4 |
func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConfig) error { |
19515a7a |
var sf = streamformatter.NewJSONStreamFormatter() |
1c62e0ae |
|
568f86eb |
// Resolve the Repository name from fqn to RepositoryInfo |
6e38a53f |
repoInfo, err := s.registryService.ResolveRepository(image) |
568f86eb |
if err != nil { |
c79b9bab |
return err |
568f86eb |
}
|
19515a7a |
// makes sure name is not empty or `scratch` |
18f46883 |
if err := validateRepoName(repoInfo.LocalName); err != nil {
return err
}
|
29ea36a8 |
endpoints, err := s.registryService.LookupPullEndpoints(repoInfo.CanonicalName) |
6856a6b1 |
if err != nil { |
c79b9bab |
return err |
6856a6b1 |
}
|
13deed38 |
logName := repoInfo.LocalName
if tag != "" {
logName = utils.ImageReference(logName, tag)
}
|
19515a7a |
var (
lastErr error
// discardNoSupportErrors is used to track whether an endpoint encountered an error of type registry.ErrNoSupport
// By default it is false, which means that if a ErrNoSupport error is encountered, it will be saved in lastErr.
// As soon as another kind of error is encountered, discardNoSupportErrors is set to true, avoiding the saving of
// any subsequent ErrNoSupport errors in lastErr.
// It's needed for pull-by-digest on v1 endpoints: if there are only v1 endpoints configured, the error should be
// returned and displayed, but if there was a v2 endpoint which supports pull-by-digest, then the last relevant
// error is the ones from v2 endpoints not v1.
discardNoSupportErrors bool |
73823e5e |
) |
19515a7a |
for _, endpoint := range endpoints {
logrus.Debugf("Trying to pull %s from %s %s", repoInfo.LocalName, endpoint.URL, endpoint.Version) |
3be4551a |
|
19515a7a |
if !endpoint.Mirror && (endpoint.Official || endpoint.Version == registry.APIVersion2) {
if repoInfo.Official {
s.trustService.UpdateBase()
} |
3be4551a |
} |
213e3d11 |
|
19515a7a |
puller, err := NewPuller(s, endpoint, repoInfo, imagePullConfig, sf) |
13deed38 |
if err != nil { |
19515a7a |
lastErr = err |
13deed38 |
continue
} |
19515a7a |
if fallback, err := puller.Pull(tag); err != nil {
if fallback {
if _, ok := err.(registry.ErrNoSupport); !ok {
// Because we found an error that's not ErrNoSupport, discard all subsequent ErrNoSupport errors.
discardNoSupportErrors = true
// save the current error
lastErr = err
} else if !discardNoSupportErrors {
// Save the ErrNoSupport error, because it's either the first error or all encountered errors
// were also ErrNoSupport errors.
lastErr = err
}
continue |
6856a6b1 |
} |
19515a7a |
logrus.Debugf("Not continuing with error: %v", err) |
6856a6b1 |
return err
}
|
19515a7a |
s.eventsService.Log("pull", logName, "")
return nil
} |
6856a6b1 |
|
19515a7a |
if lastErr == nil {
lastErr = fmt.Errorf("no endpoints found for %s", image) |
ecff6303 |
} |
19515a7a |
return lastErr |
ecff6303 |
} |
6856a6b1 |
|
ae907e7a |
func WriteStatus(requestedTag string, out io.Writer, sf *streamformatter.StreamFormatter, layersDownloaded bool) {
if layersDownloaded { |
ecff6303 |
out.Write(sf.FormatStatus("", "Status: Downloaded newer image for %s", requestedTag))
} else {
out.Write(sf.FormatStatus("", "Status: Image is up to date for %s", requestedTag)) |
6856a6b1 |
}
} |