2017-08-18 12:49:23 -04:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
2017-12-20 09:04:41 -05:00
|
|
|
"crypto/x509"
|
2017-08-18 12:49:23 -04:00
|
|
|
"os"
|
2018-02-27 10:54:36 -05:00
|
|
|
"runtime"
|
2017-08-18 12:49:23 -04:00
|
|
|
"testing"
|
|
|
|
|
2017-12-20 09:04:41 -05:00
|
|
|
cliconfig "github.com/docker/cli/cli/config"
|
2017-08-18 12:49:23 -04:00
|
|
|
"github.com/docker/cli/cli/config/configfile"
|
|
|
|
"github.com/docker/cli/cli/flags"
|
|
|
|
"github.com/docker/docker/api"
|
2017-09-20 16:13:03 -04:00
|
|
|
"github.com/docker/docker/api/types"
|
2017-08-18 12:49:23 -04:00
|
|
|
"github.com/docker/docker/client"
|
2018-03-05 18:53:52 -05:00
|
|
|
"github.com/gotestyourself/gotestyourself/assert"
|
|
|
|
is "github.com/gotestyourself/gotestyourself/assert/cmp"
|
2018-04-23 08:13:52 -04:00
|
|
|
"github.com/gotestyourself/gotestyourself/env"
|
2017-12-20 09:04:41 -05:00
|
|
|
"github.com/gotestyourself/gotestyourself/fs"
|
2017-09-20 16:13:03 -04:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
"golang.org/x/net/context"
|
2017-08-18 12:49:23 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestNewAPIClientFromFlags(t *testing.T) {
|
|
|
|
host := "unix://path"
|
2018-02-27 10:54:36 -05:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
host = "npipe://./"
|
|
|
|
}
|
2017-08-18 12:49:23 -04:00
|
|
|
opts := &flags.CommonOptions{Hosts: []string{host}}
|
|
|
|
configFile := &configfile.ConfigFile{
|
|
|
|
HTTPHeaders: map[string]string{
|
|
|
|
"My-Header": "Custom-Value",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
apiclient, err := NewAPIClientFromFlags(opts, configFile)
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(host, apiclient.DaemonHost()))
|
2017-08-18 12:49:23 -04:00
|
|
|
|
|
|
|
expectedHeaders := map[string]string{
|
|
|
|
"My-Header": "Custom-Value",
|
|
|
|
"User-Agent": UserAgent(),
|
|
|
|
}
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.DeepEqual(expectedHeaders, apiclient.(*client.Client).CustomHTTPHeaders()))
|
|
|
|
assert.Check(t, is.Equal(api.DefaultVersion, apiclient.ClientVersion()))
|
2017-08-18 12:49:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) {
|
|
|
|
customVersion := "v3.3.3"
|
2018-04-23 08:13:52 -04:00
|
|
|
defer env.Patch(t, "DOCKER_API_VERSION", customVersion)()
|
2017-08-18 12:49:23 -04:00
|
|
|
|
|
|
|
opts := &flags.CommonOptions{}
|
|
|
|
configFile := &configfile.ConfigFile{}
|
|
|
|
apiclient, err := NewAPIClientFromFlags(opts, configFile)
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(customVersion, apiclient.ClientVersion()))
|
2017-08-18 12:49:23 -04:00
|
|
|
}
|
|
|
|
|
2017-09-20 16:13:03 -04:00
|
|
|
type fakeClient struct {
|
|
|
|
client.Client
|
|
|
|
pingFunc func() (types.Ping, error)
|
|
|
|
version string
|
|
|
|
negotiated bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *fakeClient) Ping(_ context.Context) (types.Ping, error) {
|
|
|
|
return c.pingFunc()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *fakeClient) ClientVersion() string {
|
|
|
|
return c.version
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *fakeClient) NegotiateAPIVersionPing(types.Ping) {
|
|
|
|
c.negotiated = true
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInitializeFromClient(t *testing.T) {
|
|
|
|
defaultVersion := "v1.55"
|
|
|
|
|
|
|
|
var testcases = []struct {
|
|
|
|
doc string
|
|
|
|
pingFunc func() (types.Ping, error)
|
|
|
|
expectedServer ServerInfo
|
|
|
|
negotiated bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
doc: "successful ping",
|
|
|
|
pingFunc: func() (types.Ping, error) {
|
|
|
|
return types.Ping{Experimental: true, OSType: "linux", APIVersion: "v1.30"}, nil
|
|
|
|
},
|
|
|
|
expectedServer: ServerInfo{HasExperimental: true, OSType: "linux"},
|
|
|
|
negotiated: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "failed ping, no API version",
|
|
|
|
pingFunc: func() (types.Ping, error) {
|
|
|
|
return types.Ping{}, errors.New("failed")
|
|
|
|
},
|
|
|
|
expectedServer: ServerInfo{HasExperimental: true},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "failed ping, with API version",
|
|
|
|
pingFunc: func() (types.Ping, error) {
|
|
|
|
return types.Ping{APIVersion: "v1.33"}, errors.New("failed")
|
|
|
|
},
|
|
|
|
expectedServer: ServerInfo{HasExperimental: true},
|
|
|
|
negotiated: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, testcase := range testcases {
|
|
|
|
t.Run(testcase.doc, func(t *testing.T) {
|
|
|
|
apiclient := &fakeClient{
|
|
|
|
pingFunc: testcase.pingFunc,
|
|
|
|
version: defaultVersion,
|
|
|
|
}
|
|
|
|
|
|
|
|
cli := &DockerCli{client: apiclient}
|
|
|
|
cli.initializeFromClient()
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.DeepEqual(testcase.expectedServer, cli.serverInfo))
|
|
|
|
assert.Check(t, is.Equal(testcase.negotiated, apiclient.negotiated))
|
2017-09-20 16:13:03 -04:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-20 09:04:41 -05:00
|
|
|
func TestExperimentalCLI(t *testing.T) {
|
|
|
|
defaultVersion := "v1.55"
|
|
|
|
|
|
|
|
var testcases = []struct {
|
|
|
|
doc string
|
|
|
|
configfile string
|
|
|
|
expectedExperimentalCLI bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
doc: "default",
|
|
|
|
configfile: `{}`,
|
|
|
|
expectedExperimentalCLI: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "experimental",
|
|
|
|
configfile: `{
|
|
|
|
"experimental": "enabled"
|
|
|
|
}`,
|
|
|
|
expectedExperimentalCLI: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, testcase := range testcases {
|
|
|
|
t.Run(testcase.doc, func(t *testing.T) {
|
|
|
|
dir := fs.NewDir(t, testcase.doc, fs.WithFile("config.json", testcase.configfile))
|
|
|
|
defer dir.Remove()
|
|
|
|
apiclient := &fakeClient{
|
|
|
|
version: defaultVersion,
|
|
|
|
}
|
|
|
|
|
|
|
|
cli := &DockerCli{client: apiclient, err: os.Stderr}
|
|
|
|
cliconfig.SetDir(dir.Path())
|
|
|
|
err := cli.Initialize(flags.NewClientOptions())
|
2018-03-06 14:44:13 -05:00
|
|
|
assert.NilError(t, err)
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.Equal(testcase.expectedExperimentalCLI, cli.ClientInfo().HasExperimental))
|
2017-12-20 09:04:41 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-26 09:40:17 -05:00
|
|
|
func TestOrchestratorSwitch(t *testing.T) {
|
2018-01-02 17:56:07 -05:00
|
|
|
defaultVersion := "v0.00"
|
2017-12-26 09:40:17 -05:00
|
|
|
|
|
|
|
var testcases = []struct {
|
|
|
|
doc string
|
|
|
|
configfile string
|
|
|
|
envOrchestrator string
|
|
|
|
flagOrchestrator string
|
|
|
|
expectedOrchestrator string
|
|
|
|
expectedKubernetes bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
doc: "default",
|
|
|
|
configfile: `{
|
|
|
|
"experimental": "enabled"
|
|
|
|
}`,
|
|
|
|
expectedOrchestrator: "swarm",
|
|
|
|
expectedKubernetes: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "kubernetesIsExperimental",
|
|
|
|
configfile: `{
|
|
|
|
"experimental": "disabled",
|
|
|
|
"orchestrator": "kubernetes"
|
|
|
|
}`,
|
|
|
|
envOrchestrator: "kubernetes",
|
|
|
|
flagOrchestrator: "kubernetes",
|
|
|
|
expectedOrchestrator: "swarm",
|
|
|
|
expectedKubernetes: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "kubernetesConfigFile",
|
|
|
|
configfile: `{
|
|
|
|
"experimental": "enabled",
|
|
|
|
"orchestrator": "kubernetes"
|
|
|
|
}`,
|
|
|
|
expectedOrchestrator: "kubernetes",
|
|
|
|
expectedKubernetes: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "kubernetesEnv",
|
|
|
|
configfile: `{
|
|
|
|
"experimental": "enabled"
|
|
|
|
}`,
|
|
|
|
envOrchestrator: "kubernetes",
|
|
|
|
expectedOrchestrator: "kubernetes",
|
|
|
|
expectedKubernetes: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "kubernetesFlag",
|
|
|
|
configfile: `{
|
|
|
|
"experimental": "enabled"
|
|
|
|
}`,
|
|
|
|
flagOrchestrator: "kubernetes",
|
|
|
|
expectedOrchestrator: "kubernetes",
|
|
|
|
expectedKubernetes: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "envOverridesConfigFile",
|
|
|
|
configfile: `{
|
|
|
|
"experimental": "enabled",
|
|
|
|
"orchestrator": "kubernetes"
|
|
|
|
}`,
|
|
|
|
envOrchestrator: "swarm",
|
|
|
|
expectedOrchestrator: "swarm",
|
|
|
|
expectedKubernetes: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "flagOverridesEnv",
|
|
|
|
configfile: `{
|
|
|
|
"experimental": "enabled"
|
|
|
|
}`,
|
|
|
|
envOrchestrator: "kubernetes",
|
|
|
|
flagOrchestrator: "swarm",
|
|
|
|
expectedOrchestrator: "swarm",
|
|
|
|
expectedKubernetes: false,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, testcase := range testcases {
|
|
|
|
t.Run(testcase.doc, func(t *testing.T) {
|
|
|
|
dir := fs.NewDir(t, testcase.doc, fs.WithFile("config.json", testcase.configfile))
|
|
|
|
defer dir.Remove()
|
|
|
|
apiclient := &fakeClient{
|
|
|
|
version: defaultVersion,
|
|
|
|
}
|
|
|
|
if testcase.envOrchestrator != "" {
|
2018-04-23 08:13:52 -04:00
|
|
|
defer env.Patch(t, "DOCKER_ORCHESTRATOR", testcase.envOrchestrator)()
|
2017-12-26 09:40:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
cli := &DockerCli{client: apiclient, err: os.Stderr}
|
|
|
|
cliconfig.SetDir(dir.Path())
|
|
|
|
options := flags.NewClientOptions()
|
|
|
|
if testcase.flagOrchestrator != "" {
|
|
|
|
options.Common.Orchestrator = testcase.flagOrchestrator
|
|
|
|
}
|
|
|
|
err := cli.Initialize(options)
|
2018-03-06 14:44:13 -05:00
|
|
|
assert.NilError(t, err)
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.Equal(testcase.expectedKubernetes, cli.ClientInfo().HasKubernetes()))
|
|
|
|
assert.Check(t, is.Equal(testcase.expectedOrchestrator, string(cli.ClientInfo().Orchestrator)))
|
2017-12-26 09:40:17 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-20 16:13:03 -04:00
|
|
|
func TestGetClientWithPassword(t *testing.T) {
|
|
|
|
expected := "password"
|
|
|
|
|
|
|
|
var testcases = []struct {
|
|
|
|
doc string
|
|
|
|
password string
|
|
|
|
retrieverErr error
|
|
|
|
retrieverGiveup bool
|
|
|
|
newClientErr error
|
|
|
|
expectedErr string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
doc: "successful connect",
|
|
|
|
password: expected,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "password retriever exhausted",
|
|
|
|
retrieverGiveup: true,
|
|
|
|
retrieverErr: errors.New("failed"),
|
|
|
|
expectedErr: "private key is encrypted, but could not get passphrase",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "password retriever error",
|
|
|
|
retrieverErr: errors.New("failed"),
|
|
|
|
expectedErr: "failed",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
doc: "newClient error",
|
|
|
|
newClientErr: errors.New("failed to connect"),
|
|
|
|
expectedErr: "failed to connect",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, testcase := range testcases {
|
|
|
|
t.Run(testcase.doc, func(t *testing.T) {
|
|
|
|
passRetriever := func(_, _ string, _ bool, attempts int) (passphrase string, giveup bool, err error) {
|
|
|
|
// Always return an invalid pass first to test iteration
|
|
|
|
switch attempts {
|
|
|
|
case 0:
|
|
|
|
return "something else", false, nil
|
|
|
|
default:
|
|
|
|
return testcase.password, testcase.retrieverGiveup, testcase.retrieverErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newClient := func(currentPassword string) (client.APIClient, error) {
|
|
|
|
if testcase.newClientErr != nil {
|
|
|
|
return nil, testcase.newClientErr
|
|
|
|
}
|
|
|
|
if currentPassword == expected {
|
|
|
|
return &client.Client{}, nil
|
|
|
|
}
|
|
|
|
return &client.Client{}, x509.IncorrectPasswordError
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := getClientWithPassword(passRetriever, newClient)
|
|
|
|
if testcase.expectedErr != "" {
|
2018-03-06 14:03:47 -05:00
|
|
|
assert.ErrorContains(t, err, testcase.expectedErr)
|
2017-09-20 16:13:03 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-03-06 14:44:13 -05:00
|
|
|
assert.NilError(t, err)
|
2017-09-20 16:13:03 -04:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|