206 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			206 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
|    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"
 | |
| 	"encoding/json"
 | |
| 	"io"
 | |
| 
 | |
| 	"github.com/containerd/containerd/content"
 | |
| 	"github.com/containerd/containerd/errdefs"
 | |
| 	"github.com/containerd/containerd/images"
 | |
| 	"github.com/containerd/containerd/images/archive"
 | |
| 	"github.com/containerd/containerd/platforms"
 | |
| 	digest "github.com/opencontainers/go-digest"
 | |
| 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 | |
| )
 | |
| 
 | |
| type importOpts struct {
 | |
| 	indexName    string
 | |
| 	imageRefT    func(string) string
 | |
| 	dgstRefT     func(digest.Digest) string
 | |
| 	allPlatforms bool
 | |
| 	compress     bool
 | |
| }
 | |
| 
 | |
| // ImportOpt allows the caller to specify import specific options
 | |
| type ImportOpt func(*importOpts) error
 | |
| 
 | |
| // WithImageRefTranslator is used to translate the index reference
 | |
| // to an image reference for the image store.
 | |
| func WithImageRefTranslator(f func(string) string) ImportOpt {
 | |
| 	return func(c *importOpts) error {
 | |
| 		c.imageRefT = f
 | |
| 		return nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // WithDigestRef is used to create digest images for each
 | |
| // manifest in the index.
 | |
| func WithDigestRef(f func(digest.Digest) string) ImportOpt {
 | |
| 	return func(c *importOpts) error {
 | |
| 		c.dgstRefT = f
 | |
| 		return nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // WithIndexName creates a tag pointing to the imported index
 | |
| func WithIndexName(name string) ImportOpt {
 | |
| 	return func(c *importOpts) error {
 | |
| 		c.indexName = name
 | |
| 		return nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // WithAllPlatforms is used to import content for all platforms.
 | |
| func WithAllPlatforms(allPlatforms bool) ImportOpt {
 | |
| 	return func(c *importOpts) error {
 | |
| 		c.allPlatforms = allPlatforms
 | |
| 		return nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // WithImportCompression compresses uncompressed layers on import.
 | |
| // This is used for import formats which do not include the manifest.
 | |
| func WithImportCompression() ImportOpt {
 | |
| 	return func(c *importOpts) error {
 | |
| 		c.compress = true
 | |
| 		return nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Import imports an image from a Tar stream using reader.
 | |
| // Caller needs to specify importer. Future version may use oci.v1 as the default.
 | |
| // Note that unreferenced blobs may be imported to the content store as well.
 | |
| func (c *Client) Import(ctx context.Context, reader io.Reader, opts ...ImportOpt) ([]images.Image, error) {
 | |
| 	var iopts importOpts
 | |
| 	for _, o := range opts {
 | |
| 		if err := o(&iopts); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	ctx, done, err := c.WithLease(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer done(ctx)
 | |
| 
 | |
| 	var aio []archive.ImportOpt
 | |
| 	if iopts.compress {
 | |
| 		aio = append(aio, archive.WithImportCompression())
 | |
| 	}
 | |
| 
 | |
| 	index, err := archive.ImportIndex(ctx, c.ContentStore(), reader, aio...)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	var (
 | |
| 		imgs []images.Image
 | |
| 		cs   = c.ContentStore()
 | |
| 		is   = c.ImageService()
 | |
| 	)
 | |
| 
 | |
| 	if iopts.indexName != "" {
 | |
| 		imgs = append(imgs, images.Image{
 | |
| 			Name:   iopts.indexName,
 | |
| 			Target: index,
 | |
| 		})
 | |
| 	}
 | |
| 	var platformMatcher = platforms.All
 | |
| 	if !iopts.allPlatforms {
 | |
| 		platformMatcher = c.platform
 | |
| 	}
 | |
| 
 | |
| 	var handler images.HandlerFunc = func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
 | |
| 		// Only save images at top level
 | |
| 		if desc.Digest != index.Digest {
 | |
| 			return images.Children(ctx, cs, desc)
 | |
| 		}
 | |
| 
 | |
| 		p, err := content.ReadBlob(ctx, cs, desc)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		var idx ocispec.Index
 | |
| 		if err := json.Unmarshal(p, &idx); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		for _, m := range idx.Manifests {
 | |
| 			name := imageName(m.Annotations, iopts.imageRefT)
 | |
| 			if name != "" {
 | |
| 				imgs = append(imgs, images.Image{
 | |
| 					Name:   name,
 | |
| 					Target: m,
 | |
| 				})
 | |
| 			}
 | |
| 			if iopts.dgstRefT != nil {
 | |
| 				ref := iopts.dgstRefT(m.Digest)
 | |
| 				if ref != "" {
 | |
| 					imgs = append(imgs, images.Image{
 | |
| 						Name:   ref,
 | |
| 						Target: m,
 | |
| 					})
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return idx.Manifests, nil
 | |
| 	}
 | |
| 
 | |
| 	handler = images.FilterPlatforms(handler, platformMatcher)
 | |
| 	handler = images.SetChildrenLabels(cs, handler)
 | |
| 	if err := images.Walk(ctx, handler, index); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	for i := range imgs {
 | |
| 		img, err := is.Update(ctx, imgs[i], "target")
 | |
| 		if err != nil {
 | |
| 			if !errdefs.IsNotFound(err) {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 
 | |
| 			img, err = is.Create(ctx, imgs[i])
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 		}
 | |
| 		imgs[i] = img
 | |
| 	}
 | |
| 
 | |
| 	return imgs, nil
 | |
| }
 | |
| 
 | |
| func imageName(annotations map[string]string, ociCleanup func(string) string) string {
 | |
| 	name := annotations[images.AnnotationImageName]
 | |
| 	if name != "" {
 | |
| 		return name
 | |
| 	}
 | |
| 	name = annotations[ocispec.AnnotationRefName]
 | |
| 	if name != "" {
 | |
| 		if ociCleanup != nil {
 | |
| 			name = ociCleanup(name)
 | |
| 		}
 | |
| 	}
 | |
| 	return name
 | |
| }
 | 
