package project import ( "io/ioutil" "os" "path/filepath" "reflect" "testing" "github.com/aws/amazon-ecs-cli/ecs-cli/modules/cli/compose/adapter" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ecs" "github.com/stretchr/testify/assert" ) func TestParseV3WithOneFile(t *testing.T) { // set up expected ContainerConfig values wordpressCon := adapter.ContainerConfig{} wordpressCon.Name = "wordpress" wordpressCon.CapAdd = []string{"ALL"} wordpressCon.CapDrop = []string{"NET_ADMIN"} wordpressCon.Command = []string{"echo \"hello world\""} wordpressCon.Image = "wordpress" wordpressCon.Entrypoint = []string{"/wordpress/entry"} wordpressCon.PortMappings = []*ecs.PortMapping{ { ContainerPort: aws.Int64(80), HostPort: aws.Int64(80), Protocol: aws.String("tcp"), }, } wordpressCon.Devices = []*ecs.Device{ { HostPath: aws.String("/dev/sda"), ContainerPath: aws.String("/dev/sdd"), Permissions: aws.StringSlice([]string{"read"}), }, { HostPath: aws.String("/dev/sdd"), ContainerPath: aws.String("/dev/xdr"), }, { HostPath: aws.String("/dev/sda"), }, } wordpressCon.HealthCheck = &ecs.HealthCheck{ Command: aws.StringSlice([]string{"CMD-SHELL", "curl -f http://localhost || exit 1"}), Interval: aws.Int64(int64(90)), Timeout: aws.Int64(int64(10)), Retries: aws.Int64(int64(3)), } wordpressCon.DNSServers = []string{"2.2.2.2"} wordpressCon.DNSSearchDomains = []string{"wrd.search.com", "wrd.search2.com"} wordpressCon.Environment = []*ecs.KeyValuePair{ { Name: aws.String("wordpress_env"), Value: aws.String("val1"), }, } wordpressCon.DockerLabels = map[string]*string{ "com.example.wordpress": aws.String("wordpress label"), } wordpressCon.Hostname = "wrdhost" wordpressCon.Links = []string{"mysql"} wordLogDriver := aws.String("awslogs") wordLogOpts := map[string]*string{ "awslogs-group": aws.String("mywrdprs-logs"), "awslogs-region": aws.String("us-west-2"), "awslogs-stream-prefix": aws.String("wordpress"), } wordpressCon.LogConfiguration = &ecs.LogConfiguration{ LogDriver: wordLogDriver, Options: wordLogOpts, } wordTmpfsOpt := []string{"rw"} wordpressCon.Tmpfs = []*ecs.Tmpfs{ { ContainerPath: aws.String("/run"), MountOptions: aws.StringSlice(wordTmpfsOpt), Size: aws.Int64(1024), }, } wordpressCon.Privileged = true wordpressCon.ReadOnly = true wordpressCon.PseudoTerminal = true stg := int64(60) wordpressCon.StopTimeout = &stg wordpressCon.DockerSecurityOptions = []string{"label:role:ROLE", "label:user:USER"} wordpressCon.Ulimits = []*ecs.Ulimit{ { Name: aws.String("rss"), HardLimit: aws.Int64(65535), SoftLimit: aws.Int64(65535), }, { Name: aws.String("nofile"), HardLimit: aws.Int64(4000), SoftLimit: aws.Int64(2000), }, { Name: aws.String("nice"), HardLimit: aws.Int64(500), SoftLimit: aws.Int64(300), }, } wordpressCon.WorkingDirectory = "/wrdprsdir" mysqlCon := adapter.ContainerConfig{} mysqlCon.Name = "mysql" mysqlCon.Image = "mysql" mysqlCon.DockerLabels = map[string]*string{ "com.example.mysql": aws.String("mysqllabel"), "com.example.mysql2": aws.String("anothermysql label"), } mysqlCon.User = "mysqluser" mysqlCon.ExtraHosts = []*ecs.HostEntry{ { Hostname: aws.String("mysqlexhost"), IpAddress: aws.String("10.0.0.0"), }, } mysqlCon.HealthCheck = &ecs.HealthCheck{ // when test command is specified as a string, compose wraps it in CMD-SHELL Command: aws.StringSlice([]string{"CMD-SHELL", "curl -f http://example.com || exit 1"}), Interval: aws.Int64(int64(105)), Timeout: aws.Int64(int64(15)), Retries: aws.Int64(int64(5)), } // set up file composeFileString := `version: '3' services: wordpress: cap_add: - ALL cap_drop: - NET_ADMIN command: - echo "hello world" image: wordpress entrypoint: /wordpress/entry ports: ["80:80"] devices: - "/dev/sda:/dev/sdd:r" - "/dev/sdd:/dev/xdr" - "/dev/sda" dns: - 2.2.2.2 dns_search: - wrd.search.com - wrd.search2.com environment: wordpress_env: val1 labels: com.example.wordpress: "wordpress label" links: - mysql logging: driver: awslogs options: awslogs-group: mywrdprs-logs awslogs-region: us-west-2 awslogs-stream-prefix: wordpress tmpfs: - "/run:rw,size=1gb" tty: true read_only: true hostname: wrdhost security_opt: - label:role:ROLE - label:user:USER working_dir: /wrdprsdir privileged: true stop_grace_period: "1m" ulimits: rss: 65535 nofile: soft: 2000 hard: 4000 nice: soft: 300 hard: 500 healthcheck: test: ["CMD-SHELL", "curl -f http://localhost || exit 1"] interval: 1m30s timeout: 10s retries: 3 mysql: image: mysql labels: - "com.example.mysql=mysqllabel" - "com.example.mysql2=anothermysql label" user: mysqluser extra_hosts: - "mysqlexhost:10.0.0.0" healthcheck: test: curl -f http://example.com || exit 1 interval: 1m45s timeout: 15s retries: 5` tmpfile, err := ioutil.TempFile("", "test") assert.NoError(t, err, "Unexpected error in creating test file") defer os.Remove(tmpfile.Name()) _, err = tmpfile.Write([]byte(composeFileString)) assert.NoError(t, err, "Unexpected error writing file") err = tmpfile.Close() assert.NoError(t, err, "Unexpected error closing file") // add files to projects project := setupTestProject(t) project.ecsContext.ComposeFiles = append(project.ecsContext.ComposeFiles, tmpfile.Name()) // assert # and content of container configs matches expected actualConfigs, err := project.parseV3() assert.NoError(t, err, "Unexpected error parsing file") assert.Equal(t, 2, len(*actualConfigs)) wp, err := getContainerConfigByName(wordpressCon.Name, actualConfigs) assert.NoError(t, err, "Unexpected error retrieving wordpress config") verifyContainerConfig(t, wordpressCon, *wp) mysql, err := getContainerConfigByName(mysqlCon.Name, actualConfigs) assert.NoError(t, err, "Unexpected error retrieving mysql config") verifyContainerConfig(t, mysqlCon, *mysql) } func TestParseV3WithMultipleFiles(t *testing.T) { // set up expected ContainerConfig values wordpressCon := adapter.ContainerConfig{} wordpressCon.Name = "wordpress" wordpressCon.Image = "wordpress" wordpressCon.PortMappings = []*ecs.PortMapping{ { ContainerPort: aws.Int64(80), HostPort: aws.Int64(80), Protocol: aws.String("tcp"), }, { ContainerPort: aws.Int64(777), HostPort: aws.Int64(0), Protocol: aws.String("tcp"), }, } wordpressCon.Environment = []*ecs.KeyValuePair{ { Name: aws.String("WRDPRS1"), Value: aws.String("val1"), }, { Name: aws.String("WRDPRS2"), Value: aws.String("val2"), }, } mysqlCon := adapter.ContainerConfig{} mysqlCon.Name = "mysql" mysqlCon.Image = "mysql" redisCon := adapter.ContainerConfig{} redisCon.Name = "redis" redisCon.Image = "redis" redisCon.PortMappings = []*ecs.PortMapping{ { ContainerPort: aws.Int64(90), HostPort: aws.Int64(90), Protocol: aws.String("tcp"), }, } // set up files fileString1 := `version: '3' services: wordpress: image: wordpress ports: - "80:80" - "777" environment: - WRDPRS1=val1 - WRDPRS2=val2 mysql: image: mysql` fileString2 := `version: '3' services: redis: image: redis ports: ["90:90"]` // initialize temp files tmpfile1, err1 := ioutil.TempFile("", "test") assert.NoError(t, err1, "Unexpected error parsing file") defer os.Remove(tmpfile1.Name()) tmpfile2, err2 := ioutil.TempFile("", "test") assert.NoError(t, err2, "Unexpected error parsing file") defer os.Remove(tmpfile2.Name()) // write compose contents to files _, err := tmpfile1.Write([]byte(fileString1)) assert.NoError(t, err, "Unexpected error parsing file") err = tmpfile1.Close() assert.NoError(t, err, "Unexpected error parsing file") if _, err := tmpfile2.Write([]byte(fileString2)); err != nil { t.Fatal("Unexpected error writing to test file: ", err) } if err := tmpfile2.Close(); err != nil { t.Fatal("Unexpected error closing test file: ", err) } // add files to projects project := setupTestProject(t) project.ecsContext.ComposeFiles = append(project.ecsContext.ComposeFiles, tmpfile1.Name()) project.ecsContext.ComposeFiles = append(project.ecsContext.ComposeFiles, tmpfile2.Name()) // assert # and content of container configs matches expected actualConfigs, err := project.parseV3() assert.NoError(t, err, "Unexpected error parsing file") assert.Equal(t, 3, len(*actualConfigs)) wp, err := getContainerConfigByName(wordpressCon.Name, actualConfigs) assert.NoError(t, err, "Unexpected error retrieving wordpress config") verifyContainerConfig(t, wordpressCon, *wp) mysql, err := getContainerConfigByName(mysqlCon.Name, actualConfigs) assert.NoError(t, err, "Unexpected error retrieving mysql config") verifyContainerConfig(t, mysqlCon, *mysql) redis, err := getContainerConfigByName(redisCon.Name, actualConfigs) assert.NoError(t, err, "Unexpected error retrieving redis config") verifyContainerConfig(t, redisCon, *redis) } func TestParseV3WithTopLevelVolume(t *testing.T) { // set up expected ContainerConfig values wordpressCon := adapter.ContainerConfig{} wordpressCon.Name = "wordpress" wordpressCon.Image = "wordpress" wordpressCon.CapAdd = []string{"ALL"} wordpressCon.PortMappings = []*ecs.PortMapping{ { ContainerPort: aws.Int64(80), HostPort: aws.Int64(80), Protocol: aws.String("tcp"), }, } wordpressCon.Links = []string{"mysql"} wordpressCon.ReadOnly = true mysqlCon := adapter.ContainerConfig{} mysqlCon.Name = "mysql" mysqlCon.Image = "mysql" mysqlCon.DockerLabels = map[string]*string{ "com.example.mysql": aws.String("mysqllabel"), "com.example.mysql2": aws.String("anothermysql label"), } mysqlCon.Privileged = true mysqlCon.MountPoints = []*ecs.MountPoint{ { ContainerPath: aws.String("/var/lib/mysql"), ReadOnly: aws.Bool(false), SourceVolume: aws.String("volume-1"), }, { ContainerPath: aws.String("/test/place"), ReadOnly: aws.Bool(true), SourceVolume: aws.String("logging"), }, { ContainerPath: aws.String("/var/lib/mysql"), ReadOnly: aws.Bool(false), SourceVolume: aws.String("volume-2"), }, } // set up file composeFileString := `version: '3' services: wordpress: cap_add: - ALL image: wordpress ports: ["80:80"] links: - mysql read_only: true mysql: image: mysql labels: - "com.example.mysql=mysqllabel" - "com.example.mysql2=anothermysql label" privileged: true volumes: - /opt/data:/var/lib/mysql - logging:/test/place:ro - /var/lib/mysql volumes: logging:` tmpfile, err := ioutil.TempFile("", "test") assert.NoError(t, err, "Unexpected error in creating test file") defer os.Remove(tmpfile.Name()) _, err = tmpfile.Write([]byte(composeFileString)) assert.NoError(t, err, "Unexpected error writing file") err = tmpfile.Close() assert.NoError(t, err, "Unexpected error closing file") // add files to projects project := setupTestProject(t) project.ecsContext.ComposeFiles = append(project.ecsContext.ComposeFiles, tmpfile.Name()) // assert # and content of container configs matches expected services actualConfigs, err := project.parseV3() assert.NoError(t, err, "Unexpected error parsing file") assert.Equal(t, 2, len(*actualConfigs)) wp, err := getContainerConfigByName(wordpressCon.Name, actualConfigs) assert.NoError(t, err, "Unexpected error retrieving wordpress config") verifyContainerConfig(t, wordpressCon, *wp) mysql, err := getContainerConfigByName(mysqlCon.Name, actualConfigs) assert.NoError(t, err, "Unexpected error retrieving mysql config") verifyContainerConfig(t, mysqlCon, *mysql) } func TestParseV3_ErrorWithExternalVolume(t *testing.T) { // set up file with invalid Volume config ("external") composeFileString := `version: '3' services: httpd: cap_add: - ALL cap_drop: - NET_ADMIN command: echo "hello world" image: httpd entrypoint: /web/entry ports: ["80:80"] volumes: - /opt/data:/var/lib/mysql - logging:/test/place:ro - /var/lib/test volumes: logging: external:true` tmpfile, err := ioutil.TempFile("", "test") assert.NoError(t, err, "Unexpected error in creating test file") defer os.Remove(tmpfile.Name()) _, err = tmpfile.Write([]byte(composeFileString)) assert.NoError(t, err, "Unexpected error writing file") err = tmpfile.Close() assert.NoError(t, err, "Unexpected error closing file") // add files to projects project := setupTestProject(t) project.ecsContext.ComposeFiles = append(project.ecsContext.ComposeFiles, tmpfile.Name()) // assert error when parsing v3 project _, err = project.parseV3() assert.Error(t, err, "Expected error when parsing project with invalid Volume configuration") } func TestThrowErrorOnBadYaml(t *testing.T) { badPortsYaml := `version: '2' services: wordpress: image: wordpress ports: - "80:80", "77:77" mysql: image: mysql` tmpfile, err := ioutil.TempFile("", "test") if err != nil { t.Fatal("Unexpected error in creating test file: ", err) } defer os.Remove(tmpfile.Name()) _, err = tmpfile.Write([]byte(badPortsYaml)) assert.NoError(t, err, "Unexpected error parsing file") // add files to projects project := setupTestProject(t) project.ecsContext.ComposeFiles = append(project.ecsContext.ComposeFiles, tmpfile.Name()) _, err = project.parseV3() assert.Error(t, err) } func TestThrowErrorIfFileDoesNotExist(t *testing.T) { var fakeFileName = "/missingFile" project := setupTestProject(t) project.ecsContext.ComposeFiles = append(project.ecsContext.ComposeFiles, fakeFileName) _, err := project.parseV3() assert.Error(t, err) } func TestParseV3WithDefaultDotEnvFile(t *testing.T) { // Set up the .env file dir, err := ioutil.TempDir("", "dotEnvTest") assert.NoError(t, err, "Unexpected error in creating temp directory that will hold the .env file") defer os.RemoveAll(dir) envKey := "TAG" envValue := "testValue" envContents := []byte(envKey + "=" + envValue) dotEnvFilePath := filepath.Join(dir, ".env") err = ioutil.WriteFile(dotEnvFilePath, envContents, 0666) assert.NoError(t, err, "Unexpected error in writing to the .env file") expectedImage := "httpd:" + envValue composeFileString := `version: '3' services: web: image: httpd:${` + envKey + "}" composeFilePath := filepath.Join(dir, "test_compose") err = ioutil.WriteFile(composeFilePath, []byte(composeFileString), 0666) assert.NoError(t, err, "Unexpected error in writing to test Compose file") // Set up project project := setupTestProject(t) project.ecsContext.ComposeFiles = append(project.ecsContext.ComposeFiles, composeFilePath) // assert # and content of container configs matches expected services actualConfigs, err := project.parseV3() assert.NoError(t, err, "Unexpected error parsing file") actualConfig, err := getContainerConfigByName("web", actualConfigs) assert.NoError(t, err, "Unexpected error retrieving container config") assert.Equal(t, 1, len(*actualConfigs)) assert.Equal(t, expectedImage, actualConfig.Image) // Based on https://docs.docker.com/compose/env-file/ // the variables defined in .env should not automatically be visible // inside containers, which means that the environment field for container // should still be empty even when there's a .env file assert.Equal(t, []*ecs.KeyValuePair{}, actualConfig.Environment) } func TestParseV3WithEnvFile(t *testing.T) { // Set up env file envKey := "testEnv" envValue := "testValue" envContents := []byte(envKey + "=" + envValue) envFile, err := ioutil.TempFile("", "envTest") assert.NoError(t, err, "Unexpected error in creating test env file") defer os.Remove(envFile.Name()) _, err = envFile.Write(envContents) assert.NoError(t, err, "Unexpected error in writing to test env file") expectedEnv := []*ecs.KeyValuePair{ { Name: aws.String(envKey), Value: aws.String(envValue), }, } serviceName := "web" composeFileString := `version: '3' services: web: image: httpd env_file: ` + envFile.Name() tmpfile, err := ioutil.TempFile("", "test") assert.NoError(t, err, "Unexpected error in creating test file") defer os.Remove(tmpfile.Name()) _, err = tmpfile.Write([]byte(composeFileString)) assert.NoError(t, err, "Unexpected error in writing to test file") err = tmpfile.Close() assert.NoError(t, err, "Unexpected error closing file") // Set up project project := setupTestProject(t) project.ecsContext.ComposeFiles = append(project.ecsContext.ComposeFiles, tmpfile.Name()) // assert # and content of container configs matches expected services actualConfigs, err := project.parseV3() assert.NoError(t, err, "Unexpected error parsing file") actualConfig, err := getContainerConfigByName(serviceName, actualConfigs) assert.NoError(t, err, "Unexpected error retrieving container config") assert.Equal(t, 1, len(*actualConfigs)) assert.Equal(t, expectedEnv, actualConfig.Environment) } func TestParseV3_WithEnvFromShell(t *testing.T) { // Setup shell env vars for image, environment fields webImageKey := "TEST_IMAGE" webImageValue := "httpd" envKey1 := "HOST_ENV" envValue1 := "dev" envKey2 := "SHOW" envValue2 := "true" envKey3 := "SESSION_SECRET" envValue3 := "clydeIsTheGoodestDog" os.Setenv(webImageKey, webImageValue) os.Setenv(envKey1, envValue1) os.Setenv(envKey2, envValue2) os.Setenv(envKey3, envValue3) defer func() { os.Unsetenv(webImageKey) os.Unsetenv(envKey1) os.Unsetenv(envKey2) os.Unsetenv(envKey3) }() // Setup expected environment expectedEnv := []*ecs.KeyValuePair{ { Name: aws.String(envKey1), Value: aws.String("staging"), // file value overrides shell value }, { Name: aws.String(envKey2), Value: aws.String(envValue2), }, { Name: aws.String(envKey3), Value: aws.String(envValue3), }, } // Setup docker-compose file composeFileString := `version: '3' services: web: image: $TEST_IMAGE environment: - HOST_ENV=staging - SHOW= - SESSION_SECRET` tmpfile, err := ioutil.TempFile("", "test") assert.NoError(t, err, "Unexpected error in creating test file") defer os.Remove(tmpfile.Name()) _, err = tmpfile.Write([]byte(composeFileString)) assert.NoError(t, err, "Unexpected error in writing to test file") err = tmpfile.Close() assert.NoError(t, err, "Unexpected error closing file") // Set up project project := setupTestProject(t) project.ecsContext.ComposeFiles = append(project.ecsContext.ComposeFiles, tmpfile.Name()) actualConfigs, err := project.parseV3() assert.NoError(t, err, "Unexpected error parsing file") // Verify web ServiceConfig web, err := getContainerConfigByName("web", actualConfigs) assert.NoError(t, err, "Unexpected error retrieving container config") assert.Equal(t, webImageValue, web.Image, "Expected Image to match") assert.ElementsMatch(t, expectedEnv, web.Environment, "Expected Environment to match") } func TestParseV3_WithKeyOnlyEnvVars(t *testing.T) { // Setup expected environment expectedEnv := []*ecs.KeyValuePair{ { Name: aws.String("MY_CUSTOM_VAR"), Value: aws.String(""), }, { Name: aws.String("SOME_KEY"), Value: aws.String(""), }, } composeFileString := `version: '3' services: web: image: httpd environment: - MY_CUSTOM_VAR - SOME_KEY` tmpfile, err := ioutil.TempFile("", "test") assert.NoError(t, err, "Unexpected error in creating test file") defer os.Remove(tmpfile.Name()) _, err = tmpfile.Write([]byte(composeFileString)) assert.NoError(t, err, "Unexpected error in writing to test file") err = tmpfile.Close() assert.NoError(t, err, "Unexpected error closing file") // Set up project project := setupTestProject(t) project.ecsContext.ComposeFiles = append(project.ecsContext.ComposeFiles, tmpfile.Name()) actualConfigs, err := project.parseV3() assert.NoError(t, err, "Unexpected error parsing file") //Verify web, err := getContainerConfigByName("web", actualConfigs) assert.NoError(t, err, "Unexpected error retrieving container config") assert.ElementsMatch(t, expectedEnv, web.Environment, "Expected Environment to match") } func TestParseV3HealthCheckDisabled(t *testing.T) { // set up expected ContainerConfig values wordpressCon := adapter.ContainerConfig{} wordpressCon.Name = "wordpress" wordpressCon.Command = []string{"echo \"hello world\""} wordpressCon.Image = "wordpress" // set up file composeFileString := `version: '3' services: wordpress: command: - echo "hello world" image: wordpress healthcheck: test: ["CMD-SHELL", "curl -f http://localhost || exit 1"] interval: 1m30s timeout: 10s retries: 3 disable: true` tmpfile, err := ioutil.TempFile("", "test") assert.NoError(t, err, "Unexpected error in creating test file") defer os.Remove(tmpfile.Name()) _, err = tmpfile.Write([]byte(composeFileString)) assert.NoError(t, err, "Unexpected error writing file") err = tmpfile.Close() assert.NoError(t, err, "Unexpected error closing file") // add files to projects project := setupTestProject(t) project.ecsContext.ComposeFiles = append(project.ecsContext.ComposeFiles, tmpfile.Name()) // assert # and content of container configs matches expected actualConfigs, err := project.parseV3() assert.NoError(t, err, "Unexpected error parsing file") assert.Equal(t, 1, len(*actualConfigs)) wp, err := getContainerConfigByName(wordpressCon.Name, actualConfigs) assert.NoError(t, err, "Unexpected error retrieving wordpress config") verifyContainerConfig(t, wordpressCon, *wp) } // TODO: add check for fields not used by V3, use to also check V1V2 ContainerConfigs? func verifyContainerConfig(t *testing.T, expected, actual adapter.ContainerConfig) { assert.ElementsMatch(t, expected.CapAdd, actual.CapAdd, "Expected CapAdd to match") assert.ElementsMatch(t, expected.CapDrop, actual.CapDrop, "Expected CapDrop to match") assert.ElementsMatch(t, expected.Command, actual.Command, "Expected Command to match") assert.ElementsMatch(t, expected.Devices, actual.Devices, "Expected Devices to match") assert.ElementsMatch(t, expected.DNSSearchDomains, actual.DNSSearchDomains, "Expected DNSSearchDomains to match") assert.ElementsMatch(t, expected.DNSServers, actual.DNSServers, "Expected DNSServers to match") dockerLabelsEqual := reflect.DeepEqual(expected.DockerLabels, actual.DockerLabels) assert.True(t, dockerLabelsEqual, "Expected DockerLabels to match") assert.ElementsMatch(t, expected.DockerSecurityOptions, actual.DockerSecurityOptions, "Expected DockerSecurityOptions to match") assert.ElementsMatch(t, expected.Entrypoint, actual.Entrypoint, "Expected Entrypoint to match") assert.ElementsMatch(t, expected.Environment, actual.Environment, "Expected Environment to match") assert.ElementsMatch(t, expected.ExtraHosts, actual.ExtraHosts, "Expected ExtraHosts to match") assert.Equal(t, expected.Hostname, actual.Hostname, "Expected Hostname to match") assert.Equal(t, expected.Image, actual.Image, "Expected Image to match") assert.ElementsMatch(t, expected.Links, actual.Links, "Expected Links to match") assert.Equal(t, expected.LogConfiguration, actual.LogConfiguration, "Expected LogConfiguration to match") assert.ElementsMatch(t, expected.MountPoints, actual.MountPoints, "Expected MountPoints to match") assert.ElementsMatch(t, expected.PortMappings, actual.PortMappings, "Expected PortMappings to match") assert.Equal(t, expected.Privileged, actual.Privileged, "Expected Privileged to match") assert.Equal(t, expected.ReadOnly, actual.ReadOnly, "Expected ReadOnly to match") assert.ElementsMatch(t, expected.Tmpfs, actual.Tmpfs, "Expected Tmpfs to match") assert.Equal(t, expected.PseudoTerminal, actual.PseudoTerminal, "Expected PseuoTerminal to match") assert.Equal(t, expected.StopTimeout, actual.StopTimeout, "Expected StopTimeout to match") assert.ElementsMatch(t, expected.Ulimits, actual.Ulimits, "Expected Ulimits to match") assert.Equal(t, expected.User, actual.User, "Expected User to match") assert.Equal(t, expected.WorkingDirectory, actual.WorkingDirectory, "Expected WorkingDirectory to match") if expected.HealthCheck != nil && actual.HealthCheck != nil { assert.ElementsMatch(t, aws.StringValueSlice(expected.HealthCheck.Command), aws.StringValueSlice(actual.HealthCheck.Command), "Expected healthcheck command to match") assert.Equal(t, expected.HealthCheck.Interval, actual.HealthCheck.Interval, "Expected healthcheck interval to match") assert.Equal(t, expected.HealthCheck.Retries, actual.HealthCheck.Retries, "Expected healthcheck retries to match") assert.Equal(t, expected.HealthCheck.StartPeriod, actual.HealthCheck.StartPeriod, "Expected healthcheck start_period to match") assert.Equal(t, expected.HealthCheck.Timeout, actual.HealthCheck.Timeout, "Expected healthcheck timeout to match") } else { assert.Nil(t, actual.HealthCheck, "Expected healthcheck to be nil in output ContainerConfig") } }