github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/container/view_test.go (about)

     1  package container // import "github.com/Prakhar-Agarwal-byte/moby/container"
     2  
     3  import (
     4  	"math/rand"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/Prakhar-Agarwal-byte/moby/api/types"
    10  	containertypes "github.com/Prakhar-Agarwal-byte/moby/api/types/container"
    11  	"github.com/Prakhar-Agarwal-byte/moby/pkg/stringid"
    12  	"github.com/google/uuid"
    13  	"gotest.tools/v3/assert"
    14  	is "gotest.tools/v3/assert/cmp"
    15  )
    16  
    17  var root string
    18  
    19  func TestMain(m *testing.M) {
    20  	var err error
    21  	root, err = os.MkdirTemp("", "docker-container-test-")
    22  	if err != nil {
    23  		panic(err)
    24  	}
    25  	defer os.RemoveAll(root)
    26  
    27  	os.Exit(m.Run())
    28  }
    29  
    30  func newContainer(t *testing.T) *Container {
    31  	var (
    32  		id    = uuid.New().String()
    33  		cRoot = filepath.Join(root, id)
    34  	)
    35  	if err := os.MkdirAll(cRoot, 0o755); err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	c := NewBaseContainer(id, cRoot)
    39  	c.HostConfig = &containertypes.HostConfig{}
    40  	return c
    41  }
    42  
    43  func TestViewSaveDelete(t *testing.T) {
    44  	db, err := NewViewDB()
    45  	if err != nil {
    46  		t.Fatal(err)
    47  	}
    48  	c := newContainer(t)
    49  	if err := c.CheckpointTo(db); err != nil {
    50  		t.Fatal(err)
    51  	}
    52  	if err := db.Delete(c); err != nil {
    53  		t.Fatal(err)
    54  	}
    55  }
    56  
    57  func TestViewAll(t *testing.T) {
    58  	var (
    59  		db, _ = NewViewDB()
    60  		one   = newContainer(t)
    61  		two   = newContainer(t)
    62  	)
    63  	one.Pid = 10
    64  	if err := one.CheckpointTo(db); err != nil {
    65  		t.Fatal(err)
    66  	}
    67  	two.Pid = 20
    68  	if err := two.CheckpointTo(db); err != nil {
    69  		t.Fatal(err)
    70  	}
    71  
    72  	all, err := db.Snapshot().All()
    73  	if err != nil {
    74  		t.Fatal(err)
    75  	}
    76  	if l := len(all); l != 2 {
    77  		t.Fatalf("expected 2 items, got %d", l)
    78  	}
    79  	byID := make(map[string]Snapshot)
    80  	for i := range all {
    81  		byID[all[i].ID] = all[i]
    82  	}
    83  	if s, ok := byID[one.ID]; !ok || s.Pid != 10 {
    84  		t.Fatalf("expected something different with for id=%s: %v", one.ID, s)
    85  	}
    86  	if s, ok := byID[two.ID]; !ok || s.Pid != 20 {
    87  		t.Fatalf("expected something different with for id=%s: %v", two.ID, s)
    88  	}
    89  }
    90  
    91  func TestViewGet(t *testing.T) {
    92  	var (
    93  		db, _ = NewViewDB()
    94  		one   = newContainer(t)
    95  	)
    96  	one.ImageID = "some-image-123"
    97  	if err := one.CheckpointTo(db); err != nil {
    98  		t.Fatal(err)
    99  	}
   100  	s, err := db.Snapshot().Get(one.ID)
   101  	if err != nil {
   102  		t.Fatal(err)
   103  	}
   104  	if s == nil || s.ImageID != "some-image-123" {
   105  		t.Fatalf("expected ImageID=some-image-123. Got: %v", s)
   106  	}
   107  }
   108  
   109  func TestNames(t *testing.T) {
   110  	db, err := NewViewDB()
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  	assert.Check(t, db.ReserveName("name1", "containerid1"))
   115  	assert.Check(t, db.ReserveName("name1", "containerid1")) // idempotent
   116  	assert.Check(t, db.ReserveName("name2", "containerid2"))
   117  	assert.Check(t, is.Error(db.ReserveName("name2", "containerid3"), ErrNameReserved.Error()))
   118  
   119  	// Releasing a name allows the name to point to something else later.
   120  	assert.Check(t, db.ReleaseName("name2"))
   121  	assert.Check(t, db.ReserveName("name2", "containerid3"))
   122  
   123  	view := db.Snapshot()
   124  
   125  	id, err := view.GetID("name1")
   126  	assert.Check(t, err)
   127  	assert.Check(t, is.Equal("containerid1", id))
   128  
   129  	id, err = view.GetID("name2")
   130  	assert.Check(t, err)
   131  	assert.Check(t, is.Equal("containerid3", id))
   132  
   133  	_, err = view.GetID("notreserved")
   134  	assert.Check(t, is.Error(err, ErrNameNotReserved.Error()))
   135  
   136  	// Releasing and re-reserving a name doesn't affect the snapshot.
   137  	assert.Check(t, db.ReleaseName("name2"))
   138  	assert.Check(t, db.ReserveName("name2", "containerid4"))
   139  
   140  	id, err = view.GetID("name1")
   141  	assert.Check(t, err)
   142  	assert.Check(t, is.Equal("containerid1", id))
   143  
   144  	id, err = view.GetID("name2")
   145  	assert.Check(t, err)
   146  	assert.Check(t, is.Equal("containerid3", id))
   147  
   148  	// GetAllNames
   149  	assert.Check(t, is.DeepEqual(map[string][]string{"containerid1": {"name1"}, "containerid3": {"name2"}}, view.GetAllNames()))
   150  
   151  	assert.Check(t, db.ReserveName("name3", "containerid1"))
   152  	assert.Check(t, db.ReserveName("name4", "containerid1"))
   153  
   154  	view = db.Snapshot()
   155  	assert.Check(t, is.DeepEqual(map[string][]string{"containerid1": {"name1", "name3", "name4"}, "containerid4": {"name2"}}, view.GetAllNames()))
   156  
   157  	// Release containerid1's names with Delete even though no container exists
   158  	assert.Check(t, db.Delete(&Container{ID: "containerid1"}))
   159  
   160  	// Reusing one of those names should work
   161  	assert.Check(t, db.ReserveName("name1", "containerid4"))
   162  	view = db.Snapshot()
   163  	assert.Check(t, is.DeepEqual(map[string][]string{"containerid4": {"name1", "name2"}}, view.GetAllNames()))
   164  }
   165  
   166  // Test case for GitHub issue 35920
   167  func TestViewWithHealthCheck(t *testing.T) {
   168  	var (
   169  		db, _ = NewViewDB()
   170  		one   = newContainer(t)
   171  	)
   172  	one.Health = &Health{
   173  		Health: types.Health{
   174  			Status: "starting",
   175  		},
   176  	}
   177  	if err := one.CheckpointTo(db); err != nil {
   178  		t.Fatal(err)
   179  	}
   180  	s, err := db.Snapshot().Get(one.ID)
   181  	if err != nil {
   182  		t.Fatal(err)
   183  	}
   184  	if s == nil || s.Health != "starting" {
   185  		t.Fatalf("expected Health=starting. Got: %+v", s)
   186  	}
   187  }
   188  
   189  func TestTruncIndex(t *testing.T) {
   190  	db, err := NewViewDB()
   191  	if err != nil {
   192  		t.Fatal(err)
   193  	}
   194  
   195  	// Get on an empty index
   196  	if _, err := db.GetByPrefix("foobar"); err == nil {
   197  		t.Fatal("Get on an empty index should return an error")
   198  	}
   199  
   200  	id := "99b36c2c326ccc11e726eee6ee78a0baf166ef96"
   201  	// Add an id
   202  	if err := db.Save(&Container{ID: id}); err != nil {
   203  		t.Fatal(err)
   204  	}
   205  
   206  	type testacase struct {
   207  		name           string
   208  		input          string
   209  		expectedResult string
   210  		expectError    bool
   211  	}
   212  
   213  	for _, tc := range []testacase{
   214  		{
   215  			name:        "Get a non-existing id",
   216  			input:       "abracadabra",
   217  			expectError: true,
   218  		},
   219  		{
   220  			name:           "Get an empty id",
   221  			input:          "",
   222  			expectedResult: "",
   223  			expectError:    true,
   224  		},
   225  		{
   226  			name:           "Get the exact id",
   227  			input:          id,
   228  			expectedResult: id,
   229  			expectError:    false,
   230  		},
   231  		{
   232  			name:           "The first letter should match",
   233  			input:          id[:1],
   234  			expectedResult: id,
   235  			expectError:    false,
   236  		},
   237  		{
   238  			name:           "The first half should match",
   239  			input:          id[:len(id)/2],
   240  			expectedResult: id,
   241  			expectError:    false,
   242  		},
   243  		{
   244  			name:        "The second half should NOT match",
   245  			input:       id[len(id)/2:],
   246  			expectError: true,
   247  		},
   248  	} {
   249  		t.Run(tc.name, func(t *testing.T) {
   250  			assertIndexGet(t, db, tc.input, tc.expectedResult, tc.expectError)
   251  		})
   252  	}
   253  
   254  	id2 := id[:6] + "blabla"
   255  	// Add an id
   256  	if err := db.Save(&Container{ID: id2}); err != nil {
   257  		t.Fatal(err)
   258  	}
   259  
   260  	for _, tc := range []testacase{
   261  		{
   262  			name:           "id should work",
   263  			input:          id,
   264  			expectedResult: id,
   265  			expectError:    false,
   266  		},
   267  		{
   268  			name:           "id2 should work",
   269  			input:          id2,
   270  			expectedResult: id2,
   271  			expectError:    false,
   272  		},
   273  		{
   274  			name:           "6 characters should conflict",
   275  			input:          id[:6],
   276  			expectedResult: "",
   277  			expectError:    true,
   278  		},
   279  		{
   280  			name:           "4 characters should conflict",
   281  			input:          id[:4],
   282  			expectedResult: "",
   283  			expectError:    true,
   284  		},
   285  		{
   286  			name:           "1 character should conflict",
   287  			input:          id[:6],
   288  			expectedResult: "",
   289  			expectError:    true,
   290  		},
   291  		{
   292  			name:           "7 characters of id should not conflict",
   293  			input:          id[:7],
   294  			expectedResult: id,
   295  			expectError:    false,
   296  		},
   297  		{
   298  			name:           "7 characters of id2 should not conflict",
   299  			input:          id2[:7],
   300  			expectedResult: id2,
   301  			expectError:    false,
   302  		},
   303  	} {
   304  		t.Run(tc.name, func(t *testing.T) {
   305  			assertIndexGet(t, db, tc.input, tc.expectedResult, tc.expectError)
   306  		})
   307  	}
   308  
   309  	// Deleting id2 should remove conflicts
   310  	if err := db.Delete(&Container{ID: id2}); err != nil {
   311  		t.Fatal(err)
   312  	}
   313  
   314  	for _, tc := range []testacase{
   315  		{
   316  			name:           "id2 should no longer work",
   317  			input:          id2,
   318  			expectedResult: "",
   319  			expectError:    true,
   320  		},
   321  		{
   322  			name:           "7 characters id2 should no longer work",
   323  			input:          id2[:7],
   324  			expectedResult: "",
   325  			expectError:    true,
   326  		},
   327  		{
   328  			name:           "11 characters id2 should no longer work",
   329  			input:          id2[:11],
   330  			expectedResult: "",
   331  			expectError:    true,
   332  		},
   333  		{
   334  			name:           "conflicts between id[:6] and id2 should be gone",
   335  			input:          id[:6],
   336  			expectedResult: id,
   337  			expectError:    false,
   338  		},
   339  		{
   340  			name:           "conflicts between id[:4] and id2 should be gone",
   341  			input:          id[:4],
   342  			expectedResult: id,
   343  			expectError:    false,
   344  		},
   345  		{
   346  			name:           "conflicts between id[:1] and id2 should be gone",
   347  			input:          id[:1],
   348  			expectedResult: id,
   349  			expectError:    false,
   350  		},
   351  		{
   352  			name:           "non-conflicting 7 character substrings should still not conflict",
   353  			input:          id[:7],
   354  			expectedResult: id,
   355  			expectError:    false,
   356  		},
   357  		{
   358  			name:           "non-conflicting 15 character substrings should still not conflict",
   359  			input:          id[:15],
   360  			expectedResult: id,
   361  			expectError:    false,
   362  		},
   363  		{
   364  			name:           "non-conflicting substrings should still not conflict",
   365  			input:          id,
   366  			expectedResult: id,
   367  			expectError:    false,
   368  		},
   369  	} {
   370  		t.Run(tc.name, func(t *testing.T) {
   371  			assertIndexGet(t, db, tc.input, tc.expectedResult, tc.expectError)
   372  		})
   373  	}
   374  }
   375  
   376  func assertIndexGet(t *testing.T, snapshot *ViewDB, input, expectedResult string, expectError bool) {
   377  	if result, err := snapshot.GetByPrefix(input); err != nil && !expectError {
   378  		t.Fatalf("Unexpected error getting '%s': %s", input, err)
   379  	} else if err == nil && expectError {
   380  		t.Fatalf("Getting '%s' should return an error, not '%s'", input, result)
   381  	} else if result != expectedResult {
   382  		t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
   383  	}
   384  }
   385  
   386  func BenchmarkDBAdd100(b *testing.B) {
   387  	var testSet []string
   388  	for i := 0; i < 100; i++ {
   389  		testSet = append(testSet, stringid.GenerateRandomID())
   390  	}
   391  	b.ResetTimer()
   392  	for i := 0; i < b.N; i++ {
   393  		db, err := NewViewDB()
   394  		if err != nil {
   395  			b.Fatal(err)
   396  		}
   397  		for _, id := range testSet {
   398  			if err := db.Save(&Container{ID: id}); err != nil {
   399  				b.Fatal(err)
   400  			}
   401  		}
   402  	}
   403  }
   404  
   405  func BenchmarkDBGetByPrefix100(b *testing.B) {
   406  	var testSet []string
   407  	var testKeys []string
   408  	for i := 0; i < 100; i++ {
   409  		testSet = append(testSet, stringid.GenerateRandomID())
   410  	}
   411  	db, err := NewViewDB()
   412  	if err != nil {
   413  		b.Fatal(err)
   414  	}
   415  	for _, id := range testSet {
   416  		if err := db.Save(&Container{ID: id}); err != nil {
   417  			b.Fatal(err)
   418  		}
   419  		l := rand.Intn(12) + 12
   420  		testKeys = append(testKeys, id[:l])
   421  	}
   422  	b.ResetTimer()
   423  	for i := 0; i < b.N; i++ {
   424  		for _, id := range testKeys {
   425  			if res, err := db.GetByPrefix(id); err != nil {
   426  				b.Fatal(res, err)
   427  			}
   428  		}
   429  	}
   430  }
   431  
   432  func BenchmarkDBGetByPrefix250(b *testing.B) {
   433  	var testSet []string
   434  	var testKeys []string
   435  	for i := 0; i < 250; i++ {
   436  		testSet = append(testSet, stringid.GenerateRandomID())
   437  	}
   438  	db, err := NewViewDB()
   439  	if err != nil {
   440  		b.Fatal(err)
   441  	}
   442  	for _, id := range testSet {
   443  		if err := db.Save(&Container{ID: id}); err != nil {
   444  			b.Fatal(err)
   445  		}
   446  		l := rand.Intn(12) + 12
   447  		testKeys = append(testKeys, id[:l])
   448  	}
   449  	b.ResetTimer()
   450  	for i := 0; i < b.N; i++ {
   451  		for _, id := range testKeys {
   452  			if res, err := db.GetByPrefix(id); err != nil {
   453  				b.Fatal(res, err)
   454  			}
   455  		}
   456  	}
   457  }
   458  
   459  func BenchmarkDBGetByPrefix500(b *testing.B) {
   460  	var testSet []string
   461  	var testKeys []string
   462  	for i := 0; i < 500; i++ {
   463  		testSet = append(testSet, stringid.GenerateRandomID())
   464  	}
   465  	db, err := NewViewDB()
   466  	if err != nil {
   467  		b.Fatal(err)
   468  	}
   469  	for _, id := range testSet {
   470  		if err := db.Save(&Container{ID: id}); err != nil {
   471  			b.Fatal(err)
   472  		}
   473  		l := rand.Intn(12) + 12
   474  		testKeys = append(testKeys, id[:l])
   475  	}
   476  	b.ResetTimer()
   477  	for i := 0; i < b.N; i++ {
   478  		for _, id := range testKeys {
   479  			if res, err := db.GetByPrefix(id); err != nil {
   480  				b.Fatal(res, err)
   481  			}
   482  		}
   483  	}
   484  }