diff --git a/consul/state/catalog.go b/consul/state/catalog.go index f10a351898..6c3f93a22d 100644 --- a/consul/state/catalog.go +++ b/consul/state/catalog.go @@ -9,6 +9,13 @@ import ( "github.com/hashicorp/go-memdb" ) +const ( + // minUUIDLookupLen is used as a minimum length of a node name required before + // we test to see if the name is actually a UUID and perform an ID-based node + // lookup. + minUUIDLookupLen = 8 +) + // Nodes is used to pull the full list of nodes for use during snapshots. func (s *StateSnapshot) Nodes() (memdb.ResultIterator, error) { iter, err := s.tx.Get("nodes", "id") @@ -151,7 +158,7 @@ func (s *StateStore) ensureNodeTxn(tx *memdb.Txn, idx uint64, node *structs.Node // Check for an existing node existing, err := tx.First("nodes", "id", node.Node) if err != nil { - return fmt.Errorf("node lookup failed: %s", err) + return fmt.Errorf("node name lookup failed: %s", err) } // Get the indexes @@ -174,7 +181,7 @@ func (s *StateStore) ensureNodeTxn(tx *memdb.Txn, idx uint64, node *structs.Node return nil } -// GetNode is used to retrieve a node registration by node ID. +// GetNode is used to retrieve a node registration by node name ID. func (s *StateStore) GetNode(id string) (uint64, *structs.Node, error) { tx := s.db.Txn(false) defer tx.Abort() @@ -193,6 +200,25 @@ func (s *StateStore) GetNode(id string) (uint64, *structs.Node, error) { return idx, nil, nil } +// GetNodeID is used to retrieve a node registration by node ID. +func (s *StateStore) GetNodeID(id types.NodeID) (uint64, *structs.Node, error) { + tx := s.db.Txn(false) + defer tx.Abort() + + // Get the table index. + idx := maxIndexTxn(tx, "nodes") + + // Retrieve the node from the state store + node, err := tx.First("nodes", "uuid", string(id)) + if err != nil { + return 0, nil, fmt.Errorf("node lookup failed: %s", err) + } + if node != nil { + return idx, node.(*structs.Node), nil + } + return idx, nil, nil +} + // Nodes is used to return all of the known nodes. func (s *StateStore) Nodes(ws memdb.WatchSet) (uint64, structs.Nodes, error) { tx := s.db.Txn(false) @@ -662,15 +688,34 @@ func (s *StateStore) NodeServices(ws memdb.WatchSet, nodeName string) (uint64, * // Get the table index. idx := maxIndexTxn(tx, "nodes", "services") - // Query the node + // Query the node by node name watchCh, n, err := tx.FirstWatch("nodes", "id", nodeName) if err != nil { return 0, nil, fmt.Errorf("node lookup failed: %s", err) } - ws.Add(watchCh) + if n == nil { - return 0, nil, nil + if len(nodeName) >= minUUIDLookupLen { + // Attempt to lookup the node by it's node ID + var idWatchCh <-chan struct{} + idWatchCh, n, err = tx.FirstWatch("nodes", "uuid_prefix", nodeName) + if err != nil { + return 0, nil, fmt.Errorf("node ID lookup failed: %s", err) + } + if n == nil { + ws.Add(watchCh) + return 0, nil, nil + } else { + ws.Add(idWatchCh) + } + } else { + ws.Add(watchCh) + return 0, nil, nil + } + } else { + ws.Add(watchCh) } + node := n.(*structs.Node) // Read all of the services diff --git a/consul/state/catalog_test.go b/consul/state/catalog_test.go index eaecfcef71..e142b09383 100644 --- a/consul/state/catalog_test.go +++ b/consul/state/catalog_test.go @@ -10,14 +10,23 @@ import ( "github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/types" "github.com/hashicorp/go-memdb" + uuid "github.com/hashicorp/go-uuid" ) +func makeRandomNodeID(t *testing.T) types.NodeID { + id, err := uuid.GenerateUUID() + if err != nil { + t.Fatalf("err: %v", err) + } + return types.NodeID(id) +} + func TestStateStore_EnsureRegistration(t *testing.T) { s := testStateStore(t) // Start with just a node. req := &structs.RegisterRequest{ - ID: types.NodeID("40e4a748-2192-161a-0510-9bf59fe950b5"), + ID: makeRandomNodeID(t), Node: "node1", Address: "1.2.3.4", TaggedAddresses: map[string]string{ @@ -27,6 +36,7 @@ func TestStateStore_EnsureRegistration(t *testing.T) { "somekey": "somevalue", }, } + nodeID := req.ID if err := s.EnsureRegistration(1, req); err != nil { t.Fatalf("err: %s", err) } @@ -37,7 +47,7 @@ func TestStateStore_EnsureRegistration(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } - if out.ID != types.NodeID("40e4a748-2192-161a-0510-9bf59fe950b5") || + if out.ID != nodeID || out.Node != "node1" || out.Address != "1.2.3.4" || len(out.TaggedAddresses) != 1 || out.TaggedAddresses["hello"] != "world" || @@ -45,6 +55,13 @@ func TestStateStore_EnsureRegistration(t *testing.T) { out.CreateIndex != 1 || out.ModifyIndex != 1 { t.Fatalf("bad node returned: %#v", out) } + _, out2, err := s.GetNodeID(nodeID) + if err != nil { + t.Fatalf("err: %s", err) + } + if !reflect.DeepEqual(out, out2) { + t.Fatalf("bad node returned: %#v -- %#v", out, out2) + } } verifyNode() @@ -183,6 +200,7 @@ func TestStateStore_EnsureRegistration_Restore(t *testing.T) { // Start with just a node. req := &structs.RegisterRequest{ + ID: makeRandomNodeID(t), Node: "node1", Address: "1.2.3.4", } diff --git a/consul/state/schema.go b/consul/state/schema.go index cf701d64e6..5c39bb3b72 100644 --- a/consul/state/schema.go +++ b/consul/state/schema.go @@ -78,6 +78,14 @@ func nodesTableSchema() *memdb.TableSchema { Lowercase: true, }, }, + "uuid": &memdb.IndexSchema{ + Name: "uuid", + AllowMissing: false, + Unique: true, + Indexer: &memdb.UUIDFieldIndex{ + Field: "ID", + }, + }, "meta": &memdb.IndexSchema{ Name: "meta", AllowMissing: true,