Skip to content

Commit

Permalink
cliconfig: credentials: support getting all auths
Browse files Browse the repository at this point in the history
docker build is broken because it sends to the daemon the full
cliconfig file which has only Email(s). This patch retrieves all auth
configs from the credentials store.

Signed-off-by: Antonio Murdaca <[email protected]>
  • Loading branch information
runcom committed Mar 2, 2016
1 parent 4eb7a49 commit 4415214
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 7 deletions.
2 changes: 1 addition & 1 deletion api/client/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
ShmSize: shmSize,
Ulimits: flUlimits.GetList(),
BuildArgs: runconfigopts.ConvertKVStringsToMap(flBuildArg.GetAll()),
AuthConfigs: cli.configFile.AuthConfigs,
AuthConfigs: cli.retrieveAuthConfigs(),
}

response, err := cli.client.ImageBuild(context.Background(), options)
Expand Down
5 changes: 5 additions & 0 deletions api/client/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ func getCredentials(c *cliconfig.ConfigFile, serverAddress string) (types.AuthCo
return s.Get(serverAddress)
}

func getAllCredentials(c *cliconfig.ConfigFile) (map[string]types.AuthConfig, error) {
s := loadCredentialsStore(c)
return s.GetAll()
}

// storeCredentials saves the user credentials in a credentials store.
// The store is determined by the config file settings.
func storeCredentials(c *cliconfig.ConfigFile, auth types.AuthConfig) error {
Expand Down
5 changes: 5 additions & 0 deletions api/client/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,8 @@ func (cli *DockerCli) resolveAuthConfig(index *registrytypes.IndexInfo) types.Au
a, _ := getCredentials(cli.configFile, configKey)
return a
}

func (cli *DockerCli) retrieveAuthConfigs() map[string]types.AuthConfig {
acs, _ := getAllCredentials(cli.configFile)
return acs
}
2 changes: 2 additions & 0 deletions cliconfig/credentials/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ type Store interface {
Erase(serverAddress string) error
// Get retrieves credentials from the store for a given server.
Get(serverAddress string) (types.AuthConfig, error)
// GetAll retrieves all the credentials from the store.
GetAll() (map[string]types.AuthConfig, error)
// Store saves credentials in the store.
Store(authConfig types.AuthConfig) error
}
4 changes: 4 additions & 0 deletions cliconfig/credentials/file_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ func (c *fileStore) Get(serverAddress string) (types.AuthConfig, error) {
return authConfig, nil
}

func (c *fileStore) GetAll() (map[string]types.AuthConfig, error) {
return c.file.AuthConfigs, nil
}

// Store saves the given credentials in the file store.
func (c *fileStore) Store(authConfig types.AuthConfig) error {
c.file.AuthConfigs[authConfig.ServerAddress] = authConfig
Expand Down
38 changes: 38 additions & 0 deletions cliconfig/credentials/file_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,44 @@ func TestFileStoreGet(t *testing.T) {
}
}

func TestFileStoreGetAll(t *testing.T) {
s1 := "https://example.com"
s2 := "https://example2.com"
f := newConfigFile(map[string]types.AuthConfig{
s1: {
Auth: "super_secret_token",
Email: "[email protected]",
ServerAddress: "https://example.com",
},
s2: {
Auth: "super_secret_token2",
Email: "[email protected]",
ServerAddress: "https://example2.com",
},
})

s := NewFileStore(f)
as, err := s.GetAll()
if err != nil {
t.Fatal(err)
}
if len(as) != 2 {
t.Fatalf("wanted 2, got %d", len(as))
}
if as[s1].Auth != "super_secret_token" {
t.Fatalf("expected auth `super_secret_token`, got %s", as[s1].Auth)
}
if as[s1].Email != "[email protected]" {
t.Fatalf("expected email `[email protected]`, got %s", as[s1].Email)
}
if as[s2].Auth != "super_secret_token2" {
t.Fatalf("expected auth `super_secret_token2`, got %s", as[s2].Auth)
}
if as[s2].Email != "[email protected]" {
t.Fatalf("expected email `[email protected]`, got %s", as[s2].Email)
}
}

func TestFileStoreErase(t *testing.T) {
f := newConfigFile(map[string]types.AuthConfig{
"https://example.com": {
Expand Down
18 changes: 16 additions & 2 deletions cliconfig/credentials/native_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,20 @@ func (c *nativeStore) Get(serverAddress string) (types.AuthConfig, error) {
return auth, nil
}

// GetAll retrieves all the credentials from the native store.
func (c *nativeStore) GetAll() (map[string]types.AuthConfig, error) {
auths, _ := c.fileStore.GetAll()

for s, ac := range auths {
creds, _ := c.getCredentialsFromStore(s)
ac.Username = creds.Username
ac.Password = creds.Password
auths[s] = ac
}

return auths, nil
}

// Store saves the given credentials in the file store.
func (c *nativeStore) Store(authConfig types.AuthConfig) error {
if err := c.storeCredentialsInStore(authConfig); err != nil {
Expand Down Expand Up @@ -135,7 +149,7 @@ func (c *nativeStore) getCredentialsFromStore(serverAddress string) (types.AuthC
return ret, nil
}

logrus.Debugf("error adding credentials - err: %v, out: `%s`", err, t)
logrus.Debugf("error getting credentials - err: %v, out: `%s`", err, t)
return ret, fmt.Errorf(t)
}

Expand All @@ -158,7 +172,7 @@ func (c *nativeStore) eraseCredentialsFromStore(serverURL string) error {
out, err := cmd.Output()
if err != nil {
t := strings.TrimSpace(string(out))
logrus.Debugf("error adding credentials - err: %v, out: `%s`", err, t)
logrus.Debugf("error erasing credentials - err: %v, out: `%s`", err, t)
return fmt.Errorf(t)
}

Expand Down
49 changes: 47 additions & 2 deletions cliconfig/credentials/native_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

const (
validServerAddress = "https://index.docker.io/v1"
validServerAddress2 = "https://example.com:5002"
invalidServerAddress = "https://foobar.example.com"
missingCredsAddress = "https://missing.docker.io/v1"
)
Expand Down Expand Up @@ -46,7 +47,7 @@ func (m *mockCommand) Output() ([]byte, error) {
}
case "get":
switch inS {
case validServerAddress:
case validServerAddress, validServerAddress2:
return []byte(`{"Username": "foo", "Password": "bar"}`), nil
case missingCredsAddress:
return []byte(errCredentialsNotFound.Error()), errCommandExited
Expand All @@ -67,7 +68,7 @@ func (m *mockCommand) Output() ([]byte, error) {
}
}

return []byte("unknown argument"), errCommandExited
return []byte(fmt.Sprintf("unknown argument %q with %q", m.arg, inS)), errCommandExited
}

// Input sets the input to send to a remote credentials helper.
Expand Down Expand Up @@ -178,6 +179,50 @@ func TestNativeStoreGet(t *testing.T) {
}
}

func TestNativeStoreGetAll(t *testing.T) {
f := newConfigFile(map[string]types.AuthConfig{
validServerAddress: {
Email: "[email protected]",
},
validServerAddress2: {
Email: "[email protected]",
},
})
f.CredentialsStore = "mock"

s := &nativeStore{
commandFn: mockCommandFn,
fileStore: NewFileStore(f),
}
as, err := s.GetAll()
if err != nil {
t.Fatal(err)
}

if len(as) != 2 {
t.Fatalf("wanted 2, got %d", len(as))
}

if as[validServerAddress].Username != "foo" {
t.Fatalf("expected username `foo` for %s, got %s", validServerAddress, as[validServerAddress].Username)
}
if as[validServerAddress].Password != "bar" {
t.Fatalf("expected password `bar` for %s, got %s", validServerAddress, as[validServerAddress].Password)
}
if as[validServerAddress].Email != "[email protected]" {
t.Fatalf("expected email `[email protected]` for %s, got %s", validServerAddress, as[validServerAddress].Email)
}
if as[validServerAddress2].Username != "foo" {
t.Fatalf("expected username `foo` for %s, got %s", validServerAddress2, as[validServerAddress2].Username)
}
if as[validServerAddress2].Password != "bar" {
t.Fatalf("expected password `bar` for %s, got %s", validServerAddress2, as[validServerAddress2].Password)
}
if as[validServerAddress2].Email != "[email protected]" {
t.Fatalf("expected email `[email protected]` for %s, got %s", validServerAddress2, as[validServerAddress2].Email)
}
}

func TestNativeStoreGetMissingCredentials(t *testing.T) {
f := newConfigFile(map[string]types.AuthConfig{
validServerAddress: {
Expand Down
42 changes: 42 additions & 0 deletions integration-cli/docker_cli_build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6589,3 +6589,45 @@ func (s *DockerRegistryAuthSuite) TestBuildFromAuthenticatedRegistry(c *check.C)

c.Assert(err, checker.IsNil)
}

func (s *DockerRegistryAuthSuite) TestBuildWithExternalAuth(c *check.C) {
osPath := os.Getenv("PATH")
defer os.Setenv("PATH", osPath)

workingDir, err := os.Getwd()
c.Assert(err, checker.IsNil)
absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth"))
c.Assert(err, checker.IsNil)
testPath := fmt.Sprintf("%s%c%s", osPath, filepath.ListSeparator, absolute)

os.Setenv("PATH", testPath)

repoName := fmt.Sprintf("%v/dockercli/busybox:authtest", privateRegistryURL)

tmp, err := ioutil.TempDir("", "integration-cli-")
c.Assert(err, checker.IsNil)

externalAuthConfig := `{ "credsStore": "shell-test" }`

configPath := filepath.Join(tmp, "config.json")
err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644)
c.Assert(err, checker.IsNil)

dockerCmd(c, "--config", tmp, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL)

b, err := ioutil.ReadFile(configPath)
c.Assert(err, checker.IsNil)
c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":")

dockerCmd(c, "--config", tmp, "tag", "busybox", repoName)
dockerCmd(c, "--config", tmp, "push", repoName)

// make sure the image is pulled when building
dockerCmd(c, "rmi", repoName)

buildCmd := exec.Command(dockerBinary, "--config", tmp, "build", "-")
buildCmd.Stdin = strings.NewReader(fmt.Sprintf("FROM %s", repoName))

out, _, err := runCommandWithOutput(buildCmd)
c.Assert(err, check.IsNil, check.Commentf(out))
}
2 changes: 1 addition & 1 deletion integration-cli/docker_cli_pull_local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ func (s *DockerRegistryAuthSuite) TestPullWithExternalAuth(c *check.C) {
err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644)
c.Assert(err, checker.IsNil)

dockerCmd(c, "--config", tmp, "login", "-u", s.reg.username, "-p", s.reg.password, "-e", s.reg.email, privateRegistryURL)
dockerCmd(c, "--config", tmp, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL)

b, err := ioutil.ReadFile(configPath)
c.Assert(err, checker.IsNil)
Expand Down
1 change: 0 additions & 1 deletion integration-cli/docker_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import (
func init() {
cmd := exec.Command(dockerBinary, "images")
cmd.Env = appendBaseEnv(true)
fmt.Println("foobar", cmd.Env)
out, err := cmd.CombinedOutput()
if err != nil {
panic(fmt.Errorf("err=%v\nout=%s\n", err, out))
Expand Down

0 comments on commit 4415214

Please sign in to comment.