Move to a single tag-store

Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
John Howard 2017-08-17 15:43:36 -07:00
parent 8bee1e9a3b
commit 7b9a8f460b
14 changed files with 54 additions and 40 deletions

View File

@ -75,7 +75,6 @@ type daemonStore struct {
imageStore image.Store imageStore image.Store
layerStore layer.Store layerStore layer.Store
distributionMetadataStore dmetadata.Store distributionMetadataStore dmetadata.Store
referenceStore refstore.Store
} }
// Daemon holds information about the Docker daemon. // Daemon holds information about the Docker daemon.
@ -103,7 +102,8 @@ type Daemon struct {
shutdown bool shutdown bool
idMappings *idtools.IDMappings idMappings *idtools.IDMappings
stores map[string]daemonStore // By container target platform stores map[string]daemonStore // By container target platform
PluginStore *plugin.Store // todo: remove referenceStore refstore.Store
PluginStore *plugin.Store // todo: remove
pluginManager *plugin.Manager pluginManager *plugin.Manager
linkIndex *linkIndex linkIndex *linkIndex
containerd libcontainerd.Client containerd libcontainerd.Client
@ -691,7 +691,6 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
d.downloadManager = xfer.NewLayerDownloadManager(lsMap, *config.MaxConcurrentDownloads) d.downloadManager = xfer.NewLayerDownloadManager(lsMap, *config.MaxConcurrentDownloads)
logrus.Debugf("Max Concurrent Uploads: %d", *config.MaxConcurrentUploads) logrus.Debugf("Max Concurrent Uploads: %d", *config.MaxConcurrentUploads)
d.uploadManager = xfer.NewLayerUploadManager(*config.MaxConcurrentUploads) d.uploadManager = xfer.NewLayerUploadManager(*config.MaxConcurrentUploads)
for platform, ds := range d.stores { for platform, ds := range d.stores {
imageRoot := filepath.Join(config.Root, "image", ds.graphDriver) imageRoot := filepath.Join(config.Root, "image", ds.graphDriver)
ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb")) ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb"))
@ -728,18 +727,30 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
eventsService := events.New() eventsService := events.New()
// We have a single tag/reference store for the daemon globally. However, it's
// stored under the graphdriver. On host platforms which only support a single
// container OS, but multiple selectable graphdrivers, this means depending on which
// graphdriver is chosen, the global reference store is under there. For
// platforms which support multiple container operating systems, this is slightly
// more problematic as where does the global ref store get located? Fortunately,
// for Windows, which is currently the only daemon supporting multiple container
// operating systems, the list of graphdrivers available isn't user configurable.
// For backwards compatibility, we just put it under the windowsfilter
// directory regardless.
refStoreLocation := filepath.Join(d.stores[runtime.GOOS].imageRoot, `repositories.json`)
rs, err := refstore.NewReferenceStore(refStoreLocation)
if err != nil {
return nil, fmt.Errorf("Couldn't create reference store repository: %s", err)
}
d.referenceStore = rs
for platform, ds := range d.stores { for platform, ds := range d.stores {
dms, err := dmetadata.NewFSMetadataStore(filepath.Join(ds.imageRoot, "distribution"), platform) dms, err := dmetadata.NewFSMetadataStore(filepath.Join(ds.imageRoot, "distribution"), platform)
if err != nil { if err != nil {
return nil, err return nil, err
} }
rs, err := refstore.NewReferenceStore(filepath.Join(ds.imageRoot, "repositories.json"), platform)
if err != nil {
return nil, fmt.Errorf("Couldn't create Tag store repositories: %s", err)
}
ds.distributionMetadataStore = dms ds.distributionMetadataStore = dms
ds.referenceStore = rs
d.stores[platform] = ds d.stores[platform] = ds
// No content-addressability migration on Windows as it never supported pre-CA // No content-addressability migration on Windows as it never supported pre-CA

View File

@ -20,7 +20,7 @@ func (daemon *Daemon) getLayerRefs(platform string) map[layer.ChainID]int {
layerRefs := map[layer.ChainID]int{} layerRefs := map[layer.ChainID]int{}
for id, img := range tmpImages { for id, img := range tmpImages {
dgst := digest.Digest(id) dgst := digest.Digest(id)
if len(daemon.stores[platform].referenceStore.References(dgst)) == 0 && len(daemon.stores[platform].imageStore.Children(id)) != 0 { if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.stores[platform].imageStore.Children(id)) != 0 {
continue continue
} }

View File

@ -2,6 +2,7 @@ package daemon
import ( import (
"fmt" "fmt"
"runtime"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/image" "github.com/docker/docker/image"
@ -45,10 +46,17 @@ func (daemon *Daemon) GetImageIDAndPlatform(refOrID string) (image.ID, string, e
return "", "", errImageDoesNotExist{ref} return "", "", errImageDoesNotExist{ref}
} }
for platform := range daemon.stores { if digest, err := daemon.referenceStore.Get(namedRef); err == nil {
if id, err := daemon.stores[platform].referenceStore.Get(namedRef); err == nil { // Search the image stores to get the platform, defaulting to host OS.
return image.IDFromDigest(id), platform, nil imagePlatform := runtime.GOOS
id := image.IDFromDigest(digest)
for platform := range daemon.stores {
if img, err := daemon.stores[platform].imageStore.Get(id); err == nil {
imagePlatform = img.Platform()
break
}
} }
return id, imagePlatform, nil
} }
// deprecated: repo:shortid https://github.com/docker/docker/pull/799 // deprecated: repo:shortid https://github.com/docker/docker/pull/799
@ -56,7 +64,7 @@ func (daemon *Daemon) GetImageIDAndPlatform(refOrID string) (image.ID, string, e
if tag := tagged.Tag(); stringid.IsShortID(stringid.TruncateID(tag)) { if tag := tagged.Tag(); stringid.IsShortID(stringid.TruncateID(tag)) {
for platform := range daemon.stores { for platform := range daemon.stores {
if id, err := daemon.stores[platform].imageStore.Search(tag); err == nil { if id, err := daemon.stores[platform].imageStore.Search(tag); err == nil {
for _, storeRef := range daemon.stores[platform].referenceStore.References(id.Digest()) { for _, storeRef := range daemon.referenceStore.References(id.Digest()) {
if storeRef.Name() == namedRef.Name() { if storeRef.Name() == namedRef.Name() {
return id, platform, nil return id, platform, nil
} }

View File

@ -70,7 +70,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
return nil, err return nil, err
} }
repoRefs := daemon.stores[platform].referenceStore.References(imgID.Digest()) repoRefs := daemon.referenceStore.References(imgID.Digest())
var removedRepositoryRef bool var removedRepositoryRef bool
if !isImageIDPrefix(imgID.String(), imageRef) { if !isImageIDPrefix(imgID.String(), imageRef) {
@ -104,7 +104,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
daemon.LogImageEvent(imgID.String(), imgID.String(), "untag") daemon.LogImageEvent(imgID.String(), imgID.String(), "untag")
records = append(records, untaggedRecord) records = append(records, untaggedRecord)
repoRefs = daemon.stores[platform].referenceStore.References(imgID.Digest()) repoRefs = daemon.referenceStore.References(imgID.Digest())
// If a tag reference was removed and the only remaining // If a tag reference was removed and the only remaining
// references to the same repository are digest references, // references to the same repository are digest references,
@ -237,7 +237,7 @@ func (daemon *Daemon) removeImageRef(platform string, ref reference.Named) (refe
// Ignore the boolean value returned, as far as we're concerned, this // Ignore the boolean value returned, as far as we're concerned, this
// is an idempotent operation and it's okay if the reference didn't // is an idempotent operation and it's okay if the reference didn't
// exist in the first place. // exist in the first place.
_, err := daemon.stores[platform].referenceStore.Delete(ref) _, err := daemon.referenceStore.Delete(ref)
return ref, err return ref, err
} }
@ -248,7 +248,7 @@ func (daemon *Daemon) removeImageRef(platform string, ref reference.Named) (refe
// daemon's event service. An "Untagged" types.ImageDeleteResponseItem is added to the // daemon's event service. An "Untagged" types.ImageDeleteResponseItem is added to the
// given list of records. // given list of records.
func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, platform string, records *[]types.ImageDeleteResponseItem) error { func (daemon *Daemon) removeAllReferencesToImageID(imgID image.ID, platform string, records *[]types.ImageDeleteResponseItem) error {
imageRefs := daemon.stores[platform].referenceStore.References(imgID.Digest()) imageRefs := daemon.referenceStore.References(imgID.Digest())
for _, imageRef := range imageRefs { for _, imageRef := range imageRefs {
parsedRef, err := daemon.removeImageRef(platform, imageRef) parsedRef, err := daemon.removeImageRef(platform, imageRef)
@ -383,7 +383,7 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, platform string,
} }
// Check if any repository tags/digest reference this image. // Check if any repository tags/digest reference this image.
if mask&conflictActiveReference != 0 && len(daemon.stores[platform].referenceStore.References(imgID.Digest())) > 0 { if mask&conflictActiveReference != 0 && len(daemon.referenceStore.References(imgID.Digest())) > 0 {
return &imageDeleteConflict{ return &imageDeleteConflict{
imgID: imgID, imgID: imgID,
message: "image is referenced in multiple repositories", message: "image is referenced in multiple repositories",
@ -411,5 +411,5 @@ func (daemon *Daemon) checkImageDeleteConflict(imgID image.ID, platform string,
// that there are no repository references to the given image and it has no // that there are no repository references to the given image and it has no
// child images. // child images.
func (daemon *Daemon) imageIsDangling(imgID image.ID, platform string) bool { func (daemon *Daemon) imageIsDangling(imgID image.ID, platform string) bool {
return !(len(daemon.stores[platform].referenceStore.References(imgID.Digest())) > 0 || len(daemon.stores[platform].imageStore.Children(imgID)) > 0) return !(len(daemon.referenceStore.References(imgID.Digest())) > 0 || len(daemon.stores[platform].imageStore.Children(imgID)) > 0)
} }

View File

@ -19,7 +19,7 @@ func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
if system.LCOWSupported() { if system.LCOWSupported() {
platform = "linux" platform = "linux"
} }
imageExporter := tarexport.NewTarExporter(daemon.stores[platform].imageStore, daemon.stores[platform].layerStore, daemon.stores[platform].referenceStore, daemon) imageExporter := tarexport.NewTarExporter(daemon.stores[platform].imageStore, daemon.stores[platform].layerStore, daemon.referenceStore, daemon)
return imageExporter.Save(names, outStream) return imageExporter.Save(names, outStream)
} }
@ -32,6 +32,6 @@ func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet
if system.LCOWSupported() { if system.LCOWSupported() {
platform = "linux" platform = "linux"
} }
imageExporter := tarexport.NewTarExporter(daemon.stores[platform].imageStore, daemon.stores[platform].layerStore, daemon.stores[platform].referenceStore, daemon) imageExporter := tarexport.NewTarExporter(daemon.stores[platform].imageStore, daemon.stores[platform].layerStore, daemon.referenceStore, daemon)
return imageExporter.Load(inTar, outStream, quiet) return imageExporter.Load(inTar, outStream, quiet)
} }

View File

@ -69,7 +69,7 @@ func (daemon *Daemon) ImageHistory(name string) ([]*image.HistoryResponseItem, e
h.ID = id.String() h.ID = id.String()
var tags []string var tags []string
for _, r := range daemon.stores[platform].referenceStore.References(id.Digest()) { for _, r := range daemon.referenceStore.References(id.Digest()) {
if _, ok := r.(reference.NamedTagged); ok { if _, ok := r.(reference.NamedTagged); ok {
tags = append(tags, reference.FamiliarString(r)) tags = append(tags, reference.FamiliarString(r))
} }

View File

@ -24,7 +24,7 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
platform = runtime.GOOS platform = runtime.GOOS
} }
refs := daemon.stores[platform].referenceStore.References(img.ID().Digest()) refs := daemon.referenceStore.References(img.ID().Digest())
repoTags := []string{} repoTags := []string{}
repoDigests := []string{} repoDigests := []string{}
for _, ref := range refs { for _, ref := range refs {

View File

@ -74,7 +74,7 @@ func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.
ImageEventLogger: daemon.LogImageEvent, ImageEventLogger: daemon.LogImageEvent,
MetadataStore: daemon.stores[platform].distributionMetadataStore, MetadataStore: daemon.stores[platform].distributionMetadataStore,
ImageStore: distribution.NewImageConfigStoreFromStore(daemon.stores[platform].imageStore), ImageStore: distribution.NewImageConfigStoreFromStore(daemon.stores[platform].imageStore),
ReferenceStore: daemon.stores[platform].referenceStore, ReferenceStore: daemon.referenceStore,
}, },
DownloadManager: daemon.downloadManager, DownloadManager: daemon.downloadManager,
Schema2Types: distribution.ImageTypes, Schema2Types: distribution.ImageTypes,

View File

@ -56,7 +56,7 @@ func (daemon *Daemon) PushImage(ctx context.Context, image, tag string, metaHead
ImageEventLogger: daemon.LogImageEvent, ImageEventLogger: daemon.LogImageEvent,
MetadataStore: daemon.stores[platform].distributionMetadataStore, MetadataStore: daemon.stores[platform].distributionMetadataStore,
ImageStore: distribution.NewImageConfigStoreFromStore(daemon.stores[platform].imageStore), ImageStore: distribution.NewImageConfigStoreFromStore(daemon.stores[platform].imageStore),
ReferenceStore: daemon.stores[platform].referenceStore, ReferenceStore: daemon.referenceStore,
}, },
ConfigMediaType: schema2.MediaTypeImageConfig, ConfigMediaType: schema2.MediaTypeImageConfig,
LayerStore: distribution.NewLayerProviderFromStore(daemon.stores[platform].layerStore), LayerStore: distribution.NewLayerProviderFromStore(daemon.stores[platform].layerStore),

View File

@ -28,7 +28,7 @@ func (daemon *Daemon) TagImage(imageName, repository, tag string) error {
// TagImageWithReference adds the given reference to the image ID provided. // TagImageWithReference adds the given reference to the image ID provided.
func (daemon *Daemon) TagImageWithReference(imageID image.ID, platform string, newTag reference.Named) error { func (daemon *Daemon) TagImageWithReference(imageID image.ID, platform string, newTag reference.Named) error {
if err := daemon.stores[platform].referenceStore.AddTag(newTag, imageID.Digest(), true); err != nil { if err := daemon.referenceStore.AddTag(newTag, imageID.Digest(), true); err != nil {
return err return err
} }

View File

@ -149,7 +149,7 @@ func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs
newImage := newImage(img, size) newImage := newImage(img, size)
for _, ref := range daemon.stores[platform].referenceStore.References(id.Digest()) { for _, ref := range daemon.referenceStore.References(id.Digest()) {
if imageFilters.Include("reference") { if imageFilters.Include("reference") {
var found bool var found bool
var matchErr error var matchErr error

View File

@ -221,7 +221,7 @@ func (daemon *Daemon) ImagesPrune(ctx context.Context, pruneFilters filters.Args
return nil, ctx.Err() return nil, ctx.Err()
default: default:
dgst := digest.Digest(id) dgst := digest.Digest(id)
if len(daemon.stores[platform].referenceStore.References(dgst)) == 0 && len(daemon.stores[platform].imageStore.Children(id)) != 0 { if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.stores[platform].imageStore.Children(id)) != 0 {
continue continue
} }
if !until.IsZero() && img.Created.After(until) { if !until.IsZero() && img.Created.After(until) {
@ -252,7 +252,7 @@ deleteImagesLoop:
} }
deletedImages := []types.ImageDeleteResponseItem{} deletedImages := []types.ImageDeleteResponseItem{}
refs := daemon.stores[platform].referenceStore.References(dgst) refs := daemon.referenceStore.References(dgst)
if len(refs) > 0 { if len(refs) > 0 {
shouldDelete := !danglingOnly shouldDelete := !danglingOnly
if !shouldDelete { if !shouldDelete {

View File

@ -26,7 +26,7 @@ type Association struct {
ID digest.Digest ID digest.Digest
} }
// Store provides the set of methods which can operate on a tag store. // Store provides the set of methods which can operate on a reference store.
type Store interface { type Store interface {
References(id digest.Digest) []reference.Named References(id digest.Digest) []reference.Named
ReferencesByName(ref reference.Named) []Association ReferencesByName(ref reference.Named) []Association
@ -46,9 +46,6 @@ type store struct {
// referencesByIDCache is a cache of references indexed by ID, to speed // referencesByIDCache is a cache of references indexed by ID, to speed
// up References. // up References.
referencesByIDCache map[digest.Digest]map[string]reference.Named referencesByIDCache map[digest.Digest]map[string]reference.Named
// platform is the container target platform for this store (which may be
// different to the host operating system
platform string
} }
// Repository maps tags to digests. The key is a stringified Reference, // Repository maps tags to digests. The key is a stringified Reference,
@ -73,7 +70,7 @@ func (a lexicalAssociations) Less(i, j int) bool {
// NewReferenceStore creates a new reference store, tied to a file path where // NewReferenceStore creates a new reference store, tied to a file path where
// the set of references are serialized in JSON format. // the set of references are serialized in JSON format.
func NewReferenceStore(jsonPath, platform string) (Store, error) { func NewReferenceStore(jsonPath string) (Store, error) {
abspath, err := filepath.Abs(jsonPath) abspath, err := filepath.Abs(jsonPath)
if err != nil { if err != nil {
return nil, err return nil, err
@ -83,7 +80,6 @@ func NewReferenceStore(jsonPath, platform string) (Store, error) {
jsonPath: abspath, jsonPath: abspath,
Repositories: make(map[string]repository), Repositories: make(map[string]repository),
referencesByIDCache: make(map[digest.Digest]map[string]reference.Named), referencesByIDCache: make(map[digest.Digest]map[string]reference.Named),
platform: platform,
} }
// Load the json file if it exists, otherwise create it. // Load the json file if it exists, otherwise create it.
if err := store.reload(); os.IsNotExist(err) { if err := store.reload(); os.IsNotExist(err) {

View File

@ -5,7 +5,6 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"testing" "testing"
@ -41,7 +40,7 @@ func TestLoad(t *testing.T) {
} }
jsonFile.Close() jsonFile.Close()
store, err := NewReferenceStore(jsonFile.Name(), runtime.GOOS) store, err := NewReferenceStore(jsonFile.Name())
if err != nil { if err != nil {
t.Fatalf("error creating tag store: %v", err) t.Fatalf("error creating tag store: %v", err)
} }
@ -70,7 +69,7 @@ func TestSave(t *testing.T) {
jsonFile.Close() jsonFile.Close()
defer os.RemoveAll(jsonFile.Name()) defer os.RemoveAll(jsonFile.Name())
store, err := NewReferenceStore(jsonFile.Name(), runtime.GOOS) store, err := NewReferenceStore(jsonFile.Name())
if err != nil { if err != nil {
t.Fatalf("error creating tag store: %v", err) t.Fatalf("error creating tag store: %v", err)
} }
@ -112,7 +111,7 @@ func TestAddDeleteGet(t *testing.T) {
jsonFile.Close() jsonFile.Close()
defer os.RemoveAll(jsonFile.Name()) defer os.RemoveAll(jsonFile.Name())
store, err := NewReferenceStore(jsonFile.Name(), runtime.GOOS) store, err := NewReferenceStore(jsonFile.Name())
if err != nil { if err != nil {
t.Fatalf("error creating tag store: %v", err) t.Fatalf("error creating tag store: %v", err)
} }
@ -329,7 +328,7 @@ func TestInvalidTags(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "tag-store-test") tmpDir, err := ioutil.TempDir("", "tag-store-test")
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
store, err := NewReferenceStore(filepath.Join(tmpDir, "repositories.json"), runtime.GOOS) store, err := NewReferenceStore(filepath.Join(tmpDir, "repositories.json"))
if err != nil { if err != nil {
t.Fatalf("error creating tag store: %v", err) t.Fatalf("error creating tag store: %v", err)
} }