registry_test.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929
  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/reference"
  10. "github.com/docker/distribution/registry/client/transport"
  11. "github.com/docker/docker/api/types"
  12. registrytypes "github.com/docker/docker/api/types/registry"
  13. "github.com/stretchr/testify/assert"
  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), Headers(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 := emptyServiceConfig
  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. var err error
  533. config, err = makeServiceConfig(publicMirrors, []string{"example.com"})
  534. if err != nil {
  535. t.Fatal(err)
  536. }
  537. expectedIndexInfos = map[string]*registrytypes.IndexInfo{
  538. IndexName: {
  539. Name: IndexName,
  540. Official: true,
  541. Secure: true,
  542. Mirrors: publicMirrors,
  543. },
  544. "index." + IndexName: {
  545. Name: IndexName,
  546. Official: true,
  547. Secure: true,
  548. Mirrors: publicMirrors,
  549. },
  550. "example.com": {
  551. Name: "example.com",
  552. Official: false,
  553. Secure: false,
  554. Mirrors: noMirrors,
  555. },
  556. "example.com:5000": {
  557. Name: "example.com:5000",
  558. Official: false,
  559. Secure: true,
  560. Mirrors: noMirrors,
  561. },
  562. "127.0.0.1": {
  563. Name: "127.0.0.1",
  564. Official: false,
  565. Secure: false,
  566. Mirrors: noMirrors,
  567. },
  568. "127.0.0.1:5000": {
  569. Name: "127.0.0.1:5000",
  570. Official: false,
  571. Secure: false,
  572. Mirrors: noMirrors,
  573. },
  574. "other.com": {
  575. Name: "other.com",
  576. Official: false,
  577. Secure: true,
  578. Mirrors: noMirrors,
  579. },
  580. }
  581. testIndexInfo(config, expectedIndexInfos)
  582. config, err = makeServiceConfig(nil, []string{"42.42.0.0/16"})
  583. if err != nil {
  584. t.Fatal(err)
  585. }
  586. expectedIndexInfos = map[string]*registrytypes.IndexInfo{
  587. "example.com": {
  588. Name: "example.com",
  589. Official: false,
  590. Secure: false,
  591. Mirrors: noMirrors,
  592. },
  593. "example.com:5000": {
  594. Name: "example.com:5000",
  595. Official: false,
  596. Secure: false,
  597. Mirrors: noMirrors,
  598. },
  599. "127.0.0.1": {
  600. Name: "127.0.0.1",
  601. Official: false,
  602. Secure: false,
  603. Mirrors: noMirrors,
  604. },
  605. "127.0.0.1:5000": {
  606. Name: "127.0.0.1:5000",
  607. Official: false,
  608. Secure: false,
  609. Mirrors: noMirrors,
  610. },
  611. "other.com": {
  612. Name: "other.com",
  613. Official: false,
  614. Secure: true,
  615. Mirrors: noMirrors,
  616. },
  617. }
  618. testIndexInfo(config, expectedIndexInfos)
  619. }
  620. func TestMirrorEndpointLookup(t *testing.T) {
  621. containsMirror := func(endpoints []APIEndpoint) bool {
  622. for _, pe := range endpoints {
  623. if pe.URL.Host == "my.mirror" {
  624. return true
  625. }
  626. }
  627. return false
  628. }
  629. cfg, err := makeServiceConfig([]string{"https://my.mirror"}, nil)
  630. if err != nil {
  631. t.Fatal(err)
  632. }
  633. s := DefaultService{config: cfg}
  634. imageName, err := reference.WithName(IndexName + "/test/image")
  635. if err != nil {
  636. t.Error(err)
  637. }
  638. pushAPIEndpoints, err := s.LookupPushEndpoints(reference.Domain(imageName))
  639. if err != nil {
  640. t.Fatal(err)
  641. }
  642. if containsMirror(pushAPIEndpoints) {
  643. t.Fatal("Push endpoint should not contain mirror")
  644. }
  645. pullAPIEndpoints, err := s.LookupPullEndpoints(reference.Domain(imageName))
  646. if err != nil {
  647. t.Fatal(err)
  648. }
  649. if !containsMirror(pullAPIEndpoints) {
  650. t.Fatal("Pull endpoint should contain mirror")
  651. }
  652. }
  653. func TestPushRegistryTag(t *testing.T) {
  654. r := spawnTestRegistrySession(t)
  655. repoRef, err := reference.ParseNormalizedNamed(REPO)
  656. if err != nil {
  657. t.Fatal(err)
  658. }
  659. err = r.PushRegistryTag(repoRef, imageID, "stable", makeURL("/v1/"))
  660. if err != nil {
  661. t.Fatal(err)
  662. }
  663. }
  664. func TestPushImageJSONIndex(t *testing.T) {
  665. r := spawnTestRegistrySession(t)
  666. imgData := []*ImgData{
  667. {
  668. ID: "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
  669. Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37",
  670. },
  671. {
  672. ID: "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d",
  673. Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
  674. },
  675. }
  676. repoRef, err := reference.ParseNormalizedNamed(REPO)
  677. if err != nil {
  678. t.Fatal(err)
  679. }
  680. repoData, err := r.PushImageJSONIndex(repoRef, imgData, false, nil)
  681. if err != nil {
  682. t.Fatal(err)
  683. }
  684. if repoData == nil {
  685. t.Fatal("Expected RepositoryData object")
  686. }
  687. repoData, err = r.PushImageJSONIndex(repoRef, imgData, true, []string{r.indexEndpoint.String()})
  688. if err != nil {
  689. t.Fatal(err)
  690. }
  691. if repoData == nil {
  692. t.Fatal("Expected RepositoryData object")
  693. }
  694. }
  695. func TestSearchRepositories(t *testing.T) {
  696. r := spawnTestRegistrySession(t)
  697. results, err := r.SearchRepositories("fakequery", 25)
  698. if err != nil {
  699. t.Fatal(err)
  700. }
  701. if results == nil {
  702. t.Fatal("Expected non-nil SearchResults object")
  703. }
  704. assertEqual(t, results.NumResults, 1, "Expected 1 search results")
  705. assertEqual(t, results.Query, "fakequery", "Expected 'fakequery' as query")
  706. assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' to have 42 stars")
  707. }
  708. func TestTrustedLocation(t *testing.T) {
  709. for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} {
  710. req, _ := http.NewRequest("GET", url, nil)
  711. assert.False(t, trustedLocation(req))
  712. }
  713. for _, url := range []string{"https://docker.io", "https://test.docker.com:80"} {
  714. req, _ := http.NewRequest("GET", url, nil)
  715. assert.True(t, trustedLocation(req))
  716. }
  717. }
  718. func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) {
  719. for _, urls := range [][]string{
  720. {"http://docker.io", "https://docker.com"},
  721. {"https://foo.docker.io:7777", "http://bar.docker.com"},
  722. {"https://foo.docker.io", "https://example.com"},
  723. } {
  724. reqFrom, _ := http.NewRequest("GET", urls[0], nil)
  725. reqFrom.Header.Add("Content-Type", "application/json")
  726. reqFrom.Header.Add("Authorization", "super_secret")
  727. reqTo, _ := http.NewRequest("GET", urls[1], nil)
  728. addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})
  729. if len(reqTo.Header) != 1 {
  730. t.Fatalf("Expected 1 headers, got %d", len(reqTo.Header))
  731. }
  732. if reqTo.Header.Get("Content-Type") != "application/json" {
  733. t.Fatal("'Content-Type' should be 'application/json'")
  734. }
  735. if reqTo.Header.Get("Authorization") != "" {
  736. t.Fatal("'Authorization' should be empty")
  737. }
  738. }
  739. for _, urls := range [][]string{
  740. {"https://docker.io", "https://docker.com"},
  741. {"https://foo.docker.io:7777", "https://bar.docker.com"},
  742. } {
  743. reqFrom, _ := http.NewRequest("GET", urls[0], nil)
  744. reqFrom.Header.Add("Content-Type", "application/json")
  745. reqFrom.Header.Add("Authorization", "super_secret")
  746. reqTo, _ := http.NewRequest("GET", urls[1], nil)
  747. addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})
  748. if len(reqTo.Header) != 2 {
  749. t.Fatalf("Expected 2 headers, got %d", len(reqTo.Header))
  750. }
  751. if reqTo.Header.Get("Content-Type") != "application/json" {
  752. t.Fatal("'Content-Type' should be 'application/json'")
  753. }
  754. if reqTo.Header.Get("Authorization") != "super_secret" {
  755. t.Fatal("'Authorization' should be 'super_secret'")
  756. }
  757. }
  758. }
  759. func TestAllowNondistributableArtifacts(t *testing.T) {
  760. tests := []struct {
  761. addr string
  762. registries []string
  763. expected bool
  764. }{
  765. {IndexName, nil, false},
  766. {"example.com", []string{}, false},
  767. {"example.com", []string{"example.com"}, true},
  768. {"localhost", []string{"localhost:5000"}, false},
  769. {"localhost:5000", []string{"localhost:5000"}, true},
  770. {"localhost", []string{"example.com"}, false},
  771. {"127.0.0.1:5000", []string{"127.0.0.1:5000"}, true},
  772. {"localhost", nil, false},
  773. {"localhost:5000", nil, false},
  774. {"127.0.0.1", nil, false},
  775. {"localhost", []string{"example.com"}, false},
  776. {"127.0.0.1", []string{"example.com"}, false},
  777. {"example.com", nil, false},
  778. {"example.com", []string{"example.com"}, true},
  779. {"127.0.0.1", []string{"example.com"}, false},
  780. {"127.0.0.1:5000", []string{"example.com"}, false},
  781. {"example.com:5000", []string{"42.42.0.0/16"}, true},
  782. {"example.com", []string{"42.42.0.0/16"}, true},
  783. {"example.com:5000", []string{"42.42.42.42/8"}, true},
  784. {"127.0.0.1:5000", []string{"127.0.0.0/8"}, true},
  785. {"42.42.42.42:5000", []string{"42.1.1.1/8"}, true},
  786. {"invalid.domain.com", []string{"42.42.0.0/16"}, false},
  787. {"invalid.domain.com", []string{"invalid.domain.com"}, true},
  788. {"invalid.domain.com:5000", []string{"invalid.domain.com"}, false},
  789. {"invalid.domain.com:5000", []string{"invalid.domain.com:5000"}, true},
  790. }
  791. for _, tt := range tests {
  792. config, err := newServiceConfig(ServiceOptions{
  793. AllowNondistributableArtifacts: tt.registries,
  794. })
  795. if err != nil {
  796. t.Error(err)
  797. }
  798. if v := allowNondistributableArtifacts(config, tt.addr); v != tt.expected {
  799. t.Errorf("allowNondistributableArtifacts failed for %q %v, expected %v got %v", tt.addr, tt.registries, tt.expected, v)
  800. }
  801. }
  802. }
  803. func TestIsSecureIndex(t *testing.T) {
  804. tests := []struct {
  805. addr string
  806. insecureRegistries []string
  807. expected bool
  808. }{
  809. {IndexName, nil, true},
  810. {"example.com", []string{}, true},
  811. {"example.com", []string{"example.com"}, false},
  812. {"localhost", []string{"localhost:5000"}, false},
  813. {"localhost:5000", []string{"localhost:5000"}, false},
  814. {"localhost", []string{"example.com"}, false},
  815. {"127.0.0.1:5000", []string{"127.0.0.1:5000"}, false},
  816. {"localhost", nil, false},
  817. {"localhost:5000", nil, false},
  818. {"127.0.0.1", nil, false},
  819. {"localhost", []string{"example.com"}, false},
  820. {"127.0.0.1", []string{"example.com"}, false},
  821. {"example.com", nil, true},
  822. {"example.com", []string{"example.com"}, false},
  823. {"127.0.0.1", []string{"example.com"}, false},
  824. {"127.0.0.1:5000", []string{"example.com"}, false},
  825. {"example.com:5000", []string{"42.42.0.0/16"}, false},
  826. {"example.com", []string{"42.42.0.0/16"}, false},
  827. {"example.com:5000", []string{"42.42.42.42/8"}, false},
  828. {"127.0.0.1:5000", []string{"127.0.0.0/8"}, false},
  829. {"42.42.42.42:5000", []string{"42.1.1.1/8"}, false},
  830. {"invalid.domain.com", []string{"42.42.0.0/16"}, true},
  831. {"invalid.domain.com", []string{"invalid.domain.com"}, false},
  832. {"invalid.domain.com:5000", []string{"invalid.domain.com"}, true},
  833. {"invalid.domain.com:5000", []string{"invalid.domain.com:5000"}, false},
  834. }
  835. for _, tt := range tests {
  836. config, err := makeServiceConfig(nil, tt.insecureRegistries)
  837. if err != nil {
  838. t.Error(err)
  839. }
  840. if sec := isSecureIndex(config, tt.addr); sec != tt.expected {
  841. t.Errorf("isSecureIndex failed for %q %v, expected %v got %v", tt.addr, tt.insecureRegistries, tt.expected, sec)
  842. }
  843. }
  844. }
  845. type debugTransport struct {
  846. http.RoundTripper
  847. log func(...interface{})
  848. }
  849. func (tr debugTransport) RoundTrip(req *http.Request) (*http.Response, error) {
  850. dump, err := httputil.DumpRequestOut(req, false)
  851. if err != nil {
  852. tr.log("could not dump request")
  853. }
  854. tr.log(string(dump))
  855. resp, err := tr.RoundTripper.RoundTrip(req)
  856. if err != nil {
  857. return nil, err
  858. }
  859. dump, err = httputil.DumpResponse(resp, false)
  860. if err != nil {
  861. tr.log("could not dump response")
  862. }
  863. tr.log(string(dump))
  864. return resp, err
  865. }