diff --git a/pkg/metadata/image_metadata_test.go b/pkg/metadata/image_metadata_test.go index aa1be645c..c86334a13 100644 --- a/pkg/metadata/image_metadata_test.go +++ b/pkg/metadata/image_metadata_test.go @@ -19,6 +19,7 @@ package metadata import ( "testing" + imagespec "github.com/opencontainers/image-spec/specs-go/v1" assertlib "github.com/stretchr/testify/assert" "github.com/kubernetes-incubator/cri-containerd/pkg/metadata/store" @@ -27,16 +28,28 @@ import ( func TestImageMetadataStore(t *testing.T) { imageMetadataMap := map[string]*ImageMetadata{ "1": { - ID: "1", - Size: 10, + ID: "1", + ChainID: "test-chain-id-1", + RepoTags: []string{"tag-1"}, + RepoDigests: []string{"digest-1"}, + Size: 10, + Config: &imagespec.ImageConfig{}, }, "2": { - ID: "2", - Size: 20, + ID: "2", + ChainID: "test-chain-id-2", + RepoTags: []string{"tag-2"}, + RepoDigests: []string{"digest-2"}, + Size: 20, + Config: &imagespec.ImageConfig{}, }, "3": { - ID: "3", - Size: 30, + ID: "3", + RepoTags: []string{"tag-3"}, + RepoDigests: []string{"digest-3"}, + ChainID: "test-chain-id-3", + Size: 30, + Config: &imagespec.ImageConfig{}, }, } assert := assertlib.New(t) diff --git a/pkg/server/helpers_test.go b/pkg/server/helpers_test.go index e5ae6ad55..39b09bbaa 100644 --- a/pkg/server/helpers_test.go +++ b/pkg/server/helpers_test.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/containerd/containerd/reference" + imagedigest "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" "golang.org/x/net/context" @@ -162,28 +163,67 @@ func TestGetSandbox(t *testing.T) { } func TestNormalizeImageRef(t *testing.T) { - for _, ref := range []string{ - "busybox", // has nothing - "busybox:latest", // only has tag - "busybox@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582", // only has digest - "library/busybox", // only has path - "docker.io/busybox", // only has hostname - "docker.io/library/busybox", // has no tag - "docker.io/busybox:latest", // has no path - "library/busybox:latest", // has no hostname - "docker.io/library/busybox:latest", // full reference - "gcr.io/library/busybox", // gcr reference + for _, test := range []struct { + input string + expect string + }{ + { // has nothing + input: "busybox", + expect: "docker.io/library/busybox:latest", + }, + { // only has tag + input: "busybox:latest", + expect: "docker.io/library/busybox:latest", + }, + { // only has digest + input: "busybox@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582", + expect: "docker.io/library/busybox@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582", + }, + { // only has path + input: "library/busybox", + expect: "docker.io/library/busybox:latest", + }, + { // only has hostname + input: "docker.io/busybox", + expect: "docker.io/library/busybox:latest", + }, + { // has no tag + input: "docker.io/library/busybox", + expect: "docker.io/library/busybox:latest", + }, + { // has no path + input: "docker.io/busybox:latest", + expect: "docker.io/library/busybox:latest", + }, + { // has no hostname + input: "library/busybox:latest", + expect: "docker.io/library/busybox:latest", + }, + { // full reference + input: "docker.io/library/busybox:latest", + expect: "docker.io/library/busybox:latest", + }, + { // gcr reference + input: "gcr.io/library/busybox", + expect: "gcr.io/library/busybox:latest", + }, + { // both tag and digest + input: "gcr.io/library/busybox:latest@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582", + expect: "gcr.io/library/busybox@sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582", + }, } { - t.Logf("TestCase %q", ref) - normalized, err := normalizeImageRef(ref) + t.Logf("TestCase %q", test.input) + normalized, err := normalizeImageRef(test.input) assert.NoError(t, err) - _, err = reference.Parse(normalized.String()) - assert.NoError(t, err, "%q should be containerd supported reference", normalized) + output := normalized.String() + assert.Equal(t, test.expect, output) + _, err = reference.Parse(output) + assert.NoError(t, err, "%q should be containerd supported reference", output) } } -// TestGetUserFromImageUser tests the logic of getting image uid or user name of image user. -func TestGetUserFromImageUser(t *testing.T) { +// TestGetUserFromImage tests the logic of getting image uid or user name of image user. +func TestGetUserFromImage(t *testing.T) { newI64 := func(i int64) *int64 { return &i } for c, test := range map[string]struct { user string @@ -215,8 +255,34 @@ func TestGetUserFromImageUser(t *testing.T) { }, } { t.Logf("TestCase - %q", c) - actualUID, actualName := getUserFromImageUser(test.user) + actualUID, actualName := getUserFromImage(test.user) assert.Equal(t, test.uid, actualUID) assert.Equal(t, test.name, actualName) } } + +func TestGetRepoDigestAndTag(t *testing.T) { + digest := imagedigest.Digest("sha256:e6693c20186f837fc393390135d8a598a96a833917917789d63766cab6c59582") + for desc, test := range map[string]struct { + ref string + expectedRepoDigest string + expectedRepoTag string + }{ + "repo tag should be empty if original ref has no tag": { + ref: "gcr.io/library/busybox@" + digest.String(), + expectedRepoDigest: "gcr.io/library/busybox@" + digest.String(), + }, + "repo tag should not be empty if original ref has tag": { + ref: "gcr.io/library/busybox:latest", + expectedRepoDigest: "gcr.io/library/busybox@" + digest.String(), + expectedRepoTag: "gcr.io/library/busybox:latest", + }, + } { + t.Logf("TestCase %q", desc) + named, err := normalizeImageRef(test.ref) + assert.NoError(t, err) + repoDigest, repoTag := getRepoDigestAndTag(named, digest) + assert.Equal(t, test.expectedRepoDigest, repoDigest) + assert.Equal(t, test.expectedRepoTag, repoTag) + } +} diff --git a/pkg/server/image_list_test.go b/pkg/server/image_list_test.go new file mode 100644 index 000000000..3ba2eaee6 --- /dev/null +++ b/pkg/server/image_list_test.go @@ -0,0 +1,101 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package server + +import ( + "testing" + + imagespec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" + + "github.com/kubernetes-incubator/cri-containerd/pkg/metadata" +) + +func TestListImages(t *testing.T) { + c := newTestCRIContainerdService() + imagesInStore := []metadata.ImageMetadata{ + { + ID: "test-id-1", + ChainID: "test-chainid-1", + RepoTags: []string{"tag-a-1", "tag-b-1"}, + RepoDigests: []string{"digest-a-1", "digest-b-1"}, + Size: 1000, + Config: &imagespec.ImageConfig{ + User: "root", + }, + }, + { + ID: "test-id-2", + ChainID: "test-chainid-2", + RepoTags: []string{"tag-a-2", "tag-b-2"}, + RepoDigests: []string{"digest-a-2", "digest-b-2"}, + Size: 2000, + Config: &imagespec.ImageConfig{ + User: "1234:1234", + }, + }, + { + ID: "test-id-3", + ChainID: "test-chainid-3", + RepoTags: []string{"tag-a-3", "tag-b-3"}, + RepoDigests: []string{"digest-a-3", "digest-b-3"}, + Size: 3000, + Config: &imagespec.ImageConfig{ + User: "nobody", + }, + }, + } + expect := []*runtime.Image{ + { + Id: "test-id-1", + RepoTags: []string{"tag-a-1", "tag-b-1"}, + RepoDigests: []string{"digest-a-1", "digest-b-1"}, + Size_: uint64(1000), + Username: "root", + }, + { + Id: "test-id-2", + RepoTags: []string{"tag-a-2", "tag-b-2"}, + RepoDigests: []string{"digest-a-2", "digest-b-2"}, + Size_: uint64(2000), + Uid: &runtime.Int64Value{Value: 1234}, + }, + { + Id: "test-id-3", + RepoTags: []string{"tag-a-3", "tag-b-3"}, + RepoDigests: []string{"digest-a-3", "digest-b-3"}, + Size_: uint64(3000), + Username: "nobody", + }, + } + + for _, i := range imagesInStore { + assert.NoError(t, c.imageMetadataStore.Create(i)) + } + + resp, err := c.ListImages(context.Background(), &runtime.ListImagesRequest{}) + assert.NoError(t, err) + require.NotNil(t, resp) + images := resp.GetImages() + assert.Len(t, images, len(expect)) + for _, i := range expect { + assert.Contains(t, images, i) + } +} diff --git a/pkg/server/image_pull_test.go b/pkg/server/image_pull_test.go new file mode 100644 index 000000000..b7f1e242f --- /dev/null +++ b/pkg/server/image_pull_test.go @@ -0,0 +1,74 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package server + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/kubernetes-incubator/cri-containerd/pkg/metadata" +) + +func TestUpdateImageMetadata(t *testing.T) { + meta := metadata.ImageMetadata{ + ID: "test-id", + ChainID: "test-chain-id", + Size: 1234, + } + for desc, test := range map[string]struct { + repoTags []string + repoDigests []string + repoTag string + repoDigest string + expectedRepoTags []string + expectedRepoDigests []string + }{ + "Add duplicated repo tag and digest": { + repoTags: []string{"a", "b"}, + repoDigests: []string{"c", "d"}, + repoTag: "a", + repoDigest: "c", + expectedRepoTags: []string{"a", "b"}, + expectedRepoDigests: []string{"c", "d"}, + }, + "Add new repo tag and digest": { + repoTags: []string{"a", "b"}, + repoDigests: []string{"c", "d"}, + repoTag: "e", + repoDigest: "f", + expectedRepoTags: []string{"a", "b", "e"}, + expectedRepoDigests: []string{"c", "d", "f"}, + }, + "Add empty repo tag and digest": { + repoTags: []string{"a", "b"}, + repoDigests: []string{"c", "d"}, + repoTag: "", + repoDigest: "", + expectedRepoTags: []string{"a", "b"}, + expectedRepoDigests: []string{"c", "d"}, + }, + } { + t.Logf("TestCase %q", desc) + m := meta + m.RepoTags = test.repoTags + m.RepoDigests = test.repoDigests + updateImageMetadata(&m, test.repoTag, test.repoDigest) + assert.Equal(t, test.expectedRepoTags, m.RepoTags) + assert.Equal(t, test.expectedRepoDigests, m.RepoDigests) + } +} diff --git a/pkg/server/image_status_test.go b/pkg/server/image_status_test.go new file mode 100644 index 000000000..cbf7fc99b --- /dev/null +++ b/pkg/server/image_status_test.go @@ -0,0 +1,69 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package server + +import ( + "testing" + + imagespec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1" + + "github.com/kubernetes-incubator/cri-containerd/pkg/metadata" +) + +func TestImageStatus(t *testing.T) { + testID := "sha256:d848ce12891bf78792cda4a23c58984033b0c397a55e93a1556202222ecc5ed4" + meta := metadata.ImageMetadata{ + ID: testID, + ChainID: "test-chain-id", + RepoTags: []string{"a", "b"}, + RepoDigests: []string{"c", "d"}, + Size: 1234, + Config: &imagespec.ImageConfig{ + User: "user:group", + }, + } + expected := &runtime.Image{ + Id: testID, + RepoTags: []string{"a", "b"}, + RepoDigests: []string{"c", "d"}, + Size_: uint64(1234), + Username: "user", + } + + c := newTestCRIContainerdService() + t.Logf("should return nil image spec without error for non-exist image") + resp, err := c.ImageStatus(context.Background(), &runtime.ImageStatusRequest{ + Image: &runtime.ImageSpec{Image: testID}, + }) + assert.NoError(t, err) + require.NotNil(t, resp) + assert.Nil(t, resp.GetImage()) + + assert.NoError(t, c.imageMetadataStore.Create(meta)) + + t.Logf("should return correct image status for exist image") + resp, err = c.ImageStatus(context.Background(), &runtime.ImageStatusRequest{ + Image: &runtime.ImageSpec{Image: testID}, + }) + assert.NoError(t, err) + assert.NotNil(t, resp) + assert.Equal(t, expected, resp.GetImage()) +} diff --git a/pkg/server/service_test.go b/pkg/server/service_test.go index 16c503c01..5b6bffcae 100644 --- a/pkg/server/service_test.go +++ b/pkg/server/service_test.go @@ -52,6 +52,7 @@ func newTestCRIContainerdService() *criContainerdService { rootDir: testRootDir, containerService: servertesting.NewFakeExecutionClient(), sandboxStore: metadata.NewSandboxStore(store.NewMetadataStore()), + imageMetadataStore: metadata.NewImageMetadataStore(store.NewMetadataStore()), sandboxNameIndex: registrar.NewRegistrar(), sandboxIDIndex: truncindex.NewTruncIndex(nil), containerStore: metadata.NewContainerStore(store.NewMetadataStore()),