mirror of
https://github.com/minio/minio.git
synced 2024-12-25 22:55:54 -05:00
site replication: heal missing/invalid replication config (#14979)
Validate remote target ARNs and heal any stale rules in the replication config
This commit is contained in:
parent
62cd643868
commit
5c81d0d89a
@ -1636,7 +1636,7 @@ func (api objectAPIHandlers) PutBucketReplicationConfigHandler(w http.ResponseWr
|
|||||||
writeErrorResponse(ctx, w, apiErr, r.URL)
|
writeErrorResponse(ctx, w, apiErr, r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sameTarget, apiErr := validateReplicationDestination(ctx, bucket, replicationConfig)
|
sameTarget, apiErr := validateReplicationDestination(ctx, bucket, replicationConfig, true)
|
||||||
if apiErr != noError {
|
if apiErr != noError {
|
||||||
writeErrorResponse(ctx, w, apiErr, r.URL)
|
writeErrorResponse(ctx, w, apiErr, r.URL)
|
||||||
return
|
return
|
||||||
|
@ -87,7 +87,7 @@ func getReplicationConfig(ctx context.Context, bucketName string) (rc *replicati
|
|||||||
|
|
||||||
// validateReplicationDestination returns error if replication destination bucket missing or not configured
|
// validateReplicationDestination returns error if replication destination bucket missing or not configured
|
||||||
// It also returns true if replication destination is same as this server.
|
// It also returns true if replication destination is same as this server.
|
||||||
func validateReplicationDestination(ctx context.Context, bucket string, rCfg *replication.Config) (bool, APIError) {
|
func validateReplicationDestination(ctx context.Context, bucket string, rCfg *replication.Config, checkRemote bool) (bool, APIError) {
|
||||||
var arns []string
|
var arns []string
|
||||||
if rCfg.RoleArn != "" {
|
if rCfg.RoleArn != "" {
|
||||||
arns = append(arns, rCfg.RoleArn)
|
arns = append(arns, rCfg.RoleArn)
|
||||||
@ -96,26 +96,29 @@ func validateReplicationDestination(ctx context.Context, bucket string, rCfg *re
|
|||||||
arns = append(arns, rule.Destination.String())
|
arns = append(arns, rule.Destination.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var sameTarget bool
|
||||||
for _, arnStr := range arns {
|
for _, arnStr := range arns {
|
||||||
arn, err := madmin.ParseARN(arnStr)
|
arn, err := madmin.ParseARN(arnStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errorCodes.ToAPIErrWithErr(ErrBucketRemoteArnInvalid, err)
|
return sameTarget, errorCodes.ToAPIErrWithErr(ErrBucketRemoteArnInvalid, err)
|
||||||
}
|
}
|
||||||
if arn.Type != madmin.ReplicationService {
|
if arn.Type != madmin.ReplicationService {
|
||||||
return false, toAPIError(ctx, BucketRemoteArnTypeInvalid{Bucket: bucket})
|
return sameTarget, toAPIError(ctx, BucketRemoteArnTypeInvalid{Bucket: bucket})
|
||||||
}
|
}
|
||||||
clnt := globalBucketTargetSys.GetRemoteTargetClient(ctx, arnStr)
|
clnt := globalBucketTargetSys.GetRemoteTargetClient(ctx, arnStr)
|
||||||
if clnt == nil {
|
if clnt == nil {
|
||||||
return false, toAPIError(ctx, BucketRemoteTargetNotFound{Bucket: bucket})
|
return sameTarget, toAPIError(ctx, BucketRemoteTargetNotFound{Bucket: bucket})
|
||||||
}
|
}
|
||||||
|
if checkRemote { // validate remote bucket
|
||||||
if found, err := clnt.BucketExists(ctx, arn.Bucket); !found {
|
if found, err := clnt.BucketExists(ctx, arn.Bucket); !found {
|
||||||
return false, errorCodes.ToAPIErrWithErr(ErrRemoteDestinationNotFoundError, err)
|
return sameTarget, errorCodes.ToAPIErrWithErr(ErrRemoteDestinationNotFoundError, err)
|
||||||
}
|
}
|
||||||
if ret, err := globalBucketObjectLockSys.Get(bucket); err == nil {
|
if ret, err := globalBucketObjectLockSys.Get(bucket); err == nil {
|
||||||
if ret.LockEnabled {
|
if ret.LockEnabled {
|
||||||
lock, _, _, _, err := clnt.GetObjectLockConfig(ctx, arn.Bucket)
|
lock, _, _, _, err := clnt.GetObjectLockConfig(ctx, arn.Bucket)
|
||||||
if err != nil || lock != "Enabled" {
|
if err != nil || lock != "Enabled" {
|
||||||
return false, errorCodes.ToAPIErrWithErr(ErrReplicationDestinationMissingLock, err)
|
return sameTarget, errorCodes.ToAPIErrWithErr(ErrReplicationDestinationMissingLock, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,13 +126,20 @@ func validateReplicationDestination(ctx context.Context, bucket string, rCfg *re
|
|||||||
c, ok := globalBucketTargetSys.arnRemotesMap[arnStr]
|
c, ok := globalBucketTargetSys.arnRemotesMap[arnStr]
|
||||||
if ok {
|
if ok {
|
||||||
if c.EndpointURL().String() == clnt.EndpointURL().String() {
|
if c.EndpointURL().String() == clnt.EndpointURL().String() {
|
||||||
sameTarget, _ := isLocalHost(clnt.EndpointURL().Hostname(), clnt.EndpointURL().Port(), globalMinioPort)
|
selfTarget, _ := isLocalHost(clnt.EndpointURL().Hostname(), clnt.EndpointURL().Port(), globalMinioPort)
|
||||||
return sameTarget, toAPIError(ctx, nil)
|
if !sameTarget {
|
||||||
|
sameTarget = selfTarget
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(arns) == 0 {
|
||||||
return false, toAPIError(ctx, BucketRemoteTargetNotFound{Bucket: bucket})
|
return false, toAPIError(ctx, BucketRemoteTargetNotFound{Bucket: bucket})
|
||||||
}
|
}
|
||||||
|
return sameTarget, toAPIError(ctx, nil)
|
||||||
|
}
|
||||||
|
|
||||||
type mustReplicateOptions struct {
|
type mustReplicateOptions struct {
|
||||||
meta map[string]string
|
meta map[string]string
|
||||||
|
@ -876,14 +876,28 @@ func (c *SiteReplicationSys) PeerBucketConfigureReplHandler(ctx context.Context,
|
|||||||
ExistingObjectReplicate: "enable",
|
ExistingObjectReplicate: "enable",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
ruleARN := targetARN
|
||||||
for _, r := range replicationConfig.Rules {
|
for _, r := range replicationConfig.Rules {
|
||||||
if r.ID == ruleID {
|
if r.ID == ruleID {
|
||||||
hasRule = true
|
hasRule = true
|
||||||
|
ruleARN = r.Destination.Bucket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case hasRule:
|
case hasRule:
|
||||||
|
if ruleARN != opts.DestBucket {
|
||||||
|
// remove stale replication rule and replace rule with correct target ARN
|
||||||
|
if len(replicationConfig.Rules) > 1 {
|
||||||
|
err = replicationConfig.RemoveRule(opts)
|
||||||
|
} else {
|
||||||
|
replicationConfig = replication.Config{}
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = replicationConfig.AddRule(opts)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
err = replicationConfig.EditRule(opts)
|
err = replicationConfig.EditRule(opts)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
err = replicationConfig.AddRule(opts)
|
err = replicationConfig.AddRule(opts)
|
||||||
}
|
}
|
||||||
@ -902,7 +916,7 @@ func (c *SiteReplicationSys) PeerBucketConfigureReplHandler(ctx context.Context,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sameTarget, apiErr := validateReplicationDestination(ctx, bucket, newReplicationConfig)
|
sameTarget, apiErr := validateReplicationDestination(ctx, bucket, newReplicationConfig, true)
|
||||||
if apiErr != noError {
|
if apiErr != noError {
|
||||||
return fmt.Errorf("bucket replication config validation error: %#v", apiErr)
|
return fmt.Errorf("bucket replication config validation error: %#v", apiErr)
|
||||||
}
|
}
|
||||||
@ -3948,6 +3962,16 @@ func (c *SiteReplicationSys) healBucketReplicationConfig(ctx context.Context, ob
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rcfg, _, err := globalBucketMetadataSys.GetReplicationConfig(ctx, bucket)
|
||||||
|
if err != nil {
|
||||||
|
replMismatch = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate remote targets on current cluster for this bucket
|
||||||
|
_, apiErr := validateReplicationDestination(ctx, bucket, rcfg, false)
|
||||||
|
if apiErr != noError {
|
||||||
|
replMismatch = true
|
||||||
|
}
|
||||||
if replMismatch {
|
if replMismatch {
|
||||||
err := c.PeerBucketConfigureReplHandler(ctx, bucket)
|
err := c.PeerBucketConfigureReplHandler(ctx, bucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user