| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-09-13 02:06:12 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2017-2019 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2018-05-16 09:20:22 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2017-08-16 06:10:50 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2017-11-12 10:39:53 +08:00
										 |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | 	humanize "github.com/dustin/go-humanize" | 
					
						
							| 
									
										
										
										
											2019-04-29 12:40:50 +08:00
										 |  |  | 	"github.com/minio/cli" | 
					
						
							| 
									
										
										
										
											2019-05-30 07:35:12 +08:00
										 |  |  | 	"github.com/minio/minio-go/v6/pkg/set" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2017-08-16 06:10:50 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/mountinfo" | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // EndpointType - enum for endpoint type.
 | 
					
						
							|  |  |  | type EndpointType int | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// PathEndpointType - path style endpoint type enum.
 | 
					
						
							|  |  |  | 	PathEndpointType EndpointType = iota + 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// URLEndpointType - URL style endpoint type enum.
 | 
					
						
							|  |  |  | 	URLEndpointType | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	retryInterval = 5 // In Seconds.
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Endpoint - any type of endpoint.
 | 
					
						
							|  |  |  | type Endpoint struct { | 
					
						
							|  |  |  | 	*url.URL | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 	IsLocal  bool | 
					
						
							|  |  |  | 	SetIndex int | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | 	HostName string | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (endpoint Endpoint) String() string { | 
					
						
							|  |  |  | 	if endpoint.Host == "" { | 
					
						
							|  |  |  | 		return endpoint.Path | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return endpoint.URL.String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Type - returns type of endpoint.
 | 
					
						
							|  |  |  | func (endpoint Endpoint) Type() EndpointType { | 
					
						
							|  |  |  | 	if endpoint.Host == "" { | 
					
						
							|  |  |  | 		return PathEndpointType | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return URLEndpointType | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-04 14:47:12 +08:00
										 |  |  | // IsHTTPS - returns true if secure for URLEndpointType.
 | 
					
						
							|  |  |  | func (endpoint Endpoint) IsHTTPS() bool { | 
					
						
							|  |  |  | 	return endpoint.Scheme == "https" | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | // UpdateIsLocal - resolves the host and updates if it is local or not.
 | 
					
						
							|  |  |  | func (endpoint *Endpoint) UpdateIsLocal() error { | 
					
						
							|  |  |  | 	if !endpoint.IsLocal { | 
					
						
							|  |  |  | 		isLocal, err := isLocalHost(endpoint.HostName) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		endpoint.IsLocal = isLocal | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | // NewEndpoint - returns new endpoint based on given arguments.
 | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | func NewEndpoint(arg string) (ep Endpoint, e error) { | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	// isEmptyPath - check whether given path is not empty.
 | 
					
						
							|  |  |  | 	isEmptyPath := func(path string) bool { | 
					
						
							| 
									
										
										
										
											2019-08-07 03:08:58 +08:00
										 |  |  | 		return path == "" || path == SlashSeparator || path == `\` | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if isEmptyPath(arg) { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 		return ep, fmt.Errorf("empty or root endpoint is not supported") | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var isLocal bool | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | 	var host string | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	u, err := url.Parse(arg) | 
					
						
							|  |  |  | 	if err == nil && u.Host != "" { | 
					
						
							|  |  |  | 		// URL style of endpoint.
 | 
					
						
							|  |  |  | 		// Valid URL style endpoint is
 | 
					
						
							|  |  |  | 		// - Scheme field must contain "http" or "https"
 | 
					
						
							|  |  |  | 		// - All field should be empty except Host and Path.
 | 
					
						
							|  |  |  | 		if !((u.Scheme == "http" || u.Scheme == "https") && | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | 			u.User == nil && u.Opaque == "" && !u.ForceQuery && u.RawQuery == "" && u.Fragment == "") { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 			return ep, fmt.Errorf("invalid URL endpoint format") | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | 		var port string | 
					
						
							| 
									
										
										
										
											2017-08-16 06:10:50 +08:00
										 |  |  | 		host, port, err = net.SplitHostPort(u.Host) | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			if !strings.Contains(err.Error(), "missing port in address") { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 				return ep, fmt.Errorf("invalid URL endpoint format: %s", err) | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			host = u.Host | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			var p int | 
					
						
							|  |  |  | 			p, err = strconv.Atoi(port) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 				return ep, fmt.Errorf("invalid URL endpoint format: invalid port number") | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 			} else if p < 1 || p > 65535 { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 				return ep, fmt.Errorf("invalid URL endpoint format: port number must be between 1 to 65535") | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-12-14 15:37:46 +08:00
										 |  |  | 		if i := strings.Index(host, "%"); i > -1 { | 
					
						
							|  |  |  | 			host = host[:i] | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if host == "" { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 			return ep, fmt.Errorf("invalid URL endpoint format: empty host name") | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// As this is path in the URL, we should use path package, not filepath package.
 | 
					
						
							|  |  |  | 		// On MS Windows, filepath.Clean() converts into Windows path style ie `/foo` becomes `\foo`
 | 
					
						
							|  |  |  | 		u.Path = path.Clean(u.Path) | 
					
						
							|  |  |  | 		if isEmptyPath(u.Path) { | 
					
						
							| 
									
										
										
										
											2017-06-22 10:53:09 +08:00
										 |  |  | 			return ep, fmt.Errorf("empty or root path is not supported in URL endpoint") | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-07 03:08:58 +08:00
										 |  |  | 		// On windows having a preceding SlashSeparator will cause problems, if the
 | 
					
						
							| 
									
										
										
										
											2017-11-12 10:39:53 +08:00
										 |  |  | 		// command line already has C:/<export-folder/ in it. Final resulting
 | 
					
						
							|  |  |  | 		// path on windows might become C:/C:/ this will cause problems
 | 
					
						
							|  |  |  | 		// of starting minio server properly in distributed mode on windows.
 | 
					
						
							|  |  |  | 		// As a special case make sure to trim the separator.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// NOTE: It is also perfectly fine for windows users to have a path
 | 
					
						
							|  |  |  | 		// without C:/ since at that point we treat it as relative path
 | 
					
						
							|  |  |  | 		// and obtain the full filesystem path as well. Providing C:/
 | 
					
						
							|  |  |  | 		// style is necessary to provide paths other than C:/,
 | 
					
						
							|  |  |  | 		// such as F:/, D:/ etc.
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// Another additional benefit here is that this style also
 | 
					
						
							|  |  |  | 		// supports providing \\host\share support as well.
 | 
					
						
							|  |  |  | 		if runtime.GOOS == globalWindowsOSName { | 
					
						
							|  |  |  | 			if filepath.VolumeName(u.Path[1:]) != "" { | 
					
						
							|  |  |  | 				u.Path = u.Path[1:] | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2017-08-11 07:54:19 +08:00
										 |  |  | 		// Only check if the arg is an ip address and ask for scheme since its absent.
 | 
					
						
							|  |  |  | 		// localhost, example.com, any FQDN cannot be disambiguated from a regular file path such as
 | 
					
						
							|  |  |  | 		// /mnt/export1. So we go ahead and start the minio server in FS modes in these cases.
 | 
					
						
							| 
									
										
										
										
											2018-12-14 15:37:46 +08:00
										 |  |  | 		if isHostIP(arg) { | 
					
						
							| 
									
										
										
										
											2017-08-11 07:54:19 +08:00
										 |  |  | 			return ep, fmt.Errorf("invalid URL endpoint format: missing scheme http or https") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 		u = &url.URL{Path: path.Clean(arg)} | 
					
						
							|  |  |  | 		isLocal = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return Endpoint{ | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | 		URL:      u, | 
					
						
							|  |  |  | 		IsLocal:  isLocal, | 
					
						
							|  |  |  | 		HostName: host, | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // EndpointList - list of same type of endpoint.
 | 
					
						
							|  |  |  | type EndpointList []Endpoint | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-18 09:28:34 +08:00
										 |  |  | // Nodes - returns number of unique servers.
 | 
					
						
							|  |  |  | func (endpoints EndpointList) Nodes() int { | 
					
						
							|  |  |  | 	uniqueNodes := set.NewStringSet() | 
					
						
							|  |  |  | 	for _, endpoint := range endpoints { | 
					
						
							|  |  |  | 		if uniqueNodes.Contains(endpoint.Host) { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		uniqueNodes.Add(endpoint.Host) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return len(uniqueNodes) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-04 14:47:12 +08:00
										 |  |  | // IsHTTPS - returns true if secure for URLEndpointType.
 | 
					
						
							|  |  |  | func (endpoints EndpointList) IsHTTPS() bool { | 
					
						
							|  |  |  | 	return endpoints[0].IsHTTPS() | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // GetString - returns endpoint string of i-th endpoint (0-based),
 | 
					
						
							|  |  |  | // and empty string for invalid indexes.
 | 
					
						
							|  |  |  | func (endpoints EndpointList) GetString(i int) string { | 
					
						
							|  |  |  | 	if i < 0 || i >= len(endpoints) { | 
					
						
							|  |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return endpoints[i].String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | // UpdateIsLocal - resolves the host and discovers the local host.
 | 
					
						
							|  |  |  | func (endpoints EndpointList) UpdateIsLocal() error { | 
					
						
							|  |  |  | 	var epsResolved int | 
					
						
							|  |  |  | 	var foundLocal bool | 
					
						
							|  |  |  | 	resolvedList := make([]bool, len(endpoints)) | 
					
						
							|  |  |  | 	// Mark the starting time
 | 
					
						
							|  |  |  | 	startTime := time.Now() | 
					
						
							|  |  |  | 	keepAliveTicker := time.NewTicker(retryInterval * time.Second) | 
					
						
							|  |  |  | 	defer keepAliveTicker.Stop() | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		// Break if the local endpoint is found already. Or all the endpoints are resolved.
 | 
					
						
							|  |  |  | 		if foundLocal || (epsResolved == len(endpoints)) { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Retry infinitely on Kubernetes and Docker swarm.
 | 
					
						
							|  |  |  | 		// This is needed as the remote hosts are sometime
 | 
					
						
							|  |  |  | 		// not available immediately.
 | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-globalOSSignalCh: | 
					
						
							|  |  |  | 			return fmt.Errorf("The endpoint resolution got interrupted") | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			for i, resolved := range resolvedList { | 
					
						
							|  |  |  | 				if resolved { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// return err if not Docker or Kubernetes
 | 
					
						
							|  |  |  | 				// We use IsDocker() method to check for Docker Swarm environment
 | 
					
						
							|  |  |  | 				// as there is no reliable way to clearly identify Swarm from
 | 
					
						
							|  |  |  | 				// Docker environment.
 | 
					
						
							|  |  |  | 				isLocal, err := isLocalHost(endpoints[i].HostName) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					if !IsDocker() && !IsKubernetes() { | 
					
						
							|  |  |  | 						return err | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					// time elapsed
 | 
					
						
							|  |  |  | 					timeElapsed := time.Since(startTime) | 
					
						
							|  |  |  | 					// log error only if more than 1s elapsed
 | 
					
						
							|  |  |  | 					if timeElapsed > time.Second { | 
					
						
							|  |  |  | 						// log the message to console about the host not being
 | 
					
						
							|  |  |  | 						// resolveable.
 | 
					
						
							|  |  |  | 						reqInfo := (&logger.ReqInfo{}).AppendTags("host", endpoints[i].HostName) | 
					
						
							|  |  |  | 						reqInfo.AppendTags("elapsedTime", humanize.RelTime(startTime, startTime.Add(timeElapsed), "elapsed", "")) | 
					
						
							|  |  |  | 						ctx := logger.SetReqInfo(context.Background(), reqInfo) | 
					
						
							|  |  |  | 						logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					resolvedList[i] = true | 
					
						
							|  |  |  | 					endpoints[i].IsLocal = isLocal | 
					
						
							|  |  |  | 					epsResolved++ | 
					
						
							|  |  |  | 					if !foundLocal { | 
					
						
							|  |  |  | 						foundLocal = isLocal | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Wait for the tick, if the there exist a local endpoint in discovery.
 | 
					
						
							|  |  |  | 			// Non docker/kubernetes environment does not need to wait.
 | 
					
						
							|  |  |  | 			if !foundLocal && (IsDocker() && IsKubernetes()) { | 
					
						
							|  |  |  | 				<-keepAliveTicker.C | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | // NewEndpointList - returns new endpoint list based on input args.
 | 
					
						
							|  |  |  | func NewEndpointList(args ...string) (endpoints EndpointList, err error) { | 
					
						
							|  |  |  | 	var endpointType EndpointType | 
					
						
							|  |  |  | 	var scheme string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	uniqueArgs := set.NewStringSet() | 
					
						
							|  |  |  | 	// Loop through args and adds to endpoint list.
 | 
					
						
							|  |  |  | 	for i, arg := range args { | 
					
						
							|  |  |  | 		endpoint, err := NewEndpoint(arg) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("'%s': %s", arg, err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// All endpoints have to be same type and scheme if applicable.
 | 
					
						
							|  |  |  | 		if i == 0 { | 
					
						
							|  |  |  | 			endpointType = endpoint.Type() | 
					
						
							|  |  |  | 			scheme = endpoint.Scheme | 
					
						
							|  |  |  | 		} else if endpoint.Type() != endpointType { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("mixed style endpoints are not supported") | 
					
						
							|  |  |  | 		} else if endpoint.Scheme != scheme { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("mixed scheme is not supported") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		arg = endpoint.String() | 
					
						
							|  |  |  | 		if uniqueArgs.Contains(arg) { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("duplicate endpoints found") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		uniqueArgs.Add(arg) | 
					
						
							|  |  |  | 		endpoints = append(endpoints, endpoint) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	return endpoints, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-29 12:40:50 +08:00
										 |  |  | func checkEndpointsSubOptimal(ctx *cli.Context, setupType SetupType, endpoints EndpointList) (err error) { | 
					
						
							|  |  |  | 	// Validate sub optimal ordering only for distributed setup.
 | 
					
						
							|  |  |  | 	if setupType != DistXLSetupType { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var endpointOrder int | 
					
						
							|  |  |  | 	err = fmt.Errorf("Too many disk args are local, input is in sub-optimal order. Please review input args: %s", ctx.Args()) | 
					
						
							|  |  |  | 	for _, endpoint := range endpoints { | 
					
						
							|  |  |  | 		if endpoint.IsLocal { | 
					
						
							|  |  |  | 			endpointOrder++ | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			endpointOrder-- | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if endpointOrder >= 2 { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-16 06:10:50 +08:00
										 |  |  | // Checks if there are any cross device mounts.
 | 
					
						
							|  |  |  | func checkCrossDeviceMounts(endpoints EndpointList) (err error) { | 
					
						
							|  |  |  | 	var absPaths []string | 
					
						
							|  |  |  | 	for _, endpoint := range endpoints { | 
					
						
							|  |  |  | 		if endpoint.IsLocal { | 
					
						
							|  |  |  | 			var absPath string | 
					
						
							|  |  |  | 			absPath, err = filepath.Abs(endpoint.Path) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			absPaths = append(absPaths, absPath) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return mountinfo.CheckCrossDevice(absPaths) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | // CreateEndpoints - validates and creates new endpoints for given args.
 | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | func CreateEndpoints(serverAddr string, args ...[]string) (string, EndpointList, SetupType, error) { | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	var endpoints EndpointList | 
					
						
							|  |  |  | 	var setupType SetupType | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check whether serverAddr is valid for this host.
 | 
					
						
							|  |  |  | 	if err = CheckLocalServerAddr(serverAddr); err != nil { | 
					
						
							|  |  |  | 		return serverAddr, endpoints, setupType, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_, serverAddrPort := mustSplitHostPort(serverAddr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// For single arg, return FS setup.
 | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 	if len(args) == 1 && len(args[0]) == 1 { | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 		var endpoint Endpoint | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 		endpoint, err = NewEndpoint(args[0][0]) | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return serverAddr, endpoints, setupType, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | 		if err := endpoint.UpdateIsLocal(); err != nil { | 
					
						
							|  |  |  | 			return serverAddr, endpoints, setupType, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 		if endpoint.Type() != PathEndpointType { | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 			return serverAddr, endpoints, setupType, uiErrInvalidFSEndpoint(nil).Msg("use path style endpoint for FS setup") | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		endpoints = append(endpoints, endpoint) | 
					
						
							|  |  |  | 		setupType = FSSetupType | 
					
						
							| 
									
										
										
										
											2017-08-16 06:10:50 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Check for cross device mounts if any.
 | 
					
						
							|  |  |  | 		if err = checkCrossDeviceMounts(endpoints); err != nil { | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 			return serverAddr, endpoints, setupType, uiErrInvalidFSEndpoint(nil).Msg(err.Error()) | 
					
						
							| 
									
										
										
										
											2017-08-16 06:10:50 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 		return serverAddr, endpoints, setupType, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 	for i, iargs := range args { | 
					
						
							|  |  |  | 		var newEndpoints EndpointList | 
					
						
							|  |  |  | 		// Convert args to endpoints
 | 
					
						
							|  |  |  | 		var eps EndpointList | 
					
						
							|  |  |  | 		eps, err = NewEndpointList(iargs...) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 			return serverAddr, endpoints, setupType, uiErrInvalidErasureEndpoints(nil).Msg(err.Error()) | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 		// Check for cross device mounts if any.
 | 
					
						
							|  |  |  | 		if err = checkCrossDeviceMounts(eps); err != nil { | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 			return serverAddr, endpoints, setupType, uiErrInvalidErasureEndpoints(nil).Msg(err.Error()) | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for _, ep := range eps { | 
					
						
							|  |  |  | 			ep.SetIndex = i | 
					
						
							|  |  |  | 			newEndpoints = append(newEndpoints, ep) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		endpoints = append(endpoints, newEndpoints...) | 
					
						
							| 
									
										
										
										
											2017-08-16 06:10:50 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	// Return XL setup when all endpoints are path style.
 | 
					
						
							|  |  |  | 	if endpoints[0].Type() == PathEndpointType { | 
					
						
							|  |  |  | 		setupType = XLSetupType | 
					
						
							|  |  |  | 		return serverAddr, endpoints, setupType, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-20 01:26:44 +08:00
										 |  |  | 	if err := endpoints.UpdateIsLocal(); err != nil { | 
					
						
							|  |  |  | 		return serverAddr, endpoints, setupType, uiErrInvalidErasureEndpoints(nil).Msg(err.Error()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	// Here all endpoints are URL style.
 | 
					
						
							|  |  |  | 	endpointPathSet := set.NewStringSet() | 
					
						
							|  |  |  | 	localEndpointCount := 0 | 
					
						
							|  |  |  | 	localServerAddrSet := set.NewStringSet() | 
					
						
							|  |  |  | 	localPortSet := set.NewStringSet() | 
					
						
							| 
									
										
										
										
											2017-08-11 07:54:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	for _, endpoint := range endpoints { | 
					
						
							|  |  |  | 		endpointPathSet.Add(endpoint.Path) | 
					
						
							|  |  |  | 		if endpoint.IsLocal { | 
					
						
							|  |  |  | 			localServerAddrSet.Add(endpoint.Host) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var port string | 
					
						
							|  |  |  | 			_, port, err = net.SplitHostPort(endpoint.Host) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				port = serverAddrPort | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			localPortSet.Add(port) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			localEndpointCount++ | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// No local endpoint found.
 | 
					
						
							|  |  |  | 	if localEndpointCount == 0 { | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 		return serverAddr, endpoints, setupType, uiErrInvalidErasureEndpoints(nil).Msg("no endpoint pointing to the local machine is found") | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 17:08:17 +08:00
										 |  |  | 	// Check whether same path is not used in endpoints of a host on different port.
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		pathIPMap := make(map[string]set.StringSet) | 
					
						
							|  |  |  | 		for _, endpoint := range endpoints { | 
					
						
							|  |  |  | 			var host string | 
					
						
							|  |  |  | 			host, _, err = net.SplitHostPort(endpoint.Host) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				host = endpoint.Host | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-12-14 15:37:46 +08:00
										 |  |  | 			hostIPSet, _ := getHostIP(host) | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 			if IPSet, ok := pathIPMap[endpoint.Path]; ok { | 
					
						
							|  |  |  | 				if !IPSet.Intersection(hostIPSet).IsEmpty() { | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 					return serverAddr, endpoints, setupType, | 
					
						
							|  |  |  | 						uiErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("path '%s' can not be served by different port on same address", endpoint.Path)) | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2017-04-13 11:27:24 +08:00
										 |  |  | 				pathIPMap[endpoint.Path] = IPSet.Union(hostIPSet) | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				pathIPMap[endpoint.Path] = hostIPSet | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 17:08:17 +08:00
										 |  |  | 	// Check whether same path is used for more than 1 local endpoints.
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		localPathSet := set.CreateStringSet() | 
					
						
							|  |  |  | 		for _, endpoint := range endpoints { | 
					
						
							|  |  |  | 			if !endpoint.IsLocal { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if localPathSet.Contains(endpoint.Path) { | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 				return serverAddr, endpoints, setupType, | 
					
						
							|  |  |  | 					uiErrInvalidErasureEndpoints(nil).Msg(fmt.Sprintf("path '%s' cannot be served by different address on same server", endpoint.Path)) | 
					
						
							| 
									
										
										
										
											2018-02-15 17:08:17 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			localPathSet.Add(endpoint.Path) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	// Check whether serverAddrPort matches at least in one of port used in local endpoints.
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if !localPortSet.Contains(serverAddrPort) { | 
					
						
							|  |  |  | 			if len(localPortSet) > 1 { | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 				return serverAddr, endpoints, setupType, | 
					
						
							|  |  |  | 					uiErrInvalidErasureEndpoints(nil).Msg("port number in server address must match with one of the port in local endpoints") | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 			return serverAddr, endpoints, setupType, | 
					
						
							|  |  |  | 				uiErrInvalidErasureEndpoints(nil).Msg("server address and local endpoint have different ports") | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-13 11:27:24 +08:00
										 |  |  | 	// All endpoints are pointing to local host
 | 
					
						
							|  |  |  | 	if len(endpoints) == localEndpointCount { | 
					
						
							|  |  |  | 		// If all endpoints have same port number, then this is XL setup using URL style endpoints.
 | 
					
						
							|  |  |  | 		if len(localPortSet) == 1 { | 
					
						
							|  |  |  | 			if len(localServerAddrSet) > 1 { | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 				// TODO: Even though all endpoints are local, the local host is referred by different IP/name.
 | 
					
						
							| 
									
										
										
										
											2017-04-13 11:27:24 +08:00
										 |  |  | 				// eg '172.0.0.1', 'localhost' and 'mylocalhostname' point to same local host.
 | 
					
						
							|  |  |  | 				//
 | 
					
						
							|  |  |  | 				// In this case, we bind to 0.0.0.0 ie to all interfaces.
 | 
					
						
							|  |  |  | 				// The actual way to do is bind to only IPs in uniqueLocalHosts.
 | 
					
						
							|  |  |  | 				serverAddr = net.JoinHostPort("", serverAddrPort) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			endpointPaths := endpointPathSet.ToSlice() | 
					
						
							|  |  |  | 			endpoints, _ = NewEndpointList(endpointPaths...) | 
					
						
							|  |  |  | 			setupType = XLSetupType | 
					
						
							|  |  |  | 			return serverAddr, endpoints, setupType, nil | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-09 10:04:36 +08:00
										 |  |  | 		// Even though all endpoints are local, but those endpoints use different ports.
 | 
					
						
							| 
									
										
										
										
											2017-04-13 11:27:24 +08:00
										 |  |  | 		// This means it is DistXL setup.
 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		// This is DistXL setup.
 | 
					
						
							|  |  |  | 		// Check whether local server address are not 127.x.x.x
 | 
					
						
							|  |  |  | 		for _, localServerAddr := range localServerAddrSet.ToSlice() { | 
					
						
							|  |  |  | 			host, _, err := net.SplitHostPort(localServerAddr) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				host = localServerAddr | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-14 15:37:46 +08:00
										 |  |  | 			ipList, err := getHostIP(host) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			logger.FatalIf(err, "unexpected error when resolving host '%s'", host) | 
					
						
							| 
									
										
										
										
											2017-04-13 11:27:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-14 15:37:46 +08:00
										 |  |  | 			// Filter ipList by IPs those start with '127.' or '::1'
 | 
					
						
							| 
									
										
										
										
											2017-04-13 11:27:24 +08:00
										 |  |  | 			loopBackIPs := ipList.FuncMatch(func(ip string, matchString string) bool { | 
					
						
							| 
									
										
										
										
											2018-12-14 15:37:46 +08:00
										 |  |  | 				return strings.HasPrefix(ip, "127.") || strings.HasPrefix(ip, "::1") | 
					
						
							| 
									
										
										
										
											2017-04-13 11:27:24 +08:00
										 |  |  | 			}, "") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// If loop back IP is found and ipList contains only loop back IPs, then error out.
 | 
					
						
							|  |  |  | 			if len(loopBackIPs) > 0 && len(loopBackIPs) == len(ipList) { | 
					
						
							|  |  |  | 				err = fmt.Errorf("'%s' resolves to loopback address is not allowed for distributed XL", localServerAddr) | 
					
						
							|  |  |  | 				return serverAddr, endpoints, setupType, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Add missing port in all endpoints.
 | 
					
						
							|  |  |  | 	for i := range endpoints { | 
					
						
							|  |  |  | 		_, port, err := net.SplitHostPort(endpoints[i].Host) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			endpoints[i].Host = net.JoinHostPort(endpoints[i].Host, serverAddrPort) | 
					
						
							|  |  |  | 		} else if endpoints[i].IsLocal && serverAddrPort != port { | 
					
						
							|  |  |  | 			// If endpoint is local, but port is different than serverAddrPort, then make it as remote.
 | 
					
						
							|  |  |  | 			endpoints[i].IsLocal = false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 	uniqueArgs := set.NewStringSet() | 
					
						
							|  |  |  | 	for _, endpoint := range endpoints { | 
					
						
							|  |  |  | 		uniqueArgs.Add(endpoint.Host) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Error out if we have more than serverCommandLineArgsMax unique servers.
 | 
					
						
							|  |  |  | 	if len(uniqueArgs.ToSlice()) > serverCommandLineArgsMax { | 
					
						
							|  |  |  | 		err := fmt.Errorf("Unsupported number of endpoints (%s), total number of servers cannot be more than %d", endpoints, serverCommandLineArgsMax) | 
					
						
							|  |  |  | 		return serverAddr, endpoints, setupType, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Error out if we have less than 2 unique servers.
 | 
					
						
							|  |  |  | 	if len(uniqueArgs.ToSlice()) < 2 && setupType == DistXLSetupType { | 
					
						
							|  |  |  | 		err := fmt.Errorf("Unsupported number of endpoints (%s), minimum number of servers cannot be less than 2 in distributed setup", endpoints) | 
					
						
							|  |  |  | 		return serverAddr, endpoints, setupType, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-05 09:35:22 +08:00
										 |  |  | 	_, dok := os.LookupEnv("MINIO_DOMAIN") | 
					
						
							|  |  |  | 	_, eok := os.LookupEnv("MINIO_ETCD_ENDPOINTS") | 
					
						
							|  |  |  | 	_, iok := os.LookupEnv("MINIO_PUBLIC_IPS") | 
					
						
							|  |  |  | 	if dok && eok && !iok { | 
					
						
							|  |  |  | 		updateDomainIPs(uniqueArgs) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-05-16 09:20:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	setupType = DistXLSetupType | 
					
						
							|  |  |  | 	return serverAddr, endpoints, setupType, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-09 11:23:42 +08:00
										 |  |  | // GetLocalPeer - returns local peer value, returns globalMinioAddr
 | 
					
						
							|  |  |  | // for FS and Erasure mode. In case of distributed server return
 | 
					
						
							|  |  |  | // the first element from the set of peers which indicate that
 | 
					
						
							|  |  |  | // they are local. There is always one entry that is local
 | 
					
						
							|  |  |  | // even with repeated server endpoints.
 | 
					
						
							|  |  |  | func GetLocalPeer(endpoints EndpointList) (localPeer string) { | 
					
						
							|  |  |  | 	peerSet := set.NewStringSet() | 
					
						
							|  |  |  | 	for _, endpoint := range endpoints { | 
					
						
							|  |  |  | 		if endpoint.Type() != URLEndpointType { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if endpoint.IsLocal && endpoint.Host != "" { | 
					
						
							|  |  |  | 			peerSet.Add(endpoint.Host) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if peerSet.IsEmpty() { | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		// Local peer can be empty in FS or Erasure coded mode.
 | 
					
						
							|  |  |  | 		// If so, return globalMinioHost + globalMinioPort value.
 | 
					
						
							|  |  |  | 		if globalMinioHost != "" { | 
					
						
							| 
									
										
										
										
											2018-12-14 15:37:46 +08:00
										 |  |  | 			return net.JoinHostPort(globalMinioHost, globalMinioPort) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-14 15:37:46 +08:00
										 |  |  | 		return net.JoinHostPort("127.0.0.1", globalMinioPort) | 
					
						
							| 
									
										
										
										
											2017-10-09 11:23:42 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return peerSet.ToSlice()[0] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | // GetRemotePeers - get hosts information other than this minio service.
 | 
					
						
							|  |  |  | func GetRemotePeers(endpoints EndpointList) []string { | 
					
						
							|  |  |  | 	peerSet := set.NewStringSet() | 
					
						
							|  |  |  | 	for _, endpoint := range endpoints { | 
					
						
							|  |  |  | 		if endpoint.Type() != URLEndpointType { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		peer := endpoint.Host | 
					
						
							|  |  |  | 		if endpoint.IsLocal { | 
					
						
							|  |  |  | 			if _, port := mustSplitHostPort(peer); port == globalMinioPort { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		peerSet.Add(peer) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return peerSet.ToSlice() | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-05-16 09:20:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func updateDomainIPs(endPoints set.StringSet) { | 
					
						
							| 
									
										
										
										
											2018-12-05 09:35:22 +08:00
										 |  |  | 	ipList := set.NewStringSet() | 
					
						
							|  |  |  | 	for e := range endPoints { | 
					
						
							| 
									
										
										
										
											2019-08-03 03:40:51 +08:00
										 |  |  | 		host, port, err := net.SplitHostPort(e) | 
					
						
							| 
									
										
										
										
											2018-12-05 09:35:22 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			if strings.Contains(err.Error(), "missing port in address") { | 
					
						
							|  |  |  | 				host = e | 
					
						
							| 
									
										
										
										
											2019-08-03 03:40:51 +08:00
										 |  |  | 				port = globalMinioPort | 
					
						
							| 
									
										
										
										
											2018-12-05 09:35:22 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-05-16 09:20:22 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-03 03:40:51 +08:00
										 |  |  | 		IPs, err := getHostIP(host) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		IPsWithPort := IPs.ApplyFunc(func(ip string) string { | 
					
						
							|  |  |  | 			return net.JoinHostPort(ip, port) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ipList = ipList.Union(IPsWithPort) | 
					
						
							| 
									
										
										
										
											2018-05-16 09:20:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-05 09:35:22 +08:00
										 |  |  | 	globalDomainIPs = ipList.FuncMatch(func(ip string, matchString string) bool { | 
					
						
							| 
									
										
										
										
											2019-08-03 03:40:51 +08:00
										 |  |  | 		return !(strings.HasPrefix(ip, "127.") || strings.HasPrefix(ip, "::1") || strings.HasPrefix(ip, "[::1]")) | 
					
						
							| 
									
										
										
										
											2018-12-05 09:35:22 +08:00
										 |  |  | 	}, "") | 
					
						
							| 
									
										
										
										
											2018-05-16 09:20:22 +08:00
										 |  |  | } |