registry: ValidateMirror: improve validation for missing schemes

Before this patch, a missing scheme would sometimes produce a confusing
error message. If no scheme was specified at all, an empty "" would be
included in the message;

    echo '{"registry-mirrors":["example.com"]}' > my-config.json
    dockerd --config-file ./my-config.json
    # ...
    failed to start daemon: invalid mirror: unsupported scheme "" in "example.com"

If a scheme was missing, but a port was included, the hostname would be
printed as the scheme;

    echo '{"registry-mirrors":["example.com:8080"]}' > my-config.json
    dockerd --config-file ./my-config.json
    # ...
    failed to start daemon: invalid mirror: unsupported scheme "example.com" in "example.com:8080"

With this patch applied, the error messages are slightly more user-friendly;

    echo '{"registry-mirrors":["example.com"]}' > my-config.json
    dockerd --config-file ./my-config.json
    # ...
    failed to start daemon: invalid mirror: no scheme specified for "example.com": must use either 'https://' or 'http://'

    echo '{"registry-mirrors":["example.com:8080"]}' > my-config.json
    dockerd --config-file ./my-config.json
    # ...
    failed to start daemon: invalid mirror: no scheme specified for "example.com:8080": must use either 'https://' or 'http://'

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 307c18598d)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-06-19 13:40:06 +02:00
parent b959bebdfc
commit 97aa4e8550
2 changed files with 16 additions and 3 deletions

View File

@@ -298,12 +298,17 @@ func isCIDRMatch(cidrs []*registry.NetIPNet, URLHost string) bool {
// //
// It is used by the daemon to validate the daemon configuration. // It is used by the daemon to validate the daemon configuration.
func ValidateMirror(mirrorURL string) (string, error) { 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) uri, err := url.Parse(mirrorURL)
if err != nil { if err != nil {
return "", invalidParamWrapf(err, "invalid mirror: %q is not a valid URI", mirrorURL) return "", invalidParamWrapf(err, "invalid mirror: %q is not a valid URI", mirrorURL)
} }
if uri.Scheme != "http" && uri.Scheme != "https" { if uri.Scheme != "http" && uri.Scheme != "https" {
return "", invalidParamf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri) return "", invalidParamf("invalid mirror: unsupported scheme %q in %q: must use either 'https://' or 'http://'", uri.Scheme, uri)
} }
if uri.RawQuery != "" || uri.Fragment != "" { if uri.RawQuery != "" || uri.Fragment != "" {
return "", invalidParamf("invalid mirror: query or fragment at end of the URI %q", uri) return "", invalidParamf("invalid mirror: query or fragment at end of the URI %q", uri)

View File

@@ -77,9 +77,17 @@ func TestValidateMirror(t *testing.T) {
input: "!invalid!://%as%", input: "!invalid!://%as%",
expectedErr: `invalid mirror: "!invalid!://%as%" is not a valid URI: parse "!invalid!://%as%": first path segment in URL cannot contain colon`, 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", input: "ftp://mirror-1.example.com",
expectedErr: `invalid mirror: unsupported scheme "ftp" in "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", input: "http://mirror-1.example.com/?q=foo",
@@ -235,7 +243,7 @@ func TestNewServiceConfig(t *testing.T) {
opts: ServiceOptions{ opts: ServiceOptions{
Mirrors: []string{"example.com:5000"}, Mirrors: []string{"example.com:5000"},
}, },
errStr: `invalid mirror: unsupported scheme "example.com" in "example.com:5000"`, errStr: `invalid mirror: no scheme specified for "example.com:5000": must use either 'https://' or 'http://'`,
}, },
{ {
doc: "valid mirror", doc: "valid mirror",