Merge pull request #26 from hashicorp/f-tags

Adding support for multiple tags
This commit is contained in:
Armon Dadgar 2014-04-03 15:51:31 -07:00
commit cef6859069
17 changed files with 237 additions and 123 deletions

View File

@ -19,7 +19,7 @@ func TestHTTPAgentServices(t *testing.T) {
srv1 := &structs.NodeService{
ID: "mysql",
Service: "mysql",
Tag: "master",
Tags: []string{"master"},
Port: 5000,
}
srv.agent.state.AddService(srv1)
@ -394,7 +394,7 @@ func TestHTTPAgentRegisterService(t *testing.T) {
}
args := &ServiceDefinition{
Name: "test",
Tag: "master",
Tags: []string{"master"},
Port: 8000,
Check: CheckType{
TTL: 15 * time.Second,

View File

@ -108,7 +108,7 @@ func TestAgent_AddService(t *testing.T) {
srv := &structs.NodeService{
ID: "redis",
Service: "redis",
Tag: "foo",
Tags: []string{"foo"},
Port: 8000,
}
chk := &CheckType{TTL: time.Minute}

View File

@ -249,7 +249,7 @@ func TestCatalogServiceNodes(t *testing.T) {
Address: "127.0.0.1",
Service: &structs.NodeService{
Service: "api",
Tag: "a",
Tags: []string{"a"},
},
}
var out struct{}
@ -293,7 +293,7 @@ func TestCatalogNodeServices(t *testing.T) {
Address: "127.0.0.1",
Service: &structs.NodeService{
Service: "api",
Tag: "a",
Tags: []string{"a"},
},
}
var out struct{}

View File

@ -186,6 +186,14 @@ func DecodeServiceDefinition(raw interface{}) (*ServiceDefinition, error) {
if !ok {
goto AFTER_FIX
}
// If no 'tags', handle the deprecated 'tag' value.
if _, ok := rawMap["tags"]; !ok {
if tag, ok := rawMap["tag"]; ok {
rawMap["tags"] = []interface{}{tag}
}
}
sub, ok = rawMap["check"]
if !ok {
goto AFTER_FIX

View File

@ -197,7 +197,7 @@ func TestDecodeConfig(t *testing.T) {
func TestDecodeConfig_Service(t *testing.T) {
// Basics
input := `{"service": {"id": "red1", "name": "redis", "tag": "master", "port":8000, "check": {"script": "/bin/check_redis", "interval": "10s", "ttl": "15s" }}}`
input := `{"service": {"id": "red1", "name": "redis", "tags": ["master"], "port":8000, "check": {"script": "/bin/check_redis", "interval": "10s", "ttl": "15s" }}}`
config, err := DecodeConfig(bytes.NewReader([]byte(input)))
if err != nil {
t.Fatalf("err: %s", err)
@ -216,7 +216,7 @@ func TestDecodeConfig_Service(t *testing.T) {
t.Fatalf("bad: %v", serv)
}
if serv.Tag != "master" {
if !strContains(serv.Tags, "master") {
t.Fatalf("bad: %v", serv)
}

View File

@ -219,7 +219,7 @@ func TestDNS_ServiceLookup(t *testing.T) {
Address: "127.0.0.1",
Service: &structs.NodeService{
Service: "db",
Tag: "master",
Tags: []string{"master"},
Port: 12345,
},
}
@ -279,7 +279,7 @@ func TestDNS_ServiceLookup_Dedup(t *testing.T) {
Address: "127.0.0.1",
Service: &structs.NodeService{
Service: "db",
Tag: "master",
Tags: []string{"master"},
Port: 12345,
},
}
@ -295,7 +295,7 @@ func TestDNS_ServiceLookup_Dedup(t *testing.T) {
Service: &structs.NodeService{
ID: "db2",
Service: "db",
Tag: "slave",
Tags: []string{"slave"},
Port: 12345,
},
}
@ -310,7 +310,7 @@ func TestDNS_ServiceLookup_Dedup(t *testing.T) {
Service: &structs.NodeService{
ID: "db3",
Service: "db",
Tag: "slave",
Tags: []string{"slave"},
Port: 12346,
},
}
@ -355,7 +355,7 @@ func TestDNS_ServiceLookup_Dedup_SRV(t *testing.T) {
Address: "127.0.0.1",
Service: &structs.NodeService{
Service: "db",
Tag: "master",
Tags: []string{"master"},
Port: 12345,
},
}
@ -371,7 +371,7 @@ func TestDNS_ServiceLookup_Dedup_SRV(t *testing.T) {
Service: &structs.NodeService{
ID: "db2",
Service: "db",
Tag: "slave",
Tags: []string{"slave"},
Port: 12345,
},
}
@ -386,7 +386,7 @@ func TestDNS_ServiceLookup_Dedup_SRV(t *testing.T) {
Service: &structs.NodeService{
ID: "db3",
Service: "db",
Tag: "slave",
Tags: []string{"slave"},
Port: 12346,
},
}
@ -482,7 +482,7 @@ func TestDNS_ServiceLookup_FilterCritical(t *testing.T) {
Address: "127.0.0.1",
Service: &structs.NodeService{
Service: "db",
Tag: "master",
Tags: []string{"master"},
Port: 12345,
},
Check: &structs.HealthCheck{
@ -502,7 +502,7 @@ func TestDNS_ServiceLookup_FilterCritical(t *testing.T) {
Address: "127.0.0.2",
Service: &structs.NodeService{
Service: "db",
Tag: "master",
Tags: []string{"master"},
Port: 12345,
},
Check: &structs.HealthCheck{

View File

@ -29,7 +29,7 @@ func TestAgentAntiEntropy_Services(t *testing.T) {
srv1 := &structs.NodeService{
ID: "mysql",
Service: "mysql",
Tag: "master",
Tags: []string{"master"},
Port: 5000,
}
agent.state.AddService(srv1)
@ -42,7 +42,7 @@ func TestAgentAntiEntropy_Services(t *testing.T) {
srv2 := &structs.NodeService{
ID: "redis",
Service: "redis",
Tag: "",
Tags: nil,
Port: 8000,
}
agent.state.AddService(srv2)
@ -59,7 +59,7 @@ func TestAgentAntiEntropy_Services(t *testing.T) {
srv3 := &structs.NodeService{
ID: "web",
Service: "web",
Tag: "",
Tags: nil,
Port: 80,
}
agent.state.AddService(srv3)
@ -68,7 +68,7 @@ func TestAgentAntiEntropy_Services(t *testing.T) {
srv4 := &structs.NodeService{
ID: "lb",
Service: "lb",
Tag: "",
Tags: nil,
Port: 443,
}
args.Service = srv4

View File

@ -8,7 +8,7 @@ import (
type ServiceDefinition struct {
ID string
Name string
Tag string
Tags []string
Port int
Check CheckType
}
@ -17,7 +17,7 @@ func (s *ServiceDefinition) NodeService() *structs.NodeService {
ns := &structs.NodeService{
ID: s.ID,
Service: s.Name,
Tag: s.Tag,
Tags: s.Tags,
Port: s.Port,
}
if ns.ID == "" && ns.Service != "" {

View File

@ -29,3 +29,13 @@ func aeScale(interval time.Duration, n int) time.Duration {
func randomStagger(intv time.Duration) time.Duration {
return time.Duration(uint64(rand.Int63()) % uint64(intv))
}
// strContains checks if a list contains a string
func strContains(l []string, s string) bool {
for _, v := range l {
if v == s {
return true
}
}
return false
}

View File

@ -23,7 +23,7 @@ func TestCatalogRegister(t *testing.T) {
Address: "127.0.0.1",
Service: &structs.NodeService{
Service: "db",
Tag: "master",
Tags: []string{"master"},
Port: 8000,
},
}
@ -79,7 +79,7 @@ func TestCatalogRegister_ForwardLeader(t *testing.T) {
Address: "127.0.0.1",
Service: &structs.NodeService{
Service: "db",
Tag: "master",
Tags: []string{"master"},
Port: 8000,
},
}
@ -116,7 +116,7 @@ func TestCatalogRegister_ForwardDC(t *testing.T) {
Address: "127.0.0.1",
Service: &structs.NodeService{
Service: "db",
Tag: "master",
Tags: []string{"master"},
Port: 8000,
},
}
@ -277,7 +277,7 @@ func TestCatalogListServices(t *testing.T) {
// Just add a node
s1.fsm.State().EnsureNode(1, structs.Node{"foo", "127.0.0.1"})
s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{"db", "db", "primary", 5000})
s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{"db", "db", []string{"primary"}, 5000})
if err := client.Call("Catalog.ListServices", &args, &out); err != nil {
t.Fatalf("err: %v", err)
@ -287,7 +287,7 @@ func TestCatalogListServices(t *testing.T) {
t.Fatalf("bad: %v", out)
}
// Consul service should auto-register
if len(out.Services["consul"]) != 1 {
if _, ok := out.Services["consul"]; !ok {
t.Fatalf("bad: %v", out)
}
if len(out.Services["db"]) != 1 {
@ -327,7 +327,7 @@ func TestCatalogListServices_Blocking(t *testing.T) {
go func() {
time.Sleep(100 * time.Millisecond)
s1.fsm.State().EnsureNode(1, structs.Node{"foo", "127.0.0.1"})
s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{"db", "db", "primary", 5000})
s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{"db", "db", []string{"primary"}, 5000})
}()
// Re-run the query
@ -418,7 +418,7 @@ func TestCatalogListServiceNodes(t *testing.T) {
// Just add a node
s1.fsm.State().EnsureNode(1, structs.Node{"foo", "127.0.0.1"})
s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{"db", "db", "primary", 5000})
s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{"db", "db", []string{"primary"}, 5000})
if err := client.Call("Catalog.ServiceNodes", &args, &out); err != nil {
t.Fatalf("err: %v", err)
@ -462,8 +462,8 @@ func TestCatalogNodeServices(t *testing.T) {
// Just add a node
s1.fsm.State().EnsureNode(1, structs.Node{"foo", "127.0.0.1"})
s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{"db", "db", "primary", 5000})
s1.fsm.State().EnsureService(3, "foo", &structs.NodeService{"web", "web", "", 80})
s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{"db", "db", []string{"primary"}, 5000})
s1.fsm.State().EnsureService(3, "foo", &structs.NodeService{"web", "web", nil, 80})
if err := client.Call("Catalog.NodeServices", &args, &out); err != nil {
t.Fatalf("err: %v", err)
@ -476,10 +476,10 @@ func TestCatalogNodeServices(t *testing.T) {
t.Fatalf("bad: %v", out)
}
services := out.NodeServices.Services
if services["db"].Tag != "primary" || services["db"].Port != 5000 {
if !strContains(services["db"].Tags, "primary") || services["db"].Port != 5000 {
t.Fatalf("bad: %v", out)
}
if services["web"].Tag != "" || services["web"].Port != 80 {
if services["web"].Tags != nil || services["web"].Port != 80 {
t.Fatalf("bad: %v", out)
}
}
@ -498,7 +498,7 @@ func TestCatalogRegister_FailedCase1(t *testing.T) {
Address: "127.0.0.2",
Service: &structs.NodeService{
Service: "web",
Tag: "",
Tags: nil,
Port: 8000,
},
}

View File

@ -85,7 +85,7 @@ func TestFSM_RegisterNode_Service(t *testing.T) {
Service: &structs.NodeService{
ID: "db",
Service: "db",
Tag: "master",
Tags: []string{"master"},
Port: 8000,
},
Check: &structs.HealthCheck{
@ -138,7 +138,7 @@ func TestFSM_DeregisterService(t *testing.T) {
Service: &structs.NodeService{
ID: "db",
Service: "db",
Tag: "master",
Tags: []string{"master"},
Port: 8000,
},
}
@ -248,7 +248,7 @@ func TestFSM_DeregisterNode(t *testing.T) {
Service: &structs.NodeService{
ID: "db",
Service: "db",
Tag: "master",
Tags: []string{"master"},
Port: 8000,
},
Check: &structs.HealthCheck{
@ -311,10 +311,10 @@ func TestFSM_SnapshotRestore(t *testing.T) {
// Add some state
fsm.state.EnsureNode(1, structs.Node{"foo", "127.0.0.1"})
fsm.state.EnsureNode(2, structs.Node{"baz", "127.0.0.2"})
fsm.state.EnsureService(3, "foo", &structs.NodeService{"web", "web", "", 80})
fsm.state.EnsureService(4, "foo", &structs.NodeService{"db", "db", "primary", 5000})
fsm.state.EnsureService(5, "baz", &structs.NodeService{"web", "web", "", 80})
fsm.state.EnsureService(6, "baz", &structs.NodeService{"db", "db", "secondary", 5000})
fsm.state.EnsureService(3, "foo", &structs.NodeService{"web", "web", nil, 80})
fsm.state.EnsureService(4, "foo", &structs.NodeService{"db", "db", []string{"primary"}, 5000})
fsm.state.EnsureService(5, "baz", &structs.NodeService{"web", "web", nil, 80})
fsm.state.EnsureService(6, "baz", &structs.NodeService{"db", "db", []string{"secondary"}, 5000})
fsm.state.EnsureCheck(7, &structs.HealthCheck{
Node: "foo",
CheckID: "web",
@ -363,7 +363,7 @@ func TestFSM_SnapshotRestore(t *testing.T) {
if len(fooSrv.Services) != 2 {
t.Fatalf("Bad: %v", fooSrv)
}
if fooSrv.Services["db"].Tag != "primary" {
if !strContains(fooSrv.Services["db"].Tags, "primary") {
t.Fatalf("Bad: %v", fooSrv)
}
if fooSrv.Services["db"].Port != 5000 {

View File

@ -160,7 +160,7 @@ func TestHealth_ServiceNodes(t *testing.T) {
Service: &structs.NodeService{
ID: "db",
Service: "db",
Tag: "master",
Tags: []string{"master"},
},
Check: &structs.HealthCheck{
Name: "db connect",
@ -180,7 +180,7 @@ func TestHealth_ServiceNodes(t *testing.T) {
Service: &structs.NodeService{
ID: "db",
Service: "db",
Tag: "slave",
Tags: []string{"slave"},
},
Check: &structs.HealthCheck{
Name: "db connect",
@ -213,10 +213,10 @@ func TestHealth_ServiceNodes(t *testing.T) {
if nodes[1].Node.Node != "bar" {
t.Fatalf("Bad: %v", nodes[1])
}
if nodes[0].Service.Tag != "master" {
if !strContains(nodes[0].Service.Tags, "master") {
t.Fatalf("Bad: %v", nodes[0])
}
if nodes[1].Service.Tag != "slave" {
if !strContains(nodes[1].Service.Tags, "slave") {
t.Fatalf("Bad: %v", nodes[1])
}
if nodes[0].Checks[0].Status != structs.HealthPassing {

View File

@ -145,7 +145,7 @@ func (s *StateStore) initialize() error {
},
"service": &MDBIndex{
AllowBlank: true,
Fields: []string{"ServiceName", "ServiceTag"},
Fields: []string{"ServiceName"},
},
},
Decoder: func(buf []byte) interface{} {
@ -318,7 +318,7 @@ func (s *StateStore) EnsureService(index uint64, node string, ns *structs.NodeSe
Node: node,
ServiceID: ns.ID,
ServiceName: ns.Service,
ServiceTag: ns.Tag,
ServiceTags: ns.Tags,
ServicePort: ns.Port,
}
@ -382,7 +382,7 @@ func (s *StateStore) parseNodeServices(tables MDBTables, tx *MDBTxn, name string
srv := &structs.NodeService{
ID: service.ServiceID,
Service: service.ServiceName,
Tag: service.ServiceTag,
Tags: service.ServiceTags,
Port: service.ServicePort,
}
ns.Services[srv.ID] = srv
@ -456,8 +456,6 @@ func (s *StateStore) DeleteNode(index uint64, node string) error {
// Services is used to return all the services with a list of associated tags
func (s *StateStore) Services() (uint64, map[string][]string) {
// TODO: Optimize to not table scan.. We can do a distinct
// type of query to avoid this
services := make(map[string][]string)
idx, res, err := s.serviceTable.Get("id")
if err != nil {
@ -466,12 +464,17 @@ func (s *StateStore) Services() (uint64, map[string][]string) {
}
for _, r := range res {
srv := r.(*structs.ServiceNode)
tags := services[srv.ServiceName]
if !strContains(tags, srv.ServiceTag) {
tags = append(tags, srv.ServiceTag)
tags, ok := services[srv.ServiceName]
if !ok {
services[srv.ServiceName] = tags
}
for _, tag := range srv.ServiceTags {
if !strContains(tags, tag) {
tags = append(tags, tag)
services[srv.ServiceName] = tags
}
}
}
return idx, services
}
@ -508,10 +511,26 @@ func (s *StateStore) ServiceTagNodes(service, tag string) (uint64, structs.Servi
panic(fmt.Errorf("Failed to get last index: %v", err))
}
res, err := s.serviceTable.GetTxn(tx, "service", service, tag)
res, err := s.serviceTable.GetTxn(tx, "service", service)
res = serviceTagFilter(res, tag)
return idx, s.parseServiceNodes(tx, s.nodeTable, res, err)
}
// serviceTagFilter is used to filter a list of *structs.ServiceNode which do
// not have the specified tag
func serviceTagFilter(l []interface{}, tag string) []interface{} {
n := len(l)
for i := 0; i < n; i++ {
srv := l[i].(*structs.ServiceNode)
if !strContains(srv.ServiceTags, tag) {
l[i], l[n-1] = l[n-1], nil
i--
n--
}
}
return l[:n]
}
// parseServiceNodes parses results ServiceNodes and ServiceTagNodes
func (s *StateStore) parseServiceNodes(tx *MDBTxn, table *MDBTable, res []interface{}, err error) structs.ServiceNodes {
nodes := make(structs.ServiceNodes, len(res))
@ -668,7 +687,8 @@ func (s *StateStore) CheckServiceTagNodes(service, tag string) (uint64, structs.
panic(fmt.Errorf("Failed to get last index: %v", err))
}
res, err := s.serviceTable.GetTxn(tx, "service", service, tag)
res, err := s.serviceTable.GetTxn(tx, "service", service)
res = serviceTagFilter(res, tag)
return idx, s.parseCheckServiceNodes(tx, res, err)
}
@ -704,7 +724,7 @@ func (s *StateStore) parseCheckServiceNodes(tx *MDBTxn, res []interface{}, err e
nodes[i].Service = structs.NodeService{
ID: srv.ServiceID,
Service: srv.ServiceName,
Tag: srv.ServiceTag,
Tags: srv.ServiceTags,
Port: srv.ServicePort,
}
nodes[i].Checks = checks

View File

@ -96,15 +96,15 @@ func TestEnsureService(t *testing.T) {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(11, "foo", &structs.NodeService{"api", "api", "", 5000}); err != nil {
if err := store.EnsureService(11, "foo", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(12, "foo", &structs.NodeService{"api", "api", "", 5001}); err != nil {
if err := store.EnsureService(12, "foo", &structs.NodeService{"api", "api", nil, 5001}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(13, "foo", &structs.NodeService{"db", "db", "master", 8000}); err != nil {
if err := store.EnsureService(13, "foo", &structs.NodeService{"db", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
@ -117,7 +117,7 @@ func TestEnsureService(t *testing.T) {
if !ok {
t.Fatalf("missing api: %#v", services)
}
if entry.Tag != "" || entry.Port != 5001 {
if entry.Tags != nil || entry.Port != 5001 {
t.Fatalf("Bad entry: %#v", entry)
}
@ -125,7 +125,7 @@ func TestEnsureService(t *testing.T) {
if !ok {
t.Fatalf("missing db: %#v", services)
}
if entry.Tag != "master" || entry.Port != 8000 {
if !strContains(entry.Tags, "master") || entry.Port != 8000 {
t.Fatalf("Bad entry: %#v", entry)
}
}
@ -141,15 +141,15 @@ func TestEnsureService_DuplicateNode(t *testing.T) {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(11, "foo", &structs.NodeService{"api1", "api", "", 5000}); err != nil {
if err := store.EnsureService(11, "foo", &structs.NodeService{"api1", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(12, "foo", &structs.NodeService{"api2", "api", "", 5001}); err != nil {
if err := store.EnsureService(12, "foo", &structs.NodeService{"api2", "api", nil, 5001}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(13, "foo", &structs.NodeService{"api3", "api", "", 5002}); err != nil {
if err := store.EnsureService(13, "foo", &structs.NodeService{"api3", "api", nil, 5002}); err != nil {
t.Fatalf("err: %v", err)
}
@ -162,7 +162,7 @@ func TestEnsureService_DuplicateNode(t *testing.T) {
if !ok {
t.Fatalf("missing api: %#v", services)
}
if entry.Tag != "" || entry.Port != 5000 {
if entry.Tags != nil || entry.Port != 5000 {
t.Fatalf("Bad entry: %#v", entry)
}
@ -170,7 +170,7 @@ func TestEnsureService_DuplicateNode(t *testing.T) {
if !ok {
t.Fatalf("missing api: %#v", services)
}
if entry.Tag != "" || entry.Port != 5001 {
if entry.Tags != nil || entry.Port != 5001 {
t.Fatalf("Bad entry: %#v", entry)
}
@ -178,7 +178,7 @@ func TestEnsureService_DuplicateNode(t *testing.T) {
if !ok {
t.Fatalf("missing api: %#v", services)
}
if entry.Tag != "" || entry.Port != 5002 {
if entry.Tags != nil || entry.Port != 5002 {
t.Fatalf("Bad entry: %#v", entry)
}
}
@ -194,7 +194,7 @@ func TestDeleteNodeService(t *testing.T) {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(12, "foo", &structs.NodeService{"api", "api", "", 5000}); err != nil {
if err := store.EnsureService(12, "foo", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v", err)
}
@ -242,11 +242,11 @@ func TestDeleteNodeService_One(t *testing.T) {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(12, "foo", &structs.NodeService{"api", "api", "", 5000}); err != nil {
if err := store.EnsureService(12, "foo", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(13, "foo", &structs.NodeService{"api2", "api", "", 5001}); err != nil {
if err := store.EnsureService(13, "foo", &structs.NodeService{"api2", "api", nil, 5001}); err != nil {
t.Fatalf("err: %v", err)
}
@ -279,7 +279,7 @@ func TestDeleteNode(t *testing.T) {
t.Fatalf("err: %v")
}
if err := store.EnsureService(21, "foo", &structs.NodeService{"api", "api", "", 5000}); err != nil {
if err := store.EnsureService(21, "foo", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v")
}
@ -338,15 +338,15 @@ func TestGetServices(t *testing.T) {
t.Fatalf("err: %v")
}
if err := store.EnsureService(32, "foo", &structs.NodeService{"api", "api", "", 5000}); err != nil {
if err := store.EnsureService(32, "foo", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(33, "foo", &structs.NodeService{"db", "db", "master", 8000}); err != nil {
if err := store.EnsureService(33, "foo", &structs.NodeService{"db", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(34, "bar", &structs.NodeService{"db", "db", "slave", 8000}); err != nil {
if err := store.EnsureService(34, "bar", &structs.NodeService{"db", "db", []string{"slave"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
@ -359,7 +359,7 @@ func TestGetServices(t *testing.T) {
if !ok {
t.Fatalf("missing api: %#v", services)
}
if len(tags) != 1 || tags[0] != "" {
if len(tags) != 0 {
t.Fatalf("Bad entry: %#v", tags)
}
@ -388,23 +388,23 @@ func TestServiceNodes(t *testing.T) {
t.Fatalf("err: %v")
}
if err := store.EnsureService(12, "foo", &structs.NodeService{"api", "api", "", 5000}); err != nil {
if err := store.EnsureService(12, "foo", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(13, "bar", &structs.NodeService{"api", "api", "", 5000}); err != nil {
if err := store.EnsureService(13, "bar", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(14, "foo", &structs.NodeService{"db", "db", "master", 8000}); err != nil {
if err := store.EnsureService(14, "foo", &structs.NodeService{"db", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(15, "bar", &structs.NodeService{"db", "db", "slave", 8000}); err != nil {
if err := store.EnsureService(15, "bar", &structs.NodeService{"db", "db", []string{"slave"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(16, "bar", &structs.NodeService{"db2", "db", "slave", 8001}); err != nil {
if err := store.EnsureService(16, "bar", &structs.NodeService{"db2", "db", []string{"slave"}, 8001}); err != nil {
t.Fatalf("err: %v")
}
@ -424,7 +424,7 @@ func TestServiceNodes(t *testing.T) {
if nodes[0].ServiceID != "db" {
t.Fatalf("bad: %v", nodes)
}
if nodes[0].ServiceTag != "master" {
if !strContains(nodes[0].ServiceTags, "master") {
t.Fatalf("bad: %v", nodes)
}
if nodes[0].ServicePort != 8000 {
@ -440,7 +440,7 @@ func TestServiceNodes(t *testing.T) {
if nodes[1].ServiceID != "db" {
t.Fatalf("bad: %v", nodes)
}
if nodes[1].ServiceTag != "slave" {
if !strContains(nodes[1].ServiceTags, "slave") {
t.Fatalf("bad: %v", nodes)
}
if nodes[1].ServicePort != 8000 {
@ -456,7 +456,7 @@ func TestServiceNodes(t *testing.T) {
if nodes[2].ServiceID != "db2" {
t.Fatalf("bad: %v", nodes)
}
if nodes[2].ServiceTag != "slave" {
if !strContains(nodes[2].ServiceTags, "slave") {
t.Fatalf("bad: %v", nodes)
}
if nodes[2].ServicePort != 8001 {
@ -479,15 +479,15 @@ func TestServiceTagNodes(t *testing.T) {
t.Fatalf("err: %v")
}
if err := store.EnsureService(17, "foo", &structs.NodeService{"db", "db", "master", 8000}); err != nil {
if err := store.EnsureService(17, "foo", &structs.NodeService{"db", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(18, "foo", &structs.NodeService{"db2", "db", "slave", 8001}); err != nil {
if err := store.EnsureService(18, "foo", &structs.NodeService{"db2", "db", []string{"slave"}, 8001}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(19, "bar", &structs.NodeService{"db", "db", "slave", 8000}); err != nil {
if err := store.EnsureService(19, "bar", &structs.NodeService{"db", "db", []string{"slave"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
@ -504,7 +504,7 @@ func TestServiceTagNodes(t *testing.T) {
if nodes[0].Address != "127.0.0.1" {
t.Fatalf("bad: %v", nodes)
}
if nodes[0].ServiceTag != "master" {
if !strContains(nodes[0].ServiceTags, "master") {
t.Fatalf("bad: %v", nodes)
}
if nodes[0].ServicePort != 8000 {
@ -512,6 +512,82 @@ func TestServiceTagNodes(t *testing.T) {
}
}
func TestServiceTagNodes_MultipleTags(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(15, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureNode(16, structs.Node{"bar", "127.0.0.2"}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(17, "foo", &structs.NodeService{"db", "db", []string{"master", "v2"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(18, "foo", &structs.NodeService{"db2", "db", []string{"slave", "v2", "dev"}, 8001}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(19, "bar", &structs.NodeService{"db", "db", []string{"slave", "v2"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
idx, nodes := store.ServiceTagNodes("db", "master")
if idx != 19 {
t.Fatalf("bad: %v", idx)
}
if len(nodes) != 1 {
t.Fatalf("bad: %v", nodes)
}
if nodes[0].Node != "foo" {
t.Fatalf("bad: %v", nodes)
}
if nodes[0].Address != "127.0.0.1" {
t.Fatalf("bad: %v", nodes)
}
if !strContains(nodes[0].ServiceTags, "master") {
t.Fatalf("bad: %v", nodes)
}
if nodes[0].ServicePort != 8000 {
t.Fatalf("bad: %v", nodes)
}
idx, nodes = store.ServiceTagNodes("db", "v2")
if idx != 19 {
t.Fatalf("bad: %v", idx)
}
if len(nodes) != 3 {
t.Fatalf("bad: %v", nodes)
}
idx, nodes = store.ServiceTagNodes("db", "dev")
if idx != 19 {
t.Fatalf("bad: %v", idx)
}
if len(nodes) != 1 {
t.Fatalf("bad: %v", nodes)
}
if nodes[0].Node != "foo" {
t.Fatalf("bad: %v", nodes)
}
if nodes[0].Address != "127.0.0.1" {
t.Fatalf("bad: %v", nodes)
}
if !strContains(nodes[0].ServiceTags, "dev") {
t.Fatalf("bad: %v", nodes)
}
if nodes[0].ServicePort != 8001 {
t.Fatalf("bad: %v", nodes)
}
}
func TestStoreSnapshot(t *testing.T) {
store, err := testStateStore()
if err != nil {
@ -527,15 +603,15 @@ func TestStoreSnapshot(t *testing.T) {
t.Fatalf("err: %v")
}
if err := store.EnsureService(10, "foo", &structs.NodeService{"db", "db", "master", 8000}); err != nil {
if err := store.EnsureService(10, "foo", &structs.NodeService{"db", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(11, "foo", &structs.NodeService{"db2", "db", "slave", 8001}); err != nil {
if err := store.EnsureService(11, "foo", &structs.NodeService{"db2", "db", []string{"slave"}, 8001}); err != nil {
t.Fatalf("err: %v")
}
if err := store.EnsureService(12, "bar", &structs.NodeService{"db", "db", "slave", 8000}); err != nil {
if err := store.EnsureService(12, "bar", &structs.NodeService{"db", "db", []string{"slave"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
@ -580,15 +656,15 @@ func TestStoreSnapshot(t *testing.T) {
// Ensure we get the service entries
services := snap.NodeServices("foo")
if services.Services["db"].Tag != "master" {
if !strContains(services.Services["db"].Tags, "master") {
t.Fatalf("bad: %v", services)
}
if services.Services["db2"].Tag != "slave" {
if !strContains(services.Services["db2"].Tags, "slave") {
t.Fatalf("bad: %v", services)
}
services = snap.NodeServices("bar")
if services.Services["db"].Tag != "slave" {
if !strContains(services.Services["db"].Tags, "slave") {
t.Fatalf("bad: %v", services)
}
@ -624,10 +700,10 @@ func TestStoreSnapshot(t *testing.T) {
}
// Make some changes!
if err := store.EnsureService(14, "foo", &structs.NodeService{"db", "db", "slave", 8000}); err != nil {
if err := store.EnsureService(14, "foo", &structs.NodeService{"db", "db", []string{"slave"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(15, "bar", &structs.NodeService{"db", "db", "master", 8000}); err != nil {
if err := store.EnsureService(15, "bar", &structs.NodeService{"db", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureNode(16, structs.Node{"baz", "127.0.0.3"}); err != nil {
@ -656,15 +732,15 @@ func TestStoreSnapshot(t *testing.T) {
// Ensure old service entries
services = snap.NodeServices("foo")
if services.Services["db"].Tag != "master" {
if !strContains(services.Services["db"].Tags, "master") {
t.Fatalf("bad: %v", services)
}
if services.Services["db2"].Tag != "slave" {
if !strContains(services.Services["db2"].Tags, "slave") {
t.Fatalf("bad: %v", services)
}
services = snap.NodeServices("bar")
if services.Services["db"].Tag != "slave" {
if !strContains(services.Services["db"].Tags, "slave") {
t.Fatalf("bad: %v", services)
}
@ -709,7 +785,7 @@ func TestEnsureCheck(t *testing.T) {
if err := store.EnsureNode(1, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(2, "foo", &structs.NodeService{"db1", "db", "master", 8000}); err != nil {
if err := store.EnsureService(2, "foo", &structs.NodeService{"db1", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
check := &structs.HealthCheck{
@ -791,7 +867,7 @@ func TestDeleteNodeCheck(t *testing.T) {
if err := store.EnsureNode(1, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(2, "foo", &structs.NodeService{"db1", "db", "master", 8000}); err != nil {
if err := store.EnsureService(2, "foo", &structs.NodeService{"db1", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
check := &structs.HealthCheck{
@ -841,7 +917,7 @@ func TestCheckServiceNodes(t *testing.T) {
if err := store.EnsureNode(1, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(2, "foo", &structs.NodeService{"db1", "db", "master", 8000}); err != nil {
if err := store.EnsureService(2, "foo", &structs.NodeService{"db1", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
check := &structs.HealthCheck{
@ -922,7 +998,7 @@ func BenchmarkCheckServiceNodes(t *testing.B) {
if err := store.EnsureNode(1, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(2, "foo", &structs.NodeService{"db1", "db", "master", 8000}); err != nil {
if err := store.EnsureService(2, "foo", &structs.NodeService{"db1", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v")
}
check := &structs.HealthCheck{
@ -964,7 +1040,7 @@ func TestSS_Register_Deregister_Query(t *testing.T) {
srv := &structs.NodeService{
"statsite-box-stats",
"statsite-box-stats",
"",
nil,
0}
if err := store.EnsureService(2, "foo", srv); err != nil {
t.Fatalf("err: %v")
@ -973,7 +1049,7 @@ func TestSS_Register_Deregister_Query(t *testing.T) {
srv = &structs.NodeService{
"statsite-share-stats",
"statsite-share-stats",
"",
nil,
0}
if err := store.EnsureService(3, "foo", srv); err != nil {
t.Fatalf("err: %v")

View File

@ -105,7 +105,7 @@ type ServiceNode struct {
Address string
ServiceID string
ServiceName string
ServiceTag string
ServiceTags []string
ServicePort int
}
type ServiceNodes []ServiceNode
@ -114,7 +114,7 @@ type ServiceNodes []ServiceNode
type NodeService struct {
ID string
Service string
Tag string
Tags []string
Port int
}
type NodeServices struct {

View File

@ -169,7 +169,7 @@ This endpoint is hit with a GET and returns a JSON body like this:
"redis":{
"ID":"redis",
"Service":"redis",
"Tag":"",
"Tags":null,
"Port":8000
}
}
@ -301,7 +301,7 @@ body must look like:
{
"ID": "redis1",
"Name": "redis",
"Tag": "master",
"Tags": ["master", "v1"],
"Port": 8000,
"Check": {
"Script": "/usr/local/bin/check_redis.py",
@ -312,7 +312,7 @@ body must look like:
The `Name` field is mandatory, If an `ID` is not provided, it is set to `Name`.
You cannot have duplicate `ID` entries per agent, so it may be necessary to provide an ID.
`Tag`, `Port` and `Check` are optional. If `Check` is provided, only one of `Script` and `Interval`
`Tags`, `Port` and `Check` are optional. If `Check` is provided, only one of `Script` and `Interval`
or `TTL` should be provided. There is more information about checks [here](/docs/agent/checks.html).
The created check will be named "service:\<ServiceId\>".
@ -361,7 +361,7 @@ body must look like:
"Service": {
"ID": "redis1",
"Service": "redis",
"Tag": "master",
"Tags": ["master", "v1"],
"Port": 8000,
},
"Check": {
@ -381,7 +381,7 @@ the node with the catalog.
If the `Service` key is provided, then the service will also be registered. If
`ID` is not provided, it will be defaulted to `Service`. It is mandated that the
ID be node-unique. Both `Tag` and `Port` can be omitted.
ID be node-unique. Both `Tags` and `Port` can be omitted.
If the `Check` key is provided, then a health check will also be registered. It
is important to remember that this register API is very low level. This manipulates
@ -480,8 +480,8 @@ however the dc can be provided using the "?dc=" query parameter.
It returns a JSON body like this:
{
"consul":[""],
"redis":[""],
"consul":[],
"redis":[],
"postgresql":["master","slave"]
}
@ -508,7 +508,7 @@ It returns a JSON body like this:
"Address":"10.1.10.12",
"ServiceID":"redis",
"ServiceName":"redis",
"ServiceTag":"",
"ServiceTags":null,
"ServicePort":8000
}
]
@ -533,13 +533,13 @@ It returns a JSON body like this:
"consul":{
"ID":"consul",
"Service":"consul",
"Tag":"",
"Tags":null,
"Port":8300
},
"redis":{
"ID":"redis",
"Service":"redis",
"Tag":"",
"Tags":["v1"],
"Port":8000
}
}
@ -655,7 +655,7 @@ It returns a JSON body like this:
"Service":{
"ID":"redis",
"Service":"redis",
"Tag":"",
"Tags":null,
"Port":8000
},
"Checks":[

View File

@ -20,7 +20,7 @@ A service definition that is a script looks like:
{
"service": {
"name": "redis",
"tag": "master",
"tags": ["master"],
"port": 8000,
"check": {
"script": "/usr/local/bin/check_redis.py",
@ -30,12 +30,12 @@ A service definition that is a script looks like:
}
A service definition must include a `name`, and may optionally provide
an `id`, `tag`, `port`, and `check`. The `id` is set to the `name` if not
an `id`, `tags`, `port`, and `check`. The `id` is set to the `name` if not
provided. It is required that all services have a unique ID, so if names
might conflict, then unique ID's should be provided.
The `tag` is an opaque value to Consul, but can be used to distinguish
between "master" or "slave" nodes, or any other service level labels.
The `tags` is a list of opaque value to Consul, but can be used to distinguish
between "master" or "slave" nodes, different versions, or any other service level labels.
The `port` can be used as well to make a service oriented architecture
simpler to configure. This way the address and port of a service can
be discovered.