From ef3954c561ec693ce476550fab3e79e8c3068cc3 Mon Sep 17 00:00:00 2001 From: Michael Li Date: Mon, 31 Jul 2023 09:10:21 -0400 Subject: [PATCH] test(e2e): Add a docker test runner (#3499) * test(e2e): Add a docker test runner * !fixup: Self-review * !fixup: Make vault address compatible with local runs * !fixup: Remove unnecessary modification * !fixup: Remove arm64 requirement * !fixup: extend timeout --- .github/workflows/enos-run.yml | 17 +- enos/README.md | 4 +- enos/enos-modules.hcl | 4 + ...os-scenario-e2e-docker-base-with-vault.hcl | 22 +- enos/enos-scenario-e2e-docker-base.hcl | 22 +- enos/enos-scenario-e2e-ui.hcl | 48 ++-- enos/enos-variables.hcl | 17 +- enos/enos.vars.hcl | 5 +- .../modules/build_boundary_docker_crt/main.tf | 9 + .../build_boundary_docker_local/build.sh | 6 + .../build_boundary_docker_local/main.tf | 20 +- enos/modules/docker_boundary/main.tf | 2 +- enos/modules/test_e2e/main.tf | 2 +- enos/modules/test_e2e_docker/main.tf | 226 ++++++++++++++++++ enos/modules/test_e2e_docker/test.sh | 86 +++++++ enos/modules/test_e2e_docker/test_runner.sh | 46 ++++ enos/modules/test_e2e_ui/main.tf | 4 +- 17 files changed, 471 insertions(+), 69 deletions(-) create mode 100644 enos/modules/test_e2e_docker/main.tf create mode 100755 enos/modules/test_e2e_docker/test.sh create mode 100644 enos/modules/test_e2e_docker/test_runner.sh diff --git a/.github/workflows/enos-run.yml b/.github/workflows/enos-run.yml index ff110c8402..4b9de0fbc2 100644 --- a/.github/workflows/enos-run.yml +++ b/.github/workflows/enos-run.yml @@ -89,13 +89,15 @@ jobs: ENOS_VAR_aws_ssh_keypair_name: ${{ github.event.repository.name }}-ci-ssh-key ENOS_VAR_aws_ssh_private_key_path: ./support/private_key.pem ENOS_VAR_local_boundary_dir: ./support/boundary - ENOS_VAR_local_boundary_ui_dir: ./support/boundary-ui + ENOS_VAR_local_boundary_src_dir: ${{ github.workspace }} + ENOS_VAR_local_boundary_ui_src_dir: ./support/src/boundary-ui ENOS_VAR_crt_bundle_path: ./support/boundary.zip ENOS_VAR_tfc_api_token: ${{ secrets.TF_API_TOKEN }} ENOS_VAR_test_email: ${{ secrets.SERVICE_USER_EMAIL }} ENOS_VAR_boundary_edition: ${{ inputs.edition }} ENOS_VAR_boundary_docker_image_name: ${{ inputs.docker-image-name }} ENOS_VAR_boundary_docker_image_file: ./support/boundary_docker_image.tar + ENOS_VAR_go_version: ${{ inputs.go-version }} steps: - name: Checkout uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 @@ -184,11 +186,6 @@ jobs: if: matrix.filter == 'e2e_aws_base_with_vault builder:crt' || matrix.filter == 'e2e_database' || matrix.filter == 'e2e_ui builder:crt' || matrix.filter == 'e2e_docker_base_with_vault builder:crt' run: | unzip /tmp/test-deps/vault.zip -d /usr/local/bin - - name: Add hosts to /etc/hosts - # This enables the use of `boundary connect` with docker containers - if: contains(matrix.filter, 'e2e_docker') - run: | - sudo echo "127.0.0.1 localhost boundary" | sudo tee -a /etc/hosts - name: GH fix for localhost resolution if: github.repository == 'hashicorp/boundary' && contains(matrix.filter, 'e2e_docker') run: | @@ -227,14 +224,14 @@ jobs: if: matrix.filter == 'e2e_ui builder:crt' with: repository: hashicorp/boundary-ui - path: enos/support/boundary-ui + path: enos/support/src/boundary-ui - name: Install boundary-ui dependencies if: matrix.filter == 'e2e_ui builder:crt' - run: yarn --cwd enos/support/boundary-ui install + run: yarn --cwd enos/support/src/boundary-ui install - name: Install playwright dependencies (i.e. browsers) if: matrix.filter == 'e2e_ui builder:crt' run: npx playwright install --with-deps - working-directory: enos/support/boundary-ui + working-directory: enos/support/src/boundary-ui - name: Output Terraform version info run: | mkdir -p ./enos/terraform-plugin-cache @@ -260,7 +257,7 @@ jobs: uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: test-e2e-ui-debug - path: enos/support/boundary-ui/ui/admin/tests/e2e/artifacts/test-failures + path: enos/support/src/boundary-ui/ui/admin/tests/e2e/artifacts/test-failures retention-days: 5 - name: Retry Enos scenario id: run_retry diff --git a/enos/README.md b/enos/README.md index 5879aa912c..45f12443f6 100644 --- a/enos/README.md +++ b/enos/README.md @@ -85,7 +85,7 @@ It support either a local build or the output of the `build` workflow (CRT). All test scenarios create a Boundary cluster consisting of an RDS database, 1 worker, and 1 controller (behind an ALB). The count and instance type for workers and controllers is configurable. All tests require that a local copy of `boundary` -is availble in the `local_boundary_dir` to access the Boundary cluster API +is available in the `local_boundary_dir` to access the Boundary cluster API through the ALB. For example, if you install `boundary` locally via `make install` you could test that version against the cluster by setting `local_boundary_dir` to `/Users//.go/bin`, or wherever you have configured `$GOPATH/bin`. @@ -151,7 +151,7 @@ Here are the steps to configure the GitHub Actions service user: 1. **Create the Terraform Cloud Workspace** - The name of the workspace to be created depends on the repository for which it is being created, but the pattern is: `-ci-service-user-iam`, - e.g. `boundary-ci-service-user-iam`. It is important that the execution mode for the workspace be + e.g. `boundary-ci-service-user-iam`. It is important that the execution mode for the workspace be set to `local`. For help on setting up the workspace, contact the QT team on Slack (#team-quality) diff --git a/enos/enos-modules.hcl b/enos/enos-modules.hcl index 517bbb3de4..5229ca30b8 100644 --- a/enos/enos-modules.hcl +++ b/enos/enos-modules.hcl @@ -130,6 +130,10 @@ module "test_e2e_ui" { source = "./modules/test_e2e_ui" } +module "test_e2e_docker" { + source = "./modules/test_e2e_docker" +} + module "test_smoke" { source = "./modules/test_smoke" } diff --git a/enos/enos-scenario-e2e-docker-base-with-vault.hcl b/enos/enos-scenario-e2e-docker-base-with-vault.hcl index 765ac52787..6a266c01dd 100644 --- a/enos/enos-scenario-e2e-docker-base-with-vault.hcl +++ b/enos/enos-scenario-e2e-docker-base-with-vault.hcl @@ -18,9 +18,14 @@ scenario "e2e_docker_base_with_vault" { locals { aws_ssh_private_key_path = abspath(var.aws_ssh_private_key_path) local_boundary_dir = abspath(var.local_boundary_dir) + local_boundary_src_dir = abspath(var.local_boundary_src_dir) boundary_docker_image_file = abspath(var.boundary_docker_image_file) license_path = abspath(var.boundary_license_path != null ? var.boundary_license_path : joinpath(path.root, "./support/boundary.hclic")) + build_path = { + "local" = "/tmp", + "crt" = var.crt_bundle_path == null ? null : abspath(var.crt_bundle_path) + } tags = merge({ "Project Name" : var.project_name "Project" : "Enos", @@ -32,7 +37,8 @@ scenario "e2e_docker_base_with_vault" { module = matrix.builder == "crt" ? module.build_boundary_docker_crt : module.build_boundary_docker_local variables { - path = matrix.builder == "crt" ? local.boundary_docker_image_file : "/tmp/boundary_docker_image.tar" + path = matrix.builder == "crt" ? local.boundary_docker_image_file : "" + cli_build_path = local.build_path[matrix.builder] } } @@ -99,22 +105,24 @@ scenario "e2e_docker_base_with_vault" { } step "run_e2e_test" { - module = module.test_e2e + module = module.test_e2e_docker depends_on = [ step.create_boundary, step.create_vault, step.create_host, - step.create_boundary_database ] - variables { test_package = "github.com/hashicorp/boundary/testing/internal/e2e/tests/base_with_vault" + docker_mirror = var.docker_mirror + network_name = step.create_docker_network.network_name + go_version = var.go_version debug_no_run = var.e2e_debug_no_run alb_boundary_api_addr = step.create_boundary.address auth_method_id = step.create_boundary.auth_method_id auth_login_name = step.create_boundary.login_name auth_password = step.create_boundary.password - local_boundary_dir = local.local_boundary_dir + local_boundary_dir = step.build_boundary_docker_image.cli_zip_path + local_boundary_src_dir = local.local_boundary_src_dir aws_ssh_private_key_path = local.aws_ssh_private_key_path target_ip = step.create_host.address target_port = step.create_host.port @@ -125,8 +133,4 @@ scenario "e2e_docker_base_with_vault" { vault_port = step.create_vault.port } } - - output "test_results" { - value = step.run_e2e_test.test_results - } } diff --git a/enos/enos-scenario-e2e-docker-base.hcl b/enos/enos-scenario-e2e-docker-base.hcl index dd3c44c5bf..a2e896e0c4 100644 --- a/enos/enos-scenario-e2e-docker-base.hcl +++ b/enos/enos-scenario-e2e-docker-base.hcl @@ -18,9 +18,14 @@ scenario "e2e_docker_base" { locals { aws_ssh_private_key_path = abspath(var.aws_ssh_private_key_path) local_boundary_dir = abspath(var.local_boundary_dir) + local_boundary_src_dir = abspath(var.local_boundary_src_dir) boundary_docker_image_file = abspath(var.boundary_docker_image_file) license_path = abspath(var.boundary_license_path != null ? var.boundary_license_path : joinpath(path.root, "./support/boundary.hclic")) + build_path = { + "local" = "/tmp", + "crt" = var.crt_bundle_path == null ? null : abspath(var.crt_bundle_path) + } tags = merge({ "Project Name" : var.project_name "Project" : "Enos", @@ -32,7 +37,8 @@ scenario "e2e_docker_base" { module = matrix.builder == "crt" ? module.build_boundary_docker_crt : module.build_boundary_docker_local variables { - path = matrix.builder == "crt" ? local.boundary_docker_image_file : "/tmp/boundary_docker_image.tar" + path = matrix.builder == "crt" ? local.boundary_docker_image_file : "" + cli_build_path = local.build_path[matrix.builder] } } @@ -88,29 +94,27 @@ scenario "e2e_docker_base" { } step "run_e2e_test" { - module = module.test_e2e + module = module.test_e2e_docker depends_on = [ step.create_boundary, step.create_host, - step.create_boundary_database ] - variables { test_package = "github.com/hashicorp/boundary/testing/internal/e2e/tests/base" + docker_mirror = var.docker_mirror + network_name = step.create_docker_network.network_name + go_version = var.go_version debug_no_run = var.e2e_debug_no_run alb_boundary_api_addr = step.create_boundary.address auth_method_id = step.create_boundary.auth_method_id auth_login_name = step.create_boundary.login_name auth_password = step.create_boundary.password - local_boundary_dir = local.local_boundary_dir + local_boundary_dir = step.build_boundary_docker_image.cli_zip_path + local_boundary_src_dir = local.local_boundary_src_dir aws_ssh_private_key_path = local.aws_ssh_private_key_path target_ip = step.create_host.address target_port = step.create_host.port target_user = "ubuntu" } } - - output "test_results" { - value = step.run_e2e_test.test_results - } } diff --git a/enos/enos-scenario-e2e-ui.hcl b/enos/enos-scenario-e2e-ui.hcl index 78178fdb0b..2b6ae75d7f 100644 --- a/enos/enos-scenario-e2e-ui.hcl +++ b/enos/enos-scenario-e2e-ui.hcl @@ -14,11 +14,11 @@ scenario "e2e_ui" { } locals { - aws_ssh_private_key_path = abspath(var.aws_ssh_private_key_path) - boundary_install_dir = abspath(var.boundary_install_dir) - license_path = abspath(var.boundary_license_path != null ? var.boundary_license_path : joinpath(path.root, "./support/boundary.hclic")) - local_boundary_dir = abspath(var.local_boundary_dir) - local_boundary_ui_dir = abspath(var.local_boundary_ui_dir) + aws_ssh_private_key_path = abspath(var.aws_ssh_private_key_path) + boundary_install_dir = abspath(var.boundary_install_dir) + license_path = abspath(var.boundary_license_path != null ? var.boundary_license_path : joinpath(path.root, "./support/boundary.hclic")) + local_boundary_dir = abspath(var.local_boundary_dir) + local_boundary_ui_src_dir = abspath(var.local_boundary_ui_src_dir) build_path = { "local" = "/tmp", "crt" = var.crt_bundle_path == null ? null : abspath(var.crt_bundle_path) @@ -212,25 +212,25 @@ scenario "e2e_ui" { ] variables { - debug_no_run = var.e2e_debug_no_run - alb_boundary_api_addr = step.create_boundary_cluster.alb_boundary_api_addr - auth_method_id = step.create_boundary_cluster.auth_method_id - auth_login_name = step.create_boundary_cluster.auth_login_name - auth_password = step.create_boundary_cluster.auth_password - local_boundary_dir = local.local_boundary_dir - local_boundary_ui_dir = local.local_boundary_ui_dir - aws_ssh_private_key_path = local.aws_ssh_private_key_path - target_ip = step.create_targets_with_tag1.target_ips[0] - target_user = "ubuntu" - target_port = "22" - vault_addr = step.create_vault_cluster.instance_public_ips[0] - vault_root_token = step.create_vault_cluster.vault_root_token - aws_access_key_id = step.iam_setup.access_key_id - aws_secret_access_key = step.iam_setup.secret_access_key - aws_host_set_filter1 = step.create_tag1_inputs.tag_string - aws_host_set_ips1 = step.create_targets_with_tag1.target_ips - aws_host_set_filter2 = step.create_tag2_inputs.tag_string - aws_host_set_ips2 = step.create_targets_with_tag2.target_ips + debug_no_run = var.e2e_debug_no_run + alb_boundary_api_addr = step.create_boundary_cluster.alb_boundary_api_addr + auth_method_id = step.create_boundary_cluster.auth_method_id + auth_login_name = step.create_boundary_cluster.auth_login_name + auth_password = step.create_boundary_cluster.auth_password + local_boundary_dir = local.local_boundary_dir + local_boundary_ui_src_dir = local.local_boundary_ui_src_dir + aws_ssh_private_key_path = local.aws_ssh_private_key_path + target_ip = step.create_targets_with_tag1.target_ips[0] + target_user = "ubuntu" + target_port = "22" + vault_addr = step.create_vault_cluster.instance_public_ips[0] + vault_root_token = step.create_vault_cluster.vault_root_token + aws_access_key_id = step.iam_setup.access_key_id + aws_secret_access_key = step.iam_setup.secret_access_key + aws_host_set_filter1 = step.create_tag1_inputs.tag_string + aws_host_set_ips1 = step.create_targets_with_tag1.target_ips + aws_host_set_filter2 = step.create_tag2_inputs.tag_string + aws_host_set_ips2 = step.create_targets_with_tag2.target_ips } } diff --git a/enos/enos-variables.hcl b/enos/enos-variables.hcl index 1c1c7d7cdb..95fa1a824f 100644 --- a/enos/enos-variables.hcl +++ b/enos/enos-variables.hcl @@ -96,8 +96,13 @@ variable "local_boundary_dir" { type = string } -variable "local_boundary_ui_dir" { - description = "Path to local boundary-ui directory" +variable "local_boundary_src_dir" { + description = "Path to local boundary source code directory" + type = string +} + +variable "local_boundary_ui_src_dir" { + description = "Path to local boundary-ui source code directory" type = string default = null } @@ -150,7 +155,7 @@ variable "e2e_debug_no_run" { } variable "docker_mirror" { - description = "" + description = "URL to the docker repository" type = string default = "docker.mirror.hashicorp.services" } @@ -183,3 +188,9 @@ variable "aws_region" { description = "AWS region where the resources will be created" type = string } + +variable "go_version" { + description = "Version of Golang used by the application under test" + type = string + default = "" +} diff --git a/enos/enos.vars.hcl b/enos/enos.vars.hcl index 4666789a5b..c4379e1d3a 100644 --- a/enos/enos.vars.hcl +++ b/enos/enos.vars.hcl @@ -29,8 +29,11 @@ // similar to what is listed below. // local_boundary_dir = "/Users//.go/bin" +// The directory that contains the copy of boundary you want to use for e2e tests +// local_boundary_src_dir = "/Users//Developer/boundary" + // The directory that contains the copy of boundary-ui you want to use for UI tests -// local_boundary_ui_dir = "/Users//Developer/boundary-ui" +// local_boundary_ui_src_dir = "/Users//Developer/boundary-ui" // Path to a license file if required // boundary_license_path = "./support/boundary.hclic" diff --git a/enos/modules/build_boundary_docker_crt/main.tf b/enos/modules/build_boundary_docker_crt/main.tf index d079570edc..531e7b0625 100644 --- a/enos/modules/build_boundary_docker_crt/main.tf +++ b/enos/modules/build_boundary_docker_crt/main.tf @@ -14,6 +14,15 @@ variable "path" { type = string } +variable "cli_build_path" { + description = "Path to cli zip file" + type = string +} + resource "enos_local_exec" "load_docker_image" { inline = ["docker load -i ${var.path}"] } + +output "cli_zip_path" { + value = var.cli_build_path +} diff --git a/enos/modules/build_boundary_docker_local/build.sh b/enos/modules/build_boundary_docker_local/build.sh index f996397951..f4e2241073 100644 --- a/enos/modules/build_boundary_docker_local/build.sh +++ b/enos/modules/build_boundary_docker_local/build.sh @@ -8,7 +8,13 @@ set -eux -o pipefail root_dir="$(git rev-parse --show-toplevel)" pushd "${root_dir}" > /dev/null +# make docker image export IMAGE_TAG_DEV="${IMAGE_NAME}" make build-ui docker-build-dev +# make the cli to be used by the test runner +export GOOS=linux +make build +zip -j ${ARTIFACT_PATH}/boundary.zip bin/boundary + popd > /dev/null diff --git a/enos/modules/build_boundary_docker_local/main.tf b/enos/modules/build_boundary_docker_local/main.tf index d5d3fa8188..05f0c5900b 100644 --- a/enos/modules/build_boundary_docker_local/main.tf +++ b/enos/modules/build_boundary_docker_local/main.tf @@ -10,9 +10,14 @@ terraform { } variable "path" { - description = "File Path of boundary docker image that will be created" + description = "Used in `build_boundary_docker_crt`. Not used in this module." + type = string + default = "" +} + +variable "cli_build_path" { + description = "Place to store the built binary" type = string - default = "/tmp/boundary_docker_image.tar" } resource "enos_local_exec" "get_git_sha" { @@ -25,15 +30,16 @@ locals { resource "enos_local_exec" "build_docker_image" { environment = { - "IMAGE_NAME" = local.image_name + "IMAGE_NAME" = local.image_name + "ARTIFACT_PATH" = var.cli_build_path } scripts = ["${path.module}/build.sh"] } -output "artifact_path" { - value = var.path -} - output "image_name" { value = local.image_name } + +output "cli_zip_path" { + value = "${var.cli_build_path}/boundary.zip" +} diff --git a/enos/modules/docker_boundary/main.tf b/enos/modules/docker_boundary/main.tf index ff45af45f3..8724ba5fd0 100644 --- a/enos/modules/docker_boundary/main.tf +++ b/enos/modules/docker_boundary/main.tf @@ -130,7 +130,7 @@ resource "enos_local_exec" "check_health" { } output "address" { - value = "http://0.0.0.0:9200" + value = "http://${var.container_name}:9200" } output "auth_method_id" { diff --git a/enos/modules/test_e2e/main.tf b/enos/modules/test_e2e/main.tf index b3c34e8e49..ab1a04d037 100644 --- a/enos/modules/test_e2e/main.tf +++ b/enos/modules/test_e2e/main.tf @@ -133,7 +133,7 @@ variable "worker_tags" { } variable "test_timeout" { type = string - default = "10m" + default = "15m" } locals { diff --git a/enos/modules/test_e2e_docker/main.tf b/enos/modules/test_e2e_docker/main.tf new file mode 100644 index 0000000000..1ed8467f89 --- /dev/null +++ b/enos/modules/test_e2e_docker/main.tf @@ -0,0 +1,226 @@ + +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +terraform { + required_providers { + docker = { + source = "kreuzwerker/docker" + version = "3.0.1" + } + + enos = { + source = "app.terraform.io/hashicorp-qti/enos" + } + } +} + +variable "docker_mirror" { + description = "URL to the docker repository" + type = string +} +variable "network_name" { + description = "Name of Docker Network" + type = string +} +variable "container_name" { + description = "Name of Docker Container" + type = string + default = "test_runner" +} +variable "go_version" { + description = "Version of Golang used by the application under test" + type = string + default = "" +} +variable "debug_no_run" { + description = "If set, this module will not execute the tests so that you can still access environment variables" + type = bool + default = false +} +variable "test_package" { + description = "Name of Go test package to run" + type = string +} +variable "alb_boundary_api_addr" { + description = "URL of the Boundary instance" + type = string + default = "" +} +variable "auth_method_id" { + description = "Id of Auth Method used to login to Boundary instance" + type = string + default = "" +} +variable "auth_login_name" { + description = "Name of admin user" + type = string + default = "" +} +variable "auth_password" { + description = "Password of admin user" + type = string + default = "" +} +variable "local_boundary_dir" { + description = "Local Path to boundary executable" + type = string +} +variable "local_boundary_src_dir" { + description = "Local Path to boundary src code directory" + type = string +} +variable "target_user" { + description = "SSH username for target" + type = string + default = "" +} +variable "aws_ssh_private_key_path" { + description = "Local Path to key used to SSH onto created hosts" + type = string + default = "" +} +variable "target_ip" { + description = "IP address of target" + type = string + default = "" +} +variable "target_port" { + description = "Port of target" + type = string + default = "" +} +variable "vault_addr" { + description = "External network address of Vault. Will be converted to a URL below" + type = string + default = "" +} +variable "vault_addr_internal" { + description = "Internal network address of Vault (i.e. within a docker network). Will be converted to a URL below" + type = string + default = "" +} +variable "vault_root_token" { + description = "Root token for vault instance" + type = string + default = "" +} +variable "vault_port" { + description = "External Port that vault instance is attached to (outside of docker network)" + type = string + default = "8200" +} +variable "aws_access_key_id" { + description = "Access Key Id for AWS IAM user used in dynamic host catalogs" + type = string + default = "" +} +variable "aws_secret_access_key" { + description = "Secret Access Key for AWS IAM user used in dynamic host catalogs" + type = string + default = "" +} +variable "aws_host_set_filter1" { + description = "Filter tag for host set used in dynamic host catalogs" + type = string + default = "" +} +variable "aws_host_set_count1" { + description = "Number of hosts in aws_host_set_filter1" + type = number + default = 0 +} +variable "aws_host_set_ips1" { + description = "List of IP addresses in aws_host_set_filter1" + type = list(string) + default = [""] +} +variable "aws_host_set_filter2" { + description = "Filter tag for host set used in dynamic host catalogs" + type = string + default = "" +} +variable "aws_host_set_ips2" { + description = "List of IP addresses in aws_host_set_filter2" + type = list(string) + default = [""] +} +variable "aws_region" { + description = "AWS region where the resources will be created" + type = string + default = "" +} +variable "aws_bucket_name" { + description = "AWS S3 bucket name" + type = string + default = "" +} +variable "worker_tags" { + type = list(string) + default = [""] +} +variable "test_timeout" { + type = string + default = "15m" +} + +resource "enos_local_exec" "get_go_version" { + count = var.go_version == "" ? 1 : 0 + inline = ["cat $(echo $(git rev-parse --show-toplevel))/.go-version | xargs"] +} + +locals { + go_version = var.go_version == "" ? enos_local_exec.get_go_version[0].stdout : var.go_version + image_name = trimspace("${var.docker_mirror}/library/golang:${local.go_version}") + + aws_ssh_private_key_path = abspath(var.aws_ssh_private_key_path) + vault_addr = var.vault_addr != "" ? "http://${var.vault_addr}:${var.vault_port}" : "" + vault_addr_internal = var.vault_addr_internal != "" ? "http://${var.vault_addr_internal}:8200" : local.vault_addr + aws_host_set_ips1 = jsonencode(var.aws_host_set_ips1) + aws_host_set_ips2 = jsonencode(var.aws_host_set_ips2) + package_name = reverse(split("/", var.test_package))[0] +} + +resource "docker_image" "go" { + name = local.image_name + keep_locally = true +} + +resource "enos_local_exec" "run_e2e_test" { + depends_on = [docker_image.go] + environment = { + TEST_PACKAGE = var.test_package, + TEST_TIMEOUT = var.test_timeout, + TEST_RUNNER_IMAGE = docker_image.go.image_id, + TEST_NETWORK_NAME = var.network_name, + E2E_TESTS = "true", + BOUNDARY_ADDR = var.alb_boundary_api_addr, + E2E_PASSWORD_AUTH_METHOD_ID = var.auth_method_id, + E2E_PASSWORD_ADMIN_LOGIN_NAME = var.auth_login_name, + E2E_PASSWORD_ADMIN_PASSWORD = var.auth_password, + E2E_TARGET_IP = var.target_ip, + E2E_SSH_USER = var.target_user, + E2E_SSH_PORT = var.target_port, + E2E_SSH_KEY_PATH = local.aws_ssh_private_key_path, + VAULT_ADDR = local.vault_addr, + VAULT_ADDR_INTERNAL = local.vault_addr_internal, + VAULT_TOKEN = var.vault_root_token, + E2E_VAULT_ADDR = local.vault_addr_internal, + E2E_AWS_ACCESS_KEY_ID = var.aws_access_key_id, + E2E_AWS_SECRET_ACCESS_KEY = var.aws_secret_access_key, + E2E_AWS_HOST_SET_FILTER = var.aws_host_set_filter1, + E2E_AWS_HOST_SET_IPS = local.aws_host_set_ips1, + E2E_AWS_HOST_SET_FILTER2 = var.aws_host_set_filter2, + E2E_AWS_HOST_SET_IPS2 = local.aws_host_set_ips2, + E2E_AWS_REGION = var.aws_region, + E2E_AWS_BUCKET_NAME = var.aws_bucket_name, + E2E_WORKER_TAG = jsonencode(var.worker_tags), + BOUNDARY_DIR = abspath(var.local_boundary_src_dir), + BOUNDARY_CLI_DIR = abspath(var.local_boundary_dir), + MODULE_DIR = abspath(path.module) + } + + inline = var.debug_no_run ? [""] : [ + "bash ./${path.module}/test_runner.sh" + ] +} diff --git a/enos/modules/test_e2e_docker/test.sh b/enos/modules/test_e2e_docker/test.sh new file mode 100755 index 0000000000..ed39a4c6aa --- /dev/null +++ b/enos/modules/test_e2e_docker/test.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# This script is used for setting up and executing boundary e2e tests. It sets +# up `pass` as a password store (linux) so that boundary can use it to store +# auth tokens and installs the latest vault cli before executing the desired tests. + +set -eux -o pipefail + +# Install dependencies +apt update +# unzip is used to unzip boundary.zip +# pass is used to store the auth token from `boundary authenticate`` +# lsb-release is used for adding the hashicorp apt source +apt install unzip pass lsb-release -y + +# Create a GPG key +export KEY_PW=boundary +gpg --generate-key --batch <> ~/.gnupg/gpg-agent.conf +gpg-connect-agent reloadagent /bye &>/dev/null + +# Get information about the created keys +export lines=$(gpg --list-secret-keys --with-colons --with-keygrip) +export KEY_ID="" +while read -r line +do + # Save the first key id to be used later + if [[ $line =~ "fpr"* ]]; then + if [[ $KEY_ID == "" ]]; then + KEY_ID="$(echo "$line" | sed -r 's/fpr|://g')" + fi + fi + + # Cache the passphrases for the keys so passwords do not need to be entered + if [[ $line =~ "grp"* ]]; then + export KEYGRIP_ID="$(echo "$line" | sed -r 's/grp|://g')" + /usr/lib/gnupg/gpg-preset-passphrase --preset -P $KEY_PW $KEYGRIP_ID + fi +done <<< $lines + +# Trust the key +touch /tmp/test.txt +gpg -a --encrypt -r $KEY_ID --trust-model always --batch --yes /tmp/test.txt +echo "trusted-key $KEY_ID" >> ~/.gnupg/gpg.conf + +# Initialize the password store +pass init $KEY_ID &>/dev/null + +# Install the vault cli +wget -O- https://apt.releases.hashicorp.com/gpg | gpg --batch --yes --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg +export lines=$(gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint --with-colons) +while read -r line +do + if [[ $line =~ "fpr"* ]]; then + if [[ "$(echo $line | sed -r 's/fpr|://g')" != "798AEC654E5C15428C8E42EEAA16FCBCA621E701" ]]; then + echo "HashiCorp key fingerprint does not match expected" + exit 1 + else + break + fi + fi +done <<< $lines +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list +apt update +apt install vault + +# Run Tests +unzip /boundary.zip -d /usr/local/bin/ +cd /src/boundary +go test -v -count=1 $TEST_PACKAGE -timeout $TEST_TIMEOUT | tee /testlogs/test-e2e-${TEST_PACKAGE##*/}.log diff --git a/enos/modules/test_e2e_docker/test_runner.sh b/enos/modules/test_e2e_docker/test_runner.sh new file mode 100644 index 0000000000..4ea6accbcd --- /dev/null +++ b/enos/modules/test_e2e_docker/test_runner.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# This script sets up a docker container to serve as a test runner for boundary +# e2e tests + +set -eux -o pipefail + +docker run \ + --rm \ + --name test-runner \ + -e "TEST_PACKAGE=$TEST_PACKAGE" \ + -e "TEST_TIMEOUT=$TEST_TIMEOUT" \ + -e "E2E_TESTS=$E2E_TESTS" \ + -e "BOUNDARY_ADDR=$BOUNDARY_ADDR" \ + -e "E2E_PASSWORD_AUTH_METHOD_ID=$E2E_PASSWORD_AUTH_METHOD_ID" \ + -e "E2E_PASSWORD_ADMIN_LOGIN_NAME=$E2E_PASSWORD_ADMIN_LOGIN_NAME" \ + -e "E2E_PASSWORD_ADMIN_PASSWORD=$E2E_PASSWORD_ADMIN_PASSWORD" \ + -e "E2E_TARGET_IP=$E2E_TARGET_IP" \ + -e "E2E_SSH_USER=$E2E_SSH_USER" \ + -e "E2E_SSH_PORT=$E2E_SSH_PORT" \ + -e "E2E_SSH_KEY_PATH=/keys/target.pem" \ + -e "VAULT_ADDR=$VAULT_ADDR_INTERNAL" \ + -e "VAULT_TOKEN=$VAULT_TOKEN" \ + -e "E2E_VAULT_ADDR=$E2E_VAULT_ADDR" \ + -e "E2E_AWS_ACCESS_KEY_ID=$E2E_AWS_ACCESS_KEY_ID" \ + -e "E2E_AWS_SECRET_ACCESS_KEY=$E2E_AWS_SECRET_ACCESS_KEY" \ + -e "E2E_AWS_HOST_SET_FILTER=$E2E_AWS_HOST_SET_FILTER" \ + -e "E2E_AWS_HOST_SET_IPS=$E2E_AWS_HOST_SET_IPS" \ + -e "E2E_AWS_HOST_SET_FILTER2=$E2E_AWS_HOST_SET_FILTER2" \ + -e "E2E_AWS_HOST_SET_IPS2=$E2E_AWS_HOST_SET_IPS2" \ + -e "E2E_AWS_REGION=$E2E_AWS_REGION" \ + -e "E2E_AWS_BUCKET_NAME=$E2E_AWS_BUCKET_NAME" \ + -e "E2E_WORKER_TAG=$E2E_WORKER_TAG" \ + --mount type=bind,src=$BOUNDARY_DIR,dst=/src/boundary/ \ + --mount type=bind,src=$MODULE_DIR/../..,dst=/testlogs \ + --mount type=bind,src=$(go env GOCACHE),dst=/root/.cache/go-build \ + --mount type=bind,src=$(go env GOMODCACHE),dst=/go/pkg/mod \ + -v "$MODULE_DIR/test.sh:/scripts/test.sh" \ + -v "$E2E_SSH_KEY_PATH:/keys/target.pem" \ + -v "$BOUNDARY_CLI_DIR:/boundary.zip" \ + --network $TEST_NETWORK_NAME \ + --cap-add=CAP_IPC_LOCK \ + $TEST_RUNNER_IMAGE \ + /bin/sh -c /scripts/test.sh diff --git a/enos/modules/test_e2e_ui/main.tf b/enos/modules/test_e2e_ui/main.tf index 4dcb40d93a..f277ab0b0f 100644 --- a/enos/modules/test_e2e_ui/main.tf +++ b/enos/modules/test_e2e_ui/main.tf @@ -38,7 +38,7 @@ variable "local_boundary_dir" { description = "Local Path to boundary executable" type = string } -variable "local_boundary_ui_dir" { +variable "local_boundary_ui_src_dir" { description = "Local Path to boundary-ui directory" type = string } @@ -147,7 +147,7 @@ resource "enos_local_exec" "run_e2e_ui_test" { E2E_AWS_HOST_SET_IPS2 = local.aws_host_set_ips2 } - inline = var.debug_no_run ? [""] : ["set -o pipefail; PATH=\"${var.local_boundary_dir}:$PATH\" yarn --cwd ${var.local_boundary_ui_dir}/ui/admin run e2e 2>&1 | tee ${path.module}/../../test-e2e-ui.log"] + inline = var.debug_no_run ? [""] : ["set -o pipefail; PATH=\"${var.local_boundary_dir}:$PATH\" yarn --cwd ${var.local_boundary_ui_src_dir}/ui/admin run e2e 2>&1 | tee ${path.module}/../../test-e2e-ui.log"] } output "test_results" {