Switch to github.com/golang/dep for vendoring
Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
This commit is contained in:
parent
d6ab91be27
commit
8e5b17cf13
15431 changed files with 3971413 additions and 8881 deletions
15
vendor/github.com/containers/image/docker/daemon/daemon_dest.go
generated
vendored
15
vendor/github.com/containers/image/docker/daemon/daemon_dest.go
generated
vendored
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/manifest"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/docker/engine-api/client"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
|
@ -95,9 +95,12 @@ func (d *daemonImageDestination) Close() {
|
|||
if !d.committed {
|
||||
logrus.Debugf("docker-daemon: Closing tar stream to abort loading")
|
||||
// In principle, goroutineCancel() should abort the HTTP request and stop the process from continuing.
|
||||
// In practice, though, https://github.com/docker/engine-api/blob/master/client/transport/cancellable/cancellable.go
|
||||
// currently just runs the HTTP request to completion in a goroutine, and returns early if the context is canceled
|
||||
// without terminating the HTTP request at all. So we need this CloseWithError to terminate sending the HTTP request Body
|
||||
// In practice, though, various HTTP implementations used by client.Client.ImageLoad() (including
|
||||
// https://github.com/golang/net/blob/master/context/ctxhttp/ctxhttp_pre17.go and the
|
||||
// net/http version with native Context support in Go 1.7) do not always actually immediately cancel
|
||||
// the operation: they may process the HTTP request, or a part of it, to completion in a goroutine, and
|
||||
// return early if the context is canceled without terminating the goroutine at all.
|
||||
// So we need this CloseWithError to terminate sending the HTTP request Body
|
||||
// immediately, and hopefully, through terminating the sending which uses "Transfer-Encoding: chunked"" without sending
|
||||
// the terminating zero-length chunk, prevent the docker daemon from processing the tar stream at all.
|
||||
// Whether that works or not, closing the PipeWriter seems desirable in any case.
|
||||
|
@ -144,6 +147,10 @@ func (d *daemonImageDestination) AcceptsForeignLayerURLs() bool {
|
|||
// to any other readers for download using the supplied digest.
|
||||
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
|
||||
func (d *daemonImageDestination) PutBlob(stream io.Reader, inputInfo types.BlobInfo) (types.BlobInfo, error) {
|
||||
if inputInfo.Digest.String() == "" {
|
||||
return types.BlobInfo{}, errors.Errorf(`Can not stream a blob with unknown digest to "docker-daemon:"`)
|
||||
}
|
||||
|
||||
if ok, size, err := d.HasBlob(inputInfo); err == nil && ok {
|
||||
return types.BlobInfo{Digest: inputInfo.Digest, Size: size}, nil
|
||||
}
|
||||
|
|
2
vendor/github.com/containers/image/docker/daemon/daemon_src.go
generated
vendored
2
vendor/github.com/containers/image/docker/daemon/daemon_src.go
generated
vendored
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
"github.com/containers/image/manifest"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/docker/engine-api/client"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
|
|
236
vendor/github.com/containers/image/docker/daemon/daemon_transport_test.go
generated
vendored
Normal file
236
vendor/github.com/containers/image/docker/daemon/daemon_transport_test.go
generated
vendored
Normal file
|
@ -0,0 +1,236 @@
|
|||
package daemon
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
sha256digestHex = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
sha256digest = "sha256:" + sha256digestHex
|
||||
)
|
||||
|
||||
func TestTransportName(t *testing.T) {
|
||||
assert.Equal(t, "docker-daemon", Transport.Name())
|
||||
}
|
||||
|
||||
func TestTransportParseReference(t *testing.T) {
|
||||
testParseReference(t, Transport.ParseReference)
|
||||
}
|
||||
|
||||
func TestTransportValidatePolicyConfigurationScope(t *testing.T) {
|
||||
for _, scope := range []string{ // A semi-representative assortment of values; everything is rejected.
|
||||
sha256digestHex,
|
||||
sha256digest,
|
||||
"docker.io/library/busybox:latest",
|
||||
"docker.io",
|
||||
"",
|
||||
} {
|
||||
err := Transport.ValidatePolicyConfigurationScope(scope)
|
||||
assert.Error(t, err, scope)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseReference(t *testing.T) {
|
||||
testParseReference(t, ParseReference)
|
||||
}
|
||||
|
||||
// testParseReference is a test shared for Transport.ParseReference and ParseReference.
|
||||
func testParseReference(t *testing.T, fn func(string) (types.ImageReference, error)) {
|
||||
for _, c := range []struct{ input, expectedID, expectedRef string }{
|
||||
{sha256digest, sha256digest, ""}, // Valid digest format
|
||||
{"sha512:" + sha256digestHex + sha256digestHex, "", ""}, // Non-digest.Canonical digest
|
||||
{"sha256:ab", "", ""}, // Invalid digest value (too short)
|
||||
{sha256digest + "ab", "", ""}, // Invalid digest value (too long)
|
||||
{"sha256:XX23456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "", ""}, // Invalid digest value
|
||||
{"UPPERCASEISINVALID", "", ""}, // Invalid reference input
|
||||
{"busybox", "", ""}, // Missing tag or digest
|
||||
{"busybox:latest", "", "busybox:latest"}, // Explicit tag
|
||||
{"busybox@" + sha256digest, "", "busybox@" + sha256digest}, // Explicit digest
|
||||
// A github.com/distribution/reference value can have a tag and a digest at the same time!
|
||||
// github.com/docker/reference handles that by dropping the tag. That is not obviously the
|
||||
// right thing to do, but it is at least reasonable, so test that we keep behaving reasonably.
|
||||
// This test case should not be construed to make this an API promise.
|
||||
// FIXME? Instead work extra hard to reject such input?
|
||||
{"busybox:latest@" + sha256digest, "", "busybox@" + sha256digest}, // Both tag and digest
|
||||
{"docker.io/library/busybox:latest", "", "busybox:latest"}, // All implied values explicitly specified
|
||||
} {
|
||||
ref, err := fn(c.input)
|
||||
if c.expectedID == "" && c.expectedRef == "" {
|
||||
assert.Error(t, err, c.input)
|
||||
} else {
|
||||
require.NoError(t, err, c.input)
|
||||
daemonRef, ok := ref.(daemonReference)
|
||||
require.True(t, ok, c.input)
|
||||
// If we don't reject the input, the interpretation must be consistent for reference.ParseIDOrReference
|
||||
dockerID, dockerRef, err := reference.ParseIDOrReference(c.input)
|
||||
require.NoError(t, err, c.input)
|
||||
|
||||
if c.expectedRef == "" {
|
||||
assert.Equal(t, c.expectedID, daemonRef.id.String(), c.input)
|
||||
assert.Nil(t, daemonRef.ref, c.input)
|
||||
|
||||
assert.Equal(t, c.expectedID, dockerID.String(), c.input)
|
||||
assert.Nil(t, dockerRef, c.input)
|
||||
} else {
|
||||
assert.Equal(t, "", daemonRef.id.String(), c.input)
|
||||
require.NotNil(t, daemonRef.ref, c.input)
|
||||
assert.Equal(t, c.expectedRef, daemonRef.ref.String(), c.input)
|
||||
|
||||
assert.Equal(t, "", dockerID.String(), c.input)
|
||||
require.NotNil(t, dockerRef, c.input)
|
||||
assert.Equal(t, c.expectedRef, dockerRef.String(), c.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// refWithTagAndDigest is a reference.NamedTagged and reference.Canonical at the same time.
|
||||
type refWithTagAndDigest struct{ reference.Canonical }
|
||||
|
||||
func (ref refWithTagAndDigest) Tag() string {
|
||||
return "notLatest"
|
||||
}
|
||||
|
||||
// A common list of reference formats to test for the various ImageReference methods.
|
||||
// (For IDs it is much simpler, we simply use them unmodified)
|
||||
var validNamedReferenceTestCases = []struct{ input, dockerRef, stringWithinTransport string }{
|
||||
{"busybox:notlatest", "busybox:notlatest", "busybox:notlatest"}, // Explicit tag
|
||||
{"busybox" + sha256digest, "busybox" + sha256digest, "busybox" + sha256digest}, // Explicit digest
|
||||
{"docker.io/library/busybox:latest", "busybox:latest", "busybox:latest"}, // All implied values explicitly specified
|
||||
{"example.com/ns/foo:bar", "example.com/ns/foo:bar", "example.com/ns/foo:bar"}, // All values explicitly specified
|
||||
}
|
||||
|
||||
func TestNewReference(t *testing.T) {
|
||||
// An ID reference.
|
||||
id, err := digest.Parse(sha256digest)
|
||||
require.NoError(t, err)
|
||||
ref, err := NewReference(id, nil)
|
||||
require.NoError(t, err)
|
||||
daemonRef, ok := ref.(daemonReference)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, id, daemonRef.id)
|
||||
assert.Nil(t, daemonRef.ref)
|
||||
|
||||
// Named references
|
||||
for _, c := range validNamedReferenceTestCases {
|
||||
parsed, err := reference.ParseNamed(c.input)
|
||||
require.NoError(t, err)
|
||||
ref, err := NewReference("", parsed)
|
||||
require.NoError(t, err, c.input)
|
||||
daemonRef, ok := ref.(daemonReference)
|
||||
require.True(t, ok, c.input)
|
||||
assert.Equal(t, "", daemonRef.id.String())
|
||||
require.NotNil(t, daemonRef.ref)
|
||||
assert.Equal(t, c.dockerRef, daemonRef.ref.String(), c.input)
|
||||
}
|
||||
|
||||
// Both an ID and a named reference provided
|
||||
parsed, err := reference.ParseNamed("busybox:latest")
|
||||
require.NoError(t, err)
|
||||
_, err = NewReference(id, parsed)
|
||||
assert.Error(t, err)
|
||||
|
||||
// A reference with neither a tag nor digest
|
||||
parsed, err = reference.ParseNamed("busybox")
|
||||
require.NoError(t, err)
|
||||
_, err = NewReference("", parsed)
|
||||
assert.Error(t, err)
|
||||
|
||||
// A github.com/distribution/reference value can have a tag and a digest at the same time!
|
||||
parsed, err = reference.ParseNamed("busybox@" + sha256digest)
|
||||
require.NoError(t, err)
|
||||
refDigested, ok := parsed.(reference.Canonical)
|
||||
require.True(t, ok)
|
||||
tagDigestRef := refWithTagAndDigest{refDigested}
|
||||
_, err = NewReference("", tagDigestRef)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestReferenceTransport(t *testing.T) {
|
||||
ref, err := ParseReference(sha256digest)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, Transport, ref.Transport())
|
||||
|
||||
ref, err = ParseReference("busybox:latest")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, Transport, ref.Transport())
|
||||
}
|
||||
|
||||
func TestReferenceStringWithinTransport(t *testing.T) {
|
||||
ref, err := ParseReference(sha256digest)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, sha256digest, ref.StringWithinTransport())
|
||||
|
||||
for _, c := range validNamedReferenceTestCases {
|
||||
ref, err := ParseReference(c.input)
|
||||
require.NoError(t, err, c.input)
|
||||
stringRef := ref.StringWithinTransport()
|
||||
assert.Equal(t, c.stringWithinTransport, stringRef, c.input)
|
||||
// Do one more round to verify that the output can be parsed, to an equal value.
|
||||
ref2, err := Transport.ParseReference(stringRef)
|
||||
require.NoError(t, err, c.input)
|
||||
stringRef2 := ref2.StringWithinTransport()
|
||||
assert.Equal(t, stringRef, stringRef2, c.input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReferenceDockerReference(t *testing.T) {
|
||||
ref, err := ParseReference(sha256digest)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, ref.DockerReference())
|
||||
|
||||
for _, c := range validNamedReferenceTestCases {
|
||||
ref, err := ParseReference(c.input)
|
||||
require.NoError(t, err, c.input)
|
||||
dockerRef := ref.DockerReference()
|
||||
require.NotNil(t, dockerRef, c.input)
|
||||
assert.Equal(t, c.dockerRef, dockerRef.String(), c.input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReferencePolicyConfigurationIdentity(t *testing.T) {
|
||||
ref, err := ParseReference(sha256digest)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "", ref.PolicyConfigurationIdentity())
|
||||
|
||||
for _, c := range validNamedReferenceTestCases {
|
||||
ref, err := ParseReference(c.input)
|
||||
require.NoError(t, err, c.input)
|
||||
assert.Equal(t, "", ref.PolicyConfigurationIdentity(), c.input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReferencePolicyConfigurationNamespaces(t *testing.T) {
|
||||
ref, err := ParseReference(sha256digest)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, ref.PolicyConfigurationNamespaces())
|
||||
|
||||
for _, c := range validNamedReferenceTestCases {
|
||||
ref, err := ParseReference(c.input)
|
||||
require.NoError(t, err, c.input)
|
||||
assert.Empty(t, ref.PolicyConfigurationNamespaces(), c.input)
|
||||
}
|
||||
}
|
||||
|
||||
// daemonReference.NewImage, daemonReference.NewImageSource, openshiftReference.NewImageDestination
|
||||
// untested because just creating the objects immediately connects to the daemon.
|
||||
|
||||
func TestReferenceDeleteImage(t *testing.T) {
|
||||
ref, err := ParseReference(sha256digest)
|
||||
require.NoError(t, err)
|
||||
err = ref.DeleteImage(nil)
|
||||
assert.Error(t, err)
|
||||
|
||||
for _, c := range validNamedReferenceTestCases {
|
||||
ref, err := ParseReference(c.input)
|
||||
require.NoError(t, err, c.input)
|
||||
err = ref.DeleteImage(nil)
|
||||
assert.Error(t, err, c.input)
|
||||
}
|
||||
}
|
146
vendor/github.com/containers/image/docker/docker_client.go
generated
vendored
146
vendor/github.com/containers/image/docker/docker_client.go
generated
vendored
|
@ -45,14 +45,20 @@ var ErrV1NotSupported = errors.New("can't talk to a V1 docker registry")
|
|||
|
||||
// dockerClient is configuration for dealing with a single Docker registry.
|
||||
type dockerClient struct {
|
||||
ctx *types.SystemContext
|
||||
registry string
|
||||
username string
|
||||
password string
|
||||
wwwAuthenticate string // Cache of a value set by ping() if scheme is not empty
|
||||
scheme string // Cache of a value returned by a successful ping() if not empty
|
||||
client *http.Client
|
||||
signatureBase signatureStorageBase
|
||||
ctx *types.SystemContext
|
||||
registry string
|
||||
username string
|
||||
password string
|
||||
scheme string // Cache of a value returned by a successful ping() if not empty
|
||||
client *http.Client
|
||||
signatureBase signatureStorageBase
|
||||
challenges []challenge
|
||||
scope authScope
|
||||
}
|
||||
|
||||
type authScope struct {
|
||||
remoteName string
|
||||
actions string
|
||||
}
|
||||
|
||||
// this is cloned from docker/go-connections because upstream docker has changed
|
||||
|
@ -147,7 +153,7 @@ func hasFile(files []os.FileInfo, name string) bool {
|
|||
|
||||
// newDockerClient returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry)
|
||||
// “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection)
|
||||
func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool) (*dockerClient, error) {
|
||||
func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool, actions string) (*dockerClient, error) {
|
||||
registry := ref.ref.Hostname()
|
||||
if registry == dockerHostname {
|
||||
registry = dockerRegistry
|
||||
|
@ -184,6 +190,10 @@ func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool)
|
|||
password: password,
|
||||
client: client,
|
||||
signatureBase: sigBase,
|
||||
scope: authScope{
|
||||
actions: actions,
|
||||
remoteName: ref.ref.RemoteName(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -191,12 +201,9 @@ func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool)
|
|||
// url is NOT an absolute URL, but a path relative to the /v2/ top-level API path. The host name and schema is taken from the client or autodetected.
|
||||
func (c *dockerClient) makeRequest(method, url string, headers map[string][]string, stream io.Reader) (*http.Response, error) {
|
||||
if c.scheme == "" {
|
||||
pr, err := c.ping()
|
||||
if err != nil {
|
||||
if err := c.ping(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.wwwAuthenticate = pr.WWWAuthenticate
|
||||
c.scheme = pr.scheme
|
||||
}
|
||||
|
||||
url = fmt.Sprintf(baseURL, c.scheme, c.registry) + url
|
||||
|
@ -224,7 +231,7 @@ func (c *dockerClient) makeRequestToResolvedURL(method, url string, headers map[
|
|||
if c.ctx != nil && c.ctx.DockerRegistryUserAgent != "" {
|
||||
req.Header.Add("User-Agent", c.ctx.DockerRegistryUserAgent)
|
||||
}
|
||||
if c.wwwAuthenticate != "" && sendAuth {
|
||||
if sendAuth {
|
||||
if err := c.setupRequestAuth(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -237,78 +244,30 @@ func (c *dockerClient) makeRequestToResolvedURL(method, url string, headers map[
|
|||
return res, nil
|
||||
}
|
||||
|
||||
// we're using the challenges from the /v2/ ping response and not the one from the destination
|
||||
// URL in this request because:
|
||||
//
|
||||
// 1) docker does that as well
|
||||
// 2) gcr.io is sending 401 without a WWW-Authenticate header in the real request
|
||||
//
|
||||
// debugging: https://github.com/containers/image/pull/211#issuecomment-273426236 and follows up
|
||||
func (c *dockerClient) setupRequestAuth(req *http.Request) error {
|
||||
tokens := strings.SplitN(strings.TrimSpace(c.wwwAuthenticate), " ", 2)
|
||||
if len(tokens) != 2 {
|
||||
return errors.Errorf("expected 2 tokens in WWW-Authenticate: %d, %s", len(tokens), c.wwwAuthenticate)
|
||||
if len(c.challenges) == 0 {
|
||||
return nil
|
||||
}
|
||||
switch tokens[0] {
|
||||
case "Basic":
|
||||
// assume just one...
|
||||
challenge := c.challenges[0]
|
||||
switch challenge.Scheme {
|
||||
case "basic":
|
||||
req.SetBasicAuth(c.username, c.password)
|
||||
return nil
|
||||
case "Bearer":
|
||||
// FIXME? This gets a new token for every API request;
|
||||
// we may be easily able to reuse a previous token, e.g.
|
||||
// for OpenShift the token only identifies the user and does not vary
|
||||
// across operations. Should we just try the request first, and
|
||||
// only get a new token on failure?
|
||||
// OTOH what to do with the single-use body stream in that case?
|
||||
|
||||
// Try performing the request, expecting it to fail.
|
||||
testReq := *req
|
||||
// Do not use the body stream, or we couldn't reuse it for the "real" call later.
|
||||
testReq.Body = nil
|
||||
testReq.ContentLength = 0
|
||||
res, err := c.client.Do(&testReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chs := parseAuthHeader(res.Header)
|
||||
// We could end up in this "if" statement if the /v2/ call (during ping)
|
||||
// returned 401 with a valid WWW-Authenticate=Bearer header.
|
||||
// That doesn't **always** mean, however, that the specific API request
|
||||
// (different from /v2/) actually needs to be authorized.
|
||||
// One example of this _weird_ scenario happens with GCR.io docker
|
||||
// registries.
|
||||
if res.StatusCode != http.StatusUnauthorized || chs == nil || len(chs) == 0 {
|
||||
// With gcr.io, the /v2/ call returns a 401 with a valid WWW-Authenticate=Bearer
|
||||
// header but the repository could be _public_ (no authorization is needed).
|
||||
// Hence, the registry response contains no challenges and the status
|
||||
// code is not 401.
|
||||
// We just skip this case as it's not standard on docker/distribution
|
||||
// registries (https://github.com/docker/distribution/blob/master/docs/spec/api.md#api-version-check)
|
||||
if res.StatusCode != http.StatusUnauthorized {
|
||||
return nil
|
||||
}
|
||||
// gcr.io private repositories pull instead requires us to send user:pass pair in
|
||||
// order to retrieve a token and setup the correct Bearer token.
|
||||
// try again one last time with Basic Auth
|
||||
testReq2 := *req
|
||||
// Do not use the body stream, or we couldn't reuse it for the "real" call later.
|
||||
testReq2.Body = nil
|
||||
testReq2.ContentLength = 0
|
||||
testReq2.SetBasicAuth(c.username, c.password)
|
||||
res, err := c.client.Do(&testReq2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chs = parseAuthHeader(res.Header)
|
||||
if res.StatusCode != http.StatusUnauthorized || chs == nil || len(chs) == 0 {
|
||||
// no need for bearer? wtf?
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Arbitrarily use the first challenge, there is no reason to expect more than one.
|
||||
challenge := chs[0]
|
||||
if challenge.Scheme != "bearer" { // Another artifact of trying to handle WWW-Authenticate before it actually happens.
|
||||
return errors.Errorf("Unimplemented: WWW-Authenticate Bearer replaced by %#v", challenge.Scheme)
|
||||
}
|
||||
case "bearer":
|
||||
realm, ok := challenge.Parameters["realm"]
|
||||
if !ok {
|
||||
return errors.Errorf("missing realm in bearer auth challenge")
|
||||
}
|
||||
service, _ := challenge.Parameters["service"] // Will be "" if not present
|
||||
scope, _ := challenge.Parameters["scope"] // Will be "" if not present
|
||||
scope := fmt.Sprintf("repository:%s:%s", c.scope.remoteName, c.scope.actions)
|
||||
token, err := c.getBearerToken(realm, service, scope)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -316,8 +275,7 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error {
|
|||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
return nil
|
||||
}
|
||||
return errors.Errorf("no handler for %s authentication", tokens[0])
|
||||
// support docker bearer with authconfig's Auth string? see docker2aci
|
||||
return errors.Errorf("no handler for %s authentication", challenge.Scheme)
|
||||
}
|
||||
|
||||
func (c *dockerClient) getBearerToken(realm, service, scope string) (string, error) {
|
||||
|
@ -427,39 +385,31 @@ func getAuth(ctx *types.SystemContext, registry string) (string, string, error)
|
|||
return "", "", nil
|
||||
}
|
||||
|
||||
type pingResponse struct {
|
||||
WWWAuthenticate string
|
||||
APIVersion string
|
||||
scheme string
|
||||
}
|
||||
|
||||
func (c *dockerClient) ping() (*pingResponse, error) {
|
||||
ping := func(scheme string) (*pingResponse, error) {
|
||||
func (c *dockerClient) ping() error {
|
||||
ping := func(scheme string) error {
|
||||
url := fmt.Sprintf(baseURL, scheme, c.registry)
|
||||
resp, err := c.makeRequestToResolvedURL("GET", url, nil, nil, -1, true)
|
||||
logrus.Debugf("Ping %s err %#v", url, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
logrus.Debugf("Ping %s status %d", scheme+"://"+c.registry+"/v2/", resp.StatusCode)
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized {
|
||||
return nil, errors.Errorf("error pinging repository, response code %d", resp.StatusCode)
|
||||
return errors.Errorf("error pinging repository, response code %d", resp.StatusCode)
|
||||
}
|
||||
pr := &pingResponse{}
|
||||
pr.WWWAuthenticate = resp.Header.Get("WWW-Authenticate")
|
||||
pr.APIVersion = resp.Header.Get("Docker-Distribution-Api-Version")
|
||||
pr.scheme = scheme
|
||||
return pr, nil
|
||||
c.challenges = parseAuthHeader(resp.Header)
|
||||
c.scheme = scheme
|
||||
return nil
|
||||
}
|
||||
pr, err := ping("https")
|
||||
err := ping("https")
|
||||
if err != nil && c.ctx != nil && c.ctx.DockerInsecureSkipTLSVerify {
|
||||
pr, err = ping("http")
|
||||
err = ping("http")
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "pinging docker registry returned")
|
||||
if c.ctx != nil && c.ctx.DockerDisableV1Ping {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
// best effort to understand if we're talking to a V1 registry
|
||||
pingV1 := func(scheme string) bool {
|
||||
|
@ -484,7 +434,7 @@ func (c *dockerClient) ping() (*pingResponse, error) {
|
|||
err = ErrV1NotSupported
|
||||
}
|
||||
}
|
||||
return pr, err
|
||||
return err
|
||||
}
|
||||
|
||||
func getDefaultConfigDir(confPath string) string {
|
||||
|
|
432
vendor/github.com/containers/image/docker/docker_client_test.go
generated
vendored
Normal file
432
vendor/github.com/containers/image/docker/docker_client_test.go
generated
vendored
Normal file
|
@ -0,0 +1,432 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
//"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/storage/pkg/homedir"
|
||||
)
|
||||
|
||||
func TestGetAuth(t *testing.T) {
|
||||
origHomeDir := homedir.Get()
|
||||
tmpDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("using temporary home directory: %q", tmpDir)
|
||||
// override homedir
|
||||
os.Setenv(homedir.Key(), tmpDir)
|
||||
defer func() {
|
||||
err := os.RemoveAll(tmpDir)
|
||||
if err != nil {
|
||||
t.Logf("failed to cleanup temporary home directory %q: %v", tmpDir, err)
|
||||
}
|
||||
os.Setenv(homedir.Key(), origHomeDir)
|
||||
}()
|
||||
|
||||
configDir := filepath.Join(tmpDir, ".docker")
|
||||
if err := os.Mkdir(configDir, 0750); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
configPath := filepath.Join(configDir, "config.json")
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
hostname string
|
||||
authConfig testAuthConfig
|
||||
expectedUsername string
|
||||
expectedPassword string
|
||||
expectedError error
|
||||
ctx *types.SystemContext
|
||||
}{
|
||||
{
|
||||
name: "empty hostname",
|
||||
authConfig: makeTestAuthConfig(testAuthConfigDataMap{"localhost:5000": testAuthConfigData{"bob", "password"}}),
|
||||
},
|
||||
{
|
||||
name: "no auth config",
|
||||
hostname: "index.docker.io",
|
||||
},
|
||||
{
|
||||
name: "match one",
|
||||
hostname: "example.org",
|
||||
authConfig: makeTestAuthConfig(testAuthConfigDataMap{"example.org": testAuthConfigData{"joe", "mypass"}}),
|
||||
expectedUsername: "joe",
|
||||
expectedPassword: "mypass",
|
||||
},
|
||||
{
|
||||
name: "match none",
|
||||
hostname: "registry.example.org",
|
||||
authConfig: makeTestAuthConfig(testAuthConfigDataMap{"example.org": testAuthConfigData{"joe", "mypass"}}),
|
||||
},
|
||||
{
|
||||
name: "match docker.io",
|
||||
hostname: "docker.io",
|
||||
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||
"example.org": testAuthConfigData{"example", "org"},
|
||||
"index.docker.io": testAuthConfigData{"index", "docker.io"},
|
||||
"docker.io": testAuthConfigData{"docker", "io"},
|
||||
}),
|
||||
expectedUsername: "docker",
|
||||
expectedPassword: "io",
|
||||
},
|
||||
{
|
||||
name: "match docker.io normalized",
|
||||
hostname: "docker.io",
|
||||
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||
"example.org": testAuthConfigData{"bob", "pw"},
|
||||
"https://index.docker.io/v1": testAuthConfigData{"alice", "wp"},
|
||||
}),
|
||||
expectedUsername: "alice",
|
||||
expectedPassword: "wp",
|
||||
},
|
||||
{
|
||||
name: "normalize registry",
|
||||
hostname: "https://docker.io/v1",
|
||||
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||
"docker.io": testAuthConfigData{"user", "pw"},
|
||||
"localhost:5000": testAuthConfigData{"joe", "pass"},
|
||||
}),
|
||||
expectedUsername: "user",
|
||||
expectedPassword: "pw",
|
||||
},
|
||||
{
|
||||
name: "match localhost",
|
||||
hostname: "http://localhost",
|
||||
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||
"docker.io": testAuthConfigData{"user", "pw"},
|
||||
"localhost": testAuthConfigData{"joe", "pass"},
|
||||
"example.com": testAuthConfigData{"alice", "pwd"},
|
||||
}),
|
||||
expectedUsername: "joe",
|
||||
expectedPassword: "pass",
|
||||
},
|
||||
{
|
||||
name: "match ip",
|
||||
hostname: "10.10.3.56:5000",
|
||||
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||
"10.10.30.45": testAuthConfigData{"user", "pw"},
|
||||
"localhost": testAuthConfigData{"joe", "pass"},
|
||||
"10.10.3.56": testAuthConfigData{"alice", "pwd"},
|
||||
"10.10.3.56:5000": testAuthConfigData{"me", "mine"},
|
||||
}),
|
||||
expectedUsername: "me",
|
||||
expectedPassword: "mine",
|
||||
},
|
||||
{
|
||||
name: "match port",
|
||||
hostname: "https://localhost:5000",
|
||||
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||
"https://127.0.0.1:5000": testAuthConfigData{"user", "pw"},
|
||||
"http://localhost": testAuthConfigData{"joe", "pass"},
|
||||
"https://localhost:5001": testAuthConfigData{"alice", "pwd"},
|
||||
"localhost:5000": testAuthConfigData{"me", "mine"},
|
||||
}),
|
||||
expectedUsername: "me",
|
||||
expectedPassword: "mine",
|
||||
},
|
||||
{
|
||||
name: "use system context",
|
||||
hostname: "example.org",
|
||||
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||
"example.org": testAuthConfigData{"user", "pw"},
|
||||
}),
|
||||
expectedUsername: "foo",
|
||||
expectedPassword: "bar",
|
||||
ctx: &types.SystemContext{
|
||||
DockerAuthConfig: &types.DockerAuthConfig{
|
||||
Username: "foo",
|
||||
Password: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
contents, err := json.MarshalIndent(&tc.authConfig, "", " ")
|
||||
if err != nil {
|
||||
t.Errorf("[%s] failed to marshal authConfig: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
if err := ioutil.WriteFile(configPath, contents, 0640); err != nil {
|
||||
t.Errorf("[%s] failed to write file %q: %v", tc.name, configPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
var ctx *types.SystemContext
|
||||
if tc.ctx != nil {
|
||||
ctx = tc.ctx
|
||||
}
|
||||
username, password, err := getAuth(ctx, tc.hostname)
|
||||
if err == nil && tc.expectedError != nil {
|
||||
t.Errorf("[%s] got unexpected non error and username=%q, password=%q", tc.name, username, password)
|
||||
continue
|
||||
}
|
||||
if err != nil && tc.expectedError == nil {
|
||||
t.Errorf("[%s] got unexpected error: %#+v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(err, tc.expectedError) {
|
||||
t.Errorf("[%s] got unexpected error: %#+v != %#+v", tc.name, err, tc.expectedError)
|
||||
continue
|
||||
}
|
||||
|
||||
if username != tc.expectedUsername {
|
||||
t.Errorf("[%s] got unexpected user name: %q != %q", tc.name, username, tc.expectedUsername)
|
||||
}
|
||||
if password != tc.expectedPassword {
|
||||
t.Errorf("[%s] got unexpected user name: %q != %q", tc.name, password, tc.expectedPassword)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAuthFromLegacyFile(t *testing.T) {
|
||||
origHomeDir := homedir.Get()
|
||||
tmpDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("using temporary home directory: %q", tmpDir)
|
||||
// override homedir
|
||||
os.Setenv(homedir.Key(), tmpDir)
|
||||
defer func() {
|
||||
err := os.RemoveAll(tmpDir)
|
||||
if err != nil {
|
||||
t.Logf("failed to cleanup temporary home directory %q: %v", tmpDir, err)
|
||||
}
|
||||
os.Setenv(homedir.Key(), origHomeDir)
|
||||
}()
|
||||
|
||||
configPath := filepath.Join(tmpDir, ".dockercfg")
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
hostname string
|
||||
authConfig testAuthConfig
|
||||
expectedUsername string
|
||||
expectedPassword string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "normalize registry",
|
||||
hostname: "https://docker.io/v1",
|
||||
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||
"docker.io": testAuthConfigData{"user", "pw"},
|
||||
"localhost:5000": testAuthConfigData{"joe", "pass"},
|
||||
}),
|
||||
expectedUsername: "user",
|
||||
expectedPassword: "pw",
|
||||
},
|
||||
{
|
||||
name: "ignore schema and path",
|
||||
hostname: "http://index.docker.io/v1",
|
||||
authConfig: makeTestAuthConfig(testAuthConfigDataMap{
|
||||
"docker.io/v2": testAuthConfigData{"user", "pw"},
|
||||
"https://localhost/v1": testAuthConfigData{"joe", "pwd"},
|
||||
}),
|
||||
expectedUsername: "user",
|
||||
expectedPassword: "pw",
|
||||
},
|
||||
} {
|
||||
contents, err := json.MarshalIndent(&tc.authConfig.Auths, "", " ")
|
||||
if err != nil {
|
||||
t.Errorf("[%s] failed to marshal authConfig: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
if err := ioutil.WriteFile(configPath, contents, 0640); err != nil {
|
||||
t.Errorf("[%s] failed to write file %q: %v", tc.name, configPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
username, password, err := getAuth(nil, tc.hostname)
|
||||
if err == nil && tc.expectedError != nil {
|
||||
t.Errorf("[%s] got unexpected non error and username=%q, password=%q", tc.name, username, password)
|
||||
continue
|
||||
}
|
||||
if err != nil && tc.expectedError == nil {
|
||||
t.Errorf("[%s] got unexpected error: %#+v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(err, tc.expectedError) {
|
||||
t.Errorf("[%s] got unexpected error: %#+v != %#+v", tc.name, err, tc.expectedError)
|
||||
continue
|
||||
}
|
||||
|
||||
if username != tc.expectedUsername {
|
||||
t.Errorf("[%s] got unexpected user name: %q != %q", tc.name, username, tc.expectedUsername)
|
||||
}
|
||||
if password != tc.expectedPassword {
|
||||
t.Errorf("[%s] got unexpected user name: %q != %q", tc.name, password, tc.expectedPassword)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAuthPreferNewConfig(t *testing.T) {
|
||||
origHomeDir := homedir.Get()
|
||||
tmpDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("using temporary home directory: %q", tmpDir)
|
||||
// override homedir
|
||||
os.Setenv(homedir.Key(), tmpDir)
|
||||
defer func() {
|
||||
err := os.RemoveAll(tmpDir)
|
||||
if err != nil {
|
||||
t.Logf("failed to cleanup temporary home directory %q: %v", tmpDir, err)
|
||||
}
|
||||
os.Setenv(homedir.Key(), origHomeDir)
|
||||
}()
|
||||
|
||||
configDir := filepath.Join(tmpDir, ".docker")
|
||||
if err := os.Mkdir(configDir, 0750); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, data := range []struct {
|
||||
path string
|
||||
ac interface{}
|
||||
}{
|
||||
{
|
||||
filepath.Join(configDir, "config.json"),
|
||||
makeTestAuthConfig(testAuthConfigDataMap{
|
||||
"https://index.docker.io/v1/": testAuthConfigData{"alice", "pass"},
|
||||
}),
|
||||
},
|
||||
{
|
||||
filepath.Join(tmpDir, ".dockercfg"),
|
||||
makeTestAuthConfig(testAuthConfigDataMap{
|
||||
"https://index.docker.io/v1/": testAuthConfigData{"bob", "pw"},
|
||||
}).Auths,
|
||||
},
|
||||
} {
|
||||
contents, err := json.MarshalIndent(&data.ac, "", " ")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to marshal authConfig: %v", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(data.path, contents, 0640); err != nil {
|
||||
t.Fatalf("failed to write file %q: %v", data.path, err)
|
||||
}
|
||||
}
|
||||
|
||||
username, password, err := getAuth(nil, "index.docker.io")
|
||||
if err != nil {
|
||||
t.Fatalf("got unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if username != "alice" {
|
||||
t.Fatalf("got unexpected user name: %q != %q", username, "alice")
|
||||
}
|
||||
if password != "pass" {
|
||||
t.Fatalf("got unexpected user name: %q != %q", password, "pass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAuthFailsOnBadInput(t *testing.T) {
|
||||
origHomeDir := homedir.Get()
|
||||
tmpDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("using temporary home directory: %q", tmpDir)
|
||||
// override homedir
|
||||
os.Setenv(homedir.Key(), tmpDir)
|
||||
defer func() {
|
||||
err := os.RemoveAll(tmpDir)
|
||||
if err != nil {
|
||||
t.Logf("failed to cleanup temporary home directory %q: %v", tmpDir, err)
|
||||
}
|
||||
os.Setenv(homedir.Key(), origHomeDir)
|
||||
}()
|
||||
|
||||
configDir := filepath.Join(tmpDir, ".docker")
|
||||
if err := os.Mkdir(configDir, 0750); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
configPath := filepath.Join(configDir, "config.json")
|
||||
|
||||
// no config file present
|
||||
username, password, err := getAuth(nil, "index.docker.io")
|
||||
if err != nil {
|
||||
t.Fatalf("got unexpected error: %#+v", err)
|
||||
}
|
||||
if len(username) > 0 || len(password) > 0 {
|
||||
t.Fatalf("got unexpected not empty username/password: %q/%q", username, password)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(configPath, []byte("Json rocks! Unless it doesn't."), 0640); err != nil {
|
||||
t.Fatalf("failed to write file %q: %v", configPath, err)
|
||||
}
|
||||
username, password, err = getAuth(nil, "index.docker.io")
|
||||
if err == nil {
|
||||
t.Fatalf("got unexpected non-error: username=%q, password=%q", username, password)
|
||||
}
|
||||
if _, ok := err.(*json.SyntaxError); !ok {
|
||||
t.Fatalf("expected os.PathError, not: %#+v", err)
|
||||
}
|
||||
|
||||
// remove the invalid config file
|
||||
os.RemoveAll(configPath)
|
||||
// no config file present
|
||||
username, password, err = getAuth(nil, "index.docker.io")
|
||||
if err != nil {
|
||||
t.Fatalf("got unexpected error: %#+v", err)
|
||||
}
|
||||
if len(username) > 0 || len(password) > 0 {
|
||||
t.Fatalf("got unexpected not empty username/password: %q/%q", username, password)
|
||||
}
|
||||
|
||||
configPath = filepath.Join(tmpDir, ".dockercfg")
|
||||
if err := ioutil.WriteFile(configPath, []byte("I'm certainly not a json string."), 0640); err != nil {
|
||||
t.Fatalf("failed to write file %q: %v", configPath, err)
|
||||
}
|
||||
username, password, err = getAuth(nil, "index.docker.io")
|
||||
if err == nil {
|
||||
t.Fatalf("got unexpected non-error: username=%q, password=%q", username, password)
|
||||
}
|
||||
if _, ok := err.(*json.SyntaxError); !ok {
|
||||
t.Fatalf("expected os.PathError, not: %#+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type testAuthConfigData struct {
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
type testAuthConfigDataMap map[string]testAuthConfigData
|
||||
|
||||
type testAuthConfigEntry struct {
|
||||
Auth string `json:"auth,omitempty"`
|
||||
}
|
||||
|
||||
type testAuthConfig struct {
|
||||
Auths map[string]testAuthConfigEntry `json:"auths"`
|
||||
}
|
||||
|
||||
// encodeAuth creates an auth value from given authConfig data to be stored in auth config file.
|
||||
// Inspired by github.com/docker/docker/cliconfig/config.go v1.10.3.
|
||||
func encodeAuth(authConfig *testAuthConfigData) string {
|
||||
authStr := authConfig.username + ":" + authConfig.password
|
||||
msg := []byte(authStr)
|
||||
encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
|
||||
base64.StdEncoding.Encode(encoded, msg)
|
||||
return string(encoded)
|
||||
}
|
||||
|
||||
func makeTestAuthConfig(authConfigData map[string]testAuthConfigData) testAuthConfig {
|
||||
ac := testAuthConfig{
|
||||
Auths: make(map[string]testAuthConfigEntry),
|
||||
}
|
||||
for host, data := range authConfigData {
|
||||
ac.Auths[host] = testAuthConfigEntry{
|
||||
Auth: encodeAuth(&data),
|
||||
}
|
||||
}
|
||||
return ac
|
||||
}
|
24
vendor/github.com/containers/image/docker/docker_image_dest.go
generated
vendored
24
vendor/github.com/containers/image/docker/docker_image_dest.go
generated
vendored
|
@ -17,6 +17,21 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var manifestMIMETypes = []string{
|
||||
// TODO(runcom): we'll add OCI as part of another PR here
|
||||
manifest.DockerV2Schema2MediaType,
|
||||
manifest.DockerV2Schema1SignedMediaType,
|
||||
manifest.DockerV2Schema1MediaType,
|
||||
}
|
||||
|
||||
func supportedManifestMIMETypesMap() map[string]bool {
|
||||
m := make(map[string]bool, len(manifestMIMETypes))
|
||||
for _, mt := range manifestMIMETypes {
|
||||
m[mt] = true
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
type dockerImageDestination struct {
|
||||
ref dockerReference
|
||||
c *dockerClient
|
||||
|
@ -26,7 +41,7 @@ type dockerImageDestination struct {
|
|||
|
||||
// newImageDestination creates a new ImageDestination for the specified image reference.
|
||||
func newImageDestination(ctx *types.SystemContext, ref dockerReference) (types.ImageDestination, error) {
|
||||
c, err := newDockerClient(ctx, ref, true)
|
||||
c, err := newDockerClient(ctx, ref, true, "push")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -47,12 +62,7 @@ func (d *dockerImageDestination) Close() {
|
|||
}
|
||||
|
||||
func (d *dockerImageDestination) SupportedManifestMIMETypes() []string {
|
||||
return []string{
|
||||
// TODO(runcom): we'll add OCI as part of another PR here
|
||||
manifest.DockerV2Schema2MediaType,
|
||||
manifest.DockerV2Schema1SignedMediaType,
|
||||
manifest.DockerV2Schema1MediaType,
|
||||
}
|
||||
return manifestMIMETypes
|
||||
}
|
||||
|
||||
// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
|
||||
|
|
15
vendor/github.com/containers/image/docker/docker_image_src.go
generated
vendored
15
vendor/github.com/containers/image/docker/docker_image_src.go
generated
vendored
|
@ -32,13 +32,24 @@ type dockerImageSource struct {
|
|||
// nil requestedManifestMIMETypes means manifest.DefaultRequestedManifestMIMETypes.
|
||||
// The caller must call .Close() on the returned ImageSource.
|
||||
func newImageSource(ctx *types.SystemContext, ref dockerReference, requestedManifestMIMETypes []string) (*dockerImageSource, error) {
|
||||
c, err := newDockerClient(ctx, ref, false)
|
||||
c, err := newDockerClient(ctx, ref, false, "pull")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if requestedManifestMIMETypes == nil {
|
||||
requestedManifestMIMETypes = manifest.DefaultRequestedManifestMIMETypes
|
||||
}
|
||||
supportedMIMEs := supportedManifestMIMETypesMap()
|
||||
acceptableRequestedMIMEs := false
|
||||
for _, mtrequested := range requestedManifestMIMETypes {
|
||||
if supportedMIMEs[mtrequested] {
|
||||
acceptableRequestedMIMEs = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !acceptableRequestedMIMEs {
|
||||
requestedManifestMIMETypes = manifest.DefaultRequestedManifestMIMETypes
|
||||
}
|
||||
return &dockerImageSource{
|
||||
ref: ref,
|
||||
requestedManifestMIMETypes: requestedManifestMIMETypes,
|
||||
|
@ -250,7 +261,7 @@ func (s *dockerImageSource) getOneSignature(url *url.URL) (signature []byte, mis
|
|||
|
||||
// deleteImage deletes the named image from the registry, if supported.
|
||||
func deleteImage(ctx *types.SystemContext, ref dockerReference) error {
|
||||
c, err := newDockerClient(ctx, ref, true)
|
||||
c, err := newDockerClient(ctx, ref, true, "push")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
24
vendor/github.com/containers/image/docker/docker_image_src_test.go
generated
vendored
Normal file
24
vendor/github.com/containers/image/docker/docker_image_src_test.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSimplifyContentType(t *testing.T) {
|
||||
for _, c := range []struct{ input, expected string }{
|
||||
{"", ""},
|
||||
{"application/json", "application/json"},
|
||||
{"application/json;charset=utf-8", "application/json"},
|
||||
{"application/json; charset=utf-8", "application/json"},
|
||||
{"application/json ; charset=utf-8", "application/json"},
|
||||
{"application/json\t;\tcharset=utf-8", "application/json"},
|
||||
{"application/json ;charset=utf-8", "application/json"},
|
||||
{`application/json; charset="utf-8"`, "application/json"},
|
||||
{"completely invalid", ""},
|
||||
} {
|
||||
out := simplifyContentType(c.input)
|
||||
assert.Equal(t, c.expected, out, c.input)
|
||||
}
|
||||
}
|
204
vendor/github.com/containers/image/docker/docker_transport_test.go
generated
vendored
Normal file
204
vendor/github.com/containers/image/docker/docker_transport_test.go
generated
vendored
Normal file
|
@ -0,0 +1,204 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
sha256digestHex = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
sha256digest = "@sha256:" + sha256digestHex
|
||||
)
|
||||
|
||||
func TestTransportName(t *testing.T) {
|
||||
assert.Equal(t, "docker", Transport.Name())
|
||||
}
|
||||
|
||||
func TestTransportParseReference(t *testing.T) {
|
||||
testParseReference(t, Transport.ParseReference)
|
||||
}
|
||||
|
||||
func TestTransportValidatePolicyConfigurationScope(t *testing.T) {
|
||||
for _, scope := range []string{
|
||||
"docker.io/library/busybox" + sha256digest,
|
||||
"docker.io/library/busybox:notlatest",
|
||||
"docker.io/library/busybox",
|
||||
"docker.io/library",
|
||||
"docker.io",
|
||||
} {
|
||||
err := Transport.ValidatePolicyConfigurationScope(scope)
|
||||
assert.NoError(t, err, scope)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseReference(t *testing.T) {
|
||||
testParseReference(t, ParseReference)
|
||||
}
|
||||
|
||||
// testParseReference is a test shared for Transport.ParseReference and ParseReference.
|
||||
func testParseReference(t *testing.T, fn func(string) (types.ImageReference, error)) {
|
||||
for _, c := range []struct{ input, expected string }{
|
||||
{"busybox", ""}, // Missing // prefix
|
||||
{"//busybox:notlatest", "busybox:notlatest"}, // Explicit tag
|
||||
{"//busybox" + sha256digest, "busybox" + sha256digest}, // Explicit digest
|
||||
{"//busybox", "busybox:latest"}, // Default tag
|
||||
// A github.com/distribution/reference value can have a tag and a digest at the same time!
|
||||
// github.com/docker/reference handles that by dropping the tag. That is not obviously the
|
||||
// right thing to do, but it is at least reasonable, so test that we keep behaving reasonably.
|
||||
// This test case should not be construed to make this an API promise.
|
||||
// FIXME? Instead work extra hard to reject such input?
|
||||
{"//busybox:latest" + sha256digest, "busybox" + sha256digest}, // Both tag and digest
|
||||
{"//docker.io/library/busybox:latest", "busybox:latest"}, // All implied values explicitly specified
|
||||
{"//UPPERCASEISINVALID", ""}, // Invalid input
|
||||
} {
|
||||
ref, err := fn(c.input)
|
||||
if c.expected == "" {
|
||||
assert.Error(t, err, c.input)
|
||||
} else {
|
||||
require.NoError(t, err, c.input)
|
||||
dockerRef, ok := ref.(dockerReference)
|
||||
require.True(t, ok, c.input)
|
||||
assert.Equal(t, c.expected, dockerRef.ref.String(), c.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// refWithTagAndDigest is a reference.NamedTagged and reference.Canonical at the same time.
|
||||
type refWithTagAndDigest struct{ reference.Canonical }
|
||||
|
||||
func (ref refWithTagAndDigest) Tag() string {
|
||||
return "notLatest"
|
||||
}
|
||||
|
||||
// A common list of reference formats to test for the various ImageReference methods.
|
||||
var validReferenceTestCases = []struct{ input, dockerRef, stringWithinTransport string }{
|
||||
{"busybox:notlatest", "busybox:notlatest", "//busybox:notlatest"}, // Explicit tag
|
||||
{"busybox" + sha256digest, "busybox" + sha256digest, "//busybox" + sha256digest}, // Explicit digest
|
||||
{"docker.io/library/busybox:latest", "busybox:latest", "//busybox:latest"}, // All implied values explicitly specified
|
||||
{"example.com/ns/foo:bar", "example.com/ns/foo:bar", "//example.com/ns/foo:bar"}, // All values explicitly specified
|
||||
}
|
||||
|
||||
func TestNewReference(t *testing.T) {
|
||||
for _, c := range validReferenceTestCases {
|
||||
parsed, err := reference.ParseNamed(c.input)
|
||||
require.NoError(t, err)
|
||||
ref, err := NewReference(parsed)
|
||||
require.NoError(t, err, c.input)
|
||||
dockerRef, ok := ref.(dockerReference)
|
||||
require.True(t, ok, c.input)
|
||||
assert.Equal(t, c.dockerRef, dockerRef.ref.String(), c.input)
|
||||
}
|
||||
|
||||
// Neither a tag nor digest
|
||||
parsed, err := reference.ParseNamed("busybox")
|
||||
require.NoError(t, err)
|
||||
_, err = NewReference(parsed)
|
||||
assert.Error(t, err)
|
||||
|
||||
// A github.com/distribution/reference value can have a tag and a digest at the same time!
|
||||
parsed, err = reference.ParseNamed("busybox" + sha256digest)
|
||||
require.NoError(t, err)
|
||||
refDigested, ok := parsed.(reference.Canonical)
|
||||
require.True(t, ok)
|
||||
tagDigestRef := refWithTagAndDigest{refDigested}
|
||||
_, err = NewReference(tagDigestRef)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestReferenceTransport(t *testing.T) {
|
||||
ref, err := ParseReference("//busybox")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, Transport, ref.Transport())
|
||||
}
|
||||
|
||||
func TestReferenceStringWithinTransport(t *testing.T) {
|
||||
for _, c := range validReferenceTestCases {
|
||||
ref, err := ParseReference("//" + c.input)
|
||||
require.NoError(t, err, c.input)
|
||||
stringRef := ref.StringWithinTransport()
|
||||
assert.Equal(t, c.stringWithinTransport, stringRef, c.input)
|
||||
// Do one more round to verify that the output can be parsed, to an equal value.
|
||||
ref2, err := Transport.ParseReference(stringRef)
|
||||
require.NoError(t, err, c.input)
|
||||
stringRef2 := ref2.StringWithinTransport()
|
||||
assert.Equal(t, stringRef, stringRef2, c.input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReferenceDockerReference(t *testing.T) {
|
||||
for _, c := range validReferenceTestCases {
|
||||
ref, err := ParseReference("//" + c.input)
|
||||
require.NoError(t, err, c.input)
|
||||
dockerRef := ref.DockerReference()
|
||||
require.NotNil(t, dockerRef, c.input)
|
||||
assert.Equal(t, c.dockerRef, dockerRef.String(), c.input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReferencePolicyConfigurationIdentity(t *testing.T) {
|
||||
// Just a smoke test, the substance is tested in policyconfiguration.TestDockerReference.
|
||||
ref, err := ParseReference("//busybox")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "docker.io/library/busybox:latest", ref.PolicyConfigurationIdentity())
|
||||
}
|
||||
|
||||
func TestReferencePolicyConfigurationNamespaces(t *testing.T) {
|
||||
// Just a smoke test, the substance is tested in policyconfiguration.TestDockerReference.
|
||||
ref, err := ParseReference("//busybox")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{
|
||||
"docker.io/library/busybox",
|
||||
"docker.io/library",
|
||||
"docker.io",
|
||||
}, ref.PolicyConfigurationNamespaces())
|
||||
}
|
||||
|
||||
func TestReferenceNewImage(t *testing.T) {
|
||||
ref, err := ParseReference("//busybox")
|
||||
require.NoError(t, err)
|
||||
img, err := ref.NewImage(&types.SystemContext{RegistriesDirPath: "/this/doesnt/exist"})
|
||||
assert.NoError(t, err)
|
||||
defer img.Close()
|
||||
}
|
||||
|
||||
func TestReferenceNewImageSource(t *testing.T) {
|
||||
ref, err := ParseReference("//busybox")
|
||||
require.NoError(t, err)
|
||||
src, err := ref.NewImageSource(&types.SystemContext{RegistriesDirPath: "/this/doesnt/exist"}, nil)
|
||||
assert.NoError(t, err)
|
||||
defer src.Close()
|
||||
}
|
||||
|
||||
func TestReferenceNewImageDestination(t *testing.T) {
|
||||
ref, err := ParseReference("//busybox")
|
||||
require.NoError(t, err)
|
||||
dest, err := ref.NewImageDestination(&types.SystemContext{RegistriesDirPath: "/this/doesnt/exist"})
|
||||
assert.NoError(t, err)
|
||||
defer dest.Close()
|
||||
}
|
||||
|
||||
func TestReferenceTagOrDigest(t *testing.T) {
|
||||
for input, expected := range map[string]string{
|
||||
"//busybox:notlatest": "notlatest",
|
||||
"//busybox" + sha256digest: "sha256:" + sha256digestHex,
|
||||
} {
|
||||
ref, err := ParseReference(input)
|
||||
require.NoError(t, err, input)
|
||||
dockerRef, ok := ref.(dockerReference)
|
||||
require.True(t, ok, input)
|
||||
tod, err := dockerRef.tagOrDigest()
|
||||
require.NoError(t, err, input)
|
||||
assert.Equal(t, expected, tod, input)
|
||||
}
|
||||
|
||||
// Invalid input
|
||||
ref, err := reference.ParseNamed("busybox")
|
||||
require.NoError(t, err)
|
||||
dockerRef := dockerReference{ref: ref}
|
||||
_, err = dockerRef.tagOrDigest()
|
||||
assert.Error(t, err)
|
||||
}
|
1
vendor/github.com/containers/image/docker/fixtures/registries.d/emptyConfig.yaml
generated
vendored
Normal file
1
vendor/github.com/containers/image/docker/fixtures/registries.d/emptyConfig.yaml
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
14
vendor/github.com/containers/image/docker/fixtures/registries.d/internal-example.com.yaml
generated
vendored
Normal file
14
vendor/github.com/containers/image/docker/fixtures/registries.d/internal-example.com.yaml
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
docker:
|
||||
example.com:
|
||||
sigstore: https://sigstore.example.com
|
||||
registry.test.example.com:
|
||||
sigstore: http://registry.test.example.com/sigstore
|
||||
registry.test.example.com:8888:
|
||||
sigstore: http://registry.test.example.com:8889/sigstore
|
||||
sigstore-staging: https://registry.test.example.com:8889/sigstore/specialAPIserverWhichDoesntExist
|
||||
localhost:
|
||||
sigstore: file:///home/mitr/mydevelopment1
|
||||
localhost:8080:
|
||||
sigstore: file:///home/mitr/mydevelopment2
|
||||
localhost/invalid/url/test:
|
||||
sigstore: ":emptyscheme"
|
12
vendor/github.com/containers/image/docker/fixtures/registries.d/internet-user.yaml
generated
vendored
Normal file
12
vendor/github.com/containers/image/docker/fixtures/registries.d/internet-user.yaml
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
default-docker:
|
||||
sigstore: file:///mnt/companywide/signatures/for/other/repositories
|
||||
docker:
|
||||
docker.io/contoso:
|
||||
sigstore: https://sigstore.contoso.com/fordocker
|
||||
docker.io/centos:
|
||||
sigstore: https://sigstore.centos.org/
|
||||
docker.io/centos/mybetaprooduct:
|
||||
sigstore: http://localhost:9999/mybetaWIP/sigstore
|
||||
sigstore-staging: file:///srv/mybetaWIP/sigstore
|
||||
docker.io/centos/mybetaproduct:latest:
|
||||
sigstore: https://sigstore.centos.org/
|
1
vendor/github.com/containers/image/docker/fixtures/registries.d/invalid-but.notyaml
generated
vendored
Normal file
1
vendor/github.com/containers/image/docker/fixtures/registries.d/invalid-but.notyaml
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
}
|
277
vendor/github.com/containers/image/docker/lookaside_test.go
generated
vendored
Normal file
277
vendor/github.com/containers/image/docker/lookaside_test.go
generated
vendored
Normal file
|
@ -0,0 +1,277 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func dockerRefFromString(t *testing.T, s string) dockerReference {
|
||||
ref, err := ParseReference(s)
|
||||
require.NoError(t, err, s)
|
||||
dockerRef, ok := ref.(dockerReference)
|
||||
require.True(t, ok, s)
|
||||
return dockerRef
|
||||
}
|
||||
|
||||
func TestConfiguredSignatureStorageBase(t *testing.T) {
|
||||
// Error reading configuration directory (/dev/null is not a directory)
|
||||
_, err := configuredSignatureStorageBase(&types.SystemContext{RegistriesDirPath: "/dev/null"},
|
||||
dockerRefFromString(t, "//busybox"), false)
|
||||
assert.Error(t, err)
|
||||
|
||||
// No match found
|
||||
emptyDir, err := ioutil.TempDir("", "empty-dir")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(emptyDir)
|
||||
base, err := configuredSignatureStorageBase(&types.SystemContext{RegistriesDirPath: emptyDir},
|
||||
dockerRefFromString(t, "//this/is/not/in/the:configuration"), false)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, base)
|
||||
|
||||
// Invalid URL
|
||||
_, err = configuredSignatureStorageBase(&types.SystemContext{RegistriesDirPath: "fixtures/registries.d"},
|
||||
dockerRefFromString(t, "//localhost/invalid/url/test"), false)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Success
|
||||
base, err = configuredSignatureStorageBase(&types.SystemContext{RegistriesDirPath: "fixtures/registries.d"},
|
||||
dockerRefFromString(t, "//example.com/my/project"), false)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, base)
|
||||
assert.Equal(t, "https://sigstore.example.com/example.com/my/project", (*url.URL)(base).String())
|
||||
}
|
||||
|
||||
func TestRegistriesDirPath(t *testing.T) {
|
||||
const nondefaultPath = "/this/is/not/the/default/registries.d"
|
||||
const variableReference = "$HOME"
|
||||
const rootPrefix = "/root/prefix"
|
||||
|
||||
for _, c := range []struct {
|
||||
ctx *types.SystemContext
|
||||
expected string
|
||||
}{
|
||||
// The common case
|
||||
{nil, systemRegistriesDirPath},
|
||||
// There is a context, but it does not override the path.
|
||||
{&types.SystemContext{}, systemRegistriesDirPath},
|
||||
// Path overridden
|
||||
{&types.SystemContext{RegistriesDirPath: nondefaultPath}, nondefaultPath},
|
||||
// Root overridden
|
||||
{
|
||||
&types.SystemContext{RootForImplicitAbsolutePaths: rootPrefix},
|
||||
filepath.Join(rootPrefix, systemRegistriesDirPath),
|
||||
},
|
||||
// Root and path overrides present simultaneously,
|
||||
{
|
||||
&types.SystemContext{
|
||||
RootForImplicitAbsolutePaths: rootPrefix,
|
||||
RegistriesDirPath: nondefaultPath,
|
||||
},
|
||||
nondefaultPath,
|
||||
},
|
||||
// No environment expansion happens in the overridden paths
|
||||
{&types.SystemContext{RegistriesDirPath: variableReference}, variableReference},
|
||||
} {
|
||||
path := registriesDirPath(c.ctx)
|
||||
assert.Equal(t, c.expected, path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadAndMergeConfig(t *testing.T) {
|
||||
tmpDir, err := ioutil.TempDir("", "merge-config")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// No registries.d exists
|
||||
config, err := loadAndMergeConfig(filepath.Join(tmpDir, "thisdoesnotexist"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ®istryConfiguration{Docker: map[string]registryNamespace{}}, config)
|
||||
|
||||
// Empty registries.d directory
|
||||
emptyDir := filepath.Join(tmpDir, "empty")
|
||||
err = os.Mkdir(emptyDir, 0755)
|
||||
require.NoError(t, err)
|
||||
config, err = loadAndMergeConfig(emptyDir)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ®istryConfiguration{Docker: map[string]registryNamespace{}}, config)
|
||||
|
||||
// Unreadable registries.d directory
|
||||
unreadableDir := filepath.Join(tmpDir, "unreadable")
|
||||
err = os.Mkdir(unreadableDir, 0000)
|
||||
require.NoError(t, err)
|
||||
config, err = loadAndMergeConfig(unreadableDir)
|
||||
assert.Error(t, err)
|
||||
|
||||
// An unreadable file in a registries.d directory
|
||||
unreadableFileDir := filepath.Join(tmpDir, "unreadableFile")
|
||||
err = os.Mkdir(unreadableFileDir, 0755)
|
||||
require.NoError(t, err)
|
||||
err = ioutil.WriteFile(filepath.Join(unreadableFileDir, "0.yaml"), []byte("{}"), 0644)
|
||||
require.NoError(t, err)
|
||||
err = ioutil.WriteFile(filepath.Join(unreadableFileDir, "1.yaml"), nil, 0000)
|
||||
require.NoError(t, err)
|
||||
config, err = loadAndMergeConfig(unreadableFileDir)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Invalid YAML
|
||||
invalidYAMLDir := filepath.Join(tmpDir, "invalidYAML")
|
||||
err = os.Mkdir(invalidYAMLDir, 0755)
|
||||
require.NoError(t, err)
|
||||
err = ioutil.WriteFile(filepath.Join(invalidYAMLDir, "0.yaml"), []byte("}"), 0644)
|
||||
require.NoError(t, err)
|
||||
config, err = loadAndMergeConfig(invalidYAMLDir)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Duplicate DefaultDocker
|
||||
duplicateDefault := filepath.Join(tmpDir, "duplicateDefault")
|
||||
err = os.Mkdir(duplicateDefault, 0755)
|
||||
require.NoError(t, err)
|
||||
err = ioutil.WriteFile(filepath.Join(duplicateDefault, "0.yaml"),
|
||||
[]byte("default-docker:\n sigstore: file:////tmp/something"), 0644)
|
||||
require.NoError(t, err)
|
||||
err = ioutil.WriteFile(filepath.Join(duplicateDefault, "1.yaml"),
|
||||
[]byte("default-docker:\n sigstore: file:////tmp/different"), 0644)
|
||||
require.NoError(t, err)
|
||||
config, err = loadAndMergeConfig(duplicateDefault)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "0.yaml")
|
||||
assert.Contains(t, err.Error(), "1.yaml")
|
||||
|
||||
// Duplicate DefaultDocker
|
||||
duplicateNS := filepath.Join(tmpDir, "duplicateNS")
|
||||
err = os.Mkdir(duplicateNS, 0755)
|
||||
require.NoError(t, err)
|
||||
err = ioutil.WriteFile(filepath.Join(duplicateNS, "0.yaml"),
|
||||
[]byte("docker:\n example.com:\n sigstore: file:////tmp/something"), 0644)
|
||||
require.NoError(t, err)
|
||||
err = ioutil.WriteFile(filepath.Join(duplicateNS, "1.yaml"),
|
||||
[]byte("docker:\n example.com:\n sigstore: file:////tmp/different"), 0644)
|
||||
require.NoError(t, err)
|
||||
config, err = loadAndMergeConfig(duplicateNS)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "0.yaml")
|
||||
assert.Contains(t, err.Error(), "1.yaml")
|
||||
|
||||
// A fully worked example, including an empty-dictionary file and a non-.yaml file
|
||||
config, err = loadAndMergeConfig("fixtures/registries.d")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ®istryConfiguration{
|
||||
DefaultDocker: ®istryNamespace{SigStore: "file:///mnt/companywide/signatures/for/other/repositories"},
|
||||
Docker: map[string]registryNamespace{
|
||||
"example.com": {SigStore: "https://sigstore.example.com"},
|
||||
"registry.test.example.com": {SigStore: "http://registry.test.example.com/sigstore"},
|
||||
"registry.test.example.com:8888": {SigStore: "http://registry.test.example.com:8889/sigstore", SigStoreStaging: "https://registry.test.example.com:8889/sigstore/specialAPIserverWhichDoesntExist"},
|
||||
"localhost": {SigStore: "file:///home/mitr/mydevelopment1"},
|
||||
"localhost:8080": {SigStore: "file:///home/mitr/mydevelopment2"},
|
||||
"localhost/invalid/url/test": {SigStore: ":emptyscheme"},
|
||||
"docker.io/contoso": {SigStore: "https://sigstore.contoso.com/fordocker"},
|
||||
"docker.io/centos": {SigStore: "https://sigstore.centos.org/"},
|
||||
"docker.io/centos/mybetaprooduct": {
|
||||
SigStore: "http://localhost:9999/mybetaWIP/sigstore",
|
||||
SigStoreStaging: "file:///srv/mybetaWIP/sigstore",
|
||||
},
|
||||
"docker.io/centos/mybetaproduct:latest": {SigStore: "https://sigstore.centos.org/"},
|
||||
},
|
||||
}, config)
|
||||
}
|
||||
|
||||
func TestRegistryConfigurationSignaureTopLevel(t *testing.T) {
|
||||
config := registryConfiguration{
|
||||
DefaultDocker: ®istryNamespace{SigStore: "=default", SigStoreStaging: "=default+w"},
|
||||
Docker: map[string]registryNamespace{},
|
||||
}
|
||||
for _, ns := range []string{
|
||||
"localhost",
|
||||
"localhost:5000",
|
||||
"example.com",
|
||||
"example.com/ns1",
|
||||
"example.com/ns1/ns2",
|
||||
"example.com/ns1/ns2/repo",
|
||||
"example.com/ns1/ns2/repo:notlatest",
|
||||
} {
|
||||
config.Docker[ns] = registryNamespace{SigStore: ns, SigStoreStaging: ns + "+w"}
|
||||
}
|
||||
|
||||
for _, c := range []struct{ input, expected string }{
|
||||
{"example.com/ns1/ns2/repo:notlatest", "example.com/ns1/ns2/repo:notlatest"},
|
||||
{"example.com/ns1/ns2/repo:unmatched", "example.com/ns1/ns2/repo"},
|
||||
{"example.com/ns1/ns2/notrepo:notlatest", "example.com/ns1/ns2"},
|
||||
{"example.com/ns1/notns2/repo:notlatest", "example.com/ns1"},
|
||||
{"example.com/notns1/ns2/repo:notlatest", "example.com"},
|
||||
{"unknown.example.com/busybox", "=default"},
|
||||
{"localhost:5000/busybox", "localhost:5000"},
|
||||
{"localhost/busybox", "localhost"},
|
||||
{"localhost:9999/busybox", "=default"},
|
||||
} {
|
||||
dr := dockerRefFromString(t, "//"+c.input)
|
||||
|
||||
res := config.signatureTopLevel(dr, false)
|
||||
assert.Equal(t, c.expected, res, c.input)
|
||||
res = config.signatureTopLevel(dr, true) // test that forWriting is correctly propagated
|
||||
assert.Equal(t, c.expected+"+w", res, c.input)
|
||||
}
|
||||
|
||||
config = registryConfiguration{
|
||||
Docker: map[string]registryNamespace{
|
||||
"unmatched": {SigStore: "a", SigStoreStaging: "b"},
|
||||
},
|
||||
}
|
||||
dr := dockerRefFromString(t, "//thisisnotmatched")
|
||||
res := config.signatureTopLevel(dr, false)
|
||||
assert.Equal(t, "", res)
|
||||
res = config.signatureTopLevel(dr, true)
|
||||
assert.Equal(t, "", res)
|
||||
}
|
||||
|
||||
func TestRegistryNamespaceSignatureTopLevel(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
ns registryNamespace
|
||||
forWriting bool
|
||||
expected string
|
||||
}{
|
||||
{registryNamespace{SigStoreStaging: "a", SigStore: "b"}, true, "a"},
|
||||
{registryNamespace{SigStoreStaging: "a", SigStore: "b"}, false, "b"},
|
||||
{registryNamespace{SigStore: "b"}, true, "b"},
|
||||
{registryNamespace{SigStore: "b"}, false, "b"},
|
||||
{registryNamespace{SigStoreStaging: "a"}, true, "a"},
|
||||
{registryNamespace{SigStoreStaging: "a"}, false, ""},
|
||||
{registryNamespace{}, true, ""},
|
||||
{registryNamespace{}, false, ""},
|
||||
} {
|
||||
res := c.ns.signatureTopLevel(c.forWriting)
|
||||
assert.Equal(t, c.expected, res, fmt.Sprintf("%#v %v", c.ns, c.forWriting))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignatureStorageBaseSignatureStorageURL(t *testing.T) {
|
||||
const md = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
|
||||
assert.True(t, signatureStorageURL(nil, md, 0) == nil)
|
||||
for _, c := range []struct {
|
||||
base string
|
||||
index int
|
||||
expected string
|
||||
}{
|
||||
{"file:///tmp", 0, "file:///tmp@" + md + "/signature-1"},
|
||||
{"file:///tmp", 1, "file:///tmp@" + md + "/signature-2"},
|
||||
{"https://localhost:5555/root", 0, "https://localhost:5555/root@" + md + "/signature-1"},
|
||||
{"https://localhost:5555/root", 1, "https://localhost:5555/root@" + md + "/signature-2"},
|
||||
{"http://localhost:5555/root", 0, "http://localhost:5555/root@" + md + "/signature-1"},
|
||||
{"http://localhost:5555/root", 1, "http://localhost:5555/root@" + md + "/signature-2"},
|
||||
} {
|
||||
url, err := url.Parse(c.base)
|
||||
require.NoError(t, err)
|
||||
expectedURL, err := url.Parse(c.expected)
|
||||
require.NoError(t, err)
|
||||
res := signatureStorageURL(url, md, c.index)
|
||||
assert.Equal(t, expectedURL, res, c.expected)
|
||||
}
|
||||
}
|
91
vendor/github.com/containers/image/docker/policyconfiguration/naming_test.go
generated
vendored
Normal file
91
vendor/github.com/containers/image/docker/policyconfiguration/naming_test.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
|||
package policyconfiguration
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestDockerReference tests DockerReferenceIdentity and DockerReferenceNamespaces simulatenously
|
||||
// to ensure they are consistent.
|
||||
func TestDockerReference(t *testing.T) {
|
||||
sha256Digest := "@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
// Test both that DockerReferenceIdentity returns the expected value (fullName+suffix),
|
||||
// and that DockerReferenceNamespaces starts with the expected value (fullName), i.e. that the two functions are
|
||||
// consistent.
|
||||
for inputName, expectedNS := range map[string][]string{
|
||||
"example.com/ns/repo": {"example.com/ns/repo", "example.com/ns", "example.com"},
|
||||
"example.com/repo": {"example.com/repo", "example.com"},
|
||||
"localhost/ns/repo": {"localhost/ns/repo", "localhost/ns", "localhost"},
|
||||
// Note that "localhost" is special here: notlocalhost/repo is parsed as docker.io/notlocalhost.repo:
|
||||
"localhost/repo": {"localhost/repo", "localhost"},
|
||||
"notlocalhost/repo": {"docker.io/notlocalhost/repo", "docker.io/notlocalhost", "docker.io"},
|
||||
"docker.io/ns/repo": {"docker.io/ns/repo", "docker.io/ns", "docker.io"},
|
||||
"docker.io/library/repo": {"docker.io/library/repo", "docker.io/library", "docker.io"},
|
||||
"docker.io/repo": {"docker.io/library/repo", "docker.io/library", "docker.io"},
|
||||
"ns/repo": {"docker.io/ns/repo", "docker.io/ns", "docker.io"},
|
||||
"library/repo": {"docker.io/library/repo", "docker.io/library", "docker.io"},
|
||||
"repo": {"docker.io/library/repo", "docker.io/library", "docker.io"},
|
||||
} {
|
||||
for inputSuffix, mappedSuffix := range map[string]string{
|
||||
":tag": ":tag",
|
||||
sha256Digest: sha256Digest,
|
||||
// A github.com/distribution/reference value can have a tag and a digest at the same time!
|
||||
// github.com/docker/reference handles that by dropping the tag. That is not obviously the
|
||||
// right thing to do, but it is at least reasonable, so test that we keep behaving reasonably.
|
||||
// This test case should not be construed to make this an API promise.
|
||||
":tag" + sha256Digest: sha256Digest,
|
||||
} {
|
||||
fullInput := inputName + inputSuffix
|
||||
ref, err := reference.ParseNamed(fullInput)
|
||||
require.NoError(t, err, fullInput)
|
||||
|
||||
identity, err := DockerReferenceIdentity(ref)
|
||||
require.NoError(t, err, fullInput)
|
||||
assert.Equal(t, expectedNS[0]+mappedSuffix, identity, fullInput)
|
||||
|
||||
ns := DockerReferenceNamespaces(ref)
|
||||
require.NotNil(t, ns, fullInput)
|
||||
require.Len(t, ns, len(expectedNS), fullInput)
|
||||
moreSpecific := identity
|
||||
for i := range expectedNS {
|
||||
assert.Equal(t, ns[i], expectedNS[i], fmt.Sprintf("%s item %d", fullInput, i))
|
||||
assert.True(t, strings.HasPrefix(moreSpecific, ns[i]))
|
||||
moreSpecific = ns[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// refWithTagAndDigest is a reference.NamedTagged and reference.Canonical at the same time.
|
||||
type refWithTagAndDigest struct{ reference.Canonical }
|
||||
|
||||
func (ref refWithTagAndDigest) Tag() string {
|
||||
return "notLatest"
|
||||
}
|
||||
|
||||
func TestDockerReferenceIdentity(t *testing.T) {
|
||||
// TestDockerReference above has tested the core of the functionality, this tests only the failure cases.
|
||||
|
||||
// Neither a tag nor digest
|
||||
parsed, err := reference.ParseNamed("busybox")
|
||||
require.NoError(t, err)
|
||||
id, err := DockerReferenceIdentity(parsed)
|
||||
assert.Equal(t, "", id)
|
||||
assert.Error(t, err)
|
||||
|
||||
// A github.com/distribution/reference value can have a tag and a digest at the same time!
|
||||
parsed, err = reference.ParseNamed("busybox@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
|
||||
require.NoError(t, err)
|
||||
refDigested, ok := parsed.(reference.Canonical)
|
||||
require.True(t, ok)
|
||||
tagDigestRef := refWithTagAndDigest{refDigested}
|
||||
id, err = DockerReferenceIdentity(tagDigestRef)
|
||||
assert.Equal(t, "", id)
|
||||
assert.Error(t, err)
|
||||
}
|
2
vendor/github.com/containers/image/docker/reference/reference.go
generated
vendored
2
vendor/github.com/containers/image/docker/reference/reference.go
generated
vendored
|
@ -56,7 +56,7 @@ type Canonical interface {
|
|||
// returned.
|
||||
// If an error was encountered it is returned, along with a nil Reference.
|
||||
func ParseNamed(s string) (Named, error) {
|
||||
named, err := distreference.ParseNamed(s)
|
||||
named, err := distreference.ParseNormalizedNamed(s)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Error parsing reference: %q is not a valid repository/tag", s)
|
||||
}
|
||||
|
|
272
vendor/github.com/containers/image/docker/reference/reference_test.go
generated
vendored
Normal file
272
vendor/github.com/containers/image/docker/reference/reference_test.go
generated
vendored
Normal file
|
@ -0,0 +1,272 @@
|
|||
package reference
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
_ "crypto/sha256"
|
||||
)
|
||||
|
||||
func TestValidateReferenceName(t *testing.T) {
|
||||
validRepoNames := []string{
|
||||
"docker/docker",
|
||||
"library/debian",
|
||||
"debian",
|
||||
"docker.io/docker/docker",
|
||||
"docker.io/library/debian",
|
||||
"docker.io/debian",
|
||||
"index.docker.io/docker/docker",
|
||||
"index.docker.io/library/debian",
|
||||
"index.docker.io/debian",
|
||||
"127.0.0.1:5000/docker/docker",
|
||||
"127.0.0.1:5000/library/debian",
|
||||
"127.0.0.1:5000/debian",
|
||||
"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
|
||||
}
|
||||
invalidRepoNames := []string{
|
||||
"https://github.com/docker/docker",
|
||||
"docker/Docker",
|
||||
"-docker",
|
||||
"-docker/docker",
|
||||
"-docker.io/docker/docker",
|
||||
"docker///docker",
|
||||
"docker.io/docker/Docker",
|
||||
"docker.io/docker///docker",
|
||||
"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
|
||||
"docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
|
||||
}
|
||||
|
||||
for _, name := range invalidRepoNames {
|
||||
_, err := ParseNamed(name)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected invalid repo name for %q", name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range validRepoNames {
|
||||
_, err := ParseNamed(name)
|
||||
if err != nil {
|
||||
t.Fatalf("Error parsing repo name %s, got: %q", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRemoteName(t *testing.T) {
|
||||
validRepositoryNames := []string{
|
||||
// Sanity check.
|
||||
"docker/docker",
|
||||
|
||||
// Allow 64-character non-hexadecimal names (hexadecimal names are forbidden).
|
||||
"thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev",
|
||||
|
||||
// Allow embedded hyphens.
|
||||
"docker-rules/docker",
|
||||
|
||||
// Allow multiple hyphens as well.
|
||||
"docker---rules/docker",
|
||||
|
||||
//Username doc and image name docker being tested.
|
||||
"doc/docker",
|
||||
|
||||
// single character names are now allowed.
|
||||
"d/docker",
|
||||
"jess/t",
|
||||
|
||||
// Consecutive underscores.
|
||||
"dock__er/docker",
|
||||
}
|
||||
for _, repositoryName := range validRepositoryNames {
|
||||
_, err := ParseNamed(repositoryName)
|
||||
if err != nil {
|
||||
t.Errorf("Repository name should be valid: %v. Error: %v", repositoryName, err)
|
||||
}
|
||||
}
|
||||
|
||||
invalidRepositoryNames := []string{
|
||||
// Disallow capital letters.
|
||||
"docker/Docker",
|
||||
|
||||
// Only allow one slash.
|
||||
"docker///docker",
|
||||
|
||||
// Disallow 64-character hexadecimal.
|
||||
"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
|
||||
|
||||
// Disallow leading and trailing hyphens in namespace.
|
||||
"-docker/docker",
|
||||
"docker-/docker",
|
||||
"-docker-/docker",
|
||||
|
||||
// Don't allow underscores everywhere (as opposed to hyphens).
|
||||
"____/____",
|
||||
|
||||
"_docker/_docker",
|
||||
|
||||
// Disallow consecutive periods.
|
||||
"dock..er/docker",
|
||||
"dock_.er/docker",
|
||||
"dock-.er/docker",
|
||||
|
||||
// No repository.
|
||||
"docker/",
|
||||
|
||||
//namespace too long
|
||||
"this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255_this_is_not_a_valid_namespace_because_its_lenth_is_greater_than_255/docker",
|
||||
}
|
||||
for _, repositoryName := range invalidRepositoryNames {
|
||||
if _, err := ParseNamed(repositoryName); err == nil {
|
||||
t.Errorf("Repository name should be invalid: %v", repositoryName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRepositoryInfo(t *testing.T) {
|
||||
type tcase struct {
|
||||
RemoteName, NormalizedName, FullName, AmbiguousName, Hostname string
|
||||
}
|
||||
|
||||
tcases := []tcase{
|
||||
{
|
||||
RemoteName: "fooo/bar",
|
||||
NormalizedName: "fooo/bar",
|
||||
FullName: "docker.io/fooo/bar",
|
||||
AmbiguousName: "index.docker.io/fooo/bar",
|
||||
Hostname: "docker.io",
|
||||
},
|
||||
{
|
||||
RemoteName: "library/ubuntu",
|
||||
NormalizedName: "ubuntu",
|
||||
FullName: "docker.io/library/ubuntu",
|
||||
AmbiguousName: "library/ubuntu",
|
||||
Hostname: "docker.io",
|
||||
},
|
||||
{
|
||||
RemoteName: "nonlibrary/ubuntu",
|
||||
NormalizedName: "nonlibrary/ubuntu",
|
||||
FullName: "docker.io/nonlibrary/ubuntu",
|
||||
AmbiguousName: "",
|
||||
Hostname: "docker.io",
|
||||
},
|
||||
{
|
||||
RemoteName: "other/library",
|
||||
NormalizedName: "other/library",
|
||||
FullName: "docker.io/other/library",
|
||||
AmbiguousName: "",
|
||||
Hostname: "docker.io",
|
||||
},
|
||||
{
|
||||
RemoteName: "private/moonbase",
|
||||
NormalizedName: "127.0.0.1:8000/private/moonbase",
|
||||
FullName: "127.0.0.1:8000/private/moonbase",
|
||||
AmbiguousName: "",
|
||||
Hostname: "127.0.0.1:8000",
|
||||
},
|
||||
{
|
||||
RemoteName: "privatebase",
|
||||
NormalizedName: "127.0.0.1:8000/privatebase",
|
||||
FullName: "127.0.0.1:8000/privatebase",
|
||||
AmbiguousName: "",
|
||||
Hostname: "127.0.0.1:8000",
|
||||
},
|
||||
{
|
||||
RemoteName: "private/moonbase",
|
||||
NormalizedName: "example.com/private/moonbase",
|
||||
FullName: "example.com/private/moonbase",
|
||||
AmbiguousName: "",
|
||||
Hostname: "example.com",
|
||||
},
|
||||
{
|
||||
RemoteName: "privatebase",
|
||||
NormalizedName: "example.com/privatebase",
|
||||
FullName: "example.com/privatebase",
|
||||
AmbiguousName: "",
|
||||
Hostname: "example.com",
|
||||
},
|
||||
{
|
||||
RemoteName: "private/moonbase",
|
||||
NormalizedName: "example.com:8000/private/moonbase",
|
||||
FullName: "example.com:8000/private/moonbase",
|
||||
AmbiguousName: "",
|
||||
Hostname: "example.com:8000",
|
||||
},
|
||||
{
|
||||
RemoteName: "privatebasee",
|
||||
NormalizedName: "example.com:8000/privatebasee",
|
||||
FullName: "example.com:8000/privatebasee",
|
||||
AmbiguousName: "",
|
||||
Hostname: "example.com:8000",
|
||||
},
|
||||
{
|
||||
RemoteName: "library/ubuntu-12.04-base",
|
||||
NormalizedName: "ubuntu-12.04-base",
|
||||
FullName: "docker.io/library/ubuntu-12.04-base",
|
||||
AmbiguousName: "index.docker.io/library/ubuntu-12.04-base",
|
||||
Hostname: "docker.io",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tcase := range tcases {
|
||||
refStrings := []string{tcase.NormalizedName, tcase.FullName}
|
||||
if tcase.AmbiguousName != "" {
|
||||
refStrings = append(refStrings, tcase.AmbiguousName)
|
||||
}
|
||||
|
||||
var refs []Named
|
||||
for _, r := range refStrings {
|
||||
named, err := ParseNamed(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
refs = append(refs, named)
|
||||
named, err = WithName(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
refs = append(refs, named)
|
||||
}
|
||||
|
||||
for _, r := range refs {
|
||||
if expected, actual := tcase.NormalizedName, r.Name(); expected != actual {
|
||||
t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
|
||||
}
|
||||
if expected, actual := tcase.FullName, r.FullName(); expected != actual {
|
||||
t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
|
||||
}
|
||||
if expected, actual := tcase.Hostname, r.Hostname(); expected != actual {
|
||||
t.Fatalf("Invalid hostname for %q. Expected %q, got %q", r, expected, actual)
|
||||
}
|
||||
if expected, actual := tcase.RemoteName, r.RemoteName(); expected != actual {
|
||||
t.Fatalf("Invalid remoteName for %q. Expected %q, got %q", r, expected, actual)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseReferenceWithTagAndDigest(t *testing.T) {
|
||||
ref, err := ParseNamed("busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, isTagged := ref.(NamedTagged); isTagged {
|
||||
t.Fatalf("Reference from %q should not support tag", ref)
|
||||
}
|
||||
if _, isCanonical := ref.(Canonical); !isCanonical {
|
||||
t.Fatalf("Reference from %q should not support digest", ref)
|
||||
}
|
||||
if expected, actual := "busybox@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa", ref.String(); actual != expected {
|
||||
t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidReferenceComponents(t *testing.T) {
|
||||
if _, err := WithName("-foo"); err == nil {
|
||||
t.Fatal("Expected WithName to detect invalid name")
|
||||
}
|
||||
ref, err := WithName("busybox")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := WithTag(ref, "-foo"); err == nil {
|
||||
t.Fatal("Expected WithName to detect invalid tag")
|
||||
}
|
||||
}
|
45
vendor/github.com/containers/image/docker/wwwauthenticate_test.go
generated
vendored
Normal file
45
vendor/github.com/containers/image/docker/wwwauthenticate_test.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// This is just a smoke test for the common expected header formats,
|
||||
// by no means comprehensive.
|
||||
func TestParseValueAndParams(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
input string
|
||||
scope string
|
||||
params map[string]string
|
||||
}{
|
||||
{
|
||||
`Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:library/busybox:pull"`,
|
||||
"bearer",
|
||||
map[string]string{
|
||||
"realm": "https://auth.docker.io/token",
|
||||
"service": "registry.docker.io",
|
||||
"scope": "repository:library/busybox:pull",
|
||||
},
|
||||
},
|
||||
{
|
||||
`Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:library/busybox:pull,push"`,
|
||||
"bearer",
|
||||
map[string]string{
|
||||
"realm": "https://auth.docker.io/token",
|
||||
"service": "registry.docker.io",
|
||||
"scope": "repository:library/busybox:pull,push",
|
||||
},
|
||||
},
|
||||
{
|
||||
`Bearer realm="http://127.0.0.1:5000/openshift/token"`,
|
||||
"bearer",
|
||||
map[string]string{"realm": "http://127.0.0.1:5000/openshift/token"},
|
||||
},
|
||||
} {
|
||||
scope, params := parseValueAndParams(c.input)
|
||||
assert.Equal(t, c.scope, scope, c.input)
|
||||
assert.Equal(t, c.params, params, c.input)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue