Files
substrate/benchmarking/workloads/deploy.sh
T
Sairaj Pokale d3cb3d3282 benchmarking: wait for worker pool rollout in deploy scripts (#1128)
## 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`).
2026-08-21 14:36:29 -07:00

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