// Copyright 2015-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package cmd import ( "fmt" "strconv" "testing" "time" "github.com/stretchr/testify/require" "github.com/aws/amazon-ecs-cli/ecs-cli/integ" "github.com/aws/amazon-ecs-cli/ecs-cli/integ/stdout" ) // A Project is the configuration needed to create an ECS Service. type Project struct { Name string ComposeFileName string ECSParamsFileName string ConfigName string } // NewProject creates a new Project for an ECS Service. func NewProject(name string, configName string) *Project { return &Project{ Name: name, ConfigName: configName, } } // TestTaskUp runs `ecs-cli compose up` for a project. func TestTaskUp(t *testing.T, p *Project) { // Given args := []string{ "compose", "--file", p.ComposeFileName, "--ecs-params", p.ECSParamsFileName, "--project-name", p.Name, "up", "--cluster-config", p.ConfigName, } cmd := integ.GetCommand(args) // When out, err := cmd.Output() require.NoErrorf(t, err, "Failed to create task definition", "error %v, running %v, out: %s", err, args, string(out)) // Then stdout.Stdout(out).TestHasAllSubstrings(t, []string{ "Started container", }) t.Logf("Created containers for %s", p.Name) } // TestServiceUp runs `ecs-cli compose service up` for a project. func TestServiceUp(t *testing.T, p *Project) { // Given args := []string{ "compose", "--file", p.ComposeFileName, "--ecs-params", p.ECSParamsFileName, "--project-name", p.Name, "service", "up", "--cluster-config", p.ConfigName, "--create-log-groups", } cmd := integ.GetCommand(args) // When out, err := cmd.Output() require.NoErrorf(t, err, "Failed to create service", "error %v, running %v, out: %s", err, args, string(out)) // Then stdout.Stdout(out).TestHasAllSubstrings(t, []string{ "ECS Service has reached a stable state", "desiredCount=1", "serviceName=" + p.Name, }) t.Logf("Created service with name %s", p.Name) } // TestServicePs runs `ecs-cli compose service ps` for a project. func TestServicePs(t *testing.T, p *Project, wantedNumOfContainers int) { f := func(t *testing.T) bool { return testServiceHasAllRunningContainers(t, p, wantedNumOfContainers) } timeoutInS := 120 * time.Second // 2 mins sleepInS := 15 * time.Second require.True(t, integ.RetryUntilTimeout(t, f, timeoutInS, sleepInS), "Failed to get RUNNING containers") t.Logf("Project %s has %d running containers", p.Name, wantedNumOfContainers) } // TestTaskScale runs `ecs-cli compose scale` for a project. func TestTaskScale(t *testing.T, p *Project, scale int) { args := []string{ "compose", "--file", p.ComposeFileName, "--ecs-params", p.ECSParamsFileName, "--project-name", p.Name, "scale", strconv.Itoa(scale), "--cluster-config", p.ConfigName, } cmd := integ.GetCommand(args) // When out, err := cmd.Output() require.NoErrorf(t, err, "Failed to scale task", "error %v, running %v, out: %s", err, args, string(out)) // Then stdout.Stdout(out).TestHasAllSubstrings(t, []string{ "Started container", }) t.Logf("Scaled the task %s to %d", p.Name, scale) } // TestServiceScale runs `ecs-cli compose service scale` for a project. func TestServiceScale(t *testing.T, p *Project, scale int) { // Given args := []string{ "compose", "--file", p.ComposeFileName, "--ecs-params", p.ECSParamsFileName, "--project-name", p.Name, "service", "scale", strconv.Itoa(scale), "--cluster-config", p.ConfigName, } cmd := integ.GetCommand(args) // When out, err := cmd.Output() require.NoErrorf(t, err, "Failed to scale service", "error %v, running %v, out: %s", err, args, string(out)) // Then stdout.Stdout(out).TestHasAllSubstrings(t, []string{ "ECS Service has reached a stable state", fmt.Sprintf("desiredCount=%d", scale), "serviceName=" + p.Name, }) t.Logf("Scaled the service %s to %d tasks", p.Name, scale) } // TestTaskDown runs `ecs-cli compose down` for a project. func TestTaskDown(t *testing.T, p *Project) { args := []string{ "compose", "--file", p.ComposeFileName, "--ecs-params", p.ECSParamsFileName, "--project-name", p.Name, "down", "--cluster-config", p.ConfigName, } cmd := integ.GetCommand(args) // When out, err := cmd.Output() require.NoErrorf(t, err, "Failed to delete task", "error %v, running %v, out: %s", err, args, string(out)) // Then stdout.Stdout(out).TestHasAllSubstrings(t, []string{ "Stopped container", }) t.Logf("Deleted task %s", p.Name) } // TestServiceDown runs `ecs-cli compose service down` for a project. func TestServiceDown(t *testing.T, p *Project) { // Given args := []string{ "compose", "--file", p.ComposeFileName, "--ecs-params", p.ECSParamsFileName, "--project-name", p.Name, "service", "down", "--cluster-config", p.ConfigName, } cmd := integ.GetCommand(args) // When out, err := cmd.Output() require.NoErrorf(t, err, "Failed to delete service", "error %v, running %v, out: %s", err, args, string(out)) // Then stdout.Stdout(out).TestHasAllSubstrings(t, []string{ "Deleted ECS service", "ECS Service has reached a stable state", "desiredCount=0", "serviceName=" + p.Name, }) t.Logf("Deleted service %s", p.Name) } func testServiceHasAllRunningContainers(t *testing.T, p *Project, wantedNumOfContainers int) bool { // Given args := []string{ "compose", "--file", p.ComposeFileName, "--ecs-params", p.ECSParamsFileName, "--project-name", p.Name, "service", "ps", "--cluster-config", p.ConfigName, } cmd := integ.GetCommand(args) // When out, err := cmd.Output() if err != nil { t.Logf("Failed to list containers for service %v", args) return false } // Then lines := stdout.Stdout(out).Lines() if len(lines) < 2 { t.Logf("No running containers yet, out = %v", lines) return false } if lines[len(lines)-1] == "" { lines = lines[:len(lines)-1] // Drop the last new line } containers := lines[1:] // Drop the headers if wantedNumOfContainers != len(containers) { t.Logf("Wanted = %d, got = %d running containers", wantedNumOfContainers, len(containers)) t.Logf("Current running containers = %v", containers) return false } for _, container := range containers { status := integ.GetRowValues(container)[1] if status != "RUNNING" { t.Logf("Container is not RUNNING: %s", container) return false } t.Logf("Container is RUNNING: %s", container) } return true }