mirror of
https://github.com/agent-substrate/substrate.git
synced 2026-11-12 10:35:06 +08:00
## Summary Wait for the benchmark worker pool deployment to roll out before returning from `benchmarking/workloads/deploy.sh`, preventing downstream suites from racing against unready workers. ## Key Changes - **Rollout Wait:** Added `kubectl wait --for=create` followed by `kubectl rollout status` on `deployment/benchmark-ateom` in `benchmarking/workloads/deploy.sh`. - **Configurable Timeout:** Added `--wait-timeout DURATION` flag (default: `300s`) to `workloads/deploy.sh` and forwarded it through `deploy_locust.sh`. - **Validation:** Added duration regex validation (`^([0-9]+(h|m|s))+$`) to catch invalid formats/missing units early. ## Testing - Verified successful rollout wait on GKE: `deploy.sh --deploy --worker-count 2 --wait-timeout 300s` - Verified timeout failure handling: `deploy.sh --deploy --worker-count 5 --wait-timeout 1s` - Verified flag validation rejects invalid formats (`180`, `0`, `invalid`).
218 lines
7.3 KiB
Bash
Executable File
218 lines
7.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Copyright 2026 Google LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
set -o errexit -o nounset -o pipefail
|
|
|
|
ROOT="$(git rev-parse --show-toplevel)"
|
|
cd "${ROOT}"
|
|
|
|
# Source the environment variables if configured
|
|
if [[ -f .ate-dev-env.sh ]]; then
|
|
source .ate-dev-env.sh
|
|
fi
|
|
|
|
# Ensure BUCKET_NAME is set
|
|
if [[ -z "${BUCKET_NAME:-}" ]]; then
|
|
echo "Error: BUCKET_NAME environment variable is not set." >&2
|
|
exit 1
|
|
fi
|
|
|
|
MANIFEST_TEMPLATE="benchmarking/workloads/manifests/workloads.yaml.tmpl"
|
|
|
|
if [[ ! -f "${MANIFEST_TEMPLATE}" ]]; then
|
|
echo "Error: ${MANIFEST_TEMPLATE} not found in $(pwd)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
WORKER_COUNT=1
|
|
SANDBOX_CLASS="gvisor"
|
|
# Actor memory limit (ActorTemplate spec.resources.limits.memory). The default
|
|
# is the smallest size microvm admits (128Mi VMM reserve + 128Mi guest floor),
|
|
# so benchmark actors do not inherit the 2 GiB kata default and drag its page
|
|
# cache into every memory snapshot. Raise it for RAM-consuming suites.
|
|
ACTOR_MEMORY="256Mi"
|
|
# The address to which an instrumented actor container sends its telemetry.
|
|
# --otlp-endpoint sets it. Without the flag, resolve_otlp_endpoint reads the
|
|
# address that the control plane uses.
|
|
OTLP_ENDPOINT=""
|
|
# The timeout for waiting for the ateom worker pods to be ready.
|
|
WAIT_TIMEOUT="300s"
|
|
|
|
usage() {
|
|
echo "Usage: $0 [options]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --deploy Substitute env vars and deploy workloads to the cluster using ko apply"
|
|
echo " --delete Substitute env vars and delete workloads from the cluster"
|
|
echo " --worker-count N Number of WorkerPool replicas (default: 1)"
|
|
echo " --sandbox-class CLASS Sandbox runtime for the WorkerPool: gvisor | microvm (default: gvisor)."
|
|
echo " microvm requires hack/install-microvm-deps.sh --install to have run."
|
|
echo " --actor-memory SIZE Memory limit for the benchmark ActorTemplates (default: 256Mi,"
|
|
echo " the smallest size microvm admits)"
|
|
echo " --otlp-endpoint URL The address to which an instrumented actor container"
|
|
echo " sends telemetry (default: the endpoint in the"
|
|
echo " ate-otel-config ConfigMap)"
|
|
echo " --wait-timeout DURATION The timeout for waiting for the ateom workers to be ready (default: 300s)"
|
|
echo " -h, --help Show this help message"
|
|
}
|
|
|
|
# Read the endpoint from the ate-otel-config ConfigMap, which every control
|
|
# plane component reads through envFrom. The value is correct for the cluster
|
|
# in use: the GKE collector, the kind collector in otel-system, or the
|
|
# telemetry meter while a measurement runs. Thus the actors follow the control
|
|
# plane, and this script needs no test for the type of cluster.
|
|
#
|
|
# ate-system must exist, because the workloads below need its CRDs. Thus an
|
|
# absent ConfigMap is an error and not a condition to work around: a default
|
|
# here would send the actor telemetry to the wrong collector with no message.
|
|
resolve_otlp_endpoint() {
|
|
if [[ -n "${OTLP_ENDPOINT}" ]]; then
|
|
return 0
|
|
fi
|
|
OTLP_ENDPOINT="$(kubectl get configmap ate-otel-config --namespace=ate-system \
|
|
-o jsonpath='{.data.OTEL_EXPORTER_OTLP_ENDPOINT}' 2>/dev/null || true)"
|
|
if [[ -z "${OTLP_ENDPOINT}" ]]; then
|
|
echo "Error: cannot read OTEL_EXPORTER_OTLP_ENDPOINT from the ate-otel-config" >&2
|
|
echo "ConfigMap in ate-system. Deploy ate-system first, or give --otlp-endpoint." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
substitute() {
|
|
# SandboxConfig names are pinned per class (rather than defaulted) so a stale
|
|
# config from a dirty teardown fails loudly instead of silently binding this
|
|
# pool. gvisor-default is applied by hack/install-ate.sh; microvm is applied
|
|
# by hack/install-microvm-deps.sh.
|
|
local sandbox_config_name
|
|
case "${SANDBOX_CLASS}" in
|
|
gvisor) sandbox_config_name="gvisor-default" ;;
|
|
microvm) sandbox_config_name="microvm" ;;
|
|
esac
|
|
sed -e "s|\${BUCKET_NAME}|${BUCKET_NAME}|g" \
|
|
-e "s|\${WORKER_COUNT}|${WORKER_COUNT}|g" \
|
|
-e "s|\${SANDBOX_CLASS}|${SANDBOX_CLASS}|g" \
|
|
-e "s|\${SANDBOX_CONFIG_NAME}|${sandbox_config_name}|g" \
|
|
-e "s|\${OTLP_ENDPOINT}|${OTLP_ENDPOINT}|g" \
|
|
-e "s|\${ACTOR_MEMORY}|${ACTOR_MEMORY}|g" \
|
|
"${MANIFEST_TEMPLATE}"
|
|
}
|
|
|
|
deploy() {
|
|
resolve_otlp_endpoint
|
|
echo "Deploying workloads (worker_count=${WORKER_COUNT}, actor_memory=${ACTOR_MEMORY}, otlp_endpoint=${OTLP_ENDPOINT})..."
|
|
# ActorTemplate.spec has the rule `self == oldSelf` (see
|
|
# pkg/api/v1alpha1/actortemplate_types.go). Thus the API server rejects an
|
|
# apply that changes a template. A value that changes for each run — the OTLP
|
|
# endpoint or the sandbox class — needs the removal of the old template
|
|
# first. This removal is safe, because the benchmark automation deletes the
|
|
# actors between the tests.
|
|
kubectl delete actortemplate --namespace=benchmark-workloads \
|
|
--all --ignore-not-found
|
|
substitute | hack/run-tool.sh ko apply -f -
|
|
echo "Waiting for worker pool to be ready (timeout: ${WAIT_TIMEOUT})..."
|
|
kubectl wait --for=create deployment/benchmark-ateom \
|
|
--namespace=benchmark-workloads --timeout="${WAIT_TIMEOUT}"
|
|
kubectl rollout status deployment/benchmark-ateom \
|
|
--namespace=benchmark-workloads --timeout="${WAIT_TIMEOUT}"
|
|
}
|
|
|
|
delete() {
|
|
echo "Deleting workloads..."
|
|
# The template contains ko:// image references; route through `ko delete`
|
|
# so they get resolved before kubectl sees them.
|
|
substitute | hack/run-tool.sh ko delete --ignore-not-found -f -
|
|
}
|
|
|
|
if [[ "$#" -eq 0 ]]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
action=""
|
|
while [[ "$#" -gt 0 ]]; do
|
|
case "$1" in
|
|
--deploy)
|
|
action="deploy"
|
|
;;
|
|
--delete)
|
|
action="delete"
|
|
;;
|
|
--worker-count)
|
|
shift
|
|
WORKER_COUNT="$1"
|
|
;;
|
|
--worker-count=*)
|
|
WORKER_COUNT="${1#*=}"
|
|
;;
|
|
--sandbox-class)
|
|
shift
|
|
SANDBOX_CLASS="$1"
|
|
;;
|
|
--sandbox-class=*)
|
|
SANDBOX_CLASS="${1#*=}"
|
|
;;
|
|
--otlp-endpoint)
|
|
shift
|
|
OTLP_ENDPOINT="$1"
|
|
;;
|
|
--otlp-endpoint=*)
|
|
OTLP_ENDPOINT="${1#*=}"
|
|
;;
|
|
--actor-memory)
|
|
shift
|
|
ACTOR_MEMORY="$1"
|
|
;;
|
|
--actor-memory=*)
|
|
ACTOR_MEMORY="${1#*=}"
|
|
;;
|
|
--wait-timeout)
|
|
shift
|
|
WAIT_TIMEOUT="$1"
|
|
;;
|
|
--wait-timeout=*)
|
|
WAIT_TIMEOUT="${1#*=}"
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Error: Unknown option: $1" >&2
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
case "${SANDBOX_CLASS}" in
|
|
gvisor|microvm) ;;
|
|
*)
|
|
echo "Error: --sandbox-class must be gvisor or microvm, got '${SANDBOX_CLASS}'" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
if ! [[ "${WAIT_TIMEOUT}" =~ ^([0-9]+(h|m|s))+$ ]]; then
|
|
echo "Error: --wait-timeout must be a Go duration like 300s, 10m, or 1h30m, got '${WAIT_TIMEOUT}'" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${action}" == "deploy" ]]; then
|
|
deploy
|
|
elif [[ "${action}" == "delete" ]]; then
|
|
delete
|
|
fi
|