| 
									
										
										
										
											2022-11-16 23:59:10 +08:00
										 |  |  | // Copyright (c) 2015-2022 MinIO, Inc.
 | 
					
						
							| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | //
 | 
					
						
							|  |  |  | // 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/>.
 | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2023-11-29 02:39:02 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	"math/rand" | 
					
						
							| 
									
										
										
										
											2021-08-30 23:27:39 +08:00
										 |  |  | 	"reflect" | 
					
						
							| 
									
										
										
										
											2023-11-29 02:39:02 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-15 00:38:05 +08:00
										 |  |  | 	"github.com/minio/minio-go/v7/pkg/set" | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	"github.com/minio/minio/internal/grid" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2023-09-05 03:57:37 +08:00
										 |  |  | 	"github.com/minio/pkg/v2/env" | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // To abstract a node over network.
 | 
					
						
							|  |  |  | type bootstrapRESTServer struct{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | //go:generate msgp -file=$GOFILE
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | // ServerSystemConfig - captures information about server configuration.
 | 
					
						
							|  |  |  | type ServerSystemConfig struct { | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	NEndpoints int | 
					
						
							|  |  |  | 	CmdLines   []string | 
					
						
							|  |  |  | 	MinioEnv   map[string]string | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Diff - returns error on first difference found in two configs.
 | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | func (s1 *ServerSystemConfig) Diff(s2 *ServerSystemConfig) error { | 
					
						
							|  |  |  | 	ns1 := s1.NEndpoints | 
					
						
							|  |  |  | 	ns2 := s2.NEndpoints | 
					
						
							|  |  |  | 	if ns1 != ns2 { | 
					
						
							|  |  |  | 		return fmt.Errorf("Expected number of endpoints %d, seen %d", ns1, ns2) | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	for i, cmdLine := range s1.CmdLines { | 
					
						
							|  |  |  | 		if cmdLine != s2.CmdLines[i] { | 
					
						
							|  |  |  | 			return fmt.Errorf("Expected command line argument %s, seen %s", cmdLine, | 
					
						
							|  |  |  | 				s2.CmdLines[i]) | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-30 23:27:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-29 02:39:02 +08:00
										 |  |  | 	if reflect.DeepEqual(s1.MinioEnv, s2.MinioEnv) { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Report differences in environment variables.
 | 
					
						
							|  |  |  | 	var missing []string | 
					
						
							|  |  |  | 	var mismatching []string | 
					
						
							|  |  |  | 	for k, v := range s1.MinioEnv { | 
					
						
							|  |  |  | 		ev, ok := s2.MinioEnv[k] | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			missing = append(missing, k) | 
					
						
							|  |  |  | 		} else if v != ev { | 
					
						
							|  |  |  | 			mismatching = append(mismatching, k) | 
					
						
							| 
									
										
										
										
											2021-12-23 03:43:01 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-11-29 02:39:02 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	var extra []string | 
					
						
							|  |  |  | 	for k := range s2.MinioEnv { | 
					
						
							|  |  |  | 		_, ok := s1.MinioEnv[k] | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			extra = append(extra, k) | 
					
						
							| 
									
										
										
										
											2021-12-23 03:43:01 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-11-29 02:39:02 +08:00
										 |  |  | 	msg := "Expected same MINIO_ environment variables and values across all servers: " | 
					
						
							|  |  |  | 	if len(missing) > 0 { | 
					
						
							|  |  |  | 		msg += fmt.Sprintf(`Missing environment values: %v. `, missing) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(mismatching) > 0 { | 
					
						
							|  |  |  | 		msg += fmt.Sprintf(`Mismatching environment values: %v. `, mismatching) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(extra) > 0 { | 
					
						
							|  |  |  | 		msg += fmt.Sprintf(`Extra environment values: %v. `, extra) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return errors.New(strings.TrimSpace(msg)) | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-02 02:37:58 +08:00
										 |  |  | var skipEnvs = map[string]struct{}{ | 
					
						
							| 
									
										
										
										
											2023-03-20 16:40:24 +08:00
										 |  |  | 	"MINIO_OPTS":          {}, | 
					
						
							|  |  |  | 	"MINIO_CERT_PASSWD":   {}, | 
					
						
							|  |  |  | 	"MINIO_SERVER_DEBUG":  {}, | 
					
						
							|  |  |  | 	"MINIO_DSYNC_TRACE":   {}, | 
					
						
							|  |  |  | 	"MINIO_ROOT_USER":     {}, | 
					
						
							|  |  |  | 	"MINIO_ROOT_PASSWORD": {}, | 
					
						
							|  |  |  | 	"MINIO_ACCESS_KEY":    {}, | 
					
						
							|  |  |  | 	"MINIO_SECRET_KEY":    {}, | 
					
						
							| 
									
										
										
										
											2021-09-02 02:37:58 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | func getServerSystemCfg() *ServerSystemConfig { | 
					
						
							| 
									
										
										
										
											2021-08-30 23:27:39 +08:00
										 |  |  | 	envs := env.List("MINIO_") | 
					
						
							|  |  |  | 	envValues := make(map[string]string, len(envs)) | 
					
						
							|  |  |  | 	for _, envK := range envs { | 
					
						
							| 
									
										
										
										
											2021-09-02 02:37:58 +08:00
										 |  |  | 		// skip certain environment variables as part
 | 
					
						
							|  |  |  | 		// of the whitelist and could be configured
 | 
					
						
							|  |  |  | 		// differently on each nodes, update skipEnvs()
 | 
					
						
							|  |  |  | 		// map if there are such environment values
 | 
					
						
							|  |  |  | 		if _, ok := skipEnvs[envK]; ok { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-03-20 16:40:24 +08:00
										 |  |  | 		envValues[envK] = logger.HashString(env.Get(envK, "")) | 
					
						
							| 
									
										
										
										
											2021-08-30 23:27:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	scfg := &ServerSystemConfig{NEndpoints: globalEndpoints.NEndpoints(), MinioEnv: envValues} | 
					
						
							|  |  |  | 	var cmdLines []string | 
					
						
							|  |  |  | 	for _, ep := range globalEndpoints { | 
					
						
							|  |  |  | 		cmdLines = append(cmdLines, ep.CmdLine) | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	scfg.CmdLines = cmdLines | 
					
						
							|  |  |  | 	return scfg | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | func (s *bootstrapRESTServer) VerifyHandler(params *grid.MSS) (*ServerSystemConfig, *grid.RemoteErr) { | 
					
						
							|  |  |  | 	return getServerSystemCfg(), nil | 
					
						
							| 
									
										
										
										
											2023-03-20 16:40:24 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | var serverVerifyHandler = grid.NewSingleHandler[*grid.MSS, *ServerSystemConfig](grid.HandlerServerVerify, grid.NewMSS, func() *ServerSystemConfig { return &ServerSystemConfig{} }) | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // registerBootstrapRESTHandlers - register bootstrap rest router.
 | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | func registerBootstrapRESTHandlers(gm *grid.Manager) { | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	server := &bootstrapRESTServer{} | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	logger.FatalIf(serverVerifyHandler.Register(gm, server.VerifyHandler), "unable to register handler") | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-25 03:43:40 +08:00
										 |  |  | // client to talk to bootstrap NEndpoints.
 | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | type bootstrapRESTClient struct { | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	gridConn *grid.Connection | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | // Verify function verifies the server config.
 | 
					
						
							|  |  |  | func (client *bootstrapRESTClient) Verify(ctx context.Context, srcCfg *ServerSystemConfig) (err error) { | 
					
						
							|  |  |  | 	if newObjectLayerFn() != nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 04:41:20 +08:00
										 |  |  | 	recvCfg, err := serverVerifyHandler.Call(ctx, client.gridConn, grid.NewMSS()) | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-02 04:41:20 +08:00
										 |  |  | 	// We do not need the response after returning.
 | 
					
						
							|  |  |  | 	defer serverVerifyHandler.PutResponse(recvCfg) | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	return srcCfg.Diff(recvCfg) | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stringer provides a canonicalized representation of node.
 | 
					
						
							|  |  |  | func (client *bootstrapRESTClient) String() string { | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	return client.gridConn.String() | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | func verifyServerSystemConfig(ctx context.Context, endpointServerPools EndpointServerPools, gm *grid.Manager) error { | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	srcCfg := getServerSystemCfg() | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	clnts := newBootstrapRESTClients(endpointServerPools, gm) | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	var onlineServers int | 
					
						
							| 
									
										
										
										
											2022-11-16 23:59:10 +08:00
										 |  |  | 	var offlineEndpoints []error | 
					
						
							|  |  |  | 	var incorrectConfigs []error | 
					
						
							| 
									
										
										
										
											2020-07-11 00:26:21 +08:00
										 |  |  | 	var retries int | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 	var mu sync.Mutex | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	for onlineServers < len(clnts)/2 { | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 		var wg sync.WaitGroup | 
					
						
							|  |  |  | 		wg.Add(len(clnts)) | 
					
						
							|  |  |  | 		onlineServers = 0 | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 		for _, clnt := range clnts { | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 			go func(clnt *bootstrapRESTClient) { | 
					
						
							|  |  |  | 				defer wg.Done() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if clnt.gridConn.State() != grid.StateConnected { | 
					
						
							|  |  |  | 					mu.Lock() | 
					
						
							|  |  |  | 					offlineEndpoints = append(offlineEndpoints, fmt.Errorf("%s is unreachable: %w", clnt, grid.ErrDisconnected)) | 
					
						
							|  |  |  | 					mu.Unlock() | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				ctx, cancel := context.WithTimeout(ctx, 2*time.Second) | 
					
						
							|  |  |  | 				defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				err := clnt.Verify(ctx, srcCfg) | 
					
						
							|  |  |  | 				mu.Lock() | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					bootstrapTraceMsg(fmt.Sprintf("clnt.Verify: %v, endpoint: %s", err, clnt)) | 
					
						
							|  |  |  | 					if !isNetworkError(err) { | 
					
						
							|  |  |  | 						logger.LogOnceIf(context.Background(), fmt.Errorf("%s has incorrect configuration: %w", clnt, err), "incorrect_"+clnt.String()) | 
					
						
							|  |  |  | 						incorrectConfigs = append(incorrectConfigs, fmt.Errorf("%s has incorrect configuration: %w", clnt, err)) | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						offlineEndpoints = append(offlineEndpoints, fmt.Errorf("%s is unreachable: %w", clnt, err)) | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2022-11-16 23:59:10 +08:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 					onlineServers++ | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 				mu.Unlock() | 
					
						
							|  |  |  | 			}(clnt) | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 		wg.Wait() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-09 00:10:55 +08:00
										 |  |  | 		select { | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							|  |  |  | 			return ctx.Err() | 
					
						
							|  |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 			// Sleep and stagger to avoid blocked CPU and thundering
 | 
					
						
							|  |  |  | 			// herd upon start up sequence.
 | 
					
						
							|  |  |  | 			time.Sleep(25*time.Millisecond + time.Duration(rand.Int63n(int64(100*time.Millisecond)))) | 
					
						
							| 
									
										
										
										
											2020-09-09 00:10:55 +08:00
										 |  |  | 			retries++ | 
					
						
							| 
									
										
										
										
											2022-11-16 23:59:10 +08:00
										 |  |  | 			// after 20 retries start logging that servers are not reachable yet
 | 
					
						
							|  |  |  | 			if retries >= 20 { | 
					
						
							| 
									
										
										
										
											2024-01-18 15:03:17 +08:00
										 |  |  | 				logger.Info(fmt.Sprintf("Waiting for at least %d remote servers with valid configuration to be online", len(clnts)/2)) | 
					
						
							| 
									
										
										
										
											2021-08-19 09:05:05 +08:00
										 |  |  | 				if len(offlineEndpoints) > 0 { | 
					
						
							|  |  |  | 					logger.Info(fmt.Sprintf("Following servers are currently offline or unreachable %s", offlineEndpoints)) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-11-16 23:59:10 +08:00
										 |  |  | 				if len(incorrectConfigs) > 0 { | 
					
						
							| 
									
										
										
										
											2023-01-23 18:54:50 +08:00
										 |  |  | 					logger.Info(fmt.Sprintf("Following servers have mismatching configuration %s", incorrectConfigs)) | 
					
						
							| 
									
										
										
										
											2022-11-16 23:59:10 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 				retries = 0 // reset to log again after 20 retries.
 | 
					
						
							| 
									
										
										
										
											2020-09-09 00:10:55 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			offlineEndpoints = nil | 
					
						
							| 
									
										
										
										
											2022-11-16 23:59:10 +08:00
										 |  |  | 			incorrectConfigs = nil | 
					
						
							| 
									
										
										
										
											2020-07-11 00:26:21 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | func newBootstrapRESTClients(endpointServerPools EndpointServerPools, gm *grid.Manager) []*bootstrapRESTClient { | 
					
						
							|  |  |  | 	seenClient := set.NewStringSet() | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 	var clnts []*bootstrapRESTClient | 
					
						
							| 
									
										
										
										
											2020-12-02 05:50:33 +08:00
										 |  |  | 	for _, ep := range endpointServerPools { | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 		for _, endpoint := range ep.Endpoints { | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 			if endpoint.IsLocal { | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 			if seenClient.Contains(endpoint.Host) { | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-01-25 05:36:44 +08:00
										 |  |  | 			seenClient.Add(endpoint.Host) | 
					
						
							|  |  |  | 			clnts = append(clnts, &bootstrapRESTClient{gm.Connection(endpoint.GridHost())}) | 
					
						
							| 
									
										
										
										
											2019-11-23 04:45:13 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return clnts | 
					
						
							|  |  |  | } |