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 }