Skip to content

feat: S3 Object Lock support (grid + tenant + per-bucket sub-resources)#7

Merged
yehlo merged 3 commits into
mainfrom
s3lock
Apr 28, 2026
Merged

feat: S3 Object Lock support (grid + tenant + per-bucket sub-resources)#7
yehlo merged 3 commits into
mainfrom
s3lock

Conversation

@yehlo
Copy link
Copy Markdown
Collaborator

@yehlo yehlo commented Apr 28, 2026

feat: S3 Object Lock support (grid + tenant + per-bucket sub-resources)

Summary

Adds first-class support for S3 Object Lock across the SDK:

  • A new grid-wide service for /grid/compliance-global (read/update the
    complianceEnabled, legacyComplianceEnabled, createLegacyComplianceBuckets
    flags).
  • New tenant policy fields that gate S3 Object Lock per tenant
    (allowComplianceMode, maxRetentionDays, maxRetentionYears).
  • Expansion of the bucket service to call the new per-bucket sub-resource
    endpoints (region, usage, object-lock, notification, policy,
    cors, compliance) instead of always listing all buckets and filtering
    client-side.
  • An end-to-end example that walks the full grid → tenant policy → bucket flow.
  • Documentation and mock updates to match.

Changes

New: grid-wide S3 Object Lock (/grid/compliance-global)

  • models.S3ObjectLockComplianceEnabled, LegacyComplianceEnabled,
    CreateLegacyComplianceBuckets (all *bool + omitempty so partial PUTs
    and partial GET responses round-trip cleanly).
  • services.S3ObjectLockServiceInterface with Get(ctx) and
    Update(ctx, *S3ObjectLock).
  • Wired into GridClient as gridClient.S3ObjectLock().
  • testing.MockS3ObjectLockService with GetFunc / UpdateFunc and a
    compile-time interface conformance check.

Tenant policy: S3 Object Lock fields

Added to models.TenantPolicy:

  • AllowComplianceMode *bool — whether the tenant may use S3 Object Lock in
    compliance mode (requires grid-wide S3 Object Lock to be enabled).
  • MaxRetentionDays *int, MaxRetentionYears *int — per-tenant caps on the
    retention period a bucket may specify. nil means no cap.

Bucket sub-resources (/org/containers/{name}/...)

BucketServiceInterface now exposes typed methods that hit the dedicated
per-bucket endpoints, matching the current StorageGRID API surface:

  • GetBucketUsage/usage
  • GetRegion/region (returns string)
  • GetObjectLock / UpdateObjectLock/object-lock
  • GetNotification / UpdateNotification/notification
  • GetPolicy / UpdatePolicy/policy
  • GetCors / UpdateCors/cors
  • GetCompliance / UpdateCompliance/compliance (legacy compliance)

New wrapper models in models/buckets.go:

  • BucketUsage (objectCount, dataBytes)
  • BucketCorsConfiguration (cors XML, nil ⇒ disable)
  • BucketNotificationConfiguration (notification XML, nil ⇒ disable)
  • BucketPolicyConfiguration (policy, nil ⇒ disable)
  • BucketPolicy, BucketPolicyStatement — typed S3 policy document
    (Id, Version, Statement[] with Effect, Action/NotAction,
    Resource/NotResource, Condition, Principal/NotPrincipal).
    Polymorphic fields that the S3 policy language allows as either a string
    or a list (and the "*"-or-object Principal) remain interface{}; the
    Condition block is typed as
    map[string]map[string]interface{} matching condition_type → key → value.

Bucket model fixes

  • BucketS3ObjectLockDefaultRetentionSettings.Mode now documents that both
    compliance and governance are valid, instead of "must be compliance".

Mocks

MockBucketService extended with *Func fields and pass-through default
implementations for every new method. The existing compile-time interface
check still passes.

Example

examples/grid/s3-object-lock/ — new end-to-end example. By default it only
reads grid-wide settings. With STORAGEGRID_APPLY=true and tenant
credentials it:

  1. GETs /grid/compliance-global.
  2. Enables complianceEnabled grid-wide.
  3. Updates the target tenant's policy (allowComplianceMode=true,
    maxRetentionYears=10).
  4. Creates a bucket with versioning + Object Lock enabled and a default
    compliance retention period.
  5. Reads back per-bucket Object Lock settings via the new
    /org/containers/{name}/object-lock sub-resource.

examples/tenant/bucket-operations/main.go was migrated off the deprecated
GetUsage to GetBucketUsage + GetRegion.

Docs

  • Root README.md: feature bullets for grid-wide S3 Object Lock and the new
    bucket sub-resources, an API-coverage row for /grid/compliance-global, an
    API-coverage row for the bucket sub-resources, and MockS3ObjectLockService
    added to the available-mocks list.
  • examples/README.md: pruned stale links to non-existent examples and added
    the new grid/s3-object-lock entry.

Deprecations

  • BucketServiceInterface.GetUsage(ctx, name) (*BucketStats, error) is now
    marked // Deprecated:. It still works (it fetches the entire tenant
    usage payload from /org/usage and filters by name) but new code should
    use the per-bucket endpoint:

    // Before (deprecated)
    stats, err := tenantClient.Bucket().GetUsage(ctx, "my-bucket")
    
    // After
    usage, err := tenantClient.Bucket().GetBucketUsage(ctx, "my-bucket")
    region, err := tenantClient.Bucket().GetRegion(ctx, "my-bucket")

    GetBucketUsage returns the slimmer models.BucketUsage (ObjectCount,
    DataBytes); other fields previously stitched in from /org/usage
    (Region, VersioningEnabled, Encryption, …) are available via the
    matching per-bucket sub-resources.

    The deprecated method will be removed in a future release.

Compatibility

  • No changes to existing service constructors, client constructors, or
    authentication flow.
  • All previously-existing methods remain (only GetUsage is annotated as
    deprecated).

Testing

  • go build ./... and go vet ./... pass.
  • examples/grid/s3-object-lock builds standalone (go mod init +
    go mod edit -replace + go build).
  • Mock interface conformance for MockS3ObjectLockService and
    MockBucketService is enforced at compile time via var _ assertions.

References

@yehlo yehlo merged commit 282e7fb into main Apr 28, 2026
@yehlo yehlo deleted the s3lock branch April 28, 2026 05:21
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