diff --git a/docs/hosts.md b/docs/hosts.md index 235a97749..d713af9ca 100644 --- a/docs/hosts.md +++ b/docs/hosts.md @@ -274,6 +274,17 @@ or x-custom-1-2 = "another custom header" ``` +## override_path field + +`override_path` is used to indicate the host's API root endpoint is defined +in the URL path rather than by the API specification. This may be used with +non-compliant OCI registries which are missing the `/v2` prefix. +(Defaults to `false`) + +``` +override_path = true +``` + ## host field(s) (in the toml table format) `[host]."https://namespace"` and `[host].http://namespace` entries in the @@ -310,6 +321,10 @@ for this registry host namespace: [host."https://test-3.registry"] client = ["/etc/certs/client-1.pem", "/etc/certs/client-2.pem"] + +[host."https://non-compliant-mirror.registry/v2/upstream"] + capabilities = ["pull"] + override_path = true ``` **Note**: Recursion is not supported in the specification of host mirror diff --git a/remotes/docker/config/hosts.go b/remotes/docker/config/hosts.go index c31437af7..40330f17e 100644 --- a/remotes/docker/config/hosts.go +++ b/remotes/docker/config/hosts.go @@ -291,7 +291,7 @@ type hostFileConfig struct { // Accepted types // - string - Single file with public and private keys // - []string - Multiple files with public and private keys - // - [][2]string - Muliple keypairs with public and private keys in separate files + // - [][2]string - Multiple keypairs with public and private keys in separate files Client interface{} `toml:"client"` // SkipVerify skips verification of the server's certificate chain @@ -302,6 +302,12 @@ type hostFileConfig struct { // Header are additional header files to send to the server Header map[string]interface{} `toml:"header"` + // OverridePath indicates the API root endpoint is defined in the URL + // path rather than by the API specification. + // This may be used with non-compliant OCI registries to override the + // API root endpoint. + OverridePath bool `toml:"override_path"` + // TODO: Credentials: helper? name? username? alternate domain? token? } @@ -374,16 +380,12 @@ func parseHostConfig(server string, baseDir string, config hostFileConfig) (host } result.scheme = u.Scheme result.host = u.Host - // TODO: Handle path based on registry protocol - // Define a registry protocol type - // OCI v1 - Always use given path as is - // Docker v2 - Always ensure ends with /v2/ if len(u.Path) > 0 { u.Path = path.Clean(u.Path) - if !strings.HasSuffix(u.Path, "/v2") { + if !strings.HasSuffix(u.Path, "/v2") && !config.OverridePath { u.Path = u.Path + "/v2" } - } else { + } else if !config.OverridePath { u.Path = "/v2" } result.path = u.Path diff --git a/remotes/docker/config/hosts_test.go b/remotes/docker/config/hosts_test.go index 18dc1c659..44586fa5e 100644 --- a/remotes/docker/config/hosts_test.go +++ b/remotes/docker/config/hosts_test.go @@ -104,6 +104,13 @@ ca = "/etc/path/default" [host."https://test-3.registry"] client = ["/etc/certs/client-1.pem", "/etc/certs/client-2.pem"] + +[host."https://noncompliantmirror.registry/v2/namespaceprefix"] + capabilities = ["pull"] + override_path = true + +[host."https://noprefixnoncompliant.registry"] + override_path = true ` var tb, fb = true, false expected := []hostConfig{ @@ -159,6 +166,17 @@ ca = "/etc/path/default" {filepath.FromSlash("/etc/certs/client-2.pem")}, }, }, + { + scheme: "https", + host: "noncompliantmirror.registry", + path: "/v2/namespaceprefix", + capabilities: docker.HostCapabilityPull, + }, + { + scheme: "https", + host: "noprefixnoncompliant.registry", + capabilities: allCaps, + }, { scheme: "https", host: "test-default.registry",