Skip to content

Support configurable S3 addressing style - #176

Open
guozy18 wants to merge 1 commit into
kvcache-ai:mainfrom
guozy18:feat/oss-addressing-style
Open

Support configurable S3 addressing style#176
guozy18 wants to merge 1 commit into
kvcache-ai:mainfrom
guozy18:feat/oss-addressing-style

Conversation

@guozy18

@guozy18 guozy18 commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add an optional OSS/S3 bucket addressing-style override and propagate it through both snapshot data paths.

Motivation

The existing OSS backend chooses the addressing style from the endpoint:

  • Alibaba OSS endpoints and bucket-in-endpoint hosts use virtual-host style.
  • Other endpoints fall back to path style.

Some S3-compatible providers require or prefer virtual-host addressing but cannot be identified reliably by the endpoint heuristic. Without an explicit override, those providers may generate invalid requests.

This change allows users to explicitly select the required addressing style.

Changes

  • Add the optional backend.oss.addressing_style configuration:
    • virtual
    • path
    • unset for the existing endpoint-based auto-detection
  • Propagate the setting to the snapshot OSS client.
  • Propagate the setting to the generated OverlayBD runtime configuration as ossConfig.defaultAddressingStyle.
  • Apply the setting to OverlayBD remote managed-layer reads as well as snapshot repository operations and image export.
  • Reject unsupported addressing-style values.
  • Document the configuration and preserve the existing default behavior.
  • Add a wire-level regression test that verifies the actual HTTP method, request path, and Host header.
  • Keep the implementation focused by removing tests that only duplicated internal enum mappings or parser implementation details.

Configuration example

[backend.oss]
endpoint = "https://t3.storage.dev"
bucket = "agentenv-snapshots"
region = "auto"
addressing_style = "virtual"

When addressing_style is omitted, the existing endpoint-based detection remains unchanged.

Backward compatibility

Existing configurations do not require any migration.

The default behavior is preserved:

  • recognized Alibaba/bucket-host endpoints continue to use virtual-host addressing;
  • other endpoints continue to use path-style addressing unless explicitly overridden.

This change does not modify Prometheus metrics or the /metrics endpoints.

Validation

The following checks pass locally:

  • cargo fmt --all -- --check
  • git diff --check
  • cargo test -p object-store-operator
  • cargo clippy -p object-store-operator --all-targets -- -D warnings

The full AgentENV and OverlayBD test suites require a Linux environment. They could not be run locally on macOS because the workspace's io-uring dependency uses Linux-only libc symbols. Linux CI is required for the full workspace verification.

Relationship to #23

This PR supersedes #23.

The original requirement and initial implementation were proposed by @davidmyriel in #23. Thank you for that work and for the original motivation behind the change.

This PR carries the same behavior forward on top of the current main branch, with a refreshed implementation, focused regression tests, and updated documentation. The current implementation was reorganized to fit the latest codebase while preserving the original user-facing intent.

@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

🔍 OpenCodeReview found 1 issue(s) in this PR.

  • ✅ Successfully posted inline: 1 comment(s)

Comment on lines +214 to +216
/// Bucket addressing style: `"virtual"`, `"path"`, or empty to use
/// endpoint-based auto-detection.
pub default_addressing_style: String,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maintainability · low
This public field has only three valid states (auto, virtual, and path), but String makes every other value representable and requires the same parsing/validation contract to be duplicated in validate_global_config and the OSS backend. Prefer a typed enum (for example, Option<AddressingStyle>, where None means auto-detect) with Serde renames for virtual and path; invalid values will then be rejected during deserialization and direct Rust callers cannot construct an invalid config.

Suggestion:

Suggested change
/// Bucket addressing style: `"virtual"`, `"path"`, or empty to use
/// endpoint-based auto-detection.
pub default_addressing_style: String,
/// Bucket addressing style; `None` uses endpoint-based auto-detection.
pub default_addressing_style: Option<AddressingStyle>,

Comment on lines +73 to +80
assert!(
request_line.contains("/127/layers/explicit-style-object"),
"expected a path-style request for bucket '127', got: {request_line}"
);
assert!(
request_line.starts_with("GET "),
"expected a range GET request, got: {request_line}"
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test · low
Match the request line exactly (or parse its method and request target separately). The current independent contains/starts_with checks would accept an absolute-form URI, an unexpected path prefix/suffix, or another malformed target, so the test could pass without proving that path-style addressing generated exactly /127/layers/explicit-style-object. Also, the assertion message says “range GET” without checking the Range header.

Suggestion:

Suggested change
assert!(
request_line.contains("/127/layers/explicit-style-object"),
"expected a path-style request for bucket '127', got: {request_line}"
);
assert!(
request_line.starts_with("GET "),
"expected a range GET request, got: {request_line}"
);
assert_eq!(
request_line,
"GET /127/layers/explicit-style-object HTTP/1.1",
"expected an exact path-style request target"
);
assert!(
head.lines()
.any(|line| line.eq_ignore_ascii_case("Range: bytes=0-3")),
"expected a range request, got request head: {head}"
);

@guozy18
guozy18 force-pushed the feat/oss-addressing-style branch from 821e24e to 412e0be Compare August 18, 2026 01:21
Comment on lines +77 to +80
assert!(
request_line.contains("/test-bucket/layers/explicit-style-object"),
"expected a path-style request for bucket 'test-bucket', got: {request_line}"
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test · medium
This substring check can pass for an incorrect request target, such as a duplicated bucket/path prefix, an absolute-form URI, or an unexpected suffix/query. Since this test is intended as wire-level proof of path-style addressing, parse the request line and assert the method and request target exactly.

Suggestion:

Suggested change
assert!(
request_line.contains("/test-bucket/layers/explicit-style-object"),
"expected a path-style request for bucket 'test-bucket', got: {request_line}"
);
let mut request_parts = request_line.split_whitespace();
assert_eq!(request_parts.next(), Some("GET"));
assert_eq!(
request_parts.next(),
Some("/test-bucket/layers/explicit-style-object")
);
assert_eq!(request_parts.next(), Some("HTTP/1.1"));
assert_eq!(request_parts.next(), None);

Propagate the optional override through the snapshot client and OverlayBD runtime while preserving endpoint-based auto-detection by default.
@guozy18
guozy18 force-pushed the feat/oss-addressing-style branch from 412e0be to b7dcd59 Compare August 18, 2026 01:37
Comment on lines +52 to +62
let config = OssConfig {
enable: true,
access_key_id: "test-access-key".to_string(),
secret_access_key: "test-secret-key".to_string(),
default_region: "auto".to_string(),
default_endpoint: format!("http://test-bucket.localhost:{port}"),
default_addressing_style: "path".to_string(),
timeout_secs: 5,
retry_count: 1,
..Default::default()
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test · medium
This direct struct construction bypasses the runtime JSON/serde boundary for ossConfig.defaultAddressingStyle, although the test claims to prove propagation from that external setting to the wire. A regression in the camelCase mapping or generated runtime configuration could therefore leave the deployed option ineffective while this test still passes. Deserialize an OssConfig (or GlobalConfig) containing "defaultAddressingStyle": "path" before creating the backend so the wire assertion covers the configuration boundary too.

Suggestion:

Suggested change
let config = OssConfig {
enable: true,
access_key_id: "test-access-key".to_string(),
secret_access_key: "test-secret-key".to_string(),
default_region: "auto".to_string(),
default_endpoint: format!("http://test-bucket.localhost:{port}"),
default_addressing_style: "path".to_string(),
timeout_secs: 5,
retry_count: 1,
..Default::default()
};
let config: OssConfig = serde_json::from_value(serde_json::json!({
"enable": true,
"accessKeyId": "test-access-key",
"secretAccessKey": "test-secret-key",
"defaultRegion": "auto",
"defaultEndpoint": format!("http://test-bucket.localhost:{port}"),
"defaultAddressingStyle": "path",
"timeoutSecs": 5,
"retryCount": 1
}))
.expect("deserialize OSS runtime config");

@guozy18
guozy18 requested a review from blahgeek August 18, 2026 06:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant