Merge pull request #375 from yanxuean/image-trunc

support get image by truncindex
This commit is contained in:
Lantao Liu 2017-11-27 11:36:58 -08:00 committed by GitHub
commit 1b05f088b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 85 additions and 60 deletions

View File

@ -30,6 +30,7 @@ import (
func getContainerStatusTestData() (*containerstore.Metadata, *containerstore.Status, func getContainerStatusTestData() (*containerstore.Metadata, *containerstore.Status,
*imagestore.Image, *runtime.ContainerStatus) { *imagestore.Image, *runtime.ContainerStatus) {
imageID := "sha256:1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
testID := "test-id" testID := "test-id"
config := &runtime.ContainerConfig{ config := &runtime.ContainerConfig{
Metadata: &runtime.ContainerMetadata{ Metadata: &runtime.ContainerMetadata{
@ -53,7 +54,7 @@ func getContainerStatusTestData() (*containerstore.Metadata, *containerstore.Sta
Name: "test-long-name", Name: "test-long-name",
SandboxID: "test-sandbox-id", SandboxID: "test-sandbox-id",
Config: config, Config: config,
ImageRef: "test-image-id", ImageRef: imageID,
LogPath: "test-log-path", LogPath: "test-log-path",
} }
status := &containerstore.Status{ status := &containerstore.Status{
@ -62,7 +63,7 @@ func getContainerStatusTestData() (*containerstore.Metadata, *containerstore.Sta
StartedAt: startedAt, StartedAt: startedAt,
} }
image := &imagestore.Image{ image := &imagestore.Image{
ID: "test-image-id", ID: imageID,
RepoTags: []string{"test-image-repo-tag"}, RepoTags: []string{"test-image-repo-tag"},
RepoDigests: []string{"test-image-repo-digest"}, RepoDigests: []string{"test-image-repo-digest"},
} }

View File

@ -28,7 +28,6 @@ import (
"github.com/containerd/cgroups" "github.com/containerd/cgroups"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
imagedigest "github.com/opencontainers/go-digest" imagedigest "github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/identity" "github.com/opencontainers/image-spec/identity"
@ -205,34 +204,41 @@ func getRepoDigestAndTag(namedRef reference.Named, digest imagedigest.Digest, sc
// localResolve resolves image reference locally and returns corresponding image metadata. It returns // localResolve resolves image reference locally and returns corresponding image metadata. It returns
// nil without error if the reference doesn't exist. // nil without error if the reference doesn't exist.
func (c *criContainerdService) localResolve(ctx context.Context, ref string) (*imagestore.Image, error) { func (c *criContainerdService) localResolve(ctx context.Context, refOrID string) (*imagestore.Image, error) {
_, err := imagedigest.Parse(ref) getImageID := func(refOrId string) string {
if err != nil { if _, err := imagedigest.Parse(refOrID); err == nil {
return refOrID
}
return func(ref string) string {
// ref is not image id, try to resolve it locally. // ref is not image id, try to resolve it locally.
normalized, err := util.NormalizeImageRef(ref) normalized, err := util.NormalizeImageRef(ref)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid image reference %q: %v", ref, err) return ""
} }
image, err := c.client.GetImage(ctx, normalized.String()) image, err := c.client.GetImage(ctx, normalized.String())
if err != nil { if err != nil {
if errdefs.IsNotFound(err) { return ""
return nil, nil
}
return nil, fmt.Errorf("failed to get image from containerd: %v", err)
} }
desc, err := image.Config(ctx) desc, err := image.Config(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get image config descriptor: %v", err) return ""
} }
ref = desc.Digest.String() return desc.Digest.String()
}(refOrID)
}
imageID := getImageID(refOrID)
if imageID == "" {
// Try to treat ref as imageID
imageID = refOrID
} }
imageID := ref
image, err := c.imageStore.Get(imageID) image, err := c.imageStore.Get(imageID)
if err != nil { if err != nil {
if err == store.ErrNotExist { if err == store.ErrNotExist {
return nil, nil return nil, nil
} }
return nil, fmt.Errorf("failed to get image %q metadata: %v", imageID, err) return nil, fmt.Errorf("failed to get image %q : %v", imageID, err)
} }
return &image, nil return &image, nil
} }

View File

@ -32,7 +32,7 @@ func TestListImages(t *testing.T) {
c := newTestCRIContainerdService() c := newTestCRIContainerdService()
imagesInStore := []imagestore.Image{ imagesInStore := []imagestore.Image{
{ {
ID: "test-id-1", ID: "sha256:1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
ChainID: "test-chainid-1", ChainID: "test-chainid-1",
RepoTags: []string{"tag-a-1", "tag-b-1"}, RepoTags: []string{"tag-a-1", "tag-b-1"},
RepoDigests: []string{"digest-a-1", "digest-b-1"}, RepoDigests: []string{"digest-a-1", "digest-b-1"},
@ -42,7 +42,7 @@ func TestListImages(t *testing.T) {
}, },
}, },
{ {
ID: "test-id-2", ID: "sha256:2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
ChainID: "test-chainid-2", ChainID: "test-chainid-2",
RepoTags: []string{"tag-a-2", "tag-b-2"}, RepoTags: []string{"tag-a-2", "tag-b-2"},
RepoDigests: []string{"digest-a-2", "digest-b-2"}, RepoDigests: []string{"digest-a-2", "digest-b-2"},
@ -52,7 +52,7 @@ func TestListImages(t *testing.T) {
}, },
}, },
{ {
ID: "test-id-3", ID: "sha256:3123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
ChainID: "test-chainid-3", ChainID: "test-chainid-3",
RepoTags: []string{"tag-a-3", "tag-b-3"}, RepoTags: []string{"tag-a-3", "tag-b-3"},
RepoDigests: []string{"digest-a-3", "digest-b-3"}, RepoDigests: []string{"digest-a-3", "digest-b-3"},
@ -64,21 +64,21 @@ func TestListImages(t *testing.T) {
} }
expect := []*runtime.Image{ expect := []*runtime.Image{
{ {
Id: "test-id-1", Id: "sha256:1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
RepoTags: []string{"tag-a-1", "tag-b-1"}, RepoTags: []string{"tag-a-1", "tag-b-1"},
RepoDigests: []string{"digest-a-1", "digest-b-1"}, RepoDigests: []string{"digest-a-1", "digest-b-1"},
Size_: uint64(1000), Size_: uint64(1000),
Username: "root", Username: "root",
}, },
{ {
Id: "test-id-2", Id: "sha256:2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
RepoTags: []string{"tag-a-2", "tag-b-2"}, RepoTags: []string{"tag-a-2", "tag-b-2"},
RepoDigests: []string{"digest-a-2", "digest-b-2"}, RepoDigests: []string{"digest-a-2", "digest-b-2"},
Size_: uint64(2000), Size_: uint64(2000),
Uid: &runtime.Int64Value{Value: 1234}, Uid: &runtime.Int64Value{Value: 1234},
}, },
{ {
Id: "test-id-3", Id: "sha256:3123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
RepoTags: []string{"tag-a-3", "tag-b-3"}, RepoTags: []string{"tag-a-3", "tag-b-3"},
RepoDigests: []string{"digest-a-3", "digest-b-3"}, RepoDigests: []string{"digest-a-3", "digest-b-3"},
Size_: uint64(3000), Size_: uint64(3000),

View File

@ -20,7 +20,8 @@ import (
"sync" "sync"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/docker/docker/pkg/truncindex" "github.com/docker/distribution/digestset"
godigest "github.com/opencontainers/go-digest"
imagespec "github.com/opencontainers/image-spec/specs-go/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/kubernetes-incubator/cri-containerd/pkg/store" "github.com/kubernetes-incubator/cri-containerd/pkg/store"
@ -49,14 +50,14 @@ type Image struct {
type Store struct { type Store struct {
lock sync.RWMutex lock sync.RWMutex
images map[string]Image images map[string]Image
idIndex *truncindex.TruncIndex digestSet *digestset.Set
} }
// NewStore creates an image store. // NewStore creates an image store.
func NewStore() *Store { func NewStore() *Store {
return &Store{ return &Store{
images: make(map[string]Image), images: make(map[string]Image),
idIndex: truncindex.NewTruncIndex([]string{}), digestSet: digestset.NewSet(),
} }
} }
@ -64,11 +65,11 @@ func NewStore() *Store {
func (s *Store) Add(img Image) error { func (s *Store) Add(img Image) error {
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock() defer s.lock.Unlock()
if _, err := s.idIndex.Get(img.ID); err != nil { if _, err := s.digestSet.Lookup(img.ID); err != nil {
if err != truncindex.ErrNotExist { if err != digestset.ErrDigestNotFound {
return err return err
} }
if err := s.idIndex.Add(img.ID); err != nil { if err := s.digestSet.Add(godigest.Digest(img.ID)); err != nil {
return err return err
} }
} }
@ -91,14 +92,14 @@ func (s *Store) Add(img Image) error {
func (s *Store) Get(id string) (Image, error) { func (s *Store) Get(id string) (Image, error) {
s.lock.RLock() s.lock.RLock()
defer s.lock.RUnlock() defer s.lock.RUnlock()
id, err := s.idIndex.Get(id) digest, err := s.digestSet.Lookup(id)
if err != nil { if err != nil {
if err == truncindex.ErrNotExist { if err == digestset.ErrDigestNotFound {
err = store.ErrNotExist err = store.ErrNotExist
} }
return Image{}, err return Image{}, err
} }
if i, ok := s.images[id]; ok { if i, ok := s.images[digest.String()]; ok {
return i, nil return i, nil
} }
return Image{}, store.ErrNotExist return Image{}, store.ErrNotExist
@ -119,14 +120,14 @@ func (s *Store) List() []Image {
func (s *Store) Delete(id string) { func (s *Store) Delete(id string) {
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock() defer s.lock.Unlock()
id, err := s.idIndex.Get(id) digest, err := s.digestSet.Lookup(id)
if err != nil { if err != nil {
// Note: The idIndex.Delete and delete doesn't handle truncated index. // Note: The idIndex.Delete and delete doesn't handle truncated index.
// So we need to return if there are error. // So we need to return if there are error.
return return
} }
s.idIndex.Delete(id) // nolint: errcheck s.digestSet.Remove(digest) // nolint: errcheck
delete(s.images, id) delete(s.images, digest.String())
} }
// mergeStringSlices merges 2 string slices into one and remove duplicated elements. // mergeStringSlices merges 2 string slices into one and remove duplicated elements.

View File

@ -17,6 +17,7 @@ limitations under the License.
package image package image
import ( import (
"strings"
"testing" "testing"
imagespec "github.com/opencontainers/image-spec/specs-go/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1"
@ -26,33 +27,33 @@ import (
) )
func TestImageStore(t *testing.T) { func TestImageStore(t *testing.T) {
images := map[string]Image{ images := []Image{
"1": { {
ID: "1", ID: "sha256:1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
ChainID: "test-chain-id-1", ChainID: "test-chain-id-1",
RepoTags: []string{"tag-1"}, RepoTags: []string{"tag-1"},
RepoDigests: []string{"digest-1"}, RepoDigests: []string{"digest-1"},
Size: 10, Size: 10,
Config: &imagespec.ImageConfig{}, Config: &imagespec.ImageConfig{},
}, },
"2abcd": { {
ID: "2abcd", ID: "sha256:2123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
ChainID: "test-chain-id-2abcd", ChainID: "test-chain-id-2abcd",
RepoTags: []string{"tag-2abcd"}, RepoTags: []string{"tag-2abcd"},
RepoDigests: []string{"digest-2abcd"}, RepoDigests: []string{"digest-2abcd"},
Size: 20, Size: 20,
Config: &imagespec.ImageConfig{}, Config: &imagespec.ImageConfig{},
}, },
"4a333": { {
ID: "4a333", ID: "sha256:3123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
RepoTags: []string{"tag-4a333"}, RepoTags: []string{"tag-4a333"},
RepoDigests: []string{"digest-4a333"}, RepoDigests: []string{"digest-4a333"},
ChainID: "test-chain-id-4a333", ChainID: "test-chain-id-4a333",
Size: 30, Size: 30,
Config: &imagespec.ImageConfig{}, Config: &imagespec.ImageConfig{},
}, },
"4abcd": { {
ID: "4abcd", ID: "sha256:4123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
RepoTags: []string{"tag-4abcd"}, RepoTags: []string{"tag-4abcd"},
RepoDigests: []string{"digest-4abcd"}, RepoDigests: []string{"digest-4abcd"},
ChainID: "test-chain-id-4abcd", ChainID: "test-chain-id-4abcd",
@ -61,6 +62,7 @@ func TestImageStore(t *testing.T) {
}, },
} }
assert := assertlib.New(t) assert := assertlib.New(t)
genTruncIndex := func(normalName string) string { return normalName[:(len(normalName)+1)/2] }
s := NewStore() s := NewStore()
@ -71,11 +73,26 @@ func TestImageStore(t *testing.T) {
} }
t.Logf("should be able to get image") t.Logf("should be able to get image")
genTruncIndex := func(normalName string) string { return normalName[:(len(normalName)+1)/2] } for _, v := range images {
for id, img := range images { truncID := genTruncIndex(v.ID)
got, err := s.Get(genTruncIndex(id)) got, err := s.Get(truncID)
assert.NoError(err) assert.NoError(err, "truncID:%s, fullID:%s", truncID, v.ID)
assert.Equal(img, got) assert.Equal(v, got)
}
t.Logf("should be able to get image by truncated imageId without algorithm")
for _, v := range images {
truncID := genTruncIndex(v.ID[strings.Index(v.ID, ":")+1:])
got, err := s.Get(truncID)
assert.NoError(err, "truncID:%s, fullID:%s", truncID, v.ID)
assert.Equal(v, got)
}
t.Logf("should not be able to get image by ambiguous prefix")
ambiguousPrefixs := []string{"sha256", "sha256:"}
for _, v := range ambiguousPrefixs {
_, err := s.Get(v)
assert.NotEqual(nil, err)
} }
t.Logf("should be able to list images") t.Logf("should be able to list images")
@ -83,8 +100,8 @@ func TestImageStore(t *testing.T) {
assert.Len(imgs, len(images)) assert.Len(imgs, len(images))
imageNum := len(images) imageNum := len(images)
for testID, v := range images { for _, v := range images {
truncID := genTruncIndex(testID) truncID := genTruncIndex(v.ID)
oldRepoTag := v.RepoTags[0] oldRepoTag := v.RepoTags[0]
oldRepoDigest := v.RepoDigests[0] oldRepoDigest := v.RepoDigests[0]
newRepoTag := oldRepoTag + "new" newRepoTag := oldRepoTag + "new"