123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- package graph
- import (
- "fmt"
- "io"
- "github.com/Sirupsen/logrus"
- "github.com/docker/docker/cliconfig"
- "github.com/docker/docker/pkg/streamformatter"
- "github.com/docker/docker/registry"
- "github.com/docker/docker/utils"
- )
- // ImagePullConfig stores pull configuration.
- type ImagePullConfig struct {
- // MetaHeaders store meta data about the image (DockerHeaders with prefix X-Meta- in the request).
- MetaHeaders map[string][]string
- // AuthConfig holds authentication information for authorizing with the registry.
- AuthConfig *cliconfig.AuthConfig
- // OutStream is the output writer for showing the status of the pull operation.
- OutStream io.Writer
- }
- // Puller is an interface to define Pull behavior.
- 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)
- }
- // NewPuller returns a new instance of an implementation conforming to Puller interface.
- 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)
- }
- // Pull downloads a image with specified name and tag from the repo.
- func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConfig) error {
- var sf = streamformatter.NewJSONStreamFormatter()
- // Resolve the Repository name from fqn to RepositoryInfo
- repoInfo, err := s.registryService.ResolveRepository(image)
- if err != nil {
- return err
- }
- // makes sure name is not empty or `scratch`
- if err := validateRepoName(repoInfo.LocalName); err != nil {
- return err
- }
- endpoints, err := s.registryService.LookupEndpoints(repoInfo.CanonicalName)
- if err != nil {
- return err
- }
- logName := repoInfo.LocalName
- if tag != "" {
- logName = utils.ImageReference(logName, tag)
- }
- 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
- )
- for _, endpoint := range endpoints {
- logrus.Debugf("Trying to pull %s from %s %s", repoInfo.LocalName, endpoint.URL, endpoint.Version)
- if !endpoint.Mirror && (endpoint.Official || endpoint.Version == registry.APIVersion2) {
- if repoInfo.Official {
- s.trustService.UpdateBase()
- }
- }
- puller, err := NewPuller(s, endpoint, repoInfo, imagePullConfig, sf)
- if err != nil {
- lastErr = err
- continue
- }
- 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
- }
- logrus.Debugf("Not continuing with error: %v", err)
- return err
- }
- s.eventsService.Log("pull", logName, "")
- return nil
- }
- if lastErr == nil {
- lastErr = fmt.Errorf("no endpoints found for %s", image)
- }
- return lastErr
- }
- // writeStatus shows status of the pull command.
- func writeStatus(requestedTag string, out io.Writer, sf *streamformatter.StreamFormatter, layersDownloaded bool) {
- if layersDownloaded {
- 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))
- }
- }
|