s3pe
A small, dependable client and command line for S3-compatible object stores (AWS S3, Garage, MinIO, Ceph, …): nine verbs, SigV4 signing over the real payload hash, retries that know when to stop, and an integrity check on every transfer.
Website: https://strategicprojects.github.io/s3pe/rust/ · API reference: https://docs.rs/s3pe · all editions
Install
Rust 1.85 or later. TLS by rustls with bundled roots: no system OpenSSL.
Library
use ;
// endpoint and credentials come from the environment
let manifest = read_s3?;
write_s3?;
for object in list_s3?
# Ok::
With an explicit client:
let s3 = builder
.endpoint
.credentials
.bucket
.build?;
let bytes = s3.read?;
s3.upload?; // streaming
s3.download?; // streaming, .s3pe-part then rename
if let Some = s3.head?
s3.delete?;
# Ok::
Big files, copies, batches, folders, metadata, pre-signed URLs, sync
use ;
use Duration;
s3.upload?; // > 64 MiB: multipart, 4 parts at a time
s3.copy?;
let keys: = s3.list?.into_iter.map.collect;
s3.delete_many?; // DeleteObjects, 1000 per request
let level = s3.list_dir?; // level.objects, level.prefixes ("folders")
let opts = PutOptions ;
s3.write_with?;
let url = s3.presign?;
let report = s3.sync_up?;
s3.sync_down?;
# Ok::
The multipart knobs: .multipart_threshold(), .part_size(), .parallel()
on the builder, or S3PE_MULTIPART_THRESHOLD, S3PE_PART_SIZE,
S3PE_PARALLEL (defaults 64 MiB, 16 MiB, 4). headers are sent as given,
so x-amz-server-side-encryption: AES256 turns on SSE where the store
supports it. No ACLs and no versioning, by design.
Configuration
| Setting | Environment | Default |
|---|---|---|
| endpoint | S3PE_ENDPOINT, then AWS_ENDPOINT_URL |
required (https://host[:port]) |
| region | AWS_REGION, then AWS_DEFAULT_REGION |
us-east-1 |
| credentials | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY (AWS_SESSION_TOKEN) |
required |
| bucket | S3PE_BUCKET |
none: use s3://bucket/key |
Path-style URLs always (https://host/bucket/key), which every store
accepts. Keys are bare (relative to the bucket) or full s3://bucket/key
URLs.
What happens in every call
What "dependable" means
- AWS Signature V4 over the real SHA-256 of the payload: no
UNSIGNED-PAYLOAD, noaws-chunked(which some stores reject). - Connection failures and HTTP 429/500/502/503/504 are retried twice, with
a 1 s then 2 s pause. Other errors come back at once, typed:
Error::NotFound,Error::AccessDenied,Error::Http { status, code, .. },Error::Transport,Error::Config,Error::Integrity. - After an upload the server's ETag is compared with the local MD5; after a
download the byte count with
Content-Lengthand, when the ETag is an MD5, the MD5 of what arrived. - One line per transfer through the
logfacade (targets3pe):↑ s3://my-bucket/etl/sales.parquet (12.3 MB, 0.8 s).
Command line
|
Exit codes: 0 ok, 2 not found, 3 access denied, 4 configuration, 1 anything
else. -q or S3PE_QUIET=1 keeps stderr to errors.
Architecture
The family
The same package exists for Python (pip install s3pe), R
(install.packages("s3pe")) and Go (go get github.com/StrategicProjects/s3pe),
all in one repository, with one
version and one changelog. The Python package is a binding on this crate.
Made in Pernambuco 🌵
s3pe is developed in Recife, Brazil. MIT license.