github.com/moby/docker@v26.1.3+incompatible/daemon/images/image_pull.go (about) 1 package images // import "github.com/docker/docker/daemon/images" 2 3 import ( 4 "context" 5 "io" 6 "time" 7 8 "github.com/containerd/containerd/leases" 9 "github.com/containerd/containerd/namespaces" 10 "github.com/containerd/log" 11 "github.com/distribution/reference" 12 "github.com/docker/docker/api/types/backend" 13 "github.com/docker/docker/api/types/registry" 14 "github.com/docker/docker/distribution" 15 progressutils "github.com/docker/docker/distribution/utils" 16 "github.com/docker/docker/errdefs" 17 "github.com/docker/docker/pkg/progress" 18 "github.com/docker/docker/pkg/streamformatter" 19 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 20 "github.com/pkg/errors" 21 ) 22 23 // PullImage initiates a pull operation. image is the repository name to pull, and 24 // tag may be either empty, or indicate a specific tag to pull. 25 func (i *ImageService) PullImage(ctx context.Context, ref reference.Named, platform *ocispec.Platform, metaHeaders map[string][]string, authConfig *registry.AuthConfig, outStream io.Writer) error { 26 start := time.Now() 27 28 err := i.pullImageWithReference(ctx, ref, platform, metaHeaders, authConfig, outStream) 29 ImageActions.WithValues("pull").UpdateSince(start) 30 if err != nil { 31 return err 32 } 33 34 if platform != nil { 35 // If --platform was specified, check that the image we pulled matches 36 // the expected platform. This check is for situations where the image 37 // is a single-arch image, in which case (for backward compatibility), 38 // we allow the image to have a non-matching architecture. The code 39 // below checks for this situation, and returns a warning to the client, 40 // as well as logging it to the daemon logs. 41 img, err := i.GetImage(ctx, ref.String(), backend.GetImageOpts{Platform: platform}) 42 43 // Note that this is a special case where GetImage returns both an image 44 // and an error: https://github.com/docker/docker/blob/v20.10.7/daemon/images/image.go#L175-L183 45 if errdefs.IsNotFound(err) && img != nil { 46 po := streamformatter.NewJSONProgressOutput(outStream, false) 47 progress.Messagef(po, "", `WARNING: %s`, err.Error()) 48 log.G(ctx).WithError(err).WithField("image", reference.FamiliarName(ref)).Warn("ignoring platform mismatch on single-arch image") 49 } else if err != nil { 50 return err 51 } 52 } 53 54 return nil 55 } 56 57 func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference.Named, platform *ocispec.Platform, metaHeaders map[string][]string, authConfig *registry.AuthConfig, outStream io.Writer) error { 58 // Include a buffer so that slow client connections don't affect 59 // transfer performance. 60 progressChan := make(chan progress.Progress, 100) 61 62 writesDone := make(chan struct{}) 63 64 ctx, cancelFunc := context.WithCancel(ctx) 65 66 go func() { 67 progressutils.WriteDistributionProgress(cancelFunc, outStream, progressChan) 68 close(writesDone) 69 }() 70 71 ctx = namespaces.WithNamespace(ctx, i.contentNamespace) 72 // Take out a temporary lease for everything that gets persisted to the content store. 73 // Before the lease is cancelled, any content we want to keep should have it's own lease applied. 74 ctx, done, err := tempLease(ctx, i.leases) 75 if err != nil { 76 return err 77 } 78 defer done(ctx) 79 80 cs := &contentStoreForPull{ 81 ContentStore: i.content, 82 leases: i.leases, 83 } 84 imageStore := &imageStoreForPull{ 85 ImageConfigStore: distribution.NewImageConfigStoreFromStore(i.imageStore), 86 ingested: cs, 87 leases: i.leases, 88 } 89 90 imagePullConfig := &distribution.ImagePullConfig{ 91 Config: distribution.Config{ 92 MetaHeaders: metaHeaders, 93 AuthConfig: authConfig, 94 ProgressOutput: progress.ChanOutput(progressChan), 95 RegistryService: i.registryService, 96 ImageEventLogger: i.LogImageEvent, 97 MetadataStore: i.distributionMetadataStore, 98 ImageStore: imageStore, 99 ReferenceStore: i.referenceStore, 100 }, 101 DownloadManager: i.downloadManager, 102 Platform: platform, 103 } 104 105 err = distribution.Pull(ctx, ref, imagePullConfig, cs) 106 close(progressChan) 107 <-writesDone 108 return err 109 } 110 111 func tempLease(ctx context.Context, mgr leases.Manager) (context.Context, func(context.Context) error, error) { 112 nop := func(context.Context) error { return nil } 113 _, ok := leases.FromContext(ctx) 114 if ok { 115 return ctx, nop, nil 116 } 117 118 // Use an expiration that ensures the lease is cleaned up at some point if there is a crash, SIGKILL, etc. 119 opts := []leases.Opt{ 120 leases.WithRandomID(), 121 leases.WithExpiration(24 * time.Hour), 122 leases.WithLabels(map[string]string{ 123 "moby.lease/temporary": time.Now().UTC().Format(time.RFC3339Nano), 124 }), 125 } 126 l, err := mgr.Create(ctx, opts...) 127 if err != nil { 128 return ctx, nop, errors.Wrap(err, "error creating temporary lease") 129 } 130 131 ctx = leases.WithLease(ctx, l.ID) 132 return ctx, func(ctx context.Context) error { 133 return mgr.Delete(ctx, l) 134 }, nil 135 }