mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2025-09-20 09:37:56 +08:00
Fix get object lock configuration handler (#6996)
* fix GetObjectLockConfigurationHandler * cache and use bucket object lock config * subscribe to bucket configuration changes * increase bucket config cache TTL * refactor * Update weed/s3api/s3api_server.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * avoid duplidated work * rename variable * Update s3api_object_handlers_put.go * fix routing * admin ui and api handler are consistent now * use fields instead of xml * fix test * address comments * Update weed/s3api/s3api_object_handlers_put.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/s3/retention/s3_retention_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update weed/s3api/object_lock_utils.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * change error style * errorf --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
232
weed/s3api/object_lock_utils.go
Normal file
232
weed/s3api/object_lock_utils.go
Normal file
@@ -0,0 +1,232 @@
|
||||
package s3api
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
||||
)
|
||||
|
||||
// ObjectLockUtils provides shared utilities for Object Lock configuration
|
||||
// These functions are used by both Admin UI and S3 API handlers to ensure consistency
|
||||
|
||||
// VersioningUtils provides shared utilities for bucket versioning configuration
|
||||
// These functions ensure Admin UI and S3 API use the same versioning keys
|
||||
|
||||
// StoreVersioningInExtended stores versioning configuration in entry extended attributes
|
||||
func StoreVersioningInExtended(entry *filer_pb.Entry, enabled bool) error {
|
||||
if entry.Extended == nil {
|
||||
entry.Extended = make(map[string][]byte)
|
||||
}
|
||||
|
||||
if enabled {
|
||||
entry.Extended[s3_constants.ExtVersioningKey] = []byte(s3_constants.VersioningEnabled)
|
||||
} else {
|
||||
entry.Extended[s3_constants.ExtVersioningKey] = []byte(s3_constants.VersioningSuspended)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadVersioningFromExtended loads versioning configuration from entry extended attributes
|
||||
func LoadVersioningFromExtended(entry *filer_pb.Entry) (bool, bool) {
|
||||
if entry == nil || entry.Extended == nil {
|
||||
return false, false // not found, default to suspended
|
||||
}
|
||||
|
||||
// Check for S3 API compatible key
|
||||
if versioningBytes, exists := entry.Extended[s3_constants.ExtVersioningKey]; exists {
|
||||
enabled := string(versioningBytes) == s3_constants.VersioningEnabled
|
||||
return enabled, true
|
||||
}
|
||||
|
||||
return false, false // not found
|
||||
}
|
||||
|
||||
// CreateObjectLockConfiguration creates a new ObjectLockConfiguration with the specified parameters
|
||||
func CreateObjectLockConfiguration(enabled bool, mode string, days int, years int) *ObjectLockConfiguration {
|
||||
if !enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
config := &ObjectLockConfiguration{
|
||||
ObjectLockEnabled: s3_constants.ObjectLockEnabled,
|
||||
}
|
||||
|
||||
// Add default retention rule if mode and period are specified
|
||||
if mode != "" && (days > 0 || years > 0) {
|
||||
config.Rule = &ObjectLockRule{
|
||||
DefaultRetention: &DefaultRetention{
|
||||
Mode: mode,
|
||||
Days: days,
|
||||
Years: years,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// ObjectLockConfigurationToXML converts ObjectLockConfiguration to XML bytes
|
||||
func ObjectLockConfigurationToXML(config *ObjectLockConfiguration) ([]byte, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("object lock configuration is nil")
|
||||
}
|
||||
|
||||
return xml.Marshal(config)
|
||||
}
|
||||
|
||||
// StoreObjectLockConfigurationInExtended stores Object Lock configuration in entry extended attributes
|
||||
func StoreObjectLockConfigurationInExtended(entry *filer_pb.Entry, config *ObjectLockConfiguration) error {
|
||||
if entry.Extended == nil {
|
||||
entry.Extended = make(map[string][]byte)
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
// Remove Object Lock configuration
|
||||
delete(entry.Extended, s3_constants.ExtObjectLockEnabledKey)
|
||||
delete(entry.Extended, s3_constants.ExtObjectLockDefaultModeKey)
|
||||
delete(entry.Extended, s3_constants.ExtObjectLockDefaultDaysKey)
|
||||
delete(entry.Extended, s3_constants.ExtObjectLockDefaultYearsKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Store the enabled flag
|
||||
entry.Extended[s3_constants.ExtObjectLockEnabledKey] = []byte(config.ObjectLockEnabled)
|
||||
|
||||
// Store default retention configuration if present
|
||||
if config.Rule != nil && config.Rule.DefaultRetention != nil {
|
||||
defaultRetention := config.Rule.DefaultRetention
|
||||
|
||||
// Store mode
|
||||
if defaultRetention.Mode != "" {
|
||||
entry.Extended[s3_constants.ExtObjectLockDefaultModeKey] = []byte(defaultRetention.Mode)
|
||||
}
|
||||
|
||||
// Store days
|
||||
if defaultRetention.Days > 0 {
|
||||
entry.Extended[s3_constants.ExtObjectLockDefaultDaysKey] = []byte(strconv.Itoa(defaultRetention.Days))
|
||||
}
|
||||
|
||||
// Store years
|
||||
if defaultRetention.Years > 0 {
|
||||
entry.Extended[s3_constants.ExtObjectLockDefaultYearsKey] = []byte(strconv.Itoa(defaultRetention.Years))
|
||||
}
|
||||
} else {
|
||||
// Remove default retention if not present
|
||||
delete(entry.Extended, s3_constants.ExtObjectLockDefaultModeKey)
|
||||
delete(entry.Extended, s3_constants.ExtObjectLockDefaultDaysKey)
|
||||
delete(entry.Extended, s3_constants.ExtObjectLockDefaultYearsKey)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadObjectLockConfigurationFromExtended loads Object Lock configuration from entry extended attributes
|
||||
func LoadObjectLockConfigurationFromExtended(entry *filer_pb.Entry) (*ObjectLockConfiguration, bool) {
|
||||
if entry == nil || entry.Extended == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Check if Object Lock is enabled
|
||||
enabledBytes, exists := entry.Extended[s3_constants.ExtObjectLockEnabledKey]
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
enabled := string(enabledBytes)
|
||||
if enabled != s3_constants.ObjectLockEnabled && enabled != "true" {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Create basic configuration
|
||||
config := &ObjectLockConfiguration{
|
||||
ObjectLockEnabled: s3_constants.ObjectLockEnabled,
|
||||
}
|
||||
|
||||
// Load default retention configuration if present
|
||||
if modeBytes, exists := entry.Extended[s3_constants.ExtObjectLockDefaultModeKey]; exists {
|
||||
mode := string(modeBytes)
|
||||
|
||||
// Parse days and years
|
||||
var days, years int
|
||||
if daysBytes, exists := entry.Extended[s3_constants.ExtObjectLockDefaultDaysKey]; exists {
|
||||
if parsed, err := strconv.Atoi(string(daysBytes)); err == nil {
|
||||
days = parsed
|
||||
}
|
||||
}
|
||||
if yearsBytes, exists := entry.Extended[s3_constants.ExtObjectLockDefaultYearsKey]; exists {
|
||||
if parsed, err := strconv.Atoi(string(yearsBytes)); err == nil {
|
||||
years = parsed
|
||||
}
|
||||
}
|
||||
|
||||
// Create rule if we have a mode and at least days or years
|
||||
if mode != "" && (days > 0 || years > 0) {
|
||||
config.Rule = &ObjectLockRule{
|
||||
DefaultRetention: &DefaultRetention{
|
||||
Mode: mode,
|
||||
Days: days,
|
||||
Years: years,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config, true
|
||||
}
|
||||
|
||||
// ExtractObjectLockInfoFromConfig extracts basic Object Lock information from configuration
|
||||
// Returns: enabled, mode, duration (for UI display)
|
||||
func ExtractObjectLockInfoFromConfig(config *ObjectLockConfiguration) (bool, string, int32) {
|
||||
if config == nil || config.ObjectLockEnabled != s3_constants.ObjectLockEnabled {
|
||||
return false, "", 0
|
||||
}
|
||||
|
||||
if config.Rule == nil || config.Rule.DefaultRetention == nil {
|
||||
return true, "", 0
|
||||
}
|
||||
|
||||
defaultRetention := config.Rule.DefaultRetention
|
||||
|
||||
// Convert years to days for consistent representation
|
||||
days := defaultRetention.Days
|
||||
if defaultRetention.Years > 0 {
|
||||
days += defaultRetention.Years * 365
|
||||
}
|
||||
|
||||
return true, defaultRetention.Mode, int32(days)
|
||||
}
|
||||
|
||||
// CreateObjectLockConfigurationFromParams creates ObjectLockConfiguration from individual parameters
|
||||
// This is a convenience function for Admin UI usage
|
||||
func CreateObjectLockConfigurationFromParams(enabled bool, mode string, duration int32) *ObjectLockConfiguration {
|
||||
if !enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
return CreateObjectLockConfiguration(enabled, mode, int(duration), 0)
|
||||
}
|
||||
|
||||
// ValidateObjectLockParameters validates Object Lock parameters before creating configuration
|
||||
func ValidateObjectLockParameters(enabled bool, mode string, duration int32) error {
|
||||
if !enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if mode != s3_constants.RetentionModeGovernance && mode != s3_constants.RetentionModeCompliance {
|
||||
return ErrInvalidObjectLockMode
|
||||
}
|
||||
|
||||
if duration <= 0 {
|
||||
return ErrInvalidObjectLockDuration
|
||||
}
|
||||
|
||||
if duration > MaxRetentionDays {
|
||||
return ErrObjectLockDurationExceeded
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user