refactor(replication): route app storage through ecstore (#4251)

This commit is contained in:
Zhengchao An
2026-07-04 02:00:28 +08:00
committed by GitHub
parent 2214f67fba
commit 8a0617865b
13 changed files with 151 additions and 48 deletions
Generated
-2
View File
@@ -8836,7 +8836,6 @@ dependencies = [
"rustfs-policy", "rustfs-policy",
"rustfs-protocols", "rustfs-protocols",
"rustfs-protos", "rustfs-protos",
"rustfs-replication",
"rustfs-rio", "rustfs-rio",
"rustfs-s3-ops", "rustfs-s3-ops",
"rustfs-s3-types", "rustfs-s3-types",
@@ -9764,7 +9763,6 @@ dependencies = [
"rustfs-data-usage", "rustfs-data-usage",
"rustfs-ecstore", "rustfs-ecstore",
"rustfs-filemeta", "rustfs-filemeta",
"rustfs-replication",
"rustfs-storage-api", "rustfs-storage-api",
"rustfs-utils", "rustfs-utils",
"s3s", "s3s",
+13 -5
View File
@@ -43,18 +43,26 @@ mod replication_versioning_boundary;
mod runtime_boundary; mod runtime_boundary;
pub use datatypes::ResyncStatusType; pub use datatypes::ResyncStatusType;
pub use replication_config_boundary::{ObjectOpts, ReplicationConfigurationExt}; pub use replication_config_boundary::{
ObjectOpts, ReplicationConfigurationExt, ReplicationTargetValidationError, replication_target_arns,
should_remove_replication_target, validate_replication_config_target_arns,
};
#[cfg(test)] #[cfg(test)]
pub(crate) use replication_filemeta_boundary::ReplicateTargetDecision; pub(crate) use replication_filemeta_boundary::ReplicateTargetDecision;
pub(crate) use replication_filemeta_boundary::{ pub(crate) use replication_filemeta_boundary::version_purge_statuses_map;
ReplicateDecision, ReplicationState, replication_statuses_map, version_purge_statuses_map, pub use replication_filemeta_boundary::{
REPLICATE_INCOMING_DELETE, ReplicateDecision, ReplicateObjectInfo, ReplicationState, ReplicationStatusType, ReplicationType,
VersionPurgeStatusType, replication_statuses_map,
}; };
pub use replication_filemeta_boundary::{ReplicateObjectInfo, ReplicationStatusType, ReplicationType, VersionPurgeStatusType};
pub(crate) use replication_lifecycle_bridge::{ReplicationLifecycleBridge, ReplicationLifecycleConfig}; pub(crate) use replication_lifecycle_bridge::{ReplicationLifecycleBridge, ReplicationLifecycleConfig};
pub(crate) use replication_migration_bridge::ReplicationMigrationBridge; pub(crate) use replication_migration_bridge::ReplicationMigrationBridge;
pub use replication_object_bridge::ReplicationObjectBridge; pub use replication_object_bridge::ReplicationObjectBridge;
pub use replication_object_config::ReplicationConfig; pub use replication_object_config::ReplicationConfig;
pub use replication_object_decision_boundary::MustReplicateOptions; pub use replication_object_decision_boundary::{
MustReplicateOptions, ReplicationDeleteScheduleInput, ReplicationDeleteStateSource, delete_replication_state_from_config,
delete_replication_version_id, should_schedule_delete_replication, should_use_existing_delete_replication_info,
should_use_existing_delete_replication_source,
};
pub use replication_pool::{ pub use replication_pool::{
DynReplicationPool, ReplicationPoolTrait, get_global_replication_pool, get_global_replication_stats, DynReplicationPool, ReplicationPoolTrait, get_global_replication_pool, get_global_replication_stats,
init_background_replication, init_background_replication,
@@ -12,4 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
pub use rustfs_replication::{ObjectOpts, ReplicationConfigurationExt}; pub use rustfs_replication::{
ObjectOpts, ReplicationConfigurationExt, ReplicationTargetValidationError, replication_target_arns,
should_remove_replication_target, validate_replication_config_target_arns,
};
@@ -14,9 +14,11 @@
pub(crate) use rustfs_replication::{MrfOpKind, MrfReplicateEntry}; pub(crate) use rustfs_replication::{MrfOpKind, MrfReplicateEntry};
pub(crate) use rustfs_replication::{ pub(crate) use rustfs_replication::{
REPLICATE_EXISTING, REPLICATE_EXISTING_DELETE, REPLICATE_HEAL_DELETE, REPLICATE_INCOMING_DELETE, ReplicateDecision, REPLICATE_EXISTING, REPLICATE_EXISTING_DELETE, REPLICATE_HEAL_DELETE, ReplicateTargetDecision, ReplicatedInfos,
ReplicateTargetDecision, ReplicatedInfos, ReplicatedTargetInfo, ReplicationAction, ReplicationState, ReplicatedTargetInfo, ReplicationAction, ReplicationWorkerOperation, ResyncDecision, get_replication_state,
ReplicationWorkerOperation, ResyncDecision, get_replication_state, parse_replicate_decision, replication_statuses_map, parse_replicate_decision, target_reset_header, version_purge_statuses_map,
target_reset_header, version_purge_statuses_map, };
pub use rustfs_replication::{
REPLICATE_INCOMING_DELETE, ReplicateDecision, ReplicateObjectInfo, ReplicationState, ReplicationStatusType, ReplicationType,
VersionPurgeStatusType, replication_statuses_map,
}; };
pub use rustfs_replication::{ReplicateObjectInfo, ReplicationStatusType, ReplicationType, VersionPurgeStatusType};
@@ -12,7 +12,11 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
pub use rustfs_replication::MustReplicateOptions; pub use rustfs_replication::{
MustReplicateOptions, ReplicationDeleteScheduleInput, ReplicationDeleteStateSource, delete_replication_state_from_config,
delete_replication_version_id, should_schedule_delete_replication, should_use_existing_delete_replication_info,
should_use_existing_delete_replication_source,
};
pub(crate) use rustfs_replication::{ pub(crate) use rustfs_replication::{
ReplicationDeleteSource, ReplicationMultipartPartInput, ReplicationResyncTargetObject, ReplicationDeleteSource, ReplicationMultipartPartInput, ReplicationResyncTargetObject,
delete_replication_missing_source_decision, delete_replication_object_opts, heal_uses_delete_replication_path, delete_replication_missing_source_decision, delete_replication_object_opts, heal_uses_delete_replication_path,
@@ -16,8 +16,12 @@ mod storage_api;
use s3s::dto::ReplicationConfiguration; use s3s::dto::ReplicationConfiguration;
use storage_api::replication_compat::{ use storage_api::replication_compat::{
BucketStats, DeletedObjectReplicationInfo, DynReplicationPool, ObjectOpts, ReplicationConfigurationExt, BucketStats, DeletedObjectReplicationInfo, DynReplicationPool, ObjectOpts, REPLICATE_INCOMING_DELETE, ReplicateDecision,
ReplicationObjectBridge, ReplicationStats, ResyncStatusType, ReplicationConfigurationExt, ReplicationDeleteScheduleInput, ReplicationDeleteStateSource, ReplicationObjectBridge,
ReplicationState, ReplicationStats, ReplicationTargetValidationError, ResyncStatusType, delete_replication_state_from_config,
delete_replication_version_id, replication_statuses_map, replication_target_arns, should_remove_replication_target,
should_schedule_delete_replication, should_use_existing_delete_replication_info,
should_use_existing_delete_replication_source, validate_replication_config_target_arns,
}; };
fn type_name<T>() -> &'static str { fn type_name<T>() -> &'static str {
@@ -55,3 +59,43 @@ fn replication_facade_exports_runtime_and_dto_types() {
assert!(type_name::<ReplicationObjectBridge>().contains("ReplicationObjectBridge")); assert!(type_name::<ReplicationObjectBridge>().contains("ReplicationObjectBridge"));
assert!(type_name_unsized::<DynReplicationPool>().contains("ReplicationPoolTrait")); assert!(type_name_unsized::<DynReplicationPool>().contains("ReplicationPoolTrait"));
} }
#[test]
fn replication_facade_exports_app_storage_helper_contracts() {
let config = ReplicationConfiguration::default();
let target_arns = replication_target_arns(&config);
assert!(target_arns.is_empty());
let source = ReplicationDeleteStateSource {
name: String::new(),
user_tags: String::new(),
version_id: None,
delete_marker: false,
replica: false,
};
assert!(delete_replication_state_from_config(&config, &source).is_none());
assert!(replication_statuses_map("").is_empty());
let source_replication_status = Default::default();
let source_version_purge_status = Default::default();
let input = ReplicationDeleteScheduleInput {
replication_request: false,
version_id_requested: false,
source_delete_marker: false,
source_replication_status: &source_replication_status,
source_version_purge_status: &source_version_purge_status,
deleted_delete_marker_version: false,
};
assert!(!should_schedule_delete_replication(input));
assert!(delete_replication_version_id(false, None, false).is_none());
assert!(!should_remove_replication_target("arn", false, &target_arns));
assert!(!should_use_existing_delete_replication_info(false, false));
assert!(!should_use_existing_delete_replication_source(false, false, false));
assert!(validate_replication_config_target_arns(std::iter::empty(), &config).is_ok());
assert_eq!(REPLICATE_INCOMING_DELETE, "replicate:incoming:delete");
assert!(type_name::<ReplicateDecision>().contains("ReplicateDecision"));
assert!(type_name::<ReplicationState>().contains("ReplicationState"));
assert!(type_name::<ReplicationTargetValidationError>().contains("ReplicationTargetValidationError"));
}
+6 -2
View File
@@ -22,8 +22,12 @@ pub(crate) mod contract_compat {
pub(crate) mod replication_compat { pub(crate) mod replication_compat {
pub(crate) use rustfs_ecstore::api::bucket::replication::{ pub(crate) use rustfs_ecstore::api::bucket::replication::{
BucketStats, DeletedObjectReplicationInfo, DynReplicationPool, ObjectOpts, ReplicationConfigurationExt, BucketStats, DeletedObjectReplicationInfo, DynReplicationPool, ObjectOpts, REPLICATE_INCOMING_DELETE, ReplicateDecision,
ReplicationObjectBridge, ReplicationStats, ResyncStatusType, ReplicationConfigurationExt, ReplicationDeleteScheduleInput, ReplicationDeleteStateSource, ReplicationObjectBridge,
ReplicationState, ReplicationStats, ReplicationTargetValidationError, ResyncStatusType,
delete_replication_state_from_config, delete_replication_version_id, replication_statuses_map, replication_target_arns,
should_remove_replication_target, should_schedule_delete_replication, should_use_existing_delete_replication_info,
should_use_existing_delete_replication_source, validate_replication_config_target_arns,
}; };
} }
-1
View File
@@ -44,7 +44,6 @@ time = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }
rmp-serde = { workspace = true } rmp-serde = { workspace = true }
rustfs-filemeta = { workspace = true } rustfs-filemeta = { workspace = true }
rustfs-replication = { workspace = true }
tokio-util = { workspace = true } tokio-util = { workspace = true }
rustfs-ecstore = { workspace = true } rustfs-ecstore = { workspace = true }
rustfs-storage-api = { workspace = true } rustfs-storage-api = { workspace = true }
@@ -137,8 +137,8 @@ Current coupling:
- admin replication extension target filtering and resync request construction - admin replication extension target filtering and resync request construction
stay behind the admin storage boundary instead of exposing replication work stay behind the admin storage boundary instead of exposing replication work
DTO construction to handlers; DTO construction to handlers;
- scanner, admin, and storage-owner replication status/DTO consumers import - scanner, admin, storage-owner, and app storage replication status/DTO/helper
those contracts through the ECStore replication facade; consumers import those contracts through the ECStore replication facade;
- app object and multipart writes call object-replication boundary helpers - app object and multipart writes call object-replication boundary helpers
instead of constructing replication work DTOs or choosing object replication instead of constructing replication work DTOs or choosing object replication
operation types at the use-case layer; operation types at the use-case layer;
@@ -187,10 +187,11 @@ Required contracts before crate movement:
`crates/replication/src/storage_api.rs` inside `rustfs-replication`. `crates/replication/src/storage_api.rs` inside `rustfs-replication`.
- `EcstoreReplicationBoundaryImports`: ECStore-side imports from - `EcstoreReplicationBoundaryImports`: ECStore-side imports from
`rustfs-replication` are concentrated in replication `*_boundary.rs` modules. `rustfs-replication` are concentrated in replication `*_boundary.rs` modules.
- `RuntimeReplicationFacadeConsumers`: scanner, admin, and storage-owner - `RuntimeReplicationFacadeConsumers`: scanner, admin, storage-owner, and app
replication status/DTO consumers import through `rustfs-ecstore`, while app storage replication status/DTO/helper consumers import through
storage keeps direct object/delete helper calls inside its storage API `rustfs-ecstore`; runtime code under `rustfs/src` does not import
boundary. `rustfs-replication` directly, and the RustFS runtime/scanner crates do not
depend on it.
- `StorageApiReplicationContracts`: storage-api delete DTO replication - `StorageApiReplicationContracts`: storage-api delete DTO replication
state/status helpers are concentrated in `crates/storage-api/src/replication.rs` state/status helpers are concentrated in `crates/storage-api/src/replication.rs`
until the underlying wire contracts can move without a until the underlying wire contracts can move without a
-1
View File
@@ -85,7 +85,6 @@ rustfs-policy = { workspace = true }
rustfs-protocols = { workspace = true } rustfs-protocols = { workspace = true }
rustfs-protos = { workspace = true } rustfs-protos = { workspace = true }
rustfs-rio = { workspace = true } rustfs-rio = { workspace = true }
rustfs-replication = { workspace = true }
rustfs-s3-types = { workspace = true } rustfs-s3-types = { workspace = true }
rustfs-s3-ops = { workspace = true } rustfs-s3-ops = { workspace = true }
rustfs-security-governance = { workspace = true } rustfs-security-governance = { workspace = true }
+21 -19
View File
@@ -609,18 +609,20 @@ pub(crate) mod bucket {
use std::sync::Arc; use std::sync::Arc;
use uuid::Uuid; use uuid::Uuid;
use crate::storage::storage_api::ecstore_bucket::replication as replication_contracts;
pub(crate) type DeletedObjectReplicationInfo = pub(crate) type DeletedObjectReplicationInfo =
crate::storage::storage_api::ecstore_bucket::replication::DeletedObjectReplicationInfo; crate::storage::storage_api::ecstore_bucket::replication::DeletedObjectReplicationInfo;
type ReplicationObjectBridge = crate::storage::storage_api::ecstore_bucket::replication::ReplicationObjectBridge; type ReplicationObjectBridge = crate::storage::storage_api::ecstore_bucket::replication::ReplicationObjectBridge;
pub(crate) type ReplicateDecision = rustfs_replication::ReplicateDecision; pub(crate) type ReplicateDecision = replication_contracts::ReplicateDecision;
#[cfg(test)] #[cfg(test)]
pub(crate) type ReplicationState = rustfs_replication::ReplicationState; pub(crate) type ReplicationState = replication_contracts::ReplicationState;
pub(crate) type ReplicationStatusType = rustfs_replication::ReplicationStatusType; pub(crate) type ReplicationStatusType = replication_contracts::ReplicationStatusType;
pub(crate) type ReplicationTargetValidationError = rustfs_replication::ReplicationTargetValidationError; pub(crate) type ReplicationTargetValidationError = replication_contracts::ReplicationTargetValidationError;
pub(crate) type VersionPurgeStatusType = rustfs_replication::VersionPurgeStatusType; pub(crate) type VersionPurgeStatusType = replication_contracts::VersionPurgeStatusType;
pub(crate) const REPLICATE_INCOMING_DELETE: &str = rustfs_replication::REPLICATE_INCOMING_DELETE; pub(crate) const REPLICATE_INCOMING_DELETE: &str = replication_contracts::REPLICATE_INCOMING_DELETE;
#[cfg(test)] #[cfg(test)]
pub(crate) use rustfs_replication::replication_statuses_map; pub(crate) use replication_contracts::replication_statuses_map;
pub(crate) async fn check_replicate_delete( pub(crate) async fn check_replicate_delete(
bucket: &str, bucket: &str,
@@ -636,7 +638,7 @@ pub(crate) mod bucket {
replication_source: &crate::storage::storage_api::StorageObjectInfo, replication_source: &crate::storage::storage_api::StorageObjectInfo,
deleted_delete_marker_version: bool, deleted_delete_marker_version: bool,
) -> Option<Uuid> { ) -> Option<Uuid> {
rustfs_replication::delete_replication_version_id( replication_contracts::delete_replication_version_id(
replication_source.delete_marker, replication_source.delete_marker,
replication_source.version_id, replication_source.version_id,
deleted_delete_marker_version, deleted_delete_marker_version,
@@ -655,7 +657,7 @@ pub(crate) mod bucket {
user_defined, user_defined,
user_tags, user_tags,
status, status,
rustfs_replication::ReplicationType::Object, replication_contracts::ReplicationType::Object,
opts, opts,
); );
ReplicationObjectBridge::must_replicate(bucket, object, mopts).await ReplicationObjectBridge::must_replicate(bucket, object, mopts).await
@@ -666,7 +668,7 @@ pub(crate) mod bucket {
store: Arc<crate::storage::storage_api::ECStore>, store: Arc<crate::storage::storage_api::ECStore>,
dsc: ReplicateDecision, dsc: ReplicateDecision,
) { ) {
ReplicationObjectBridge::schedule_object(oi, store, dsc, rustfs_replication::ReplicationType::Object).await; ReplicationObjectBridge::schedule_object(oi, store, dsc, replication_contracts::ReplicationType::Object).await;
} }
pub(crate) async fn schedule_replication_delete(dv: DeletedObjectReplicationInfo) { pub(crate) async fn schedule_replication_delete(dv: DeletedObjectReplicationInfo) {
@@ -678,19 +680,19 @@ pub(crate) mod bucket {
obj_info: &crate::storage::storage_api::StorageObjectInfo, obj_info: &crate::storage::storage_api::StorageObjectInfo,
version_id: Option<Uuid>, version_id: Option<Uuid>,
replica: bool, replica: bool,
) -> Option<rustfs_replication::ReplicationState> { ) -> Option<replication_contracts::ReplicationState> {
let source = rustfs_replication::ReplicationDeleteStateSource { let source = replication_contracts::ReplicationDeleteStateSource {
name: obj_info.name.clone(), name: obj_info.name.clone(),
user_tags: (*obj_info.user_tags).clone(), user_tags: (*obj_info.user_tags).clone(),
version_id, version_id,
delete_marker: obj_info.delete_marker, delete_marker: obj_info.delete_marker,
replica, replica,
}; };
rustfs_replication::delete_replication_state_from_config(config, &source) replication_contracts::delete_replication_state_from_config(config, &source)
} }
pub(crate) fn replication_target_arns(config: &s3s::dto::ReplicationConfiguration) -> HashSet<String> { pub(crate) fn replication_target_arns(config: &s3s::dto::ReplicationConfiguration) -> HashSet<String> {
rustfs_replication::replication_target_arns(config) replication_contracts::replication_target_arns(config)
} }
pub(crate) fn should_remove_replication_target( pub(crate) fn should_remove_replication_target(
@@ -698,7 +700,7 @@ pub(crate) mod bucket {
is_replication_service: bool, is_replication_service: bool,
target_arns: &HashSet<String>, target_arns: &HashSet<String>,
) -> bool { ) -> bool {
rustfs_replication::should_remove_replication_target(target_arn, is_replication_service, target_arns) replication_contracts::should_remove_replication_target(target_arn, is_replication_service, target_arns)
} }
pub(crate) fn should_schedule_delete_replication( pub(crate) fn should_schedule_delete_replication(
@@ -706,7 +708,7 @@ pub(crate) mod bucket {
replication_source: &crate::storage::storage_api::StorageObjectInfo, replication_source: &crate::storage::storage_api::StorageObjectInfo,
deleted_delete_marker_version: bool, deleted_delete_marker_version: bool,
) -> bool { ) -> bool {
rustfs_replication::should_schedule_delete_replication(rustfs_replication::ReplicationDeleteScheduleInput { replication_contracts::should_schedule_delete_replication(replication_contracts::ReplicationDeleteScheduleInput {
replication_request: opts.replication_request, replication_request: opts.replication_request,
version_id_requested: opts.version_id.is_some(), version_id_requested: opts.version_id.is_some(),
source_delete_marker: replication_source.delete_marker, source_delete_marker: replication_source.delete_marker,
@@ -719,7 +721,7 @@ pub(crate) mod bucket {
pub(crate) fn should_use_existing_delete_replication_info( pub(crate) fn should_use_existing_delete_replication_info(
opts: &crate::storage::storage_api::StorageObjectOptions, opts: &crate::storage::storage_api::StorageObjectOptions,
) -> bool { ) -> bool {
rustfs_replication::should_use_existing_delete_replication_info(opts.version_id.is_some(), opts.delete_marker) replication_contracts::should_use_existing_delete_replication_info(opts.version_id.is_some(), opts.delete_marker)
} }
pub(crate) fn should_use_existing_delete_replication_source( pub(crate) fn should_use_existing_delete_replication_source(
@@ -727,7 +729,7 @@ pub(crate) mod bucket {
deleted_delete_marker: bool, deleted_delete_marker: bool,
has_existing_info: bool, has_existing_info: bool,
) -> bool { ) -> bool {
rustfs_replication::should_use_existing_delete_replication_source( replication_contracts::should_use_existing_delete_replication_source(
replication_request, replication_request,
deleted_delete_marker, deleted_delete_marker,
has_existing_info, has_existing_info,
@@ -738,7 +740,7 @@ pub(crate) mod bucket {
configured_arns: impl Iterator<Item = &'a str>, configured_arns: impl Iterator<Item = &'a str>,
config: &s3s::dto::ReplicationConfiguration, config: &s3s::dto::ReplicationConfiguration,
) -> Result<(), ReplicationTargetValidationError> { ) -> Result<(), ReplicationTargetValidationError> {
rustfs_replication::validate_replication_config_target_arns(configured_arns, config) replication_contracts::validate_replication_config_target_arns(configured_arns, config)
} }
} }
+14 -3
View File
@@ -210,6 +210,7 @@ REPLICATION_FACADE_WILDCARD_EXPORT_HITS_FILE="${TMP_DIR}/replication_facade_wild
ECSTORE_REPLICATION_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/ecstore_replication_boundary_bypass_hits.txt" ECSTORE_REPLICATION_BOUNDARY_BYPASS_HITS_FILE="${TMP_DIR}/ecstore_replication_boundary_bypass_hits.txt"
SCANNER_REPLICATION_FACADE_BYPASS_HITS_FILE="${TMP_DIR}/scanner_replication_facade_bypass_hits.txt" SCANNER_REPLICATION_FACADE_BYPASS_HITS_FILE="${TMP_DIR}/scanner_replication_facade_bypass_hits.txt"
RUSTFS_REPLICATION_FACADE_BYPASS_HITS_FILE="${TMP_DIR}/rustfs_replication_facade_bypass_hits.txt" RUSTFS_REPLICATION_FACADE_BYPASS_HITS_FILE="${TMP_DIR}/rustfs_replication_facade_bypass_hits.txt"
RUNTIME_REPLICATION_DEPENDENCY_BYPASS_HITS_FILE="${TMP_DIR}/runtime_replication_dependency_bypass_hits.txt"
REPLICATION_CRATE_FILEMETA_BYPASS_HITS_FILE="${TMP_DIR}/replication_crate_filemeta_bypass_hits.txt" REPLICATION_CRATE_FILEMETA_BYPASS_HITS_FILE="${TMP_DIR}/replication_crate_filemeta_bypass_hits.txt"
REPLICATION_CRATE_STORAGE_API_BYPASS_HITS_FILE="${TMP_DIR}/replication_crate_storage_api_bypass_hits.txt" REPLICATION_CRATE_STORAGE_API_BYPASS_HITS_FILE="${TMP_DIR}/replication_crate_storage_api_bypass_hits.txt"
REPLICATION_CONFIG_RULE_CONTRACT_BACKSLIDE_HITS_FILE="${TMP_DIR}/replication_config_rule_contract_backslide_hits.txt" REPLICATION_CONFIG_RULE_CONTRACT_BACKSLIDE_HITS_FILE="${TMP_DIR}/replication_config_rule_contract_backslide_hits.txt"
@@ -2643,12 +2644,22 @@ fi
cd "$ROOT_DIR" cd "$ROOT_DIR"
rg -n --with-filename 'rustfs_replication::|use\s+rustfs_replication\b' \ rg -n --with-filename 'rustfs_replication::|use\s+rustfs_replication\b' \
rustfs/src \ rustfs/src \
--glob '*.rs' | --glob '*.rs' || true
rg -v '^rustfs/src/app/storage_api\.rs:' || true
) >"$RUSTFS_REPLICATION_FACADE_BYPASS_HITS_FILE" ) >"$RUSTFS_REPLICATION_FACADE_BYPASS_HITS_FILE"
if [[ -s "$RUSTFS_REPLICATION_FACADE_BYPASS_HITS_FILE" ]]; then if [[ -s "$RUSTFS_REPLICATION_FACADE_BYPASS_HITS_FILE" ]]; then
report_failure "RustFS runtime replication contracts must stay behind storage API facades: $(paste -sd '; ' "$RUSTFS_REPLICATION_FACADE_BYPASS_HITS_FILE")" report_failure "RustFS runtime replication contracts must come through storage API / ECStore replication facades: $(paste -sd '; ' "$RUSTFS_REPLICATION_FACADE_BYPASS_HITS_FILE")"
fi
(
cd "$ROOT_DIR"
rg -n --with-filename '^rustfs-replication\b' \
rustfs/Cargo.toml \
crates/scanner/Cargo.toml || true
) >"$RUNTIME_REPLICATION_DEPENDENCY_BYPASS_HITS_FILE"
if [[ -s "$RUNTIME_REPLICATION_DEPENDENCY_BYPASS_HITS_FILE" ]]; then
report_failure "RustFS runtime and scanner crates must not depend on rustfs-replication directly: $(paste -sd '; ' "$RUNTIME_REPLICATION_DEPENDENCY_BYPASS_HITS_FILE")"
fi fi
( (
+28
View File
@@ -744,6 +744,29 @@ envsubst < "${TEMPLATE_PATH}" > "${CONF_OUTPUT_PATH}" || {
log_info "Provisioning s3-tests alt user..." log_info "Provisioning s3-tests alt user..."
# Helper function to install Python packages with fallback for externally-managed environments # Helper function to install Python packages with fallback for externally-managed environments
ensure_python_pip() {
if python3 -m pip --version >/dev/null 2>&1; then
return 0
fi
log_info "Installing pip for Python package setup..."
if python3 -m ensurepip --upgrade >/dev/null 2>&1; then
return 0
fi
if command -v apt-get >/dev/null 2>&1; then
sudo apt-get update && sudo apt-get install -y python3-pip || {
log_warn "Failed to install python3-pip via apt-get"
}
elif command -v brew >/dev/null 2>&1; then
brew install python || {
log_warn "Failed to install Python via brew"
}
fi
python3 -m pip --version >/dev/null 2>&1
}
install_python_package() { install_python_package() {
local package=$1 local package=$1
local error_output local error_output
@@ -759,6 +782,11 @@ install_python_package() {
log_warn "Failed to install ${package} with uv, falling back to python3 -m pip" log_warn "Failed to install ${package} with uv, falling back to python3 -m pip"
fi fi
ensure_python_pip || {
log_error "python3 -m pip is unavailable"
return 1
}
# Try --user first (works on most Linux systems) # Try --user first (works on most Linux systems)
error_output=$(python3 -m pip install --user --upgrade pip "${package}" 2>&1) error_output=$(python3 -m pip install --user --upgrade pip "${package}" 2>&1)
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then