| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2020-10-13 05:19:46 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 17:02:32 +08:00
										 |  |  | 	jsoniter "github.com/json-iterator/go" | 
					
						
							| 
									
										
										
										
											2023-06-20 08:53:08 +08:00
										 |  |  | 	"github.com/minio/madmin-go/v3" | 
					
						
							| 
									
										
										
										
											2022-06-25 02:12:52 +08:00
										 |  |  | 	"github.com/minio/minio-go/v7" | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	"github.com/minio/minio-go/v7/pkg/credentials" | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 	"github.com/minio/minio/internal/bucket/replication" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/crypto" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/kms" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 	defaultHealthCheckDuration = 5 * time.Second | 
					
						
							|  |  |  | 	// default interval for reload of all remote target endpoints
 | 
					
						
							|  |  |  | 	defaultHealthCheckReloadDuration = 30 * time.Minute | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 	hMutex        sync.RWMutex | 
					
						
							|  |  |  | 	hc            map[string]epHealth | 
					
						
							|  |  |  | 	hcClient      *madmin.AnonymousClient | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | type latencyStat struct { | 
					
						
							|  |  |  | 	lastmin lastMinuteLatency | 
					
						
							|  |  |  | 	curr    time.Duration | 
					
						
							|  |  |  | 	avg     time.Duration | 
					
						
							|  |  |  | 	peak    time.Duration | 
					
						
							|  |  |  | 	N       int64 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *latencyStat) update(d time.Duration) { | 
					
						
							|  |  |  | 	l.lastmin.add(d) | 
					
						
							|  |  |  | 	l.N++ | 
					
						
							|  |  |  | 	if d > l.peak { | 
					
						
							|  |  |  | 		l.peak = d | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	l.curr = l.lastmin.getTotal().avg() | 
					
						
							|  |  |  | 	l.avg = time.Duration((int64(l.avg)*(l.N-1) + int64(l.curr)) / l.N) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | // epHealth struct represents health of a replication target endpoint.
 | 
					
						
							|  |  |  | type epHealth struct { | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 	Endpoint        string | 
					
						
							|  |  |  | 	Scheme          string | 
					
						
							|  |  |  | 	Online          bool | 
					
						
							|  |  |  | 	lastOnline      time.Time | 
					
						
							|  |  |  | 	lastHCAt        time.Time | 
					
						
							|  |  |  | 	offlineDuration time.Duration | 
					
						
							|  |  |  | 	latency         latencyStat | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // isOffline returns current liveness result of remote target. Add endpoint to
 | 
					
						
							| 
									
										
										
										
											2023-08-02 01:54:26 +08:00
										 |  |  | // healthCheck map if missing and default to online status
 | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | func (sys *BucketTargetSys) isOffline(ep *url.URL) bool { | 
					
						
							|  |  |  | 	sys.hMutex.RLock() | 
					
						
							|  |  |  | 	defer sys.hMutex.RUnlock() | 
					
						
							|  |  |  | 	if h, ok := sys.hc[ep.Host]; ok { | 
					
						
							|  |  |  | 		return !h.Online | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	go sys.initHC(ep) | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-25 00:24:26 +08:00
										 |  |  | // markOffline sets endpoint to offline if network i/o timeout seen.
 | 
					
						
							|  |  |  | func (sys *BucketTargetSys) markOffline(ep *url.URL) { | 
					
						
							|  |  |  | 	sys.hMutex.Lock() | 
					
						
							|  |  |  | 	defer sys.hMutex.Unlock() | 
					
						
							|  |  |  | 	if h, ok := sys.hc[ep.Host]; ok { | 
					
						
							|  |  |  | 		h.Online = false | 
					
						
							|  |  |  | 		sys.hc[ep.Host] = h | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | func (sys *BucketTargetSys) initHC(ep *url.URL) { | 
					
						
							|  |  |  | 	sys.hMutex.Lock() | 
					
						
							|  |  |  | 	sys.hc[ep.Host] = epHealth{ | 
					
						
							|  |  |  | 		Endpoint: ep.Host, | 
					
						
							|  |  |  | 		Scheme:   ep.Scheme, | 
					
						
							|  |  |  | 		Online:   true, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sys.hMutex.Unlock() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newHCClient initializes an anonymous client for performing health check on the remote endpoints
 | 
					
						
							|  |  |  | func newHCClient() *madmin.AnonymousClient { | 
					
						
							|  |  |  | 	clnt, e := madmin.NewAnonymousClientNoEndpoint() | 
					
						
							|  |  |  | 	if e != nil { | 
					
						
							|  |  |  | 		logger.LogOnceIf(GlobalContext, fmt.Errorf("WARNING: Unable to initialize health check client"), string(replicationSubsystem)) | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	clnt.SetCustomTransport(globalRemoteTargetTransport) | 
					
						
							|  |  |  | 	return clnt | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // heartBeat performs liveness check on remote endpoints.
 | 
					
						
							|  |  |  | func (sys *BucketTargetSys) heartBeat(ctx context.Context) { | 
					
						
							|  |  |  | 	hcTimer := time.NewTimer(defaultHealthCheckDuration) | 
					
						
							|  |  |  | 	defer hcTimer.Stop() | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-hcTimer.C: | 
					
						
							|  |  |  | 			sys.hMutex.RLock() | 
					
						
							| 
									
										
										
										
											2023-01-27 03:11:54 +08:00
										 |  |  | 			eps := make([]madmin.ServerProperties, 0, len(sys.hc)) | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 			for _, ep := range sys.hc { | 
					
						
							|  |  |  | 				eps = append(eps, madmin.ServerProperties{Endpoint: ep.Endpoint, Scheme: ep.Scheme}) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			sys.hMutex.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if len(eps) > 0 { | 
					
						
							|  |  |  | 				cctx, cancel := context.WithTimeout(ctx, 30*time.Second) | 
					
						
							| 
									
										
										
										
											2023-01-27 03:11:54 +08:00
										 |  |  | 				m := make(map[string]epHealth, len(eps)) | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 				start := time.Now() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 				for result := range sys.hcClient.Alive(cctx, madmin.AliveOpts{}, eps...) { | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 					var lastOnline time.Time | 
					
						
							|  |  |  | 					var offline time.Duration | 
					
						
							|  |  |  | 					//	var deploymentID string
 | 
					
						
							|  |  |  | 					sys.hMutex.RLock() | 
					
						
							|  |  |  | 					prev, ok := sys.hc[result.Endpoint.Host] | 
					
						
							|  |  |  | 					sys.hMutex.RUnlock() | 
					
						
							|  |  |  | 					if ok { | 
					
						
							|  |  |  | 						if prev.Online != result.Online || !result.Online { | 
					
						
							|  |  |  | 							if !prev.lastHCAt.IsZero() { | 
					
						
							|  |  |  | 								offline = time.Since(prev.lastHCAt) + prev.offlineDuration | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								offline = prev.offlineDuration | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} else if result.Online { | 
					
						
							|  |  |  | 							offline = prev.offlineDuration | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					lastOnline = prev.lastOnline | 
					
						
							|  |  |  | 					if result.Online { | 
					
						
							|  |  |  | 						lastOnline = time.Now() | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					l := prev.latency | 
					
						
							|  |  |  | 					l.update(time.Since(start)) | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 					m[result.Endpoint.Host] = epHealth{ | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 						Endpoint:        result.Endpoint.Host, | 
					
						
							|  |  |  | 						Scheme:          result.Endpoint.Scheme, | 
					
						
							|  |  |  | 						Online:          result.Online, | 
					
						
							|  |  |  | 						lastOnline:      lastOnline, | 
					
						
							|  |  |  | 						offlineDuration: offline, | 
					
						
							|  |  |  | 						lastHCAt:        time.Now(), | 
					
						
							|  |  |  | 						latency:         l, | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-09-21 18:08:45 +08:00
										 |  |  | 				cancel() | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 				sys.hMutex.Lock() | 
					
						
							|  |  |  | 				sys.hc = m | 
					
						
							|  |  |  | 				sys.hMutex.Unlock() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			hcTimer.Reset(defaultHealthCheckDuration) | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-02 01:54:26 +08:00
										 |  |  | // periodically rebuild the healthCheck map from list of targets to clear
 | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | // out stale endpoints
 | 
					
						
							|  |  |  | func (sys *BucketTargetSys) reloadHealthCheckers(ctx context.Context) { | 
					
						
							|  |  |  | 	m := make(map[string]epHealth) | 
					
						
							|  |  |  | 	tgts := sys.ListTargets(ctx, "", "") | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 	sys.hMutex.Lock() | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 	for _, t := range tgts { | 
					
						
							|  |  |  | 		if _, ok := m[t.Endpoint]; !ok { | 
					
						
							|  |  |  | 			scheme := "http" | 
					
						
							|  |  |  | 			if t.Secure { | 
					
						
							|  |  |  | 				scheme = "https" | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 			epHealth := epHealth{ | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 				Online:   true, | 
					
						
							|  |  |  | 				Endpoint: t.Endpoint, | 
					
						
							|  |  |  | 				Scheme:   scheme, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 			if prev, ok := sys.hc[t.Endpoint]; ok { | 
					
						
							|  |  |  | 				epHealth.lastOnline = prev.lastOnline | 
					
						
							|  |  |  | 				epHealth.offlineDuration = prev.offlineDuration | 
					
						
							|  |  |  | 				epHealth.lastHCAt = prev.lastHCAt | 
					
						
							|  |  |  | 				epHealth.latency = prev.latency | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			m[t.Endpoint] = epHealth | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// swap out the map
 | 
					
						
							|  |  |  | 	sys.hc = m | 
					
						
							|  |  |  | 	sys.hMutex.Unlock() | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | func (sys *BucketTargetSys) healthStats() map[string]epHealth { | 
					
						
							|  |  |  | 	sys.hMutex.RLock() | 
					
						
							|  |  |  | 	defer sys.hMutex.RUnlock() | 
					
						
							|  |  |  | 	m := make(map[string]epHealth, len(sys.hc)) | 
					
						
							|  |  |  | 	for k, v := range sys.hc { | 
					
						
							|  |  |  | 		m[k] = v | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return m | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 	h := sys.healthStats() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	if bucket != "" { | 
					
						
							|  |  |  | 		if ts, err := sys.ListBucketTargets(ctx, bucket); err == nil { | 
					
						
							|  |  |  | 			for _, t := range ts.Targets { | 
					
						
							|  |  |  | 				if string(t.Type) == arnType || arnType == "" { | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 					if hs, ok := h[t.URL().Host]; ok { | 
					
						
							|  |  |  | 						t.TotalDowntime = hs.offlineDuration | 
					
						
							|  |  |  | 						t.Online = hs.Online | 
					
						
							|  |  |  | 						t.LastOnline = hs.lastOnline | 
					
						
							|  |  |  | 						t.Latency = madmin.LatencyStat{ | 
					
						
							|  |  |  | 							Curr: hs.latency.curr, | 
					
						
							|  |  |  | 							Avg:  hs.latency.avg, | 
					
						
							|  |  |  | 							Max:  hs.latency.peak, | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 					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 == "" { | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 				if hs, ok := h[t.URL().Host]; ok { | 
					
						
							|  |  |  | 					t.TotalDowntime = hs.offlineDuration | 
					
						
							|  |  |  | 					t.Online = hs.Online | 
					
						
							|  |  |  | 					t.LastOnline = hs.lastOnline | 
					
						
							|  |  |  | 					t.Latency = madmin.LatencyStat{ | 
					
						
							|  |  |  | 						Curr: hs.latency.curr, | 
					
						
							|  |  |  | 						Avg:  hs.latency.avg, | 
					
						
							|  |  |  | 						Max:  hs.latency.peak, | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 				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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-24 23:39:58 +08:00
										 |  |  | // Delete clears targets present for a bucket
 | 
					
						
							|  |  |  | func (sys *BucketTargetSys) Delete(bucket string) { | 
					
						
							|  |  |  | 	sys.Lock() | 
					
						
							|  |  |  | 	defer sys.Unlock() | 
					
						
							|  |  |  | 	tgts, ok := sys.targetsMap[bucket] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, t := range tgts { | 
					
						
							|  |  |  | 		delete(sys.arnRemotesMap, t.Arn) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	delete(sys.targetsMap, 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 { | 
					
						
							|  |  |  | 	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 { | 
					
						
							| 
									
										
										
										
											2023-04-07 22:50:46 +08:00
										 |  |  | 		return BucketRemoteTargetNotFound{Bucket: tgt.TargetBucket, Err: err} | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-25 11:09:05 +08:00
										 |  |  | 	// validate if target credentials are ok
 | 
					
						
							| 
									
										
										
										
											2022-10-14 08:46:49 +08:00
										 |  |  | 	exists, err := clnt.BucketExists(ctx, tgt.TargetBucket) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-12-14 19:24:06 +08:00
										 |  |  | 		switch minio.ToErrorResponse(err).Code { | 
					
						
							|  |  |  | 		case "NoSuchBucket": | 
					
						
							| 
									
										
										
										
											2023-05-08 22:51:59 +08:00
										 |  |  | 			return BucketRemoteTargetNotFound{Bucket: tgt.TargetBucket, Err: err} | 
					
						
							| 
									
										
										
										
											2022-12-14 19:24:06 +08:00
										 |  |  | 		case "AccessDenied": | 
					
						
							|  |  |  | 			return RemoteTargetConnectionErr{Bucket: tgt.TargetBucket, AccessKey: tgt.Credentials.AccessKey, Err: err} | 
					
						
							| 
									
										
										
										
											2020-11-25 11:09:05 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-12-14 19:24:06 +08:00
										 |  |  | 		return RemoteTargetConnectionErr{Bucket: tgt.TargetBucket, AccessKey: tgt.Credentials.AccessKey, Err: err} | 
					
						
							| 
									
										
										
										
											2020-11-25 11:09:05 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-10-14 08:46:49 +08:00
										 |  |  | 	if !exists { | 
					
						
							|  |  |  | 		return BucketRemoteTargetNotFound{Bucket: tgt.TargetBucket} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	if tgt.Type == madmin.ReplicationService { | 
					
						
							|  |  |  | 		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 { | 
					
						
							| 
									
										
										
										
											2022-12-14 19:24:06 +08:00
										 |  |  | 			return RemoteTargetConnectionErr{Bucket: tgt.TargetBucket, Err: err, AccessKey: tgt.Credentials.AccessKey} | 
					
						
							| 
									
										
										
										
											2020-09-04 23:48:38 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-10-14 08:46:49 +08:00
										 |  |  | 		if !vcfg.Enabled() { | 
					
						
							| 
									
										
										
										
											2020-10-09 01:54:11 +08:00
										 |  |  | 			return BucketRemoteTargetNotVersioned{Bucket: tgt.TargetBucket} | 
					
						
							| 
									
										
										
										
											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] | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 			if t.Arn == tgt.Arn { | 
					
						
							|  |  |  | 				if !update { | 
					
						
							|  |  |  | 					return BucketRemoteAlreadyExists{Bucket: t.TargetBucket} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				newtgts[idx] = *tgt | 
					
						
							|  |  |  | 				found = true | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-12-24 07:44:48 +08:00
										 |  |  | 			// fail if endpoint is already present in list of targets and not a matching ARN
 | 
					
						
							|  |  |  | 			if t.Endpoint == tgt.Endpoint { | 
					
						
							|  |  |  | 				return BucketRemoteAlreadyExists{Bucket: t.TargetBucket} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		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) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	sys.targetsMap[bucket] = newtgts | 
					
						
							|  |  |  | 	sys.arnRemotesMap[tgt.Arn] = clnt | 
					
						
							| 
									
										
										
										
											2023-01-19 21:22:16 +08:00
										 |  |  | 	sys.updateBandwidthLimit(bucket, tgt.Arn, tgt.BandwidthLimit) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-19 21:22:16 +08:00
										 |  |  | func (sys *BucketTargetSys) updateBandwidthLimit(bucket, arn string, limit int64) { | 
					
						
							| 
									
										
										
										
											2021-06-25 09:29:30 +08:00
										 |  |  | 	if limit == 0 { | 
					
						
							| 
									
										
										
										
											2023-01-19 21:22:16 +08:00
										 |  |  | 		globalBucketMonitor.DeleteBucketThrottle(bucket, arn) | 
					
						
							| 
									
										
										
										
											2021-06-25 09:29:30 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Setup bandwidth throttling
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-19 21:22:16 +08:00
										 |  |  | 	globalBucketMonitor.SetBandwidthLimit(bucket, arn, limit) | 
					
						
							| 
									
										
										
										
											2021-06-25 09:29:30 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | // RemoveTarget - removes a remote bucket target for this source bucket.
 | 
					
						
							|  |  |  | func (sys *BucketTargetSys) RemoveTarget(ctx context.Context, bucket, arnStr string) error { | 
					
						
							|  |  |  | 	if arnStr == "" { | 
					
						
							|  |  |  | 		return BucketRemoteArnInvalid{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-30 07:41:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	arn, err := madmin.ParseARN(arnStr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return BucketRemoteArnInvalid{Bucket: bucket} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-30 07:41:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	if arn.Type == madmin.ReplicationService { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		// reject removal of remote target if replication configuration is present
 | 
					
						
							|  |  |  | 		rcfg, err := getReplicationConfig(ctx, bucket) | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 		if err == nil { | 
					
						
							| 
									
										
										
										
											2022-05-25 10:40:45 +08:00
										 |  |  | 			for _, tgtArn := range rcfg.FilterTargetArns(replication.ObjectOpts{OpType: replication.AllReplicationType}) { | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 				if err == nil && (tgtArn == arnStr || rcfg.RoleArn == arnStr) { | 
					
						
							|  |  |  | 					sys.RLock() | 
					
						
							|  |  |  | 					_, ok := sys.arnRemotesMap[arnStr] | 
					
						
							|  |  |  | 					sys.RUnlock() | 
					
						
							|  |  |  | 					if ok { | 
					
						
							|  |  |  | 						return BucketRemoteRemoveDisallowed{Bucket: bucket} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							| 
									
										
										
										
											2023-01-19 21:22:16 +08:00
										 |  |  | 	sys.updateBandwidthLimit(bucket, arnStr, 0) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 01:54:11 +08:00
										 |  |  | // GetRemoteTargetClient returns minio-go client for replication target instance
 | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | func (sys *BucketTargetSys) GetRemoteTargetClient(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.
 | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | func NewBucketTargetSys(ctx context.Context) *BucketTargetSys { | 
					
						
							|  |  |  | 	sys := &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), | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 		hc:            make(map[string]epHealth), | 
					
						
							|  |  |  | 		hcClient:      newHCClient(), | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-08-02 01:54:26 +08:00
										 |  |  | 	// reload healthCheck endpoints map periodically to remove stale endpoints from the map.
 | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		rTimer := time.NewTimer(defaultHealthCheckReloadDuration) | 
					
						
							|  |  |  | 		defer rTimer.Stop() | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-rTimer.C: | 
					
						
							|  |  |  | 				sys.reloadHealthCheckers(ctx) | 
					
						
							|  |  |  | 				rTimer.Reset(defaultHealthCheckReloadDuration) | 
					
						
							|  |  |  | 			case <-ctx.Done(): | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	go sys.heartBeat(ctx) | 
					
						
							|  |  |  | 	return sys | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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() | 
					
						
							| 
									
										
										
										
											2022-06-25 02:12:52 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Remove existingtarget and arn association
 | 
					
						
							| 
									
										
										
										
											2023-09-06 11:21:59 +08:00
										 |  |  | 	if stgts, ok := sys.targetsMap[bucket]; ok { | 
					
						
							|  |  |  | 		for _, t := range stgts { | 
					
						
							| 
									
										
										
										
											2022-06-25 02:12:52 +08:00
										 |  |  | 			delete(sys.arnRemotesMap, t.Arn) | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		delete(sys.targetsMap, bucket) | 
					
						
							| 
									
										
										
										
											2022-06-25 02:12:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-06 11:21:59 +08:00
										 |  |  | 	if tgts != nil { | 
					
						
							|  |  |  | 		for _, tgt := range tgts.Targets { | 
					
						
							|  |  |  | 			tgtClient, err := sys.getRemoteTargetClient(&tgt) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			sys.arnRemotesMap[tgt.Arn] = tgtClient | 
					
						
							|  |  |  | 			sys.updateBandwidthLimit(bucket, tgt.Arn, tgt.BandwidthLimit) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-06 11:21:59 +08:00
										 |  |  | 		if !tgts.Empty() { | 
					
						
							|  |  |  | 			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
 | 
					
						
							| 
									
										
										
										
											2021-09-21 08:41:01 +08:00
										 |  |  | func (sys *BucketTargetSys) set(bucket BucketInfo, meta BucketMetadata) { | 
					
						
							|  |  |  | 	cfg := meta.bucketTargetConfig | 
					
						
							|  |  |  | 	if cfg == nil || cfg.Empty() { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sys.Lock() | 
					
						
							|  |  |  | 	defer sys.Unlock() | 
					
						
							|  |  |  | 	for _, tgt := range cfg.Targets { | 
					
						
							|  |  |  | 		tgtClient, err := sys.getRemoteTargetClient(&tgt) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-09-21 08:41:01 +08:00
										 |  |  | 			logger.LogIf(GlobalContext, err) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-09-21 08:41:01 +08:00
										 |  |  | 		sys.arnRemotesMap[tgt.Arn] = tgtClient | 
					
						
							| 
									
										
										
										
											2023-01-19 21:22:16 +08:00
										 |  |  | 		sys.updateBandwidthLimit(bucket.Name, tgt.Arn, tgt.BandwidthLimit) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-21 08:41:01 +08:00
										 |  |  | 	sys.targetsMap[bucket.Name] = cfg.Targets | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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, "") | 
					
						
							| 
									
										
										
										
											2021-07-29 06:20:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-06 03:18:50 +08:00
										 |  |  | 	api, err := minio.New(tcfg.Endpoint, &minio.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, | 
					
						
							| 
									
										
										
										
											2022-06-23 07:28:25 +08:00
										 |  |  | 		Transport: globalRemoteTargetTransport, | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-15 05:19:30 +08:00
										 |  |  | 	api.SetAppInfo("minio-replication-target", ReleaseTag+" "+tcfg.Arn) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-09-19 04:31:35 +08:00
										 |  |  | 		ARN:                 tcfg.Arn, | 
					
						
							|  |  |  | 		ResetID:             tcfg.ResetID, | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 		Endpoint:            tcfg.Endpoint, | 
					
						
							|  |  |  | 		Secure:              tcfg.Secure, | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return tc, nil | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getRemoteARN gets existing ARN for an endpoint or generates a new one.
 | 
					
						
							| 
									
										
										
										
											2023-02-20 18:36:13 +08:00
										 |  |  | func (sys *BucketTargetSys) getRemoteARN(bucket string, target *madmin.BucketTarget, deplID string) (arn string, exists bool) { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	if target == nil { | 
					
						
							| 
									
										
										
										
											2022-12-14 19:24:06 +08:00
										 |  |  | 		return | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 	sys.RLock() | 
					
						
							|  |  |  | 	defer sys.RUnlock() | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	tgts := sys.targetsMap[bucket] | 
					
						
							|  |  |  | 	for _, tgt := range tgts { | 
					
						
							| 
									
										
										
										
											2023-02-15 05:19:30 +08:00
										 |  |  | 		if tgt.Type == target.Type && | 
					
						
							|  |  |  | 			tgt.TargetBucket == target.TargetBucket && | 
					
						
							|  |  |  | 			target.URL().String() == tgt.URL().String() && | 
					
						
							|  |  |  | 			tgt.Credentials.AccessKey == target.Credentials.AccessKey { | 
					
						
							| 
									
										
										
										
											2022-12-14 19:24:06 +08:00
										 |  |  | 			return tgt.Arn, true | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-05-25 00:28:19 +08:00
										 |  |  | 	if !target.Type.IsValid() { | 
					
						
							| 
									
										
										
										
											2022-12-14 19:24:06 +08:00
										 |  |  | 		return | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-20 18:36:13 +08:00
										 |  |  | 	return generateARN(target, deplID), false | 
					
						
							| 
									
										
										
										
											2020-11-26 03:24:50 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-14 23:16:40 +08:00
										 |  |  | // getRemoteARNForPeer returns the remote target for a peer site in site replication
 | 
					
						
							|  |  |  | func (sys *BucketTargetSys) getRemoteARNForPeer(bucket string, peer madmin.PeerInfo) string { | 
					
						
							| 
									
										
										
										
											2023-08-30 16:00:59 +08:00
										 |  |  | 	sys.RLock() | 
					
						
							|  |  |  | 	defer sys.RUnlock() | 
					
						
							| 
									
										
										
										
											2022-11-14 23:16:40 +08:00
										 |  |  | 	tgts := sys.targetsMap[bucket] | 
					
						
							|  |  |  | 	for _, target := range tgts { | 
					
						
							|  |  |  | 		ep, _ := url.Parse(peer.Endpoint) | 
					
						
							|  |  |  | 		if target.SourceBucket == bucket && | 
					
						
							|  |  |  | 			target.TargetBucket == bucket && | 
					
						
							|  |  |  | 			target.Endpoint == ep.Host && | 
					
						
							|  |  |  | 			target.Secure == (ep.Scheme == "https") && | 
					
						
							|  |  |  | 			target.Type == madmin.ReplicationService { | 
					
						
							|  |  |  | 			return target.Arn | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-26 03:24:50 +08:00
										 |  |  | // generate ARN that is unique to this target type
 | 
					
						
							| 
									
										
										
										
											2023-02-20 18:36:13 +08:00
										 |  |  | func generateARN(t *madmin.BucketTarget, deplID string) string { | 
					
						
							|  |  |  | 	uuid := deplID | 
					
						
							|  |  |  | 	if uuid == "" { | 
					
						
							|  |  |  | 		uuid = mustGetUUID() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	arn := madmin.ARN{ | 
					
						
							| 
									
										
										
										
											2020-11-26 03:24:50 +08:00
										 |  |  | 		Type:   t.Type, | 
					
						
							| 
									
										
										
										
											2023-02-20 18:36:13 +08:00
										 |  |  | 		ID:     uuid, | 
					
						
							| 
									
										
										
										
											2020-11-26 03:24:50 +08:00
										 |  |  | 		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 | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 	json := jsoniter.ConfigCompatibleWithStandardLibrary | 
					
						
							| 
									
										
										
										
											2020-12-22 08:21:33 +08:00
										 |  |  | 	if len(cmetadata) != 0 { | 
					
						
							|  |  |  | 		if err := json.Unmarshal(cmetadata, &meta); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if crypto.S3.IsEncrypted(meta) { | 
					
						
							| 
									
										
										
										
											2021-05-11 09:15:11 +08:00
										 |  |  | 			if data, err = decryptBucketMetadata(cdata, bucket, meta, kms.Context{ | 
					
						
							| 
									
										
										
										
											2021-04-04 00:03:42 +08:00
										 |  |  | 				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 { | 
					
						
							| 
									
										
										
										
											2022-12-06 03:18:50 +08:00
										 |  |  | 	*minio.Client | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | 	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 | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 	ARN                 string // ARN to uniquely identify remote target
 | 
					
						
							| 
									
										
										
										
											2021-09-19 04:31:35 +08:00
										 |  |  | 	ResetID             string | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 	Endpoint            string | 
					
						
							|  |  |  | 	Secure              bool | 
					
						
							| 
									
										
										
										
											2021-01-12 14:36:51 +08:00
										 |  |  | } |