Repository navigation
feat(#66): test environment layer — provisioning, config discovery, test data, config apply #144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a00bb64
7bbc078
1e877fe
da80b98
38a8ba4
4dfface
485e672
2a041f5
e1b7858
72df789
16476d2
8b92f7a
460b50f
acc562f
33e857c
03087da
6765388
01a7e2b
e3cc817
090d5ae
43a834b
436d5fe
2f0b31a
915f607
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| # cht-agent-net.override.yml | ||
| # | ||
| # Layers onto cht-core's local-build compose (or the docker-helper stack) so the | ||
| # running CHT instance joins the shared `cht-agent-net` network that the | ||
| # dockerized cht-agent is also attached to. This lets the agent reach the | ||
| # instance at https://nginx with no host-port juggling and — importantly — with | ||
| # NO Docker access (the agent only polls /api/v2/monitoring). | ||
| # | ||
| # HUMAN-run only (the cht-agent never runs Docker). scripts/test-env-up.sh applies | ||
| # it with a per-checkout project, internal network and data dir (scripts/lib/test-env.sh); | ||
| # by hand, pass the same values that test_env_compose passes, not only -p. Without | ||
| # CHT_NETWORK, the stack shares the default `cht-net` network and the couchdb/haproxy/api | ||
| # names with any other CHT stack that also uses `cht-net`. Without COUCHDB_PASSWORD, | ||
| # Compose stops. Use this command: | ||
| # COUCHDB_USER=medic COUCHDB_PASSWORD=password COMMON_NAME=nginx \ | ||
| # NGINX_HTTP_PORT=127.0.0.1:80 NGINX_HTTPS_PORT=127.0.0.1:443 \ | ||
| # CHT_NETWORK=<project>-net COUCHDB_DATA=./srv-<project> \ | ||
| # docker compose -p <project> -f cht-couchdb.yml -f cht-core.yml -f cht-agent-net.override.yml up -d | ||
| # Only one stack's nginx can be on cht-agent-net at a time: `nginx` would resolve to both. | ||
| # | ||
| # VALIDATE before relying on this: the service name (`nginx`) and the key of the | ||
| # stack's internal network (`cht-net` below; CHT_NETWORK sets its actual name) must | ||
| # match what `npm run local-images` generates in `local-build/` for your cht-core | ||
| # version. Confirm with: | ||
| # docker compose ... config # see resolved service/network names | ||
| # docker network inspect cht-agent-net # confirm nginx is attached | ||
| # If the internal network differs, update the first entry under nginx.networks. | ||
|
|
||
| networks: | ||
| cht-agent-net: | ||
| external: true | ||
|
|
||
| services: | ||
| # nginx is the HTTPS entry point the agent reaches. Keep it on the stack's | ||
| # internal network AND join the shared cht-agent-net. | ||
| nginx: | ||
| networks: | ||
| - cht-net | ||
| - cht-agent-net |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,91 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # shellcheck shell=bash | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Shared by scripts/test-env-{up,down,restart}.sh (sourced, not run). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick (non-blocking): The SC2034 warning for
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Every cht-core checkout builds in a directory named `local-build`, and Compose | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # names a project after its directory, so without -p all checkouts share one project | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # and `down -v` on one deletes another's stack. Each checkout therefore gets its own | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # project, and with it its own internal network (the couchdb/api/haproxy names), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # CouchDB bind mount, and cert volume. Only nginx joins the shared cht-agent-net. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Env overrides: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # CHT_TEST_ENV_PROJECT Compose project (default cht-agent-<dir>-<path hash>) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # COUCHDB_USER / COUCHDB_PASSWORD admin (default medic / password, the agent's DEFAULT_AUTH) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # COUCHDB_DATA CouchDB bind mount (default local-build/srv-<project>) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # NGINX_HTTP_PORT / NGINX_HTTPS_PORT host binds (default 127.0.0.1:80 / 127.0.0.1:443; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # set 443 to reach the stack from another device, and then also set | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # COUCHDB_PASSWORD: the default admin is medic / password) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # COMMON_NAME cert CN (default nginx, the host name the containerized agent uses) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TEST_ENV_REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TEST_ENV_OVERRIDE="$TEST_ENV_REPO_ROOT/docker/cht-agent-net.override.yml" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Must match the external network in docker/cht-agent-net.override.yml. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # shellcheck disable=SC2034 # used by test-env-up.sh | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TEST_ENV_NETWORK="cht-agent-net" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test_env_default_target() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local path="${1:-}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| printf '%s\n' "${path:-${CHT_CORE_PATH:-${CHT_CORE_CLONE_DIR:-$TEST_ENV_REPO_ROOT/.cht-core}}}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (non-blocking): SonarCloud flags
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return $? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test_env_hash() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if command -v sha256sum >/dev/null 2>&1; then sha256sum; else shasum -a 256; fi | cut -c1-8 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (non-blocking): SonarCloud flags
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return $? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Sets TEST_ENV_TARGET (physical path) and TEST_ENV_PROJECT for an existing checkout. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test_env_select() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local path="$1" base | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TEST_ENV_TARGET="$(cd "$path" && pwd -P)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| base="$(basename "$TEST_ENV_TARGET" | LC_ALL=C tr '[:upper:]' '[:lower:]' | LC_ALL=C sed -e 's/[^a-z0-9_-]/-/g' -e 's/^-*//')" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TEST_ENV_PROJECT="${CHT_TEST_ENV_PROJECT:-cht-agent-${base}-$(printf '%s' "$TEST_ENV_TARGET" | test_env_hash)}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (non-blocking): SonarCloud flags
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return $? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test_env_require_build() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local path="$1" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ ! -d "$path/local-build" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "error: no cht-core build at $path (pass a path or set CHT_CORE_PATH)" >&2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # COUCHDB_PASSWORD goes to every subcommand: the compose files declare ${COUCHDB_PASSWORD:?...}. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test_env_compose() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ( cd "$TEST_ENV_TARGET/local-build" && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COUCHDB_USER="${COUCHDB_USER:-medic}" COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-password}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CHT_NETWORK="$TEST_ENV_PROJECT-net" COUCHDB_DATA="${COUCHDB_DATA:-./srv-$TEST_ENV_PROJECT}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NGINX_HTTP_PORT="${NGINX_HTTP_PORT:-127.0.0.1:80}" NGINX_HTTPS_PORT="${NGINX_HTTPS_PORT:-127.0.0.1:443}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COMMON_NAME="${COMMON_NAME:-nginx}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docker compose -p "$TEST_ENV_PROJECT" -f cht-couchdb.yml -f cht-core.yml -f "$TEST_ENV_OVERRIDE" "$@" ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (non-blocking): SonarCloud flags
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return $? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Refuse a project that another checkout started. With a shared CHT_TEST_ENV_PROJECT, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # up would recreate that checkout's containers, and down -v would delete them and its volumes. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test_env_require_owner() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local dirs dir | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dirs="$(docker ps -a --filter "label=com.docker.compose.project=$TEST_ENV_PROJECT" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| --format '{{.Label "com.docker.compose.project.working_dir"}}' | sort -u)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while IFS= read -r dir; do | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -n "$dir" && "$dir" != "$TEST_ENV_TARGET/local-build" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "error: Compose project '$TEST_ENV_PROJECT' has containers from $dir, which is not $TEST_ENV_TARGET/local-build." >&2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| done <<< "$dirs" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # restart and down would otherwise report success against a project with nothing in it. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test_env_require_containers() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| local containers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| containers="$(test_env_compose ps -a -q)" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [[ -z "$containers" ]]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "error: Compose project '$TEST_ENV_PROJECT' has no containers." >&2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo " 'docker compose ls -a' lists the projects that exist; a stack this checkout" >&2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo " started under another name needs CHT_TEST_ENV_PROJECT=<name>." >&2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| test_env_require_owner | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+79
to
+91
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (non-blocking): My round-1 suggestion added the The suggestion refuses a project whose The label holds the path that Compose saw. So the check also refuses a stack that an operator started by hand through a symlinked path. The message names that path. My comment on
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| #!/usr/bin/env bash | ||
| # | ||
| # Human-run teardown for the Test Environment Layer. | ||
| # The cht-agent NEVER runs this or any Docker command itself. | ||
| # Usage: scripts/test-env-down.sh [<cht-core-path>] (overrides: scripts/lib/test-env.sh) | ||
| set -euo pipefail | ||
| source "$(dirname "$0")/lib/test-env.sh" | ||
|
|
||
| # Same resolution as test-env-up.sh, minus the clone: the stack must already exist. | ||
| TARGET="$(test_env_default_target "${1:-}")" | ||
| test_env_require_build "$TARGET" | ||
| test_env_select "$TARGET" | ||
| test_env_require_containers | ||
| test_env_compose down -v | ||
|
|
||
| echo "CHT environment '$TEST_ENV_PROJECT' torn down. -v removed its named volumes; CouchDB data in the" \ | ||
| "${COUCHDB_DATA:-$TEST_ENV_TARGET/local-build/srv-$TEST_ENV_PROJECT} bind mount stays." |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| #!/usr/bin/env bash | ||
| # | ||
| # Human-run restart for the Test Environment Layer (reset tier: restart). | ||
| # The cht-agent NEVER runs this or any Docker command itself. | ||
| # Usage: scripts/test-env-restart.sh [<cht-core-path>] (overrides: scripts/lib/test-env.sh) | ||
| set -euo pipefail | ||
| source "$(dirname "$0")/lib/test-env.sh" | ||
|
|
||
| # Same resolution as test-env-up.sh, minus the clone: the stack must already exist. | ||
| TARGET="$(test_env_default_target "${1:-}")" | ||
| test_env_require_build "$TARGET" | ||
| test_env_select "$TARGET" | ||
| test_env_require_containers | ||
| test_env_compose restart | ||
|
|
||
| echo "CHT services in '$TEST_ENV_PROJECT' restarted. The agent should re-confirm health (provision/waitForReady)." |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,110 @@ | ||||||||||||||||||||
| #!/usr/bin/env bash | ||||||||||||||||||||
| # | ||||||||||||||||||||
| # Human-run bring-up for the Test Environment Layer (Model A — rebuild on change). | ||||||||||||||||||||
| # The cht-agent NEVER runs this or any Docker command itself; the agent's | ||||||||||||||||||||
| # provision() only polls /api/v2/monitoring and waits for this to finish. The | ||||||||||||||||||||
| # clone/install below is this HUMAN script's convenience — the agent still never | ||||||||||||||||||||
| # clones, installs, or runs Docker. | ||||||||||||||||||||
| # | ||||||||||||||||||||
| # Usage: scripts/test-env-up.sh [<cht-core-path>] | ||||||||||||||||||||
| # | ||||||||||||||||||||
| # With no argument the stack is built from $CHT_CORE_PATH, or from a managed | ||||||||||||||||||||
| # checkout at $CHT_CORE_CLONE_DIR (default <repo>/.cht-core), cloned from master | ||||||||||||||||||||
| # on first use. A path you pass is used as-is and is never cloned into. | ||||||||||||||||||||
| # | ||||||||||||||||||||
| # Project, credential, port and cert overrides: see scripts/lib/test-env.sh. | ||||||||||||||||||||
| # | ||||||||||||||||||||
| # TLS: the stack serves a SAN-less self-signed cert whose CN is COMMON_NAME (nginx). | ||||||||||||||||||||
| # The agent's cht-conf child accepts it via --accept-self-signed-certs; the agent's | ||||||||||||||||||||
| # own fetch (readiness/discovery/reset) needs it trusted via NODE_EXTRA_CA_CERTS | ||||||||||||||||||||
| # (copy command printed at the end). NODE_TLS_REJECT_UNAUTHORIZED=0 disables | ||||||||||||||||||||
| # verification for ALL of the agent's traffic (LLM/MCP included) and is acceptable | ||||||||||||||||||||
| # only inside a disposable runner container. | ||||||||||||||||||||
| set -euo pipefail | ||||||||||||||||||||
| source "$(dirname "$0")/lib/test-env.sh" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| CLONE_DIR="${CHT_CORE_CLONE_DIR:-$TEST_ENV_REPO_ROOT/.cht-core}" | ||||||||||||||||||||
| CHT_CORE_UPSTREAM="${CHT_CORE_UPSTREAM:-https://github.com/medic/cht-core.git}" | ||||||||||||||||||||
| CHT_CORE_BRANCH="${CHT_CORE_BRANCH:-master}" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # cht-core's own engines require Node >= 22.15; npm ci and the image build both | ||||||||||||||||||||
| # fail in confusing ways on older runtimes. | ||||||||||||||||||||
| NODE_MAJOR="$(node -v 2>/dev/null | sed 's/^v\([0-9]*\).*/\1/')" | ||||||||||||||||||||
| if [[ -z "$NODE_MAJOR" ]] || [[ "$NODE_MAJOR" -lt 22 ]]; then | ||||||||||||||||||||
| echo "error: cht-core needs Node >= 22.15 (found: $(node -v 2>/dev/null || echo 'no node')). Try 'nvm use 22'." >&2 | ||||||||||||||||||||
| exit 1 | ||||||||||||||||||||
| fi | ||||||||||||||||||||
|
|
||||||||||||||||||||
| TARGET="${1:-${CHT_CORE_PATH:-}}" | ||||||||||||||||||||
| if [[ -z "$TARGET" ]]; then | ||||||||||||||||||||
| TARGET="$CLONE_DIR" | ||||||||||||||||||||
| if [[ ! -d "$TARGET/.git" ]]; then | ||||||||||||||||||||
| echo "No cht-core path given — cloning $CHT_CORE_BRANCH into $TARGET (shallow)." | ||||||||||||||||||||
| echo "Pass a path, or set CHT_CORE_PATH, to build from a working copy instead." | ||||||||||||||||||||
| # Shallow is safe: cht-core derives its image version from the branch name | ||||||||||||||||||||
| # (cht-core's scripts/build/versions.js), not from git tags. | ||||||||||||||||||||
| git clone --depth 1 --branch "$CHT_CORE_BRANCH" "$CHT_CORE_UPSTREAM" "$TARGET" | ||||||||||||||||||||
| fi | ||||||||||||||||||||
| fi | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if [[ ! -d "$TARGET" ]]; then | ||||||||||||||||||||
| echo "error: cht-core path not found: $TARGET" >&2 | ||||||||||||||||||||
| exit 1 | ||||||||||||||||||||
| fi | ||||||||||||||||||||
| test_env_select "$TARGET" | ||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (non-blocking): This goes with my suggestion on
Suggested change
|
||||||||||||||||||||
| test_env_require_owner | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # 1. Shared network the cht-agent and CHT both join. | ||||||||||||||||||||
| docker network inspect "$TEST_ENV_NETWORK" >/dev/null 2>&1 || docker network create "$TEST_ENV_NETWORK" >/dev/null | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # The agent reaches CHT as https://nginx on that network; a second stack's nginx | ||||||||||||||||||||
| # there would split the name between two instances. -a: a stopped nginx comes back | ||||||||||||||||||||
| # when the Docker daemon restarts (restart: always). | ||||||||||||||||||||
| nginx_projects="$(docker ps -a --filter "network=$TEST_ENV_NETWORK" --filter label=com.docker.compose.service=nginx \ | ||||||||||||||||||||
| --format '{{.Label "com.docker.compose.project"}}')" | ||||||||||||||||||||
|
Comment on lines
+60
to
+64
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (non-blocking): With the default ports, only one nginx can bind With
Suggested change
|
||||||||||||||||||||
| while IFS= read -r project; do | ||||||||||||||||||||
| if [[ -n "$project" && "$project" != "$TEST_ENV_PROJECT" ]]; then | ||||||||||||||||||||
| echo "error: Compose project '$project' already has an nginx on $TEST_ENV_NETWORK." >&2 | ||||||||||||||||||||
| echo " Tear it down first: CHT_TEST_ENV_PROJECT='$project' scripts/test-env-down.sh <its cht-core path>" >&2 | ||||||||||||||||||||
| echo " If that checkout is gone: docker rm -f \$(docker ps -aq --filter label=com.docker.compose.project=$project)" >&2 | ||||||||||||||||||||
| exit 1 | ||||||||||||||||||||
| fi | ||||||||||||||||||||
| done <<< "$nginx_projects" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # `npm run local-images` builds from node_modules (bowser, uglifyjs, cleancss); | ||||||||||||||||||||
| # without them it dies on an opaque `cp: cannot stat` deep inside the build. | ||||||||||||||||||||
| if [[ ! -d "$TARGET/node_modules" ]]; then | ||||||||||||||||||||
| echo "Installing cht-core dependencies in $TARGET (npm ci — several minutes, ~1.2GB)." | ||||||||||||||||||||
| # Lifecycle scripts must run: cht-core's postinstall is patch-package, and skipping | ||||||||||||||||||||
| # it leaves the patches unapplied and the build broken. This is why --ignore-scripts | ||||||||||||||||||||
| # is not used here. The code being installed is the same tree we are about to build | ||||||||||||||||||||
| # images from and run, so npm scripts add no privilege beyond what follows. | ||||||||||||||||||||
| ( cd "$TARGET" && npm ci ) | ||||||||||||||||||||
|
Check warning on line 82 in scripts/test-env-up.sh
|
||||||||||||||||||||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
|
||||||||||||||||||||
| elif [[ ! -e "$TARGET/node_modules/bowser/bundled.js" ]] || [[ ! -x "$TARGET/node_modules/.bin/uglifyjs" ]]; then | ||||||||||||||||||||
| echo "error: cht-core dependencies in $TARGET look incomplete (the image build needs" >&2 | ||||||||||||||||||||
| echo " bowser + uglifyjs). Run 'npm ci' there yourself — this script will not" >&2 | ||||||||||||||||||||
| echo " replace an existing node_modules." >&2 | ||||||||||||||||||||
| exit 1 | ||||||||||||||||||||
| fi | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # 2. Build the app. `npm run local-images` only PACKAGES an already-built tree: | ||||||||||||||||||||
| # build-service-images.sh copies into api/build/static/, which is created by | ||||||||||||||||||||
| # build-prepare.sh (ddocs, enketo css, admin app) and filled by build-webapp-dev. | ||||||||||||||||||||
| # npm ci alone does not produce it — build-dev also runs the per-module installs. | ||||||||||||||||||||
| if [[ ! -d "$TARGET/api/build/static" ]] || [[ "${CHT_CORE_REBUILD:-}" = "1" ]]; then | ||||||||||||||||||||
| echo "Building cht-core in $TARGET (npm run build-dev — several minutes)." | ||||||||||||||||||||
| ( cd "$TARGET" && npm run build-dev ) | ||||||||||||||||||||
| else | ||||||||||||||||||||
| echo "Reusing the existing cht-core build in $TARGET." | ||||||||||||||||||||
| echo "Set CHT_CORE_REBUILD=1 to rebuild after changing cht-core source (Model A is rebuild-on-change)." | ||||||||||||||||||||
| fi | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # 3. Package those build outputs into local Docker images. | ||||||||||||||||||||
| ( cd "$TARGET" && npm run local-images ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # 4. Start the stack, joined to the shared network via the override. | ||||||||||||||||||||
| test_env_compose up -d | ||||||||||||||||||||
|
|
||||||||||||||||||||
| echo "CHT starting as Compose project '$TEST_ENV_PROJECT' on '$TEST_ENV_NETWORK'. The agent will poll /api/v2/monitoring until healthy." | ||||||||||||||||||||
| echo "To trust its cert: docker cp $TEST_ENV_PROJECT-nginx-1:/etc/nginx/private/cert.pem <file>, then NODE_EXTRA_CA_CERTS=<file> for the agent." | ||||||||||||||||||||
| echo "Node reads that file once per process: start the agent after this cert exists, and restart it after test-env-down.sh (the next up makes a new cert)." | ||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (non-blocking): Section 4 has the agent poll
https://nginx, but it never says how the agent trusts the stack's self-signed cert. At 436d5fe, readiness treats a TLS verification error as terminal (src/utils/cht-readiness.ts:17-24, :47, :140-141). I probed a self-signed server on Node 22: fetch fails withDEPTH_ZERO_SELF_SIGNED_CERT, soprovision()throws as soon as nginx answers. Node loads theNODE_EXTRA_CA_CERTSfile once, no later than the first TLS call, so the cert must be in place before the run starts.docker/docker-compose.cht-agent.yml:46-53also does not forwardCOUCHDB_USERorCOUCHDB_PASSWORD.No in-container command calls
provision()yet, so this is a documentation gap, not a break on the main path. The suggestion adds the trust step and the credential pass-through in the order that works.