| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2021-02-25 01:00:15 +08:00
										 |  |  | 	"crypto/sha256" | 
					
						
							| 
									
										
										
										
											2020-11-26 03:24:50 +08:00
										 |  |  | 	"encoding/hex" | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 	"sync/atomic" | 
					
						
							| 
									
										
										
										
											2020-10-13 05:19:46 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-04 23:48:38 +08:00
										 |  |  | 	minio "github.com/minio/minio-go/v7" | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	miniogo "github.com/minio/minio-go/v7" | 
					
						
							|  |  |  | 	"github.com/minio/minio-go/v7/pkg/credentials" | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/crypto" | 
					
						
							| 
									
										
										
										
											2021-04-04 00:03:42 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/bucket/versioning" | 
					
						
							|  |  |  | 	"github.com/minio/minio/pkg/madmin" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2021-01-28 03:22:34 +08:00
										 |  |  | 	defaultHealthCheckDuration = 100 * time.Second | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | // BucketTargetSys represents bucket targets subsystem
 | 
					
						
							|  |  |  | type BucketTargetSys struct { | 
					
						
							|  |  |  | 	sync.RWMutex | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 	arnRemotesMap map[string]*TargetClient | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	targetsMap    map[string][]madmin.BucketTarget | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | // 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 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	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() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	tgts, ok := sys.targetsMap[bucket] | 
					
						
							|  |  |  | 	if ok { | 
					
						
							|  |  |  | 		return &madmin.BucketTargets{Targets: tgts}, nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	return nil, BucketRemoteTargetNotFound{Bucket: bucket} | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetTarget - sets a new minio-go client target for this bucket.
 | 
					
						
							| 
									
										
										
										
											2020-11-25 11:09:05 +08:00
										 |  |  | func (sys *BucketTargetSys) SetTarget(ctx context.Context, bucket string, tgt *madmin.BucketTarget, update bool) error { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	if globalIsGateway { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-25 11:09:05 +08:00
										 |  |  | 	if !tgt.Type.IsValid() && !update { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		return BucketRemoteArnTypeInvalid{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	clnt, err := sys.getRemoteTargetClient(tgt) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return BucketRemoteTargetNotFound{Bucket: tgt.TargetBucket} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-25 11:09:05 +08:00
										 |  |  | 	// validate if target credentials are ok
 | 
					
						
							|  |  |  | 	if _, err = clnt.BucketExists(ctx, tgt.TargetBucket); err != nil { | 
					
						
							|  |  |  | 		if minio.ToErrorResponse(err).Code == "NoSuchBucket" { | 
					
						
							|  |  |  | 			return BucketRemoteTargetNotFound{Bucket: tgt.TargetBucket} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-03-27 09:58:13 +08:00
										 |  |  | 		return BucketRemoteConnectionErr{Bucket: tgt.TargetBucket, Err: err} | 
					
						
							| 
									
										
										
										
											2020-11-25 11:09:05 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	if tgt.Type == madmin.ReplicationService { | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  | 		if !globalIsErasure { | 
					
						
							| 
									
										
										
										
											2021-04-16 07:20:45 +08:00
										 |  |  | 			return NotImplemented{Message: "Replication is not implemented in " + getMinioMode()} | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 		if !globalBucketVersioningSys.Enabled(bucket) { | 
					
						
							|  |  |  | 			return BucketReplicationSourceNotVersioned{Bucket: bucket} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		vcfg, err := clnt.GetBucketVersioning(ctx, tgt.TargetBucket) | 
					
						
							| 
									
										
										
										
											2020-08-13 08:32:24 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-03-27 09:58:13 +08:00
										 |  |  | 			return BucketRemoteConnectionErr{Bucket: tgt.TargetBucket, Err: err} | 
					
						
							| 
									
										
										
										
											2020-09-04 23:48:38 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if vcfg.Status != string(versioning.Enabled) { | 
					
						
							| 
									
										
										
										
											2020-10-09 01:54:11 +08:00
										 |  |  | 			return BucketRemoteTargetNotVersioned{Bucket: tgt.TargetBucket} | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-04-16 07:20:45 +08:00
										 |  |  | 		if tgt.ReplicationSync && tgt.BandwidthLimit > 0 { | 
					
						
							|  |  |  | 			return NotImplemented{Message: "Synchronous replication does not support bandwidth limits"} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	sys.Lock() | 
					
						
							|  |  |  | 	defer sys.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-12 01:08:42 +08:00
										 |  |  | 	tgts := sys.targetsMap[bucket] | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	newtgts := make([]madmin.BucketTarget, len(tgts)) | 
					
						
							|  |  |  | 	found := false | 
					
						
							|  |  |  | 	for idx, t := range tgts { | 
					
						
							|  |  |  | 		if t.Type == tgt.Type { | 
					
						
							| 
									
										
										
										
											2020-11-25 11:09:05 +08:00
										 |  |  | 			if t.Arn == tgt.Arn && !update { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 				return BucketRemoteAlreadyExists{Bucket: t.TargetBucket} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			newtgts[idx] = *tgt | 
					
						
							|  |  |  | 			found = true | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		newtgts[idx] = t | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-25 11:09:05 +08:00
										 |  |  | 	if !found && !update { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		newtgts = append(newtgts, *tgt) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sys.targetsMap[bucket] = newtgts | 
					
						
							|  |  |  | 	sys.arnRemotesMap[tgt.Arn] = clnt | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RemoveTarget - removes a remote bucket target for this source bucket.
 | 
					
						
							|  |  |  | func (sys *BucketTargetSys) RemoveTarget(ctx context.Context, bucket, arnStr string) error { | 
					
						
							|  |  |  | 	if globalIsGateway { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if arnStr == "" { | 
					
						
							|  |  |  | 		return BucketRemoteArnInvalid{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	arn, err := madmin.ParseARN(arnStr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return BucketRemoteArnInvalid{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	if arn.Type == madmin.ReplicationService { | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  | 		if !globalIsErasure { | 
					
						
							| 
									
										
										
										
											2021-04-16 07:20:45 +08:00
										 |  |  | 			return NotImplemented{Message: "Replication is not implemented in " + getMinioMode()} | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		// reject removal of remote target if replication configuration is present
 | 
					
						
							|  |  |  | 		rcfg, err := getReplicationConfig(ctx, bucket) | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 		if err == nil && rcfg.RoleArn == arnStr { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 			if _, ok := sys.arnRemotesMap[arnStr]; ok { | 
					
						
							|  |  |  | 				return BucketRemoteRemoveDisallowed{Bucket: bucket} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-13 04:12:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	// delete ARN type from list of matching targets
 | 
					
						
							|  |  |  | 	sys.Lock() | 
					
						
							|  |  |  | 	defer sys.Unlock() | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	found := false | 
					
						
							| 
									
										
										
										
											2021-01-07 08:13:10 +08:00
										 |  |  | 	tgts, ok := sys.targetsMap[bucket] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return BucketRemoteTargetNotFound{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	targets := make([]madmin.BucketTarget, 0, len(tgts)) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	for _, tgt := range tgts { | 
					
						
							|  |  |  | 		if tgt.Arn != arnStr { | 
					
						
							|  |  |  | 			targets = append(targets, tgt) | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 		found = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !found { | 
					
						
							|  |  |  | 		return BucketRemoteTargetNotFound{Bucket: bucket} | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	sys.targetsMap[bucket] = targets | 
					
						
							|  |  |  | 	delete(sys.arnRemotesMap, arnStr) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 01:54:11 +08:00
										 |  |  | // GetRemoteTargetClient returns minio-go client for replication target instance
 | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | func (sys *BucketTargetSys) GetRemoteTargetClient(ctx context.Context, arn string) *TargetClient { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	sys.RLock() | 
					
						
							|  |  |  | 	defer sys.RUnlock() | 
					
						
							|  |  |  | 	return sys.arnRemotesMap[arn] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 06:26:20 +08:00
										 |  |  | // GetRemoteBucketTargetByArn returns BucketTarget for a ARN
 | 
					
						
							|  |  |  | func (sys *BucketTargetSys) GetRemoteBucketTargetByArn(ctx context.Context, bucket, arn string) madmin.BucketTarget { | 
					
						
							|  |  |  | 	sys.RLock() | 
					
						
							|  |  |  | 	defer sys.RUnlock() | 
					
						
							|  |  |  | 	var tgt madmin.BucketTarget | 
					
						
							|  |  |  | 	for _, t := range sys.targetsMap[bucket] { | 
					
						
							|  |  |  | 		if t.Arn == arn { | 
					
						
							|  |  |  | 			tgt = t.Clone() | 
					
						
							|  |  |  | 			tgt.Credentials = t.Credentials | 
					
						
							|  |  |  | 			return tgt | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return tgt | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | // NewBucketTargetSys - creates new replication system.
 | 
					
						
							|  |  |  | func NewBucketTargetSys() *BucketTargetSys { | 
					
						
							|  |  |  | 	return &BucketTargetSys{ | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 		arnRemotesMap: make(map[string]*TargetClient), | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		targetsMap:    make(map[string][]madmin.BucketTarget), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Init initializes the bucket targets subsystem for buckets which have targets configured.
 | 
					
						
							|  |  |  | func (sys *BucketTargetSys) Init(ctx context.Context, buckets []BucketInfo, objAPI ObjectLayer) error { | 
					
						
							|  |  |  | 	if objAPI == nil { | 
					
						
							|  |  |  | 		return errServerNotInitialized | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// In gateway mode, bucket targets is not supported.
 | 
					
						
							|  |  |  | 	if globalIsGateway { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-21 01:38:53 +08:00
										 |  |  | 	// Load bucket targets once during boot in background.
 | 
					
						
							|  |  |  | 	go sys.load(ctx, buckets, objAPI) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-10 11:36:00 +08:00
										 |  |  | // UpdateAllTargets updates target to reflect metadata updates
 | 
					
						
							|  |  |  | func (sys *BucketTargetSys) UpdateAllTargets(bucket string, tgts *madmin.BucketTargets) { | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	if sys == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sys.Lock() | 
					
						
							|  |  |  | 	defer sys.Unlock() | 
					
						
							| 
									
										
										
										
											2020-10-10 11:36:00 +08:00
										 |  |  | 	if tgts == nil || tgts.Empty() { | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 		// 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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-10 11:36:00 +08:00
										 |  |  | 	if len(tgts.Targets) > 0 { | 
					
						
							|  |  |  | 		sys.targetsMap[bucket] = tgts.Targets | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-10-10 11:36:00 +08:00
										 |  |  | 	for _, tgt := range tgts.Targets { | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 		tgtClient, err := sys.getRemoteTargetClient(&tgt) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		sys.arnRemotesMap[tgt.Arn] = tgtClient | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-10-10 11:36:00 +08:00
										 |  |  | 	sys.targetsMap[bucket] = tgts.Targets | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | // create minio-go clients for buckets having remote targets
 | 
					
						
							|  |  |  | func (sys *BucketTargetSys) load(ctx context.Context, buckets []BucketInfo, objAPI ObjectLayer) { | 
					
						
							|  |  |  | 	for _, bucket := range buckets { | 
					
						
							|  |  |  | 		cfg, err := globalBucketMetadataSys.GetBucketTargetsConfig(bucket.Name) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-04-04 00:03:42 +08:00
										 |  |  | 			logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if cfg == nil || cfg.Empty() { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if len(cfg.Targets) > 0 { | 
					
						
							|  |  |  | 			sys.targetsMap[bucket.Name] = cfg.Targets | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for _, tgt := range cfg.Targets { | 
					
						
							|  |  |  | 			tgtClient, err := sys.getRemoteTargetClient(&tgt) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2021-04-04 00:03:42 +08:00
										 |  |  | 				logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			sys.arnRemotesMap[tgt.Arn] = tgtClient | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		sys.targetsMap[bucket.Name] = cfg.Targets | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getRemoteTargetInstanceTransport contains a singleton roundtripper.
 | 
					
						
							|  |  |  | var getRemoteTargetInstanceTransport http.RoundTripper | 
					
						
							|  |  |  | var getRemoteTargetInstanceTransportOnce sync.Once | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Returns a minio-go Client configured to access remote host described in replication target config.
 | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | func (sys *BucketTargetSys) getRemoteTargetClient(tcfg *madmin.BucketTarget) (*TargetClient, error) { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	config := tcfg.Credentials | 
					
						
							|  |  |  | 	creds := credentials.NewStaticV4(config.AccessKey, config.SecretKey, "") | 
					
						
							|  |  |  | 	getRemoteTargetInstanceTransportOnce.Do(func() { | 
					
						
							| 
									
										
										
										
											2021-04-17 09:58:26 +08:00
										 |  |  | 		getRemoteTargetInstanceTransport = NewRemoteTargetHTTPTransport() | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 	api, err := minio.New(tcfg.Endpoint, &miniogo.Options{ | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		Creds:     creds, | 
					
						
							|  |  |  | 		Secure:    tcfg.Secure, | 
					
						
							| 
									
										
										
										
											2021-02-09 00:54:27 +08:00
										 |  |  | 		Region:    tcfg.Region, | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		Transport: getRemoteTargetInstanceTransport, | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-13 12:48:04 +08:00
										 |  |  | 	hcDuration := defaultHealthCheckDuration | 
					
						
							|  |  |  | 	if tcfg.HealthCheckDuration >= 1 { // require minimum health check duration of 1 sec.
 | 
					
						
							|  |  |  | 		hcDuration = tcfg.HealthCheckDuration | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	tc := &TargetClient{ | 
					
						
							|  |  |  | 		Client:              api, | 
					
						
							|  |  |  | 		healthCheckDuration: hcDuration, | 
					
						
							|  |  |  | 		replicateSync:       tcfg.ReplicationSync, | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  | 		Bucket:              tcfg.TargetBucket, | 
					
						
							|  |  |  | 		StorageClass:        tcfg.StorageClass, | 
					
						
							| 
									
										
										
										
											2021-04-29 06:26:20 +08:00
										 |  |  | 		disableProxy:        tcfg.DisableProxy, | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	go tc.healthCheck() | 
					
						
							|  |  |  | 	return tc, nil | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getRemoteARN gets existing ARN for an endpoint or generates a new one.
 | 
					
						
							|  |  |  | func (sys *BucketTargetSys) getRemoteARN(bucket string, target *madmin.BucketTarget) string { | 
					
						
							|  |  |  | 	if target == nil { | 
					
						
							|  |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tgts := sys.targetsMap[bucket] | 
					
						
							|  |  |  | 	for _, tgt := range tgts { | 
					
						
							| 
									
										
										
										
											2020-11-26 03:24:50 +08:00
										 |  |  | 		if tgt.Type == target.Type && tgt.TargetBucket == target.TargetBucket && target.URL().String() == tgt.URL().String() { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 			return tgt.Arn | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	if !madmin.ServiceType(target.Type).IsValid() { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-26 03:24:50 +08:00
										 |  |  | 	return generateARN(target) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // generate ARN that is unique to this target type
 | 
					
						
							|  |  |  | func generateARN(t *madmin.BucketTarget) string { | 
					
						
							|  |  |  | 	hash := sha256.New() | 
					
						
							|  |  |  | 	hash.Write([]byte(t.Type)) | 
					
						
							|  |  |  | 	hash.Write([]byte(t.Region)) | 
					
						
							|  |  |  | 	hash.Write([]byte(t.TargetBucket)) | 
					
						
							|  |  |  | 	hashSum := hex.EncodeToString(hash.Sum(nil)) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	arn := madmin.ARN{ | 
					
						
							| 
									
										
										
										
											2020-11-26 03:24:50 +08:00
										 |  |  | 		Type:   t.Type, | 
					
						
							|  |  |  | 		ID:     hashSum, | 
					
						
							|  |  |  | 		Region: t.Region, | 
					
						
							|  |  |  | 		Bucket: t.TargetBucket, | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return arn.String() | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Returns parsed target config. If KMS is configured, remote target is decrypted
 | 
					
						
							|  |  |  | func parseBucketTargetConfig(bucket string, cdata, cmetadata []byte) (*madmin.BucketTargets, error) { | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		data []byte | 
					
						
							|  |  |  | 		err  error | 
					
						
							|  |  |  | 		t    madmin.BucketTargets | 
					
						
							|  |  |  | 		meta map[string]string | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if len(cdata) == 0 { | 
					
						
							|  |  |  | 		return nil, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	data = cdata | 
					
						
							|  |  |  | 	if len(cmetadata) != 0 { | 
					
						
							|  |  |  | 		if err := json.Unmarshal(cmetadata, &meta); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if crypto.S3.IsEncrypted(meta) { | 
					
						
							| 
									
										
										
										
											2021-04-04 00:03:42 +08:00
										 |  |  | 			if data, err = decryptBucketMetadata(cdata, bucket, meta, crypto.Context{ | 
					
						
							|  |  |  | 				bucket:            bucket, | 
					
						
							|  |  |  | 				bucketTargetsFile: bucketTargetsFile, | 
					
						
							|  |  |  | 			}); err != nil { | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = json.Unmarshal(data, &t); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &t, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TargetClient is the struct for remote target client.
 | 
					
						
							|  |  |  | type TargetClient struct { | 
					
						
							|  |  |  | 	*miniogo.Client | 
					
						
							|  |  |  | 	up                  int32 | 
					
						
							|  |  |  | 	healthCheckDuration time.Duration | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  | 	Bucket              string // remote bucket target
 | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 	replicateSync       bool | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  | 	StorageClass        string // storage class on remote
 | 
					
						
							| 
									
										
										
										
											2021-04-29 06:26:20 +08:00
										 |  |  | 	disableProxy        bool | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (tc *TargetClient) isOffline() bool { | 
					
						
							|  |  |  | 	return atomic.LoadInt32(&tc.up) == 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (tc *TargetClient) healthCheck() { | 
					
						
							|  |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  | 		_, err := tc.BucketExists(GlobalContext, tc.Bucket) | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			atomic.StoreInt32(&tc.up, 0) | 
					
						
							| 
									
										
										
										
											2021-01-28 03:22:34 +08:00
										 |  |  | 			time.Sleep(tc.healthCheckDuration) | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		atomic.StoreInt32(&tc.up, 1) | 
					
						
							| 
									
										
										
										
											2021-01-28 03:22:34 +08:00
										 |  |  | 		time.Sleep(tc.healthCheckDuration) | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } |