mirror of https://github.com/containers/image.git
889 lines
24 KiB
Go
889 lines
24 KiB
Go
package config
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"io/ioutil"
|
||
"os"
|
||
"path/filepath"
|
||
"runtime"
|
||
"strings"
|
||
"testing"
|
||
|
||
"github.com/containers/image/v5/docker/reference"
|
||
"github.com/containers/image/v5/types"
|
||
"github.com/stretchr/testify/assert"
|
||
"github.com/stretchr/testify/require"
|
||
)
|
||
|
||
func TestGetPathToAuth(t *testing.T) {
|
||
const linux = "linux"
|
||
const darwin = "darwin"
|
||
|
||
uid := fmt.Sprintf("%d", os.Getuid())
|
||
// We don’t have to override the home directory for this because use of this path does not depend
|
||
// on any state of the filesystem.
|
||
darwinDefault := filepath.Join(os.Getenv("HOME"), ".config", "containers", "auth.json")
|
||
|
||
tmpDir, err := ioutil.TempDir("", "TestGetPathToAuth")
|
||
require.NoError(t, err)
|
||
defer os.RemoveAll(tmpDir)
|
||
|
||
// Environment is per-process, so this looks very unsafe; actually it seems fine because tests are not
|
||
// run in parallel unless they opt in by calling t.Parallel(). So don’t do that.
|
||
oldXRD, hasXRD := os.LookupEnv("XDG_RUNTIME_DIR")
|
||
defer func() {
|
||
if hasXRD {
|
||
os.Setenv("XDG_RUNTIME_DIR", oldXRD)
|
||
} else {
|
||
os.Unsetenv("XDG_RUNTIME_DIR")
|
||
}
|
||
}()
|
||
|
||
for _, c := range []struct {
|
||
sys *types.SystemContext
|
||
os string
|
||
xrd string
|
||
expected string
|
||
legacyFormat bool
|
||
}{
|
||
// Default paths
|
||
{&types.SystemContext{}, linux, "", "/run/containers/" + uid + "/auth.json", false},
|
||
{&types.SystemContext{}, darwin, "", darwinDefault, false},
|
||
{nil, linux, "", "/run/containers/" + uid + "/auth.json", false},
|
||
{nil, darwin, "", darwinDefault, false},
|
||
// SystemContext overrides
|
||
{&types.SystemContext{AuthFilePath: "/absolute/path"}, linux, "", "/absolute/path", false},
|
||
{&types.SystemContext{AuthFilePath: "/absolute/path"}, darwin, "", "/absolute/path", false},
|
||
{&types.SystemContext{LegacyFormatAuthFilePath: "/absolute/path"}, linux, "", "/absolute/path", true},
|
||
{&types.SystemContext{LegacyFormatAuthFilePath: "/absolute/path"}, darwin, "", "/absolute/path", true},
|
||
{&types.SystemContext{RootForImplicitAbsolutePaths: "/prefix"}, linux, "", "/prefix/run/containers/" + uid + "/auth.json", false},
|
||
{&types.SystemContext{RootForImplicitAbsolutePaths: "/prefix"}, darwin, "", "/prefix/run/containers/" + uid + "/auth.json", false},
|
||
// XDG_RUNTIME_DIR defined
|
||
{nil, linux, tmpDir, tmpDir + "/containers/auth.json", false},
|
||
{nil, darwin, tmpDir, darwinDefault, false},
|
||
{nil, linux, tmpDir + "/thisdoesnotexist", "", false},
|
||
{nil, darwin, tmpDir + "/thisdoesnotexist", darwinDefault, false},
|
||
} {
|
||
if c.xrd != "" {
|
||
os.Setenv("XDG_RUNTIME_DIR", c.xrd)
|
||
} else {
|
||
os.Unsetenv("XDG_RUNTIME_DIR")
|
||
}
|
||
res, lf, err := getPathToAuthWithOS(c.sys, c.os)
|
||
if c.expected == "" {
|
||
assert.Error(t, err)
|
||
} else {
|
||
require.NoError(t, err)
|
||
assert.Equal(t, c.expected, res)
|
||
assert.Equal(t, c.legacyFormat, lf)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestGetAuth(t *testing.T) {
|
||
origXDG := os.Getenv("XDG_RUNTIME_DIR")
|
||
tmpXDGRuntimeDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
t.Logf("using temporary XDG_RUNTIME_DIR directory: %q", tmpXDGRuntimeDir)
|
||
// override XDG_RUNTIME_DIR
|
||
os.Setenv("XDG_RUNTIME_DIR", tmpXDGRuntimeDir)
|
||
defer func() {
|
||
err := os.RemoveAll(tmpXDGRuntimeDir)
|
||
if err != nil {
|
||
t.Logf("failed to cleanup temporary home directory %q: %v", tmpXDGRuntimeDir, err)
|
||
}
|
||
os.Setenv("XDG_RUNTIME_DIR", origXDG)
|
||
}()
|
||
|
||
// override PATH for executing credHelper
|
||
curtDir, err := os.Getwd()
|
||
require.NoError(t, err)
|
||
origPath := os.Getenv("PATH")
|
||
newPath := fmt.Sprintf("%s:%s", filepath.Join(curtDir, "testdata"), origPath)
|
||
os.Setenv("PATH", newPath)
|
||
t.Logf("using PATH: %q", newPath)
|
||
defer func() {
|
||
os.Setenv("PATH", origPath)
|
||
}()
|
||
|
||
tmpHomeDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
t.Logf("using temporary home directory: %q", tmpHomeDir)
|
||
defer func() {
|
||
err := os.RemoveAll(tmpHomeDir)
|
||
if err != nil {
|
||
t.Logf("failed to cleanup temporary home directory %q: %v", tmpHomeDir, err)
|
||
}
|
||
}()
|
||
|
||
configDir1 := filepath.Join(tmpXDGRuntimeDir, "containers")
|
||
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
||
configDir1 = filepath.Join(tmpHomeDir, ".config", "containers")
|
||
}
|
||
if err := os.MkdirAll(configDir1, 0700); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
configDir2 := filepath.Join(tmpHomeDir, ".docker")
|
||
if err := os.MkdirAll(configDir2, 0700); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
configPaths := [2]string{filepath.Join(configDir1, "auth.json"), filepath.Join(configDir2, "config.json")}
|
||
|
||
for _, configPath := range configPaths {
|
||
for _, tc := range []struct {
|
||
name string
|
||
ref string
|
||
hostname string
|
||
path string
|
||
expected types.DockerAuthConfig
|
||
expectedError error
|
||
sys *types.SystemContext
|
||
testPreviousAPI bool
|
||
}{
|
||
{
|
||
name: "no auth config",
|
||
hostname: "index.docker.io",
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "empty hostname",
|
||
path: filepath.Join("testdata", "example.json"),
|
||
},
|
||
{
|
||
name: "match one",
|
||
hostname: "example.org",
|
||
path: filepath.Join("testdata", "example.json"),
|
||
expected: types.DockerAuthConfig{
|
||
Username: "example",
|
||
Password: "org",
|
||
},
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "match none",
|
||
hostname: "registry.example.org",
|
||
path: filepath.Join("testdata", "example.json"),
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "match docker.io",
|
||
hostname: "docker.io",
|
||
path: filepath.Join("testdata", "full.json"),
|
||
expected: types.DockerAuthConfig{
|
||
Username: "docker",
|
||
Password: "io",
|
||
},
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "match docker.io normalized",
|
||
hostname: "docker.io",
|
||
path: filepath.Join("testdata", "abnormal.json"),
|
||
expected: types.DockerAuthConfig{
|
||
Username: "index",
|
||
Password: "docker.io",
|
||
},
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "normalize registry",
|
||
hostname: "https://example.org/v1",
|
||
path: filepath.Join("testdata", "full.json"),
|
||
expected: types.DockerAuthConfig{
|
||
Username: "example",
|
||
Password: "org",
|
||
},
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "match localhost",
|
||
hostname: "http://localhost",
|
||
path: filepath.Join("testdata", "full.json"),
|
||
expected: types.DockerAuthConfig{
|
||
Username: "local",
|
||
Password: "host",
|
||
},
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "match ip",
|
||
hostname: "10.10.30.45:5000",
|
||
path: filepath.Join("testdata", "full.json"),
|
||
expected: types.DockerAuthConfig{
|
||
Username: "10.10",
|
||
Password: "30.45-5000",
|
||
},
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "match port",
|
||
hostname: "https://localhost:5000",
|
||
path: filepath.Join("testdata", "abnormal.json"),
|
||
expected: types.DockerAuthConfig{
|
||
Username: "local",
|
||
Password: "host-5000",
|
||
},
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "use system context",
|
||
hostname: "example.org",
|
||
path: filepath.Join("testdata", "example.json"),
|
||
expected: types.DockerAuthConfig{
|
||
Username: "foo",
|
||
Password: "bar",
|
||
},
|
||
sys: &types.SystemContext{
|
||
DockerAuthConfig: &types.DockerAuthConfig{
|
||
Username: "foo",
|
||
Password: "bar",
|
||
},
|
||
},
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "identity token",
|
||
hostname: "example.org",
|
||
path: filepath.Join("testdata", "example_identitytoken.json"),
|
||
expected: types.DockerAuthConfig{
|
||
Username: "00000000-0000-0000-0000-000000000000",
|
||
Password: "",
|
||
IdentityToken: "some very long identity token",
|
||
},
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "match none (empty.json)",
|
||
hostname: "https://localhost:5000",
|
||
path: filepath.Join("testdata", "empty.json"),
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "credhelper from registries.conf",
|
||
hostname: "registry-a.com",
|
||
sys: &types.SystemContext{
|
||
SystemRegistriesConfPath: filepath.Join("testdata", "cred-helper.conf"),
|
||
SystemRegistriesConfDirPath: filepath.Join("testdata", "IdoNotExist"),
|
||
},
|
||
expected: types.DockerAuthConfig{
|
||
Username: "foo",
|
||
Password: "bar",
|
||
},
|
||
testPreviousAPI: true,
|
||
},
|
||
{
|
||
name: "match ref image",
|
||
hostname: "example.org",
|
||
ref: "example.org/repo/image:latest",
|
||
path: filepath.Join("testdata", "refpath.json"),
|
||
expected: types.DockerAuthConfig{
|
||
Username: "example",
|
||
Password: "org",
|
||
},
|
||
testPreviousAPI: false,
|
||
},
|
||
{
|
||
name: "match ref repo",
|
||
hostname: "example.org",
|
||
ref: "example.org/repo",
|
||
path: filepath.Join("testdata", "refpath.json"),
|
||
expected: types.DockerAuthConfig{
|
||
Username: "example",
|
||
Password: "org",
|
||
},
|
||
testPreviousAPI: false,
|
||
},
|
||
{
|
||
name: "match ref host",
|
||
hostname: "example.org",
|
||
ref: "example.org/image:latest",
|
||
path: filepath.Join("testdata", "refpath.json"),
|
||
expected: types.DockerAuthConfig{
|
||
Username: "local",
|
||
Password: "host",
|
||
},
|
||
testPreviousAPI: false,
|
||
},
|
||
} {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
if err := os.RemoveAll(configPath); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
if tc.path != "" {
|
||
contents, err := ioutil.ReadFile(tc.path)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
if err := ioutil.WriteFile(configPath, contents, 0640); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
|
||
var sys *types.SystemContext
|
||
if tc.sys != nil {
|
||
sys = tc.sys
|
||
}
|
||
|
||
var ref reference.Named
|
||
if tc.ref != "" {
|
||
ref, err = reference.ParseNamed(tc.ref)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
auth, err := getCredentialsWithHomeDir(sys, ref, tc.hostname, tmpHomeDir)
|
||
assert.Equal(t, tc.expectedError, err)
|
||
assert.Equal(t, tc.expected, auth)
|
||
|
||
// Test for the previous APIs.
|
||
if tc.testPreviousAPI {
|
||
username, password, err := getAuthenticationWithHomeDir(sys, tc.hostname, tmpHomeDir)
|
||
if tc.expected.IdentityToken != "" {
|
||
assert.Equal(t, "", username)
|
||
assert.Equal(t, "", password)
|
||
assert.Error(t, err)
|
||
} else {
|
||
assert.Equal(t, tc.expected.Username, username)
|
||
assert.Equal(t, tc.expected.Password, password)
|
||
assert.Equal(t, tc.expectedError, err)
|
||
}
|
||
}
|
||
|
||
require.NoError(t, os.RemoveAll(configPath))
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestGetAuthFromLegacyFile(t *testing.T) {
|
||
tmpDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
t.Logf("using temporary home directory: %q", tmpDir)
|
||
defer func() {
|
||
err := os.RemoveAll(tmpDir)
|
||
if err != nil {
|
||
t.Logf("failed to cleanup temporary home directory %q: %v", tmpDir, err)
|
||
}
|
||
}()
|
||
|
||
configPath := filepath.Join(tmpDir, ".dockercfg")
|
||
contents, err := ioutil.ReadFile(filepath.Join("testdata", "legacy.json"))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
for _, tc := range []struct {
|
||
name string
|
||
hostname string
|
||
expected types.DockerAuthConfig
|
||
expectedError error
|
||
}{
|
||
{
|
||
name: "normalize registry",
|
||
hostname: "https://docker.io/v1",
|
||
expected: types.DockerAuthConfig{
|
||
Username: "docker",
|
||
Password: "io-legacy",
|
||
},
|
||
},
|
||
{
|
||
name: "ignore schema and path",
|
||
hostname: "http://index.docker.io/v1",
|
||
expected: types.DockerAuthConfig{
|
||
Username: "docker",
|
||
Password: "io-legacy",
|
||
},
|
||
},
|
||
} {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
if err := ioutil.WriteFile(configPath, contents, 0640); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
auth, err := getCredentialsWithHomeDir(nil, nil, tc.hostname, tmpDir)
|
||
assert.Equal(t, tc.expectedError, err)
|
||
assert.Equal(t, tc.expected, auth)
|
||
|
||
// Testing for previous APIs
|
||
username, password, err := getAuthenticationWithHomeDir(nil, tc.hostname, tmpDir)
|
||
assert.Equal(t, tc.expectedError, err)
|
||
assert.Equal(t, tc.expected.Username, username)
|
||
assert.Equal(t, tc.expected.Password, password)
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestGetAuthPreferNewConfig(t *testing.T) {
|
||
tmpDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
t.Logf("using temporary home directory: %q", tmpDir)
|
||
defer func() {
|
||
err := os.RemoveAll(tmpDir)
|
||
if err != nil {
|
||
t.Logf("failed to cleanup temporary home directory %q: %v", tmpDir, err)
|
||
}
|
||
}()
|
||
|
||
configDir := filepath.Join(tmpDir, ".docker")
|
||
if err := os.Mkdir(configDir, 0750); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
for _, data := range []struct {
|
||
source string
|
||
target string
|
||
}{
|
||
{
|
||
source: filepath.Join("testdata", "full.json"),
|
||
target: filepath.Join(configDir, "config.json"),
|
||
},
|
||
{
|
||
source: filepath.Join("testdata", "legacy.json"),
|
||
target: filepath.Join(tmpDir, ".dockercfg"),
|
||
},
|
||
} {
|
||
contents, err := ioutil.ReadFile(data.source)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
if err := ioutil.WriteFile(data.target, contents, 0640); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
|
||
auth, err := getCredentialsWithHomeDir(nil, nil, "docker.io", tmpDir)
|
||
assert.NoError(t, err)
|
||
assert.Equal(t, "docker", auth.Username)
|
||
assert.Equal(t, "io", auth.Password)
|
||
}
|
||
|
||
func TestGetAuthFailsOnBadInput(t *testing.T) {
|
||
origXDG := os.Getenv("XDG_RUNTIME_DIR")
|
||
tmpXDGRuntimeDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
t.Logf("using temporary XDG_RUNTIME_DIR directory: %q", tmpXDGRuntimeDir)
|
||
// override XDG_RUNTIME_DIR
|
||
os.Setenv("XDG_RUNTIME_DIR", tmpXDGRuntimeDir)
|
||
defer func() {
|
||
err := os.RemoveAll(tmpXDGRuntimeDir)
|
||
if err != nil {
|
||
t.Logf("failed to cleanup temporary home directory %q: %v", tmpXDGRuntimeDir, err)
|
||
}
|
||
os.Setenv("XDG_RUNTIME_DIR", origXDG)
|
||
}()
|
||
|
||
tmpHomeDir, err := ioutil.TempDir("", "test_docker_client_get_auth")
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
t.Logf("using temporary home directory: %q", tmpHomeDir)
|
||
defer func() {
|
||
err := os.RemoveAll(tmpHomeDir)
|
||
if err != nil {
|
||
t.Logf("failed to cleanup temporary home directory %q: %v", tmpHomeDir, err)
|
||
}
|
||
}()
|
||
|
||
configDir := filepath.Join(tmpXDGRuntimeDir, "containers")
|
||
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
||
configDir = filepath.Join(tmpHomeDir, ".config", "containers")
|
||
}
|
||
if err := os.MkdirAll(configDir, 0750); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
configPath := filepath.Join(configDir, "auth.json")
|
||
|
||
// no config file present
|
||
auth, err := getCredentialsWithHomeDir(nil, nil, "index.docker.io", tmpHomeDir)
|
||
if err != nil {
|
||
t.Fatalf("got unexpected error: %#+v", err)
|
||
}
|
||
assert.Equal(t, types.DockerAuthConfig{}, auth)
|
||
|
||
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)
|
||
}
|
||
auth, err = getCredentialsWithHomeDir(nil, nil, "index.docker.io", tmpHomeDir)
|
||
if err == nil {
|
||
t.Fatalf("got unexpected non-error: username=%q, password=%q", auth.Username, auth.Password)
|
||
}
|
||
if !strings.Contains(err.Error(), "unmarshaling JSON") {
|
||
t.Fatalf("expected JSON syntax error, not: %#+v", err)
|
||
}
|
||
|
||
// remove the invalid config file
|
||
os.RemoveAll(configPath)
|
||
// no config file present
|
||
auth, err = getCredentialsWithHomeDir(nil, nil, "index.docker.io", tmpHomeDir)
|
||
if err != nil {
|
||
t.Fatalf("got unexpected error: %#+v", err)
|
||
}
|
||
assert.Equal(t, types.DockerAuthConfig{}, auth)
|
||
|
||
configPath = filepath.Join(tmpHomeDir, ".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)
|
||
}
|
||
auth, err = getCredentialsWithHomeDir(nil, nil, "index.docker.io", tmpHomeDir)
|
||
if err == nil {
|
||
t.Fatalf("got unexpected non-error: username=%q, password=%q", auth.Username, auth.Password)
|
||
}
|
||
if !strings.Contains(err.Error(), "unmarshaling JSON") {
|
||
t.Fatalf("expected JSON syntax error, not: %#+v", err)
|
||
}
|
||
}
|
||
|
||
func TestGetAllCredentials(t *testing.T) {
|
||
// Create a temporary authentication file.
|
||
tmpFile, err := ioutil.TempFile("", "auth.json.")
|
||
require.NoError(t, err)
|
||
_, err = tmpFile.Write([]byte{'{', '}'})
|
||
require.NoError(t, err)
|
||
err = tmpFile.Close()
|
||
require.NoError(t, err)
|
||
authFilePath := tmpFile.Name()
|
||
// override PATH for executing credHelper
|
||
path, err := os.Getwd()
|
||
require.NoError(t, err)
|
||
origPath := os.Getenv("PATH")
|
||
newPath := fmt.Sprintf("%s:%s", filepath.Join(path, "testdata"), origPath)
|
||
os.Setenv("PATH", newPath)
|
||
t.Logf("using PATH: %q", newPath)
|
||
defer func() {
|
||
os.Setenv("PATH", origPath)
|
||
}()
|
||
err = os.Chmod(filepath.Join(path, "testdata", "docker-credential-helper-registry"), os.ModePerm)
|
||
require.NoError(t, err)
|
||
sys := types.SystemContext{
|
||
AuthFilePath: authFilePath,
|
||
SystemRegistriesConfPath: filepath.Join("testdata", "cred-helper-with-auth-files.conf"),
|
||
SystemRegistriesConfDirPath: filepath.Join("testdata", "IdoNotExist"),
|
||
}
|
||
|
||
data := []struct {
|
||
server string
|
||
username string
|
||
password string
|
||
noStore bool
|
||
}{
|
||
{
|
||
server: "example.org",
|
||
username: "example-user",
|
||
password: "example-password",
|
||
},
|
||
{
|
||
server: "quay.io",
|
||
username: "quay-user",
|
||
password: "quay-password",
|
||
},
|
||
{
|
||
server: "localhost:5000",
|
||
username: "local-user",
|
||
password: "local-password",
|
||
},
|
||
{
|
||
server: "registry-a.com",
|
||
username: "foo",
|
||
password: "bar",
|
||
noStore: true,
|
||
},
|
||
}
|
||
|
||
// Write the credentials to the authfile.
|
||
for _, d := range data {
|
||
if d.noStore {
|
||
continue
|
||
}
|
||
err := SetAuthentication(&sys, d.server, d.username, d.password)
|
||
require.NoError(t, err)
|
||
}
|
||
|
||
// Now ask for all credentials and make sure that map includes all
|
||
// servers and the correct credentials.
|
||
authConfigs, err := GetAllCredentials(&sys)
|
||
require.NoError(t, err)
|
||
require.Equal(t, len(data), len(authConfigs))
|
||
|
||
for _, d := range data {
|
||
conf, exists := authConfigs[d.server]
|
||
require.True(t, exists, "%v", d)
|
||
require.Equal(t, d.username, conf.Username, "%v", d)
|
||
require.Equal(t, d.password, conf.Password, "%v", d)
|
||
}
|
||
|
||
}
|
||
|
||
func TestAuthKeysForRef(t *testing.T) {
|
||
for _, tc := range []struct {
|
||
name, ref string
|
||
expected []string
|
||
}{
|
||
{
|
||
name: "image without tag",
|
||
ref: "quay.io/image",
|
||
expected: []string{
|
||
"quay.io/image",
|
||
"quay.io",
|
||
},
|
||
},
|
||
{
|
||
name: "image with tag",
|
||
ref: "quay.io/image:latest",
|
||
expected: []string{
|
||
"quay.io/image",
|
||
"quay.io",
|
||
},
|
||
},
|
||
{
|
||
name: "image single path tag",
|
||
ref: "quay.io/user/image:latest",
|
||
expected: []string{
|
||
"quay.io/user/image",
|
||
"quay.io/user",
|
||
"quay.io",
|
||
},
|
||
},
|
||
{
|
||
name: "image with nested path",
|
||
ref: "quay.io/a/b/c/d/image:latest",
|
||
expected: []string{
|
||
"quay.io/a/b/c/d/image",
|
||
"quay.io/a/b/c/d",
|
||
"quay.io/a/b/c",
|
||
"quay.io/a/b",
|
||
"quay.io/a",
|
||
"quay.io",
|
||
},
|
||
},
|
||
} {
|
||
ref, err := reference.ParseNamed(tc.ref)
|
||
require.NoError(t, err, tc.name)
|
||
|
||
result := authKeysForRef(ref)
|
||
require.Equal(t, tc.expected, result, tc.name)
|
||
}
|
||
}
|
||
|
||
func TestSetCredentials(t *testing.T) {
|
||
const (
|
||
usernamePrefix = "username-"
|
||
passwordPrefix = "password-"
|
||
)
|
||
getAuth := func(sys *types.SystemContext, input string) types.DockerAuthConfig {
|
||
ref, err := reference.ParseNamed(input)
|
||
require.NoError(t, err)
|
||
auth, err := GetCredentialsForRef(sys, ref)
|
||
require.NoError(t, err)
|
||
return auth
|
||
}
|
||
|
||
for _, tc := range []struct {
|
||
input []string
|
||
assert func(*types.SystemContext, dockerConfigFile)
|
||
}{
|
||
{
|
||
input: []string{"quay.io"},
|
||
assert: func(sys *types.SystemContext, auth dockerConfigFile) {
|
||
assert.Len(t, auth.AuthConfigs, 1)
|
||
assert.NotEmpty(t, auth.AuthConfigs["quay.io"].Auth)
|
||
},
|
||
},
|
||
{
|
||
input: []string{"quay.io/a/b/c/d/image"},
|
||
assert: func(sys *types.SystemContext, auth dockerConfigFile) {
|
||
assert.Len(t, auth.AuthConfigs, 1)
|
||
assert.NotEmpty(t, auth.AuthConfigs["quay.io/a/b/c/d/image"].Auth)
|
||
|
||
ta := getAuth(sys, "quay.io/a/b/c/d/image")
|
||
assert.Equal(t, usernamePrefix+"0", ta.Username)
|
||
assert.Equal(t, passwordPrefix+"0", ta.Password)
|
||
},
|
||
},
|
||
{
|
||
input: []string{
|
||
"quay.io/a/b/c",
|
||
"quay.io/a/b",
|
||
"quay.io/a",
|
||
"quay.io",
|
||
"my-registry.local",
|
||
"my-registry.local",
|
||
},
|
||
assert: func(sys *types.SystemContext, auth dockerConfigFile) {
|
||
assert.Len(t, auth.AuthConfigs, 5)
|
||
assert.NotEmpty(t, auth.AuthConfigs["quay.io/a/b/c"].Auth)
|
||
assert.NotEmpty(t, auth.AuthConfigs["quay.io/a/b"].Auth)
|
||
assert.NotEmpty(t, auth.AuthConfigs["quay.io/a"].Auth)
|
||
assert.NotEmpty(t, auth.AuthConfigs["quay.io"].Auth)
|
||
assert.NotEmpty(t, auth.AuthConfigs["my-registry.local"].Auth)
|
||
|
||
ta0 := getAuth(sys, "quay.io/a/b/c")
|
||
assert.Equal(t, usernamePrefix+"0", ta0.Username)
|
||
assert.Equal(t, passwordPrefix+"0", ta0.Password)
|
||
|
||
ta1 := getAuth(sys, "quay.io/a/b")
|
||
assert.Equal(t, usernamePrefix+"1", ta1.Username)
|
||
assert.Equal(t, passwordPrefix+"1", ta1.Password)
|
||
|
||
ta2 := getAuth(sys, "quay.io/a")
|
||
assert.Equal(t, usernamePrefix+"2", ta2.Username)
|
||
assert.Equal(t, passwordPrefix+"2", ta2.Password)
|
||
|
||
},
|
||
},
|
||
} {
|
||
tmpFile, err := ioutil.TempFile("", "auth.json.set")
|
||
require.NoError(t, err)
|
||
defer os.RemoveAll(tmpFile.Name())
|
||
|
||
_, err = tmpFile.WriteString("{}")
|
||
require.NoError(t, err)
|
||
sys := &types.SystemContext{AuthFilePath: tmpFile.Name()}
|
||
|
||
for i, input := range tc.input {
|
||
_, err := SetCredentials(
|
||
sys,
|
||
input,
|
||
usernamePrefix+fmt.Sprint(i),
|
||
passwordPrefix+fmt.Sprint(i),
|
||
)
|
||
assert.NoError(t, err)
|
||
}
|
||
|
||
auth, err := readJSONFile(tmpFile.Name(), false)
|
||
require.NoError(t, err)
|
||
|
||
tc.assert(sys, auth)
|
||
}
|
||
}
|
||
|
||
func TestRemoveAuthentication(t *testing.T) {
|
||
testAuth := dockerAuthConfig{Auth: "ZXhhbXBsZTpvcmc="}
|
||
for _, tc := range []struct {
|
||
config dockerConfigFile
|
||
inputs []string
|
||
shouldError bool
|
||
assert func(dockerConfigFile)
|
||
}{
|
||
{
|
||
config: dockerConfigFile{
|
||
AuthConfigs: map[string]dockerAuthConfig{
|
||
"quay.io": testAuth,
|
||
},
|
||
},
|
||
inputs: []string{"quay.io"},
|
||
assert: func(auth dockerConfigFile) {
|
||
assert.Len(t, auth.AuthConfigs, 0)
|
||
},
|
||
},
|
||
{
|
||
config: dockerConfigFile{
|
||
AuthConfigs: map[string]dockerAuthConfig{
|
||
"quay.io": testAuth,
|
||
},
|
||
},
|
||
inputs: []string{"quay.io/user/image"},
|
||
shouldError: true, // not logged in
|
||
assert: func(auth dockerConfigFile) {
|
||
assert.Len(t, auth.AuthConfigs, 1)
|
||
assert.NotEmpty(t, auth.AuthConfigs["quay.io"].Auth)
|
||
},
|
||
},
|
||
{
|
||
config: dockerConfigFile{
|
||
AuthConfigs: map[string]dockerAuthConfig{
|
||
"quay.io": testAuth,
|
||
"my-registry.local": testAuth,
|
||
},
|
||
},
|
||
inputs: []string{"my-registry.local"},
|
||
assert: func(auth dockerConfigFile) {
|
||
assert.Len(t, auth.AuthConfigs, 1)
|
||
assert.NotEmpty(t, auth.AuthConfigs["quay.io"].Auth)
|
||
},
|
||
},
|
||
{
|
||
config: dockerConfigFile{
|
||
AuthConfigs: map[string]dockerAuthConfig{
|
||
"quay.io/a/b/c": testAuth,
|
||
"quay.io/a/b": testAuth,
|
||
"quay.io/a": testAuth,
|
||
"quay.io": testAuth,
|
||
"my-registry.local": testAuth,
|
||
},
|
||
},
|
||
inputs: []string{
|
||
"quay.io/a/b",
|
||
"quay.io",
|
||
"my-registry.local",
|
||
},
|
||
assert: func(auth dockerConfigFile) {
|
||
assert.Len(t, auth.AuthConfigs, 2)
|
||
assert.NotEmpty(t, auth.AuthConfigs["quay.io/a/b/c"].Auth)
|
||
assert.NotEmpty(t, auth.AuthConfigs["quay.io/a"].Auth)
|
||
},
|
||
},
|
||
} {
|
||
|
||
content, err := json.Marshal(&tc.config)
|
||
require.NoError(t, err)
|
||
|
||
tmpFile, err := ioutil.TempFile("", "auth.json")
|
||
require.NoError(t, err)
|
||
defer os.RemoveAll(tmpFile.Name())
|
||
|
||
_, err = tmpFile.Write(content)
|
||
require.NoError(t, err)
|
||
|
||
sys := &types.SystemContext{AuthFilePath: tmpFile.Name()}
|
||
|
||
for _, input := range tc.inputs {
|
||
err := RemoveAuthentication(sys, input)
|
||
if tc.shouldError {
|
||
assert.Error(t, err)
|
||
} else {
|
||
assert.NoError(t, err)
|
||
}
|
||
}
|
||
|
||
auth, err := readJSONFile(tmpFile.Name(), false)
|
||
require.NoError(t, err)
|
||
|
||
tc.assert(auth)
|
||
}
|
||
}
|
||
|
||
func TestValidateKey(t *testing.T) {
|
||
for _, tc := range []struct {
|
||
key string
|
||
shouldError bool
|
||
isNamespaced bool
|
||
}{
|
||
{"my-registry.local", false, false},
|
||
{"https://my-registry.local", true, false},
|
||
{"my-registry.local/path", false, true},
|
||
{"quay.io/a/b/c/d", false, true},
|
||
} {
|
||
|
||
isNamespaced, err := validateKey(tc.key)
|
||
if tc.shouldError {
|
||
assert.Error(t, err)
|
||
} else {
|
||
assert.NoError(t, err)
|
||
}
|
||
assert.Equal(t, tc.isNamespaced, isNamespaced)
|
||
}
|
||
}
|