fix(ci): isolate deployment smoke client (#1)
This commit is contained in:
+27
-16
@@ -144,18 +144,19 @@ jobs:
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
compose_file="deploy/compose/compose.yaml"
|
compose_file="deploy/compose/compose.yaml"
|
||||||
secret="deploy/compose/secrets/signing-key"
|
secret="deploy/compose/secrets/signing-key"
|
||||||
compose_network=""
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if [[ -n "$compose_network" ]]; then
|
|
||||||
docker network disconnect "$compose_network" "$HOSTNAME" >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
RENDEZVOUS_UID=1654 RENDEZVOUS_GID=1654 \
|
RENDEZVOUS_UID=1654 RENDEZVOUS_GID=1654 \
|
||||||
docker compose -f "$compose_file" down --volumes >/dev/null 2>&1 || true
|
docker compose -f "$compose_file" down --volumes >/dev/null 2>&1 || true
|
||||||
rm -f "$secret"
|
rm -f "$secret"
|
||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
umask 077
|
||||||
install -d -m 0700 deploy/compose/secrets
|
install -d -m 0700 deploy/compose/secrets
|
||||||
openssl rand -out "$secret" 32
|
openssl rand -out "$secret" 32
|
||||||
|
chmod 0600 "$secret"
|
||||||
|
publisher_credential="$(RENDEZVOUS_SMOKE_LOCAL_KEY="$secret" \
|
||||||
|
./scripts/mint-local-publisher-credential.sh)"
|
||||||
|
export RENDEZVOUS_PUBLISHER_CREDENTIAL="$publisher_credential"
|
||||||
chmod 0444 "$secret"
|
chmod 0444 "$secret"
|
||||||
export RENDEZVOUS_UID=1654
|
export RENDEZVOUS_UID=1654
|
||||||
export RENDEZVOUS_GID=1654
|
export RENDEZVOUS_GID=1654
|
||||||
@@ -168,6 +169,10 @@ jobs:
|
|||||||
export RENDEZVOUS_CONFIG_SOURCE="$runner_workspace_source/deploy/compose/appsettings.Production.json"
|
export RENDEZVOUS_CONFIG_SOURCE="$runner_workspace_source/deploy/compose/appsettings.Production.json"
|
||||||
export RENDEZVOUS_SECRET_SOURCE="$runner_workspace_source/deploy/compose/secrets/signing-key"
|
export RENDEZVOUS_SECRET_SOURCE="$runner_workspace_source/deploy/compose/secrets/signing-key"
|
||||||
export SOURCE_REVISION_ID="$GITHUB_SHA"
|
export SOURCE_REVISION_ID="$GITHUB_SHA"
|
||||||
|
smoke_client_image="rendezvous-smoke-client:${GITHUB_SHA}"
|
||||||
|
docker build --file eng/release-builder.Dockerfile \
|
||||||
|
--target release-builder \
|
||||||
|
--tag "$smoke_client_image" .
|
||||||
docker compose -f "$compose_file" build \
|
docker compose -f "$compose_file" build \
|
||||||
--build-arg SOURCE_REVISION_ID="$SOURCE_REVISION_ID"
|
--build-arg SOURCE_REVISION_ID="$SOURCE_REVISION_ID"
|
||||||
docker compose -f "$compose_file" up --no-build --detach
|
docker compose -f "$compose_file" up --no-build --detach
|
||||||
@@ -175,27 +180,33 @@ jobs:
|
|||||||
test -n "$container_id"
|
test -n "$container_id"
|
||||||
compose_network="$(docker inspect "$container_id" | jq -er \
|
compose_network="$(docker inspect "$container_id" | jq -er \
|
||||||
'.[0].NetworkSettings.Networks | keys[0]')"
|
'.[0].NetworkSettings.Networks | keys[0]')"
|
||||||
docker network connect "$compose_network" "$HOSTNAME"
|
|
||||||
service_ip="$(docker inspect "$container_id" | jq -er \
|
service_ip="$(docker inspect "$container_id" | jq -er \
|
||||||
--arg network "$compose_network" \
|
--arg network "$compose_network" \
|
||||||
'.[0].NetworkSettings.Networks[$network].IPAddress')"
|
'.[0].NetworkSettings.Networks[$network].IPAddress')"
|
||||||
export RENDEZVOUS_SMOKE_HTTP_URL="http://${service_ip}:8080/"
|
|
||||||
export RENDEZVOUS_SMOKE_UDP_ENDPOINT="${service_ip}:9050"
|
|
||||||
test "$(docker inspect --format '{{.Config.User}}' "$container_id")" = "1654:1654"
|
test "$(docker inspect --format '{{.Config.User}}' "$container_id")" = "1654:1654"
|
||||||
test "$(docker inspect --format '{{.HostConfig.ReadonlyRootfs}}' "$container_id")" = "true"
|
test "$(docker inspect --format '{{.HostConfig.ReadonlyRootfs}}' "$container_id")" = "true"
|
||||||
test "$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/app/appsettings.Production.json"}}{{.RW}}{{end}}{{end}}' "$container_id")" = "false"
|
test "$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/app/appsettings.Production.json"}}{{.RW}}{{end}}{{end}}' "$container_id")" = "false"
|
||||||
test "$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/run/secrets/rendezvous-signing-key"}}{{.RW}}{{end}}{{end}}' "$container_id")" = "false"
|
test "$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/run/secrets/rendezvous-signing-key"}}{{.RW}}{{end}}{{end}}' "$container_id")" = "false"
|
||||||
for attempt in {1..100}; do
|
docker run --rm \
|
||||||
if curl --fail --silent "${RENDEZVOUS_SMOKE_HTTP_URL%/}/health/ready" >/dev/null 2>&1; then
|
--network "$compose_network" \
|
||||||
break
|
--user "$(id -u):$(id -g)" \
|
||||||
fi
|
--env HOME=/tmp \
|
||||||
if (( attempt == 100 )); then
|
--env RENDEZVOUS_SMOKE_HTTP_URL="http://${service_ip}:8080/" \
|
||||||
docker compose -f "$compose_file" logs rendezvous
|
--env RENDEZVOUS_SMOKE_UDP_ENDPOINT="${service_ip}:9050" \
|
||||||
exit 1
|
--env RENDEZVOUS_PUBLISHER_CREDENTIAL \
|
||||||
fi
|
--volume "$runner_workspace_source:/source:ro" \
|
||||||
|
--workdir /source \
|
||||||
|
"$smoke_client_image" \
|
||||||
|
bash -lc '
|
||||||
|
for attempt in {1..300}; do
|
||||||
|
curl --fail --silent "${RENDEZVOUS_SMOKE_HTTP_URL%/}/health/ready" >/dev/null 2>&1 && exec ./scripts/smoke-deployment.sh
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
done
|
done
|
||||||
./scripts/smoke-deployment.sh
|
exit 1
|
||||||
|
' || {
|
||||||
|
docker compose -f "$compose_file" logs rendezvous
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
docker compose -f "$compose_file" stop --timeout 40 rendezvous
|
docker compose -f "$compose_file" stop --timeout 40 rendezvous
|
||||||
test "$(docker inspect --format '{{.State.Running}}' "$container_id")" = "false"
|
test "$(docker inspect --format '{{.State.Running}}' "$container_id")" = "false"
|
||||||
test "$(docker inspect --format '{{.State.ExitCode}}' "$container_id")" = "0"
|
test "$(docker inspect --format '{{.State.ExitCode}}' "$container_id")" = "0"
|
||||||
|
|||||||
@@ -112,18 +112,19 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
secret="deploy/compose/secrets/signing-key"
|
secret="deploy/compose/secrets/signing-key"
|
||||||
compose_network=""
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if [[ -n "$compose_network" ]]; then
|
|
||||||
docker network disconnect "$compose_network" "$HOSTNAME" >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
RENDEZVOUS_UID=1654 RENDEZVOUS_GID=1654 RENDEZVOUS_IMAGE="git.finalfactory.de/heikyu/rendezvous:${RENDEZVOUS_VERSION}" \
|
RENDEZVOUS_UID=1654 RENDEZVOUS_GID=1654 RENDEZVOUS_IMAGE="git.finalfactory.de/heikyu/rendezvous:${RENDEZVOUS_VERSION}" \
|
||||||
docker compose -f deploy/compose/compose.yaml down --volumes >/dev/null 2>&1 || true
|
docker compose -f deploy/compose/compose.yaml down --volumes >/dev/null 2>&1 || true
|
||||||
rm -f "$secret"
|
rm -f "$secret"
|
||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
umask 077
|
||||||
install -d -m 0700 deploy/compose/secrets
|
install -d -m 0700 deploy/compose/secrets
|
||||||
openssl rand -out "$secret" 32
|
openssl rand -out "$secret" 32
|
||||||
|
chmod 0600 "$secret"
|
||||||
|
publisher_credential="$(RENDEZVOUS_SMOKE_LOCAL_KEY="$secret" \
|
||||||
|
./scripts/mint-local-publisher-credential.sh)"
|
||||||
|
export RENDEZVOUS_PUBLISHER_CREDENTIAL="$publisher_credential"
|
||||||
chmod 0444 "$secret"
|
chmod 0444 "$secret"
|
||||||
export RENDEZVOUS_UID=1654
|
export RENDEZVOUS_UID=1654
|
||||||
export RENDEZVOUS_GID=1654
|
export RENDEZVOUS_GID=1654
|
||||||
@@ -141,21 +142,29 @@ jobs:
|
|||||||
test -n "$container_id"
|
test -n "$container_id"
|
||||||
compose_network="$(docker inspect "$container_id" | jq -er \
|
compose_network="$(docker inspect "$container_id" | jq -er \
|
||||||
'.[0].NetworkSettings.Networks | keys[0]')"
|
'.[0].NetworkSettings.Networks | keys[0]')"
|
||||||
docker network connect "$compose_network" "$HOSTNAME"
|
|
||||||
service_ip="$(docker inspect "$container_id" | jq -er \
|
service_ip="$(docker inspect "$container_id" | jq -er \
|
||||||
--arg network "$compose_network" \
|
--arg network "$compose_network" \
|
||||||
'.[0].NetworkSettings.Networks[$network].IPAddress')"
|
'.[0].NetworkSettings.Networks[$network].IPAddress')"
|
||||||
export RENDEZVOUS_SMOKE_HTTP_URL="http://${service_ip}:8080/"
|
docker run --rm \
|
||||||
export RENDEZVOUS_SMOKE_UDP_ENDPOINT="${service_ip}:9050"
|
--network "$compose_network" \
|
||||||
for attempt in {1..100}; do
|
--user "$(id -u):$(id -g)" \
|
||||||
curl --fail --silent "${RENDEZVOUS_SMOKE_HTTP_URL%/}/health/ready" >/dev/null 2>&1 && break
|
--env HOME=/tmp \
|
||||||
if (( attempt == 100 )); then
|
--env RENDEZVOUS_SMOKE_HTTP_URL="http://${service_ip}:8080/" \
|
||||||
docker compose -f deploy/compose/compose.yaml logs rendezvous
|
--env RENDEZVOUS_SMOKE_UDP_ENDPOINT="${service_ip}:9050" \
|
||||||
exit 1
|
--env RENDEZVOUS_PUBLISHER_CREDENTIAL \
|
||||||
fi
|
--volume "$runner_workspace_source:/source:ro" \
|
||||||
|
--workdir /source \
|
||||||
|
"$RENDEZVOUS_RELEASE_BUILDER" \
|
||||||
|
bash -lc '
|
||||||
|
for attempt in {1..300}; do
|
||||||
|
curl --fail --silent "${RENDEZVOUS_SMOKE_HTTP_URL%/}/health/ready" >/dev/null 2>&1 && exec ./scripts/smoke-deployment.sh
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
done
|
done
|
||||||
./scripts/smoke-deployment.sh
|
exit 1
|
||||||
|
' || {
|
||||||
|
docker compose -f deploy/compose/compose.yaml logs rendezvous
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
- name: Scan candidate for high and critical vulnerabilities
|
- name: Scan candidate for high and critical vulnerabilities
|
||||||
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0, post-incident safe SHA
|
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0, post-incident safe SHA
|
||||||
|
|||||||
@@ -49,9 +49,11 @@ Compose, then point `RENDEZVOUS_SMOKE_HTTP_URL` and
|
|||||||
and production-advertised service ports remain 8080/9050. Sibling-container CI
|
and production-advertised service ports remain 8080/9050. Sibling-container CI
|
||||||
runners may also set `RENDEZVOUS_CONFIG_SOURCE` and
|
runners may also set `RENDEZVOUS_CONFIG_SOURCE` and
|
||||||
`RENDEZVOUS_SECRET_SOURCE` to host-visible absolute bind-source paths; local
|
`RENDEZVOUS_SECRET_SOURCE` to host-visible absolute bind-source paths; local
|
||||||
operators should normally keep the checked-in relative defaults. CI attaches
|
operators should normally keep the checked-in relative defaults. CI starts its
|
||||||
its ephemeral job container to the isolated Compose network for smoke traffic;
|
ephemeral smoke client directly on the isolated Compose network; it does not
|
||||||
it does not widen the default loopback HTTP publication.
|
widen the default loopback HTTP publication. It mints the disposable publisher
|
||||||
|
credential while the generated key is still owner-private, then makes the key
|
||||||
|
read-only for the non-root service container.
|
||||||
|
|
||||||
`deploy/compose/appsettings.Production.json` is a local/private-bridge smoke
|
`deploy/compose/appsettings.Production.json` is a local/private-bridge smoke
|
||||||
profile, not an Internet template: TCP is published only on host loopback, the
|
profile, not an Internet template: TCP is published only on host loopback, the
|
||||||
|
|||||||
@@ -179,10 +179,13 @@ public sealed class ReleaseCompatibilityTests
|
|||||||
string workflow = File.ReadAllText(Path.Combine(root, ".gitea/workflows", workflowName));
|
string workflow = File.ReadAllText(Path.Combine(root, ".gitea/workflows", workflowName));
|
||||||
Assert.Contains("GITHUB_RUN_ID", workflow, StringComparison.Ordinal);
|
Assert.Contains("GITHUB_RUN_ID", workflow, StringComparison.Ordinal);
|
||||||
Assert.Contains("compose_network", workflow, StringComparison.Ordinal);
|
Assert.Contains("compose_network", workflow, StringComparison.Ordinal);
|
||||||
Assert.Contains("docker network connect", workflow, StringComparison.Ordinal);
|
Assert.Contains("--network \"$compose_network\"", workflow, StringComparison.Ordinal);
|
||||||
Assert.Contains("docker network disconnect", workflow, StringComparison.Ordinal);
|
Assert.Contains("publisher_credential", workflow, StringComparison.Ordinal);
|
||||||
Assert.Contains("\"$compose_network\" \"$HOSTNAME\"", workflow, StringComparison.Ordinal);
|
Assert.Contains("RENDEZVOUS_PUBLISHER_CREDENTIAL", workflow, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("for attempt in {1..300}", workflow, StringComparison.Ordinal);
|
||||||
Assert.Contains("service_ip", workflow, StringComparison.Ordinal);
|
Assert.Contains("service_ip", workflow, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("http://${service_ip}:8080/", workflow, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("${service_ip}:9050", workflow, StringComparison.Ordinal);
|
||||||
Assert.Contains("RENDEZVOUS_HTTP_HOST_PORT", workflow, StringComparison.Ordinal);
|
Assert.Contains("RENDEZVOUS_HTTP_HOST_PORT", workflow, StringComparison.Ordinal);
|
||||||
Assert.Contains("RENDEZVOUS_UDP_HOST_PORT", workflow, StringComparison.Ordinal);
|
Assert.Contains("RENDEZVOUS_UDP_HOST_PORT", workflow, StringComparison.Ordinal);
|
||||||
Assert.Contains("RENDEZVOUS_SMOKE_HTTP_URL", workflow, StringComparison.Ordinal);
|
Assert.Contains("RENDEZVOUS_SMOKE_HTTP_URL", workflow, StringComparison.Ordinal);
|
||||||
|
|||||||
Reference in New Issue
Block a user