* feat(s3api): add bucket quota S3 extension via ?seaweedfs-quota
Add a SeaweedFS-specific S3 subresource for bucket quota management:
PUT /{bucket}?seaweedfs-quota — set bucket quota (s3:PutBucketQuota)
GET /{bucket}?seaweedfs-quota — get bucket quota (s3:GetBucketQuota)
The request/response body is JSON:
{"quota_size": 100, "quota_unit": "GB", "quota_enabled": true}
Quota is stored on the bucket's filer entry (positive = enabled,
negative = disabled but retained, zero = no quota), matching the
existing admin REST API behavior. When quota is cleared, the bucket's
read-only flag is also lifted.
Authentication uses the existing S3 SigV4 flow — no new global secret
is needed. Authorization uses two new dedicated IAM permissions:
s3:PutBucketQuota
s3:GetBucketQuota
This allows integrations like Apache CloudStack to manage per-bucket
quotas through the S3 endpoint with a scoped credential, without
exposing the broad admin REST API or requiring a separate admin token.
The credential can be limited to s3:PutBucketQuota/s3:GetBucketQuota
only, preventing bucket deletion, user management, or cluster topology
changes.
The coarse-grained ACTION_PUT_BUCKET_QUOTA/ACTION_GET_BUCKET_QUOTA
constants are added to s3_constants, and the action resolver maps the
seaweedfs-quota query parameter to the fine-grained s3: actions for
policy evaluation.
* docs: update design for S3 ?seaweedfs-quota extension approach
Replace the broad admin REST API + bearer-token design with the narrow,
scoped S3 ?seaweedfs-quota extension. Update quota, usage reporting, and
SeaweedFS-side changes sections to reflect PR #11279.
* fix(s3api): address review comments on quota handler
Fix four issues identified by Devin, Greptile, and CodeRabbit reviews:
1. Integer overflow in convertQuotaToBytes: large quota_size values
(e.g. 8388608 TB) could overflow int64, wrapping to negative and
being silently treated as zero quota. Now returns an error when
size * multiplier would exceed math.MaxInt64.
2. Disabled quotas returned negative sizes in GET: the GET handler
returned entry.Quota directly, which is negative for disabled-but-
retained quotas. Now returns the absolute magnitude as quota_size
and derives quota_enabled from the sign, making the response
round-trippable.
3. Missing buckets returned 500 instead of NoSuchBucket: the PUT
handler treated all lookup failures as internal errors. Now
distinguishes filer_pb.ErrNotFound and returns ErrNoSuchBucket.
4. Trailing JSON was silently accepted: the decoder read only the
first JSON object without checking for trailing data. Now
requires EOF after the object, rejecting malformed payloads.
Also add tests for overflow detection and trailing data rejection.
* fix(s3api): cast math.MaxInt64 to int64 for 32-bit vet
On 32-bit platforms, math.MaxInt64 is an untyped int constant that
overflows int (32-bit) when used directly in fmt.Errorf with %d.
Cast to int64 explicitly to fix Go Vet 32-bit.
* docs: reconcile design doc with implementation and add AWS tools note
- Resolve open question about IAM endpoint path: driver accepts optional
iamUrl and defaults to <s3Url>/iam
- Add note explaining ?seaweedfs-quota is not callable by standard AWS tools
(aws s3api, s3cmd, rclone), and how this compares to MinIO and Ceph quota
APIs which also live outside the standard S3 API
* docs: fix IAM endpoint default — SeaweedFS IAM is at POST / on S3 endpoint
SeaweedFS registers its embedded IAM API at POST / on the same S3
endpoint (UnifiedPostHandler), not under /iam. The design doc
previously said the driver defaults iamUrl to <s3Url>/iam, which would
send IAM operations to an unregistered path. Correct the default to
s3Url.
Found by Greptile review on PR #11279.
* docs: fix credential model, signer, and GET response shape in design doc
Three issues found by CodeRabbit review on PR #11279:
1. Credential-scope contradiction: the doc claimed the service credential
is scoped to only s3:PutBucketQuota/s3:GetBucketQuota, but the
implementation uses it as the admin credential for all operations
(bucket CRUD, IAM user provisioning, quota). Document the actual
model.
2. S3Signer -> AWSS3V4Signer: the doc said 'S3Signer for SigV4 signing'
but S3Signer is legacy SigV2. Correct to AWSS3V4Signer.
3. GET response shape: the doc showed a single JSON example with 'GB'
for both PUT and GET, but GET always returns quota_unit 'B' and the
absolute byte count. Document PUT input and GET response separately.
21 KiB
SeaweedFS as an Apache CloudStack Object Storage Provider
A CloudStack ObjectStore plugin that makes SeaweedFS a first-class object storage backend inside Apache CloudStack, alongside the existing MinIO and Ceph RGW providers. This is a collaboration with proIO (Swen), who builds private clouds on CloudStack and wants SeaweedFS as a storage option.
The request
We can only add MinIO and Ceph as object storage [in CloudStack] today. I want to get SeaweedFS into this project... What we need is to build a provider which does the communication between Cloudstack and SeaweedFS.
This is not a SeaweedFS-side feature. The work lives in the Apache CloudStack
repo (Java): a new plugin under plugins/storage/object/seaweedfs/ that implements
CloudStack's ObjectStore plugin framework and talks to SeaweedFS over its S3 and
IAM APIs. SeaweedFS itself needs no changes for the core to work — its S3 API
already covers every bucket operation CloudStack requires, and its IAM API covers
user/credential management.
How the CloudStack ObjectStore framework works
CloudStack 4.18+ introduced an Object Storage framework. An admin registers an
object storage pool via addObjectStoragePool (URL + provider + credentials);
tenants then create and manage buckets on it through CloudStack APIs. CloudStack
manages pool and bucket lifecycle; the underlying provider handles the actual
object protocol.
A provider is a plugin module implementing three interfaces:
1. ObjectStoreProvider — registration
MinIOObjectStoreProviderImpl is the reference. It is a Spring @Component that:
- Returns a provider name (
"MinIO") - Returns
DataStoreProviderType.OBJECT - In
configure(), injects the lifecycle and driver implementations and callsstoreMgr.registerDriver(name, driver)
2. ObjectStoreLifeCycle — pool add/remove
MinIOObjectStoreLifeCycleImpl.initialize() reads the URL, name, and
accesskey/secretkey details from the addObjectStoragePool call, tests the
connection by listing buckets, and persists an ObjectStoreVO via
ObjectStoreHelper. The other methods (attachCluster/Host/Zone, maintain,
deleteDataStore) are no-ops for object storage.
3. ObjectStoreDriver — bucket + user operations
ObjectStoreDriver (in engine/storage/.../object/ObjectStoreDriver.java) extends
DataStoreDriver and defines the bucket/user contract. Every provider must
implement:
| Method | Purpose |
|---|---|
createBucket(Bucket, boolean objectLock) |
Create a bucket |
listBuckets(long storeId) |
List all buckets |
deleteBucket(BucketTO, long storeId) |
Delete a bucket |
createUser(long accountId, long storeId) |
Provision a user + credentials for a CloudStack account |
setBucketPolicy / getBucketPolicy / deleteBucketPolicy |
Bucket policy CRUD |
setBucketEncryption / deleteBucketEncryption |
SSE config |
setBucketVersioning / deleteBucketVersioning |
Versioning enable/suspend |
setBucketQuota(BucketTO, long storeId, long size) |
Per-bucket quota |
getAllBucketsUsage(long storeId) |
Usage map for billing/accounting |
getBucketAcl / setBucketAcl |
ACLs (MinIO/Ceph return null / no-op) |
BaseObjectStoreDriverImpl provides no-op defaults for the DataStoreDriver
methods (createAsync, deleteAsync, copyAsync, canCopy, resize,
getTO, getStoreTO), so object-store providers only implement the bucket/user
methods above.
How the four existing providers differ (and where SeaweedFS lands)
CloudStack ships four object-store providers. Three are relevant; the simulator is a test stub.
| Concern | MinIO | Ceph RGW | Cloudian HyperStore | SeaweedFS |
|---|---|---|---|---|
| Bucket CRUD | MinioClient (S3) |
AmazonS3 (AWS SDK v1) |
AmazonS3 (AWS SDK v1) |
AmazonS3 (AWS SDK v1) |
| Bucket policy | MinioClient |
AmazonS3 |
AmazonS3 |
AmazonS3 |
| Versioning | MinioClient |
AmazonS3 |
AmazonS3 |
AmazonS3 |
| Encryption | MinioClient |
not implemented | AmazonS3 |
AmazonS3 |
| User creation | MinioAdminClient |
RgwAdmin |
AmazonIdentityManagement |
AmazonIdentityManagement |
| Per-bucket quota | MinioAdminClient |
RgwAdmin |
not supported (throws) | S3 extension (PUT /{bucket}?seaweedfs-quota, SigV4, s3:PutBucketQuota) |
| Usage reporting | MinioAdminClient |
RgwAdmin |
Cloudian admin API | S3 ListObjectsV2 (MVP); Prometheus / SOSAPI capacity.xml (recommended) |
Cloudian HyperStore is the direct precedent. It is an S3-compatible store
that, like SeaweedFS, manages users via the standard AWS IAM API using the
AWS IAM Java SDK (com.amazonaws.services.identitymanagement). Its driver
(CloudianHyperStoreObjectStoreDriverImpl) and util
(CloudianHyperStoreUtil) are the template this design follows almost line for
line. Cloudian even validates the quota limitation the same way this design
proposes for the MVP: setBucketQuota throws for any non-zero size and only
accepts 0 (no quota).
The SeaweedFS plugin is therefore a simpler Cloudian — same AWS S3 + IAM SDK
clients, same store-details keys (s3Url, iamUrl, accesskey, secretkey),
same IAM-user-with-restricted-policy pattern, but with no proprietary admin
client at all (Cloudian has its own CloudianClient for its admin API; SeaweedFS
needs only S3 + IAM). For quota, the plugin uses a narrow SeaweedFS S3 extension
(see below); for usage reporting, it falls back to S3 ListObjectsV2 in the MVP
and recommends Prometheus or SOSAPI capacity.xml for production scale.
Quota via the S3 ?seaweedfs-quota extension
SeaweedFS supports bucket quota natively (server-side enforcement via a read-only flag when usage exceeds the limit). Rather than exposing the broad admin REST API (which would require a global bearer token and grant cluster-wide admin access), the integration uses a narrow, scoped S3 subresource:
PUT /{bucket}?seaweedfs-quota— set bucket quota (IAM permissions3:PutBucketQuota)GET /{bucket}?seaweedfs-quota— get bucket quota (IAM permissions3:GetBucketQuota)
PUT request body (JSON):
{"quota_size": 100, "quota_unit": "GB", "quota_enabled": true}
GET response body (JSON):
{"quota_size": 107374182400, "quota_unit": "B", "quota_enabled": true}
Note: GET always returns quota_unit: "B" and the absolute byte count, not
the original unit. A disabled-but-retained quota returns a positive
quota_size with quota_enabled: false.
Quota is stored on the bucket's filer entry (positive = enabled, negative = disabled but retained, zero = no quota), matching the existing admin REST API behavior. When quota is cleared, the bucket's read-only flag is also lifted.
Authentication uses the existing S3 SigV4 flow — no new global secret is
needed. The CloudStack service credential (the accesskey/secretkey on the
object store) is the admin credential used for all driver operations: bucket
CRUD, IAM user provisioning, and quota management. It must have broad S3 and
IAM permissions. The per-account IAM users created by createUser are the
ones with restricted permissions (full S3 access except bucket
creation/deletion). A future hardening could split quota management onto a
separate credential scoped to only s3:PutBucketQuota/s3:GetBucketQuota,
but the MVP uses the single admin credential for simplicity, matching how
the MinIO and Ceph providers work.
The plugin's setBucketQuota signs and sends the PUT /{bucket}?seaweedfs-quota
request using the AWS SDK v1 AWSS3V4Signer for SigV4 signing, then sends the
signed request via java.net.http.HttpClient (the AWS S3 SDK doesn't natively
support custom subresources, so we sign manually and send the request
ourselves). The seaweedfs-quota query parameter is included in the signed
canonical query string.
Usage reporting
getAllBucketsUsage must return a Map<String, Long> of bucket name → size.
MinIO uses MinioAdminClient.getDataUsageInfo; Ceph uses
RgwAdmin.listBucketInfo. SeaweedFS has no admin rollup endpoint, so the MVP
plugin computes it by listing buckets and summing object sizes via S3
ListObjectsV2 — expensive for large stores.
For production scale, SeaweedFS already exposes per-bucket size in:
- Prometheus metrics (
bucket_size_bytesgauge, refreshed every minute) - SOSAPI
capacity.xml(reports capacity, available space, and usage through the S3 endpoint)
Operators should consume one of those instead of S3 list-based aggregation for large deployments. The MVP's list-based approach is correct but slow; flag it as a known limitation.
SeaweedFS API surface (what the plugin relies on)
SeaweedFS exposes two relevant APIs, both AWS-compatible:
S3 API (weed s3)
Full S3-compatible surface. Confirmed against the SeaweedFS S3 wiki and code:
CreateBucket,HeadBucket,ListBuckets,DeleteBucketPutBucketPolicy,GetBucketPolicy,DeleteBucketPolicyPutBucketVersioning(Enabled / Suspended),GetBucketVersioningPutBucketEncryption,GetBucketEncryption,DeleteBucketEncryptionPutBucketAcl,GetBucketAclListObjectsV2,HeadObject,GetObject,PutObject,DeleteObject- Bucket quota via extended attributes /
s3.bucket.quota(enforced server-side, surfaced as a read-only state when exceeded — see PR #10224)
IAM API (weed iam / iamapi)
AWS IAM-compatible REST endpoints, implemented in weed/iamapi/. Confirmed by
the test suite which uses the AWS IAM SDK (aws-sdk-go/service/iam) against
the same handlers CloudStack would call:
CreateUser,DeleteUser,ListUsers,GetUserCreateAccessKey,DeleteAccessKey,ListAccessKeysPutUserPolicy,GetUserPolicy,DeleteUserPolicyAttachUserPolicy,ListAttachedUserPolicies
This means the CloudStack plugin can manage SeaweedFS users with the AWS IAM
Java SDK (com.amazonaws.services.identitymanagement.AmazonIdentityManagement),
exactly the way the AWS IAM Go SDK is used in SeaweedFS's own tests. No proprietary
admin client is needed. Cloudian HyperStore already does exactly this in the
CloudStack tree — the SeaweedFS plugin follows the same pattern.
Design
Module layout
New CloudStack plugin module, mirroring plugins/storage/object/cloudian/
(the closest precedent — same AWS S3 + IAM SDK approach):
plugins/storage/object/seaweedfs/
pom.xml
src/main/java/org/apache/cloudstack/storage/datastore/
driver/SeaweedFSObjectStoreDriverImpl.java
lifecycle/SeaweedFSObjectStoreLifeCycleImpl.java
provider/SeaweedFSObjectStoreProviderImpl.java
util/SeaweedFSObjectStoreUtil.java
src/test/java/org/apache/cloudstack/storage/datastore/
driver/SeaweedFSObjectStoreDriverImplTest.java
provider/SeaweedFSObjectStoreProviderImplTest.java
src/main/resources/META-INF/cloudstack/storage-object-seaweedfs/
module.properties
spring-storage-object-seaweedfs-context.xml
SeaweedFSObjectStoreProviderImpl
Direct copy of MinIOObjectStoreProviderImpl with providerName = "SeaweedFS",
injecting the SeaweedFS lifecycle and driver. Registers via
storeMgr.registerDriver.
SeaweedFSObjectStoreLifeCycleImpl
Copy of MinIOObjectStoreLifeCycleImpl. initialize() reads url, name,
accesskey, secretkey from the addObjectStoragePool details map, tests the
connection by calling AmazonS3.listBuckets() against the SeaweedFS S3 endpoint,
and persists the ObjectStoreVO. No proprietary client needed — the AWS S3 SDK
is enough for the health check.
SeaweedFSObjectStoreDriverImpl
The substantive class. Uses two AWS SDK v1 clients (same dependency Ceph already pulls in, so no new CloudStack dependency):
AmazonS3for bucket operations (path-style, endpoint-pinned,us-east-1region placeholder — same as Ceph'sgetS3Client)AmazonIdentityManagementfor user/credential operations, pointed at the SeaweedFS IAM endpoint
Bucket operations — straightforward S3
| Interface method | Implementation |
|---|---|
createBucket |
s3.createBucket(name); reject if doesBucketExistV2; persist access/secret key + URL on BucketVO (same as Ceph) |
listBuckets |
s3.listBuckets() → wrap as BucketObject (same as Ceph) |
deleteBucket |
s3.deleteBucket(name) (same as Ceph) |
setBucketPolicy |
s3.setBucketPolicy(...) with the same public/private JSON the MinIO/Ceph drivers build |
getBucketPolicy / deleteBucketPolicy |
s3.getBucketPolicy / s3.deleteBucketPolicy |
setBucketVersioning |
s3.setBucketVersioningConfiguration(Enabled) |
deleteBucketVersioning |
s3.setBucketVersioningConfiguration(Suspended) |
setBucketEncryption |
s3.setBucketEncryptionConfiguration(SSE-S3 rule) |
deleteBucketEncryption |
s3.deleteBucketEncryptionConfiguration |
getBucketAcl / setBucketAcl |
no-op / null (same as MinIO and Ceph) |
User creation — the key difference
MinIO calls MinioAdminClient.addUser; Ceph calls RgwAdmin.createUser. SeaweedFS
exposes the standard AWS IAM API, so the plugin calls:
AmazonIdentityManagement iam = getIamClient(storeId);
String userName = "acs-" + account.getUuid();
// CreateUser (idempotent — check GetUser first, like Ceph does)
iam.createUser(new CreateUserRequest(userName));
// CreateAccessKey → returns the access key + secret key to persist
CreateAccessKeyResult result = iam.createAccessKey(
new CreateAccessKeyRequest().withUserName(userName));
AccessKey key = result.getAccessKey();
// Persist per-account, same pattern as Ceph's CEPH_ACCESS_KEY/CEPH_SECRET_KEY
details.put(SEAWEEDFS_ACCESS_KEY, key.getAccessKeyId());
details.put(SEAWEEDFS_SECRET_KEY, key.getSecretAccessKey());
_accountDetailsDao.persist(accountId, details);
This is the cleanest mapping of the three providers: no proprietary admin client,
just the AWS IAM SDK that CloudStack already has access to. The IAM endpoint URL
is provided as iamUrl in the store details. If iamUrl is omitted, the driver
defaults it to s3Url — SeaweedFS registers its embedded IAM API at POST / on
the same S3 endpoint (UnifiedPostHandler in s3api_server.go), so the IAM
endpoint is the same as the S3 endpoint unless the deployment runs a separate
weed iam server.
Bucket quota — S3 ?seaweedfs-quota extension
This is the one genuine gap. MinIO and Ceph both have an admin API to set a per-bucket quota that the backend enforces. SeaweedFS enforces bucket quota server-side, but the configuration path was not exposed over a standard S3 or IAM API — it was only set via the admin REST API or shell commands.
The integration adds a narrow S3 subresource to SeaweedFS:
PUT /{bucket}?seaweedfs-quota— set bucket quota (IAM permissions3:PutBucketQuota)GET /{bucket}?seaweedfs-quota— get bucket quota (IAM permissions3:GetBucketQuota)
This is implemented in SeaweedFS PR #11279. It uses SigV4 authentication and dedicated IAM permissions, so the CloudStack service credential can be scoped to quota management only — no global admin token, no cluster-wide admin access. The enforcement already exists (PR #10224); this PR only adds the HTTP configuration surface.
An earlier approach (PR #11278, closed) added bearer-token auth to the broad admin REST API. After review, that was unnecessary for this integration — static S3 config plus standard S3 APIs plus one scoped quota mutation API is sufficient and far safer.
Note on AWS tools compatibility.
?seaweedfs-quotais a SeaweedFS-specific S3 subresource, not part of the AWS S3 API. Standard AWS tools (aws s3api,s3cmd,rclone) cannot call it directly. This is the same limitation MinIO and Ceph have — MinIO quota lives behind a separate admin API (mc admin bucket quota), and Ceph quota lives behind the Admin Ops API (radosgw-admin quota set). Neither is callable viaaws s3apieither. SeaweedFS's approach is the closest to standard S3 because it uses the same endpoint and same SigV4 credentials, just with a custom query parameter. Interactive quota management remains available viaweed shell; the S3 extension exists for programmatic integration (CloudStack) where the integrator can sign SigV4 requests but cannot run shell commands.
Usage reporting
getAllBucketsUsage must return a Map<String, Long> of bucket name → size.
MinIO uses MinioAdminClient.getDataUsageInfo; Ceph uses
RgwAdmin.listBucketInfo. SeaweedFS has no admin rollup endpoint, so the MVP
plugin computes it by listing buckets and summing object sizes via S3
ListObjectsV2 — expensive for large stores. Better options exist in
SeaweedFS already:
- Prometheus metrics (
bucket_size_bytesgauge, refreshed every minute) - SOSAPI
capacity.xml(reports capacity, available space, and usage through the S3 endpoint — note: the current "return zero on backend error" behavior should be validated before using it for billing)
For the MVP, listBuckets + per-bucket size via the S3 API is correct but slow;
flag it as a known limitation. Operators should consume Prometheus or SOSAPI
for production-scale usage reporting.
Spring wiring
spring-storage-object-seaweedfs-context.xml registers the provider bean,
identical to the MinIO one. module.properties sets
name=storage-object-seaweedfs, parent=storage.
pom.xml
Depends on aws-java-sdk-s3 and aws-java-sdk-iam — both already in the
CloudStack dependency tree (Ceph uses the S3 SDK; the IAM SDK is the standard AWS
bundle). No new third-party dependency, unlike MinIO which pulls in the MinIO
Java client.
What changes on the SeaweedFS side
One narrow S3 extension is required for quota management. SeaweedFS PR #11279
adds the ?seaweedfs-quota S3 subresource:
PUT /{bucket}?seaweedfs-quota— set bucket quota (IAM permissions3:PutBucketQuota)GET /{bucket}?seaweedfs-quota— get bucket quota (IAM permissions3:GetBucketQuota)
This is authenticated via standard S3 SigV4 and authorized via dedicated IAM permissions, so no global admin token is needed. The enforcement already exists (PR #10224); this PR only adds the HTTP configuration surface.
One follow-up improvement on the SeaweedFS side would close the usage reporting gap:
- Validate SOSAPI
capacity.xmlusage calculation — the current "return zero on backend error" behavior should be validated before using it for billing. If reliable, CloudStack can consume it directly instead of list-based aggregation.
Open questions for proIO / Swen
- IAM endpoint path.
Where doesResolved. SeaweedFS registers its embedded IAM API atweed iamlisten relative to the S3 endpoint in a typical proIO deployment?POST /on the same S3 endpoint (UnifiedPostHandler), so the driver defaultsiamUrltos3Url. A separateiamUrlis only needed if the deployment runs a standaloneweed iamserver on a different host/port. - Quota requirements. Do proIO's customers need server-enforced per-bucket
quotas, or is CloudStack-side accounting sufficient for the first release?
The
?seaweedfs-quotaS3 extension (PR #11279) provides server-enforced quotas via a scoped credential; this is the recommended path. - Object Lock.
createBuckettakes anobjectLockboolean. MinIO supports it; Ceph ignores it. SeaweedFS has Object Lock support. Should the plugin pass it through? - Contribution model. Does proIO want to submit the PR to
apache/cloudstackthemselves (with SeaweedFS maintainers as reviewers), or the reverse? Apache CloudStack requires an ICLA for non-trivial contributions.
Files
All in the apache/cloudstack repo (new module):
| File | Purpose |
|---|---|
plugins/storage/object/seaweedfs/pom.xml |
Maven module |
.../datastore/util/SeaweedFSObjectStoreUtil.java |
S3 + IAM client builders, constants, URL validators |
.../datastore/provider/SeaweedFSObjectStoreProviderImpl.java |
Spring provider registration |
.../datastore/lifecycle/SeaweedFSObjectStoreLifeCycleImpl.java |
Pool add/health-check |
.../datastore/driver/SeaweedFSObjectStoreDriverImpl.java |
Bucket + user ops via S3 + IAM SDK |
.../resources/META-INF/cloudstack/storage-object-seaweedfs/module.properties |
Module name |
.../resources/META-INF/cloudstack/storage-object-seaweedfs/spring-storage-object-seaweedfs-context.xml |
Spring bean |
plugins/pom.xml |
Register storage/object/seaweedfs module |
No files in seaweedfs/seaweedfs for the MVP.
SeaweedFS-side changes (PR #11279)
| File | Purpose |
|---|---|
weed/s3api/s3_constants/s3_action_strings.go |
Add S3_ACTION_PUT_BUCKET_QUOTA and S3_ACTION_GET_BUCKET_QUOTA |
weed/s3api/s3_constants/s3_actions.go |
Add coarse-grained ACTION_PUT_BUCKET_QUOTA and ACTION_GET_BUCKET_QUOTA |
weed/s3api/s3_action_resolver.go |
Map seaweedfs-quota query param to fine-grained s3: actions |
weed/s3api/s3api_bucket_quota_handlers.go |
New — PutBucketQuotaHandler and GetBucketQuotaHandler |
weed/s3api/s3api_bucket_quota_handlers_test.go |
New — tests for unit conversion, validation, and error paths |
weed/s3api/s3api_server.go |
Register the two routes in the bucket subrouter |