2018-02-05 21:05:59 +00:00
|
|
|
package registry // import "github.com/docker/docker/registry"
|
2014-10-03 19:46:42 +00:00
|
|
|
|
2015-01-21 03:37:21 +00:00
|
|
|
import (
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2022-08-25 07:52:04 +00:00
|
|
|
"strings"
|
2015-01-21 03:37:21 +00:00
|
|
|
"testing"
|
2023-08-29 08:31:46 +00:00
|
|
|
|
2022-08-25 07:52:04 +00:00
|
|
|
"github.com/docker/docker/api/types/registry"
|
2023-08-29 08:31:46 +00:00
|
|
|
"gotest.tools/v3/assert"
|
|
|
|
is "gotest.tools/v3/assert/cmp"
|
2015-01-21 03:37:21 +00:00
|
|
|
)
|
2014-10-03 19:46:42 +00:00
|
|
|
|
2022-08-25 07:59:30 +00:00
|
|
|
func TestV1EndpointPing(t *testing.T) {
|
2022-08-25 07:52:04 +00:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
|
2022-08-25 07:59:30 +00:00
|
|
|
func TestV1Endpoint(t *testing.T) {
|
2022-08-25 07:52:04 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-29 08:31:46 +00:00
|
|
|
func TestV1EndpointParse(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
address string
|
|
|
|
expected string
|
|
|
|
expectedErr string
|
2014-10-03 19:46:42 +00:00
|
|
|
}{
|
2023-08-29 08:31:46 +00:00
|
|
|
{
|
|
|
|
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/",
|
2023-08-29 08:47:29 +00:00
|
|
|
expectedErr: "search is not supported on v2 endpoints: https://0.0.0.0:5000/v2/",
|
2023-08-29 08:31:46 +00:00
|
|
|
},
|
2016-03-01 07:07:41 +00:00
|
|
|
}
|
2023-08-29 08:31:46 +00:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
})
|
2016-03-01 07:07:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-21 03:37:21 +00:00
|
|
|
// Ensure that a registry endpoint that responds with a 401 only is determined
|
2016-03-01 07:07:41 +00:00
|
|
|
// to be a valid v1 registry endpoint
|
2022-08-25 07:59:30 +00:00
|
|
|
func TestV1EndpointValidate(t *testing.T) {
|
2015-01-21 03:37:21 +00:00
|
|
|
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()
|
|
|
|
|
2023-09-08 13:38:20 +00:00
|
|
|
testEndpoint, err := newV1Endpoint(®istry.IndexInfo{Name: testServer.URL}, nil)
|
2015-01-21 03:37:21 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2016-03-01 07:07:41 +00:00
|
|
|
if testEndpoint.URL.Scheme != "http" {
|
|
|
|
t.Fatalf("expecting to validate endpoint as http, got url %s", testEndpoint.String())
|
2015-01-21 03:37:21 +00:00
|
|
|
}
|
|
|
|
}
|
2022-08-25 08:13:03 +00:00
|
|
|
|
|
|
|
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'")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|