123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- /*
- Copyright The containerd Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package containerd
- import (
- "context"
- "github.com/containerd/containerd/errdefs"
- "github.com/containerd/containerd/images"
- "github.com/containerd/containerd/platforms"
- "github.com/containerd/containerd/remotes"
- "github.com/containerd/containerd/remotes/docker"
- "github.com/containerd/containerd/remotes/docker/schema1"
- ocispec "github.com/opencontainers/image-spec/specs-go/v1"
- "github.com/pkg/errors"
- "golang.org/x/sync/semaphore"
- )
- // Pull downloads the provided content into containerd's content store
- // and returns a platform specific image object
- func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image, error) {
- pullCtx := defaultRemoteContext()
- for _, o := range opts {
- if err := o(c, pullCtx); err != nil {
- return nil, err
- }
- }
- if pullCtx.PlatformMatcher == nil {
- if len(pullCtx.Platforms) > 1 {
- return nil, errors.New("cannot pull multiplatform image locally, try Fetch")
- } else if len(pullCtx.Platforms) == 0 {
- pullCtx.PlatformMatcher = platforms.Default()
- } else {
- p, err := platforms.Parse(pullCtx.Platforms[0])
- if err != nil {
- return nil, errors.Wrapf(err, "invalid platform %s", pullCtx.Platforms[0])
- }
- pullCtx.PlatformMatcher = platforms.Only(p)
- }
- }
- ctx, done, err := c.WithLease(ctx)
- if err != nil {
- return nil, err
- }
- defer done(ctx)
- img, err := c.fetch(ctx, pullCtx, ref, 1)
- if err != nil {
- return nil, err
- }
- i := NewImageWithPlatform(c, img, pullCtx.PlatformMatcher)
- if pullCtx.Unpack {
- if err := i.Unpack(ctx, pullCtx.Snapshotter); err != nil {
- return nil, errors.Wrapf(err, "failed to unpack image on snapshotter %s", pullCtx.Snapshotter)
- }
- }
- return i, nil
- }
- func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, limit int) (images.Image, error) {
- store := c.ContentStore()
- name, desc, err := rCtx.Resolver.Resolve(ctx, ref)
- if err != nil {
- return images.Image{}, errors.Wrapf(err, "failed to resolve reference %q", ref)
- }
- fetcher, err := rCtx.Resolver.Fetcher(ctx, name)
- if err != nil {
- return images.Image{}, errors.Wrapf(err, "failed to get fetcher for %q", name)
- }
- var (
- handler images.Handler
- isConvertible bool
- converterFunc func(context.Context, ocispec.Descriptor) (ocispec.Descriptor, error)
- limiter *semaphore.Weighted
- )
- if desc.MediaType == images.MediaTypeDockerSchema1Manifest && rCtx.ConvertSchema1 {
- schema1Converter := schema1.NewConverter(store, fetcher)
- handler = images.Handlers(append(rCtx.BaseHandlers, schema1Converter)...)
- isConvertible = true
- converterFunc = func(ctx context.Context, _ ocispec.Descriptor) (ocispec.Descriptor, error) {
- return schema1Converter.Convert(ctx)
- }
- } else {
- // Get all the children for a descriptor
- childrenHandler := images.ChildrenHandler(store)
- // Set any children labels for that content
- childrenHandler = images.SetChildrenLabels(store, childrenHandler)
- // Filter manifests by platforms but allow to handle manifest
- // and configuration for not-target platforms
- childrenHandler = remotes.FilterManifestByPlatformHandler(childrenHandler, rCtx.PlatformMatcher)
- // Sort and limit manifests if a finite number is needed
- if limit > 0 {
- childrenHandler = images.LimitManifests(childrenHandler, rCtx.PlatformMatcher, limit)
- }
- // set isConvertible to true if there is application/octet-stream media type
- convertibleHandler := images.HandlerFunc(
- func(_ context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
- if desc.MediaType == docker.LegacyConfigMediaType {
- isConvertible = true
- }
- return []ocispec.Descriptor{}, nil
- },
- )
- handlers := append(rCtx.BaseHandlers,
- remotes.FetchHandler(store, fetcher),
- convertibleHandler,
- childrenHandler,
- )
- // append distribution source label to blob data
- if rCtx.AppendDistributionSourceLabel {
- appendDistSrcLabelHandler, err := docker.AppendDistributionSourceLabel(store, ref)
- if err != nil {
- return images.Image{}, err
- }
- handlers = append(handlers, appendDistSrcLabelHandler)
- }
- handler = images.Handlers(handlers...)
- converterFunc = func(ctx context.Context, desc ocispec.Descriptor) (ocispec.Descriptor, error) {
- return docker.ConvertManifest(ctx, store, desc)
- }
- }
- if rCtx.HandlerWrapper != nil {
- handler = rCtx.HandlerWrapper(handler)
- }
- if rCtx.MaxConcurrentDownloads > 0 {
- limiter = semaphore.NewWeighted(int64(rCtx.MaxConcurrentDownloads))
- }
- if err := images.Dispatch(ctx, handler, limiter, desc); err != nil {
- return images.Image{}, err
- }
- if isConvertible {
- if desc, err = converterFunc(ctx, desc); err != nil {
- return images.Image{}, err
- }
- }
- img := images.Image{
- Name: name,
- Target: desc,
- Labels: rCtx.Labels,
- }
- is := c.ImageService()
- for {
- if created, err := is.Create(ctx, img); err != nil {
- if !errdefs.IsAlreadyExists(err) {
- return images.Image{}, err
- }
- updated, err := is.Update(ctx, img)
- if err != nil {
- // if image was removed, try create again
- if errdefs.IsNotFound(err) {
- continue
- }
- return images.Image{}, err
- }
- img = updated
- } else {
- img = created
- }
- return img, nil
- }
- }
|