Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
aa855a9
e2e test setup
rishabh-gupta-hashicorp Apr 6, 2026
c50aa59
fix lint issues
rishabh-gupta-hashicorp Apr 6, 2026
cf29156
fix issue the input device is not a TTY
rishabh-gupta-hashicorp Apr 6, 2026
4645e3f
key file permission
rishabh-gupta-hashicorp Apr 6, 2026
f18fdbc
minor chage
rishabh-gupta-hashicorp Apr 6, 2026
c9f2a70
minor change
rishabh-gupta-hashicorp Apr 6, 2026
7c47fef
ci: add --ignore-stdin to httpie calls in setup-peerings.mjs patch
rishabh-gupta-hashicorp Apr 6, 2026
ac1c659
code cleanup and adds step level timeout
rishabh-gupta-hashicorp Apr 6, 2026
7a1f09d
set continue on error to false
rishabh-gupta-hashicorp Apr 6, 2026
2c38879
adds logging
rishabh-gupta-hashicorp Apr 6, 2026
7a4c3c6
patch fixes to the workflow
rishabh-gupta-hashicorp Apr 6, 2026
ca85f0f
fix auth utils
rishabh-gupta-hashicorp Apr 6, 2026
618d120
add token create test
rishabh-gupta-hashicorp Apr 6, 2026
e02f14e
file licensing
rishabh-gupta-hashicorp Apr 6, 2026
a6383d3
fix linting issues
rishabh-gupta-hashicorp Apr 6, 2026
fd43722
move the patch into a script file
rishabh-gupta-hashicorp Apr 6, 2026
0699234
load balance test runs
rishabh-gupta-hashicorp Apr 7, 2026
1c2e12d
add parallel flag for load balancing
rishabh-gupta-hashicorp Apr 7, 2026
e130133
upgrade ember-exam to v10.1.0
rishabh-gupta-hashicorp Apr 7, 2026
8eef882
changes to fix timeout issue of test partitions
rishabh-gupta-hashicorp Apr 7, 2026
57f55a1
minor change
rishabh-gupta-hashicorp Apr 7, 2026
d817356
minor changes
rishabh-gupta-hashicorp Apr 7, 2026
8b2689b
test code cleanup and reduce bloat
rishabh-gupta-hashicorp Apr 13, 2026
1888d74
use local binary for consul server
rishabh-gupta-hashicorp Apr 14, 2026
7fdb397
minor fix
rishabh-gupta-hashicorp Apr 14, 2026
21bd5ed
make dev-docker usage
rishabh-gupta-hashicorp Apr 14, 2026
26e7112
remove dependency on 4200 ui for CI test run
rishabh-gupta-hashicorp Apr 14, 2026
fb5d621
fix lint issue
rishabh-gupta-hashicorp Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 198 additions & 0 deletions .github/scripts/consul_ui_testing_ci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
#!/usr/bin/env bash
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

set -euo pipefail

readonly START_LOG_FILE="${CONSUL_UI_TESTING_START_LOG_FILE:-consul-start.log}"
readonly START_PID_FILE="${CONSUL_UI_TESTING_START_PID_FILE:-consul-start.pid}"
readonly PEERING_SUCCESS_TEXT="We have successfully setup peering"

require_consul_image() {
if [[ -z "${CONSUL_IMAGE:-}" ]]; then
echo "::error::CONSUL_IMAGE must be set"
exit 1
fi
}

startup_pid() {
cat "$START_PID_FILE"
}

print_docker_state() {
echo "--- docker compose ps ---"
docker compose ps || true
echo "--- docker ps -a ---"
docker ps -a || true
}

fail_with_startup_logs() {
local message="$1"
local log_lines="$2"

echo "::error::${message}"
echo "--- Last ${log_lines} lines of ${START_LOG_FILE} ---"
tail -n "$log_lines" "$START_LOG_FILE" || true
print_docker_state
exit 1
}

start_servers() {
local edition_label="$1"

require_consul_image

echo "Starting Consul API servers (${edition_label}) at $(date '+%Y-%m-%d %H:%M:%S')"
yarn start "$CONSUL_IMAGE" > "$START_LOG_FILE" 2>&1 &
local pid=$!
printf '%s\n' "$pid" > "$START_PID_FILE"

echo "consul-ui-testing start PID: $(startup_pid)"
echo "Process table entry for startup command:"
ps -fp "$(startup_pid)" || true

sleep 10
if ! kill -0 "$(startup_pid)" 2>/dev/null; then
echo "--- ${START_LOG_FILE} ---"
tail -n 200 "$START_LOG_FILE" || true
echo "--- docker ps -a ---"
docker ps -a || true
echo "::error::Consul startup process exited within the first 10 seconds"
exit 1
fi

echo "Startup process is still running after 10 seconds"
echo "--- Initial ${START_LOG_FILE} (last 100 lines) ---"
tail -n 100 "$START_LOG_FILE" || true
print_docker_state
}

wait_for_peering() {
local last_log_line=0

echo "Waiting up to 5 minutes for Consul peering setup..."
for i in $(seq 1 60); do
local current_log_line
current_log_line=$(wc -l < "$START_LOG_FILE" 2>/dev/null || echo 0)

if [[ "$current_log_line" -gt "$last_log_line" ]]; then
echo "--- New ${START_LOG_FILE} output (lines $((last_log_line + 1))-${current_log_line}) ---"
sed -n "$((last_log_line + 1)),${current_log_line}p" "$START_LOG_FILE" || true
last_log_line=$current_log_line
fi

if grep -q "$PEERING_SUCCESS_TEXT" "$START_LOG_FILE" 2>/dev/null; then
echo "Consul peering is ready (after ~$((i * 5))s)"
echo "--- docker compose ps at peering completion ---"
docker compose ps || true
return 0
fi

if ! kill -0 "$(startup_pid)" 2>/dev/null; then
fail_with_startup_logs "Consul startup process exited before peering completed" 300
fi

if (( i % 6 == 0 )); then
echo "Still waiting... ($((i * 5))s elapsed)"
echo "--- docker compose ps ---"
docker compose ps || true
fi

sleep 5
done

fail_with_startup_logs "Timed out waiting for Consul peering setup (300s)" 300
}

verify_api_readiness() {
echo "Verifying Consul API readiness before starting UI..."
echo "--- docker compose ps ---"
docker compose ps || true

for i in $(seq 1 12); do
local primary_leader secondary_leader
primary_leader=$(curl -fsS http://localhost:8500/v1/status/leader || true)
secondary_leader=$(curl -fsS http://localhost:8501/v1/status/leader || true)

if [[ -n "$primary_leader" && -n "$secondary_leader" && "$primary_leader" != '""' && "$secondary_leader" != '""' ]]; then
echo "Primary leader: $primary_leader"
echo "Secondary leader: $secondary_leader"
return 0
fi

echo "Consul APIs not ready yet ($((i * 5))s elapsed)"
echo "Primary leader response: ${primary_leader:-<empty>}"
echo "Secondary leader response: ${secondary_leader:-<empty>}"
sleep 5
done

fail_with_startup_logs "Consul HTTP APIs did not become ready in time" 200
}

report_container_states() {
echo "Container states after Consul readiness:"
docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'
echo "Exited containers:"
docker ps -a --filter status=exited --format '{{.Names}} {{.Status}}' || true

local container
for container in product-api public-api payments product-db product-api-secondary public-api-secondary payments-secondary product-db-secondary frontend frontend-secondary; do
if docker ps -a --format '{{.Names}}' | grep -qx "$container"; then
echo "--- Last 40 log lines for $container ---"
docker logs --tail 40 "$container" || true
fi
done
}

wait_for_url() {
local url="$1"
local label="$2"
local attempts="${3:-30}"
local interval_seconds="${4:-2}"

echo "Waiting for ${label} on ${url}..."
for i in $(seq 1 "$attempts"); do
if curl -sf "$url" > /dev/null 2>&1; then
echo "${label} is ready (after ~$((i * interval_seconds))s)"
return 0
fi

echo "Waiting for ${label}... ($((i * interval_seconds))s elapsed)"
sleep "$interval_seconds"
done

echo "::error::Timed out waiting for ${label} ($((attempts * interval_seconds))s)"
exit 1
}

main() {
local command="${1:-}"

case "$command" in
start)
start_servers "${2:-}"
;;
wait-for-peering)
wait_for_peering
;;
verify-api)
verify_api_readiness
;;
report-containers)
report_container_states
;;
wait-for-url)
if [[ $# -lt 3 ]]; then
echo "Usage: $0 wait-for-url <url> <label> [attempts] [interval_seconds]"
exit 1
fi
wait_for_url "$2" "$3" "${4:-30}" "${5:-2}"
;;
*)
echo "Usage: $0 {start <edition>|wait-for-peering|verify-api|report-containers|wait-for-url <url> <label> [attempts] [interval_seconds]}"
exit 1
;;
esac
}

main "$@"
104 changes: 104 additions & 0 deletions .github/scripts/patch_consul_ui_testing.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env bash
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0
#
# Apply runtime patches to consul-ui-testing so it works on GitHub Actions CI.
# Must be run from within the consul-ui-testing checkout directory.

set -euo pipefail

replace_all() {
local file_path="$1"
local old_text="$2"
local new_text="$3"

python3 - "$file_path" "$old_text" "$new_text" <<'EOF'
from pathlib import Path
import sys

file_path, old_text, new_text = sys.argv[1:4]
path = Path(file_path)
content = path.read_text()

if old_text not in content:
print(f"ERROR: expected text not found in {file_path}", file=sys.stderr)
sys.exit(1)

path.write_text(content.replace(old_text, new_text))
EOF
}

replace_regex_once() {
local file_path="$1"
local pattern="$2"
local replacement="$3"

python3 - "$file_path" "$pattern" "$replacement" <<'EOF'
from pathlib import Path
import re
import sys

file_path, pattern, replacement = sys.argv[1:4]
path = Path(file_path)
content = path.read_text()
updated, count = re.subn(pattern, replacement, content, count=1, flags=re.MULTILINE)

if count != 1:
print(f"ERROR: expected one structural match in {file_path}, got {count}", file=sys.stderr)
sys.exit(1)

path.write_text(updated)
EOF
}

replace_all_dockerfiles() {
python3 <<'EOF'
from pathlib import Path
import sys

matches = list(Path('.').rglob('Dockerfile*'))
if not matches:
print('ERROR: no Dockerfile* files found', file=sys.stderr)
sys.exit(1)

updated_files = 0
for path in matches:
content = path.read_text()
if 'linux/arm64' in content:
path.write_text(content.replace('linux/arm64', 'linux/amd64'))
updated_files += 1

if updated_files == 0:
print('ERROR: linux/arm64 not found in any Dockerfile*', file=sys.stderr)
sys.exit(1)
EOF
}

# Fix architecture: GitHub-hosted Ubuntu runners are amd64, not arm64.
replace_all_dockerfiles

# Remove interactive TTY flag and make copied certs world-readable.
# GitHub Actions has no TTY, so docker run -it will hang.
replace_all build_images.sh 'docker run -it --rm' 'docker run --rm'
replace_all build_images.sh 'cp /certificates/* /tmp"' 'cp /certificates/* /tmp && chmod 644 /tmp/*.pem"'

# Reduce retry window from 100s to 60s which is sufficient in CI.
replace_all scripts/start-docker.mjs "retry(20, '5s'" "retry(12, '5s'"

# HTTPie reads stdin when there's no TTY; --ignore-stdin prevents the conflict.
replace_all scripts/setup-peerings.mjs '--verify no' '--verify no --ignore-stdin'

# Remove the secondary DC exported-services write from setup-peerings.mjs.
# Secondary DCs reject these config writes, causing the setup to fail.
replace_regex_once \
scripts/setup-peerings.mjs \
'(^\s*await createConfigurationEntry\(\{\n\s*host:\s*SERVER_2,\n\s*kind:\s*"exported-services",\n\s*config:\s*\{\n\s*Services:\s*\{\n\s*Name:\s*"redis",\n\s*Namespace:\s*"default",\n\s*Consumers:\s*\[\n\s*\{\n\s*Peer:\s*"from-dc1",\n\s*\},\n\s*\],\n\s*\},\n\s*\},\n\s*\}\);)' \
' // Secondary DC exported-services writes are rejected in Consul; skip in CI.'

# Skip the duplicate product-db seed in install.sh.
# The container already seeds via docker-entrypoint-initdb.d/products.sql on init;
# the manual psql import causes "relation already exists" errors.
replace_regex_once \
install.sh \
'(^\s*echo "Populate table\.\."\n\s*psql postgres://postgres:password@localhost:5432/products\?sslmode=disable -f /docker-entrypoint-initdb\.d/products\.sql\n\s*if \[ \$\? -eq 0 \]; then\n\s*consul services register /tmp/svc_db\.hcl\n\s*consul config write /tmp/product-db\.hcl\n\s*consul config write /tmp/intention\.hcl\n\s*sudo nohup consul connect envoy -sidecar-for \$SERVICE -token=\$CONSUL_HTTP_TOKEN >/tmp/proxy\.log 2>&1\n\s*else\n\s*sleep 2\n\s*psql postgres://postgres:password@localhost:5432/products\?sslmode=disable -f /docker-entrypoint-initdb\.d/products\.sql\n\s*consul services register /tmp/svc_db\.hcl\n\s*consul config write /tmp/product-db\.hcl\n\s*consul config write /tmp/intention\.hcl\n\s*sudo nohup consul connect envoy -sidecar-for \$SERVICE -token=\$CONSUL_HTTP_TOKEN >/tmp/proxy\.log 2>&1\n\s*fi)' \
' echo "Populate table.."\n consul services register /tmp/svc_db.hcl\n consul config write /tmp/product-db.hcl\n consul config write /tmp/intention.hcl\n sudo nohup consul connect envoy -sidecar-for $SERVICE -token=$CONSUL_HTTP_TOKEN >/tmp/proxy.log 2>&1'
Loading
Loading