Have server store unique attrs in glob format
This commit is contained in:
parent
d646903a47
commit
ccb932a9f0
|
@ -4,13 +4,14 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/mitchellh/hashstructure"
|
||||
)
|
||||
|
||||
const (
|
||||
// A suffix that can be appended to node meta keys to mark them for
|
||||
// exclusion in computed node class.
|
||||
NodeMetaUnique = "_unique"
|
||||
// NodeUniqueSuffix is a suffix that can be appended to node meta or
|
||||
// attribute keys to mark them for exclusion in computed node class.
|
||||
NodeUniqueSuffix = "_unique"
|
||||
)
|
||||
|
||||
// ComputeClass computes a derived class for the node based on its attributes.
|
||||
|
@ -18,7 +19,6 @@ const (
|
|||
// attributes and capabilities. Thus, when calculating a node's computed class
|
||||
// we avoid including any uniquely identifing fields.
|
||||
func (n *Node) ComputeClass() error {
|
||||
// TODO: Bucket node resources such as DiskMB/IOPS/etc.
|
||||
hash, err := hashstructure.Hash(n, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -40,13 +40,45 @@ func (n Node) HashInclude(field string, v interface{}) (bool, error) {
|
|||
return false, nil
|
||||
case "CreateIndex", "ModifyIndex": // Raft indexes
|
||||
return false, nil
|
||||
case "Reserved": // Doesn't effect placement capability
|
||||
case "Resources", "Reserved": // Doesn't effect placement capability
|
||||
return false, nil
|
||||
default:
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// GlobalUniqueAttrs is a set of attributes that uniquely identify all
|
||||
// nodes. It is stored once by the server, rather than by each node to
|
||||
// reduce storage costs.
|
||||
GlobalUniqueAttrs = []glob.Glob{
|
||||
glob.MustCompile("consul.name"),
|
||||
glob.MustCompile("platform.gce.hostname"),
|
||||
glob.MustCompile("platform.gce.id"),
|
||||
glob.MustCompile("platform.gce.network.*.ip"),
|
||||
glob.MustCompile("platform.gce.network.*.external-ip"),
|
||||
glob.MustCompile("platform.aws.ami-id"),
|
||||
glob.MustCompile("platform.aws.hostname"),
|
||||
glob.MustCompile("platform.aws.instance-id"),
|
||||
glob.MustCompile("platform.aws.local*"),
|
||||
glob.MustCompile("platform.aws.public*"),
|
||||
glob.MustCompile("network.ip-address"),
|
||||
glob.MustCompile("storage.*"), // Ignore all storage
|
||||
}
|
||||
)
|
||||
|
||||
// excludeAttr returns whether the key should be excluded when calculating
|
||||
// computed node class.
|
||||
func excludeAttr(key string) bool {
|
||||
for _, g := range GlobalUniqueAttrs {
|
||||
if g.Match(key) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HashIncludeMap is used to blacklist uniquely identifying node map keys from being
|
||||
// included in the computed node class.
|
||||
func (n Node) HashIncludeMap(field string, k, v interface{}) (bool, error) {
|
||||
|
@ -55,28 +87,15 @@ func (n Node) HashIncludeMap(field string, k, v interface{}) (bool, error) {
|
|||
return false, fmt.Errorf("map key %v not a string")
|
||||
}
|
||||
|
||||
// Check if the user marked the key as unique.
|
||||
isUnique := strings.HasSuffix(key, NodeUniqueSuffix)
|
||||
|
||||
switch field {
|
||||
case "Attributes":
|
||||
// Check if the key is marked as unique by the fingerprinters.
|
||||
_, unique := n.UniqueAttributes[key]
|
||||
return !unique, nil
|
||||
return !excludeAttr(key) && !isUnique, nil
|
||||
case "Meta":
|
||||
// Check if the user marked the key as unique.
|
||||
return !strings.HasSuffix(key, NodeMetaUnique), nil
|
||||
return !isUnique, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected map field: %v", field)
|
||||
}
|
||||
}
|
||||
|
||||
// HashInclude is used to blacklist uniquely identifying network fields from being
|
||||
// included in the computed node class.
|
||||
func (n NetworkResource) HashInclude(field string, v interface{}) (bool, error) {
|
||||
switch field {
|
||||
case "IP", "CIDR": // Uniquely identifying
|
||||
return false, nil
|
||||
case "ReservedPorts", "DynamicPorts": // Doesn't effect placement capability
|
||||
return false, nil
|
||||
default:
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -15,7 +16,6 @@ func testNode() *Node {
|
|||
"version": "0.1.0",
|
||||
"driver.exec": "1",
|
||||
},
|
||||
UniqueAttributes: make(map[string]struct{}),
|
||||
Resources: &Resources{
|
||||
CPU: 4000,
|
||||
MemoryMB: 8192,
|
||||
|
@ -29,19 +29,6 @@ func testNode() *Node {
|
|||
},
|
||||
},
|
||||
},
|
||||
Reserved: &Resources{
|
||||
CPU: 100,
|
||||
MemoryMB: 256,
|
||||
DiskMB: 4 * 1024,
|
||||
Networks: []*NetworkResource{
|
||||
&NetworkResource{
|
||||
Device: "eth0",
|
||||
IP: "192.168.0.100",
|
||||
ReservedPorts: []Port{{Label: "main", Value: 22}},
|
||||
MBits: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
Links: map[string]string{
|
||||
"consul": "foobar.dc1",
|
||||
},
|
||||
|
@ -111,46 +98,6 @@ func TestNode_ComputedClass_Ignore(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNode_ComputedClass_NetworkResources(t *testing.T) {
|
||||
// Create a node with a few network resources and gets it computed class
|
||||
nr1 := &NetworkResource{
|
||||
Device: "eth0",
|
||||
CIDR: "192.168.0.100/32",
|
||||
MBits: 1000,
|
||||
}
|
||||
nr2 := &NetworkResource{
|
||||
Device: "eth1",
|
||||
CIDR: "192.168.0.100/32",
|
||||
MBits: 500,
|
||||
}
|
||||
n := &Node{
|
||||
Resources: &Resources{
|
||||
Networks: []*NetworkResource{nr1, nr2},
|
||||
},
|
||||
}
|
||||
if err := n.ComputeClass(); err != nil {
|
||||
t.Fatalf("ComputeClass() failed: %v", err)
|
||||
}
|
||||
if n.ComputedClass == 0 {
|
||||
t.Fatal("ComputeClass() didn't set computed class")
|
||||
}
|
||||
old := n.ComputedClass
|
||||
|
||||
// Change the order of the network resources and compute the class again.
|
||||
n.Resources.Networks = []*NetworkResource{nr2, nr1}
|
||||
if err := n.ComputeClass(); err != nil {
|
||||
t.Fatalf("ComputeClass() failed: %v", err)
|
||||
}
|
||||
if n.ComputedClass == 0 {
|
||||
t.Fatal("ComputeClass() didn't set computed class")
|
||||
}
|
||||
|
||||
if old != n.ComputedClass {
|
||||
t.Fatal("ComputeClass() didn't ignore NetworkResource order")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNode_ComputedClass_Attr(t *testing.T) {
|
||||
// Create a node and gets it computed class
|
||||
n := testNode()
|
||||
|
@ -162,6 +109,15 @@ func TestNode_ComputedClass_Attr(t *testing.T) {
|
|||
}
|
||||
old := n.ComputedClass
|
||||
|
||||
// Add a unique addr and compute the class again
|
||||
n.Attributes[fmt.Sprintf("%s%s", "foo", NodeUniqueSuffix)] = "bar"
|
||||
if err := n.ComputeClass(); err != nil {
|
||||
t.Fatalf("ComputeClass() failed: %v", err)
|
||||
}
|
||||
if old != n.ComputedClass {
|
||||
t.Fatal("ComputeClass() didn't ignore unique attr suffix")
|
||||
}
|
||||
|
||||
// Modify an attribute and compute the class again.
|
||||
n.Attributes["version"] = "New Version"
|
||||
if err := n.ComputeClass(); err != nil {
|
||||
|
@ -176,9 +132,7 @@ func TestNode_ComputedClass_Attr(t *testing.T) {
|
|||
old = n.ComputedClass
|
||||
|
||||
// Add an ignored attribute and compute the class again.
|
||||
key := "ignore"
|
||||
n.Attributes[key] = "hello world"
|
||||
n.UniqueAttributes[key] = struct{}{}
|
||||
n.Attributes["network.ip-address"] = "hello world"
|
||||
if err := n.ComputeClass(); err != nil {
|
||||
t.Fatalf("ComputeClass() failed: %v", err)
|
||||
}
|
||||
|
|
|
@ -476,9 +476,6 @@ type Node struct {
|
|||
// "docker.runtime=1.8.3"
|
||||
Attributes map[string]string
|
||||
|
||||
// UniqueAttributes are attributes that uniquely identify a node.
|
||||
UniqueAttributes map[string]struct{}
|
||||
|
||||
// Resources is the available resources on the client.
|
||||
// For example 'cpu=2' 'memory=2048'
|
||||
Resources *Resources
|
||||
|
@ -570,7 +567,7 @@ type Resources struct {
|
|||
MemoryMB int `mapstructure:"memory"`
|
||||
DiskMB int `mapstructure:"disk"`
|
||||
IOPS int
|
||||
Networks []*NetworkResource `hash:"set"`
|
||||
Networks []*NetworkResource
|
||||
}
|
||||
|
||||
// Copy returns a deep copy of the resources
|
||||
|
|
Loading…
Reference in New Issue