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  }