
The primary feature we get with this PR is support for filters and labels on the image metadata store. In the process of doing this, the conventions for the API have been converged between containers and images, providing a model for other services. With images, `Put` (renamed to `Update` briefly) has been split into a `Create` and `Update`, allowing one to control the behavior around these operations. `Update` now includes support for masking fields at the datastore-level across both the containers and image service. Filters are now just string values to interpreted directly within the data store. This should allow for some interesting future use cases in which the datastore might use the syntax for more efficient query paths. The containers service has been updated to follow these conventions as closely as possible. Signed-off-by: Stephen J Day <stephen.day@docker.com>
126 lines
3.8 KiB
Go
126 lines
3.8 KiB
Go
package images
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"time"
|
|
|
|
"github.com/containerd/containerd/content"
|
|
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)
|
|
}
|
|
|
|
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 image.MediaType {
|
|
case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
|
|
rc, err := provider.Reader(ctx, image.Digest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rc.Close()
|
|
|
|
p, err := ioutil.ReadAll(rc)
|
|
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)
|
|
}
|
|
|
|
// 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
|
|
}
|