vendor: remove dep and use vndr

Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
Antonio Murdaca 2017-06-06 09:19:04 +02:00
parent 16f44674a4
commit 148e72d81e
No known key found for this signature in database
GPG key ID: B2BEAD150DE936B9
16131 changed files with 73815 additions and 4235138 deletions

View file

@ -1,198 +0,0 @@
package archive
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"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
tarFixture = "fixtures/almostempty.tar"
)
func TestTransportName(t *testing.T) {
assert.Equal(t, "docker-archive", 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.
"docker.io/library/busybox:notlatest",
"docker.io/library/busybox",
"docker.io/library",
"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, expectedPath, expectedRef string }{
{"", "", ""}, // Empty input is explicitly rejected
{"/path", "/path", ""},
{"/path:busybox:notlatest", "/path", "docker.io/library/busybox:notlatest"}, // Explicit tag
{"/path:busybox" + sha256digest, "", ""}, // Digest references are forbidden
{"/path:busybox", "/path", "docker.io/library/busybox:latest"}, // Default tag
// A github.com/distribution/reference value can have a tag and a digest at the same time!
{"/path:busybox:latest" + sha256digest, "", ""}, // Both tag and digest is rejected
{"/path:docker.io/library/busybox:latest", "/path", "docker.io/library/busybox:latest"}, // All implied values explicitly specified
{"/path:UPPERCASEISINVALID", "", ""}, // Invalid input
} {
ref, err := fn(c.input)
if c.expectedPath == "" {
assert.Error(t, err, c.input)
} else {
require.NoError(t, err, c.input)
archiveRef, ok := ref.(archiveReference)
require.True(t, ok, c.input)
assert.Equal(t, c.expectedPath, archiveRef.path, c.input)
if c.expectedRef == "" {
assert.Nil(t, archiveRef.destinationRef, c.input)
} else {
require.NotNil(t, archiveRef.destinationRef, c.input)
assert.Equal(t, c.expectedRef, archiveRef.destinationRef.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 }{
{"/pathonly", "", "/pathonly"},
{"/path:busybox:notlatest", "docker.io/library/busybox:notlatest", "/path:docker.io/library/busybox:notlatest"}, // Explicit tag
{"/path:docker.io/library/busybox:latest", "docker.io/library/busybox:latest", "/path:docker.io/library/busybox:latest"}, // All implied values explicitly specified
{"/path:example.com/ns/foo:bar", "example.com/ns/foo:bar", "/path:example.com/ns/foo:bar"}, // All values explicitly specified
}
func TestReferenceTransport(t *testing.T) {
ref, err := ParseReference("/tmp/archive.tar")
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()
if c.dockerRef != "" {
require.NotNil(t, dockerRef, c.input)
assert.Equal(t, c.dockerRef, dockerRef.String(), c.input)
} else {
require.Nil(t, dockerRef, c.input)
}
}
}
func TestReferencePolicyConfigurationIdentity(t *testing.T) {
for _, c := range validReferenceTestCases {
ref, err := ParseReference(c.input)
require.NoError(t, err, c.input)
assert.Equal(t, "", ref.PolicyConfigurationIdentity(), c.input)
}
}
func TestReferencePolicyConfigurationNamespaces(t *testing.T) {
for _, c := range validReferenceTestCases {
ref, err := ParseReference(c.input)
require.NoError(t, err, c.input)
assert.Empty(t, "", ref.PolicyConfigurationNamespaces(), c.input)
}
}
func TestReferenceNewImage(t *testing.T) {
for _, suffix := range []string{"", ":thisisignoredbutaccepted"} {
ref, err := ParseReference(tarFixture + suffix)
require.NoError(t, err, suffix)
img, err := ref.NewImage(nil)
assert.NoError(t, err, suffix)
defer img.Close()
}
}
func TestReferenceNewImageSource(t *testing.T) {
for _, suffix := range []string{"", ":thisisignoredbutaccepted"} {
ref, err := ParseReference(tarFixture + suffix)
require.NoError(t, err, suffix)
src, err := ref.NewImageSource(nil, nil)
assert.NoError(t, err, suffix)
defer src.Close()
}
}
func TestReferenceNewImageDestination(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "docker-archive-test")
require.NoError(t, err)
defer os.RemoveAll(tmpDir)
ref, err := ParseReference(filepath.Join(tmpDir, "no-reference"))
require.NoError(t, err)
dest, err := ref.NewImageDestination(nil)
assert.Error(t, err)
ref, err = ParseReference(filepath.Join(tmpDir, "with-reference") + "busybox:latest")
require.NoError(t, err)
dest, err = ref.NewImageDestination(nil)
assert.NoError(t, err)
defer dest.Close()
}
func TestReferenceDeleteImage(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "docker-archive-test")
require.NoError(t, err)
defer os.RemoveAll(tmpDir)
for i, suffix := range []string{"", ":thisisignoredbutaccepted"} {
testFile := filepath.Join(tmpDir, fmt.Sprintf("file%d.tar", i))
err := ioutil.WriteFile(testFile, []byte("nonempty"), 0644)
require.NoError(t, err, suffix)
ref, err := ParseReference(testFile + suffix)
require.NoError(t, err, suffix)
err = ref.DeleteImage(nil)
assert.Error(t, err, suffix)
_, err = os.Lstat(testFile)
assert.NoError(t, err, suffix)
}
}

View file

@ -1,228 +0,0 @@
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", "", "docker.io/library/busybox:latest"}, // Explicit tag
{"busybox@" + sha256digest, "", "docker.io/library/busybox@" + sha256digest}, // Explicit digest
// A github.com/distribution/reference value can have a tag and a digest at the same time!
// Most versions of docker/reference do not handle that (ignoring the tag), so we reject such input.
{"busybox:latest@" + sha256digest, "", ""}, // Both tag and digest
{"docker.io/library/busybox:latest", "", "docker.io/library/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 with reference.ParseAnyReference
dockerRef, err := reference.ParseAnyReference(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)
_, ok := dockerRef.(reference.Digested)
require.True(t, ok, c.input)
assert.Equal(t, c.expectedID, dockerRef.String(), 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)
_, ok := dockerRef.(reference.Named)
require.True(t, ok, c.input)
assert.Equal(t, c.expectedRef, dockerRef.String(), c.input)
}
}
}
}
// 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", "docker.io/library/busybox:notlatest", "busybox:notlatest"}, // Explicit tag
{"busybox" + sha256digest, "docker.io/library/busybox" + sha256digest, "busybox" + sha256digest}, // Explicit digest
{"docker.io/library/busybox:latest", "docker.io/library/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.ParseNormalizedNamed(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.ParseNormalizedNamed("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.ParseNormalizedNamed("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.ParseNormalizedNamed("busybox:notlatest@" + sha256digest)
require.NoError(t, err)
_, ok = parsed.(reference.Canonical)
require.True(t, ok)
_, ok = parsed.(reference.NamedTagged)
require.True(t, ok)
_, err = NewReference("", parsed)
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)
}
}

View file

@ -34,6 +34,8 @@ const (
dockerCfgFileName = "config.json"
dockerCfgObsolete = ".dockercfg"
systemPerHostCertDirPath = "/etc/docker/certs.d"
resolvedPingV2URL = "%s://%s/v2/"
resolvedPingV1URL = "%s://%s/v1/_ping"
tagsPath = "/v2/%s/tags/list"
@ -129,12 +131,29 @@ func newTransport() *http.Transport {
return tr
}
func setupCertificates(dir string, tlsc *tls.Config) error {
if dir == "" {
return nil
// dockerCertDir returns a path to a directory to be consumed by setupCertificates() depending on ctx and hostPort.
func dockerCertDir(ctx *types.SystemContext, hostPort string) string {
if ctx != nil && ctx.DockerCertPath != "" {
return ctx.DockerCertPath
}
var hostCertDir string
if ctx != nil && ctx.DockerPerHostCertDirPath != "" {
hostCertDir = ctx.DockerPerHostCertDirPath
} else if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" {
hostCertDir = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemPerHostCertDirPath)
} else {
hostCertDir = systemPerHostCertDirPath
}
return filepath.Join(hostCertDir, hostPort)
}
func setupCertificates(dir string, tlsc *tls.Config) error {
logrus.Debugf("Looking for TLS certificates and private keys in %s", dir)
fs, err := ioutil.ReadDir(dir)
if err != nil && !os.IsNotExist(err) {
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
@ -146,7 +165,7 @@ func setupCertificates(dir string, tlsc *tls.Config) error {
return errors.Wrap(err, "unable to get system cert pool")
}
tlsc.RootCAs = systemPool
logrus.Debugf("crt: %s", fullPath)
logrus.Debugf(" crt: %s", fullPath)
data, err := ioutil.ReadFile(fullPath)
if err != nil {
return err
@ -156,7 +175,7 @@ func setupCertificates(dir string, tlsc *tls.Config) error {
if strings.HasSuffix(f.Name(), ".cert") {
certName := f.Name()
keyName := certName[:len(certName)-5] + ".key"
logrus.Debugf("cert: %s", fullPath)
logrus.Debugf(" cert: %s", fullPath)
if !hasFile(fs, keyName) {
return errors.Errorf("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt", keyName, certName)
}
@ -169,7 +188,7 @@ func setupCertificates(dir string, tlsc *tls.Config) error {
if strings.HasSuffix(f.Name(), ".key") {
keyName := f.Name()
certName := keyName[:len(keyName)-4] + ".cert"
logrus.Debugf("key: %s", fullPath)
logrus.Debugf(" key: %s", fullPath)
if !hasFile(fs, certName) {
return errors.Errorf("missing client certificate %s for key %s", certName, keyName)
}
@ -199,18 +218,18 @@ func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool,
return nil, err
}
tr := newTransport()
if ctx != nil && (ctx.DockerCertPath != "" || ctx.DockerInsecureSkipTLSVerify) {
tlsc := &tls.Config{}
if err := setupCertificates(ctx.DockerCertPath, tlsc); err != nil {
return nil, err
}
tlsc.InsecureSkipVerify = ctx.DockerInsecureSkipTLSVerify
tr.TLSClientConfig = tlsc
tr.TLSClientConfig = serverDefault()
// It is undefined whether the host[:port] string for dockerHostname should be dockerHostname or dockerRegistry,
// because docker/docker does not read the certs.d subdirectory at all in that case. We use the user-visible
// dockerHostname here, because it is more symmetrical to read the configuration in that case as well, and because
// generally the UI hides the existence of the different dockerRegistry. But note that this behavior is
// undocumented and may change if docker/docker changes.
certDir := dockerCertDir(ctx, reference.Domain(ref.ref))
if err := setupCertificates(certDir, tr.TLSClientConfig); err != nil {
return nil, err
}
if tr.TLSClientConfig == nil {
tr.TLSClientConfig = serverDefault()
if ctx != nil && ctx.DockerInsecureSkipTLSVerify {
tr.TLSClientConfig.InsecureSkipVerify = true
}
client := &http.Client{Transport: tr}

View file

@ -1,432 +0,0 @@
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
}

View file

@ -1,24 +0,0 @@
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)
}
}

View file

@ -1,196 +0,0 @@
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", "docker.io/library/busybox:notlatest"}, // Explicit tag
{"//busybox" + sha256digest, "docker.io/library/busybox" + sha256digest}, // Explicit digest
{"//busybox", "docker.io/library/busybox:latest"}, // Default tag
// A github.com/distribution/reference value can have a tag and a digest at the same time!
// The docker/distribution API does not really support that (we cant ask for an image with a specific
// tag and digest), so fail. This MAY be accepted in the future.
{"//busybox:latest" + sha256digest, ""}, // Both tag and digest
{"//docker.io/library/busybox:latest", "docker.io/library/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)
}
}
}
// A common list of reference formats to test for the various ImageReference methods.
var validReferenceTestCases = []struct{ input, dockerRef, stringWithinTransport string }{
{"busybox:notlatest", "docker.io/library/busybox:notlatest", "//busybox:notlatest"}, // Explicit tag
{"busybox" + sha256digest, "docker.io/library/busybox" + sha256digest, "//busybox" + sha256digest}, // Explicit digest
{"docker.io/library/busybox:latest", "docker.io/library/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.ParseNormalizedNamed(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.ParseNormalizedNamed("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.ParseNormalizedNamed("busybox:notlatest" + sha256digest)
require.NoError(t, err)
_, ok := parsed.(reference.Canonical)
require.True(t, ok)
_, ok = parsed.(reference.NamedTagged)
require.True(t, ok)
_, err = NewReference(parsed)
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.ParseNormalizedNamed("busybox")
require.NoError(t, err)
dockerRef := dockerReference{ref: ref}
_, err = dockerRef.tagOrDigest()
assert.Error(t, err)
}

View file

@ -1,14 +0,0 @@
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"

View file

@ -1,12 +0,0 @@
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/

View file

@ -1,278 +0,0 @@
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/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, &registryConfiguration{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, &registryConfiguration{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, &registryConfiguration{
DefaultDocker: &registryNamespace{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: &registryNamespace{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 mdInput = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
const mdMapped = "sha256=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
assert.True(t, signatureStorageURL(nil, mdInput, 0) == nil)
for _, c := range []struct {
base string
index int
expected string
}{
{"file:///tmp", 0, "file:///tmp@" + mdMapped + "/signature-1"},
{"file:///tmp", 1, "file:///tmp@" + mdMapped + "/signature-2"},
{"https://localhost:5555/root", 0, "https://localhost:5555/root@" + mdMapped + "/signature-1"},
{"https://localhost:5555/root", 1, "https://localhost:5555/root@" + mdMapped + "/signature-2"},
{"http://localhost:5555/root", 0, "http://localhost:5555/root@" + mdMapped + "/signature-1"},
{"http://localhost:5555/root", 1, "http://localhost:5555/root@" + mdMapped + "/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, mdInput, c.index)
assert.Equal(t, expectedURL, res, c.expected)
}
}

View file

@ -1,79 +0,0 @@
package policyconfiguration
import (
"fmt"
"strings"
"testing"
"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,
} {
fullInput := inputName + inputSuffix
ref, err := reference.ParseNormalizedNamed(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]
}
}
}
}
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.ParseNormalizedNamed("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.ParseNormalizedNamed("busybox:notlatest@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
require.NoError(t, err)
_, ok := parsed.(reference.Canonical)
require.True(t, ok)
_, ok = parsed.(reference.NamedTagged)
require.True(t, ok)
id, err = DockerReferenceIdentity(parsed)
assert.Equal(t, "", id)
assert.Error(t, err)
}

View file

@ -1,573 +0,0 @@
package reference
import (
"strconv"
"testing"
"github.com/opencontainers/go-digest"
)
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",
// This test case was moved from invalid to valid since it is valid input
// when specified with a hostname, it removes the ambiguity from about
// whether the value is an identifier or repository name
"docker.io/1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a",
}
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",
}
for _, name := range invalidRepoNames {
_, err := ParseNormalizedNamed(name)
if err == nil {
t.Fatalf("Expected invalid repo name for %q", name)
}
}
for _, name := range validRepoNames {
_, err := ParseNormalizedNamed(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 := ParseNormalizedNamed(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 := ParseNormalizedNamed(repositoryName); err == nil {
t.Errorf("Repository name should be invalid: %v", repositoryName)
}
}
}
func TestParseRepositoryInfo(t *testing.T) {
type tcase struct {
RemoteName, FamiliarName, FullName, AmbiguousName, Domain string
}
tcases := []tcase{
{
RemoteName: "fooo/bar",
FamiliarName: "fooo/bar",
FullName: "docker.io/fooo/bar",
AmbiguousName: "index.docker.io/fooo/bar",
Domain: "docker.io",
},
{
RemoteName: "library/ubuntu",
FamiliarName: "ubuntu",
FullName: "docker.io/library/ubuntu",
AmbiguousName: "library/ubuntu",
Domain: "docker.io",
},
{
RemoteName: "nonlibrary/ubuntu",
FamiliarName: "nonlibrary/ubuntu",
FullName: "docker.io/nonlibrary/ubuntu",
AmbiguousName: "",
Domain: "docker.io",
},
{
RemoteName: "other/library",
FamiliarName: "other/library",
FullName: "docker.io/other/library",
AmbiguousName: "",
Domain: "docker.io",
},
{
RemoteName: "private/moonbase",
FamiliarName: "127.0.0.1:8000/private/moonbase",
FullName: "127.0.0.1:8000/private/moonbase",
AmbiguousName: "",
Domain: "127.0.0.1:8000",
},
{
RemoteName: "privatebase",
FamiliarName: "127.0.0.1:8000/privatebase",
FullName: "127.0.0.1:8000/privatebase",
AmbiguousName: "",
Domain: "127.0.0.1:8000",
},
{
RemoteName: "private/moonbase",
FamiliarName: "example.com/private/moonbase",
FullName: "example.com/private/moonbase",
AmbiguousName: "",
Domain: "example.com",
},
{
RemoteName: "privatebase",
FamiliarName: "example.com/privatebase",
FullName: "example.com/privatebase",
AmbiguousName: "",
Domain: "example.com",
},
{
RemoteName: "private/moonbase",
FamiliarName: "example.com:8000/private/moonbase",
FullName: "example.com:8000/private/moonbase",
AmbiguousName: "",
Domain: "example.com:8000",
},
{
RemoteName: "privatebasee",
FamiliarName: "example.com:8000/privatebasee",
FullName: "example.com:8000/privatebasee",
AmbiguousName: "",
Domain: "example.com:8000",
},
{
RemoteName: "library/ubuntu-12.04-base",
FamiliarName: "ubuntu-12.04-base",
FullName: "docker.io/library/ubuntu-12.04-base",
AmbiguousName: "index.docker.io/library/ubuntu-12.04-base",
Domain: "docker.io",
},
{
RemoteName: "library/foo",
FamiliarName: "foo",
FullName: "docker.io/library/foo",
AmbiguousName: "docker.io/foo",
Domain: "docker.io",
},
{
RemoteName: "library/foo/bar",
FamiliarName: "library/foo/bar",
FullName: "docker.io/library/foo/bar",
AmbiguousName: "",
Domain: "docker.io",
},
{
RemoteName: "store/foo/bar",
FamiliarName: "store/foo/bar",
FullName: "docker.io/store/foo/bar",
AmbiguousName: "",
Domain: "docker.io",
},
}
for _, tcase := range tcases {
refStrings := []string{tcase.FamiliarName, tcase.FullName}
if tcase.AmbiguousName != "" {
refStrings = append(refStrings, tcase.AmbiguousName)
}
var refs []Named
for _, r := range refStrings {
named, err := ParseNormalizedNamed(r)
if err != nil {
t.Fatal(err)
}
refs = append(refs, named)
}
for _, r := range refs {
if expected, actual := tcase.FamiliarName, FamiliarName(r); expected != actual {
t.Fatalf("Invalid normalized reference for %q. Expected %q, got %q", r, expected, actual)
}
if expected, actual := tcase.FullName, r.String(); expected != actual {
t.Fatalf("Invalid canonical reference for %q. Expected %q, got %q", r, expected, actual)
}
if expected, actual := tcase.Domain, Domain(r); expected != actual {
t.Fatalf("Invalid domain for %q. Expected %q, got %q", r, expected, actual)
}
if expected, actual := tcase.RemoteName, Path(r); expected != actual {
t.Fatalf("Invalid remoteName for %q. Expected %q, got %q", r, expected, actual)
}
}
}
}
func TestParseReferenceWithTagAndDigest(t *testing.T) {
shortRef := "busybox:latest@sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"
ref, err := ParseNormalizedNamed(shortRef)
if err != nil {
t.Fatal(err)
}
if expected, actual := "docker.io/library/"+shortRef, ref.String(); actual != expected {
t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
}
if _, isTagged := ref.(NamedTagged); !isTagged {
t.Fatalf("Reference from %q should support tag", ref)
}
if _, isCanonical := ref.(Canonical); !isCanonical {
t.Fatalf("Reference from %q should support digest", ref)
}
if expected, actual := shortRef, FamiliarString(ref); actual != expected {
t.Fatalf("Invalid parsed reference for %q: expected %q, got %q", ref, expected, actual)
}
}
func TestInvalidReferenceComponents(t *testing.T) {
if _, err := ParseNormalizedNamed("-foo"); err == nil {
t.Fatal("Expected WithName to detect invalid name")
}
ref, err := ParseNormalizedNamed("busybox")
if err != nil {
t.Fatal(err)
}
if _, err := WithTag(ref, "-foo"); err == nil {
t.Fatal("Expected WithName to detect invalid tag")
}
if _, err := WithDigest(ref, digest.Digest("foo")); err == nil {
t.Fatal("Expected WithDigest to detect invalid digest")
}
}
func equalReference(r1, r2 Reference) bool {
switch v1 := r1.(type) {
case digestReference:
if v2, ok := r2.(digestReference); ok {
return v1 == v2
}
case repository:
if v2, ok := r2.(repository); ok {
return v1 == v2
}
case taggedReference:
if v2, ok := r2.(taggedReference); ok {
return v1 == v2
}
case canonicalReference:
if v2, ok := r2.(canonicalReference); ok {
return v1 == v2
}
case reference:
if v2, ok := r2.(reference); ok {
return v1 == v2
}
}
return false
}
func TestParseAnyReference(t *testing.T) {
tcases := []struct {
Reference string
Equivalent string
Expected Reference
}{
{
Reference: "redis",
Equivalent: "docker.io/library/redis",
},
{
Reference: "redis:latest",
Equivalent: "docker.io/library/redis:latest",
},
{
Reference: "docker.io/library/redis:latest",
Equivalent: "docker.io/library/redis:latest",
},
{
Reference: "redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
Equivalent: "docker.io/library/redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
},
{
Reference: "docker.io/library/redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
Equivalent: "docker.io/library/redis@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
},
{
Reference: "dmcgowan/myapp",
Equivalent: "docker.io/dmcgowan/myapp",
},
{
Reference: "dmcgowan/myapp:latest",
Equivalent: "docker.io/dmcgowan/myapp:latest",
},
{
Reference: "docker.io/mcgowan/myapp:latest",
Equivalent: "docker.io/mcgowan/myapp:latest",
},
{
Reference: "dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
Equivalent: "docker.io/dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
},
{
Reference: "docker.io/dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
Equivalent: "docker.io/dmcgowan/myapp@sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
},
{
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
Equivalent: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
},
{
Reference: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
Expected: digestReference("sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c"),
Equivalent: "sha256:dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9c",
},
{
Reference: "dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
Equivalent: "docker.io/library/dbcc1c35ac38df41fd2f5e4130b32ffdb93ebae8b3dbe638c23575912276fc9",
},
}
for _, tcase := range tcases {
var ref Reference
var err error
ref, err = ParseAnyReference(tcase.Reference)
if err != nil {
t.Fatalf("Error parsing reference %s: %v", tcase.Reference, err)
}
if ref.String() != tcase.Equivalent {
t.Fatalf("Unexpected string: %s, expected %s", ref.String(), tcase.Equivalent)
}
expected := tcase.Expected
if expected == nil {
expected, err = Parse(tcase.Equivalent)
if err != nil {
t.Fatalf("Error parsing reference %s: %v", tcase.Equivalent, err)
}
}
if !equalReference(ref, expected) {
t.Errorf("Unexpected reference %#v, expected %#v", ref, expected)
}
}
}
func TestNormalizedSplitHostname(t *testing.T) {
testcases := []struct {
input string
domain string
name string
}{
{
input: "test.com/foo",
domain: "test.com",
name: "foo",
},
{
input: "test_com/foo",
domain: "docker.io",
name: "test_com/foo",
},
{
input: "docker/migrator",
domain: "docker.io",
name: "docker/migrator",
},
{
input: "test.com:8080/foo",
domain: "test.com:8080",
name: "foo",
},
{
input: "test-com:8080/foo",
domain: "test-com:8080",
name: "foo",
},
{
input: "foo",
domain: "docker.io",
name: "library/foo",
},
{
input: "xn--n3h.com/foo",
domain: "xn--n3h.com",
name: "foo",
},
{
input: "xn--n3h.com:18080/foo",
domain: "xn--n3h.com:18080",
name: "foo",
},
{
input: "docker.io/foo",
domain: "docker.io",
name: "library/foo",
},
{
input: "docker.io/library/foo",
domain: "docker.io",
name: "library/foo",
},
{
input: "docker.io/library/foo/bar",
domain: "docker.io",
name: "library/foo/bar",
},
}
for _, testcase := range testcases {
failf := func(format string, v ...interface{}) {
t.Logf(strconv.Quote(testcase.input)+": "+format, v...)
t.Fail()
}
named, err := ParseNormalizedNamed(testcase.input)
if err != nil {
failf("error parsing name: %s", err)
}
domain, name := SplitHostname(named)
if domain != testcase.domain {
failf("unexpected domain: got %q, expected %q", domain, testcase.domain)
}
if name != testcase.name {
failf("unexpected name: got %q, expected %q", name, testcase.name)
}
}
}
func TestMatchError(t *testing.T) {
named, err := ParseAnyReference("foo")
if err != nil {
t.Fatal(err)
}
_, err = FamiliarMatch("[-x]", named)
if err == nil {
t.Fatalf("expected an error, got nothing")
}
}
func TestMatch(t *testing.T) {
matchCases := []struct {
reference string
pattern string
expected bool
}{
{
reference: "foo",
pattern: "foo/**/ba[rz]",
expected: false,
},
{
reference: "foo/any/bat",
pattern: "foo/**/ba[rz]",
expected: false,
},
{
reference: "foo/a/bar",
pattern: "foo/**/ba[rz]",
expected: true,
},
{
reference: "foo/b/baz",
pattern: "foo/**/ba[rz]",
expected: true,
},
{
reference: "foo/c/baz:tag",
pattern: "foo/**/ba[rz]",
expected: true,
},
{
reference: "foo/c/baz:tag",
pattern: "foo/*/baz:tag",
expected: true,
},
{
reference: "foo/c/baz:tag",
pattern: "foo/c/baz:tag",
expected: true,
},
{
reference: "example.com/foo/c/baz:tag",
pattern: "*/foo/c/baz",
expected: true,
},
{
reference: "example.com/foo/c/baz:tag",
pattern: "example.com/foo/c/baz",
expected: true,
},
}
for _, c := range matchCases {
named, err := ParseAnyReference(c.reference)
if err != nil {
t.Fatal(err)
}
actual, err := FamiliarMatch(c.pattern, named)
if err != nil {
t.Fatal(err)
}
if actual != c.expected {
t.Fatalf("expected %s match %s to be %v, was %v", c.reference, c.pattern, c.expected, actual)
}
}
}

View file

@ -1,659 +0,0 @@
package reference
import (
_ "crypto/sha256"
_ "crypto/sha512"
"encoding/json"
"strconv"
"strings"
"testing"
"github.com/opencontainers/go-digest"
)
func TestReferenceParse(t *testing.T) {
// referenceTestcases is a unified set of testcases for
// testing the parsing of references
referenceTestcases := []struct {
// input is the repository name or name component testcase
input string
// err is the error expected from Parse, or nil
err error
// repository is the string representation for the reference
repository string
// domain is the domain expected in the reference
domain string
// tag is the tag for the reference
tag string
// digest is the digest for the reference (enforces digest reference)
digest string
}{
{
input: "test_com",
repository: "test_com",
},
{
input: "test.com:tag",
repository: "test.com",
tag: "tag",
},
{
input: "test.com:5000",
repository: "test.com",
tag: "5000",
},
{
input: "test.com/repo:tag",
domain: "test.com",
repository: "test.com/repo",
tag: "tag",
},
{
input: "test:5000/repo",
domain: "test:5000",
repository: "test:5000/repo",
},
{
input: "test:5000/repo:tag",
domain: "test:5000",
repository: "test:5000/repo",
tag: "tag",
},
{
input: "test:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
domain: "test:5000",
repository: "test:5000/repo",
digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
},
{
input: "test:5000/repo:tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
domain: "test:5000",
repository: "test:5000/repo",
tag: "tag",
digest: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
},
{
input: "test:5000/repo",
domain: "test:5000",
repository: "test:5000/repo",
},
{
input: "",
err: ErrNameEmpty,
},
{
input: ":justtag",
err: ErrReferenceInvalidFormat,
},
{
input: "@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
err: ErrReferenceInvalidFormat,
},
{
input: "repo@sha256:ffffffffffffffffffffffffffffffffff",
err: digest.ErrDigestInvalidLength,
},
{
input: "validname@invaliddigest:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
err: digest.ErrDigestUnsupported,
},
{
input: "Uppercase:tag",
err: ErrNameContainsUppercase,
},
// FIXME "Uppercase" is incorrectly handled as a domain-name here, therefore passes.
// See https://github.com/docker/distribution/pull/1778, and https://github.com/docker/docker/pull/20175
//{
// input: "Uppercase/lowercase:tag",
// err: ErrNameContainsUppercase,
//},
{
input: "test:5000/Uppercase/lowercase:tag",
err: ErrNameContainsUppercase,
},
{
input: "lowercase:Uppercase",
repository: "lowercase",
tag: "Uppercase",
},
{
input: strings.Repeat("a/", 128) + "a:tag",
err: ErrNameTooLong,
},
{
input: strings.Repeat("a/", 127) + "a:tag-puts-this-over-max",
domain: "a",
repository: strings.Repeat("a/", 127) + "a",
tag: "tag-puts-this-over-max",
},
{
input: "aa/asdf$$^/aa",
err: ErrReferenceInvalidFormat,
},
{
input: "sub-dom1.foo.com/bar/baz/quux",
domain: "sub-dom1.foo.com",
repository: "sub-dom1.foo.com/bar/baz/quux",
},
{
input: "sub-dom1.foo.com/bar/baz/quux:some-long-tag",
domain: "sub-dom1.foo.com",
repository: "sub-dom1.foo.com/bar/baz/quux",
tag: "some-long-tag",
},
{
input: "b.gcr.io/test.example.com/my-app:test.example.com",
domain: "b.gcr.io",
repository: "b.gcr.io/test.example.com/my-app",
tag: "test.example.com",
},
{
input: "xn--n3h.com/myimage:xn--n3h.com", // ☃.com in punycode
domain: "xn--n3h.com",
repository: "xn--n3h.com/myimage",
tag: "xn--n3h.com",
},
{
input: "xn--7o8h.com/myimage:xn--7o8h.com@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // 🐳.com in punycode
domain: "xn--7o8h.com",
repository: "xn--7o8h.com/myimage",
tag: "xn--7o8h.com",
digest: "sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
},
{
input: "foo_bar.com:8080",
repository: "foo_bar.com",
tag: "8080",
},
{
input: "foo/foo_bar.com:8080",
domain: "foo",
repository: "foo/foo_bar.com",
tag: "8080",
},
}
for _, testcase := range referenceTestcases {
failf := func(format string, v ...interface{}) {
t.Logf(strconv.Quote(testcase.input)+": "+format, v...)
t.Fail()
}
repo, err := Parse(testcase.input)
if testcase.err != nil {
if err == nil {
failf("missing expected error: %v", testcase.err)
} else if testcase.err != err {
failf("mismatched error: got %v, expected %v", err, testcase.err)
}
continue
} else if err != nil {
failf("unexpected parse error: %v", err)
continue
}
if repo.String() != testcase.input {
failf("mismatched repo: got %q, expected %q", repo.String(), testcase.input)
}
if named, ok := repo.(Named); ok {
if named.Name() != testcase.repository {
failf("unexpected repository: got %q, expected %q", named.Name(), testcase.repository)
}
domain, _ := SplitHostname(named)
if domain != testcase.domain {
failf("unexpected domain: got %q, expected %q", domain, testcase.domain)
}
} else if testcase.repository != "" || testcase.domain != "" {
failf("expected named type, got %T", repo)
}
tagged, ok := repo.(Tagged)
if testcase.tag != "" {
if ok {
if tagged.Tag() != testcase.tag {
failf("unexpected tag: got %q, expected %q", tagged.Tag(), testcase.tag)
}
} else {
failf("expected tagged type, got %T", repo)
}
} else if ok {
failf("unexpected tagged type")
}
digested, ok := repo.(Digested)
if testcase.digest != "" {
if ok {
if digested.Digest().String() != testcase.digest {
failf("unexpected digest: got %q, expected %q", digested.Digest().String(), testcase.digest)
}
} else {
failf("expected digested type, got %T", repo)
}
} else if ok {
failf("unexpected digested type")
}
}
}
// TestWithNameFailure tests cases where WithName should fail. Cases where it
// should succeed are covered by TestSplitHostname, below.
func TestWithNameFailure(t *testing.T) {
testcases := []struct {
input string
err error
}{
{
input: "",
err: ErrNameEmpty,
},
{
input: ":justtag",
err: ErrReferenceInvalidFormat,
},
{
input: "@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
err: ErrReferenceInvalidFormat,
},
{
input: "validname@invaliddigest:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
err: ErrReferenceInvalidFormat,
},
{
input: strings.Repeat("a/", 128) + "a:tag",
err: ErrNameTooLong,
},
{
input: "aa/asdf$$^/aa",
err: ErrReferenceInvalidFormat,
},
}
for _, testcase := range testcases {
failf := func(format string, v ...interface{}) {
t.Logf(strconv.Quote(testcase.input)+": "+format, v...)
t.Fail()
}
_, err := WithName(testcase.input)
if err == nil {
failf("no error parsing name. expected: %s", testcase.err)
}
}
}
func TestSplitHostname(t *testing.T) {
testcases := []struct {
input string
domain string
name string
}{
{
input: "test.com/foo",
domain: "test.com",
name: "foo",
},
{
input: "test_com/foo",
domain: "",
name: "test_com/foo",
},
{
input: "test:8080/foo",
domain: "test:8080",
name: "foo",
},
{
input: "test.com:8080/foo",
domain: "test.com:8080",
name: "foo",
},
{
input: "test-com:8080/foo",
domain: "test-com:8080",
name: "foo",
},
{
input: "xn--n3h.com:18080/foo",
domain: "xn--n3h.com:18080",
name: "foo",
},
}
for _, testcase := range testcases {
failf := func(format string, v ...interface{}) {
t.Logf(strconv.Quote(testcase.input)+": "+format, v...)
t.Fail()
}
named, err := WithName(testcase.input)
if err != nil {
failf("error parsing name: %s", err)
}
domain, name := SplitHostname(named)
if domain != testcase.domain {
failf("unexpected domain: got %q, expected %q", domain, testcase.domain)
}
if name != testcase.name {
failf("unexpected name: got %q, expected %q", name, testcase.name)
}
}
}
type serializationType struct {
Description string
Field Field
}
func TestSerialization(t *testing.T) {
testcases := []struct {
description string
input string
name string
tag string
digest string
err error
}{
{
description: "empty value",
err: ErrNameEmpty,
},
{
description: "just a name",
input: "example.com:8000/named",
name: "example.com:8000/named",
},
{
description: "name with a tag",
input: "example.com:8000/named:tagged",
name: "example.com:8000/named",
tag: "tagged",
},
{
description: "name with digest",
input: "other.com/named@sha256:1234567890098765432112345667890098765432112345667890098765432112",
name: "other.com/named",
digest: "sha256:1234567890098765432112345667890098765432112345667890098765432112",
},
}
for _, testcase := range testcases {
failf := func(format string, v ...interface{}) {
t.Logf(strconv.Quote(testcase.input)+": "+format, v...)
t.Fail()
}
m := map[string]string{
"Description": testcase.description,
"Field": testcase.input,
}
b, err := json.Marshal(m)
if err != nil {
failf("error marshalling: %v", err)
}
t := serializationType{}
if err := json.Unmarshal(b, &t); err != nil {
if testcase.err == nil {
failf("error unmarshalling: %v", err)
}
if err != testcase.err {
failf("wrong error, expected %v, got %v", testcase.err, err)
}
continue
} else if testcase.err != nil {
failf("expected error unmarshalling: %v", testcase.err)
}
if t.Description != testcase.description {
failf("wrong description, expected %q, got %q", testcase.description, t.Description)
}
ref := t.Field.Reference()
if named, ok := ref.(Named); ok {
if named.Name() != testcase.name {
failf("unexpected repository: got %q, expected %q", named.Name(), testcase.name)
}
} else if testcase.name != "" {
failf("expected named type, got %T", ref)
}
tagged, ok := ref.(Tagged)
if testcase.tag != "" {
if ok {
if tagged.Tag() != testcase.tag {
failf("unexpected tag: got %q, expected %q", tagged.Tag(), testcase.tag)
}
} else {
failf("expected tagged type, got %T", ref)
}
} else if ok {
failf("unexpected tagged type")
}
digested, ok := ref.(Digested)
if testcase.digest != "" {
if ok {
if digested.Digest().String() != testcase.digest {
failf("unexpected digest: got %q, expected %q", digested.Digest().String(), testcase.digest)
}
} else {
failf("expected digested type, got %T", ref)
}
} else if ok {
failf("unexpected digested type")
}
t = serializationType{
Description: testcase.description,
Field: AsField(ref),
}
b2, err := json.Marshal(t)
if err != nil {
failf("error marshing serialization type: %v", err)
}
if string(b) != string(b2) {
failf("unexpected serialized value: expected %q, got %q", string(b), string(b2))
}
// Ensure t.Field is not implementing "Reference" directly, getting
// around the Reference type system
var fieldInterface interface{} = t.Field
if _, ok := fieldInterface.(Reference); ok {
failf("field should not implement Reference interface")
}
}
}
func TestWithTag(t *testing.T) {
testcases := []struct {
name string
digest digest.Digest
tag string
combined string
}{
{
name: "test.com/foo",
tag: "tag",
combined: "test.com/foo:tag",
},
{
name: "foo",
tag: "tag2",
combined: "foo:tag2",
},
{
name: "test.com:8000/foo",
tag: "tag4",
combined: "test.com:8000/foo:tag4",
},
{
name: "test.com:8000/foo",
tag: "TAG5",
combined: "test.com:8000/foo:TAG5",
},
{
name: "test.com:8000/foo",
digest: "sha256:1234567890098765432112345667890098765",
tag: "TAG5",
combined: "test.com:8000/foo:TAG5@sha256:1234567890098765432112345667890098765",
},
}
for _, testcase := range testcases {
failf := func(format string, v ...interface{}) {
t.Logf(strconv.Quote(testcase.name)+": "+format, v...)
t.Fail()
}
named, err := WithName(testcase.name)
if err != nil {
failf("error parsing name: %s", err)
}
if testcase.digest != "" {
canonical, err := WithDigest(named, testcase.digest)
if err != nil {
failf("error adding digest")
}
named = canonical
}
tagged, err := WithTag(named, testcase.tag)
if err != nil {
failf("WithTag failed: %s", err)
}
if tagged.String() != testcase.combined {
failf("unexpected: got %q, expected %q", tagged.String(), testcase.combined)
}
}
}
func TestWithDigest(t *testing.T) {
testcases := []struct {
name string
digest digest.Digest
tag string
combined string
}{
{
name: "test.com/foo",
digest: "sha256:1234567890098765432112345667890098765",
combined: "test.com/foo@sha256:1234567890098765432112345667890098765",
},
{
name: "foo",
digest: "sha256:1234567890098765432112345667890098765",
combined: "foo@sha256:1234567890098765432112345667890098765",
},
{
name: "test.com:8000/foo",
digest: "sha256:1234567890098765432112345667890098765",
combined: "test.com:8000/foo@sha256:1234567890098765432112345667890098765",
},
{
name: "test.com:8000/foo",
digest: "sha256:1234567890098765432112345667890098765",
tag: "latest",
combined: "test.com:8000/foo:latest@sha256:1234567890098765432112345667890098765",
},
}
for _, testcase := range testcases {
failf := func(format string, v ...interface{}) {
t.Logf(strconv.Quote(testcase.name)+": "+format, v...)
t.Fail()
}
named, err := WithName(testcase.name)
if err != nil {
failf("error parsing name: %s", err)
}
if testcase.tag != "" {
tagged, err := WithTag(named, testcase.tag)
if err != nil {
failf("error adding tag")
}
named = tagged
}
digested, err := WithDigest(named, testcase.digest)
if err != nil {
failf("WithDigest failed: %s", err)
}
if digested.String() != testcase.combined {
failf("unexpected: got %q, expected %q", digested.String(), testcase.combined)
}
}
}
func TestParseNamed(t *testing.T) {
testcases := []struct {
input string
domain string
name string
err error
}{
{
input: "test.com/foo",
domain: "test.com",
name: "foo",
},
{
input: "test:8080/foo",
domain: "test:8080",
name: "foo",
},
{
input: "test_com/foo",
err: ErrNameNotCanonical,
},
{
input: "test.com",
err: ErrNameNotCanonical,
},
{
input: "foo",
err: ErrNameNotCanonical,
},
{
input: "library/foo",
err: ErrNameNotCanonical,
},
{
input: "docker.io/library/foo",
domain: "docker.io",
name: "library/foo",
},
// Ambiguous case, parser will add "library/" to foo
{
input: "docker.io/foo",
err: ErrNameNotCanonical,
},
}
for _, testcase := range testcases {
failf := func(format string, v ...interface{}) {
t.Logf(strconv.Quote(testcase.input)+": "+format, v...)
t.Fail()
}
named, err := ParseNamed(testcase.input)
if err != nil && testcase.err == nil {
failf("error parsing name: %s", err)
continue
} else if err == nil && testcase.err != nil {
failf("parsing succeded: expected error %v", testcase.err)
continue
} else if err != testcase.err {
failf("unexpected error %v, expected %v", err, testcase.err)
continue
} else if err != nil {
continue
}
domain, name := SplitHostname(named)
if domain != testcase.domain {
failf("unexpected domain: got %q, expected %q", domain, testcase.domain)
}
if name != testcase.name {
failf("unexpected name: got %q, expected %q", name, testcase.name)
}
}
}

View file

@ -1,553 +0,0 @@
package reference
import (
"regexp"
"strings"
"testing"
)
type regexpMatch struct {
input string
match bool
subs []string
}
func checkRegexp(t *testing.T, r *regexp.Regexp, m regexpMatch) {
matches := r.FindStringSubmatch(m.input)
if m.match && matches != nil {
if len(matches) != (r.NumSubexp()+1) || matches[0] != m.input {
t.Fatalf("Bad match result %#v for %q", matches, m.input)
}
if len(matches) < (len(m.subs) + 1) {
t.Errorf("Expected %d sub matches, only have %d for %q", len(m.subs), len(matches)-1, m.input)
}
for i := range m.subs {
if m.subs[i] != matches[i+1] {
t.Errorf("Unexpected submatch %d: %q, expected %q for %q", i+1, matches[i+1], m.subs[i], m.input)
}
}
} else if m.match {
t.Errorf("Expected match for %q", m.input)
} else if matches != nil {
t.Errorf("Unexpected match for %q", m.input)
}
}
func TestDomainRegexp(t *testing.T) {
hostcases := []regexpMatch{
{
input: "test.com",
match: true,
},
{
input: "test.com:10304",
match: true,
},
{
input: "test.com:http",
match: false,
},
{
input: "localhost",
match: true,
},
{
input: "localhost:8080",
match: true,
},
{
input: "a",
match: true,
},
{
input: "a.b",
match: true,
},
{
input: "ab.cd.com",
match: true,
},
{
input: "a-b.com",
match: true,
},
{
input: "-ab.com",
match: false,
},
{
input: "ab-.com",
match: false,
},
{
input: "ab.c-om",
match: true,
},
{
input: "ab.-com",
match: false,
},
{
input: "ab.com-",
match: false,
},
{
input: "0101.com",
match: true, // TODO(dmcgowan): valid if this should be allowed
},
{
input: "001a.com",
match: true,
},
{
input: "b.gbc.io:443",
match: true,
},
{
input: "b.gbc.io",
match: true,
},
{
input: "xn--n3h.com", // ☃.com in punycode
match: true,
},
{
input: "Asdf.com", // uppercase character
match: true,
},
}
r := regexp.MustCompile(`^` + domainRegexp.String() + `$`)
for i := range hostcases {
checkRegexp(t, r, hostcases[i])
}
}
func TestFullNameRegexp(t *testing.T) {
if anchoredNameRegexp.NumSubexp() != 2 {
t.Fatalf("anchored name regexp should have two submatches: %v, %v != 2",
anchoredNameRegexp, anchoredNameRegexp.NumSubexp())
}
testcases := []regexpMatch{
{
input: "",
match: false,
},
{
input: "short",
match: true,
subs: []string{"", "short"},
},
{
input: "simple/name",
match: true,
subs: []string{"simple", "name"},
},
{
input: "library/ubuntu",
match: true,
subs: []string{"library", "ubuntu"},
},
{
input: "docker/stevvooe/app",
match: true,
subs: []string{"docker", "stevvooe/app"},
},
{
input: "aa/aa/aa/aa/aa/aa/aa/aa/aa/bb/bb/bb/bb/bb/bb",
match: true,
subs: []string{"aa", "aa/aa/aa/aa/aa/aa/aa/aa/bb/bb/bb/bb/bb/bb"},
},
{
input: "aa/aa/bb/bb/bb",
match: true,
subs: []string{"aa", "aa/bb/bb/bb"},
},
{
input: "a/a/a/a",
match: true,
subs: []string{"a", "a/a/a"},
},
{
input: "a/a/a/a/",
match: false,
},
{
input: "a//a/a",
match: false,
},
{
input: "a",
match: true,
subs: []string{"", "a"},
},
{
input: "a/aa",
match: true,
subs: []string{"a", "aa"},
},
{
input: "a/aa/a",
match: true,
subs: []string{"a", "aa/a"},
},
{
input: "foo.com",
match: true,
subs: []string{"", "foo.com"},
},
{
input: "foo.com/",
match: false,
},
{
input: "foo.com:8080/bar",
match: true,
subs: []string{"foo.com:8080", "bar"},
},
{
input: "foo.com:http/bar",
match: false,
},
{
input: "foo.com/bar",
match: true,
subs: []string{"foo.com", "bar"},
},
{
input: "foo.com/bar/baz",
match: true,
subs: []string{"foo.com", "bar/baz"},
},
{
input: "localhost:8080/bar",
match: true,
subs: []string{"localhost:8080", "bar"},
},
{
input: "sub-dom1.foo.com/bar/baz/quux",
match: true,
subs: []string{"sub-dom1.foo.com", "bar/baz/quux"},
},
{
input: "blog.foo.com/bar/baz",
match: true,
subs: []string{"blog.foo.com", "bar/baz"},
},
{
input: "a^a",
match: false,
},
{
input: "aa/asdf$$^/aa",
match: false,
},
{
input: "asdf$$^/aa",
match: false,
},
{
input: "aa-a/a",
match: true,
subs: []string{"aa-a", "a"},
},
{
input: strings.Repeat("a/", 128) + "a",
match: true,
subs: []string{"a", strings.Repeat("a/", 127) + "a"},
},
{
input: "a-/a/a/a",
match: false,
},
{
input: "foo.com/a-/a/a",
match: false,
},
{
input: "-foo/bar",
match: false,
},
{
input: "foo/bar-",
match: false,
},
{
input: "foo-/bar",
match: false,
},
{
input: "foo/-bar",
match: false,
},
{
input: "_foo/bar",
match: false,
},
{
input: "foo_bar",
match: true,
subs: []string{"", "foo_bar"},
},
{
input: "foo_bar.com",
match: true,
subs: []string{"", "foo_bar.com"},
},
{
input: "foo_bar.com:8080",
match: false,
},
{
input: "foo_bar.com:8080/app",
match: false,
},
{
input: "foo.com/foo_bar",
match: true,
subs: []string{"foo.com", "foo_bar"},
},
{
input: "____/____",
match: false,
},
{
input: "_docker/_docker",
match: false,
},
{
input: "docker_/docker_",
match: false,
},
{
input: "b.gcr.io/test.example.com/my-app",
match: true,
subs: []string{"b.gcr.io", "test.example.com/my-app"},
},
{
input: "xn--n3h.com/myimage", // ☃.com in punycode
match: true,
subs: []string{"xn--n3h.com", "myimage"},
},
{
input: "xn--7o8h.com/myimage", // 🐳.com in punycode
match: true,
subs: []string{"xn--7o8h.com", "myimage"},
},
{
input: "example.com/xn--7o8h.com/myimage", // 🐳.com in punycode
match: true,
subs: []string{"example.com", "xn--7o8h.com/myimage"},
},
{
input: "example.com/some_separator__underscore/myimage",
match: true,
subs: []string{"example.com", "some_separator__underscore/myimage"},
},
{
input: "example.com/__underscore/myimage",
match: false,
},
{
input: "example.com/..dots/myimage",
match: false,
},
{
input: "example.com/.dots/myimage",
match: false,
},
{
input: "example.com/nodouble..dots/myimage",
match: false,
},
{
input: "example.com/nodouble..dots/myimage",
match: false,
},
{
input: "docker./docker",
match: false,
},
{
input: ".docker/docker",
match: false,
},
{
input: "docker-/docker",
match: false,
},
{
input: "-docker/docker",
match: false,
},
{
input: "do..cker/docker",
match: false,
},
{
input: "do__cker:8080/docker",
match: false,
},
{
input: "do__cker/docker",
match: true,
subs: []string{"", "do__cker/docker"},
},
{
input: "b.gcr.io/test.example.com/my-app",
match: true,
subs: []string{"b.gcr.io", "test.example.com/my-app"},
},
{
input: "registry.io/foo/project--id.module--name.ver---sion--name",
match: true,
subs: []string{"registry.io", "foo/project--id.module--name.ver---sion--name"},
},
{
input: "Asdf.com/foo/bar", // uppercase character in hostname
match: true,
},
{
input: "Foo/FarB", // uppercase characters in remote name
match: false,
},
}
for i := range testcases {
checkRegexp(t, anchoredNameRegexp, testcases[i])
}
}
func TestReferenceRegexp(t *testing.T) {
if ReferenceRegexp.NumSubexp() != 3 {
t.Fatalf("anchored name regexp should have three submatches: %v, %v != 3",
ReferenceRegexp, ReferenceRegexp.NumSubexp())
}
testcases := []regexpMatch{
{
input: "registry.com:8080/myapp:tag",
match: true,
subs: []string{"registry.com:8080/myapp", "tag", ""},
},
{
input: "registry.com:8080/myapp@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
match: true,
subs: []string{"registry.com:8080/myapp", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
},
{
input: "registry.com:8080/myapp:tag2@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
match: true,
subs: []string{"registry.com:8080/myapp", "tag2", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
},
{
input: "registry.com:8080/myapp@sha256:badbadbadbad",
match: false,
},
{
input: "registry.com:8080/myapp:invalid~tag",
match: false,
},
{
input: "bad_hostname.com:8080/myapp:tag",
match: false,
},
{
input:// localhost treated as name, missing tag with 8080 as tag
"localhost:8080@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
match: true,
subs: []string{"localhost", "8080", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
},
{
input: "localhost:8080/name@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
match: true,
subs: []string{"localhost:8080/name", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
},
{
input: "localhost:http/name@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
match: false,
},
{
// localhost will be treated as an image name without a host
input: "localhost@sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912",
match: true,
subs: []string{"localhost", "", "sha256:be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912"},
},
{
input: "registry.com:8080/myapp@bad",
match: false,
},
{
input: "registry.com:8080/myapp@2bad",
match: false, // TODO(dmcgowan): Support this as valid
},
}
for i := range testcases {
checkRegexp(t, ReferenceRegexp, testcases[i])
}
}
func TestIdentifierRegexp(t *testing.T) {
fullCases := []regexpMatch{
{
input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf9821",
match: true,
},
{
input: "7EC43B381E5AEFE6E04EFB0B3F0693FF2A4A50652D64AEC573905F2DB5889A1C",
match: false,
},
{
input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf",
match: false,
},
{
input: "sha256:da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf9821",
match: false,
},
{
input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf98218482",
match: false,
},
}
shortCases := []regexpMatch{
{
input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf9821",
match: true,
},
{
input: "7EC43B381E5AEFE6E04EFB0B3F0693FF2A4A50652D64AEC573905F2DB5889A1C",
match: false,
},
{
input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf",
match: true,
},
{
input: "sha256:da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf9821",
match: false,
},
{
input: "da304e823d8ca2b9d863a3c897baeb852ba21ea9a9f1414736394ae7fcaf98218482",
match: false,
},
{
input: "da304",
match: false,
},
{
input: "da304e",
match: true,
},
}
for i := range fullCases {
checkRegexp(t, anchoredIdentifierRegexp, fullCases[i])
}
for i := range shortCases {
checkRegexp(t, anchoredShortIdentifierRegexp, shortCases[i])
}
}

View file

@ -1,45 +0,0 @@
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)
}
}