rustfs-cli 0.1.36

A Rust S3 CLI client for S3-compatible object storage
Documentation
//! Process-level contracts for MinIO-compatible replication metrics.

#![cfg(not(windows))]

mod admin_support;

use std::process::{Command, Output};
use std::time::Duration;

use admin_support::{rc_binary, rc_host_alias, start_admin_test_server};

const MINIO_METRICS: &str =
    include_str!("../../core/tests/fixtures/replication_metrics_minio_v1.json");

fn run_status(json: bool) -> (Output, admin_support::CapturedAdminRequest) {
    let config_dir = tempfile::tempdir().expect("create isolated config dir");
    let (endpoint, receiver, handle) = start_admin_test_server(MINIO_METRICS);
    let mut command = Command::new(rc_binary());
    command.arg("--no-color");
    if json {
        command.arg("--json");
    } else {
        command.args(["--format", "human"]);
    }
    let output = command
        .args(["bucket", "replication", "status", "myalias/source-bucket"])
        .env("RC_CONFIG_DIR", config_dir.path())
        .env("RC_HOST_myalias", rc_host_alias(&endpoint))
        .output()
        .expect("execute replication status");
    let request = receiver
        .recv_timeout(Duration::from_secs(5))
        .expect("captured replication metrics request");
    handle.join().expect("admin test server finished");
    (output, request)
}

#[test]
fn replication_status_human_accepts_minio_metrics() {
    let (output, request) = run_status(false);

    assert!(
        output.status.success(),
        "stderr: {}",
        String::from_utf8_lossy(&output.stderr)
    );
    assert!(output.stderr.is_empty());
    let stdout = String::from_utf8(output.stdout).expect("UTF-8 human output");
    assert!(
        stdout.contains("provider=available, cluster=complete (1/1 nodes)"),
        "stdout: {stdout}"
    );
    assert!(
        stdout.contains("Totals: replicated 1 / 0 objects, 20 / 0 bytes"),
        "stdout: {stdout}"
    );
    assert!(
        stdout.contains("1 / 20 bytes  0 / 0 bytes  unavailable  legacy_unknown"),
        "stdout: {stdout}"
    );
    assert_eq!(
        request.target,
        "/rustfs/admin/v3/replicationmetrics?bucket=source-bucket"
    );
}

#[test]
fn replication_status_json_accepts_minio_metrics() {
    let (output, request) = run_status(true);

    assert!(
        output.status.success(),
        "stderr: {}",
        String::from_utf8_lossy(&output.stderr)
    );
    assert!(output.stderr.is_empty());
    let value: serde_json::Value =
        serde_json::from_slice(&output.stdout).expect("v3 replication JSON");
    let schema: serde_json::Value =
        serde_json::from_str(include_str!("../../../schemas/output_v3.json"))
            .expect("output-v3 schema");
    let validator = jsonschema::validator_for(&schema).expect("compiled output-v3 schema");
    let errors = validator
        .iter_errors(&value)
        .map(|error| error.to_string())
        .collect::<Vec<_>>();
    assert!(
        errors.is_empty(),
        "replication status violates output-v3 schema: {}",
        errors.join("; ")
    );
    assert_eq!(value["schema_version"], 3);
    assert_eq!(value["type"], "replication");
    assert_eq!(value["status"], "success");
    assert_eq!(value["data"]["availability"], "available");
    assert_eq!(value["data"]["cluster"]["state"], "complete");
    assert_eq!(value["data"]["totals"]["replicated_count"], 1);
    assert_eq!(value["data"]["totals"]["replicated_size_bytes"], 20);
    assert_eq!(value["data"]["targets"][0]["replicated_count"], 1);
    assert_eq!(value["data"]["targets"][0]["replicated_size_bytes"], 20);
    assert_eq!(
        value["data"]["targets"][0]["latency"]["scope"],
        "unavailable"
    );
    assert_eq!(
        request.target,
        "/rustfs/admin/v3/replicationmetrics?bucket=source-bucket"
    );
}