registry_test.go 25 KB

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