mirror of
https://github.com/rustfs/rustfs.git
synced 2026-11-01 08:40:23 +00:00
4a63e53161
* test(validation): add pinned auto-testing manual lane * Install pinned ILM client for canonical validation lane * Prepare Python client for pool validation cases * Install validation client for replication cases * test(validation): provide the Python client for Heal cases * test(validation): build the candidate-bound Heal inspector artifact * test(validation): expose tool artifacts through the existing manual lane * test(validation): verify pinned Heal tools before fleet execution * test(validation): verify Heal tools against released shard captures * ci: audit retained validation environments before finalization * test(validation): add optional native Heal regression tooling * test(validation): route native tools through the existing driver * Run validation tool resolution on hosted Linux without acquiring the fleet lock * test(validation): bind C07 to pinned native authority evidence * test(validation): build optional C10 fixture codec on hosted Linux * test(ci): bind C10 execution to the verified shared codec * feat(ci): expose read-only validation fleet inventory * docs(ci): describe supported validation recovery * test(ci): add isolated manager native validation builds * test(ci): expose manager regressions through tools-only dispatch * ci(validation): consume pinned manager native prerequisites * fix(heal): preserve typed bucket errors for bounded retries * fix(heal): preserve typed errors and SSE-C response headers * ci(validation): publish isolated immutable candidate packages * test(validation): enforce private candidate manifest schema * test(validation): verify isolated package publication failures * ci(validation): dispatch isolated packages through the driver * fix(heal): deeply verify read repair payload failures * test(ecstore): verify incarnation fence at repair commit * ci(validation): verify C11 native storage evidence * fix(ci): isolate validation package publication credentials * test(ci): archive scoped F07 directory fsync evidence * fix(ecstore): retain typed quorum errors for missing parts * chore(ecstore): shrink typed-error ratchet baseline * fix(ecstore): retain below-quorum data during no-remove heal * fix(ci): allow recovery of completed cancelled validation runs * fix(heal): retain exhausted admin batch repair receipts * fix(observability): retain typed object error diagnostics * fix(s3): map typed RPC unavailability to service refusal * fix(filemeta): classify supported inline framing damage as corrupt * test(ecstore): exercise heal during committed overwrite cleanup * ci(validation): admit source-bound G01 native evidence before fleet execution * test(ecstore): fence version races and bind cleanup slots * ci(validation): require complete race native runtime preflight * test(ecstore): fence marker and batch deletes during heal * ci(validation): require G04 native and runtime admission * test(ecstore): fence multipart continue complete and abort during heal * test(ecstore): cover default heal owners and copy metadata races * ci(validation): admit source-bound owner and copy race proofs * test(scanner): assert typed corruption after metadata truncation * fix(ci): require complete native admission for multipart races * test(validation): wire I05 queued-successor native prerequisites * fix(scanner): bind object heals to the scanning disk scope * test(scanner): reuse storage boundary for scope regression * style: format RPC digest precedence assertion * fix(heal): query automatic tasks on their owning node * test(admin): reuse error facade in heal query regression * fix(heal): retain cancellation after successful bucket storage I/O * fix(ci): retain validation evidence outside runner temp * fix(heal): retain resolved object identity on terminal failures * test(scanner): adapt scope regression to admission receipts * ci(validation): inspect shared runner without fleet access * ci(validation): prepare Python before creating client environments * ci(validation): isolate each client virtual environment * ci(validation): retain interrupted evidence without fleet recovery * ci(validation): bind recovery to retained runner evidence * ci(validation): wire persistence and lock source plans * fix(validation): align read-only dispatch defaults --------- Co-authored-by: Chris <anzhengchao@gmail.com>
104 lines
4.6 KiB
Python
104 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Inspect GitHub runner registration and queued jobs without accessing the fleet."""
|
|
|
|
import argparse
|
|
from datetime import datetime, timezone
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
from resolve_functional_candidate import REPOSITORY, api, require
|
|
|
|
|
|
LABEL = "smoke-testing"
|
|
ACTIVE_STATUSES = ("queued", "in_progress", "pending", "waiting", "requested")
|
|
|
|
|
|
def validate_request(inputs):
|
|
require(isinstance(inputs, dict) and inputs.get("validation_runner_inspect_only") is True,
|
|
"runner inspection must be explicitly selected")
|
|
defaults = {"validation_runner_inspect_only": True,
|
|
"validation_retain_evidence_run_attempt": "1",
|
|
"i05_diagnostic_branch": "source-plan", "validation_tools_run_attempt": "1"}
|
|
require(all(value == defaults[key] if key in defaults else value in ("", False, None)
|
|
for key, value in inputs.items()),
|
|
"runner inspection cannot select a candidate, tools, fleet case or recovery")
|
|
|
|
|
|
def pages(path, field):
|
|
rows = []
|
|
for page in range(1, 11):
|
|
separator = "&" if "?" in path else "?"
|
|
batch = api(f"{path}{separator}per_page=100&page={page}").get(field)
|
|
require(isinstance(batch, list), "GitHub inventory response is incomplete")
|
|
rows.extend(batch)
|
|
if len(batch) < 100:
|
|
return rows
|
|
raise ValueError("GitHub inventory exceeds the bounded inspection limit")
|
|
|
|
|
|
def collect(report):
|
|
prefix = f"repos/{REPOSITORY}/actions"
|
|
report["stage"] = "runner-inventory"
|
|
runners = pages(prefix + "/runners", "runners")
|
|
report["runners"] = [
|
|
{"id": row["id"], "name": row["name"], "status": row["status"],
|
|
"busy": row["busy"], "labels": [label["name"] for label in row["labels"]]}
|
|
for row in runners if LABEL in {label["name"] for label in row["labels"]}]
|
|
online = [row for row in report["runners"] if row["status"] == "online"]
|
|
report["runnerAvailability"] = (
|
|
"online-idle" if any(row["busy"] is False for row in online) else
|
|
"online-busy" if online else "offline" if report["runners"] else "unregistered")
|
|
report["stage"] = "active-workflows"
|
|
runs = {}
|
|
for status in ACTIVE_STATUSES:
|
|
for row in pages(prefix + f"/runs?status={status}", "workflow_runs"):
|
|
runs[row["id"]] = row
|
|
report["fleetJobs"] = []
|
|
report["runsWithoutJobs"] = []
|
|
for run_id, row in sorted(runs.items()):
|
|
report["stage"] = "workflow-jobs"
|
|
jobs = pages(prefix + f"/runs/{run_id}/jobs", "jobs")
|
|
identity = {"runId": run_id, "workflow": row["name"], "status": row["status"],
|
|
"createdAt": row["created_at"], "url": row["html_url"]}
|
|
if not jobs:
|
|
report["runsWithoutJobs"].append(identity)
|
|
for job in jobs:
|
|
if LABEL in job.get("labels", []) and job["status"] != "completed":
|
|
report["fleetJobs"].append({**identity, "jobId": job["id"], "jobName": job["name"],
|
|
"jobStatus": job["status"], "runnerName": job.get("runner_name"),
|
|
"labels": job["labels"], "jobUrl": job["html_url"]})
|
|
report.update(stage="complete", complete=True, activeWorkflowCount=len(runs))
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--output", type=Path, required=True)
|
|
args = parser.parse_args()
|
|
require(not args.output.exists(), "retain the original runner inspection report")
|
|
report = {"schemaVersion": 1, "repository": REPOSITORY, "label": LABEL,
|
|
"observedAt": datetime.now(timezone.utc).isoformat(), "complete": False,
|
|
"runId": os.environ.get("GITHUB_RUN_ID"), "runAttempt": os.environ.get("GITHUB_RUN_ATTEMPT"),
|
|
"workflowCommit": os.environ.get("GITHUB_SHA"),
|
|
"runnerAvailability": "unknown", "stage": "request-validation",
|
|
"fleetAccessed": False, "mutated": False,
|
|
"cleanStateCertified": False, "sharedWorkflowIdleCertified": False,
|
|
"canonicalCasePassClaimed": False}
|
|
try:
|
|
validate_request(json.loads(os.environ["RUNNER_INSPECTION_INPUTS"]))
|
|
collect(report)
|
|
except Exception as error:
|
|
# Subprocess diagnostics can include configuration or credentials.
|
|
report["errorType"] = type(error).__name__
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
with args.output.open("x") as stream:
|
|
json.dump(report, stream, indent=2)
|
|
stream.write("\n")
|
|
print(json.dumps({key: report[key] for key in ("complete", "stage", "runnerAvailability")}))
|
|
return 0 if report["complete"] else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|