mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-06-13 23:36:45 +03:00
54dd4f091d
* s3api: extend lifecycle XML types with NoncurrentVersionExpiration, AbortIncompleteMultipartUpload Add missing S3 lifecycle rule types to the XML data model: - NoncurrentVersionExpiration with NoncurrentDays and NewerNoncurrentVersions - NoncurrentVersionTransition with NoncurrentDays and StorageClass - AbortIncompleteMultipartUpload with DaysAfterInitiation - Filter.ObjectSizeGreaterThan and ObjectSizeLessThan - And.ObjectSizeGreaterThan and ObjectSizeLessThan - Filter.UnmarshalXML to properly parse Tag, And, and size filter elements Each new type follows the existing set-field pattern for conditional XML marshaling. No behavior changes - these types are not yet wired into handlers or the lifecycle worker. * s3lifecycle: add lifecycle rule evaluator package New package weed/s3api/s3lifecycle/ provides a pure-function lifecycle rule evaluation engine. The evaluator accepts flattened Rule structs and ObjectInfo metadata, and returns the appropriate Action. Components: - evaluator.go: Evaluate() for per-object actions with S3 priority ordering (delete marker > noncurrent version > current expiration), ShouldExpireNoncurrentVersion() with NewerNoncurrentVersions support, EvaluateMPUAbort() for multipart upload rules - filter.go: prefix, tag, and size-based filter matching - tags.go: ExtractTags() extracts S3 tags from filer Extended metadata, HasTagRules() for scan-time optimization - version_time.go: GetVersionTimestamp() extracts timestamps from SeaweedFS version IDs (both old and new format) Comprehensive test coverage: 54 tests covering all action types, filter combinations, edge cases, and version ID formats. * s3api: add UnmarshalXML for Expiration, Transition, ExpireDeleteMarker Add UnmarshalXML methods that set the internal 'set' flag during XML parsing. Previously these flags were only set programmatically, causing XML round-trip to drop elements. This ensures lifecycle configurations stored as XML survive unmarshal/marshal cycles correctly. Add comprehensive XML round-trip tests for all lifecycle rule types including NoncurrentVersionExpiration, AbortIncompleteMultipartUpload, Filter with Tag/And/size constraints, and a complete Terraform-style lifecycle configuration. * s3lifecycle: address review feedback - Fix version_time.go overflow: guard timestampPart > MaxInt64 before the inversion subtraction to prevent uint64 wrap - Make all expiry checks inclusive (!now.Before instead of now.After) so actions trigger at the exact scheduled instant - Add NoncurrentIndex to ObjectInfo so Evaluate() can properly handle NewerNoncurrentVersions via ShouldExpireNoncurrentVersion() - Add test for high-bit overflow version ID * s3lifecycle: guard ShouldExpireNoncurrentVersion against zero SuccessorModTime Add early return when obj.IsLatest or obj.SuccessorModTime.IsZero() to prevent premature expiration of versions with uninitialized successor timestamps (zero value would compute to epoch, always expired). --------- Co-authored-by: Copilot <copilot@github.com>
43 lines
1.2 KiB
Go
43 lines
1.2 KiB
Go
package s3lifecycle
|
|
|
|
import (
|
|
"math"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// versionIdFormatThreshold distinguishes old vs new format version IDs.
|
|
// New format (inverted timestamps) produces values above this threshold;
|
|
// old format (raw timestamps) produces values below it.
|
|
const versionIdFormatThreshold = 0x4000000000000000
|
|
|
|
// GetVersionTimestamp extracts the actual timestamp from a SeaweedFS version ID,
|
|
// handling both old (raw nanosecond) and new (inverted nanosecond) formats.
|
|
// Returns zero time if the version ID is invalid or "null".
|
|
func GetVersionTimestamp(versionId string) time.Time {
|
|
ns := getVersionTimestampNanos(versionId)
|
|
if ns == 0 {
|
|
return time.Time{}
|
|
}
|
|
return time.Unix(0, ns)
|
|
}
|
|
|
|
// getVersionTimestampNanos extracts the raw nanosecond timestamp from a version ID.
|
|
func getVersionTimestampNanos(versionId string) int64 {
|
|
if len(versionId) < 16 || versionId == "null" {
|
|
return 0
|
|
}
|
|
timestampPart, err := strconv.ParseUint(versionId[:16], 16, 64)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
if timestampPart > math.MaxInt64 {
|
|
return 0
|
|
}
|
|
if timestampPart > versionIdFormatThreshold {
|
|
// New format: inverted timestamp, convert back.
|
|
return int64(math.MaxInt64 - timestampPart)
|
|
}
|
|
return int64(timestampPart)
|
|
}
|