Skip to content

feat(bucket): manage S3 lifecycle rules#2

Open
munin92 wants to merge 2 commits into
OpenProjectX:masterfrom
munin92:master
Open

feat(bucket): manage S3 lifecycle rules#2
munin92 wants to merge 2 commits into
OpenProjectX:masterfrom
munin92:master

Conversation

@munin92

@munin92 munin92 commented Jul 15, 2026

Copy link
Copy Markdown

What

Adds an optional lifecycle list to BucketSpec, so a bucket's expiration rules
become part of the same declarative object as its quota and versioning.

rc-core already exposes get/set/delete_bucket_lifecycle on ObjectStore; this
only surfaces them through the CRD and reconcile loop.

Why

A pull-through cache bucket that no client ever prunes. The application writing it
cannot GC its own cache, and every external cleaner needs a LIST — which is exactly
what fails once the volume is full, so the cleanup breaks precisely when it is needed.
A server-side lifecycle rule is the only thing that still works under pressure, and it
was the one bucket property the operator could not express — forcing an imperative
PUT ?lifecycle job alongside otherwise declarative CRs.

Semantics

Follows the existing quota/versioning fields:

  • unset → unmanaged: the operator never even reads the bucket's lifecycle, so
    adopting a bucket that already has rules cannot wipe them
  • empty list → "no rules": DELETEs the configuration (S3 rejects a PUT of an
    empty one)
  • non-empty → replaces the configuration when it differs

Scope

The CRD models a deliberate subset — expiration by age and abort incomplete
multipart uploads
. Transitions and non-current-version rules are omitted: they need
storage tiers / versioning that RustFS does not offer, and a CRD field that silently
does nothing is worse than no field.

Drift is detected on the serialized form because rc-core's LifecycleRule has no
PartialEq — comparing only the modelled fields would call a bucket in sync while an
unmodelled rule still differed.

Tests

  • 4 unit tests: apply / no-op when matching / empty list deletes / unset is untouched
  • Integration test round-trips a rule through a real RustFS container — asserts the rule
    comes back with its prefix, expiration and abort-multipart days, and is gone after
    delete

CI green on the fork; integration test additionally verified locally against a real
RustFS container.

munin92 and others added 2 commits July 14, 2026 18:14
Adds an optional `lifecycle` list to BucketSpec, so a bucket's expiration rules
become part of the same declarative object as its quota and versioning.

Motivation: a pull-through cache bucket that no client ever prunes. The application
writing it could not GC its own cache, and every external cleaner we tried needed a
LIST — which is exactly what fails once the volume is full, so the cleanup broke
precisely when it was needed. A server-side lifecycle rule is the only thing that
still works under pressure, and it was the one bucket property the operator could not
express, forcing an imperative PUT ?lifecycle job alongside otherwise declarative CRs.

rc-core already exposes get/set/delete_bucket_lifecycle on ObjectStore; this only
surfaces them.

Semantics follow the existing quota/versioning fields:
  - unset      -> unmanaged: the operator never even READS the bucket's lifecycle,
                  so adopting a bucket that already has rules cannot wipe them
  - empty list -> "no rules": DELETEs the configuration (S3 rejects a PUT of an
                  empty one)
  - non-empty  -> replaces the configuration when it differs

The CRD models a deliberate subset (expiration by age, abort incomplete multipart
uploads). Transitions and non-current-version rules are omitted: they need storage
tiers / versioning that RustFS does not offer, and a CRD field that silently does
nothing is worse than no field. Drift is detected on the serialized form because
rc-core's LifecycleRule has no PartialEq — comparing only the modelled fields would
call a bucket in sync while an unmodelled rule still differed.

Tests: 4 unit tests (apply / no-op when matching / empty list deletes / unset is
untouched) and the integration test now round-trips a rule through a real RustFS
container, asserting the rule comes back with its prefix, expiration and
abort-multipart days, and is gone after delete.
feat(bucket): manage S3 lifecycle rules
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