123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484 |
- package container // import "github.com/docker/docker/container"
- import (
- "math/rand"
- "os"
- "path/filepath"
- "testing"
- "github.com/docker/docker/api/types"
- containertypes "github.com/docker/docker/api/types/container"
- "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, 0o755); 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)
- }
- }
- 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)
- }
- }
- }
- }
|