Skip to content

Commit

Permalink
Added bucket details inside objects listing (#1502)
Browse files Browse the repository at this point in the history
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
  • Loading branch information
bexsoft authored Feb 2, 2022
1 parent 02acb76 commit 96b1d4f
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
decodeFileName,
encodeFileName,
niceBytes,
niceBytesInt,
} from "../../../../../../common/utils";

import {
Expand Down Expand Up @@ -1094,27 +1095,6 @@ const ListObjects = ({
uploadPath = uploadPath.concat(currentPath);
}

// TODO: Add bucket information panel
/*
*
* subTitle={
<Fragment>
<Grid item xs={12} className={classes.bucketDetails}>
<span className={classes.detailsSpacer}>
Created:&nbsp;&nbsp;&nbsp;<strong></strong>
</span>
<span className={classes.detailsSpacer}>
Access:&nbsp;&nbsp;&nbsp;<strong></strong>
</span>
<span className={classes.detailsSpacer}>
SIZE / TOTAL OBJECTS
</span>
</Grid>
</Fragment>
}
*
* */

return (
<React.Fragment>
{shareFileModalOpen && selectedPreview && (
Expand Down Expand Up @@ -1178,6 +1158,38 @@ const ListObjects = ({
fullSizeBreadcrumbs
/>
}
subTitle={
<Fragment>
<Grid item xs={12} className={classes.bucketDetails}>
<span className={classes.detailsSpacer}>
Created:&nbsp;&nbsp;&nbsp;
<strong>{bucketInfo?.creation_date || ""}</strong>
</span>
<span className={classes.detailsSpacer}>
Access:&nbsp;&nbsp;&nbsp;
<strong>{bucketInfo?.access || ""}</strong>
</span>
{bucketInfo && (
<Fragment>
<span className={classes.detailsSpacer}>
{bucketInfo.size && (
<Fragment>{niceBytesInt(bucketInfo.size)}</Fragment>
)}
{bucketInfo.size && bucketInfo.objects ? " / " : ""}
{bucketInfo.objects && (
<Fragment>
{bucketInfo.objects}&nbsp;Object
{bucketInfo.objects && bucketInfo.objects !== 1
? "s"
: ""}
</Fragment>
)}
</span>
</Fragment>
)}
</Grid>
</Fragment>
}
actions={
<Fragment>
<input
Expand Down
3 changes: 3 additions & 0 deletions portal-ui/src/screens/Console/Buckets/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ export interface BucketInfo {
name: string;
access: string;
definition: string;
creation_date?: string;
objects?: number;
size?: number;
}

export interface BucketList {
Expand Down
43 changes: 38 additions & 5 deletions restapi/user_buckets.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"strings"
"time"

"github.com/minio/madmin-go"
"github.com/minio/mc/cmd"
"github.com/minio/mc/pkg/probe"
"github.com/minio/minio-go/v7"
Expand Down Expand Up @@ -561,11 +562,19 @@ func getBucketSetPolicyResponse(session *models.Principal, bucketName string, re
// defining the client to be used
minioClient := minioClient{client: mClient}

mAdmin, err := NewMinioAdminClient(session)
if err != nil {
return nil, prepareError(err)
}
// create a minioClient interface implementation
// defining the client to be used
adminClient := AdminClient{Client: mAdmin}

if err := setBucketAccessPolicy(ctx, minioClient, bucketName, *req.Access, req.Definition); err != nil {
return nil, prepareError(err)
}
// set bucket access policy
bucket, err := getBucketInfo(ctx, minioClient, bucketName)
bucket, err := getBucketInfo(ctx, minioClient, adminClient, bucketName)
if err != nil {
return nil, prepareError(err)
}
Expand Down Expand Up @@ -624,7 +633,7 @@ func getDeleteBucketResponse(session *models.Principal, params user_api.DeleteBu
}

// getBucketInfo return bucket information including name, policy access, size and creation date
func getBucketInfo(ctx context.Context, client MinioClient, bucketName string) (*models.Bucket, error) {
func getBucketInfo(ctx context.Context, client MinioClient, adminClient MinioAdmin, bucketName string) (*models.Bucket, error) {
var bucketAccess models.BucketAccess
policyStr, err := client.getBucketPolicy(context.Background(), bucketName)
if err != nil {
Expand Down Expand Up @@ -655,13 +664,28 @@ func getBucketInfo(ctx context.Context, client MinioClient, bucketName string) (
if bucketTags != nil {
bucketDetails.Tags = bucketTags.ToMap()
}

info, err := adminClient.AccountInfo(ctx)
if err != nil {
return nil, err
}

var bucketInfo madmin.BucketAccessInfo

for _, bucket := range info.Buckets {
if bucket.Name == bucketName {
bucketInfo = bucket
}
}

return &models.Bucket{
Name: &bucketName,
Access: &bucketAccess,
Definition: policyStr,
CreationDate: "", // to be implemented
Size: 0, // to be implemented
CreationDate: bucketInfo.Created.Format(time.RFC3339),
Size: int64(bucketInfo.Size),
Details: bucketDetails,
Objects: int64(bucketInfo.Objects),
}, nil
}

Expand All @@ -676,7 +700,16 @@ func getBucketInfoResponse(session *models.Principal, params user_api.BucketInfo
// create a minioClient interface implementation
// defining the client to be used
minioClient := minioClient{client: mClient}
bucket, err := getBucketInfo(ctx, minioClient, params.Name)

mAdmin, err := NewMinioAdminClient(session)
if err != nil {
return nil, prepareError(err)
}
// create a minioClient interface implementation
// defining the client to be used
adminClient := AdminClient{Client: mAdmin}

bucket, err := getBucketInfo(ctx, minioClient, adminClient, params.Name)
if err != nil {
return nil, prepareError(err)
}
Expand Down
31 changes: 19 additions & 12 deletions restapi/user_buckets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ func TestBucketInfo(t *testing.T) {
assert := assert.New(t)
// mock minIO client
minClient := minioClientMock{}
adminClient := adminClientMock{}
ctx := context.Background()
function := "getBucketInfo()"

Expand All @@ -269,8 +270,9 @@ func TestBucketInfo(t *testing.T) {
outputExpected := &models.Bucket{
Name: swag.String(bucketToSet),
Access: models.NewBucketAccess(models.BucketAccessPRIVATE),
CreationDate: "", // to be implemented
Size: 0, // to be implemented
CreationDate: "0001-01-01T00:00:00Z",
Size: 0,
Objects: 0,
}
infoPolicy := `
{
Expand Down Expand Up @@ -307,14 +309,15 @@ func TestBucketInfo(t *testing.T) {
return mockBucketList, nil
}

bucketInfo, err := getBucketInfo(ctx, minClient, bucketToSet)
bucketInfo, err := getBucketInfo(ctx, minClient, adminClient, bucketToSet)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
assert.Equal(outputExpected.Name, bucketInfo.Name)
assert.Equal(outputExpected.Access, bucketInfo.Access)
assert.Equal(outputExpected.CreationDate, bucketInfo.CreationDate)
assert.Equal(outputExpected.Size, bucketInfo.Size)
assert.Equal(outputExpected.Objects, bucketInfo.Objects)

// Test-2: getBucketInfo() get a bucket with PUBLIC access
// mock policy for bucket csbucket with readWrite access (should return PUBLIC)
Expand All @@ -326,17 +329,19 @@ func TestBucketInfo(t *testing.T) {
outputExpected = &models.Bucket{
Name: swag.String(bucketToSet),
Access: models.NewBucketAccess(models.BucketAccessPUBLIC),
CreationDate: "", // to be implemented
Size: 0, // to be implemented
CreationDate: "0001-01-01T00:00:00Z",
Size: 0,
Objects: 0,
}
bucketInfo, err = getBucketInfo(ctx, minClient, bucketToSet)
bucketInfo, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
assert.Equal(outputExpected.Name, bucketInfo.Name)
assert.Equal(outputExpected.Access, bucketInfo.Access)
assert.Equal(outputExpected.CreationDate, bucketInfo.CreationDate)
assert.Equal(outputExpected.Size, bucketInfo.Size)
assert.Equal(outputExpected.Objects, bucketInfo.Objects)

// Test-3: getBucketInfo() get a bucket with PRIVATE access
// if bucket has a null statement, the the bucket is PRIVATE
Expand All @@ -348,17 +353,19 @@ func TestBucketInfo(t *testing.T) {
outputExpected = &models.Bucket{
Name: swag.String(bucketToSet),
Access: models.NewBucketAccess(models.BucketAccessPRIVATE),
CreationDate: "", // to be implemented
Size: 0, // to be implemented
CreationDate: "0001-01-01T00:00:00Z",
Size: 0,
Objects: 0,
}
bucketInfo, err = getBucketInfo(ctx, minClient, bucketToSet)
bucketInfo, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
assert.Equal(outputExpected.Name, bucketInfo.Name)
assert.Equal(outputExpected.Access, bucketInfo.Access)
assert.Equal(outputExpected.CreationDate, bucketInfo.CreationDate)
assert.Equal(outputExpected.Size, bucketInfo.Size)
assert.Equal(outputExpected.Objects, bucketInfo.Objects)

// Test-4: getBucketInfo() returns an error while parsing invalid policy
mockPolicy = "policyinvalid"
Expand All @@ -369,10 +376,10 @@ func TestBucketInfo(t *testing.T) {
outputExpected = &models.Bucket{
Name: swag.String(bucketToSet),
Access: models.NewBucketAccess(models.BucketAccessCUSTOM),
CreationDate: "", // to be implemented
Size: 0, // to be implemented
CreationDate: "",
Size: 0,
}
_, err = getBucketInfo(ctx, minClient, bucketToSet)
_, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet)
if assert.Error(err) {
assert.Equal("invalid character 'p' looking for beginning of value", err.Error())
}
Expand Down

0 comments on commit 96b1d4f

Please sign in to comment.