Skip to content

Commit

Permalink
Merge pull request #227 from dnephin/expose-config
Browse files Browse the repository at this point in the history
expose config credentials without needing the Cli
  • Loading branch information
Vincent Demeester authored Jun 29, 2017
2 parents 8de604c + 105b21d commit 74af31b
Show file tree
Hide file tree
Showing 19 changed files with 334 additions and 479 deletions.
69 changes: 2 additions & 67 deletions cli/command/cli.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package command

import (
"fmt"
"io"
"net/http"
"os"
Expand All @@ -10,11 +9,9 @@ import (
"github.com/docker/cli/cli"
cliconfig "github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/config/credentials"
cliflags "github.com/docker/cli/cli/flags"
dopts "github.com/docker/cli/opts"
"github.com/docker/docker/api"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/docker/go-connections/sockets"
"github.com/docker/go-connections/tlsconfig"
Expand All @@ -39,7 +36,7 @@ type Cli interface {
In() *InStream
SetIn(in *InStream)
ConfigFile() *configfile.ConfigFile
CredentialsStore(serverAddress string) credentials.Store
ServerInfo() ServerInfo
}

// DockerCli is an instance the docker command line client.
Expand Down Expand Up @@ -104,59 +101,10 @@ func (cli *DockerCli) ServerInfo() ServerInfo {
return cli.server
}

// GetAllCredentials returns all of the credentials stored in all of the
// configured credential stores.
func (cli *DockerCli) GetAllCredentials() (map[string]types.AuthConfig, error) {
auths := make(map[string]types.AuthConfig)
for registry := range cli.configFile.CredentialHelpers {
helper := cli.CredentialsStore(registry)
newAuths, err := helper.GetAll()
if err != nil {
return nil, err
}
addAll(auths, newAuths)
}
defaultStore := cli.CredentialsStore("")
newAuths, err := defaultStore.GetAll()
if err != nil {
return nil, err
}
addAll(auths, newAuths)
return auths, nil
}

func addAll(to, from map[string]types.AuthConfig) {
for reg, ac := range from {
to[reg] = ac
}
}

// CredentialsStore returns a new credentials store based
// on the settings provided in the configuration file. Empty string returns
// the default credential store.
func (cli *DockerCli) CredentialsStore(serverAddress string) credentials.Store {
if helper := getConfiguredCredentialStore(cli.configFile, serverAddress); helper != "" {
return credentials.NewNativeStore(cli.configFile, helper)
}
return credentials.NewFileStore(cli.configFile)
}

// getConfiguredCredentialStore returns the credential helper configured for the
// given registry, the default credsStore, or the empty string if neither are
// configured.
func getConfiguredCredentialStore(c *configfile.ConfigFile, serverAddress string) string {
if c.CredentialHelpers != nil && serverAddress != "" {
if helper, exists := c.CredentialHelpers[serverAddress]; exists {
return helper
}
}
return c.CredentialsStore
}

// Initialize the dockerCli runs initialization that must happen after command
// line flags are parsed.
func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error {
cli.configFile = LoadDefaultConfigFile(cli.err)
cli.configFile = cliconfig.LoadDefaultConfigFile(cli.err)

var err error
cli.client, err = NewAPIClientFromFlags(opts.Common, cli.configFile)
Expand Down Expand Up @@ -213,19 +161,6 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer) *DockerCli {
return &DockerCli{in: NewInStream(in), out: NewOutStream(out), err: err}
}

// LoadDefaultConfigFile attempts to load the default config file and returns
// an initialized ConfigFile struct if none is found.
func LoadDefaultConfigFile(err io.Writer) *configfile.ConfigFile {
configFile, e := cliconfig.Load(cliconfig.Dir())
if e != nil {
fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e)
}
if !configFile.ContainsAuth() {
credentials.DetectDefaultStore(configFile)
}
return configFile
}

// NewAPIClientFromFlags creates a new APIClient from command line flags
func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
host, err := getServerHost(opts.Hosts, opts.TLSOptions)
Expand Down
9 changes: 5 additions & 4 deletions cli/command/image/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (o buildOptions) contextFromStdin() bool {
}

// NewBuildCommand creates a new `docker build` command
func NewBuildCommand(dockerCli *command.DockerCli) *cobra.Command {
func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
ulimits := make(map[string]*units.Ulimit)
options := buildOptions{
tags: opts.NewListOpts(validateTag),
Expand Down Expand Up @@ -159,7 +159,7 @@ func (out *lastProgressOutput) WriteProgress(prog progress.Progress) error {
}

// nolint: gocyclo
func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
func runBuild(dockerCli command.Cli, options buildOptions) error {
var (
buildCtx io.ReadCloser
dockerfileCtx io.ReadCloser
Expand Down Expand Up @@ -336,7 +336,8 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
body = buildCtx
}

authConfigs, _ := dockerCli.GetAllCredentials()
configFile := dockerCli.ConfigFile()
authConfigs, _ := configFile.GetAllCredentials()
buildOptions := types.ImageBuildOptions{
Memory: options.memory.Value(),
MemorySwap: options.memorySwap.Value(),
Expand All @@ -356,7 +357,7 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
Dockerfile: relDockerfile,
ShmSize: options.shmSize.Value(),
Ulimits: options.ulimits.GetList(),
BuildArgs: dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(), options.buildArgs.GetAll()),
BuildArgs: configFile.ParseProxyConfig(dockerCli.Client().DaemonHost(), options.buildArgs.GetAll()),
AuthConfigs: authConfigs,
Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()),
CacheFrom: options.cacheFrom,
Expand Down
4 changes: 2 additions & 2 deletions cli/command/image/build_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ import (

const clientSessionRemote = "client-session"

func isSessionSupported(dockerCli *command.DockerCli) bool {
func isSessionSupported(dockerCli command.Cli) bool {
return dockerCli.ServerInfo().HasExperimental && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.31")
}

func trySession(dockerCli *command.DockerCli, contextDir string) (*session.Session, error) {
func trySession(dockerCli command.Cli, contextDir string) (*session.Session, error) {
var s *session.Session
if isSessionSupported(dockerCli) {
sharedKey, err := getBuildSharedKey(contextDir)
Expand Down
3 changes: 1 addition & 2 deletions cli/command/image/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import (
)

// NewImageCommand returns a cobra command for `image` subcommands
// nolint: interfacer
func NewImageCommand(dockerCli *command.DockerCli) *cobra.Command {
func NewImageCommand(dockerCli command.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "image",
Short: "Manage images",
Expand Down
9 changes: 7 additions & 2 deletions cli/command/image/pull_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io/ioutil"
"testing"

"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/internal/test"
"github.com/docker/docker/pkg/testutil"
"github.com/docker/docker/pkg/testutil/golden"
Expand Down Expand Up @@ -41,7 +42,9 @@ func TestNewPullCommandErrors(t *testing.T) {
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewPullCommand(test.NewFakeCli(&fakeClient{}, buf))
cli := test.NewFakeCli(&fakeClient{}, buf)
cli.SetConfigfile(configfile.New("filename"))
cmd := NewPullCommand(cli)
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
Expand All @@ -64,7 +67,9 @@ func TestNewPullCommandSuccess(t *testing.T) {
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewPullCommand(test.NewFakeCli(&fakeClient{}, buf))
cli := test.NewFakeCli(&fakeClient{}, buf)
cli.SetConfigfile(configfile.New("filename"))
cmd := NewPullCommand(cli)
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
err := cmd.Execute()
Expand Down
11 changes: 8 additions & 3 deletions cli/command/image/push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"
"testing"

"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/testutil"
Expand Down Expand Up @@ -47,7 +48,9 @@ func TestNewPushCommandErrors(t *testing.T) {
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewPushCommand(test.NewFakeCli(&fakeClient{imagePushFunc: tc.imagePushFunc}, buf))
cli := test.NewFakeCli(&fakeClient{imagePushFunc: tc.imagePushFunc}, buf)
cli.SetConfigfile(configfile.New("filename"))
cmd := NewPushCommand(cli)
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
Expand All @@ -66,11 +69,13 @@ func TestNewPushCommandSuccess(t *testing.T) {
}
for _, tc := range testCases {
buf := new(bytes.Buffer)
cmd := NewPushCommand(test.NewFakeCli(&fakeClient{
cli := test.NewFakeCli(&fakeClient{
imagePushFunc: func(ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
return ioutil.NopCloser(strings.NewReader("")), nil
},
}, buf))
}, buf)
cli.SetConfigfile(configfile.New("filename"))
cmd := NewPushCommand(cli)
cmd.SetOutput(ioutil.Discard)
cmd.SetArgs(tc.args)
assert.NoError(t, cmd.Execute())
Expand Down
4 changes: 2 additions & 2 deletions cli/command/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func ResolveAuthConfig(ctx context.Context, cli Cli, index *registrytypes.IndexI
configKey = ElectAuthServer(ctx, cli)
}

a, _ := cli.CredentialsStore(configKey).Get(configKey)
a, _ := cli.ConfigFile().GetAuthConfig(configKey)
return a
}

Expand All @@ -85,7 +85,7 @@ func ConfigureAuth(cli Cli, flUser, flPassword, serverAddress string, isDefaultR
serverAddress = registry.ConvertToHostname(serverAddress)
}

authconfig, err := cli.CredentialsStore(serverAddress).Get(serverAddress)
authconfig, err := cli.ConfigFile().GetAuthConfig(serverAddress)
if err != nil {
return authconfig, err
}
Expand Down
2 changes: 1 addition & 1 deletion cli/command/registry/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func runLogin(dockerCli command.Cli, opts loginOptions) error {
authConfig.Password = ""
authConfig.IdentityToken = response.IdentityToken
}
if err := dockerCli.CredentialsStore(serverAddress).Store(authConfig); err != nil {
if err := dockerCli.ConfigFile().GetCredentialsStore(serverAddress).Store(authConfig); err != nil {
return errors.Errorf("Error saving credentials: %v", err)
}

Expand Down
2 changes: 1 addition & 1 deletion cli/command/registry/logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func runLogout(dockerCli command.Cli, serverAddress string) error {

fmt.Fprintf(dockerCli.Out(), "Removing login credentials for %s\n", hostnameAddress)
for _, r := range regsToLogout {
if err := dockerCli.CredentialsStore(r).Erase(r); err != nil {
if err := dockerCli.ConfigFile().GetCredentialsStore(r).Erase(r); err != nil {
fmt.Fprintf(dockerCli.Err(), "WARNING: could not erase credentials: %v\n", err)
}
}
Expand Down
50 changes: 25 additions & 25 deletions cli/config/config.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package config

import (
"fmt"
"io"
"os"
"path/filepath"

"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/config/credentials"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/homedir"
"github.com/pkg/errors"
Expand Down Expand Up @@ -38,15 +40,6 @@ func SetDir(dir string) {
configDir = dir
}

// NewConfigFile initializes an empty configuration file for the given filename 'fn'
func NewConfigFile(fn string) *configfile.ConfigFile {
return &configfile.ConfigFile{
AuthConfigs: make(map[string]types.AuthConfig),
HTTPHeaders: make(map[string]string),
Filename: fn,
}
}

// LegacyLoadFromReader is a convenience function that creates a ConfigFile object from
// a non-nested reader
func LegacyLoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
Expand Down Expand Up @@ -75,46 +68,53 @@ func Load(configDir string) (*configfile.ConfigFile, error) {
configDir = Dir()
}

configFile := configfile.ConfigFile{
AuthConfigs: make(map[string]types.AuthConfig),
Filename: filepath.Join(configDir, ConfigFileName),
}
filename := filepath.Join(configDir, ConfigFileName)
configFile := configfile.New(filename)

// Try happy path first - latest config file
if _, err := os.Stat(configFile.Filename); err == nil {
file, err := os.Open(configFile.Filename)
if _, err := os.Stat(filename); err == nil {
file, err := os.Open(filename)
if err != nil {
return &configFile, errors.Errorf("%s - %v", configFile.Filename, err)
return configFile, errors.Errorf("%s - %v", filename, err)
}
defer file.Close()
err = configFile.LoadFromReader(file)
if err != nil {
err = errors.Errorf("%s - %v", configFile.Filename, err)
err = errors.Errorf("%s - %v", filename, err)
}
return &configFile, err
return configFile, err
} else if !os.IsNotExist(err) {
// if file is there but we can't stat it for any reason other
// than it doesn't exist then stop
return &configFile, errors.Errorf("%s - %v", configFile.Filename, err)
return configFile, errors.Errorf("%s - %v", filename, err)
}

// Can't find latest config file so check for the old one
confFile := filepath.Join(homedir.Get(), oldConfigfile)
if _, err := os.Stat(confFile); err != nil {
return &configFile, nil //missing file is not an error
return configFile, nil //missing file is not an error
}
file, err := os.Open(confFile)
if err != nil {
return &configFile, errors.Errorf("%s - %v", confFile, err)
return configFile, errors.Errorf("%s - %v", confFile, err)
}
defer file.Close()
err = configFile.LegacyLoadFromReader(file)
if err != nil {
return &configFile, errors.Errorf("%s - %v", confFile, err)
return configFile, errors.Errorf("%s - %v", confFile, err)
}
return configFile, nil
}

if configFile.HTTPHeaders == nil {
configFile.HTTPHeaders = map[string]string{}
// LoadDefaultConfigFile attempts to load the default config file and returns
// an initialized ConfigFile struct if none is found.
func LoadDefaultConfigFile(stderr io.Writer) *configfile.ConfigFile {
configFile, err := Load(Dir())
if err != nil {
fmt.Fprintf(stderr, "WARNING: Error loading config file: %v\n", err)
}
if !configFile.ContainsAuth() {
configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore)
}
return &configFile, nil
return configFile
}
Loading

0 comments on commit 74af31b

Please sign in to comment.