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  }