From eb712ac75f227aee42d78c9aa7a044cd612ddd1f Mon Sep 17 00:00:00 2001 From: Guillaume Tardif Date: Tue, 5 May 2020 17:55:53 +0200 Subject: [PATCH] Added aci e2e tests, not run in CI since requiring azure login. Need a `docker rm` command to add nginx e2e test, and compose sample to follow --- .golangci.yml | 2 + Makefile | 3 + azure/aci.go | 32 ++++++++ tests/aci-e2e/e2e-aci.go | 151 ++++++++++++++++++++++++++++++++++++++ tests/e2e/e2e.go | 104 ++++++++++---------------- tests/framework/helper.go | 39 ++++++++++ 6 files changed, 265 insertions(+), 66 deletions(-) create mode 100644 tests/aci-e2e/e2e-aci.go create mode 100644 tests/framework/helper.go diff --git a/.golangci.yml b/.golangci.yml index a7517bb47..7d0f744dd 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -33,3 +33,5 @@ issues: # golangci hides some golint warnings (the warning about exported things # withtout documentation for example), this will make it show them anyway. exclude-use-default: false + exclude: + - should not use dot imports diff --git a/Makefile b/Makefile index a5c565533..d8a5e7b38 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,9 @@ cli: ## Compile the cli e2e-local: ## Run End to end local tests go run ./tests/e2e/e2e.go +e2e-aci: ## Run End to end ACI tests (requires azure login) + go run ./tests/aci-e2e/e2e-aci.go + cross: ## Compile the CLI for linux, darwin and windows @docker build . \ --output type=local,dest=./bin \ diff --git a/azure/aci.go b/azure/aci.go index 258598d4d..7404483d8 100644 --- a/azure/aci.go +++ b/azure/aci.go @@ -10,6 +10,8 @@ import ( "strings" "time" + "github.com/Azure/azure-sdk-for-go/profiles/2019-03-01/resources/mgmt/resources" + "github.com/Azure/azure-sdk-for-go/profiles/preview/preview/subscription/mgmt/subscription" "github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance" "github.com/Azure/azure-sdk-for-go/services/keyvault/auth" "github.com/Azure/go-autorest/autorest" @@ -261,3 +263,33 @@ func getContainerClient(subscriptionID string) (containerinstance.ContainerClien containerClient.Authorizer = auth return containerClient, nil } + +func getSubscriptionsClient() subscription.SubscriptionsClient { + subc := subscription.NewSubscriptionsClient() + authorizer, _ := auth.NewAuthorizerFromCLI() + subc.Authorizer = authorizer + return subc +} + +//GetGroupsClient ... +func GetGroupsClient(subscriptionID string) resources.GroupsClient { + groupsClient := resources.NewGroupsClient(subscriptionID) + authorizer, _ := auth.NewAuthorizerFromCLI() + groupsClient.Authorizer = authorizer + return groupsClient +} + +//GetSubscriptionID ... +func GetSubscriptionID(ctx context.Context) (string, error) { + c := getSubscriptionsClient() + res, err := c.List(ctx) + if err != nil { + return "", err + } + subs := res.Values() + if len(subs) == 0 { + return "", errors.New("no subscriptions found") + } + sub := subs[0] + return *sub.SubscriptionID, nil +} diff --git a/tests/aci-e2e/e2e-aci.go b/tests/aci-e2e/e2e-aci.go new file mode 100644 index 000000000..ce78be0ad --- /dev/null +++ b/tests/aci-e2e/e2e-aci.go @@ -0,0 +1,151 @@ +package main + +import ( + "context" + "log" + + "github.com/Azure/azure-sdk-for-go/profiles/2019-03-01/resources/mgmt/resources" + "github.com/docker/api/azure" + . "github.com/docker/api/tests/framework" + . "github.com/onsi/gomega" +) + +const resourceGroupName = "resourceGroupTest" + +var location = "westeurope" + +const contextName = "acitest" + +func main() { + SetupTest() + + It("ensures context command includes azure-login and aci-create", func() { + output := NewDockerCommand("context", "create", "--help").ExecOrDie() + Expect(output).To(ContainSubstring("docker context create CONTEXT BACKEND [OPTIONS] [flags]")) + Expect(output).To(ContainSubstring("--aci-location")) + Expect(output).To(ContainSubstring("--aci-subscription-id")) + Expect(output).To(ContainSubstring("--aci-resource-group")) + }) + + It("should be initialized with default context", func() { + NewCommand("docker", "context", "use", "default").ExecOrDie() + output := NewCommand("docker", "context", "ls").ExecOrDie() + Expect(output).To(Not(ContainSubstring(contextName))) + Expect(output).To(ContainSubstring("default *")) + }) + + It("creates a new aci context for tests", func() { + setupTestResourecGroup(resourceGroupName) + subscriptionID, err := azure.GetSubscriptionID(context.TODO()) + Expect(err).To(BeNil()) + + NewDockerCommand("context", "create", contextName, "aci", "--aci-subscription-id", subscriptionID, "--aci-resource-group", resourceGroupName, "--aci-location", location).ExecOrDie() + //Expect(output).To(ContainSubstring("ACI context acitest created")) + }) + + defer deleteResourceGroup(resourceGroupName) + + It("uses the aci context", func() { + currentContext := NewCommand("docker", "context", "use", contextName).ExecOrDie() + Expect(currentContext).To(ContainSubstring(contextName)) + output := NewCommand("docker", "context", "ls").ExecOrDie() + Expect(output).To(ContainSubstring("acitest *")) + }) + + It("ensures no container is running initially", func() { + output := NewDockerCommand("ps").ExecOrDie() + Expect(len(Lines(output))).To(Equal(1)) + }) + + /* + var nginxID string + It("runs nginx on port 80", func() { + NewDockerCommand("run", "nginx", "-p", "80:80").ExecOrDie() + output := NewDockerCommand("ps").ExecOrDie() + Lines := Lines(output) + Expect(len(Lines)).To(Equal(2)) + + containerFields := Columns(Lines[1]) + nginxID = containerFields[0] + Expect(containerFields[1]).To(Equal("nginx")) + Expect(containerFields[2]).To(Equal("Running")) + exposedIP := containerFields[3] + Expect(exposedIP).To(ContainSubstring(":80->80/TCP")) + + url := strings.ReplaceAll(exposedIP, "->80/TCP", "") + output = NewCommand("curl", url).ExecOrDie() + Expect(output).To(ContainSubstring("Welcome to nginx!")) + }) + + It("removes container nginx", func() { + output := NewDockerCommand("rm", nginxID).ExecOrDie() + Expect(Lines(output)[0]).To(Equal(nginxID)) + }) + */ + + /* + It("deploys a compose app", func() { + output := NewDockerCommand("compose", "up", "-f", "./composefiles/aci_demo_ports_secrets_volumes/aci_demo_port.yaml", "-n", "acicompose").ExecOrDie() + Expect(output).To(ContainSubstring("Successfully deployed")) + output = NewDockerCommand("ps").ExecOrDie() + Lines := Lines(output) + Expect(len(Lines)).To(Equal(4)) + var webChecked = false + for _, line := range Lines { + if strings.Contains(line, "acicompose_web") { + webChecked = true + containerFields := Columns(line) + exposedIP := containerFields[3] + Expect(exposedIP).To(ContainSubstring(":80->80/TCP")) + + url := strings.ReplaceAll(exposedIP, "->80/TCP", "") + output = NewCommand("curl", url).ExecOrDie() + Expect(output).To(ContainSubstring("Docker Compose demo")) + output = NewCommand("curl", url+"/words/noun").ExecOrDie() + Expect(output).To(ContainSubstring("\"word\":")) + } + } + Expect(webChecked).To(BeTrue()) + }) + + It("get logs from web service", func() { + output := NewDockerCommand("logs", "acicompose_web").ExecOrDie() + Expect(output).To(ContainSubstring("Calling http://127.0.0.1:8080/noun")) + }) + + It("shutdown compose app", func() { + NewDockerCommand("compose", "down", "-f", "./composefiles/aci_demo_ports_secrets_volumes/aci_demo_port.yaml", "-n", "acicompose").ExecOrDie() + }) + */ + It("switches back to default context", func() { + output := NewCommand("docker", "context", "use", "default").ExecOrDie() + Expect(output).To(ContainSubstring("default")) + }) + + It("deletes test context", func() { + output := NewCommand("docker", "context", "rm", contextName).ExecOrDie() + Expect(output).To(ContainSubstring(contextName)) + }) +} + +func setupTestResourecGroup(groupName string) { + log.Println("Creating resource group " + resourceGroupName) + ctx := context.TODO() + subscriptionID, err := azure.GetSubscriptionID(ctx) + Expect(err).To(BeNil()) + gc := azure.GetGroupsClient(subscriptionID) + _, err = gc.CreateOrUpdate(ctx, groupName, resources.Group{ + Location: &location, + }) + Expect(err).To(BeNil()) +} + +func deleteResourceGroup(groupName string) { + log.Println("Deleting resource group " + resourceGroupName) + ctx := context.TODO() + subscriptionID, err := azure.GetSubscriptionID(ctx) + Expect(err).To(BeNil()) + gc := azure.GetGroupsClient(subscriptionID) + _, err = gc.Delete(ctx, groupName) + Expect(err).To(BeNil()) +} diff --git a/tests/e2e/e2e.go b/tests/e2e/e2e.go index 762612cdc..6bbd46c92 100644 --- a/tests/e2e/e2e.go +++ b/tests/e2e/e2e.go @@ -1,107 +1,79 @@ package main import ( - "log" - "strings" "time" - - "github.com/robpike/filter" - - g "github.com/onsi/gomega" - - f "github.com/docker/api/tests/framework" + . "github.com/docker/api/tests/framework" + . "github.com/onsi/gomega" ) func main() { - setup() + SetupTest() It("ensures context command includes azure-login and aci-create", func() { - output := f.NewDockerCommand("context", "create", "--help").ExecOrDie() - g.Expect(output).To(g.ContainSubstring("docker context create CONTEXT BACKEND [OPTIONS] [flags]")) - g.Expect(output).To(g.ContainSubstring("--aci-location")) - g.Expect(output).To(g.ContainSubstring("--aci-subscription-id")) - g.Expect(output).To(g.ContainSubstring("--aci-resource-group")) + output := NewDockerCommand("context", "create", "--help").ExecOrDie() + Expect(output).To(ContainSubstring("docker context create CONTEXT BACKEND [OPTIONS] [flags]")) + Expect(output).To(ContainSubstring("--aci-location")) + Expect(output).To(ContainSubstring("--aci-subscription-id")) + Expect(output).To(ContainSubstring("--aci-resource-group")) }) It("should be initialized with default context", func() { - f.NewCommand("docker", "context", "use", "default").ExecOrDie() - output := f.NewCommand("docker", "context", "ls").ExecOrDie() - g.Expect(output).To(g.ContainSubstring("default *")) + NewCommand("docker", "context", "use", "default").ExecOrDie() + output := NewCommand("docker", "context", "ls").ExecOrDie() + Expect(output).To(ContainSubstring("default *")) }) It("should list all legacy commands", func() { - output := f.NewDockerCommand("--help").ExecOrDie() - g.Expect(output).To(g.ContainSubstring("swarm")) + output := NewDockerCommand("--help").ExecOrDie() + Expect(output).To(ContainSubstring("swarm")) }) It("should execute legacy commands", func() { - output, _ := f.NewDockerCommand("swarm", "join").Exec() - g.Expect(output).To(g.ContainSubstring("\"docker swarm join\" requires exactly 1 argument.")) + output, _ := NewDockerCommand("swarm", "join").Exec() + Expect(output).To(ContainSubstring("\"docker swarm join\" requires exactly 1 argument.")) }) It("should run local container in less than 2 secs", func() { - f.NewDockerCommand("pull", "hello-world").ExecOrDie() - output := f.NewDockerCommand("run", "hello-world").WithTimeout(time.NewTimer(2 * time.Second).C).ExecOrDie() - g.Expect(output).To(g.ContainSubstring("Hello from Docker!")) + NewDockerCommand("pull", "hello-world").ExecOrDie() + output := NewDockerCommand("run", "hello-world").WithTimeout(time.NewTimer(2 * time.Second).C).ExecOrDie() + Expect(output).To(ContainSubstring("Hello from Docker!")) }) It("should list local container", func() { - output := f.NewDockerCommand("ps", "-a").ExecOrDie() - g.Expect(output).To(g.ContainSubstring("hello-world")) + output := NewDockerCommand("ps", "-a").ExecOrDie() + Expect(output).To(ContainSubstring("hello-world")) }) It("creates a new test context to hardcoded example backend", func() { - f.NewDockerCommand("context", "create", "test-example", "example").ExecOrDie() - //g.Expect(output).To(g.ContainSubstring("test-example context acitest created")) + NewDockerCommand("context", "create", "test-example", "example").ExecOrDie() + //Expect(output).To(ContainSubstring("test-example context acitest created")) }) - defer f.NewCommand("docker", "context", "rm", "test-example", "-f").ExecOrDie() + defer NewCommand("docker", "context", "rm", "test-example", "-f").ExecOrDie() It("uses the test context", func() { - currentContext := f.NewCommand("docker", "context", "use", "test-example").ExecOrDie() - g.Expect(currentContext).To(g.ContainSubstring("test-example")) - output := f.NewCommand("docker", "context", "ls").ExecOrDie() - g.Expect(output).To(g.ContainSubstring("test-example *")) + currentContext := NewCommand("docker", "context", "use", "test-example").ExecOrDie() + Expect(currentContext).To(ContainSubstring("test-example")) + output := NewCommand("docker", "context", "ls").ExecOrDie() + Expect(output).To(ContainSubstring("test-example *")) }) It("can run ps command", func() { - output := f.NewDockerCommand("ps").ExecOrDie() - lines := lines(output) - g.Expect(len(lines)).To(g.Equal(3)) - g.Expect(lines[2]).To(g.ContainSubstring("1234 alpine")) + output := NewDockerCommand("ps").ExecOrDie() + lines := Lines(output) + Expect(len(lines)).To(Equal(3)) + Expect(lines[2]).To(ContainSubstring("1234 alpine")) }) It("can run quiet ps command", func() { - output := f.NewDockerCommand("ps", "-q").ExecOrDie() - lines := lines(output) - g.Expect(len(lines)).To(g.Equal(2)) - g.Expect(lines[0]).To(g.Equal("id")) - g.Expect(lines[1]).To(g.Equal("1234")) + output := NewDockerCommand("ps", "-q").ExecOrDie() + lines := Lines(output) + Expect(len(lines)).To(Equal(2)) + Expect(lines[0]).To(Equal("id")) + Expect(lines[1]).To(Equal("1234")) }) It("can run 'run' command", func() { - output := f.NewDockerCommand("run", "nginx", "-p", "80:80").ExecOrDie() - g.Expect(output).To(g.ContainSubstring("Running container \"nginx\" with name")) + output := NewDockerCommand("run", "nginx", "-p", "80:80").ExecOrDie() + Expect(output).To(ContainSubstring("Running container \"nginx\" with name")) }) } - -func nonEmptyString(s string) bool { - return strings.TrimSpace(s) != "" -} - -func lines(output string) []string { - return filter.Choose(strings.Split(output, "\n"), nonEmptyString).([]string) -} - -// It runs func -func It(description string, test func()) { - test() - log.Print("Passed: ", description) -} - -func gomegaFailHandler(message string, callerSkip ...int) { - log.Fatal(message) -} - -func setup() { - g.RegisterFailHandler(gomegaFailHandler) -} diff --git a/tests/framework/helper.go b/tests/framework/helper.go new file mode 100644 index 000000000..4eb79a0ff --- /dev/null +++ b/tests/framework/helper.go @@ -0,0 +1,39 @@ +package framework + +import ( + "log" + "strings" + + "github.com/robpike/filter" + + "github.com/onsi/gomega" +) + +func nonEmptyString(s string) bool { + return strings.TrimSpace(s) != "" +} + +//Lines get lines from a raw string +func Lines(output string) []string { + return filter.Choose(strings.Split(output, "\n"), nonEmptyString).([]string) +} + +//Columns get columns from a line +func Columns(line string) []string { + return filter.Choose(strings.Split(line, " "), nonEmptyString).([]string) +} + +// It runs func +func It(description string, test func()) { + test() + log.Print("Passed: ", description) +} + +func gomegaFailHandler(message string, callerSkip ...int) { + log.Fatal(message) +} + +//SetupTest Init gomega fail handler +func SetupTest() { + gomega.RegisterFailHandler(gomegaFailHandler) +}