mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
9ca3bb632e
This allows us to cache manifests and avoid extra round trips to the registry for content we already know about. dockerd currently does not support containerd on Windows, so this does not store manifests on Windows, yet. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
174 lines
5.3 KiB
Go
174 lines
5.3 KiB
Go
package images // import "github.com/docker/docker/daemon/images"
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/containerd/containerd/leases"
|
|
"github.com/containerd/containerd/namespaces"
|
|
dist "github.com/docker/distribution"
|
|
"github.com/docker/distribution/reference"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/distribution"
|
|
progressutils "github.com/docker/docker/distribution/utils"
|
|
"github.com/docker/docker/errdefs"
|
|
"github.com/docker/docker/pkg/progress"
|
|
"github.com/docker/docker/registry"
|
|
digest "github.com/opencontainers/go-digest"
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// PullImage initiates a pull operation. image is the repository name to pull, and
|
|
// tag may be either empty, or indicate a specific tag to pull.
|
|
func (i *ImageService) PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
|
|
start := time.Now()
|
|
// Special case: "pull -a" may send an image name with a
|
|
// trailing :. This is ugly, but let's not break API
|
|
// compatibility.
|
|
image = strings.TrimSuffix(image, ":")
|
|
|
|
ref, err := reference.ParseNormalizedNamed(image)
|
|
if err != nil {
|
|
return errdefs.InvalidParameter(err)
|
|
}
|
|
|
|
if tag != "" {
|
|
// The "tag" could actually be a digest.
|
|
var dgst digest.Digest
|
|
dgst, err = digest.Parse(tag)
|
|
if err == nil {
|
|
ref, err = reference.WithDigest(reference.TrimNamed(ref), dgst)
|
|
} else {
|
|
ref, err = reference.WithTag(ref, tag)
|
|
}
|
|
if err != nil {
|
|
return errdefs.InvalidParameter(err)
|
|
}
|
|
}
|
|
|
|
err = i.pullImageWithReference(ctx, ref, platform, metaHeaders, authConfig, outStream)
|
|
imageActions.WithValues("pull").UpdateSince(start)
|
|
return err
|
|
}
|
|
|
|
func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference.Named, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
|
|
// Include a buffer so that slow client connections don't affect
|
|
// transfer performance.
|
|
progressChan := make(chan progress.Progress, 100)
|
|
|
|
writesDone := make(chan struct{})
|
|
|
|
ctx, cancelFunc := context.WithCancel(ctx)
|
|
|
|
go func() {
|
|
progressutils.WriteDistributionProgress(cancelFunc, outStream, progressChan)
|
|
close(writesDone)
|
|
}()
|
|
|
|
ctx = namespaces.WithNamespace(ctx, i.contentNamespace)
|
|
// Take out a temporary lease for everything that gets persisted to the content store.
|
|
// Before the lease is cancelled, any content we want to keep should have it's own lease applied.
|
|
ctx, done, err := tempLease(ctx, i.leases)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer done(ctx)
|
|
|
|
cs := &contentStoreForPull{
|
|
ContentStore: i.content,
|
|
leases: i.leases,
|
|
}
|
|
imageStore := &imageStoreForPull{
|
|
ImageConfigStore: distribution.NewImageConfigStoreFromStore(i.imageStore),
|
|
ingested: cs,
|
|
leases: i.leases,
|
|
}
|
|
|
|
imagePullConfig := &distribution.ImagePullConfig{
|
|
Config: distribution.Config{
|
|
MetaHeaders: metaHeaders,
|
|
AuthConfig: authConfig,
|
|
ProgressOutput: progress.ChanOutput(progressChan),
|
|
RegistryService: i.registryService,
|
|
ImageEventLogger: i.LogImageEvent,
|
|
MetadataStore: i.distributionMetadataStore,
|
|
ImageStore: imageStore,
|
|
ReferenceStore: i.referenceStore,
|
|
},
|
|
DownloadManager: i.downloadManager,
|
|
Schema2Types: distribution.ImageTypes,
|
|
Platform: platform,
|
|
}
|
|
|
|
err = distribution.Pull(ctx, ref, imagePullConfig, cs)
|
|
close(progressChan)
|
|
<-writesDone
|
|
return err
|
|
}
|
|
|
|
// GetRepository returns a repository from the registry.
|
|
func (i *ImageService) GetRepository(ctx context.Context, ref reference.Named, authConfig *types.AuthConfig) (dist.Repository, bool, error) {
|
|
// get repository info
|
|
repoInfo, err := i.registryService.ResolveRepository(ref)
|
|
if err != nil {
|
|
return nil, false, errdefs.InvalidParameter(err)
|
|
}
|
|
// makes sure name is not empty or `scratch`
|
|
if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
|
|
return nil, false, errdefs.InvalidParameter(err)
|
|
}
|
|
|
|
// get endpoints
|
|
endpoints, err := i.registryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
// retrieve repository
|
|
var (
|
|
confirmedV2 bool
|
|
repository dist.Repository
|
|
lastError error
|
|
)
|
|
|
|
for _, endpoint := range endpoints {
|
|
if endpoint.Version == registry.APIVersion1 {
|
|
continue
|
|
}
|
|
|
|
repository, confirmedV2, lastError = distribution.NewV2Repository(ctx, repoInfo, endpoint, nil, authConfig, "pull")
|
|
if lastError == nil && confirmedV2 {
|
|
break
|
|
}
|
|
}
|
|
return repository, confirmedV2, lastError
|
|
}
|
|
|
|
func tempLease(ctx context.Context, mgr leases.Manager) (context.Context, func(context.Context) error, error) {
|
|
nop := func(context.Context) error { return nil }
|
|
_, ok := leases.FromContext(ctx)
|
|
if ok {
|
|
return ctx, nop, nil
|
|
}
|
|
|
|
// Use an expiration that ensures the lease is cleaned up at some point if there is a crash, SIGKILL, etc.
|
|
opts := []leases.Opt{
|
|
leases.WithRandomID(),
|
|
leases.WithExpiration(24 * time.Hour),
|
|
leases.WithLabels(map[string]string{
|
|
"moby.lease/temporary": time.Now().UTC().Format(time.RFC3339Nano),
|
|
}),
|
|
}
|
|
l, err := mgr.Create(ctx, opts...)
|
|
if err != nil {
|
|
return ctx, nop, errors.Wrap(err, "error creating temporary lease")
|
|
}
|
|
|
|
ctx = leases.WithLease(ctx, l.ID)
|
|
return ctx, func(ctx context.Context) error {
|
|
return mgr.Delete(ctx, l)
|
|
}, nil
|
|
}
|