From f0a4bce17c295eafba1fcb475cf55d6487e1df09 Mon Sep 17 00:00:00 2001 From: Michael Li Date: Mon, 30 Oct 2023 09:48:08 -0400 Subject: [PATCH] test(e2e): Add test for controller-led worker registration (#3882) * test(e2e): Add test for controller led worker registration * chore(e2e): Rename log files to ensure uniqueness This updates the log files to be based on the scenario name instead of just the test package in case some scenarios run the same test package --- .github/workflows/enos-run.yml | 9 +- ...ker-worker-registration-controller-led.hcl | 192 ++++++++++++++++++ .../modules/docker_boundary/get_auth_token.sh | 10 + .../docker_boundary/get_worker_token.sh | 9 + enos/modules/docker_boundary/main.tf | 61 ++++++ enos/modules/docker_boundary/script_runner.sh | 26 +++ enos/modules/docker_worker/main.tf | 6 + .../worker-config-controller-led.hcl | 64 ++++++ 8 files changed, 376 insertions(+), 1 deletion(-) create mode 100644 enos/enos-scenario-e2e-docker-worker-registration-controller-led.hcl create mode 100755 enos/modules/docker_boundary/get_auth_token.sh create mode 100755 enos/modules/docker_boundary/get_worker_token.sh create mode 100644 enos/modules/docker_boundary/script_runner.sh create mode 100644 enos/modules/docker_worker/worker-config-controller-led.hcl diff --git a/.github/workflows/enos-run.yml b/.github/workflows/enos-run.yml index 375902ad41..39bf719095 100644 --- a/.github/workflows/enos-run.yml +++ b/.github/workflows/enos-run.yml @@ -82,6 +82,7 @@ jobs: - filter: 'e2e_docker_base_with_vault builder:crt' - filter: 'e2e_docker_base_with_postgres builder:crt' - filter: 'e2e_docker_base_with_worker builder:crt' + - filter: 'e2e_docker_worker_registration_controller_led builder:crt' runs-on: ${{ fromJSON(vars.RUNNER_LARGE) }} env: GITHUB_TOKEN: ${{ secrets.SERVICE_USER_GITHUB_TOKEN }} @@ -248,11 +249,17 @@ jobs: mkdir -p ./enos/terraform-plugin-cache export ENOS_VAR_enos_user=$GITHUB_ACTOR && \ enos scenario launch --timeout 60m0s --chdir ./enos ${{ matrix.filter }} + - name: Rename e2e tests output + run : | + pushd enos + scenario="${{ matrix.filter }}" + for f in *.log; do mv -- "$f" "${f%.log}_${scenario%% *}.log"; done + popd - name: Upload e2e tests output uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: test-e2e-output - path: enos/test*.log + path: enos/*.log retention-days: 5 - name: Get logs from postgres container # Retrieve logs from the postgres container on a failed diff --git a/enos/enos-scenario-e2e-docker-worker-registration-controller-led.hcl b/enos/enos-scenario-e2e-docker-worker-registration-controller-led.hcl new file mode 100644 index 0000000000..2ca1f90d6d --- /dev/null +++ b/enos/enos-scenario-e2e-docker-worker-registration-controller-led.hcl @@ -0,0 +1,192 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +# For this scenario to work, add the following line to /etc/hosts +# 127.0.0.1 localhost boundary +# 127.0.0.1 localhost worker + +scenario "e2e_docker_worker_registration_controller_led" { + terraform_cli = terraform_cli.default + terraform = terraform.default + providers = [ + provider.enos.default + ] + + matrix { + builder = ["local", "crt"] + } + + 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")) + + network_cluster = "e2e_cluster" + network_host = "e2e_host" + network_database = "e2e_db" + + 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", + "Environment" : "ci" + }, var.tags) + } + + step "build_boundary_docker_image" { + module = matrix.builder == "crt" ? module.build_boundary_docker_crt : module.build_boundary_docker_local + + variables { + path = matrix.builder == "crt" ? local.boundary_docker_image_file : "" + cli_build_path = local.build_path[matrix.builder] + edition = var.boundary_edition + } + } + + step "create_docker_network_database" { + module = module.docker_network + variables { + network_name = local.network_database + } + } + + step "create_docker_network_cluster" { + module = module.docker_network + variables { + network_name = local.network_cluster + } + } + + step "create_docker_network_host" { + module = module.docker_network + variables { + network_name = local.network_host + } + } + + step "create_boundary_database" { + depends_on = [ + step.create_docker_network_cluster + ] + variables { + image_name = "${var.docker_mirror}/library/postgres:latest" + network_name = [local.network_database] + } + module = module.docker_postgres + } + + step "read_license" { + skip_step = var.boundary_edition == "oss" + module = module.read_license + + variables { + file_name = local.license_path + } + } + + step "create_boundary" { + module = module.docker_boundary + depends_on = [ + step.create_docker_network_cluster, + step.create_docker_network_database, + step.create_boundary_database, + step.build_boundary_docker_image + ] + variables { + image_name = matrix.builder == "crt" ? var.boundary_docker_image_name : step.build_boundary_docker_image.image_name + network_name = [local.network_cluster, local.network_database] + database_network = local.network_database + postgres_address = step.create_boundary_database.address + boundary_license = var.boundary_edition != "oss" ? step.read_license.license : "" + config_file = "boundary-config.hcl" + get_auth_token = true + get_worker_token = true + } + } + + step "create_vault" { + module = module.docker_vault + depends_on = [ + step.create_docker_network_cluster + ] + variables { + image_name = "${var.docker_mirror}/hashicorp/vault:${var.vault_version}" + network_name = [local.network_cluster] + } + } + + step "create_host" { + module = module.docker_openssh_server + depends_on = [ + step.create_docker_network_host + ] + variables { + image_name = "${var.docker_mirror}/linuxserver/openssh-server:latest" + network_name = [local.network_host] + private_key_file_path = local.aws_ssh_private_key_path + } + } + + locals { + egress_tag = "egress" + } + + step "create_worker" { + module = module.docker_worker + depends_on = [ + step.create_docker_network_cluster, + step.create_docker_network_host, + step.build_boundary_docker_image, + step.create_boundary + ] + variables { + image_name = matrix.builder == "crt" ? var.boundary_docker_image_name : step.build_boundary_docker_image.image_name + boundary_license = var.boundary_edition != "oss" ? step.read_license.license : "" + config_file = "worker-config-controller-led.hcl" + container_name = "worker" + initial_upstream = step.create_boundary.upstream_address + network_name = [local.network_cluster, local.network_host] + tags = [local.egress_tag] + port = "9402" + token = step.create_boundary.worker_token + } + } + + step "run_e2e_test" { + module = module.test_e2e_docker + depends_on = [ + step.create_boundary, + step.create_vault, + step.create_host, + step.create_worker, + ] + variables { + test_package = "github.com/hashicorp/boundary/testing/internal/e2e/tests/base_with_worker" + docker_mirror = var.docker_mirror + network_name = step.create_docker_network_cluster.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 = 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_address = step.create_host.address + target_port = step.create_host.port + target_user = "ubuntu" + vault_addr = step.create_vault.address + vault_addr_internal = step.create_vault.address_internal + vault_root_token = step.create_vault.token + vault_port = step.create_vault.port + worker_tag_egress = local.egress_tag + worker_tag_collocated = step.create_boundary.worker_tag + } + } +} diff --git a/enos/modules/docker_boundary/get_auth_token.sh b/enos/modules/docker_boundary/get_auth_token.sh new file mode 100755 index 0000000000..981a00648c --- /dev/null +++ b/enos/modules/docker_boundary/get_auth_token.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env sh +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -eu -o pipefail + +boundary authenticate password \ + -login-name $LOGIN_NAME \ + -password env://BPASS \ + -format json diff --git a/enos/modules/docker_boundary/get_worker_token.sh b/enos/modules/docker_boundary/get_worker_token.sh new file mode 100755 index 0000000000..2c512518ef --- /dev/null +++ b/enos/modules/docker_boundary/get_worker_token.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -eu -o pipefail + +boundary workers create controller-led \ + -token env://BOUNDARY_TOKEN \ + -format json diff --git a/enos/modules/docker_boundary/main.tf b/enos/modules/docker_boundary/main.tf index d12b352aec..82c32c225d 100644 --- a/enos/modules/docker_boundary/main.tf +++ b/enos/modules/docker_boundary/main.tf @@ -54,6 +54,16 @@ variable "worker_tag" { type = string default = "collocated" } +variable "get_auth_token" { + description = "Flag to retrieve a boundary auth token" + type = bool + default = false +} +variable "get_worker_token" { + description = "Flag to retrieve a boundary worker token" + type = bool + default = false +} resource "docker_image" "boundary" { name = var.image_name @@ -150,6 +160,47 @@ resource "enos_local_exec" "check_health" { inline = ["timeout 10s bash -c 'until curl -i http://0.0.0.0:9203/health; do sleep 2; done'"] } +resource "enos_local_exec" "get_auth_token" { + count = var.get_auth_token ? 1 : 0 + depends_on = [enos_local_exec.check_health] + environment = { + TEST_BOUNDARY_IMAGE = var.image_name + BOUNDARY_ADDR = local.address + TEST_NETWORK_NAME = var.network_name[0] + E2E_PASSWORD_ADMIN_LOGIN_NAME = local.login_name + E2E_PASSWORD_ADMIN_PASSWORD = local.password + MODULE_DIR = abspath(path.module) + SCRIPT = "${abspath(path.module)}/get_auth_token.sh" + } + inline = ["bash ./${path.module}/script_runner.sh"] +} + +locals { + auth_info = var.get_auth_token ? jsondecode(enos_local_exec.get_auth_token[0].stdout) : null + auth_token = var.get_auth_token ? local.auth_info["item"]["attributes"]["token"] : "" +} + +resource "enos_local_exec" "get_worker_token" { + count = var.get_worker_token ? 1 : 0 + depends_on = [enos_local_exec.check_health] + environment = { + TEST_BOUNDARY_IMAGE = var.image_name, + BOUNDARY_ADDR = local.address + TEST_NETWORK_NAME = var.network_name[0] + E2E_PASSWORD_ADMIN_LOGIN_NAME = local.login_name + E2E_PASSWORD_ADMIN_PASSWORD = local.password + MODULE_DIR = abspath(path.module) + SCRIPT = "${abspath(path.module)}/get_worker_token.sh" + BOUNDARY_TOKEN = local.auth_token + } + inline = ["bash ./${path.module}/script_runner.sh"] +} + +locals { + worker_info = var.get_worker_token ? jsondecode(enos_local_exec.get_worker_token[0].stdout) : null + worker_token = var.get_worker_token ? local.worker_info["item"]["controller_generated_activation_token"] : "" +} + output "address" { value = local.address } @@ -173,3 +224,13 @@ output "password" { output "worker_tag" { value = var.worker_tag } + +output "auth_token" { + value = local.auth_token + sensitive = true +} + +output "worker_token" { + value = local.worker_token + sensitive = true +} diff --git a/enos/modules/docker_boundary/script_runner.sh b/enos/modules/docker_boundary/script_runner.sh new file mode 100644 index 0000000000..0dd9e72d99 --- /dev/null +++ b/enos/modules/docker_boundary/script_runner.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +# This script initializes a postgres database to work with Boundary by spinning up a temporary +# Boundary docker container pointed to the specified database and invoking `boundary database init`. +# +# This script must only output the JSON that comes from `boundary database init` as the output is +# consumed by other scripts. + +TEST_CONTAINER_NAME=boundary-script-runner +SOURCE=$(realpath $(dirname ${BASH_SOURCE[0]})) # get directory of this script + +docker run \ + --rm \ + --name $TEST_CONTAINER_NAME \ + -e "BOUNDARY_ADDR=$BOUNDARY_ADDR" \ + -e "LOGIN_NAME=$E2E_PASSWORD_ADMIN_LOGIN_NAME" \ + -e "BPASS=$E2E_PASSWORD_ADMIN_PASSWORD" \ + -e "BOUNDARY_TOKEN=$BOUNDARY_TOKEN" \ + -e "SKIP_CHOWN=true" \ + --cap-add IPC_LOCK \ + --network $TEST_NETWORK_NAME \ + -v "$SCRIPT:/script.sh" \ + $TEST_BOUNDARY_IMAGE \ + /bin/sh -c /script.sh diff --git a/enos/modules/docker_worker/main.tf b/enos/modules/docker_worker/main.tf index f03a439491..f0cd71427c 100644 --- a/enos/modules/docker_worker/main.tf +++ b/enos/modules/docker_worker/main.tf @@ -55,6 +55,11 @@ variable "config_file" { type = string default = "worker-config.hcl" } +variable "token" { + description = "Controller generated activation token to initialize worker" + type = string + default = "" +} resource "docker_image" "boundary" { name = var.image_name @@ -102,6 +107,7 @@ resource "docker_container" "worker" { recording_storage_path = local.recording_storage_path port = var.port port_ops = local.port_ops + token = var.token }) file = "/boundary/worker-config.hcl" } diff --git a/enos/modules/docker_worker/worker-config-controller-led.hcl b/enos/modules/docker_worker/worker-config-controller-led.hcl new file mode 100644 index 0000000000..97713596e2 --- /dev/null +++ b/enos/modules/docker_worker/worker-config-controller-led.hcl @@ -0,0 +1,64 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +disable_mlock = true + +telemetry { + prometheus_retention_time = "24h" + disable_hostname = true +} + +listener "tcp" { + address = "0.0.0.0:${port}" + purpose = "proxy" + tls_disable = true +} + +listener "tcp" { + address = "0.0.0.0:${port_ops}" + purpose = "ops" + tls_disable = true +} + +worker { + public_addr = "${worker_name}:${port}" + initial_upstreams = ["${initial_upstream}"] + controller_generated_activation_token = "${token}" + + tags { + type = ${type_tags}, + } + + auth_storage_path = "/tmp/boundary/worker" +} + +events { + audit_enabled = true + sysevents_enabled = true + observations_enable = true + + sink "stderr" { + name = "all-events" + description = "All events sent to stderr" + event_types = ["*"] + format = "cloudevents-json" + } + + sink { + name = "Log File" + event_types = ["*"] + format = "cloudevents-json" + + file { + path = "/boundary/logs" + file_name = "events.log" + } + + audit_config { + audit_filter_overrides { + secret = "redact" + sensitive = "redact" + } + } + } +}