Przeglądaj źródła

Add e2e tests for plugin behavior and commands

Signed-off-by: Guillaume Lours <[email protected]>
Signed-off-by: Nicolas De Loof <[email protected]>
Guillaume Lours 5 lat temu
rodzic
commit
6febf68748

+ 1 - 1
ecs/Makefile

@@ -4,7 +4,7 @@ clean:
 build:
 	go build -v -o dist/docker-ecs cmd/main/main.go
 
-test: ## Run tests
+test: build ## Run tests
 	go test ./... -v
 
 dev: build

+ 6 - 0
ecs/cmd/main/main.go

@@ -43,6 +43,12 @@ func NewRootCmd(name string, dockerCli command.Cli) *cobra.Command {
 			opts, err = docker.CheckAwsContextExists(contextName)
 			return err
 		},
+		RunE: func(cmd *cobra.Command, args []string) error {
+			if len(args) != 0 {
+				return fmt.Errorf("%q is not a docker ecs command\nSee 'docker ecs --help'", args[0])
+			}
+			return nil
+		},
 	}
 	cmd.AddCommand(
 		VersionCommand(),

+ 1 - 0
ecs/go.mod

@@ -50,6 +50,7 @@ require (
 	gopkg.in/dancannon/gorethink.v3 v3.0.5 // indirect
 	gopkg.in/fatih/pool.v2 v2.0.0 // indirect
 	gopkg.in/gorethink/gorethink.v3 v3.0.5 // indirect
+	gotest.tools v2.2.0+incompatible
 	gotest.tools/v3 v3.0.2
 	vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787 // indirect
 )

+ 14 - 5
ecs/pkg/docker/contextStore.go

@@ -3,6 +3,7 @@ package docker
 import (
 	"fmt"
 
+	"github.com/docker/cli/cli/command"
 	cliconfig "github.com/docker/cli/cli/config"
 	"github.com/docker/cli/cli/context/store"
 	"github.com/mitchellh/mapstructure"
@@ -25,7 +26,12 @@ type AwsContext struct {
 }
 
 func NewContext(name string, awsContext *AwsContext) error {
-	contextStore := initContextStore()
+	_, err := NewContextWithStore(name, awsContext, cliconfig.ContextStoreDir())
+	return err
+}
+
+func NewContextWithStore(name string, awsContext *AwsContext, contextDirectory string) (store.Store, error) {
+	contextStore := initContextStore(contextDirectory)
 	endpoints := map[string]interface{}{
 		"aws":    awsContext,
 		"docker": awsContext,
@@ -36,16 +42,19 @@ func NewContext(name string, awsContext *AwsContext) error {
 		Endpoints: endpoints,
 		Metadata:  TypeContext{Type: contextType},
 	}
-	return contextStore.CreateOrUpdate(metadata)
+	return contextStore, contextStore.CreateOrUpdate(metadata)
 }
 
-func initContextStore() store.Store {
+func initContextStore(contextDir string) store.Store {
 	config := store.NewConfig(getter)
-	return store.New(cliconfig.ContextStoreDir(), config)
+	return store.New(contextDir, config)
 }
 
 func CheckAwsContextExists(contextName string) (*AwsContext, error) {
-	contextStore := initContextStore()
+	if contextName == command.DefaultContextName {
+		return nil, fmt.Errorf("can't use \"%s\" with ECS command because it is not an AWS context", contextName)
+	}
+	contextStore := initContextStore(cliconfig.ContextStoreDir())
 	metadata, err := contextStore.GetMetadata(contextName)
 	if err != nil {
 		return nil, err

+ 18 - 0
ecs/tests/command_test.go

@@ -0,0 +1,18 @@
+package tests
+
+import (
+	"testing"
+
+	"gotest.tools/v3/icmd"
+)
+
+func TestExitErrorCode(t *testing.T) {
+	cmd, cleanup := dockerCli.createTestCmd()
+	defer cleanup()
+
+	cmd.Command = dockerCli.Command("ecs", "unknown_command")
+	icmd.RunCmd(cmd).Assert(t, icmd.Expected{
+		ExitCode: 1,
+		Err:      "\"unknown_command\" is not a docker ecs command\nSee 'docker ecs --help'",
+	})
+}

+ 127 - 0
ecs/tests/main_test.go

@@ -0,0 +1,127 @@
+package tests
+
+import (
+	"encoding/json"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"runtime"
+	"testing"
+
+	dockerConfigFile "github.com/docker/cli/cli/config/configfile"
+	"github.com/docker/ecs-plugin/pkg/docker"
+	"gotest.tools/v3/icmd"
+)
+
+var (
+	e2ePath         = flag.String("e2e-path", ".", "Set path to the e2e directory")
+	dockerCliPath   = os.Getenv("DOCKERCLI_BINARY")
+	dockerCli       dockerCliCommand
+	testContextName = "testAwsContextToBeRemoved"
+)
+
+type dockerCliCommand struct {
+	path         string
+	cliPluginDir string
+}
+
+type ConfigFileOperator func(configFile *dockerConfigFile.ConfigFile)
+
+func (d dockerCliCommand) createTestCmd(ops ...ConfigFileOperator) (icmd.Cmd, func()) {
+	configDir, err := ioutil.TempDir("", "config")
+	if err != nil {
+		panic(err)
+	}
+	configFilePath := filepath.Join(configDir, "config.json")
+	config := dockerConfigFile.ConfigFile{
+		CLIPluginsExtraDirs: []string{
+			d.cliPluginDir,
+		},
+		Filename: configFilePath,
+	}
+	for _, op := range ops {
+		op(&config)
+	}
+	configFile, err := os.Create(configFilePath)
+	if err != nil {
+		panic(err)
+	}
+	defer configFile.Close()
+	err = json.NewEncoder(configFile).Encode(config)
+	if err != nil {
+		panic(err)
+	}
+
+	awsContext := docker.AwsContext{
+		Profile: "TestProfile",
+		Cluster: "TestCluster",
+		Region:  "TestRegion",
+	}
+	testStore, err := docker.NewContextWithStore(testContextName, &awsContext, filepath.Join(configDir, "contexts"))
+	if err != nil {
+		panic(err)
+	}
+	cleanup := func() {
+		fmt.Println("cleanup")
+		testStore.Remove(testContextName)
+		os.RemoveAll(configDir)
+	}
+	env := append(os.Environ(),
+		"DOCKER_CONFIG="+configDir,
+		"DOCKER_CLI_EXPERIMENTAL=enabled") // TODO: Remove this once docker ecs plugin is no more experimental
+	return icmd.Cmd{Env: env}, cleanup
+}
+
+func (d dockerCliCommand) Command(args ...string) []string {
+	return append([]string{d.path, "--context", testContextName}, args...)
+}
+
+func TestMain(m *testing.M) {
+	flag.Parse()
+	if err := os.Chdir(*e2ePath); err != nil {
+		panic(err)
+	}
+	cwd, err := os.Getwd()
+	if err != nil {
+		panic(err)
+	}
+	dockerEcs := os.Getenv("DOCKERECS_BINARY")
+	if dockerEcs == "" {
+		dockerEcs = filepath.Join(cwd, "../dist/docker-ecs")
+	}
+	dockerEcs, err = filepath.Abs(dockerEcs)
+	if err != nil {
+		panic(err)
+	}
+	if dockerCliPath == "" {
+		dockerCliPath = "docker"
+	} else {
+		dockerCliPath, err = filepath.Abs(dockerCliPath)
+		if err != nil {
+			panic(err)
+		}
+	}
+	// Prepare docker cli to call the docker-ecs plugin binary:
+	// - Create a symbolic link with the dockerEcs binary to the plugin directory
+	cliPluginDir, err := ioutil.TempDir("", "configContent")
+	if err != nil {
+		panic(err)
+	}
+	defer os.RemoveAll(cliPluginDir)
+	createDockerECSSymLink(dockerEcs, cliPluginDir)
+
+	dockerCli = dockerCliCommand{path: dockerCliPath, cliPluginDir: cliPluginDir}
+	os.Exit(m.Run())
+}
+
+func createDockerECSSymLink(dockerEcs, configDir string) {
+	dockerEcsExecName := "docker-ecs"
+	if runtime.GOOS == "windows" {
+		dockerEcsExecName += ".exe"
+	}
+	if err := os.Symlink(dockerEcs, filepath.Join(configDir, dockerEcsExecName)); err != nil {
+		panic(err)
+	}
+}

+ 33 - 0
ecs/tests/plugin_test.go

@@ -0,0 +1,33 @@
+package tests
+
+import (
+	"regexp"
+	"testing"
+
+	"gotest.tools/assert"
+	"gotest.tools/v3/golden"
+	"gotest.tools/v3/icmd"
+)
+
+func TestInvokePluginFromCLI(t *testing.T) {
+	cmd, cleanup := dockerCli.createTestCmd()
+	defer cleanup()
+	// docker --help should list app as a top command
+	cmd.Command = dockerCli.Command("--help")
+	icmd.RunCmd(cmd).Assert(t, icmd.Expected{
+		Out: "ecs*        Docker ECS (Docker Inc.,",
+	})
+
+	// docker app --help prints docker-app help
+	cmd.Command = dockerCli.Command("ecs", "--help")
+	usage := icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined()
+
+	goldenFile := "plugin-usage.golden"
+	golden.Assert(t, usage, goldenFile)
+
+	// docker info should print app version and short description
+	cmd.Command = dockerCli.Command("info")
+	re := regexp.MustCompile(`ecs: Docker ECS \(Docker Inc\., .*\)`)
+	output := icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined()
+	assert.Assert(t, re.MatchString(output))
+}

+ 34 - 0
ecs/tests/setup_command_test.go

@@ -0,0 +1,34 @@
+package tests
+
+import (
+	"strings"
+	"testing"
+
+	"gotest.tools/assert"
+	"gotest.tools/v3/golden"
+	"gotest.tools/v3/icmd"
+)
+
+func TestSetupMandatoryArguments(t *testing.T) {
+	cmd, cleanup := dockerCli.createTestCmd()
+	defer cleanup()
+
+	cmd.Command = dockerCli.Command("ecs", "setup")
+	icmd.RunCmd(cmd).Assert(t, icmd.Expected{
+		ExitCode: 1,
+		Err:      "required flag(s) \"cluster\", \"profile\", \"region\" not set",
+	})
+}
+func TestDefaultAwsContextName(t *testing.T) {
+	cmd, cleanup := dockerCli.createTestCmd()
+	defer cleanup()
+
+	cmd.Command = dockerCli.Command("ecs", "setup", "--cluster", "clusterName", "--profile", "profileName",
+		"--region", "regionName")
+	icmd.RunCmd(cmd).Assert(t, icmd.Success)
+
+	cmd.Command = dockerCli.Command("context", "inspect", "aws")
+	output := icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined()
+	expected := golden.Get(t, "context-inspect.golden")
+	assert.Assert(t, strings.HasPrefix(output, string(expected)))
+}

+ 16 - 0
ecs/tests/testdata/context-inspect.golden

@@ -0,0 +1,16 @@
+[
+    {
+        "Name": "aws",
+        "Metadata": {},
+        "Endpoints": {
+            "aws": {
+                "Cluster": "clusterName",
+                "Profile": "profileName",
+                "Region": "regionName"
+            },
+            "docker": {
+                "SkipTLSVerify": false
+            }
+        },
+        "TLSMaterial": {},
+        "Storage":

+ 14 - 0
ecs/tests/testdata/plugin-usage.golden

@@ -0,0 +1,14 @@
+
+Usage:	docker ecs COMMAND
+
+run multi-container applications on Amazon ECS.
+
+Management Commands:
+  compose     
+  secret      Manages secrets
+
+Commands:
+  setup       
+  version     Show version.
+
+Run 'docker ecs COMMAND --help' for more information on a command.