// Copyright 2015-2017 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 utils

import (
	"io/ioutil"
	"os"
	"reflect"
	"testing"

	"github.com/aws/amazon-ecs-cli/ecs-cli/modules/cli/compose/adapter"
	"github.com/aws/amazon-ecs-cli/ecs-cli/modules/utils/regcredio"
	"github.com/aws/amazon-ecs-cli/ecs-cli/modules/utils/value"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ecs"
	"github.com/docker/libcompose/yaml"
	"github.com/stretchr/testify/assert"
)

const (
	projectName    = "ProjectName"
	containerPath  = "/tmp/cache"
	containerPath2 = "/tmp/cache2"
	hostPath       = "./cache"
	namedVolume    = "named_volume"
	namedVolume2   = "named_volume2"
	namedVolume3   = "named_volume3"
)

var defaultNetwork = &yaml.Network{
	Name:     "default",
	RealName: "project_default",
}

// TODO Extract test docker file and use in test (to avoid gaps between parse and conversion unit tests)
var testContainerConfig = adapter.ContainerConfig{
	Name:    "mysql",
	Command: []string{"cmd"},
	CPU:     int64(131072),
	Devices: []*ecs.Device{
		{
			HostPath:      aws.String("/dev/sda"),
			ContainerPath: aws.String("/dev/sdd"),
			Permissions:   aws.StringSlice([]string{"read"}),
		},
		{
			HostPath: aws.String("/dev/sda"),
		},
	},
	DNSSearchDomains: []string{"search.example.com"},
	DNSServers:       []string{"1.2.3.4"},
	DockerLabels: map[string]*string{
		"label1":         aws.String(""),
		"com.foo.label2": aws.String("value"),
	},
	DockerSecurityOptions: []string{"label:type:test_virt"},
	Entrypoint:            []string{"/code/entrypoint.sh"},
	Environment: []*ecs.KeyValuePair{
		{
			Name:  aws.String("rails_env"),
			Value: aws.String("development"),
		},
	},
	ExtraHosts: []*ecs.HostEntry{
		{
			Hostname:  aws.String("test.local"),
			IpAddress: aws.String("127.10.10.10"),
		},
	},
	HealthCheck: &ecs.HealthCheck{
		Command:     aws.StringSlice([]string{"CMD-SHELL", "curl -f http://localhost"}),
		Interval:    aws.Int64(int64(70)),
		Timeout:     aws.Int64(int64(15)),
		Retries:     aws.Int64(int64(5)),
		StartPeriod: aws.Int64(int64(40)),
	},
	Hostname: "foobarbaz",
	Image:    "testimage",
	Links:    []string{"container1"},
	LogConfiguration: &ecs.LogConfiguration{
		LogDriver: aws.String("json-file"),
		Options: map[string]*string{
			"max-file": aws.String("50"),
			"max-size": aws.String("50k"),
		},
	},
	Memory:            int64(131072),
	MemoryReservation: int64(65536),
	MountPoints: []*ecs.MountPoint{
		{
			ContainerPath: aws.String("./code"),
			ReadOnly:      aws.Bool(false),
			SourceVolume:  aws.String("volume-0"),
		},
	},
	PortMappings: []*ecs.PortMapping{
		{
			ContainerPort: aws.Int64(5000),
			HostPort:      aws.Int64(5000),
			Protocol:      aws.String("tcp"),
		},
	},
	Privileged:     true,
	PseudoTerminal: true,
	ReadOnly:       true,
	ShmSize:        int64(128), // Realistically, we expect customers to specify sizes larger than the default of 64M
	Tmpfs: []*ecs.Tmpfs{
		{
			ContainerPath: aws.String("/tmp"),
			MountOptions:  aws.StringSlice([]string{"ro", "rw"}),
			Size:          aws.Int64(64),
		},
	},
	Ulimits: []*ecs.Ulimit{
		{
			Name:      aws.String("nofile"),
			HardLimit: aws.Int64(40000),
			SoftLimit: aws.Int64(20000),
		},
	},
	VolumesFrom: []*ecs.VolumeFrom{
		{
			ReadOnly:        aws.Bool(true),
			SourceContainer: aws.String("web"),
		},
	},
	User:             "user",
	WorkingDirectory: "/var",
}

func TestConvertToTaskDefinition(t *testing.T) {
	// Expected values on container
	name := "mysql"
	cpu := int64(131072) // 128 * 1024
	command := []string{"cmd"}
	devices := []*ecs.Device{
		{
			HostPath:      aws.String("/dev/sda"),
			ContainerPath: aws.String("/dev/sdd"),
			Permissions:   aws.StringSlice([]string{"read"}),
		},
		{
			HostPath: aws.String("/dev/sda"),
		},
	}
	entryPoint := []string{"/code/entrypoint.sh"}
	env := []*ecs.KeyValuePair{
		{
			Name:  aws.String("rails_env"),
			Value: aws.String("development"),
		},
	}
	extraHosts := []*ecs.HostEntry{
		{
			Hostname:  aws.String("test.local"),
			IpAddress: aws.String("127.10.10.10"),
		},
	}
	dnsSearchDomains := []string{"search.example.com"}
	dnsServers := []string{"1.2.3.4"}
	dockerLabels := map[string]string{
		"label1":         "",
		"com.foo.label2": "value",
	}
	hostname := "foobarbaz"
	image := "testimage"
	links := []string{"container1"}
	logOpts := map[string]*string{
		"max-file": aws.String("50"),
		"max-size": aws.String("50k"),
	}
	logging := &ecs.LogConfiguration{
		LogDriver: aws.String("json-file"),
		Options:   logOpts,
	}
	memory := int64(131072) // 128 GiB = 131072 MiB
	memoryReservation := int64(65536)
	mountPoints := []*ecs.MountPoint{
		{
			ContainerPath: aws.String("./code"),
			ReadOnly:      aws.Bool(false),
			SourceVolume:  aws.String("volume-0"),
		},
	}
	ports := []*ecs.PortMapping{
		{
			ContainerPort: aws.Int64(5000),
			HostPort:      aws.Int64(5000),
			Protocol:      aws.String("tcp"),
		},
	}
	privileged := true
	pseudoterminal := true
	readOnly := true
	securityOpts := []string{"label:type:test_virt"}
	shmSize := int64(128)
	tmpfs := []*ecs.Tmpfs{
		{
			ContainerPath: aws.String("/tmp"),
			MountOptions:  aws.StringSlice([]string{"ro", "rw"}),
			Size:          aws.Int64(64),
		},
	}
	user := "user"
	ulimits := []*ecs.Ulimit{
		{
			Name:      aws.String("nofile"),
			HardLimit: aws.Int64(40000),
			SoftLimit: aws.Int64(20000),
		},
	}
	volumesFrom := []*ecs.VolumeFrom{
		{
			ReadOnly:        aws.Bool(true),
			SourceContainer: aws.String("web"),
		},
	}
	workingDir := "/var"

	// Expected values on task definition
	taskRoleArn := "arn:aws:iam::123456789012:role/my_role"
	// TODO add top-level volumes

	// convert
	taskDefinition, err := convertToTaskDefinitionForTest(t, []adapter.ContainerConfig{testContainerConfig}, taskRoleArn, "", nil, nil)
	assert.NoError(t, err, "Unexpected error converting Task Definition")

	containerDef := *taskDefinition.ContainerDefinitions[0]

	// verify task def fields
	assert.Equal(t, taskRoleArn, aws.StringValue(taskDefinition.TaskRoleArn), "Expected taskRoleArn to match")
	assert.Empty(t, taskDefinition.RequiresCompatibilities, "Did not expect RequiresCompatibilities to be set")
	// PID and IPC should be unset
	assert.Nil(t, taskDefinition.IpcMode, "Expected IpcMode to be nil")
	assert.Nil(t, taskDefinition.PidMode, "Expected PidMode to be nil")

	// verify container def fields
	assert.Equal(t, aws.String(name), containerDef.Name, "Expected container def name to match")
	assert.Equal(t, aws.StringSlice(command), containerDef.Command, "Expected container def command to match")
	assert.Equal(t, aws.Int64(cpu), containerDef.Cpu, "Expected container def cpu to match")
	assert.ElementsMatch(t, devices, containerDef.LinuxParameters.Devices, "Expected container def devices to match")
	assert.Equal(t, aws.StringSlice(dnsSearchDomains), containerDef.DnsSearchDomains, "Expected container def DNS Search Domains to match")
	assert.Equal(t, aws.StringSlice(dnsServers), containerDef.DnsServers, "Expected container def DNS Servers to match")
	assert.Equal(t, aws.StringMap(dockerLabels), containerDef.DockerLabels, "Expected container def Docker labels to match")
	assert.Equal(t, aws.StringSlice(securityOpts), containerDef.DockerSecurityOptions, "Expected container def docker security options to match")
	assert.Equal(t, env, containerDef.Environment, "Expected Environment to match")
	assert.Equal(t, aws.StringSlice(entryPoint), containerDef.EntryPoint, "Expected EntryPoint to be match")
	assert.Equal(t, extraHosts, containerDef.ExtraHosts, "Expected ExtraHosts to match")
	assert.Equal(t, aws.String(hostname), containerDef.Hostname, "Expected container def hostname to match")
	assert.Equal(t, aws.String(image), containerDef.Image, "Expected container def image to match")
	assert.Equal(t, aws.StringSlice(links), containerDef.Links, "Expected container def links to match")
	assert.Equal(t, logging, containerDef.LogConfiguration, "Expected LogConfiguration to match")
	assert.Equal(t, aws.Int64(memory), containerDef.Memory, "Expected container def memory to match")
	assert.Equal(t, aws.Int64(memoryReservation), containerDef.MemoryReservation, "Expected container def memoryReservation to match")
	assert.Equal(t, mountPoints, containerDef.MountPoints, "Expected MountPoints to match")

	assert.Equal(t, ports, containerDef.PortMappings, "Expected PortMappings to match")

	assert.Equal(t, aws.Bool(privileged), containerDef.Privileged, "Expected container def privileged to match")
	assert.Equal(t, aws.Bool(pseudoterminal), containerDef.PseudoTerminal, "Expected container def pseudoterminal to match")
	assert.Equal(t, aws.Bool(readOnly), containerDef.ReadonlyRootFilesystem, "Expected container def ReadonlyRootFilesystem to match")
	assert.Equal(t, aws.Int64(shmSize), containerDef.LinuxParameters.SharedMemorySize, "Expected sharedMemorySize to match")
	assert.ElementsMatch(t, tmpfs, containerDef.LinuxParameters.Tmpfs, "Expected tmpfs to match")
	assert.Equal(t, aws.String(user), containerDef.User, "Expected container def user to match")
	assert.ElementsMatch(t, ulimits, containerDef.Ulimits, "Expected Ulimits to match")
	assert.Equal(t, volumesFrom, containerDef.VolumesFrom, "Expected VolumesFrom to match")
	assert.Equal(t, aws.String(workingDir), containerDef.WorkingDirectory, "Expected container def WorkingDirectory to match")
	assert.Equal(t, testContainerConfig.HealthCheck, containerDef.HealthCheck)

	// If no containers are specified as being essential, all containers
	// are marked "essential"
	for _, container := range taskDefinition.ContainerDefinitions {
		assert.True(t, aws.BoolValue(container.Essential), "Expected essential to be true")
	}
}

func TestConvertToTaskDefinitionWithNoSharedMemorySize(t *testing.T) {
	containerConfig := adapter.ContainerConfig{
		ShmSize: int64(0),
	}

	taskDefinition, err := convertToTaskDefinitionForTest(t, []adapter.ContainerConfig{containerConfig}, "", "", nil, nil)
	assert.NoError(t, err, "Unexpected error converting Task Definition")

	containerDef := *taskDefinition.ContainerDefinitions[0]

	assert.Nil(t, containerDef.LinuxParameters.SharedMemorySize, "Expected sharedMemorySize to be null")
}

func TestConvertToTaskDefinitionWithNoTmpfs(t *testing.T) {
	containerConfig := adapter.ContainerConfig{
		Tmpfs: nil,
	}

	taskDefinition, err := convertToTaskDefinitionForTest(t, []adapter.ContainerConfig{containerConfig}, "", "", nil, nil)
	assert.NoError(t, err, "Unexpected error converting Task Definition")

	containerDef := *taskDefinition.ContainerDefinitions[0]

	assert.Nil(t, containerDef.LinuxParameters.Tmpfs, "Expected Tmpfs to be null")
}

func TestConvertToTaskDefinitionWithBlankHostname(t *testing.T) {
	containerConfig := adapter.ContainerConfig{
		Hostname: "",
	}

	taskDefinition, err := convertToTaskDefinitionForTest(t, []adapter.ContainerConfig{containerConfig}, "", "", nil, nil)
	assert.NoError(t, err, "Unexpected error converting Task Definition")

	containerDef := *taskDefinition.ContainerDefinitions[0]

	assert.Nil(t, containerDef.Hostname, "Expected Hostname to be nil")
}

// TODO add test for nil cap add/cap drop

// Test Launch Types
func TestConvertToTaskDefinitionLaunchTypeEmpty(t *testing.T) {
	containerConfig := adapter.ContainerConfig{}

	taskDefinition, err := convertToTaskDefinitionForTest(t, []adapter.ContainerConfig{containerConfig}, "", "", nil, nil)
	assert.NoError(t, err, "Unexpected error converting Task Definition")
	if len(taskDefinition.RequiresCompatibilities) > 0 {
		t.Error("Did not expect RequiresCompatibilities to be set")
	}
}

func TestConvertToTaskDefinitionLaunchTypeEC2(t *testing.T) {
	containerConfig := adapter.ContainerConfig{}

	taskDefinition, err := convertToTaskDefinitionForTest(t, []adapter.ContainerConfig{containerConfig}, "", "EC2", nil, nil)
	assert.NoError(t, err, "Unexpected error converting Task Definition")

	if len(taskDefinition.RequiresCompatibilities) != 1 {
		t.Error("Expected exactly one required compatibility to be set.")
	}
	assert.Equal(t, "EC2", aws.StringValue(taskDefinition.RequiresCompatibilities[0]))
}

func TestConvertToTaskDefinitionLaunchTypeFargate(t *testing.T) {
	containerConfig := adapter.ContainerConfig{}

	taskDefinition, err := convertToTaskDefinitionForTest(t, []adapter.ContainerConfig{containerConfig}, "", "FARGATE", nil, nil)
	assert.NoError(t, err, "Unexpected error converting Task Definition")

	if len(taskDefinition.RequiresCompatibilities) != 1 {
		t.Error("Expected exactly one required compatibility to be set.")
	}
	assert.Equal(t, "FARGATE", aws.StringValue(taskDefinition.RequiresCompatibilities[0]))
}

// Tests for ConvertToTaskDefinition with ECS Params
func TestConvertToTaskDefinitionWithECSParams_ComposeMemoryLessThanMemoryRes(t *testing.T) {
	// set up containerConfig w/o value for Memory
	containerConfig := &adapter.ContainerConfig{
		Name:   "web",
		Image:  "httpd",
		CPU:    int64(5),
		Memory: int64(512),
	}

	// define ecs-params value we expect to be present in final containerDefinition
	ecsParamsString := `version: 1
task_definition:
  services:
    web:
      mem_reservation: 1g`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	_, err = convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	assert.Error(t, err, "Expected error because memory reservation is greater than memory limit")
}

func testContainerConfigs(names []string) []adapter.ContainerConfig {
	containerConfigs := []adapter.ContainerConfig{}
	for _, name := range names {
		config := adapter.ContainerConfig{Name: name}
		containerConfigs = append(containerConfigs, config)
	}

	return containerConfigs
}

func TestConvertToTaskDefinitionWithECSParams(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	ecsParamsString := `version: 1
task_definition:
  ecs_network_mode: host
  task_role_arn: arn:aws:iam::123456789012:role/my_role`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs fields tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs fields tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	if assert.NoError(t, err) {
		// PID and IPC should be unset
		assert.Nil(t, taskDefinition.IpcMode, "Expected IpcMode to be nil")
		assert.Nil(t, taskDefinition.PidMode, "Expected PidMode to be nil")

		assert.Equal(t, "host", aws.StringValue(taskDefinition.NetworkMode), "Expected network mode to match")
		assert.Equal(t, "arn:aws:iam::123456789012:role/my_role", aws.StringValue(taskDefinition.TaskRoleArn), "Expected task role ARN to match")

		// If no containers are specified as being essential, all
		// containers are marked "essential"
		for _, container := range taskDefinition.ContainerDefinitions {
			assert.True(t, aws.BoolValue(container.Essential), "Expected essential to be true")
		}
	}
}

func TestConvertToTaskDefinitionWithECSParams_AllFields(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	ecsParamsString := `version: 1
task_definition:
  ecs_network_mode: awsvpc
  task_role_arn: arn:aws:iam::123456789012:role/tweedledee
  services:
    mysql:
      essential: false
      init_process_enabled: true
      repository_credentials:
        credentials_parameter: arn:aws:secretsmanager:1234567890:secret:test-secret
  task_size:
    mem_limit: 5Gb
    cpu_limit: 256`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs fields tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs fields tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	mysql := findContainerByName("mysql", containerDefs)
	wordpress := findContainerByName("wordpress", containerDefs)

	if assert.NoError(t, err) {
		assert.Equal(t, "awsvpc", aws.StringValue(taskDefinition.NetworkMode), "Expected network mode to match")
		assert.Equal(t, "arn:aws:iam::123456789012:role/tweedledee", aws.StringValue(taskDefinition.TaskRoleArn), "Expected task role ARN to match")

		assert.False(t, aws.BoolValue(mysql.Essential), "Expected container with name: '%v' to be false", *mysql.Name)
		assert.Equal(t, "256", aws.StringValue(taskDefinition.Cpu), "Expected CPU to match")
		assert.Equal(t, "5Gb", aws.StringValue(taskDefinition.Memory), "Expected CPU to match")
		assert.True(t, aws.BoolValue(wordpress.Essential), "Expected container with name: '%v' to be true", *wordpress.Name)
		assert.Equal(t, "arn:aws:secretsmanager:1234567890:secret:test-secret", aws.StringValue(mysql.RepositoryCredentials.CredentialsParameter), "Expected CredentialsParameter to match")
		assert.Equal(t, true, aws.BoolValue(mysql.LinuxParameters.InitProcessEnabled), "Expected container def initProcessEnabled to match")
	}
}

func TestConvertToTaskDefinitionWithECSParams_Essential_OneContainer(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	ecsParamsString := `version: 1
task_definition:
  services:
    mysql:
      essential: false`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs fields tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs fields tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	mysql := findContainerByName("mysql", containerDefs)
	wordpress := findContainerByName("wordpress", containerDefs)

	if assert.NoError(t, err) {
		assert.False(t, aws.BoolValue(mysql.Essential), "Expected container with name: '%v' to be false", *mysql.Name)
		assert.True(t, aws.BoolValue(wordpress.Essential), "Expected container with name: '%v' to be true", *wordpress.Name)
	}
}

func TestConvertToTaskDefinitionWithECSParams_EssentialExplicitlyMarkedTrue(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	ecsParamsString := `version: 1
task_definition:
  services:
    mysql:
      essential: true
    wordpress:
      essential: true`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs fields tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs fields tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	mysql := findContainerByName("mysql", containerDefs)
	wordpress := findContainerByName("wordpress", containerDefs)

	if assert.NoError(t, err) {
		assert.True(t, aws.BoolValue(mysql.Essential), "Expected container with name: '%v' to be true", *mysql.Name)
		assert.True(t, aws.BoolValue(wordpress.Essential), "Expected container with name: '%v' to be true", *wordpress.Name)
	}
}

func TestConvertToTaskDefinitionWithECSParams_EssentialExplicitlyMarked(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	ecsParamsString := `version: 1
task_definition:
  services:
    mysql:
      essential: false
    wordpress:
      essential: true`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs fields tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs fields tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	mysql := findContainerByName("mysql", containerDefs)
	wordpress := findContainerByName("wordpress", containerDefs)

	if assert.NoError(t, err) {
		assert.False(t, aws.BoolValue(mysql.Essential), "Expected container with name: '%v' to be false", *mysql.Name)
		assert.True(t, aws.BoolValue(wordpress.Essential), "Expected container with name: '%v' to be true", *wordpress.Name)
	}
}

func TestConvertToTaskDefinitionWithECSParams_EssentialBlankForOneService(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	ecsParamsString := `version: 1
task_definition:
  ecs_network_mode: host
  task_role_arn: arn:aws:iam::123456789012:role/my_role
  services:
    wordpress:`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs fields tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs fields tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	mysql := findContainerByName("mysql", containerDefs)
	wordpress := findContainerByName("wordpress", containerDefs)

	if assert.NoError(t, err) {
		assert.True(t, aws.BoolValue(mysql.Essential), "Expected container with name: '%v' to be true", *mysql.Name)
		assert.False(t, aws.BoolValue(wordpress.Essential), "Expected container with name: '%v' to be false", *wordpress.Name)
	}
}

func TestConvertToTaskDefinitionWithECSParams_EssentialBlankForAllServices(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	ecsParamsString := `version: 1
task_definition:
  ecs_network_mode: host
  task_role_arn: arn:aws:iam::123456789012:role/my_role
  services:
    mysql:
    wordpress:`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs fields tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs fields tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	_, err = convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	assert.Error(t, err, "Expected error if no containers are marked essential")
}

func TestConvertToTaskDefinitionWithECSParams_AllContainersMarkedNotEssential(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	ecsParamsString := `version: 1
task_definition:
  services:
    mysql:
      essential: false
    wordpress:
      essential: false`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs fields tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs fields tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	_, err = convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	assert.Error(t, err, "Expected error if no containers are marked essential")
}

func TestConvertToTaskDefinitionWithECSParams_EssentialDefaultsToTrueWhenNoServicesSpecified(t *testing.T) {
	// We expect essential to be set to be true in the converter
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	ecsParamsString := `version: 1
task_definition:
  ecs_network_mode: host`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs fields tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs fields tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)
	assert.NoError(t, err, "Unexpected error when no containers are marked essential")

	mysql := findContainerByName("mysql", taskDefinition.ContainerDefinitions)
	assert.True(t, *mysql.Essential, "Expected mysql to be essential")
	wordpress := findContainerByName("wordpress", taskDefinition.ContainerDefinitions)
	assert.True(t, *wordpress.Essential, "Expected wordpressto be essential")
}

func TestConvertToTaskDefinitionWithECSParams_EssentialDefaultsToTrueWhenNotSpecified(t *testing.T) {
	// We expect essential to be set to be true in the unmarshaller
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	ecsParamsString := `version: 1
task_definition:
  ecs_network_mode: host
  services:
    wordpress:
      mem_limit: 1000000
    mysql:
      mem_limit: 1000000`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs fields tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs fields tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	assert.NoError(t, err, "Unexpected error when no containers are marked essential")
	mysql := findContainerByName("mysql", taskDefinition.ContainerDefinitions)
	assert.True(t, *mysql.Essential, "Expected mysql to be essential")
	wordpress := findContainerByName("wordpress", taskDefinition.ContainerDefinitions)
	assert.True(t, *wordpress.Essential, "Expected wordpressto be essential")
}

func TestConvertToTaskDefinitionWithECSParamsAndTaskRoleArnFlag(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	ecsParamsString := `version: 1
task_definition:
  ecs_network_mode: host
  task_role_arn: arn:aws:iam::123456789012:role/tweedledee`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs fields tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs fields tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	taskRoleArn := "arn:aws:iam::123456789012:role/tweedledum"

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, taskRoleArn, "", ecsParams, nil)

	if assert.NoError(t, err) {
		assert.Equal(t, "host", aws.StringValue(taskDefinition.NetworkMode), "Expected network mode to match")
		assert.Equal(t, "arn:aws:iam::123456789012:role/tweedledum", aws.StringValue(taskDefinition.TaskRoleArn), "Expected task role arn to match")
	}
}

func TestConvertToTaskDefinitionWithECSParams_ContainerResourcesPresent(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})

	mysqlCPU := int64(100)
	mysqlMem := int64(15)
	mysqlMemRes := int64(10)

	wordpressCPU := int64(4)
	wordpressMem := int64(8)
	wordpressMemRes := int64(5)

	ecsParamsString := `version: 1
task_definition:
  services:
    mysql:
      cpu_shares: 100
      mem_limit: 15m
      mem_reservation: 10m
    wordpress:
      cpu_shares: 4
      mem_limit: 8m
      mem_reservation: 5m`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	mysql := findContainerByName("mysql", containerDefs)
	wordpress := findContainerByName("wordpress", containerDefs)

	if assert.NoError(t, err) {
		assert.Equal(t, mysqlCPU, aws.Int64Value(mysql.Cpu), "Expected CPU to match")
		assert.Equal(t, mysqlMem, aws.Int64Value(mysql.Memory), "Expected Memory to match")
		assert.Equal(t, mysqlMemRes, aws.Int64Value(mysql.MemoryReservation), "Expected MemoryReservation to match")

		assert.Equal(t, wordpressCPU, aws.Int64Value(wordpress.Cpu), "Expected CPU to match")
		assert.Equal(t, wordpressMem, aws.Int64Value(wordpress.Memory), "Expected Memory to match")
		assert.Equal(t, wordpressMemRes, aws.Int64Value(wordpress.MemoryReservation), "Expected MemoryReservation to match")
	}
}

func TestConvertToTaskDefinitionWithECSParams_ContainerResourcesOverrideProvidedVals(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name:              "web",
		Image:             "httpd",
		CPU:               int64(2),
		Memory:            int64(3),
		MemoryReservation: int64(3),
	}

	// define ecs-params values we expect to override containerConfig vals
	webCPU := int64(5)
	webMem := int64(15)
	webMemRes := int64(10)

	ecsParamsString := `version: 1
task_definition:
  services:
    web:
      cpu_shares: 5
      mem_limit: 15m
      mem_reservation: 10m`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	if assert.NoError(t, err) {
		assert.Equal(t, webCPU, aws.Int64Value(web.Cpu), "Expected CPU to match")
		assert.Equal(t, webMem, aws.Int64Value(web.Memory), "Expected Memory to match")
		assert.Equal(t, webMemRes, aws.Int64Value(web.MemoryReservation), "Expected MemoryReservation to match")
	}
}

func TestConvertToTaskDefinitionWithECSParams_NoMemoryProvided(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name:  "web",
		Image: "httpd",
	}

	// define ecs-params values we expect to override containerConfig vals
	webCPU := int64(5)

	ecsParamsString := `version: 1
task_definition:
  services:
    web:
      cpu_shares: 5`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	if assert.NoError(t, err) {
		assert.Equal(t, webCPU, aws.Int64Value(web.Cpu), "Expected CPU to match")
		assert.Equal(t, int64(defaultMemLimit), aws.Int64Value(web.Memory), "Expected Memory to match default")
		assert.Empty(t, aws.Int64Value(web.MemoryReservation), "Expected MemoryReservation to be empty")
	}
}

func TestConvertToTaskDefinitionWithECSParams_MemLimitOnlyProvided(t *testing.T) {
	// set up containerConfig w/o value for Memory
	containerConfig := &adapter.ContainerConfig{
		Name:              "web",
		Image:             "httpd",
		CPU:               int64(5),
		Memory:            int64(15),
		MemoryReservation: int64(10),
	}

	// define ecs-params value we expect to be present in final containerDefinition
	webMem := int64(20)

	ecsParamsString := `version: 1
task_definition:
  services:
    web:
      mem_limit: 20m`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	if assert.NoError(t, err) {
		// check ecs-params override value is present
		assert.Equal(t, webMem, aws.Int64Value(web.Memory), "Expected Memory to match")
		// check config values not present in ecs-params are present
		assert.Equal(t, containerConfig.CPU, aws.Int64Value(web.Cpu), "Expected CPU to match")
		assert.Equal(t, containerConfig.MemoryReservation, aws.Int64Value(web.MemoryReservation), "Expected MemoryReservation to match")
	}
}

func TestConvertToTaskDefinitionWithECSParams_MemReservationOnlyProvided(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"web"})

	// define ecs-params value we expect to be present in final containerDefinition
	webMem := int64(20)

	ecsParamsString := `version: 1
task_definition:
  services:
    web:
      mem_reservation: 20m`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	if assert.NoError(t, err) {
		assert.Equal(t, webMem, aws.Int64Value(web.MemoryReservation), "Expected MemoryReservation to match")
		// check mem_limit not set
		assert.Empty(t, web.Memory, "Expected Memory to be nil")
		// check config values not present in ecs-params are present
		assert.Empty(t, web.Cpu, "Expected CPU to be empty")
	}
}

func TestConvertToTaskDefinitionWithECSParams_MemResGreaterThanMemLimit(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{Name: "web"}
	ecsParamsString := `version: 1
task_definition:
  services:
    web:
      mem_limit: 10m
      mem_reservation: 15m`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	_, err = convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	assert.Error(t, err, "Expected error if mem_reservation was more than mem_limit")
}

func TestConvertToTaskDefinitionWithECSParams_WithTaskSize(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	ecsParamsString := `version: 1
task_definition:
  task_size:
    mem_limit: 10MB
    cpu_limit: 200`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs fields tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs fields tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	if assert.NoError(t, err) {
		assert.Equal(t, "200", aws.StringValue(taskDefinition.Cpu), "Expected CPU to match")
		assert.Equal(t, "10MB", aws.StringValue(taskDefinition.Memory), "Expected CPU to match")
	}
}

func TestConvertToTaskDefinition_MemLimitOnlyProvided(t *testing.T) {
	webMem := int64(1048576)
	containerConfig := adapter.ContainerConfig{
		Name:   "web",
		Memory: webMem,
	}

	taskDefinition, err := convertToTaskDefinitionForTest(t, []adapter.ContainerConfig{containerConfig}, "", "", nil, nil)
	assert.NoError(t, err, "Unexpected error converting Task Definition")

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	assert.Equal(t, webMem, aws.Int64Value(web.Memory), "Expected Memory to match")
	assert.Empty(t, web.MemoryReservation, "Expected MemoryReservation to be empty")
	assert.Empty(t, web.Cpu, "Expected CPU to be empty")
}

func TestConvertToTaskDefinition_MemReservationOnlyProvided(t *testing.T) {
	webMem := int64(1048576)
	containerConfig := adapter.ContainerConfig{
		Name:              "web",
		MemoryReservation: webMem,
	}

	taskDefinition, err := convertToTaskDefinitionForTest(t, []adapter.ContainerConfig{containerConfig}, "", "", nil, nil)
	assert.NoError(t, err, "Unexpected error converting Task Definition")

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	assert.Empty(t, web.Memory, "Expected Memory to be nil")
	assert.Equal(t, webMem, aws.Int64Value(web.MemoryReservation), "Expected MemoryReservation to match")
	assert.Empty(t, web.Cpu, "Expected CPU to be empty")
}

func TestConvertToTaskDefinition_NoMemoryProvided(t *testing.T) {
	containerConfig := adapter.ContainerConfig{
		Name: "web",
	}

	taskDefinition, err := convertToTaskDefinitionForTest(t, []adapter.ContainerConfig{containerConfig}, "", "", nil, nil)
	assert.NoError(t, err, "Unexpected error converting Task Definition")

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	assert.Equal(t, aws.Int64(defaultMemLimit), web.Memory, "Expected Memory to match default")
	assert.Empty(t, web.MemoryReservation, "Expected MemoryReservation to be empty")
	assert.Empty(t, web.Cpu, "Expected CPU to be empty")
}

func TestMemReservationHigherThanMemLimit(t *testing.T) {
	containerConfig := adapter.ContainerConfig{
		Memory:            int64(524288),
		MemoryReservation: int64(1048576),
	}

	volumeConfigs := adapter.NewVolumes()
	containerConfigs := []adapter.ContainerConfig{containerConfig}

	testParams := ConvertTaskDefParams{
		TaskDefName:            projectName,
		TaskRoleArn:            "",
		RequiredCompatibilites: "",
		Volumes:                volumeConfigs,
		ContainerConfigs:       containerConfigs,
		ECSParams:              nil,
	}

	_, err := ConvertToTaskDefinition(testParams)
	assert.EqualError(t, err, "mem_limit must be greater than mem_reservation")
}

func TestConvertToTaskDefinitionWithECSParams_PIDandIPC(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name:  "web",
		Image: "httpd",
	}

	ecsParamsString := `version: 1
task_definition:
  pid_mode: task
  ipc_mode: host`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	if assert.NoError(t, err) {
		assert.Equal(t, "task", aws.StringValue(taskDefinition.PidMode))
		assert.Equal(t, "host", aws.StringValue(taskDefinition.IpcMode))
	}
}

func TestConvertToTaskDefinitionWithVolumes(t *testing.T) {
	volumeConfigs := &adapter.Volumes{
		VolumeWithHost: map[string]string{
			hostPath: containerPath,
		},
		VolumeEmptyHost: []string{namedVolume},
	}

	mountPoints := []*ecs.MountPoint{
		{
			ContainerPath: aws.String("/tmp/cache"),
			ReadOnly:      aws.Bool(false),
			SourceVolume:  aws.String("volume-0"),
		},
		{
			ContainerPath: aws.String("/tmp/cache"),
			ReadOnly:      aws.Bool(false),
			SourceVolume:  aws.String("named_volume"),
		},
	}
	containerConfig := adapter.ContainerConfig{
		MountPoints: mountPoints,
	}

	containerConfigs := []adapter.ContainerConfig{containerConfig}

	host := &ecs.HostVolumeProperties{SourcePath: aws.String(hostPath)}
	expectedVolumes := []*ecs.Volume{
		{
			Host: host,
			Name: aws.String(containerPath),
		},
		{
			Name: aws.String(namedVolume),
		},
	}

	testParams := ConvertTaskDefParams{
		TaskDefName:            projectName,
		TaskRoleArn:            "",
		RequiredCompatibilites: "",
		Volumes:                volumeConfigs,
		ContainerConfigs:       containerConfigs,
		ECSParams:              nil,
	}

	taskDefinition, err := ConvertToTaskDefinition(testParams)
	assert.NoError(t, err, "Unexpected error converting Task Definition")

	actualVolumes := taskDefinition.Volumes
	assert.ElementsMatch(t, expectedVolumes, actualVolumes, "Expected volumes to match")
}

func TestConvertToTaskDefinitionWithVolumesWithHostOnly(t *testing.T) {
	volumeConfigs := &adapter.Volumes{
		VolumeWithHost: map[string]string{
			hostPath: containerPath,
		},
	}

	mountPoints := []*ecs.MountPoint{
		{
			ContainerPath: aws.String("/tmp/cache"),
			ReadOnly:      aws.Bool(false),
			SourceVolume:  aws.String("volume-0"),
		},
	}
	containerConfig := adapter.ContainerConfig{
		MountPoints: mountPoints,
	}

	containerConfigs := []adapter.ContainerConfig{containerConfig}

	host := &ecs.HostVolumeProperties{SourcePath: aws.String(hostPath)}
	expectedVolumes := []*ecs.Volume{
		{
			Host: host,
			Name: aws.String(containerPath),
		},
	}

	testParams := ConvertTaskDefParams{
		TaskDefName:            projectName,
		TaskRoleArn:            "",
		RequiredCompatibilites: "",
		Volumes:                volumeConfigs,
		ContainerConfigs:       containerConfigs,
		ECSParams:              nil,
	}

	taskDefinition, err := ConvertToTaskDefinition(testParams)
	assert.NoError(t, err, "Unexpected error converting Task Definition")

	actualVolumes := taskDefinition.Volumes
	assert.ElementsMatch(t, expectedVolumes, actualVolumes, "Expected volumes to match")
}

func TestConvertToTaskDefinitionWithECSParamsVolumeWithoutNameError(t *testing.T) {
	volumeConfigs := &adapter.Volumes{
		VolumeEmptyHost: []string{namedVolume, namedVolume2},
	}

	mountPoints := []*ecs.MountPoint{
		{
			ContainerPath: aws.String("/var/log"),
			ReadOnly:      aws.Bool(false),
			SourceVolume:  aws.String("named_volume"),
		},
		{
			ContainerPath: aws.String("/tmp/cache"),
			ReadOnly:      aws.Bool(false),
			SourceVolume:  aws.String("named_volume2"),
		},
	}
	containerConfig := adapter.ContainerConfig{
		MountPoints: mountPoints,
	}

	containerConfigs := []adapter.ContainerConfig{containerConfig}
	labels := map[string]string{
		"testing.thisdoesntactuallyreallyadvancetheplot": "true",
	}
	options := map[string]string{
		"Clyde": "says Goodbye Stranger, decides to Take The Long Way Home, and enjoys some Breakfast in America",
		"He":    "is a big fan of 70s music",
	}

	ecsParams := &ECSParams{
		TaskDefinition: EcsTaskDef{
			DockerVolumes: []DockerVolume{
				DockerVolume{
					Autoprovision: aws.Bool(true),
					Scope:         aws.String("shared"),
					Driver:        nil,
					DriverOptions: options,
					Labels:        labels,
				},
			},
		},
	}

	testParams := ConvertTaskDefParams{
		TaskDefName:            projectName,
		TaskRoleArn:            "",
		RequiredCompatibilites: "",
		Volumes:                volumeConfigs,
		ContainerConfigs:       containerConfigs,
		ECSParams:              ecsParams,
	}

	_, err := ConvertToTaskDefinition(testParams)
	assert.Error(t, err, "Expected error converting Task Definition with ECS Params volume without name")
}

func TestConvertToTaskDefinitionWithEFSVolume(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name:  "web",
		Image: "httpd",
		MountPoints: []*ecs.MountPoint{{
			SourceVolume:  aws.String("myEFSVolume"),
			ContainerPath: aws.String("/mount/efs"),
			ReadOnly:      aws.Bool(true),
		}},
	}

	ecsParamsString := `version: 1
task_definition:
  efs_volumes:
    - name: myEFSVolume
      filesystem_id: fs-1234
      root_directory: /
      transit_encryption: "DISABLED"
      iam: "DISABLED"`

	ecsParams, err := createTempECSParamsForTest(t, ecsParamsString)
	assert.NoError(t, err)

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	volumes := adapter.Volumes{
		VolumeWithHost: map[string]string{
			"/mount/efs": "myEFSVolume",
		},
	}
	testParams := ConvertTaskDefParams{
		TaskDefName:            projectName,
		TaskRoleArn:            "",
		RequiredCompatibilites: "",
		Volumes:                &volumes,
		ContainerConfigs:       containerConfigs,
		ECSParams:              ecsParams,
		ECSRegistryCreds:       nil,
	}

	taskDefinition, err := ConvertToTaskDefinition(testParams)
	assert.NoError(t, err)
	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)
	mp := web.MountPoints[0]
	if assert.NoError(t, err) {
		assert.Equal(t, *mp.SourceVolume, "myEFSVolume")
		assert.Equal(t, mp.ContainerPath, aws.String("/mount/efs"))
		assert.Equal(t, *taskDefinition.Volumes[0].Name, "myEFSVolume")
	}
}
func TestConvertToTaskDefinitionWithEFSVolumeNoId(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name:  "web",
		Image: "httpd",
		MountPoints: []*ecs.MountPoint{{
			SourceVolume:  aws.String("myEFSVolume"),
			ContainerPath: aws.String("/mount/efs"),
			ReadOnly:      aws.Bool(true),
		}},
	}
	ecsParamsString := `version: 1
task_definition:
  efs_volumes:
    - name: myEFSVolume
      root_directory: /
      transit_encryption: "DISABLED"
      iam: "DISABLED"`

	ecsParams, err := createTempECSParamsForTest(t, ecsParamsString)
	assert.NoError(t, err)

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	volumes := adapter.Volumes{
		VolumeWithHost: map[string]string{
			"/mount/efs": "myEFSVolume",
		},
	}
	testParams := ConvertTaskDefParams{
		TaskDefName:            projectName,
		TaskRoleArn:            "",
		RequiredCompatibilites: "",
		Volumes:                &volumes,
		ContainerConfigs:       containerConfigs,
		ECSParams:              ecsParams,
		ECSRegistryCreds:       nil,
	}

	_, err = ConvertToTaskDefinition(testParams)
	assert.EqualError(t, err, "file system id is required for efs volumes")
}

func TestConvertToTaskDefinitionWithEFSVolumeAuthError(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name:  "web",
		Image: "httpd",
		MountPoints: []*ecs.MountPoint{{
			SourceVolume:  aws.String("myEFSVolume"),
			ContainerPath: aws.String("/mount/efs"),
			ReadOnly:      aws.Bool(true),
		}},
	}
	ecsParamsString := `version: 1
task_definition:
  efs_volumes:
    - name: myEFSVolume
      filesystem_id: fs-1234
      root_directory: /
      transit_encryption: "DISABLED"
      iam: "ENABLED"`

	ecsParams, err := createTempECSParamsForTest(t, ecsParamsString)
	assert.NoError(t, err)

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	volumes := adapter.Volumes{
		VolumeWithHost: map[string]string{
			"/mount/efs": "myEFSVolume",
		},
	}
	testParams := ConvertTaskDefParams{
		TaskDefName:            projectName,
		TaskRoleArn:            "",
		RequiredCompatibilites: "",
		Volumes:                &volumes,
		ContainerConfigs:       containerConfigs,
		ECSParams:              ecsParams,
		ECSRegistryCreds:       nil,
	}

	_, err = ConvertToTaskDefinition(testParams)
	assert.EqualError(t, err, "Transit encryption is required when using IAM access or an access point")
}

func TestConvertToTaskDefinitionWithEFSVolumeAPError(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name:  "web",
		Image: "httpd",
		MountPoints: []*ecs.MountPoint{{
			SourceVolume:  aws.String("myEFSVolume"),
			ContainerPath: aws.String("/mount/efs"),
			ReadOnly:      aws.Bool(true),
		}},
	}
	ecsParamsString := `version: 1
task_definition:
  efs_volumes:
    - name: myEFSVolume
      filesystem_id: fs-1234
      root_directory: /
      transit_encryption: "DISABLED"
      access_point: "ap-1234"`

	ecsParams, err := createTempECSParamsForTest(t, ecsParamsString)
	assert.NoError(t, err)

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	volumes := adapter.Volumes{
		VolumeWithHost: map[string]string{
			"/mount/efs": "myEFSVolume",
		},
	}
	testParams := ConvertTaskDefParams{
		TaskDefName:            projectName,
		TaskRoleArn:            "",
		RequiredCompatibilites: "",
		Volumes:                &volumes,
		ContainerConfigs:       containerConfigs,
		ECSParams:              ecsParams,
		ECSRegistryCreds:       nil,
	}

	_, err = ConvertToTaskDefinition(testParams)
	assert.EqualError(t, err, "Transit encryption is required when using IAM access or an access point")
}

func TestIsZeroForEmptyConfig(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{}

	configValue := reflect.ValueOf(containerConfig).Elem()
	configType := configValue.Type()

	for i := 0; i < configValue.NumField(); i++ {
		f := configValue.Field(i)
		ft := configType.Field(i)
		isZero := value.IsZero(f)
		if !isZero {
			t.Errorf("Expected field [%s] to be zero but was not", ft.Name)
		}
	}
}

func TestIsZeroWhenConfigHasValues(t *testing.T) {
	hasValues := map[string]bool{
		"CPU":               true,
		"Command":           true,
		"Hostname":          true,
		"Image":             true,
		"Links":             true,
		"Memory":            true,
		"MemoryReservation": true,
		"Privileged":        true,
		"ReadOnly":          true,
		"User":              true,
		"WorkingDirectory":  true,
	}

	containerConfig := &adapter.ContainerConfig{
		CPU:               int64(10),
		Command:           []string{"cmd"},
		Hostname:          "foobarbaz",
		Image:             "testimage",
		Links:             []string{"container1"},
		Memory:            int64(104857600),
		MemoryReservation: int64(52428800),
		Privileged:        true,
		ReadOnly:          true,
		User:              "user",
		WorkingDirectory:  "/var",
	}

	configValue := reflect.ValueOf(containerConfig).Elem()
	configType := configValue.Type()

	for i := 0; i < configValue.NumField(); i++ {
		f := configValue.Field(i)
		ft := configType.Field(i)
		fieldName := ft.Name

		zeroValue := value.IsZero(f)
		_, hasValue := hasValues[fieldName]
		assert.NotEqual(t, zeroValue, hasValue)
	}
}

func TestConvertToTaskDefinitionWithECSParams_HealthCheck(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name:  "web",
		Image: "httpd",
	}

	ecsParamsString := `version: 1
task_definition:
  services:
    web:
      healthcheck:
        test: curl -f http://localhost
        interval: 10m
        timeout: 15s
        retries: 5
        start_period: 50`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	if assert.NoError(t, err) {
		assert.Equal(t, []string{"CMD-SHELL", "curl -f http://localhost"}, aws.StringValueSlice(web.HealthCheck.Command))
		assert.Equal(t, aws.Int64(600), web.HealthCheck.Interval)
		assert.Equal(t, aws.Int64(15), web.HealthCheck.Timeout)
		assert.Equal(t, aws.Int64(5), web.HealthCheck.Retries)
		assert.Equal(t, aws.Int64(50), web.HealthCheck.StartPeriod)
	}
}

func TestConvertToTaskDefinitionWithECSParams_ContainerDependency(t *testing.T) {
	webContainerConfig := &adapter.ContainerConfig{
		Name:  "web",
		Image: "httpd",
	}

	logRouterContainerConfig := &adapter.ContainerConfig{
		Name:  "log_router",
		Image: "amazon/aws-for-fluent-bit",
	}

	ecsParamsString := `version: 1
task_definition:
  services:
    web:
      depends_on:
        - container_name: log_router
          condition: STARTED`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	containerConfigs := []adapter.ContainerConfig{*webContainerConfig, * logRouterContainerConfig}
	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	if assert.NoError(t, err) {
		assert.Equal(t, aws.String("STARTED"), web.DependsOn[0].Condition)
		assert.Equal(t, aws.String("log_router"), web.DependsOn[0].ContainerName)
	}
}

func TestConvertToTaskDefinitionWithECSParams_OnlyTaskMemProvided(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name: "web",
	}
	ecsParams := &ECSParams{
		TaskDefinition: EcsTaskDef{
			TaskSize: TaskSize{
				Memory: "1gb",
			},
		},
	}

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	assert.NoError(t, err)
	assert.Empty(t, web.Memory, "Expected Memory to be nil")
	assert.Equal(t, taskDefinition.Memory, aws.String("1gb"))
}

func TestConvertToTaskDefinitionWithECSParams_HealthCheckOverrideCompose(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name:  "web",
		Image: "httpd",
		HealthCheck: &ecs.HealthCheck{
			Command:     aws.StringSlice([]string{"CMD", "curl", "-f", "http://example.com"}),
			Interval:    aws.Int64(int64(91)),
			Timeout:     aws.Int64(int64(17)),
			Retries:     aws.Int64(int64(7)),
			StartPeriod: aws.Int64(int64(73)),
		},
	}

	ecsParamsString := `version: 1
task_definition:
  services:
    web:
      healthcheck:
        test: curl -f http://localhost
        interval: 10m
        timeout: 15s
        retries: 5
        start_period: 50`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	if assert.NoError(t, err) {
		assert.Equal(t, []string{"CMD-SHELL", "curl -f http://localhost"}, aws.StringValueSlice(web.HealthCheck.Command))
		assert.Equal(t, aws.Int64(600), web.HealthCheck.Interval)
		assert.Equal(t, aws.Int64(15), web.HealthCheck.Timeout)
		assert.Equal(t, aws.Int64(5), web.HealthCheck.Retries)
		assert.Equal(t, aws.Int64(50), web.HealthCheck.StartPeriod)
	}
}

func TestConvertToTaskDefinitionWithECSRegistryCreds(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	credsFileString := `version: "1"
registry_credential_outputs:
  task_execution_role: someTestRole
  container_credentials:
    my.example.registry.net:
      credentials_parameter: arn:aws:secretsmanager::secret:amazon-ecs-cli-setup-my.example.registry.net
      container_names:
      - mysql`

	content := []byte(credsFileString)

	tmpfile, err := ioutil.TempFile("", regcredio.ECSCredFileBaseName)
	assert.NoError(t, err, "Could not create reg creds tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to reg creds tempfile")

	regCreds, err := regcredio.ReadCredsOutput(tmpfile.Name())
	assert.NoError(t, err, "Could not read reg creds file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", nil, regCreds)
	assert.NoError(t, err, "Unexpected error when converting task definition")
	assert.Equal(t, "someTestRole", aws.StringValue(taskDefinition.ExecutionRoleArn))

	mysqlContainer := findContainerByName("mysql", taskDefinition.ContainerDefinitions)
	assert.NotEmpty(t, mysqlContainer)
	assert.NotEmpty(t, mysqlContainer.RepositoryCredentials)
	assert.Equal(t, "arn:aws:secretsmanager::secret:amazon-ecs-cli-setup-my.example.registry.net", aws.StringValue(mysqlContainer.RepositoryCredentials.CredentialsParameter))
}

func TestConvertToTaskDefinitionWithECSRegistryCreds_EmptyContainerCredMap(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	credsNoContainersFileString := `version: "1"
registry_credential_outputs:
  task_execution_role: someTestRole
  container_credentials:
    my.example.registry.net:
      credentials_parameter: arn:aws:secretsmanager::secret:amazon-ecs-cli-setup-my.example.registry.net
      container_names:`

	content := []byte(credsNoContainersFileString)

	tmpfile, err := ioutil.TempFile("", regcredio.ECSCredFileBaseName)
	assert.NoError(t, err, "Could not create reg creds tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to reg creds tempfile")

	regCreds, err := regcredio.ReadCredsOutput(tmpfile.Name())
	assert.NoError(t, err, "Could not read reg creds file")

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", nil, regCreds)
	assert.NoError(t, err, "Unexpected error when converting task definition")
	assert.Equal(t, "someTestRole", aws.StringValue(taskDefinition.ExecutionRoleArn))

	mysqlContainer := findContainerByName("mysql", taskDefinition.ContainerDefinitions)
	assert.NotEmpty(t, mysqlContainer)
	assert.Empty(t, mysqlContainer.RepositoryCredentials)
}

func TestConvertToTaskDefinitionWithECSRegistryCreds_OverrideECSParamsValues(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})

	// set up reg cred file
	credsFileString := `version: "1"
registry_credential_outputs:
  task_execution_role: someTestRole
  container_credentials:
    my.example.registry.net:
      credentials_parameter: arn:aws:secretsmanager::secret:amazon-ecs-cli-setup-my.example.registry.net
      container_names:
      - mysql`

	credsContent := []byte(credsFileString)

	tmpfileCreds, err := ioutil.TempFile("", regcredio.ECSCredFileBaseName)
	assert.NoError(t, err, "Could not create reg creds tempfile")

	defer os.Remove(tmpfileCreds.Name())

	_, err = tmpfileCreds.Write(credsContent)
	assert.NoError(t, err, "Could not write data to reg creds tempfile")

	regCreds, err := regcredio.ReadCredsOutput(tmpfileCreds.Name())
	assert.NoError(t, err, "Could not read reg creds file")

	// set up ecs-params
	ecsParamsString := `version: 1
task_definition:
  ecs_network_mode: host
  task_execution_role: arn:aws:iam::123456789012:role/my_role
  services:
    mysql:
      essential: true
      cpu_shares: 100
      mem_limit: 524288000
      repository_credentials:
        credentials_parameter: arn:aws:secretsmanager:1234567890:secret:test-RT4iv`

	contentECSParams := []byte(ecsParamsString)

	tmpfileECSParams, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs-params tempfile")

	ecsParamsFileName := tmpfileECSParams.Name()
	defer os.Remove(tmpfileECSParams.Name())

	_, err = tmpfileECSParams.Write(contentECSParams)
	assert.NoError(t, err, "Could not write data to ecs-params tempfile")

	err = tmpfileECSParams.Close()
	assert.NoError(t, err, "Could not close ecs-params tempfile")

	ecsParams, err := ReadECSParams(ecsParamsFileName)

	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, regCreds)
	assert.NoError(t, err, "Unexpected error when converting task definition")
	assert.Equal(t, "someTestRole", aws.StringValue(taskDefinition.ExecutionRoleArn))

	mysqlContainer := findContainerByName("mysql", taskDefinition.ContainerDefinitions)
	assert.NotEmpty(t, mysqlContainer)
	assert.NotEmpty(t, mysqlContainer.RepositoryCredentials)
	assert.Equal(t, "arn:aws:secretsmanager::secret:amazon-ecs-cli-setup-my.example.registry.net", aws.StringValue(mysqlContainer.RepositoryCredentials.CredentialsParameter))
}

func TestConvertToTaskDefinitionWithECSRegistryCreds_ErrorOnDuplicateContainers(t *testing.T) {
	containerConfigs := testContainerConfigs([]string{"mysql", "wordpress"})
	credsDuplicateContainersFileString := `version: "1"
registry_credential_outputs:
  task_execution_role: someTestRole
  container_credentials:
    my.example.registry.net:
      credentials_parameter: arn:aws:secretsmanager::secret:amazon-ecs-cli-setup-my.example.registry.net
      container_names:
      - mysql
    my.otherregistry.net:
      credentials_parameter: arn:aws:secretsmanager::secret:amazon-ecs-cli-setup-my.otherregistry.net
      container_names:
      - mysql`

	content := []byte(credsDuplicateContainersFileString)

	tmpfile, err := ioutil.TempFile("", regcredio.ECSCredFileBaseName)
	assert.NoError(t, err, "Could not create reg creds tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to reg creds tempfile")

	regCreds, err := regcredio.ReadCredsOutput(tmpfile.Name())
	assert.NoError(t, err, "Could not read reg creds file")

	_, err = convertToTaskDefinitionForTest(t, containerConfigs, "", "", nil, regCreds)
	assert.Error(t, err, "Expected error when converting task definition")
}

func TestConvertToTaskDefinitionWithECSParams_FirelensConfiguration(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name:  "log_router",
		Image: "amazon/aws-for-fluent-bit",
	}

	ecsParamsString := `version: 1
task_definition:
  services:
    log_router:
      firelens_configuration:
        type: fluentbit
        options:
           enable-ecs-log-metadata: "true"`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	log_router := findContainerByName("log_router", containerDefs)

	if assert.NoError(t, err) {
		assert.Equal(t, "fluentbit", aws.StringValue(log_router.FirelensConfiguration.Type))
		assert.Equal(t, "true", aws.StringValue(log_router.FirelensConfiguration.Options["enable-ecs-log-metadata"]))
	}
}

func TestConvertToTaskDefinitionWithECSParams_Secrets(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name:  "web",
		Image: "wordpress",
	}

	ecsParamsString := `version: 1
task_definition:
  services:
    web:
      secrets:
        - value_from: /mysecrets/dbusername
          name: DB_USERNAME
        - value_from: arn:aws:ssm:eu-west-1:111111111111:parameter/mysecrets/dbpassword
          name: DB_PASSWORD`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	expectedSecrets := []*ecs.Secret{
		&ecs.Secret{
			ValueFrom: aws.String("arn:aws:ssm:eu-west-1:111111111111:parameter/mysecrets/dbpassword"),
			Name:      aws.String("DB_PASSWORD"),
		},
		&ecs.Secret{
			ValueFrom: aws.String("/mysecrets/dbusername"),
			Name:      aws.String("DB_USERNAME"),
		},
	}

	if assert.NoError(t, err) {
		assert.ElementsMatch(t, expectedSecrets, web.Secrets, "Expected secrets to match")
	}
}

func TestConvertToTaskDefinitionWithECSParams_LoggingSecretOptions(t *testing.T) {
	containerConfig := &adapter.ContainerConfig{
		Name:  "web",
		Image: "wordpress",
		LogConfiguration: &ecs.LogConfiguration{
			LogDriver: aws.String("json-file"),
		},
	}

	ecsParamsString := `version: 1
task_definition:
  services:
    web:
      logging:
        secret_options:
          - value_from: /mysecrets/dbusername
            name: DB_USERNAME
          - value_from: arn:aws:ssm:eu-west-1:111111111111:parameter/mysecrets/dbpassword
            name: DB_PASSWORD`

	content := []byte(ecsParamsString)

	tmpfile, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(tmpfile.Name())

	_, err = tmpfile.Write(content)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = tmpfile.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := tmpfile.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")

	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	expectedSecretOptions := []*ecs.Secret{
		&ecs.Secret{
			ValueFrom: aws.String("arn:aws:ssm:eu-west-1:111111111111:parameter/mysecrets/dbpassword"),
			Name:      aws.String("DB_PASSWORD"),
		},
		&ecs.Secret{
			ValueFrom: aws.String("/mysecrets/dbusername"),
			Name:      aws.String("DB_USERNAME"),
		},
	}

	if assert.NoError(t, err) {
		assert.ElementsMatch(t, expectedSecretOptions, web.LogConfiguration.SecretOptions, "Expected secrets to match")
	}
}

func TestConvertToTaskDefinitionWithECSParams_Gpu(t *testing.T) {
	expectedGpuValue := "2"
	content := `version: 1
task_definition:
  services:
    web:
      gpu: ` + expectedGpuValue
	ecsParams, err := createTempECSParamsForTest(t, content)

	containerConfig := &adapter.ContainerConfig{
		Name:  "web",
		Image: "wordpress",
	}
	containerConfigs := []adapter.ContainerConfig{*containerConfig}
	taskDefinition, err := convertToTaskDefinitionForTest(t, containerConfigs, "", "", ecsParams, nil)

	containerDefs := taskDefinition.ContainerDefinitions
	web := findContainerByName("web", containerDefs)

	resourceType := ecs.ResourceTypeGpu
	expectedResourceRequirements := []*ecs.ResourceRequirement{
		{
			Type:  &resourceType,
			Value: &expectedGpuValue,
		},
	}

	if assert.NoError(t, err) {
		assert.ElementsMatch(t,
			expectedResourceRequirements,
			web.ResourceRequirements,
			"Expected resourceRequirements to match")
	}
}

///////////////////////
// helper functions //
//////////////////////

func convertToTaskDefinitionForTest(t *testing.T, containerConfigs []adapter.ContainerConfig, taskRoleArn string, launchType string, ecsParams *ECSParams, ecsRegCreds *regcredio.ECSRegistryCredsOutput) (*ecs.TaskDefinition, error) {
	volumeConfigs := &adapter.Volumes{
		VolumeEmptyHost: []string{namedVolume},
	}

	testParams := ConvertTaskDefParams{
		TaskDefName:            projectName,
		TaskRoleArn:            taskRoleArn,
		RequiredCompatibilites: launchType,
		Volumes:                volumeConfigs,
		ContainerConfigs:       containerConfigs,
		ECSParams:              ecsParams,
		ECSRegistryCreds:       ecsRegCreds,
	}

	taskDefinition, err := ConvertToTaskDefinition(testParams)
	if err != nil {
		return nil, err
	}

	return taskDefinition, nil
}

func findContainerByName(name string, containerDefs []*ecs.ContainerDefinition) *ecs.ContainerDefinition {
	for _, cd := range containerDefs {
		if aws.StringValue(cd.Name) == name {
			return cd
		}
	}
	return nil
}

func createTempECSParamsForTest(t *testing.T, content string) (*ECSParams, error) {
	b := []byte(content)

	f, err := ioutil.TempFile("", "ecs-params")
	assert.NoError(t, err, "Could not create ecs params tempfile")

	defer os.Remove(f.Name())

	_, err = f.Write(b)
	assert.NoError(t, err, "Could not write data to ecs params tempfile")

	err = f.Close()
	assert.NoError(t, err, "Could not close tempfile")

	ecsParamsFileName := f.Name()
	ecsParams, err := ReadECSParams(ecsParamsFileName)
	assert.NoError(t, err, "Could not read ECS Params file")
	return ecsParams, err
}
