github.com/crquan/docker@v1.8.1/graph/pull.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  	"github.com/docker/docker/utils"
    12  )
    13  
    14  type ImagePullConfig struct {
    15  	MetaHeaders map[string][]string
    16  	AuthConfig  *cliconfig.AuthConfig
    17  	OutStream   io.Writer
    18  }
    19  
    20  type Puller interface {
    21  	// Pull tries to pull the image referenced by `tag`
    22  	// Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint.
    23  	//
    24  	// TODO(tiborvass): have Pull() take a reference to repository + tag, so that the puller itself is repository-agnostic.
    25  	Pull(tag string) (fallback bool, err error)
    26  }
    27  
    28  func NewPuller(s *TagStore, endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, imagePullConfig *ImagePullConfig, sf *streamformatter.StreamFormatter) (Puller, error) {
    29  	switch endpoint.Version {
    30  	case registry.APIVersion2:
    31  		return &v2Puller{
    32  			TagStore: s,
    33  			endpoint: endpoint,
    34  			config:   imagePullConfig,
    35  			sf:       sf,
    36  			repoInfo: repoInfo,
    37  		}, nil
    38  	case registry.APIVersion1:
    39  		return &v1Puller{
    40  			TagStore: s,
    41  			endpoint: endpoint,
    42  			config:   imagePullConfig,
    43  			sf:       sf,
    44  			repoInfo: repoInfo,
    45  		}, nil
    46  	}
    47  	return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL)
    48  }
    49  
    50  func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConfig) error {
    51  	var sf = streamformatter.NewJSONStreamFormatter()
    52  
    53  	// Resolve the Repository name from fqn to RepositoryInfo
    54  	repoInfo, err := s.registryService.ResolveRepository(image)
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	// makes sure name is not empty or `scratch`
    60  	if err := validateRepoName(repoInfo.LocalName); err != nil {
    61  		return err
    62  	}
    63  
    64  	endpoints, err := s.registryService.LookupPullEndpoints(repoInfo.CanonicalName)
    65  	if err != nil {
    66  		return err
    67  	}
    68  
    69  	logName := repoInfo.LocalName
    70  	if tag != "" {
    71  		logName = utils.ImageReference(logName, tag)
    72  	}
    73  
    74  	var (
    75  		lastErr error
    76  
    77  		// discardNoSupportErrors is used to track whether an endpoint encountered an error of type registry.ErrNoSupport
    78  		// By default it is false, which means that if a ErrNoSupport error is encountered, it will be saved in lastErr.
    79  		// As soon as another kind of error is encountered, discardNoSupportErrors is set to true, avoiding the saving of
    80  		// any subsequent ErrNoSupport errors in lastErr.
    81  		// It's needed for pull-by-digest on v1 endpoints: if there are only v1 endpoints configured, the error should be
    82  		// returned and displayed, but if there was a v2 endpoint which supports pull-by-digest, then the last relevant
    83  		// error is the ones from v2 endpoints not v1.
    84  		discardNoSupportErrors bool
    85  	)
    86  	for _, endpoint := range endpoints {
    87  		logrus.Debugf("Trying to pull %s from %s %s", repoInfo.LocalName, endpoint.URL, endpoint.Version)
    88  
    89  		if !endpoint.Mirror && (endpoint.Official || endpoint.Version == registry.APIVersion2) {
    90  			if repoInfo.Official {
    91  				s.trustService.UpdateBase()
    92  			}
    93  		}
    94  
    95  		puller, err := NewPuller(s, endpoint, repoInfo, imagePullConfig, sf)
    96  		if err != nil {
    97  			lastErr = err
    98  			continue
    99  		}
   100  		if fallback, err := puller.Pull(tag); err != nil {
   101  			if fallback {
   102  				if _, ok := err.(registry.ErrNoSupport); !ok {
   103  					// Because we found an error that's not ErrNoSupport, discard all subsequent ErrNoSupport errors.
   104  					discardNoSupportErrors = true
   105  					// save the current error
   106  					lastErr = err
   107  				} else if !discardNoSupportErrors {
   108  					// Save the ErrNoSupport error, because it's either the first error or all encountered errors
   109  					// were also ErrNoSupport errors.
   110  					lastErr = err
   111  				}
   112  				continue
   113  			}
   114  			logrus.Debugf("Not continuing with error: %v", err)
   115  			return err
   116  
   117  		}
   118  
   119  		s.eventsService.Log("pull", logName, "")
   120  		return nil
   121  	}
   122  
   123  	if lastErr == nil {
   124  		lastErr = fmt.Errorf("no endpoints found for %s", image)
   125  	}
   126  	return lastErr
   127  }
   128  
   129  func WriteStatus(requestedTag string, out io.Writer, sf *streamformatter.StreamFormatter, layersDownloaded bool) {
   130  	if layersDownloaded {
   131  		out.Write(sf.FormatStatus("", "Status: Downloaded newer image for %s", requestedTag))
   132  	} else {
   133  		out.Write(sf.FormatStatus("", "Status: Image is up to date for %s", requestedTag))
   134  	}
   135  }