mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-20 13:30:46 +02:00
admin: show bucket lifecycle rules in the Admin UI (#10313)
* admin: surface lifecycle rule counts in bucket listing * admin: add bucket lifecycle JSON endpoint * admin: show lifecycle rules on the buckets page * admin: make the lifecycle count badge keyboard-accessible * admin: match buckets empty-state colspan to the column count * admin: drop stale lifecycle modal responses * make
This commit is contained in:
+100
-13
@@ -33,7 +33,10 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/lifecycle_xml"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3lifecycle"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3lifecycle/scheduler"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3tables"
|
||||
"github.com/seaweedfs/seaweedfs/weed/worker/tasks"
|
||||
|
||||
@@ -797,20 +800,23 @@ func (s *AdminServer) GetS3Buckets() ([]S3Bucket, error) {
|
||||
lastModified = time.Unix(resp.Entry.Attributes.Mtime, 0)
|
||||
}
|
||||
readOnly := fc.MatchStorageRule(filerConfig.BucketsPath + "/" + bucketName + "/").ReadOnly
|
||||
lifecycleRuleCount, lifecycleEnabledCount := extractLifecycleCountsFromEntry(resp.Entry)
|
||||
bucket := S3Bucket{
|
||||
Name: bucketName,
|
||||
CreatedAt: createdAt,
|
||||
LogicalSize: logicalSize,
|
||||
PhysicalSize: physicalSize,
|
||||
LastModified: lastModified,
|
||||
Quota: quota,
|
||||
QuotaEnabled: quotaEnabled,
|
||||
ReadOnly: readOnly,
|
||||
VersioningStatus: versioningStatus,
|
||||
ObjectLockEnabled: objectLockEnabled,
|
||||
ObjectLockMode: objectLockMode,
|
||||
ObjectLockDuration: objectLockDuration,
|
||||
Owner: owner,
|
||||
Name: bucketName,
|
||||
CreatedAt: createdAt,
|
||||
LogicalSize: logicalSize,
|
||||
PhysicalSize: physicalSize,
|
||||
LastModified: lastModified,
|
||||
Quota: quota,
|
||||
QuotaEnabled: quotaEnabled,
|
||||
ReadOnly: readOnly,
|
||||
VersioningStatus: versioningStatus,
|
||||
ObjectLockEnabled: objectLockEnabled,
|
||||
ObjectLockMode: objectLockMode,
|
||||
ObjectLockDuration: objectLockDuration,
|
||||
Owner: owner,
|
||||
LifecycleRuleCount: lifecycleRuleCount,
|
||||
LifecycleEnabledCount: lifecycleEnabledCount,
|
||||
}
|
||||
buckets = append(buckets, bucket)
|
||||
}
|
||||
@@ -915,6 +921,7 @@ func (s *AdminServer) GetBucketDetails(bucketName string) (*BucketDetails, error
|
||||
details.Bucket.ObjectLockMode = objectLockMode
|
||||
details.Bucket.ObjectLockDuration = objectLockDuration
|
||||
details.Bucket.Owner = owner
|
||||
details.Bucket.LifecycleRuleCount, details.Bucket.LifecycleEnabledCount = extractLifecycleCountsFromEntry(bucketResp.Entry)
|
||||
|
||||
return nil
|
||||
})
|
||||
@@ -926,6 +933,68 @@ func (s *AdminServer) GetBucketDetails(bucketName string) (*BucketDetails, error
|
||||
return details, nil
|
||||
}
|
||||
|
||||
// GetBucketLifecycle returns the lifecycle configuration stored on a bucket's filer entry
|
||||
func (s *AdminServer) GetBucketLifecycle(bucketName string) (*BucketLifecycle, error) {
|
||||
filerConfig, err := s.getFilerConfig()
|
||||
if err != nil {
|
||||
glog.Warningf("Failed to get filer configuration, using defaults: %v", err)
|
||||
}
|
||||
|
||||
lifecycle := &BucketLifecycle{
|
||||
Bucket: bucketName,
|
||||
Rules: []BucketLifecycleRule{},
|
||||
}
|
||||
|
||||
err = s.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
||||
resp, err := client.LookupDirectoryEntry(context.Background(), &filer_pb.LookupDirectoryEntryRequest{
|
||||
Directory: filerConfig.BucketsPath,
|
||||
Name: bucketName,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("bucket not found: %w", err)
|
||||
}
|
||||
|
||||
xmlBytes := resp.Entry.Extended[scheduler.BucketLifecycleConfigurationXMLKey]
|
||||
if len(xmlBytes) == 0 {
|
||||
return nil
|
||||
}
|
||||
rules, err := lifecycle_xml.ParseCanonical(xmlBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse lifecycle configuration: %w", err)
|
||||
}
|
||||
lifecycle.XML = string(xmlBytes)
|
||||
for _, rule := range rules {
|
||||
lifecycle.Rules = append(lifecycle.Rules, toBucketLifecycleRule(rule))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return lifecycle, nil
|
||||
}
|
||||
|
||||
func toBucketLifecycleRule(rule *s3lifecycle.Rule) BucketLifecycleRule {
|
||||
out := BucketLifecycleRule{
|
||||
ID: rule.ID,
|
||||
Status: rule.Status,
|
||||
Prefix: rule.Prefix,
|
||||
Tags: rule.FilterTags,
|
||||
SizeGreaterThan: rule.FilterSizeGreaterThan,
|
||||
SizeLessThan: rule.FilterSizeLessThan,
|
||||
ExpirationDays: rule.ExpirationDays,
|
||||
ExpiredObjectDeleteMarker: rule.ExpiredObjectDeleteMarker,
|
||||
NoncurrentVersionExpirationDays: rule.NoncurrentVersionExpirationDays,
|
||||
NewerNoncurrentVersions: rule.NewerNoncurrentVersions,
|
||||
AbortMultipartDays: rule.AbortMPUDaysAfterInitiation,
|
||||
}
|
||||
if !rule.ExpirationDate.IsZero() {
|
||||
out.ExpirationDate = rule.ExpirationDate.Format(time.DateOnly)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// CreateS3Bucket creates a new S3 bucket
|
||||
func (s *AdminServer) CreateS3Bucket(bucketName string) error {
|
||||
return s.CreateS3BucketWithQuota(bucketName, 0, false)
|
||||
@@ -1812,6 +1881,24 @@ func extractVersioningFromEntry(entry *filer_pb.Entry) string {
|
||||
return s3api.GetVersioningStatus(entry)
|
||||
}
|
||||
|
||||
func extractLifecycleCountsFromEntry(entry *filer_pb.Entry) (ruleCount, enabledCount int) {
|
||||
xmlBytes := entry.Extended[scheduler.BucketLifecycleConfigurationXMLKey]
|
||||
if len(xmlBytes) == 0 {
|
||||
return
|
||||
}
|
||||
rules, err := lifecycle_xml.ParseCanonical(xmlBytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, rule := range rules {
|
||||
ruleCount++
|
||||
if rule.Status == s3lifecycle.StatusEnabled {
|
||||
enabledCount++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetConfigPersistence returns the config persistence manager
|
||||
func (as *AdminServer) GetConfigPersistence() *ConfigPersistence {
|
||||
return as.configPersistence
|
||||
|
||||
Reference in New Issue
Block a user