bucketwarden-server 0.1.0

BucketWarden storage server runtime.
Documentation
mod common;

use bucketwarden_s3::S3HttpRequest;
use common::*;

#[test]
fn replication_preserves_legal_hold_and_retention_state() {
    let mut runtime = runtime();
    runtime
        .handle_s3_http(S3HttpRequest::new("alice", "PUT", "/archive-001"))
        .expect("source");
    runtime
        .handle_s3_http(S3HttpRequest::new("alice", "PUT", "/archive-002"))
        .expect("destination");
    for bucket in ["archive-001", "archive-002"] {
        runtime
            .handle_s3_http(
                S3HttpRequest::new("alice", "PUT", format!("/{bucket}"))
                    .with_query("object-lock", "")
                    .with_body(
                        b"<ObjectLockConfiguration><ObjectLockEnabled>Enabled</ObjectLockEnabled></ObjectLockConfiguration>"
                            .to_vec(),
                    ),
            )
            .expect("object lock");
    }
    runtime
        .handle_s3_http(
            S3HttpRequest::new("alice", "PUT", "/archive-001")
                .with_query("replication", "")
                .with_body(
                    br#"<ReplicationConfiguration>
                        <Role>arn:aws:iam::123456789012:role/bucketwarden-replication</Role>
                        <Rule>
                            <ID>records</ID>
                            <Status>Enabled</Status>
                            <Filter><Prefix>records/</Prefix></Filter>
                            <Destination><Bucket>arn:aws:s3:::archive-002</Bucket></Destination>
                            <DeleteMarkerReplication><Status>Enabled</Status></DeleteMarkerReplication>
                            <SourceSelectionCriteria><SseKmsEncryptedObjects><Status>Enabled</Status></SseKmsEncryptedObjects></SourceSelectionCriteria>
                        </Rule>
                    </ReplicationConfiguration>"#
                        .to_vec(),
                ),
        )
        .expect("replication config");

    let put = runtime
        .handle_s3_http(
            S3HttpRequest::new("alice", "PUT", "/archive-001/records/a.json")
                .with_header("x-amz-object-lock-legal-hold", "ON")
                .with_header("x-amz-object-lock-mode", "COMPLIANCE")
                .with_header(
                    "x-amz-object-lock-retain-until-date",
                    "1970-01-03T00:00:00Z",
                )
                .with_body(br#"{"locked":true}"#.to_vec()),
        )
        .expect("put locked object");
    let version_id = put
        .headers
        .get("x-amz-version-id")
        .expect("version")
        .clone();

    let run = runtime
        .run_bucket_replication("alice", "archive-001")
        .expect("replicate");
    assert_eq!(run.replicated_object_versions, 1);

    let source_retention = runtime
        .get_object_retention("alice", "archive-001", "records/a.json", Some(&version_id))
        .expect("source retention");
    let destination_retention = runtime
        .get_object_retention("alice", "archive-002", "records/a.json", Some(&version_id))
        .expect("destination retention");
    assert_eq!(destination_retention.mode, source_retention.mode);
    assert_eq!(
        destination_retention.retain_until_epoch_seconds,
        source_retention.retain_until_epoch_seconds
    );

    let source_hold = runtime
        .get_object_legal_hold("alice", "archive-001", "records/a.json", Some(&version_id))
        .expect("source hold");
    let destination_hold = runtime
        .get_object_legal_hold("alice", "archive-002", "records/a.json", Some(&version_id))
        .expect("destination hold");
    assert!(source_hold.enabled);
    assert!(destination_hold.enabled);
    assert_eq!(
        runtime
            .get_object_version("alice", "archive-001", "records/a.json", &version_id)
            .expect("source version")
            .replication_status
            .as_deref(),
        Some("COMPLETED")
    );
}