Makes the filled-in parts of ServiceNode more explicit.

This commit is contained in:
James Phillips 2016-08-12 16:28:56 -07:00
parent cdeff022dd
commit f7eaa06616
No known key found for this signature in database
GPG Key ID: 77183E682AC5FC11
3 changed files with 51 additions and 19 deletions

View File

@ -678,9 +678,11 @@ func (s *StateStore) ensureServiceTxn(tx *memdb.Txn, idx uint64, watches *DumbWa
return fmt.Errorf("failed service lookup: %s", err) return fmt.Errorf("failed service lookup: %s", err)
} }
// Create the service node entry and populate the indexes. We leave the // Create the service node entry and populate the indexes. Note that
// address blank and fill that in on the way out during queries. // conversion doesn't populate any of the node-specific information
entry := svc.ToServiceNode(node, "") // (Address and TaggedAddresses). That's always populated when we read
// from the state store.
entry := svc.ToServiceNode(node)
if existing != nil { if existing != nil {
entry.CreateIndex = existing.(*structs.ServiceNode).CreateIndex entry.CreateIndex = existing.(*structs.ServiceNode).CreateIndex
entry.ModifyIndex = idx entry.ModifyIndex = idx
@ -830,18 +832,23 @@ func (s *StateStore) parseServiceNodes(tx *memdb.Txn, services structs.ServiceNo
var results structs.ServiceNodes var results structs.ServiceNodes
for _, sn := range services { for _, sn := range services {
// Note that we have to clone here because we don't want to // Note that we have to clone here because we don't want to
// modify the address field on the object in the database, // modify the node-related fields on the object in the database,
// which is what we are referencing. // which is what we are referencing.
s := sn.Clone() s := sn.PartialClone()
// Fill in the address of the node. // Grab the corresponding node record.
n, err := tx.First("nodes", "id", sn.Node) n, err := tx.First("nodes", "id", sn.Node)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed node lookup: %s", err) return nil, fmt.Errorf("failed node lookup: %s", err)
} }
// Populate the node-related fields. The tagged addresses may be
// used by agents to perform address translation if they are
// configured to do that.
node := n.(*structs.Node) node := n.(*structs.Node)
s.Address = node.Address s.Address = node.Address
s.TaggedAddresses = node.TaggedAddresses s.TaggedAddresses = node.TaggedAddresses
results = append(results, s) results = append(results, s)
} }
return results, nil return results, nil

View File

@ -260,7 +260,11 @@ type Nodes []*Node
// Maps service name to available tags // Maps service name to available tags
type Services map[string][]string type Services map[string][]string
// ServiceNode represents a node that is part of a service // ServiceNode represents a node that is part of a service. Address and
// TaggedAddresses are node-related fields that are always empty in the state
// store and are filled in on the way out by parseServiceNodes(). This is also
// why PartialClone() skips them, because we know they are blank already so it
// would be a waste of time to copy them.
type ServiceNode struct { type ServiceNode struct {
Node string Node string
Address string Address string
@ -275,14 +279,16 @@ type ServiceNode struct {
RaftIndex RaftIndex
} }
// Clone returns a clone of the given service node. // PartialClone() returns a clone of the given service node, minus the node-
func (s *ServiceNode) Clone() *ServiceNode { // related fields that get filled in later, Address and TaggedAddresses.
func (s *ServiceNode) PartialClone() *ServiceNode {
tags := make([]string, len(s.ServiceTags)) tags := make([]string, len(s.ServiceTags))
copy(tags, s.ServiceTags) copy(tags, s.ServiceTags)
return &ServiceNode{ return &ServiceNode{
Node: s.Node, Node: s.Node,
Address: s.Address, // Skip Address, see above.
// Skip TaggedAddresses, see above.
ServiceID: s.ServiceID, ServiceID: s.ServiceID,
ServiceName: s.ServiceName, ServiceName: s.ServiceName,
ServiceTags: tags, ServiceTags: tags,
@ -344,10 +350,11 @@ func (s *NodeService) IsSame(other *NodeService) bool {
} }
// ToServiceNode converts the given node service to a service node. // ToServiceNode converts the given node service to a service node.
func (s *NodeService) ToServiceNode(node, address string) *ServiceNode { func (s *NodeService) ToServiceNode(node string) *ServiceNode {
return &ServiceNode{ return &ServiceNode{
Node: node, Node: node,
Address: address, // Skip Address, see ServiceNode definition.
// Skip TaggedAddresses, see ServiceNode definition.
ServiceID: s.ID, ServiceID: s.ID,
ServiceName: s.Service, ServiceName: s.Service,
ServiceTags: s.Tags, ServiceTags: s.Tags,

View File

@ -108,8 +108,11 @@ func TestStructs_ACL_IsSame(t *testing.T) {
// testServiceNode gives a fully filled out ServiceNode instance. // testServiceNode gives a fully filled out ServiceNode instance.
func testServiceNode() *ServiceNode { func testServiceNode() *ServiceNode {
return &ServiceNode{ return &ServiceNode{
Node: "node1", Node: "node1",
Address: "127.0.0.1", Address: "127.0.0.1",
TaggedAddresses: map[string]string{
"hello": "world",
},
ServiceID: "service1", ServiceID: "service1",
ServiceName: "dogs", ServiceName: "dogs",
ServiceTags: []string{"prod", "v1"}, ServiceTags: []string{"prod", "v1"},
@ -123,10 +126,20 @@ func testServiceNode() *ServiceNode {
} }
} }
func TestStructs_ServiceNode_Clone(t *testing.T) { func TestStructs_ServiceNode_PartialClone(t *testing.T) {
sn := testServiceNode() sn := testServiceNode()
clone := sn.Clone() clone := sn.PartialClone()
// Make sure the parts that weren't supposed to be cloned didn't get
// copied over, then zero-value them out so we can do a DeepEqual() on
// the rest of the contents.
if clone.Address != "" || len(clone.TaggedAddresses) != 0 {
t.Fatalf("bad: %v", clone)
}
sn.Address = ""
sn.TaggedAddresses = nil
if !reflect.DeepEqual(sn, clone) { if !reflect.DeepEqual(sn, clone) {
t.Fatalf("bad: %v", clone) t.Fatalf("bad: %v", clone)
} }
@ -140,7 +153,12 @@ func TestStructs_ServiceNode_Clone(t *testing.T) {
func TestStructs_ServiceNode_Conversions(t *testing.T) { func TestStructs_ServiceNode_Conversions(t *testing.T) {
sn := testServiceNode() sn := testServiceNode()
sn2 := sn.ToNodeService().ToServiceNode("node1", "127.0.0.1") sn2 := sn.ToNodeService().ToServiceNode("node1")
// These two fields get lost in the conversion, so we have to zero-value
// them out before we do the compare.
sn.Address = ""
sn.TaggedAddresses = nil
if !reflect.DeepEqual(sn, sn2) { if !reflect.DeepEqual(sn, sn2) {
t.Fatalf("bad: %v", sn2) t.Fatalf("bad: %v", sn2)
} }