github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/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 []*structs.Allocation, allocs *allocUpdates) *diffResult { 35 // Scan the existing allocations 36 result := &diffResult{} 37 existIdx := make(map[string]struct{}) 38 for _, exist := range existing { 39 // Mark this as existing 40 existIdx[exist.ID] = struct{}{} 41 42 // Check if the alloc was updated or filtered because an update wasn't 43 // needed. 44 alloc, pulled := allocs.pulled[exist.ID] 45 _, filtered := allocs.filtered[exist.ID] 46 47 // If not updated or filtered, removed 48 if !pulled && !filtered { 49 result.removed = append(result.removed, exist) 50 continue 51 } 52 53 // Check for an update 54 if pulled && alloc.AllocModifyIndex > exist.AllocModifyIndex { 55 result.updated = append(result.updated, allocTuple{exist, alloc}) 56 continue 57 } 58 59 // Ignore this 60 result.ignore = append(result.ignore, exist) 61 } 62 63 // Scan the updated allocations for any that are new 64 for id, pulled := range allocs.pulled { 65 if _, ok := existIdx[id]; !ok { 66 result.added = append(result.added, pulled) 67 } 68 } 69 return result 70 } 71 72 // Returns a random stagger interval between 0 and the duration 73 func randomStagger(intv time.Duration) time.Duration { 74 return time.Duration(uint64(rand.Int63()) % uint64(intv)) 75 } 76 77 // shuffleStrings randomly shuffles the list of strings 78 func shuffleStrings(list []string) { 79 for i := range list { 80 j := rand.Intn(i + 1) 81 list[i], list[j] = list[j], list[i] 82 } 83 } 84 85 // persistState is used to help with saving state 86 func persistState(path string, data interface{}) error { 87 buf, err := json.Marshal(data) 88 if err != nil { 89 return fmt.Errorf("failed to encode state: %v", err) 90 } 91 if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { 92 return fmt.Errorf("failed to make dirs for %s: %v", path, err) 93 } 94 if err := ioutil.WriteFile(path, buf, 0600); err != nil { 95 return fmt.Errorf("failed to save state: %v", err) 96 } 97 return nil 98 } 99 100 // restoreState is used to read back in the persisted state 101 func restoreState(path string, data interface{}) error { 102 buf, err := ioutil.ReadFile(path) 103 if err != nil { 104 if os.IsNotExist(err) { 105 return nil 106 } 107 return fmt.Errorf("failed to read state: %v", err) 108 } 109 if err := json.Unmarshal(buf, data); err != nil { 110 return fmt.Errorf("failed to decode state: %v", err) 111 } 112 return nil 113 }