diff --git a/cmd/api-response.go b/cmd/api-response.go index dd329a3b4..d7d631201 100644 --- a/cmd/api-response.go +++ b/cmd/api-response.go @@ -240,6 +240,16 @@ type CommonPrefix struct { type Bucket struct { Name string CreationDate string // time string of format "2006-01-02T15:04:05.000Z" + + // Usage size of the bucket not reflective of + // actual usage atomically, but an ever increasing + // value. + Usage *BucketUsageInfo `xml:"Usage,omitempty"` + + // Provides information about various bucket features + // enabled such as versioning, object locking, tagging + // quota, replication config etc. + Details *BucketDetailsInfo `xml:"Details,omitempty"` } // ObjectVersion container for object version metadata @@ -263,7 +273,7 @@ func (o ObjectVersion) MarshalXML(e *xml.Encoder, start xml.StartElement) error return e.EncodeElement(objectVersionWrapper(o), start) } -// StringMap is a map[string]string. +// StringMap is a map[string]string type StringMap map[string]string // MarshalXML - StringMap marshals into XML. @@ -424,10 +434,12 @@ func generateListBucketsResponse(buckets []BucketInfo) ListBucketsResponse { } for _, bucket := range buckets { - var listbucket = Bucket{} - listbucket.Name = bucket.Name - listbucket.CreationDate = bucket.Created.UTC().Format(iso8601TimeFormat) - listbuckets = append(listbuckets, listbucket) + listbuckets = append(listbuckets, Bucket{ + Name: bucket.Name, + CreationDate: bucket.Created.UTC().Format(iso8601TimeFormat), + Usage: bucket.Usage, + Details: bucket.Details, + }) } data.Owner = owner diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index b0ef77c6f..922c7d7d4 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -306,6 +306,8 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R return } + metadata := r.Form.Get("metadata") == "true" + // If etcd, dns federation configured list buckets from etcd. var bucketsInfo []BucketInfo if globalDNSConfig != nil && globalBucketFederation { @@ -370,6 +372,49 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R } } + if metadata && !globalIsGateway { + usageInfo, err := loadDataUsageFromBackend(ctx, objectAPI) + if err != nil { + writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) + return + } + for i, bucket := range bucketsInfo { + if bu, ok := usageInfo.BucketsUsage[bucket.Name]; ok { + bucketsInfo[i].Usage = &BucketUsageInfo{ + Size: bu.Size, + ObjectsCount: bu.ObjectsCount, + ObjectSizesHistogram: StringMap{}, + } + for k, v := range bu.ObjectSizesHistogram { + bucketsInfo[i].Usage.ObjectSizesHistogram[k] = fmt.Sprint(v) + } + } else { + bucketsInfo[i].Usage = &BucketUsageInfo{ + ObjectSizesHistogram: StringMap{}, + } + } + lcfg, _ := globalBucketObjectLockSys.Get(bucket.Name) + quota, _ := globalBucketQuotaSys.Get(bucket.Name) + var bquota *BucketQuotaConfig + if quota != nil { + bquota = &BucketQuotaConfig{ + Quota: quota.Quota, + Type: quota.Type, + } + } + rcfg, _ := globalBucketMetadataSys.GetReplicationConfig(ctx, bucket.Name) + tcfg, _ := globalBucketMetadataSys.GetTaggingConfig(bucket.Name) + bucketsInfo[i].Details = &BucketDetailsInfo{ + Versioning: globalBucketVersioningSys.Enabled(bucket.Name), + VersioningSuspended: globalBucketVersioningSys.Suspended(bucket.Name), + Replication: rcfg != nil, + Locking: lcfg.LockEnabled, + Quota: bquota, + Tagging: tcfg, + } + } + } + // Generate response. response := generateListBucketsResponse(bucketsInfo) encodedSuccessResponse := encodeResponse(response) diff --git a/cmd/erasure-bucket.go b/cmd/erasure-bucket.go index b198d5dcb..e49fe1b60 100644 --- a/cmd/erasure-bucket.go +++ b/cmd/erasure-bucket.go @@ -104,7 +104,10 @@ func (er erasureObjects) getBucketInfo(ctx context.Context, bucketName string) ( if err != nil { return err } - bucketsInfo[index] = BucketInfo(volInfo) + bucketsInfo[index] = BucketInfo{ + Name: volInfo.Name, + Created: volInfo.Created, + } return nil }, index) } diff --git a/cmd/erasure-sets.go b/cmd/erasure-sets.go index 25c6b3eef..55910b4d1 100644 --- a/cmd/erasure-sets.go +++ b/cmd/erasure-sets.go @@ -859,7 +859,10 @@ func (s *erasureSets) ListBuckets(ctx context.Context) (buckets []BucketInfo, er } for _, v := range healBuckets { - listBuckets = append(listBuckets, BucketInfo(v)) + listBuckets = append(listBuckets, BucketInfo{ + Name: v.Name, + Created: v.Created, + }) } sort.Slice(listBuckets, func(i, j int) bool { diff --git a/cmd/object-api-datatypes.go b/cmd/object-api-datatypes.go index 8aef0c8b9..a918b5662 100644 --- a/cmd/object-api-datatypes.go +++ b/cmd/object-api-datatypes.go @@ -24,6 +24,7 @@ import ( humanize "github.com/dustin/go-humanize" "github.com/minio/madmin-go" + "github.com/minio/minio-go/v7/pkg/tags" "github.com/minio/minio/internal/bucket/replication" "github.com/minio/minio/internal/hash" ) @@ -70,6 +71,30 @@ var ObjectsHistogramIntervals = []objectHistogramInterval{ {"GREATER_THAN_512_MB", humanize.MiByte * 512, math.MaxInt64}, } +// BucketUsageInfo represents per bucket usage statistics +type BucketUsageInfo struct { + Size uint64 + ObjectsCount uint64 + ObjectSizesHistogram StringMap +} + +// BucketQuotaConfig holds bucket quota restrictions +type BucketQuotaConfig struct { + Quota uint64 + Type madmin.QuotaType +} + +// BucketDetailsInfo provides information about features currently +// turned-on per bucket. +type BucketDetailsInfo struct { + Versioning bool + VersioningSuspended bool + Locking bool + Replication bool + Tagging *tags.Tags `xml:",omitempty"` + Quota *BucketQuotaConfig `xml:",omitempty"` +} + // BucketInfo - represents bucket metadata. type BucketInfo struct { // Name of the bucket. @@ -77,6 +102,16 @@ type BucketInfo struct { // Date and time when the bucket was created. Created time.Time + + // Usage size of the bucket not reflective of + // actual usage atomically, but an ever increasing + // value. + Usage *BucketUsageInfo `xml:",omitempty"` + + // Provides information about various bucket features + // enabled such as versioning, object locking, tagging + // quota, replication config etc. + Details *BucketDetailsInfo `xml:",omitempty"` } // ObjectInfo - represents object metadata.