containerd/images/storage.go
Stephen J Day cc983be17a
cmd/dist, images: allow image delete
This adds very simple deletion of images by name. We still need to
consider the approach to handling image name, so this may change. For
the time being, it allows one to delete an image entry in the metadata
database.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
2017-03-31 12:49:44 -07:00

186 lines
4.1 KiB
Go

package images
import (
"encoding/binary"
"fmt"
"github.com/boltdb/bolt"
"github.com/docker/containerd/log"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
var (
errImageUnknown = fmt.Errorf("image: unknown")
)
var (
bucketKeyStorageVersion = []byte("v1")
bucketKeyImages = []byte("images")
bucketKeyDigest = []byte("digest")
bucketKeyMediaType = []byte("mediatype")
bucketKeySize = []byte("size")
)
// TODO(stevvooe): This file comprises the data required to implement the
// "metadata" store. For now, it is bound tightly to the local machine and bolt
// but we can take this and use it to define a service interface.
func InitDB(db *bolt.DB) error {
log.L.Debug("init db")
return db.Update(func(tx *bolt.Tx) error {
_, err := createBucketIfNotExists(tx, bucketKeyStorageVersion, bucketKeyImages)
return err
})
}
func Register(tx *bolt.Tx, name string, desc ocispec.Descriptor) error {
return withImagesBucket(tx, func(bkt *bolt.Bucket) error {
ibkt, err := bkt.CreateBucketIfNotExists([]byte(name))
if err != nil {
return err
}
var (
buf [binary.MaxVarintLen64]byte
sizeEncoded []byte = buf[:]
)
sizeEncoded = sizeEncoded[:binary.PutVarint(sizeEncoded, desc.Size)]
if len(sizeEncoded) == 0 {
return fmt.Errorf("failed encoding size = %v", desc.Size)
}
for _, v := range [][2][]byte{
{bucketKeyDigest, []byte(desc.Digest)},
{bucketKeyMediaType, []byte(desc.MediaType)},
{bucketKeySize, sizeEncoded},
} {
if err := ibkt.Put(v[0], v[1]); err != nil {
return err
}
}
return nil
})
}
func Get(tx *bolt.Tx, name string) (Image, error) {
var image Image
if err := withImageBucket(tx, name, func(bkt *bolt.Bucket) error {
image.Name = name
return readImage(&image, bkt)
}); err != nil {
return Image{}, err
}
return image, nil
}
func List(tx *bolt.Tx) ([]Image, error) {
var images []Image
if err := withImagesBucket(tx, func(bkt *bolt.Bucket) error {
return bkt.ForEach(func(k, v []byte) error {
var (
image = Image{
Name: string(k),
}
kbkt = bkt.Bucket(k)
)
if err := readImage(&image, kbkt); err != nil {
return err
}
images = append(images, image)
return nil
})
}); err != nil {
return nil, err
}
return images, nil
}
func Delete(tx *bolt.Tx, name string) error {
return withImagesBucket(tx, func(bkt *bolt.Bucket) error {
return bkt.DeleteBucket([]byte(name))
})
}
func readImage(image *Image, bkt *bolt.Bucket) error {
return bkt.ForEach(func(k, v []byte) error {
if v == nil {
return nil // skip it? a bkt maybe?
}
// TODO(stevvooe): This is why we need to use byte values for
// keys, rather than full arrays.
switch string(k) {
case string(bucketKeyDigest):
image.Descriptor.Digest = digest.Digest(v)
case string(bucketKeyMediaType):
image.Descriptor.MediaType = string(v)
case string(bucketKeySize):
image.Descriptor.Size, _ = binary.Varint(v)
}
return nil
})
}
func createBucketIfNotExists(tx *bolt.Tx, keys ...[]byte) (*bolt.Bucket, error) {
bkt, err := tx.CreateBucketIfNotExists(keys[0])
if err != nil {
return nil, err
}
for _, key := range keys[1:] {
bkt, err = bkt.CreateBucketIfNotExists(key)
if err != nil {
return nil, err
}
}
return bkt, nil
}
func withImagesBucket(tx *bolt.Tx, fn func(bkt *bolt.Bucket) error) error {
bkt := getImagesBucket(tx)
if bkt == nil {
return errImageUnknown
}
return fn(bkt)
}
func withImageBucket(tx *bolt.Tx, name string, fn func(bkt *bolt.Bucket) error) error {
bkt := getImageBucket(tx, name)
if bkt == nil {
return errImageUnknown
}
return fn(bkt)
}
func getImagesBucket(tx *bolt.Tx) *bolt.Bucket {
return getBucket(tx, bucketKeyStorageVersion, bucketKeyImages)
}
func getImageBucket(tx *bolt.Tx, name string) *bolt.Bucket {
return getBucket(tx, bucketKeyStorageVersion, bucketKeyImages, []byte(name))
}
func getBucket(tx *bolt.Tx, keys ...[]byte) *bolt.Bucket {
bkt := tx.Bucket(keys[0])
for _, key := range keys[1:] {
if bkt == nil {
break
}
bkt = bkt.Bucket(key)
}
return bkt
}