diff --git a/cmd/client-fs.go b/cmd/client-fs.go index 53a1063f7b..713bc38de2 100644 --- a/cmd/client-fs.go +++ b/cmd/client-fs.go @@ -409,13 +409,6 @@ func (f *fsClient) Put(ctx context.Context, reader io.Reader, size int64, metada return f.put(reader, size, nil, progress) } -func (f *fsClient) PutRetention(ctx context.Context, metadata map[string]string) *probe.Error { - return probe.NewError(APINotImplemented{ - API: "PutRetention", - APIType: "filesystem", - }) -} - // ShareDownload - share download not implemented for filesystem. func (f *fsClient) ShareDownload(expires time.Duration) (string, *probe.Error) { return "", probe.NewError(APINotImplemented{ @@ -1039,7 +1032,7 @@ func (f *fsClient) GetAccessRules() (map[string]string, *probe.Error) { } // Set object retention for a given object. -func (f *fsClient) PutObjectRetention(path string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit) *probe.Error { +func (f *fsClient) PutObjectRetention(mode *minio.RetentionMode, retainUntilDate *time.Time) *probe.Error { return probe.NewError(APINotImplemented{API: "PutObjectRetention", APIType: "filesystem"}) } diff --git a/cmd/client-s3.go b/cmd/client-s3.go index b841d0adc0..c8a2abcbdf 100644 --- a/cmd/client-s3.go +++ b/cmd/client-s3.go @@ -895,58 +895,6 @@ func (c *s3Client) Put(ctx context.Context, reader io.Reader, size int64, metada } return n, nil } -func (c *s3Client) PutRetention(ctx context.Context, metadata map[string]string) *probe.Error { - bucket, object := c.url2BucketAndObject() - - if bucket == "" { - return probe.NewError(BucketNameEmpty{}) - } - lockModeStr, ok := metadata[AmzObjectLockMode] - lockMode := minio.RetentionMode("") - if ok { - lockMode = minio.RetentionMode(lockModeStr) - delete(metadata, AmzObjectLockMode) - } - - retainUntilDateStr, ok := metadata[AmzObjectLockRetainUntilDate] - retainUntilDate := timeSentinel - if ok { - delete(metadata, AmzObjectLockRetainUntilDate) - if t, e := time.Parse(time.RFC3339, retainUntilDateStr); e == nil { - retainUntilDate = t.UTC() - } - } - opts := minio.PutObjectRetentionOptions{Mode: &lockMode, RetainUntilDate: &retainUntilDate, GovernanceBypass: false} - e := c.api.PutObjectRetention(bucket, object, opts) - if e != nil { - errResponse := minio.ToErrorResponse(e) - if errResponse.Code == "AccessDenied" { - return probe.NewError(PathInsufficientPermission{ - Path: c.targetURL.String(), - }) - } - if errResponse.Code == "MethodNotAllowed" { - return probe.NewError(ObjectAlreadyExists{ - Object: object, - }) - } - if errResponse.Code == "NoSuchBucket" { - return probe.NewError(BucketDoesNotExist{ - Bucket: bucket, - }) - } - if errResponse.Code == "InvalidBucketName" { - return probe.NewError(BucketInvalid{ - Bucket: bucket, - }) - } - if errResponse.Code == "NoSuchKey" { - return probe.NewError(ObjectMissing{}) - } - return probe.NewError(e) - } - return nil -} // Remove incomplete uploads. func (c *s3Client) removeIncompleteObjects(bucket string, objectsCh <-chan string) <-chan minio.RemoveObjectError { @@ -2076,24 +2024,11 @@ func (c *s3Client) SetObjectLockConfig(mode *minio.RetentionMode, validity *uint } // Set object retention for a given object. -func (c *s3Client) PutObjectRetention(path string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit) *probe.Error { - bucket, object := c.splitPath(path) - t := UTCNow() - if *unit == minio.Years { - t = t.AddDate(int(*validity), 0, 0) - } else { - t = t.AddDate(0, 0, int(*validity)) - } - timeStr := t.Format(time.RFC3339) +func (c *s3Client) PutObjectRetention(mode *minio.RetentionMode, retainUntilDate *time.Time) *probe.Error { + bucket, object := c.url2BucketAndObject() - t1, e := time.Parse( - time.RFC3339, - timeStr) - if e != nil { - return probe.NewError(e) - } opts := minio.PutObjectRetentionOptions{ - RetainUntilDate: &t1, + RetainUntilDate: retainUntilDate, Mode: mode, } err := c.api.PutObjectRetention(bucket, object, opts) diff --git a/cmd/client.go b/cmd/client.go index 2edf6da35f..98dd6b28d5 100644 --- a/cmd/client.go +++ b/cmd/client.go @@ -67,10 +67,8 @@ type Client interface { // I/O operations with metadata. 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) - PutRetention(ctx context.Context, metadata map[string]string) *probe.Error - // Object Locking related API - PutObjectRetention(path string, mode *minio.RetentionMode, validity *uint, unit *minio.ValidityUnit) *probe.Error + PutObjectRetention(mode *minio.RetentionMode, retainUntilDate *time.Time) *probe.Error // I/O operations with expiration ShareDownload(expires time.Duration) (string, *probe.Error) diff --git a/cmd/common-methods.go b/cmd/common-methods.go index 473417f392..89d2af9e48 100644 --- a/cmd/common-methods.go +++ b/cmd/common-methods.go @@ -26,12 +26,14 @@ import ( "path/filepath" "regexp" "strings" + "time" "golang.org/x/net/http/httpguts" "gopkg.in/h2non/filetype.v1" "github.com/minio/cli" "github.com/minio/mc/pkg/probe" + minio "github.com/minio/minio-go/v6" "github.com/minio/minio-go/v6/pkg/encrypt" ) @@ -217,7 +219,22 @@ func putTargetRetention(ctx context.Context, alias string, urlStr string, metada if err != nil { return err.Trace(alias, urlStr) } - if err := targetClnt.PutRetention(ctx, metadata); err != nil { + lockModeStr, ok := metadata[AmzObjectLockMode] + lockMode := minio.RetentionMode("") + if ok { + lockMode = minio.RetentionMode(lockModeStr) + delete(metadata, AmzObjectLockMode) + } + + retainUntilDateStr, ok := metadata[AmzObjectLockRetainUntilDate] + retainUntilDate := timeSentinel + if ok { + delete(metadata, AmzObjectLockRetainUntilDate) + if t, e := time.Parse(time.RFC3339, retainUntilDateStr); e == nil { + retainUntilDate = t.UTC() + } + } + if err := targetClnt.PutObjectRetention(&lockMode, &retainUntilDate); err != nil { return err.Trace(alias, urlStr) } return nil diff --git a/cmd/mirror-main.go b/cmd/mirror-main.go index 52e34f0a5c..f8155e4ed8 100644 --- a/cmd/mirror-main.go +++ b/cmd/mirror-main.go @@ -394,6 +394,7 @@ func (mj *mirrorJob) watchMirror(ctx context.Context, cancelMirror context.Cance // newClient needs the unexpanded path, newCLientURL needs the expanded path targetAlias, expandedTargetPath, _ := mustExpandAlias(targetPath) targetURL := newClientURL(expandedTargetPath) + sourcePath := filepath.ToSlash(filepath.Join(sourceAlias, sourceURL.Path)) srcSSE := getSSE(sourcePath, mj.encKeyDB[sourceAlias]) tgtSSE := getSSE(targetPath, mj.encKeyDB[targetAlias]) diff --git a/cmd/retention-main.go b/cmd/retention-main.go index 76c07a386b..4b695da056 100644 --- a/cmd/retention-main.go +++ b/cmd/retention-main.go @@ -22,6 +22,7 @@ import ( "fmt" "strconv" "strings" + "time" "github.com/fatih/color" "github.com/minio/cli" @@ -84,7 +85,27 @@ func setRetention(urlStr string, mode *minio.RetentionMode, validity *uint, unit if err != nil { fatalIf(err.Trace(), "Cannot parse the provided url.") } + alias, _, _ := mustExpandAlias(urlStr) + retainUntilDate := func() (time.Time, error) { + if validity == nil { + return timeSentinel, fmt.Errorf("invalid validity '%v'", validity) + } + t := UTCNow() + if *unit == minio.Years { + t = t.AddDate(int(*validity), 0, 0) + } else { + t = t.AddDate(0, 0, int(*validity)) + } + timeStr := t.Format(time.RFC3339) + t1, e := time.Parse( + time.RFC3339, + timeStr) + if e != nil { + return timeSentinel, e + } + return t1, nil + } validityStr := func() *string { if validity == nil { return nil @@ -106,7 +127,17 @@ func setRetention(urlStr string, mode *minio.RetentionMode, validity *uint, unit cErr = exitStatus(globalErrorExitStatus) // Set the exit status. continue } - probeErr := clnt.PutObjectRetention(content.URL.Path, mode, validity, unit) + retainUntil, err := retainUntilDate() + if err != nil { + errorIf(content.Err.Trace(clnt.GetURL().String()), "Invalid retention date") + continue + } + newClnt, perr := newClientFromAlias(alias, content.URL.String()) + if perr != nil { + errorIf(content.Err.Trace(clnt.GetURL().String()), "Invalid URL") + continue + } + probeErr := newClnt.PutObjectRetention(mode, &retainUntil) if probeErr != nil { errorsFound = true printMsg(retentionCmdMessage{