search_endpoint_v1_test.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. package registry // import "github.com/docker/docker/registry"
  2. import (
  3. "net/http"
  4. "net/http/httptest"
  5. "net/url"
  6. "os"
  7. "strings"
  8. "testing"
  9. "github.com/docker/docker/api/types/registry"
  10. "gotest.tools/v3/assert"
  11. is "gotest.tools/v3/assert/cmp"
  12. "gotest.tools/v3/skip"
  13. )
  14. func TestV1EndpointPing(t *testing.T) {
  15. skip.If(t, os.Getuid() != 0, "skipping test that requires root")
  16. testPing := func(index *registry.IndexInfo, expectedStandalone bool, assertMessage string) {
  17. ep, err := newV1Endpoint(index, nil)
  18. if err != nil {
  19. t.Fatal(err)
  20. }
  21. regInfo, err := ep.ping()
  22. if err != nil {
  23. t.Fatal(err)
  24. }
  25. assert.Equal(t, regInfo.Standalone, expectedStandalone, assertMessage)
  26. }
  27. testPing(makeIndex("/v1/"), true, "Expected standalone to be true (default)")
  28. testPing(makeHTTPSIndex("/v1/"), true, "Expected standalone to be true (default)")
  29. testPing(makePublicIndex(), false, "Expected standalone to be false for public index")
  30. }
  31. func TestV1Endpoint(t *testing.T) {
  32. skip.If(t, os.Getuid() != 0, "skipping test that requires root")
  33. // Simple wrapper to fail test if err != nil
  34. expandEndpoint := func(index *registry.IndexInfo) *v1Endpoint {
  35. endpoint, err := newV1Endpoint(index, nil)
  36. if err != nil {
  37. t.Fatal(err)
  38. }
  39. return endpoint
  40. }
  41. assertInsecureIndex := func(index *registry.IndexInfo) {
  42. index.Secure = true
  43. _, err := newV1Endpoint(index, nil)
  44. assert.ErrorContains(t, err, "insecure-registry", index.Name+": Expected insecure-registry error for insecure index")
  45. index.Secure = false
  46. }
  47. assertSecureIndex := func(index *registry.IndexInfo) {
  48. index.Secure = true
  49. _, err := newV1Endpoint(index, nil)
  50. assert.ErrorContains(t, err, "certificate signed by unknown authority", index.Name+": Expected cert error for secure index")
  51. index.Secure = false
  52. }
  53. index := &registry.IndexInfo{}
  54. index.Name = makeURL("/v1/")
  55. endpoint := expandEndpoint(index)
  56. assert.Equal(t, endpoint.String(), index.Name, "Expected endpoint to be "+index.Name)
  57. assertInsecureIndex(index)
  58. index.Name = makeURL("")
  59. endpoint = expandEndpoint(index)
  60. assert.Equal(t, endpoint.String(), index.Name+"/v1/", index.Name+": Expected endpoint to be "+index.Name+"/v1/")
  61. assertInsecureIndex(index)
  62. httpURL := makeURL("")
  63. index.Name = strings.SplitN(httpURL, "://", 2)[1]
  64. endpoint = expandEndpoint(index)
  65. assert.Equal(t, endpoint.String(), httpURL+"/v1/", index.Name+": Expected endpoint to be "+httpURL+"/v1/")
  66. assertInsecureIndex(index)
  67. index.Name = makeHTTPSURL("/v1/")
  68. endpoint = expandEndpoint(index)
  69. assert.Equal(t, endpoint.String(), index.Name, "Expected endpoint to be "+index.Name)
  70. assertSecureIndex(index)
  71. index.Name = makeHTTPSURL("")
  72. endpoint = expandEndpoint(index)
  73. assert.Equal(t, endpoint.String(), index.Name+"/v1/", index.Name+": Expected endpoint to be "+index.Name+"/v1/")
  74. assertSecureIndex(index)
  75. httpsURL := makeHTTPSURL("")
  76. index.Name = strings.SplitN(httpsURL, "://", 2)[1]
  77. endpoint = expandEndpoint(index)
  78. assert.Equal(t, endpoint.String(), httpsURL+"/v1/", index.Name+": Expected endpoint to be "+httpsURL+"/v1/")
  79. assertSecureIndex(index)
  80. badEndpoints := []string{
  81. "http://127.0.0.1/v1/",
  82. "https://127.0.0.1/v1/",
  83. "http://127.0.0.1",
  84. "https://127.0.0.1",
  85. "127.0.0.1",
  86. }
  87. for _, address := range badEndpoints {
  88. index.Name = address
  89. _, err := newV1Endpoint(index, nil)
  90. assert.Check(t, err != nil, "Expected error while expanding bad endpoint: %s", address)
  91. }
  92. }
  93. func TestV1EndpointParse(t *testing.T) {
  94. tests := []struct {
  95. address string
  96. expected string
  97. expectedErr string
  98. }{
  99. {
  100. address: IndexServer,
  101. expected: IndexServer,
  102. },
  103. {
  104. address: "https://0.0.0.0:5000/v1/",
  105. expected: "https://0.0.0.0:5000/v1/",
  106. },
  107. {
  108. address: "https://0.0.0.0:5000",
  109. expected: "https://0.0.0.0:5000/v1/",
  110. },
  111. {
  112. address: "0.0.0.0:5000",
  113. expected: "https://0.0.0.0:5000/v1/",
  114. },
  115. {
  116. address: "https://0.0.0.0:5000/nonversion/",
  117. expected: "https://0.0.0.0:5000/nonversion/v1/",
  118. },
  119. {
  120. address: "https://0.0.0.0:5000/v0/",
  121. expected: "https://0.0.0.0:5000/v0/v1/",
  122. },
  123. {
  124. address: "https://0.0.0.0:5000/v2/",
  125. expectedErr: "search is not supported on v2 endpoints: https://0.0.0.0:5000/v2/",
  126. },
  127. }
  128. for _, tc := range tests {
  129. tc := tc
  130. t.Run(tc.address, func(t *testing.T) {
  131. ep, err := newV1EndpointFromStr(tc.address, nil, nil)
  132. if tc.expectedErr != "" {
  133. assert.Check(t, is.Error(err, tc.expectedErr))
  134. assert.Check(t, is.Nil(ep))
  135. } else {
  136. assert.NilError(t, err)
  137. assert.Check(t, is.Equal(ep.String(), tc.expected))
  138. }
  139. })
  140. }
  141. }
  142. // Ensure that a registry endpoint that responds with a 401 only is determined
  143. // to be a valid v1 registry endpoint
  144. func TestV1EndpointValidate(t *testing.T) {
  145. requireBasicAuthHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  146. w.Header().Add("WWW-Authenticate", `Basic realm="localhost"`)
  147. w.WriteHeader(http.StatusUnauthorized)
  148. })
  149. // Make a test server which should validate as a v1 server.
  150. testServer := httptest.NewServer(requireBasicAuthHandler)
  151. defer testServer.Close()
  152. testServerURL, err := url.Parse(testServer.URL)
  153. if err != nil {
  154. t.Fatal(err)
  155. }
  156. testEndpoint := v1Endpoint{
  157. URL: testServerURL,
  158. client: httpClient(newTransport(nil)),
  159. }
  160. if err = validateEndpoint(&testEndpoint); err != nil {
  161. t.Fatal(err)
  162. }
  163. if testEndpoint.URL.Scheme != "http" {
  164. t.Fatalf("expecting to validate endpoint as http, got url %s", testEndpoint.String())
  165. }
  166. }
  167. func TestTrustedLocation(t *testing.T) {
  168. for _, u := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} {
  169. req, _ := http.NewRequest(http.MethodGet, u, nil)
  170. assert.Check(t, !trustedLocation(req))
  171. }
  172. for _, u := range []string{"https://docker.io", "https://test.docker.com:80"} {
  173. req, _ := http.NewRequest(http.MethodGet, u, nil)
  174. assert.Check(t, trustedLocation(req))
  175. }
  176. }
  177. func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) {
  178. for _, urls := range [][]string{
  179. {"http://docker.io", "https://docker.com"},
  180. {"https://foo.docker.io:7777", "http://bar.docker.com"},
  181. {"https://foo.docker.io", "https://example.com"},
  182. } {
  183. reqFrom, _ := http.NewRequest(http.MethodGet, urls[0], nil)
  184. reqFrom.Header.Add("Content-Type", "application/json")
  185. reqFrom.Header.Add("Authorization", "super_secret")
  186. reqTo, _ := http.NewRequest(http.MethodGet, urls[1], nil)
  187. _ = addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})
  188. if len(reqTo.Header) != 1 {
  189. t.Fatalf("Expected 1 headers, got %d", len(reqTo.Header))
  190. }
  191. if reqTo.Header.Get("Content-Type") != "application/json" {
  192. t.Fatal("'Content-Type' should be 'application/json'")
  193. }
  194. if reqTo.Header.Get("Authorization") != "" {
  195. t.Fatal("'Authorization' should be empty")
  196. }
  197. }
  198. for _, urls := range [][]string{
  199. {"https://docker.io", "https://docker.com"},
  200. {"https://foo.docker.io:7777", "https://bar.docker.com"},
  201. } {
  202. reqFrom, _ := http.NewRequest(http.MethodGet, urls[0], nil)
  203. reqFrom.Header.Add("Content-Type", "application/json")
  204. reqFrom.Header.Add("Authorization", "super_secret")
  205. reqTo, _ := http.NewRequest(http.MethodGet, urls[1], nil)
  206. _ = addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})
  207. if len(reqTo.Header) != 2 {
  208. t.Fatalf("Expected 2 headers, got %d", len(reqTo.Header))
  209. }
  210. if reqTo.Header.Get("Content-Type") != "application/json" {
  211. t.Fatal("'Content-Type' should be 'application/json'")
  212. }
  213. if reqTo.Header.Get("Authorization") != "super_secret" {
  214. t.Fatal("'Authorization' should be 'super_secret'")
  215. }
  216. }
  217. }