registry_test.go 27 KB

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