205 lines
8.5 KiB
Bash
Executable File
205 lines
8.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Build, publish, and deploy independent Harmony Fleet releases.
|
|
|
|
Usage:
|
|
fleet/scripts/release.sh [options]
|
|
|
|
At least one release version is required:
|
|
--control-version VERSION Operator and NATS callout version
|
|
--agent-version VERSION Standalone fleet-agent version
|
|
|
|
Agent options:
|
|
--agent-arch ARCH x86_64 (default) or aarch64
|
|
--namespace NAMESPACE Namespace containing Device CRs
|
|
--device NAME Device to upgrade; repeat for multiple devices
|
|
|
|
Control-plane deployment options:
|
|
--deploy-manifest PATH Private deployment Cargo.toml
|
|
--deploy-bin NAME Deployment binary
|
|
--context NAME Harmony deployment context
|
|
|
|
Registry options:
|
|
--registry HOST Default: hub.nationtech.io
|
|
--repository PATH Default: harmony
|
|
|
|
Other:
|
|
--kube-context NAME kubectl context used for Device patches
|
|
--publish-only Build and push without deploying
|
|
-h, --help
|
|
|
|
Environment equivalents:
|
|
CONTROL_VERSION, AGENT_VERSION, AGENT_ARCH, FLEET_DEPLOY_MANIFEST,
|
|
FLEET_DEPLOY_BIN, FLEET_CONTEXT, FLEET_NAMESPACE, KUBECTL_CONTEXT,
|
|
REGISTRY_USER, REGISTRY_TOKEN, OPENBAO_TOKEN, RUST_LOG.
|
|
EOF
|
|
}
|
|
|
|
fail() {
|
|
printf 'error: %s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
require_command() {
|
|
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
|
|
}
|
|
|
|
validate_version() {
|
|
[[ "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]] ||
|
|
fail "version must be SemVer without a leading v or build metadata: $1"
|
|
}
|
|
|
|
control_version="${CONTROL_VERSION:-}"
|
|
agent_version="${AGENT_VERSION:-}"
|
|
agent_arch="${AGENT_ARCH:-x86_64}"
|
|
deploy_manifest="${FLEET_DEPLOY_MANIFEST:-}"
|
|
deploy_bin="${FLEET_DEPLOY_BIN:-}"
|
|
deploy_context="${FLEET_CONTEXT:-}"
|
|
namespace="${FLEET_NAMESPACE:-}"
|
|
kube_context="${KUBECTL_CONTEXT:-}"
|
|
registry="${FLEET_REGISTRY:-hub.nationtech.io}"
|
|
repository="${FLEET_REGISTRY_REPOSITORY:-harmony}"
|
|
publish_only=0
|
|
devices=()
|
|
|
|
while (($#)); do
|
|
case "$1" in
|
|
--control-version) control_version="${2:?missing control version}"; shift 2 ;;
|
|
--agent-version) agent_version="${2:?missing agent version}"; shift 2 ;;
|
|
--agent-arch) agent_arch="${2:?missing agent architecture}"; shift 2 ;;
|
|
--deploy-manifest) deploy_manifest="${2:?missing deployment manifest}"; shift 2 ;;
|
|
--deploy-bin) deploy_bin="${2:?missing deployment binary}"; shift 2 ;;
|
|
--context) deploy_context="${2:?missing deployment context}"; shift 2 ;;
|
|
--namespace) namespace="${2:?missing namespace}"; shift 2 ;;
|
|
--device) devices+=("${2:?missing device name}"); shift 2 ;;
|
|
--kube-context) kube_context="${2:?missing kubectl context}"; shift 2 ;;
|
|
--registry) registry="${2:?missing registry}"; shift 2 ;;
|
|
--repository) repository="${2:?missing repository}"; shift 2 ;;
|
|
--publish-only) publish_only=1; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) fail "unknown argument: $1" ;;
|
|
esac
|
|
done
|
|
|
|
[[ -n "$control_version" || -n "$agent_version" ]] || fail "set a control or agent version"
|
|
[[ -z "$control_version" ]] || validate_version "$control_version"
|
|
[[ -z "$agent_version" ]] || validate_version "$agent_version"
|
|
[[ "$agent_arch" == "x86_64" || "$agent_arch" == "aarch64" ]] ||
|
|
fail "agent architecture must be x86_64 or aarch64"
|
|
if [[ -n "$control_version" && ("$registry" != "hub.nationtech.io" || "$repository" != "harmony") ]]; then
|
|
fail "control-plane releases publish to hub.nationtech.io/harmony"
|
|
fi
|
|
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
repo_root="$(cd "$script_dir/../.." && pwd)"
|
|
cd "$repo_root"
|
|
|
|
require_command cargo
|
|
require_command sha256sum
|
|
require_command stat
|
|
[[ -z "$control_version" ]] || require_command docker
|
|
|
|
if [[ -n "$control_version" || -n "$agent_version" ]]; then
|
|
: "${REGISTRY_USER:?REGISTRY_USER is required for publication}"
|
|
: "${REGISTRY_TOKEN:?REGISTRY_TOKEN is required for publication}"
|
|
fi
|
|
|
|
tmp="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp"' EXIT
|
|
|
|
agent_binary=""
|
|
agent_size=""
|
|
agent_sha256=""
|
|
agent_tag_ref=""
|
|
agent_oci_ref=""
|
|
|
|
if [[ -n "$agent_version" ]]; then
|
|
printf '==> Building fleet agent %s for %s\n' "$agent_version" "$agent_arch"
|
|
if [[ "$agent_arch" == "x86_64" ]]; then
|
|
[[ "$(uname -m)" == "x86_64" ]] || fail "x86_64 agent build requires an x86_64 host"
|
|
HARMONY_FLEET_AGENT_VERSION="$agent_version" \
|
|
cargo build --release --locked -p harmony-fleet-agent
|
|
agent_binary="$repo_root/target/release/harmony-fleet-agent"
|
|
version_output="$("$agent_binary" --version)"
|
|
[[ "$version_output" == *" $agent_version" ]] ||
|
|
fail "built agent reports the wrong version: $version_output"
|
|
else
|
|
require_command rustup
|
|
rustup target add aarch64-unknown-linux-gnu
|
|
HARMONY_FLEET_AGENT_VERSION="$agent_version" \
|
|
cargo build --release --locked --target aarch64-unknown-linux-gnu \
|
|
-p harmony-fleet-agent
|
|
agent_binary="$repo_root/target/aarch64-unknown-linux-gnu/release/harmony-fleet-agent"
|
|
fi
|
|
[[ -x "$agent_binary" ]] || fail "agent binary not produced: $agent_binary"
|
|
agent_size="$(stat -c %s "$agent_binary")"
|
|
agent_sha256="$(sha256sum "$agent_binary" | cut -d' ' -f1)"
|
|
agent_tag_ref="$registry/$repository/harmony-fleet-agent:$agent_version-$agent_arch"
|
|
fi
|
|
|
|
operator_ref=""
|
|
callout_ref=""
|
|
if [[ -n "$agent_version" ]]; then
|
|
printf '==> Publishing %s\n' "$agent_tag_ref"
|
|
agent_release_log="$tmp/agent-release.log"
|
|
REGISTRY_USER="$REGISTRY_USER" REGISTRY_TOKEN="$REGISTRY_TOKEN" \
|
|
RUST_LOG="${RUST_LOG:-info}" \
|
|
cargo run --release -p harmony-fleet-deploy --bin harmony-fleet-release -- \
|
|
agent --binary "$agent_binary" --reference "$agent_tag_ref" \
|
|
--version "$agent_version" | tee "$agent_release_log"
|
|
agent_oci_ref="$(awk -F= '$1 == "agent" { value=$2 } END { print value }' "$agent_release_log")"
|
|
[[ "$agent_oci_ref" =~ @sha256:[0-9a-f]{64}$ ]] || fail "agent digest missing from release output"
|
|
fi
|
|
|
|
if [[ -n "$control_version" ]]; then
|
|
printf '==> Resolving or publishing Fleet control plane %s\n' "$control_version"
|
|
release_log="$tmp/control-release.log"
|
|
REGISTRY_USER="$REGISTRY_USER" REGISTRY_TOKEN="$REGISTRY_TOKEN" \
|
|
RUST_LOG="${RUST_LOG:-info}" \
|
|
cargo run --release -p harmony-fleet-deploy --bin harmony-fleet-release -- \
|
|
control --version "$control_version" --push | tee "$release_log"
|
|
operator_ref="$(awk -F= '$1 == "operator" { value=$2 } END { print value }' "$release_log")"
|
|
callout_ref="$(awk -F= '$1 == "callout" { value=$2 } END { print value }' "$release_log")"
|
|
[[ "$operator_ref" =~ @sha256:[0-9a-f]{64}$ ]] || fail "operator digest missing from release output"
|
|
[[ "$callout_ref" =~ @sha256:[0-9a-f]{64}$ ]] || fail "callout digest missing from release output"
|
|
fi
|
|
|
|
if ((publish_only == 0)) && [[ -n "$control_version" ]]; then
|
|
[[ -n "$deploy_manifest" ]] || fail "--deploy-manifest is required to deploy the control plane"
|
|
[[ -n "$deploy_bin" ]] || fail "--deploy-bin is required to deploy the control plane"
|
|
[[ -n "$deploy_context" ]] || fail "--context is required to deploy the control plane"
|
|
: "${OPENBAO_TOKEN:?OPENBAO_TOKEN is required for deployment}"
|
|
printf '==> Deploying Fleet control plane %s\n' "$control_version"
|
|
OPENBAO_TOKEN="$OPENBAO_TOKEN" RUST_LOG="${RUST_LOG:-info}" \
|
|
cargo run --release --manifest-path "$deploy_manifest" --bin "$deploy_bin" -- \
|
|
deploy --context "$deploy_context" --tag "$control_version" \
|
|
--image "operator=$operator_ref" --image "callout=$callout_ref"
|
|
fi
|
|
|
|
if ((publish_only == 0)) && [[ -n "$agent_version" && ${#devices[@]} -gt 0 ]]; then
|
|
[[ -n "$namespace" ]] || fail "--namespace is required when --device is used"
|
|
require_command kubectl
|
|
printf 'note: Device setup must have refreshed the bootstrap updater with OCI support.\n'
|
|
kubectl_args=()
|
|
[[ -z "$kube_context" ]] || kubectl_args+=(--context "$kube_context")
|
|
for device in "${devices[@]}"; do
|
|
printf '==> Targeting agent %s on %s\n' "$agent_version" "$device"
|
|
kubectl "${kubectl_args[@]}" -n "$namespace" patch \
|
|
devices.fleet.nationtech.io "$device" --type merge -p \
|
|
"{\"spec\":{\"agentUpgrade\":{\"version\":\"$agent_version\",\"architecture\":\"$agent_arch\",\"artifactUrl\":\"$agent_oci_ref\",\"maxBytes\":$agent_size,\"sha256\":\"$agent_sha256\"}}}"
|
|
done
|
|
fi
|
|
|
|
printf '\nRelease outputs:\n'
|
|
[[ -z "$operator_ref" ]] || printf ' operator=%s\n callout=%s\n' "$operator_ref" "$callout_ref"
|
|
if [[ -n "$agent_oci_ref" ]]; then
|
|
printf ' agent.version=%s\n' "$agent_version"
|
|
printf ' agent.architecture=%s\n' "$agent_arch"
|
|
printf ' agent.artifactUrl=%s\n' "$agent_oci_ref"
|
|
printf ' agent.maxBytes=%s\n' "$agent_size"
|
|
printf ' agent.sha256=%s\n' "$agent_sha256"
|
|
fi
|