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 }