From adcaa6f9de889ddf37ca0ef7d36707c0fcce36f6 Mon Sep 17 00:00:00 2001 From: poornas Date: Thu, 6 Aug 2020 17:10:21 -0700 Subject: [PATCH] fix: Change ListBucketTargets handler (#10217) to list all targets across a tenant. Also fixing some validations. --- cmd/admin-bucket-handlers.go | 50 +++++++-------- cmd/admin-router.go | 16 ++--- cmd/api-errors.go | 8 +++ cmd/bucket-replication.go | 6 +- cmd/bucket-targets.go | 89 +++++++++++++++++++++++---- cmd/object-api-errors.go | 7 +++ cmd/peer-rest-server.go | 4 ++ docs/bucket/replication/README.md | 14 ++--- pkg/bucket/replication/replication.go | 10 +-- pkg/madmin/remote-target-commands.go | 61 +++++++++++------- 10 files changed, 180 insertions(+), 85 deletions(-) diff --git a/cmd/admin-bucket-handlers.go b/cmd/admin-bucket-handlers.go index 660c8a389..e184f125d 100644 --- a/cmd/admin-bucket-handlers.go +++ b/cmd/admin-bucket-handlers.go @@ -26,7 +26,6 @@ import ( "github.com/gorilla/mux" "github.com/minio/minio/cmd/config" "github.com/minio/minio/cmd/logger" - "github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/env" iampolicy "github.com/minio/minio/pkg/iam/policy" "github.com/minio/minio/pkg/madmin" @@ -122,8 +121,8 @@ func (a adminAPIHandlers) GetBucketQuotaConfigHandler(w http.ResponseWriter, r * writeSuccessResponseJSON(w, configData) } -// SetBucketTargetHandler - sets a remote target for bucket -func (a adminAPIHandlers) SetBucketTargetHandler(w http.ResponseWriter, r *http.Request) { +// SetRemoteTargetHandler - sets a remote target for bucket +func (a adminAPIHandlers) SetRemoteTargetHandler(w http.ResponseWriter, r *http.Request) { ctx := newContext(r, w, "SetBucketTarget") defer logger.AuditLog(w, r, "SetBucketTarget", mustGetClaimsFromToken(r)) @@ -174,6 +173,7 @@ func (a adminAPIHandlers) SetBucketTargetHandler(w http.ResponseWriter, r *http. writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrBucketRemoteIdenticalToSource), r.URL) return } + target.SourceBucket = bucket target.Arn = globalBucketTargetSys.getRemoteARN(bucket, &target) if target.Arn == "" { writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) @@ -183,7 +183,7 @@ func (a adminAPIHandlers) SetBucketTargetHandler(w http.ResponseWriter, r *http. writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL) return } - targets, err := globalBucketTargetSys.ListTargets(ctx, bucket) + targets, err := globalBucketTargetSys.ListBucketTargets(ctx, bucket) if err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) return @@ -209,9 +209,9 @@ func (a adminAPIHandlers) SetBucketTargetHandler(w http.ResponseWriter, r *http. writeSuccessResponseJSON(w, data) } -// ListBucketTargetsHandler - lists remote target(s) for a bucket or gets a target +// ListRemoteTargetsHandler - lists remote target(s) for a bucket or gets a target // for a particular ARN type -func (a adminAPIHandlers) ListBucketTargetsHandler(w http.ResponseWriter, r *http.Request) { +func (a adminAPIHandlers) ListRemoteTargetsHandler(w http.ResponseWriter, r *http.Request) { ctx := newContext(r, w, "ListBucketTargets") defer logger.AuditLog(w, r, "ListBucketTargets", mustGetClaimsFromToken(r)) @@ -225,28 +225,13 @@ func (a adminAPIHandlers) ListBucketTargetsHandler(w http.ResponseWriter, r *htt writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) return } - - cfg, err := globalBucketMetadataSys.GetBucketTargetsConfig(bucket) - if err != nil { - writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) - return - } - var ( - targets []madmin.BucketTarget - tgt, ct madmin.BucketTarget - creds auth.Credentials - ) - if cfg != nil && !cfg.Empty() { - for idx, t := range cfg.Targets { - if string(t.Type) == arnType || arnType == "" { - ct = cfg.Targets[idx] - // remove secretKey from creds - creds.AccessKey = ct.Credentials.AccessKey - tgt = madmin.BucketTarget{Endpoint: ct.Endpoint, Secure: ct.Secure, TargetBucket: ct.TargetBucket, Credentials: &creds, Arn: ct.Arn, Type: ct.Type} - targets = append(targets, tgt) - } + if bucket != "" { + if _, err := globalBucketMetadataSys.GetBucketTargetsConfig(bucket); err != nil { + writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) + return } } + targets := globalBucketTargetSys.ListTargets(ctx, bucket, arnType) data, err := json.Marshal(targets) if err != nil { writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) @@ -256,8 +241,8 @@ func (a adminAPIHandlers) ListBucketTargetsHandler(w http.ResponseWriter, r *htt writeSuccessResponseJSON(w, data) } -// RemoveBucketTargetHandler - removes a remote target for bucket with specified ARN -func (a adminAPIHandlers) RemoveBucketTargetHandler(w http.ResponseWriter, r *http.Request) { +// RemoveRemoteTargetHandler - removes a remote target for bucket with specified ARN +func (a adminAPIHandlers) RemoveRemoteTargetHandler(w http.ResponseWriter, r *http.Request) { ctx := newContext(r, w, "RemoveBucketTarget") defer logger.AuditLog(w, r, "RemoveBucketTarget", mustGetClaimsFromToken(r)) @@ -275,11 +260,18 @@ func (a adminAPIHandlers) RemoveBucketTargetHandler(w http.ResponseWriter, r *ht writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL) return } + + // Check if bucket exists. + if _, err := objectAPI.GetBucketInfo(ctx, bucket); err != nil { + writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL) + return + } + if err := globalBucketTargetSys.RemoveTarget(ctx, bucket, arn); err != nil { writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL) return } - targets, err := globalBucketTargetSys.ListTargets(ctx, bucket) + targets, err := globalBucketTargetSys.ListBucketTargets(ctx, bucket) if err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) return diff --git a/cmd/admin-router.go b/cmd/admin-router.go index dd339b844..f6c72296e 100644 --- a/cmd/admin-router.go +++ b/cmd/admin-router.go @@ -183,14 +183,14 @@ func registerAdminRouter(router *mux.Router, enableConfigOps, enableIAMOps bool) } // Bucket replication operations // GetBucketTargetHandler - adminRouter.Methods(http.MethodGet).Path(adminVersion+"/list-bucket-targets").HandlerFunc( - httpTraceHdrs(adminAPI.ListBucketTargetsHandler)).Queries("bucket", "{bucket:.*}", "type", "{type:.*}") - // SetBucketTargetHandler - adminRouter.Methods(http.MethodPut).Path(adminVersion+"/set-bucket-target").HandlerFunc( - httpTraceHdrs(adminAPI.SetBucketTargetHandler)).Queries("bucket", "{bucket:.*}") - // SetBucketTargetHandler - adminRouter.Methods(http.MethodDelete).Path(adminVersion+"/remove-bucket-target").HandlerFunc( - httpTraceHdrs(adminAPI.RemoveBucketTargetHandler)).Queries("bucket", "{bucket:.*}", "arn", "{arn:.*}") + adminRouter.Methods(http.MethodGet).Path(adminVersion+"/list-remote-targets").HandlerFunc( + httpTraceHdrs(adminAPI.ListRemoteTargetsHandler)).Queries("bucket", "{bucket:.*}", "type", "{type:.*}") + // SetRemoteTargetHandler + adminRouter.Methods(http.MethodPut).Path(adminVersion+"/set-remote-target").HandlerFunc( + httpTraceHdrs(adminAPI.SetRemoteTargetHandler)).Queries("bucket", "{bucket:.*}") + // SetRemoteTargetHandler + adminRouter.Methods(http.MethodDelete).Path(adminVersion+"/remove-remote-target").HandlerFunc( + httpTraceHdrs(adminAPI.RemoveRemoteTargetHandler)).Queries("bucket", "{bucket:.*}", "arn", "{arn:.*}") } // -- Top APIs -- diff --git a/cmd/api-errors.go b/cmd/api-errors.go index c7d606943..509fa0160 100644 --- a/cmd/api-errors.go +++ b/cmd/api-errors.go @@ -115,6 +115,7 @@ const ( ErrBucketRemoteArnInvalid ErrBucketRemoteRemoveDisallowed ErrReplicationTargetNotVersionedError + ErrReplicationSourceNotVersionedError ErrReplicationNeedsVersioningError ErrReplicationBucketNeedsVersioningError ErrBucketReplicationDisabledError @@ -871,6 +872,11 @@ var errorCodes = errorCodeMap{ Description: "The replication target does not have versioning enabled", HTTPStatusCode: http.StatusBadRequest, }, + ErrReplicationSourceNotVersionedError: { + Code: "ReplicationSourceNotVersionedError", + Description: "The replication source does not have versioning enabled", + HTTPStatusCode: http.StatusBadRequest, + }, ErrReplicationNeedsVersioningError: { Code: "InvalidRequest", Description: "Versioning must be 'Enabled' on the bucket to apply a replication configuration", @@ -1929,6 +1935,8 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) { apiErr = ErrBucketRemoteRemoveDisallowed case BucketReplicationTargetNotVersioned: apiErr = ErrReplicationTargetNotVersionedError + case BucketReplicationSourceNotVersioned: + apiErr = ErrReplicationSourceNotVersionedError case BucketQuotaExceeded: apiErr = ErrAdminBucketQuotaExceeded case *event.ErrInvalidEventName: diff --git a/cmd/bucket-replication.go b/cmd/bucket-replication.go index 8926a7947..c5653195b 100644 --- a/cmd/bucket-replication.go +++ b/cmd/bucket-replication.go @@ -49,7 +49,7 @@ func getReplicationConfig(ctx context.Context, bucketName string) (rc *replicati // validateReplicationDestination returns error if replication destination bucket missing or not configured // It also returns true if replication destination is same as this server. func validateReplicationDestination(ctx context.Context, bucket string, rCfg *replication.Config) (bool, error) { - clnt := globalBucketTargetSys.GetReplicationTargetClient(ctx, rCfg.ReplicationArn) + clnt := globalBucketTargetSys.GetReplicationTargetClient(ctx, rCfg.RoleArn) if clnt == nil { return false, BucketRemoteTargetNotFound{Bucket: bucket} } @@ -65,7 +65,7 @@ func validateReplicationDestination(ctx context.Context, bucket string, rCfg *re } } // validate replication ARN against target endpoint - c, ok := globalBucketTargetSys.arnRemotesMap[rCfg.ReplicationArn] + c, ok := globalBucketTargetSys.arnRemotesMap[rCfg.RoleArn] if ok { if c.EndpointURL().String() == clnt.EndpointURL().String() { sameTarget, _ := isLocalHost(clnt.EndpointURL().Hostname(), clnt.EndpointURL().Port(), globalMinioPort) @@ -159,7 +159,7 @@ func replicateObject(ctx context.Context, bucket, object, versionID string, obje logger.LogIf(ctx, err) return } - tgt := globalBucketTargetSys.GetReplicationTargetClient(ctx, cfg.ReplicationArn) + tgt := globalBucketTargetSys.GetReplicationTargetClient(ctx, cfg.RoleArn) if tgt == nil { return } diff --git a/cmd/bucket-targets.go b/cmd/bucket-targets.go index 5d60a3006..24059ff2f 100644 --- a/cmd/bucket-targets.go +++ b/cmd/bucket-targets.go @@ -18,7 +18,6 @@ package cmd import ( "context" - "fmt" "net/http" "sync" @@ -36,16 +35,42 @@ type BucketTargetSys struct { clientsCache map[string]*miniogo.Core } -// ListTargets - gets list of bucket targets for this bucket. -func (sys *BucketTargetSys) ListTargets(ctx context.Context, bucket string) (*madmin.BucketTargets, error) { - if globalIsGateway { - return nil, nil +// ListTargets lists bucket targets across tenant or for individual bucket, and returns +// results filtered by arnType +func (sys *BucketTargetSys) ListTargets(ctx context.Context, bucket, arnType string) (targets []madmin.BucketTarget) { + if bucket != "" { + if ts, err := sys.ListBucketTargets(ctx, bucket); err == nil { + for _, t := range ts.Targets { + if string(t.Type) == arnType || arnType == "" { + targets = append(targets, t.Clone()) + } + } + } + return targets } + sys.RLock() + defer sys.RUnlock() + for _, tgts := range sys.targetsMap { + for _, t := range tgts { + if string(t.Type) == arnType || arnType == "" { + targets = append(targets, t.Clone()) + } + } + } + return +} + +// ListBucketTargets - gets list of bucket targets for this bucket. +func (sys *BucketTargetSys) ListBucketTargets(ctx context.Context, bucket string) (*madmin.BucketTargets, error) { + + sys.RLock() + defer sys.RUnlock() + tgts, ok := sys.targetsMap[bucket] if ok { return &madmin.BucketTargets{Targets: tgts}, nil } - return nil, fmt.Errorf("No remote targets exist for bucket %s", bucket) + return nil, BucketRemoteTargetNotFound{Bucket: bucket} } // SetTarget - sets a new minio-go client target for this bucket. @@ -60,8 +85,10 @@ func (sys *BucketTargetSys) SetTarget(ctx context.Context, bucket string, tgt *m if err != nil { return BucketRemoteTargetNotFound{Bucket: tgt.TargetBucket} } - - if tgt.Type == madmin.ReplicationArn { + if tgt.Type == madmin.ReplicationService { + if !globalBucketVersioningSys.Enabled(bucket) { + return BucketReplicationSourceNotVersioned{Bucket: bucket} + } vcfg, err := clnt.GetBucketVersioning(ctx, tgt.TargetBucket) if err != nil || vcfg.Status != string(versioning.Enabled) { if isErrBucketNotFound(err) { @@ -112,10 +139,10 @@ func (sys *BucketTargetSys) RemoveTarget(ctx context.Context, bucket, arnStr str if err != nil { return BucketRemoteArnInvalid{Bucket: bucket} } - if arn.Type == madmin.ReplicationArn { + if arn.Type == madmin.ReplicationService { // reject removal of remote target if replication configuration is present rcfg, err := getReplicationConfig(ctx, bucket) - if err == nil && rcfg.ReplicationArn == arnStr { + if err == nil && rcfg.RoleArn == arnStr { if _, ok := sys.arnRemotesMap[arnStr]; ok { return BucketRemoteRemoveDisallowed{Bucket: bucket} } @@ -125,11 +152,17 @@ func (sys *BucketTargetSys) RemoveTarget(ctx context.Context, bucket, arnStr str sys.Lock() defer sys.Unlock() targets := make([]madmin.BucketTarget, 0) + found := false tgts := sys.targetsMap[bucket] for _, tgt := range tgts { if tgt.Arn != arnStr { targets = append(targets, tgt) + continue } + found = true + } + if !found { + return BucketRemoteTargetNotFound{Bucket: bucket} } sys.targetsMap[bucket] = targets delete(sys.arnRemotesMap, arnStr) @@ -168,6 +201,40 @@ func (sys *BucketTargetSys) Init(ctx context.Context, buckets []BucketInfo, objA return nil } +// UpdateTarget updates target to reflect metadata updates +func (sys *BucketTargetSys) UpdateTarget(bucket string, cfg *madmin.BucketTargets) { + if sys == nil { + return + } + sys.Lock() + defer sys.Unlock() + if cfg == nil || cfg.Empty() { + // remove target and arn association + if tgts, ok := sys.targetsMap[bucket]; ok { + for _, t := range tgts { + delete(sys.arnRemotesMap, t.Arn) + } + } + delete(sys.targetsMap, bucket) + return + } + + if len(cfg.Targets) > 0 { + sys.targetsMap[bucket] = cfg.Targets + } + for _, tgt := range cfg.Targets { + tgtClient, err := sys.getRemoteTargetClient(&tgt) + if err != nil { + continue + } + sys.arnRemotesMap[tgt.Arn] = tgtClient + if _, ok := sys.clientsCache[tgtClient.EndpointURL().String()]; !ok { + sys.clientsCache[tgtClient.EndpointURL().String()] = tgtClient + } + } + sys.targetsMap[bucket] = cfg.Targets +} + // create minio-go clients for buckets having remote targets func (sys *BucketTargetSys) load(ctx context.Context, buckets []BucketInfo, objAPI ObjectLayer) { for _, bucket := range buckets { @@ -229,7 +296,7 @@ func (sys *BucketTargetSys) getRemoteARN(bucket string, target *madmin.BucketTar return tgt.Arn } } - if !madmin.ArnType(target.Type).IsValid() { + if !madmin.ServiceType(target.Type).IsValid() { return "" } arn := madmin.ARN{ diff --git a/cmd/object-api-errors.go b/cmd/object-api-errors.go index 3f095126c..30b346a38 100644 --- a/cmd/object-api-errors.go +++ b/cmd/object-api-errors.go @@ -411,6 +411,13 @@ func (e BucketReplicationTargetNotVersioned) Error() string { return "Replication target does not have versioning enabled: " + e.Bucket } +// BucketReplicationSourceNotVersioned replication source does not have versioning enabled. +type BucketReplicationSourceNotVersioned GenericError + +func (e BucketReplicationSourceNotVersioned) Error() string { + return "Replication source does not have versioning enabled: " + e.Bucket +} + /// Bucket related errors. // BucketNameInvalid - bucketname provided is invalid. diff --git a/cmd/peer-rest-server.go b/cmd/peer-rest-server.go index 4be4e2a3d..c54afb598 100644 --- a/cmd/peer-rest-server.go +++ b/cmd/peer-rest-server.go @@ -610,6 +610,10 @@ func (s *peerRESTServer) LoadBucketMetadataHandler(w http.ResponseWriter, r *htt if meta.notificationConfig != nil { globalNotificationSys.AddRulesMap(bucketName, meta.notificationConfig.ToRulesMap()) } + + if meta.bucketTargetConfig != nil { + globalBucketTargetSys.UpdateTarget(bucketName, meta.bucketTargetConfig) + } } // ReloadFormatHandler - Reload Format. diff --git a/docs/bucket/replication/README.md b/docs/bucket/replication/README.md index 7b7affcb6..4b5b95f23 100644 --- a/docs/bucket/replication/README.md +++ b/docs/bucket/replication/README.md @@ -13,22 +13,22 @@ To replicate objects in a bucket to a destination bucket on a target site either Create a replication target on the source cluster as shown below: ``` -mc admin bucket remote set myminio/srcbucket https://accessKey:secretKey@replica-endpoint:9000/destbucket --service replication --region us-east-1 -Replication ARN = 'arn:minio:replication:us-east-1:c5be6b16-769d-432a-9ef1-4567081f3566:destbucket' +mc admin bucket remote add myminio/srcbucket https://accessKey:secretKey@replica-endpoint:9000/destbucket --service replication --region us-east-1 +Role ARN = 'arn:minio:replication:us-east-1:c5be6b16-769d-432a-9ef1-4567081f3566:destbucket' ``` Note that the admin needs *s3:GetReplicationConfigurationAction* permission on source cluster. The credential used at the destination requires *s3:ReplicateObject* permission. Once successfully created and authorized this generates a replication target ARN. The command below lists all the currently authorized replication targets: ``` -mc admin bucket remote list myminio/srcbucket --service "replication" -Replication ARN = 'arn:minio:replication:us-east-1:c5be6b16-769d-432a-9ef1-4567081f3566:destbucket' +mc admin bucket remote ls myminio/srcbucket --service "replication" +Role ARN = 'arn:minio:replication:us-east-1:c5be6b16-769d-432a-9ef1-4567081f3566:destbucket' ``` -The replication configuration can now be added to the source bucket by applying the json file with replication configuration. The ReplicationArn is passed in as a json element in the configuration. +The replication configuration can now be added to the source bucket by applying the json file with replication configuration. The Role ARN above is passed in as a json element in the configuration. ```json { - "ReplicationArn" :"arn:minio:replication:us-east-1:c5be6b16-769d-432a-9ef1-4567081f3566:destbucket", + "Role" :"arn:minio:replication:us-east-1:c5be6b16-769d-432a-9ef1-4567081f3566:destbucket", "Rules": [ { "Status": "Enabled", @@ -63,7 +63,7 @@ mc replicate add myminio/srcbucket/Tax --priority 1 --arn "arn:minio:replication Replication configuration applied successfully to myminio/srcbucket. ``` -Apart from *ReplicationArn* , rest of the configuration follows [AWS S3 Spec](https://docs.aws.amazon.com/AmazonS3/latest/dev/replication-add-config.html). Any objects uploaded to the source bucket that meet replication criteria will now be automatically replicated by the MinIO server to the remote destination bucket. Replication can be disabled at any time by disabling specific rules in the configuration or deleting the replication configuration entirely. +The replication configuration follows [AWS S3 Spec](https://docs.aws.amazon.com/AmazonS3/latest/dev/replication-add-config.html). Any objects uploaded to the source bucket that meet replication criteria will now be automatically replicated by the MinIO server to the remote destination bucket. Replication can be disabled at any time by disabling specific rules in the configuration or deleting the replication configuration entirely. When an object is deleted from the source bucket, the replica will not be deleted as per S3 spec. diff --git a/pkg/bucket/replication/replication.go b/pkg/bucket/replication/replication.go index 5a32b9b99..1d87fd479 100644 --- a/pkg/bucket/replication/replication.go +++ b/pkg/bucket/replication/replication.go @@ -50,7 +50,7 @@ var ( errReplicationNoRule = Errorf("Replication configuration should have at least one rule") errReplicationUniquePriority = Errorf("Replication configuration has duplicate priority") errReplicationDestinationMismatch = Errorf("The destination bucket must be same for all rules") - errReplicationArnMissing = Errorf("Replication Arn missing") + errRoleArnMissing = Errorf("Missing required parameter `Role` in ReplicationConfiguration") ) // Config - replication configuration specified in @@ -58,8 +58,8 @@ var ( type Config struct { XMLName xml.Name `xml:"ReplicationConfiguration" json:"-"` Rules []Rule `xml:"Rule" json:"Rules"` - // ReplicationArn is a MinIO only extension and optional for AWS - ReplicationArn string `xml:"ReplicationArn,omitempty" json:"ReplicationArn,omitempty"` + // RoleArn is being reused for MinIO replication ARN + RoleArn string `xml:"Role" json:"Role"` } // Maximum 2MiB size per replication config. @@ -84,8 +84,8 @@ func (c Config) Validate(bucket string, sameTarget bool) error { if len(c.Rules) == 0 { return errReplicationNoRule } - if c.ReplicationArn == "" { - return errReplicationArnMissing + if c.RoleArn == "" { + return errRoleArnMissing } // Validate all the rules in the replication config targetMap := make(map[string]struct{}) diff --git a/pkg/madmin/remote-target-commands.go b/pkg/madmin/remote-target-commands.go index ccad8e3a6..61993d765 100644 --- a/pkg/madmin/remote-target-commands.go +++ b/pkg/madmin/remote-target-commands.go @@ -29,22 +29,22 @@ import ( "github.com/minio/minio/pkg/auth" ) -// ArnType represents bucket ARN type -type ArnType string +// ServiceType represents service type +type ServiceType string const ( - // ReplicationArn specifies a ARN type of replication - ReplicationArn ArnType = "replication" + // ReplicationService specifies replication service + ReplicationService ServiceType = "replication" ) -// IsValid returns true if ARN type is replication -func (t ArnType) IsValid() bool { - return t == ReplicationArn +// IsValid returns true if ARN type represents replication +func (t ServiceType) IsValid() bool { + return t == ReplicationService } // ARN is a struct to define arn. type ARN struct { - Type ArnType + Type ServiceType ID string Region string Bucket string @@ -75,7 +75,7 @@ func ParseARN(s string) (*ARN, error) { } return &ARN{ - Type: ArnType(tokens[2]), + Type: ServiceType(tokens[2]), Region: tokens[3], ID: tokens[4], Bucket: tokens[5], @@ -84,6 +84,7 @@ func ParseARN(s string) (*ARN, error) { // BucketTarget represents the target bucket and site association. type BucketTarget struct { + SourceBucket string `json:"sourcebucket"` Endpoint string `json:"endpoint"` Credentials *auth.Credentials `json:"credentials"` TargetBucket string `json:"targetbucket"` @@ -91,10 +92,26 @@ type BucketTarget struct { Path string `json:"path,omitempty"` API string `json:"api,omitempty"` Arn string `json:"arn,omitempty"` - Type ArnType `json:"type"` + Type ServiceType `json:"type"` Region string `json:"omitempty"` } +// Clone returns shallow clone of BucketTarget without secret key in credentials +func (t *BucketTarget) Clone() BucketTarget { + return BucketTarget{ + SourceBucket: t.SourceBucket, + Endpoint: t.Endpoint, + TargetBucket: t.TargetBucket, + Credentials: &auth.Credentials{AccessKey: t.Credentials.AccessKey}, + Secure: t.Secure, + Path: t.Path, + API: t.Path, + Arn: t.Arn, + Type: t.Type, + Region: t.Region, + } +} + // URL returns target url func (t BucketTarget) URL() string { scheme := "http" @@ -132,18 +149,18 @@ func (t BucketTargets) Empty() bool { return empty } -// ListBucketTargets - gets target(s) for this bucket -func (adm *AdminClient) ListBucketTargets(ctx context.Context, bucket, arnType string) (targets []BucketTarget, err error) { +// ListRemoteTargets - gets target(s) for this bucket +func (adm *AdminClient) ListRemoteTargets(ctx context.Context, bucket, arnType string) (targets []BucketTarget, err error) { queryValues := url.Values{} queryValues.Set("bucket", bucket) queryValues.Set("type", arnType) reqData := requestData{ - relPath: adminAPIPrefix + "/list-bucket-targets", + relPath: adminAPIPrefix + "/list-remote-targets", queryValues: queryValues, } - // Execute GET on /minio/admin/v3/list-bucket-targets + // Execute GET on /minio/admin/v3/list-remote-targets resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) defer closeResponse(resp) @@ -165,8 +182,8 @@ func (adm *AdminClient) ListBucketTargets(ctx context.Context, bucket, arnType s return targets, nil } -// SetBucketTarget sets up a remote target for this bucket -func (adm *AdminClient) SetBucketTarget(ctx context.Context, bucket string, target *BucketTarget) (string, error) { +// SetRemoteTarget sets up a remote target for this bucket +func (adm *AdminClient) SetRemoteTarget(ctx context.Context, bucket string, target *BucketTarget) (string, error) { data, err := json.Marshal(target) if err != nil { return "", err @@ -179,12 +196,12 @@ func (adm *AdminClient) SetBucketTarget(ctx context.Context, bucket string, targ queryValues.Set("bucket", bucket) reqData := requestData{ - relPath: adminAPIPrefix + "/set-bucket-target", + relPath: adminAPIPrefix + "/set-remote-target", queryValues: queryValues, content: encData, } - // Execute PUT on /minio/admin/v3/set-bucket-target to set a target for this bucket of specific arn type. + // Execute PUT on /minio/admin/v3/set-remote-target to set a target for this bucket of specific arn type. resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) defer closeResponse(resp) @@ -206,18 +223,18 @@ func (adm *AdminClient) SetBucketTarget(ctx context.Context, bucket string, targ return arn, nil } -// RemoveBucketTarget removes a remote target associated with particular ARN for this bucket -func (adm *AdminClient) RemoveBucketTarget(ctx context.Context, bucket, arn string) error { +// RemoveRemoteTarget removes a remote target associated with particular ARN for this bucket +func (adm *AdminClient) RemoveRemoteTarget(ctx context.Context, bucket, arn string) error { queryValues := url.Values{} queryValues.Set("bucket", bucket) queryValues.Set("arn", arn) reqData := requestData{ - relPath: adminAPIPrefix + "/remove-bucket-target", + relPath: adminAPIPrefix + "/remove-remote-target", queryValues: queryValues, } - // Execute PUT on /minio/admin/v3/remove-bucket-target to remove a target for this bucket + // Execute PUT on /minio/admin/v3/remove-remote-target to remove a target for this bucket // with specific ARN resp, err := adm.executeMethod(ctx, http.MethodDelete, reqData) defer closeResponse(resp)