From 3398b05230ad9c367148a3d06de622f1561bf10b Mon Sep 17 00:00:00 2001 From: Jean-Pierre Huynh Date: Fri, 21 Jul 2017 11:53:05 +0100 Subject: [PATCH 1/7] Update event filter zsh completion with `disable`, `enable`, `install`, and `remove` Signed-off-by: Jean-Pierre Huynh Upstream-commit: d31df921ad4e217c2d7db26a036da99f4be2af46 Component: cli --- components/cli/contrib/completion/zsh/_docker | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/cli/contrib/completion/zsh/_docker b/components/cli/contrib/completion/zsh/_docker index 82420821f4..906e8c7471 100644 --- a/components/cli/contrib/completion/zsh/_docker +++ b/components/cli/contrib/completion/zsh/_docker @@ -450,9 +450,9 @@ __docker_complete_events_filter() { ;; (event) local -a event_opts - event_opts=('attach' 'commit' 'connect' 'copy' 'create' 'delete' 'destroy' 'detach' 'die' 'disconnect' 'exec_create' 'exec_detach' - 'exec_start' 'export' 'health_status' 'import' 'kill' 'load' 'mount' 'oom' 'pause' 'pull' 'push' 'reload' 'rename' 'resize' 'restart' 'save' 'start' - 'stop' 'tag' 'top' 'unmount' 'unpause' 'untag' 'update') + event_opts=('attach' 'commit' 'connect' 'copy' 'create' 'delete' 'destroy' 'detach' 'die' 'disable' 'disconnect' 'enable' 'exec_create' 'exec_detach' + 'exec_start' 'export' 'health_status' 'import' 'install' 'kill' 'load' 'mount' 'oom' 'pause' 'pull' 'push' 'reload' 'remove' 'rename' 'resize' + 'restart' 'save' 'start' 'stop' 'tag' 'top' 'unmount' 'unpause' 'untag' 'update') _describe -t event-filter-opts "event filter options" event_opts && ret=0 ;; (image) From 6d1342e8058ccb3ce3371a4a0a8dbd78657ae561 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 27 Sep 2017 10:46:32 +0200 Subject: [PATCH 2/7] Fix leading characters being stripped from example sections in YAML `strings.Trim()` strips any character listed in the `cutset` argument, so any example section having `E`, `x`, `a`, `m`, `p`, `l`, `e`, or `s` in the first word, had these characters missing in the generated YAML. Also trim superfluent whitespace characters to consistently use `|-` ("strip") as block chomping indicator (see http://www.yaml.org/spec/1.2/spec.html#id2794534) Signed-off-by: Sebastiaan van Stijn Upstream-commit: 1f605d43ca4730b0387cc439a9de493d94531703 Component: cli --- components/cli/docs/yaml/yaml.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/cli/docs/yaml/yaml.go b/components/cli/docs/yaml/yaml.go index 2dbe406bdb..42c46ec2f0 100644 --- a/components/cli/docs/yaml/yaml.go +++ b/components/cli/docs/yaml/yaml.go @@ -220,10 +220,10 @@ func parseMDContent(mdString string) (description string, examples string) { parsedContent := strings.Split(mdString, "\n## ") for _, s := range parsedContent { if strings.Index(s, "Description") == 0 { - description = strings.Trim(s, "Description\n") + description = strings.TrimSpace(strings.TrimPrefix(s, "Description")) } if strings.Index(s, "Examples") == 0 { - examples = strings.Trim(s, "Examples\n") + examples = strings.TrimSpace(strings.TrimPrefix(s, "Examples")) } } return From d873e90bed4b10b2707927477e5f9521fe7bccfa Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 15 Sep 2017 13:39:55 +0200 Subject: [PATCH 3/7] Update gitignore Adding some lines from the Moby gitignore Signed-off-by: Sebastiaan van Stijn Upstream-commit: cdaf6f44ba7292ba259afc2ce893c9375cd4ce5c Component: cli --- components/cli/.gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/cli/.gitignore b/components/cli/.gitignore index fa99439ac8..59cec00b21 100644 --- a/components/cli/.gitignore +++ b/components/cli/.gitignore @@ -1,4 +1,12 @@ +# if you want to ignore files created by your editor/tools, +# please consider a global .gitignore https://help.github.com/articles/ignoring-files +*.exe +*.exe~ +*.orig +.*.swp .DS_Store +Thumbs.db +.editorconfig /build/ cli/winresources/rsrc_386.syso cli/winresources/rsrc_amd64.syso From ac13dc6ce4e025adbb426d3b8e61b2a11196eb6a Mon Sep 17 00:00:00 2001 From: Jake Sanders Date: Fri, 26 Jan 2018 14:38:04 -0800 Subject: [PATCH 4/7] Use Get rather than GetAll to retrieve credentials from credential helpers. Signed-off-by: Jake Sanders Upstream-commit: 1d0a37c4600650cc98fc664005d533fffedd8cb8 Component: cli --- components/cli/cli/config/configfile/file.go | 38 +-- .../cli/cli/config/configfile/file_test.go | 216 +++++++++++++++++- 2 files changed, 237 insertions(+), 17 deletions(-) diff --git a/components/cli/cli/config/configfile/file.go b/components/cli/cli/config/configfile/file.go index 4f1dc1ae5b..f513144bdc 100644 --- a/components/cli/cli/config/configfile/file.go +++ b/components/cli/cli/config/configfile/file.go @@ -251,24 +251,29 @@ func decodeAuth(authStr string) (string, string, error) { // GetCredentialsStore returns a new credentials store from the settings in the // configuration file -func (configFile *ConfigFile) GetCredentialsStore(serverAddress string) credentials.Store { - if helper := getConfiguredCredentialStore(configFile, serverAddress); helper != "" { - return credentials.NewNativeStore(configFile, helper) +func (configFile *ConfigFile) GetCredentialsStore(registryHostname string) credentials.Store { + if helper := getConfiguredCredentialStore(configFile, registryHostname); helper != "" { + return newNativeStore(configFile, helper) } return credentials.NewFileStore(configFile) } +// var for unit testing. +var newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { + return credentials.NewNativeStore(configFile, helperSuffix) +} + // GetAuthConfig for a repository from the credential store -func (configFile *ConfigFile) GetAuthConfig(serverAddress string) (types.AuthConfig, error) { - return configFile.GetCredentialsStore(serverAddress).Get(serverAddress) +func (configFile *ConfigFile) GetAuthConfig(registryHostname string) (types.AuthConfig, error) { + return configFile.GetCredentialsStore(registryHostname).Get(registryHostname) } // 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, serverAddress string) string { - if c.CredentialHelpers != nil && serverAddress != "" { - if helper, exists := c.CredentialHelpers[serverAddress]; exists { +func getConfiguredCredentialStore(c *ConfigFile, registryHostname string) string { + if c.CredentialHelpers != nil && registryHostname != "" { + if helper, exists := c.CredentialHelpers[registryHostname]; exists { return helper } } @@ -285,19 +290,20 @@ func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, } } - for registry := range configFile.CredentialHelpers { - helper := configFile.GetCredentialsStore(registry) - newAuths, err := helper.GetAll() - if err != nil { - return nil, err - } - addAll(newAuths) - } defaultStore := configFile.GetCredentialsStore("") newAuths, err := defaultStore.GetAll() if err != nil { return nil, err } addAll(newAuths) + + // Auth configs from a registry-specific helper should override those from the default store. + for registryHostname := range configFile.CredentialHelpers { + newAuth, err := configFile.GetAuthConfig(registryHostname) + if err != nil { + return nil, err + } + auths[registryHostname] = newAuth + } return auths, nil } diff --git a/components/cli/cli/config/configfile/file_test.go b/components/cli/cli/config/configfile/file_test.go index f2a61b1794..75df18c49a 100644 --- a/components/cli/cli/config/configfile/file_test.go +++ b/components/cli/cli/config/configfile/file_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "github.com/docker/cli/cli/config/credentials" "github.com/docker/docker/api/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -142,7 +143,37 @@ func TestConfigFile(t *testing.T) { assert.Equal(t, configFilename, configFile.Filename) } -func TestGetAllCredentials(t *testing.T) { +type mockNativeStore struct { + GetAllCallCount int + authConfigs map[string]types.AuthConfig +} + +func (c *mockNativeStore) Erase(registryHostname string) error { + delete(c.authConfigs, registryHostname) + return nil +} + +func (c *mockNativeStore) Get(registryHostname string) (types.AuthConfig, error) { + return c.authConfigs[registryHostname], nil +} + +func (c *mockNativeStore) GetAll() (map[string]types.AuthConfig, error) { + c.GetAllCallCount = c.GetAllCallCount + 1 + return c.authConfigs, nil +} + +func (c *mockNativeStore) Store(authConfig types.AuthConfig) error { + return nil +} + +// make sure it satisfies the interface +var _ credentials.Store = (*mockNativeStore)(nil) + +func NewMockNativeStore(authConfigs map[string]types.AuthConfig) credentials.Store { + return &mockNativeStore{authConfigs: authConfigs} +} + +func TestGetAllCredentialsFileStoreOnly(t *testing.T) { configFile := New("filename") exampleAuth := types.AuthConfig{ Username: "user", @@ -157,3 +188,186 @@ func TestGetAllCredentials(t *testing.T) { expected["example.com/foo"] = exampleAuth assert.Equal(t, expected, authConfigs) } + +func TestGetAllCredentialsCredsStore(t *testing.T) { + configFile := New("filename") + configFile.CredentialsStore = "test_creds_store" + testRegistryHostname := "example.com" + expectedAuth := types.AuthConfig{ + Username: "user", + Password: "pass", + } + + testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedAuth}) + + tmpNewNativeStore := newNativeStore + defer func() { newNativeStore = tmpNewNativeStore }() + newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { + return testCredsStore + } + + authConfigs, err := configFile.GetAllCredentials() + require.NoError(t, err) + + expected := make(map[string]types.AuthConfig) + expected[testRegistryHostname] = expectedAuth + assert.Equal(t, expected, authConfigs) + assert.Equal(t, 1, testCredsStore.(*mockNativeStore).GetAllCallCount) +} + +func TestGetAllCredentialsCredHelper(t *testing.T) { + testCredHelperSuffix := "test_cred_helper" + testCredHelperRegistryHostname := "credhelper.com" + testExtraCredHelperRegistryHostname := "somethingweird.com" + + unexpectedCredHelperAuth := types.AuthConfig{ + Username: "file_store_user", + Password: "file_store_pass", + } + expectedCredHelperAuth := types.AuthConfig{ + Username: "cred_helper_user", + Password: "cred_helper_pass", + } + + configFile := New("filename") + configFile.CredentialHelpers = map[string]string{testCredHelperRegistryHostname: testCredHelperSuffix} + + testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{ + testCredHelperRegistryHostname: expectedCredHelperAuth, + // Add in an extra auth entry which doesn't appear in CredentialHelpers section of the configFile. + // This verifies that only explicitly configured registries are being requested from the cred helpers. + testExtraCredHelperRegistryHostname: unexpectedCredHelperAuth, + }) + + tmpNewNativeStore := newNativeStore + defer func() { newNativeStore = tmpNewNativeStore }() + newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { + return testCredHelper + } + + authConfigs, err := configFile.GetAllCredentials() + require.NoError(t, err) + + expected := make(map[string]types.AuthConfig) + expected[testCredHelperRegistryHostname] = expectedCredHelperAuth + assert.Equal(t, expected, authConfigs) + assert.Equal(t, 0, testCredHelper.(*mockNativeStore).GetAllCallCount) +} + +func TestGetAllCredentialsFileStoreAndCredHelper(t *testing.T) { + testFileStoreRegistryHostname := "example.com" + testCredHelperSuffix := "test_cred_helper" + testCredHelperRegistryHostname := "credhelper.com" + + expectedFileStoreAuth := types.AuthConfig{ + Username: "file_store_user", + Password: "file_store_pass", + } + expectedCredHelperAuth := types.AuthConfig{ + Username: "cred_helper_user", + Password: "cred_helper_pass", + } + + configFile := New("filename") + configFile.CredentialHelpers = map[string]string{testCredHelperRegistryHostname: testCredHelperSuffix} + configFile.AuthConfigs[testFileStoreRegistryHostname] = expectedFileStoreAuth + + testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth}) + + newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { + return testCredHelper + } + + tmpNewNativeStore := newNativeStore + defer func() { newNativeStore = tmpNewNativeStore }() + authConfigs, err := configFile.GetAllCredentials() + require.NoError(t, err) + + expected := make(map[string]types.AuthConfig) + expected[testFileStoreRegistryHostname] = expectedFileStoreAuth + expected[testCredHelperRegistryHostname] = expectedCredHelperAuth + assert.Equal(t, expected, authConfigs) + assert.Equal(t, 0, testCredHelper.(*mockNativeStore).GetAllCallCount) +} + +func TestGetAllCredentialsCredStoreAndCredHelper(t *testing.T) { + testCredStoreSuffix := "test_creds_store" + testCredStoreRegistryHostname := "credstore.com" + testCredHelperSuffix := "test_cred_helper" + testCredHelperRegistryHostname := "credhelper.com" + + configFile := New("filename") + configFile.CredentialsStore = testCredStoreSuffix + configFile.CredentialHelpers = map[string]string{testCredHelperRegistryHostname: testCredHelperSuffix} + + expectedCredStoreAuth := types.AuthConfig{ + Username: "cred_store_user", + Password: "cred_store_pass", + } + expectedCredHelperAuth := types.AuthConfig{ + Username: "cred_helper_user", + Password: "cred_helper_pass", + } + + testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth}) + testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testCredStoreRegistryHostname: expectedCredStoreAuth}) + + tmpNewNativeStore := newNativeStore + defer func() { newNativeStore = tmpNewNativeStore }() + newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { + if helperSuffix == testCredHelperSuffix { + return testCredHelper + } + return testCredsStore + } + + authConfigs, err := configFile.GetAllCredentials() + require.NoError(t, err) + + expected := make(map[string]types.AuthConfig) + expected[testCredStoreRegistryHostname] = expectedCredStoreAuth + expected[testCredHelperRegistryHostname] = expectedCredHelperAuth + assert.Equal(t, expected, authConfigs) + assert.Equal(t, 1, testCredsStore.(*mockNativeStore).GetAllCallCount) + assert.Equal(t, 0, testCredHelper.(*mockNativeStore).GetAllCallCount) +} + +func TestGetAllCredentialsCredHelperOverridesDefaultStore(t *testing.T) { + testCredStoreSuffix := "test_creds_store" + testCredHelperSuffix := "test_cred_helper" + testRegistryHostname := "example.com" + + configFile := New("filename") + configFile.CredentialsStore = testCredStoreSuffix + configFile.CredentialHelpers = map[string]string{testRegistryHostname: testCredHelperSuffix} + + unexpectedCredStoreAuth := types.AuthConfig{ + Username: "cred_store_user", + Password: "cred_store_pass", + } + expectedCredHelperAuth := types.AuthConfig{ + Username: "cred_helper_user", + Password: "cred_helper_pass", + } + + testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedCredHelperAuth}) + testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: unexpectedCredStoreAuth}) + + tmpNewNativeStore := newNativeStore + defer func() { newNativeStore = tmpNewNativeStore }() + newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { + if helperSuffix == testCredHelperSuffix { + return testCredHelper + } + return testCredsStore + } + + authConfigs, err := configFile.GetAllCredentials() + require.NoError(t, err) + + expected := make(map[string]types.AuthConfig) + expected[testRegistryHostname] = expectedCredHelperAuth + assert.Equal(t, expected, authConfigs) + assert.Equal(t, 1, testCredsStore.(*mockNativeStore).GetAllCallCount) + assert.Equal(t, 0, testCredHelper.(*mockNativeStore).GetAllCallCount) +} From cd579fc56567f64c0a9a02c080b1ac932591f047 Mon Sep 17 00:00:00 2001 From: Jake Sanders Date: Fri, 9 Feb 2018 13:48:01 -0800 Subject: [PATCH 5/7] defaultIndexserver -> defaultIndexServer Signed-off-by: Jake Sanders Upstream-commit: e1b607a3511dfe7259434de959cda87ec9e0bd34 Component: cli --- components/cli/cli/config/configfile/file.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/cli/cli/config/configfile/file.go b/components/cli/cli/config/configfile/file.go index f513144bdc..9338bc4e66 100644 --- a/components/cli/cli/config/configfile/file.go +++ b/components/cli/cli/config/configfile/file.go @@ -19,7 +19,7 @@ const ( // This constant is only used for really old config files when the // URL wasn't saved as part of the config file and it was just // assumed to be this value. - defaultIndexserver = "https://index.docker.io/v1/" + defaultIndexServer = "https://index.docker.io/v1/" ) // ConfigFile ~/.docker/config.json file info @@ -87,8 +87,8 @@ func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error { if err != nil { return err } - authConfig.ServerAddress = defaultIndexserver - configFile.AuthConfigs[defaultIndexserver] = authConfig + authConfig.ServerAddress = defaultIndexServer + configFile.AuthConfigs[defaultIndexServer] = authConfig } else { for k, authConfig := range configFile.AuthConfigs { authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth) From f2f9da377615a97e92894ede91f02418ea4caf4c Mon Sep 17 00:00:00 2001 From: Arash Deshmeh Date: Tue, 13 Feb 2018 05:38:14 -0500 Subject: [PATCH 6/7] add tests to plugin create/remove/enable/disable commands. Part of work on #37 Signed-off-by: Arash Deshmeh Upstream-commit: 0a914da708409514fe992a9bba3d6eec89ef81de Component: cli --- .../cli/cli/command/plugin/client_test.go | 45 +++++++ .../cli/cli/command/plugin/create_test.go | 114 ++++++++++++++++++ .../cli/cli/command/plugin/disable_test.go | 58 +++++++++ .../cli/cli/command/plugin/enable_test.go | 70 +++++++++++ .../cli/cli/command/plugin/remove_test.go | 71 +++++++++++ 5 files changed, 358 insertions(+) create mode 100644 components/cli/cli/command/plugin/client_test.go create mode 100644 components/cli/cli/command/plugin/create_test.go create mode 100644 components/cli/cli/command/plugin/disable_test.go create mode 100644 components/cli/cli/command/plugin/enable_test.go create mode 100644 components/cli/cli/command/plugin/remove_test.go diff --git a/components/cli/cli/command/plugin/client_test.go b/components/cli/cli/command/plugin/client_test.go new file mode 100644 index 0000000000..b543542764 --- /dev/null +++ b/components/cli/cli/command/plugin/client_test.go @@ -0,0 +1,45 @@ +package plugin + +import ( + "io" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + "golang.org/x/net/context" +) + +type fakeClient struct { + client.Client + pluginCreateFunc func(createContext io.Reader, createOptions types.PluginCreateOptions) error + pluginDisableFunc func(name string, disableOptions types.PluginDisableOptions) error + pluginEnableFunc func(name string, options types.PluginEnableOptions) error + pluginRemoveFunc func(name string, options types.PluginRemoveOptions) error +} + +func (c *fakeClient) PluginCreate(ctx context.Context, createContext io.Reader, createOptions types.PluginCreateOptions) error { + if c.pluginCreateFunc != nil { + return c.pluginCreateFunc(createContext, createOptions) + } + return nil +} + +func (c *fakeClient) PluginEnable(ctx context.Context, name string, enableOptions types.PluginEnableOptions) error { + if c.pluginEnableFunc != nil { + return c.pluginEnableFunc(name, enableOptions) + } + return nil +} + +func (c *fakeClient) PluginDisable(context context.Context, name string, disableOptions types.PluginDisableOptions) error { + if c.pluginDisableFunc != nil { + return c.pluginDisableFunc(name, disableOptions) + } + return nil +} + +func (c *fakeClient) PluginRemove(context context.Context, name string, removeOptions types.PluginRemoveOptions) error { + if c.pluginRemoveFunc != nil { + return c.pluginRemoveFunc(name, removeOptions) + } + return nil +} diff --git a/components/cli/cli/command/plugin/create_test.go b/components/cli/cli/command/plugin/create_test.go new file mode 100644 index 0000000000..739e4197eb --- /dev/null +++ b/components/cli/cli/command/plugin/create_test.go @@ -0,0 +1,114 @@ +package plugin + +import ( + "fmt" + "io" + "io/ioutil" + "testing" + + "github.com/docker/cli/internal/test" + "github.com/docker/cli/internal/test/testutil" + "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/fs" + "github.com/stretchr/testify/assert" +) + +func TestCreateErrors(t *testing.T) { + + testCases := []struct { + args []string + expectedError string + }{ + { + args: []string{}, + expectedError: "requires at least 2 arguments", + }, + { + args: []string{"INVALID_TAG", "context-dir"}, + expectedError: "invalid", + }, + { + args: []string{"plugin-foo", "nonexistent_context_dir"}, + expectedError: "no such file or directory", + }, + } + + for _, tc := range testCases { + cli := test.NewFakeCli(&fakeClient{}) + cmd := newCreateCommand(cli) + cmd.SetArgs(tc.args) + cmd.SetOutput(ioutil.Discard) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + } +} + +func TestCreateErrorOnFileAsContextDir(t *testing.T) { + tmpFile := fs.NewFile(t, "file-as-context-dir") + defer tmpFile.Remove() + + cli := test.NewFakeCli(&fakeClient{}) + cmd := newCreateCommand(cli) + cmd.SetArgs([]string{"plugin-foo", tmpFile.Path()}) + cmd.SetOutput(ioutil.Discard) + testutil.ErrorContains(t, cmd.Execute(), "context must be a directory") +} + +func TestCreateErrorOnContextDirWithoutConfig(t *testing.T) { + tmpDir := fs.NewDir(t, "plugin-create-test") + defer tmpDir.Remove() + + cli := test.NewFakeCli(&fakeClient{}) + cmd := newCreateCommand(cli) + cmd.SetArgs([]string{"plugin-foo", tmpDir.Path()}) + cmd.SetOutput(ioutil.Discard) + testutil.ErrorContains(t, cmd.Execute(), "config.json: no such file or directory") +} + +func TestCreateErrorOnInvalidConfig(t *testing.T) { + tmpDir := fs.NewDir(t, "plugin-create-test", + fs.WithDir("rootfs"), + fs.WithFile("config.json", "invalid-config-contents")) + defer tmpDir.Remove() + + cli := test.NewFakeCli(&fakeClient{}) + cmd := newCreateCommand(cli) + cmd.SetArgs([]string{"plugin-foo", tmpDir.Path()}) + cmd.SetOutput(ioutil.Discard) + testutil.ErrorContains(t, cmd.Execute(), "invalid") +} + +func TestCreateErrorFromDaemon(t *testing.T) { + tmpDir := fs.NewDir(t, "plugin-create-test", + fs.WithDir("rootfs"), + fs.WithFile("config.json", `{ "Name": "plugin-foo" }`)) + defer tmpDir.Remove() + + cli := test.NewFakeCli(&fakeClient{ + pluginCreateFunc: func(createContext io.Reader, createOptions types.PluginCreateOptions) error { + return fmt.Errorf("Error creating plugin") + }, + }) + + cmd := newCreateCommand(cli) + cmd.SetArgs([]string{"plugin-foo", tmpDir.Path()}) + cmd.SetOutput(ioutil.Discard) + testutil.ErrorContains(t, cmd.Execute(), "Error creating plugin") +} + +func TestCreatePlugin(t *testing.T) { + tmpDir := fs.NewDir(t, "plugin-create-test", + fs.WithDir("rootfs"), + fs.WithFile("config.json", `{ "Name": "plugin-foo" }`)) + defer tmpDir.Remove() + + cli := test.NewFakeCli(&fakeClient{ + pluginCreateFunc: func(createContext io.Reader, createOptions types.PluginCreateOptions) error { + return nil + }, + }) + + cmd := newCreateCommand(cli) + cmd.SetArgs([]string{"plugin-foo", tmpDir.Path()}) + assert.NoError(t, cmd.Execute()) + assert.Equal(t, "plugin-foo\n", cli.OutBuffer().String()) +} diff --git a/components/cli/cli/command/plugin/disable_test.go b/components/cli/cli/command/plugin/disable_test.go new file mode 100644 index 0000000000..96fce34646 --- /dev/null +++ b/components/cli/cli/command/plugin/disable_test.go @@ -0,0 +1,58 @@ +package plugin + +import ( + "fmt" + "io/ioutil" + "testing" + + "github.com/docker/cli/internal/test" + "github.com/docker/cli/internal/test/testutil" + "github.com/docker/docker/api/types" + "github.com/stretchr/testify/assert" +) + +func TestPluginDisableErrors(t *testing.T) { + testCases := []struct { + args []string + expectedError string + pluginDisableFunc func(name string, disableOptions types.PluginDisableOptions) error + }{ + { + args: []string{}, + expectedError: "requires exactly 1 argument", + }, + { + args: []string{"too", "many", "arguments"}, + expectedError: "requires exactly 1 argument", + }, + { + args: []string{"plugin-foo"}, + expectedError: "Error disabling plugin", + pluginDisableFunc: func(name string, disableOptions types.PluginDisableOptions) error { + return fmt.Errorf("Error disabling plugin") + }, + }, + } + + for _, tc := range testCases { + cmd := newDisableCommand( + test.NewFakeCli(&fakeClient{ + pluginDisableFunc: tc.pluginDisableFunc, + })) + cmd.SetArgs(tc.args) + cmd.SetOutput(ioutil.Discard) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + } +} + +func TestPluginDisable(t *testing.T) { + cli := test.NewFakeCli(&fakeClient{ + pluginDisableFunc: func(name string, disableOptions types.PluginDisableOptions) error { + return nil + }, + }) + cmd := newDisableCommand(cli) + cmd.SetArgs([]string{"plugin-foo"}) + assert.NoError(t, cmd.Execute()) + assert.Equal(t, "plugin-foo\n", cli.OutBuffer().String()) +} diff --git a/components/cli/cli/command/plugin/enable_test.go b/components/cli/cli/command/plugin/enable_test.go new file mode 100644 index 0000000000..68b50eae6f --- /dev/null +++ b/components/cli/cli/command/plugin/enable_test.go @@ -0,0 +1,70 @@ +package plugin + +import ( + "fmt" + "io/ioutil" + "testing" + + "github.com/docker/cli/internal/test" + "github.com/docker/cli/internal/test/testutil" + "github.com/docker/docker/api/types" + "github.com/stretchr/testify/assert" +) + +func TestPluginEnableErrors(t *testing.T) { + testCases := []struct { + args []string + flags map[string]string + pluginEnableFunc func(name string, options types.PluginEnableOptions) error + expectedError string + }{ + { + args: []string{}, + expectedError: "requires exactly 1 argument", + }, + { + args: []string{"too-many", "arguments"}, + expectedError: "requires exactly 1 argument", + }, + { + args: []string{"plugin-foo"}, + pluginEnableFunc: func(name string, options types.PluginEnableOptions) error { + return fmt.Errorf("failed to enable plugin") + }, + expectedError: "failed to enable plugin", + }, + { + args: []string{"plugin-foo"}, + flags: map[string]string{ + "timeout": "-1", + }, + expectedError: "negative timeout -1 is invalid", + }, + } + + for _, tc := range testCases { + cmd := newEnableCommand( + test.NewFakeCli(&fakeClient{ + pluginEnableFunc: tc.pluginEnableFunc, + })) + cmd.SetArgs(tc.args) + for key, value := range tc.flags { + cmd.Flags().Set(key, value) + } + cmd.SetOutput(ioutil.Discard) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + } +} + +func TestPluginEnable(t *testing.T) { + cli := test.NewFakeCli(&fakeClient{ + pluginEnableFunc: func(name string, options types.PluginEnableOptions) error { + return nil + }, + }) + + cmd := newEnableCommand(cli) + cmd.SetArgs([]string{"plugin-foo"}) + assert.NoError(t, cmd.Execute()) + assert.Equal(t, "plugin-foo\n", cli.OutBuffer().String()) +} diff --git a/components/cli/cli/command/plugin/remove_test.go b/components/cli/cli/command/plugin/remove_test.go new file mode 100644 index 0000000000..cc179091b9 --- /dev/null +++ b/components/cli/cli/command/plugin/remove_test.go @@ -0,0 +1,71 @@ +package plugin + +import ( + "fmt" + "io/ioutil" + "testing" + + "github.com/docker/cli/internal/test" + "github.com/docker/cli/internal/test/testutil" + "github.com/docker/docker/api/types" + "github.com/stretchr/testify/assert" +) + +func TestRemoveErrors(t *testing.T) { + + testCases := []struct { + args []string + pluginRemoveFunc func(name string, options types.PluginRemoveOptions) error + expectedError string + }{ + { + args: []string{}, + expectedError: "requires at least 1 argument", + }, + { + args: []string{"plugin-foo"}, + pluginRemoveFunc: func(name string, options types.PluginRemoveOptions) error { + return fmt.Errorf("Error removing plugin") + }, + expectedError: "Error removing plugin", + }, + } + + for _, tc := range testCases { + cli := test.NewFakeCli(&fakeClient{ + pluginRemoveFunc: tc.pluginRemoveFunc, + }) + cmd := newRemoveCommand(cli) + cmd.SetArgs(tc.args) + cmd.SetOutput(ioutil.Discard) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + } +} + +func TestRemove(t *testing.T) { + cli := test.NewFakeCli(&fakeClient{ + pluginRemoveFunc: func(name string, options types.PluginRemoveOptions) error { + return nil + }, + }) + cmd := newRemoveCommand(cli) + cmd.SetArgs([]string{"plugin-foo"}) + assert.NoError(t, cmd.Execute()) + assert.Equal(t, "plugin-foo\n", cli.OutBuffer().String()) +} + +func TestRemoveWithForceOption(t *testing.T) { + force := false + cli := test.NewFakeCli(&fakeClient{ + pluginRemoveFunc: func(name string, options types.PluginRemoveOptions) error { + force = options.Force + return nil + }, + }) + cmd := newRemoveCommand(cli) + cmd.SetArgs([]string{"plugin-foo"}) + cmd.Flags().Set("force", "true") + assert.NoError(t, cmd.Execute()) + assert.True(t, force) + assert.Equal(t, "plugin-foo\n", cli.OutBuffer().String()) +} From d3ed73b92e3685a6b6507ce00c65458f1d3fa35c Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 16 Feb 2018 11:20:05 +0100 Subject: [PATCH 7/7] Update authors Signed-off-by: Sebastiaan van Stijn Upstream-commit: f8da04a5100130f964f8159984a51c87ba06d50c Component: cli --- components/cli/.mailmap | 3 +++ components/cli/AUTHORS | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/components/cli/.mailmap b/components/cli/.mailmap index 44c0bb2659..855b20e232 100644 --- a/components/cli/.mailmap +++ b/components/cli/.mailmap @@ -232,6 +232,7 @@ Kai Qiang Wu (Kennan) Kai Qiang Wu (Kennan) Kamil Domański Kamjar Gerami +Kat Samperi Ken Cochrane Ken Herner Kenfe-Mickaël Laventure @@ -281,6 +282,7 @@ Martin Redmond Mary Anthony Mary Anthony Mary Anthony moxiegirl +Mateusz Major Matt Bentley Matt Schurenko Matt Williams @@ -397,6 +399,7 @@ Thatcher Peskens Thatcher Peskens Thatcher Peskens Thomas Gazagnaire +Thomas Krzero Thomas Léveil Thomas Léveil Tibor Vass diff --git a/components/cli/AUTHORS b/components/cli/AUTHORS index 4678a73041..7196404ef3 100644 --- a/components/cli/AUTHORS +++ b/components/cli/AUTHORS @@ -17,6 +17,7 @@ Aidan Feldman Aidan Hobson Sayers AJ Bowen Akihiro Suda +Akim Demaille Alan Thompson Albert Callarisa Aleksa Sarai @@ -107,6 +108,7 @@ Christophe Robin Christophe Vidal Christopher Biscardi Christopher Jones +Christy Perez Chun Chen Clinton Kitson Coenraad Loubser @@ -178,6 +180,7 @@ Eric-Olivier Lamey Erica Windisch Erik Hollensbe Erik St. Martin +Ethan Haynes Eugene Yakubovich Evan Allrich Evan Hazlett @@ -234,6 +237,7 @@ Ilya Khlopotov Ilya Sotkov Isabel Jimenez Ivan Grcic +Ivan Markin Jacob Atzen Jacob Tomlinson Jaivish Kothari @@ -314,6 +318,7 @@ Kai Qiang Wu (Kennan) Kara Alexandra Kareem Khazem Karthik Nayak +Kat Samperi Katie McLaughlin Ke Xu Kei Ohmura @@ -380,6 +385,7 @@ Mark Oates Martin Mosegaard Amdisen Mary Anthony Mason Malone +Mateusz Major Matt Gucci Matt Robenolt Matthew Heon @@ -420,6 +426,7 @@ Moorthy RS Morgan Bauer Moysés Borges Mrunal Patel +muicoder Muthukumar R Máximo Cuadros Nace Oroz @@ -520,6 +527,7 @@ Shukui Yang Sian Lerk Lau Sidhartha Mani sidharthamani +Silvin Lubecki Simei He Simon Ferquel Sindhu S @@ -538,6 +546,7 @@ Steve Durrheimer Steven Burgess Subhajit Ghosh Sun Jianbo +Sungwon Han Sven Dowideit Sylvain Baubeau Sébastien HOUZÉ @@ -546,6 +555,7 @@ TAGOMORI Satoshi Taylor Jones Thatcher Peskens Thomas Gazagnaire +Thomas Krzero Thomas Leonard Thomas Léveil Thomas Riccardi