Add host specific headers

Allows configuring headers per registry host

Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
Derek McGowan 2020-05-18 16:38:45 -07:00
parent 84619ee998
commit 3dd8242a67
No known key found for this signature in database
GPG Key ID: F58C5D0A4405ACDB
4 changed files with 56 additions and 1 deletions

View File

@ -48,6 +48,8 @@ type hostConfig struct {
clientPairs [][2]string
skipVerify *bool
header http.Header
// TODO: API ("docker" or "oci")
// TODO: API Version ("v1", "v2")
// TODO: Add credential configuration (domain alias, username)
@ -143,6 +145,7 @@ func ConfigureHosts(ctx context.Context, options HostOptions) docker.RegistryHos
rhosts[i].Host = host.host
rhosts[i].Path = host.path
rhosts[i].Capabilities = host.capabilities
rhosts[i].Header = host.header
if host.caCerts != nil || host.clientPairs != nil || host.skipVerify != nil {
tr := defaultTransport.Clone()
@ -203,7 +206,6 @@ func ConfigureHosts(ctx context.Context, options HostOptions) docker.RegistryHos
rhosts[i].Client = client
rhosts[i].Authorizer = authorizer
}
}
return rhosts, nil
@ -274,6 +276,8 @@ type hostFileConfig struct {
SkipVerify *bool `toml:"skip_verify"`
Header map[string]toml.Primitive `toml:"header"`
// API (default: "docker")
// API Version (default: "v2")
// Credentials: helper? name? username? alternate domain? token?
@ -433,6 +437,31 @@ func parseHostsFile(ctx context.Context, baseDir string, b []byte) ([]hostConfig
return nil, errors.Errorf("invalid type %v for \"client\"", t)
}
}
headerKey := append(baseKey, "header")
if md.IsDefined(headerKey...) {
header := http.Header{}
for key, prim := range hostConfig.Header {
switch t := md.Type(append(headerKey, key)...); t {
case "String":
var value string
if err := md.PrimitiveDecode(prim, &value); err != nil {
return nil, errors.Wrapf(err, "failed to decode header %q", key)
}
header[key] = []string{value}
case "Array":
var value []string
if err := md.PrimitiveDecode(prim, &value); err != nil {
return nil, errors.Wrapf(err, "failed to decode header %q", key)
}
header[key] = value
default:
return nil, errors.Errorf("invalid type %v for header %q", t, key)
}
}
hosts[i].header = header
}
}
return hosts, nil

View File

@ -20,6 +20,7 @@ import (
"bytes"
"context"
"fmt"
"net/http"
"path/filepath"
"testing"
@ -76,11 +77,15 @@ func TestParseHostFile(t *testing.T) {
const testtoml = `
server = "https://test-default.registry"
ca = "/etc/path/default"
[header]
x-custom-1 = "custom header"
[host."https://mirror.registry"]
capabilities = ["pull"]
ca = "/etc/certs/mirror.pem"
skip_verify = false
[host."https://mirror.registry".header]
x-custom-2 = ["value1", "value2"]
[host."https://mirror-bak.registry/us"]
capabilities = ["pull"]
@ -109,6 +114,7 @@ ca = "/etc/path/default"
capabilities: docker.HostCapabilityPull,
caCerts: []string{filepath.FromSlash("/etc/certs/mirror.pem")},
skipVerify: &fb,
header: http.Header{"x-custom-2": {"value1", "value2"}},
},
{
scheme: "https",
@ -159,6 +165,7 @@ ca = "/etc/path/default"
path: "/v2",
capabilities: allCaps,
caCerts: []string{filepath.FromSlash("/etc/path/default")},
header: http.Header{"x-custom-1": {"custom header"}},
},
}
hosts, err := parseHostsFile(ctx, "", []byte(testtoml))
@ -241,6 +248,20 @@ func compareHostConfig(j, k hostConfig) bool {
return false
}
if len(j.header) != len(k.header) {
return false
}
for key := range j.header {
if len(j.header[key]) != len(k.header[key]) {
return false
}
for i := range j.header[key] {
if j.header[key][i] != k.header[key][i] {
return false
}
}
}
return true
}
@ -258,6 +279,7 @@ func printHostConfig(hc []hostConfig) string {
} else {
fmt.Fprintf(b, "\t\tskip-verify: %t\n", *hc[i].skipVerify)
}
fmt.Fprintf(b, "\t\theader: %#v\n", hc[i].header)
}
return b.String()
}

View File

@ -70,6 +70,7 @@ type RegistryHost struct {
Scheme string
Path string
Capabilities HostCapabilities
Header http.Header
}
// RegistryHosts fetches the registry hosts for a given namespace,

View File

@ -450,6 +450,9 @@ func (r *dockerBase) request(host RegistryHost, method string, ps ...string) *re
for key, value := range r.header {
header[key] = append(header[key], value...)
}
for key, value := range host.Header {
header[key] = append(header[key], value...)
}
parts := append([]string{"/", host.Path, r.namespace}, ps...)
p := path.Join(parts...)
// Join strips trailing slash, re-add ending "/" if included