store_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. package reference
  2. import (
  3. "bytes"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "testing"
  9. "github.com/docker/docker/image"
  10. )
  11. var (
  12. saveLoadTestCases = map[string]image.ID{
  13. "registry:5000/foobar:HEAD": "sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6",
  14. "registry:5000/foobar:alternate": "sha256:ae300ebc4a4f00693702cfb0a5e0b7bc527b353828dc86ad09fb95c8a681b793",
  15. "registry:5000/foobar:latest": "sha256:6153498b9ac00968d71b66cca4eac37e990b5f9eb50c26877eb8799c8847451b",
  16. "registry:5000/foobar:master": "sha256:6c9917af4c4e05001b346421959d7ea81b6dc9d25718466a37a6add865dfd7fc",
  17. "jess/hollywood:latest": "sha256:ae7a5519a0a55a2d4ef20ddcbd5d0ca0888a1f7ab806acc8e2a27baf46f529fe",
  18. "registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6": "sha256:24126a56805beb9711be5f4590cc2eb55ab8d4a85ebd618eed72bb19fc50631c",
  19. "busybox:latest": "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c",
  20. }
  21. marshalledSaveLoadTestCases = []byte(`{"Repositories":{"busybox":{"busybox:latest":"sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c"},"jess/hollywood":{"jess/hollywood:latest":"sha256:ae7a5519a0a55a2d4ef20ddcbd5d0ca0888a1f7ab806acc8e2a27baf46f529fe"},"registry":{"registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6":"sha256:24126a56805beb9711be5f4590cc2eb55ab8d4a85ebd618eed72bb19fc50631c"},"registry:5000/foobar":{"registry:5000/foobar:HEAD":"sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6","registry:5000/foobar:alternate":"sha256:ae300ebc4a4f00693702cfb0a5e0b7bc527b353828dc86ad09fb95c8a681b793","registry:5000/foobar:latest":"sha256:6153498b9ac00968d71b66cca4eac37e990b5f9eb50c26877eb8799c8847451b","registry:5000/foobar:master":"sha256:6c9917af4c4e05001b346421959d7ea81b6dc9d25718466a37a6add865dfd7fc"}}}`)
  22. )
  23. func TestLoad(t *testing.T) {
  24. jsonFile, err := ioutil.TempFile("", "tag-store-test")
  25. if err != nil {
  26. t.Fatalf("error creating temp file: %v", err)
  27. }
  28. defer os.RemoveAll(jsonFile.Name())
  29. // Write canned json to the temp file
  30. _, err = jsonFile.Write(marshalledSaveLoadTestCases)
  31. if err != nil {
  32. t.Fatalf("error writing to temp file: %v", err)
  33. }
  34. jsonFile.Close()
  35. store, err := NewReferenceStore(jsonFile.Name())
  36. if err != nil {
  37. t.Fatalf("error creating tag store: %v", err)
  38. }
  39. for refStr, expectedID := range saveLoadTestCases {
  40. ref, err := ParseNamed(refStr)
  41. if err != nil {
  42. t.Fatalf("failed to parse reference: %v", err)
  43. }
  44. id, err := store.Get(ref)
  45. if err != nil {
  46. t.Fatalf("could not find reference %s: %v", refStr, err)
  47. }
  48. if id != expectedID {
  49. t.Fatalf("expected %s - got %s", expectedID, id)
  50. }
  51. }
  52. }
  53. func TestSave(t *testing.T) {
  54. jsonFile, err := ioutil.TempFile("", "tag-store-test")
  55. if err != nil {
  56. t.Fatalf("error creating temp file: %v", err)
  57. }
  58. _, err = jsonFile.Write([]byte(`{}`))
  59. jsonFile.Close()
  60. defer os.RemoveAll(jsonFile.Name())
  61. store, err := NewReferenceStore(jsonFile.Name())
  62. if err != nil {
  63. t.Fatalf("error creating tag store: %v", err)
  64. }
  65. for refStr, id := range saveLoadTestCases {
  66. ref, err := ParseNamed(refStr)
  67. if err != nil {
  68. t.Fatalf("failed to parse reference: %v", err)
  69. }
  70. if canonical, ok := ref.(Canonical); ok {
  71. err = store.AddDigest(canonical, id, false)
  72. if err != nil {
  73. t.Fatalf("could not add digest reference %s: %v", refStr, err)
  74. }
  75. } else {
  76. err = store.AddTag(ref, id, false)
  77. if err != nil {
  78. t.Fatalf("could not add reference %s: %v", refStr, err)
  79. }
  80. }
  81. }
  82. jsonBytes, err := ioutil.ReadFile(jsonFile.Name())
  83. if err != nil {
  84. t.Fatalf("could not read json file: %v", err)
  85. }
  86. if !bytes.Equal(jsonBytes, marshalledSaveLoadTestCases) {
  87. t.Fatalf("save output did not match expectations\nexpected:\n%s\ngot:\n%s", marshalledSaveLoadTestCases, jsonBytes)
  88. }
  89. }
  90. func TestAddDeleteGet(t *testing.T) {
  91. jsonFile, err := ioutil.TempFile("", "tag-store-test")
  92. if err != nil {
  93. t.Fatalf("error creating temp file: %v", err)
  94. }
  95. _, err = jsonFile.Write([]byte(`{}`))
  96. jsonFile.Close()
  97. defer os.RemoveAll(jsonFile.Name())
  98. store, err := NewReferenceStore(jsonFile.Name())
  99. if err != nil {
  100. t.Fatalf("error creating tag store: %v", err)
  101. }
  102. testImageID1 := image.ID("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9c")
  103. testImageID2 := image.ID("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9d")
  104. testImageID3 := image.ID("sha256:9655aef5fd742a1b4e1b7b163aa9f1c76c186304bf39102283d80927c916ca9e")
  105. // Try adding a reference with no tag or digest
  106. nameOnly, err := WithName("username/repo")
  107. if err != nil {
  108. t.Fatalf("could not parse reference: %v", err)
  109. }
  110. if err = store.AddTag(nameOnly, testImageID1, false); err != nil {
  111. t.Fatalf("error adding to store: %v", err)
  112. }
  113. // Add a few references
  114. ref1, err := ParseNamed("username/repo1:latest")
  115. if err != nil {
  116. t.Fatalf("could not parse reference: %v", err)
  117. }
  118. if err = store.AddTag(ref1, testImageID1, false); err != nil {
  119. t.Fatalf("error adding to store: %v", err)
  120. }
  121. ref2, err := ParseNamed("username/repo1:old")
  122. if err != nil {
  123. t.Fatalf("could not parse reference: %v", err)
  124. }
  125. if err = store.AddTag(ref2, testImageID2, false); err != nil {
  126. t.Fatalf("error adding to store: %v", err)
  127. }
  128. ref3, err := ParseNamed("username/repo1:alias")
  129. if err != nil {
  130. t.Fatalf("could not parse reference: %v", err)
  131. }
  132. if err = store.AddTag(ref3, testImageID1, false); err != nil {
  133. t.Fatalf("error adding to store: %v", err)
  134. }
  135. ref4, err := ParseNamed("username/repo2:latest")
  136. if err != nil {
  137. t.Fatalf("could not parse reference: %v", err)
  138. }
  139. if err = store.AddTag(ref4, testImageID2, false); err != nil {
  140. t.Fatalf("error adding to store: %v", err)
  141. }
  142. ref5, err := ParseNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
  143. if err != nil {
  144. t.Fatalf("could not parse reference: %v", err)
  145. }
  146. if err = store.AddDigest(ref5.(Canonical), testImageID2, false); err != nil {
  147. t.Fatalf("error adding to store: %v", err)
  148. }
  149. // Attempt to overwrite with force == false
  150. if err = store.AddTag(ref4, testImageID3, false); err == nil || !strings.HasPrefix(err.Error(), "Conflict:") {
  151. t.Fatalf("did not get expected error on overwrite attempt - got %v", err)
  152. }
  153. // Repeat to overwrite with force == true
  154. if err = store.AddTag(ref4, testImageID3, true); err != nil {
  155. t.Fatalf("failed to force tag overwrite: %v", err)
  156. }
  157. // Check references so far
  158. id, err := store.Get(nameOnly)
  159. if err != nil {
  160. t.Fatalf("Get returned error: %v", err)
  161. }
  162. if id != testImageID1 {
  163. t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID1.String())
  164. }
  165. id, err = store.Get(ref1)
  166. if err != nil {
  167. t.Fatalf("Get returned error: %v", err)
  168. }
  169. if id != testImageID1 {
  170. t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID1.String())
  171. }
  172. id, err = store.Get(ref2)
  173. if err != nil {
  174. t.Fatalf("Get returned error: %v", err)
  175. }
  176. if id != testImageID2 {
  177. t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID2.String())
  178. }
  179. id, err = store.Get(ref3)
  180. if err != nil {
  181. t.Fatalf("Get returned error: %v", err)
  182. }
  183. if id != testImageID1 {
  184. t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID1.String())
  185. }
  186. id, err = store.Get(ref4)
  187. if err != nil {
  188. t.Fatalf("Get returned error: %v", err)
  189. }
  190. if id != testImageID3 {
  191. t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID3.String())
  192. }
  193. id, err = store.Get(ref5)
  194. if err != nil {
  195. t.Fatalf("Get returned error: %v", err)
  196. }
  197. if id != testImageID2 {
  198. t.Fatalf("id mismatch: got %s instead of %s", id.String(), testImageID3.String())
  199. }
  200. // Get should return ErrDoesNotExist for a nonexistent repo
  201. nonExistRepo, err := ParseNamed("username/nonexistrepo:latest")
  202. if err != nil {
  203. t.Fatalf("could not parse reference: %v", err)
  204. }
  205. if _, err = store.Get(nonExistRepo); err != ErrDoesNotExist {
  206. t.Fatal("Expected ErrDoesNotExist from Get")
  207. }
  208. // Get should return ErrDoesNotExist for a nonexistent tag
  209. nonExistTag, err := ParseNamed("username/repo1:nonexist")
  210. if err != nil {
  211. t.Fatalf("could not parse reference: %v", err)
  212. }
  213. if _, err = store.Get(nonExistTag); err != ErrDoesNotExist {
  214. t.Fatal("Expected ErrDoesNotExist from Get")
  215. }
  216. // Check References
  217. refs := store.References(testImageID1)
  218. if len(refs) != 3 {
  219. t.Fatal("unexpected number of references")
  220. }
  221. // Looking for the references in this order verifies that they are
  222. // returned lexically sorted.
  223. if refs[0].String() != ref3.String() {
  224. t.Fatalf("unexpected reference: %v", refs[0].String())
  225. }
  226. if refs[1].String() != ref1.String() {
  227. t.Fatalf("unexpected reference: %v", refs[1].String())
  228. }
  229. if refs[2].String() != nameOnly.String()+":latest" {
  230. t.Fatalf("unexpected reference: %v", refs[2].String())
  231. }
  232. // Check ReferencesByName
  233. repoName, err := WithName("username/repo1")
  234. if err != nil {
  235. t.Fatalf("could not parse reference: %v", err)
  236. }
  237. associations := store.ReferencesByName(repoName)
  238. if len(associations) != 3 {
  239. t.Fatal("unexpected number of associations")
  240. }
  241. // Looking for the associations in this order verifies that they are
  242. // returned lexically sorted.
  243. if associations[0].Ref.String() != ref3.String() {
  244. t.Fatalf("unexpected reference: %v", associations[0].Ref.String())
  245. }
  246. if associations[0].ImageID != testImageID1 {
  247. t.Fatalf("unexpected reference: %v", associations[0].Ref.String())
  248. }
  249. if associations[1].Ref.String() != ref1.String() {
  250. t.Fatalf("unexpected reference: %v", associations[1].Ref.String())
  251. }
  252. if associations[1].ImageID != testImageID1 {
  253. t.Fatalf("unexpected reference: %v", associations[1].Ref.String())
  254. }
  255. if associations[2].Ref.String() != ref2.String() {
  256. t.Fatalf("unexpected reference: %v", associations[2].Ref.String())
  257. }
  258. if associations[2].ImageID != testImageID2 {
  259. t.Fatalf("unexpected reference: %v", associations[2].Ref.String())
  260. }
  261. // Delete should return ErrDoesNotExist for a nonexistent repo
  262. if _, err = store.Delete(nonExistRepo); err != ErrDoesNotExist {
  263. t.Fatal("Expected ErrDoesNotExist from Delete")
  264. }
  265. // Delete should return ErrDoesNotExist for a nonexistent tag
  266. if _, err = store.Delete(nonExistTag); err != ErrDoesNotExist {
  267. t.Fatal("Expected ErrDoesNotExist from Delete")
  268. }
  269. // Delete a few references
  270. if deleted, err := store.Delete(ref1); err != nil || deleted != true {
  271. t.Fatal("Delete failed")
  272. }
  273. if _, err := store.Get(ref1); err != ErrDoesNotExist {
  274. t.Fatal("Expected ErrDoesNotExist from Get")
  275. }
  276. if deleted, err := store.Delete(ref5); err != nil || deleted != true {
  277. t.Fatal("Delete failed")
  278. }
  279. if _, err := store.Get(ref5); err != ErrDoesNotExist {
  280. t.Fatal("Expected ErrDoesNotExist from Get")
  281. }
  282. if deleted, err := store.Delete(nameOnly); err != nil || deleted != true {
  283. t.Fatal("Delete failed")
  284. }
  285. if _, err := store.Get(nameOnly); err != ErrDoesNotExist {
  286. t.Fatal("Expected ErrDoesNotExist from Get")
  287. }
  288. }
  289. func TestInvalidTags(t *testing.T) {
  290. tmpDir, err := ioutil.TempDir("", "tag-store-test")
  291. defer os.RemoveAll(tmpDir)
  292. store, err := NewReferenceStore(filepath.Join(tmpDir, "repositories.json"))
  293. if err != nil {
  294. t.Fatalf("error creating tag store: %v", err)
  295. }
  296. id := image.ID("sha256:470022b8af682154f57a2163d030eb369549549cba00edc69e1b99b46bb924d6")
  297. // sha256 as repo name
  298. ref, err := ParseNamed("sha256:abc")
  299. if err != nil {
  300. t.Fatal(err)
  301. }
  302. err = store.AddTag(ref, id, true)
  303. if err == nil {
  304. t.Fatalf("expected setting tag %q to fail", ref)
  305. }
  306. // setting digest as a tag
  307. ref, err = ParseNamed("registry@sha256:367eb40fd0330a7e464777121e39d2f5b3e8e23a1e159342e53ab05c9e4d94e6")
  308. if err != nil {
  309. t.Fatal(err)
  310. }
  311. err = store.AddTag(ref, id, true)
  312. if err == nil {
  313. t.Fatalf("expected setting digest %q to fail", ref)
  314. }
  315. }