registry_test.go 27 KB

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