package nomad import ( crand "crypto/rand" "fmt" "math/rand" "net" "os" "path/filepath" "runtime" "strconv" "time" "github.com/hashicorp/serf/serf" ) // ensurePath is used to make sure a path exists func ensurePath(path string, dir bool) error { if !dir { path = filepath.Dir(path) } return os.MkdirAll(path, 0755) } // RuntimeStats is used to return various runtime information func RuntimeStats() map[string]string { return map[string]string{ "kernel.name": runtime.GOOS, "arch": runtime.GOARCH, "version": runtime.Version(), "max_procs": strconv.FormatInt(int64(runtime.GOMAXPROCS(0)), 10), "goroutines": strconv.FormatInt(int64(runtime.NumGoroutine()), 10), "cpu_count": strconv.FormatInt(int64(runtime.NumCPU()), 10), } } // serverParts is used to return the parts of a server role type serverParts struct { Name string Region string Datacenter string Port int Bootstrap bool Expect int Version int Addr net.Addr } func (s *serverParts) String() string { return fmt.Sprintf("%s (Addr: %s) (DC: %s)", s.Name, s.Addr, s.Datacenter) } // Returns if a member is a Nomad server. Returns a boolean, // and a struct with the various important components func isNomadServer(m serf.Member) (bool, *serverParts) { if m.Tags["role"] != "nomad" { return false, nil } region := m.Tags["region"] datacenter := m.Tags["dc"] _, bootstrap := m.Tags["bootstrap"] expect := 0 expect_str, ok := m.Tags["expect"] var err error if ok { expect, err = strconv.Atoi(expect_str) if err != nil { return false, nil } } port_str := m.Tags["port"] port, err := strconv.Atoi(port_str) if err != nil { return false, nil } vsn_str := m.Tags["vsn"] vsn, err := strconv.Atoi(vsn_str) if err != nil { return false, nil } addr := &net.TCPAddr{IP: m.Addr, Port: port} parts := &serverParts{ Name: m.Name, Region: region, Datacenter: datacenter, Port: port, Bootstrap: bootstrap, Expect: expect, Addr: addr, Version: vsn, } return true, parts } // generateUUID is used to generate a random UUID func generateUUID() string { buf := make([]byte, 16) if _, err := crand.Read(buf); err != nil { panic(fmt.Errorf("failed to read random bytes: %v", err)) } return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x", buf[0:4], buf[4:6], buf[6:8], buf[8:10], buf[10:16]) } // Returns a random stagger interval between 0 and the duration func randomStagger(intv time.Duration) time.Duration { return time.Duration(uint64(rand.Int63()) % uint64(intv)) } // shuffleStrings randomly shuffles the list of strings func shuffleStrings(list []string) { for i := range list { j := rand.Intn(i + 1) list[i], list[j] = list[j], list[i] } } // maxUint64 returns the maximum value func maxUint64(a, b uint64) uint64 { if a >= b { return a } return b } // rateScaledInterval is used to choose an interval to perform an action in order // to target an aggregate number of actions per second across the whole cluster. func rateScaledInterval(rate float64, min time.Duration, n int) time.Duration { interval := time.Duration(float64(time.Second) * float64(n) / rate) if interval < min { return min } return interval }