github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/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  
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  )
    13  
    14  type allocTuple struct {
    15  	exist, updated *structs.Allocation
    16  }
    17  
    18  // diffResult is used to return the sets that result from a diff
    19  type diffResult struct {
    20  	added   []*structs.Allocation
    21  	removed []*structs.Allocation
    22  	updated []allocTuple
    23  	ignore  []*structs.Allocation
    24  }
    25  
    26  func (d *diffResult) GoString() string {
    27  	return fmt.Sprintf("allocs: (added %d) (removed %d) (updated %d) (ignore %d)",
    28  		len(d.added), len(d.removed), len(d.updated), len(d.ignore))
    29  }
    30  
    31  // diffAllocs is used to diff the existing and updated allocations
    32  // to see what has happened.
    33  func diffAllocs(existing []*structs.Allocation, allocs *allocUpdates) *diffResult {
    34  	// Scan the existing allocations
    35  	result := &diffResult{}
    36  	existIdx := make(map[string]struct{})
    37  	for _, exist := range existing {
    38  		// Mark this as existing
    39  		existIdx[exist.ID] = struct{}{}
    40  
    41  		// Check if the alloc was updated or filtered because an update wasn't
    42  		// needed.
    43  		alloc, pulled := allocs.pulled[exist.ID]
    44  		_, filtered := allocs.filtered[exist.ID]
    45  
    46  		// If not updated or filtered, removed
    47  		if !pulled && !filtered {
    48  			result.removed = append(result.removed, exist)
    49  			continue
    50  		}
    51  
    52  		// Check for an update
    53  		if pulled && alloc.AllocModifyIndex > exist.AllocModifyIndex {
    54  			result.updated = append(result.updated, allocTuple{exist, alloc})
    55  			continue
    56  		}
    57  
    58  		// Ignore this
    59  		result.ignore = append(result.ignore, exist)
    60  	}
    61  
    62  	// Scan the updated allocations for any that are new
    63  	for id, pulled := range allocs.pulled {
    64  		if _, ok := existIdx[id]; !ok {
    65  			result.added = append(result.added, pulled)
    66  		}
    67  	}
    68  	return result
    69  }
    70  
    71  // shuffleStrings randomly shuffles the list of strings
    72  func shuffleStrings(list []string) {
    73  	for i := range list {
    74  		j := rand.Intn(i + 1)
    75  		list[i], list[j] = list[j], list[i]
    76  	}
    77  }
    78  
    79  // persistState is used to help with saving state
    80  func persistState(path string, data interface{}) error {
    81  	buf, err := json.Marshal(data)
    82  	if err != nil {
    83  		return fmt.Errorf("failed to encode state: %v", err)
    84  	}
    85  	if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
    86  		return fmt.Errorf("failed to make dirs for %s: %v", path, err)
    87  	}
    88  	tmpPath := path + ".tmp"
    89  	if err := ioutil.WriteFile(tmpPath, buf, 0600); err != nil {
    90  		return fmt.Errorf("failed to save state to tmp: %v", err)
    91  	}
    92  	if err := os.Rename(tmpPath, path); err != nil {
    93  		return fmt.Errorf("failed to rename tmp to path: %v", err)
    94  	}
    95  
    96  	// Sanity check since users have reported empty state files on disk
    97  	if stat, err := os.Stat(path); err != nil {
    98  		return fmt.Errorf("unable to stat state file %s: %v", path, err)
    99  	} else if stat.Size() == 0 {
   100  		return fmt.Errorf("persisted invalid state file %s; see https://github.com/hashicorp/nomad/issues/1367", path)
   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  }