mirror of
https://github.com/rustfs/rustfs.git
synced 2026-10-31 16:22:06 +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>
245 lines
11 KiB
YAML
245 lines
11 KiB
YAML
# Copyright 2024 RustFS Team
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
name: RustFS Validation Package
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
source_sha:
|
|
description: Exact RustFS commit SHA to build
|
|
required: true
|
|
type: string
|
|
secrets:
|
|
R2_ACCESS_KEY_ID:
|
|
required: true
|
|
R2_SECRET_ACCESS_KEY:
|
|
required: true
|
|
R2_ENDPOINT:
|
|
required: true
|
|
R2_BUCKET:
|
|
required: true
|
|
workflow_dispatch:
|
|
inputs:
|
|
source_sha:
|
|
description: Exact RustFS commit SHA to build
|
|
required: true
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: rustfs-validation-package-${{ inputs.source_sha }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
resolve-source:
|
|
if: github.repository == 'rustfs/rustfs'
|
|
name: Resolve exact validation source
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
outputs:
|
|
source_sha: ${{ steps.source.outputs.sha }}
|
|
source_ref: ${{ steps.source.outputs.ref }}
|
|
steps:
|
|
- name: Validate requested source
|
|
id: source
|
|
env:
|
|
REQUESTED_SHA: ${{ inputs.source_sha }}
|
|
run: |
|
|
set -euo pipefail
|
|
[[ "${REQUESTED_SHA}" =~ ^[0-9a-f]{40}$ ]]
|
|
source_directory="$(mktemp -d "${RUNNER_TEMP}/validation-source.XXXXXX")"
|
|
trap 'rm -rf "${source_directory}"' EXIT
|
|
git init -q "${source_directory}"
|
|
git -C "${source_directory}" fetch --no-tags --depth=1 https://github.com/rustfs/rustfs.git "${REQUESTED_SHA}"
|
|
test "$(git -C "${source_directory}" rev-parse FETCH_HEAD)" = "${REQUESTED_SHA}"
|
|
echo "sha=${REQUESTED_SHA}" >> "${GITHUB_OUTPUT}"
|
|
echo "ref=${REQUESTED_SHA}" >> "${GITHUB_OUTPUT}"
|
|
|
|
build:
|
|
needs: resolve-source
|
|
name: Build immutable validation DEB
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 150
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
VALIDATION_SOURCE_SHA: ${{ needs.resolve-source.outputs.source_sha }}
|
|
steps:
|
|
- name: Checkout exact source
|
|
uses: actions/checkout@f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a # v7
|
|
with:
|
|
persist-credentials: false
|
|
ref: ${{ needs.resolve-source.outputs.source_sha }}
|
|
|
|
- name: Confirm checked out source
|
|
env:
|
|
EXPECTED_SHA: ${{ needs.resolve-source.outputs.source_sha }}
|
|
run: test "$(git rev-parse HEAD)" = "${EXPECTED_SHA}"
|
|
|
|
- name: Setup Rust environment
|
|
uses: ./.github/actions/setup
|
|
with:
|
|
cache-shared-key: validation-package-hosted-x86_64-unknown-linux-gnu
|
|
cache-save-if: "false"
|
|
install-build-packaging-tools: "false"
|
|
install-test-tools: "false"
|
|
|
|
- name: Build RustFS
|
|
run: cargo build --release --locked --target x86_64-unknown-linux-gnu -p rustfs --bins
|
|
|
|
- name: Build validation DEB
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
command -v fakeroot >/dev/null 2>&1 || sudo apt-get install -y -qq fakeroot
|
|
package_dir="rustfs-validation-${VALIDATION_SOURCE_SHA}"
|
|
package_file="${RUNNER_TEMP}/validation-package/rustfs.deb"
|
|
mkdir -p "${RUNNER_TEMP}/validation-package"
|
|
binary="target/x86_64-unknown-linux-gnu/release/rustfs"
|
|
test -x "${binary}"
|
|
mkdir -p "${package_dir}/DEBIAN" "${package_dir}/usr/bin" "${package_dir}/etc/default" \
|
|
"${package_dir}/lib/systemd/system" "${package_dir}/usr/share/doc/rustfs"
|
|
cp "${binary}" "${package_dir}/usr/bin/rustfs"
|
|
chmod 755 "${package_dir}/usr/bin/rustfs"
|
|
cp deploy/build/rustfs.service "${package_dir}/lib/systemd/system/"
|
|
cat > "${package_dir}/etc/default/rustfs" <<'ENVEOF'
|
|
# RustFS Environment Configuration
|
|
# RUSTFS_ACCESS_KEY=<changeme>
|
|
# RUSTFS_SECRET_KEY=<changeme>
|
|
# RUSTFS_VOLUMES=""
|
|
# RUSTFS_ADDRESS=":9000"
|
|
ENVEOF
|
|
cat > "${package_dir}/DEBIAN/control" <<'CONTROLEOF'
|
|
Package: rustfs
|
|
Version: 0.0.0~validation
|
|
Section: utils
|
|
Priority: optional
|
|
Architecture: amd64
|
|
Depends: libc6 (>= 2.31)
|
|
Maintainer: RustFS Team <support@rustfs.com>
|
|
Description: RustFS immutable validation package
|
|
Candidate package for RustFS validation only.
|
|
CONTROLEOF
|
|
printf '%s\n' /etc/default/rustfs > "${package_dir}/DEBIAN/conffiles"
|
|
./scripts/release/package_service_scripts.sh after-install > "${package_dir}/DEBIAN/postinst"
|
|
./scripts/release/package_service_scripts.sh before-remove > "${package_dir}/DEBIAN/prerm"
|
|
./scripts/release/package_service_scripts.sh after-remove > "${package_dir}/DEBIAN/postrm"
|
|
chmod 755 "${package_dir}/DEBIAN/postinst" "${package_dir}/DEBIAN/prerm" "${package_dir}/DEBIAN/postrm"
|
|
cp LICENSE README.md "${package_dir}/usr/share/doc/rustfs/"
|
|
fakeroot dpkg-deb --build "${package_dir}" "${package_file}"
|
|
test -s "${package_file}"
|
|
|
|
- name: Upload opaque validation package
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: validation-package-bytes-${{ github.run_id }}-${{ github.run_attempt }}
|
|
path: ${{ runner.temp }}/validation-package/rustfs.deb
|
|
if-no-files-found: error
|
|
|
|
publish:
|
|
needs: [resolve-source, build]
|
|
name: Publish immutable validation DEB
|
|
# Candidate-controlled build scripts never run on this fresh hosted runner.
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
env:
|
|
VALIDATION_SOURCE_SHA: ${{ needs.resolve-source.outputs.source_sha }}
|
|
VALIDATION_SOURCE_REF: ${{ needs.resolve-source.outputs.source_ref }}
|
|
steps:
|
|
- name: Download opaque validation package
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: validation-package-bytes-${{ github.run_id }}-${{ github.run_attempt }}
|
|
path: ${{ runner.temp }}/validation-package-input
|
|
|
|
- name: Validate and stage opaque package bytes
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
python3 -I - <<'PYTHON'
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import stat
|
|
|
|
root = Path(os.environ["RUNNER_TEMP"])
|
|
incoming = root / "validation-package-input"
|
|
if incoming.is_symlink() or not incoming.is_dir() or sorted(p.name for p in incoming.iterdir()) != ["rustfs.deb"]:
|
|
raise SystemExit("Validation artifact must contain exactly one rustfs.deb file")
|
|
fd = os.open(incoming / "rustfs.deb", os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK)
|
|
with os.fdopen(fd, "rb") as source:
|
|
info = os.fstat(source.fileno())
|
|
if not stat.S_ISREG(info.st_mode) or info.st_size == 0:
|
|
raise SystemExit("Validation package must be a nonempty regular file")
|
|
staged = root / "validation-publication"
|
|
staged.mkdir(mode=0o700)
|
|
with (staged / "rustfs.deb").open("xb") as target:
|
|
shutil.copyfileobj(source, target)
|
|
PYTHON
|
|
|
|
- name: Prepare trusted publication tools
|
|
id: tools
|
|
env:
|
|
AWS_EC2_METADATA_DISABLED: true
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
AWS_CLI=aws
|
|
if ! "${AWS_CLI}" s3api put-object --generate-cli-skeleton input | jq -e 'has("IfNoneMatch")' >/dev/null; then
|
|
sudo apt-get update
|
|
sudo apt-get install -y -qq python3-venv
|
|
AWS_CLI_DIR="$(mktemp -d "${RUNNER_TEMP}/validation-awscli.XXXXXX")"
|
|
python3 -m venv "${AWS_CLI_DIR}"
|
|
"${AWS_CLI_DIR}/bin/python" -m pip install --disable-pip-version-check 'awscli==1.44.79'
|
|
AWS_CLI="${AWS_CLI_DIR}/bin/aws"
|
|
fi
|
|
"${AWS_CLI}" s3api put-object --generate-cli-skeleton input | jq -e 'has("IfNoneMatch")' >/dev/null
|
|
echo "aws_cli=${AWS_CLI}" >> "${GITHUB_OUTPUT}"
|
|
|
|
- name: Upload validation package to immutable R2 path
|
|
id: publish
|
|
env:
|
|
PACKAGE_FILE: ${{ runner.temp }}/validation-publication/rustfs.deb
|
|
AWS_CLI: ${{ steps.tools.outputs.aws_cli }}
|
|
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
|
|
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
|
|
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
|
|
R2_BUCKET: ${{ secrets.R2_BUCKET }}
|
|
AWS_EC2_METADATA_DISABLED: true
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
if [[ -z "${R2_ACCESS_KEY_ID}" || -z "${R2_SECRET_ACCESS_KEY}" || -z "${R2_ENDPOINT}" || -z "${R2_BUCKET}" ]]; then
|
|
echo "Validation package publication requires complete R2 configuration" >&2
|
|
exit 1
|
|
fi
|
|
[[ "${VALIDATION_SOURCE_SHA}" =~ ^[0-9a-f]{40}$ ]]
|
|
test "${VALIDATION_SOURCE_REF}" = "${VALIDATION_SOURCE_SHA}"
|
|
test -s "${PACKAGE_FILE}" && test ! -L "${PACKAGE_FILE}"
|
|
export AWS_ACCESS_KEY_ID="${R2_ACCESS_KEY_ID}" AWS_SECRET_ACCESS_KEY="${R2_SECRET_ACCESS_KEY}" AWS_DEFAULT_REGION=auto
|
|
package_sha256="$(sha256sum "${PACKAGE_FILE}" | cut -d ' ' -f 1)"
|
|
key="artifacts/rustfs/packages/validation/runs/${GITHUB_RUN_ID}/${GITHUB_RUN_ATTEMPT}/${package_sha256}/rustfs.deb"
|
|
url="https://dl.rustfs.com/${key}"
|
|
"${AWS_CLI}" s3api put-object --bucket "${R2_BUCKET}" --key "${key}" --body "${PACKAGE_FILE}" \
|
|
--if-none-match '*' --endpoint-url "${R2_ENDPOINT}"
|
|
downloaded_sha256="$(curl -fsSL --retry 3 --connect-timeout 15 --max-time 300 "${url}" | sha256sum | cut -d ' ' -f 1)"
|
|
test "${downloaded_sha256}" = "${package_sha256}"
|
|
manifest="${RUNNER_TEMP}/validation-candidate-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}.json"
|
|
jq -n --arg workflow_sha "${GITHUB_SHA}" --arg source_ref "${VALIDATION_SOURCE_REF}" \
|
|
--arg source_sha "${VALIDATION_SOURCE_SHA}" --arg channel validation \
|
|
--argjson build_run_id "${GITHUB_RUN_ID}" --argjson build_run_attempt "${GITHUB_RUN_ATTEMPT}" \
|
|
--arg package_url "${url}" --arg package_sha256 "${package_sha256}" \
|
|
'{schema: 3, workflow_sha: $workflow_sha, source_ref: $source_ref, source_sha: $source_sha, channel: $channel, build_run_id: $build_run_id, build_run_attempt: $build_run_attempt, package_url: $package_url, package_sha256: $package_sha256}' > "${manifest}"
|
|
echo "candidate_file=${manifest}" >> "${GITHUB_OUTPUT}"
|
|
|
|
- name: Upload validation candidate manifest
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: validation-candidate-${{ github.run_id }}-${{ github.run_attempt }}
|
|
path: ${{ steps.publish.outputs.candidate_file }}
|
|
if-no-files-found: error
|