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" + } + } + } +}