image/store.go
01ba0a93
 package image
 
 import (
 	"encoding/json"
 	"errors"
 	"fmt"
 	"sync"
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/distribution/digest"
 	"github.com/docker/docker/layer"
 )
 
 // Store is an interface for creating and accessing images
 type Store interface {
 	Create(config []byte) (ID, error)
 	Get(id ID) (*Image, error)
 	Delete(id ID) ([]layer.Metadata, error)
 	Search(partialID string) (ID, error)
 	SetParent(id ID, parent ID) error
 	GetParent(id ID) (ID, error)
 	Children(id ID) []ID
 	Map() map[ID]*Image
 	Heads() map[ID]*Image
 }
 
 // LayerGetReleaser is a minimal interface for getting and releasing images.
 type LayerGetReleaser interface {
 	Get(layer.ChainID) (layer.Layer, error)
 	Release(layer.Layer) ([]layer.Metadata, error)
 }
 
 type imageMeta struct {
 	layer    layer.Layer
 	children map[ID]struct{}
 }
 
 type store struct {
 	sync.Mutex
 	ls        LayerGetReleaser
 	images    map[ID]*imageMeta
 	fs        StoreBackend
 	digestSet *digest.Set
 }
 
 // NewImageStore returns new store object for given layer store
 func NewImageStore(fs StoreBackend, ls LayerGetReleaser) (Store, error) {
 	is := &store{
 		ls:        ls,
 		images:    make(map[ID]*imageMeta),
 		fs:        fs,
 		digestSet: digest.NewSet(),
 	}
 
 	// load all current images and retain layers
 	if err := is.restore(); err != nil {
 		return nil, err
 	}
 
 	return is, nil
 }
 
 func (is *store) restore() error {
 	err := is.fs.Walk(func(id ID) error {
 		img, err := is.Get(id)
 		if err != nil {
 			logrus.Errorf("invalid image %v, %v", id, err)
 			return nil
 		}
 		var l layer.Layer
 		if chainID := img.RootFS.ChainID(); chainID != "" {
 			l, err = is.ls.Get(chainID)
 			if err != nil {
 				return err
 			}
 		}
 		if err := is.digestSet.Add(digest.Digest(id)); err != nil {
 			return err
 		}
 
 		imageMeta := &imageMeta{
 			layer:    l,
 			children: make(map[ID]struct{}),
 		}
 
 		is.images[ID(id)] = imageMeta
 
 		return nil
 	})
 	if err != nil {
 		return err
 	}
 
 	// Second pass to fill in children maps
 	for id := range is.images {
 		if parent, err := is.GetParent(id); err == nil {
 			if parentMeta := is.images[parent]; parentMeta != nil {
 				parentMeta.children[id] = struct{}{}
 			}
 		}
 	}
 
 	return nil
 }
 
 func (is *store) Create(config []byte) (ID, error) {
 	var img Image
 	err := json.Unmarshal(config, &img)
 	if err != nil {
 		return "", err
 	}
 
 	// Must reject any config that references diffIDs from the history
 	// which aren't among the rootfs layers.
 	rootFSLayers := make(map[layer.DiffID]struct{})
 	for _, diffID := range img.RootFS.DiffIDs {
 		rootFSLayers[diffID] = struct{}{}
 	}
 
 	layerCounter := 0
 	for _, h := range img.History {
 		if !h.EmptyLayer {
 			layerCounter++
 		}
 	}
 	if layerCounter > len(img.RootFS.DiffIDs) {
 		return "", errors.New("too many non-empty layers in History section")
 	}
 
 	dgst, err := is.fs.Set(config)
 	if err != nil {
 		return "", err
 	}
 	imageID := ID(dgst)
 
 	is.Lock()
 	defer is.Unlock()
 
 	if _, exists := is.images[imageID]; exists {
 		return imageID, nil
 	}
 
 	layerID := img.RootFS.ChainID()
 
 	var l layer.Layer
 	if layerID != "" {
 		l, err = is.ls.Get(layerID)
 		if err != nil {
 			return "", err
 		}
 	}
 
 	imageMeta := &imageMeta{
 		layer:    l,
 		children: make(map[ID]struct{}),
 	}
 
 	is.images[imageID] = imageMeta
 	if err := is.digestSet.Add(digest.Digest(imageID)); err != nil {
 		delete(is.images, imageID)
 		return "", err
 	}
 
 	return imageID, nil
 }
 
 func (is *store) Search(term string) (ID, error) {
 	is.Lock()
 	defer is.Unlock()
 
 	dgst, err := is.digestSet.Lookup(term)
 	if err != nil {
ed231d40
 		if err == digest.ErrDigestNotFound {
 			err = fmt.Errorf("No such image: %s", term)
 		}
01ba0a93
 		return "", err
 	}
 	return ID(dgst), nil
 }
 
 func (is *store) Get(id ID) (*Image, error) {
 	// todo: Check if image is in images
 	// todo: Detect manual insertions and start using them
 	config, err := is.fs.Get(id)
 	if err != nil {
 		return nil, err
 	}
 
 	img, err := NewFromJSON(config)
 	if err != nil {
 		return nil, err
 	}
 	img.computedID = id
 
 	img.Parent, err = is.GetParent(id)
 	if err != nil {
 		img.Parent = ""
 	}
 
 	return img, nil
 }
 
 func (is *store) Delete(id ID) ([]layer.Metadata, error) {
 	is.Lock()
 	defer is.Unlock()
 
 	imageMeta := is.images[id]
 	if imageMeta == nil {
 		return nil, fmt.Errorf("unrecognized image ID %s", id.String())
 	}
 	for id := range imageMeta.children {
 		is.fs.DeleteMetadata(id, "parent")
 	}
 	if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil {
 		delete(is.images[parent].children, id)
 	}
 
fcb083c6
 	if err := is.digestSet.Remove(digest.Digest(id)); err != nil {
 		logrus.Errorf("error removing %s from digest set: %q", id, err)
 	}
01ba0a93
 	delete(is.images, id)
 	is.fs.Delete(id)
 
 	if imageMeta.layer != nil {
 		return is.ls.Release(imageMeta.layer)
 	}
 	return nil, nil
 }
 
 func (is *store) SetParent(id, parent ID) error {
 	is.Lock()
 	defer is.Unlock()
 	parentMeta := is.images[parent]
 	if parentMeta == nil {
 		return fmt.Errorf("unknown parent image ID %s", parent.String())
 	}
48529324
 	if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil {
 		delete(is.images[parent].children, id)
 	}
01ba0a93
 	parentMeta.children[id] = struct{}{}
 	return is.fs.SetMetadata(id, "parent", []byte(parent))
 }
 
 func (is *store) GetParent(id ID) (ID, error) {
 	d, err := is.fs.GetMetadata(id, "parent")
 	if err != nil {
 		return "", err
 	}
 	return ID(d), nil // todo: validate?
 }
 
 func (is *store) Children(id ID) []ID {
 	is.Lock()
 	defer is.Unlock()
 
 	return is.children(id)
 }
 
 func (is *store) children(id ID) []ID {
 	var ids []ID
 	if is.images[id] != nil {
 		for id := range is.images[id].children {
 			ids = append(ids, id)
 		}
 	}
 	return ids
 }
 
 func (is *store) Heads() map[ID]*Image {
 	return is.imagesMap(false)
 }
 
 func (is *store) Map() map[ID]*Image {
 	return is.imagesMap(true)
 }
 
 func (is *store) imagesMap(all bool) map[ID]*Image {
 	is.Lock()
 	defer is.Unlock()
 
 	images := make(map[ID]*Image)
 
 	for id := range is.images {
 		if !all && len(is.children(id)) > 0 {
 			continue
 		}
 		img, err := is.Get(id)
 		if err != nil {
 			logrus.Errorf("invalid image access: %q, error: %q", id, err)
 			continue
 		}
 		images[id] = img
 	}
 	return images
 }