github.com/quite/nomad@v0.8.6/nomad/structs/node_class.go (about)

     1  package structs
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/mitchellh/hashstructure"
     8  )
     9  
    10  const (
    11  	// NodeUniqueNamespace is a prefix that can be appended to node meta or
    12  	// attribute keys to mark them for exclusion in computed node class.
    13  	NodeUniqueNamespace = "unique."
    14  )
    15  
    16  // UniqueNamespace takes a key and returns the key marked under the unique
    17  // namespace.
    18  func UniqueNamespace(key string) string {
    19  	return fmt.Sprintf("%s%s", NodeUniqueNamespace, key)
    20  }
    21  
    22  // IsUniqueNamespace returns whether the key is under the unique namespace.
    23  func IsUniqueNamespace(key string) bool {
    24  	return strings.HasPrefix(key, NodeUniqueNamespace)
    25  }
    26  
    27  // ComputeClass computes a derived class for the node based on its attributes.
    28  // ComputedClass is a unique id that identifies nodes with a common set of
    29  // attributes and capabilities. Thus, when calculating a node's computed class
    30  // we avoid including any uniquely identifying fields.
    31  func (n *Node) ComputeClass() error {
    32  	hash, err := hashstructure.Hash(n, nil)
    33  	if err != nil {
    34  		return err
    35  	}
    36  
    37  	n.ComputedClass = fmt.Sprintf("v1:%d", hash)
    38  	return nil
    39  }
    40  
    41  // HashInclude is used to blacklist uniquely identifying node fields from being
    42  // included in the computed node class.
    43  func (n Node) HashInclude(field string, v interface{}) (bool, error) {
    44  	switch field {
    45  	case "Datacenter", "Attributes", "Meta", "NodeClass":
    46  		return true, nil
    47  	default:
    48  		return false, nil
    49  	}
    50  }
    51  
    52  // HashIncludeMap is used to blacklist uniquely identifying node map keys from being
    53  // included in the computed node class.
    54  func (n Node) HashIncludeMap(field string, k, v interface{}) (bool, error) {
    55  	key, ok := k.(string)
    56  	if !ok {
    57  		return false, fmt.Errorf("map key %v not a string", k)
    58  	}
    59  
    60  	switch field {
    61  	case "Meta", "Attributes":
    62  		return !IsUniqueNamespace(key), nil
    63  	default:
    64  		return false, fmt.Errorf("unexpected map field: %v", field)
    65  	}
    66  }
    67  
    68  // EscapedConstraints takes a set of constraints and returns the set that
    69  // escapes computed node classes.
    70  func EscapedConstraints(constraints []*Constraint) []*Constraint {
    71  	var escaped []*Constraint
    72  	for _, c := range constraints {
    73  		if constraintTargetEscapes(c.LTarget) || constraintTargetEscapes(c.RTarget) {
    74  			escaped = append(escaped, c)
    75  		}
    76  	}
    77  
    78  	return escaped
    79  }
    80  
    81  // constraintTargetEscapes returns whether the target of a constraint escapes
    82  // computed node class optimization.
    83  func constraintTargetEscapes(target string) bool {
    84  	switch {
    85  	case strings.HasPrefix(target, "${node.unique."):
    86  		return true
    87  	case strings.HasPrefix(target, "${attr.unique."):
    88  		return true
    89  	case strings.HasPrefix(target, "${meta.unique."):
    90  		return true
    91  	default:
    92  		return false
    93  	}
    94  }