Files
containerd/images/image.go
Stephen J Day ea8adf9021 cmd/ctr: show resolved image platform
Signed-off-by: Stephen J Day <stephen.day@docker.com>
2017-09-13 12:41:55 -07:00

153 lines
4.9 KiB
Go

package images
import (
"context"
"encoding/json"
"time"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/platforms"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
// Image provides the model for how containerd views container images.
type Image struct {
Name string
Labels map[string]string
Target ocispec.Descriptor
CreatedAt, UpdatedAt time.Time
}
type Store interface {
Get(ctx context.Context, name string) (Image, error)
List(ctx context.Context, filters ...string) ([]Image, error)
Create(ctx context.Context, image Image) (Image, error)
// Update will replace the data in the store with the provided image. If
// one or more fieldpaths are provided, only those fields will be updated.
Update(ctx context.Context, image Image, fieldpaths ...string) (Image, error)
Delete(ctx context.Context, name string) error
}
// TODO(stevvooe): Many of these functions make strong platform assumptions,
// which are untrue in a lot of cases. More refactoring must be done here to
// make this work in all cases.
// Config resolves the image configuration descriptor.
//
// The caller can then use the descriptor to resolve and process the
// configuration of the image.
func (image *Image) Config(ctx context.Context, provider content.Provider) (ocispec.Descriptor, error) {
return Config(ctx, provider, image.Target)
}
// RootFS returns the unpacked diffids that make up and images rootfs.
//
// These are used to verify that a set of layers unpacked to the expected
// values.
func (image *Image) RootFS(ctx context.Context, provider content.Provider) ([]digest.Digest, error) {
desc, err := image.Config(ctx, provider)
if err != nil {
return nil, err
}
return RootFS(ctx, provider, desc)
}
// Size returns the total size of an image's packed resources.
func (image *Image) Size(ctx context.Context, provider content.Provider) (int64, error) {
var size int64
return size, Walk(ctx, Handlers(HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
if desc.Size < 0 {
return nil, errors.Errorf("invalid size %v in %v (%v)", desc.Size, desc.Digest, desc.MediaType)
}
size += desc.Size
return nil, nil
}), ChildrenHandler(provider)), image.Target)
}
// Config resolves the image configuration descriptor using a content provided
// to resolve child resources on the image.
//
// The caller can then use the descriptor to resolve and process the
// configuration of the image.
func Config(ctx context.Context, provider content.Provider, image ocispec.Descriptor) (ocispec.Descriptor, error) {
var configDesc ocispec.Descriptor
return configDesc, Walk(ctx, HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
switch desc.MediaType {
case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
p, err := content.ReadBlob(ctx, provider, image.Digest)
if err != nil {
return nil, err
}
var manifest ocispec.Manifest
if err := json.Unmarshal(p, &manifest); err != nil {
return nil, err
}
configDesc = manifest.Config
return nil, nil
default:
return nil, errors.New("could not resolve config")
}
}), image)
}
// Platforms returns one or more platforms supported by the image.
func Platforms(ctx context.Context, provider content.Provider, image ocispec.Descriptor) ([]ocispec.Platform, error) {
var platformSpecs []ocispec.Platform
return platformSpecs, Walk(ctx, Handlers(HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
if desc.Platform != nil {
platformSpecs = append(platformSpecs, *desc.Platform)
return nil, SkipDesc
}
switch desc.MediaType {
case MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig:
p, err := content.ReadBlob(ctx, provider, desc.Digest)
if err != nil {
return nil, err
}
var image ocispec.Image
if err := json.Unmarshal(p, &image); err != nil {
return nil, err
}
platformSpecs = append(platformSpecs,
platforms.Normalize(ocispec.Platform{OS: image.OS, Architecture: image.Architecture}))
}
return nil, nil
}), ChildrenHandler(provider)), image)
}
// RootFS returns the unpacked diffids that make up and images rootfs.
//
// These are used to verify that a set of layers unpacked to the expected
// values.
func RootFS(ctx context.Context, provider content.Provider, configDesc ocispec.Descriptor) ([]digest.Digest, error) {
p, err := content.ReadBlob(ctx, provider, configDesc.Digest)
if err != nil {
return nil, err
}
var config ocispec.Image
if err := json.Unmarshal(p, &config); err != nil {
return nil, err
}
// TODO(stevvooe): Remove this bit when OCI structure uses correct type for
// rootfs.DiffIDs.
var diffIDs []digest.Digest
for _, diffID := range config.RootFS.DiffIDs {
diffIDs = append(diffIDs, digest.Digest(diffID))
}
return diffIDs, nil
}