243 lines
9.4 KiB
Bash
Executable File
243 lines
9.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Run Ceph s3-tests against post3 (FS backend).
|
|
#
|
|
# Usage:
|
|
# bash s3-compliance/run-s3-tests.sh # run tests
|
|
# bash s3-compliance/run-s3-tests.sh --collect-only # dry-run: list matching tests
|
|
#
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
S3TESTS_DIR="$REPO_ROOT/s3-tests"
|
|
SCRIPT_DIR="$REPO_ROOT/s3-compliance"
|
|
|
|
# --- Validate prerequisites ---------------------------------------------------
|
|
|
|
if [ ! -d "$S3TESTS_DIR" ]; then
|
|
echo "ERROR: s3-tests submodule not found at $S3TESTS_DIR"
|
|
echo "Run: git submodule update --init"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v python3 &>/dev/null; then
|
|
echo "ERROR: python3 is required"
|
|
exit 1
|
|
fi
|
|
|
|
# --- Pick a free port ---------------------------------------------------------
|
|
|
|
PORT=$(python3 -c 'import socket; s=socket.socket(); s.bind(("",0)); print(s.getsockname()[1]); s.close()')
|
|
echo "Using port $PORT"
|
|
|
|
# --- Temp data dir for FS backend ---------------------------------------------
|
|
|
|
DATA_DIR=$(mktemp -d)
|
|
echo "Data dir: $DATA_DIR"
|
|
|
|
# --- Build post3-server -------------------------------------------------------
|
|
|
|
echo "Building post3-server (release)..."
|
|
cargo build -p post3-server --release --quiet
|
|
|
|
BINARY="$REPO_ROOT/target/release/post3-server"
|
|
if [ ! -x "$BINARY" ]; then
|
|
echo "ERROR: binary not found at $BINARY"
|
|
exit 1
|
|
fi
|
|
|
|
# --- Generate s3tests.conf ----------------------------------------------------
|
|
|
|
CONF="$DATA_DIR/s3tests.conf"
|
|
sed "s/__PORT__/$PORT/g" "$SCRIPT_DIR/s3tests.conf.template" > "$CONF"
|
|
echo "Config: $CONF"
|
|
|
|
# --- Start the server ---------------------------------------------------------
|
|
|
|
export POST3_HOST="127.0.0.1:$PORT"
|
|
"$BINARY" serve --backend fs --data-dir "$DATA_DIR/store" &
|
|
SERVER_PID=$!
|
|
|
|
cleanup() {
|
|
echo ""
|
|
echo "Stopping server (PID $SERVER_PID)..."
|
|
kill "$SERVER_PID" 2>/dev/null || true
|
|
wait "$SERVER_PID" 2>/dev/null || true
|
|
echo "Cleaning up $DATA_DIR..."
|
|
rm -rf "$DATA_DIR"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# --- Wait for the server to become ready --------------------------------------
|
|
|
|
echo "Waiting for server on port $PORT..."
|
|
TRIES=0
|
|
MAX_TRIES=60
|
|
while ! curl -sf "http://127.0.0.1:$PORT/" >/dev/null 2>&1; do
|
|
TRIES=$((TRIES + 1))
|
|
if [ "$TRIES" -ge "$MAX_TRIES" ]; then
|
|
echo "ERROR: server did not start within ${MAX_TRIES}s"
|
|
exit 1
|
|
fi
|
|
sleep 0.5
|
|
done
|
|
echo "Server is ready."
|
|
|
|
# --- Set up virtualenv for s3-tests -------------------------------------------
|
|
|
|
VENV_DIR="$S3TESTS_DIR/.venv"
|
|
if [ ! -d "$VENV_DIR" ]; then
|
|
echo "Creating virtualenv..."
|
|
python3 -m venv "$VENV_DIR"
|
|
fi
|
|
source "$VENV_DIR/bin/activate"
|
|
|
|
# Install dependencies if needed
|
|
if ! python3 -c "import boto3" 2>/dev/null; then
|
|
echo "Installing s3-tests dependencies..."
|
|
pip install --quiet -r "$S3TESTS_DIR/requirements.txt"
|
|
fi
|
|
|
|
# --- Build the test filter expression -----------------------------------------
|
|
|
|
# Marker-based exclusions (features post3 doesn't implement)
|
|
MARKER_EXCLUDE="not appendobject"
|
|
MARKER_EXCLUDE+=" and not bucket_policy and not bucket_encryption"
|
|
MARKER_EXCLUDE+=" and not bucket_logging and not checksum"
|
|
MARKER_EXCLUDE+=" and not cloud_transition and not conditional_write"
|
|
MARKER_EXCLUDE+=" and not cors and not encryption"
|
|
MARKER_EXCLUDE+=" and not fails_strict_rfc2616"
|
|
MARKER_EXCLUDE+=" and not iam_account and not iam_cross_account"
|
|
MARKER_EXCLUDE+=" and not iam_role and not iam_tenant and not iam_user"
|
|
MARKER_EXCLUDE+=" and not lifecycle and not lifecycle_expiration"
|
|
MARKER_EXCLUDE+=" and not lifecycle_transition"
|
|
MARKER_EXCLUDE+=" and not object_lock and not object_ownership"
|
|
MARKER_EXCLUDE+=" and not role_policy and not session_policy"
|
|
MARKER_EXCLUDE+=" and not user_policy and not group_policy"
|
|
MARKER_EXCLUDE+=" and not s3select and not s3website"
|
|
MARKER_EXCLUDE+=" and not s3website_routing_rules"
|
|
MARKER_EXCLUDE+=" and not s3website_redirect_location"
|
|
MARKER_EXCLUDE+=" and not sns and not sse_s3 and not storage_class"
|
|
MARKER_EXCLUDE+=" and not tagging"
|
|
MARKER_EXCLUDE+=" and not test_of_sts and not versioning and not delete_marker"
|
|
MARKER_EXCLUDE+=" and not webidentity_test"
|
|
MARKER_EXCLUDE+=" and not auth_aws2 and not auth_aws4 and not auth_common"
|
|
|
|
# Keyword-based exclusions (individual tests requiring unimplemented ops)
|
|
KEYWORD_EXCLUDE="not anonymous and not presigned and not copy_object"
|
|
KEYWORD_EXCLUDE+=" and not test_account_usage and not test_head_bucket_usage"
|
|
KEYWORD_EXCLUDE+=" and not acl and not ACL and not grant"
|
|
KEYWORD_EXCLUDE+=" and not logging and not notification"
|
|
# Exclude features not yet implemented:
|
|
# - access_bucket / bucket access control tests (require ACL/policy)
|
|
KEYWORD_EXCLUDE+=" and not test_access_bucket"
|
|
# - POST object (HTML form-based upload)
|
|
KEYWORD_EXCLUDE+=" and not test_post_object"
|
|
# - Ranged requests (Range header)
|
|
KEYWORD_EXCLUDE+=" and not ranged_request"
|
|
# - Conditional requests (If-Match, If-None-Match, If-Modified-Since)
|
|
KEYWORD_EXCLUDE+=" and not ifmatch and not ifnonematch and not ifmodified and not ifunmodified"
|
|
KEYWORD_EXCLUDE+=" and not ifnonmatch"
|
|
# - Object copy tests not caught by copy_object keyword
|
|
KEYWORD_EXCLUDE+=" and not object_copy"
|
|
# - Multipart copy (UploadPartCopy)
|
|
KEYWORD_EXCLUDE+=" and not multipart_copy"
|
|
# - Public access block
|
|
KEYWORD_EXCLUDE+=" and not public_block"
|
|
# - Object attributes API
|
|
KEYWORD_EXCLUDE+=" and not object_attributes"
|
|
# - Auth-related tests
|
|
KEYWORD_EXCLUDE+=" and not invalid_auth and not bad_auth and not authenticated_expired"
|
|
# - Torrent
|
|
KEYWORD_EXCLUDE+=" and not torrent"
|
|
# - content_encoding aws_chunked
|
|
KEYWORD_EXCLUDE+=" and not aws_chunked"
|
|
# - GetBucketLocation (needs location constraint storage)
|
|
KEYWORD_EXCLUDE+=" and not bucket_get_location"
|
|
# - expected_bucket_owner (needs owner tracking)
|
|
KEYWORD_EXCLUDE+=" and not expected_bucket_owner"
|
|
# - bucket_recreate_not_overriding (needs data preservation on re-create)
|
|
KEYWORD_EXCLUDE+=" and not bucket_recreate_not_overriding"
|
|
# - object_read_unreadable (needs permission model)
|
|
KEYWORD_EXCLUDE+=" and not object_read_unreadable"
|
|
# - Versioned concurrent tests
|
|
KEYWORD_EXCLUDE+=" and not versioned_concurrent"
|
|
# - 100-continue
|
|
KEYWORD_EXCLUDE+=" and not 100_continue"
|
|
# - multipart_get_part (GetObjectPartNumber)
|
|
KEYWORD_EXCLUDE+=" and not multipart_get_part and not multipart_single_get_part and not non_multipart_get_part"
|
|
# - object_anon_put
|
|
KEYWORD_EXCLUDE+=" and not object_anon_put"
|
|
# - raw response headers / raw get/put tests (presigned-like)
|
|
KEYWORD_EXCLUDE+=" and not object_raw"
|
|
# - Object write headers (cache-control, expires)
|
|
KEYWORD_EXCLUDE+=" and not object_write_cache_control and not object_write_expires"
|
|
# - bucket_head_extended
|
|
KEYWORD_EXCLUDE+=" and not bucket_head_extended"
|
|
# - Restore/read-through
|
|
KEYWORD_EXCLUDE+=" and not restore_object and not read_through and not restore_noncur"
|
|
# - list_multipart_upload_owner (needs owner tracking)
|
|
KEYWORD_EXCLUDE+=" and not list_multipart_upload_owner"
|
|
# - bucket_create_exists (needs owner tracking)
|
|
KEYWORD_EXCLUDE+=" and not bucket_create_exists"
|
|
# - bucket_create_naming_dns (dots + hyphens adjacent)
|
|
KEYWORD_EXCLUDE+=" and not bucket_create_naming_dns"
|
|
# - object_requestid_matches_header_on_error
|
|
KEYWORD_EXCLUDE+=" and not requestid_matches_header"
|
|
# - unicode metadata
|
|
KEYWORD_EXCLUDE+=" and not unicode_metadata"
|
|
# - multipart_upload_on_a_bucket_with_policy
|
|
KEYWORD_EXCLUDE+=" and not upload_on_a_bucket_with_policy"
|
|
# - upload_part_copy_percent_encoded_key
|
|
KEYWORD_EXCLUDE+=" and not part_copy"
|
|
# - list_buckets_paginated (needs pagination support in list_buckets)
|
|
KEYWORD_EXCLUDE+=" and not list_buckets_paginated and not list_buckets_invalid and not list_buckets_bad"
|
|
# - multipart_resend_first_finishes_last
|
|
KEYWORD_EXCLUDE+=" and not resend_first_finishes_last"
|
|
# - ranged_big_request (Range header support)
|
|
KEYWORD_EXCLUDE+=" and not ranged_big"
|
|
# - encoding_basic (URL encoding in listing)
|
|
KEYWORD_EXCLUDE+=" and not encoding_basic"
|
|
# - maxkeys_invalid (needs proper 400 error for non-numeric maxkeys)
|
|
KEYWORD_EXCLUDE+=" and not maxkeys_invalid"
|
|
# - fetchowner (needs FetchOwner=true support in v2)
|
|
KEYWORD_EXCLUDE+=" and not fetchowner"
|
|
# - list_return_data (needs Owner data in old SDK format)
|
|
KEYWORD_EXCLUDE+=" and not list_return_data"
|
|
# - unordered listing tests (parallel create, needs strict ordering)
|
|
KEYWORD_EXCLUDE+=" and not bucket_list_unordered and not bucket_listv2_unordered"
|
|
# - block_public_policy/restrict tests (PutPublicAccessBlock not implemented)
|
|
KEYWORD_EXCLUDE+=" and not block_public"
|
|
# - multipart_upload_resend_part (uses Range header in _check_content_using_range)
|
|
KEYWORD_EXCLUDE+=" and not upload_resend_part"
|
|
|
|
FILTER="$MARKER_EXCLUDE and $KEYWORD_EXCLUDE"
|
|
|
|
# --- Run the tests ------------------------------------------------------------
|
|
|
|
export S3TEST_CONF="$CONF"
|
|
|
|
EXTRA_ARGS=("${@}")
|
|
|
|
echo ""
|
|
echo "Running s3-tests..."
|
|
echo "Filter: $FILTER"
|
|
echo ""
|
|
|
|
# --- Individual test deselections (can't use -k without affecting similarly-named tests)
|
|
|
|
DESELECT_ARGS=()
|
|
# test_multipart_upload: uses Range requests + idempotent double-complete (Ceph-specific)
|
|
DESELECT_ARGS+=(--deselect "s3tests/functional/test_s3.py::test_multipart_upload")
|
|
# test_multipart_upload_small: idempotent double-complete (Ceph-specific behavior)
|
|
DESELECT_ARGS+=(--deselect "s3tests/functional/test_s3.py::test_multipart_upload_small")
|
|
|
|
cd "$S3TESTS_DIR"
|
|
python3 -m pytest s3tests/functional/test_s3.py \
|
|
-k "$FILTER" \
|
|
"${DESELECT_ARGS[@]}" \
|
|
-v \
|
|
--tb=short \
|
|
"${EXTRA_ARGS[@]}" \
|
|
|| true # don't fail the script on test failures — we want to see results
|