github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/nomad/util.go (about)

     1  package nomad
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"net"
     7  	"os"
     8  	"path/filepath"
     9  	"strconv"
    10  
    11  	version "github.com/hashicorp/go-version"
    12  	"github.com/hashicorp/serf/serf"
    13  )
    14  
    15  // ensurePath is used to make sure a path exists
    16  func ensurePath(path string, dir bool) error {
    17  	if !dir {
    18  		path = filepath.Dir(path)
    19  	}
    20  	return os.MkdirAll(path, 0755)
    21  }
    22  
    23  // serverParts is used to return the parts of a server role
    24  type serverParts struct {
    25  	Name         string
    26  	ID           string
    27  	Region       string
    28  	Datacenter   string
    29  	Port         int
    30  	Bootstrap    bool
    31  	Expect       int
    32  	MajorVersion int
    33  	MinorVersion int
    34  	Build        version.Version
    35  	RaftVersion  int
    36  	Addr         net.Addr
    37  	RPCAddr      net.Addr
    38  	Status       serf.MemberStatus
    39  }
    40  
    41  func (s *serverParts) String() string {
    42  	return fmt.Sprintf("%s (Addr: %s) (DC: %s)",
    43  		s.Name, s.Addr, s.Datacenter)
    44  }
    45  
    46  func (s *serverParts) Copy() *serverParts {
    47  	ns := new(serverParts)
    48  	*ns = *s
    49  	return ns
    50  }
    51  
    52  // Returns if a member is a Nomad server. Returns a boolean,
    53  // and a struct with the various important components
    54  func isNomadServer(m serf.Member) (bool, *serverParts) {
    55  	if m.Tags["role"] != "nomad" {
    56  		return false, nil
    57  	}
    58  
    59  	id := "unknown"
    60  	if v, ok := m.Tags["id"]; ok {
    61  		id = v
    62  	}
    63  	region := m.Tags["region"]
    64  	datacenter := m.Tags["dc"]
    65  	_, bootstrap := m.Tags["bootstrap"]
    66  
    67  	expect := 0
    68  	expectStr, ok := m.Tags["expect"]
    69  	var err error
    70  	if ok {
    71  		expect, err = strconv.Atoi(expectStr)
    72  		if err != nil {
    73  			return false, nil
    74  		}
    75  	}
    76  
    77  	// If the server is missing the rpc_addr tag, default to the serf advertise addr
    78  	rpcIP := net.ParseIP(m.Tags["rpc_addr"])
    79  	if rpcIP == nil {
    80  		rpcIP = m.Addr
    81  	}
    82  
    83  	portStr := m.Tags["port"]
    84  	port, err := strconv.Atoi(portStr)
    85  	if err != nil {
    86  		return false, nil
    87  	}
    88  
    89  	buildVersion, err := version.NewVersion(m.Tags["build"])
    90  	if err != nil {
    91  		return false, nil
    92  	}
    93  
    94  	// The "vsn" tag was Version, which is now the MajorVersion number.
    95  	majorVersionStr := m.Tags["vsn"]
    96  	majorVersion, err := strconv.Atoi(majorVersionStr)
    97  	if err != nil {
    98  		return false, nil
    99  	}
   100  
   101  	// To keep some semblance of convention, "mvn" is now the "Minor
   102  	// Version Number."
   103  	minorVersionStr := m.Tags["mvn"]
   104  	minorVersion, err := strconv.Atoi(minorVersionStr)
   105  	if err != nil {
   106  		minorVersion = 0
   107  	}
   108  
   109  	raftVsn := 0
   110  	raftVsnString, ok := m.Tags["raft_vsn"]
   111  	if ok {
   112  		raftVsn, err = strconv.Atoi(raftVsnString)
   113  		if err != nil {
   114  			return false, nil
   115  		}
   116  	}
   117  
   118  	addr := &net.TCPAddr{IP: m.Addr, Port: port}
   119  	rpcAddr := &net.TCPAddr{IP: rpcIP, Port: port}
   120  	parts := &serverParts{
   121  		Name:         m.Name,
   122  		ID:           id,
   123  		Region:       region,
   124  		Datacenter:   datacenter,
   125  		Port:         port,
   126  		Bootstrap:    bootstrap,
   127  		Expect:       expect,
   128  		Addr:         addr,
   129  		RPCAddr:      rpcAddr,
   130  		MajorVersion: majorVersion,
   131  		MinorVersion: minorVersion,
   132  		Build:        *buildVersion,
   133  		RaftVersion:  raftVsn,
   134  		Status:       m.Status,
   135  	}
   136  	return true, parts
   137  }
   138  
   139  // ServersMeetMinimumVersion returns whether the given alive servers are at least on the
   140  // given Nomad version
   141  func ServersMeetMinimumVersion(members []serf.Member, minVersion *version.Version) bool {
   142  	for _, member := range members {
   143  		if valid, parts := isNomadServer(member); valid && parts.Status == serf.StatusAlive {
   144  			// Check if the versions match - version.LessThan will return true for
   145  			// 0.8.0-rc1 < 0.8.0, so we want to ignore the metadata
   146  			versionsMatch := slicesMatch(minVersion.Segments(), parts.Build.Segments())
   147  			if parts.Build.LessThan(minVersion) && !versionsMatch {
   148  				return false
   149  			}
   150  		}
   151  	}
   152  
   153  	return true
   154  }
   155  
   156  func slicesMatch(a, b []int) bool {
   157  	if a == nil && b == nil {
   158  		return true
   159  	}
   160  
   161  	if a == nil || b == nil {
   162  		return false
   163  	}
   164  
   165  	if len(a) != len(b) {
   166  		return false
   167  	}
   168  
   169  	for i := range a {
   170  		if a[i] != b[i] {
   171  			return false
   172  		}
   173  	}
   174  
   175  	return true
   176  }
   177  
   178  // shuffleStrings randomly shuffles the list of strings
   179  func shuffleStrings(list []string) {
   180  	for i := range list {
   181  		j := rand.Intn(i + 1)
   182  		list[i], list[j] = list[j], list[i]
   183  	}
   184  }
   185  
   186  // maxUint64 returns the maximum value
   187  func maxUint64(inputs ...uint64) uint64 {
   188  	l := len(inputs)
   189  	if l == 0 {
   190  		return 0
   191  	} else if l == 1 {
   192  		return inputs[0]
   193  	}
   194  
   195  	max := inputs[0]
   196  	for i := 1; i < l; i++ {
   197  		cur := inputs[i]
   198  		if cur > max {
   199  			max = cur
   200  		}
   201  	}
   202  	return max
   203  }