From c406ff306a14023d65b5e7a7618f9e09e8af75a3 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 17:30:50 +0530 Subject: [PATCH 01/27] Modify mesh-task and controller --- .github/workflows/terraform-ci.yml | 65 +- modules/acl-controller/README.md | 6 - modules/acl-controller/variables.tf | 98 --- modules/controller/README.md | 5 + modules/controller/config.tf | 41 ++ .../{acl-controller => controller}/main.tf | 50 +- modules/controller/variables.tf | 171 +++++ modules/mesh-task/config.tf | 34 +- modules/mesh-task/iam.tf | 13 +- modules/mesh-task/main.tf | 157 ++--- .../templates/consul_agent_defaults.hcl.tpl | 63 -- .../templates/consul_client_command.tpl | 97 --- modules/mesh-task/validation.tf | 6 +- modules/mesh-task/variables.tf | 204 +++--- test/acceptance/tests/basic/basic_test.go | 615 ++++++++++-------- .../admin-partition-validate/main.tf | 2 +- .../terraform/audit-logging-validate/main.tf | 30 - .../basic/terraform/basic-install/main.tf | 94 +-- .../checks-validate/test-invalid-checks.json | 20 - .../checks-validate/test-no-checks.json | 1 - .../checks-validate/test-valid-checks.json | 46 -- .../consul-ecs-config-validate/main.tf | 2 +- .../main.tf | 13 +- .../main.tf | 10 +- .../test-complete-config.json | 6 + .../test-empty-config.json | 1 + .../test-invalid-config.json | 5 + .../test-partial-config.json | 4 + .../http-tls-config-validate/main.tf | 21 + .../test-complete-config.json | 7 + .../test-empty-config.json | 1 + .../test-invalid-config.json | 5 + .../test-partial-config.json | 4 + .../terraform/pass-app-entrypoint/main.tf | 2 +- .../terraform/pass-existing-iam-roles/main.tf | 2 +- .../main.tf | 4 +- .../public-listener-port-validate/main.tf | 2 +- .../terraform/role-path-validate/main.tf | 2 +- .../terraform/service-name-validate/main.tf | 2 +- .../terraform/upstreams-validate/main.tf | 2 +- .../basic/terraform/volume-variable/main.tf | 2 +- 41 files changed, 908 insertions(+), 1007 deletions(-) delete mode 100644 modules/acl-controller/README.md delete mode 100644 modules/acl-controller/variables.tf create mode 100644 modules/controller/README.md create mode 100644 modules/controller/config.tf rename modules/{acl-controller => controller}/main.tf (77%) create mode 100644 modules/controller/variables.tf delete mode 100644 modules/mesh-task/templates/consul_agent_defaults.hcl.tpl delete mode 100644 modules/mesh-task/templates/consul_client_command.tpl delete mode 100644 test/acceptance/tests/basic/terraform/audit-logging-validate/main.tf delete mode 100644 test/acceptance/tests/basic/terraform/checks-validate/test-invalid-checks.json delete mode 100644 test/acceptance/tests/basic/terraform/checks-validate/test-no-checks.json delete mode 100644 test/acceptance/tests/basic/terraform/checks-validate/test-valid-checks.json rename test/acceptance/tests/basic/terraform/{ca-cert-validate => envoy-readiness-port-validate}/main.tf (59%) rename test/acceptance/tests/basic/terraform/{checks-validate => grpc-tls-config-validate}/main.tf (59%) create mode 100644 test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-complete-config.json create mode 100644 test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-empty-config.json create mode 100644 test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-invalid-config.json create mode 100644 test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-partial-config.json create mode 100644 test/acceptance/tests/basic/terraform/http-tls-config-validate/main.tf create mode 100644 test/acceptance/tests/basic/terraform/http-tls-config-validate/test-complete-config.json create mode 100644 test/acceptance/tests/basic/terraform/http-tls-config-validate/test-empty-config.json create mode 100644 test/acceptance/tests/basic/terraform/http-tls-config-validate/test-invalid-config.json create mode 100644 test/acceptance/tests/basic/terraform/http-tls-config-validate/test-partial-config.json diff --git a/.github/workflows/terraform-ci.yml b/.github/workflows/terraform-ci.yml index 79dfc0b5..322f497b 100644 --- a/.github/workflows/terraform-ci.yml +++ b/.github/workflows/terraform-ci.yml @@ -105,42 +105,47 @@ jobs: # HCP is always disabled for tests on PRs. matrix: name: - - acceptance-1.15-FARGATE-HCP - - acceptance-1.14-FARGATE-HCP - - acceptance-1.13-FARGATE - - acceptance-1.15-EC2 - - acceptance-1.14-EC2 - - acceptance-1.13-EC2-HCP + # - acceptance-1.15-FARGATE-HCP + # - acceptance-1.14-FARGATE-HCP + # - acceptance-1.13-FARGATE + # - acceptance-1.15-EC2 + # - acceptance-1.14-EC2 + # - acceptance-1.13-EC2-HCP + - acceptance-1.16-FARGATE include: - - name: acceptance-1.15-FARGATE-HCP - consul-version: '1.15.4' - enable-hcp: true - launch-type: FARGATE + # - name: acceptance-1.15-FARGATE-HCP + # consul-version: '1.15.4' + # enable-hcp: true + # launch-type: FARGATE - - name: acceptance-1.14-FARGATE-HCP - consul-version: '1.14.7' - enable-hcp: true - launch-type: FARGATE + # - name: acceptance-1.14-FARGATE-HCP + # consul-version: '1.14.7' + # enable-hcp: true + # launch-type: FARGATE - - name: acceptance-1.13-FARGATE - consul-version: '1.13.9' - enable-hcp: false - launch-type: FARGATE + # - name: acceptance-1.13-FARGATE + # consul-version: '1.13.9' + # enable-hcp: false + # launch-type: FARGATE - - name: acceptance-1.15-EC2 - consul-version: '1.15.4' - enable-hcp: false - launch-type: EC2 + # - name: acceptance-1.15-EC2 + # consul-version: '1.15.4' + # enable-hcp: false + # launch-type: EC2 - - name: acceptance-1.14-EC2 - consul-version: '1.14.8' - enable-hcp: false - launch-type: EC2 + # - name: acceptance-1.14-EC2 + # consul-version: '1.14.8' + # enable-hcp: false + # launch-type: EC2 - - name: acceptance-1.13-EC2-HCP - consul-version: '1.13.8' - enable-hcp: true - launch-type: EC2 + # - name: acceptance-1.13-EC2-HCP + # consul-version: '1.13.8' + # enable-hcp: true + # launch-type: EC2 + - name: acceptance-1.16-FARGATE + consul-version: '1.16.0' + enable-hcp: false + launch-type: FARGATE steps: - name: Checkout diff --git a/modules/acl-controller/README.md b/modules/acl-controller/README.md deleted file mode 100644 index efe2dbb3..00000000 --- a/modules/acl-controller/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# ACL Controller - -This module deploys a Consul ACL controller for managing tokens for tasks on the -Consul service mesh. - -See https://www.consul.io/docs/ecs for additional documentation. diff --git a/modules/acl-controller/variables.tf b/modules/acl-controller/variables.tf deleted file mode 100644 index 90eab182..00000000 --- a/modules/acl-controller/variables.tf +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -variable "consul_ecs_image" { - description = "consul-ecs Docker image." - type = string - default = "public.ecr.aws/hashicorp/consul-ecs:0.6.0" -} - -variable "ecs_cluster_arn" { - description = "The ARN of the ECS cluster where the controller will be running." - type = string -} - -variable "region" { - description = "AWS region of the ECS cluster." - type = string -} - -variable "requires_compatibilities" { - description = "Set of launch types required by the task." - type = list(string) - default = ["EC2", "FARGATE"] -} - -variable "launch_type" { - description = "Launch type on which to run service. Valid values are EC2 and FARGATE." - type = string - default = "FARGATE" -} - -variable "consul_bootstrap_token_secret_arn" { - description = "The ARN of the AWS SecretsManager secret containing the token to be used by this controller. The token needs to have at least `acl:write` privileges in Consul." - type = string -} - -variable "log_configuration" { - description = "Task definition log configuration object (https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html)." - type = any - default = null -} - -variable "iam_role_path" { - description = "IAM roles at this path will be permitted to login to the Consul AWS IAM auth method configured by this controller." - type = string - default = "/consul-ecs/" -} - -variable "subnets" { - description = "Subnets where the controller task should be deployed. If these are private subnets then there must be a NAT gateway for image pulls to work. If these are public subnets then you must also set assign_public_ip for image pulls to work." - type = list(string) -} - -variable "consul_server_http_addr" { - description = "The HTTP(S) address of the Consul server. This must be a full URL, including port and scheme, e.g. https://consul.example.com:8501." - type = string -} - -variable "name_prefix" { - description = "The prefix that will be used for all resources created by this module. Must be non-empty." - type = string -} - -variable "consul_server_ca_cert_arn" { - description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate." - type = string - default = "" -} - -variable "assign_public_ip" { - description = "Configure the ECS Service to assign a public IP to the task. This is required if running tasks on a public subnet." - type = bool - default = false -} - -variable "consul_partitions_enabled" { - description = "Enable admin partitions [Consul Enterprise]." - type = bool - default = false -} - -variable "consul_partition" { - description = "Admin partition the controller will manage [Consul Enterprise]." - type = string - default = "default" -} - -variable "security_groups" { - description = "Configure the ECS service with security groups. If not specified, the default security group for the VPC is used." - type = list(string) - default = [] -} - -variable "additional_execution_role_policies" { - description = "List of additional policy ARNs to attach to the execution role." - type = list(string) - default = [] -} diff --git a/modules/controller/README.md b/modules/controller/README.md new file mode 100644 index 00000000..4a7493e9 --- /dev/null +++ b/modules/controller/README.md @@ -0,0 +1,5 @@ +# ECS Controller + +This module deploys a Consul ECS controller for managing tokens and consul services for tasks on the Consul service mesh. + +See https://www.consul.io/docs/ecs for additional documentation. diff --git a/modules/controller/config.tf b/modules/controller/config.tf new file mode 100644 index 00000000..a36c3c40 --- /dev/null +++ b/modules/controller/config.tf @@ -0,0 +1,41 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +locals { + httpTLSSettings = merge( + { + port = var.tls ? 8501 : 8500 + http = var.tls + }, + var.http_tls_config + ) + + grpcTLSSettings = merge( + { + port = var.tls ? 8503 : 8502 + }, + var.grpc_tls_config + ) + + config = { + controller = { + iamRolePath = var.iam_role_path + partitionsEnabled = var.consul_partitions_enabled + partition = var.consul_partitions_enabled ? var.consul_partition : "" + } + bootstrapDir = "/consul" + consulServers = { + hosts = var.consul_server_address + skipServerWatch = var.skip_server_watch + defaults = { + tls = var.tls + tlsServerName = var.tls_server_name + caCertFile = var.ca_cert_file + } + http = local.httpTLSSettings + grpc = local.grpcTLSSettings + } + } + + encoded_config = jsonencode(local.config) +} \ No newline at end of file diff --git a/modules/acl-controller/main.tf b/modules/controller/main.tf similarity index 77% rename from modules/acl-controller/main.tf rename to modules/controller/main.tf index 8f9a4251..9df62760 100644 --- a/modules/acl-controller/main.tf +++ b/modules/controller/main.tf @@ -1,8 +1,13 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 +locals { + https_ca_cert_arn = var.consul_https_ca_cert_arn != "" ? var.consul_https_ca_cert_arn : var.consul_server_ca_cert_arn + grpc_ca_cert_arn = var.consul_grpc_ca_cert_arn != "" ? var.consul_grpc_ca_cert_arn : var.consul_server_ca_cert_arn +} + resource "aws_ecs_service" "this" { - name = "consul-acl-controller" + name = "consul-ecs-controller" cluster = var.ecs_cluster_arn task_definition = aws_ecs_task_definition.this.arn desired_count = 1 @@ -16,7 +21,7 @@ resource "aws_ecs_service" "this" { } resource "aws_ecs_task_definition" "this" { - family = "${var.name_prefix}-consul-acl-controller" + family = "${var.name_prefix}-consul-ecs-controller" requires_compatibilities = var.requires_compatibilities network_mode = "awsvpc" cpu = 256 @@ -25,19 +30,11 @@ resource "aws_ecs_task_definition" "this" { execution_role_arn = aws_iam_role.this_execution.arn container_definitions = jsonencode([ { - name = "consul-acl-controller" + name = "consul-ecs-controller" image = var.consul_ecs_image essential = true logConfiguration = var.log_configuration, - command = concat( - [ - "acl-controller", "-iam-role-path", var.iam_role_path, - ], - var.consul_partitions_enabled ? [ - "-partitions-enabled", - "-partition", var.consul_partition - ] : [], - ) + command = ["controller"] linuxParameters = { initProcessEnabled = true } @@ -46,16 +43,23 @@ resource "aws_ecs_task_definition" "this" { name = "CONSUL_HTTP_TOKEN", valueFrom = var.consul_bootstrap_token_secret_arn }], - var.consul_server_ca_cert_arn != "" ? [ + local.grpc_ca_cert_arn != "" ? [ { - name = "CONSUL_CACERT_PEM", - valueFrom = var.consul_server_ca_cert_arn - } - ] : []) + name = "CONSUL_GRPC_CACERT_PEM", + valueFrom = local.grpc_ca_cert_arn + }, + ] : [], + local.https_ca_cert_arn != "" ? [ + { + name = "CONSUL_HTTPS_CACERT_PEM", + valueFrom = local.https_ca_cert_arn + }, + ] : [], + ) environment = [ { - name = "CONSUL_HTTP_ADDR" - value = var.consul_server_http_addr + name = "CONSUL_ECS_CONFIG_JSON", + value = local.encoded_config } ] readonlyRootFilesystem = true @@ -64,7 +68,7 @@ resource "aws_ecs_task_definition" "this" { } resource "aws_iam_role" "this_task" { - name = "${var.name_prefix}-consul-acl-controller-task" + name = "${var.name_prefix}-consul-ecs-controller-task" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ @@ -103,7 +107,7 @@ resource "aws_iam_role" "this_task" { } resource "aws_iam_policy" "this_execution" { - name = "${var.name_prefix}-consul-acl-controller-execution" + name = "${var.name_prefix}-consul-ecs-controller-execution" path = "/ecs/" description = "Consul controller execution" @@ -145,7 +149,7 @@ EOF } resource "aws_iam_role" "this_execution" { - name = "${var.name_prefix}-consul-acl-controller-execution" + name = "${var.name_prefix}-consul-ecs-controller-execution" path = "/ecs/" assume_role_policy = < 0 || var.acls + https_ca_cert_arn = var.consul_https_ca_cert_arn != "" ? var.consul_https_ca_cert_arn : var.consul_server_ca_cert_arn + grpc_ca_cert_arn = var.consul_grpc_ca_cert_arn != "" ? var.consul_grpc_ca_cert_arn : var.consul_server_ca_cert_arn - consul_agent_defaults_hcl = templatefile( - "${path.module}/templates/consul_agent_defaults.hcl.tpl", - { - gossip_encryption_enabled = local.gossip_encryption_enabled - retry_join = var.retry_join - tls = var.tls - acls = var.acls - partition = var.consul_partition - primary_datacenter = var.consul_primary_datacenter - enable_token_replication = var.enable_acl_token_replication - audit_logging = var.audit_logging - } - ) + defaulted_check_containers = [for def in local.container_defs_with_depends_on : def.name + if contains(keys(def), "essential") && contains(keys(def), "healthCheck") && (try(def.healthCheck, null) != null)] } resource "aws_ecs_task_definition" "this" { @@ -189,11 +175,11 @@ resource "aws_ecs_task_definition" "this" { local.container_defs_with_depends_on, [ { - name = "consul-ecs-mesh-init" + name = "consul-ecs-control-plane" image = var.consul_ecs_image essential = false logConfiguration = var.log_configuration - command = ["mesh-init"] + command = ["control-plane"] mountPoints = [ local.consul_data_mount_read_write, { @@ -213,88 +199,51 @@ resource "aws_ecs_task_definition" "this" { linuxParameters = { initProcessEnabled = true } - }, - { - name = "consul-client" - image = var.consul_image - essential = false - portMappings = [] - logConfiguration = var.log_configuration - entryPoint = ["/bin/sh", "-ec"] - command = [replace( - templatefile( - "${path.module}/templates/consul_client_command.tpl", - { - consul_agent_defaults_hcl = local.consul_agent_defaults_hcl - consul_agent_configuration_hcl = var.consul_agent_configuration - tls = var.tls - acls = var.acls - consul_http_addr = var.consul_http_addr - https = var.consul_https_ca_cert_arn != "" - client_token_auth_method_name = var.client_token_auth_method_name - consul_partition = var.consul_partition - region = data.aws_region.current.name - } - ), "\r", "") - ] - mountPoints = [ - local.consul_data_mount_read_write, - { - sourceVolume = local.consul_binary_volume_name - containerPath = "/bin/consul-inject" - } - ] - linuxParameters = { - initProcessEnabled = true + healthCheck = { + command = ["CMD-SHELL", "curl localhost:10000/consul-ecs/health"] # consul-ecs-control-plane exposes a listener on 10000 to indicate it's readiness + interval = 30 + retries = 10 + timeout = 5 } - cpu = 0 - volumesFrom = [] - environment = [ - { - name = "CONSUL_DATACENTER" - value = var.consul_datacenter - } - ] secrets = concat( var.tls ? [ - { - name = "CONSUL_CACERT_PEM", - valueFrom = var.consul_server_ca_cert_arn - } - ] : [], - var.consul_https_ca_cert_arn != "" ? [ - { - name = "CONSUL_HTTPS_CACERT_PEM", - valueFrom = var.consul_https_ca_cert_arn - } - ] : [], - local.gossip_encryption_enabled ? [ - { - name = "CONSUL_GOSSIP_ENCRYPTION_KEY", - valueFrom = var.gossip_key_secret_arn - } + concat( + local.https_ca_cert_arn != "" ? [ + { + name = "CONSUL_HTTPS_CACERT_PEM", + valueFrom = local.https_ca_cert_arn + }, + ] : [], + local.grpc_ca_cert_arn != "" ? [ + { + name = "CONSUL_GRPC_CACERT_PEM", + valueFrom = local.grpc_ca_cert_arn + }, + ] : [], + ), ] : [], + [] ) }, { - name = "sidecar-proxy" - image = var.envoy_image + name = "consul-dataplane" + image = var.consul_dataplane_image essential = false logConfiguration = var.log_configuration entryPoint = ["/consul/consul-ecs", "envoy-entrypoint"] - command = ["envoy", "--config-path", "/consul/envoy-bootstrap.json"] + command = ["consul-dataplane", "-config-file", "/consul/consul-dataplane.json"] # consul-ecs-control-plane dumps the dataplane's config into consul-dataplane.json portMappings = [] mountPoints = [ local.consul_data_mount ] dependsOn = [ { - containerName = "consul-ecs-mesh-init" - condition = "SUCCESS" + containerName = "consul-ecs-control-plane" + condition = "HEALTHY" }, ] healthCheck = { - command = ["/consul/consul-ecs", "net-dial", format("127.0.0.1:%d", var.envoy_public_listener_port)] + command = ["/consul/consul-ecs", "net-dial", format("127.0.0.1:%d", var.envoy_readiness_port)] interval = 30 retries = 3 timeout = 5 @@ -312,35 +261,7 @@ resource "aws_ecs_task_definition" "this" { }] }, ], - local.health_sync_enabled ? [{ - name = "consul-ecs-health-sync" - image = var.consul_ecs_image - essential = false - logConfiguration = var.log_configuration - command = ["health-sync"] - cpu = 0 - volumesFrom = [] - environment = [ - { - name = "CONSUL_ECS_CONFIG_JSON", - value = local.encoded_config - } - ] - portMappings = [] - mountPoints = [ - local.consul_data_mount - ] - dependsOn = [ - { - containerName = "consul-ecs-mesh-init" - condition = "SUCCESS" - }, - ] - linuxParameters = { - initProcessEnabled = true - } - }] : [], ) ) ) -} +} \ No newline at end of file diff --git a/modules/mesh-task/templates/consul_agent_defaults.hcl.tpl b/modules/mesh-task/templates/consul_agent_defaults.hcl.tpl deleted file mode 100644 index 47cc07a3..00000000 --- a/modules/mesh-task/templates/consul_agent_defaults.hcl.tpl +++ /dev/null @@ -1,63 +0,0 @@ -addresses = { - dns = "127.0.0.1" - grpc = "127.0.0.1" - http = "127.0.0.1" -} -advertise_addr = "$ECS_IPV4" -advertise_reconnect_timeout = "15m" -client_addr = "0.0.0.0" -datacenter = "$CONSUL_DATACENTER" -enable_central_service_config = true -%{ if gossip_encryption_enabled ~} -encrypt = "$CONSUL_GOSSIP_ENCRYPTION_KEY" -%{ endif ~} -leave_on_terminate = true -ports { - grpc = 8502 -} -retry_join = [ -%{ for j in retry_join ~} - "${j}", -%{ endfor ~} -] - -%{~ if tls ~} -auto_encrypt = { - tls = true - ip_san = ["$ECS_IPV4"] -} -ca_file = "/consul/consul-ca-cert.pem" -verify_outgoing = true -%{ endif ~} - -%{~ if acls ~} -acl { - enabled = true - default_policy = "deny" - down_policy = "async-cache" - tokens { - agent = "$AGENT_TOKEN" - } - enable_token_replication = ${enable_token_replication} -} -%{ endif ~} - -%{ if partition != null && partition != "" ~} -partition = "${partition}" -%{ endif ~} - -%{ if primary_datacenter != "" ~} -primary_datacenter = "${primary_datacenter}" -%{ endif ~} - -%{~ if audit_logging ~} -audit { - enabled = true - sink "stdout" { - type = "file" - format = "json" - path = "/dev/stdout" - delivery_guarantee = "best-effort" - } -} -%{ endif ~} diff --git a/modules/mesh-task/templates/consul_client_command.tpl b/modules/mesh-task/templates/consul_client_command.tpl deleted file mode 100644 index 4abbaacb..00000000 --- a/modules/mesh-task/templates/consul_client_command.tpl +++ /dev/null @@ -1,97 +0,0 @@ -cp /bin/consul /bin/consul-inject/consul - -ECS_IPV4=$(curl -s $ECS_CONTAINER_METADATA_URI_V4 | jq -r '.Networks[0].IPv4Addresses[0]') - -ECS_TASK_META=$(curl -s $ECS_CONTAINER_METADATA_URI_V4/task) -TASK_REGION=$(echo "$ECS_TASK_META" | jq -r .TaskARN | cut -d ':' -f 4) -TASK_ID=$(echo "$ECS_TASK_META" | jq -r .TaskARN | cut -d '/' -f 3) -CLUSTER_ARN=$(echo "$ECS_TASK_META" | jq -r .TaskARN | sed -E 's|:task/([^/]+).*|:cluster/\1|') - -%{ if tls ~} -echo "$CONSUL_CACERT_PEM" > /consul/consul-ca-cert.pem -%{ endif ~} - -%{ if https ~} -echo "$CONSUL_HTTPS_CACERT_PEM" > /consul/consul-https-ca-cert.pem -%{ endif ~} - -%{ if acls ~} -consul_login() { - echo "Logging into auth method: name=${ client_token_auth_method_name }" - consul login \ - -http-addr ${ consul_http_addr } \ - %{ if https ~} - -ca-file /consul/consul-https-ca-cert.pem \ - %{ endif ~} - %{ if consul_partition != "" ~} - -partition ${ consul_partition } \ - %{ endif ~} - -type aws-iam \ - -method ${ client_token_auth_method_name } \ - -meta "consul.hashicorp.com/task-id=$TASK_ID" \ - -meta "consul.hashicorp.com/cluster=$CLUSTER_ARN" \ - -aws-region "$TASK_REGION" \ - -aws-auto-bearer-token -aws-include-entity \ - -token-sink-file /consul/client-token -} - -read_token_stale() { - # Attempt to read the token via the HTTP API. We don't use the `consul` CLI to read - # the token here because there is an issue in Consul 1.15.0 that causes the read to - # fail even for valid requests. The issue is fixed in Consul > 1.15.0 but in order - # to support 1.15.0 we use the HTTP API in all cases. - curl '${ consul_http_addr }/v1/acl/token/self?stale' \ - -H "X-Consul-Token: \$(cat /consul/client-token)" \ - %{ if https ~} - --cacert /consul/consul-https-ca-cert.pem \ - %{ endif ~} - -sS -o /dev/null -} - -# Retry in order to login successfully. -while ! consul_login; do - sleep 2 -done - -# Allow the health-sync container to read this token for consul logout. -# The user here is root, but health-sync runs as a 'consul-ecs' user. -chmod 0644 /consul/client-token - -# Wait for raft replication to hopefully occur. Without this, an "ACL not found" may be cached for a while. -# Technically, the problem could still occur but this should handle most cases. -# This waits at most 2s (20 attempts with 0.1s sleep) -COUNT=20 -while [ "$COUNT" -gt 0 ]; do - echo "Checking that the ACL token exists when reading it in the stale consistency mode ($COUNT attempts remaining)" - if read_token_stale; then - echo "Successfully read ACL token from the server" - break - fi - sleep 0.1 - COUNT=$((COUNT - 1)) -done -if [ "$COUNT" -eq 0 ]; then - echo "Unable to read ACL token from a Consul server; please check that your server cluster is healthy" - exit 1 -fi - -# This is an env var which is interpolated into the agent-defaults.hcl -export AGENT_TOKEN=$(cat /consul/client-token) -%{ endif ~} - -cat << EOF > /consul/agent-defaults.hcl -${consul_agent_defaults_hcl} -EOF - -%{ if consul_agent_configuration_hcl != "" ~} -cat << EOF > /consul/agent-extra.hcl -${consul_agent_configuration_hcl} -EOF -%{ endif ~} - -exec consul agent \ - -data-dir /consul/data \ - -config-file /consul/agent-defaults.hcl \ -%{ if consul_agent_configuration_hcl != "" ~} - -config-file /consul/agent-extra.hcl -%{ endif ~} diff --git a/modules/mesh-task/validation.tf b/modules/mesh-task/validation.tf index 0bf36cc9..40db6156 100644 --- a/modules/mesh-task/validation.tf +++ b/modules/mesh-task/validation.tf @@ -3,11 +3,9 @@ locals { require_port_unless_no_port_true = !var.outbound_only && var.port == 0 ? file("ERROR: port must be set if outbound_only is false") : null - require_ca_cert_if_tls_enabled = (var.tls && var.consul_server_ca_cert_arn == "") ? file("ERROR: consul_server_ca_cert_arn must be set if tls is true") : null - require_consul_http_addr_if_acls = (var.acls && var.consul_http_addr == "") ? file("ERROR: consul_http_addr must be set if acls is true") : null + require_listener_and_readiness_ports_to_be_different = (var.envoy_public_listener_port == var.envoy_readiness_port) ? file("ERROR: envoy_public_listener_port should not conflict with envoy_readiness_port") : null require_namespace_if_partition_is_set = (var.consul_partition != "" && var.consul_namespace == "") ? file("ERROR: consul_namespace must be set if consul_partition is set") : null require_partition_if_namespace_is_set = (var.consul_namespace != "" && var.consul_partition == "") ? file("ERROR: consul_partition must be set if consul_namespace is set") : null require_no_additional_task_policies_with_passed_role = (!var.create_task_role && length(var.additional_task_role_policies) > 0) ? file("ERROR: cannot set additional_task_role_policies when create_task_role=false") : null require_no_additional_execution_policies_with_passed_role = (!var.create_execution_role && length(var.additional_execution_role_policies) > 0) ? file("ERROR: cannot set additional_execution_role_policies when create_execution_role=false") : null - require_acls_if_audit_enabled = (var.audit_logging && !var.acls) ? file("ERROR: ACLs must be enabled if audit logging is enabled") : null -} +} \ No newline at end of file diff --git a/modules/mesh-task/variables.tf b/modules/mesh-task/variables.tf index 3a8257d1..416e9249 100644 --- a/modules/mesh-task/variables.tf +++ b/modules/mesh-task/variables.tf @@ -124,6 +124,12 @@ variable "additional_execution_role_policies" { default = [] } +variable "skip_server_watch" { + description = "If true, setting this prevents the consul-dataplane and consul-ecs-control-plane from watching the Consul servers for changes. This is useful for situations where Consul servers are behind a load balancer." + type = bool + default = false +} + variable "port" { description = "Port that the application listens on. If the application does not listen on a port, set outbound_only to true." type = number @@ -139,19 +145,19 @@ variable "outbound_only" { variable "consul_image" { description = "Consul Docker image." type = string - default = "public.ecr.aws/hashicorp/consul:1.15.1" + default = "public.ecr.aws/hashicorp/consul:1.16.0" } variable "consul_ecs_image" { description = "consul-ecs Docker image." type = string - default = "public.ecr.aws/hashicorp/consul-ecs:0.6.0" + default = "ganeshrockz/ecs" } -variable "envoy_image" { - description = "Envoy Docker image." +variable "consul_dataplane_image" { + description = "consul-dataplane docker image." type = string - default = "envoyproxy/envoy-distroless:v1.23.1" + default = "docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.3-dev" } variable "envoy_public_listener_port" { @@ -165,7 +171,7 @@ variable "envoy_public_listener_port" { } validation { - error_message = "The envoy_public_listener_port must not conflict with the following ports that are reserved for Consul and Envoy: 8300, 8301, 8302, 8500, 8501, 8502, 8600, 19000." + error_message = "The envoy_public_listener_port must not conflict with the following ports that are reserved for Consul and Envoy: 8300, 8301, 8302, 8500, 8501, 8502, 8600, 19000, 10000." condition = !contains([ 8300, // consul rpc port 8301, // consul lan serf @@ -175,10 +181,37 @@ variable "envoy_public_listener_port" { 8502, // consul grpc 8600, // consul dns 19000, // envoy admin port + 10000, // consul-ecs-control-plane health check port ], var.envoy_public_listener_port) } } +variable "envoy_readiness_port" { + description = "The port that is exposed by Envoy to indicate it's readiness. ECS uses this port to detect envoy's readiness and start the app containers depending on this." + type = number + default = 22000 + + validation { + error_message = "The envoy_readiness_port must be greater than 0 and less than or equal to 65535." + condition = var.envoy_readiness_port > 0 && var.envoy_readiness_port <= 65535 + } + + validation { + error_message = "The envoy_readiness_port must not conflict with the following ports that are reserved for Consul and Envoy: 8300, 8301, 8302, 8500, 8501, 8502, 8600, 19000, 10000." + condition = !contains([ + 8300, // consul rpc port + 8301, // consul lan serf + 8302, // consul wan serf + 8500, // consul http + 8501, // consul https + 8502, // consul grpc + 8600, // consul dns + 19000, // envoy admin port + 10000, // consul-ecs-control-plane health check port + ], var.envoy_readiness_port) + } +} + variable "log_configuration" { description = "Task definition log configuration object (https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html)." type = any @@ -191,6 +224,11 @@ variable "container_definitions" { type = any } +variable "consul_server_address" { + description = "Address of the consul server host" + type = string +} + variable "upstreams" { description = <<-EOT Upstream services that this service will call. This follows the schema of the `proxy.upstreams` field of the @@ -233,77 +271,6 @@ variable "upstreams" { } } -variable "checks" { - description = <<-EOT - A list of maps defining Consul checks for this service. This follows the schema of the `service.checks` field - of the consul-ecs config file (https://github.com/hashicorp/consul-ecs/blob/main/config/schema.json). See - the Consul checks documentation (https://www.consul.io/docs/discovery/checks) for more. - EOT - - type = any - default = [] - - validation { - error_message = "Check fields must be one of 'checkId', 'name', 'args', 'items', 'interval', 'timeout', 'ttl', 'http', 'header', 'method', 'body', 'tcp', 'status', 'notes', 'tlsServerName', 'tlsSkipVerify', 'grpc', 'grpcUseTls', 'h2ping', 'h2pingUseTls', 'aliasNode', 'aliasService', 'successBeforePassing', or 'failuresBeforeCritical'." - condition = alltrue(flatten([ - for check in var.checks : [ - for key in keys(check) : contains( - [ - "checkId", - "name", - "args", - "items", - "interval", - "timeout", - "ttl", - "http", - "header", - "method", - "body", - "tcp", - "status", - "notes", - "tlsServerName", - "tlsSkipVerify", - "grpc", - "grpcUseTls", - "h2ping", - "h2pingUseTls", - "aliasNode", - "aliasService", - "successBeforePassing", - "failuresBeforeCritical", - ], - key - ) - ] - ])) - } -} - -variable "retry_join" { - description = "Arguments to pass to -retry-join (https://www.consul.io/docs/agent/options#_retry_join)." - type = list(string) -} - -variable "consul_http_addr" { - description = "Consul HTTP Address. Required when using the IAM Auth Method to obtain ACL tokens." - type = string - default = "" -} - -variable "consul_https_ca_cert_arn" { - description = "The ARN of the Secrets Manager secret containing the CA certificate for Consul's HTTPS interface." - type = string - default = "" -} - -variable "client_token_auth_method_name" { - description = "The name of the Consul Auth Method to login to for client tokens." - type = string - default = "iam-ecs-client-token" -} - variable "service_token_auth_method_name" { description = "The name of the Consul Auth Method to login to for service tokens." type = string @@ -317,53 +284,50 @@ variable "tags" { } variable "tls" { - description = "Whether to enable TLS for the mesh-task for the control plane traffic." + description = "Whether to enable TLS for the communication between mesh-task and Consul's HTTP and gRPC interfaces." type = bool default = false } -variable "consul_server_ca_cert_arn" { - description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's internal RPC." +variable "tls_server_name" { + description = "The server name to use as the SNI host when connecting via TLS for Consul's HTTP and gRPC interfaces." type = string default = "" } -variable "gossip_key_secret_arn" { - description = "The ARN of the Secrets Manager secret containing the Consul gossip encryption key." +variable "ca_cert_file" { + description = <<-EOT + The CA certificate file for Consul's internal HTTP and gRPC interfaces. `CONSUL_HTTPS_CACERT_PEM` and + `CONSUL_GRPC_CACERT_PEM` takes a higher precedence when configuring TLS settings in the mesh-task." + EOT type = string default = "" } -variable "acls" { - description = "Whether to enable ACLs for the mesh task." - type = bool - default = false -} - -variable "enable_acl_token_replication" { - type = bool - description = "Whether or not to enable ACL token replication for federated. ACL token replication is required when the mesh-task is part of a WAN federated Consul service mesh." - default = false -} - -variable "consul_datacenter" { +variable "consul_server_ca_cert_arn" { + description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's internal RPC and HTTP interfaces." type = string - description = "The name of the Consul datacenter the client belongs to." - default = "dc1" + default = "" } -variable "consul_primary_datacenter" { +variable "consul_grpc_ca_cert_arn" { + description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's internal RPC. Overrides var.consul_server_ca_cert_arn" type = string - description = "The name of the primary Consul datacenter. Required when the mesh-task is part of a WAN federated Consul service mesh." default = "" } -variable "consul_agent_configuration" { +variable "consul_https_ca_cert_arn" { + description = "The ARN of the Secrets Manager secret containing the CA certificate for Consul server's HTTP interface. Overrides var.consul_server_ca_cert_arn" type = string - description = "The contents of a configuration file for the Consul Agent in HCL format." default = "" } +variable "acls" { + description = "Whether to enable ACLs for the mesh task." + type = bool + default = false +} + variable "application_shutdown_delay_seconds" { type = number description = <<-EOT @@ -384,7 +348,7 @@ variable "consul_ecs_config" { description = <<-EOT Additional configuration to pass to the consul-ecs binary for Consul service and sidecar proxy registration requests. This accepts a subset of the consul-ecs config file (https://github.com/hashicorp/consul-ecs/blob/main/config/schema.json). - For the remainder of the consul-ecs config file contents, use the variables `upstreams`, `checks`, `consul_service_name`, + For the remainder of the consul-ecs config file contents, use the variables `upstreams`, `consul_service_name`, `consul_service_tags`, `consul_service_meta`, `consul_namespace`, and `consul_partition`. In most cases, these separate variables will suffice. EOT @@ -477,8 +441,36 @@ variable "consul_ecs_config" { } -variable "audit_logging" { - description = "Whether to enable audit logging for the Consul agent [Consul Enterprise]. ACLs must be enabled to enable audit logging." - type = bool - default = false +variable "http_tls_config" { + type = any + default = {} + description = <<-EOT + This accepts HTTP specific TLS configuration based on the `consulServers.http` schema present in https://github.com/hashicorp/consul-ecs/blob/main/config/schema.json. + If empty, values of `var.tls`, `var.tls_server_name` and `var.ca_cert_file` will be used to configure TLS settings for HTTP. + EOT + + validation { + error_message = "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_tls_config." + condition = alltrue([ + for key in keys(var.http_tls_config) : + contains(["port", "https", "tls", "tlsServerName", "caCertFile"], key) + ]) + } } + +variable "grpc_tls_config" { + type = any + default = {} + description = <<-EOT + This accepts gRPC specific TLS configuration based on the `consulServers.grpc` schema present in https://github.com/hashicorp/consul-ecs/blob/main/config/schema.json. + If empty, values of `var.tls`, `var.tls_server_name` and `var.ca_cert_file` will be used to configure TLS settings for gRPC. + EOT + + validation { + error_message = "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_tls_config." + condition = alltrue([ + for key in keys(var.grpc_tls_config) : + contains(["port", "tls", "tlsServerName", "caCertFile"], key) + ]) + } +} \ No newline at end of file diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index d9719460..a3b4e261 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -25,21 +25,6 @@ import ( "github.com/stretchr/testify/require" ) -// Test the validation that if TLS is enabled, Consul's CA certificate must also be provided. -func TestValidation_CACertRequiredIfTLSIsEnabled(t *testing.T) { - t.Parallel() - terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "./terraform/ca-cert-validate", - NoColor: true, - }) - t.Cleanup(func() { - _, _ = terraform.DestroyE(t, terraformOptions) - }) - _, err := terraform.InitAndPlanE(t, terraformOptions) - require.Error(t, err) - require.Contains(t, err.Error(), "ERROR: consul_server_ca_cert_arn must be set if tls is true") -} - // TestVolumeVariable tests passing a list of volumes to mesh-task. // This validates a big nested dynamic block in mesh-task. func TestVolumeVariable(t *testing.T) { @@ -401,50 +386,59 @@ func TestValidation_EnvoyPublicListenerPort(t *testing.T) { } } -func TestValidation_ChecksVariable(t *testing.T) { +func TestValidation_EnvoyReadinessPort(t *testing.T) { t.Parallel() cases := map[string]struct { - checksFile string - error bool + port int + error string }{ - "no-checks": { - checksFile: "test-no-checks.json", + "allowed-port": { + port: 23000, }, - "valid-checks": { - checksFile: "test-valid-checks.json", + "too-high-port": { + port: 65536, + error: "The envoy_readiness_port must be greater than 0 and less than or equal to 65535.", }, - "invalid-checks": { - checksFile: "test-invalid-checks.json", - error: true, + "disallowed-port": { + port: 19000, + error: "The envoy_readiness_port must not conflict with the following ports that are reserved for Consul and Envoy", + }, + "conflicts-with-listener-port": { + port: 20000, + error: "envoy_public_listener_port should not conflict with envoy_readiness_port", }, } terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/checks-validate", + TerraformDir: "./terraform/envoy-readiness-port-validate", NoColor: true, } terraform.Init(t, terraformOptions) for name, c := range cases { + c := c t.Run(name, func(t *testing.T) { + t.Parallel() + out, err := terraform.PlanE(t, &terraform.Options{ TerraformDir: terraformOptions.TerraformDir, NoColor: true, Vars: map[string]interface{}{ - "checks_file": c.checksFile, + "envoy_readiness_port": c.port, }, }) - if c.error { - require.Error(t, err) - require.Regexp(t, "Check fields must be one of.*", out) - } else { + if c.error == "" { require.NoError(t, err) + } else { + // handle multiline error messages. + regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") + require.Error(t, err) + require.Regexp(t, regex, out) } }) } - } func TestValidation_ConsulServiceName(t *testing.T) { @@ -558,6 +552,120 @@ func TestValidation_ConsulEcsConfigVariable(t *testing.T) { } } +func TestValidation_HTTPTLSConfig(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + configFile string + errors []string + }{ + "empty-map": { + configFile: "test-empty-config.json", + }, + "complete-config": { + configFile: "test-complete-config.json", + }, + "partial-config": { + configFile: "test-partial-config.json", + }, + "invalid-config": { + configFile: "test-invalid-config.json", + errors: []string{ + "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_tls_config.", + }, + }, + } + + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/http-tls-config-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) + + for name, c := range cases { + c := c + + t.Run(name, func(t *testing.T) { + t.Parallel() + + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "http_tls_config_file": c.configFile, + }, + }) + + if len(c.errors) == 0 { + require.NoError(t, err) + } else { + for _, msg := range c.errors { + // error messages are wrapped, so a space may turn into a newline. + regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") + require.Regexp(t, regex, out) + } + } + }) + } +} + +func TestValidation_GRPCTLSConfig(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + configFile string + errors []string + }{ + "empty-map": { + configFile: "test-empty-config.json", + }, + "complete-config": { + configFile: "test-complete-config.json", + }, + "partial-config": { + configFile: "test-partial-config.json", + }, + "invalid-config": { + configFile: "test-invalid-config.json", + errors: []string{ + "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_tls_config.", + }, + }, + } + + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/grpc-tls-config-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) + + for name, c := range cases { + c := c + + t.Run(name, func(t *testing.T) { + t.Parallel() + + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "grpc_tls_config_file": c.configFile, + }, + }) + + if len(c.errors) == 0 { + require.NoError(t, err) + } else { + for _, msg := range c.errors { + // error messages are wrapped, so a space may turn into a newline. + regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") + require.Regexp(t, regex, out) + } + } + }) + } +} + // Test the validation that both partition and namespace must be provided or neither. func TestValidation_NamespaceAndPartitionRequired(t *testing.T) { t.Parallel() @@ -664,148 +772,150 @@ func TestValidation_RolePath(t *testing.T) { } -func TestValidation_MeshGateway(t *testing.T) { - t.Parallel() - - terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "./terraform/mesh-gateway-validate", - NoColor: true, - }) - _ = terraform.Init(t, terraformOptions) - - cases := map[string]struct { - kind string - enableMeshGatewayWANFed bool - tls bool - securityGroups []string - wanAddress string - lbEnabled bool - lbVpcID string - lbSubnets []string - lbCreateSecGroup bool - lbModifySecGroup bool - lbModifySecGroupID string - expError string - }{ - "kind is required": { - kind: "", - enableMeshGatewayWANFed: false, - expError: `variable "kind" is not set`, - }, - "kind must be mesh-gateway": { - kind: "not-mesh-gateway", - enableMeshGatewayWANFed: false, - expError: `Gateway kind must be 'mesh-gateway'`, - }, - "no WAN federation": { - kind: "mesh-gateway", - enableMeshGatewayWANFed: false, - }, - "mesh gateway WAN federation, no TLS": { - kind: "mesh-gateway", - enableMeshGatewayWANFed: true, - tls: false, - expError: "tls must be true when enable_mesh_gateway_wan_federation is true", - }, - "mesh gateway WAN federation": { - kind: "mesh-gateway", - enableMeshGatewayWANFed: true, - tls: true, - }, - "WAN address and LB enabled": { - kind: "mesh-gateway", - enableMeshGatewayWANFed: false, - wanAddress: "10.1.2.3", - lbEnabled: true, - expError: "Only one of wan_address or lb_enabled may be provided", - }, - "lb_enabled": { - kind: "mesh-gateway", - lbEnabled: true, - lbSubnets: []string{"subnet"}, - lbVpcID: "vpc", - }, - "lb_enabled and no lb subnets": { - kind: "mesh-gateway", - lbEnabled: true, - lbVpcID: "vpc", - expError: "lb_subnets is required when lb_enabled is true", - }, - "lb_enabled and no VPC": { - kind: "mesh-gateway", - lbEnabled: true, - lbSubnets: []string{"subnet"}, - expError: "lb_vpc_id is required when lb_enabled is true", - }, - "lb create security group and modify security group": { - kind: "mesh-gateway", - securityGroups: []string{"sg"}, - lbEnabled: true, - lbSubnets: []string{"subnet"}, - lbVpcID: "vpc", - lbCreateSecGroup: true, - lbModifySecGroup: true, - expError: "Only one of lb_create_security_group or lb_modify_security_group may be true", - }, - "lb modify security group and no security group ID": { - kind: "mesh-gateway", - securityGroups: []string{"sg"}, - lbEnabled: true, - lbSubnets: []string{"subnet"}, - lbVpcID: "vpc", - lbCreateSecGroup: false, - lbModifySecGroup: true, - lbModifySecGroupID: "", - expError: "lb_modify_security_group_id is required when lb_modify_security_group is true", - }, - "lb modify security group with security group ID": { - kind: "mesh-gateway", - securityGroups: []string{"sg"}, - lbEnabled: true, - lbSubnets: []string{"subnet"}, - lbVpcID: "vpc", - lbCreateSecGroup: false, - lbModifySecGroup: true, - lbModifySecGroupID: "mod-sg", - }, - } - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - t.Parallel() - - tfVars := map[string]interface{}{ - "enable_mesh_gateway_wan_federation": c.enableMeshGatewayWANFed, - "tls": c.tls, - "security_groups": c.securityGroups, - "wan_address": c.wanAddress, - "lb_enabled": c.lbEnabled, - "lb_subnets": c.lbSubnets, - "lb_vpc_id": c.lbVpcID, - "lb_create_security_group": c.lbCreateSecGroup, - "lb_modify_security_group": c.lbModifySecGroup, - "lb_modify_security_group_id": c.lbModifySecGroupID, - } - if len(c.kind) > 0 { - tfVars["kind"] = c.kind - } - applyOpts := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: terraformOptions.NoColor, - Vars: tfVars, - }) - t.Cleanup(func() { _, _ = terraform.DestroyE(t, applyOpts) }) - - _, err := terraform.PlanE(t, applyOpts) - if len(c.expError) > 0 { - require.Error(t, err) - require.Contains(t, err.Error(), c.expError) - } else { - require.NoError(t, err) - } - }) - } -} +// TODO: Revisit this test +// +// func TestValidation_MeshGateway(t *testing.T) { +// t.Parallel() + +// terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ +// TerraformDir: "./terraform/mesh-gateway-validate", +// NoColor: true, +// }) +// _ = terraform.Init(t, terraformOptions) + +// cases := map[string]struct { +// kind string +// enableMeshGatewayWANFed bool +// tls bool +// securityGroups []string +// wanAddress string +// lbEnabled bool +// lbVpcID string +// lbSubnets []string +// lbCreateSecGroup bool +// lbModifySecGroup bool +// lbModifySecGroupID string +// expError string +// }{ +// "kind is required": { +// kind: "", +// enableMeshGatewayWANFed: false, +// expError: `variable "kind" is not set`, +// }, +// "kind must be mesh-gateway": { +// kind: "not-mesh-gateway", +// enableMeshGatewayWANFed: false, +// expError: `Gateway kind must be 'mesh-gateway'`, +// }, +// "no WAN federation": { +// kind: "mesh-gateway", +// enableMeshGatewayWANFed: false, +// }, +// "mesh gateway WAN federation, no TLS": { +// kind: "mesh-gateway", +// enableMeshGatewayWANFed: true, +// tls: false, +// expError: "tls must be true when enable_mesh_gateway_wan_federation is true", +// }, +// "mesh gateway WAN federation": { +// kind: "mesh-gateway", +// enableMeshGatewayWANFed: true, +// tls: true, +// }, +// "WAN address and LB enabled": { +// kind: "mesh-gateway", +// enableMeshGatewayWANFed: false, +// wanAddress: "10.1.2.3", +// lbEnabled: true, +// expError: "Only one of wan_address or lb_enabled may be provided", +// }, +// "lb_enabled": { +// kind: "mesh-gateway", +// lbEnabled: true, +// lbSubnets: []string{"subnet"}, +// lbVpcID: "vpc", +// }, +// "lb_enabled and no lb subnets": { +// kind: "mesh-gateway", +// lbEnabled: true, +// lbVpcID: "vpc", +// expError: "lb_subnets is required when lb_enabled is true", +// }, +// "lb_enabled and no VPC": { +// kind: "mesh-gateway", +// lbEnabled: true, +// lbSubnets: []string{"subnet"}, +// expError: "lb_vpc_id is required when lb_enabled is true", +// }, +// "lb create security group and modify security group": { +// kind: "mesh-gateway", +// securityGroups: []string{"sg"}, +// lbEnabled: true, +// lbSubnets: []string{"subnet"}, +// lbVpcID: "vpc", +// lbCreateSecGroup: true, +// lbModifySecGroup: true, +// expError: "Only one of lb_create_security_group or lb_modify_security_group may be true", +// }, +// "lb modify security group and no security group ID": { +// kind: "mesh-gateway", +// securityGroups: []string{"sg"}, +// lbEnabled: true, +// lbSubnets: []string{"subnet"}, +// lbVpcID: "vpc", +// lbCreateSecGroup: false, +// lbModifySecGroup: true, +// lbModifySecGroupID: "", +// expError: "lb_modify_security_group_id is required when lb_modify_security_group is true", +// }, +// "lb modify security group with security group ID": { +// kind: "mesh-gateway", +// securityGroups: []string{"sg"}, +// lbEnabled: true, +// lbSubnets: []string{"subnet"}, +// lbVpcID: "vpc", +// lbCreateSecGroup: false, +// lbModifySecGroup: true, +// lbModifySecGroupID: "mod-sg", +// }, +// } +// for name, c := range cases { +// c := c +// t.Run(name, func(t *testing.T) { +// t.Parallel() + +// tfVars := map[string]interface{}{ +// "enable_mesh_gateway_wan_federation": c.enableMeshGatewayWANFed, +// "tls": c.tls, +// "security_groups": c.securityGroups, +// "wan_address": c.wanAddress, +// "lb_enabled": c.lbEnabled, +// "lb_subnets": c.lbSubnets, +// "lb_vpc_id": c.lbVpcID, +// "lb_create_security_group": c.lbCreateSecGroup, +// "lb_modify_security_group": c.lbModifySecGroup, +// "lb_modify_security_group_id": c.lbModifySecGroupID, +// } +// if len(c.kind) > 0 { +// tfVars["kind"] = c.kind +// } +// applyOpts := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ +// TerraformDir: terraformOptions.TerraformDir, +// NoColor: terraformOptions.NoColor, +// Vars: tfVars, +// }) +// t.Cleanup(func() { _, _ = terraform.DestroyE(t, applyOpts) }) + +// _, err := terraform.PlanE(t, applyOpts) +// if len(c.expError) > 0 { +// require.Error(t, err) +// require.Contains(t, err.Error(), c.expError) +// } else { +// require.NoError(t, err) +// } +// }) +// } +// } func TestBasic(t *testing.T) { t.Parallel() @@ -818,8 +928,8 @@ func TestBasic(t *testing.T) { datacenter string }{ {secure: false}, - {secure: true}, - {secure: true, enterprise: true}, + //{secure: true}, + // {secure: true, enterprise: true}, } cfg := suite.Config() @@ -925,6 +1035,44 @@ func TestBasic(t *testing.T) { consulServerTaskARN = tasks.TaskARNs[0] }) + var controllerTaskID string + if c.secure { + retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { + taskListOut := shell.RunCommandAndGetOutput(t, shell.Command{ + Command: "aws", + Args: []string{ + "ecs", + "list-tasks", + "--region", + cfg.Region, + "--cluster", + c.ecsClusterARN, + "--family", + fmt.Sprintf("%s-consul-ecs-controller", randomSuffix), + }, + }) + + var tasks listTasksResponse + require.NoError(t, json.Unmarshal([]byte(taskListOut), &tasks)) + require.Len(t, tasks.TaskARNs, 1) + controllerTaskARN := tasks.TaskARNs[0] + arnParts := strings.Split(controllerTaskARN, "/") + controllerTaskID = arnParts[len(arnParts)-1] + }) + + // Check controller logs to see if the anonymous token gets configured. This should + // indicate that the controller has created the service auth method, policies and roles. + retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { + appLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, controllerTaskID, "consul-ecs-controller") + require.NoError(r, err) + + logMsg := "Successfully configured the anonymous token" + appLogs = appLogs.Filter(logMsg) + require.Len(r, appLogs, 1) + require.Contains(r, appLogs[0].Message, logMsg) + }) + } + // Wait for both tasks to be registered in Consul. retry.RunWith(&retry.Timer{Timeout: 6 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { out, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", `/bin/sh -c "consul catalog services"`) @@ -942,23 +1090,34 @@ func TestBasic(t *testing.T) { } // Wait for passing health check for `serverServiceName` and `clientServiceName`. - // `serverServiceName` has a Consul native HTTP check. - // `clientServiceName` has a check synced from ECS. - services := []string{serverServiceName, clientServiceName} - for _, serviceName := range services { - retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { - out, err := helpers.ExecuteRemoteCommand( - t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", - fmt.Sprintf(`/bin/sh -c 'curl %s localhost:8500/v1/health/checks/%s_%s'`, tokenHeader, serviceName, randomSuffix), - ) - r.Check(err) + // `clientServiceName` has a check synced from ECS and a consul-dataplane check. + // We check if both of them are in a passing state. + retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { + out, err := helpers.ExecuteRemoteCommand( + t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", + fmt.Sprintf(`/bin/sh -c 'curl %s localhost:8500/v1/health/checks/%s_%s'`, tokenHeader, clientServiceName, randomSuffix), + ) + r.Check(err) - statusRegex := regexp.MustCompile(`"Status"\s*:\s*"passing"`) - if statusRegex.FindAllString(out, 1) == nil { - r.Errorf("Check status not yet passing") - } - }) - } + statusRegex := regexp.MustCompile(`"Status"\s*:\s*"passing"`) + if statusRegex.FindAllString(out, 2) == nil { + r.Errorf("Check status not yet passing") + } + }) + + // `serverServiceName` has a backing consul-dataplane check. + retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { + out, err := helpers.ExecuteRemoteCommand( + t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", + fmt.Sprintf(`/bin/sh -c 'curl %s localhost:8500/v1/health/checks/%s_%s'`, tokenHeader, serverServiceName, randomSuffix), + ) + r.Check(err) + + statusRegex := regexp.MustCompile(`"Status"\s*:\s*"passing"`) + if statusRegex.FindAllString(out, 1) == nil { + r.Errorf("Check status not yet passing") + } + }) // Use aws exec to curl between the apps. taskListOut := shell.RunCommandAndGetOutput(t, shell.Command{ @@ -1057,7 +1216,7 @@ func TestBasic(t *testing.T) { // Check that the Envoy entrypoint received the sigterm. retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { - envoyLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "sidecar-proxy") + envoyLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "consul-dataplane") require.NoError(r, err) logMsg := "consul-ecs: waiting for application container(s) to stop" @@ -1090,27 +1249,13 @@ func TestBasic(t *testing.T) { require.GreaterOrEqual(r, applicationOkLogs.Duration().Seconds(), 8.0) }) - // Validate that passing additional Consul agent configuration works. - // We enable DEBUG logs on one of the Consul agents. - retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { - agentLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "consul-client") - - require.NoError(r, err) - logMsg := "[DEBUG] agent:" - agentLogs = agentLogs.Filter(logMsg) - require.GreaterOrEqual(r, len(agentLogs), 1) - require.Contains(r, agentLogs[0].Message, logMsg) - }) - if c.secure { retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { - // Validate that health-sync attempts the 'consul logout' for each of the tokens - syncLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "consul-ecs-health-sync") + // Validate that the controller cleans up the token for the failed task + syncLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, controllerTaskID, "consul-ecs-controller") require.NoError(r, err) - syncLogs = syncLogs.Filter("[INFO] log out token:") - require.GreaterOrEqual(r, len(syncLogs), 2) - require.Contains(r, syncLogs[0].Message, "/consul/service-token") - require.Contains(r, syncLogs[1].Message, "/consul/client-token") + syncLogs = syncLogs.Filter("token deleted successfully") + require.GreaterOrEqual(r, len(syncLogs), 1) }) } @@ -1122,51 +1267,3 @@ func TestBasic(t *testing.T) { type listTasksResponse struct { TaskARNs []string `json:"taskArns"` } - -func TestValidation_AuditLogging(t *testing.T) { - t.Parallel() - - terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "./terraform/audit-logging-validate", - NoColor: true, - }) - _ = terraform.Init(t, terraformOptions) - - cases := map[string]struct { - auditLogging bool - acls bool - errMsg string - }{ - "with audit_logging and acls": { - auditLogging: true, - acls: true, - errMsg: "", - }, - "with audit_logging, without acls": { - auditLogging: true, - acls: false, - errMsg: "ERROR: ACLs must be enabled if audit logging is enabled", - }, - } - - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - t.Parallel() - terraformOptions.Vars = map[string]interface{}{ - "audit_logging": c.auditLogging, - "acls": c.acls, - } - t.Cleanup(func() { - _, _ = terraform.DestroyE(t, terraformOptions) - }) - _, err := terraform.PlanE(t, terraformOptions) - if c.errMsg == "" { - require.NoError(t, err) - } else { - require.Error(t, err) - require.Contains(t, err.Error(), c.errMsg) - } - }) - } -} diff --git a/test/acceptance/tests/basic/terraform/admin-partition-validate/main.tf b/test/acceptance/tests/basic/terraform/admin-partition-validate/main.tf index e05dc79b..c1d29b32 100644 --- a/test/acceptance/tests/basic/terraform/admin-partition-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/admin-partition-validate/main.tf @@ -22,7 +22,7 @@ module "test_client" { name = "basic" }] outbound_only = true - retry_join = ["test"] + consul_server_address = "consul.dc1.host" consul_partition = var.partition consul_namespace = var.namespace diff --git a/test/acceptance/tests/basic/terraform/audit-logging-validate/main.tf b/test/acceptance/tests/basic/terraform/audit-logging-validate/main.tf deleted file mode 100644 index ad6abe60..00000000 --- a/test/acceptance/tests/basic/terraform/audit-logging-validate/main.tf +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: MPL-2.0 - -provider "aws" { - region = "us-west-2" -} - -variable "audit_logging" { - type = bool - default = false -} - -variable "acls" { - type = bool - default = false -} - -module "test_client" { - source = "../../../../../../modules/mesh-task" - family = "family" - container_definitions = [{ - name = "basic" - }] - outbound_only = true - retry_join = ["test"] - - audit_logging = var.audit_logging - consul_http_addr = "test" - acls = var.acls -} diff --git a/test/acceptance/tests/basic/terraform/basic-install/main.tf b/test/acceptance/tests/basic/terraform/basic-install/main.tf index 761d6466..eafed83b 100644 --- a/test/acceptance/tests/basic/terraform/basic-install/main.tf +++ b/test/acceptance/tests/basic/terraform/basic-install/main.tf @@ -64,7 +64,7 @@ variable "launch_type" { variable "consul_ecs_image" { description = "Consul ECS image to use." type = string - default = "docker.mirror.hashicorp.services/hashicorpdev/consul-ecs:latest" + default = "ganeshrockz/ecs" } variable "server_service_name" { @@ -85,24 +85,6 @@ locals { enterprise_enabled = var.consul_license != "" } -// Generate a gossip encryption key if a secure installation. -resource "random_id" "gossip_encryption_key" { - count = var.secure ? 1 : 0 - byte_length = 32 -} - -resource "aws_secretsmanager_secret" "gossip_key" { - count = var.secure ? 1 : 0 - // Only 'consul_server*' secrets are allowed by the IAM role used by Circle CI - name = "consul_server_${var.suffix}-gossip-encryption-key" -} - -resource "aws_secretsmanager_secret_version" "gossip_key" { - count = var.secure ? 1 : 0 - secret_id = aws_secretsmanager_secret.gossip_key[0].id - secret_string = random_id.gossip_encryption_key[0].b64_std -} - module "consul_server" { source = "../../../../../../modules/dev-server" lb_enabled = false @@ -122,11 +104,8 @@ module "consul_server" { tags = var.tags - tls = var.secure - gossip_encryption_enabled = var.secure - generate_gossip_encryption_key = false - gossip_key_secret_arn = var.secure ? aws_secretsmanager_secret.gossip_key[0].arn : "" - acls = var.secure + tls = var.secure + acls = var.secure service_discovery_namespace = var.consul_datacenter datacenter = var.consul_datacenter @@ -153,27 +132,29 @@ resource "aws_security_group_rule" "consul_server_ingress" { security_group_id = module.consul_server.security_group_id } -module "acl_controller" { +module "ecs_controller" { count = var.secure ? 1 : 0 - source = "../../../../../../modules/acl-controller" + source = "../../../../../../modules/controller" log_configuration = { logDriver = "awslogs" options = { awslogs-group = var.log_group_name awslogs-region = var.region - awslogs-stream-prefix = "consul-acl-controller" + awslogs-stream-prefix = "consul-ecs-controller" } } launch_type = var.launch_type consul_bootstrap_token_secret_arn = module.consul_server.bootstrap_token_secret_arn - consul_server_http_addr = "https://${module.consul_server.server_dns}:8501" - consul_server_ca_cert_arn = module.consul_server.ca_cert_arn + consul_server_address = module.consul_server.server_dns + consul_grpc_ca_cert_arn = module.consul_server.ca_cert_arn + consul_https_ca_cert_arn = module.consul_server.ca_cert_arn ecs_cluster_arn = var.ecs_cluster_arn region = var.region subnets = var.subnets name_prefix = var.suffix consul_ecs_image = var.consul_ecs_image consul_partitions_enabled = local.enterprise_enabled + tls = var.secure } resource "aws_ecs_service" "test_client" { @@ -242,7 +223,7 @@ EOT ] } ] - retry_join = [module.consul_server.server_dns] + consul_server_address = module.consul_server.server_dns upstreams = [ { destinationName = "${var.server_service_name}_${var.suffix}" @@ -256,25 +237,17 @@ EOT // Test with a port other than the default of 20000. envoy_public_listener_port = 21000 - tls = var.secure - consul_server_ca_cert_arn = var.secure ? module.consul_server.ca_cert_arn : "" - gossip_key_secret_arn = var.secure ? aws_secretsmanager_secret.gossip_key[0].arn : "" - acls = var.secure - consul_ecs_image = var.consul_ecs_image - consul_image = var.consul_image + tls = var.secure + consul_grpc_ca_cert_arn = var.secure ? module.consul_server.ca_cert_arn : "" + acls = var.secure + consul_ecs_image = var.consul_ecs_image + consul_image = var.consul_image additional_task_role_policies = [aws_iam_policy.execute-command.arn] - consul_http_addr = var.secure ? "https://${module.consul_server.server_dns}:8501" : "" - # For dev-server, the server_ca_cert (internal rpc) and the https ca cert are the same. + # For dev-server, the grpc_ca_cert (internal rpc) and the https ca cert are the same. # But, they are different in HCP. consul_https_ca_cert_arn = var.secure ? module.consul_server.ca_cert_arn : "" - - consul_agent_configuration = <<-EOT - log_level = "debug" - EOT - - consul_datacenter = var.consul_datacenter } resource "aws_ecs_service" "test_server" { @@ -302,28 +275,16 @@ module "test_server" { essential = true logConfiguration = local.test_server_log_configuration }] - retry_join = [module.consul_server.server_dns] - log_configuration = local.test_server_log_configuration - checks = [ - { - checkId = "server-http" - name = "HTTP health check on port 9090" - http = "http://localhost:9090/health" - method = "GET" - timeout = "10s" - interval = "2s" - } - ] - port = 9090 + consul_server_address = module.consul_server.server_dns + log_configuration = local.test_server_log_configuration + port = 9090 - tls = var.secure - consul_server_ca_cert_arn = var.secure ? module.consul_server.ca_cert_arn : "" - gossip_key_secret_arn = var.secure ? aws_secretsmanager_secret.gossip_key[0].arn : "" - acls = var.secure - consul_ecs_image = var.consul_ecs_image - consul_image = var.consul_image + tls = var.secure + consul_grpc_ca_cert_arn = var.secure ? module.consul_server.ca_cert_arn : "" + acls = var.secure + consul_ecs_image = var.consul_ecs_image + consul_image = var.consul_image - consul_http_addr = var.secure ? "https://${module.consul_server.server_dns}:8501" : "" consul_https_ca_cert_arn = var.secure ? module.consul_server.ca_cert_arn : "" // Test passing in roles. This requires users to correctly configure the roles outside mesh-task. @@ -331,8 +292,6 @@ module "test_server" { create_execution_role = false task_role = aws_iam_role.task execution_role = aws_iam_role.execution - - consul_datacenter = var.consul_datacenter } // Configure a task role for passing in to mesh-task. @@ -455,7 +414,6 @@ resource "aws_iam_role" "execution" { ], "Resource": [ "${module.consul_server.ca_cert_arn}", - "${aws_secretsmanager_secret.gossip_key[0].arn}" ] }, %{endif~} @@ -491,4 +449,4 @@ locals { awslogs-stream-prefix = "test_client_${var.suffix}" } } -} +} \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/checks-validate/test-invalid-checks.json b/test/acceptance/tests/basic/terraform/checks-validate/test-invalid-checks.json deleted file mode 100644 index 74c8f940..00000000 --- a/test/acceptance/tests/basic/terraform/checks-validate/test-invalid-checks.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "invalid-key": "", - "checkId": "frontend-http", - "name": "frontend-http", - "http": "http://localhost:8080/health", - "method": "POST", - "body": "{\"method\": \"health\"}", - "notes": "Health check for the frontend service", - "header": { - "Content-Type": [ - "application/json" - ] - }, - "interval": "30s", - "timeout": "10s", - "successBeforePassing": 3, - "failuresBeforeCritical": 4 - } -] diff --git a/test/acceptance/tests/basic/terraform/checks-validate/test-no-checks.json b/test/acceptance/tests/basic/terraform/checks-validate/test-no-checks.json deleted file mode 100644 index fe51488c..00000000 --- a/test/acceptance/tests/basic/terraform/checks-validate/test-no-checks.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/test/acceptance/tests/basic/terraform/checks-validate/test-valid-checks.json b/test/acceptance/tests/basic/terraform/checks-validate/test-valid-checks.json deleted file mode 100644 index db28d153..00000000 --- a/test/acceptance/tests/basic/terraform/checks-validate/test-valid-checks.json +++ /dev/null @@ -1,46 +0,0 @@ -[ - { - "checkId": "frontend-http", - "name": "frontend-http", - "http": "http://localhost:8080/health", - "method": "POST", - "body": "{\"method\": \"health\"}", - "notes": "Health check for the frontend service", - "header": { - "Content-Type": [ - "application/json" - ] - }, - "interval": "30s", - "timeout": "10s", - "successBeforePassing": 3, - "failuresBeforeCritical": 4 - }, - { - "checkId": "frontend-tcp", - "name": "frontend-tcp", - "tcp": "localhost:8080", - "interval": "15s", - "timeout": "5s" - }, - { - "checkId": "frontend-grpc", - "name": "frontend-grpc", - "grpc": "localhost:8080", - "grpcUseTls": true, - "interval": "20s", - "timeout": "5s" - }, - { - "checkId": "frontend-ttl", - "name": "frontend-ttl", - "ttl": "10m", - "status": "passing" - }, - { - "checkId": "frontend-backend-alias", - "name": "frontend-backend-alias", - "aliasNode": "backend-node", - "aliasService": "backend" - } -] diff --git a/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/main.tf b/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/main.tf index 7afef6e4..5c7ac35e 100644 --- a/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - retry_join = ["test"] + consul_server_address = "consul.dc1.host" outbound_only = true consul_ecs_config = jsondecode(file("${path.module}/${var.consul_ecs_config_file}")) } diff --git a/test/acceptance/tests/basic/terraform/ca-cert-validate/main.tf b/test/acceptance/tests/basic/terraform/envoy-readiness-port-validate/main.tf similarity index 59% rename from test/acceptance/tests/basic/terraform/ca-cert-validate/main.tf rename to test/acceptance/tests/basic/terraform/envoy-readiness-port-validate/main.tf index 7555d347..d3a1a037 100644 --- a/test/acceptance/tests/basic/terraform/ca-cert-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/envoy-readiness-port-validate/main.tf @@ -1,17 +1,20 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: MPL-2.0 - provider "aws" { region = "us-west-2" } +variable "envoy_readiness_port" { + type = number +} + module "test_client" { source = "../../../../../../modules/mesh-task" family = "family" container_definitions = [{ name = "basic" }] - outbound_only = true - retry_join = ["test"] - tls = true -} + consul_server_address = "consul.dc1" + outbound_only = true + envoy_readiness_port = var.envoy_readiness_port +} \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/checks-validate/main.tf b/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/main.tf similarity index 59% rename from test/acceptance/tests/basic/terraform/checks-validate/main.tf rename to test/acceptance/tests/basic/terraform/grpc-tls-config-validate/main.tf index 6e885479..0b38993d 100644 --- a/test/acceptance/tests/basic/terraform/checks-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/main.tf @@ -5,7 +5,7 @@ provider "aws" { region = "us-west-2" } -variable "checks_file" { +variable "grpc_tls_config_file" { type = string } @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - retry_join = ["test"] - outbound_only = true - checks = jsondecode(file("${path.module}/${var.checks_file}")) -} + consul_server_address = "consul.dc1.host" + outbound_only = true + grpc_tls_config = jsondecode(file("${path.module}/${var.grpc_tls_config_file}")) +} \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-complete-config.json b/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-complete-config.json new file mode 100644 index 00000000..7dcca338 --- /dev/null +++ b/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-complete-config.json @@ -0,0 +1,6 @@ +{ + "port": 8503, + "tls": true, + "tlsServerName": "consul.dc1.name", + "caCertFile": "cert.pem" +} \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-empty-config.json b/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-empty-config.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-empty-config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-invalid-config.json b/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-invalid-config.json new file mode 100644 index 00000000..46c340cb --- /dev/null +++ b/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-invalid-config.json @@ -0,0 +1,5 @@ +{ + "invalid-field": "invalid-value", + "tls": true, + "tlsServerName": "consul.dc1.name" +} \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-partial-config.json b/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-partial-config.json new file mode 100644 index 00000000..428dc1c8 --- /dev/null +++ b/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-partial-config.json @@ -0,0 +1,4 @@ +{ + "tls": true, + "tlsServerName": "consul.dc1.name" +} \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/http-tls-config-validate/main.tf b/test/acceptance/tests/basic/terraform/http-tls-config-validate/main.tf new file mode 100644 index 00000000..6b95b576 --- /dev/null +++ b/test/acceptance/tests/basic/terraform/http-tls-config-validate/main.tf @@ -0,0 +1,21 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +provider "aws" { + region = "us-west-2" +} + +variable "http_tls_config_file" { + type = string +} + +module "test_client" { + source = "../../../../../../modules/mesh-task" + family = "family" + container_definitions = [{ + name = "basic" + }] + consul_server_address = "consul.dc1.host" + outbound_only = true + http_tls_config = jsondecode(file("${path.module}/${var.http_tls_config_file}")) +} \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-complete-config.json b/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-complete-config.json new file mode 100644 index 00000000..3e8047f6 --- /dev/null +++ b/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-complete-config.json @@ -0,0 +1,7 @@ +{ + "port": 8501, + "https": true, + "tls": true, + "tlsServerName": "consul.dc1.name", + "caCertFile": "cert.pem" +} \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-empty-config.json b/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-empty-config.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-empty-config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-invalid-config.json b/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-invalid-config.json new file mode 100644 index 00000000..46c340cb --- /dev/null +++ b/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-invalid-config.json @@ -0,0 +1,5 @@ +{ + "invalid-field": "invalid-value", + "tls": true, + "tlsServerName": "consul.dc1.name" +} \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-partial-config.json b/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-partial-config.json new file mode 100644 index 00000000..428dc1c8 --- /dev/null +++ b/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-partial-config.json @@ -0,0 +1,4 @@ +{ + "tls": true, + "tlsServerName": "consul.dc1.name" +} \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/pass-app-entrypoint/main.tf b/test/acceptance/tests/basic/terraform/pass-app-entrypoint/main.tf index 256d6882..b31a4ec7 100644 --- a/test/acceptance/tests/basic/terraform/pass-app-entrypoint/main.tf +++ b/test/acceptance/tests/basic/terraform/pass-app-entrypoint/main.tf @@ -18,7 +18,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - retry_join = ["test"] + consul_server_address = "consul.dc1" outbound_only = true application_shutdown_delay_seconds = var.application_shutdown_delay_seconds diff --git a/test/acceptance/tests/basic/terraform/pass-existing-iam-roles/main.tf b/test/acceptance/tests/basic/terraform/pass-existing-iam-roles/main.tf index eab13448..a63ad53b 100644 --- a/test/acceptance/tests/basic/terraform/pass-existing-iam-roles/main.tf +++ b/test/acceptance/tests/basic/terraform/pass-existing-iam-roles/main.tf @@ -61,7 +61,7 @@ module "test_client_pass_existing_roles" { family = local.pass_roles_family log_configuration = null container_definitions = local.container_definitions - retry_join = ["test"] + consul_server_address = "consul.dc1" outbound_only = true create_task_role = false diff --git a/test/acceptance/tests/basic/terraform/pass-role-additional-policies-validate/main.tf b/test/acceptance/tests/basic/terraform/pass-role-additional-policies-validate/main.tf index 5784b1ce..7914a319 100644 --- a/test/acceptance/tests/basic/terraform/pass-role-additional-policies-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/pass-role-additional-policies-validate/main.tf @@ -28,7 +28,7 @@ module "task_role_test" { family = "family-1" log_configuration = null container_definitions = local.container_definitions - retry_join = ["test"] + consul_server_address = "consul.dc1" outbound_only = true create_task_role = false @@ -46,7 +46,7 @@ module "execution_role_test" { family = "family-2" log_configuration = null container_definitions = local.container_definitions - retry_join = ["test"] + consul_server_address = "consul.dc1" outbound_only = true create_execution_role = false diff --git a/test/acceptance/tests/basic/terraform/public-listener-port-validate/main.tf b/test/acceptance/tests/basic/terraform/public-listener-port-validate/main.tf index 9276c6a3..6783fd60 100644 --- a/test/acceptance/tests/basic/terraform/public-listener-port-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/public-listener-port-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - retry_join = ["test"] + consul_server_address = "consul.dc1" outbound_only = true envoy_public_listener_port = var.envoy_public_listener_port } diff --git a/test/acceptance/tests/basic/terraform/role-path-validate/main.tf b/test/acceptance/tests/basic/terraform/role-path-validate/main.tf index 0678a570..5ac7505a 100644 --- a/test/acceptance/tests/basic/terraform/role-path-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/role-path-validate/main.tf @@ -16,7 +16,7 @@ module "test_client" { name = "basic" }] outbound_only = true - retry_join = ["test"] + consul_server_address = "consul.dc1" iam_role_path = var.iam_role_path } diff --git a/test/acceptance/tests/basic/terraform/service-name-validate/main.tf b/test/acceptance/tests/basic/terraform/service-name-validate/main.tf index 5cf6198a..c19ddbe1 100644 --- a/test/acceptance/tests/basic/terraform/service-name-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/service-name-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - retry_join = ["test"] + consul_server_address = "consul.dc1" outbound_only = true consul_service_name = var.consul_service_name } diff --git a/test/acceptance/tests/basic/terraform/upstreams-validate/main.tf b/test/acceptance/tests/basic/terraform/upstreams-validate/main.tf index 62cb3e0e..52bd185f 100644 --- a/test/acceptance/tests/basic/terraform/upstreams-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/upstreams-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - retry_join = ["test"] + consul_server_address = "consul.dc1" outbound_only = true upstreams = jsondecode(file("${path.module}/${var.upstreams_file}")) } diff --git a/test/acceptance/tests/basic/terraform/volume-variable/main.tf b/test/acceptance/tests/basic/terraform/volume-variable/main.tf index c9c7e1fe..0d0b6110 100644 --- a/test/acceptance/tests/basic/terraform/volume-variable/main.tf +++ b/test/acceptance/tests/basic/terraform/volume-variable/main.tf @@ -16,6 +16,6 @@ module "test_client" { container_definitions = [{ name = "basic" }] - retry_join = ["test"] + consul_server_address = "consul.dc1" outbound_only = true } From 0577f54d43038eba7eb84579484b4cecf72394c8 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 17:34:22 +0530 Subject: [PATCH 02/27] Fmt terraform --- .../tests/basic/terraform/admin-partition-validate/main.tf | 2 +- .../tests/basic/terraform/consul-ecs-config-validate/main.tf | 4 ++-- .../basic/terraform/envoy-readiness-port-validate/main.tf | 4 ++-- .../tests/basic/terraform/grpc-tls-config-validate/main.tf | 4 ++-- .../tests/basic/terraform/http-tls-config-validate/main.tf | 4 ++-- .../tests/basic/terraform/pass-app-entrypoint/main.tf | 2 +- .../basic/terraform/public-listener-port-validate/main.tf | 2 +- .../tests/basic/terraform/role-path-validate/main.tf | 2 +- .../tests/basic/terraform/service-name-validate/main.tf | 4 ++-- .../tests/basic/terraform/upstreams-validate/main.tf | 4 ++-- test/acceptance/tests/basic/terraform/volume-variable/main.tf | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/test/acceptance/tests/basic/terraform/admin-partition-validate/main.tf b/test/acceptance/tests/basic/terraform/admin-partition-validate/main.tf index c1d29b32..52de922f 100644 --- a/test/acceptance/tests/basic/terraform/admin-partition-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/admin-partition-validate/main.tf @@ -21,7 +21,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - outbound_only = true + outbound_only = true consul_server_address = "consul.dc1.host" consul_partition = var.partition diff --git a/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/main.tf b/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/main.tf index 5c7ac35e..3bfe3b36 100644 --- a/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/main.tf @@ -16,6 +16,6 @@ module "test_client" { name = "basic" }] consul_server_address = "consul.dc1.host" - outbound_only = true - consul_ecs_config = jsondecode(file("${path.module}/${var.consul_ecs_config_file}")) + outbound_only = true + consul_ecs_config = jsondecode(file("${path.module}/${var.consul_ecs_config_file}")) } diff --git a/test/acceptance/tests/basic/terraform/envoy-readiness-port-validate/main.tf b/test/acceptance/tests/basic/terraform/envoy-readiness-port-validate/main.tf index d3a1a037..f1c3d589 100644 --- a/test/acceptance/tests/basic/terraform/envoy-readiness-port-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/envoy-readiness-port-validate/main.tf @@ -15,6 +15,6 @@ module "test_client" { name = "basic" }] consul_server_address = "consul.dc1" - outbound_only = true - envoy_readiness_port = var.envoy_readiness_port + outbound_only = true + envoy_readiness_port = var.envoy_readiness_port } \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/main.tf b/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/main.tf index 0b38993d..82542a3e 100644 --- a/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/main.tf @@ -16,6 +16,6 @@ module "test_client" { name = "basic" }] consul_server_address = "consul.dc1.host" - outbound_only = true - grpc_tls_config = jsondecode(file("${path.module}/${var.grpc_tls_config_file}")) + outbound_only = true + grpc_tls_config = jsondecode(file("${path.module}/${var.grpc_tls_config_file}")) } \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/http-tls-config-validate/main.tf b/test/acceptance/tests/basic/terraform/http-tls-config-validate/main.tf index 6b95b576..0ed7db5a 100644 --- a/test/acceptance/tests/basic/terraform/http-tls-config-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/http-tls-config-validate/main.tf @@ -16,6 +16,6 @@ module "test_client" { name = "basic" }] consul_server_address = "consul.dc1.host" - outbound_only = true - http_tls_config = jsondecode(file("${path.module}/${var.http_tls_config_file}")) + outbound_only = true + http_tls_config = jsondecode(file("${path.module}/${var.http_tls_config_file}")) } \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/pass-app-entrypoint/main.tf b/test/acceptance/tests/basic/terraform/pass-app-entrypoint/main.tf index b31a4ec7..d62647a5 100644 --- a/test/acceptance/tests/basic/terraform/pass-app-entrypoint/main.tf +++ b/test/acceptance/tests/basic/terraform/pass-app-entrypoint/main.tf @@ -19,7 +19,7 @@ module "test_client" { name = "basic" }] consul_server_address = "consul.dc1" - outbound_only = true + outbound_only = true application_shutdown_delay_seconds = var.application_shutdown_delay_seconds } diff --git a/test/acceptance/tests/basic/terraform/public-listener-port-validate/main.tf b/test/acceptance/tests/basic/terraform/public-listener-port-validate/main.tf index 6783fd60..478deda7 100644 --- a/test/acceptance/tests/basic/terraform/public-listener-port-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/public-listener-port-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - consul_server_address = "consul.dc1" + consul_server_address = "consul.dc1" outbound_only = true envoy_public_listener_port = var.envoy_public_listener_port } diff --git a/test/acceptance/tests/basic/terraform/role-path-validate/main.tf b/test/acceptance/tests/basic/terraform/role-path-validate/main.tf index 5ac7505a..4a9d45cb 100644 --- a/test/acceptance/tests/basic/terraform/role-path-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/role-path-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - outbound_only = true + outbound_only = true consul_server_address = "consul.dc1" iam_role_path = var.iam_role_path diff --git a/test/acceptance/tests/basic/terraform/service-name-validate/main.tf b/test/acceptance/tests/basic/terraform/service-name-validate/main.tf index c19ddbe1..4e064581 100644 --- a/test/acceptance/tests/basic/terraform/service-name-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/service-name-validate/main.tf @@ -16,6 +16,6 @@ module "test_client" { name = "basic" }] consul_server_address = "consul.dc1" - outbound_only = true - consul_service_name = var.consul_service_name + outbound_only = true + consul_service_name = var.consul_service_name } diff --git a/test/acceptance/tests/basic/terraform/upstreams-validate/main.tf b/test/acceptance/tests/basic/terraform/upstreams-validate/main.tf index 52bd185f..969a274a 100644 --- a/test/acceptance/tests/basic/terraform/upstreams-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/upstreams-validate/main.tf @@ -16,6 +16,6 @@ module "test_client" { name = "basic" }] consul_server_address = "consul.dc1" - outbound_only = true - upstreams = jsondecode(file("${path.module}/${var.upstreams_file}")) + outbound_only = true + upstreams = jsondecode(file("${path.module}/${var.upstreams_file}")) } diff --git a/test/acceptance/tests/basic/terraform/volume-variable/main.tf b/test/acceptance/tests/basic/terraform/volume-variable/main.tf index 0d0b6110..0d1efd0c 100644 --- a/test/acceptance/tests/basic/terraform/volume-variable/main.tf +++ b/test/acceptance/tests/basic/terraform/volume-variable/main.tf @@ -17,5 +17,5 @@ module "test_client" { name = "basic" }] consul_server_address = "consul.dc1" - outbound_only = true + outbound_only = true } From ff8cca6b3358f567c6bf84df62814048a6c5b0f9 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 17:39:02 +0530 Subject: [PATCH 03/27] Fix go lint --- test/acceptance/tests/basic/basic_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index a3b4e261..34643ccd 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -1053,8 +1053,8 @@ func TestBasic(t *testing.T) { }) var tasks listTasksResponse - require.NoError(t, json.Unmarshal([]byte(taskListOut), &tasks)) - require.Len(t, tasks.TaskARNs, 1) + require.NoError(r, json.Unmarshal([]byte(taskListOut), &tasks)) + require.Len(r, tasks.TaskARNs, 1) controllerTaskARN := tasks.TaskARNs[0] arnParts := strings.Split(controllerTaskARN, "/") controllerTaskID = arnParts[len(arnParts)-1] From ffb26c8d2f4f931f7d52dec39f1cd84c44d6a7c4 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 20:44:24 +0530 Subject: [PATCH 04/27] Test basic test cases --- test/acceptance/tests/basic/basic_test.go | 1339 ++++++++--------- .../terraform/pass-existing-iam-roles/main.tf | 2 +- 2 files changed, 669 insertions(+), 672 deletions(-) diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index 34643ccd..31a472d4 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -4,7 +4,6 @@ package basic import ( - "context" "encoding/json" "fmt" "os" @@ -13,9 +12,7 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ecs" - "github.com/aws/aws-sdk-go-v2/service/iam" "github.com/gruntwork-io/terratest/modules/random" "github.com/gruntwork-io/terratest/modules/shell" "github.com/gruntwork-io/terratest/modules/terraform" @@ -27,750 +24,750 @@ import ( // TestVolumeVariable tests passing a list of volumes to mesh-task. // This validates a big nested dynamic block in mesh-task. -func TestVolumeVariable(t *testing.T) { - t.Parallel() - volumes := []map[string]interface{}{ - { - "name": "my-vol1", - }, - { - "name": "my-vol2", - "host_path": "/tmp/fake/path", - }, - { - "name": "no-optional-fields", - "docker_volume_configuration": map[string]interface{}{}, - "efs_volume_configuration": map[string]interface{}{ - "file_system_id": "fakeid123", - }, - }, - { - "name": "all-the-fields", - "docker_volume_configuration": map[string]interface{}{ - "scope": "shared", - "autoprovision": true, - "driver": "local", - "driver_opts": map[string]interface{}{ - "type": "nfs", - "device": "host.example.com:/", - "o": "addr=host.example.com,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport", - }, - }, - "fsx_windows_file_server_volume_configuration": map[string]interface{}{ - "file_system_id": "fakeid456", - "root_directory": `\\data`, - "authorization_config": map[string]interface{}{ - "credentials_parameter": "arn:aws:secretsmanager:us-east-1:000000000000:secret:fake-fake-fake-fake", - "domain": "domain-name", - }, - }, - }, - } +// func TestVolumeVariable(t *testing.T) { +// t.Parallel() +// volumes := []map[string]interface{}{ +// { +// "name": "my-vol1", +// }, +// { +// "name": "my-vol2", +// "host_path": "/tmp/fake/path", +// }, +// { +// "name": "no-optional-fields", +// "docker_volume_configuration": map[string]interface{}{}, +// "efs_volume_configuration": map[string]interface{}{ +// "file_system_id": "fakeid123", +// }, +// }, +// { +// "name": "all-the-fields", +// "docker_volume_configuration": map[string]interface{}{ +// "scope": "shared", +// "autoprovision": true, +// "driver": "local", +// "driver_opts": map[string]interface{}{ +// "type": "nfs", +// "device": "host.example.com:/", +// "o": "addr=host.example.com,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport", +// }, +// }, +// "fsx_windows_file_server_volume_configuration": map[string]interface{}{ +// "file_system_id": "fakeid456", +// "root_directory": `\\data`, +// "authorization_config": map[string]interface{}{ +// "credentials_parameter": "arn:aws:secretsmanager:us-east-1:000000000000:secret:fake-fake-fake-fake", +// "domain": "domain-name", +// }, +// }, +// }, +// } - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/volume-variable", - Vars: map[string]interface{}{"volumes": volumes}, - NoColor: true, - } - t.Cleanup(func() { - _, _ = terraform.DestroyE(t, terraformOptions) - }) - terraform.InitAndPlan(t, terraformOptions) -} +// terraformOptions := &terraform.Options{ +// TerraformDir: "./terraform/volume-variable", +// Vars: map[string]interface{}{"volumes": volumes}, +// NoColor: true, +// } +// t.Cleanup(func() { +// _, _ = terraform.DestroyE(t, terraformOptions) +// }) +// terraform.InitAndPlan(t, terraformOptions) +// } -// TestPassingExistingRoles will create the task definitions to validate -// creation and passing of IAM roles by mesh-task. It creates two task definitions -// with mesh-task: -// - one which has mesh-task create the roles -// - one which passes in existing roles -// -// This test does not start any services. -// -// Note: We don't have a validation for create_task_role=true XOR task_role=. -// -// If the role is created as part of the terraform plan/apply and passed in to mesh-task, -// then the role is an unknown value during the plan, since it is not yet created, and you -// can't reliably test its value for validations. -func TestPassingExistingRoles(t *testing.T) { - t.Parallel() +// // TestPassingExistingRoles will create the task definitions to validate +// // creation and passing of IAM roles by mesh-task. It creates two task definitions +// // with mesh-task: +// // - one which has mesh-task create the roles +// // - one which passes in existing roles +// // +// // This test does not start any services. +// // +// // Note: We don't have a validation for create_task_role=true XOR task_role=. +// // +// // If the role is created as part of the terraform plan/apply and passed in to mesh-task, +// // then the role is an unknown value during the plan, since it is not yet created, and you +// // can't reliably test its value for validations. +// func TestPassingExistingRoles(t *testing.T) { +// t.Parallel() - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/pass-existing-iam-roles", - NoColor: true, - } - terraform.Init(t, terraformOptions) +// terraformOptions := &terraform.Options{ +// TerraformDir: "./terraform/pass-existing-iam-roles", +// NoColor: true, +// } +// terraform.Init(t, terraformOptions) - // Init AWS clients. - ctx := context.Background() - cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-west-2")) - require.NoError(t, err, "unable to initialize ECS client") - ecsClient := ecs.NewFromConfig(cfg) - iamClient := iam.NewFromConfig(cfg) +// // Init AWS clients. +// ctx := context.Background() +// cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-west-2")) +// require.NoError(t, err, "unable to initialize ECS client") +// ecsClient := ecs.NewFromConfig(cfg) +// iamClient := iam.NewFromConfig(cfg) - t.Cleanup(func() { - _, _ = terraform.DestroyE(t, terraformOptions) - }) - terraform.InitAndApply(t, terraformOptions) +// t.Cleanup(func() { +// _, _ = terraform.DestroyE(t, terraformOptions) +// }) +// terraform.InitAndApply(t, terraformOptions) - outputs := terraform.OutputAll(t, terraformOptions) - suffix := outputs["suffix"].(string) +// outputs := terraform.OutputAll(t, terraformOptions) +// suffix := outputs["suffix"].(string) - { - // Check that mesh-task creates roles by default. - taskDefArn := outputs["create_roles_task_definition_arn"].(string) - family := outputs["create_roles_family"].(string) +// { +// // Check that mesh-task creates roles by default. +// taskDefArn := outputs["create_roles_task_definition_arn"].(string) +// family := outputs["create_roles_family"].(string) - resp, err := ecsClient.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ - TaskDefinition: &taskDefArn, - }) - require.NoError(t, err) - - // Expected role names, as created by mesh-task - expTaskRoleName := family + "-task" - expExecRoleName := family + "-execution" - - // mesh-task should create roles and use them - taskDef := resp.TaskDefinition - require.NotNil(t, taskDef.TaskRoleArn) - require.NotNil(t, taskDef.ExecutionRoleArn) - require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs/`+expTaskRoleName, *taskDef.TaskRoleArn) - require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs/`+expExecRoleName, *taskDef.ExecutionRoleArn) - - // Check that the roles were really created. - for _, roleName := range []string{expTaskRoleName, expExecRoleName} { - resp, err := iamClient.GetRole(ctx, &iam.GetRoleInput{RoleName: &roleName}) - require.NoError(t, err) - require.Equal(t, *resp.Role.RoleName, roleName) - } - } +// resp, err := ecsClient.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ +// TaskDefinition: &taskDefArn, +// }) +// require.NoError(t, err) + +// // Expected role names, as created by mesh-task +// expTaskRoleName := family + "-task" +// expExecRoleName := family + "-execution" + +// // mesh-task should create roles and use them +// taskDef := resp.TaskDefinition +// require.NotNil(t, taskDef.TaskRoleArn) +// require.NotNil(t, taskDef.ExecutionRoleArn) +// require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs/`+expTaskRoleName, *taskDef.TaskRoleArn) +// require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs/`+expExecRoleName, *taskDef.ExecutionRoleArn) + +// // Check that the roles were really created. +// for _, roleName := range []string{expTaskRoleName, expExecRoleName} { +// resp, err := iamClient.GetRole(ctx, &iam.GetRoleInput{RoleName: &roleName}) +// require.NoError(t, err) +// require.Equal(t, *resp.Role.RoleName, roleName) +// } +// } - { - // Check that mesh-task uses the passed in roles and doesn't create roles when roles are passed in. - taskDefArn := outputs["pass_roles_task_definition_arn"].(string) - family := outputs["pass_roles_family"].(string) +// { +// // Check that mesh-task uses the passed in roles and doesn't create roles when roles are passed in. +// taskDefArn := outputs["pass_roles_task_definition_arn"].(string) +// family := outputs["pass_roles_family"].(string) - resp, err := ecsClient.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ - TaskDefinition: &taskDefArn, - }) - require.NoError(t, err) - - // mesh-task should use the passed in roles. - taskDef := resp.TaskDefinition - require.NotNil(t, taskDef.TaskRoleArn) - require.NotNil(t, taskDef.ExecutionRoleArn) - require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs-test-pass-task-role-`+suffix, *taskDef.TaskRoleArn) - require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs-test-pass-execution-role-`+suffix, *taskDef.ExecutionRoleArn) - - // mesh-task should not create roles when they are passed in. - expTaskRoleName := family + "-task" - expExecRoleName := family + "-execution" - for _, roleName := range []string{expTaskRoleName, expExecRoleName} { - _, err := iamClient.GetRole(ctx, &iam.GetRoleInput{RoleName: &roleName}) - require.Error(t, err) - require.Contains(t, err.Error(), "StatusCode: 404") - } - } +// resp, err := ecsClient.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ +// TaskDefinition: &taskDefArn, +// }) +// require.NoError(t, err) + +// // mesh-task should use the passed in roles. +// taskDef := resp.TaskDefinition +// require.NotNil(t, taskDef.TaskRoleArn) +// require.NotNil(t, taskDef.ExecutionRoleArn) +// require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs-test-pass-task-role-`+suffix, *taskDef.TaskRoleArn) +// require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs-test-pass-execution-role-`+suffix, *taskDef.ExecutionRoleArn) + +// // mesh-task should not create roles when they are passed in. +// expTaskRoleName := family + "-task" +// expExecRoleName := family + "-execution" +// for _, roleName := range []string{expTaskRoleName, expExecRoleName} { +// _, err := iamClient.GetRole(ctx, &iam.GetRoleInput{RoleName: &roleName}) +// require.Error(t, err) +// require.Contains(t, err.Error(), "StatusCode: 404") +// } +// } - t.Log("Test Successful!") -} +// t.Log("Test Successful!") +// } -func TestValidation_AdditionalPolicies(t *testing.T) { - t.Parallel() - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/pass-role-additional-policies-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) +// func TestValidation_AdditionalPolicies(t *testing.T) { +// t.Parallel() +// terraformOptions := &terraform.Options{ +// TerraformDir: "./terraform/pass-role-additional-policies-validate", +// NoColor: true, +// } +// terraform.Init(t, terraformOptions) - cases := map[string]struct { - execution bool - errMsg string - }{ - "task": { - execution: false, - errMsg: "ERROR: cannot set additional_task_role_policies when create_task_role=false", - }, - "execution": { - execution: true, - errMsg: "ERROR: cannot set additional_execution_role_policies when create_execution_role=false", - }, - } - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - t.Parallel() +// cases := map[string]struct { +// execution bool +// errMsg string +// }{ +// "task": { +// execution: false, +// errMsg: "ERROR: cannot set additional_task_role_policies when create_task_role=false", +// }, +// "execution": { +// execution: true, +// errMsg: "ERROR: cannot set additional_execution_role_policies when create_execution_role=false", +// }, +// } +// for name, c := range cases { +// c := c +// t.Run(name, func(t *testing.T) { +// t.Parallel() - _, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "test_execution_role": c.execution, - }, - }) - require.Error(t, err) - // error messages are wrapped, so a space may turn into a newline. - regex := strings.ReplaceAll(regexp.QuoteMeta(c.errMsg), " ", "\\s+") - require.Regexp(t, regex, err.Error()) - }) - } -} +// _, err := terraform.PlanE(t, &terraform.Options{ +// TerraformDir: terraformOptions.TerraformDir, +// NoColor: true, +// Vars: map[string]interface{}{ +// "test_execution_role": c.execution, +// }, +// }) +// require.Error(t, err) +// // error messages are wrapped, so a space may turn into a newline. +// regex := strings.ReplaceAll(regexp.QuoteMeta(c.errMsg), " ", "\\s+") +// require.Regexp(t, regex, err.Error()) +// }) +// } +// } -func TestPassingAppEntrypoint(t *testing.T) { - t.Parallel() +// func TestPassingAppEntrypoint(t *testing.T) { +// t.Parallel() - newint := func(x int) *int { return &x } - cases := map[string]struct { - value *int - expEntrypoint bool - }{ - "null": {nil, false}, - "negative": {newint(-1), false}, - "zero": {newint(0), false}, - "one": {newint(1), true}, - "five": {newint(5), true}, - } +// newint := func(x int) *int { return &x } +// cases := map[string]struct { +// value *int +// expEntrypoint bool +// }{ +// "null": {nil, false}, +// "negative": {newint(-1), false}, +// "zero": {newint(0), false}, +// "one": {newint(1), true}, +// "five": {newint(5), true}, +// } - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/pass-app-entrypoint", - NoColor: true, - } - t.Cleanup(func() { - _, _ = terraform.DestroyE(t, terraformOptions) - }) +// terraformOptions := &terraform.Options{ +// TerraformDir: "./terraform/pass-app-entrypoint", +// NoColor: true, +// } +// t.Cleanup(func() { +// _, _ = terraform.DestroyE(t, terraformOptions) +// }) - terraform.Init(t, terraformOptions) - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - t.Parallel() - opts := &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - //"application_shutdown_delay_seconds": nil, - }, - } - if c.value != nil { - opts.Vars["application_shutdown_delay_seconds"] = *c.value - } - out := terraform.Plan(t, opts) - - if c.expEntrypoint { - // Look for app-entrypoint in the Terraform diff. - regex := strings.Join([]string{ - `\+ entryPoint = \[`, - ` \+ "/consul/consul-ecs",`, - ` \+ "app-entrypoint",`, - ` \+ "-shutdown-delay",`, - ` \+ "\d+s",`, // e.g. "2s", "10s", etc - `\]`, - }, `\s+`) - require.Regexp(t, regex, out) - } else { - require.NotContains(t, out, "app-entrypoint") - } +// terraform.Init(t, terraformOptions) +// for name, c := range cases { +// c := c +// t.Run(name, func(t *testing.T) { +// t.Parallel() +// opts := &terraform.Options{ +// TerraformDir: terraformOptions.TerraformDir, +// NoColor: true, +// Vars: map[string]interface{}{ +// //"application_shutdown_delay_seconds": nil, +// }, +// } +// if c.value != nil { +// opts.Vars["application_shutdown_delay_seconds"] = *c.value +// } +// out := terraform.Plan(t, opts) + +// if c.expEntrypoint { +// // Look for app-entrypoint in the Terraform diff. +// regex := strings.Join([]string{ +// `\+ entryPoint = \[`, +// ` \+ "/consul/consul-ecs",`, +// ` \+ "app-entrypoint",`, +// ` \+ "-shutdown-delay",`, +// ` \+ "\d+s",`, // e.g. "2s", "10s", etc +// `\]`, +// }, `\s+`) +// require.Regexp(t, regex, out) +// } else { +// require.NotContains(t, out, "app-entrypoint") +// } - }) - } -} +// }) +// } +// } -func TestValidation_UpstreamsVariable(t *testing.T) { - t.Parallel() +// func TestValidation_UpstreamsVariable(t *testing.T) { +// t.Parallel() - cases := map[string]struct { - upstreamsFile string - errors []string - }{ - "no-upstreams": { - upstreamsFile: "test-no-upstreams.json", - }, - "valid-upstreams": { - upstreamsFile: "test-valid-upstreams.json", - }, - "invalid-upstreams": { - upstreamsFile: "test-invalid-upstreams.json", - errors: []string{ - "Upstream fields must be one of.*", - }, - }, - "requires-destination-name": { - upstreamsFile: "test-missing-destinationName.json", - errors: []string{ - "Upstream fields .* are required.", - }, - }, - "requires-local-bind-port": { - upstreamsFile: "test-missing-localBindPort.json", - errors: []string{ - "Upstream fields .* are required.", - }, - }, - } +// cases := map[string]struct { +// upstreamsFile string +// errors []string +// }{ +// "no-upstreams": { +// upstreamsFile: "test-no-upstreams.json", +// }, +// "valid-upstreams": { +// upstreamsFile: "test-valid-upstreams.json", +// }, +// "invalid-upstreams": { +// upstreamsFile: "test-invalid-upstreams.json", +// errors: []string{ +// "Upstream fields must be one of.*", +// }, +// }, +// "requires-destination-name": { +// upstreamsFile: "test-missing-destinationName.json", +// errors: []string{ +// "Upstream fields .* are required.", +// }, +// }, +// "requires-local-bind-port": { +// upstreamsFile: "test-missing-localBindPort.json", +// errors: []string{ +// "Upstream fields .* are required.", +// }, +// }, +// } - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/upstreams-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) +// terraformOptions := &terraform.Options{ +// TerraformDir: "./terraform/upstreams-validate", +// NoColor: true, +// } +// terraform.Init(t, terraformOptions) - for name, c := range cases { - t.Run(name, func(t *testing.T) { - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "upstreams_file": c.upstreamsFile, - }, - }) +// for name, c := range cases { +// t.Run(name, func(t *testing.T) { +// out, err := terraform.PlanE(t, &terraform.Options{ +// TerraformDir: terraformOptions.TerraformDir, +// NoColor: true, +// Vars: map[string]interface{}{ +// "upstreams_file": c.upstreamsFile, +// }, +// }) - if len(c.errors) == 0 { - require.NoError(t, err) - } else { - require.Error(t, err) - for _, regex := range c.errors { - require.Regexp(t, regex, out) - } - } - }) - } -} +// if len(c.errors) == 0 { +// require.NoError(t, err) +// } else { +// require.Error(t, err) +// for _, regex := range c.errors { +// require.Regexp(t, regex, out) +// } +// } +// }) +// } +// } -func TestValidation_EnvoyPublicListenerPort(t *testing.T) { - t.Parallel() +// func TestValidation_EnvoyPublicListenerPort(t *testing.T) { +// t.Parallel() - cases := map[string]struct { - port int - error string - }{ - "allowed-port": { - port: 21000, - }, - "too-high-port": { - port: 65536, - error: "The envoy_public_listener_port must be greater than 0 and less than or equal to 65535.", - }, - "disallowed-port": { - port: 19000, - error: "The envoy_public_listener_port must not conflict with the following ports that are reserved for Consul and Envoy", - }, - } +// cases := map[string]struct { +// port int +// error string +// }{ +// "allowed-port": { +// port: 21000, +// }, +// "too-high-port": { +// port: 65536, +// error: "The envoy_public_listener_port must be greater than 0 and less than or equal to 65535.", +// }, +// "disallowed-port": { +// port: 19000, +// error: "The envoy_public_listener_port must not conflict with the following ports that are reserved for Consul and Envoy", +// }, +// } - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/public-listener-port-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) +// terraformOptions := &terraform.Options{ +// TerraformDir: "./terraform/public-listener-port-validate", +// NoColor: true, +// } +// terraform.Init(t, terraformOptions) - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - t.Parallel() +// for name, c := range cases { +// c := c +// t.Run(name, func(t *testing.T) { +// t.Parallel() - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "envoy_public_listener_port": c.port, - }, - }) +// out, err := terraform.PlanE(t, &terraform.Options{ +// TerraformDir: terraformOptions.TerraformDir, +// NoColor: true, +// Vars: map[string]interface{}{ +// "envoy_public_listener_port": c.port, +// }, +// }) - if c.error == "" { - require.NoError(t, err) - } else { - // handle multiline error messages. - regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") - require.Error(t, err) - require.Regexp(t, regex, out) - } - }) - } -} +// if c.error == "" { +// require.NoError(t, err) +// } else { +// // handle multiline error messages. +// regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") +// require.Error(t, err) +// require.Regexp(t, regex, out) +// } +// }) +// } +// } -func TestValidation_EnvoyReadinessPort(t *testing.T) { - t.Parallel() +// func TestValidation_EnvoyReadinessPort(t *testing.T) { +// t.Parallel() - cases := map[string]struct { - port int - error string - }{ - "allowed-port": { - port: 23000, - }, - "too-high-port": { - port: 65536, - error: "The envoy_readiness_port must be greater than 0 and less than or equal to 65535.", - }, - "disallowed-port": { - port: 19000, - error: "The envoy_readiness_port must not conflict with the following ports that are reserved for Consul and Envoy", - }, - "conflicts-with-listener-port": { - port: 20000, - error: "envoy_public_listener_port should not conflict with envoy_readiness_port", - }, - } +// cases := map[string]struct { +// port int +// error string +// }{ +// "allowed-port": { +// port: 23000, +// }, +// "too-high-port": { +// port: 65536, +// error: "The envoy_readiness_port must be greater than 0 and less than or equal to 65535.", +// }, +// "disallowed-port": { +// port: 19000, +// error: "The envoy_readiness_port must not conflict with the following ports that are reserved for Consul and Envoy", +// }, +// "conflicts-with-listener-port": { +// port: 20000, +// error: "envoy_public_listener_port should not conflict with envoy_readiness_port", +// }, +// } - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/envoy-readiness-port-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) +// terraformOptions := &terraform.Options{ +// TerraformDir: "./terraform/envoy-readiness-port-validate", +// NoColor: true, +// } +// terraform.Init(t, terraformOptions) - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - t.Parallel() +// for name, c := range cases { +// c := c +// t.Run(name, func(t *testing.T) { +// t.Parallel() - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "envoy_readiness_port": c.port, - }, - }) +// out, err := terraform.PlanE(t, &terraform.Options{ +// TerraformDir: terraformOptions.TerraformDir, +// NoColor: true, +// Vars: map[string]interface{}{ +// "envoy_readiness_port": c.port, +// }, +// }) - if c.error == "" { - require.NoError(t, err) - } else { - // handle multiline error messages. - regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") - require.Error(t, err) - require.Regexp(t, regex, out) - } - }) - } -} +// if c.error == "" { +// require.NoError(t, err) +// } else { +// // handle multiline error messages. +// regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") +// require.Error(t, err) +// require.Regexp(t, regex, out) +// } +// }) +// } +// } -func TestValidation_ConsulServiceName(t *testing.T) { - t.Parallel() +// func TestValidation_ConsulServiceName(t *testing.T) { +// t.Parallel() - cases := map[string]struct { - serviceName string - error bool - }{ - "empty": {}, - "lowercase": { - serviceName: "lower-case-name", - }, - "uppercase": { - serviceName: "UPPER-CASE-NAME", - error: true, - }, - } +// cases := map[string]struct { +// serviceName string +// error bool +// }{ +// "empty": {}, +// "lowercase": { +// serviceName: "lower-case-name", +// }, +// "uppercase": { +// serviceName: "UPPER-CASE-NAME", +// error: true, +// }, +// } - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/service-name-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) +// terraformOptions := &terraform.Options{ +// TerraformDir: "./terraform/service-name-validate", +// NoColor: true, +// } +// terraform.Init(t, terraformOptions) - for name, c := range cases { - c := c +// for name, c := range cases { +// c := c - t.Run(name, func(t *testing.T) { - t.Parallel() +// t.Run(name, func(t *testing.T) { +// t.Parallel() - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "consul_service_name": c.serviceName, - }, - }) +// out, err := terraform.PlanE(t, &terraform.Options{ +// TerraformDir: terraformOptions.TerraformDir, +// NoColor: true, +// Vars: map[string]interface{}{ +// "consul_service_name": c.serviceName, +// }, +// }) - if c.error { - require.Error(t, err) - require.Regexp(t, "The consul_service_name must be lower case.", out) - } else { - require.NoError(t, err) - } - }) - } +// if c.error { +// require.Error(t, err) +// require.Regexp(t, "The consul_service_name must be lower case.", out) +// } else { +// require.NoError(t, err) +// } +// }) +// } -} +// } -func TestValidation_ConsulEcsConfigVariable(t *testing.T) { - t.Parallel() +// func TestValidation_ConsulEcsConfigVariable(t *testing.T) { +// t.Parallel() - cases := map[string]struct { - configFile string - errors []string - }{ - "empty-map": { - configFile: "test-empty-config.json", - }, - "complete-config": { - configFile: "test-complete-config.json", - }, - "partial-config": { - configFile: "test-partial-config.json", - }, - "invalid-config": { - configFile: "test-invalid-config.json", - errors: []string{ - "Only the 'service', 'proxy', and 'consulLogin' fields are allowed in consul_ecs_config.", - "Only the 'enableTagOverride' and 'weights' fields are allowed in consul_ecs_config.service.", - "Only the 'meshGateway', 'expose', and 'config' fields are allowed in consul_ecs_config.proxy.", - "Only the 'mode' field is allowed in consul_ecs_config.proxy.meshGateway.", - "Only the 'checks' and 'paths' fields are allowed in consul_ecs_config.proxy.expose.", - "Only the 'listenerPort', 'path', 'localPathPort', and 'protocol' fields are allowed in each item of consul_ecs_config.proxy.expose.paths[*].", - "Only the 'enabled', 'method', 'includeEntity', 'meta', 'region', 'stsEndpoint', and 'serverIdHeaderValue' fields are allowed in consul_ecs_config.consulLogin.", - }, - }, - } +// cases := map[string]struct { +// configFile string +// errors []string +// }{ +// "empty-map": { +// configFile: "test-empty-config.json", +// }, +// "complete-config": { +// configFile: "test-complete-config.json", +// }, +// "partial-config": { +// configFile: "test-partial-config.json", +// }, +// "invalid-config": { +// configFile: "test-invalid-config.json", +// errors: []string{ +// "Only the 'service', 'proxy', and 'consulLogin' fields are allowed in consul_ecs_config.", +// "Only the 'enableTagOverride' and 'weights' fields are allowed in consul_ecs_config.service.", +// "Only the 'meshGateway', 'expose', and 'config' fields are allowed in consul_ecs_config.proxy.", +// "Only the 'mode' field is allowed in consul_ecs_config.proxy.meshGateway.", +// "Only the 'checks' and 'paths' fields are allowed in consul_ecs_config.proxy.expose.", +// "Only the 'listenerPort', 'path', 'localPathPort', and 'protocol' fields are allowed in each item of consul_ecs_config.proxy.expose.paths[*].", +// "Only the 'enabled', 'method', 'includeEntity', 'meta', 'region', 'stsEndpoint', and 'serverIdHeaderValue' fields are allowed in consul_ecs_config.consulLogin.", +// }, +// }, +// } - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/consul-ecs-config-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) +// terraformOptions := &terraform.Options{ +// TerraformDir: "./terraform/consul-ecs-config-validate", +// NoColor: true, +// } +// terraform.Init(t, terraformOptions) - for name, c := range cases { - c := c +// for name, c := range cases { +// c := c - t.Run(name, func(t *testing.T) { - t.Parallel() +// t.Run(name, func(t *testing.T) { +// t.Parallel() - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "consul_ecs_config_file": c.configFile, - }, - }) +// out, err := terraform.PlanE(t, &terraform.Options{ +// TerraformDir: terraformOptions.TerraformDir, +// NoColor: true, +// Vars: map[string]interface{}{ +// "consul_ecs_config_file": c.configFile, +// }, +// }) - if len(c.errors) == 0 { - require.NoError(t, err) - } else { - for _, msg := range c.errors { - // error messages are wrapped, so a space may turn into a newline. - regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") - require.Regexp(t, regex, out) - } - } - }) - } -} +// if len(c.errors) == 0 { +// require.NoError(t, err) +// } else { +// for _, msg := range c.errors { +// // error messages are wrapped, so a space may turn into a newline. +// regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") +// require.Regexp(t, regex, out) +// } +// } +// }) +// } +// } -func TestValidation_HTTPTLSConfig(t *testing.T) { - t.Parallel() +// func TestValidation_HTTPTLSConfig(t *testing.T) { +// t.Parallel() - cases := map[string]struct { - configFile string - errors []string - }{ - "empty-map": { - configFile: "test-empty-config.json", - }, - "complete-config": { - configFile: "test-complete-config.json", - }, - "partial-config": { - configFile: "test-partial-config.json", - }, - "invalid-config": { - configFile: "test-invalid-config.json", - errors: []string{ - "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_tls_config.", - }, - }, - } +// cases := map[string]struct { +// configFile string +// errors []string +// }{ +// "empty-map": { +// configFile: "test-empty-config.json", +// }, +// "complete-config": { +// configFile: "test-complete-config.json", +// }, +// "partial-config": { +// configFile: "test-partial-config.json", +// }, +// "invalid-config": { +// configFile: "test-invalid-config.json", +// errors: []string{ +// "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_tls_config.", +// }, +// }, +// } - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/http-tls-config-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) +// terraformOptions := &terraform.Options{ +// TerraformDir: "./terraform/http-tls-config-validate", +// NoColor: true, +// } +// terraform.Init(t, terraformOptions) - for name, c := range cases { - c := c +// for name, c := range cases { +// c := c - t.Run(name, func(t *testing.T) { - t.Parallel() +// t.Run(name, func(t *testing.T) { +// t.Parallel() - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "http_tls_config_file": c.configFile, - }, - }) +// out, err := terraform.PlanE(t, &terraform.Options{ +// TerraformDir: terraformOptions.TerraformDir, +// NoColor: true, +// Vars: map[string]interface{}{ +// "http_tls_config_file": c.configFile, +// }, +// }) - if len(c.errors) == 0 { - require.NoError(t, err) - } else { - for _, msg := range c.errors { - // error messages are wrapped, so a space may turn into a newline. - regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") - require.Regexp(t, regex, out) - } - } - }) - } -} +// if len(c.errors) == 0 { +// require.NoError(t, err) +// } else { +// for _, msg := range c.errors { +// // error messages are wrapped, so a space may turn into a newline. +// regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") +// require.Regexp(t, regex, out) +// } +// } +// }) +// } +// } -func TestValidation_GRPCTLSConfig(t *testing.T) { - t.Parallel() +// func TestValidation_GRPCTLSConfig(t *testing.T) { +// t.Parallel() - cases := map[string]struct { - configFile string - errors []string - }{ - "empty-map": { - configFile: "test-empty-config.json", - }, - "complete-config": { - configFile: "test-complete-config.json", - }, - "partial-config": { - configFile: "test-partial-config.json", - }, - "invalid-config": { - configFile: "test-invalid-config.json", - errors: []string{ - "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_tls_config.", - }, - }, - } +// cases := map[string]struct { +// configFile string +// errors []string +// }{ +// "empty-map": { +// configFile: "test-empty-config.json", +// }, +// "complete-config": { +// configFile: "test-complete-config.json", +// }, +// "partial-config": { +// configFile: "test-partial-config.json", +// }, +// "invalid-config": { +// configFile: "test-invalid-config.json", +// errors: []string{ +// "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_tls_config.", +// }, +// }, +// } - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/grpc-tls-config-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) +// terraformOptions := &terraform.Options{ +// TerraformDir: "./terraform/grpc-tls-config-validate", +// NoColor: true, +// } +// terraform.Init(t, terraformOptions) - for name, c := range cases { - c := c +// for name, c := range cases { +// c := c - t.Run(name, func(t *testing.T) { - t.Parallel() +// t.Run(name, func(t *testing.T) { +// t.Parallel() - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "grpc_tls_config_file": c.configFile, - }, - }) +// out, err := terraform.PlanE(t, &terraform.Options{ +// TerraformDir: terraformOptions.TerraformDir, +// NoColor: true, +// Vars: map[string]interface{}{ +// "grpc_tls_config_file": c.configFile, +// }, +// }) - if len(c.errors) == 0 { - require.NoError(t, err) - } else { - for _, msg := range c.errors { - // error messages are wrapped, so a space may turn into a newline. - regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") - require.Regexp(t, regex, out) - } - } - }) - } -} +// if len(c.errors) == 0 { +// require.NoError(t, err) +// } else { +// for _, msg := range c.errors { +// // error messages are wrapped, so a space may turn into a newline. +// regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") +// require.Regexp(t, regex, out) +// } +// } +// }) +// } +// } -// Test the validation that both partition and namespace must be provided or neither. -func TestValidation_NamespaceAndPartitionRequired(t *testing.T) { - t.Parallel() +// // Test the validation that both partition and namespace must be provided or neither. +// func TestValidation_NamespaceAndPartitionRequired(t *testing.T) { +// t.Parallel() - cases := map[string]struct { - partition string - namespace string - errMsg string - }{ - "without partition and namespace": { - partition: "", - namespace: "", - errMsg: "", - }, - "with partition and namespace": { - partition: "default", - namespace: "default", - errMsg: "", - }, - "with partition, without namespace": { - partition: "default", - namespace: "", - errMsg: "ERROR: consul_namespace must be set if consul_partition is set", - }, - "without partition, with namespace": { - partition: "", - namespace: "default", - errMsg: "ERROR: consul_partition must be set if consul_namespace is set", - }, - } +// cases := map[string]struct { +// partition string +// namespace string +// errMsg string +// }{ +// "without partition and namespace": { +// partition: "", +// namespace: "", +// errMsg: "", +// }, +// "with partition and namespace": { +// partition: "default", +// namespace: "default", +// errMsg: "", +// }, +// "with partition, without namespace": { +// partition: "default", +// namespace: "", +// errMsg: "ERROR: consul_namespace must be set if consul_partition is set", +// }, +// "without partition, with namespace": { +// partition: "", +// namespace: "default", +// errMsg: "ERROR: consul_partition must be set if consul_namespace is set", +// }, +// } - terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "./terraform/admin-partition-validate", - NoColor: true, - }) - _ = terraform.Init(t, terraformOptions) +// terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ +// TerraformDir: "./terraform/admin-partition-validate", +// NoColor: true, +// }) +// _ = terraform.Init(t, terraformOptions) - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - t.Parallel() - terraformOptions.Vars = map[string]interface{}{ - "partition": c.partition, - "namespace": c.namespace, - } - t.Cleanup(func() { - _, _ = terraform.DestroyE(t, terraformOptions) - }) - _, err := terraform.PlanE(t, terraformOptions) - if c.errMsg == "" { - require.NoError(t, err) - } else { - require.Error(t, err) - require.Contains(t, err.Error(), c.errMsg) - } - }) - } -} +// for name, c := range cases { +// c := c +// t.Run(name, func(t *testing.T) { +// t.Parallel() +// terraformOptions.Vars = map[string]interface{}{ +// "partition": c.partition, +// "namespace": c.namespace, +// } +// t.Cleanup(func() { +// _, _ = terraform.DestroyE(t, terraformOptions) +// }) +// _, err := terraform.PlanE(t, terraformOptions) +// if c.errMsg == "" { +// require.NoError(t, err) +// } else { +// require.Error(t, err) +// require.Contains(t, err.Error(), c.errMsg) +// } +// }) +// } +// } -func TestValidation_RolePath(t *testing.T) { - t.Parallel() +// func TestValidation_RolePath(t *testing.T) { +// t.Parallel() - terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "./terraform/role-path-validate", - NoColor: true, - }) - _ = terraform.Init(t, terraformOptions) +// terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ +// TerraformDir: "./terraform/role-path-validate", +// NoColor: true, +// }) +// _ = terraform.Init(t, terraformOptions) - cases := []struct { - path string - expError bool - }{ - {"", true}, - {"test", true}, - {"/test", false}, - {"/test/", false}, - } - for _, c := range cases { - c := c - t.Run(fmt.Sprintf("path=%q", c.path), func(t *testing.T) { - t.Parallel() +// cases := []struct { +// path string +// expError bool +// }{ +// {"", true}, +// {"test", true}, +// {"/test", false}, +// {"/test/", false}, +// } +// for _, c := range cases { +// c := c +// t.Run(fmt.Sprintf("path=%q", c.path), func(t *testing.T) { +// t.Parallel() - applyOpts := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: terraformOptions.NoColor, - Vars: map[string]interface{}{ - "iam_role_path": c.path, - }, - }) +// applyOpts := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ +// TerraformDir: terraformOptions.TerraformDir, +// NoColor: terraformOptions.NoColor, +// Vars: map[string]interface{}{ +// "iam_role_path": c.path, +// }, +// }) - t.Cleanup(func() { - _, _ = terraform.DestroyE(t, applyOpts) - }) - _, err := terraform.PlanE(t, applyOpts) - if c.expError { - require.Error(t, err) - require.Contains(t, err.Error(), "iam_role_path must begin with '/'") - } else { - require.NoError(t, err) - } +// t.Cleanup(func() { +// _, _ = terraform.DestroyE(t, applyOpts) +// }) +// _, err := terraform.PlanE(t, applyOpts) +// if c.expError { +// require.Error(t, err) +// require.Contains(t, err.Error(), "iam_role_path must begin with '/'") +// } else { +// require.NoError(t, err) +// } - }) - } +// }) +// } -} +// } // TODO: Revisit this test // diff --git a/test/acceptance/tests/basic/terraform/pass-existing-iam-roles/main.tf b/test/acceptance/tests/basic/terraform/pass-existing-iam-roles/main.tf index a63ad53b..72c05082 100644 --- a/test/acceptance/tests/basic/terraform/pass-existing-iam-roles/main.tf +++ b/test/acceptance/tests/basic/terraform/pass-existing-iam-roles/main.tf @@ -49,7 +49,7 @@ module "test_client_create_new_roles" { family = local.create_roles_family log_configuration = null container_definitions = local.container_definitions - retry_join = ["test"] + consul_server_address = "consul.dc1" outbound_only = true // Roles are not passed. This tests the default values for create_task_role, From 0a4b56896b6efe442b36806f5c64f8570def5441 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 20:56:52 +0530 Subject: [PATCH 05/27] Add non tls grpc port --- modules/dev-server/main.tf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/dev-server/main.tf b/modules/dev-server/main.tf index 5f11d60f..9fa16707 100644 --- a/modules/dev-server/main.tf +++ b/modules/dev-server/main.tf @@ -412,6 +412,8 @@ exec consul agent -server \ -hcl='verify_incoming_rpc = true' \ -hcl='verify_outgoing = true' \ -hcl='verify_server_hostname = true' \ +%{else~} + -hcl='ports { grpc = 8502 }' \ %{endif~} %{if var.acls~} -hcl='acl {enabled = true, default_policy = "deny", down_policy = "extend-cache", enable_token_persistence = true}' \ From 42e85f9b711444b38dfeec18529e2ecd77511b6c Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 21:07:34 +0530 Subject: [PATCH 06/27] Fix typo --- modules/controller/config.tf | 2 +- modules/mesh-task/config.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/controller/config.tf b/modules/controller/config.tf index a36c3c40..514c1cd5 100644 --- a/modules/controller/config.tf +++ b/modules/controller/config.tf @@ -5,7 +5,7 @@ locals { httpTLSSettings = merge( { port = var.tls ? 8501 : 8500 - http = var.tls + https = var.tls }, var.http_tls_config ) diff --git a/modules/mesh-task/config.tf b/modules/mesh-task/config.tf index 062416a3..8fe6ca33 100644 --- a/modules/mesh-task/config.tf +++ b/modules/mesh-task/config.tf @@ -15,7 +15,7 @@ locals { httpTLSSettings = merge( { port = var.tls ? 8501 : 8500 - http = var.tls + https = var.tls }, var.http_tls_config ) From 22ef337820b751dbec37c65b562333309270c121 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 21:25:30 +0530 Subject: [PATCH 07/27] FMTed --- modules/controller/config.tf | 2 +- modules/mesh-task/config.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/controller/config.tf b/modules/controller/config.tf index 514c1cd5..b0bd2439 100644 --- a/modules/controller/config.tf +++ b/modules/controller/config.tf @@ -4,7 +4,7 @@ locals { httpTLSSettings = merge( { - port = var.tls ? 8501 : 8500 + port = var.tls ? 8501 : 8500 https = var.tls }, var.http_tls_config diff --git a/modules/mesh-task/config.tf b/modules/mesh-task/config.tf index 8fe6ca33..e5afec21 100644 --- a/modules/mesh-task/config.tf +++ b/modules/mesh-task/config.tf @@ -14,7 +14,7 @@ locals { httpTLSSettings = merge( { - port = var.tls ? 8501 : 8500 + port = var.tls ? 8501 : 8500 https = var.tls }, var.http_tls_config From b56f9e84f20f81dd602c57ab19adefdf48a3a839 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 21:37:57 +0530 Subject: [PATCH 08/27] Test ACLs --- test/acceptance/tests/basic/basic_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index 31a472d4..ccab1b98 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -924,8 +924,8 @@ func TestBasic(t *testing.T) { ecsClusterARN string datacenter string }{ - {secure: false}, - //{secure: true}, + //{secure: false}, + {secure: true}, // {secure: true, enterprise: true}, } From de6adddd2ff98970d8a263eaff978e423a9508b4 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 21:54:28 +0530 Subject: [PATCH 09/27] Fix issues --- modules/controller/main.tf | 4 ++-- modules/mesh-task/main.tf | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/controller/main.tf b/modules/controller/main.tf index 9df62760..f75cb85b 100644 --- a/modules/controller/main.tf +++ b/modules/controller/main.tf @@ -47,13 +47,13 @@ resource "aws_ecs_task_definition" "this" { { name = "CONSUL_GRPC_CACERT_PEM", valueFrom = local.grpc_ca_cert_arn - }, + } ] : [], local.https_ca_cert_arn != "" ? [ { name = "CONSUL_HTTPS_CACERT_PEM", valueFrom = local.https_ca_cert_arn - }, + } ] : [], ) environment = [ diff --git a/modules/mesh-task/main.tf b/modules/mesh-task/main.tf index 1332df49..7823e8e6 100644 --- a/modules/mesh-task/main.tf +++ b/modules/mesh-task/main.tf @@ -212,13 +212,13 @@ resource "aws_ecs_task_definition" "this" { { name = "CONSUL_HTTPS_CACERT_PEM", valueFrom = local.https_ca_cert_arn - }, + } ] : [], local.grpc_ca_cert_arn != "" ? [ { name = "CONSUL_GRPC_CACERT_PEM", valueFrom = local.grpc_ca_cert_arn - }, + } ] : [], ), ] : [], From 0f155b406e432fee025227ee7f15262501bf11af Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 22:01:47 +0530 Subject: [PATCH 10/27] Fix tests --- test/acceptance/tests/basic/terraform/basic-install/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/acceptance/tests/basic/terraform/basic-install/main.tf b/test/acceptance/tests/basic/terraform/basic-install/main.tf index eafed83b..5970a5a1 100644 --- a/test/acceptance/tests/basic/terraform/basic-install/main.tf +++ b/test/acceptance/tests/basic/terraform/basic-install/main.tf @@ -413,7 +413,7 @@ resource "aws_iam_role" "execution" { "secretsmanager:GetSecretValue" ], "Resource": [ - "${module.consul_server.ca_cert_arn}", + "${module.consul_server.ca_cert_arn}" ] }, %{endif~} From 9c37a6d0506dd2b096f961a09d2cfe5ca2370796 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 22:17:02 +0530 Subject: [PATCH 11/27] Test fix --- modules/controller/main.tf | 8 ++++---- modules/mesh-task/main.tf | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/controller/main.tf b/modules/controller/main.tf index f75cb85b..9e197df7 100644 --- a/modules/controller/main.tf +++ b/modules/controller/main.tf @@ -40,25 +40,25 @@ resource "aws_ecs_task_definition" "this" { } secrets = concat([ { - name = "CONSUL_HTTP_TOKEN", + name = "CONSUL_HTTP_TOKEN" valueFrom = var.consul_bootstrap_token_secret_arn }], local.grpc_ca_cert_arn != "" ? [ { - name = "CONSUL_GRPC_CACERT_PEM", + name = "CONSUL_GRPC_CACERT_PEM" valueFrom = local.grpc_ca_cert_arn } ] : [], local.https_ca_cert_arn != "" ? [ { - name = "CONSUL_HTTPS_CACERT_PEM", + name = "CONSUL_HTTPS_CACERT_PEM" valueFrom = local.https_ca_cert_arn } ] : [], ) environment = [ { - name = "CONSUL_ECS_CONFIG_JSON", + name = "CONSUL_ECS_CONFIG_JSON" value = local.encoded_config } ] diff --git a/modules/mesh-task/main.tf b/modules/mesh-task/main.tf index 7823e8e6..9bfeebd7 100644 --- a/modules/mesh-task/main.tf +++ b/modules/mesh-task/main.tf @@ -210,13 +210,13 @@ resource "aws_ecs_task_definition" "this" { concat( local.https_ca_cert_arn != "" ? [ { - name = "CONSUL_HTTPS_CACERT_PEM", + name = "CONSUL_HTTPS_CACERT_PEM" valueFrom = local.https_ca_cert_arn } ] : [], local.grpc_ca_cert_arn != "" ? [ { - name = "CONSUL_GRPC_CACERT_PEM", + name = "CONSUL_GRPC_CACERT_PEM" valueFrom = local.grpc_ca_cert_arn } ] : [], From 34fd370960f608b10df1b261913eeafd5c845008 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 22:24:05 +0530 Subject: [PATCH 12/27] Fix tests :( --- modules/mesh-task/main.tf | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/modules/mesh-task/main.tf b/modules/mesh-task/main.tf index 9bfeebd7..929e3083 100644 --- a/modules/mesh-task/main.tf +++ b/modules/mesh-task/main.tf @@ -207,20 +207,14 @@ resource "aws_ecs_task_definition" "this" { } secrets = concat( var.tls ? [ - concat( - local.https_ca_cert_arn != "" ? [ - { - name = "CONSUL_HTTPS_CACERT_PEM" - valueFrom = local.https_ca_cert_arn - } - ] : [], - local.grpc_ca_cert_arn != "" ? [ - { - name = "CONSUL_GRPC_CACERT_PEM" - valueFrom = local.grpc_ca_cert_arn - } - ] : [], - ), + { + name = "CONSUL_HTTPS_CACERT_PEM" + valueFrom = local.https_ca_cert_arn + }, + { + name = "CONSUL_GRPC_CACERT_PEM" + valueFrom = local.grpc_ca_cert_arn + } ] : [], [] ) From 41f4bc65c827d151822581a1113f288bbf456328 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 22:37:30 +0530 Subject: [PATCH 13/27] Try fix tests --- modules/controller/main.tf | 22 ++++++++++++++++++++++ modules/mesh-task/iam.tf | 24 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/modules/controller/main.tf b/modules/controller/main.tf index 9e197df7..daba7124 100644 --- a/modules/controller/main.tf +++ b/modules/controller/main.tf @@ -134,6 +134,28 @@ resource "aws_iam_policy" "this_execution" { "${var.consul_server_ca_cert_arn}" ] }, +%{endif~} +%{if var.consul_https_ca_cert_arn != ""~} + { + "Effect": "Allow", + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Resource": [ + "${var.consul_https_ca_cert_arn}" + ] + }, +%{endif~} +%{if var.consul_grpc_ca_cert_arn != ""~} + { + "Effect": "Allow", + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Resource": [ + "${var.consul_grpc_ca_cert_arn}" + ] + }, %{endif~} { "Effect": "Allow", diff --git a/modules/mesh-task/iam.tf b/modules/mesh-task/iam.tf index 00c1301c..8cb8ddc0 100644 --- a/modules/mesh-task/iam.tf +++ b/modules/mesh-task/iam.tf @@ -109,6 +109,7 @@ resource "aws_iam_policy" "execution" { "Version": "2012-10-17", "Statement": [ %{if var.tls~} +%{if var.consul_server_ca_cert_arn != ""~} { "Effect": "Allow", "Action": [ @@ -118,6 +119,29 @@ resource "aws_iam_policy" "execution" { "${var.consul_server_ca_cert_arn}" ] }, +%{endif~} +%{if var.consul_https_ca_cert_arn != ""~} + { + "Effect": "Allow", + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Resource": [ + "${var.consul_https_ca_cert_arn}" + ] + }, +%{endif~} +%{if var.consul_grpc_ca_cert_arn != ""~} + { + "Effect": "Allow", + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Resource": [ + "${var.consul_grpc_ca_cert_arn}" + ] + }, +%{endif~} %{endif~} { "Effect": "Allow", From 48b5b958624e83b2c3624253005298034f9bbf0f Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 22:53:55 +0530 Subject: [PATCH 14/27] Test enterprise --- test/acceptance/tests/basic/basic_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index ccab1b98..6300ac47 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -925,8 +925,8 @@ func TestBasic(t *testing.T) { datacenter string }{ //{secure: false}, - {secure: true}, - // {secure: true, enterprise: true}, + //{secure: true}, + {secure: true, enterprise: true}, } cfg := suite.Config() From e10b031af30c58dd995e6c27a8ccf3d161b3da9f Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 23:06:25 +0530 Subject: [PATCH 15/27] Don't run E2E tests --- test/acceptance/tests/basic/basic_test.go | 1985 ++++++++++----------- 1 file changed, 990 insertions(+), 995 deletions(-) diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index 6300ac47..1318515c 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -4,770 +4,765 @@ package basic import ( - "encoding/json" + "context" "fmt" - "os" "regexp" "strings" "testing" - "time" + "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ecs" - "github.com/gruntwork-io/terratest/modules/random" - "github.com/gruntwork-io/terratest/modules/shell" + "github.com/aws/aws-sdk-go-v2/service/iam" "github.com/gruntwork-io/terratest/modules/terraform" - "github.com/hashicorp/consul/sdk/testutil/retry" - "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/framework/helpers" - "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/framework/logger" "github.com/stretchr/testify/require" ) // TestVolumeVariable tests passing a list of volumes to mesh-task. // This validates a big nested dynamic block in mesh-task. -// func TestVolumeVariable(t *testing.T) { -// t.Parallel() -// volumes := []map[string]interface{}{ -// { -// "name": "my-vol1", -// }, -// { -// "name": "my-vol2", -// "host_path": "/tmp/fake/path", -// }, -// { -// "name": "no-optional-fields", -// "docker_volume_configuration": map[string]interface{}{}, -// "efs_volume_configuration": map[string]interface{}{ -// "file_system_id": "fakeid123", -// }, -// }, -// { -// "name": "all-the-fields", -// "docker_volume_configuration": map[string]interface{}{ -// "scope": "shared", -// "autoprovision": true, -// "driver": "local", -// "driver_opts": map[string]interface{}{ -// "type": "nfs", -// "device": "host.example.com:/", -// "o": "addr=host.example.com,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport", -// }, -// }, -// "fsx_windows_file_server_volume_configuration": map[string]interface{}{ -// "file_system_id": "fakeid456", -// "root_directory": `\\data`, -// "authorization_config": map[string]interface{}{ -// "credentials_parameter": "arn:aws:secretsmanager:us-east-1:000000000000:secret:fake-fake-fake-fake", -// "domain": "domain-name", -// }, -// }, -// }, -// } - -// terraformOptions := &terraform.Options{ -// TerraformDir: "./terraform/volume-variable", -// Vars: map[string]interface{}{"volumes": volumes}, -// NoColor: true, -// } -// t.Cleanup(func() { -// _, _ = terraform.DestroyE(t, terraformOptions) -// }) -// terraform.InitAndPlan(t, terraformOptions) -// } - -// // TestPassingExistingRoles will create the task definitions to validate -// // creation and passing of IAM roles by mesh-task. It creates two task definitions -// // with mesh-task: -// // - one which has mesh-task create the roles -// // - one which passes in existing roles -// // -// // This test does not start any services. -// // -// // Note: We don't have a validation for create_task_role=true XOR task_role=. -// // -// // If the role is created as part of the terraform plan/apply and passed in to mesh-task, -// // then the role is an unknown value during the plan, since it is not yet created, and you -// // can't reliably test its value for validations. -// func TestPassingExistingRoles(t *testing.T) { -// t.Parallel() +func TestVolumeVariable(t *testing.T) { + t.Parallel() + volumes := []map[string]interface{}{ + { + "name": "my-vol1", + }, + { + "name": "my-vol2", + "host_path": "/tmp/fake/path", + }, + { + "name": "no-optional-fields", + "docker_volume_configuration": map[string]interface{}{}, + "efs_volume_configuration": map[string]interface{}{ + "file_system_id": "fakeid123", + }, + }, + { + "name": "all-the-fields", + "docker_volume_configuration": map[string]interface{}{ + "scope": "shared", + "autoprovision": true, + "driver": "local", + "driver_opts": map[string]interface{}{ + "type": "nfs", + "device": "host.example.com:/", + "o": "addr=host.example.com,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport", + }, + }, + "fsx_windows_file_server_volume_configuration": map[string]interface{}{ + "file_system_id": "fakeid456", + "root_directory": `\\data`, + "authorization_config": map[string]interface{}{ + "credentials_parameter": "arn:aws:secretsmanager:us-east-1:000000000000:secret:fake-fake-fake-fake", + "domain": "domain-name", + }, + }, + }, + } -// terraformOptions := &terraform.Options{ -// TerraformDir: "./terraform/pass-existing-iam-roles", -// NoColor: true, -// } -// terraform.Init(t, terraformOptions) + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/volume-variable", + Vars: map[string]interface{}{"volumes": volumes}, + NoColor: true, + } + t.Cleanup(func() { + _, _ = terraform.DestroyE(t, terraformOptions) + }) + terraform.InitAndPlan(t, terraformOptions) +} -// // Init AWS clients. -// ctx := context.Background() -// cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-west-2")) -// require.NoError(t, err, "unable to initialize ECS client") -// ecsClient := ecs.NewFromConfig(cfg) -// iamClient := iam.NewFromConfig(cfg) +// TestPassingExistingRoles will create the task definitions to validate +// creation and passing of IAM roles by mesh-task. It creates two task definitions +// with mesh-task: +// - one which has mesh-task create the roles +// - one which passes in existing roles +// +// This test does not start any services. +// +// Note: We don't have a validation for create_task_role=true XOR task_role=. +// +// If the role is created as part of the terraform plan/apply and passed in to mesh-task, +// then the role is an unknown value during the plan, since it is not yet created, and you +// can't reliably test its value for validations. +func TestPassingExistingRoles(t *testing.T) { + t.Parallel() -// t.Cleanup(func() { -// _, _ = terraform.DestroyE(t, terraformOptions) -// }) -// terraform.InitAndApply(t, terraformOptions) + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/pass-existing-iam-roles", + NoColor: true, + } + terraform.Init(t, terraformOptions) -// outputs := terraform.OutputAll(t, terraformOptions) -// suffix := outputs["suffix"].(string) + // Init AWS clients. + ctx := context.Background() + cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-west-2")) + require.NoError(t, err, "unable to initialize ECS client") + ecsClient := ecs.NewFromConfig(cfg) + iamClient := iam.NewFromConfig(cfg) -// { -// // Check that mesh-task creates roles by default. -// taskDefArn := outputs["create_roles_task_definition_arn"].(string) -// family := outputs["create_roles_family"].(string) + t.Cleanup(func() { + _, _ = terraform.DestroyE(t, terraformOptions) + }) + terraform.InitAndApply(t, terraformOptions) -// resp, err := ecsClient.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ -// TaskDefinition: &taskDefArn, -// }) -// require.NoError(t, err) - -// // Expected role names, as created by mesh-task -// expTaskRoleName := family + "-task" -// expExecRoleName := family + "-execution" - -// // mesh-task should create roles and use them -// taskDef := resp.TaskDefinition -// require.NotNil(t, taskDef.TaskRoleArn) -// require.NotNil(t, taskDef.ExecutionRoleArn) -// require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs/`+expTaskRoleName, *taskDef.TaskRoleArn) -// require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs/`+expExecRoleName, *taskDef.ExecutionRoleArn) - -// // Check that the roles were really created. -// for _, roleName := range []string{expTaskRoleName, expExecRoleName} { -// resp, err := iamClient.GetRole(ctx, &iam.GetRoleInput{RoleName: &roleName}) -// require.NoError(t, err) -// require.Equal(t, *resp.Role.RoleName, roleName) -// } -// } + outputs := terraform.OutputAll(t, terraformOptions) + suffix := outputs["suffix"].(string) -// { -// // Check that mesh-task uses the passed in roles and doesn't create roles when roles are passed in. -// taskDefArn := outputs["pass_roles_task_definition_arn"].(string) -// family := outputs["pass_roles_family"].(string) + { + // Check that mesh-task creates roles by default. + taskDefArn := outputs["create_roles_task_definition_arn"].(string) + family := outputs["create_roles_family"].(string) -// resp, err := ecsClient.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ -// TaskDefinition: &taskDefArn, -// }) -// require.NoError(t, err) - -// // mesh-task should use the passed in roles. -// taskDef := resp.TaskDefinition -// require.NotNil(t, taskDef.TaskRoleArn) -// require.NotNil(t, taskDef.ExecutionRoleArn) -// require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs-test-pass-task-role-`+suffix, *taskDef.TaskRoleArn) -// require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs-test-pass-execution-role-`+suffix, *taskDef.ExecutionRoleArn) - -// // mesh-task should not create roles when they are passed in. -// expTaskRoleName := family + "-task" -// expExecRoleName := family + "-execution" -// for _, roleName := range []string{expTaskRoleName, expExecRoleName} { -// _, err := iamClient.GetRole(ctx, &iam.GetRoleInput{RoleName: &roleName}) -// require.Error(t, err) -// require.Contains(t, err.Error(), "StatusCode: 404") -// } -// } + resp, err := ecsClient.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ + TaskDefinition: &taskDefArn, + }) + require.NoError(t, err) + + // Expected role names, as created by mesh-task + expTaskRoleName := family + "-task" + expExecRoleName := family + "-execution" + + // mesh-task should create roles and use them + taskDef := resp.TaskDefinition + require.NotNil(t, taskDef.TaskRoleArn) + require.NotNil(t, taskDef.ExecutionRoleArn) + require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs/`+expTaskRoleName, *taskDef.TaskRoleArn) + require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs/`+expExecRoleName, *taskDef.ExecutionRoleArn) + + // Check that the roles were really created. + for _, roleName := range []string{expTaskRoleName, expExecRoleName} { + resp, err := iamClient.GetRole(ctx, &iam.GetRoleInput{RoleName: &roleName}) + require.NoError(t, err) + require.Equal(t, *resp.Role.RoleName, roleName) + } + } -// t.Log("Test Successful!") -// } + { + // Check that mesh-task uses the passed in roles and doesn't create roles when roles are passed in. + taskDefArn := outputs["pass_roles_task_definition_arn"].(string) + family := outputs["pass_roles_family"].(string) -// func TestValidation_AdditionalPolicies(t *testing.T) { -// t.Parallel() -// terraformOptions := &terraform.Options{ -// TerraformDir: "./terraform/pass-role-additional-policies-validate", -// NoColor: true, -// } -// terraform.Init(t, terraformOptions) + resp, err := ecsClient.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ + TaskDefinition: &taskDefArn, + }) + require.NoError(t, err) + + // mesh-task should use the passed in roles. + taskDef := resp.TaskDefinition + require.NotNil(t, taskDef.TaskRoleArn) + require.NotNil(t, taskDef.ExecutionRoleArn) + require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs-test-pass-task-role-`+suffix, *taskDef.TaskRoleArn) + require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs-test-pass-execution-role-`+suffix, *taskDef.ExecutionRoleArn) + + // mesh-task should not create roles when they are passed in. + expTaskRoleName := family + "-task" + expExecRoleName := family + "-execution" + for _, roleName := range []string{expTaskRoleName, expExecRoleName} { + _, err := iamClient.GetRole(ctx, &iam.GetRoleInput{RoleName: &roleName}) + require.Error(t, err) + require.Contains(t, err.Error(), "StatusCode: 404") + } + } -// cases := map[string]struct { -// execution bool -// errMsg string -// }{ -// "task": { -// execution: false, -// errMsg: "ERROR: cannot set additional_task_role_policies when create_task_role=false", -// }, -// "execution": { -// execution: true, -// errMsg: "ERROR: cannot set additional_execution_role_policies when create_execution_role=false", -// }, -// } -// for name, c := range cases { -// c := c -// t.Run(name, func(t *testing.T) { -// t.Parallel() + t.Log("Test Successful!") +} -// _, err := terraform.PlanE(t, &terraform.Options{ -// TerraformDir: terraformOptions.TerraformDir, -// NoColor: true, -// Vars: map[string]interface{}{ -// "test_execution_role": c.execution, -// }, -// }) -// require.Error(t, err) -// // error messages are wrapped, so a space may turn into a newline. -// regex := strings.ReplaceAll(regexp.QuoteMeta(c.errMsg), " ", "\\s+") -// require.Regexp(t, regex, err.Error()) -// }) -// } -// } +func TestValidation_AdditionalPolicies(t *testing.T) { + t.Parallel() + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/pass-role-additional-policies-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) -// func TestPassingAppEntrypoint(t *testing.T) { -// t.Parallel() + cases := map[string]struct { + execution bool + errMsg string + }{ + "task": { + execution: false, + errMsg: "ERROR: cannot set additional_task_role_policies when create_task_role=false", + }, + "execution": { + execution: true, + errMsg: "ERROR: cannot set additional_execution_role_policies when create_execution_role=false", + }, + } + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + t.Parallel() -// newint := func(x int) *int { return &x } -// cases := map[string]struct { -// value *int -// expEntrypoint bool -// }{ -// "null": {nil, false}, -// "negative": {newint(-1), false}, -// "zero": {newint(0), false}, -// "one": {newint(1), true}, -// "five": {newint(5), true}, -// } + _, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "test_execution_role": c.execution, + }, + }) + require.Error(t, err) + // error messages are wrapped, so a space may turn into a newline. + regex := strings.ReplaceAll(regexp.QuoteMeta(c.errMsg), " ", "\\s+") + require.Regexp(t, regex, err.Error()) + }) + } +} -// terraformOptions := &terraform.Options{ -// TerraformDir: "./terraform/pass-app-entrypoint", -// NoColor: true, -// } -// t.Cleanup(func() { -// _, _ = terraform.DestroyE(t, terraformOptions) -// }) +func TestPassingAppEntrypoint(t *testing.T) { + t.Parallel() -// terraform.Init(t, terraformOptions) -// for name, c := range cases { -// c := c -// t.Run(name, func(t *testing.T) { -// t.Parallel() -// opts := &terraform.Options{ -// TerraformDir: terraformOptions.TerraformDir, -// NoColor: true, -// Vars: map[string]interface{}{ -// //"application_shutdown_delay_seconds": nil, -// }, -// } -// if c.value != nil { -// opts.Vars["application_shutdown_delay_seconds"] = *c.value -// } -// out := terraform.Plan(t, opts) - -// if c.expEntrypoint { -// // Look for app-entrypoint in the Terraform diff. -// regex := strings.Join([]string{ -// `\+ entryPoint = \[`, -// ` \+ "/consul/consul-ecs",`, -// ` \+ "app-entrypoint",`, -// ` \+ "-shutdown-delay",`, -// ` \+ "\d+s",`, // e.g. "2s", "10s", etc -// `\]`, -// }, `\s+`) -// require.Regexp(t, regex, out) -// } else { -// require.NotContains(t, out, "app-entrypoint") -// } + newint := func(x int) *int { return &x } + cases := map[string]struct { + value *int + expEntrypoint bool + }{ + "null": {nil, false}, + "negative": {newint(-1), false}, + "zero": {newint(0), false}, + "one": {newint(1), true}, + "five": {newint(5), true}, + } -// }) -// } -// } + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/pass-app-entrypoint", + NoColor: true, + } + t.Cleanup(func() { + _, _ = terraform.DestroyE(t, terraformOptions) + }) -// func TestValidation_UpstreamsVariable(t *testing.T) { -// t.Parallel() + terraform.Init(t, terraformOptions) + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + t.Parallel() + opts := &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + //"application_shutdown_delay_seconds": nil, + }, + } + if c.value != nil { + opts.Vars["application_shutdown_delay_seconds"] = *c.value + } + out := terraform.Plan(t, opts) + + if c.expEntrypoint { + // Look for app-entrypoint in the Terraform diff. + regex := strings.Join([]string{ + `\+ entryPoint = \[`, + ` \+ "/consul/consul-ecs",`, + ` \+ "app-entrypoint",`, + ` \+ "-shutdown-delay",`, + ` \+ "\d+s",`, // e.g. "2s", "10s", etc + `\]`, + }, `\s+`) + require.Regexp(t, regex, out) + } else { + require.NotContains(t, out, "app-entrypoint") + } -// cases := map[string]struct { -// upstreamsFile string -// errors []string -// }{ -// "no-upstreams": { -// upstreamsFile: "test-no-upstreams.json", -// }, -// "valid-upstreams": { -// upstreamsFile: "test-valid-upstreams.json", -// }, -// "invalid-upstreams": { -// upstreamsFile: "test-invalid-upstreams.json", -// errors: []string{ -// "Upstream fields must be one of.*", -// }, -// }, -// "requires-destination-name": { -// upstreamsFile: "test-missing-destinationName.json", -// errors: []string{ -// "Upstream fields .* are required.", -// }, -// }, -// "requires-local-bind-port": { -// upstreamsFile: "test-missing-localBindPort.json", -// errors: []string{ -// "Upstream fields .* are required.", -// }, -// }, -// } + }) + } +} -// terraformOptions := &terraform.Options{ -// TerraformDir: "./terraform/upstreams-validate", -// NoColor: true, -// } -// terraform.Init(t, terraformOptions) +func TestValidation_UpstreamsVariable(t *testing.T) { + t.Parallel() -// for name, c := range cases { -// t.Run(name, func(t *testing.T) { -// out, err := terraform.PlanE(t, &terraform.Options{ -// TerraformDir: terraformOptions.TerraformDir, -// NoColor: true, -// Vars: map[string]interface{}{ -// "upstreams_file": c.upstreamsFile, -// }, -// }) + cases := map[string]struct { + upstreamsFile string + errors []string + }{ + "no-upstreams": { + upstreamsFile: "test-no-upstreams.json", + }, + "valid-upstreams": { + upstreamsFile: "test-valid-upstreams.json", + }, + "invalid-upstreams": { + upstreamsFile: "test-invalid-upstreams.json", + errors: []string{ + "Upstream fields must be one of.*", + }, + }, + "requires-destination-name": { + upstreamsFile: "test-missing-destinationName.json", + errors: []string{ + "Upstream fields .* are required.", + }, + }, + "requires-local-bind-port": { + upstreamsFile: "test-missing-localBindPort.json", + errors: []string{ + "Upstream fields .* are required.", + }, + }, + } -// if len(c.errors) == 0 { -// require.NoError(t, err) -// } else { -// require.Error(t, err) -// for _, regex := range c.errors { -// require.Regexp(t, regex, out) -// } -// } -// }) -// } -// } + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/upstreams-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) -// func TestValidation_EnvoyPublicListenerPort(t *testing.T) { -// t.Parallel() + for name, c := range cases { + t.Run(name, func(t *testing.T) { + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "upstreams_file": c.upstreamsFile, + }, + }) -// cases := map[string]struct { -// port int -// error string -// }{ -// "allowed-port": { -// port: 21000, -// }, -// "too-high-port": { -// port: 65536, -// error: "The envoy_public_listener_port must be greater than 0 and less than or equal to 65535.", -// }, -// "disallowed-port": { -// port: 19000, -// error: "The envoy_public_listener_port must not conflict with the following ports that are reserved for Consul and Envoy", -// }, -// } + if len(c.errors) == 0 { + require.NoError(t, err) + } else { + require.Error(t, err) + for _, regex := range c.errors { + require.Regexp(t, regex, out) + } + } + }) + } +} -// terraformOptions := &terraform.Options{ -// TerraformDir: "./terraform/public-listener-port-validate", -// NoColor: true, -// } -// terraform.Init(t, terraformOptions) +func TestValidation_EnvoyPublicListenerPort(t *testing.T) { + t.Parallel() -// for name, c := range cases { -// c := c -// t.Run(name, func(t *testing.T) { -// t.Parallel() + cases := map[string]struct { + port int + error string + }{ + "allowed-port": { + port: 21000, + }, + "too-high-port": { + port: 65536, + error: "The envoy_public_listener_port must be greater than 0 and less than or equal to 65535.", + }, + "disallowed-port": { + port: 19000, + error: "The envoy_public_listener_port must not conflict with the following ports that are reserved for Consul and Envoy", + }, + } -// out, err := terraform.PlanE(t, &terraform.Options{ -// TerraformDir: terraformOptions.TerraformDir, -// NoColor: true, -// Vars: map[string]interface{}{ -// "envoy_public_listener_port": c.port, -// }, -// }) + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/public-listener-port-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) -// if c.error == "" { -// require.NoError(t, err) -// } else { -// // handle multiline error messages. -// regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") -// require.Error(t, err) -// require.Regexp(t, regex, out) -// } -// }) -// } -// } + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + t.Parallel() -// func TestValidation_EnvoyReadinessPort(t *testing.T) { -// t.Parallel() + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "envoy_public_listener_port": c.port, + }, + }) -// cases := map[string]struct { -// port int -// error string -// }{ -// "allowed-port": { -// port: 23000, -// }, -// "too-high-port": { -// port: 65536, -// error: "The envoy_readiness_port must be greater than 0 and less than or equal to 65535.", -// }, -// "disallowed-port": { -// port: 19000, -// error: "The envoy_readiness_port must not conflict with the following ports that are reserved for Consul and Envoy", -// }, -// "conflicts-with-listener-port": { -// port: 20000, -// error: "envoy_public_listener_port should not conflict with envoy_readiness_port", -// }, -// } + if c.error == "" { + require.NoError(t, err) + } else { + // handle multiline error messages. + regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") + require.Error(t, err) + require.Regexp(t, regex, out) + } + }) + } +} -// terraformOptions := &terraform.Options{ -// TerraformDir: "./terraform/envoy-readiness-port-validate", -// NoColor: true, -// } -// terraform.Init(t, terraformOptions) +func TestValidation_EnvoyReadinessPort(t *testing.T) { + t.Parallel() -// for name, c := range cases { -// c := c -// t.Run(name, func(t *testing.T) { -// t.Parallel() + cases := map[string]struct { + port int + error string + }{ + "allowed-port": { + port: 23000, + }, + "too-high-port": { + port: 65536, + error: "The envoy_readiness_port must be greater than 0 and less than or equal to 65535.", + }, + "disallowed-port": { + port: 19000, + error: "The envoy_readiness_port must not conflict with the following ports that are reserved for Consul and Envoy", + }, + "conflicts-with-listener-port": { + port: 20000, + error: "envoy_public_listener_port should not conflict with envoy_readiness_port", + }, + } -// out, err := terraform.PlanE(t, &terraform.Options{ -// TerraformDir: terraformOptions.TerraformDir, -// NoColor: true, -// Vars: map[string]interface{}{ -// "envoy_readiness_port": c.port, -// }, -// }) + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/envoy-readiness-port-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) -// if c.error == "" { -// require.NoError(t, err) -// } else { -// // handle multiline error messages. -// regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") -// require.Error(t, err) -// require.Regexp(t, regex, out) -// } -// }) -// } -// } + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + t.Parallel() -// func TestValidation_ConsulServiceName(t *testing.T) { -// t.Parallel() + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "envoy_readiness_port": c.port, + }, + }) -// cases := map[string]struct { -// serviceName string -// error bool -// }{ -// "empty": {}, -// "lowercase": { -// serviceName: "lower-case-name", -// }, -// "uppercase": { -// serviceName: "UPPER-CASE-NAME", -// error: true, -// }, -// } + if c.error == "" { + require.NoError(t, err) + } else { + // handle multiline error messages. + regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") + require.Error(t, err) + require.Regexp(t, regex, out) + } + }) + } +} -// terraformOptions := &terraform.Options{ -// TerraformDir: "./terraform/service-name-validate", -// NoColor: true, -// } -// terraform.Init(t, terraformOptions) +func TestValidation_ConsulServiceName(t *testing.T) { + t.Parallel() -// for name, c := range cases { -// c := c + cases := map[string]struct { + serviceName string + error bool + }{ + "empty": {}, + "lowercase": { + serviceName: "lower-case-name", + }, + "uppercase": { + serviceName: "UPPER-CASE-NAME", + error: true, + }, + } -// t.Run(name, func(t *testing.T) { -// t.Parallel() + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/service-name-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) -// out, err := terraform.PlanE(t, &terraform.Options{ -// TerraformDir: terraformOptions.TerraformDir, -// NoColor: true, -// Vars: map[string]interface{}{ -// "consul_service_name": c.serviceName, -// }, -// }) + for name, c := range cases { + c := c -// if c.error { -// require.Error(t, err) -// require.Regexp(t, "The consul_service_name must be lower case.", out) -// } else { -// require.NoError(t, err) -// } -// }) -// } + t.Run(name, func(t *testing.T) { + t.Parallel() -// } + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "consul_service_name": c.serviceName, + }, + }) -// func TestValidation_ConsulEcsConfigVariable(t *testing.T) { -// t.Parallel() + if c.error { + require.Error(t, err) + require.Regexp(t, "The consul_service_name must be lower case.", out) + } else { + require.NoError(t, err) + } + }) + } -// cases := map[string]struct { -// configFile string -// errors []string -// }{ -// "empty-map": { -// configFile: "test-empty-config.json", -// }, -// "complete-config": { -// configFile: "test-complete-config.json", -// }, -// "partial-config": { -// configFile: "test-partial-config.json", -// }, -// "invalid-config": { -// configFile: "test-invalid-config.json", -// errors: []string{ -// "Only the 'service', 'proxy', and 'consulLogin' fields are allowed in consul_ecs_config.", -// "Only the 'enableTagOverride' and 'weights' fields are allowed in consul_ecs_config.service.", -// "Only the 'meshGateway', 'expose', and 'config' fields are allowed in consul_ecs_config.proxy.", -// "Only the 'mode' field is allowed in consul_ecs_config.proxy.meshGateway.", -// "Only the 'checks' and 'paths' fields are allowed in consul_ecs_config.proxy.expose.", -// "Only the 'listenerPort', 'path', 'localPathPort', and 'protocol' fields are allowed in each item of consul_ecs_config.proxy.expose.paths[*].", -// "Only the 'enabled', 'method', 'includeEntity', 'meta', 'region', 'stsEndpoint', and 'serverIdHeaderValue' fields are allowed in consul_ecs_config.consulLogin.", -// }, -// }, -// } +} -// terraformOptions := &terraform.Options{ -// TerraformDir: "./terraform/consul-ecs-config-validate", -// NoColor: true, -// } -// terraform.Init(t, terraformOptions) +func TestValidation_ConsulEcsConfigVariable(t *testing.T) { + t.Parallel() -// for name, c := range cases { -// c := c + cases := map[string]struct { + configFile string + errors []string + }{ + "empty-map": { + configFile: "test-empty-config.json", + }, + "complete-config": { + configFile: "test-complete-config.json", + }, + "partial-config": { + configFile: "test-partial-config.json", + }, + "invalid-config": { + configFile: "test-invalid-config.json", + errors: []string{ + "Only the 'service', 'proxy', and 'consulLogin' fields are allowed in consul_ecs_config.", + "Only the 'enableTagOverride' and 'weights' fields are allowed in consul_ecs_config.service.", + "Only the 'meshGateway', 'expose', and 'config' fields are allowed in consul_ecs_config.proxy.", + "Only the 'mode' field is allowed in consul_ecs_config.proxy.meshGateway.", + "Only the 'checks' and 'paths' fields are allowed in consul_ecs_config.proxy.expose.", + "Only the 'listenerPort', 'path', 'localPathPort', and 'protocol' fields are allowed in each item of consul_ecs_config.proxy.expose.paths[*].", + "Only the 'enabled', 'method', 'includeEntity', 'meta', 'region', 'stsEndpoint', and 'serverIdHeaderValue' fields are allowed in consul_ecs_config.consulLogin.", + }, + }, + } -// t.Run(name, func(t *testing.T) { -// t.Parallel() + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/consul-ecs-config-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) -// out, err := terraform.PlanE(t, &terraform.Options{ -// TerraformDir: terraformOptions.TerraformDir, -// NoColor: true, -// Vars: map[string]interface{}{ -// "consul_ecs_config_file": c.configFile, -// }, -// }) + for name, c := range cases { + c := c -// if len(c.errors) == 0 { -// require.NoError(t, err) -// } else { -// for _, msg := range c.errors { -// // error messages are wrapped, so a space may turn into a newline. -// regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") -// require.Regexp(t, regex, out) -// } -// } -// }) -// } -// } + t.Run(name, func(t *testing.T) { + t.Parallel() -// func TestValidation_HTTPTLSConfig(t *testing.T) { -// t.Parallel() + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "consul_ecs_config_file": c.configFile, + }, + }) -// cases := map[string]struct { -// configFile string -// errors []string -// }{ -// "empty-map": { -// configFile: "test-empty-config.json", -// }, -// "complete-config": { -// configFile: "test-complete-config.json", -// }, -// "partial-config": { -// configFile: "test-partial-config.json", -// }, -// "invalid-config": { -// configFile: "test-invalid-config.json", -// errors: []string{ -// "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_tls_config.", -// }, -// }, -// } + if len(c.errors) == 0 { + require.NoError(t, err) + } else { + for _, msg := range c.errors { + // error messages are wrapped, so a space may turn into a newline. + regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") + require.Regexp(t, regex, out) + } + } + }) + } +} -// terraformOptions := &terraform.Options{ -// TerraformDir: "./terraform/http-tls-config-validate", -// NoColor: true, -// } -// terraform.Init(t, terraformOptions) +func TestValidation_HTTPTLSConfig(t *testing.T) { + t.Parallel() -// for name, c := range cases { -// c := c + cases := map[string]struct { + configFile string + errors []string + }{ + "empty-map": { + configFile: "test-empty-config.json", + }, + "complete-config": { + configFile: "test-complete-config.json", + }, + "partial-config": { + configFile: "test-partial-config.json", + }, + "invalid-config": { + configFile: "test-invalid-config.json", + errors: []string{ + "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_tls_config.", + }, + }, + } -// t.Run(name, func(t *testing.T) { -// t.Parallel() + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/http-tls-config-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) -// out, err := terraform.PlanE(t, &terraform.Options{ -// TerraformDir: terraformOptions.TerraformDir, -// NoColor: true, -// Vars: map[string]interface{}{ -// "http_tls_config_file": c.configFile, -// }, -// }) + for name, c := range cases { + c := c -// if len(c.errors) == 0 { -// require.NoError(t, err) -// } else { -// for _, msg := range c.errors { -// // error messages are wrapped, so a space may turn into a newline. -// regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") -// require.Regexp(t, regex, out) -// } -// } -// }) -// } -// } + t.Run(name, func(t *testing.T) { + t.Parallel() -// func TestValidation_GRPCTLSConfig(t *testing.T) { -// t.Parallel() + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "http_tls_config_file": c.configFile, + }, + }) -// cases := map[string]struct { -// configFile string -// errors []string -// }{ -// "empty-map": { -// configFile: "test-empty-config.json", -// }, -// "complete-config": { -// configFile: "test-complete-config.json", -// }, -// "partial-config": { -// configFile: "test-partial-config.json", -// }, -// "invalid-config": { -// configFile: "test-invalid-config.json", -// errors: []string{ -// "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_tls_config.", -// }, -// }, -// } + if len(c.errors) == 0 { + require.NoError(t, err) + } else { + for _, msg := range c.errors { + // error messages are wrapped, so a space may turn into a newline. + regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") + require.Regexp(t, regex, out) + } + } + }) + } +} -// terraformOptions := &terraform.Options{ -// TerraformDir: "./terraform/grpc-tls-config-validate", -// NoColor: true, -// } -// terraform.Init(t, terraformOptions) +func TestValidation_GRPCTLSConfig(t *testing.T) { + t.Parallel() -// for name, c := range cases { -// c := c + cases := map[string]struct { + configFile string + errors []string + }{ + "empty-map": { + configFile: "test-empty-config.json", + }, + "complete-config": { + configFile: "test-complete-config.json", + }, + "partial-config": { + configFile: "test-partial-config.json", + }, + "invalid-config": { + configFile: "test-invalid-config.json", + errors: []string{ + "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_tls_config.", + }, + }, + } -// t.Run(name, func(t *testing.T) { -// t.Parallel() + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/grpc-tls-config-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) -// out, err := terraform.PlanE(t, &terraform.Options{ -// TerraformDir: terraformOptions.TerraformDir, -// NoColor: true, -// Vars: map[string]interface{}{ -// "grpc_tls_config_file": c.configFile, -// }, -// }) + for name, c := range cases { + c := c -// if len(c.errors) == 0 { -// require.NoError(t, err) -// } else { -// for _, msg := range c.errors { -// // error messages are wrapped, so a space may turn into a newline. -// regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") -// require.Regexp(t, regex, out) -// } -// } -// }) -// } -// } + t.Run(name, func(t *testing.T) { + t.Parallel() -// // Test the validation that both partition and namespace must be provided or neither. -// func TestValidation_NamespaceAndPartitionRequired(t *testing.T) { -// t.Parallel() + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "grpc_tls_config_file": c.configFile, + }, + }) -// cases := map[string]struct { -// partition string -// namespace string -// errMsg string -// }{ -// "without partition and namespace": { -// partition: "", -// namespace: "", -// errMsg: "", -// }, -// "with partition and namespace": { -// partition: "default", -// namespace: "default", -// errMsg: "", -// }, -// "with partition, without namespace": { -// partition: "default", -// namespace: "", -// errMsg: "ERROR: consul_namespace must be set if consul_partition is set", -// }, -// "without partition, with namespace": { -// partition: "", -// namespace: "default", -// errMsg: "ERROR: consul_partition must be set if consul_namespace is set", -// }, -// } + if len(c.errors) == 0 { + require.NoError(t, err) + } else { + for _, msg := range c.errors { + // error messages are wrapped, so a space may turn into a newline. + regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") + require.Regexp(t, regex, out) + } + } + }) + } +} -// terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ -// TerraformDir: "./terraform/admin-partition-validate", -// NoColor: true, -// }) -// _ = terraform.Init(t, terraformOptions) +// Test the validation that both partition and namespace must be provided or neither. +func TestValidation_NamespaceAndPartitionRequired(t *testing.T) { + t.Parallel() -// for name, c := range cases { -// c := c -// t.Run(name, func(t *testing.T) { -// t.Parallel() -// terraformOptions.Vars = map[string]interface{}{ -// "partition": c.partition, -// "namespace": c.namespace, -// } -// t.Cleanup(func() { -// _, _ = terraform.DestroyE(t, terraformOptions) -// }) -// _, err := terraform.PlanE(t, terraformOptions) -// if c.errMsg == "" { -// require.NoError(t, err) -// } else { -// require.Error(t, err) -// require.Contains(t, err.Error(), c.errMsg) -// } -// }) -// } -// } + cases := map[string]struct { + partition string + namespace string + errMsg string + }{ + "without partition and namespace": { + partition: "", + namespace: "", + errMsg: "", + }, + "with partition and namespace": { + partition: "default", + namespace: "default", + errMsg: "", + }, + "with partition, without namespace": { + partition: "default", + namespace: "", + errMsg: "ERROR: consul_namespace must be set if consul_partition is set", + }, + "without partition, with namespace": { + partition: "", + namespace: "default", + errMsg: "ERROR: consul_partition must be set if consul_namespace is set", + }, + } -// func TestValidation_RolePath(t *testing.T) { -// t.Parallel() + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: "./terraform/admin-partition-validate", + NoColor: true, + }) + _ = terraform.Init(t, terraformOptions) -// terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ -// TerraformDir: "./terraform/role-path-validate", -// NoColor: true, -// }) -// _ = terraform.Init(t, terraformOptions) + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + t.Parallel() + terraformOptions.Vars = map[string]interface{}{ + "partition": c.partition, + "namespace": c.namespace, + } + t.Cleanup(func() { + _, _ = terraform.DestroyE(t, terraformOptions) + }) + _, err := terraform.PlanE(t, terraformOptions) + if c.errMsg == "" { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), c.errMsg) + } + }) + } +} -// cases := []struct { -// path string -// expError bool -// }{ -// {"", true}, -// {"test", true}, -// {"/test", false}, -// {"/test/", false}, -// } -// for _, c := range cases { -// c := c -// t.Run(fmt.Sprintf("path=%q", c.path), func(t *testing.T) { -// t.Parallel() +func TestValidation_RolePath(t *testing.T) { + t.Parallel() -// applyOpts := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ -// TerraformDir: terraformOptions.TerraformDir, -// NoColor: terraformOptions.NoColor, -// Vars: map[string]interface{}{ -// "iam_role_path": c.path, -// }, -// }) + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: "./terraform/role-path-validate", + NoColor: true, + }) + _ = terraform.Init(t, terraformOptions) -// t.Cleanup(func() { -// _, _ = terraform.DestroyE(t, applyOpts) -// }) -// _, err := terraform.PlanE(t, applyOpts) -// if c.expError { -// require.Error(t, err) -// require.Contains(t, err.Error(), "iam_role_path must begin with '/'") -// } else { -// require.NoError(t, err) -// } + cases := []struct { + path string + expError bool + }{ + {"", true}, + {"test", true}, + {"/test", false}, + {"/test/", false}, + } + for _, c := range cases { + c := c + t.Run(fmt.Sprintf("path=%q", c.path), func(t *testing.T) { + t.Parallel() -// }) -// } + applyOpts := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: terraformOptions.NoColor, + Vars: map[string]interface{}{ + "iam_role_path": c.path, + }, + }) -// } + t.Cleanup(func() { + _, _ = terraform.DestroyE(t, applyOpts) + }) + _, err := terraform.PlanE(t, applyOpts) + if c.expError { + require.Error(t, err) + require.Contains(t, err.Error(), "iam_role_path must begin with '/'") + } else { + require.NoError(t, err) + } + + }) + } + +} // TODO: Revisit this test // @@ -914,353 +909,353 @@ import ( // } // } -func TestBasic(t *testing.T) { - t.Parallel() +// func TestBasic(t *testing.T) { +// t.Parallel() - cases := []struct { - secure bool - enterprise bool - stateFile string - ecsClusterARN string - datacenter string - }{ - //{secure: false}, - //{secure: true}, - {secure: true, enterprise: true}, - } +// cases := []struct { +// secure bool +// enterprise bool +// stateFile string +// ecsClusterARN string +// datacenter string +// }{ +// {secure: false}, +// {secure: true}, +// {secure: true, enterprise: true}, +// } - cfg := suite.Config() - require.GreaterOrEqual(t, len(cfg.ECSClusterARNs), len(cases), - "TestBasic requires %d ECS clusters. Update setup-terraform and re-run.", len(cases), - ) +// cfg := suite.Config() +// require.GreaterOrEqual(t, len(cfg.ECSClusterARNs), len(cases), +// "TestBasic requires %d ECS clusters. Update setup-terraform and re-run.", len(cases), +// ) - initOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "./terraform/basic-install", - NoColor: true, - }) - terraform.Init(t, initOptions) +// initOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ +// TerraformDir: "./terraform/basic-install", +// NoColor: true, +// }) +// terraform.Init(t, initOptions) - for i, c := range cases { - c := c +// for i, c := range cases { +// c := c - // To support running in parallel, each test case should have: - // - a unique cluster - // - a unique Terrform state file to isolate resources - // - a unique datacenter within the VPC to avoid conflicts in CloudMap namespaces - // If more clusters are needed, update the cluster count in setup-terraform - c.ecsClusterARN = cfg.ECSClusterARNs[i] - c.datacenter = fmt.Sprintf("dc%d", i) - c.stateFile = fmt.Sprintf("terraform-%d.tfstate", i) +// // To support running in parallel, each test case should have: +// // - a unique cluster +// // - a unique Terrform state file to isolate resources +// // - a unique datacenter within the VPC to avoid conflicts in CloudMap namespaces +// // If more clusters are needed, update the cluster count in setup-terraform +// c.ecsClusterARN = cfg.ECSClusterARNs[i] +// c.datacenter = fmt.Sprintf("dc%d", i) +// c.stateFile = fmt.Sprintf("terraform-%d.tfstate", i) - t.Run(fmt.Sprintf("secure: %t,enterprise: %t", c.secure, c.enterprise), func(t *testing.T) { - t.Parallel() +// t.Run(fmt.Sprintf("secure: %t,enterprise: %t", c.secure, c.enterprise), func(t *testing.T) { +// t.Parallel() - randomSuffix := strings.ToLower(random.UniqueId()) +// randomSuffix := strings.ToLower(random.UniqueId()) - tfEnvVars := map[string]string{ - // Use a unique state file for each parallel invocation of Terraform. - "TF_CLI_ARGS": fmt.Sprintf("-state=%s -state-out=%s", c.stateFile, c.stateFile), - } +// tfEnvVars := map[string]string{ +// // Use a unique state file for each parallel invocation of Terraform. +// "TF_CLI_ARGS": fmt.Sprintf("-state=%s -state-out=%s", c.stateFile, c.stateFile), +// } - tfVars := cfg.TFVars("route_table_ids", "ecs_cluster_arns") - tfVars["secure"] = c.secure - tfVars["suffix"] = randomSuffix - tfVars["ecs_cluster_arn"] = c.ecsClusterARN - tfVars["consul_datacenter"] = c.datacenter - clientServiceName := "test_client" - - serverServiceName := "test_server" - if c.secure { - // This uses the explicitly passed service name rather than the task's family name. - serverServiceName = "custom_test_server" - } - tfVars["server_service_name"] = serverServiceName +// tfVars := cfg.TFVars("route_table_ids", "ecs_cluster_arns") +// tfVars["secure"] = c.secure +// tfVars["suffix"] = randomSuffix +// tfVars["ecs_cluster_arn"] = c.ecsClusterARN +// tfVars["consul_datacenter"] = c.datacenter +// clientServiceName := "test_client" + +// serverServiceName := "test_server" +// if c.secure { +// // This uses the explicitly passed service name rather than the task's family name. +// serverServiceName = "custom_test_server" +// } +// tfVars["server_service_name"] = serverServiceName - image := cfg.ConsulImageURI(c.enterprise) - t.Logf("using consul image = %s", image) - tfVars["consul_image"] = image - if c.enterprise { - license := os.Getenv("CONSUL_LICENSE") - require.True(t, license != "", "CONSUL_LICENSE not found but is required for enterprise tests") +// image := cfg.ConsulImageURI(c.enterprise) +// t.Logf("using consul image = %s", image) +// tfVars["consul_image"] = image +// if c.enterprise { +// license := os.Getenv("CONSUL_LICENSE") +// require.True(t, license != "", "CONSUL_LICENSE not found but is required for enterprise tests") - // Pass the license via environment variable to help ensure it is not logged. - tfEnvVars["TF_VAR_consul_license"] = license - } +// // Pass the license via environment variable to help ensure it is not logged. +// tfEnvVars["TF_VAR_consul_license"] = license +// } - applyOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: initOptions.TerraformDir, - Vars: tfVars, - NoColor: true, - EnvVars: tfEnvVars, - }) +// applyOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ +// TerraformDir: initOptions.TerraformDir, +// Vars: tfVars, +// NoColor: true, +// EnvVars: tfEnvVars, +// }) - t.Cleanup(func() { - if cfg.NoCleanupOnFailure && t.Failed() { - logger.Log(t, "skipping resource cleanup because -no-cleanup-on-failure=true") - } else { - terraform.Destroy(t, applyOptions) - } - }) +// t.Cleanup(func() { +// if cfg.NoCleanupOnFailure && t.Failed() { +// logger.Log(t, "skipping resource cleanup because -no-cleanup-on-failure=true") +// } else { +// terraform.Destroy(t, applyOptions) +// } +// }) - terraform.Apply(t, applyOptions) - - // Wait for consul server to be up. - var consulServerTaskARN string - retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 10 * time.Second}, t, func(r *retry.R) { - taskListOut, err := shell.RunCommandAndGetOutputE(t, shell.Command{ - Command: "aws", - Args: []string{ - "ecs", - "list-tasks", - "--region", - cfg.Region, - "--cluster", - c.ecsClusterARN, - "--family", - fmt.Sprintf("consul_server_%s", randomSuffix), - }, - }) - r.Check(err) - - var tasks listTasksResponse - r.Check(json.Unmarshal([]byte(taskListOut), &tasks)) - if len(tasks.TaskARNs) != 1 { - r.Errorf("expected 1 task, got %d", len(tasks.TaskARNs)) - return - } +// terraform.Apply(t, applyOptions) + +// // Wait for consul server to be up. +// var consulServerTaskARN string +// retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 10 * time.Second}, t, func(r *retry.R) { +// taskListOut, err := shell.RunCommandAndGetOutputE(t, shell.Command{ +// Command: "aws", +// Args: []string{ +// "ecs", +// "list-tasks", +// "--region", +// cfg.Region, +// "--cluster", +// c.ecsClusterARN, +// "--family", +// fmt.Sprintf("consul_server_%s", randomSuffix), +// }, +// }) +// r.Check(err) + +// var tasks listTasksResponse +// r.Check(json.Unmarshal([]byte(taskListOut), &tasks)) +// if len(tasks.TaskARNs) != 1 { +// r.Errorf("expected 1 task, got %d", len(tasks.TaskARNs)) +// return +// } - consulServerTaskARN = tasks.TaskARNs[0] - }) +// consulServerTaskARN = tasks.TaskARNs[0] +// }) - var controllerTaskID string - if c.secure { - retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { - taskListOut := shell.RunCommandAndGetOutput(t, shell.Command{ - Command: "aws", - Args: []string{ - "ecs", - "list-tasks", - "--region", - cfg.Region, - "--cluster", - c.ecsClusterARN, - "--family", - fmt.Sprintf("%s-consul-ecs-controller", randomSuffix), - }, - }) - - var tasks listTasksResponse - require.NoError(r, json.Unmarshal([]byte(taskListOut), &tasks)) - require.Len(r, tasks.TaskARNs, 1) - controllerTaskARN := tasks.TaskARNs[0] - arnParts := strings.Split(controllerTaskARN, "/") - controllerTaskID = arnParts[len(arnParts)-1] - }) - - // Check controller logs to see if the anonymous token gets configured. This should - // indicate that the controller has created the service auth method, policies and roles. - retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { - appLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, controllerTaskID, "consul-ecs-controller") - require.NoError(r, err) - - logMsg := "Successfully configured the anonymous token" - appLogs = appLogs.Filter(logMsg) - require.Len(r, appLogs, 1) - require.Contains(r, appLogs[0].Message, logMsg) - }) - } +// var controllerTaskID string +// if c.secure { +// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { +// taskListOut := shell.RunCommandAndGetOutput(t, shell.Command{ +// Command: "aws", +// Args: []string{ +// "ecs", +// "list-tasks", +// "--region", +// cfg.Region, +// "--cluster", +// c.ecsClusterARN, +// "--family", +// fmt.Sprintf("%s-consul-ecs-controller", randomSuffix), +// }, +// }) + +// var tasks listTasksResponse +// require.NoError(r, json.Unmarshal([]byte(taskListOut), &tasks)) +// require.Len(r, tasks.TaskARNs, 1) +// controllerTaskARN := tasks.TaskARNs[0] +// arnParts := strings.Split(controllerTaskARN, "/") +// controllerTaskID = arnParts[len(arnParts)-1] +// }) + +// // Check controller logs to see if the anonymous token gets configured. This should +// // indicate that the controller has created the service auth method, policies and roles. +// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { +// appLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, controllerTaskID, "consul-ecs-controller") +// require.NoError(r, err) + +// logMsg := "Successfully configured the anonymous token" +// appLogs = appLogs.Filter(logMsg) +// require.Len(r, appLogs, 1) +// require.Contains(r, appLogs[0].Message, logMsg) +// }) +// } - // Wait for both tasks to be registered in Consul. - retry.RunWith(&retry.Timer{Timeout: 6 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { - out, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", `/bin/sh -c "consul catalog services"`) - r.Check(err) - if !strings.Contains(out, fmt.Sprintf("%s_%s", serverServiceName, randomSuffix)) || - !strings.Contains(out, fmt.Sprintf("%s_%s", clientServiceName, randomSuffix)) { - r.Errorf("services not yet registered, got %q", out) - } - }) +// // Wait for both tasks to be registered in Consul. +// retry.RunWith(&retry.Timer{Timeout: 6 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { +// out, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", `/bin/sh -c "consul catalog services"`) +// r.Check(err) +// if !strings.Contains(out, fmt.Sprintf("%s_%s", serverServiceName, randomSuffix)) || +// !strings.Contains(out, fmt.Sprintf("%s_%s", clientServiceName, randomSuffix)) { +// r.Errorf("services not yet registered, got %q", out) +// } +// }) - // Wait for passing health check for test_server and test_client - tokenHeader := "" - if c.secure { - tokenHeader = `-H "X-Consul-Token: $CONSUL_HTTP_TOKEN"` - } +// // Wait for passing health check for test_server and test_client +// tokenHeader := "" +// if c.secure { +// tokenHeader = `-H "X-Consul-Token: $CONSUL_HTTP_TOKEN"` +// } - // Wait for passing health check for `serverServiceName` and `clientServiceName`. - // `clientServiceName` has a check synced from ECS and a consul-dataplane check. - // We check if both of them are in a passing state. - retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { - out, err := helpers.ExecuteRemoteCommand( - t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", - fmt.Sprintf(`/bin/sh -c 'curl %s localhost:8500/v1/health/checks/%s_%s'`, tokenHeader, clientServiceName, randomSuffix), - ) - r.Check(err) - - statusRegex := regexp.MustCompile(`"Status"\s*:\s*"passing"`) - if statusRegex.FindAllString(out, 2) == nil { - r.Errorf("Check status not yet passing") - } - }) +// // Wait for passing health check for `serverServiceName` and `clientServiceName`. +// // `clientServiceName` has a check synced from ECS and a consul-dataplane check. +// // We check if both of them are in a passing state. +// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { +// out, err := helpers.ExecuteRemoteCommand( +// t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", +// fmt.Sprintf(`/bin/sh -c 'curl %s localhost:8500/v1/health/checks/%s_%s'`, tokenHeader, clientServiceName, randomSuffix), +// ) +// r.Check(err) + +// statusRegex := regexp.MustCompile(`"Status"\s*:\s*"passing"`) +// if statusRegex.FindAllString(out, 2) == nil { +// r.Errorf("Check status not yet passing") +// } +// }) - // `serverServiceName` has a backing consul-dataplane check. - retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { - out, err := helpers.ExecuteRemoteCommand( - t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", - fmt.Sprintf(`/bin/sh -c 'curl %s localhost:8500/v1/health/checks/%s_%s'`, tokenHeader, serverServiceName, randomSuffix), - ) - r.Check(err) - - statusRegex := regexp.MustCompile(`"Status"\s*:\s*"passing"`) - if statusRegex.FindAllString(out, 1) == nil { - r.Errorf("Check status not yet passing") - } - }) +// // `serverServiceName` has a backing consul-dataplane check. +// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { +// out, err := helpers.ExecuteRemoteCommand( +// t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", +// fmt.Sprintf(`/bin/sh -c 'curl %s localhost:8500/v1/health/checks/%s_%s'`, tokenHeader, serverServiceName, randomSuffix), +// ) +// r.Check(err) + +// statusRegex := regexp.MustCompile(`"Status"\s*:\s*"passing"`) +// if statusRegex.FindAllString(out, 1) == nil { +// r.Errorf("Check status not yet passing") +// } +// }) - // Use aws exec to curl between the apps. - taskListOut := shell.RunCommandAndGetOutput(t, shell.Command{ - Command: "aws", - Args: []string{ - "ecs", - "list-tasks", - "--region", - cfg.Region, - "--cluster", - c.ecsClusterARN, - "--family", - fmt.Sprintf("Test_Client_%s", randomSuffix), - }, - }) +// // Use aws exec to curl between the apps. +// taskListOut := shell.RunCommandAndGetOutput(t, shell.Command{ +// Command: "aws", +// Args: []string{ +// "ecs", +// "list-tasks", +// "--region", +// cfg.Region, +// "--cluster", +// c.ecsClusterARN, +// "--family", +// fmt.Sprintf("Test_Client_%s", randomSuffix), +// }, +// }) - var tasks listTasksResponse - require.NoError(t, json.Unmarshal([]byte(taskListOut), &tasks)) - require.Len(t, tasks.TaskARNs, 1) - testClientTaskARN := tasks.TaskARNs[0] - arnParts := strings.Split(testClientTaskARN, "/") - testClientTaskID := arnParts[len(arnParts)-1] - - // Create an intention. - if c.secure { - // First check that connection between apps is unsuccessful. - retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { - curlOut, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, testClientTaskARN, "basic", `/bin/sh -c "curl localhost:1234"`) - r.Check(err) - if !strings.Contains(curlOut, `curl: (52) Empty reply from server`) { - r.Errorf("response was unexpected: %q", curlOut) - } - }) - retry.RunWith(&retry.Timer{Timeout: 6 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { - consulCmd := fmt.Sprintf(`/bin/sh -c "consul intention create %s_%s %s_%s"`, clientServiceName, randomSuffix, serverServiceName, randomSuffix) - _, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", consulCmd) - r.Check(err) - }) - } +// var tasks listTasksResponse +// require.NoError(t, json.Unmarshal([]byte(taskListOut), &tasks)) +// require.Len(t, tasks.TaskARNs, 1) +// testClientTaskARN := tasks.TaskARNs[0] +// arnParts := strings.Split(testClientTaskARN, "/") +// testClientTaskID := arnParts[len(arnParts)-1] + +// // Create an intention. +// if c.secure { +// // First check that connection between apps is unsuccessful. +// retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { +// curlOut, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, testClientTaskARN, "basic", `/bin/sh -c "curl localhost:1234"`) +// r.Check(err) +// if !strings.Contains(curlOut, `curl: (52) Empty reply from server`) { +// r.Errorf("response was unexpected: %q", curlOut) +// } +// }) +// retry.RunWith(&retry.Timer{Timeout: 6 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { +// consulCmd := fmt.Sprintf(`/bin/sh -c "consul intention create %s_%s %s_%s"`, clientServiceName, randomSuffix, serverServiceName, randomSuffix) +// _, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", consulCmd) +// r.Check(err) +// }) +// } - retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { - curlOut, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, testClientTaskARN, "basic", `/bin/sh -c "curl localhost:1234"`) - r.Check(err) - if !strings.Contains(curlOut, `"code": 200`) { - r.Errorf("response was unexpected: %q", curlOut) - } - }) +// retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { +// curlOut, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, testClientTaskARN, "basic", `/bin/sh -c "curl localhost:1234"`) +// r.Check(err) +// if !strings.Contains(curlOut, `"code": 200`) { +// r.Errorf("response was unexpected: %q", curlOut) +// } +// }) - // Validate graceful shutdown behavior. We check the client app can reach its upstream after the task is stopped. - // This relies on a couple of helpers: - // * a custom entrypoint for the client app that keeps it running for 10s into Task shutdown, and - // * an additional "shutdown-monitor" container that makes requests to the client app - // Since this is timing dependent, we check logs after the fact to validate when the containers exited. - shell.RunCommandAndGetOutput(t, shell.Command{ - Command: "aws", - Args: []string{ - "ecs", - "stop-task", - "--region", cfg.Region, - "--cluster", c.ecsClusterARN, - "--task", testClientTaskARN, - "--reason", "Stopped to validate graceful shutdown in acceptance tests", - }, - }) +// // Validate graceful shutdown behavior. We check the client app can reach its upstream after the task is stopped. +// // This relies on a couple of helpers: +// // * a custom entrypoint for the client app that keeps it running for 10s into Task shutdown, and +// // * an additional "shutdown-monitor" container that makes requests to the client app +// // Since this is timing dependent, we check logs after the fact to validate when the containers exited. +// shell.RunCommandAndGetOutput(t, shell.Command{ +// Command: "aws", +// Args: []string{ +// "ecs", +// "stop-task", +// "--region", cfg.Region, +// "--cluster", c.ecsClusterARN, +// "--task", testClientTaskARN, +// "--reason", "Stopped to validate graceful shutdown in acceptance tests", +// }, +// }) - // Wait for the task to stop (~30 seconds) - retry.RunWith(&retry.Timer{Timeout: 1 * time.Minute, Wait: 10 * time.Second}, t, func(r *retry.R) { - describeTasksOut, err := shell.RunCommandAndGetOutputE(t, shell.Command{ - Command: "aws", - Args: []string{ - "ecs", - "describe-tasks", - "--region", cfg.Region, - "--cluster", c.ecsClusterARN, - "--task", testClientTaskARN, - }, - }) - r.Check(err) - - var describeTasks ecs.DescribeTasksOutput - r.Check(json.Unmarshal([]byte(describeTasksOut), &describeTasks)) - require.Len(r, describeTasks.Tasks, 1) - require.NotEqual(r, "RUNNING", describeTasks.Tasks[0].LastStatus) - }) +// // Wait for the task to stop (~30 seconds) +// retry.RunWith(&retry.Timer{Timeout: 1 * time.Minute, Wait: 10 * time.Second}, t, func(r *retry.R) { +// describeTasksOut, err := shell.RunCommandAndGetOutputE(t, shell.Command{ +// Command: "aws", +// Args: []string{ +// "ecs", +// "describe-tasks", +// "--region", cfg.Region, +// "--cluster", c.ecsClusterARN, +// "--task", testClientTaskARN, +// }, +// }) +// r.Check(err) + +// var describeTasks ecs.DescribeTasksOutput +// r.Check(json.Unmarshal([]byte(describeTasksOut), &describeTasks)) +// require.Len(r, describeTasks.Tasks, 1) +// require.NotEqual(r, "RUNNING", describeTasks.Tasks[0].LastStatus) +// }) - // Check logs to see that the application ignored the TERM signal and exited about 10s later. - retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { - appLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "basic") - require.NoError(r, err) +// // Check logs to see that the application ignored the TERM signal and exited about 10s later. +// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { +// appLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "basic") +// require.NoError(r, err) - logMsg := "consul-ecs: received sigterm. waiting 10s before terminating application." - appLogs = appLogs.Filter(logMsg) - require.Len(r, appLogs, 1) - require.Contains(r, appLogs[0].Message, logMsg) - }) +// logMsg := "consul-ecs: received sigterm. waiting 10s before terminating application." +// appLogs = appLogs.Filter(logMsg) +// require.Len(r, appLogs, 1) +// require.Contains(r, appLogs[0].Message, logMsg) +// }) - // Check that the Envoy entrypoint received the sigterm. - retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { - envoyLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "consul-dataplane") - require.NoError(r, err) +// // Check that the Envoy entrypoint received the sigterm. +// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { +// envoyLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "consul-dataplane") +// require.NoError(r, err) - logMsg := "consul-ecs: waiting for application container(s) to stop" - envoyLogs = envoyLogs.Filter(logMsg) - require.GreaterOrEqual(r, len(envoyLogs), 1) - require.Contains(r, envoyLogs[0].Message, logMsg) - }) +// logMsg := "consul-ecs: waiting for application container(s) to stop" +// envoyLogs = envoyLogs.Filter(logMsg) +// require.GreaterOrEqual(r, len(envoyLogs), 1) +// require.Contains(r, envoyLogs[0].Message, logMsg) +// }) - // Retrieve "shutdown-monitor" logs to check outgoing requests succeeded. - retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { - monitorLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "shutdown-monitor") - require.NoError(r, err) - - // Check how long after shutdown the upstream was reachable. - upstreamOkLogs := monitorLogs.Filter( - "Signal received: signal=terminated", - "upstream: [OK] GET http://localhost:1234 (200)", - ) - // The client app is configured to run for about 10 seconds after Task shutdown. - require.GreaterOrEqual(r, len(upstreamOkLogs.Filter("upstream: [OK]")), 7) - require.GreaterOrEqual(r, upstreamOkLogs.Duration().Seconds(), 8.0) - - // Double-check the application was still functional for about 10 seconds into Task shutdown. - // The FakeService makes requests to the upstream, so this further validates Envoy allows outgoing requests. - applicationOkLogs := monitorLogs.Filter( - "Signal received: signal=terminated", - "application: [OK] GET http://localhost:9090 (200)", - ) - require.GreaterOrEqual(r, len(applicationOkLogs.Filter("application: [OK]")), 7) - require.GreaterOrEqual(r, applicationOkLogs.Duration().Seconds(), 8.0) - }) +// // Retrieve "shutdown-monitor" logs to check outgoing requests succeeded. +// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { +// monitorLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "shutdown-monitor") +// require.NoError(r, err) + +// // Check how long after shutdown the upstream was reachable. +// upstreamOkLogs := monitorLogs.Filter( +// "Signal received: signal=terminated", +// "upstream: [OK] GET http://localhost:1234 (200)", +// ) +// // The client app is configured to run for about 10 seconds after Task shutdown. +// require.GreaterOrEqual(r, len(upstreamOkLogs.Filter("upstream: [OK]")), 7) +// require.GreaterOrEqual(r, upstreamOkLogs.Duration().Seconds(), 8.0) + +// // Double-check the application was still functional for about 10 seconds into Task shutdown. +// // The FakeService makes requests to the upstream, so this further validates Envoy allows outgoing requests. +// applicationOkLogs := monitorLogs.Filter( +// "Signal received: signal=terminated", +// "application: [OK] GET http://localhost:9090 (200)", +// ) +// require.GreaterOrEqual(r, len(applicationOkLogs.Filter("application: [OK]")), 7) +// require.GreaterOrEqual(r, applicationOkLogs.Duration().Seconds(), 8.0) +// }) - if c.secure { - retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { - // Validate that the controller cleans up the token for the failed task - syncLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, controllerTaskID, "consul-ecs-controller") - require.NoError(r, err) - syncLogs = syncLogs.Filter("token deleted successfully") - require.GreaterOrEqual(r, len(syncLogs), 1) - }) - } +// if c.secure { +// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { +// // Validate that the controller cleans up the token for the failed task +// syncLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, controllerTaskID, "consul-ecs-controller") +// require.NoError(r, err) +// syncLogs = syncLogs.Filter("token deleted successfully") +// require.GreaterOrEqual(r, len(syncLogs), 1) +// }) +// } - logger.Log(t, "Test successful!") - }) - } -} +// logger.Log(t, "Test successful!") +// }) +// } +// } -type listTasksResponse struct { - TaskARNs []string `json:"taskArns"` -} +// type listTasksResponse struct { +// TaskARNs []string `json:"taskArns"` +// } From 1a93a7e32c763656d9e8031dfa4234122ac8a849 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 24 Jul 2023 23:15:01 +0530 Subject: [PATCH 16/27] Fix all tests --- .github/workflows/terraform-ci.yml | 24 +- test/acceptance/tests/basic/basic_test.go | 644 +++++++++++----------- 2 files changed, 332 insertions(+), 336 deletions(-) diff --git a/.github/workflows/terraform-ci.yml b/.github/workflows/terraform-ci.yml index 322f497b..44c490b3 100644 --- a/.github/workflows/terraform-ci.yml +++ b/.github/workflows/terraform-ci.yml @@ -107,11 +107,9 @@ jobs: name: # - acceptance-1.15-FARGATE-HCP # - acceptance-1.14-FARGATE-HCP - # - acceptance-1.13-FARGATE - # - acceptance-1.15-EC2 - # - acceptance-1.14-EC2 # - acceptance-1.13-EC2-HCP - acceptance-1.16-FARGATE + - acceptance-1.16-EC2 include: # - name: acceptance-1.15-FARGATE-HCP # consul-version: '1.15.4' @@ -123,29 +121,19 @@ jobs: # enable-hcp: true # launch-type: FARGATE - # - name: acceptance-1.13-FARGATE - # consul-version: '1.13.9' - # enable-hcp: false - # launch-type: FARGATE - - # - name: acceptance-1.15-EC2 - # consul-version: '1.15.4' - # enable-hcp: false - # launch-type: EC2 - - # - name: acceptance-1.14-EC2 - # consul-version: '1.14.8' - # enable-hcp: false - # launch-type: EC2 - # - name: acceptance-1.13-EC2-HCP # consul-version: '1.13.8' # enable-hcp: true # launch-type: EC2 + - name: acceptance-1.16-FARGATE consul-version: '1.16.0' enable-hcp: false launch-type: FARGATE + - name: acceptance-1.16-EC2 + consul-version: '1.16.0' + enable-hcp: false + launch-type: EC2 steps: - name: Checkout diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index 1318515c..dd66f0d5 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -5,15 +5,23 @@ package basic import ( "context" + "encoding/json" "fmt" + "os" "regexp" "strings" "testing" + "time" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ecs" "github.com/aws/aws-sdk-go-v2/service/iam" + "github.com/gruntwork-io/terratest/modules/random" + "github.com/gruntwork-io/terratest/modules/shell" "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/hashicorp/serf/testutil/retry" + "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/framework/helpers" + "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/framework/logger" "github.com/stretchr/testify/require" ) @@ -909,353 +917,353 @@ func TestValidation_RolePath(t *testing.T) { // } // } -// func TestBasic(t *testing.T) { -// t.Parallel() +func TestBasic(t *testing.T) { + t.Parallel() -// cases := []struct { -// secure bool -// enterprise bool -// stateFile string -// ecsClusterARN string -// datacenter string -// }{ -// {secure: false}, -// {secure: true}, -// {secure: true, enterprise: true}, -// } + cases := []struct { + secure bool + enterprise bool + stateFile string + ecsClusterARN string + datacenter string + }{ + {secure: false}, + {secure: true}, + {secure: true, enterprise: true}, + } -// cfg := suite.Config() -// require.GreaterOrEqual(t, len(cfg.ECSClusterARNs), len(cases), -// "TestBasic requires %d ECS clusters. Update setup-terraform and re-run.", len(cases), -// ) + cfg := suite.Config() + require.GreaterOrEqual(t, len(cfg.ECSClusterARNs), len(cases), + "TestBasic requires %d ECS clusters. Update setup-terraform and re-run.", len(cases), + ) -// initOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ -// TerraformDir: "./terraform/basic-install", -// NoColor: true, -// }) -// terraform.Init(t, initOptions) + initOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: "./terraform/basic-install", + NoColor: true, + }) + terraform.Init(t, initOptions) -// for i, c := range cases { -// c := c + for i, c := range cases { + c := c -// // To support running in parallel, each test case should have: -// // - a unique cluster -// // - a unique Terrform state file to isolate resources -// // - a unique datacenter within the VPC to avoid conflicts in CloudMap namespaces -// // If more clusters are needed, update the cluster count in setup-terraform -// c.ecsClusterARN = cfg.ECSClusterARNs[i] -// c.datacenter = fmt.Sprintf("dc%d", i) -// c.stateFile = fmt.Sprintf("terraform-%d.tfstate", i) + // To support running in parallel, each test case should have: + // - a unique cluster + // - a unique Terrform state file to isolate resources + // - a unique datacenter within the VPC to avoid conflicts in CloudMap namespaces + // If more clusters are needed, update the cluster count in setup-terraform + c.ecsClusterARN = cfg.ECSClusterARNs[i] + c.datacenter = fmt.Sprintf("dc%d", i) + c.stateFile = fmt.Sprintf("terraform-%d.tfstate", i) -// t.Run(fmt.Sprintf("secure: %t,enterprise: %t", c.secure, c.enterprise), func(t *testing.T) { -// t.Parallel() + t.Run(fmt.Sprintf("secure: %t,enterprise: %t", c.secure, c.enterprise), func(t *testing.T) { + t.Parallel() -// randomSuffix := strings.ToLower(random.UniqueId()) + randomSuffix := strings.ToLower(random.UniqueId()) -// tfEnvVars := map[string]string{ -// // Use a unique state file for each parallel invocation of Terraform. -// "TF_CLI_ARGS": fmt.Sprintf("-state=%s -state-out=%s", c.stateFile, c.stateFile), -// } + tfEnvVars := map[string]string{ + // Use a unique state file for each parallel invocation of Terraform. + "TF_CLI_ARGS": fmt.Sprintf("-state=%s -state-out=%s", c.stateFile, c.stateFile), + } -// tfVars := cfg.TFVars("route_table_ids", "ecs_cluster_arns") -// tfVars["secure"] = c.secure -// tfVars["suffix"] = randomSuffix -// tfVars["ecs_cluster_arn"] = c.ecsClusterARN -// tfVars["consul_datacenter"] = c.datacenter -// clientServiceName := "test_client" - -// serverServiceName := "test_server" -// if c.secure { -// // This uses the explicitly passed service name rather than the task's family name. -// serverServiceName = "custom_test_server" -// } -// tfVars["server_service_name"] = serverServiceName + tfVars := cfg.TFVars("route_table_ids", "ecs_cluster_arns") + tfVars["secure"] = c.secure + tfVars["suffix"] = randomSuffix + tfVars["ecs_cluster_arn"] = c.ecsClusterARN + tfVars["consul_datacenter"] = c.datacenter + clientServiceName := "test_client" + + serverServiceName := "test_server" + if c.secure { + // This uses the explicitly passed service name rather than the task's family name. + serverServiceName = "custom_test_server" + } + tfVars["server_service_name"] = serverServiceName -// image := cfg.ConsulImageURI(c.enterprise) -// t.Logf("using consul image = %s", image) -// tfVars["consul_image"] = image -// if c.enterprise { -// license := os.Getenv("CONSUL_LICENSE") -// require.True(t, license != "", "CONSUL_LICENSE not found but is required for enterprise tests") + image := cfg.ConsulImageURI(c.enterprise) + t.Logf("using consul image = %s", image) + tfVars["consul_image"] = image + if c.enterprise { + license := os.Getenv("CONSUL_LICENSE") + require.True(t, license != "", "CONSUL_LICENSE not found but is required for enterprise tests") -// // Pass the license via environment variable to help ensure it is not logged. -// tfEnvVars["TF_VAR_consul_license"] = license -// } + // Pass the license via environment variable to help ensure it is not logged. + tfEnvVars["TF_VAR_consul_license"] = license + } -// applyOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ -// TerraformDir: initOptions.TerraformDir, -// Vars: tfVars, -// NoColor: true, -// EnvVars: tfEnvVars, -// }) + applyOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: initOptions.TerraformDir, + Vars: tfVars, + NoColor: true, + EnvVars: tfEnvVars, + }) -// t.Cleanup(func() { -// if cfg.NoCleanupOnFailure && t.Failed() { -// logger.Log(t, "skipping resource cleanup because -no-cleanup-on-failure=true") -// } else { -// terraform.Destroy(t, applyOptions) -// } -// }) + t.Cleanup(func() { + if cfg.NoCleanupOnFailure && t.Failed() { + logger.Log(t, "skipping resource cleanup because -no-cleanup-on-failure=true") + } else { + terraform.Destroy(t, applyOptions) + } + }) -// terraform.Apply(t, applyOptions) - -// // Wait for consul server to be up. -// var consulServerTaskARN string -// retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 10 * time.Second}, t, func(r *retry.R) { -// taskListOut, err := shell.RunCommandAndGetOutputE(t, shell.Command{ -// Command: "aws", -// Args: []string{ -// "ecs", -// "list-tasks", -// "--region", -// cfg.Region, -// "--cluster", -// c.ecsClusterARN, -// "--family", -// fmt.Sprintf("consul_server_%s", randomSuffix), -// }, -// }) -// r.Check(err) - -// var tasks listTasksResponse -// r.Check(json.Unmarshal([]byte(taskListOut), &tasks)) -// if len(tasks.TaskARNs) != 1 { -// r.Errorf("expected 1 task, got %d", len(tasks.TaskARNs)) -// return -// } - -// consulServerTaskARN = tasks.TaskARNs[0] -// }) + terraform.Apply(t, applyOptions) + + // Wait for consul server to be up. + var consulServerTaskARN string + retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 10 * time.Second}, t, func(r *retry.R) { + taskListOut, err := shell.RunCommandAndGetOutputE(t, shell.Command{ + Command: "aws", + Args: []string{ + "ecs", + "list-tasks", + "--region", + cfg.Region, + "--cluster", + c.ecsClusterARN, + "--family", + fmt.Sprintf("consul_server_%s", randomSuffix), + }, + }) + r.Check(err) + + var tasks listTasksResponse + r.Check(json.Unmarshal([]byte(taskListOut), &tasks)) + if len(tasks.TaskARNs) != 1 { + r.Errorf("expected 1 task, got %d", len(tasks.TaskARNs)) + return + } -// var controllerTaskID string -// if c.secure { -// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { -// taskListOut := shell.RunCommandAndGetOutput(t, shell.Command{ -// Command: "aws", -// Args: []string{ -// "ecs", -// "list-tasks", -// "--region", -// cfg.Region, -// "--cluster", -// c.ecsClusterARN, -// "--family", -// fmt.Sprintf("%s-consul-ecs-controller", randomSuffix), -// }, -// }) - -// var tasks listTasksResponse -// require.NoError(r, json.Unmarshal([]byte(taskListOut), &tasks)) -// require.Len(r, tasks.TaskARNs, 1) -// controllerTaskARN := tasks.TaskARNs[0] -// arnParts := strings.Split(controllerTaskARN, "/") -// controllerTaskID = arnParts[len(arnParts)-1] -// }) - -// // Check controller logs to see if the anonymous token gets configured. This should -// // indicate that the controller has created the service auth method, policies and roles. -// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { -// appLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, controllerTaskID, "consul-ecs-controller") -// require.NoError(r, err) - -// logMsg := "Successfully configured the anonymous token" -// appLogs = appLogs.Filter(logMsg) -// require.Len(r, appLogs, 1) -// require.Contains(r, appLogs[0].Message, logMsg) -// }) -// } + consulServerTaskARN = tasks.TaskARNs[0] + }) -// // Wait for both tasks to be registered in Consul. -// retry.RunWith(&retry.Timer{Timeout: 6 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { -// out, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", `/bin/sh -c "consul catalog services"`) -// r.Check(err) -// if !strings.Contains(out, fmt.Sprintf("%s_%s", serverServiceName, randomSuffix)) || -// !strings.Contains(out, fmt.Sprintf("%s_%s", clientServiceName, randomSuffix)) { -// r.Errorf("services not yet registered, got %q", out) -// } -// }) + var controllerTaskID string + if c.secure { + retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { + taskListOut := shell.RunCommandAndGetOutput(t, shell.Command{ + Command: "aws", + Args: []string{ + "ecs", + "list-tasks", + "--region", + cfg.Region, + "--cluster", + c.ecsClusterARN, + "--family", + fmt.Sprintf("%s-consul-ecs-controller", randomSuffix), + }, + }) + + var tasks listTasksResponse + require.NoError(r, json.Unmarshal([]byte(taskListOut), &tasks)) + require.Len(r, tasks.TaskARNs, 1) + controllerTaskARN := tasks.TaskARNs[0] + arnParts := strings.Split(controllerTaskARN, "/") + controllerTaskID = arnParts[len(arnParts)-1] + }) + + // Check controller logs to see if the anonymous token gets configured. This should + // indicate that the controller has created the service auth method, policies and roles. + retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { + appLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, controllerTaskID, "consul-ecs-controller") + require.NoError(r, err) + + logMsg := "Successfully configured the anonymous token" + appLogs = appLogs.Filter(logMsg) + require.Len(r, appLogs, 1) + require.Contains(r, appLogs[0].Message, logMsg) + }) + } -// // Wait for passing health check for test_server and test_client -// tokenHeader := "" -// if c.secure { -// tokenHeader = `-H "X-Consul-Token: $CONSUL_HTTP_TOKEN"` -// } + // Wait for both tasks to be registered in Consul. + retry.RunWith(&retry.Timer{Timeout: 6 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { + out, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", `/bin/sh -c "consul catalog services"`) + r.Check(err) + if !strings.Contains(out, fmt.Sprintf("%s_%s", serverServiceName, randomSuffix)) || + !strings.Contains(out, fmt.Sprintf("%s_%s", clientServiceName, randomSuffix)) { + r.Errorf("services not yet registered, got %q", out) + } + }) -// // Wait for passing health check for `serverServiceName` and `clientServiceName`. -// // `clientServiceName` has a check synced from ECS and a consul-dataplane check. -// // We check if both of them are in a passing state. -// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { -// out, err := helpers.ExecuteRemoteCommand( -// t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", -// fmt.Sprintf(`/bin/sh -c 'curl %s localhost:8500/v1/health/checks/%s_%s'`, tokenHeader, clientServiceName, randomSuffix), -// ) -// r.Check(err) - -// statusRegex := regexp.MustCompile(`"Status"\s*:\s*"passing"`) -// if statusRegex.FindAllString(out, 2) == nil { -// r.Errorf("Check status not yet passing") -// } -// }) + // Wait for passing health check for test_server and test_client + tokenHeader := "" + if c.secure { + tokenHeader = `-H "X-Consul-Token: $CONSUL_HTTP_TOKEN"` + } -// // `serverServiceName` has a backing consul-dataplane check. -// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { -// out, err := helpers.ExecuteRemoteCommand( -// t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", -// fmt.Sprintf(`/bin/sh -c 'curl %s localhost:8500/v1/health/checks/%s_%s'`, tokenHeader, serverServiceName, randomSuffix), -// ) -// r.Check(err) - -// statusRegex := regexp.MustCompile(`"Status"\s*:\s*"passing"`) -// if statusRegex.FindAllString(out, 1) == nil { -// r.Errorf("Check status not yet passing") -// } -// }) + // Wait for passing health check for `serverServiceName` and `clientServiceName`. + // `clientServiceName` has a check synced from ECS and a consul-dataplane check. + // We check if both of them are in a passing state. + retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { + out, err := helpers.ExecuteRemoteCommand( + t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", + fmt.Sprintf(`/bin/sh -c 'curl %s localhost:8500/v1/health/checks/%s_%s'`, tokenHeader, clientServiceName, randomSuffix), + ) + r.Check(err) + + statusRegex := regexp.MustCompile(`"Status"\s*:\s*"passing"`) + if statusRegex.FindAllString(out, 2) == nil { + r.Errorf("Check status not yet passing") + } + }) -// // Use aws exec to curl between the apps. -// taskListOut := shell.RunCommandAndGetOutput(t, shell.Command{ -// Command: "aws", -// Args: []string{ -// "ecs", -// "list-tasks", -// "--region", -// cfg.Region, -// "--cluster", -// c.ecsClusterARN, -// "--family", -// fmt.Sprintf("Test_Client_%s", randomSuffix), -// }, -// }) + // `serverServiceName` has a backing consul-dataplane check. + retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { + out, err := helpers.ExecuteRemoteCommand( + t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", + fmt.Sprintf(`/bin/sh -c 'curl %s localhost:8500/v1/health/checks/%s_%s'`, tokenHeader, serverServiceName, randomSuffix), + ) + r.Check(err) + + statusRegex := regexp.MustCompile(`"Status"\s*:\s*"passing"`) + if statusRegex.FindAllString(out, 1) == nil { + r.Errorf("Check status not yet passing") + } + }) -// var tasks listTasksResponse -// require.NoError(t, json.Unmarshal([]byte(taskListOut), &tasks)) -// require.Len(t, tasks.TaskARNs, 1) -// testClientTaskARN := tasks.TaskARNs[0] -// arnParts := strings.Split(testClientTaskARN, "/") -// testClientTaskID := arnParts[len(arnParts)-1] - -// // Create an intention. -// if c.secure { -// // First check that connection between apps is unsuccessful. -// retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { -// curlOut, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, testClientTaskARN, "basic", `/bin/sh -c "curl localhost:1234"`) -// r.Check(err) -// if !strings.Contains(curlOut, `curl: (52) Empty reply from server`) { -// r.Errorf("response was unexpected: %q", curlOut) -// } -// }) -// retry.RunWith(&retry.Timer{Timeout: 6 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { -// consulCmd := fmt.Sprintf(`/bin/sh -c "consul intention create %s_%s %s_%s"`, clientServiceName, randomSuffix, serverServiceName, randomSuffix) -// _, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", consulCmd) -// r.Check(err) -// }) -// } + // Use aws exec to curl between the apps. + taskListOut := shell.RunCommandAndGetOutput(t, shell.Command{ + Command: "aws", + Args: []string{ + "ecs", + "list-tasks", + "--region", + cfg.Region, + "--cluster", + c.ecsClusterARN, + "--family", + fmt.Sprintf("Test_Client_%s", randomSuffix), + }, + }) -// retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { -// curlOut, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, testClientTaskARN, "basic", `/bin/sh -c "curl localhost:1234"`) -// r.Check(err) -// if !strings.Contains(curlOut, `"code": 200`) { -// r.Errorf("response was unexpected: %q", curlOut) -// } -// }) + var tasks listTasksResponse + require.NoError(t, json.Unmarshal([]byte(taskListOut), &tasks)) + require.Len(t, tasks.TaskARNs, 1) + testClientTaskARN := tasks.TaskARNs[0] + arnParts := strings.Split(testClientTaskARN, "/") + testClientTaskID := arnParts[len(arnParts)-1] + + // Create an intention. + if c.secure { + // First check that connection between apps is unsuccessful. + retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { + curlOut, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, testClientTaskARN, "basic", `/bin/sh -c "curl localhost:1234"`) + r.Check(err) + if !strings.Contains(curlOut, `curl: (52) Empty reply from server`) { + r.Errorf("response was unexpected: %q", curlOut) + } + }) + retry.RunWith(&retry.Timer{Timeout: 6 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { + consulCmd := fmt.Sprintf(`/bin/sh -c "consul intention create %s_%s %s_%s"`, clientServiceName, randomSuffix, serverServiceName, randomSuffix) + _, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, consulServerTaskARN, "consul-server", consulCmd) + r.Check(err) + }) + } -// // Validate graceful shutdown behavior. We check the client app can reach its upstream after the task is stopped. -// // This relies on a couple of helpers: -// // * a custom entrypoint for the client app that keeps it running for 10s into Task shutdown, and -// // * an additional "shutdown-monitor" container that makes requests to the client app -// // Since this is timing dependent, we check logs after the fact to validate when the containers exited. -// shell.RunCommandAndGetOutput(t, shell.Command{ -// Command: "aws", -// Args: []string{ -// "ecs", -// "stop-task", -// "--region", cfg.Region, -// "--cluster", c.ecsClusterARN, -// "--task", testClientTaskARN, -// "--reason", "Stopped to validate graceful shutdown in acceptance tests", -// }, -// }) + retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 20 * time.Second}, t, func(r *retry.R) { + curlOut, err := helpers.ExecuteRemoteCommand(t, cfg, c.ecsClusterARN, testClientTaskARN, "basic", `/bin/sh -c "curl localhost:1234"`) + r.Check(err) + if !strings.Contains(curlOut, `"code": 200`) { + r.Errorf("response was unexpected: %q", curlOut) + } + }) -// // Wait for the task to stop (~30 seconds) -// retry.RunWith(&retry.Timer{Timeout: 1 * time.Minute, Wait: 10 * time.Second}, t, func(r *retry.R) { -// describeTasksOut, err := shell.RunCommandAndGetOutputE(t, shell.Command{ -// Command: "aws", -// Args: []string{ -// "ecs", -// "describe-tasks", -// "--region", cfg.Region, -// "--cluster", c.ecsClusterARN, -// "--task", testClientTaskARN, -// }, -// }) -// r.Check(err) - -// var describeTasks ecs.DescribeTasksOutput -// r.Check(json.Unmarshal([]byte(describeTasksOut), &describeTasks)) -// require.Len(r, describeTasks.Tasks, 1) -// require.NotEqual(r, "RUNNING", describeTasks.Tasks[0].LastStatus) -// }) + // Validate graceful shutdown behavior. We check the client app can reach its upstream after the task is stopped. + // This relies on a couple of helpers: + // * a custom entrypoint for the client app that keeps it running for 10s into Task shutdown, and + // * an additional "shutdown-monitor" container that makes requests to the client app + // Since this is timing dependent, we check logs after the fact to validate when the containers exited. + shell.RunCommandAndGetOutput(t, shell.Command{ + Command: "aws", + Args: []string{ + "ecs", + "stop-task", + "--region", cfg.Region, + "--cluster", c.ecsClusterARN, + "--task", testClientTaskARN, + "--reason", "Stopped to validate graceful shutdown in acceptance tests", + }, + }) -// // Check logs to see that the application ignored the TERM signal and exited about 10s later. -// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { -// appLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "basic") -// require.NoError(r, err) + // Wait for the task to stop (~30 seconds) + retry.RunWith(&retry.Timer{Timeout: 1 * time.Minute, Wait: 10 * time.Second}, t, func(r *retry.R) { + describeTasksOut, err := shell.RunCommandAndGetOutputE(t, shell.Command{ + Command: "aws", + Args: []string{ + "ecs", + "describe-tasks", + "--region", cfg.Region, + "--cluster", c.ecsClusterARN, + "--task", testClientTaskARN, + }, + }) + r.Check(err) + + var describeTasks ecs.DescribeTasksOutput + r.Check(json.Unmarshal([]byte(describeTasksOut), &describeTasks)) + require.Len(r, describeTasks.Tasks, 1) + require.NotEqual(r, "RUNNING", describeTasks.Tasks[0].LastStatus) + }) -// logMsg := "consul-ecs: received sigterm. waiting 10s before terminating application." -// appLogs = appLogs.Filter(logMsg) -// require.Len(r, appLogs, 1) -// require.Contains(r, appLogs[0].Message, logMsg) -// }) + // Check logs to see that the application ignored the TERM signal and exited about 10s later. + retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { + appLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "basic") + require.NoError(r, err) -// // Check that the Envoy entrypoint received the sigterm. -// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { -// envoyLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "consul-dataplane") -// require.NoError(r, err) + logMsg := "consul-ecs: received sigterm. waiting 10s before terminating application." + appLogs = appLogs.Filter(logMsg) + require.Len(r, appLogs, 1) + require.Contains(r, appLogs[0].Message, logMsg) + }) -// logMsg := "consul-ecs: waiting for application container(s) to stop" -// envoyLogs = envoyLogs.Filter(logMsg) -// require.GreaterOrEqual(r, len(envoyLogs), 1) -// require.Contains(r, envoyLogs[0].Message, logMsg) -// }) + // Check that the Envoy entrypoint received the sigterm. + retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { + envoyLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "consul-dataplane") + require.NoError(r, err) -// // Retrieve "shutdown-monitor" logs to check outgoing requests succeeded. -// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { -// monitorLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "shutdown-monitor") -// require.NoError(r, err) - -// // Check how long after shutdown the upstream was reachable. -// upstreamOkLogs := monitorLogs.Filter( -// "Signal received: signal=terminated", -// "upstream: [OK] GET http://localhost:1234 (200)", -// ) -// // The client app is configured to run for about 10 seconds after Task shutdown. -// require.GreaterOrEqual(r, len(upstreamOkLogs.Filter("upstream: [OK]")), 7) -// require.GreaterOrEqual(r, upstreamOkLogs.Duration().Seconds(), 8.0) - -// // Double-check the application was still functional for about 10 seconds into Task shutdown. -// // The FakeService makes requests to the upstream, so this further validates Envoy allows outgoing requests. -// applicationOkLogs := monitorLogs.Filter( -// "Signal received: signal=terminated", -// "application: [OK] GET http://localhost:9090 (200)", -// ) -// require.GreaterOrEqual(r, len(applicationOkLogs.Filter("application: [OK]")), 7) -// require.GreaterOrEqual(r, applicationOkLogs.Duration().Seconds(), 8.0) -// }) + logMsg := "consul-ecs: waiting for application container(s) to stop" + envoyLogs = envoyLogs.Filter(logMsg) + require.GreaterOrEqual(r, len(envoyLogs), 1) + require.Contains(r, envoyLogs[0].Message, logMsg) + }) -// if c.secure { -// retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { -// // Validate that the controller cleans up the token for the failed task -// syncLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, controllerTaskID, "consul-ecs-controller") -// require.NoError(r, err) -// syncLogs = syncLogs.Filter("token deleted successfully") -// require.GreaterOrEqual(r, len(syncLogs), 1) -// }) -// } + // Retrieve "shutdown-monitor" logs to check outgoing requests succeeded. + retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { + monitorLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, testClientTaskID, "shutdown-monitor") + require.NoError(r, err) + + // Check how long after shutdown the upstream was reachable. + upstreamOkLogs := monitorLogs.Filter( + "Signal received: signal=terminated", + "upstream: [OK] GET http://localhost:1234 (200)", + ) + // The client app is configured to run for about 10 seconds after Task shutdown. + require.GreaterOrEqual(r, len(upstreamOkLogs.Filter("upstream: [OK]")), 7) + require.GreaterOrEqual(r, upstreamOkLogs.Duration().Seconds(), 8.0) + + // Double-check the application was still functional for about 10 seconds into Task shutdown. + // The FakeService makes requests to the upstream, so this further validates Envoy allows outgoing requests. + applicationOkLogs := monitorLogs.Filter( + "Signal received: signal=terminated", + "application: [OK] GET http://localhost:9090 (200)", + ) + require.GreaterOrEqual(r, len(applicationOkLogs.Filter("application: [OK]")), 7) + require.GreaterOrEqual(r, applicationOkLogs.Duration().Seconds(), 8.0) + }) -// logger.Log(t, "Test successful!") -// }) -// } -// } + if c.secure { + retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { + // Validate that the controller cleans up the token for the failed task + syncLogs, err := helpers.GetCloudWatchLogEvents(t, cfg, c.ecsClusterARN, controllerTaskID, "consul-ecs-controller") + require.NoError(r, err) + syncLogs = syncLogs.Filter("token deleted successfully") + require.GreaterOrEqual(r, len(syncLogs), 1) + }) + } -// type listTasksResponse struct { -// TaskARNs []string `json:"taskArns"` -// } + logger.Log(t, "Test successful!") + }) + } +} + +type listTasksResponse struct { + TaskARNs []string `json:"taskArns"` +} From d87e130beefb173a3e340c5f47c94970f2c8a250 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Tue, 25 Jul 2023 21:51:03 +0530 Subject: [PATCH 17/27] Fix variable names --- modules/controller/config.tf | 4 ++-- modules/controller/variables.tf | 12 ++++++------ modules/mesh-task/config.tf | 4 ++-- modules/mesh-task/variables.tf | 12 ++++++------ test/acceptance/tests/basic/basic_test.go | 8 ++++---- .../main.tf | 4 ++-- .../test-complete-config.json | 0 .../test-empty-config.json | 0 .../test-invalid-config.json | 0 .../test-partial-config.json | 0 .../main.tf | 4 ++-- .../test-complete-config.json | 0 .../test-empty-config.json | 0 .../test-invalid-config.json | 0 .../test-partial-config.json | 0 15 files changed, 24 insertions(+), 24 deletions(-) rename test/acceptance/tests/basic/terraform/{grpc-tls-config-validate => grpc-config-validate}/main.tf (74%) rename test/acceptance/tests/basic/terraform/{grpc-tls-config-validate => grpc-config-validate}/test-complete-config.json (100%) rename test/acceptance/tests/basic/terraform/{grpc-tls-config-validate => grpc-config-validate}/test-empty-config.json (100%) rename test/acceptance/tests/basic/terraform/{grpc-tls-config-validate => grpc-config-validate}/test-invalid-config.json (100%) rename test/acceptance/tests/basic/terraform/{grpc-tls-config-validate => grpc-config-validate}/test-partial-config.json (100%) rename test/acceptance/tests/basic/terraform/{http-tls-config-validate => http-config-validate}/main.tf (74%) rename test/acceptance/tests/basic/terraform/{http-tls-config-validate => http-config-validate}/test-complete-config.json (100%) rename test/acceptance/tests/basic/terraform/{http-tls-config-validate => http-config-validate}/test-empty-config.json (100%) rename test/acceptance/tests/basic/terraform/{http-tls-config-validate => http-config-validate}/test-invalid-config.json (100%) rename test/acceptance/tests/basic/terraform/{http-tls-config-validate => http-config-validate}/test-partial-config.json (100%) diff --git a/modules/controller/config.tf b/modules/controller/config.tf index b0bd2439..036be062 100644 --- a/modules/controller/config.tf +++ b/modules/controller/config.tf @@ -7,14 +7,14 @@ locals { port = var.tls ? 8501 : 8500 https = var.tls }, - var.http_tls_config + var.http_config ) grpcTLSSettings = merge( { port = var.tls ? 8503 : 8502 }, - var.grpc_tls_config + var.grpc_config ) config = { diff --git a/modules/controller/variables.tf b/modules/controller/variables.tf index 57fa5900..43339997 100644 --- a/modules/controller/variables.tf +++ b/modules/controller/variables.tf @@ -136,7 +136,7 @@ variable "additional_execution_role_policies" { default = [] } -variable "http_tls_config" { +variable "http_config" { type = any default = {} description = <<-EOT @@ -145,15 +145,15 @@ variable "http_tls_config" { EOT validation { - error_message = "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_tls_config." + error_message = "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_config." condition = alltrue([ - for key in keys(var.http_tls_config) : + for key in keys(var.http_config) : contains(["port", "https", "tls", "tlsServerName", "caCertFile"], key) ]) } } -variable "grpc_tls_config" { +variable "grpc_config" { type = any default = {} description = <<-EOT @@ -162,9 +162,9 @@ variable "grpc_tls_config" { EOT validation { - error_message = "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_tls_config." + error_message = "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_config." condition = alltrue([ - for key in keys(var.grpc_tls_config) : + for key in keys(var.grpc_config) : contains(["port", "tls", "tlsServerName", "caCertFile"], key) ]) } diff --git a/modules/mesh-task/config.tf b/modules/mesh-task/config.tf index e5afec21..efd0680d 100644 --- a/modules/mesh-task/config.tf +++ b/modules/mesh-task/config.tf @@ -17,14 +17,14 @@ locals { port = var.tls ? 8501 : 8500 https = var.tls }, - var.http_tls_config + var.http_config ) grpcTLSSettings = merge( { port = var.tls ? 8503 : 8502 }, - var.grpc_tls_config + var.grpc_config ) config = { diff --git a/modules/mesh-task/variables.tf b/modules/mesh-task/variables.tf index 416e9249..120dddf5 100644 --- a/modules/mesh-task/variables.tf +++ b/modules/mesh-task/variables.tf @@ -441,7 +441,7 @@ variable "consul_ecs_config" { } -variable "http_tls_config" { +variable "http_config" { type = any default = {} description = <<-EOT @@ -450,15 +450,15 @@ variable "http_tls_config" { EOT validation { - error_message = "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_tls_config." + error_message = "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_config." condition = alltrue([ - for key in keys(var.http_tls_config) : + for key in keys(var.http_config) : contains(["port", "https", "tls", "tlsServerName", "caCertFile"], key) ]) } } -variable "grpc_tls_config" { +variable "grpc_config" { type = any default = {} description = <<-EOT @@ -467,9 +467,9 @@ variable "grpc_tls_config" { EOT validation { - error_message = "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_tls_config." + error_message = "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_config." condition = alltrue([ - for key in keys(var.grpc_tls_config) : + for key in keys(var.grpc_config) : contains(["port", "tls", "tlsServerName", "caCertFile"], key) ]) } diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index dd66f0d5..20e672a1 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -571,7 +571,7 @@ func TestValidation_HTTPTLSConfig(t *testing.T) { "invalid-config": { configFile: "test-invalid-config.json", errors: []string{ - "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_tls_config.", + "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_config.", }, }, } @@ -592,7 +592,7 @@ func TestValidation_HTTPTLSConfig(t *testing.T) { TerraformDir: terraformOptions.TerraformDir, NoColor: true, Vars: map[string]interface{}{ - "http_tls_config_file": c.configFile, + "http_config_file": c.configFile, }, }) @@ -628,7 +628,7 @@ func TestValidation_GRPCTLSConfig(t *testing.T) { "invalid-config": { configFile: "test-invalid-config.json", errors: []string{ - "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_tls_config.", + "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_config.", }, }, } @@ -649,7 +649,7 @@ func TestValidation_GRPCTLSConfig(t *testing.T) { TerraformDir: terraformOptions.TerraformDir, NoColor: true, Vars: map[string]interface{}{ - "grpc_tls_config_file": c.configFile, + "grpc_config_file": c.configFile, }, }) diff --git a/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/main.tf b/test/acceptance/tests/basic/terraform/grpc-config-validate/main.tf similarity index 74% rename from test/acceptance/tests/basic/terraform/grpc-tls-config-validate/main.tf rename to test/acceptance/tests/basic/terraform/grpc-config-validate/main.tf index 82542a3e..4c5b04d8 100644 --- a/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/grpc-config-validate/main.tf @@ -5,7 +5,7 @@ provider "aws" { region = "us-west-2" } -variable "grpc_tls_config_file" { +variable "grpc_config_file" { type = string } @@ -17,5 +17,5 @@ module "test_client" { }] consul_server_address = "consul.dc1.host" outbound_only = true - grpc_tls_config = jsondecode(file("${path.module}/${var.grpc_tls_config_file}")) + grpc_config = jsondecode(file("${path.module}/${var.grpc_config_file}")) } \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-complete-config.json b/test/acceptance/tests/basic/terraform/grpc-config-validate/test-complete-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-complete-config.json rename to test/acceptance/tests/basic/terraform/grpc-config-validate/test-complete-config.json diff --git a/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-empty-config.json b/test/acceptance/tests/basic/terraform/grpc-config-validate/test-empty-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-empty-config.json rename to test/acceptance/tests/basic/terraform/grpc-config-validate/test-empty-config.json diff --git a/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-invalid-config.json b/test/acceptance/tests/basic/terraform/grpc-config-validate/test-invalid-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-invalid-config.json rename to test/acceptance/tests/basic/terraform/grpc-config-validate/test-invalid-config.json diff --git a/test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-partial-config.json b/test/acceptance/tests/basic/terraform/grpc-config-validate/test-partial-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/grpc-tls-config-validate/test-partial-config.json rename to test/acceptance/tests/basic/terraform/grpc-config-validate/test-partial-config.json diff --git a/test/acceptance/tests/basic/terraform/http-tls-config-validate/main.tf b/test/acceptance/tests/basic/terraform/http-config-validate/main.tf similarity index 74% rename from test/acceptance/tests/basic/terraform/http-tls-config-validate/main.tf rename to test/acceptance/tests/basic/terraform/http-config-validate/main.tf index 0ed7db5a..9b781f5b 100644 --- a/test/acceptance/tests/basic/terraform/http-tls-config-validate/main.tf +++ b/test/acceptance/tests/basic/terraform/http-config-validate/main.tf @@ -5,7 +5,7 @@ provider "aws" { region = "us-west-2" } -variable "http_tls_config_file" { +variable "http_config_file" { type = string } @@ -17,5 +17,5 @@ module "test_client" { }] consul_server_address = "consul.dc1.host" outbound_only = true - http_tls_config = jsondecode(file("${path.module}/${var.http_tls_config_file}")) + http_config = jsondecode(file("${path.module}/${var.http_config_file}")) } \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-complete-config.json b/test/acceptance/tests/basic/terraform/http-config-validate/test-complete-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/http-tls-config-validate/test-complete-config.json rename to test/acceptance/tests/basic/terraform/http-config-validate/test-complete-config.json diff --git a/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-empty-config.json b/test/acceptance/tests/basic/terraform/http-config-validate/test-empty-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/http-tls-config-validate/test-empty-config.json rename to test/acceptance/tests/basic/terraform/http-config-validate/test-empty-config.json diff --git a/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-invalid-config.json b/test/acceptance/tests/basic/terraform/http-config-validate/test-invalid-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/http-tls-config-validate/test-invalid-config.json rename to test/acceptance/tests/basic/terraform/http-config-validate/test-invalid-config.json diff --git a/test/acceptance/tests/basic/terraform/http-tls-config-validate/test-partial-config.json b/test/acceptance/tests/basic/terraform/http-config-validate/test-partial-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/http-tls-config-validate/test-partial-config.json rename to test/acceptance/tests/basic/terraform/http-config-validate/test-partial-config.json From e6587259cdb5d16629f75f4ca57fe4428477ea32 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Tue, 25 Jul 2023 21:52:46 +0530 Subject: [PATCH 18/27] Fix var names --- modules/controller/config.tf | 8 ++++---- modules/mesh-task/config.tf | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/controller/config.tf b/modules/controller/config.tf index 036be062..a04c0458 100644 --- a/modules/controller/config.tf +++ b/modules/controller/config.tf @@ -2,7 +2,7 @@ # SPDX-License-Identifier: MPL-2.0 locals { - httpTLSSettings = merge( + httpSettings = merge( { port = var.tls ? 8501 : 8500 https = var.tls @@ -10,7 +10,7 @@ locals { var.http_config ) - grpcTLSSettings = merge( + grpcSettings = merge( { port = var.tls ? 8503 : 8502 }, @@ -32,8 +32,8 @@ locals { tlsServerName = var.tls_server_name caCertFile = var.ca_cert_file } - http = local.httpTLSSettings - grpc = local.grpcTLSSettings + http = local.httpSettings + grpc = local.grpcSettings } } diff --git a/modules/mesh-task/config.tf b/modules/mesh-task/config.tf index efd0680d..2ab96b58 100644 --- a/modules/mesh-task/config.tf +++ b/modules/mesh-task/config.tf @@ -12,7 +12,7 @@ locals { method = var.service_token_auth_method_name } : null - httpTLSSettings = merge( + httpSettings = merge( { port = var.tls ? 8501 : 8500 https = var.tls @@ -20,7 +20,7 @@ locals { var.http_config ) - grpcTLSSettings = merge( + grpcSettings = merge( { port = var.tls ? 8503 : 8502 }, @@ -58,8 +58,8 @@ locals { tlsServerName = var.tls_server_name caCertFile = var.ca_cert_file } - http = local.httpTLSSettings - grpc = local.grpcTLSSettings + http = local.httpSettings + grpc = local.grpcSettings } } From 96ed8f703d7408655b547a5294e61cdedc407040 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Wed, 26 Jul 2023 09:03:42 +0530 Subject: [PATCH 19/27] Address comments --- modules/controller/README.md | 2 +- modules/mesh-task/variables.tf | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/controller/README.md b/modules/controller/README.md index 4a7493e9..3e7495b6 100644 --- a/modules/controller/README.md +++ b/modules/controller/README.md @@ -2,4 +2,4 @@ This module deploys a Consul ECS controller for managing tokens and consul services for tasks on the Consul service mesh. -See https://www.consul.io/docs/ecs for additional documentation. +See https://developer.hashicorp.com/consul/docs/ecs for additional documentation. diff --git a/modules/mesh-task/variables.tf b/modules/mesh-task/variables.tf index 120dddf5..ace49b48 100644 --- a/modules/mesh-task/variables.tf +++ b/modules/mesh-task/variables.tf @@ -155,7 +155,7 @@ variable "consul_ecs_image" { } variable "consul_dataplane_image" { - description = "consul-dataplane docker image." + description = "consul-dataplane Docker image." type = string default = "docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.3-dev" } @@ -171,7 +171,7 @@ variable "envoy_public_listener_port" { } validation { - error_message = "The envoy_public_listener_port must not conflict with the following ports that are reserved for Consul and Envoy: 8300, 8301, 8302, 8500, 8501, 8502, 8600, 19000, 10000." + error_message = "The envoy_public_listener_port must not conflict with the following ports that are reserved for Consul and Envoy: 8300, 8301, 8302, 8500, 8501, 8502, 8600, 10000, 19000." condition = !contains([ 8300, // consul rpc port 8301, // consul lan serf @@ -180,8 +180,8 @@ variable "envoy_public_listener_port" { 8501, // consul https 8502, // consul grpc 8600, // consul dns - 19000, // envoy admin port 10000, // consul-ecs-control-plane health check port + 19000, // envoy admin port ], var.envoy_public_listener_port) } } @@ -197,7 +197,7 @@ variable "envoy_readiness_port" { } validation { - error_message = "The envoy_readiness_port must not conflict with the following ports that are reserved for Consul and Envoy: 8300, 8301, 8302, 8500, 8501, 8502, 8600, 19000, 10000." + error_message = "The envoy_readiness_port must not conflict with the following ports that are reserved for Consul and Envoy: 8300, 8301, 8302, 8500, 8501, 8502, 8600, 10000, 19000." condition = !contains([ 8300, // consul rpc port 8301, // consul lan serf @@ -206,8 +206,8 @@ variable "envoy_readiness_port" { 8501, // consul https 8502, // consul grpc 8600, // consul dns - 19000, // envoy admin port 10000, // consul-ecs-control-plane health check port + 19000, // envoy admin port ], var.envoy_readiness_port) } } From f90a15a43c22776552d7b14a2ce1378cc3468126 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 31 Jul 2023 15:16:33 +0530 Subject: [PATCH 20/27] Address comments --- .github/workflows/terraform-ci.yml | 1 + modules/controller/config.tf | 3 +- modules/controller/main.tf | 8 +- modules/controller/variables.tf | 35 +- modules/mesh-task/config.tf | 2 +- modules/mesh-task/iam.tf | 4 +- modules/mesh-task/main.tf | 4 +- modules/mesh-task/variables.tf | 32 +- test/acceptance/setup-terraform/main.tf | 4 + test/acceptance/tests/basic/basic_test.go | 899 +---------------- .../basic/terraform/basic-install/main.tf | 6 +- .../admin-partition-validate/main.tf | 2 +- .../consul-ecs-config-validate/main.tf | 2 +- .../test-complete-config.json | 0 .../test-empty-config.json | 0 .../test-invalid-config.json | 0 .../test-partial-config.json | 0 .../envoy-readiness-port-validate/main.tf | 2 +- .../terraform/grpc-config-validate/main.tf | 2 +- .../test-complete-config.json | 0 .../test-empty-config.json | 0 .../test-invalid-config.json | 0 .../test-partial-config.json | 0 .../terraform/http-config-validate/main.tf | 2 +- .../test-complete-config.json | 0 .../test-empty-config.json | 0 .../test-invalid-config.json | 0 .../test-partial-config.json | 0 .../terraform/mesh-gateway-validate/main.tf | 0 .../terraform/pass-app-entrypoint/main.tf | 2 +- .../terraform/pass-existing-iam-roles/main.tf | 4 +- .../main.tf | 4 +- .../public-listener-port-validate/main.tf | 2 +- .../terraform/role-path-validate/main.tf | 2 +- .../terraform/service-name-validate/main.tf | 2 +- .../terraform/upstreams-validate/main.tf | 2 +- .../test-invalid-upstreams.json | 0 .../test-missing-destinationName.json | 0 .../test-missing-localBindPort.json | 0 .../upstreams-validate/test-no-upstreams.json | 0 .../test-valid-upstreams.json | 0 .../terraform/volume-variable/main.tf | 2 +- .../tests/validation/validation_test.go | 906 ++++++++++++++++++ 43 files changed, 966 insertions(+), 968 deletions(-) rename test/acceptance/tests/{basic => validation}/terraform/admin-partition-validate/main.tf (91%) rename test/acceptance/tests/{basic => validation}/terraform/consul-ecs-config-validate/main.tf (90%) rename test/acceptance/tests/{basic => validation}/terraform/consul-ecs-config-validate/test-complete-config.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/consul-ecs-config-validate/test-empty-config.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/consul-ecs-config-validate/test-invalid-config.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/consul-ecs-config-validate/test-partial-config.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/envoy-readiness-port-validate/main.tf (90%) rename test/acceptance/tests/{basic => validation}/terraform/grpc-config-validate/main.tf (90%) rename test/acceptance/tests/{basic => validation}/terraform/grpc-config-validate/test-complete-config.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/grpc-config-validate/test-empty-config.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/grpc-config-validate/test-invalid-config.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/grpc-config-validate/test-partial-config.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/http-config-validate/main.tf (90%) rename test/acceptance/tests/{basic => validation}/terraform/http-config-validate/test-complete-config.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/http-config-validate/test-empty-config.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/http-config-validate/test-invalid-config.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/http-config-validate/test-partial-config.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/mesh-gateway-validate/main.tf (100%) rename test/acceptance/tests/{basic => validation}/terraform/pass-app-entrypoint/main.tf (92%) rename test/acceptance/tests/{basic => validation}/terraform/pass-existing-iam-roles/main.tf (97%) rename test/acceptance/tests/{basic => validation}/terraform/pass-role-additional-policies-validate/main.tf (94%) rename test/acceptance/tests/{basic => validation}/terraform/public-listener-port-validate/main.tf (90%) rename test/acceptance/tests/{basic => validation}/terraform/role-path-validate/main.tf (90%) rename test/acceptance/tests/{basic => validation}/terraform/service-name-validate/main.tf (90%) rename test/acceptance/tests/{basic => validation}/terraform/upstreams-validate/main.tf (91%) rename test/acceptance/tests/{basic => validation}/terraform/upstreams-validate/test-invalid-upstreams.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/upstreams-validate/test-missing-destinationName.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/upstreams-validate/test-missing-localBindPort.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/upstreams-validate/test-no-upstreams.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/upstreams-validate/test-valid-upstreams.json (100%) rename test/acceptance/tests/{basic => validation}/terraform/volume-variable/main.tf (90%) create mode 100644 test/acceptance/tests/validation/validation_test.go diff --git a/.github/workflows/terraform-ci.yml b/.github/workflows/terraform-ci.yml index 44c490b3..52b37020 100644 --- a/.github/workflows/terraform-ci.yml +++ b/.github/workflows/terraform-ci.yml @@ -10,6 +10,7 @@ on: pull_request: branches: - 'main' + - 'ganeshrockz/dataplane-arch-changes' #TODO: Remove this when merging to main - 'release/**' env: CONSUL_LICENSE: ${{ secrets.CONSUL_LICENSE }} diff --git a/modules/controller/config.tf b/modules/controller/config.tf index a04c0458..3b2edd19 100644 --- a/modules/controller/config.tf +++ b/modules/controller/config.tf @@ -25,12 +25,11 @@ locals { } bootstrapDir = "/consul" consulServers = { - hosts = var.consul_server_address + hosts = var.consul_server_hosts skipServerWatch = var.skip_server_watch defaults = { tls = var.tls tlsServerName = var.tls_server_name - caCertFile = var.ca_cert_file } http = local.httpSettings grpc = local.grpcSettings diff --git a/modules/controller/main.tf b/modules/controller/main.tf index daba7124..2c5fb4f8 100644 --- a/modules/controller/main.tf +++ b/modules/controller/main.tf @@ -2,8 +2,8 @@ # SPDX-License-Identifier: MPL-2.0 locals { - https_ca_cert_arn = var.consul_https_ca_cert_arn != "" ? var.consul_https_ca_cert_arn : var.consul_server_ca_cert_arn - grpc_ca_cert_arn = var.consul_grpc_ca_cert_arn != "" ? var.consul_grpc_ca_cert_arn : var.consul_server_ca_cert_arn + https_ca_cert_arn = var.consul_https_ca_cert_arn != "" ? var.consul_https_ca_cert_arn : var.consul_ca_cert_arn + grpc_ca_cert_arn = var.consul_grpc_ca_cert_arn != "" ? var.consul_grpc_ca_cert_arn : var.consul_ca_cert_arn } resource "aws_ecs_service" "this" { @@ -124,14 +124,14 @@ resource "aws_iam_policy" "this_execution" { "${var.consul_bootstrap_token_secret_arn}" ] }, -%{if var.consul_server_ca_cert_arn != ""~} +%{if var.consul_ca_cert_arn != ""~} { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue" ], "Resource": [ - "${var.consul_server_ca_cert_arn}" + "${var.consul_ca_cert_arn}" ] }, %{endif~} diff --git a/modules/controller/variables.tf b/modules/controller/variables.tf index 43339997..d91d331e 100644 --- a/modules/controller/variables.tf +++ b/modules/controller/variables.tf @@ -30,7 +30,7 @@ variable "launch_type" { } variable "consul_bootstrap_token_secret_arn" { - description = "The ARN of the AWS SecretsManager secret containing the token to be used by this controller. The token needs to have at least `acl:write` and `node:write` privileges in Consul." + description = "The ARN of the AWS SecretsManager secret containing the token to be used by this controller. The token needs to have at least `acl:write`, `node:write` and `operator:write`(in case of Consul Enterprise) privileges in Consul." type = string } @@ -47,12 +47,12 @@ variable "iam_role_path" { } variable "subnets" { - description = "Subnets where the controller task should be deployed. If these are private subnets then there must be a NAT gateway for image pulls to work. If these are public subnets then you must also set assign_public_ip for image pulls to work." + description = "Subnet IDs where the controller task should be deployed. If these are private subnets then there must be a NAT gateway for image pulls to work. If these are public subnets then you must also set assign_public_ip for image pulls to work." type = list(string) } -variable "consul_server_address" { - description = "Address of the consul server host" +variable "consul_server_hosts" { + description = "Address of the Consul servers. This can be an IP address, a DNS name, or an `exec=` string specifying a script that outputs IP addresses. Refer to https://github.com/hashicorp/go-netaddrs#summary for details. This variable should not specify the port. Instead, use var.http_config.port and var.grpc_config.port to change the server HTTP and gRPC ports." type = string } @@ -67,20 +67,20 @@ variable "name_prefix" { type = string } -variable "consul_server_ca_cert_arn" { - description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's internal RPC and HTTP interfaces." +variable "consul_ca_cert_arn" { + description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's gRPC and HTTP interfaces. This is the default CA certificate used if consul_grpc_ca_cert_arn or consul_https_ca_cert_arn is not set" type = string default = "" } variable "consul_grpc_ca_cert_arn" { - description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's internal RPC. Overrides var.consul_server_ca_cert_arn" + description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's gRPC. Overrides var.consul_ca_cert_arn" type = string default = "" } variable "consul_https_ca_cert_arn" { - description = "The ARN of the Secrets Manager secret containing the CA certificate for Consul server's HTTP interface. Overrides var.consul_server_ca_cert_arn" + description = "The ARN of the Secrets Manager secret containing the CA certificate for Consul server's HTTP interface. Overrides var.consul_ca_cert_arn" type = string default = "" } @@ -104,16 +104,7 @@ variable "tls" { } variable "tls_server_name" { - description = "The server name to use as the SNI host when connecting via TLS for Consul's HTTP and gRPC interfaces." - type = string - default = "" -} - -variable "ca_cert_file" { - description = <<-EOT - The CA certificate file for Consul's internal HTTP and gRPC interfaces. `CONSUL_HTTPS_CACERT_PEM` and - `CONSUL_GRPC_CACERT_PEM` takes a higher precedence when configuring TLS settings in the controller." - EOT + description = "The server name to use as the SNI host when connecting via TLS to Consul's HTTP and gRPC interfaces. This is the default value used when grpc_config.tlsServerName or http_config.tlsServerName is unset." type = string default = "" } @@ -125,7 +116,7 @@ variable "consul_partition" { } variable "security_groups" { - description = "Configure the ECS service with security groups. If not specified, the default security group for the VPC is used." + description = "Configure the ECS service with security group IDs. If not specified, the default security group for the VPC is used." type = list(string) default = [] } @@ -141,7 +132,8 @@ variable "http_config" { default = {} description = <<-EOT This accepts HTTP specific TLS configuration based on the `consulServers.http` schema present in https://github.com/hashicorp/consul-ecs/blob/main/config/schema.json. - If empty, values of `var.tls`, `var.tls_server_name` and `var.ca_cert_file` will be used to configure TLS settings for HTTP. + If unset, values of `var.tls`, `var.tls_server_name` and `var.ca_cert_file` will be used to configure TLS settings for HTTP. The HTTP port defaults to 8500 if TLS is + not enabled or 8501 if TLS is enabled. EOT validation { @@ -158,7 +150,8 @@ variable "grpc_config" { default = {} description = <<-EOT This accepts gRPC specific TLS configuration based on the `consulServers.grpc` schema present in https://github.com/hashicorp/consul-ecs/blob/main/config/schema.json. - If empty, values of `var.tls`, `var.tls_server_name` and `var.ca_cert_file` will be used to configure TLS settings for gRPC. + If unset, values of `var.tls`, `var.tls_server_name` and `var.ca_cert_file` will be used to configure TLS settings for gRPC. The gRPC port defaults to 8502 if TLS is + not enabled or 8503 if TLS is enabled. EOT validation { diff --git a/modules/mesh-task/config.tf b/modules/mesh-task/config.tf index 2ab96b58..4b3870aa 100644 --- a/modules/mesh-task/config.tf +++ b/modules/mesh-task/config.tf @@ -51,7 +51,7 @@ locals { healthSyncContainers = local.defaulted_check_containers bootstrapDir = local.consul_data_mount.containerPath consulServers = { - hosts = var.consul_server_address + hosts = var.consul_server_hosts skipServerWatch = var.skip_server_watch defaults = { tls = var.tls diff --git a/modules/mesh-task/iam.tf b/modules/mesh-task/iam.tf index 8cb8ddc0..92eea664 100644 --- a/modules/mesh-task/iam.tf +++ b/modules/mesh-task/iam.tf @@ -109,14 +109,14 @@ resource "aws_iam_policy" "execution" { "Version": "2012-10-17", "Statement": [ %{if var.tls~} -%{if var.consul_server_ca_cert_arn != ""~} +%{if var.consul_ca_cert_arn != ""~} { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue" ], "Resource": [ - "${var.consul_server_ca_cert_arn}" + "${var.consul_ca_cert_arn}" ] }, %{endif~} diff --git a/modules/mesh-task/main.tf b/modules/mesh-task/main.tf index 929e3083..b8474e23 100644 --- a/modules/mesh-task/main.tf +++ b/modules/mesh-task/main.tf @@ -70,8 +70,8 @@ locals { ) ] - https_ca_cert_arn = var.consul_https_ca_cert_arn != "" ? var.consul_https_ca_cert_arn : var.consul_server_ca_cert_arn - grpc_ca_cert_arn = var.consul_grpc_ca_cert_arn != "" ? var.consul_grpc_ca_cert_arn : var.consul_server_ca_cert_arn + https_ca_cert_arn = var.consul_https_ca_cert_arn != "" ? var.consul_https_ca_cert_arn : var.consul_ca_cert_arn + grpc_ca_cert_arn = var.consul_grpc_ca_cert_arn != "" ? var.consul_grpc_ca_cert_arn : var.consul_ca_cert_arn defaulted_check_containers = [for def in local.container_defs_with_depends_on : def.name if contains(keys(def), "essential") && contains(keys(def), "healthCheck") && (try(def.healthCheck, null) != null)] diff --git a/modules/mesh-task/variables.tf b/modules/mesh-task/variables.tf index ace49b48..d0ee4baf 100644 --- a/modules/mesh-task/variables.tf +++ b/modules/mesh-task/variables.tf @@ -173,12 +173,6 @@ variable "envoy_public_listener_port" { validation { error_message = "The envoy_public_listener_port must not conflict with the following ports that are reserved for Consul and Envoy: 8300, 8301, 8302, 8500, 8501, 8502, 8600, 10000, 19000." condition = !contains([ - 8300, // consul rpc port - 8301, // consul lan serf - 8302, // consul wan serf - 8500, // consul http - 8501, // consul https - 8502, // consul grpc 8600, // consul dns 10000, // consul-ecs-control-plane health check port 19000, // envoy admin port @@ -199,12 +193,6 @@ variable "envoy_readiness_port" { validation { error_message = "The envoy_readiness_port must not conflict with the following ports that are reserved for Consul and Envoy: 8300, 8301, 8302, 8500, 8501, 8502, 8600, 10000, 19000." condition = !contains([ - 8300, // consul rpc port - 8301, // consul lan serf - 8302, // consul wan serf - 8500, // consul http - 8501, // consul https - 8502, // consul grpc 8600, // consul dns 10000, // consul-ecs-control-plane health check port 19000, // envoy admin port @@ -224,8 +212,8 @@ variable "container_definitions" { type = any } -variable "consul_server_address" { - description = "Address of the consul server host" +variable "consul_server_hosts" { + description = "Address of the Consul servers. This can be an IP address, a DNS name, or an `exec=` string specifying a script that outputs IP addresses. Refer to https://github.com/hashicorp/go-netaddrs#summary for details. This variable should not specify the port. Instead, use var.http_config.port and var.grpc_config.port to change the server HTTP and gRPC ports." type = string } @@ -290,7 +278,7 @@ variable "tls" { } variable "tls_server_name" { - description = "The server name to use as the SNI host when connecting via TLS for Consul's HTTP and gRPC interfaces." + description = "The server name to use as the SNI host when connecting via TLS to Consul's HTTP and gRPC interfaces. This is the default value used when grpc_config.tlsServerName or http_config.tlsServerName is unset." type = string default = "" } @@ -304,20 +292,20 @@ variable "ca_cert_file" { default = "" } -variable "consul_server_ca_cert_arn" { - description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's internal RPC and HTTP interfaces." +variable "consul_ca_cert_arn" { + description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's internal gRPC and HTTP interfaces." type = string default = "" } variable "consul_grpc_ca_cert_arn" { - description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's internal RPC. Overrides var.consul_server_ca_cert_arn" + description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's internal gRPC communications. Overrides var.consul_ca_cert_arn" type = string default = "" } variable "consul_https_ca_cert_arn" { - description = "The ARN of the Secrets Manager secret containing the CA certificate for Consul server's HTTP interface. Overrides var.consul_server_ca_cert_arn" + description = "The ARN of the Secrets Manager secret containing the CA certificate for Consul server's HTTP interface. Overrides var.consul_ca_cert_arn" type = string default = "" } @@ -446,7 +434,8 @@ variable "http_config" { default = {} description = <<-EOT This accepts HTTP specific TLS configuration based on the `consulServers.http` schema present in https://github.com/hashicorp/consul-ecs/blob/main/config/schema.json. - If empty, values of `var.tls`, `var.tls_server_name` and `var.ca_cert_file` will be used to configure TLS settings for HTTP. + If unset, values of `var.tls`, `var.tls_server_name` and `var.ca_cert_file` will be used to configure TLS settings for HTTP. The HTTP port defaults to 8500 if TLS is + not enabled or 8501 if TLS is enabled. EOT validation { @@ -463,7 +452,8 @@ variable "grpc_config" { default = {} description = <<-EOT This accepts gRPC specific TLS configuration based on the `consulServers.grpc` schema present in https://github.com/hashicorp/consul-ecs/blob/main/config/schema.json. - If empty, values of `var.tls`, `var.tls_server_name` and `var.ca_cert_file` will be used to configure TLS settings for gRPC. + If unset, values of `var.tls`, `var.tls_server_name` and `var.ca_cert_file` will be used to configure TLS settings for gRPC. The gRPC port defaults to 8502 if TLS is + not enabled or 8503 if TLS is enabled. EOT validation { diff --git a/test/acceptance/setup-terraform/main.tf b/test/acceptance/setup-terraform/main.tf index 4c4227c6..a62b9eb5 100644 --- a/test/acceptance/setup-terraform/main.tf +++ b/test/acceptance/setup-terraform/main.tf @@ -5,6 +5,10 @@ provider "aws" { region = var.region } +provider "hcp" { + project_id = "6a513c39-907d-4a92-9159-3b03bd00ecda" +} + locals { name = "consul-ecs-${random_string.suffix.result}" suffix = random_string.suffix.result diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index 20e672a1..aa40d267 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -4,7 +4,6 @@ package basic import ( - "context" "encoding/json" "fmt" "os" @@ -13,910 +12,16 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ecs" - "github.com/aws/aws-sdk-go-v2/service/iam" "github.com/gruntwork-io/terratest/modules/random" "github.com/gruntwork-io/terratest/modules/shell" "github.com/gruntwork-io/terratest/modules/terraform" - "github.com/hashicorp/serf/testutil/retry" + "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/framework/helpers" "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/framework/logger" "github.com/stretchr/testify/require" ) -// TestVolumeVariable tests passing a list of volumes to mesh-task. -// This validates a big nested dynamic block in mesh-task. -func TestVolumeVariable(t *testing.T) { - t.Parallel() - volumes := []map[string]interface{}{ - { - "name": "my-vol1", - }, - { - "name": "my-vol2", - "host_path": "/tmp/fake/path", - }, - { - "name": "no-optional-fields", - "docker_volume_configuration": map[string]interface{}{}, - "efs_volume_configuration": map[string]interface{}{ - "file_system_id": "fakeid123", - }, - }, - { - "name": "all-the-fields", - "docker_volume_configuration": map[string]interface{}{ - "scope": "shared", - "autoprovision": true, - "driver": "local", - "driver_opts": map[string]interface{}{ - "type": "nfs", - "device": "host.example.com:/", - "o": "addr=host.example.com,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport", - }, - }, - "fsx_windows_file_server_volume_configuration": map[string]interface{}{ - "file_system_id": "fakeid456", - "root_directory": `\\data`, - "authorization_config": map[string]interface{}{ - "credentials_parameter": "arn:aws:secretsmanager:us-east-1:000000000000:secret:fake-fake-fake-fake", - "domain": "domain-name", - }, - }, - }, - } - - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/volume-variable", - Vars: map[string]interface{}{"volumes": volumes}, - NoColor: true, - } - t.Cleanup(func() { - _, _ = terraform.DestroyE(t, terraformOptions) - }) - terraform.InitAndPlan(t, terraformOptions) -} - -// TestPassingExistingRoles will create the task definitions to validate -// creation and passing of IAM roles by mesh-task. It creates two task definitions -// with mesh-task: -// - one which has mesh-task create the roles -// - one which passes in existing roles -// -// This test does not start any services. -// -// Note: We don't have a validation for create_task_role=true XOR task_role=. -// -// If the role is created as part of the terraform plan/apply and passed in to mesh-task, -// then the role is an unknown value during the plan, since it is not yet created, and you -// can't reliably test its value for validations. -func TestPassingExistingRoles(t *testing.T) { - t.Parallel() - - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/pass-existing-iam-roles", - NoColor: true, - } - terraform.Init(t, terraformOptions) - - // Init AWS clients. - ctx := context.Background() - cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-west-2")) - require.NoError(t, err, "unable to initialize ECS client") - ecsClient := ecs.NewFromConfig(cfg) - iamClient := iam.NewFromConfig(cfg) - - t.Cleanup(func() { - _, _ = terraform.DestroyE(t, terraformOptions) - }) - terraform.InitAndApply(t, terraformOptions) - - outputs := terraform.OutputAll(t, terraformOptions) - suffix := outputs["suffix"].(string) - - { - // Check that mesh-task creates roles by default. - taskDefArn := outputs["create_roles_task_definition_arn"].(string) - family := outputs["create_roles_family"].(string) - - resp, err := ecsClient.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ - TaskDefinition: &taskDefArn, - }) - require.NoError(t, err) - - // Expected role names, as created by mesh-task - expTaskRoleName := family + "-task" - expExecRoleName := family + "-execution" - - // mesh-task should create roles and use them - taskDef := resp.TaskDefinition - require.NotNil(t, taskDef.TaskRoleArn) - require.NotNil(t, taskDef.ExecutionRoleArn) - require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs/`+expTaskRoleName, *taskDef.TaskRoleArn) - require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs/`+expExecRoleName, *taskDef.ExecutionRoleArn) - - // Check that the roles were really created. - for _, roleName := range []string{expTaskRoleName, expExecRoleName} { - resp, err := iamClient.GetRole(ctx, &iam.GetRoleInput{RoleName: &roleName}) - require.NoError(t, err) - require.Equal(t, *resp.Role.RoleName, roleName) - } - } - - { - // Check that mesh-task uses the passed in roles and doesn't create roles when roles are passed in. - taskDefArn := outputs["pass_roles_task_definition_arn"].(string) - family := outputs["pass_roles_family"].(string) - - resp, err := ecsClient.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ - TaskDefinition: &taskDefArn, - }) - require.NoError(t, err) - - // mesh-task should use the passed in roles. - taskDef := resp.TaskDefinition - require.NotNil(t, taskDef.TaskRoleArn) - require.NotNil(t, taskDef.ExecutionRoleArn) - require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs-test-pass-task-role-`+suffix, *taskDef.TaskRoleArn) - require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs-test-pass-execution-role-`+suffix, *taskDef.ExecutionRoleArn) - - // mesh-task should not create roles when they are passed in. - expTaskRoleName := family + "-task" - expExecRoleName := family + "-execution" - for _, roleName := range []string{expTaskRoleName, expExecRoleName} { - _, err := iamClient.GetRole(ctx, &iam.GetRoleInput{RoleName: &roleName}) - require.Error(t, err) - require.Contains(t, err.Error(), "StatusCode: 404") - } - } - - t.Log("Test Successful!") -} - -func TestValidation_AdditionalPolicies(t *testing.T) { - t.Parallel() - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/pass-role-additional-policies-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) - - cases := map[string]struct { - execution bool - errMsg string - }{ - "task": { - execution: false, - errMsg: "ERROR: cannot set additional_task_role_policies when create_task_role=false", - }, - "execution": { - execution: true, - errMsg: "ERROR: cannot set additional_execution_role_policies when create_execution_role=false", - }, - } - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - t.Parallel() - - _, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "test_execution_role": c.execution, - }, - }) - require.Error(t, err) - // error messages are wrapped, so a space may turn into a newline. - regex := strings.ReplaceAll(regexp.QuoteMeta(c.errMsg), " ", "\\s+") - require.Regexp(t, regex, err.Error()) - }) - } -} - -func TestPassingAppEntrypoint(t *testing.T) { - t.Parallel() - - newint := func(x int) *int { return &x } - cases := map[string]struct { - value *int - expEntrypoint bool - }{ - "null": {nil, false}, - "negative": {newint(-1), false}, - "zero": {newint(0), false}, - "one": {newint(1), true}, - "five": {newint(5), true}, - } - - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/pass-app-entrypoint", - NoColor: true, - } - t.Cleanup(func() { - _, _ = terraform.DestroyE(t, terraformOptions) - }) - - terraform.Init(t, terraformOptions) - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - t.Parallel() - opts := &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - //"application_shutdown_delay_seconds": nil, - }, - } - if c.value != nil { - opts.Vars["application_shutdown_delay_seconds"] = *c.value - } - out := terraform.Plan(t, opts) - - if c.expEntrypoint { - // Look for app-entrypoint in the Terraform diff. - regex := strings.Join([]string{ - `\+ entryPoint = \[`, - ` \+ "/consul/consul-ecs",`, - ` \+ "app-entrypoint",`, - ` \+ "-shutdown-delay",`, - ` \+ "\d+s",`, // e.g. "2s", "10s", etc - `\]`, - }, `\s+`) - require.Regexp(t, regex, out) - } else { - require.NotContains(t, out, "app-entrypoint") - } - - }) - } -} - -func TestValidation_UpstreamsVariable(t *testing.T) { - t.Parallel() - - cases := map[string]struct { - upstreamsFile string - errors []string - }{ - "no-upstreams": { - upstreamsFile: "test-no-upstreams.json", - }, - "valid-upstreams": { - upstreamsFile: "test-valid-upstreams.json", - }, - "invalid-upstreams": { - upstreamsFile: "test-invalid-upstreams.json", - errors: []string{ - "Upstream fields must be one of.*", - }, - }, - "requires-destination-name": { - upstreamsFile: "test-missing-destinationName.json", - errors: []string{ - "Upstream fields .* are required.", - }, - }, - "requires-local-bind-port": { - upstreamsFile: "test-missing-localBindPort.json", - errors: []string{ - "Upstream fields .* are required.", - }, - }, - } - - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/upstreams-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) - - for name, c := range cases { - t.Run(name, func(t *testing.T) { - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "upstreams_file": c.upstreamsFile, - }, - }) - - if len(c.errors) == 0 { - require.NoError(t, err) - } else { - require.Error(t, err) - for _, regex := range c.errors { - require.Regexp(t, regex, out) - } - } - }) - } -} - -func TestValidation_EnvoyPublicListenerPort(t *testing.T) { - t.Parallel() - - cases := map[string]struct { - port int - error string - }{ - "allowed-port": { - port: 21000, - }, - "too-high-port": { - port: 65536, - error: "The envoy_public_listener_port must be greater than 0 and less than or equal to 65535.", - }, - "disallowed-port": { - port: 19000, - error: "The envoy_public_listener_port must not conflict with the following ports that are reserved for Consul and Envoy", - }, - } - - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/public-listener-port-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) - - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - t.Parallel() - - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "envoy_public_listener_port": c.port, - }, - }) - - if c.error == "" { - require.NoError(t, err) - } else { - // handle multiline error messages. - regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") - require.Error(t, err) - require.Regexp(t, regex, out) - } - }) - } -} - -func TestValidation_EnvoyReadinessPort(t *testing.T) { - t.Parallel() - - cases := map[string]struct { - port int - error string - }{ - "allowed-port": { - port: 23000, - }, - "too-high-port": { - port: 65536, - error: "The envoy_readiness_port must be greater than 0 and less than or equal to 65535.", - }, - "disallowed-port": { - port: 19000, - error: "The envoy_readiness_port must not conflict with the following ports that are reserved for Consul and Envoy", - }, - "conflicts-with-listener-port": { - port: 20000, - error: "envoy_public_listener_port should not conflict with envoy_readiness_port", - }, - } - - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/envoy-readiness-port-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) - - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - t.Parallel() - - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "envoy_readiness_port": c.port, - }, - }) - - if c.error == "" { - require.NoError(t, err) - } else { - // handle multiline error messages. - regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") - require.Error(t, err) - require.Regexp(t, regex, out) - } - }) - } -} - -func TestValidation_ConsulServiceName(t *testing.T) { - t.Parallel() - - cases := map[string]struct { - serviceName string - error bool - }{ - "empty": {}, - "lowercase": { - serviceName: "lower-case-name", - }, - "uppercase": { - serviceName: "UPPER-CASE-NAME", - error: true, - }, - } - - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/service-name-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) - - for name, c := range cases { - c := c - - t.Run(name, func(t *testing.T) { - t.Parallel() - - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "consul_service_name": c.serviceName, - }, - }) - - if c.error { - require.Error(t, err) - require.Regexp(t, "The consul_service_name must be lower case.", out) - } else { - require.NoError(t, err) - } - }) - } - -} - -func TestValidation_ConsulEcsConfigVariable(t *testing.T) { - t.Parallel() - - cases := map[string]struct { - configFile string - errors []string - }{ - "empty-map": { - configFile: "test-empty-config.json", - }, - "complete-config": { - configFile: "test-complete-config.json", - }, - "partial-config": { - configFile: "test-partial-config.json", - }, - "invalid-config": { - configFile: "test-invalid-config.json", - errors: []string{ - "Only the 'service', 'proxy', and 'consulLogin' fields are allowed in consul_ecs_config.", - "Only the 'enableTagOverride' and 'weights' fields are allowed in consul_ecs_config.service.", - "Only the 'meshGateway', 'expose', and 'config' fields are allowed in consul_ecs_config.proxy.", - "Only the 'mode' field is allowed in consul_ecs_config.proxy.meshGateway.", - "Only the 'checks' and 'paths' fields are allowed in consul_ecs_config.proxy.expose.", - "Only the 'listenerPort', 'path', 'localPathPort', and 'protocol' fields are allowed in each item of consul_ecs_config.proxy.expose.paths[*].", - "Only the 'enabled', 'method', 'includeEntity', 'meta', 'region', 'stsEndpoint', and 'serverIdHeaderValue' fields are allowed in consul_ecs_config.consulLogin.", - }, - }, - } - - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/consul-ecs-config-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) - - for name, c := range cases { - c := c - - t.Run(name, func(t *testing.T) { - t.Parallel() - - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "consul_ecs_config_file": c.configFile, - }, - }) - - if len(c.errors) == 0 { - require.NoError(t, err) - } else { - for _, msg := range c.errors { - // error messages are wrapped, so a space may turn into a newline. - regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") - require.Regexp(t, regex, out) - } - } - }) - } -} - -func TestValidation_HTTPTLSConfig(t *testing.T) { - t.Parallel() - - cases := map[string]struct { - configFile string - errors []string - }{ - "empty-map": { - configFile: "test-empty-config.json", - }, - "complete-config": { - configFile: "test-complete-config.json", - }, - "partial-config": { - configFile: "test-partial-config.json", - }, - "invalid-config": { - configFile: "test-invalid-config.json", - errors: []string{ - "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_config.", - }, - }, - } - - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/http-tls-config-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) - - for name, c := range cases { - c := c - - t.Run(name, func(t *testing.T) { - t.Parallel() - - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "http_config_file": c.configFile, - }, - }) - - if len(c.errors) == 0 { - require.NoError(t, err) - } else { - for _, msg := range c.errors { - // error messages are wrapped, so a space may turn into a newline. - regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") - require.Regexp(t, regex, out) - } - } - }) - } -} - -func TestValidation_GRPCTLSConfig(t *testing.T) { - t.Parallel() - - cases := map[string]struct { - configFile string - errors []string - }{ - "empty-map": { - configFile: "test-empty-config.json", - }, - "complete-config": { - configFile: "test-complete-config.json", - }, - "partial-config": { - configFile: "test-partial-config.json", - }, - "invalid-config": { - configFile: "test-invalid-config.json", - errors: []string{ - "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_config.", - }, - }, - } - - terraformOptions := &terraform.Options{ - TerraformDir: "./terraform/grpc-tls-config-validate", - NoColor: true, - } - terraform.Init(t, terraformOptions) - - for name, c := range cases { - c := c - - t.Run(name, func(t *testing.T) { - t.Parallel() - - out, err := terraform.PlanE(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: true, - Vars: map[string]interface{}{ - "grpc_config_file": c.configFile, - }, - }) - - if len(c.errors) == 0 { - require.NoError(t, err) - } else { - for _, msg := range c.errors { - // error messages are wrapped, so a space may turn into a newline. - regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") - require.Regexp(t, regex, out) - } - } - }) - } -} - -// Test the validation that both partition and namespace must be provided or neither. -func TestValidation_NamespaceAndPartitionRequired(t *testing.T) { - t.Parallel() - - cases := map[string]struct { - partition string - namespace string - errMsg string - }{ - "without partition and namespace": { - partition: "", - namespace: "", - errMsg: "", - }, - "with partition and namespace": { - partition: "default", - namespace: "default", - errMsg: "", - }, - "with partition, without namespace": { - partition: "default", - namespace: "", - errMsg: "ERROR: consul_namespace must be set if consul_partition is set", - }, - "without partition, with namespace": { - partition: "", - namespace: "default", - errMsg: "ERROR: consul_partition must be set if consul_namespace is set", - }, - } - - terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "./terraform/admin-partition-validate", - NoColor: true, - }) - _ = terraform.Init(t, terraformOptions) - - for name, c := range cases { - c := c - t.Run(name, func(t *testing.T) { - t.Parallel() - terraformOptions.Vars = map[string]interface{}{ - "partition": c.partition, - "namespace": c.namespace, - } - t.Cleanup(func() { - _, _ = terraform.DestroyE(t, terraformOptions) - }) - _, err := terraform.PlanE(t, terraformOptions) - if c.errMsg == "" { - require.NoError(t, err) - } else { - require.Error(t, err) - require.Contains(t, err.Error(), c.errMsg) - } - }) - } -} - -func TestValidation_RolePath(t *testing.T) { - t.Parallel() - - terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "./terraform/role-path-validate", - NoColor: true, - }) - _ = terraform.Init(t, terraformOptions) - - cases := []struct { - path string - expError bool - }{ - {"", true}, - {"test", true}, - {"/test", false}, - {"/test/", false}, - } - for _, c := range cases { - c := c - t.Run(fmt.Sprintf("path=%q", c.path), func(t *testing.T) { - t.Parallel() - - applyOpts := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: terraformOptions.TerraformDir, - NoColor: terraformOptions.NoColor, - Vars: map[string]interface{}{ - "iam_role_path": c.path, - }, - }) - - t.Cleanup(func() { - _, _ = terraform.DestroyE(t, applyOpts) - }) - _, err := terraform.PlanE(t, applyOpts) - if c.expError { - require.Error(t, err) - require.Contains(t, err.Error(), "iam_role_path must begin with '/'") - } else { - require.NoError(t, err) - } - - }) - } - -} - -// TODO: Revisit this test -// -// func TestValidation_MeshGateway(t *testing.T) { -// t.Parallel() - -// terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ -// TerraformDir: "./terraform/mesh-gateway-validate", -// NoColor: true, -// }) -// _ = terraform.Init(t, terraformOptions) - -// cases := map[string]struct { -// kind string -// enableMeshGatewayWANFed bool -// tls bool -// securityGroups []string -// wanAddress string -// lbEnabled bool -// lbVpcID string -// lbSubnets []string -// lbCreateSecGroup bool -// lbModifySecGroup bool -// lbModifySecGroupID string -// expError string -// }{ -// "kind is required": { -// kind: "", -// enableMeshGatewayWANFed: false, -// expError: `variable "kind" is not set`, -// }, -// "kind must be mesh-gateway": { -// kind: "not-mesh-gateway", -// enableMeshGatewayWANFed: false, -// expError: `Gateway kind must be 'mesh-gateway'`, -// }, -// "no WAN federation": { -// kind: "mesh-gateway", -// enableMeshGatewayWANFed: false, -// }, -// "mesh gateway WAN federation, no TLS": { -// kind: "mesh-gateway", -// enableMeshGatewayWANFed: true, -// tls: false, -// expError: "tls must be true when enable_mesh_gateway_wan_federation is true", -// }, -// "mesh gateway WAN federation": { -// kind: "mesh-gateway", -// enableMeshGatewayWANFed: true, -// tls: true, -// }, -// "WAN address and LB enabled": { -// kind: "mesh-gateway", -// enableMeshGatewayWANFed: false, -// wanAddress: "10.1.2.3", -// lbEnabled: true, -// expError: "Only one of wan_address or lb_enabled may be provided", -// }, -// "lb_enabled": { -// kind: "mesh-gateway", -// lbEnabled: true, -// lbSubnets: []string{"subnet"}, -// lbVpcID: "vpc", -// }, -// "lb_enabled and no lb subnets": { -// kind: "mesh-gateway", -// lbEnabled: true, -// lbVpcID: "vpc", -// expError: "lb_subnets is required when lb_enabled is true", -// }, -// "lb_enabled and no VPC": { -// kind: "mesh-gateway", -// lbEnabled: true, -// lbSubnets: []string{"subnet"}, -// expError: "lb_vpc_id is required when lb_enabled is true", -// }, -// "lb create security group and modify security group": { -// kind: "mesh-gateway", -// securityGroups: []string{"sg"}, -// lbEnabled: true, -// lbSubnets: []string{"subnet"}, -// lbVpcID: "vpc", -// lbCreateSecGroup: true, -// lbModifySecGroup: true, -// expError: "Only one of lb_create_security_group or lb_modify_security_group may be true", -// }, -// "lb modify security group and no security group ID": { -// kind: "mesh-gateway", -// securityGroups: []string{"sg"}, -// lbEnabled: true, -// lbSubnets: []string{"subnet"}, -// lbVpcID: "vpc", -// lbCreateSecGroup: false, -// lbModifySecGroup: true, -// lbModifySecGroupID: "", -// expError: "lb_modify_security_group_id is required when lb_modify_security_group is true", -// }, -// "lb modify security group with security group ID": { -// kind: "mesh-gateway", -// securityGroups: []string{"sg"}, -// lbEnabled: true, -// lbSubnets: []string{"subnet"}, -// lbVpcID: "vpc", -// lbCreateSecGroup: false, -// lbModifySecGroup: true, -// lbModifySecGroupID: "mod-sg", -// }, -// } -// for name, c := range cases { -// c := c -// t.Run(name, func(t *testing.T) { -// t.Parallel() - -// tfVars := map[string]interface{}{ -// "enable_mesh_gateway_wan_federation": c.enableMeshGatewayWANFed, -// "tls": c.tls, -// "security_groups": c.securityGroups, -// "wan_address": c.wanAddress, -// "lb_enabled": c.lbEnabled, -// "lb_subnets": c.lbSubnets, -// "lb_vpc_id": c.lbVpcID, -// "lb_create_security_group": c.lbCreateSecGroup, -// "lb_modify_security_group": c.lbModifySecGroup, -// "lb_modify_security_group_id": c.lbModifySecGroupID, -// } -// if len(c.kind) > 0 { -// tfVars["kind"] = c.kind -// } -// applyOpts := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ -// TerraformDir: terraformOptions.TerraformDir, -// NoColor: terraformOptions.NoColor, -// Vars: tfVars, -// }) -// t.Cleanup(func() { _, _ = terraform.DestroyE(t, applyOpts) }) - -// _, err := terraform.PlanE(t, applyOpts) -// if len(c.expError) > 0 { -// require.Error(t, err) -// require.Contains(t, err.Error(), c.expError) -// } else { -// require.NoError(t, err) -// } -// }) -// } -// } - func TestBasic(t *testing.T) { t.Parallel() @@ -929,7 +34,7 @@ func TestBasic(t *testing.T) { }{ {secure: false}, {secure: true}, - {secure: true, enterprise: true}, + //{secure: true, enterprise: true}, } cfg := suite.Config() diff --git a/test/acceptance/tests/basic/terraform/basic-install/main.tf b/test/acceptance/tests/basic/terraform/basic-install/main.tf index 5970a5a1..81734741 100644 --- a/test/acceptance/tests/basic/terraform/basic-install/main.tf +++ b/test/acceptance/tests/basic/terraform/basic-install/main.tf @@ -145,7 +145,7 @@ module "ecs_controller" { } launch_type = var.launch_type consul_bootstrap_token_secret_arn = module.consul_server.bootstrap_token_secret_arn - consul_server_address = module.consul_server.server_dns + consul_server_hosts = module.consul_server.server_dns consul_grpc_ca_cert_arn = module.consul_server.ca_cert_arn consul_https_ca_cert_arn = module.consul_server.ca_cert_arn ecs_cluster_arn = var.ecs_cluster_arn @@ -223,7 +223,7 @@ EOT ] } ] - consul_server_address = module.consul_server.server_dns + consul_server_hosts = module.consul_server.server_dns upstreams = [ { destinationName = "${var.server_service_name}_${var.suffix}" @@ -275,7 +275,7 @@ module "test_server" { essential = true logConfiguration = local.test_server_log_configuration }] - consul_server_address = module.consul_server.server_dns + consul_server_hosts = module.consul_server.server_dns log_configuration = local.test_server_log_configuration port = 9090 diff --git a/test/acceptance/tests/basic/terraform/admin-partition-validate/main.tf b/test/acceptance/tests/validation/terraform/admin-partition-validate/main.tf similarity index 91% rename from test/acceptance/tests/basic/terraform/admin-partition-validate/main.tf rename to test/acceptance/tests/validation/terraform/admin-partition-validate/main.tf index 52de922f..3a849bfc 100644 --- a/test/acceptance/tests/basic/terraform/admin-partition-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/admin-partition-validate/main.tf @@ -22,7 +22,7 @@ module "test_client" { name = "basic" }] outbound_only = true - consul_server_address = "consul.dc1.host" + consul_server_hosts = "consul.dc1.host" consul_partition = var.partition consul_namespace = var.namespace diff --git a/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/main.tf b/test/acceptance/tests/validation/terraform/consul-ecs-config-validate/main.tf similarity index 90% rename from test/acceptance/tests/basic/terraform/consul-ecs-config-validate/main.tf rename to test/acceptance/tests/validation/terraform/consul-ecs-config-validate/main.tf index 3bfe3b36..5e6d6ca2 100644 --- a/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/consul-ecs-config-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - consul_server_address = "consul.dc1.host" + consul_server_hosts = "consul.dc1.host" outbound_only = true consul_ecs_config = jsondecode(file("${path.module}/${var.consul_ecs_config_file}")) } diff --git a/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/test-complete-config.json b/test/acceptance/tests/validation/terraform/consul-ecs-config-validate/test-complete-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/consul-ecs-config-validate/test-complete-config.json rename to test/acceptance/tests/validation/terraform/consul-ecs-config-validate/test-complete-config.json diff --git a/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/test-empty-config.json b/test/acceptance/tests/validation/terraform/consul-ecs-config-validate/test-empty-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/consul-ecs-config-validate/test-empty-config.json rename to test/acceptance/tests/validation/terraform/consul-ecs-config-validate/test-empty-config.json diff --git a/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/test-invalid-config.json b/test/acceptance/tests/validation/terraform/consul-ecs-config-validate/test-invalid-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/consul-ecs-config-validate/test-invalid-config.json rename to test/acceptance/tests/validation/terraform/consul-ecs-config-validate/test-invalid-config.json diff --git a/test/acceptance/tests/basic/terraform/consul-ecs-config-validate/test-partial-config.json b/test/acceptance/tests/validation/terraform/consul-ecs-config-validate/test-partial-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/consul-ecs-config-validate/test-partial-config.json rename to test/acceptance/tests/validation/terraform/consul-ecs-config-validate/test-partial-config.json diff --git a/test/acceptance/tests/basic/terraform/envoy-readiness-port-validate/main.tf b/test/acceptance/tests/validation/terraform/envoy-readiness-port-validate/main.tf similarity index 90% rename from test/acceptance/tests/basic/terraform/envoy-readiness-port-validate/main.tf rename to test/acceptance/tests/validation/terraform/envoy-readiness-port-validate/main.tf index f1c3d589..779fafa7 100644 --- a/test/acceptance/tests/basic/terraform/envoy-readiness-port-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/envoy-readiness-port-validate/main.tf @@ -14,7 +14,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - consul_server_address = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true envoy_readiness_port = var.envoy_readiness_port } \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/grpc-config-validate/main.tf b/test/acceptance/tests/validation/terraform/grpc-config-validate/main.tf similarity index 90% rename from test/acceptance/tests/basic/terraform/grpc-config-validate/main.tf rename to test/acceptance/tests/validation/terraform/grpc-config-validate/main.tf index 4c5b04d8..5a4c0acc 100644 --- a/test/acceptance/tests/basic/terraform/grpc-config-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/grpc-config-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - consul_server_address = "consul.dc1.host" + consul_server_hosts = "consul.dc1.host" outbound_only = true grpc_config = jsondecode(file("${path.module}/${var.grpc_config_file}")) } \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/grpc-config-validate/test-complete-config.json b/test/acceptance/tests/validation/terraform/grpc-config-validate/test-complete-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/grpc-config-validate/test-complete-config.json rename to test/acceptance/tests/validation/terraform/grpc-config-validate/test-complete-config.json diff --git a/test/acceptance/tests/basic/terraform/grpc-config-validate/test-empty-config.json b/test/acceptance/tests/validation/terraform/grpc-config-validate/test-empty-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/grpc-config-validate/test-empty-config.json rename to test/acceptance/tests/validation/terraform/grpc-config-validate/test-empty-config.json diff --git a/test/acceptance/tests/basic/terraform/grpc-config-validate/test-invalid-config.json b/test/acceptance/tests/validation/terraform/grpc-config-validate/test-invalid-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/grpc-config-validate/test-invalid-config.json rename to test/acceptance/tests/validation/terraform/grpc-config-validate/test-invalid-config.json diff --git a/test/acceptance/tests/basic/terraform/grpc-config-validate/test-partial-config.json b/test/acceptance/tests/validation/terraform/grpc-config-validate/test-partial-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/grpc-config-validate/test-partial-config.json rename to test/acceptance/tests/validation/terraform/grpc-config-validate/test-partial-config.json diff --git a/test/acceptance/tests/basic/terraform/http-config-validate/main.tf b/test/acceptance/tests/validation/terraform/http-config-validate/main.tf similarity index 90% rename from test/acceptance/tests/basic/terraform/http-config-validate/main.tf rename to test/acceptance/tests/validation/terraform/http-config-validate/main.tf index 9b781f5b..864ad289 100644 --- a/test/acceptance/tests/basic/terraform/http-config-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/http-config-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - consul_server_address = "consul.dc1.host" + consul_server_hosts = "consul.dc1.host" outbound_only = true http_config = jsondecode(file("${path.module}/${var.http_config_file}")) } \ No newline at end of file diff --git a/test/acceptance/tests/basic/terraform/http-config-validate/test-complete-config.json b/test/acceptance/tests/validation/terraform/http-config-validate/test-complete-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/http-config-validate/test-complete-config.json rename to test/acceptance/tests/validation/terraform/http-config-validate/test-complete-config.json diff --git a/test/acceptance/tests/basic/terraform/http-config-validate/test-empty-config.json b/test/acceptance/tests/validation/terraform/http-config-validate/test-empty-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/http-config-validate/test-empty-config.json rename to test/acceptance/tests/validation/terraform/http-config-validate/test-empty-config.json diff --git a/test/acceptance/tests/basic/terraform/http-config-validate/test-invalid-config.json b/test/acceptance/tests/validation/terraform/http-config-validate/test-invalid-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/http-config-validate/test-invalid-config.json rename to test/acceptance/tests/validation/terraform/http-config-validate/test-invalid-config.json diff --git a/test/acceptance/tests/basic/terraform/http-config-validate/test-partial-config.json b/test/acceptance/tests/validation/terraform/http-config-validate/test-partial-config.json similarity index 100% rename from test/acceptance/tests/basic/terraform/http-config-validate/test-partial-config.json rename to test/acceptance/tests/validation/terraform/http-config-validate/test-partial-config.json diff --git a/test/acceptance/tests/basic/terraform/mesh-gateway-validate/main.tf b/test/acceptance/tests/validation/terraform/mesh-gateway-validate/main.tf similarity index 100% rename from test/acceptance/tests/basic/terraform/mesh-gateway-validate/main.tf rename to test/acceptance/tests/validation/terraform/mesh-gateway-validate/main.tf diff --git a/test/acceptance/tests/basic/terraform/pass-app-entrypoint/main.tf b/test/acceptance/tests/validation/terraform/pass-app-entrypoint/main.tf similarity index 92% rename from test/acceptance/tests/basic/terraform/pass-app-entrypoint/main.tf rename to test/acceptance/tests/validation/terraform/pass-app-entrypoint/main.tf index d62647a5..2a58c30b 100644 --- a/test/acceptance/tests/basic/terraform/pass-app-entrypoint/main.tf +++ b/test/acceptance/tests/validation/terraform/pass-app-entrypoint/main.tf @@ -18,7 +18,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - consul_server_address = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true application_shutdown_delay_seconds = var.application_shutdown_delay_seconds diff --git a/test/acceptance/tests/basic/terraform/pass-existing-iam-roles/main.tf b/test/acceptance/tests/validation/terraform/pass-existing-iam-roles/main.tf similarity index 97% rename from test/acceptance/tests/basic/terraform/pass-existing-iam-roles/main.tf rename to test/acceptance/tests/validation/terraform/pass-existing-iam-roles/main.tf index 72c05082..521635e8 100644 --- a/test/acceptance/tests/basic/terraform/pass-existing-iam-roles/main.tf +++ b/test/acceptance/tests/validation/terraform/pass-existing-iam-roles/main.tf @@ -49,7 +49,7 @@ module "test_client_create_new_roles" { family = local.create_roles_family log_configuration = null container_definitions = local.container_definitions - consul_server_address = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true // Roles are not passed. This tests the default values for create_task_role, @@ -61,7 +61,7 @@ module "test_client_pass_existing_roles" { family = local.pass_roles_family log_configuration = null container_definitions = local.container_definitions - consul_server_address = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true create_task_role = false diff --git a/test/acceptance/tests/basic/terraform/pass-role-additional-policies-validate/main.tf b/test/acceptance/tests/validation/terraform/pass-role-additional-policies-validate/main.tf similarity index 94% rename from test/acceptance/tests/basic/terraform/pass-role-additional-policies-validate/main.tf rename to test/acceptance/tests/validation/terraform/pass-role-additional-policies-validate/main.tf index 7914a319..a8f059d5 100644 --- a/test/acceptance/tests/basic/terraform/pass-role-additional-policies-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/pass-role-additional-policies-validate/main.tf @@ -28,7 +28,7 @@ module "task_role_test" { family = "family-1" log_configuration = null container_definitions = local.container_definitions - consul_server_address = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true create_task_role = false @@ -46,7 +46,7 @@ module "execution_role_test" { family = "family-2" log_configuration = null container_definitions = local.container_definitions - consul_server_address = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true create_execution_role = false diff --git a/test/acceptance/tests/basic/terraform/public-listener-port-validate/main.tf b/test/acceptance/tests/validation/terraform/public-listener-port-validate/main.tf similarity index 90% rename from test/acceptance/tests/basic/terraform/public-listener-port-validate/main.tf rename to test/acceptance/tests/validation/terraform/public-listener-port-validate/main.tf index 478deda7..e7e899e1 100644 --- a/test/acceptance/tests/basic/terraform/public-listener-port-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/public-listener-port-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - consul_server_address = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true envoy_public_listener_port = var.envoy_public_listener_port } diff --git a/test/acceptance/tests/basic/terraform/role-path-validate/main.tf b/test/acceptance/tests/validation/terraform/role-path-validate/main.tf similarity index 90% rename from test/acceptance/tests/basic/terraform/role-path-validate/main.tf rename to test/acceptance/tests/validation/terraform/role-path-validate/main.tf index 4a9d45cb..2cb213ef 100644 --- a/test/acceptance/tests/basic/terraform/role-path-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/role-path-validate/main.tf @@ -16,7 +16,7 @@ module "test_client" { name = "basic" }] outbound_only = true - consul_server_address = "consul.dc1" + consul_server_hosts = "consul.dc1" iam_role_path = var.iam_role_path } diff --git a/test/acceptance/tests/basic/terraform/service-name-validate/main.tf b/test/acceptance/tests/validation/terraform/service-name-validate/main.tf similarity index 90% rename from test/acceptance/tests/basic/terraform/service-name-validate/main.tf rename to test/acceptance/tests/validation/terraform/service-name-validate/main.tf index 4e064581..43431155 100644 --- a/test/acceptance/tests/basic/terraform/service-name-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/service-name-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - consul_server_address = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true consul_service_name = var.consul_service_name } diff --git a/test/acceptance/tests/basic/terraform/upstreams-validate/main.tf b/test/acceptance/tests/validation/terraform/upstreams-validate/main.tf similarity index 91% rename from test/acceptance/tests/basic/terraform/upstreams-validate/main.tf rename to test/acceptance/tests/validation/terraform/upstreams-validate/main.tf index 969a274a..5c26b638 100644 --- a/test/acceptance/tests/basic/terraform/upstreams-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/upstreams-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - consul_server_address = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true upstreams = jsondecode(file("${path.module}/${var.upstreams_file}")) } diff --git a/test/acceptance/tests/basic/terraform/upstreams-validate/test-invalid-upstreams.json b/test/acceptance/tests/validation/terraform/upstreams-validate/test-invalid-upstreams.json similarity index 100% rename from test/acceptance/tests/basic/terraform/upstreams-validate/test-invalid-upstreams.json rename to test/acceptance/tests/validation/terraform/upstreams-validate/test-invalid-upstreams.json diff --git a/test/acceptance/tests/basic/terraform/upstreams-validate/test-missing-destinationName.json b/test/acceptance/tests/validation/terraform/upstreams-validate/test-missing-destinationName.json similarity index 100% rename from test/acceptance/tests/basic/terraform/upstreams-validate/test-missing-destinationName.json rename to test/acceptance/tests/validation/terraform/upstreams-validate/test-missing-destinationName.json diff --git a/test/acceptance/tests/basic/terraform/upstreams-validate/test-missing-localBindPort.json b/test/acceptance/tests/validation/terraform/upstreams-validate/test-missing-localBindPort.json similarity index 100% rename from test/acceptance/tests/basic/terraform/upstreams-validate/test-missing-localBindPort.json rename to test/acceptance/tests/validation/terraform/upstreams-validate/test-missing-localBindPort.json diff --git a/test/acceptance/tests/basic/terraform/upstreams-validate/test-no-upstreams.json b/test/acceptance/tests/validation/terraform/upstreams-validate/test-no-upstreams.json similarity index 100% rename from test/acceptance/tests/basic/terraform/upstreams-validate/test-no-upstreams.json rename to test/acceptance/tests/validation/terraform/upstreams-validate/test-no-upstreams.json diff --git a/test/acceptance/tests/basic/terraform/upstreams-validate/test-valid-upstreams.json b/test/acceptance/tests/validation/terraform/upstreams-validate/test-valid-upstreams.json similarity index 100% rename from test/acceptance/tests/basic/terraform/upstreams-validate/test-valid-upstreams.json rename to test/acceptance/tests/validation/terraform/upstreams-validate/test-valid-upstreams.json diff --git a/test/acceptance/tests/basic/terraform/volume-variable/main.tf b/test/acceptance/tests/validation/terraform/volume-variable/main.tf similarity index 90% rename from test/acceptance/tests/basic/terraform/volume-variable/main.tf rename to test/acceptance/tests/validation/terraform/volume-variable/main.tf index 0d1efd0c..2b8dffde 100644 --- a/test/acceptance/tests/basic/terraform/volume-variable/main.tf +++ b/test/acceptance/tests/validation/terraform/volume-variable/main.tf @@ -16,6 +16,6 @@ module "test_client" { container_definitions = [{ name = "basic" }] - consul_server_address = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true } diff --git a/test/acceptance/tests/validation/validation_test.go b/test/acceptance/tests/validation/validation_test.go new file mode 100644 index 00000000..bee96c20 --- /dev/null +++ b/test/acceptance/tests/validation/validation_test.go @@ -0,0 +1,906 @@ +package validation + +import ( + "context" + "fmt" + "regexp" + "strings" + "testing" + + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/ecs" + "github.com/aws/aws-sdk-go-v2/service/iam" + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/require" +) + +// TestVolumeVariable tests passing a list of volumes to mesh-task. +// This validates a big nested dynamic block in mesh-task. +func TestVolumeVariable(t *testing.T) { + t.Parallel() + volumes := []map[string]interface{}{ + { + "name": "my-vol1", + }, + { + "name": "my-vol2", + "host_path": "/tmp/fake/path", + }, + { + "name": "no-optional-fields", + "docker_volume_configuration": map[string]interface{}{}, + "efs_volume_configuration": map[string]interface{}{ + "file_system_id": "fakeid123", + }, + }, + { + "name": "all-the-fields", + "docker_volume_configuration": map[string]interface{}{ + "scope": "shared", + "autoprovision": true, + "driver": "local", + "driver_opts": map[string]interface{}{ + "type": "nfs", + "device": "host.example.com:/", + "o": "addr=host.example.com,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport", + }, + }, + "fsx_windows_file_server_volume_configuration": map[string]interface{}{ + "file_system_id": "fakeid456", + "root_directory": `\\data`, + "authorization_config": map[string]interface{}{ + "credentials_parameter": "arn:aws:secretsmanager:us-east-1:000000000000:secret:fake-fake-fake-fake", + "domain": "domain-name", + }, + }, + }, + } + + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/volume-variable", + Vars: map[string]interface{}{"volumes": volumes}, + NoColor: true, + } + t.Cleanup(func() { + _, _ = terraform.DestroyE(t, terraformOptions) + }) + terraform.InitAndPlan(t, terraformOptions) +} + +// TestPassingExistingRoles will create the task definitions to validate +// creation and passing of IAM roles by mesh-task. It creates two task definitions +// with mesh-task: +// - one which has mesh-task create the roles +// - one which passes in existing roles +// +// This test does not start any services. +// +// Note: We don't have a validation for create_task_role=true XOR task_role=. +// +// If the role is created as part of the terraform plan/apply and passed in to mesh-task, +// then the role is an unknown value during the plan, since it is not yet created, and you +// can't reliably test its value for validations. +func TestPassingExistingRoles(t *testing.T) { + t.Parallel() + + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/pass-existing-iam-roles", + NoColor: true, + } + terraform.Init(t, terraformOptions) + + // Init AWS clients. + ctx := context.Background() + cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-west-2")) + require.NoError(t, err, "unable to initialize ECS client") + ecsClient := ecs.NewFromConfig(cfg) + iamClient := iam.NewFromConfig(cfg) + + t.Cleanup(func() { + _, _ = terraform.DestroyE(t, terraformOptions) + }) + terraform.InitAndApply(t, terraformOptions) + + outputs := terraform.OutputAll(t, terraformOptions) + suffix := outputs["suffix"].(string) + + { + // Check that mesh-task creates roles by default. + taskDefArn := outputs["create_roles_task_definition_arn"].(string) + family := outputs["create_roles_family"].(string) + + resp, err := ecsClient.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ + TaskDefinition: &taskDefArn, + }) + require.NoError(t, err) + + // Expected role names, as created by mesh-task + expTaskRoleName := family + "-task" + expExecRoleName := family + "-execution" + + // mesh-task should create roles and use them + taskDef := resp.TaskDefinition + require.NotNil(t, taskDef.TaskRoleArn) + require.NotNil(t, taskDef.ExecutionRoleArn) + require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs/`+expTaskRoleName, *taskDef.TaskRoleArn) + require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs/`+expExecRoleName, *taskDef.ExecutionRoleArn) + + // Check that the roles were really created. + for _, roleName := range []string{expTaskRoleName, expExecRoleName} { + resp, err := iamClient.GetRole(ctx, &iam.GetRoleInput{RoleName: &roleName}) + require.NoError(t, err) + require.Equal(t, *resp.Role.RoleName, roleName) + } + } + + { + // Check that mesh-task uses the passed in roles and doesn't create roles when roles are passed in. + taskDefArn := outputs["pass_roles_task_definition_arn"].(string) + family := outputs["pass_roles_family"].(string) + + resp, err := ecsClient.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ + TaskDefinition: &taskDefArn, + }) + require.NoError(t, err) + + // mesh-task should use the passed in roles. + taskDef := resp.TaskDefinition + require.NotNil(t, taskDef.TaskRoleArn) + require.NotNil(t, taskDef.ExecutionRoleArn) + require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs-test-pass-task-role-`+suffix, *taskDef.TaskRoleArn) + require.Regexp(t, `arn:aws:iam::\d+:role/consul-ecs-test-pass-execution-role-`+suffix, *taskDef.ExecutionRoleArn) + + // mesh-task should not create roles when they are passed in. + expTaskRoleName := family + "-task" + expExecRoleName := family + "-execution" + for _, roleName := range []string{expTaskRoleName, expExecRoleName} { + _, err := iamClient.GetRole(ctx, &iam.GetRoleInput{RoleName: &roleName}) + require.Error(t, err) + require.Contains(t, err.Error(), "StatusCode: 404") + } + } + + t.Log("Test Successful!") +} + +func TestValidation_AdditionalPolicies(t *testing.T) { + t.Parallel() + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/pass-role-additional-policies-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) + + cases := map[string]struct { + execution bool + errMsg string + }{ + "task": { + execution: false, + errMsg: "ERROR: cannot set additional_task_role_policies when create_task_role=false", + }, + "execution": { + execution: true, + errMsg: "ERROR: cannot set additional_execution_role_policies when create_execution_role=false", + }, + } + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + t.Parallel() + + _, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "test_execution_role": c.execution, + }, + }) + require.Error(t, err) + // error messages are wrapped, so a space may turn into a newline. + regex := strings.ReplaceAll(regexp.QuoteMeta(c.errMsg), " ", "\\s+") + require.Regexp(t, regex, err.Error()) + }) + } +} + +func TestPassingAppEntrypoint(t *testing.T) { + t.Parallel() + + newint := func(x int) *int { return &x } + cases := map[string]struct { + value *int + expEntrypoint bool + }{ + "null": {nil, false}, + "negative": {newint(-1), false}, + "zero": {newint(0), false}, + "one": {newint(1), true}, + "five": {newint(5), true}, + } + + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/pass-app-entrypoint", + NoColor: true, + } + t.Cleanup(func() { + _, _ = terraform.DestroyE(t, terraformOptions) + }) + + terraform.Init(t, terraformOptions) + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + t.Parallel() + opts := &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + //"application_shutdown_delay_seconds": nil, + }, + } + if c.value != nil { + opts.Vars["application_shutdown_delay_seconds"] = *c.value + } + out := terraform.Plan(t, opts) + + if c.expEntrypoint { + // Look for app-entrypoint in the Terraform diff. + regex := strings.Join([]string{ + `\+ entryPoint = \[`, + ` \+ "/consul/consul-ecs",`, + ` \+ "app-entrypoint",`, + ` \+ "-shutdown-delay",`, + ` \+ "\d+s",`, // e.g. "2s", "10s", etc + `\]`, + }, `\s+`) + require.Regexp(t, regex, out) + } else { + require.NotContains(t, out, "app-entrypoint") + } + + }) + } +} + +func TestValidation_UpstreamsVariable(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + upstreamsFile string + errors []string + }{ + "no-upstreams": { + upstreamsFile: "test-no-upstreams.json", + }, + "valid-upstreams": { + upstreamsFile: "test-valid-upstreams.json", + }, + "invalid-upstreams": { + upstreamsFile: "test-invalid-upstreams.json", + errors: []string{ + "Upstream fields must be one of.*", + }, + }, + "requires-destination-name": { + upstreamsFile: "test-missing-destinationName.json", + errors: []string{ + "Upstream fields .* are required.", + }, + }, + "requires-local-bind-port": { + upstreamsFile: "test-missing-localBindPort.json", + errors: []string{ + "Upstream fields .* are required.", + }, + }, + } + + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/upstreams-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "upstreams_file": c.upstreamsFile, + }, + }) + + if len(c.errors) == 0 { + require.NoError(t, err) + } else { + require.Error(t, err) + for _, regex := range c.errors { + require.Regexp(t, regex, out) + } + } + }) + } +} + +func TestValidation_EnvoyPublicListenerPort(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + port int + error string + }{ + "allowed-port": { + port: 21000, + }, + "too-high-port": { + port: 65536, + error: "The envoy_public_listener_port must be greater than 0 and less than or equal to 65535.", + }, + "disallowed-port": { + port: 19000, + error: "The envoy_public_listener_port must not conflict with the following ports that are reserved for Consul and Envoy", + }, + } + + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/public-listener-port-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) + + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + t.Parallel() + + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "envoy_public_listener_port": c.port, + }, + }) + + if c.error == "" { + require.NoError(t, err) + } else { + // handle multiline error messages. + regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") + require.Error(t, err) + require.Regexp(t, regex, out) + } + }) + } +} + +func TestValidation_EnvoyReadinessPort(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + port int + error string + }{ + "allowed-port": { + port: 23000, + }, + "too-high-port": { + port: 65536, + error: "The envoy_readiness_port must be greater than 0 and less than or equal to 65535.", + }, + "disallowed-port": { + port: 19000, + error: "The envoy_readiness_port must not conflict with the following ports that are reserved for Consul and Envoy", + }, + "conflicts-with-listener-port": { + port: 20000, + error: "envoy_public_listener_port should not conflict with envoy_readiness_port", + }, + } + + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/envoy-readiness-port-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) + + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + t.Parallel() + + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "envoy_readiness_port": c.port, + }, + }) + + if c.error == "" { + require.NoError(t, err) + } else { + // handle multiline error messages. + regex := strings.ReplaceAll(regexp.QuoteMeta(c.error), " ", "\\s+") + require.Error(t, err) + require.Regexp(t, regex, out) + } + }) + } +} + +func TestValidation_ConsulServiceName(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + serviceName string + error bool + }{ + "empty": {}, + "lowercase": { + serviceName: "lower-case-name", + }, + "uppercase": { + serviceName: "UPPER-CASE-NAME", + error: true, + }, + } + + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/service-name-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) + + for name, c := range cases { + c := c + + t.Run(name, func(t *testing.T) { + t.Parallel() + + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "consul_service_name": c.serviceName, + }, + }) + + if c.error { + require.Error(t, err) + require.Regexp(t, "The consul_service_name must be lower case.", out) + } else { + require.NoError(t, err) + } + }) + } + +} + +func TestValidation_ConsulEcsConfigVariable(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + configFile string + errors []string + }{ + "empty-map": { + configFile: "test-empty-config.json", + }, + "complete-config": { + configFile: "test-complete-config.json", + }, + "partial-config": { + configFile: "test-partial-config.json", + }, + "invalid-config": { + configFile: "test-invalid-config.json", + errors: []string{ + "Only the 'service', 'proxy', and 'consulLogin' fields are allowed in consul_ecs_config.", + "Only the 'enableTagOverride' and 'weights' fields are allowed in consul_ecs_config.service.", + "Only the 'meshGateway', 'expose', and 'config' fields are allowed in consul_ecs_config.proxy.", + "Only the 'mode' field is allowed in consul_ecs_config.proxy.meshGateway.", + "Only the 'checks' and 'paths' fields are allowed in consul_ecs_config.proxy.expose.", + "Only the 'listenerPort', 'path', 'localPathPort', and 'protocol' fields are allowed in each item of consul_ecs_config.proxy.expose.paths[*].", + "Only the 'enabled', 'method', 'includeEntity', 'meta', 'region', 'stsEndpoint', and 'serverIdHeaderValue' fields are allowed in consul_ecs_config.consulLogin.", + }, + }, + } + + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/consul-ecs-config-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) + + for name, c := range cases { + c := c + + t.Run(name, func(t *testing.T) { + t.Parallel() + + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "consul_ecs_config_file": c.configFile, + }, + }) + + if len(c.errors) == 0 { + require.NoError(t, err) + } else { + for _, msg := range c.errors { + // error messages are wrapped, so a space may turn into a newline. + regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") + require.Regexp(t, regex, out) + } + } + }) + } +} + +func TestValidation_HTTPConfig(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + configFile string + errors []string + }{ + "empty-map": { + configFile: "test-empty-config.json", + }, + "complete-config": { + configFile: "test-complete-config.json", + }, + "partial-config": { + configFile: "test-partial-config.json", + }, + "invalid-config": { + configFile: "test-invalid-config.json", + errors: []string{ + "Only the 'port', 'https', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in http_config.", + }, + }, + } + + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/http-config-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) + + for name, c := range cases { + c := c + + t.Run(name, func(t *testing.T) { + t.Parallel() + + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "http_config_file": c.configFile, + }, + }) + + if len(c.errors) == 0 { + require.NoError(t, err) + } else { + for _, msg := range c.errors { + // error messages are wrapped, so a space may turn into a newline. + regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") + require.Regexp(t, regex, out) + } + } + }) + } +} + +func TestValidation_GRPCConfig(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + configFile string + errors []string + }{ + "empty-map": { + configFile: "test-empty-config.json", + }, + "complete-config": { + configFile: "test-complete-config.json", + }, + "partial-config": { + configFile: "test-partial-config.json", + }, + "invalid-config": { + configFile: "test-invalid-config.json", + errors: []string{ + "Only the 'port', 'tls', 'tlsServerName' and 'caCertFile' fields are allowed in grpc_config.", + }, + }, + } + + terraformOptions := &terraform.Options{ + TerraformDir: "./terraform/grpc-config-validate", + NoColor: true, + } + terraform.Init(t, terraformOptions) + + for name, c := range cases { + c := c + + t.Run(name, func(t *testing.T) { + t.Parallel() + + out, err := terraform.PlanE(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: true, + Vars: map[string]interface{}{ + "grpc_config_file": c.configFile, + }, + }) + + if len(c.errors) == 0 { + require.NoError(t, err) + } else { + for _, msg := range c.errors { + // error messages are wrapped, so a space may turn into a newline. + regex := strings.ReplaceAll(regexp.QuoteMeta(msg), " ", "\\s+") + require.Regexp(t, regex, out) + } + } + }) + } +} + +// Test the validation that both partition and namespace must be provided or neither. +func TestValidation_NamespaceAndPartitionRequired(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + partition string + namespace string + errMsg string + }{ + "without partition and namespace": { + partition: "", + namespace: "", + errMsg: "", + }, + "with partition and namespace": { + partition: "default", + namespace: "default", + errMsg: "", + }, + "with partition, without namespace": { + partition: "default", + namespace: "", + errMsg: "ERROR: consul_namespace must be set if consul_partition is set", + }, + "without partition, with namespace": { + partition: "", + namespace: "default", + errMsg: "ERROR: consul_partition must be set if consul_namespace is set", + }, + } + + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: "./terraform/admin-partition-validate", + NoColor: true, + }) + _ = terraform.Init(t, terraformOptions) + + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + t.Parallel() + terraformOptions.Vars = map[string]interface{}{ + "partition": c.partition, + "namespace": c.namespace, + } + t.Cleanup(func() { + _, _ = terraform.DestroyE(t, terraformOptions) + }) + _, err := terraform.PlanE(t, terraformOptions) + if c.errMsg == "" { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), c.errMsg) + } + }) + } +} + +func TestValidation_RolePath(t *testing.T) { + t.Parallel() + + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: "./terraform/role-path-validate", + NoColor: true, + }) + _ = terraform.Init(t, terraformOptions) + + cases := []struct { + path string + expError bool + }{ + {"", true}, + {"test", true}, + {"/test", false}, + {"/test/", false}, + } + for _, c := range cases { + c := c + t.Run(fmt.Sprintf("path=%q", c.path), func(t *testing.T) { + t.Parallel() + + applyOpts := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: terraformOptions.NoColor, + Vars: map[string]interface{}{ + "iam_role_path": c.path, + }, + }) + + t.Cleanup(func() { + _, _ = terraform.DestroyE(t, applyOpts) + }) + _, err := terraform.PlanE(t, applyOpts) + if c.expError { + require.Error(t, err) + require.Contains(t, err.Error(), "iam_role_path must begin with '/'") + } else { + require.NoError(t, err) + } + + }) + } + +} + +func TestValidation_MeshGateway(t *testing.T) { + t.Skip("TODO(ganeshrockz): revisit this test when gateway modules are modified for the new architecture") + t.Parallel() + + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: "./terraform/mesh-gateway-validate", + NoColor: true, + }) + _ = terraform.Init(t, terraformOptions) + + cases := map[string]struct { + kind string + enableMeshGatewayWANFed bool + tls bool + securityGroups []string + wanAddress string + lbEnabled bool + lbVpcID string + lbSubnets []string + lbCreateSecGroup bool + lbModifySecGroup bool + lbModifySecGroupID string + expError string + }{ + "kind is required": { + kind: "", + enableMeshGatewayWANFed: false, + expError: `variable "kind" is not set`, + }, + "kind must be mesh-gateway": { + kind: "not-mesh-gateway", + enableMeshGatewayWANFed: false, + expError: `Gateway kind must be 'mesh-gateway'`, + }, + "no WAN federation": { + kind: "mesh-gateway", + enableMeshGatewayWANFed: false, + }, + "mesh gateway WAN federation, no TLS": { + kind: "mesh-gateway", + enableMeshGatewayWANFed: true, + tls: false, + expError: "tls must be true when enable_mesh_gateway_wan_federation is true", + }, + "mesh gateway WAN federation": { + kind: "mesh-gateway", + enableMeshGatewayWANFed: true, + tls: true, + }, + "WAN address and LB enabled": { + kind: "mesh-gateway", + enableMeshGatewayWANFed: false, + wanAddress: "10.1.2.3", + lbEnabled: true, + expError: "Only one of wan_address or lb_enabled may be provided", + }, + "lb_enabled": { + kind: "mesh-gateway", + lbEnabled: true, + lbSubnets: []string{"subnet"}, + lbVpcID: "vpc", + }, + "lb_enabled and no lb subnets": { + kind: "mesh-gateway", + lbEnabled: true, + lbVpcID: "vpc", + expError: "lb_subnets is required when lb_enabled is true", + }, + "lb_enabled and no VPC": { + kind: "mesh-gateway", + lbEnabled: true, + lbSubnets: []string{"subnet"}, + expError: "lb_vpc_id is required when lb_enabled is true", + }, + "lb create security group and modify security group": { + kind: "mesh-gateway", + securityGroups: []string{"sg"}, + lbEnabled: true, + lbSubnets: []string{"subnet"}, + lbVpcID: "vpc", + lbCreateSecGroup: true, + lbModifySecGroup: true, + expError: "Only one of lb_create_security_group or lb_modify_security_group may be true", + }, + "lb modify security group and no security group ID": { + kind: "mesh-gateway", + securityGroups: []string{"sg"}, + lbEnabled: true, + lbSubnets: []string{"subnet"}, + lbVpcID: "vpc", + lbCreateSecGroup: false, + lbModifySecGroup: true, + lbModifySecGroupID: "", + expError: "lb_modify_security_group_id is required when lb_modify_security_group is true", + }, + "lb modify security group with security group ID": { + kind: "mesh-gateway", + securityGroups: []string{"sg"}, + lbEnabled: true, + lbSubnets: []string{"subnet"}, + lbVpcID: "vpc", + lbCreateSecGroup: false, + lbModifySecGroup: true, + lbModifySecGroupID: "mod-sg", + }, + } + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + t.Parallel() + + tfVars := map[string]interface{}{ + "enable_mesh_gateway_wan_federation": c.enableMeshGatewayWANFed, + "tls": c.tls, + "security_groups": c.securityGroups, + "wan_address": c.wanAddress, + "lb_enabled": c.lbEnabled, + "lb_subnets": c.lbSubnets, + "lb_vpc_id": c.lbVpcID, + "lb_create_security_group": c.lbCreateSecGroup, + "lb_modify_security_group": c.lbModifySecGroup, + "lb_modify_security_group_id": c.lbModifySecGroupID, + } + if len(c.kind) > 0 { + tfVars["kind"] = c.kind + } + applyOpts := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: terraformOptions.TerraformDir, + NoColor: terraformOptions.NoColor, + Vars: tfVars, + }) + t.Cleanup(func() { _, _ = terraform.DestroyE(t, applyOpts) }) + + _, err := terraform.PlanE(t, applyOpts) + if len(c.expError) > 0 { + require.Error(t, err) + require.Contains(t, err.Error(), c.expError) + } else { + require.NoError(t, err) + } + }) + } +} From 3f940f7ca8444cd374ed00e7e1ea200f9ee3e575 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 31 Jul 2023 15:20:05 +0530 Subject: [PATCH 21/27] Terraform fmted --- test/acceptance/tests/basic/terraform/basic-install/main.tf | 6 +++--- .../validation/terraform/admin-partition-validate/main.tf | 2 +- .../validation/terraform/consul-ecs-config-validate/main.tf | 4 ++-- .../terraform/envoy-readiness-port-validate/main.tf | 6 +++--- .../tests/validation/terraform/grpc-config-validate/main.tf | 4 ++-- .../tests/validation/terraform/http-config-validate/main.tf | 4 ++-- .../tests/validation/terraform/pass-app-entrypoint/main.tf | 2 +- .../validation/terraform/pass-existing-iam-roles/main.tf | 4 ++-- .../pass-role-additional-policies-validate/main.tf | 4 ++-- .../terraform/public-listener-port-validate/main.tf | 2 +- .../tests/validation/terraform/role-path-validate/main.tf | 2 +- .../validation/terraform/service-name-validate/main.tf | 4 ++-- .../tests/validation/terraform/upstreams-validate/main.tf | 4 ++-- .../tests/validation/terraform/volume-variable/main.tf | 2 +- 14 files changed, 25 insertions(+), 25 deletions(-) diff --git a/test/acceptance/tests/basic/terraform/basic-install/main.tf b/test/acceptance/tests/basic/terraform/basic-install/main.tf index 81734741..1be2f823 100644 --- a/test/acceptance/tests/basic/terraform/basic-install/main.tf +++ b/test/acceptance/tests/basic/terraform/basic-install/main.tf @@ -145,7 +145,7 @@ module "ecs_controller" { } launch_type = var.launch_type consul_bootstrap_token_secret_arn = module.consul_server.bootstrap_token_secret_arn - consul_server_hosts = module.consul_server.server_dns + consul_server_hosts = module.consul_server.server_dns consul_grpc_ca_cert_arn = module.consul_server.ca_cert_arn consul_https_ca_cert_arn = module.consul_server.ca_cert_arn ecs_cluster_arn = var.ecs_cluster_arn @@ -276,8 +276,8 @@ module "test_server" { logConfiguration = local.test_server_log_configuration }] consul_server_hosts = module.consul_server.server_dns - log_configuration = local.test_server_log_configuration - port = 9090 + log_configuration = local.test_server_log_configuration + port = 9090 tls = var.secure consul_grpc_ca_cert_arn = var.secure ? module.consul_server.ca_cert_arn : "" diff --git a/test/acceptance/tests/validation/terraform/admin-partition-validate/main.tf b/test/acceptance/tests/validation/terraform/admin-partition-validate/main.tf index 3a849bfc..a222d9a7 100644 --- a/test/acceptance/tests/validation/terraform/admin-partition-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/admin-partition-validate/main.tf @@ -21,7 +21,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - outbound_only = true + outbound_only = true consul_server_hosts = "consul.dc1.host" consul_partition = var.partition diff --git a/test/acceptance/tests/validation/terraform/consul-ecs-config-validate/main.tf b/test/acceptance/tests/validation/terraform/consul-ecs-config-validate/main.tf index 5e6d6ca2..4445160b 100644 --- a/test/acceptance/tests/validation/terraform/consul-ecs-config-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/consul-ecs-config-validate/main.tf @@ -16,6 +16,6 @@ module "test_client" { name = "basic" }] consul_server_hosts = "consul.dc1.host" - outbound_only = true - consul_ecs_config = jsondecode(file("${path.module}/${var.consul_ecs_config_file}")) + outbound_only = true + consul_ecs_config = jsondecode(file("${path.module}/${var.consul_ecs_config_file}")) } diff --git a/test/acceptance/tests/validation/terraform/envoy-readiness-port-validate/main.tf b/test/acceptance/tests/validation/terraform/envoy-readiness-port-validate/main.tf index 779fafa7..e4bef758 100644 --- a/test/acceptance/tests/validation/terraform/envoy-readiness-port-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/envoy-readiness-port-validate/main.tf @@ -14,7 +14,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - consul_server_hosts = "consul.dc1" - outbound_only = true - envoy_readiness_port = var.envoy_readiness_port + consul_server_hosts = "consul.dc1" + outbound_only = true + envoy_readiness_port = var.envoy_readiness_port } \ No newline at end of file diff --git a/test/acceptance/tests/validation/terraform/grpc-config-validate/main.tf b/test/acceptance/tests/validation/terraform/grpc-config-validate/main.tf index 5a4c0acc..b6f2f8ec 100644 --- a/test/acceptance/tests/validation/terraform/grpc-config-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/grpc-config-validate/main.tf @@ -16,6 +16,6 @@ module "test_client" { name = "basic" }] consul_server_hosts = "consul.dc1.host" - outbound_only = true - grpc_config = jsondecode(file("${path.module}/${var.grpc_config_file}")) + outbound_only = true + grpc_config = jsondecode(file("${path.module}/${var.grpc_config_file}")) } \ No newline at end of file diff --git a/test/acceptance/tests/validation/terraform/http-config-validate/main.tf b/test/acceptance/tests/validation/terraform/http-config-validate/main.tf index 864ad289..e9d08cbe 100644 --- a/test/acceptance/tests/validation/terraform/http-config-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/http-config-validate/main.tf @@ -16,6 +16,6 @@ module "test_client" { name = "basic" }] consul_server_hosts = "consul.dc1.host" - outbound_only = true - http_config = jsondecode(file("${path.module}/${var.http_config_file}")) + outbound_only = true + http_config = jsondecode(file("${path.module}/${var.http_config_file}")) } \ No newline at end of file diff --git a/test/acceptance/tests/validation/terraform/pass-app-entrypoint/main.tf b/test/acceptance/tests/validation/terraform/pass-app-entrypoint/main.tf index 2a58c30b..dd0eecf1 100644 --- a/test/acceptance/tests/validation/terraform/pass-app-entrypoint/main.tf +++ b/test/acceptance/tests/validation/terraform/pass-app-entrypoint/main.tf @@ -19,7 +19,7 @@ module "test_client" { name = "basic" }] consul_server_hosts = "consul.dc1" - outbound_only = true + outbound_only = true application_shutdown_delay_seconds = var.application_shutdown_delay_seconds } diff --git a/test/acceptance/tests/validation/terraform/pass-existing-iam-roles/main.tf b/test/acceptance/tests/validation/terraform/pass-existing-iam-roles/main.tf index 521635e8..1acc9d7c 100644 --- a/test/acceptance/tests/validation/terraform/pass-existing-iam-roles/main.tf +++ b/test/acceptance/tests/validation/terraform/pass-existing-iam-roles/main.tf @@ -49,7 +49,7 @@ module "test_client_create_new_roles" { family = local.create_roles_family log_configuration = null container_definitions = local.container_definitions - consul_server_hosts = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true // Roles are not passed. This tests the default values for create_task_role, @@ -61,7 +61,7 @@ module "test_client_pass_existing_roles" { family = local.pass_roles_family log_configuration = null container_definitions = local.container_definitions - consul_server_hosts = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true create_task_role = false diff --git a/test/acceptance/tests/validation/terraform/pass-role-additional-policies-validate/main.tf b/test/acceptance/tests/validation/terraform/pass-role-additional-policies-validate/main.tf index a8f059d5..7291e551 100644 --- a/test/acceptance/tests/validation/terraform/pass-role-additional-policies-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/pass-role-additional-policies-validate/main.tf @@ -28,7 +28,7 @@ module "task_role_test" { family = "family-1" log_configuration = null container_definitions = local.container_definitions - consul_server_hosts = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true create_task_role = false @@ -46,7 +46,7 @@ module "execution_role_test" { family = "family-2" log_configuration = null container_definitions = local.container_definitions - consul_server_hosts = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true create_execution_role = false diff --git a/test/acceptance/tests/validation/terraform/public-listener-port-validate/main.tf b/test/acceptance/tests/validation/terraform/public-listener-port-validate/main.tf index e7e899e1..b12e5fae 100644 --- a/test/acceptance/tests/validation/terraform/public-listener-port-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/public-listener-port-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - consul_server_hosts = "consul.dc1" + consul_server_hosts = "consul.dc1" outbound_only = true envoy_public_listener_port = var.envoy_public_listener_port } diff --git a/test/acceptance/tests/validation/terraform/role-path-validate/main.tf b/test/acceptance/tests/validation/terraform/role-path-validate/main.tf index 2cb213ef..edc32630 100644 --- a/test/acceptance/tests/validation/terraform/role-path-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/role-path-validate/main.tf @@ -15,7 +15,7 @@ module "test_client" { container_definitions = [{ name = "basic" }] - outbound_only = true + outbound_only = true consul_server_hosts = "consul.dc1" iam_role_path = var.iam_role_path diff --git a/test/acceptance/tests/validation/terraform/service-name-validate/main.tf b/test/acceptance/tests/validation/terraform/service-name-validate/main.tf index 43431155..d6c7e875 100644 --- a/test/acceptance/tests/validation/terraform/service-name-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/service-name-validate/main.tf @@ -16,6 +16,6 @@ module "test_client" { name = "basic" }] consul_server_hosts = "consul.dc1" - outbound_only = true - consul_service_name = var.consul_service_name + outbound_only = true + consul_service_name = var.consul_service_name } diff --git a/test/acceptance/tests/validation/terraform/upstreams-validate/main.tf b/test/acceptance/tests/validation/terraform/upstreams-validate/main.tf index 5c26b638..28f0b72d 100644 --- a/test/acceptance/tests/validation/terraform/upstreams-validate/main.tf +++ b/test/acceptance/tests/validation/terraform/upstreams-validate/main.tf @@ -16,6 +16,6 @@ module "test_client" { name = "basic" }] consul_server_hosts = "consul.dc1" - outbound_only = true - upstreams = jsondecode(file("${path.module}/${var.upstreams_file}")) + outbound_only = true + upstreams = jsondecode(file("${path.module}/${var.upstreams_file}")) } diff --git a/test/acceptance/tests/validation/terraform/volume-variable/main.tf b/test/acceptance/tests/validation/terraform/volume-variable/main.tf index 2b8dffde..c85db85e 100644 --- a/test/acceptance/tests/validation/terraform/volume-variable/main.tf +++ b/test/acceptance/tests/validation/terraform/volume-variable/main.tf @@ -17,5 +17,5 @@ module "test_client" { name = "basic" }] consul_server_hosts = "consul.dc1" - outbound_only = true + outbound_only = true } From 75622486695264f7b1d72855e576013c2e669296 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 31 Jul 2023 17:29:06 +0530 Subject: [PATCH 22/27] Fix tests --- .../framework/helpers/ecs_commands.go | 91 +++++++++++++++ test/acceptance/tests/basic/basic_test.go | 104 ++++-------------- 2 files changed, 110 insertions(+), 85 deletions(-) create mode 100644 test/acceptance/framework/helpers/ecs_commands.go diff --git a/test/acceptance/framework/helpers/ecs_commands.go b/test/acceptance/framework/helpers/ecs_commands.go new file mode 100644 index 00000000..ee0a0132 --- /dev/null +++ b/test/acceptance/framework/helpers/ecs_commands.go @@ -0,0 +1,91 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package helpers + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/ecs" + "github.com/gruntwork-io/terratest/modules/shell" +) + +type ListTasksResponse struct { + TaskARNs []string `json:"taskArns"` +} + +func ListTasks(t *testing.T, clusterARN, region, family string) (*ListTasksResponse, error) { + args := []string{ + "ecs", + "list-tasks", + "--region", region, + "--cluster", clusterARN, + "--family", family, + } + + taskListOut, err := shell.RunCommandAndGetOutputE(t, shell.Command{ + Command: "aws", + Args: args, + }) + if err != nil { + return nil, fmt.Errorf("failed to run `aws %v`: %w", args, err) + } + + var tasks *ListTasksResponse + err = json.Unmarshal([]byte(taskListOut), &tasks) + if err != nil { + return nil, err + } + + return tasks, nil +} + +func DescribeTasks(t *testing.T, clusterARN, region, taskARN string) (*ecs.DescribeTasksOutput, error) { + args := []string{ + "ecs", + "describe-tasks", + "--region", region, + "--cluster", clusterARN, + "--task", taskARN, + } + + taskListOut, err := shell.RunCommandAndGetOutputE(t, shell.Command{ + Command: "aws", + Args: args, + }) + if err != nil { + return nil, fmt.Errorf("failed to run `aws %v`: %w", args, err) + } + + var tasks *ecs.DescribeTasksOutput + err = json.Unmarshal([]byte(taskListOut), &tasks) + if err != nil { + return nil, err + } + + return tasks, nil +} + +func StopTask(t *testing.T, clusterARN, region, taskARN, reason string) string { + args := []string{ + "ecs", + "stop-task", + "--region", region, + "--cluster", clusterARN, + "--task", taskARN, + "--reason", reason, + } + + return shell.RunCommandAndGetOutput(t, shell.Command{ + Command: "aws", + Args: args, + }) +} + +func GetTaskIDFromARN(taskARN string) string { + arnParts := strings.Split(taskARN, "/") + return arnParts[len(arnParts)-1] +} diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index aa40d267..0d9aed85 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -4,7 +4,6 @@ package basic import ( - "encoding/json" "fmt" "os" "regexp" @@ -12,9 +11,7 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/service/ecs" "github.com/gruntwork-io/terratest/modules/random" - "github.com/gruntwork-io/terratest/modules/shell" "github.com/gruntwork-io/terratest/modules/terraform" "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/terraform-aws-consul-ecs/test/acceptance/framework/helpers" @@ -34,7 +31,7 @@ func TestBasic(t *testing.T) { }{ {secure: false}, {secure: true}, - //{secure: true, enterprise: true}, + {secure: true, enterprise: true}, } cfg := suite.Config() @@ -115,54 +112,24 @@ func TestBasic(t *testing.T) { // Wait for consul server to be up. var consulServerTaskARN string retry.RunWith(&retry.Timer{Timeout: 3 * time.Minute, Wait: 10 * time.Second}, t, func(r *retry.R) { - taskListOut, err := shell.RunCommandAndGetOutputE(t, shell.Command{ - Command: "aws", - Args: []string{ - "ecs", - "list-tasks", - "--region", - cfg.Region, - "--cluster", - c.ecsClusterARN, - "--family", - fmt.Sprintf("consul_server_%s", randomSuffix), - }, - }) - r.Check(err) - - var tasks listTasksResponse - r.Check(json.Unmarshal([]byte(taskListOut), &tasks)) - if len(tasks.TaskARNs) != 1 { - r.Errorf("expected 1 task, got %d", len(tasks.TaskARNs)) - return - } + tasks, err := helpers.ListTasks(t, c.ecsClusterARN, cfg.Region, fmt.Sprintf("consul_server_%s", randomSuffix)) + r.Check(err) + require.NotNil(t, tasks) + require.Len(r, tasks.TaskARNs, 1) consulServerTaskARN = tasks.TaskARNs[0] }) var controllerTaskID string if c.secure { retry.RunWith(&retry.Timer{Timeout: 2 * time.Minute, Wait: 30 * time.Second}, t, func(r *retry.R) { - taskListOut := shell.RunCommandAndGetOutput(t, shell.Command{ - Command: "aws", - Args: []string{ - "ecs", - "list-tasks", - "--region", - cfg.Region, - "--cluster", - c.ecsClusterARN, - "--family", - fmt.Sprintf("%s-consul-ecs-controller", randomSuffix), - }, - }) - - var tasks listTasksResponse - require.NoError(r, json.Unmarshal([]byte(taskListOut), &tasks)) + tasks, err := helpers.ListTasks(t, c.ecsClusterARN, cfg.Region, fmt.Sprintf("%s-consul-ecs-controller", randomSuffix)) + + r.Check(err) + require.NotNil(t, tasks) require.Len(r, tasks.TaskARNs, 1) - controllerTaskARN := tasks.TaskARNs[0] - arnParts := strings.Split(controllerTaskARN, "/") - controllerTaskID = arnParts[len(arnParts)-1] + + controllerTaskID = helpers.GetTaskIDFromARN(tasks.TaskARNs[0]) }) // Check controller logs to see if the anonymous token gets configured. This should @@ -225,26 +192,13 @@ func TestBasic(t *testing.T) { }) // Use aws exec to curl between the apps. - taskListOut := shell.RunCommandAndGetOutput(t, shell.Command{ - Command: "aws", - Args: []string{ - "ecs", - "list-tasks", - "--region", - cfg.Region, - "--cluster", - c.ecsClusterARN, - "--family", - fmt.Sprintf("Test_Client_%s", randomSuffix), - }, - }) + tasks, err := helpers.ListTasks(t, c.ecsClusterARN, cfg.Region, fmt.Sprintf("Test_Client_%s", randomSuffix)) - var tasks listTasksResponse - require.NoError(t, json.Unmarshal([]byte(taskListOut), &tasks)) + require.NoError(t, err) require.Len(t, tasks.TaskARNs, 1) + testClientTaskARN := tasks.TaskARNs[0] - arnParts := strings.Split(testClientTaskARN, "/") - testClientTaskID := arnParts[len(arnParts)-1] + testClientTaskID := helpers.GetTaskIDFromARN(tasks.TaskARNs[0]) // Create an intention. if c.secure { @@ -276,34 +230,14 @@ func TestBasic(t *testing.T) { // * a custom entrypoint for the client app that keeps it running for 10s into Task shutdown, and // * an additional "shutdown-monitor" container that makes requests to the client app // Since this is timing dependent, we check logs after the fact to validate when the containers exited. - shell.RunCommandAndGetOutput(t, shell.Command{ - Command: "aws", - Args: []string{ - "ecs", - "stop-task", - "--region", cfg.Region, - "--cluster", c.ecsClusterARN, - "--task", testClientTaskARN, - "--reason", "Stopped to validate graceful shutdown in acceptance tests", - }, - }) + helpers.StopTask(t, c.ecsClusterARN, cfg.Region, testClientTaskARN, "Stopped to validate graceful shutdown in acceptance tests") // Wait for the task to stop (~30 seconds) retry.RunWith(&retry.Timer{Timeout: 1 * time.Minute, Wait: 10 * time.Second}, t, func(r *retry.R) { - describeTasksOut, err := shell.RunCommandAndGetOutputE(t, shell.Command{ - Command: "aws", - Args: []string{ - "ecs", - "describe-tasks", - "--region", cfg.Region, - "--cluster", c.ecsClusterARN, - "--task", testClientTaskARN, - }, - }) - r.Check(err) + describeTasks, err := helpers.DescribeTasks(t, c.ecsClusterARN, cfg.Region, testClientTaskARN) - var describeTasks ecs.DescribeTasksOutput - r.Check(json.Unmarshal([]byte(describeTasksOut), &describeTasks)) + r.Check(err) + require.NotNil(r, describeTasks) require.Len(r, describeTasks.Tasks, 1) require.NotEqual(r, "RUNNING", describeTasks.Tasks[0].LastStatus) }) From 2bb4c2cf5d100ab011fb1acb60769f696cd507ed Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 31 Jul 2023 21:03:45 +0530 Subject: [PATCH 23/27] Fix go fmt --- test/acceptance/tests/basic/basic_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index 0d9aed85..2734dbc7 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -302,7 +302,3 @@ func TestBasic(t *testing.T) { }) } } - -type listTasksResponse struct { - TaskARNs []string `json:"taskArns"` -} From 4931537d9964206bfc34f3192aa6fd260e13600e Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Mon, 31 Jul 2023 22:04:25 +0530 Subject: [PATCH 24/27] Fix golang ci --- test/acceptance/tests/basic/basic_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/acceptance/tests/basic/basic_test.go b/test/acceptance/tests/basic/basic_test.go index 2734dbc7..9a468eb4 100644 --- a/test/acceptance/tests/basic/basic_test.go +++ b/test/acceptance/tests/basic/basic_test.go @@ -115,7 +115,7 @@ func TestBasic(t *testing.T) { tasks, err := helpers.ListTasks(t, c.ecsClusterARN, cfg.Region, fmt.Sprintf("consul_server_%s", randomSuffix)) r.Check(err) - require.NotNil(t, tasks) + require.NotNil(r, tasks) require.Len(r, tasks.TaskARNs, 1) consulServerTaskARN = tasks.TaskARNs[0] }) @@ -126,7 +126,7 @@ func TestBasic(t *testing.T) { tasks, err := helpers.ListTasks(t, c.ecsClusterARN, cfg.Region, fmt.Sprintf("%s-consul-ecs-controller", randomSuffix)) r.Check(err) - require.NotNil(t, tasks) + require.NotNil(r, tasks) require.Len(r, tasks.TaskARNs, 1) controllerTaskID = helpers.GetTaskIDFromARN(tasks.TaskARNs[0]) From de756ccd070d3ddf4b4933f23e633efa43e1bc71 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Tue, 1 Aug 2023 22:11:13 +0530 Subject: [PATCH 25/27] Address comments --- .github/workflows/terraform-ci.yml | 1 + modules/controller/README.md | 2 +- modules/controller/variables.tf | 6 +++--- modules/mesh-task/variables.tf | 2 +- test/acceptance/setup-terraform/main.tf | 2 +- test/acceptance/setup-terraform/variables.tf | 5 +++++ 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/terraform-ci.yml b/.github/workflows/terraform-ci.yml index 52b37020..1355d530 100644 --- a/.github/workflows/terraform-ci.yml +++ b/.github/workflows/terraform-ci.yml @@ -176,6 +176,7 @@ jobs: VARS="-var tags={\"build_url\":\"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID\"}" VARS+=' -var launch_type=${{ matrix.launch-type }}' VARS+=' -var consul_version=${{ matrix.consul-version }}' + VARS+=' -var hcp_project_id=${{ secrets.HCP_PROJECT_ID }}' case $GITHUB_REF_NAME in main | release/*) VARS+=" -var enable_hcp=${{ matrix.enable-hcp }}";; *) VARS+=" -var enable_hcp=false";; diff --git a/modules/controller/README.md b/modules/controller/README.md index 3e7495b6..dd3b8fe2 100644 --- a/modules/controller/README.md +++ b/modules/controller/README.md @@ -1,5 +1,5 @@ # ECS Controller -This module deploys a Consul ECS controller for managing tokens and consul services for tasks on the Consul service mesh. +This module deploys a Consul ECS controller for managing tokens and Consul services for tasks on the Consul service mesh. See https://developer.hashicorp.com/consul/docs/ecs for additional documentation. diff --git a/modules/controller/variables.tf b/modules/controller/variables.tf index d91d331e..af10a065 100644 --- a/modules/controller/variables.tf +++ b/modules/controller/variables.tf @@ -30,7 +30,7 @@ variable "launch_type" { } variable "consul_bootstrap_token_secret_arn" { - description = "The ARN of the AWS SecretsManager secret containing the token to be used by this controller. The token needs to have at least `acl:write`, `node:write` and `operator:write`(in case of Consul Enterprise) privileges in Consul." + description = "The ARN of the AWS SecretsManager secret containing the token to be used by this controller. The token needs to have at least `acl:write`, `node:write` and `operator:write` (in case of Consul Enterprise) privileges in Consul." type = string } @@ -57,7 +57,7 @@ variable "consul_server_hosts" { } variable "skip_server_watch" { - description = "If true, setting this prevents the consul-dataplane and consul-ecs-control-plane from watching the Consul servers for changes. This is useful for situations where Consul servers are behind a load balancer." + description = "Set this to true to prevent the consul-dataplane and consul-ecs-control-plane from watching the Consul servers for changes. This is useful for situations where Consul servers are behind a load balancer." type = bool default = false } @@ -74,7 +74,7 @@ variable "consul_ca_cert_arn" { } variable "consul_grpc_ca_cert_arn" { - description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's gRPC. Overrides var.consul_ca_cert_arn" + description = "The ARN of the Secrets Manager secret containing the Consul server CA certificate for Consul's gRPC interface. Overrides var.consul_ca_cert_arn" type = string default = "" } diff --git a/modules/mesh-task/variables.tf b/modules/mesh-task/variables.tf index d0ee4baf..0d48fd93 100644 --- a/modules/mesh-task/variables.tf +++ b/modules/mesh-task/variables.tf @@ -125,7 +125,7 @@ variable "additional_execution_role_policies" { } variable "skip_server_watch" { - description = "If true, setting this prevents the consul-dataplane and consul-ecs-control-plane from watching the Consul servers for changes. This is useful for situations where Consul servers are behind a load balancer." + description = "Set this to true to prevent the consul-dataplane and consul-ecs-control-plane from watching the Consul servers for changes. This is useful for situations where Consul servers are behind a load balancer." type = bool default = false } diff --git a/test/acceptance/setup-terraform/main.tf b/test/acceptance/setup-terraform/main.tf index a62b9eb5..4588fdfc 100644 --- a/test/acceptance/setup-terraform/main.tf +++ b/test/acceptance/setup-terraform/main.tf @@ -6,7 +6,7 @@ provider "aws" { } provider "hcp" { - project_id = "6a513c39-907d-4a92-9159-3b03bd00ecda" + project_id = var.hcp_project_id } locals { diff --git a/test/acceptance/setup-terraform/variables.tf b/test/acceptance/setup-terraform/variables.tf index 6abaa09d..76906068 100644 --- a/test/acceptance/setup-terraform/variables.tf +++ b/test/acceptance/setup-terraform/variables.tf @@ -48,3 +48,8 @@ variable "consul_version" { error_message = "Must a valid MAJOR.MINOR.PATCH version string." } } + +variable "hcp_project_id" { + description = "ID of the HCP project where the Consul specific resources will be created." + type = string +} From 5e0735e9c6b6d6976e4364be914da541b46daca2 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Tue, 1 Aug 2023 22:44:23 +0530 Subject: [PATCH 26/27] Add project to destroy --- .github/workflows/terraform-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/terraform-ci.yml b/.github/workflows/terraform-ci.yml index 1355d530..5f72f658 100644 --- a/.github/workflows/terraform-ci.yml +++ b/.github/workflows/terraform-ci.yml @@ -200,6 +200,7 @@ jobs: VARS="-var tags={\"build_url\":\"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID\"}" VARS+=' -var launch_type=${{ matrix.launch-type }}' VARS+=' -var consul_version=${{ matrix.consul-version }}' + VARS+=' -var hcp_project_id=${{ secrets.HCP_PROJECT_ID }}' case $GITHUB_REF_NAME in main | release/*) VARS+=" -var enable_hcp=${{ matrix.enable-hcp }}";; *) VARS+=" -var enable_hcp=false";; From 137ab4f2bbadeb3c18e3a9e4386789e8ad77a9b8 Mon Sep 17 00:00:00 2001 From: Ganeshrockz Date: Wed, 2 Aug 2023 14:36:14 +0530 Subject: [PATCH 27/27] Fix tests --- test/acceptance/tests/basic/terraform/basic-install/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/acceptance/tests/basic/terraform/basic-install/main.tf b/test/acceptance/tests/basic/terraform/basic-install/main.tf index 1be2f823..4a26bc9a 100644 --- a/test/acceptance/tests/basic/terraform/basic-install/main.tf +++ b/test/acceptance/tests/basic/terraform/basic-install/main.tf @@ -38,7 +38,7 @@ variable "tags" { } variable "secure" { - description = "Whether to create all resources in a secure installation (with TLS, ACLs and gossip encryption)." + description = "Whether to create all resources in a secure installation (with TLS and ACLs)." type = bool default = false }