diff --git a/cmd/admin-router.go b/cmd/admin-router.go index f6b578acf..d5e4b9185 100644 --- a/cmd/admin-router.go +++ b/cmd/admin-router.go @@ -180,19 +180,19 @@ func registerAdminRouter(router *mux.Router, enableConfigOps, enableIAMOps bool) // PutBucketQuotaConfig adminRouter.Methods(http.MethodPut).Path(adminVersion+"/set-bucket-quota").HandlerFunc( httpTraceHdrs(adminAPI.PutBucketQuotaConfigHandler)).Queries("bucket", "{bucket:.*}") - } - // Bucket replication operations - // GetBucketTargetHandler - 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:.*}") - } + // Bucket replication operations + // GetBucketTargetHandler + 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 -- // Top locks if globalIsDistErasure { diff --git a/cmd/api-errors.go b/cmd/api-errors.go index 0c03c76db..d21a734b2 100644 --- a/cmd/api-errors.go +++ b/cmd/api-errors.go @@ -106,16 +106,17 @@ const ( ErrNoSuchCORSConfiguration ErrNoSuchWebsiteConfiguration ErrReplicationConfigurationNotFoundError - ErrReplicationDestinationNotFoundError + ErrRemoteDestinationNotFoundError ErrReplicationDestinationMissingLock - ErrReplicationTargetNotFoundError + ErrRemoteTargetNotFoundError ErrReplicationRemoteConnectionError ErrBucketRemoteIdenticalToSource ErrBucketRemoteAlreadyExists + ErrBucketRemoteLabelInUse ErrBucketRemoteArnTypeInvalid ErrBucketRemoteArnInvalid ErrBucketRemoteRemoveDisallowed - ErrReplicationTargetNotVersionedError + ErrRemoteTargetNotVersionedError ErrReplicationSourceNotVersionedError ErrReplicationNeedsVersioningError ErrReplicationBucketNeedsVersioningError @@ -809,9 +810,9 @@ var errorCodes = errorCodeMap{ Description: "The replication configuration was not found", HTTPStatusCode: http.StatusNotFound, }, - ErrReplicationDestinationNotFoundError: { - Code: "ReplicationDestinationNotFoundError", - Description: "The replication destination bucket does not exist", + ErrRemoteDestinationNotFoundError: { + Code: "RemoteDestinationNotFoundError", + Description: "The remote destination bucket does not exist", HTTPStatusCode: http.StatusNotFound, }, ErrReplicationDestinationMissingLock: { @@ -819,9 +820,9 @@ var errorCodes = errorCodeMap{ Description: "The replication destination bucket does not have object locking enabled", HTTPStatusCode: http.StatusBadRequest, }, - ErrReplicationTargetNotFoundError: { - Code: "XMinioAdminReplicationTargetNotFoundError", - Description: "The replication target does not exist", + ErrRemoteTargetNotFoundError: { + Code: "XMinioAdminRemoteTargetNotFoundError", + Description: "The remote target does not exist", HTTPStatusCode: http.StatusNotFound, }, ErrReplicationRemoteConnectionError: { @@ -839,9 +840,14 @@ var errorCodes = errorCodeMap{ Description: "The remote target already exists", HTTPStatusCode: http.StatusBadRequest, }, + ErrBucketRemoteLabelInUse: { + Code: "XMinioAdminBucketRemoteLabelInUse", + Description: "The remote target with this label already exists", + HTTPStatusCode: http.StatusBadRequest, + }, ErrBucketRemoteRemoveDisallowed: { Code: "XMinioAdminRemoteRemoveDisallowed", - Description: "Replication configuration exists with this ARN.", + Description: "This ARN is in use by an existing configuration", HTTPStatusCode: http.StatusBadRequest, }, ErrBucketRemoteArnTypeInvalid: { @@ -854,9 +860,9 @@ var errorCodes = errorCodeMap{ Description: "The bucket remote ARN does not have correct format", HTTPStatusCode: http.StatusBadRequest, }, - ErrReplicationTargetNotVersionedError: { - Code: "ReplicationTargetNotVersionedError", - Description: "The replication target does not have versioning enabled", + ErrRemoteTargetNotVersionedError: { + Code: "RemoteTargetNotVersionedError", + Description: "The remote target does not have versioning enabled", HTTPStatusCode: http.StatusBadRequest, }, ErrReplicationSourceNotVersionedError: { @@ -1906,24 +1912,26 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) { apiErr = ErrAdminNoSuchQuotaConfiguration case BucketReplicationConfigNotFound: apiErr = ErrReplicationConfigurationNotFoundError - case BucketReplicationDestinationNotFound: - apiErr = ErrReplicationDestinationNotFoundError + case BucketRemoteDestinationNotFound: + apiErr = ErrRemoteDestinationNotFoundError case BucketReplicationDestinationMissingLock: apiErr = ErrReplicationDestinationMissingLock case BucketRemoteTargetNotFound: - apiErr = ErrReplicationTargetNotFoundError + apiErr = ErrRemoteTargetNotFoundError case BucketRemoteConnectionErr: apiErr = ErrReplicationRemoteConnectionError case BucketRemoteAlreadyExists: apiErr = ErrBucketRemoteAlreadyExists + case BucketRemoteLabelInUse: + apiErr = ErrBucketRemoteLabelInUse case BucketRemoteArnTypeInvalid: apiErr = ErrBucketRemoteArnTypeInvalid case BucketRemoteArnInvalid: apiErr = ErrBucketRemoteArnInvalid case BucketRemoteRemoveDisallowed: apiErr = ErrBucketRemoteRemoveDisallowed - case BucketReplicationTargetNotVersioned: - apiErr = ErrReplicationTargetNotVersionedError + case BucketRemoteTargetNotVersioned: + apiErr = ErrRemoteTargetNotVersionedError case BucketReplicationSourceNotVersioned: apiErr = ErrReplicationSourceNotVersionedError case BucketQuotaExceeded: diff --git a/cmd/bucket-replication.go b/cmd/bucket-replication.go index 1ebe333c3..b94288f92 100644 --- a/cmd/bucket-replication.go +++ b/cmd/bucket-replication.go @@ -52,12 +52,12 @@ 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.RoleArn) + clnt := globalBucketTargetSys.GetRemoteTargetClient(ctx, rCfg.RoleArn) if clnt == nil { return false, BucketRemoteTargetNotFound{Bucket: bucket} } if found, _ := clnt.BucketExists(ctx, rCfg.GetDestination().Bucket); !found { - return false, BucketReplicationDestinationNotFound{Bucket: rCfg.GetDestination().Bucket} + return false, BucketRemoteDestinationNotFound{Bucket: rCfg.GetDestination().Bucket} } if ret, err := globalBucketObjectLockSys.Get(bucket); err == nil { if ret.LockEnabled { @@ -182,8 +182,7 @@ func replicateObject(ctx context.Context, objInfo ObjectInfo, objectAPI ObjectLa logger.LogIf(ctx, err) return } - - tgt := globalBucketTargetSys.GetReplicationTargetClient(ctx, cfg.RoleArn) + tgt := globalBucketTargetSys.GetRemoteTargetClient(ctx, cfg.RoleArn) if tgt == nil { return } diff --git a/cmd/bucket-targets.go b/cmd/bucket-targets.go index 8c6a01840..023b645b6 100644 --- a/cmd/bucket-targets.go +++ b/cmd/bucket-targets.go @@ -98,10 +98,9 @@ func (sys *BucketTargetSys) SetTarget(ctx context.Context, bucket string, tgt *m return BucketRemoteConnectionErr{Bucket: tgt.TargetBucket} } if vcfg.Status != string(versioning.Enabled) { - return BucketReplicationTargetNotVersioned{Bucket: tgt.TargetBucket} + return BucketRemoteTargetNotVersioned{Bucket: tgt.TargetBucket} } } - sys.Lock() defer sys.Unlock() @@ -113,6 +112,9 @@ func (sys *BucketTargetSys) SetTarget(ctx context.Context, bucket string, tgt *m if t.Arn == tgt.Arn { return BucketRemoteAlreadyExists{Bucket: t.TargetBucket} } + if t.Label == tgt.Label { + return BucketRemoteLabelInUse{Bucket: t.TargetBucket} + } newtgts[idx] = *tgt found = true continue @@ -173,8 +175,8 @@ func (sys *BucketTargetSys) RemoveTarget(ctx context.Context, bucket, arnStr str return nil } -// GetReplicationTargetClient returns minio-go client for replication target instance -func (sys *BucketTargetSys) GetReplicationTargetClient(ctx context.Context, arn string) *miniogo.Core { +// GetRemoteTargetClient returns minio-go client for replication target instance +func (sys *BucketTargetSys) GetRemoteTargetClient(ctx context.Context, arn string) *miniogo.Core { sys.RLock() defer sys.RUnlock() return sys.arnRemotesMap[arn] diff --git a/cmd/object-api-errors.go b/cmd/object-api-errors.go index 1893c0aad..790daf26e 100644 --- a/cmd/object-api-errors.go +++ b/cmd/object-api-errors.go @@ -361,10 +361,10 @@ func (e BucketReplicationConfigNotFound) Error() string { return "The replication configuration was not found: " + e.Bucket } -// BucketReplicationDestinationNotFound bucket does not exist. -type BucketReplicationDestinationNotFound GenericError +// BucketRemoteDestinationNotFound bucket does not exist. +type BucketRemoteDestinationNotFound GenericError -func (e BucketReplicationDestinationNotFound) Error() string { +func (e BucketRemoteDestinationNotFound) Error() string { return "Destination bucket does not exist: " + e.Bucket } @@ -396,6 +396,13 @@ func (e BucketRemoteAlreadyExists) Error() string { return "Remote already exists for this bucket: " + e.Bucket } +// BucketRemoteLabelInUse remote already exists for this target label. +type BucketRemoteLabelInUse GenericError + +func (e BucketRemoteLabelInUse) Error() string { + return "Remote with this label already exists for this bucket: " + e.Bucket +} + // BucketRemoteArnTypeInvalid arn type for remote is not valid. type BucketRemoteArnTypeInvalid GenericError @@ -417,11 +424,11 @@ func (e BucketRemoteRemoveDisallowed) Error() string { return "Replication configuration exists with this ARN:" + e.Bucket } -// BucketReplicationTargetNotVersioned replication target does not have versioning enabled. -type BucketReplicationTargetNotVersioned GenericError +// BucketRemoteTargetNotVersioned remote target does not have versioning enabled. +type BucketRemoteTargetNotVersioned GenericError -func (e BucketReplicationTargetNotVersioned) Error() string { - return "Replication target does not have versioning enabled: " + e.Bucket +func (e BucketRemoteTargetNotVersioned) Error() string { + return "Remote target does not have versioning enabled: " + e.Bucket } // BucketReplicationSourceNotVersioned replication source does not have versioning enabled. diff --git a/pkg/madmin/remote-target-commands.go b/pkg/madmin/remote-target-commands.go index 9b9280817..1f514df7f 100644 --- a/pkg/madmin/remote-target-commands.go +++ b/pkg/madmin/remote-target-commands.go @@ -94,6 +94,7 @@ type BucketTarget struct { Arn string `json:"arn,omitempty"` Type ServiceType `json:"type"` Region string `json:"omitempty"` + Label string `json:"label,omitempty"` } // Clone returns shallow clone of BucketTarget without secret key in credentials @@ -109,6 +110,7 @@ func (t *BucketTarget) Clone() BucketTarget { Arn: t.Arn, Type: t.Type, Region: t.Region, + Label: t.Label, } }