style: format deployment helpers
Build web service image / build (push) Successful in 40s

This commit is contained in:
2026-09-12 12:05:24 +08:00
parent 0d29b828bb
commit 97cfba7f01
4 changed files with 121 additions and 47 deletions
+36 -27
View File
@@ -12,31 +12,40 @@ source=$2
target=$3
case "$command" in
backup)
[ -f "$source" ] || { echo "data file does not exist: $source" >&2; exit 1; }
mkdir -p "$target"
name="$(basename "$source").$(date -u +%Y%m%dT%H%M%SZ).manual.json"
temporary="$target/.wxagent-backup-$$.tmp"
trap 'rm -f "$temporary"' EXIT
install -m 600 "$source" "$temporary"
mv -f "$temporary" "$target/$name"
sha256sum "$target/$name"
;;
restore)
[ -f "$source" ] || { echo "backup file does not exist: $source" >&2; exit 1; }
directory=$(dirname "$target")
mkdir -p "$directory"
lock="$target.lock"
exec 9>"$lock"
flock -n 9 || { echo "data file is in use; stop the active control plane first" >&2; exit 1; }
temporary="$directory/.wxagent-restore-$$.tmp"
trap 'rm -f "$temporary"' EXIT
install -m 600 "$source" "$temporary"
python3 -m json.tool "$temporary" >/dev/null
mv -f "$temporary" "$target"
sha256sum "$target"
;;
*)
usage
;;
backup)
[ -f "$source" ] || {
echo "data file does not exist: $source" >&2
exit 1
}
mkdir -p "$target"
name="$(basename "$source").$(date -u +%Y%m%dT%H%M%SZ).manual.json"
temporary="$target/.wxagent-backup-$$.tmp"
trap 'rm -f "$temporary"' EXIT
install -m 600 "$source" "$temporary"
mv -f "$temporary" "$target/$name"
sha256sum "$target/$name"
;;
restore)
[ -f "$source" ] || {
echo "backup file does not exist: $source" >&2
exit 1
}
directory=$(dirname "$target")
mkdir -p "$directory"
lock="$target.lock"
exec 9>"$lock"
flock -n 9 || {
echo "data file is in use; stop the active control plane first" >&2
exit 1
}
temporary="$directory/.wxagent-restore-$$.tmp"
trap 'rm -f "$temporary"' EXIT
install -m 600 "$source" "$temporary"
python3 -m json.tool "$temporary" >/dev/null
mv -f "$temporary" "$target"
sha256sum "$target"
;;
*)
usage
;;
esac
+74 -16
View File
@@ -16,10 +16,22 @@ import uuid
from urllib.parse import urlsplit
def request(base_url: str, path: str, method: str, body: object | None, headers: dict[str, str], timeout: float):
def request(
base_url: str,
path: str,
method: str,
body: object | None,
headers: dict[str, str],
timeout: float,
):
url = base_url.rstrip("/") + path
parts = urlsplit(url)
if parts.scheme.lower() not in {"http", "https"} or not parts.hostname or parts.username or parts.password:
if (
parts.scheme.lower() not in {"http", "https"}
or not parts.hostname
or parts.username
or parts.password
):
raise ValueError("base URL must be an http/https URL without user information")
target = parts.path or "/"
if parts.query:
@@ -27,12 +39,21 @@ def request(base_url: str, path: str, method: str, body: object | None, headers:
data = None if body is None else json.dumps(body, ensure_ascii=False).encode()
connection: http.client.HTTPConnection
if parts.scheme.lower() == "https":
connection = http.client.HTTPSConnection(parts.hostname, parts.port, timeout=timeout, context=ssl.create_default_context())
connection = http.client.HTTPSConnection(
parts.hostname,
parts.port,
timeout=timeout,
context=ssl.create_default_context(),
)
else:
connection = http.client.HTTPConnection(parts.hostname, parts.port, timeout=timeout)
connection = http.client.HTTPConnection(
parts.hostname, parts.port, timeout=timeout
)
started = time.perf_counter()
try:
connection.request(method, target, body=data, headers={"Accept": "application/json", **headers})
connection.request(
method, target, body=data, headers={"Accept": "application/json", **headers}
)
response = connection.getresponse()
return response.status, time.perf_counter() - started, response.read()
except OSError as error:
@@ -51,23 +72,37 @@ def percentile(values: list[float], p: float) -> float:
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--base-url", default=os.getenv("WXAGENT_CONTROL_PLANE_URL", "http://127.0.0.1:8090"))
parser.add_argument(
"--base-url",
default=os.getenv("WXAGENT_CONTROL_PLANE_URL", "http://127.0.0.1:8090"),
)
parser.add_argument("--mode", choices=("ready", "reads"), default="reads")
parser.add_argument("--web-user", default=os.getenv("WXAGENT_SCALE_WEB_USER", "admin"))
parser.add_argument(
"--web-user", default=os.getenv("WXAGENT_SCALE_WEB_USER", "admin")
)
parser.add_argument("--node-id", default=os.getenv("WXAGENT_SCALE_NODE_ID", ""))
parser.add_argument("--account-id", default=os.getenv("WXAGENT_SCALE_ACCOUNT_ID", ""))
parser.add_argument(
"--account-id", default=os.getenv("WXAGENT_SCALE_ACCOUNT_ID", "")
)
parser.add_argument("--requests", type=int, default=100)
parser.add_argument("--workers", type=int, default=10)
parser.add_argument("--timeout", type=float, default=15.0)
args = parser.parse_args()
if args.requests < 1 or args.requests > 5000 or args.workers < 1 or args.workers > 100:
if (
args.requests < 1
or args.requests > 5000
or args.workers < 1
or args.workers > 100
):
parser.error("requests must be 1..5000 and workers must be 1..100")
headers: dict[str, str] = {}
if args.mode == "reads":
password = os.getenv("WXAGENT_SCALE_WEB_PASSWORD")
if not password or not args.node_id or not args.account_id:
parser.error("reads mode requires WXAGENT_SCALE_WEB_PASSWORD, --node-id and --account-id")
parser.error(
"reads mode requires WXAGENT_SCALE_WEB_PASSWORD, --node-id and --account-id"
)
status, _, payload = request(
args.base_url,
"/v1/auth/login",
@@ -77,19 +112,37 @@ def main() -> int:
args.timeout,
)
if status != 200:
print(json.dumps({"ok": False, "phase": "login", "status": status, "body": payload[:256].decode(errors="replace")}))
print(
json.dumps(
{
"ok": False,
"phase": "login",
"status": status,
"body": payload[:256].decode(errors="replace"),
}
)
)
return 1
try:
login = json.loads(payload)
access_token = login["access_token"]
except (UnicodeDecodeError, json.JSONDecodeError, KeyError, TypeError) as error:
print(json.dumps({"ok": False, "phase": "login-response", "error": str(error)}))
print(
json.dumps(
{"ok": False, "phase": "login-response", "error": str(error)}
)
)
return 1
headers = {"Authorization": "Bearer " + access_token, "Content-Type": "application/json"}
headers = {
"Authorization": "Bearer " + access_token,
"Content-Type": "application/json",
}
def one(index: int) -> tuple[int, float]:
if args.mode == "ready":
status, elapsed, _ = request(args.base_url, "/readyz", "GET", None, {}, args.timeout)
status, elapsed, _ = request(
args.base_url, "/readyz", "GET", None, {}, args.timeout
)
else:
body = {
"node_id": args.node_id,
@@ -98,7 +151,9 @@ def main() -> int:
"limit": 1,
"offset": 0,
}
status, elapsed, _ = request(args.base_url, "/v1/reads/sessions", "POST", body, headers, args.timeout)
status, elapsed, _ = request(
args.base_url, "/v1/reads/sessions", "POST", body, headers, args.timeout
)
return status, elapsed
with concurrent.futures.ThreadPoolExecutor(max_workers=args.workers) as executor:
@@ -114,7 +169,10 @@ def main() -> int:
"failed": args.requests - success,
"p50_ms": round(statistics.median(elapsed) * 1000, 2) if elapsed else 0,
"p95_ms": round(percentile(elapsed, 0.95) * 1000, 2),
"statuses": {str(status): sum(item[0] == status for item in results) for status, _ in results},
"statuses": {
str(status): sum(item[0] == status for item in results)
for status, _ in results
},
}
print(json.dumps(summary, ensure_ascii=False, sort_keys=True))
return 0 if summary["ok"] else 1
+10 -4
View File
@@ -7,10 +7,16 @@ if [ "$#" -ne 2 ]; then
fi
certificate=$1
list=$2
[ -f "$certificate" ] || { echo "certificate does not exist: $certificate" >&2; exit 1; }
[ -f "$certificate" ] || {
echo "certificate does not exist: $certificate" >&2
exit 1
}
fingerprint=$(openssl x509 -in "$certificate" -outform DER | sha256sum | awk '{print $1}')
[ "${#fingerprint}" -eq 64 ] || { echo "could not calculate certificate fingerprint" >&2; exit 1; }
[ "${#fingerprint}" -eq 64 ] || {
echo "could not calculate certificate fingerprint" >&2
exit 1
}
mkdir -p "$(dirname "$list")"
lock="$list.lock"
exec 9>"$lock"
@@ -22,9 +28,9 @@ fi
temporary="${list}.tmp.$$"
trap 'rm -f "$temporary"' EXIT
if [ -f "$list" ]; then
cat "$list" > "$temporary"
cat "$list" >"$temporary"
fi
printf '%s\n' "$fingerprint" >> "$temporary"
printf '%s\n' "$fingerprint" >>"$temporary"
chmod 600 "$temporary"
mv -f "$temporary" "$list"
printf '%s\n' "$fingerprint"