view_test.go 11 KB

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