1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/container/view_test.go

485 lines
11 KiB
Go
Raw Normal View History

package container // import "github.com/docker/docker/container"
import (
Use hashicorp/go-memdb instead of truncindex memdb already knows how to search by prefix so there is no need to keep a separate list of container ids in the truncindex Benchmarks: $ go test -benchmem -run=^$ -count 5 -tags linux -bench ^BenchmarkDBGetByPrefix100$ github.com/docker/docker/container goos: linux goarch: amd64 pkg: github.com/docker/docker/container cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz BenchmarkDBGetByPrefix100-6 16018 73935 ns/op 33888 B/op 1100 allocs/op BenchmarkDBGetByPrefix100-6 16502 73150 ns/op 33888 B/op 1100 allocs/op BenchmarkDBGetByPrefix100-6 16218 74014 ns/op 33856 B/op 1100 allocs/op BenchmarkDBGetByPrefix100-6 15733 73370 ns/op 33792 B/op 1100 allocs/op BenchmarkDBGetByPrefix100-6 16432 72546 ns/op 33744 B/op 1100 allocs/op PASS ok github.com/docker/docker/container 9.752s $ go test -benchmem -run=^$ -count 5 -tags linux -bench ^BenchmarkTruncIndexGet100$ github.com/docker/docker/pkg/truncindex goos: linux goarch: amd64 pkg: github.com/docker/docker/pkg/truncindex cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz BenchmarkTruncIndexGet100-6 16862 73732 ns/op 44776 B/op 1173 allocs/op BenchmarkTruncIndexGet100-6 16832 73629 ns/op 45184 B/op 1179 allocs/op BenchmarkTruncIndexGet100-6 17214 73571 ns/op 45160 B/op 1178 allocs/op BenchmarkTruncIndexGet100-6 16113 71680 ns/op 45360 B/op 1182 allocs/op BenchmarkTruncIndexGet100-6 16676 71246 ns/op 45056 B/op 1184 allocs/op PASS ok github.com/docker/docker/pkg/truncindex 9.759s $ go test -benchmem -run=^$ -count 5 -tags linux -bench ^BenchmarkDBGetByPrefix500$ github.com/docker/docker/container goos: linux goarch: amd64 pkg: github.com/docker/docker/container cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz BenchmarkDBGetByPrefix500-6 1539 753541 ns/op 169381 B/op 5500 allocs/op BenchmarkDBGetByPrefix500-6 1624 749975 ns/op 169458 B/op 5500 allocs/op BenchmarkDBGetByPrefix500-6 1635 761222 ns/op 169298 B/op 5500 allocs/op BenchmarkDBGetByPrefix500-6 1693 727856 ns/op 169297 B/op 5500 allocs/op BenchmarkDBGetByPrefix500-6 1874 710813 ns/op 169570 B/op 5500 allocs/op PASS ok github.com/docker/docker/container 6.711s $ go test -benchmem -run=^$ -count 5 -tags linux -bench ^BenchmarkTruncIndexGet500$ github.com/docker/docker/pkg/truncindex goos: linux goarch: amd64 pkg: github.com/docker/docker/pkg/truncindex cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz BenchmarkTruncIndexGet500-6 1934 780328 ns/op 224073 B/op 5929 allocs/op BenchmarkTruncIndexGet500-6 1713 713935 ns/op 225011 B/op 5937 allocs/op BenchmarkTruncIndexGet500-6 1780 702847 ns/op 224090 B/op 5943 allocs/op BenchmarkTruncIndexGet500-6 1736 711086 ns/op 224027 B/op 5929 allocs/op BenchmarkTruncIndexGet500-6 2448 508694 ns/op 222322 B/op 5914 allocs/op PASS ok github.com/docker/docker/pkg/truncindex 6.877s Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
2022-05-20 06:12:02 -04:00
"math/rand"
"os"
"path/filepath"
"testing"
"github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container"
Use hashicorp/go-memdb instead of truncindex memdb already knows how to search by prefix so there is no need to keep a separate list of container ids in the truncindex Benchmarks: $ go test -benchmem -run=^$ -count 5 -tags linux -bench ^BenchmarkDBGetByPrefix100$ github.com/docker/docker/container goos: linux goarch: amd64 pkg: github.com/docker/docker/container cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz BenchmarkDBGetByPrefix100-6 16018 73935 ns/op 33888 B/op 1100 allocs/op BenchmarkDBGetByPrefix100-6 16502 73150 ns/op 33888 B/op 1100 allocs/op BenchmarkDBGetByPrefix100-6 16218 74014 ns/op 33856 B/op 1100 allocs/op BenchmarkDBGetByPrefix100-6 15733 73370 ns/op 33792 B/op 1100 allocs/op BenchmarkDBGetByPrefix100-6 16432 72546 ns/op 33744 B/op 1100 allocs/op PASS ok github.com/docker/docker/container 9.752s $ go test -benchmem -run=^$ -count 5 -tags linux -bench ^BenchmarkTruncIndexGet100$ github.com/docker/docker/pkg/truncindex goos: linux goarch: amd64 pkg: github.com/docker/docker/pkg/truncindex cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz BenchmarkTruncIndexGet100-6 16862 73732 ns/op 44776 B/op 1173 allocs/op BenchmarkTruncIndexGet100-6 16832 73629 ns/op 45184 B/op 1179 allocs/op BenchmarkTruncIndexGet100-6 17214 73571 ns/op 45160 B/op 1178 allocs/op BenchmarkTruncIndexGet100-6 16113 71680 ns/op 45360 B/op 1182 allocs/op BenchmarkTruncIndexGet100-6 16676 71246 ns/op 45056 B/op 1184 allocs/op PASS ok github.com/docker/docker/pkg/truncindex 9.759s $ go test -benchmem -run=^$ -count 5 -tags linux -bench ^BenchmarkDBGetByPrefix500$ github.com/docker/docker/container goos: linux goarch: amd64 pkg: github.com/docker/docker/container cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz BenchmarkDBGetByPrefix500-6 1539 753541 ns/op 169381 B/op 5500 allocs/op BenchmarkDBGetByPrefix500-6 1624 749975 ns/op 169458 B/op 5500 allocs/op BenchmarkDBGetByPrefix500-6 1635 761222 ns/op 169298 B/op 5500 allocs/op BenchmarkDBGetByPrefix500-6 1693 727856 ns/op 169297 B/op 5500 allocs/op BenchmarkDBGetByPrefix500-6 1874 710813 ns/op 169570 B/op 5500 allocs/op PASS ok github.com/docker/docker/container 6.711s $ go test -benchmem -run=^$ -count 5 -tags linux -bench ^BenchmarkTruncIndexGet500$ github.com/docker/docker/pkg/truncindex goos: linux goarch: amd64 pkg: github.com/docker/docker/pkg/truncindex cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz BenchmarkTruncIndexGet500-6 1934 780328 ns/op 224073 B/op 5929 allocs/op BenchmarkTruncIndexGet500-6 1713 713935 ns/op 225011 B/op 5937 allocs/op BenchmarkTruncIndexGet500-6 1780 702847 ns/op 224090 B/op 5943 allocs/op BenchmarkTruncIndexGet500-6 1736 711086 ns/op 224027 B/op 5929 allocs/op BenchmarkTruncIndexGet500-6 2448 508694 ns/op 222322 B/op 5914 allocs/op PASS ok github.com/docker/docker/pkg/truncindex 6.877s Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
2022-05-20 06:12:02 -04:00
"github.com/docker/docker/pkg/stringid"
"github.com/google/uuid"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
var root string
func TestMain(m *testing.M) {
var err error
root, err = os.MkdirTemp("", "docker-container-test-")
if err != nil {
panic(err)
}
defer os.RemoveAll(root)
os.Exit(m.Run())
}
func newContainer(t *testing.T) *Container {
var (
id = uuid.New().String()
cRoot = filepath.Join(root, id)
)
if err := os.MkdirAll(cRoot, 0755); err != nil {
t.Fatal(err)
}
c := NewBaseContainer(id, cRoot)
c.HostConfig = &containertypes.HostConfig{}
return c
}
func TestViewSaveDelete(t *testing.T) {
db, err := NewViewDB()
if err != nil {
t.Fatal(err)
}
c := newContainer(t)
if err := c.CheckpointTo(db); err != nil {
t.Fatal(err)
}
if err := db.Delete(c); err != nil {
t.Fatal(err)
}
}
func TestViewAll(t *testing.T) {
var (
db, _ = NewViewDB()
one = newContainer(t)
two = newContainer(t)
)
one.Pid = 10
if err := one.CheckpointTo(db); err != nil {
t.Fatal(err)
}
two.Pid = 20
if err := two.CheckpointTo(db); err != nil {
t.Fatal(err)
}
all, err := db.Snapshot().All()
if err != nil {
t.Fatal(err)
}
if l := len(all); l != 2 {
t.Fatalf("expected 2 items, got %d", l)
}
byID := make(map[string]Snapshot)
for i := range all {
byID[all[i].ID] = all[i]
}
if s, ok := byID[one.ID]; !ok || s.Pid != 10 {
t.Fatalf("expected something different with for id=%s: %v", one.ID, s)
}
if s, ok := byID[two.ID]; !ok || s.Pid != 20 {
t.Fatalf("expected something different with for id=%s: %v", two.ID, s)
}
}
func TestViewGet(t *testing.T) {
var (
db, _ = NewViewDB()
one = newContainer(t)
)
one.ImageID = "some-image-123"
if err := one.CheckpointTo(db); err != nil {
t.Fatal(err)
}
s, err := db.Snapshot().Get(one.ID)
if err != nil {
t.Fatal(err)
}
if s == nil || s.ImageID != "some-image-123" {
t.Fatalf("expected ImageID=some-image-123. Got: %v", s)
}
}
func TestNames(t *testing.T) {
db, err := NewViewDB()
if err != nil {
t.Fatal(err)
}
assert.Check(t, db.ReserveName("name1", "containerid1"))
assert.Check(t, db.ReserveName("name1", "containerid1")) // idempotent
assert.Check(t, db.ReserveName("name2", "containerid2"))
assert.Check(t, is.Error(db.ReserveName("name2", "containerid3"), ErrNameReserved.Error()))
// Releasing a name allows the name to point to something else later.
assert.Check(t, db.ReleaseName("name2"))
assert.Check(t, db.ReserveName("name2", "containerid3"))
view := db.Snapshot()
id, err := view.GetID("name1")
assert.Check(t, err)
assert.Check(t, is.Equal("containerid1", id))
id, err = view.GetID("name2")
assert.Check(t, err)
assert.Check(t, is.Equal("containerid3", id))
_, err = view.GetID("notreserved")
assert.Check(t, is.Error(err, ErrNameNotReserved.Error()))
// Releasing and re-reserving a name doesn't affect the snapshot.
assert.Check(t, db.ReleaseName("name2"))
assert.Check(t, db.ReserveName("name2", "containerid4"))
id, err = view.GetID("name1")
assert.Check(t, err)
assert.Check(t, is.Equal("containerid1", id))
id, err = view.GetID("name2")
assert.Check(t, err)
assert.Check(t, is.Equal("containerid3", id))
// GetAllNames
assert.Check(t, is.DeepEqual(map[string][]string{"containerid1": {"name1"}, "containerid3": {"name2"}}, view.GetAllNames()))
assert.Check(t, db.ReserveName("name3", "containerid1"))
assert.Check(t, db.ReserveName("name4", "containerid1"))
view = db.Snapshot()
assert.Check(t, is.DeepEqual(map[string][]string{"containerid1": {"name1", "name3", "name4"}, "containerid4": {"name2"}}, view.GetAllNames()))
// Release containerid1's names with Delete even though no container exists
assert.Check(t, db.Delete(&Container{ID: "containerid1"}))
// Reusing one of those names should work
assert.Check(t, db.ReserveName("name1", "containerid4"))
view = db.Snapshot()
assert.Check(t, is.DeepEqual(map[string][]string{"containerid4": {"name1", "name2"}}, view.GetAllNames()))
}
// Test case for GitHub issue 35920
func TestViewWithHealthCheck(t *testing.T) {
var (
db, _ = NewViewDB()
one = newContainer(t)
)
one.Health = &Health{
Health: types.Health{
Status: "starting",
},
}
if err := one.CheckpointTo(db); err != nil {
t.Fatal(err)
}
s, err := db.Snapshot().Get(one.ID)
if err != nil {
t.Fatal(err)
}
if s == nil || s.Health != "starting" {
t.Fatalf("expected Health=starting. Got: %+v", s)
}
}
Use hashicorp/go-memdb instead of truncindex memdb already knows how to search by prefix so there is no need to keep a separate list of container ids in the truncindex Benchmarks: $ go test -benchmem -run=^$ -count 5 -tags linux -bench ^BenchmarkDBGetByPrefix100$ github.com/docker/docker/container goos: linux goarch: amd64 pkg: github.com/docker/docker/container cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz BenchmarkDBGetByPrefix100-6 16018 73935 ns/op 33888 B/op 1100 allocs/op BenchmarkDBGetByPrefix100-6 16502 73150 ns/op 33888 B/op 1100 allocs/op BenchmarkDBGetByPrefix100-6 16218 74014 ns/op 33856 B/op 1100 allocs/op BenchmarkDBGetByPrefix100-6 15733 73370 ns/op 33792 B/op 1100 allocs/op BenchmarkDBGetByPrefix100-6 16432 72546 ns/op 33744 B/op 1100 allocs/op PASS ok github.com/docker/docker/container 9.752s $ go test -benchmem -run=^$ -count 5 -tags linux -bench ^BenchmarkTruncIndexGet100$ github.com/docker/docker/pkg/truncindex goos: linux goarch: amd64 pkg: github.com/docker/docker/pkg/truncindex cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz BenchmarkTruncIndexGet100-6 16862 73732 ns/op 44776 B/op 1173 allocs/op BenchmarkTruncIndexGet100-6 16832 73629 ns/op 45184 B/op 1179 allocs/op BenchmarkTruncIndexGet100-6 17214 73571 ns/op 45160 B/op 1178 allocs/op BenchmarkTruncIndexGet100-6 16113 71680 ns/op 45360 B/op 1182 allocs/op BenchmarkTruncIndexGet100-6 16676 71246 ns/op 45056 B/op 1184 allocs/op PASS ok github.com/docker/docker/pkg/truncindex 9.759s $ go test -benchmem -run=^$ -count 5 -tags linux -bench ^BenchmarkDBGetByPrefix500$ github.com/docker/docker/container goos: linux goarch: amd64 pkg: github.com/docker/docker/container cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz BenchmarkDBGetByPrefix500-6 1539 753541 ns/op 169381 B/op 5500 allocs/op BenchmarkDBGetByPrefix500-6 1624 749975 ns/op 169458 B/op 5500 allocs/op BenchmarkDBGetByPrefix500-6 1635 761222 ns/op 169298 B/op 5500 allocs/op BenchmarkDBGetByPrefix500-6 1693 727856 ns/op 169297 B/op 5500 allocs/op BenchmarkDBGetByPrefix500-6 1874 710813 ns/op 169570 B/op 5500 allocs/op PASS ok github.com/docker/docker/container 6.711s $ go test -benchmem -run=^$ -count 5 -tags linux -bench ^BenchmarkTruncIndexGet500$ github.com/docker/docker/pkg/truncindex goos: linux goarch: amd64 pkg: github.com/docker/docker/pkg/truncindex cpu: Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz BenchmarkTruncIndexGet500-6 1934 780328 ns/op 224073 B/op 5929 allocs/op BenchmarkTruncIndexGet500-6 1713 713935 ns/op 225011 B/op 5937 allocs/op BenchmarkTruncIndexGet500-6 1780 702847 ns/op 224090 B/op 5943 allocs/op BenchmarkTruncIndexGet500-6 1736 711086 ns/op 224027 B/op 5929 allocs/op BenchmarkTruncIndexGet500-6 2448 508694 ns/op 222322 B/op 5914 allocs/op PASS ok github.com/docker/docker/pkg/truncindex 6.877s Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
2022-05-20 06:12:02 -04:00
func TestTruncIndex(t *testing.T) {
db, err := NewViewDB()
if err != nil {
t.Fatal(err)
}
// Get on an empty index
if _, err := db.GetByPrefix("foobar"); err == nil {
t.Fatal("Get on an empty index should return an error")
}
id := "99b36c2c326ccc11e726eee6ee78a0baf166ef96"
// Add an id
if err := db.Save(&Container{ID: id}); err != nil {
t.Fatal(err)
}
type testacase struct {
name string
input string
expectedResult string
expectError bool
}
for _, tc := range []testacase{
{
name: "Get a non-existing id",
input: "abracadabra",
expectError: true,
},
{
name: "Get an empty id",
input: "",
expectedResult: "",
expectError: true,
},
{
name: "Get the exact id",
input: id,
expectedResult: id,
expectError: false,
},
{
name: "The first letter should match",
input: id[:1],
expectedResult: id,
expectError: false,
},
{
name: "The first half should match",
input: id[:len(id)/2],
expectedResult: id,
expectError: false,
},
{
name: "The second half should NOT match",
input: id[len(id)/2:],
expectError: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
assertIndexGet(t, db, tc.input, tc.expectedResult, tc.expectError)
})
}
id2 := id[:6] + "blabla"
// Add an id
if err := db.Save(&Container{ID: id2}); err != nil {
t.Fatal(err)
}
for _, tc := range []testacase{
{
name: "id should work",
input: id,
expectedResult: id,
expectError: false,
},
{
name: "id2 should work",
input: id2,
expectedResult: id2,
expectError: false,
},
{
name: "6 characters should conflict",
input: id[:6],
expectedResult: "",
expectError: true,
},
{
name: "4 characters should conflict",
input: id[:4],
expectedResult: "",
expectError: true,
},
{
name: "1 character should conflict",
input: id[:6],
expectedResult: "",
expectError: true,
},
{
name: "7 characters of id should not conflict",
input: id[:7],
expectedResult: id,
expectError: false,
},
{
name: "7 characters of id2 should not conflict",
input: id2[:7],
expectedResult: id2,
expectError: false,
},
} {
t.Run(tc.name, func(t *testing.T) {
assertIndexGet(t, db, tc.input, tc.expectedResult, tc.expectError)
})
}
// Deleting id2 should remove conflicts
if err := db.Delete(&Container{ID: id2}); err != nil {
t.Fatal(err)
}
for _, tc := range []testacase{
{
name: "id2 should no longer work",
input: id2,
expectedResult: "",
expectError: true,
},
{
name: "7 characters id2 should no longer work",
input: id2[:7],
expectedResult: "",
expectError: true,
},
{
name: "11 characters id2 should no longer work",
input: id2[:11],
expectedResult: "",
expectError: true,
},
{
name: "conflicts between id[:6] and id2 should be gone",
input: id[:6],
expectedResult: id,
expectError: false,
},
{
name: "conflicts between id[:4] and id2 should be gone",
input: id[:4],
expectedResult: id,
expectError: false,
},
{
name: "conflicts between id[:1] and id2 should be gone",
input: id[:1],
expectedResult: id,
expectError: false,
},
{
name: "non-conflicting 7 character substrings should still not conflict",
input: id[:7],
expectedResult: id,
expectError: false,
},
{
name: "non-conflicting 15 character substrings should still not conflict",
input: id[:15],
expectedResult: id,
expectError: false,
},
{
name: "non-conflicting substrings should still not conflict",
input: id,
expectedResult: id,
expectError: false,
},
} {
t.Run(tc.name, func(t *testing.T) {
assertIndexGet(t, db, tc.input, tc.expectedResult, tc.expectError)
})
}
}
func assertIndexGet(t *testing.T, snapshot ViewDB, input, expectedResult string, expectError bool) {
if result, err := snapshot.GetByPrefix(input); err != nil && !expectError {
t.Fatalf("Unexpected error getting '%s': %s", input, err)
} else if err == nil && expectError {
t.Fatalf("Getting '%s' should return an error, not '%s'", input, result)
} else if result != expectedResult {
t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
}
}
func BenchmarkDBAdd100(b *testing.B) {
var testSet []string
for i := 0; i < 100; i++ {
testSet = append(testSet, stringid.GenerateRandomID())
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
db, err := NewViewDB()
if err != nil {
b.Fatal(err)
}
for _, id := range testSet {
if err := db.Save(&Container{ID: id}); err != nil {
b.Fatal(err)
}
}
}
}
func BenchmarkDBGetByPrefix100(b *testing.B) {
var testSet []string
var testKeys []string
for i := 0; i < 100; i++ {
testSet = append(testSet, stringid.GenerateRandomID())
}
db, err := NewViewDB()
if err != nil {
b.Fatal(err)
}
for _, id := range testSet {
if err := db.Save(&Container{ID: id}); err != nil {
b.Fatal(err)
}
l := rand.Intn(12) + 12
testKeys = append(testKeys, id[:l])
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, id := range testKeys {
if res, err := db.GetByPrefix(id); err != nil {
b.Fatal(res, err)
}
}
}
}
func BenchmarkDBGetByPrefix250(b *testing.B) {
var testSet []string
var testKeys []string
for i := 0; i < 250; i++ {
testSet = append(testSet, stringid.GenerateRandomID())
}
db, err := NewViewDB()
if err != nil {
b.Fatal(err)
}
for _, id := range testSet {
if err := db.Save(&Container{ID: id}); err != nil {
b.Fatal(err)
}
l := rand.Intn(12) + 12
testKeys = append(testKeys, id[:l])
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, id := range testKeys {
if res, err := db.GetByPrefix(id); err != nil {
b.Fatal(res, err)
}
}
}
}
func BenchmarkDBGetByPrefix500(b *testing.B) {
var testSet []string
var testKeys []string
for i := 0; i < 500; i++ {
testSet = append(testSet, stringid.GenerateRandomID())
}
db, err := NewViewDB()
if err != nil {
b.Fatal(err)
}
for _, id := range testSet {
if err := db.Save(&Container{ID: id}); err != nil {
b.Fatal(err)
}
l := rand.Intn(12) + 12
testKeys = append(testKeys, id[:l])
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, id := range testKeys {
if res, err := db.GetByPrefix(id); err != nil {
b.Fatal(res, err)
}
}
}
}