Repository navigation
feat(provisioning): support transition actions in bucket lifecycle - #281
Merged
GatewayJ merged 1 commit intoOct 17, 2026
Merged
Conversation
Bucket lifecycle rules accept `transition` (current versions) and `noncurrentVersionTransition` (noncurrent versions), both after zero or more days, matching RustFS validation. `storageClass` names a tier that is registered in RustFS; the operator checks it against RustFS tier-name rules (uppercase, not STANDARD or REDUCED_REDUNDANCY) and does not register tiers itself. Before applying a lifecycle that references tiers, the operator lists them via GET /rustfs/admin/v3/tier. A missing tier keeps the bucket Pending and requeues instead of failing on RustFS's generic 400, so a tier registered after the rule is picked up. Only tier names are read from that response. The admin client serializes the new elements in the order RustFS returns from GetBucketLifecycleConfiguration, so the canonical ownership hash of an applied configuration matches the live document. Rules without transitions serialize exactly as before. Closes rustfs#279
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Type of Change
Related Issues
Closes #279
Summary of Changes
Bucket lifecycle rules in
spec.buckets[].lifecyclecan now declaretransitionandnoncurrentVersionTransition, so ILM tiering rules are managed by the operator together with the other lifecycle rules of the bucket.transition {days, storageClass}andnoncurrentVersionTransition {noncurrentDays, storageClass}, both with days>= 0as RustFS validates them (Transition/NoncurrentVersionTransitionreject only negative days). The rule-level CEL rule now accepts any of the four actions.storageClassis checked against the RustFS tier-name rules (uppercase, notSTANDARDorREDUCED_REDUNDANCY, valid XML text), so a typo fails with a clear status message.400 InvalidArgument: invalid tier., which would leave the bucketFaileduntil the spec changes. Before applying a lifecycle that references tiers, the operator lists them withGET /rustfs/admin/v3/tier; while one is missing the bucket staysPendingwith a message naming the tier and the run requeues, the same way a user whose referenced policy is not ready is handled. The lookup runs only on the apply path, not when the live configuration already matches. Only tier names are read from the response; endpoints and credentials are dropped while parsing.rustfs-adminserializes the new elements in the order RustFS returns them fromGetBucketLifecycleConfiguration(... ID, NoncurrentVersionTransition, Status, Transition). The ownership check compares canonical XML without reordering, so this keeps the hash of an applied configuration equal to the live one and the rule is adopted instead of re-applied on every reconcile. Rules without transitions serialize byte-for-byte as before, so existing ownership hashes are unaffected.examples/provisioning-tenant.yamlis unchanged on purpose: a transition needs a tier that the example does not create, so the bucket would stayPendingfor anyone applying the example as is.Checklist
make pre-commit(fmt-check + clippy + test + console-lint + console-fmt-check)[Unreleased](if user-visible change)Impact
The CRD change is additive. The only changed existing text is the CEL message of the rule-level validation, which now lists all four actions.
Verification
New tests:
rustfs-admin:lifecycle_xml_with_transitions_matches_rustfs_get_response_shape,lifecycle_xml_transition_allows_zero_days_and_escapes_storage_class,tier_names_are_read_from_the_backend_object_and_secrets_are_ignored,tier_list_without_a_backend_name_is_rejected.lifecycle_configuration_validates_transition_fields,transition_bucket_lifecycle_is_applied_when_live_configuration_is_missing,transition_bucket_lifecycle_waits_for_an_unregistered_tier,rustfs_shaped_live_transition_lifecycle_is_adopted_without_write; the existing expiration apply test now also asserts that rules without transitions never query tiers.bucket_lifecycle_deserializes_transition_actionsand CRD schema assertions for the new minimums and the CEL message.The fixtures come from a live RustFS
1.0.2-preview.2: a lifecycle sent with its elements scrambled came back fromGetBucketLifecycleConfigurationin the order above, a rule with an unregisteredStorageClasswas rejected with400 InvalidArgument: invalid tier., and the tier list has the{"type": "s3", "s3": {"name": ...}}shape used in the parser test.Run in containers matching CI: Rust stable in
rust:1-bookwormwith Helm 3.21.3 and cargo-nextest,console-webinnode:24with pnpm 10.28.1. Allmake pre-committargets passed,cargo nextest run --allran 768 tests with 768 passing,helm lintpassed for both charts, andhelm templateof the default operator chart renders.Live check on a staging cluster. An operator image built from this branch (the
operatorbinary on top ofrustfs/operator:0.0.8) with the CRD from this branch, managing a RustFS1.0.2-preview.2Tenant whoseSTORJtier points at Storj:transition: {days: 0, storageClass: STORJ}Ready; the live document is exactly the one the operator sent; a new object transitioned (x-amz-storage-class: STORJ) and GET returned the original bytestransitiontoLATE, a tier that did not exist yetPendingwithwaiting for RustFS tier(s) LATE referenced by bucket lifecycle, no lifecycle PUTLATEregistered afterwards withrc ilm tier addReadyabout 6 s later without any spec changeReadyWhile a tier is missing the Tenant reports
Ready=FalsewithProvisioningPending, the same as for a user whose referenced policy is not ready.Additional Notes
lifecycle_canonicalization_preserves_unknown_actionsusedTransitionas its example of an action the operator does not model. It now usesNoncurrentVersionExpiration, which is still unmodeled, so the test keeps checking the same property.Transition.Date) andNewerNoncurrentVersionsare not modeled; they can follow if there is interest.Thank you for your contribution! Please ensure your PR follows the community standards (CODE_OF_CONDUCT.md) and sign the CLA if this is your first contribution.