From f939dd4d47aae3a851a854733bd65d0e8b9b2448 Mon Sep 17 00:00:00 2001 From: Guillaume Tardif <guillaume.tardif@docker.com> Date: Fri, 5 Jun 2020 17:30:27 +0200 Subject: [PATCH] Display friendly message if unknown command is available in default context --- cli/dockerclassic/exec.go | 18 +++++++++++++++++- cli/main.go | 18 +++++++++++++++++- tests/e2e/e2e_test.go | 7 +++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/cli/dockerclassic/exec.go b/cli/dockerclassic/exec.go index 9bd72c29b..50411b458 100644 --- a/cli/dockerclassic/exec.go +++ b/cli/dockerclassic/exec.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "os/exec" + "strings" "github.com/spf13/cobra" @@ -12,6 +13,9 @@ import ( "github.com/docker/api/context/store" ) +// ClassicCliName name of the classic cli binary +const ClassicCliName = "docker-classic" + // Exec delegates to docker-classic func Exec(ctx context.Context) { currentContext := apicontext.CurrentContext(ctx) @@ -21,7 +25,7 @@ func Exec(ctx context.Context) { // Only run original docker command if the current context is not // ours. if err != nil { - cmd := exec.CommandContext(ctx, "docker-classic", os.Args[1:]...) + cmd := exec.CommandContext(ctx, ClassicCliName, os.Args[1:]...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -41,3 +45,15 @@ func ExecCmd(command *cobra.Command) error { Exec(command.Context()) return nil } + +// IsDefaultContextCommand checks if the command exists in the classic cli (issues a shellout --help) +func IsDefaultContextCommand(dockerCommand string) bool { + cmd := exec.Command(ClassicCliName, dockerCommand, "--help") + b, e := cmd.CombinedOutput() + if e != nil { + fmt.Println(e) + } + output := string(b) + contains := strings.Contains(output, "Usage:\tdocker "+dockerCommand) + return contains +} diff --git a/cli/main.go b/cli/main.go index 586b4bae4..5a2d88d55 100644 --- a/cli/main.go +++ b/cli/main.go @@ -34,6 +34,7 @@ import ( "os" "os/signal" "path/filepath" + "regexp" "syscall" "time" @@ -171,11 +172,26 @@ func main() { os.Exit(1) } dockerclassic.Exec(ctx) - fmt.Println(err) + + checkIfUnknownCommandExistInDefaultContext(err, currentContext) + fmt.Fprintln(os.Stderr, err) os.Exit(1) } } +func checkIfUnknownCommandExistInDefaultContext(err error, currentContext string) { + re := regexp.MustCompile(`unknown command "([^"]*)"`) + submatch := re.FindSubmatch([]byte(err.Error())) + if len(submatch) == 2 { + dockerCommand := string(submatch[1]) + + if dockerclassic.IsDefaultContextCommand(dockerCommand) { + fmt.Fprintf(os.Stderr, "Command \"%s\" not available in current context (%s), you can use the \"default\" context to run this command\n", dockerCommand, currentContext) + os.Exit(1) + } + } +} + func newSigContext() (context.Context, func()) { ctx, cancel := context.WithCancel(context.Background()) s := make(chan os.Signal) diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 54e39aac4..c85858637 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -162,6 +162,13 @@ func (s *E2eSuite) TestLeaveLegacyErrorMessagesUnchanged() { Expect(err).NotTo(BeNil()) } +func (s *E2eSuite) TestDisplayFriendlyErrorMessageForLegacyCommands() { + s.NewDockerCommand("context", "create", "test-example", "example").ExecOrDie() + output, err := s.NewDockerCommand("--context", "test-example", "images").Exec() + Expect(output).To(Equal("Command \"images\" not available in current context (test-example), you can use the \"default\" context to run this command\n")) + Expect(err).NotTo(BeNil()) +} + func (s *E2eSuite) TestMockBackend() { It("creates a new test context to hardcoded example backend", func() { s.NewDockerCommand("context", "create", "test-example", "example").ExecOrDie()