Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -281,3 +281,76 @@ jobs:
env:
RUST_BACKTRACE: full
run: cargo test-api smoke docker

replica_live_s3:
name: Replica Live S3 (${{ matrix.server.name }})
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
server:
# Two S3-compatible implementations: MinIO exercises the full API
# (including ListMultipartUploads for the orphan sweep); RustFS
# pins compatibility with a second, Rust-native implementation.
- name: minio
start: >-
docker run -d --name live-s3 -p 9000:9000
-e MINIO_ROOT_USER=bencher-test
-e MINIO_ROOT_PASSWORD=bencher-test-secret
minio/minio:RELEASE.2025-09-07T16-13-09Z server /data
- name: rustfs
start: >-
docker run -d --name live-s3 -p 9000:9000
-e RUSTFS_ACCESS_KEY=bencher-test
-e RUSTFS_SECRET_KEY=bencher-test-secret
rustfs/rustfs:1.0.0-beta.10
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: ./.github/actions/setup-rust
with:
cache-key: replica-live-s3
mold-version: ${{ inputs.mold-version }}
- name: Start S3 server
env:
START_SERVER: ${{ matrix.server.start }}
run: |
${START_SERVER}
for _ in $(seq 1 30); do
if curl -s -o /dev/null http://127.0.0.1:9000/; then
exit 0
fi
sleep 2
done
echo "S3 server did not come up" >&2
docker logs live-s3 >&2 || true
exit 1
- name: Create bucket
env:
AWS_ACCESS_KEY_ID: bencher-test
AWS_SECRET_ACCESS_KEY: bencher-test-secret
AWS_DEFAULT_REGION: us-east-1
run: |
for _ in $(seq 1 15); do
if aws --endpoint-url http://127.0.0.1:9000 s3api create-bucket \
--bucket bencher-replica-test; then
exit 0
fi
sleep 2
done
echo "failed to create the test bucket" >&2
docker logs live-s3 >&2 || true
exit 1
- name: Run live S3 tier
env:
RUST_BACKTRACE: "1"
BENCHER_REPLICA_TEST_S3_BUCKET: bencher-replica-test
BENCHER_REPLICA_TEST_S3_ACCESS_KEY_ID: bencher-test
BENCHER_REPLICA_TEST_S3_SECRET_ACCESS_KEY: bencher-test-secret
BENCHER_REPLICA_TEST_S3_ENDPOINT: http://127.0.0.1:9000
BENCHER_REPLICA_TEST_S3_REGION: us-east-1
BENCHER_REPLICA_TEST_S3_PREFIX: ci-live-s3
run: |
cargo nextest run -p bencher_replica --features plus,testing \
--run-ignored ignored-only --no-fail-fast -E 'binary(live_s3)'
37 changes: 37 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ bencher_otel_provider = { path = "plus/bencher_otel_provider" }
bencher_oci_storage = { path = "plus/bencher_oci_storage" }
bencher_rate_limiter = { path = "plus/bencher_rate_limiter" }
bencher_recaptcha = { path = "plus/bencher_recaptcha" }
bencher_replica = { path = "plus/bencher_replica" }
# plus - runner
bencher_runner = { path = "plus/bencher_runner" }
bencher_oci = { path = "plus/bencher_oci" }
Expand Down
1 change: 1 addition & 0 deletions docker/bench.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ RUN cargo init --lib bencher_otel
RUN cargo init --lib bencher_otel_provider
RUN cargo init --lib bencher_rate_limiter
RUN cargo init --lib bencher_recaptcha
RUN cargo init --lib bencher_replica
RUN cargo init --lib bencher_rootfs
RUN cargo init --lib bencher_runner
RUN cargo init --lib bencher_init
Expand Down
6 changes: 5 additions & 1 deletion lib/api_server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ slog.workspace = true

[dev-dependencies]
bencher_api_tests.workspace = true
bencher_json = { workspace = true, features = ["server", "schema"] }
bencher_json = { workspace = true, features = ["server", "schema", "test-clock"] }
bencher_replica = { workspace = true, features = ["plus", "testing"] }
camino.workspace = true
http.workspace = true
rusqlite.workspace = true
tempfile.workspace = true
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }

[lints]
Expand Down
3 changes: 2 additions & 1 deletion lib/api_server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub async fn server_config_options(
/// View server configuration
///
/// View the API server configuration.
/// Secrets in the configuration are masked in the response.
/// The user must be an admin on the server to use this route.
#[endpoint {
method = GET,
Expand All @@ -53,7 +54,7 @@ async fn get_one_inner(log: &Logger) -> Result<JsonConfig, HttpError> {
)
})?
.unwrap_or_default()
.into())
.sanitized())
}

#[endpoint {
Expand Down
8 changes: 8 additions & 0 deletions lib/api_server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@
#[cfg(test)]
use bencher_api_tests as _;
#[cfg(test)]
use bencher_replica as _;
#[cfg(test)]
use camino as _;
#[cfg(test)]
use http as _;
#[cfg(test)]
use rusqlite as _;
#[cfg(test)]
use tempfile as _;
#[cfg(test)]
use tokio as _;

mod backup;
Expand Down
19 changes: 17 additions & 2 deletions lib/api_server/src/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use bencher_json::{
};
use bencher_schema::{
auth_conn,
context::{ApiContext, DbConnection},
context::{ApiContext, DbConnection, configure_standalone_connection},
error::{forbidden_error, issue_error, not_found_error},
model::{
server::QueryServer,
Expand Down Expand Up @@ -49,8 +49,23 @@ pub async fn server_stats_get(

async fn get_one_inner(log: &Logger, context: &ApiContext) -> Result<JsonServerStats, HttpError> {
let query_server = QueryServer::get_server(auth_conn!(context))?;
let conn = DbConnection::establish(context.database.path.to_string_lossy().as_ref())
let mut conn = DbConnection::establish(context.database.path.to_string_lossy().as_ref())
.map_err(not_found_error)?;
// Route this standalone connection through the shared configuration so it
// does not bypass the busy_timeout / autocheckpoint settings the pools and
// the writer use: under replication a stray write must never checkpoint.
configure_standalone_connection(
&mut conn,
context.database.busy_timeout,
context.database.replicated,
)
.map_err(|e| {
issue_error(
"Failed to configure server stats connection",
"Failed to configure the server stats database connection PRAGMAs",
e,
)
})?;
query_server
.get_stats(log.clone(), conn, context.is_bencher_cloud)
.await
Expand Down
Loading
Loading