internal/registry: remove code related to mirrors

The CLI does not have information about mirrors, and doesn't
configure them, so we can remove these parts.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit e0b351b3d9)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2025-07-24 01:57:57 +02:00
parent ecd54bc6dd
commit 72f85ccd16
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
5 changed files with 13 additions and 673 deletions

View File

@ -22,7 +22,6 @@ import (
// ServiceOptions holds command line options.
type ServiceOptions struct {
Mirrors []string `json:"registry-mirrors,omitempty"`
InsecureRegistries []string `json:"insecure-registries,omitempty"`
}
@ -93,51 +92,6 @@ func CertsDir() string {
return certsDir
}
// newServiceConfig returns a new instance of ServiceConfig
func newServiceConfig(options ServiceOptions) (*serviceConfig, error) {
config := &serviceConfig{}
if err := config.loadMirrors(options.Mirrors); err != nil {
return nil, err
}
if err := config.loadInsecureRegistries(options.InsecureRegistries); err != nil {
return nil, err
}
return config, nil
}
// loadMirrors loads mirrors to config, after removing duplicates.
// Returns an error if mirrors contains an invalid mirror.
func (config *serviceConfig) loadMirrors(mirrors []string) error {
mMap := map[string]struct{}{}
unique := []string{}
for _, mirror := range mirrors {
m, err := ValidateMirror(mirror)
if err != nil {
return err
}
if _, exist := mMap[m]; !exist {
mMap[m] = struct{}{}
unique = append(unique, m)
}
}
config.Mirrors = unique
// Configure public registry since mirrors may have changed.
config.IndexConfigs = map[string]*registry.IndexInfo{
IndexName: {
Name: IndexName,
Mirrors: unique,
Secure: true,
Official: true,
},
}
return nil
}
// loadInsecureRegistries loads insecure registries to config
func (config *serviceConfig) loadInsecureRegistries(registries []string) error {
// Localhost is by default considered as an insecure registry. This is a
@ -184,7 +138,6 @@ skip:
// Assume `host:port` if not CIDR.
indexConfigs[r] = &registry.IndexInfo{
Name: r,
Mirrors: []string{},
Secure: false,
Official: false,
}
@ -194,7 +147,6 @@ skip:
// Configure public registry.
indexConfigs[IndexName] = &registry.IndexInfo{
Name: IndexName,
Mirrors: config.Mirrors,
Secure: true,
Official: true,
}
@ -267,35 +219,6 @@ func isCIDRMatch(cidrs []*registry.NetIPNet, urlHost string) bool {
return false
}
// ValidateMirror validates and normalizes an HTTP(S) registry mirror. It
// returns an error if the given mirrorURL is invalid, or the normalized
// format for the URL otherwise.
//
// It is used by the daemon to validate the daemon configuration.
func ValidateMirror(mirrorURL string) (string, error) {
// Fast path for missing scheme, as url.Parse splits by ":", which can
// cause the hostname to be considered the "scheme" when using "hostname:port".
if scheme, _, ok := strings.Cut(mirrorURL, "://"); !ok || scheme == "" {
return "", invalidParamf("invalid mirror: no scheme specified for %q: must use either 'https://' or 'http://'", mirrorURL)
}
uri, err := url.Parse(mirrorURL)
if err != nil {
return "", invalidParamWrapf(err, "invalid mirror: %q is not a valid URI", mirrorURL)
}
if uri.Scheme != "http" && uri.Scheme != "https" {
return "", invalidParamf("invalid mirror: unsupported scheme %q in %q: must use either 'https://' or 'http://'", uri.Scheme, uri)
}
if uri.RawQuery != "" || uri.Fragment != "" {
return "", invalidParamf("invalid mirror: query or fragment at end of the URI %q", uri)
}
if uri.User != nil {
// strip password from output
uri.User = url.UserPassword(uri.User.Username(), "xxxxx")
return "", invalidParamf("invalid mirror: username/password not allowed in URI %q", uri)
}
return strings.TrimSuffix(mirrorURL, "/") + "/", nil
}
// ValidateIndexName validates an index name. It is used by the daemon to
// validate the daemon configuration.
func ValidateIndexName(val string) (string, error) {
@ -348,7 +271,6 @@ func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
Name: reference.TrimNamed(reposName),
Index: &registry.IndexInfo{
Name: IndexName,
Mirrors: []string{},
Secure: true,
Official: true,
},
@ -358,9 +280,8 @@ func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
return &RepositoryInfo{
Name: reference.TrimNamed(reposName),
Index: &registry.IndexInfo{
Name: indexName,
Mirrors: []string{},
Secure: !isInsecure(indexName),
Name: indexName,
Secure: !isInsecure(indexName),
},
}, nil
}

View File

@ -8,138 +8,6 @@ import (
is "gotest.tools/v3/assert/cmp"
)
func TestValidateMirror(t *testing.T) {
tests := []struct {
input string
output string
expectedErr string
}{
// Valid cases
{
input: "http://mirror-1.example.com",
output: "http://mirror-1.example.com/",
},
{
input: "http://mirror-1.example.com/",
output: "http://mirror-1.example.com/",
},
{
input: "https://mirror-1.example.com",
output: "https://mirror-1.example.com/",
},
{
input: "https://mirror-1.example.com/",
output: "https://mirror-1.example.com/",
},
{
input: "http://localhost",
output: "http://localhost/",
},
{
input: "https://localhost",
output: "https://localhost/",
},
{
input: "http://localhost:5000",
output: "http://localhost:5000/",
},
{
input: "https://localhost:5000",
output: "https://localhost:5000/",
},
{
input: "http://127.0.0.1",
output: "http://127.0.0.1/",
},
{
input: "https://127.0.0.1",
output: "https://127.0.0.1/",
},
{
input: "http://127.0.0.1:5000",
output: "http://127.0.0.1:5000/",
},
{
input: "https://127.0.0.1:5000",
output: "https://127.0.0.1:5000/",
},
{
input: "http://mirror-1.example.com/v1/",
output: "http://mirror-1.example.com/v1/",
},
{
input: "https://mirror-1.example.com/v1/",
output: "https://mirror-1.example.com/v1/",
},
// Invalid cases
{
input: "!invalid!://%as%",
expectedErr: `invalid mirror: "!invalid!://%as%" is not a valid URI: parse "!invalid!://%as%": first path segment in URL cannot contain colon`,
},
{
input: "mirror-1.example.com",
expectedErr: `invalid mirror: no scheme specified for "mirror-1.example.com": must use either 'https://' or 'http://'`,
},
{
input: "mirror-1.example.com:5000",
expectedErr: `invalid mirror: no scheme specified for "mirror-1.example.com:5000": must use either 'https://' or 'http://'`,
},
{
input: "ftp://mirror-1.example.com",
expectedErr: `invalid mirror: unsupported scheme "ftp" in "ftp://mirror-1.example.com": must use either 'https://' or 'http://'`,
},
{
input: "http://mirror-1.example.com/?q=foo",
expectedErr: `invalid mirror: query or fragment at end of the URI "http://mirror-1.example.com/?q=foo"`,
},
{
input: "http://mirror-1.example.com/v1/?q=foo",
expectedErr: `invalid mirror: query or fragment at end of the URI "http://mirror-1.example.com/v1/?q=foo"`,
},
{
input: "http://mirror-1.example.com/v1/?q=foo#frag",
expectedErr: `invalid mirror: query or fragment at end of the URI "http://mirror-1.example.com/v1/?q=foo#frag"`,
},
{
input: "http://mirror-1.example.com?q=foo",
expectedErr: `invalid mirror: query or fragment at end of the URI "http://mirror-1.example.com?q=foo"`,
},
{
input: "https://mirror-1.example.com#frag",
expectedErr: `invalid mirror: query or fragment at end of the URI "https://mirror-1.example.com#frag"`,
},
{
input: "https://mirror-1.example.com/#frag",
expectedErr: `invalid mirror: query or fragment at end of the URI "https://mirror-1.example.com/#frag"`,
},
{
input: "http://foo:bar@mirror-1.example.com/",
expectedErr: `invalid mirror: username/password not allowed in URI "http://foo:xxxxx@mirror-1.example.com/"`,
},
{
input: "https://mirror-1.example.com/v1/#frag",
expectedErr: `invalid mirror: query or fragment at end of the URI "https://mirror-1.example.com/v1/#frag"`,
},
{
input: "https://mirror-1.example.com?q",
expectedErr: `invalid mirror: query or fragment at end of the URI "https://mirror-1.example.com?q"`,
},
}
for _, tc := range tests {
t.Run(tc.input, func(t *testing.T) {
out, err := ValidateMirror(tc.input)
if tc.expectedErr != "" {
assert.Error(t, err, tc.expectedErr)
} else {
assert.NilError(t, err)
}
assert.Check(t, is.Equal(out, tc.output))
})
}
}
func TestLoadInsecureRegistries(t *testing.T) {
testCases := []struct {
registries []string
@ -229,56 +97,6 @@ func TestLoadInsecureRegistries(t *testing.T) {
}
}
func TestNewServiceConfig(t *testing.T) {
tests := []struct {
doc string
opts ServiceOptions
errStr string
}{
{
doc: "empty config",
},
{
doc: "invalid mirror",
opts: ServiceOptions{
Mirrors: []string{"example.com:5000"},
},
errStr: `invalid mirror: no scheme specified for "example.com:5000": must use either 'https://' or 'http://'`,
},
{
doc: "valid mirror",
opts: ServiceOptions{
Mirrors: []string{"https://example.com:5000"},
},
},
{
doc: "invalid insecure registry",
opts: ServiceOptions{
InsecureRegistries: []string{"[fe80::]/64"},
},
errStr: `insecure registry [fe80::]/64 is not valid: invalid host "[fe80::]/64"`,
},
{
doc: "valid insecure registry",
opts: ServiceOptions{
InsecureRegistries: []string{"102.10.8.1/24"},
},
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
_, err := newServiceConfig(tc.opts)
if tc.errStr != "" {
assert.Check(t, is.Error(err, tc.errStr))
assert.Check(t, cerrdefs.IsInvalidArgument(err))
} else {
assert.Check(t, err)
}
})
}
}
func TestValidateIndexName(t *testing.T) {
valid := []struct {
index string

View File

@ -1,8 +1,6 @@
package registry
import (
"errors"
"net"
"testing"
"github.com/distribution/reference"
@ -11,46 +9,6 @@ import (
is "gotest.tools/v3/assert/cmp"
)
// overrideLookupIP overrides net.LookupIP for testing.
func overrideLookupIP(t *testing.T) {
t.Helper()
restoreLookup := lookupIP
// override net.LookupIP
lookupIP = func(host string) ([]net.IP, error) {
mockHosts := map[string][]net.IP{
"": {net.ParseIP("0.0.0.0")},
"localhost": {net.ParseIP("127.0.0.1"), net.ParseIP("::1")},
"example.com": {net.ParseIP("42.42.42.42")},
"other.com": {net.ParseIP("43.43.43.43")},
}
if addrs, ok := mockHosts[host]; ok {
return addrs, nil
}
return nil, errors.New("lookup: no such host")
}
t.Cleanup(func() {
lookupIP = restoreLookup
})
}
// newIndexInfo returns IndexInfo configuration from indexName
func newIndexInfo(config *serviceConfig, indexName string) *registry.IndexInfo {
indexName = normalizeIndexName(indexName)
// Return any configured index info, first.
if index, ok := config.IndexConfigs[indexName]; ok {
return index
}
// Construct a non-configured index info.
return &registry.IndexInfo{
Name: indexName,
Mirrors: []string{},
Secure: config.isSecureIndex(indexName),
}
}
func TestParseRepositoryInfo(t *testing.T) {
type staticRepositoryInfo struct {
Index *registry.IndexInfo
@ -63,7 +21,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"fooo/bar": {
Index: &registry.IndexInfo{
Name: IndexName,
Mirrors: []string{},
Official: true,
Secure: true,
},
@ -74,7 +31,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"library/ubuntu": {
Index: &registry.IndexInfo{
Name: IndexName,
Mirrors: []string{},
Official: true,
Secure: true,
},
@ -85,7 +41,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"nonlibrary/ubuntu": {
Index: &registry.IndexInfo{
Name: IndexName,
Mirrors: []string{},
Official: true,
Secure: true,
},
@ -96,7 +51,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"ubuntu": {
Index: &registry.IndexInfo{
Name: IndexName,
Mirrors: []string{},
Official: true,
Secure: true,
},
@ -107,7 +61,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"other/library": {
Index: &registry.IndexInfo{
Name: IndexName,
Mirrors: []string{},
Official: true,
Secure: true,
},
@ -118,7 +71,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"127.0.0.1:8000/private/moonbase": {
Index: &registry.IndexInfo{
Name: "127.0.0.1:8000",
Mirrors: []string{},
Official: false,
Secure: false,
},
@ -129,7 +81,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"127.0.0.1:8000/privatebase": {
Index: &registry.IndexInfo{
Name: "127.0.0.1:8000",
Mirrors: []string{},
Official: false,
Secure: false,
},
@ -140,7 +91,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"[::1]:8000/private/moonbase": {
Index: &registry.IndexInfo{
Name: "[::1]:8000",
Mirrors: []string{},
Official: false,
Secure: false,
},
@ -151,7 +101,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"[::1]:8000/privatebase": {
Index: &registry.IndexInfo{
Name: "[::1]:8000",
Mirrors: []string{},
Official: false,
Secure: false,
},
@ -164,7 +113,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"[::2]:8000/private/moonbase": {
Index: &registry.IndexInfo{
Name: "[::2]:8000",
Mirrors: []string{},
Official: false,
Secure: true,
},
@ -177,7 +125,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"[::2]:8000/privatebase": {
Index: &registry.IndexInfo{
Name: "[::2]:8000",
Mirrors: []string{},
Official: false,
Secure: true,
},
@ -188,7 +135,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"localhost:8000/private/moonbase": {
Index: &registry.IndexInfo{
Name: "localhost:8000",
Mirrors: []string{},
Official: false,
Secure: false,
},
@ -199,7 +145,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"localhost:8000/privatebase": {
Index: &registry.IndexInfo{
Name: "localhost:8000",
Mirrors: []string{},
Official: false,
Secure: false,
},
@ -210,7 +155,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"example.com/private/moonbase": {
Index: &registry.IndexInfo{
Name: "example.com",
Mirrors: []string{},
Official: false,
Secure: true,
},
@ -221,7 +165,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"example.com/privatebase": {
Index: &registry.IndexInfo{
Name: "example.com",
Mirrors: []string{},
Official: false,
Secure: true,
},
@ -232,7 +175,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"example.com:8000/private/moonbase": {
Index: &registry.IndexInfo{
Name: "example.com:8000",
Mirrors: []string{},
Official: false,
Secure: true,
},
@ -243,7 +185,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"example.com:8000/privatebase": {
Index: &registry.IndexInfo{
Name: "example.com:8000",
Mirrors: []string{},
Official: false,
Secure: true,
},
@ -254,7 +195,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"localhost/private/moonbase": {
Index: &registry.IndexInfo{
Name: "localhost",
Mirrors: []string{},
Official: false,
Secure: false,
},
@ -265,7 +205,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"localhost/privatebase": {
Index: &registry.IndexInfo{
Name: "localhost",
Mirrors: []string{},
Official: false,
Secure: false,
},
@ -276,7 +215,6 @@ func TestParseRepositoryInfo(t *testing.T) {
IndexName + "/public/moonbase": {
Index: &registry.IndexInfo{
Name: IndexName,
Mirrors: []string{},
Official: true,
Secure: true,
},
@ -287,7 +225,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"index." + IndexName + "/public/moonbase": {
Index: &registry.IndexInfo{
Name: IndexName,
Mirrors: []string{},
Official: true,
Secure: true,
},
@ -298,7 +235,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"ubuntu-12.04-base": {
Index: &registry.IndexInfo{
Name: IndexName,
Mirrors: []string{},
Official: true,
Secure: true,
},
@ -309,7 +245,6 @@ func TestParseRepositoryInfo(t *testing.T) {
IndexName + "/ubuntu-12.04-base": {
Index: &registry.IndexInfo{
Name: IndexName,
Mirrors: []string{},
Official: true,
Secure: true,
},
@ -320,7 +255,6 @@ func TestParseRepositoryInfo(t *testing.T) {
"index." + IndexName + "/ubuntu-12.04-base": {
Index: &registry.IndexInfo{
Name: IndexName,
Mirrors: []string{},
Official: true,
Secure: true,
},
@ -345,310 +279,3 @@ func TestParseRepositoryInfo(t *testing.T) {
})
}
}
func TestNewIndexInfo(t *testing.T) {
overrideLookupIP(t)
// ipv6Loopback is the CIDR for the IPv6 loopback address ("::1"); "::1/128"
ipv6Loopback := &net.IPNet{
IP: net.IPv6loopback,
Mask: net.CIDRMask(128, 128),
}
// ipv4Loopback is the CIDR for IPv4 loopback addresses ("127.0.0.0/8")
ipv4Loopback := &net.IPNet{
IP: net.IPv4(127, 0, 0, 0),
Mask: net.CIDRMask(8, 32),
}
// emptyServiceConfig is a default service-config for situations where
// no config-file is available (e.g. when used in the CLI). It won't
// have mirrors configured, but does have the default insecure registry
// CIDRs for loopback interfaces configured.
emptyServiceConfig := &serviceConfig{
IndexConfigs: map[string]*registry.IndexInfo{
IndexName: {
Name: IndexName,
Mirrors: []string{},
Secure: true,
Official: true,
},
},
InsecureRegistryCIDRs: []*registry.NetIPNet{
(*registry.NetIPNet)(ipv6Loopback),
(*registry.NetIPNet)(ipv4Loopback),
},
}
expectedIndexInfos := map[string]*registry.IndexInfo{
IndexName: {
Name: IndexName,
Official: true,
Secure: true,
Mirrors: []string{},
},
"index." + IndexName: {
Name: IndexName,
Official: true,
Secure: true,
Mirrors: []string{},
},
"example.com": {
Name: "example.com",
Official: false,
Secure: true,
Mirrors: []string{},
},
"127.0.0.1:5000": {
Name: "127.0.0.1:5000",
Official: false,
Secure: false,
Mirrors: []string{},
},
}
t.Run("no mirrors", func(t *testing.T) {
for indexName, expected := range expectedIndexInfos {
t.Run(indexName, func(t *testing.T) {
actual := newIndexInfo(emptyServiceConfig, indexName)
assert.Check(t, is.DeepEqual(actual, expected))
})
}
})
expectedIndexInfos = map[string]*registry.IndexInfo{
IndexName: {
Name: IndexName,
Official: true,
Secure: true,
Mirrors: []string{"http://mirror1.local/", "http://mirror2.local/"},
},
"index." + IndexName: {
Name: IndexName,
Official: true,
Secure: true,
Mirrors: []string{"http://mirror1.local/", "http://mirror2.local/"},
},
"example.com": {
Name: "example.com",
Official: false,
Secure: false,
Mirrors: []string{},
},
"example.com:5000": {
Name: "example.com:5000",
Official: false,
Secure: true,
Mirrors: []string{},
},
"127.0.0.1": {
Name: "127.0.0.1",
Official: false,
Secure: false,
Mirrors: []string{},
},
"127.0.0.1:5000": {
Name: "127.0.0.1:5000",
Official: false,
Secure: false,
Mirrors: []string{},
},
"127.255.255.255": {
Name: "127.255.255.255",
Official: false,
Secure: false,
Mirrors: []string{},
},
"127.255.255.255:5000": {
Name: "127.255.255.255:5000",
Official: false,
Secure: false,
Mirrors: []string{},
},
"::1": {
Name: "::1",
Official: false,
Secure: false,
Mirrors: []string{},
},
"[::1]:5000": {
Name: "[::1]:5000",
Official: false,
Secure: false,
Mirrors: []string{},
},
// IPv6 only has a single loopback address, so ::2 is not a loopback,
// hence not marked "insecure".
"::2": {
Name: "::2",
Official: false,
Secure: true,
Mirrors: []string{},
},
// IPv6 only has a single loopback address, so ::2 is not a loopback,
// hence not marked "insecure".
"[::2]:5000": {
Name: "[::2]:5000",
Official: false,
Secure: true,
Mirrors: []string{},
},
"other.com": {
Name: "other.com",
Official: false,
Secure: true,
Mirrors: []string{},
},
}
t.Run("mirrors", func(t *testing.T) {
// Note that newServiceConfig calls ValidateMirror internally, which normalizes
// mirror-URLs to have a trailing slash.
config, err := newServiceConfig(ServiceOptions{
Mirrors: []string{"http://mirror1.local", "http://mirror2.local"},
InsecureRegistries: []string{"example.com"},
})
assert.NilError(t, err)
for indexName, expected := range expectedIndexInfos {
t.Run(indexName, func(t *testing.T) {
actual := newIndexInfo(config, indexName)
assert.Check(t, is.DeepEqual(actual, expected))
})
}
})
expectedIndexInfos = map[string]*registry.IndexInfo{
"example.com": {
Name: "example.com",
Official: false,
Secure: false,
Mirrors: []string{},
},
"example.com:5000": {
Name: "example.com:5000",
Official: false,
Secure: false,
Mirrors: []string{},
},
"127.0.0.1": {
Name: "127.0.0.1",
Official: false,
Secure: false,
Mirrors: []string{},
},
"127.0.0.1:5000": {
Name: "127.0.0.1:5000",
Official: false,
Secure: false,
Mirrors: []string{},
},
"42.42.0.1:5000": {
Name: "42.42.0.1:5000",
Official: false,
Secure: false,
Mirrors: []string{},
},
"42.43.0.1:5000": {
Name: "42.43.0.1:5000",
Official: false,
Secure: true,
Mirrors: []string{},
},
"other.com": {
Name: "other.com",
Official: false,
Secure: true,
Mirrors: []string{},
},
}
t.Run("custom insecure", func(t *testing.T) {
config, err := newServiceConfig(ServiceOptions{
InsecureRegistries: []string{"42.42.0.0/16"},
})
assert.NilError(t, err)
for indexName, expected := range expectedIndexInfos {
t.Run(indexName, func(t *testing.T) {
actual := newIndexInfo(config, indexName)
assert.Check(t, is.DeepEqual(actual, expected))
})
}
})
}
func TestMirrorEndpointLookup(t *testing.T) {
containsMirror := func(endpoints []APIEndpoint) bool {
for _, pe := range endpoints {
if pe.URL.Host == "my.mirror" {
return true
}
}
return false
}
cfg, err := newServiceConfig(ServiceOptions{
Mirrors: []string{"https://my.mirror"},
})
assert.NilError(t, err)
s := Service{config: cfg}
imageName, err := reference.WithName(IndexName + "/test/image")
if err != nil {
t.Error(err)
}
pushAPIEndpoints, err := s.LookupPushEndpoints(reference.Domain(imageName))
if err != nil {
t.Fatal(err)
}
if containsMirror(pushAPIEndpoints) {
t.Fatal("Push endpoint should not contain mirror")
}
pullAPIEndpoints, err := s.LookupPullEndpoints(reference.Domain(imageName))
if err != nil {
t.Fatal(err)
}
if !containsMirror(pullAPIEndpoints) {
t.Fatal("Pull endpoint should contain mirror")
}
}
func TestIsSecureIndex(t *testing.T) {
overrideLookupIP(t)
tests := []struct {
addr string
insecureRegistries []string
expected bool
}{
{IndexName, nil, true},
{"example.com", []string{}, true},
{"example.com", []string{"example.com"}, false},
{"localhost", []string{"localhost:5000"}, false},
{"localhost:5000", []string{"localhost:5000"}, false},
{"localhost", []string{"example.com"}, false},
{"127.0.0.1:5000", []string{"127.0.0.1:5000"}, false},
{"localhost", nil, false},
{"localhost:5000", nil, false},
{"127.0.0.1", nil, false},
{"localhost", []string{"example.com"}, false},
{"127.0.0.1", []string{"example.com"}, false},
{"example.com", nil, true},
{"example.com", []string{"example.com"}, false},
{"127.0.0.1", []string{"example.com"}, false},
{"127.0.0.1:5000", []string{"example.com"}, false},
{"example.com:5000", []string{"42.42.0.0/16"}, false},
{"example.com", []string{"42.42.0.0/16"}, false},
{"example.com:5000", []string{"42.42.42.42/8"}, false},
{"127.0.0.1:5000", []string{"127.0.0.0/8"}, false},
{"42.42.42.42:5000", []string{"42.1.1.1/8"}, false},
{"invalid.example.com", []string{"42.42.0.0/16"}, true},
{"invalid.example.com", []string{"invalid.example.com"}, false},
{"invalid.example.com:5000", []string{"invalid.example.com"}, true},
{"invalid.example.com:5000", []string{"invalid.example.com:5000"}, false},
}
for _, tc := range tests {
config, err := newServiceConfig(ServiceOptions{
InsecureRegistries: tc.insecureRegistries,
})
assert.NilError(t, err)
sec := config.isSecureIndex(tc.addr)
assert.Equal(t, sec, tc.expected, "isSecureIndex failed for %q %v, expected %v got %v", tc.addr, tc.insecureRegistries, tc.expected, sec)
}
}

View File

@ -21,12 +21,13 @@ type Service struct {
// NewService returns a new instance of [Service] ready to be installed into
// an engine.
func NewService(options ServiceOptions) (*Service, error) {
config, err := newServiceConfig(options)
if err != nil {
return nil, err
config := &serviceConfig{}
if len(options.InsecureRegistries) > 0 {
if err := config.loadInsecureRegistries(options.InsecureRegistries); err != nil {
return nil, err
}
}
return &Service{config: config}, err
return &Service{config: config}, nil
}
// Auth contacts the public registry with the provided credentials,
@ -48,9 +49,8 @@ func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, use
registryHostName = u.Host
}
// Lookup endpoints for authentication but exclude mirrors to prevent
// sending credentials of the upstream registry to a mirror.
endpoints, err := s.lookupV2Endpoints(ctx, registryHostName, false)
// Lookup endpoints for authentication.
endpoints, err := s.lookupV2Endpoints(ctx, registryHostName)
if err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return "", "", err
@ -84,7 +84,6 @@ func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, use
// APIEndpoint represents a remote API endpoint
type APIEndpoint struct {
Mirror bool
URL *url.URL
TLSConfig *tls.Config
}
@ -92,11 +91,11 @@ type APIEndpoint struct {
// LookupPullEndpoints creates a list of v2 endpoints to try to pull from, in order of preference.
// It gives preference to mirrors over the actual registry, and HTTPS over plain HTTP.
func (s *Service) LookupPullEndpoints(hostname string) ([]APIEndpoint, error) {
return s.lookupV2Endpoints(context.TODO(), hostname, true)
return s.lookupV2Endpoints(context.TODO(), hostname)
}
// LookupPushEndpoints creates a list of v2 endpoints to try to push to, in order of preference.
// It gives preference to HTTPS over plain HTTP. Mirrors are not included.
func (s *Service) LookupPushEndpoints(hostname string) ([]APIEndpoint, error) {
return s.lookupV2Endpoints(context.TODO(), hostname, false)
return s.lookupV2Endpoints(context.TODO(), hostname)
}

View File

@ -3,38 +3,13 @@ package registry
import (
"context"
"net/url"
"strings"
"github.com/docker/go-connections/tlsconfig"
)
func (s *Service) lookupV2Endpoints(ctx context.Context, hostname string, includeMirrors bool) ([]APIEndpoint, error) {
func (s *Service) lookupV2Endpoints(ctx context.Context, hostname string) ([]APIEndpoint, error) {
var endpoints []APIEndpoint
if hostname == DefaultNamespace || hostname == IndexHostname {
if includeMirrors {
for _, mirror := range s.config.Mirrors {
if ctx.Err() != nil {
return nil, ctx.Err()
}
if !strings.HasPrefix(mirror, "http://") && !strings.HasPrefix(mirror, "https://") {
mirror = "https://" + mirror
}
mirrorURL, err := url.Parse(mirror)
if err != nil {
return nil, invalidParam(err)
}
// TODO(thaJeztah); this should all be memoized when loading the config. We're resolving mirrors and loading TLS config every time.
mirrorTLSConfig, err := newTLSConfig(ctx, mirrorURL.Host, s.config.isSecureIndex(mirrorURL.Host))
if err != nil {
return nil, err
}
endpoints = append(endpoints, APIEndpoint{
URL: mirrorURL,
Mirror: true,
TLSConfig: mirrorTLSConfig,
})
}
}
endpoints = append(endpoints, APIEndpoint{
URL: DefaultV2Registry,
TLSConfig: tlsconfig.ServerDefault(),