registry_test.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. package registry
  2. import (
  3. "fmt"
  4. "net/http"
  5. "net/url"
  6. "strings"
  7. "testing"
  8. "github.com/docker/docker/utils"
  9. )
  10. var (
  11. token = []string{"fake-token"}
  12. )
  13. const (
  14. imageID = "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d"
  15. REPO = "foo42/bar"
  16. )
  17. func spawnTestRegistrySession(t *testing.T) *Session {
  18. authConfig := &AuthConfig{}
  19. endpoint, err := NewEndpoint(makeIndex("/v1/"))
  20. if err != nil {
  21. t.Fatal(err)
  22. }
  23. r, err := NewSession(authConfig, utils.NewHTTPRequestFactory(), endpoint, true)
  24. if err != nil {
  25. t.Fatal(err)
  26. }
  27. return r
  28. }
  29. func TestPublicSession(t *testing.T) {
  30. authConfig := &AuthConfig{}
  31. getSessionDecorators := func(index *IndexInfo) int {
  32. endpoint, err := NewEndpoint(index)
  33. if err != nil {
  34. t.Fatal(err)
  35. }
  36. r, err := NewSession(authConfig, utils.NewHTTPRequestFactory(), endpoint, true)
  37. if err != nil {
  38. t.Fatal(err)
  39. }
  40. return len(r.reqFactory.GetDecorators())
  41. }
  42. decorators := getSessionDecorators(makeIndex("/v1/"))
  43. assertEqual(t, decorators, 0, "Expected no decorator on http session")
  44. decorators = getSessionDecorators(makeHttpsIndex("/v1/"))
  45. assertNotEqual(t, decorators, 0, "Expected decorator on https session")
  46. decorators = getSessionDecorators(makePublicIndex())
  47. assertEqual(t, decorators, 0, "Expected no decorator on public session")
  48. }
  49. func TestPingRegistryEndpoint(t *testing.T) {
  50. testPing := func(index *IndexInfo, expectedStandalone bool, assertMessage string) {
  51. ep, err := NewEndpoint(index)
  52. if err != nil {
  53. t.Fatal(err)
  54. }
  55. regInfo, err := ep.Ping()
  56. if err != nil {
  57. t.Fatal(err)
  58. }
  59. assertEqual(t, regInfo.Standalone, expectedStandalone, assertMessage)
  60. }
  61. testPing(makeIndex("/v1/"), true, "Expected standalone to be true (default)")
  62. testPing(makeHttpsIndex("/v1/"), true, "Expected standalone to be true (default)")
  63. testPing(makePublicIndex(), false, "Expected standalone to be false for public index")
  64. }
  65. func TestEndpoint(t *testing.T) {
  66. // Simple wrapper to fail test if err != nil
  67. expandEndpoint := func(index *IndexInfo) *Endpoint {
  68. endpoint, err := NewEndpoint(index)
  69. if err != nil {
  70. t.Fatal(err)
  71. }
  72. return endpoint
  73. }
  74. assertInsecureIndex := func(index *IndexInfo) {
  75. index.Secure = true
  76. _, err := NewEndpoint(index)
  77. assertNotEqual(t, err, nil, index.Name+": Expected error for insecure index")
  78. assertEqual(t, strings.Contains(err.Error(), "insecure-registry"), true, index.Name+": Expected insecure-registry error for insecure index")
  79. index.Secure = false
  80. }
  81. assertSecureIndex := func(index *IndexInfo) {
  82. index.Secure = true
  83. _, err := NewEndpoint(index)
  84. assertNotEqual(t, err, nil, index.Name+": Expected cert error for secure index")
  85. assertEqual(t, strings.Contains(err.Error(), "certificate signed by unknown authority"), true, index.Name+": Expected cert error for secure index")
  86. index.Secure = false
  87. }
  88. index := &IndexInfo{}
  89. index.Name = makeURL("/v1/")
  90. endpoint := expandEndpoint(index)
  91. assertEqual(t, endpoint.String(), index.Name, "Expected endpoint to be "+index.Name)
  92. if endpoint.Version != APIVersion1 {
  93. t.Fatal("Expected endpoint to be v1")
  94. }
  95. assertInsecureIndex(index)
  96. index.Name = makeURL("")
  97. endpoint = expandEndpoint(index)
  98. assertEqual(t, endpoint.String(), index.Name+"/v1/", index.Name+": Expected endpoint to be "+index.Name+"/v1/")
  99. if endpoint.Version != APIVersion1 {
  100. t.Fatal("Expected endpoint to be v1")
  101. }
  102. assertInsecureIndex(index)
  103. httpURL := makeURL("")
  104. index.Name = strings.SplitN(httpURL, "://", 2)[1]
  105. endpoint = expandEndpoint(index)
  106. assertEqual(t, endpoint.String(), httpURL+"/v1/", index.Name+": Expected endpoint to be "+httpURL+"/v1/")
  107. if endpoint.Version != APIVersion1 {
  108. t.Fatal("Expected endpoint to be v1")
  109. }
  110. assertInsecureIndex(index)
  111. index.Name = makeHttpsURL("/v1/")
  112. endpoint = expandEndpoint(index)
  113. assertEqual(t, endpoint.String(), index.Name, "Expected endpoint to be "+index.Name)
  114. if endpoint.Version != APIVersion1 {
  115. t.Fatal("Expected endpoint to be v1")
  116. }
  117. assertSecureIndex(index)
  118. index.Name = makeHttpsURL("")
  119. endpoint = expandEndpoint(index)
  120. assertEqual(t, endpoint.String(), index.Name+"/v1/", index.Name+": Expected endpoint to be "+index.Name+"/v1/")
  121. if endpoint.Version != APIVersion1 {
  122. t.Fatal("Expected endpoint to be v1")
  123. }
  124. assertSecureIndex(index)
  125. httpsURL := makeHttpsURL("")
  126. index.Name = strings.SplitN(httpsURL, "://", 2)[1]
  127. endpoint = expandEndpoint(index)
  128. assertEqual(t, endpoint.String(), httpsURL+"/v1/", index.Name+": Expected endpoint to be "+httpsURL+"/v1/")
  129. if endpoint.Version != APIVersion1 {
  130. t.Fatal("Expected endpoint to be v1")
  131. }
  132. assertSecureIndex(index)
  133. badEndpoints := []string{
  134. "http://127.0.0.1/v1/",
  135. "https://127.0.0.1/v1/",
  136. "http://127.0.0.1",
  137. "https://127.0.0.1",
  138. "127.0.0.1",
  139. }
  140. for _, address := range badEndpoints {
  141. index.Name = address
  142. _, err := NewEndpoint(index)
  143. checkNotEqual(t, err, nil, "Expected error while expanding bad endpoint")
  144. }
  145. }
  146. func TestGetRemoteHistory(t *testing.T) {
  147. r := spawnTestRegistrySession(t)
  148. hist, err := r.GetRemoteHistory(imageID, makeURL("/v1/"), token)
  149. if err != nil {
  150. t.Fatal(err)
  151. }
  152. assertEqual(t, len(hist), 2, "Expected 2 images in history")
  153. assertEqual(t, hist[0], imageID, "Expected "+imageID+"as first ancestry")
  154. assertEqual(t, hist[1], "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
  155. "Unexpected second ancestry")
  156. }
  157. func TestLookupRemoteImage(t *testing.T) {
  158. r := spawnTestRegistrySession(t)
  159. err := r.LookupRemoteImage(imageID, makeURL("/v1/"), token)
  160. assertEqual(t, err, nil, "Expected error of remote lookup to nil")
  161. if err := r.LookupRemoteImage("abcdef", makeURL("/v1/"), token); err == nil {
  162. t.Fatal("Expected error of remote lookup to not nil")
  163. }
  164. }
  165. func TestGetRemoteImageJSON(t *testing.T) {
  166. r := spawnTestRegistrySession(t)
  167. json, size, err := r.GetRemoteImageJSON(imageID, makeURL("/v1/"), token)
  168. if err != nil {
  169. t.Fatal(err)
  170. }
  171. assertEqual(t, size, 154, "Expected size 154")
  172. if len(json) <= 0 {
  173. t.Fatal("Expected non-empty json")
  174. }
  175. _, _, err = r.GetRemoteImageJSON("abcdef", makeURL("/v1/"), token)
  176. if err == nil {
  177. t.Fatal("Expected image not found error")
  178. }
  179. }
  180. func TestGetRemoteImageLayer(t *testing.T) {
  181. r := spawnTestRegistrySession(t)
  182. data, err := r.GetRemoteImageLayer(imageID, makeURL("/v1/"), token, 0)
  183. if err != nil {
  184. t.Fatal(err)
  185. }
  186. if data == nil {
  187. t.Fatal("Expected non-nil data result")
  188. }
  189. _, err = r.GetRemoteImageLayer("abcdef", makeURL("/v1/"), token, 0)
  190. if err == nil {
  191. t.Fatal("Expected image not found error")
  192. }
  193. }
  194. func TestGetRemoteTags(t *testing.T) {
  195. r := spawnTestRegistrySession(t)
  196. tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, REPO, token)
  197. if err != nil {
  198. t.Fatal(err)
  199. }
  200. assertEqual(t, len(tags), 1, "Expected one tag")
  201. assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID)
  202. _, err = r.GetRemoteTags([]string{makeURL("/v1/")}, "foo42/baz", token)
  203. if err == nil {
  204. t.Fatal("Expected error when fetching tags for bogus repo")
  205. }
  206. }
  207. func TestGetRepositoryData(t *testing.T) {
  208. r := spawnTestRegistrySession(t)
  209. parsedURL, err := url.Parse(makeURL("/v1/"))
  210. if err != nil {
  211. t.Fatal(err)
  212. }
  213. host := "http://" + parsedURL.Host + "/v1/"
  214. data, err := r.GetRepositoryData("foo42/bar")
  215. if err != nil {
  216. t.Fatal(err)
  217. }
  218. assertEqual(t, len(data.ImgList), 2, "Expected 2 images in ImgList")
  219. assertEqual(t, len(data.Endpoints), 2,
  220. fmt.Sprintf("Expected 2 endpoints in Endpoints, found %d instead", len(data.Endpoints)))
  221. assertEqual(t, data.Endpoints[0], host,
  222. fmt.Sprintf("Expected first endpoint to be %s but found %s instead", host, data.Endpoints[0]))
  223. assertEqual(t, data.Endpoints[1], "http://test.example.com/v1/",
  224. fmt.Sprintf("Expected first endpoint to be http://test.example.com/v1/ but found %s instead", data.Endpoints[1]))
  225. }
  226. func TestPushImageJSONRegistry(t *testing.T) {
  227. r := spawnTestRegistrySession(t)
  228. imgData := &ImgData{
  229. ID: "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
  230. Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37",
  231. }
  232. err := r.PushImageJSONRegistry(imgData, []byte{0x42, 0xdf, 0x0}, makeURL("/v1/"), token)
  233. if err != nil {
  234. t.Fatal(err)
  235. }
  236. }
  237. func TestPushImageLayerRegistry(t *testing.T) {
  238. r := spawnTestRegistrySession(t)
  239. layer := strings.NewReader("")
  240. _, _, err := r.PushImageLayerRegistry(imageID, layer, makeURL("/v1/"), token, []byte{})
  241. if err != nil {
  242. t.Fatal(err)
  243. }
  244. }
  245. func TestValidateRepositoryName(t *testing.T) {
  246. validRepoNames := []string{
  247. "docker/docker",
  248. "library/debian",
  249. "debian",
  250. "docker.io/docker/docker",
  251. "docker.io/library/debian",
  252. "docker.io/debian",
  253. "index.docker.io/docker/docker",
  254. "index.docker.io/library/debian",
  255. "index.docker.io/debian",
  256. "127.0.0.1:5000/docker/docker",
  257. "127.0.0.1:5000/library/debian",
  258. "127.0.0.1:5000/debian",
  259. "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
  260. }
  261. invalidRepoNames := []string{
  262. "https://github.com/docker/docker",
  263. "docker/Docker",
  264. "docker///docker",
  265. "docker.io/docker/Docker",
  266. "docker.io/docker///docker",
  267. "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
  268. "docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
  269. }
  270. for _, name := range invalidRepoNames {
  271. err := ValidateRepositoryName(name)
  272. assertNotEqual(t, err, nil, "Expected invalid repo name: "+name)
  273. }
  274. for _, name := range validRepoNames {
  275. err := ValidateRepositoryName(name)
  276. assertEqual(t, err, nil, "Expected valid repo name: "+name)
  277. }
  278. err := ValidateRepositoryName(invalidRepoNames[0])
  279. assertEqual(t, err, ErrInvalidRepositoryName, "Expected ErrInvalidRepositoryName: "+invalidRepoNames[0])
  280. }
  281. func TestParseRepositoryInfo(t *testing.T) {
  282. expectedRepoInfos := map[string]RepositoryInfo{
  283. "fooo/bar": {
  284. Index: &IndexInfo{
  285. Name: IndexServerName(),
  286. Official: true,
  287. },
  288. RemoteName: "fooo/bar",
  289. LocalName: "fooo/bar",
  290. CanonicalName: "fooo/bar",
  291. Official: false,
  292. },
  293. "library/ubuntu": {
  294. Index: &IndexInfo{
  295. Name: IndexServerName(),
  296. Official: true,
  297. },
  298. RemoteName: "library/ubuntu",
  299. LocalName: "ubuntu",
  300. CanonicalName: "ubuntu",
  301. Official: true,
  302. },
  303. "nonlibrary/ubuntu": {
  304. Index: &IndexInfo{
  305. Name: IndexServerName(),
  306. Official: true,
  307. },
  308. RemoteName: "nonlibrary/ubuntu",
  309. LocalName: "nonlibrary/ubuntu",
  310. CanonicalName: "nonlibrary/ubuntu",
  311. Official: false,
  312. },
  313. "ubuntu": {
  314. Index: &IndexInfo{
  315. Name: IndexServerName(),
  316. Official: true,
  317. },
  318. RemoteName: "library/ubuntu",
  319. LocalName: "ubuntu",
  320. CanonicalName: "ubuntu",
  321. Official: true,
  322. },
  323. "other/library": {
  324. Index: &IndexInfo{
  325. Name: IndexServerName(),
  326. Official: true,
  327. },
  328. RemoteName: "other/library",
  329. LocalName: "other/library",
  330. CanonicalName: "other/library",
  331. Official: false,
  332. },
  333. "127.0.0.1:8000/private/moonbase": {
  334. Index: &IndexInfo{
  335. Name: "127.0.0.1:8000",
  336. Official: false,
  337. },
  338. RemoteName: "private/moonbase",
  339. LocalName: "127.0.0.1:8000/private/moonbase",
  340. CanonicalName: "127.0.0.1:8000/private/moonbase",
  341. Official: false,
  342. },
  343. "127.0.0.1:8000/privatebase": {
  344. Index: &IndexInfo{
  345. Name: "127.0.0.1:8000",
  346. Official: false,
  347. },
  348. RemoteName: "privatebase",
  349. LocalName: "127.0.0.1:8000/privatebase",
  350. CanonicalName: "127.0.0.1:8000/privatebase",
  351. Official: false,
  352. },
  353. "localhost:8000/private/moonbase": {
  354. Index: &IndexInfo{
  355. Name: "localhost:8000",
  356. Official: false,
  357. },
  358. RemoteName: "private/moonbase",
  359. LocalName: "localhost:8000/private/moonbase",
  360. CanonicalName: "localhost:8000/private/moonbase",
  361. Official: false,
  362. },
  363. "localhost:8000/privatebase": {
  364. Index: &IndexInfo{
  365. Name: "localhost:8000",
  366. Official: false,
  367. },
  368. RemoteName: "privatebase",
  369. LocalName: "localhost:8000/privatebase",
  370. CanonicalName: "localhost:8000/privatebase",
  371. Official: false,
  372. },
  373. "example.com/private/moonbase": {
  374. Index: &IndexInfo{
  375. Name: "example.com",
  376. Official: false,
  377. },
  378. RemoteName: "private/moonbase",
  379. LocalName: "example.com/private/moonbase",
  380. CanonicalName: "example.com/private/moonbase",
  381. Official: false,
  382. },
  383. "example.com/privatebase": {
  384. Index: &IndexInfo{
  385. Name: "example.com",
  386. Official: false,
  387. },
  388. RemoteName: "privatebase",
  389. LocalName: "example.com/privatebase",
  390. CanonicalName: "example.com/privatebase",
  391. Official: false,
  392. },
  393. "example.com:8000/private/moonbase": {
  394. Index: &IndexInfo{
  395. Name: "example.com:8000",
  396. Official: false,
  397. },
  398. RemoteName: "private/moonbase",
  399. LocalName: "example.com:8000/private/moonbase",
  400. CanonicalName: "example.com:8000/private/moonbase",
  401. Official: false,
  402. },
  403. "example.com:8000/privatebase": {
  404. Index: &IndexInfo{
  405. Name: "example.com:8000",
  406. Official: false,
  407. },
  408. RemoteName: "privatebase",
  409. LocalName: "example.com:8000/privatebase",
  410. CanonicalName: "example.com:8000/privatebase",
  411. Official: false,
  412. },
  413. "localhost/private/moonbase": {
  414. Index: &IndexInfo{
  415. Name: "localhost",
  416. Official: false,
  417. },
  418. RemoteName: "private/moonbase",
  419. LocalName: "localhost/private/moonbase",
  420. CanonicalName: "localhost/private/moonbase",
  421. Official: false,
  422. },
  423. "localhost/privatebase": {
  424. Index: &IndexInfo{
  425. Name: "localhost",
  426. Official: false,
  427. },
  428. RemoteName: "privatebase",
  429. LocalName: "localhost/privatebase",
  430. CanonicalName: "localhost/privatebase",
  431. Official: false,
  432. },
  433. IndexServerName() + "/public/moonbase": {
  434. Index: &IndexInfo{
  435. Name: IndexServerName(),
  436. Official: true,
  437. },
  438. RemoteName: "public/moonbase",
  439. LocalName: "public/moonbase",
  440. CanonicalName: "public/moonbase",
  441. Official: false,
  442. },
  443. "index." + IndexServerName() + "/public/moonbase": {
  444. Index: &IndexInfo{
  445. Name: IndexServerName(),
  446. Official: true,
  447. },
  448. RemoteName: "public/moonbase",
  449. LocalName: "public/moonbase",
  450. CanonicalName: "public/moonbase",
  451. Official: false,
  452. },
  453. IndexServerName() + "/public/moonbase": {
  454. Index: &IndexInfo{
  455. Name: IndexServerName(),
  456. Official: true,
  457. },
  458. RemoteName: "public/moonbase",
  459. LocalName: "public/moonbase",
  460. CanonicalName: "public/moonbase",
  461. Official: false,
  462. },
  463. "ubuntu-12.04-base": {
  464. Index: &IndexInfo{
  465. Name: IndexServerName(),
  466. Official: true,
  467. },
  468. RemoteName: "library/ubuntu-12.04-base",
  469. LocalName: "ubuntu-12.04-base",
  470. CanonicalName: "ubuntu-12.04-base",
  471. Official: true,
  472. },
  473. IndexServerName() + "/ubuntu-12.04-base": {
  474. Index: &IndexInfo{
  475. Name: IndexServerName(),
  476. Official: true,
  477. },
  478. RemoteName: "library/ubuntu-12.04-base",
  479. LocalName: "ubuntu-12.04-base",
  480. CanonicalName: "ubuntu-12.04-base",
  481. Official: true,
  482. },
  483. IndexServerName() + "/ubuntu-12.04-base": {
  484. Index: &IndexInfo{
  485. Name: IndexServerName(),
  486. Official: true,
  487. },
  488. RemoteName: "library/ubuntu-12.04-base",
  489. LocalName: "ubuntu-12.04-base",
  490. CanonicalName: "ubuntu-12.04-base",
  491. Official: true,
  492. },
  493. "index." + IndexServerName() + "/ubuntu-12.04-base": {
  494. Index: &IndexInfo{
  495. Name: IndexServerName(),
  496. Official: true,
  497. },
  498. RemoteName: "library/ubuntu-12.04-base",
  499. LocalName: "ubuntu-12.04-base",
  500. CanonicalName: "ubuntu-12.04-base",
  501. Official: true,
  502. },
  503. }
  504. for reposName, expectedRepoInfo := range expectedRepoInfos {
  505. repoInfo, err := ParseRepositoryInfo(reposName)
  506. if err != nil {
  507. t.Error(err)
  508. } else {
  509. checkEqual(t, repoInfo.Index.Name, expectedRepoInfo.Index.Name, reposName)
  510. checkEqual(t, repoInfo.RemoteName, expectedRepoInfo.RemoteName, reposName)
  511. checkEqual(t, repoInfo.LocalName, expectedRepoInfo.LocalName, reposName)
  512. checkEqual(t, repoInfo.CanonicalName, expectedRepoInfo.CanonicalName, reposName)
  513. checkEqual(t, repoInfo.Index.Official, expectedRepoInfo.Index.Official, reposName)
  514. checkEqual(t, repoInfo.Official, expectedRepoInfo.Official, reposName)
  515. }
  516. }
  517. }
  518. func TestNewIndexInfo(t *testing.T) {
  519. testIndexInfo := func(config *ServiceConfig, expectedIndexInfos map[string]*IndexInfo) {
  520. for indexName, expectedIndexInfo := range expectedIndexInfos {
  521. index, err := config.NewIndexInfo(indexName)
  522. if err != nil {
  523. t.Fatal(err)
  524. } else {
  525. checkEqual(t, index.Name, expectedIndexInfo.Name, indexName+" name")
  526. checkEqual(t, index.Official, expectedIndexInfo.Official, indexName+" is official")
  527. checkEqual(t, index.Secure, expectedIndexInfo.Secure, indexName+" is secure")
  528. checkEqual(t, len(index.Mirrors), len(expectedIndexInfo.Mirrors), indexName+" mirrors")
  529. }
  530. }
  531. }
  532. config := NewServiceConfig(nil)
  533. noMirrors := make([]string, 0)
  534. expectedIndexInfos := map[string]*IndexInfo{
  535. IndexServerName(): {
  536. Name: IndexServerName(),
  537. Official: true,
  538. Secure: true,
  539. Mirrors: noMirrors,
  540. },
  541. "index." + IndexServerName(): {
  542. Name: IndexServerName(),
  543. Official: true,
  544. Secure: true,
  545. Mirrors: noMirrors,
  546. },
  547. "example.com": {
  548. Name: "example.com",
  549. Official: false,
  550. Secure: true,
  551. Mirrors: noMirrors,
  552. },
  553. "127.0.0.1:5000": {
  554. Name: "127.0.0.1:5000",
  555. Official: false,
  556. Secure: false,
  557. Mirrors: noMirrors,
  558. },
  559. }
  560. testIndexInfo(config, expectedIndexInfos)
  561. publicMirrors := []string{"http://mirror1.local", "http://mirror2.local"}
  562. config = makeServiceConfig(publicMirrors, []string{"example.com"})
  563. expectedIndexInfos = map[string]*IndexInfo{
  564. IndexServerName(): {
  565. Name: IndexServerName(),
  566. Official: true,
  567. Secure: true,
  568. Mirrors: publicMirrors,
  569. },
  570. "index." + IndexServerName(): {
  571. Name: IndexServerName(),
  572. Official: true,
  573. Secure: true,
  574. Mirrors: publicMirrors,
  575. },
  576. "example.com": {
  577. Name: "example.com",
  578. Official: false,
  579. Secure: false,
  580. Mirrors: noMirrors,
  581. },
  582. "example.com:5000": {
  583. Name: "example.com:5000",
  584. Official: false,
  585. Secure: true,
  586. Mirrors: noMirrors,
  587. },
  588. "127.0.0.1": {
  589. Name: "127.0.0.1",
  590. Official: false,
  591. Secure: false,
  592. Mirrors: noMirrors,
  593. },
  594. "127.0.0.1:5000": {
  595. Name: "127.0.0.1:5000",
  596. Official: false,
  597. Secure: false,
  598. Mirrors: noMirrors,
  599. },
  600. "other.com": {
  601. Name: "other.com",
  602. Official: false,
  603. Secure: true,
  604. Mirrors: noMirrors,
  605. },
  606. }
  607. testIndexInfo(config, expectedIndexInfos)
  608. config = makeServiceConfig(nil, []string{"42.42.0.0/16"})
  609. expectedIndexInfos = map[string]*IndexInfo{
  610. "example.com": {
  611. Name: "example.com",
  612. Official: false,
  613. Secure: false,
  614. Mirrors: noMirrors,
  615. },
  616. "example.com:5000": {
  617. Name: "example.com:5000",
  618. Official: false,
  619. Secure: false,
  620. Mirrors: noMirrors,
  621. },
  622. "127.0.0.1": {
  623. Name: "127.0.0.1",
  624. Official: false,
  625. Secure: false,
  626. Mirrors: noMirrors,
  627. },
  628. "127.0.0.1:5000": {
  629. Name: "127.0.0.1:5000",
  630. Official: false,
  631. Secure: false,
  632. Mirrors: noMirrors,
  633. },
  634. "other.com": {
  635. Name: "other.com",
  636. Official: false,
  637. Secure: true,
  638. Mirrors: noMirrors,
  639. },
  640. }
  641. testIndexInfo(config, expectedIndexInfos)
  642. }
  643. func TestPushRegistryTag(t *testing.T) {
  644. r := spawnTestRegistrySession(t)
  645. err := r.PushRegistryTag("foo42/bar", imageID, "stable", makeURL("/v1/"), token)
  646. if err != nil {
  647. t.Fatal(err)
  648. }
  649. }
  650. func TestPushImageJSONIndex(t *testing.T) {
  651. r := spawnTestRegistrySession(t)
  652. imgData := []*ImgData{
  653. {
  654. ID: "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
  655. Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37",
  656. },
  657. {
  658. ID: "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d",
  659. Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
  660. },
  661. }
  662. repoData, err := r.PushImageJSONIndex("foo42/bar", imgData, false, nil)
  663. if err != nil {
  664. t.Fatal(err)
  665. }
  666. if repoData == nil {
  667. t.Fatal("Expected RepositoryData object")
  668. }
  669. repoData, err = r.PushImageJSONIndex("foo42/bar", imgData, true, []string{r.indexEndpoint.String()})
  670. if err != nil {
  671. t.Fatal(err)
  672. }
  673. if repoData == nil {
  674. t.Fatal("Expected RepositoryData object")
  675. }
  676. }
  677. func TestSearchRepositories(t *testing.T) {
  678. r := spawnTestRegistrySession(t)
  679. results, err := r.SearchRepositories("fakequery")
  680. if err != nil {
  681. t.Fatal(err)
  682. }
  683. if results == nil {
  684. t.Fatal("Expected non-nil SearchResults object")
  685. }
  686. assertEqual(t, results.NumResults, 1, "Expected 1 search results")
  687. assertEqual(t, results.Query, "fakequery", "Expected 'fakequery' as query")
  688. assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' a ot hae 42 stars")
  689. }
  690. func TestValidRemoteName(t *testing.T) {
  691. validRepositoryNames := []string{
  692. // Sanity check.
  693. "docker/docker",
  694. // Allow 64-character non-hexadecimal names (hexadecimal names are forbidden).
  695. "thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
  696. // Allow embedded hyphens.
  697. "docker-rules/docker",
  698. // Allow underscores everywhere (as opposed to hyphens).
  699. "____/____",
  700. }
  701. for _, repositoryName := range validRepositoryNames {
  702. if err := validateRemoteName(repositoryName); err != nil {
  703. t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
  704. }
  705. }
  706. invalidRepositoryNames := []string{
  707. // Disallow capital letters.
  708. "docker/Docker",
  709. // Only allow one slash.
  710. "docker///docker",
  711. // Disallow 64-character hexadecimal.
  712. "1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
  713. // Disallow leading and trailing hyphens in namespace.
  714. "-docker/docker",
  715. "docker-/docker",
  716. "-docker-/docker",
  717. // Disallow consecutive hyphens.
  718. "dock--er/docker",
  719. // Namespace too short.
  720. "doc/docker",
  721. // No repository.
  722. "docker/",
  723. }
  724. for _, repositoryName := range invalidRepositoryNames {
  725. if err := validateRemoteName(repositoryName); err == nil {
  726. t.Errorf("Repository name should be invalid: %v", repositoryName)
  727. }
  728. }
  729. }
  730. func TestTrustedLocation(t *testing.T) {
  731. for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} {
  732. req, _ := http.NewRequest("GET", url, nil)
  733. if trustedLocation(req) == true {
  734. t.Fatalf("'%s' shouldn't be detected as a trusted location", url)
  735. }
  736. }
  737. for _, url := range []string{"https://docker.io", "https://test.docker.com:80"} {
  738. req, _ := http.NewRequest("GET", url, nil)
  739. if trustedLocation(req) == false {
  740. t.Fatalf("'%s' should be detected as a trusted location", url)
  741. }
  742. }
  743. }
  744. func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) {
  745. for _, urls := range [][]string{
  746. {"http://docker.io", "https://docker.com"},
  747. {"https://foo.docker.io:7777", "http://bar.docker.com"},
  748. {"https://foo.docker.io", "https://example.com"},
  749. } {
  750. reqFrom, _ := http.NewRequest("GET", urls[0], nil)
  751. reqFrom.Header.Add("Content-Type", "application/json")
  752. reqFrom.Header.Add("Authorization", "super_secret")
  753. reqTo, _ := http.NewRequest("GET", urls[1], nil)
  754. AddRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})
  755. if len(reqTo.Header) != 1 {
  756. t.Fatalf("Expected 1 headers, got %d", len(reqTo.Header))
  757. }
  758. if reqTo.Header.Get("Content-Type") != "application/json" {
  759. t.Fatal("'Content-Type' should be 'application/json'")
  760. }
  761. if reqTo.Header.Get("Authorization") != "" {
  762. t.Fatal("'Authorization' should be empty")
  763. }
  764. }
  765. for _, urls := range [][]string{
  766. {"https://docker.io", "https://docker.com"},
  767. {"https://foo.docker.io:7777", "https://bar.docker.com"},
  768. } {
  769. reqFrom, _ := http.NewRequest("GET", urls[0], nil)
  770. reqFrom.Header.Add("Content-Type", "application/json")
  771. reqFrom.Header.Add("Authorization", "super_secret")
  772. reqTo, _ := http.NewRequest("GET", urls[1], nil)
  773. AddRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})
  774. if len(reqTo.Header) != 2 {
  775. t.Fatalf("Expected 2 headers, got %d", len(reqTo.Header))
  776. }
  777. if reqTo.Header.Get("Content-Type") != "application/json" {
  778. t.Fatal("'Content-Type' should be 'application/json'")
  779. }
  780. if reqTo.Header.Get("Authorization") != "super_secret" {
  781. t.Fatal("'Authorization' should be 'super_secret'")
  782. }
  783. }
  784. }
  785. func TestIsSecureIndex(t *testing.T) {
  786. tests := []struct {
  787. addr string
  788. insecureRegistries []string
  789. expected bool
  790. }{
  791. {IndexServerName(), nil, true},
  792. {"example.com", []string{}, true},
  793. {"example.com", []string{"example.com"}, false},
  794. {"localhost", []string{"localhost:5000"}, false},
  795. {"localhost:5000", []string{"localhost:5000"}, false},
  796. {"localhost", []string{"example.com"}, false},
  797. {"127.0.0.1:5000", []string{"127.0.0.1:5000"}, false},
  798. {"localhost", nil, false},
  799. {"localhost:5000", nil, false},
  800. {"127.0.0.1", nil, false},
  801. {"localhost", []string{"example.com"}, false},
  802. {"127.0.0.1", []string{"example.com"}, false},
  803. {"example.com", nil, true},
  804. {"example.com", []string{"example.com"}, false},
  805. {"127.0.0.1", []string{"example.com"}, false},
  806. {"127.0.0.1:5000", []string{"example.com"}, false},
  807. {"example.com:5000", []string{"42.42.0.0/16"}, false},
  808. {"example.com", []string{"42.42.0.0/16"}, false},
  809. {"example.com:5000", []string{"42.42.42.42/8"}, false},
  810. {"127.0.0.1:5000", []string{"127.0.0.0/8"}, false},
  811. {"42.42.42.42:5000", []string{"42.1.1.1/8"}, false},
  812. {"invalid.domain.com", []string{"42.42.0.0/16"}, true},
  813. {"invalid.domain.com", []string{"invalid.domain.com"}, false},
  814. {"invalid.domain.com:5000", []string{"invalid.domain.com"}, true},
  815. {"invalid.domain.com:5000", []string{"invalid.domain.com:5000"}, false},
  816. }
  817. for _, tt := range tests {
  818. config := makeServiceConfig(nil, tt.insecureRegistries)
  819. if sec := config.isSecureIndex(tt.addr); sec != tt.expected {
  820. t.Errorf("isSecureIndex failed for %q %v, expected %v got %v", tt.addr, tt.insecureRegistries, tt.expected, sec)
  821. }
  822. }
  823. }