moby/registry/search_endpoint_v1_test.go
Sebastiaan van Stijn 5da2dd98e9
registry: move search-related code to separate files
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-08-29 16:45:43 +02:00

251 lines
7.5 KiB
Go

package registry // import "github.com/docker/docker/registry"
import (
"net/http"
"net/http/httptest"
"net/url"
"os"
"strings"
"testing"
"github.com/docker/docker/api/types/registry"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip"
)
func TestV1EndpointPing(t *testing.T) {
skip.If(t, os.Getuid() != 0, "skipping test that requires root")
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) {
skip.If(t, os.Getuid() != 0, "skipping test that requires root")
// 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 := &registry.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()
testServerURL, err := url.Parse(testServer.URL)
if err != nil {
t.Fatal(err)
}
testEndpoint := v1Endpoint{
URL: testServerURL,
client: httpClient(newTransport(nil)),
}
if err = validateEndpoint(&testEndpoint); 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'")
}
}
}