diff --git a/pkg/metadata/sandbox_test.go b/pkg/metadata/sandbox_test.go new file mode 100644 index 000000000..8eb1cab86 --- /dev/null +++ b/pkg/metadata/sandbox_test.go @@ -0,0 +1,120 @@ +/* +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 metadata + +import ( + "testing" + "time" + + assertlib "github.com/stretchr/testify/assert" + + "github.com/kubernetes-incubator/cri-containerd/pkg/metadata/store" + + "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" +) + +func TestSandboxStore(t *testing.T) { + sandboxes := map[string]*SandboxMetadata{ + "1": { + ID: "1", + Name: "Sandbox-1", + Config: &runtime.PodSandboxConfig{ + Metadata: &runtime.PodSandboxMetadata{ + Name: "TestPod-1", + Uid: "TestUid-1", + Namespace: "TestNamespace-1", + Attempt: 1, + }, + }, + CreatedAt: time.Now().UnixNano(), + NetNS: "TestNetNS-1", + }, + "2": { + ID: "2", + Name: "Sandbox-2", + Config: &runtime.PodSandboxConfig{ + Metadata: &runtime.PodSandboxMetadata{ + Name: "TestPod-2", + Uid: "TestUid-2", + Namespace: "TestNamespace-2", + Attempt: 2, + }, + }, + CreatedAt: time.Now().UnixNano(), + NetNS: "TestNetNS-2", + }, + "3": { + ID: "3", + Name: "Sandbox-3", + Config: &runtime.PodSandboxConfig{ + Metadata: &runtime.PodSandboxMetadata{ + Name: "TestPod-3", + Uid: "TestUid-3", + Namespace: "TestNamespace-3", + Attempt: 3, + }, + }, + CreatedAt: time.Now().UnixNano(), + NetNS: "TestNetNS-3", + }, + } + assert := assertlib.New(t) + + s := NewSandboxStore(store.NewMetadataStore()) + + t.Logf("should be able to create sandbox metadata") + for _, meta := range sandboxes { + assert.NoError(s.Create(*meta)) + } + + t.Logf("should be able to get sandbox metadata") + for id, expectMeta := range sandboxes { + meta, err := s.Get(id) + assert.NoError(err) + assert.Equal(expectMeta, meta) + } + + t.Logf("should be able to list sandbox metadata") + sbs, err := s.List() + assert.NoError(err) + assert.Len(sbs, 3) + + t.Logf("should be able to update sandbox metadata") + testID := "2" + newCreatedAt := time.Now().UnixNano() + expectMeta := *sandboxes[testID] + expectMeta.CreatedAt = newCreatedAt + err = s.Update(testID, func(o SandboxMetadata) (SandboxMetadata, error) { + o.CreatedAt = newCreatedAt + return o, nil + }) + assert.NoError(err) + newMeta, err := s.Get(testID) + assert.NoError(err) + assert.Equal(&expectMeta, newMeta) + + t.Logf("should be able to delete sandbox metadata") + assert.NoError(s.Delete(testID)) + sbs, err = s.List() + assert.NoError(err) + assert.Len(sbs, 2) + + t.Logf("get should return nil without error after deletion") + meta, err := s.Get(testID) + assert.NoError(err) + assert.Nil(meta) +} diff --git a/pkg/metadata/store/metadata_store_test.go b/pkg/metadata/store/metadata_store_test.go new file mode 100644 index 000000000..2b8491dac --- /dev/null +++ b/pkg/metadata/store/metadata_store_test.go @@ -0,0 +1,197 @@ +/* +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 store + +import ( + "bytes" + "errors" + "fmt" + "sync" + "testing" + + assertlib "github.com/stretchr/testify/assert" +) + +func TestMetadata(t *testing.T) { + testData := [][]byte{ + []byte("test-data-1"), + []byte("test-data-2"), + } + updateErr := errors.New("update error") + assert := assertlib.New(t) + + t.Logf("simple create and get") + meta, err := newMetadata(testData[0]) + assert.NoError(err) + old := meta.get() + assert.Equal(testData[0], old) + + t.Logf("failed update should not take effect") + err = meta.update(func(in []byte) ([]byte, error) { + return testData[1], updateErr + }) + assert.Equal(updateErr, err) + assert.Equal(testData[0], meta.get()) + + t.Logf("successful update should take effect") + err = meta.update(func(in []byte) ([]byte, error) { + return testData[1], nil + }) + assert.NoError(err) + assert.Equal(testData[1], meta.get()) + + t.Logf("successful update should not affect existing snapshot") + assert.Equal(testData[0], old) + + // TODO(random-liu): Test deleteCheckpoint and cleanupCheckpoint after + // disk based implementation is added. +} + +func TestMetadataStore(t *testing.T) { + testIds := []string{"id-0", "id-1"} + testMeta := map[string][]byte{ + testIds[0]: []byte("metadata-0"), + testIds[1]: []byte("metadata-1"), + } + assert := assertlib.New(t) + + m := NewMetadataStore() + + t.Logf("should be empty initially") + metas, err := m.List() + assert.NoError(err) + assert.Empty(metas) + + t.Logf("should be able to create metadata") + err = m.Create(testIds[0], testMeta[testIds[0]]) + assert.NoError(err) + + t.Logf("should not be able to create metadata with the same id") + err = m.Create(testIds[0], testMeta[testIds[0]]) + assert.Error(err) + + t.Logf("should be able to list metadata") + err = m.Create(testIds[1], testMeta[testIds[1]]) + assert.NoError(err) + metas, err = m.List() + assert.NoError(err) + assert.True(sliceContainsMap(metas, testMeta)) + + t.Logf("should be able to get metadata by id") + meta, err := m.Get(testIds[1]) + assert.NoError(err) + assert.Equal(testMeta[testIds[1]], meta) + + t.Logf("update should take effect") + m.Update(testIds[1], func(in []byte) ([]byte, error) { + return []byte("updated-metadata-1"), nil + }) + newMeta, err := m.Get(testIds[1]) + assert.NoError(err) + assert.Equal([]byte("updated-metadata-1"), newMeta) + + t.Logf("should be able to delete metadata") + assert.NoError(m.Delete(testIds[1])) + metas, err = m.List() + assert.NoError(err) + assert.Len(metas, 1) + assert.Equal(testMeta[testIds[0]], metas[0]) + meta, err = m.Get(testIds[1]) + assert.NoError(err) + assert.Nil(meta) + + t.Logf("existing reference should not be affected by delete") + assert.Equal([]byte("updated-metadata-1"), newMeta) + + t.Logf("should be able to reuse the same id after deletion") + err = m.Create(testIds[1], testMeta[testIds[1]]) + assert.NoError(err) +} + +// sliceMatchMap checks the same elements with a map. +func sliceContainsMap(s [][]byte, m map[string][]byte) bool { + if len(m) != len(s) { + return false + } + for _, expect := range m { + found := false + for _, got := range s { + if bytes.Equal(expect, got) { + found = true + break + } + } + if !found { + return false + } + } + return true +} + +func TestMultithreadAccess(t *testing.T) { + m := NewMetadataStore() + assert := assertlib.New(t) + routineNum := 10 + var wg sync.WaitGroup + for i := 0; i < routineNum; i++ { + wg.Add(1) + go func(i int) { + id := fmt.Sprintf("%d", i) + + t.Logf("should be able to create id %q", id) + expect := []byte(id) + err := m.Create(id, expect) + assert.NoError(err) + + got, err := m.Get(id) + assert.NoError(err) + assert.Equal(expect, got) + + gotList, err := m.List() + assert.NoError(err) + assert.Contains(gotList, expect) + + t.Logf("should be able to update id %q", id) + expect = []byte("update-" + id) + err = m.Update(id, func([]byte) ([]byte, error) { + return expect, nil + }) + assert.NoError(err) + + got, err = m.Get(id) + assert.NoError(err) + assert.Equal(expect, got) + + t.Logf("should be able to delete id %q", id) + err = m.Delete(id) + assert.NoError(err) + + got, err = m.Get(id) + assert.NoError(err) + assert.Nil(got) + + gotList, err = m.List() + assert.NoError(err) + assert.NotContains(gotList, expect) + + wg.Done() + }(i) + } + wg.Wait() +} + +// TODO(random-liu): Test recover logic once checkpoint recovery is added.