Skip to content

Commit

Permalink
mc tag command
Browse files Browse the repository at this point in the history
  • Loading branch information
BigUstad committed Mar 20, 2020
2 parents 49d4bd6 + b010699 commit b3a1043
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 151 deletions.
2 changes: 1 addition & 1 deletion cmd/client-fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,7 @@ func (f *fsClient) GetAccessRules() (map[string]string, *probe.Error) {
}

// Set object retention for a given object.
func (f *fsClient) PutObjectRetention(mode *minio.RetentionMode, retainUntilDate *time.Time) *probe.Error {
func (f *fsClient) PutObjectRetention(mode *minio.RetentionMode, retainUntilDate *time.Time, bypassGovernance bool) *probe.Error {
return probe.NewError(APINotImplemented{
API: "PutObjectRetention",
APIType: "filesystem",
Expand Down
54 changes: 14 additions & 40 deletions cmd/client-s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ type s3Client struct {

const (
amazonHostNameAccelerated = "s3-accelerate.amazonaws.com"

googleHostName = "storage.googleapis.com"
serverEncryptionKeyPrefix = "x-amz-server-side-encryption"

Expand All @@ -80,13 +79,6 @@ const (
AmzObjectLockRetainUntilDate = "X-Amz-Object-Lock-Retain-Until-Date"
)

// cseHeaders is list of client side encryption headers
var cseHeaders = []string{
"X-Amz-Meta-X-Amz-Iv",
"X-Amz-Meta-X-Amz-Key",
"X-Amz-Meta-X-Amz-Matdesc",
}

var timeSentinel = time.Unix(0, 0).UTC()

// newFactory encloses New function with client cache.
Expand Down Expand Up @@ -827,6 +819,10 @@ func (c *s3Client) Copy(source string, size int64, progress io.Reader, srcSSE, t
// Put - upload an object with custom metadata.
func (c *s3Client) Put(ctx context.Context, reader io.Reader, size int64, metadata map[string]string, progress io.Reader, sse encrypt.ServerSide) (int64, *probe.Error) {
bucket, object := c.url2BucketAndObject()
if bucket == "" {
return 0, probe.NewError(BucketNameEmpty{})
}

contentType, ok := metadata["Content-Type"]
if ok {
delete(metadata, "Content-Type")
Expand Down Expand Up @@ -875,10 +871,6 @@ func (c *s3Client) Put(ctx context.Context, reader io.Reader, size int64, metada
retainUntilDate = t.UTC()
}
}

if bucket == "" {
return 0, probe.NewError(BucketNameEmpty{})
}
opts := minio.PutObjectOptions{
UserMetadata: metadata,
Progress: progress,
Expand Down Expand Up @@ -1271,16 +1263,13 @@ func (c *s3Client) Stat(isIncomplete, isFetchMeta, isPreserve bool, sse encrypt.
objectMetadata.Size = objectMultipartInfo.Size
objectMetadata.Type = os.FileMode(0664)
objectMetadata.Metadata = map[string]string{}
objectMetadata.EncryptionHeaders = map[string]string{}
return objectMetadata, nil
}

if strings.HasSuffix(objectMultipartInfo.Key, string(c.targetURL.Separator)) {
objectMetadata.URL = *c.targetURL
objectMetadata.Type = os.ModeDir
objectMetadata.Metadata = map[string]string{}
objectMetadata.EncryptionHeaders = map[string]string{}

return objectMetadata, nil
}
}
Expand All @@ -1304,7 +1293,6 @@ func (c *s3Client) Stat(isIncomplete, isFetchMeta, isPreserve bool, sse encrypt.
}
objectMetadata.ETag = stat.ETag
objectMetadata.Metadata = stat.Metadata
objectMetadata.EncryptionHeaders = stat.EncryptionHeaders
objectMetadata.Expires = stat.Expires
}
return objectMetadata, nil
Expand All @@ -1317,14 +1305,12 @@ func (c *s3Client) Stat(isIncomplete, isFetchMeta, isPreserve bool, sse encrypt.
objectMetadata.Type = os.FileMode(0664)
objectMetadata.Metadata = map[string]string{}
objectMetadata.Expires = objectStat.Expires
objectMetadata.EncryptionHeaders = map[string]string{}
if isFetchMeta {
stat, err := c.getObjectStat(bucket, object, opts)
if err != nil {
return nil, err
}
objectMetadata.Metadata = stat.Metadata
objectMetadata.EncryptionHeaders = stat.EncryptionHeaders
objectMetadata.Expires = stat.Expires
}
return objectMetadata, nil
Expand Down Expand Up @@ -1364,25 +1350,8 @@ func (c *s3Client) getObjectStat(bucket, object string, opts minio.StatObjectOpt
objectMetadata.Expires = objectStat.Expires
objectMetadata.Type = os.FileMode(0664)
objectMetadata.Metadata = map[string]string{}
objectMetadata.EncryptionHeaders = map[string]string{}
objectMetadata.Metadata["Content-Type"] = objectStat.ContentType
for k, v := range objectStat.Metadata {
isCSEHeader := false
for _, header := range cseHeaders {
if (strings.Compare(strings.ToLower(header), strings.ToLower(k)) == 0) ||
strings.HasPrefix(strings.ToLower(serverEncryptionKeyPrefix), strings.ToLower(k)) {
if len(v) > 0 {
objectMetadata.EncryptionHeaders[k] = v[0]
}
isCSEHeader = true
break
}
}
if !isCSEHeader {
if len(v) > 0 {
objectMetadata.Metadata[k] = v[0]
}
}
for k := range objectStat.Metadata {
objectMetadata.Metadata[k] = objectStat.Metadata.Get(k)
}
objectMetadata.ETag = objectStat.ETag
return objectMetadata, nil
Expand Down Expand Up @@ -1767,10 +1736,14 @@ func (c *s3Client) objectInfo2ClientContent(bucket string, entry minio.ObjectInf
content.ETag = entry.ETag
content.Time = entry.LastModified
content.Expires = entry.Expires
content.Metadata = map[string]string{}
content.UserMetadata = map[string]string{}
for k, v := range entry.UserMetadata {
content.UserMetadata[k] = v
}
for k := range entry.Metadata {
content.Metadata[k] = entry.Metadata.Get(k)
}
if strings.HasSuffix(entry.Key, string(c.targetURL.Separator)) && entry.Size == 0 && entry.LastModified.IsZero() {
content.Type = os.ModeDir
content.Time = time.Now()
Expand Down Expand Up @@ -2076,12 +2049,13 @@ func (c *s3Client) SetObjectLockConfig(mode *minio.RetentionMode, validity *uint
}

// Set object retention for a given object.
func (c *s3Client) PutObjectRetention(mode *minio.RetentionMode, retainUntilDate *time.Time) *probe.Error {
func (c *s3Client) PutObjectRetention(mode *minio.RetentionMode, retainUntilDate *time.Time, bypassGovernance bool) *probe.Error {
bucket, object := c.url2BucketAndObject()

opts := minio.PutObjectRetentionOptions{
RetainUntilDate: retainUntilDate,
Mode: mode,
RetainUntilDate: retainUntilDate,
Mode: mode,
GovernanceBypass: bypassGovernance,
}
err := c.api.PutObjectRetention(bucket, object, opts)
if err != nil {
Expand Down
25 changes: 12 additions & 13 deletions cmd/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ type Client interface {
Get(sse encrypt.ServerSide) (reader io.ReadCloser, err *probe.Error)
Put(ctx context.Context, reader io.Reader, size int64, metadata map[string]string, progress io.Reader, sse encrypt.ServerSide) (n int64, err *probe.Error)
// Object Locking related API
PutObjectRetention(mode *minio.RetentionMode, retainUntilDate *time.Time) *probe.Error
PutObjectRetention(mode *minio.RetentionMode, retainUntilDate *time.Time, bypassGovernance bool) *probe.Error

// I/O operations with expiration
ShareDownload(expires time.Duration) (string, *probe.Error)
Expand All @@ -94,18 +94,17 @@ type Client interface {

// Content container for content metadata
type clientContent struct {
URL clientURL
Time time.Time
Size int64
Type os.FileMode
StorageClass string
Metadata map[string]string
UserMetadata map[string]string
ETag string
Expires time.Time
EncryptionHeaders map[string]string
Retention bool
Err *probe.Error
URL clientURL
Time time.Time
Size int64
Type os.FileMode
StorageClass string
Metadata map[string]string
UserMetadata map[string]string
ETag string
Expires time.Time
Retention bool
Err *probe.Error
}

// Config - see http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html
Expand Down
4 changes: 2 additions & 2 deletions cmd/common-methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func putTargetRetention(ctx context.Context, alias string, urlStr string, metada
retainUntilDate = t.UTC()
}
}
if err := targetClnt.PutObjectRetention(&lockMode, &retainUntilDate); err != nil {
if err := targetClnt.PutObjectRetention(&lockMode, &retainUntilDate, false); err != nil {
return err.Trace(alias, urlStr)
}
return nil
Expand Down Expand Up @@ -287,7 +287,7 @@ func filterMetadata(metadata map[string]string) map[string]string {
}
}
for k := range metadata {
if strings.HasPrefix(http.CanonicalHeaderKey(k), "X-Amz-Server-Side-Encryption-") {
if strings.HasPrefix(http.CanonicalHeaderKey(k), http.CanonicalHeaderKey(serverEncryptionKeyPrefix)) {
delete(newMetadata, k)
}
}
Expand Down
35 changes: 26 additions & 9 deletions cmd/retention-main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,24 @@ import (
"github.com/minio/minio/pkg/console"
)

var (
rFlags = []cli.Flag{
cli.BoolFlag{
Name: "recursive, r",
Usage: "apply retention recursively",
},
cli.BoolFlag{
Name: "bypass",
Usage: "bypass governance",
},
}
)
var retentionCmd = cli.Command{
Name: "retention",
Usage: "set object retention for objects with a given prefix",
Action: mainRetention,
Before: setGlobalsFromContext,
Flags: globalFlags,
Flags: append(rFlags, globalFlags...),
CustomHelpTemplate: `NAME:
{{.HelpName}} - {{.Usage}}
Expand All @@ -50,9 +62,13 @@ VALIDITY:
This argument must be formatted like Nd or Ny where 'd' denotes days and 'y' denotes years e.g. 10d, 3y.
EXAMPLES:
1. Set object retention for objects in a given prefix
$ {{.HelpName}} myminio/mybucket/prefix compliance 30d
`,
1. Set object retention for a specific object
$ {{.HelpName}} myminio/mybucket/prefix/obj.csv compliance 30d
2. Set object retention for objects in a given prefix
$ {{.HelpName}} myminio/mybucket/prefix compliance 30d --recursive
`,
}

// Structured message depending on the type of console.
Expand Down Expand Up @@ -80,7 +96,7 @@ func (m retentionCmdMessage) JSON() string {
}

// setRetention - Set Retention for all objects within a given prefix.
func setRetention(urlStr string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit) error {
func setRetention(urlStr string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit, bypassGovernance, isRecursive bool) error {
clnt, err := newClient(urlStr)
if err != nil {
fatalIf(err.Trace(), "Cannot parse the provided url.")
Expand All @@ -93,6 +109,7 @@ func setRetention(urlStr string, mode *minio.RetentionMode, validity *uint, unit
}

alias, _, _ := mustExpandAlias(urlStr)

retainUntilDate := func() (time.Time, error) {
if validity == nil {
return timeSentinel, fmt.Errorf("invalid validity '%v'", validity)
Expand Down Expand Up @@ -128,7 +145,7 @@ func setRetention(urlStr string, mode *minio.RetentionMode, validity *uint, unit

var cErr error
errorsFound := false
for content := range clnt.List(true, false, false, DirNone) {
for content := range clnt.List(isRecursive, false, false, DirNone) {
if content.Err != nil {
errorIf(content.Err.Trace(clnt.GetURL().String()), "Unable to list folder.")
cErr = exitStatus(globalErrorExitStatus) // Set the exit status.
Expand All @@ -144,7 +161,7 @@ func setRetention(urlStr string, mode *minio.RetentionMode, validity *uint, unit
errorIf(content.Err.Trace(clnt.GetURL().String()), "Invalid URL")
continue
}
probeErr := newClnt.PutObjectRetention(mode, &retainUntil)
probeErr := newClnt.PutObjectRetention(mode, &retainUntil, bypassGovernance)
if probeErr != nil {
errorsFound = true
printMsg(retentionCmdMessage{
Expand All @@ -169,7 +186,7 @@ func setRetention(urlStr string, mode *minio.RetentionMode, validity *uint, unit
if errorsFound {
console.Print(console.Colorize("RetentionPartialFailure", fmt.Sprintf("Errors found while setting retention on objects with prefix `%s`.\n", urlStr)))
} else {
console.Print(console.Colorize("RetentionSuccess", fmt.Sprintf("Object retention successfully set for prefix `%s`.\n", urlStr)))
console.Print(console.Colorize("RetentionSuccess", fmt.Sprintf("Object retention successfully set for `%s`.\n", urlStr)))
}
}
return cErr
Expand Down Expand Up @@ -221,5 +238,5 @@ func mainRetention(ctx *cli.Context) error {
default:
cli.ShowCommandHelpAndExit(ctx, "retention", 1)
}
return setRetention(urlStr, mode, validity, unit)
return setRetention(urlStr, mode, validity, unit, ctx.Bool("bypass"), ctx.Bool("recursive"))
}
2 changes: 0 additions & 2 deletions cmd/stat-main.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ func mainStat(ctx *cli.Context) error {
console.SetColor("Date", color.New(color.FgWhite))
console.SetColor("Size", color.New(color.FgWhite))
console.SetColor("ETag", color.New(color.FgWhite))

console.SetColor("EncryptionHeaders", color.New(color.FgWhite))
console.SetColor("Metadata", color.New(color.FgWhite))

// Parse encryption keys per command.
Expand Down
49 changes: 29 additions & 20 deletions cmd/stat.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,14 @@ import (

// contentMessage container for content message structure.
type statMessage struct {
Status string `json:"status"`
Key string `json:"name"`
Date time.Time `json:"lastModified"`
Size int64 `json:"size"`
ETag string `json:"etag"`
Type string `json:"type"`
Expires time.Time `json:"expires"`
EncryptionHeaders map[string]string `json:"encryption,omitempty"`
Metadata map[string]string `json:"metadata"`
Status string `json:"status"`
Key string `json:"name"`
Date time.Time `json:"lastModified"`
Size int64 `json:"size"`
ETag string `json:"etag"`
Type string `json:"type"`
Expires time.Time `json:"expires"`
Metadata map[string]string `json:"metadata"`
}

// String colorized string message.
Expand All @@ -57,26 +56,37 @@ func printStat(stat statMessage) {
}
var maxKey = 0
for k := range stat.Metadata {
if len(k) > maxKey {
maxKey = len(k)
// Skip encryption headers, we print them later.
if !strings.HasPrefix(strings.ToLower(k), serverEncryptionKeyPrefix) {
if len(k) > maxKey {
maxKey = len(k)
}
}
}
if len(stat.Metadata) > 0 {
if maxKey > 0 {
console.Println(fmt.Sprintf("%-10s:", "Metadata"))
for k, v := range stat.Metadata {
console.Println(fmt.Sprintf(" %-*.*s: %s ", maxKey, maxKey, k, v))
// Skip encryption headers, we print them later.
if !strings.HasPrefix(strings.ToLower(k), serverEncryptionKeyPrefix) {
console.Println(fmt.Sprintf(" %-*.*s: %s ", maxKey, maxKey, k, v))
}
}
}

maxKey = 0
for k := range stat.EncryptionHeaders {
if len(k) > maxKey {
maxKey = len(k)
for k := range stat.Metadata {
if strings.HasPrefix(strings.ToLower(k), serverEncryptionKeyPrefix) {
if len(k) > maxKey {
maxKey = len(k)
}
}
}
if len(stat.EncryptionHeaders) > 0 {
if maxKey > 0 {
console.Println(fmt.Sprintf("%-10s:", "Encrypted"))
for k, v := range stat.EncryptionHeaders {
console.Println(fmt.Sprintf(" %-*.*s: %s ", maxKey, maxKey, k, v))
for k, v := range stat.Metadata {
if strings.HasPrefix(strings.ToLower(k), serverEncryptionKeyPrefix) {
console.Println(fmt.Sprintf(" %-*.*s: %s ", maxKey, maxKey, k, v))
}
}
}
console.Println()
Expand Down Expand Up @@ -108,7 +118,6 @@ func parseStat(c *clientContent) statMessage {
content.ETag = strings.TrimPrefix(c.ETag, "\"")
content.ETag = strings.TrimSuffix(content.ETag, "\"")
content.Expires = c.Expires
content.EncryptionHeaders = c.EncryptionHeaders
return content
}

Expand Down
Loading

0 comments on commit b3a1043

Please sign in to comment.