package registry // import "github.com/docker/docker/registry" import ( "net/http" "net/http/httptest" "strings" "testing" "github.com/docker/docker/api/types/registry" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestV1EndpointPing(t *testing.T) { testPing := func(index *registry.IndexInfo, expectedStandalone bool, assertMessage string) { ep, err := newV1Endpoint(index, nil) if err != nil { t.Fatal(err) } regInfo, err := ep.ping() if err != nil { t.Fatal(err) } assert.Equal(t, regInfo.Standalone, expectedStandalone, assertMessage) } testPing(makeIndex("/v1/"), true, "Expected standalone to be true (default)") testPing(makeHTTPSIndex("/v1/"), true, "Expected standalone to be true (default)") testPing(makePublicIndex(), false, "Expected standalone to be false for public index") } func TestV1Endpoint(t *testing.T) { // Simple wrapper to fail test if err != nil expandEndpoint := func(index *registry.IndexInfo) *v1Endpoint { endpoint, err := newV1Endpoint(index, nil) if err != nil { t.Fatal(err) } return endpoint } assertInsecureIndex := func(index *registry.IndexInfo) { index.Secure = true _, err := newV1Endpoint(index, nil) assert.ErrorContains(t, err, "insecure-registry", index.Name+": Expected insecure-registry error for insecure index") index.Secure = false } assertSecureIndex := func(index *registry.IndexInfo) { index.Secure = true _, err := newV1Endpoint(index, nil) assert.ErrorContains(t, err, "certificate signed by unknown authority", index.Name+": Expected cert error for secure index") index.Secure = false } index := ®istry.IndexInfo{} index.Name = makeURL("/v1/") endpoint := expandEndpoint(index) assert.Equal(t, endpoint.String(), index.Name, "Expected endpoint to be "+index.Name) assertInsecureIndex(index) index.Name = makeURL("") endpoint = expandEndpoint(index) assert.Equal(t, endpoint.String(), index.Name+"/v1/", index.Name+": Expected endpoint to be "+index.Name+"/v1/") assertInsecureIndex(index) httpURL := makeURL("") index.Name = strings.SplitN(httpURL, "://", 2)[1] endpoint = expandEndpoint(index) assert.Equal(t, endpoint.String(), httpURL+"/v1/", index.Name+": Expected endpoint to be "+httpURL+"/v1/") assertInsecureIndex(index) index.Name = makeHTTPSURL("/v1/") endpoint = expandEndpoint(index) assert.Equal(t, endpoint.String(), index.Name, "Expected endpoint to be "+index.Name) assertSecureIndex(index) index.Name = makeHTTPSURL("") endpoint = expandEndpoint(index) assert.Equal(t, endpoint.String(), index.Name+"/v1/", index.Name+": Expected endpoint to be "+index.Name+"/v1/") assertSecureIndex(index) httpsURL := makeHTTPSURL("") index.Name = strings.SplitN(httpsURL, "://", 2)[1] endpoint = expandEndpoint(index) assert.Equal(t, endpoint.String(), httpsURL+"/v1/", index.Name+": Expected endpoint to be "+httpsURL+"/v1/") assertSecureIndex(index) badEndpoints := []string{ "http://127.0.0.1/v1/", "https://127.0.0.1/v1/", "http://127.0.0.1", "https://127.0.0.1", "127.0.0.1", } for _, address := range badEndpoints { index.Name = address _, err := newV1Endpoint(index, nil) assert.Check(t, err != nil, "Expected error while expanding bad endpoint: %s", address) } } func TestV1EndpointParse(t *testing.T) { tests := []struct { address string expected string expectedErr string }{ { address: IndexServer, expected: IndexServer, }, { address: "https://0.0.0.0:5000/v1/", expected: "https://0.0.0.0:5000/v1/", }, { address: "https://0.0.0.0:5000", expected: "https://0.0.0.0:5000/v1/", }, { address: "0.0.0.0:5000", expected: "https://0.0.0.0:5000/v1/", }, { address: "https://0.0.0.0:5000/nonversion/", expected: "https://0.0.0.0:5000/nonversion/v1/", }, { address: "https://0.0.0.0:5000/v0/", expected: "https://0.0.0.0:5000/v0/v1/", }, { address: "https://0.0.0.0:5000/v2/", expectedErr: "search is not supported on v2 endpoints: https://0.0.0.0:5000/v2/", }, } for _, tc := range tests { tc := tc t.Run(tc.address, func(t *testing.T) { ep, err := newV1EndpointFromStr(tc.address, nil, nil) if tc.expectedErr != "" { assert.Check(t, is.Error(err, tc.expectedErr)) assert.Check(t, is.Nil(ep)) } else { assert.NilError(t, err) assert.Check(t, is.Equal(ep.String(), tc.expected)) } }) } } // Ensure that a registry endpoint that responds with a 401 only is determined // to be a valid v1 registry endpoint func TestV1EndpointValidate(t *testing.T) { requireBasicAuthHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Add("WWW-Authenticate", `Basic realm="localhost"`) w.WriteHeader(http.StatusUnauthorized) }) // Make a test server which should validate as a v1 server. testServer := httptest.NewServer(requireBasicAuthHandler) defer testServer.Close() testEndpoint, err := newV1Endpoint(®istry.IndexInfo{Name: testServer.URL}, nil) if err != nil { t.Fatal(err) } if testEndpoint.URL.Scheme != "http" { t.Fatalf("expecting to validate endpoint as http, got url %s", testEndpoint.String()) } } func TestTrustedLocation(t *testing.T) { for _, u := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} { req, _ := http.NewRequest(http.MethodGet, u, nil) assert.Check(t, !trustedLocation(req)) } for _, u := range []string{"https://docker.io", "https://test.docker.com:80"} { req, _ := http.NewRequest(http.MethodGet, u, nil) assert.Check(t, trustedLocation(req)) } } func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) { for _, urls := range [][]string{ {"http://docker.io", "https://docker.com"}, {"https://foo.docker.io:7777", "http://bar.docker.com"}, {"https://foo.docker.io", "https://example.com"}, } { reqFrom, _ := http.NewRequest(http.MethodGet, urls[0], nil) reqFrom.Header.Add("Content-Type", "application/json") reqFrom.Header.Add("Authorization", "super_secret") reqTo, _ := http.NewRequest(http.MethodGet, urls[1], nil) _ = addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) if len(reqTo.Header) != 1 { t.Fatalf("Expected 1 headers, got %d", len(reqTo.Header)) } if reqTo.Header.Get("Content-Type") != "application/json" { t.Fatal("'Content-Type' should be 'application/json'") } if reqTo.Header.Get("Authorization") != "" { t.Fatal("'Authorization' should be empty") } } for _, urls := range [][]string{ {"https://docker.io", "https://docker.com"}, {"https://foo.docker.io:7777", "https://bar.docker.com"}, } { reqFrom, _ := http.NewRequest(http.MethodGet, urls[0], nil) reqFrom.Header.Add("Content-Type", "application/json") reqFrom.Header.Add("Authorization", "super_secret") reqTo, _ := http.NewRequest(http.MethodGet, urls[1], nil) _ = addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) if len(reqTo.Header) != 2 { t.Fatalf("Expected 2 headers, got %d", len(reqTo.Header)) } if reqTo.Header.Get("Content-Type") != "application/json" { t.Fatal("'Content-Type' should be 'application/json'") } if reqTo.Header.Get("Authorization") != "super_secret" { t.Fatal("'Authorization' should be 'super_secret'") } } }