github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/client/util.go (about) 1 package client 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "math/rand" 8 "os" 9 "path/filepath" 10 "time" 11 12 "github.com/hashicorp/nomad/nomad/structs" 13 ) 14 15 type allocTuple struct { 16 exist, updated *structs.Allocation 17 } 18 19 // diffResult is used to return the sets that result from a diff 20 type diffResult struct { 21 added []*structs.Allocation 22 removed []*structs.Allocation 23 updated []allocTuple 24 ignore []*structs.Allocation 25 } 26 27 func (d *diffResult) GoString() string { 28 return fmt.Sprintf("allocs: (added %d) (removed %d) (updated %d) (ignore %d)", 29 len(d.added), len(d.removed), len(d.updated), len(d.ignore)) 30 } 31 32 // diffAllocs is used to diff the existing and updated allocations 33 // to see what has happened. 34 func diffAllocs(existing, updated []*structs.Allocation) *diffResult { 35 result := &diffResult{} 36 37 // Index the updated allocations by id 38 idx := make(map[string]*structs.Allocation) 39 for _, update := range updated { 40 idx[update.ID] = update 41 } 42 43 // Scan the existing allocations 44 existIdx := make(map[string]struct{}) 45 for _, exist := range existing { 46 // Mark this as existing 47 existIdx[exist.ID] = struct{}{} 48 49 // Check for presence in the new set 50 update, ok := idx[exist.ID] 51 52 // If not present, removed 53 if !ok { 54 result.removed = append(result.removed, exist) 55 continue 56 } 57 58 // Check for an update 59 if update.ModifyIndex > exist.ModifyIndex { 60 result.updated = append(result.updated, allocTuple{exist, update}) 61 continue 62 } 63 64 // Ignore this 65 result.ignore = append(result.ignore, exist) 66 } 67 68 // Scan the updated allocations for any that are new 69 for _, update := range updated { 70 if _, ok := existIdx[update.ID]; !ok { 71 result.added = append(result.added, update) 72 } 73 } 74 return result 75 } 76 77 // Returns a random stagger interval between 0 and the duration 78 func randomStagger(intv time.Duration) time.Duration { 79 return time.Duration(uint64(rand.Int63()) % uint64(intv)) 80 } 81 82 // shuffleStrings randomly shuffles the list of strings 83 func shuffleStrings(list []string) { 84 for i := range list { 85 j := rand.Intn(i + 1) 86 list[i], list[j] = list[j], list[i] 87 } 88 } 89 90 // persistState is used to help with saving state 91 func persistState(path string, data interface{}) error { 92 buf, err := json.Marshal(data) 93 if err != nil { 94 return fmt.Errorf("failed to encode state: %v", err) 95 } 96 if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { 97 return fmt.Errorf("failed to make dirs for %s: %v", path, err) 98 } 99 if err := ioutil.WriteFile(path, buf, 0600); err != nil { 100 return fmt.Errorf("failed to save state: %v", err) 101 } 102 return nil 103 } 104 105 // restoreState is used to read back in the persisted state 106 func restoreState(path string, data interface{}) error { 107 buf, err := ioutil.ReadFile(path) 108 if err != nil { 109 if os.IsNotExist(err) { 110 return nil 111 } 112 return fmt.Errorf("failed to read state: %v", err) 113 } 114 if err := json.Unmarshal(buf, data); err != nil { 115 return fmt.Errorf("failed to decode state: %v", err) 116 } 117 return nil 118 }