Files
rustfs/scripts/initialize_validation_evidence.py
T
cxymds 4a63e53161 test(validation): run canonical cases and build pinned Heal tools (#8455)
* 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>
2026-10-29 08:50:42 +08:00

63 lines
3.0 KiB
Python

#!/usr/bin/env python3
"""Retain self-hosted validation evidence outside Actions' temporary cleanup."""
import json
import os
from pathlib import Path
import re
import stat
import sys
MINIMUM_FREE_BYTES = 10 * 1024**3
MINIMUM_FREE_INODES = 50000
def initialize(environ=None):
env = os.environ if environ is None else environ
run, attempt = env['GITHUB_RUN_ID'], env['GITHUB_RUN_ATTEMPT']
if not all(re.fullmatch(r'[1-9][0-9]*', value) for value in (run, attempt)):
raise ValueError('Evidence requires numeric workflow run and attempt identities')
tools_only = env['VALIDATION_TOOLS_ONLY']
if tools_only not in {'true', 'false'}:
raise ValueError('Evidence requires an explicit tools-only selection')
temporary = Path(env['RUNNER_TEMP'])
if not temporary.is_absolute() or temporary.resolve(strict=True) != temporary or not temporary.is_dir():
raise ValueError('Runner temporary directory must be an existing canonical absolute directory')
parent = temporary if tools_only == 'true' else temporary.parent / 'rustfs-validation-evidence'
if tools_only == 'false':
parent.mkdir(mode=0o700, exist_ok=True)
info = parent.lstat()
if not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid() or stat.S_IMODE(info.st_mode) != 0o700:
raise ValueError('Persistent evidence parent must be a private runner-owned directory')
root = parent / f'rustfs-validation-{run}-{attempt}'
root.mkdir(mode=0o700)
observations = []
for path in dict.fromkeys((temporary, parent)):
usage = os.statvfs(path)
observations.append({'path': str(path), 'availableBytes': usage.f_bavail * usage.f_frsize,
'availableInodes': usage.f_favail, 'minimumFreeBytes': MINIMUM_FREE_BYTES,
'minimumFreeInodes': MINIMUM_FREE_INODES})
ready = all(item['availableBytes'] >= MINIMUM_FREE_BYTES and item['availableInodes'] >= MINIMUM_FREE_INODES
for item in observations)
report = {'schemaVersion': 1, 'runId': run, 'runAttempt': attempt, 'evidenceRoot': str(root),
'persistentOutsideRunnerTemp': tools_only == 'false', 'storage': observations, 'ready': ready,
'automaticEvidenceDeletion': False, 'canonicalPassClaimed': False}
with (root / 'runner-storage-preflight.json').open('x') as stream:
json.dump(report, stream, indent=2)
stream.write(chr(10))
with Path(env['GITHUB_ENV']).open('a') as stream:
if any(character in str(root) for character in (chr(10), chr(13))):
raise ValueError('Evidence path cannot contain environment-file delimiters')
stream.write('VALIDATION_ROOT=' + str(root) + chr(10))
print(json.dumps(report, sort_keys=True))
if not ready:
raise ValueError('Validation requires at least 10 GiB and 50000 free inodes on runner storage')
return root
if __name__ == '__main__':
try:
initialize()
except (OSError, ValueError, KeyError) as error:
print(f'Validation evidence initialization failed: {error}', file=sys.stderr)
sys.exit(1)