Deploy billing-service from build artifact
This commit is contained in:
parent
8a18e547fc
commit
7fbc23257a
52
.github/workflows/release-traceability.yml
vendored
52
.github/workflows/release-traceability.yml
vendored
@ -26,6 +26,18 @@ jobs:
|
||||
SERVICE_IMAGE_LATEST_REF: ghcr.io/${{ github.repository }}:latest
|
||||
run: bash ./scripts/github-actions/build-service-image.sh
|
||||
|
||||
- name: Build linux binary artifact
|
||||
env:
|
||||
BILLING_SERVICE_BINARY_ARTIFACT: dist/billing-service-linux-amd64
|
||||
run: bash ./scripts/github-actions/build-service-binary.sh
|
||||
|
||||
- name: Upload billing-service binary artifact
|
||||
uses: actions/upload-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: billing-service-linux-amd64
|
||||
path: dist/billing-service-linux-amd64
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Push image
|
||||
run: bash ./scripts/github-actions/push-image-placeholder.sh
|
||||
|
||||
@ -35,12 +47,32 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Download billing-service binary artifact
|
||||
uses: actions/download-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: billing-service-linux-amd64
|
||||
path: dist
|
||||
|
||||
- name: Install ansible
|
||||
run: sudo apt-get update && sudo apt-get install -y ansible
|
||||
|
||||
- name: Configure deploy SSH
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
run: |
|
||||
test -n "${SSH_PRIVATE_KEY}"
|
||||
install -d -m 0700 ~/.ssh
|
||||
printf '%s\n' "${SSH_PRIVATE_KEY}" > ~/.ssh/id_ed25519
|
||||
chmod 0600 ~/.ssh/id_ed25519
|
||||
ssh-keyscan -H jp-xhttp-contabo.svc.plus >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Deploy via playbook
|
||||
env:
|
||||
IMAGE_REF: ${{ needs.build.outputs.service_image_ref }}
|
||||
BILLING_SERVICE_IMAGE_REF: ${{ needs.build.outputs.service_image_ref }}
|
||||
BILLING_SERVICE_IMAGE_TAG: ${{ needs.build.outputs.service_image_tag }}
|
||||
BILLING_SERVICE_IMAGE_COMMIT: ${{ needs.build.outputs.service_image_commit }}
|
||||
BILLING_SERVICE_BINARY_ARTIFACT: dist/billing-service-linux-amd64
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
INTERNAL_SERVICE_TOKEN: ${{ secrets.INTERNAL_SERVICE_TOKEN }}
|
||||
STACK_TARGET_HOST: jp-xhttp-contabo.svc.plus
|
||||
run: bash ./scripts/github-actions/deploy-billing-service.sh
|
||||
|
||||
validate:
|
||||
@ -57,8 +89,18 @@ jobs:
|
||||
- name: Verify traceability script cases
|
||||
run: bash ./scripts/github-actions/test-validate-release-traceability.sh
|
||||
|
||||
- name: Configure validate SSH
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
run: |
|
||||
test -n "${SSH_PRIVATE_KEY}"
|
||||
install -d -m 0700 ~/.ssh
|
||||
printf '%s\n' "${SSH_PRIVATE_KEY}" > ~/.ssh/id_ed25519
|
||||
chmod 0600 ~/.ssh/id_ed25519
|
||||
ssh-keyscan -H jp-xhttp-contabo.svc.plus >> ~/.ssh/known_hosts
|
||||
|
||||
- name: Validate runtime traceability
|
||||
env:
|
||||
SERVICE_IMAGE_REF: ${{ needs.build.outputs.service_image_ref }}
|
||||
RUNTIME_PING_URL: https://accounts.svc.plus/api/ping
|
||||
run: bash ./scripts/github-actions/validate-release-traceability.sh
|
||||
STACK_TARGET_HOST: jp-xhttp-contabo.svc.plus
|
||||
run: bash ./scripts/github-actions/validate-release-traceability-remote.sh
|
||||
|
||||
@ -5,7 +5,9 @@ release identity.
|
||||
|
||||
## Runtime contract
|
||||
|
||||
- `IMAGE` must contain the full image reference used to start the container.
|
||||
- `IMAGE` must contain the full release image reference produced by the build
|
||||
job, even when the target host runs the service as a systemd binary instead
|
||||
of a container.
|
||||
- `/api/ping` returns `image`, `tag`, `commit`, and `version`.
|
||||
- `tag`, `commit`, and `version` are derived from `IMAGE`.
|
||||
- If `IMAGE` is missing or malformed, the derived fields stay empty instead of
|
||||
@ -14,9 +16,13 @@ release identity.
|
||||
## Pipeline contract
|
||||
|
||||
- Build must produce `service_image_ref` only from the full `GITHUB_SHA`.
|
||||
- Deploy must consume `service_image_ref` and pass it through as the runtime
|
||||
image identity.
|
||||
- Validate must use `GET https://accounts.svc.plus/api/ping`.
|
||||
- Build must also produce the linux billing-service binary artifact consumed by
|
||||
deploy.
|
||||
- Deploy must consume that build artifact directly and must not rebuild on the
|
||||
target host.
|
||||
- Deploy must pass `service_image_ref` through as the runtime image identity.
|
||||
- Validate must query `billing-service` on the deployment target at
|
||||
`http://127.0.0.1:8081/api/ping` over SSH.
|
||||
- Validate must derive `tag` and `commit` from `service_image_ref` and compare
|
||||
them against `/api/ping`.
|
||||
- Validate must fail when runtime `image`, `tag`, `commit`, or `version` is
|
||||
@ -27,11 +33,11 @@ release identity.
|
||||
## External playbook alignment
|
||||
|
||||
The external `playbooks/deploy_billing_service.yml` playbook should accept
|
||||
`IMAGE_REF` (or an equivalent full image reference variable), derive any
|
||||
repo/tag helpers from it, and inject `IMAGE=<full image ref>` into the running
|
||||
container environment for the service exposed through
|
||||
`https://accounts.svc.plus/api/ping`.
|
||||
`BILLING_SERVICE_BINARY_ARTIFACT` and `BILLING_SERVICE_IMAGE_REF`, deploy the
|
||||
binary artifact to the target host without rebuilding it there, and inject
|
||||
`IMAGE=<full image ref>` into `/etc/default/billing-service` for the runtime
|
||||
served through `http://127.0.0.1:8081/api/ping`.
|
||||
|
||||
If `accounts.svc.plus/api/ping` keeps returning empty runtime metadata, treat
|
||||
If `billing-service /api/ping` keeps returning empty runtime metadata, treat
|
||||
that as a deployment contract failure: the runtime did not receive the full
|
||||
`IMAGE` value and release traceability is broken.
|
||||
|
||||
14
scripts/github-actions/build-service-binary.sh
Normal file
14
scripts/github-actions/build-service-binary.sh
Normal file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
artifact_path="${BILLING_SERVICE_BINARY_ARTIFACT:?BILLING_SERVICE_BINARY_ARTIFACT is required}"
|
||||
target_dir="$(dirname "${artifact_path}")"
|
||||
|
||||
mkdir -p "${target_dir}"
|
||||
|
||||
CGO_ENABLED="${CGO_ENABLED:-0}" \
|
||||
GOOS="${GOOS:-linux}" \
|
||||
GOARCH="${GOARCH:-amd64}" \
|
||||
go build -buildvcs=false -o "${artifact_path}" ./cmd/billing-service
|
||||
|
||||
chmod 0755 "${artifact_path}"
|
||||
@ -1,5 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
test -n "${IMAGE_REF:?IMAGE_REF is required}"
|
||||
ansible-playbook -i inventory playbooks/deploy_billing_service.yml
|
||||
target_host="${STACK_TARGET_HOST:?STACK_TARGET_HOST is required}"
|
||||
artifact_path="${BILLING_SERVICE_BINARY_ARTIFACT:?BILLING_SERVICE_BINARY_ARTIFACT is required}"
|
||||
image_ref="${BILLING_SERVICE_IMAGE_REF:-${IMAGE_REF:-}}"
|
||||
database_url="${DATABASE_URL:?DATABASE_URL is required}"
|
||||
internal_service_token="${INTERNAL_SERVICE_TOKEN:?INTERNAL_SERVICE_TOKEN is required}"
|
||||
playbooks_repo_url="${PLAYBOOKS_REPO_URL:-https://github.com/x-evor/playbooks.git}"
|
||||
playbooks_repo_ref="${PLAYBOOKS_REPO_REF:-c0f1a1c2ee00e4131db2484c8cc00b2bc4dc1263}"
|
||||
|
||||
if [[ ! -f "${artifact_path}" ]]; then
|
||||
echo "binary artifact not found: ${artifact_path}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${image_ref}" ]]; then
|
||||
echo "BILLING_SERVICE_IMAGE_REF or IMAGE_REF is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
workdir="$(mktemp -d)"
|
||||
trap 'rm -rf "${workdir}"' EXIT
|
||||
|
||||
git clone --depth 1 "${playbooks_repo_url}" "${workdir}/playbooks"
|
||||
git -C "${workdir}/playbooks" fetch --depth 1 origin "${playbooks_repo_ref}"
|
||||
git -C "${workdir}/playbooks" checkout --detach FETCH_HEAD
|
||||
|
||||
export ANSIBLE_HOST_KEY_CHECKING=false
|
||||
export BILLING_SERVICE_BINARY_ARTIFACT="$(cd "$(dirname "${artifact_path}")" && pwd)/$(basename "${artifact_path}")"
|
||||
export BILLING_SERVICE_IMAGE_REF="${image_ref}"
|
||||
export DATABASE_URL="${database_url}"
|
||||
export INTERNAL_SERVICE_TOKEN="${internal_service_token}"
|
||||
|
||||
cd "${workdir}/playbooks"
|
||||
ansible-playbook -i inventory.ini deploy_billing_service.yml --limit "${target_host}"
|
||||
|
||||
@ -30,6 +30,34 @@ if not any(line.strip() == "- build" for line in validate_block):
|
||||
if not any(line.strip() == "- deploy" for line in validate_block):
|
||||
raise SystemExit("validate job must depend on deploy")
|
||||
|
||||
build_block = []
|
||||
deploy_block = []
|
||||
current_job = None
|
||||
|
||||
for line in lines:
|
||||
if line.startswith(" build:"):
|
||||
current_job = "build"
|
||||
elif line.startswith(" deploy:"):
|
||||
current_job = "deploy"
|
||||
elif line.startswith(" validate:"):
|
||||
current_job = "validate"
|
||||
elif line.startswith(" ") and not line.startswith(" "):
|
||||
current_job = None
|
||||
|
||||
if current_job == "build":
|
||||
build_block.append(line)
|
||||
elif current_job == "deploy":
|
||||
deploy_block.append(line)
|
||||
|
||||
if not any("Upload billing-service binary artifact" in line for line in build_block):
|
||||
raise SystemExit("build job must upload the billing-service binary artifact")
|
||||
|
||||
if not any("Download billing-service binary artifact" in line for line in deploy_block):
|
||||
raise SystemExit("deploy job must download the billing-service binary artifact")
|
||||
|
||||
if not any("BILLING_SERVICE_IMAGE_REF: ${{ needs.build.outputs.service_image_ref }}" in line for line in deploy_block):
|
||||
raise SystemExit("deploy job must consume needs.build.outputs.service_image_ref")
|
||||
|
||||
if not any("SERVICE_IMAGE_REF: ${{ needs.build.outputs.service_image_ref }}" in line for line in validate_block):
|
||||
raise SystemExit("validate job must consume needs.build.outputs.service_image_ref")
|
||||
PY
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
service_image_ref="${SERVICE_IMAGE_REF:?SERVICE_IMAGE_REF is required}"
|
||||
target_host="${STACK_TARGET_HOST:?STACK_TARGET_HOST is required}"
|
||||
ssh_target="${RUNTIME_SSH_TARGET:-root@${target_host}}"
|
||||
runtime_ping_path="${RUNTIME_PING_PATH:-http://127.0.0.1:8081/api/ping}"
|
||||
tag="${service_image_ref##*:}"
|
||||
commit="${tag#sha-}"
|
||||
|
||||
ssh -o BatchMode=yes "${ssh_target}" "systemctl is-active billing-service >/dev/null"
|
||||
|
||||
runtime_payload="$(ssh -o BatchMode=yes "${ssh_target}" "curl -fsS ${runtime_ping_path}")"
|
||||
|
||||
jq -e \
|
||||
--arg image "${service_image_ref}" \
|
||||
--arg tag "${tag}" \
|
||||
--arg commit "${commit}" \
|
||||
'
|
||||
(.image | type == "string" and length > 0) and
|
||||
(.tag | type == "string" and length > 0) and
|
||||
(.commit | type == "string" and length > 0) and
|
||||
(.version | type == "string" and length > 0) and
|
||||
.image == $image and
|
||||
.tag == $tag and
|
||||
.commit == $commit and
|
||||
.version == $commit
|
||||
' <<<"${runtime_payload}" >/dev/null
|
||||
Loading…
Reference in New Issue
Block a user