mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix(storage): close S3 deployment gaps
Honor shared URL addressing in the Git probe, expose the signing region through Helm, and make storage validation guidance match the actual startup probe behavior. Co-authored-by: Kalvin Chau <kalvin@block.xyz> Signed-off-by: Kalvin Chau <kalvin@block.xyz>
This commit is contained in:
co-authored by
Kalvin Chau
parent
bd7b275fb4
commit
6879f48152
Generated
+1
@@ -1239,6 +1239,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"buzz-core",
|
||||
"buzz-media",
|
||||
"buzz-sdk",
|
||||
"buzz-ws-client",
|
||||
"chrono",
|
||||
|
||||
@@ -36,6 +36,7 @@ sha2 = { workspace = true }
|
||||
sqlx = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
s3 = { version = "0.37", package = "rust-s3", default-features = false, features = ["tokio-rustls-tls", "fail-on-err", "tags"] }
|
||||
buzz-media = { workspace = true }
|
||||
buzz-sdk = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
|
||||
@@ -21,6 +21,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
|
||||
use buzz_media::S3AddressingStyle;
|
||||
use nostr::{EventBuilder, Keys, Kind, Tag};
|
||||
use s3::creds::Credentials;
|
||||
use s3::{Bucket, Region};
|
||||
@@ -115,6 +116,27 @@ struct PointerSnapshot {
|
||||
}
|
||||
|
||||
impl GitS3Probe {
|
||||
fn bucket(
|
||||
endpoint: String,
|
||||
access_key: &str,
|
||||
secret_key: &str,
|
||||
bucket_name: &str,
|
||||
region_name: String,
|
||||
addressing_style: S3AddressingStyle,
|
||||
) -> Box<Bucket> {
|
||||
let region = Region::Custom {
|
||||
region: region_name,
|
||||
endpoint,
|
||||
};
|
||||
let creds = Credentials::new(Some(access_key), Some(secret_key), None, None, None)
|
||||
.expect("S3 credentials");
|
||||
let bucket = Bucket::new(bucket_name, region, creds).expect("S3 bucket");
|
||||
match addressing_style {
|
||||
S3AddressingStyle::Path => bucket.with_path_style(),
|
||||
S3AddressingStyle::Virtual => bucket,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_env() -> Self {
|
||||
let endpoint = std::env::var("BUZZ_GIT_S3_ENDPOINT")
|
||||
.or_else(|_| std::env::var("BUZZ_S3_ENDPOINT"))
|
||||
@@ -125,19 +147,25 @@ impl GitS3Probe {
|
||||
let secret_key = std::env::var("BUZZ_GIT_S3_SECRET_KEY")
|
||||
.or_else(|_| std::env::var("BUZZ_S3_SECRET_KEY"))
|
||||
.unwrap_or_else(|_| "buzz_dev_secret".to_string());
|
||||
let bucket = std::env::var("BUZZ_GIT_S3_BUCKET")
|
||||
let bucket_name = std::env::var("BUZZ_GIT_S3_BUCKET")
|
||||
.or_else(|_| std::env::var("BUZZ_S3_BUCKET"))
|
||||
.unwrap_or_else(|_| "buzz-media".to_string());
|
||||
let region_name = std::env::var("BUZZ_GIT_S3_REGION")
|
||||
.or_else(|_| std::env::var("BUZZ_S3_REGION"))
|
||||
.unwrap_or_else(|_| "us-east-1".to_string());
|
||||
let addressing_style = std::env::var("BUZZ_S3_ADDRESSING_STYLE")
|
||||
.unwrap_or_else(|_| "path".to_string())
|
||||
.parse::<S3AddressingStyle>()
|
||||
.expect("BUZZ_S3_ADDRESSING_STYLE must be 'path' or 'virtual'");
|
||||
|
||||
let region = Region::Custom {
|
||||
region: "us-east-1".into(),
|
||||
let bucket = Self::bucket(
|
||||
endpoint,
|
||||
};
|
||||
let creds = Credentials::new(Some(&access_key), Some(&secret_key), None, None, None)
|
||||
.expect("S3 credentials");
|
||||
let bucket = Bucket::new(&bucket, region, creds)
|
||||
.expect("S3 bucket")
|
||||
.with_path_style();
|
||||
&access_key,
|
||||
&secret_key,
|
||||
&bucket_name,
|
||||
region_name,
|
||||
addressing_style,
|
||||
);
|
||||
Self { bucket }
|
||||
}
|
||||
|
||||
@@ -192,6 +220,31 @@ impl GitS3Probe {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn git_s3_probe_builds_both_addressing_styles() {
|
||||
let path = GitS3Probe::bucket(
|
||||
"https://storage.example".to_string(),
|
||||
"access",
|
||||
"secret",
|
||||
"buzz-media",
|
||||
"us-east-1".to_string(),
|
||||
S3AddressingStyle::Path,
|
||||
);
|
||||
assert!(path.is_path_style());
|
||||
assert_eq!(path.url(), "https://storage.example/buzz-media");
|
||||
|
||||
let virtual_hosted = GitS3Probe::bucket(
|
||||
"https://storage.example".to_string(),
|
||||
"access",
|
||||
"secret",
|
||||
"buzz-media",
|
||||
"auto".to_string(),
|
||||
S3AddressingStyle::Virtual,
|
||||
);
|
||||
assert!(virtual_hosted.is_subdomain_style());
|
||||
assert_eq!(virtual_hosted.url(), "https://buzz-media.storage.example");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore = "requires live relay + MinIO + git"]
|
||||
async fn git_clone_push_fetch_force_roundtrip() {
|
||||
|
||||
@@ -61,28 +61,36 @@ Buzz uses one URL style for both media and Git/CAS object-store requests:
|
||||
| `path` (default) | `https://endpoint/bucket/key` | Bundled MinIO and endpoints whose DNS does not resolve bucket subdomains |
|
||||
| `virtual` | `https://bucket.endpoint/key` | AWS-style providers and new Railway Storage Buckets |
|
||||
|
||||
The chart renders this value as `BUZZ_S3_ADDRESSING_STYLE`. Only `path` and
|
||||
`virtual` are accepted; invalid values fail chart rendering and relay startup.
|
||||
The bundled MinIO quickstart deliberately keeps `path` because its Service DNS
|
||||
resolves one endpoint hostname, not arbitrary `<bucket>.<service>` names.
|
||||
The chart renders `s3.region` and `s3.addressingStyle` as `BUZZ_S3_REGION`
|
||||
and `BUZZ_S3_ADDRESSING_STYLE`. Only `path` and `virtual` addressing styles are
|
||||
accepted; invalid values fail chart rendering and relay startup. The bundled
|
||||
MinIO quickstart deliberately keeps `path` because its Service DNS resolves one
|
||||
endpoint hostname, not arbitrary `<bucket>.<service>` names.
|
||||
|
||||
For a Railway Storage Bucket named `Object Storage`, map its variables onto the
|
||||
Buzz service and set the style explicitly:
|
||||
For a Railway Storage Bucket, map its variables to chart values in the service
|
||||
or generated Helm configuration:
|
||||
|
||||
```text
|
||||
BUZZ_S3_ENDPOINT=${{Object Storage.ENDPOINT}}
|
||||
BUZZ_S3_BUCKET=${{Object Storage.BUCKET}}
|
||||
BUZZ_S3_ACCESS_KEY=${{Object Storage.ACCESS_KEY_ID}}
|
||||
BUZZ_S3_SECRET_KEY=${{Object Storage.SECRET_ACCESS_KEY}}
|
||||
BUZZ_S3_REGION=${{Object Storage.REGION}}
|
||||
BUZZ_S3_ADDRESSING_STYLE=virtual
|
||||
```yaml
|
||||
s3:
|
||||
endpoint: "${{Object Storage.ENDPOINT}}"
|
||||
bucket: "${{Object Storage.BUCKET}}"
|
||||
region: "${{Object Storage.REGION}}"
|
||||
addressingStyle: virtual
|
||||
```
|
||||
|
||||
Railway's Credentials tab is authoritative for older buckets, which may still
|
||||
require `path`. The setting changes request routing and SigV4 signing, so do not
|
||||
put the bucket into `BUZZ_S3_ENDPOINT`; pass Railway's base `ENDPOINT` and
|
||||
`BUCKET` separately. The relay validates storage before becoming ready, and the
|
||||
Git conformance probe remains startup-fatal when enabled.
|
||||
Store `BUZZ_S3_ACCESS_KEY=${{Object Storage.ACCESS_KEY_ID}}` and
|
||||
`BUZZ_S3_SECRET_KEY=${{Object Storage.SECRET_ACCESS_KEY}}` in the Secret named by
|
||||
`secrets.existingSecret`. Railway's Credentials tab is authoritative for older
|
||||
buckets, which may still require `path`. The setting changes request routing and
|
||||
SigV4 signing, so do not put the bucket into `s3.endpoint`; pass Railway's base
|
||||
`ENDPOINT` and `BUCKET` separately.
|
||||
|
||||
Object storage is contacted during relay startup only when
|
||||
`BUZZ_GIT_CONFORMANCE_PROBE` is enabled (the relay default). A probe failure is
|
||||
startup-fatal, so Kubernetes readiness never opens. If an operator explicitly
|
||||
disables that probe through `relay.extraEnv`, `/_readiness` does not test object
|
||||
storage; configuration is still parsed strictly, but reachability and addressing
|
||||
errors surface on the first storage operation.
|
||||
|
||||
## Relay Pod extensions
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ spec:
|
||||
s3:
|
||||
endpoint: "https://s3.us-east-1.amazonaws.com"
|
||||
bucket: "buzz-media"
|
||||
region: "us-east-1"
|
||||
addressingStyle: virtual
|
||||
# accessKey / secretKey live in buzz-secrets
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ spec:
|
||||
s3:
|
||||
endpoint: "https://s3.us-east-1.amazonaws.com"
|
||||
bucket: "buzz-media"
|
||||
region: "us-east-1"
|
||||
addressingStyle: virtual
|
||||
|
||||
persistence:
|
||||
|
||||
@@ -75,16 +75,12 @@ surface at template time regardless of which manifest helm renders first.
|
||||
{{- fail "Postgres source missing: enable postgresql.enabled=true, set externalPostgresql.url, or provide secrets.existingSecret with key DATABASE_URL." -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* S3 addressing style is duplicated here in addition to values.schema.json
|
||||
because Helm unittest does not consistently apply schema validation. */}}
|
||||
{{- if not (has .Values.s3.addressingStyle (list "path" "virtual")) -}}
|
||||
{{- fail "s3.addressingStyle must be 'path' or 'virtual'. Use path for bundled MinIO and virtual for providers such as new Railway Storage Buckets." -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* S3 / object-storage source must exist somewhere (relay hard-fails its
|
||||
startup conformance probe without a reachable bucket). */}}
|
||||
{{/* S3 / object-storage source must exist somewhere. With the default
|
||||
BUZZ_GIT_CONFORMANCE_PROBE behavior, an unreachable bucket is detected
|
||||
before the relay opens its listener; operators can explicitly disable that
|
||||
startup gate. */}}
|
||||
{{- if not (or .Values.minio.enabled .Values.s3.endpoint .Values.secrets.existingSecret) -}}
|
||||
{{- fail "S3/object-storage source missing: enable minio.enabled=true (quickstart in-cluster), set s3.endpoint + s3.bucket + credentials, or provide secrets.existingSecret with keys BUZZ_S3_ACCESS_KEY + BUZZ_S3_SECRET_KEY. The relay runs a startup S3 conformance probe and exits if storage is unreachable." -}}
|
||||
{{- fail "S3/object-storage source missing: enable minio.enabled=true (quickstart in-cluster), set s3.endpoint + s3.bucket + credentials, or provide secrets.existingSecret with keys BUZZ_S3_ACCESS_KEY + BUZZ_S3_SECRET_KEY. By default the relay runs a startup S3 conformance probe and exits if storage is unreachable; disabling BUZZ_GIT_CONFORMANCE_PROBE also removes that startup storage check." -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- end -}}
|
||||
|
||||
@@ -170,6 +170,7 @@ spec:
|
||||
- { name: BUZZ_S3_ENDPOINT, value: {{ $s3Endpoint | quote }} }
|
||||
{{- end }}
|
||||
- { name: BUZZ_S3_BUCKET, value: {{ .Values.s3.bucket | quote }} }
|
||||
- { name: BUZZ_S3_REGION, value: {{ .Values.s3.region | quote }} }
|
||||
- { name: BUZZ_S3_ADDRESSING_STYLE, value: {{ .Values.s3.addressingStyle | quote }} }
|
||||
|
||||
# ── Secrets (from chart-managed or existing) ─────────────
|
||||
|
||||
@@ -30,6 +30,12 @@ tests:
|
||||
path: kind
|
||||
value: Service
|
||||
template: templates/service.yaml
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].env
|
||||
content:
|
||||
name: BUZZ_S3_REGION
|
||||
value: "us-east-1"
|
||||
template: templates/deployment.yaml
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].env
|
||||
content:
|
||||
@@ -61,10 +67,17 @@ tests:
|
||||
externalRedis.url: redis://h:6379
|
||||
s3.endpoint: https://storage.railway.app
|
||||
s3.bucket: buzz-media-example
|
||||
s3.region: auto
|
||||
s3.addressingStyle: virtual
|
||||
s3.accessKey: a
|
||||
s3.secretKey: s
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].env
|
||||
content:
|
||||
name: BUZZ_S3_REGION
|
||||
value: "auto"
|
||||
template: templates/deployment.yaml
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].env
|
||||
content:
|
||||
|
||||
@@ -198,6 +198,11 @@
|
||||
"properties": {
|
||||
"endpoint": { "type": "string", "pattern": "^(https?://.+)?$" },
|
||||
"bucket": { "type": "string", "minLength": 1 },
|
||||
"region": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "S3 region used for SigV4 signing. Defaults to us-east-1 for bundled MinIO compatibility."
|
||||
},
|
||||
"addressingStyle": {
|
||||
"type": "string",
|
||||
"enum": ["path", "virtual"],
|
||||
|
||||
@@ -338,6 +338,9 @@ externalRedis:
|
||||
s3:
|
||||
endpoint: ""
|
||||
bucket: "buzz-media"
|
||||
# SigV4 signing region. Keep us-east-1 for bundled MinIO; set this to the
|
||||
# provider's credential value for managed S3 (for example Railway's REGION).
|
||||
region: "us-east-1"
|
||||
# path: https://endpoint/bucket/key (bundled MinIO-compatible default)
|
||||
# virtual: https://bucket.endpoint/key (standard S3; required by new Railway buckets)
|
||||
addressingStyle: path
|
||||
|
||||
Reference in New Issue
Block a user