mirror of
				https://github.com/docker/compose.git
				synced 2025-11-04 13:44:48 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			375 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			375 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package ecs
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"github.com/docker/api/compose"
 | 
						|
	"reflect"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/aws/aws-sdk-go/service/elbv2"
 | 
						|
	"github.com/awslabs/goformation/v4/cloudformation"
 | 
						|
	"github.com/awslabs/goformation/v4/cloudformation/ec2"
 | 
						|
	"github.com/awslabs/goformation/v4/cloudformation/ecs"
 | 
						|
	"github.com/awslabs/goformation/v4/cloudformation/elasticloadbalancingv2"
 | 
						|
	"github.com/awslabs/goformation/v4/cloudformation/iam"
 | 
						|
	"github.com/awslabs/goformation/v4/cloudformation/logs"
 | 
						|
	"github.com/compose-spec/compose-go/cli"
 | 
						|
	"github.com/compose-spec/compose-go/loader"
 | 
						|
	"github.com/compose-spec/compose-go/types"
 | 
						|
	"gotest.tools/v3/assert"
 | 
						|
	"gotest.tools/v3/golden"
 | 
						|
)
 | 
						|
 | 
						|
func TestSimpleConvert(t *testing.T) {
 | 
						|
	project := load(t, "testdata/input/simple-single-service.yaml")
 | 
						|
	result := convertResultAsString(t, project)
 | 
						|
	expected := "simple/simple-cloudformation-conversion.golden"
 | 
						|
	golden.Assert(t, result, expected)
 | 
						|
}
 | 
						|
 | 
						|
func TestLogging(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  foo:
 | 
						|
    image: hello_world
 | 
						|
    logging:
 | 
						|
      options:
 | 
						|
        awslogs-datetime-pattern: "FOO"
 | 
						|
 | 
						|
x-aws-logs_retention: 10
 | 
						|
`)
 | 
						|
	def := template.Resources["FooTaskDefinition"].(*ecs.TaskDefinition)
 | 
						|
	logging := def.ContainerDefinitions[0].LogConfiguration
 | 
						|
	assert.Equal(t, logging.Options["awslogs-datetime-pattern"], "FOO")
 | 
						|
 | 
						|
	logGroup := template.Resources["LogGroup"].(*logs.LogGroup)
 | 
						|
	assert.Equal(t, logGroup.RetentionInDays, 10)
 | 
						|
}
 | 
						|
 | 
						|
func TestEnvFile(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  foo:
 | 
						|
    image: hello_world
 | 
						|
    env_file:
 | 
						|
      - testdata/input/envfile
 | 
						|
`)
 | 
						|
	def := template.Resources["FooTaskDefinition"].(*ecs.TaskDefinition)
 | 
						|
	env := def.ContainerDefinitions[0].Environment
 | 
						|
	var found bool
 | 
						|
	for _, pair := range env {
 | 
						|
		if pair.Name == "FOO" {
 | 
						|
			assert.Equal(t, pair.Value, "BAR")
 | 
						|
			found = true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	assert.Check(t, found, "environment variable FOO not set")
 | 
						|
}
 | 
						|
 | 
						|
func TestEnvFileAndEnv(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  foo:
 | 
						|
    image: hello_world
 | 
						|
    env_file:
 | 
						|
      - testdata/input/envfile
 | 
						|
    environment:
 | 
						|
      - "FOO=ZOT"
 | 
						|
`)
 | 
						|
	def := template.Resources["FooTaskDefinition"].(*ecs.TaskDefinition)
 | 
						|
	env := def.ContainerDefinitions[0].Environment
 | 
						|
	var found bool
 | 
						|
	for _, pair := range env {
 | 
						|
		if pair.Name == "FOO" {
 | 
						|
			assert.Equal(t, pair.Value, "ZOT")
 | 
						|
			found = true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	assert.Check(t, found, "environment variable FOO not set")
 | 
						|
}
 | 
						|
 | 
						|
func TestRollingUpdateLimits(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  foo:
 | 
						|
    image: hello_world
 | 
						|
    deploy:
 | 
						|
      replicas: 4 
 | 
						|
      update_config:
 | 
						|
        parallelism: 2
 | 
						|
`)
 | 
						|
	service := template.Resources["FooService"].(*ecs.Service)
 | 
						|
	assert.Check(t, service.DeploymentConfiguration.MaximumPercent == 150)
 | 
						|
	assert.Check(t, service.DeploymentConfiguration.MinimumHealthyPercent == 50)
 | 
						|
}
 | 
						|
 | 
						|
func TestRollingUpdateExtension(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  foo:
 | 
						|
    image: hello_world
 | 
						|
    deploy:
 | 
						|
      update_config:
 | 
						|
        x-aws-min_percent: 25
 | 
						|
        x-aws-max_percent: 125
 | 
						|
`)
 | 
						|
	service := template.Resources["FooService"].(*ecs.Service)
 | 
						|
	assert.Check(t, service.DeploymentConfiguration.MaximumPercent == 125)
 | 
						|
	assert.Check(t, service.DeploymentConfiguration.MinimumHealthyPercent == 25)
 | 
						|
}
 | 
						|
 | 
						|
func TestRolePolicy(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  foo:
 | 
						|
    image: hello_world
 | 
						|
    x-aws-pull_credentials: "secret"
 | 
						|
`)
 | 
						|
	role := template.Resources["FooTaskExecutionRole"].(*iam.Role)
 | 
						|
	assert.Check(t, role != nil)
 | 
						|
	assert.Check(t, role.ManagedPolicyArns[0] == ECSTaskExecutionPolicy)
 | 
						|
	assert.Check(t, role.ManagedPolicyArns[1] == ECRReadOnlyPolicy)
 | 
						|
	// We expect an extra policy has been created for x-aws-pull_credentials
 | 
						|
	assert.Check(t, len(role.Policies) == 1)
 | 
						|
	policy := role.Policies[0].PolicyDocument.(*PolicyDocument)
 | 
						|
	expected := []string{"secretsmanager:GetSecretValue", "ssm:GetParameters", "kms:Decrypt"}
 | 
						|
	assert.DeepEqual(t, expected, policy.Statement[0].Action)
 | 
						|
	assert.DeepEqual(t, []string{"secret"}, policy.Statement[0].Resource)
 | 
						|
}
 | 
						|
 | 
						|
func TestMapNetworksToSecurityGroups(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  test:
 | 
						|
    image: hello_world
 | 
						|
    networks:
 | 
						|
      - front-tier
 | 
						|
      - back-tier
 | 
						|
 | 
						|
networks:
 | 
						|
  front-tier:
 | 
						|
    name: public
 | 
						|
  back-tier:
 | 
						|
    internal: true
 | 
						|
`)
 | 
						|
	assert.Check(t, template.Resources["TestPublicNetwork"] != nil)
 | 
						|
	assert.Check(t, template.Resources["TestBacktierNetwork"] != nil)
 | 
						|
	assert.Check(t, template.Resources["TestBacktierNetworkIngress"] != nil)
 | 
						|
	ingress := template.Resources["TestPublicNetworkIngress"].(*ec2.SecurityGroupIngress)
 | 
						|
	assert.Check(t, ingress != nil)
 | 
						|
	assert.Check(t, ingress.SourceSecurityGroupId == cloudformation.Ref("TestPublicNetwork"))
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
func TestLoadBalancerTypeApplication(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test123456789009876543211234567890", `
 | 
						|
services:
 | 
						|
  test:
 | 
						|
    image: nginx
 | 
						|
    ports:
 | 
						|
      - 80:80
 | 
						|
`)
 | 
						|
	lb := template.Resources["TestLoadBalancer"].(*elasticloadbalancingv2.LoadBalancer)
 | 
						|
	assert.Check(t, lb != nil)
 | 
						|
	assert.Check(t, len(lb.Name) <= 32)
 | 
						|
	assert.Check(t, lb.Type == elbv2.LoadBalancerTypeEnumApplication)
 | 
						|
	assert.Check(t, len(lb.SecurityGroups) > 0)
 | 
						|
}
 | 
						|
 | 
						|
func TestNoLoadBalancerIfNoPortExposed(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  test:
 | 
						|
    image: nginx
 | 
						|
  foo:
 | 
						|
    image: bar
 | 
						|
`)
 | 
						|
	for _, r := range template.Resources {
 | 
						|
		assert.Check(t, r.AWSCloudFormationType() != "AWS::ElasticLoadBalancingV2::TargetGroup")
 | 
						|
		assert.Check(t, r.AWSCloudFormationType() != "AWS::ElasticLoadBalancingV2::Listener")
 | 
						|
		assert.Check(t, r.AWSCloudFormationType() != "AWS::ElasticLoadBalancingV2::LoadBalancer")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestServiceReplicas(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  test:
 | 
						|
    image: nginx
 | 
						|
    deploy:
 | 
						|
      replicas: 10
 | 
						|
`)
 | 
						|
	s := template.Resources["TestService"].(*ecs.Service)
 | 
						|
	assert.Check(t, s != nil)
 | 
						|
	assert.Check(t, s.DesiredCount == 10)
 | 
						|
}
 | 
						|
 | 
						|
func TestTaskSizeConvert(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  test:
 | 
						|
    image: nginx
 | 
						|
    deploy:
 | 
						|
      resources:
 | 
						|
        limits:
 | 
						|
          cpus: '0.5'
 | 
						|
          memory: 2048M
 | 
						|
        reservations:
 | 
						|
          cpus: '0.5'
 | 
						|
          memory: 2048M
 | 
						|
`)
 | 
						|
	def := template.Resources["TestTaskDefinition"].(*ecs.TaskDefinition)
 | 
						|
	assert.Equal(t, def.Cpu, "512")
 | 
						|
	assert.Equal(t, def.Memory, "2048")
 | 
						|
 | 
						|
	template = convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  test:
 | 
						|
    image: nginx
 | 
						|
    deploy:
 | 
						|
      resources:
 | 
						|
        limits:
 | 
						|
          cpus: '4'
 | 
						|
          memory: 8192M
 | 
						|
        reservations:
 | 
						|
          cpus: '4'
 | 
						|
          memory: 8192M
 | 
						|
`)
 | 
						|
	def = template.Resources["TestTaskDefinition"].(*ecs.TaskDefinition)
 | 
						|
	assert.Equal(t, def.Cpu, "4096")
 | 
						|
	assert.Equal(t, def.Memory, "8192")
 | 
						|
}
 | 
						|
func TestTaskSizeConvertFailure(t *testing.T) {
 | 
						|
	model := loadConfig(t, "test", `
 | 
						|
services:
 | 
						|
  test:
 | 
						|
    image: nginx
 | 
						|
    deploy:
 | 
						|
      resources:
 | 
						|
        limits:
 | 
						|
          cpus: '0.5'
 | 
						|
          memory: 2043248M
 | 
						|
`)
 | 
						|
	backend := &ecsAPIService{}
 | 
						|
	_, err := backend.convert(model)
 | 
						|
	assert.ErrorContains(t, err, "the resources requested are not supported by ECS/Fargate")
 | 
						|
}
 | 
						|
 | 
						|
func TestLoadBalancerTypeNetwork(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  test:
 | 
						|
    image: nginx
 | 
						|
    ports:
 | 
						|
      - 80:80
 | 
						|
      - 88:88
 | 
						|
`)
 | 
						|
	lb := template.Resources["TestLoadBalancer"].(*elasticloadbalancingv2.LoadBalancer)
 | 
						|
	assert.Check(t, lb != nil)
 | 
						|
	assert.Check(t, lb.Type == elbv2.LoadBalancerTypeEnumNetwork)
 | 
						|
}
 | 
						|
 | 
						|
func TestServiceMapping(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  test:
 | 
						|
    image: "image"
 | 
						|
    command: "command"
 | 
						|
    entrypoint: "entrypoint"
 | 
						|
    environment:
 | 
						|
      - "FOO=BAR"
 | 
						|
    cap_add:
 | 
						|
      - SYS_PTRACE
 | 
						|
    cap_drop:
 | 
						|
      - SYSLOG
 | 
						|
    init: true
 | 
						|
    user: "user"
 | 
						|
    working_dir: "working_dir"
 | 
						|
`)
 | 
						|
	def := template.Resources["TestTaskDefinition"].(*ecs.TaskDefinition)
 | 
						|
	container := def.ContainerDefinitions[0]
 | 
						|
	assert.Equal(t, container.Image, "image")
 | 
						|
	assert.Equal(t, container.Command[0], "command")
 | 
						|
	assert.Equal(t, container.EntryPoint[0], "entrypoint")
 | 
						|
	assert.Equal(t, get(container.Environment, "FOO"), "BAR")
 | 
						|
	assert.Check(t, container.LinuxParameters.InitProcessEnabled)
 | 
						|
	assert.Equal(t, container.LinuxParameters.Capabilities.Add[0], "SYS_PTRACE")
 | 
						|
	assert.Equal(t, container.LinuxParameters.Capabilities.Drop[0], "SYSLOG")
 | 
						|
	assert.Equal(t, container.User, "user")
 | 
						|
	assert.Equal(t, container.WorkingDirectory, "working_dir")
 | 
						|
}
 | 
						|
 | 
						|
func get(l []ecs.TaskDefinition_KeyValuePair, name string) string {
 | 
						|
	for _, e := range l {
 | 
						|
		if e.Name == name {
 | 
						|
			return e.Value
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return ""
 | 
						|
}
 | 
						|
 | 
						|
func TestResourcesHaveProjectTagSet(t *testing.T) {
 | 
						|
	template := convertYaml(t, "test", `
 | 
						|
services:
 | 
						|
  test:
 | 
						|
    image: nginx
 | 
						|
    ports:
 | 
						|
      - 80:80
 | 
						|
      - 88:88
 | 
						|
`)
 | 
						|
	for _, r := range template.Resources {
 | 
						|
		tags := reflect.Indirect(reflect.ValueOf(r)).FieldByName("Tags")
 | 
						|
		if !tags.IsValid() {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		for i := 0; i < tags.Len(); i++ {
 | 
						|
			k := tags.Index(i).FieldByName("Key").String()
 | 
						|
			v := tags.Index(i).FieldByName("Value").String()
 | 
						|
			if k == compose.ProjectTag {
 | 
						|
				assert.Equal(t, v, "Test")
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func convertResultAsString(t *testing.T, project *types.Project) string {
 | 
						|
	backend := &ecsAPIService{}
 | 
						|
	template, err := backend.convert(project)
 | 
						|
	assert.NilError(t, err)
 | 
						|
	resultAsJSON, err := Marshall(template)
 | 
						|
	assert.NilError(t, err)
 | 
						|
	return fmt.Sprintf("%s\n", string(resultAsJSON))
 | 
						|
}
 | 
						|
 | 
						|
func load(t *testing.T, paths ...string) *types.Project {
 | 
						|
	options := cli.ProjectOptions{
 | 
						|
		Name:        t.Name(),
 | 
						|
		ConfigPaths: paths,
 | 
						|
	}
 | 
						|
	project, err := cli.ProjectFromOptions(&options)
 | 
						|
	assert.NilError(t, err)
 | 
						|
	return project
 | 
						|
}
 | 
						|
 | 
						|
func convertYaml(t *testing.T, name string, yaml string) *cloudformation.Template {
 | 
						|
	project := loadConfig(t, name, yaml)
 | 
						|
	backend := &ecsAPIService{}
 | 
						|
	template, err := backend.convert(project)
 | 
						|
	assert.NilError(t, err)
 | 
						|
	return template
 | 
						|
}
 | 
						|
 | 
						|
func loadConfig(t *testing.T, name string, yaml string) *types.Project {
 | 
						|
	dict, err := loader.ParseYAML([]byte(yaml))
 | 
						|
	assert.NilError(t, err)
 | 
						|
	model, err := loader.Load(types.ConfigDetails{
 | 
						|
		ConfigFiles: []types.ConfigFile{
 | 
						|
			{Config: dict},
 | 
						|
		},
 | 
						|
	}, func(options *loader.Options) {
 | 
						|
		options.Name = "Test"
 | 
						|
	})
 | 
						|
	assert.NilError(t, err)
 | 
						|
	return model
 | 
						|
}
 |