1ea809dc2a
Server and Client images of the image store are now provided. We have created an image metadata interface and converted the bolt functions to implement that interface over an transaction. A remote client implementation is provided that implements the same interface. Signed-off-by: Stephen J Day <stephen.day@docker.com>
125 lines
3.2 KiB
Go
125 lines
3.2 KiB
Go
package images
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io/ioutil"
|
|
|
|
"github.com/containerd/containerd/content"
|
|
"github.com/containerd/containerd/log"
|
|
digest "github.com/opencontainers/go-digest"
|
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
)
|
|
|
|
// Image provides the model for how containerd views container images.
|
|
type Image struct {
|
|
Name string
|
|
Target ocispec.Descriptor
|
|
}
|
|
|
|
// 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) {
|
|
var configDesc ocispec.Descriptor
|
|
return configDesc, Walk(ctx, HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
|
switch image.Target.MediaType {
|
|
case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
|
|
rc, err := provider.Reader(ctx, image.Target.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.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
|
|
}
|
|
|
|
p, err := content.ReadBlob(ctx, provider, desc.Digest)
|
|
if err != nil {
|
|
log.G(ctx).Fatal(err)
|
|
}
|
|
|
|
var config ocispec.Image
|
|
if err := json.Unmarshal(p, &config); err != nil {
|
|
log.G(ctx).Fatal(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
|
|
}
|
|
|
|
// 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, HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
|
switch image.Target.MediaType {
|
|
case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
|
|
size += desc.Size
|
|
rc, err := provider.Reader(ctx, image.Target.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
|
|
}
|
|
|
|
size += manifest.Config.Size
|
|
|
|
for _, layer := range manifest.Layers {
|
|
size += layer.Size
|
|
}
|
|
|
|
return nil, nil
|
|
default:
|
|
return nil, errors.New("unsupported type")
|
|
}
|
|
|
|
}), image.Target)
|
|
}
|