open-consul/consul/state_store_test.go
Atin Malaviya aa0cecd04e Ephemeral Nodes for via Session behavior settings.
Added a "delete" behavior for session invalidation, in addition to
the default "release" behavior. On session invalidation, the sessions
Behavior field is checked and if it is set to "delete", all nodes owned
by the session are deleted. If it is "release", then just the locks
are released as default.
2014-11-20 11:34:45 -05:00

2473 lines
53 KiB
Go

package consul
import (
"os"
"reflect"
"sort"
"testing"
"time"
"github.com/hashicorp/consul/consul/structs"
)
func testStateStore() (*StateStore, error) {
return NewStateStore(os.Stderr)
}
func TestEnsureRegistration(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
reg := &structs.RegisterRequest{
Node: "foo",
Address: "127.0.0.1",
Service: &structs.NodeService{"api", "api", nil, 5000},
Check: &structs.HealthCheck{
Node: "foo",
CheckID: "api",
Name: "Can connect",
Status: structs.HealthPassing,
ServiceID: "api",
},
}
if err := store.EnsureRegistration(13, reg); err != nil {
t.Fatalf("err: %v", err)
}
idx, found, addr := store.GetNode("foo")
if idx != 13 || !found || addr != "127.0.0.1" {
t.Fatalf("Bad: %v %v %v", idx, found, addr)
}
idx, services := store.NodeServices("foo")
if idx != 13 {
t.Fatalf("bad: %v", idx)
}
entry, ok := services.Services["api"]
if !ok {
t.Fatalf("missing api: %#v", services)
}
if len(entry.Tags) != 0 || entry.Port != 5000 {
t.Fatalf("Bad entry: %#v", entry)
}
idx, checks := store.NodeChecks("foo")
if idx != 13 {
t.Fatalf("bad: %v", idx)
}
if len(checks) != 1 {
t.Fatalf("check: %#v", checks)
}
}
func TestEnsureNode(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(3, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
idx, found, addr := store.GetNode("foo")
if idx != 3 || !found || addr != "127.0.0.1" {
t.Fatalf("Bad: %v %v %v", idx, found, addr)
}
if err := store.EnsureNode(4, structs.Node{"foo", "127.0.0.2"}); err != nil {
t.Fatalf("err: %v", err)
}
idx, found, addr = store.GetNode("foo")
if idx != 4 || !found || addr != "127.0.0.2" {
t.Fatalf("Bad: %v %v %v", idx, found, addr)
}
}
func TestGetNodes(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(40, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureNode(41, structs.Node{"bar", "127.0.0.2"}); err != nil {
t.Fatalf("err: %v", err)
}
idx, nodes := store.Nodes()
if idx != 41 {
t.Fatalf("idx: %v", idx)
}
if len(nodes) != 2 {
t.Fatalf("Bad: %v", nodes)
}
if nodes[1].Node != "foo" && nodes[0].Node != "bar" {
t.Fatalf("Bad: %v", nodes)
}
}
func BenchmarkGetNodes(b *testing.B) {
store, err := testStateStore()
if err != nil {
b.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(100, structs.Node{"foo", "127.0.0.1"}); err != nil {
b.Fatalf("err: %v", err)
}
if err := store.EnsureNode(101, structs.Node{"bar", "127.0.0.2"}); err != nil {
b.Fatalf("err: %v", err)
}
for i := 0; i < b.N; i++ {
store.Nodes()
}
}
func TestEnsureService(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(10, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
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", nil, 5001}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(13, "foo", &structs.NodeService{"db", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
idx, services := store.NodeServices("foo")
if idx != 13 {
t.Fatalf("bad: %v", idx)
}
entry, ok := services.Services["api"]
if !ok {
t.Fatalf("missing api: %#v", services)
}
if len(entry.Tags) != 0 || entry.Port != 5001 {
t.Fatalf("Bad entry: %#v", entry)
}
entry, ok = services.Services["db"]
if !ok {
t.Fatalf("missing db: %#v", services)
}
if !strContains(entry.Tags, "master") || entry.Port != 8000 {
t.Fatalf("Bad entry: %#v", entry)
}
}
func TestEnsureService_DuplicateNode(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(10, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
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", nil, 5001}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(13, "foo", &structs.NodeService{"api3", "api", nil, 5002}); err != nil {
t.Fatalf("err: %v", err)
}
idx, services := store.NodeServices("foo")
if idx != 13 {
t.Fatalf("bad: %v", idx)
}
entry, ok := services.Services["api1"]
if !ok {
t.Fatalf("missing api: %#v", services)
}
if len(entry.Tags) != 0 || entry.Port != 5000 {
t.Fatalf("Bad entry: %#v", entry)
}
entry, ok = services.Services["api2"]
if !ok {
t.Fatalf("missing api: %#v", services)
}
if len(entry.Tags) != 0 || entry.Port != 5001 {
t.Fatalf("Bad entry: %#v", entry)
}
entry, ok = services.Services["api3"]
if !ok {
t.Fatalf("missing api: %#v", services)
}
if len(entry.Tags) != 0 || entry.Port != 5002 {
t.Fatalf("Bad entry: %#v", entry)
}
}
func TestDeleteNodeService(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(11, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(12, "foo", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v", err)
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "api",
Name: "Can connect",
Status: structs.HealthPassing,
ServiceID: "api",
}
if err := store.EnsureCheck(13, check); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.DeleteNodeService(14, "foo", "api"); err != nil {
t.Fatalf("err: %v", err)
}
idx, services := store.NodeServices("foo")
if idx != 14 {
t.Fatalf("bad: %v", idx)
}
_, ok := services.Services["api"]
if ok {
t.Fatalf("has api: %#v", services)
}
idx, checks := store.NodeChecks("foo")
if idx != 14 {
t.Fatalf("bad: %v", idx)
}
if len(checks) != 0 {
t.Fatalf("has check: %#v", checks)
}
}
func TestDeleteNodeService_One(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(11, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
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", nil, 5001}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.DeleteNodeService(14, "foo", "api"); err != nil {
t.Fatalf("err: %v", err)
}
idx, services := store.NodeServices("foo")
if idx != 14 {
t.Fatalf("bad: %v", idx)
}
_, ok := services.Services["api"]
if ok {
t.Fatalf("has api: %#v", services)
}
_, ok = services.Services["api2"]
if !ok {
t.Fatalf("does not have api2: %#v", services)
}
}
func TestDeleteNode(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(20, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(21, "foo", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v", err)
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "db",
Name: "Can connect",
Status: structs.HealthPassing,
ServiceID: "api",
}
if err := store.EnsureCheck(22, check); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.DeleteNode(23, "foo"); err != nil {
t.Fatalf("err: %v", err)
}
idx, services := store.NodeServices("foo")
if idx != 23 {
t.Fatalf("bad: %v", idx)
}
if services != nil {
t.Fatalf("has services: %#v", services)
}
idx, checks := store.NodeChecks("foo")
if idx != 23 {
t.Fatalf("bad: %v", idx)
}
if len(checks) > 0 {
t.Fatalf("has checks: %v", checks)
}
idx, found, _ := store.GetNode("foo")
if idx != 23 {
t.Fatalf("bad: %v", idx)
}
if found {
t.Fatalf("found node")
}
}
func TestGetServices(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(30, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureNode(31, structs.Node{"bar", "127.0.0.2"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(32, "foo", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(33, "foo", &structs.NodeService{"db", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(34, "bar", &structs.NodeService{"db", "db", []string{"slave"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
idx, services := store.Services()
if idx != 34 {
t.Fatalf("bad: %v", idx)
}
tags, ok := services["api"]
if !ok {
t.Fatalf("missing api: %#v", services)
}
if len(tags) != 0 {
t.Fatalf("Bad entry: %#v", tags)
}
tags, ok = services["db"]
sort.Strings(tags)
if !ok {
t.Fatalf("missing db: %#v", services)
}
if len(tags) != 2 || tags[0] != "master" || tags[1] != "slave" {
t.Fatalf("Bad entry: %#v", tags)
}
}
func TestServiceNodes(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(10, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureNode(11, structs.Node{"bar", "127.0.0.2"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(12, "foo", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(13, "bar", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(14, "foo", &structs.NodeService{"db", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(15, "bar", &structs.NodeService{"db", "db", []string{"slave"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(16, "bar", &structs.NodeService{"db2", "db", []string{"slave"}, 8001}); err != nil {
t.Fatalf("err: %v", err)
}
idx, nodes := store.ServiceNodes("db")
if idx != 16 {
t.Fatalf("bad: %v", 16)
}
if len(nodes) != 3 {
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 nodes[0].ServiceID != "db" {
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)
}
if nodes[1].Node != "bar" {
t.Fatalf("bad: %v", nodes)
}
if nodes[1].Address != "127.0.0.2" {
t.Fatalf("bad: %v", nodes)
}
if nodes[1].ServiceID != "db" {
t.Fatalf("bad: %v", nodes)
}
if !strContains(nodes[1].ServiceTags, "slave") {
t.Fatalf("bad: %v", nodes)
}
if nodes[1].ServicePort != 8000 {
t.Fatalf("bad: %v", nodes)
}
if nodes[2].Node != "bar" {
t.Fatalf("bad: %v", nodes)
}
if nodes[2].Address != "127.0.0.2" {
t.Fatalf("bad: %v", nodes)
}
if nodes[2].ServiceID != "db2" {
t.Fatalf("bad: %v", nodes)
}
if !strContains(nodes[2].ServiceTags, "slave") {
t.Fatalf("bad: %v", nodes)
}
if nodes[2].ServicePort != 8001 {
t.Fatalf("bad: %v", nodes)
}
}
func TestServiceTagNodes(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", err)
}
if err := store.EnsureNode(16, structs.Node{"bar", "127.0.0.2"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(17, "foo", &structs.NodeService{"db", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(18, "foo", &structs.NodeService{"db2", "db", []string{"slave"}, 8001}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(19, "bar", &structs.NodeService{"db", "db", []string{"slave"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
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)
}
}
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", err)
}
if err := store.EnsureNode(16, structs.Node{"bar", "127.0.0.2"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(17, "foo", &structs.NodeService{"db", "db", []string{"master", "v2"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(18, "foo", &structs.NodeService{"db2", "db", []string{"slave", "v2", "dev"}, 8001}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(19, "bar", &structs.NodeService{"db", "db", []string{"slave", "v2"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
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 {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(8, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureNode(9, structs.Node{"bar", "127.0.0.2"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(10, "foo", &structs.NodeService{"db", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(11, "foo", &structs.NodeService{"db2", "db", []string{"slave"}, 8001}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(12, "bar", &structs.NodeService{"db", "db", []string{"slave"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "db",
Name: "Can connect",
Status: structs.HealthPassing,
ServiceID: "db",
}
if err := store.EnsureCheck(13, check); err != nil {
t.Fatalf("err: %v", err)
}
// Add some KVS entries
d := &structs.DirEntry{Key: "/web/a", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(14, d); err != nil {
t.Fatalf("err: %v", err)
}
d = &structs.DirEntry{Key: "/web/b", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(15, d); err != nil {
t.Fatalf("err: %v", err)
}
// Add some sessions
session := &structs.Session{ID: generateUUID(), Node: "foo"}
if err := store.SessionCreate(16, session); err != nil {
t.Fatalf("err: %v", err)
}
session = &structs.Session{ID: generateUUID(), Node: "bar"}
if err := store.SessionCreate(17, session); err != nil {
t.Fatalf("err: %v", err)
}
d.Session = session.ID
if ok, err := store.KVSLock(18, d); err != nil || !ok {
t.Fatalf("err: %v", err)
}
a1 := &structs.ACL{
ID: generateUUID(),
Name: "User token",
Type: structs.ACLTypeClient,
}
if err := store.ACLSet(19, a1); err != nil {
t.Fatalf("err: %v", err)
}
a2 := &structs.ACL{
ID: generateUUID(),
Name: "User token",
Type: structs.ACLTypeClient,
}
if err := store.ACLSet(20, a2); err != nil {
t.Fatalf("err: %v", err)
}
// Take a snapshot
snap, err := store.Snapshot()
if err != nil {
t.Fatalf("err: %v", err)
}
defer snap.Close()
// Check the last nodes
if idx := snap.LastIndex(); idx != 20 {
t.Fatalf("bad: %v", idx)
}
// Check snapshot has old values
nodes := snap.Nodes()
if len(nodes) != 2 {
t.Fatalf("bad: %v", nodes)
}
// Ensure we get the service entries
services := snap.NodeServices("foo")
if !strContains(services.Services["db"].Tags, "master") {
t.Fatalf("bad: %v", services)
}
if !strContains(services.Services["db2"].Tags, "slave") {
t.Fatalf("bad: %v", services)
}
services = snap.NodeServices("bar")
if !strContains(services.Services["db"].Tags, "slave") {
t.Fatalf("bad: %v", services)
}
// Ensure we get the checks
checks := snap.NodeChecks("foo")
if len(checks) != 1 {
t.Fatalf("bad: %v", checks)
}
if !reflect.DeepEqual(checks[0], check) {
t.Fatalf("bad: %v", checks[0])
}
// Check we have the entries
streamCh := make(chan interface{}, 64)
doneCh := make(chan struct{})
var ents []*structs.DirEntry
go func() {
for {
obj := <-streamCh
if obj == nil {
close(doneCh)
return
}
ents = append(ents, obj.(*structs.DirEntry))
}
}()
if err := snap.KVSDump(streamCh); err != nil {
t.Fatalf("err: %v", err)
}
<-doneCh
if len(ents) != 2 {
t.Fatalf("missing KVS entries!")
}
// Check there are 2 sessions
sessions, err := snap.SessionList()
if err != nil {
t.Fatalf("err: %v", err)
}
if len(sessions) != 2 {
t.Fatalf("missing sessions")
}
// Check for an acl
acls, err := snap.ACLList()
if err != nil {
t.Fatalf("err: %v", err)
}
if len(acls) != 2 {
t.Fatalf("missing acls")
}
// Make some changes!
if err := store.EnsureService(21, "foo", &structs.NodeService{"db", "db", []string{"slave"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(22, "bar", &structs.NodeService{"db", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureNode(23, structs.Node{"baz", "127.0.0.3"}); err != nil {
t.Fatalf("err: %v", err)
}
checkAfter := &structs.HealthCheck{
Node: "foo",
CheckID: "db",
Name: "Can connect",
Status: structs.HealthCritical,
ServiceID: "db",
}
if err := store.EnsureCheck(24, checkAfter); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.KVSDelete(25, "/web/b"); err != nil {
t.Fatalf("err: %v", err)
}
// Nuke an ACL
if err := store.ACLDelete(26, a1.ID); err != nil {
t.Fatalf("err: %v", err)
}
// Check snapshot has old values
nodes = snap.Nodes()
if len(nodes) != 2 {
t.Fatalf("bad: %v", nodes)
}
// Ensure old service entries
services = snap.NodeServices("foo")
if !strContains(services.Services["db"].Tags, "master") {
t.Fatalf("bad: %v", services)
}
if !strContains(services.Services["db2"].Tags, "slave") {
t.Fatalf("bad: %v", services)
}
services = snap.NodeServices("bar")
if !strContains(services.Services["db"].Tags, "slave") {
t.Fatalf("bad: %v", services)
}
checks = snap.NodeChecks("foo")
if len(checks) != 1 {
t.Fatalf("bad: %v", checks)
}
if !reflect.DeepEqual(checks[0], check) {
t.Fatalf("bad: %v", checks[0])
}
// Check we have the entries
streamCh = make(chan interface{}, 64)
doneCh = make(chan struct{})
ents = nil
go func() {
for {
obj := <-streamCh
if obj == nil {
close(doneCh)
return
}
ents = append(ents, obj.(*structs.DirEntry))
}
}()
if err := snap.KVSDump(streamCh); err != nil {
t.Fatalf("err: %v", err)
}
<-doneCh
if len(ents) != 2 {
t.Fatalf("missing KVS entries!")
}
// Check there are 2 sessions
sessions, err = snap.SessionList()
if err != nil {
t.Fatalf("err: %v", err)
}
if len(sessions) != 2 {
t.Fatalf("missing sessions")
}
// Check for an acl
acls, err = snap.ACLList()
if err != nil {
t.Fatalf("err: %v", err)
}
if len(acls) != 2 {
t.Fatalf("missing acls")
}
}
func TestEnsureCheck(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
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", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "db",
Name: "Can connect",
Status: structs.HealthPassing,
ServiceID: "db1",
}
if err := store.EnsureCheck(3, check); err != nil {
t.Fatalf("err: %v", err)
}
check2 := &structs.HealthCheck{
Node: "foo",
CheckID: "memory",
Name: "memory utilization",
Status: structs.HealthWarning,
}
if err := store.EnsureCheck(4, check2); err != nil {
t.Fatalf("err: %v", err)
}
idx, checks := store.NodeChecks("foo")
if idx != 4 {
t.Fatalf("bad: %v", idx)
}
if len(checks) != 2 {
t.Fatalf("bad: %v", checks)
}
if !reflect.DeepEqual(checks[0], check) {
t.Fatalf("bad: %v", checks[0])
}
if !reflect.DeepEqual(checks[1], check2) {
t.Fatalf("bad: %v", checks[1])
}
idx, checks = store.ServiceChecks("db")
if idx != 4 {
t.Fatalf("bad: %v", idx)
}
if len(checks) != 1 {
t.Fatalf("bad: %v", checks)
}
if !reflect.DeepEqual(checks[0], check) {
t.Fatalf("bad: %v", checks[0])
}
idx, checks = store.ChecksInState(structs.HealthPassing)
if idx != 4 {
t.Fatalf("bad: %v", idx)
}
if len(checks) != 1 {
t.Fatalf("bad: %v", checks)
}
if !reflect.DeepEqual(checks[0], check) {
t.Fatalf("bad: %v", checks[0])
}
idx, checks = store.ChecksInState(structs.HealthWarning)
if idx != 4 {
t.Fatalf("bad: %v", idx)
}
if len(checks) != 1 {
t.Fatalf("bad: %v", checks)
}
if !reflect.DeepEqual(checks[0], check2) {
t.Fatalf("bad: %v", checks[0])
}
idx, checks = store.ChecksInState(structs.HealthAny)
if idx != 4 {
t.Fatalf("bad: %v", idx)
}
if len(checks) != 2 {
t.Fatalf("bad: %v", checks)
}
if !reflect.DeepEqual(checks[0], check) {
t.Fatalf("bad: %v", checks[0])
}
if !reflect.DeepEqual(checks[1], check2) {
t.Fatalf("bad: %v", checks[1])
}
}
func TestDeleteNodeCheck(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
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", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "db",
Name: "Can connect",
Status: structs.HealthPassing,
ServiceID: "db1",
}
if err := store.EnsureCheck(3, check); err != nil {
t.Fatalf("err: %v", err)
}
check2 := &structs.HealthCheck{
Node: "foo",
CheckID: "memory",
Name: "memory utilization",
Status: structs.HealthWarning,
}
if err := store.EnsureCheck(4, check2); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.DeleteNodeCheck(5, "foo", "db"); err != nil {
t.Fatalf("err: %v", err)
}
idx, checks := store.NodeChecks("foo")
if idx != 5 {
t.Fatalf("bad: %v", idx)
}
if len(checks) != 1 {
t.Fatalf("bad: %v", checks)
}
if !reflect.DeepEqual(checks[0], check2) {
t.Fatalf("bad: %v", checks[0])
}
}
func TestCheckServiceNodes(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
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", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "db",
Name: "Can connect",
Status: structs.HealthPassing,
ServiceID: "db1",
}
if err := store.EnsureCheck(3, check); err != nil {
t.Fatalf("err: %v", err)
}
check = &structs.HealthCheck{
Node: "foo",
CheckID: SerfCheckID,
Name: SerfCheckName,
Status: structs.HealthPassing,
}
if err := store.EnsureCheck(4, check); err != nil {
t.Fatalf("err: %v", err)
}
idx, nodes := store.CheckServiceNodes("db")
if idx != 4 {
t.Fatalf("bad: %v", idx)
}
if len(nodes) != 1 {
t.Fatalf("Bad: %v", nodes)
}
if nodes[0].Node.Node != "foo" {
t.Fatalf("Bad: %v", nodes[0])
}
if nodes[0].Service.ID != "db1" {
t.Fatalf("Bad: %v", nodes[0])
}
if len(nodes[0].Checks) != 2 {
t.Fatalf("Bad: %v", nodes[0])
}
if nodes[0].Checks[0].CheckID != "db" {
t.Fatalf("Bad: %v", nodes[0])
}
if nodes[0].Checks[1].CheckID != SerfCheckID {
t.Fatalf("Bad: %v", nodes[0])
}
idx, nodes = store.CheckServiceTagNodes("db", "master")
if idx != 4 {
t.Fatalf("bad: %v", idx)
}
if len(nodes) != 1 {
t.Fatalf("Bad: %v", nodes)
}
if nodes[0].Node.Node != "foo" {
t.Fatalf("Bad: %v", nodes[0])
}
if nodes[0].Service.ID != "db1" {
t.Fatalf("Bad: %v", nodes[0])
}
if len(nodes[0].Checks) != 2 {
t.Fatalf("Bad: %v", nodes[0])
}
if nodes[0].Checks[0].CheckID != "db" {
t.Fatalf("Bad: %v", nodes[0])
}
if nodes[0].Checks[1].CheckID != SerfCheckID {
t.Fatalf("Bad: %v", nodes[0])
}
}
func BenchmarkCheckServiceNodes(t *testing.B) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
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", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "db",
Name: "Can connect",
Status: structs.HealthPassing,
ServiceID: "db1",
}
if err := store.EnsureCheck(3, check); err != nil {
t.Fatalf("err: %v", err)
}
check = &structs.HealthCheck{
Node: "foo",
CheckID: SerfCheckID,
Name: SerfCheckName,
Status: structs.HealthPassing,
}
if err := store.EnsureCheck(4, check); err != nil {
t.Fatalf("err: %v", err)
}
for i := 0; i < t.N; i++ {
store.CheckServiceNodes("db")
}
}
func TestSS_Register_Deregister_Query(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(1, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
srv := &structs.NodeService{
"statsite-box-stats",
"statsite-box-stats",
nil,
0}
if err := store.EnsureService(2, "foo", srv); err != nil {
t.Fatalf("err: %v", err)
}
srv = &structs.NodeService{
"statsite-share-stats",
"statsite-share-stats",
nil,
0}
if err := store.EnsureService(3, "foo", srv); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.DeleteNode(4, "foo"); err != nil {
t.Fatalf("err: %v", err)
}
idx, nodes := store.CheckServiceNodes("statsite-share-stats")
if idx != 4 {
t.Fatalf("bad: %v", idx)
}
if len(nodes) != 0 {
t.Fatalf("Bad: %v", nodes)
}
}
func TestNodeInfo(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
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", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "db",
Name: "Can connect",
Status: structs.HealthPassing,
ServiceID: "db1",
}
if err := store.EnsureCheck(3, check); err != nil {
t.Fatalf("err: %v", err)
}
check = &structs.HealthCheck{
Node: "foo",
CheckID: SerfCheckID,
Name: SerfCheckName,
Status: structs.HealthPassing,
}
if err := store.EnsureCheck(4, check); err != nil {
t.Fatalf("err: %v", err)
}
idx, dump := store.NodeInfo("foo")
if idx != 4 {
t.Fatalf("bad: %v", idx)
}
if len(dump) != 1 {
t.Fatalf("Bad: %v", dump)
}
info := dump[0]
if info.Node != "foo" {
t.Fatalf("Bad: %v", info)
}
if info.Services[0].ID != "db1" {
t.Fatalf("Bad: %v", info)
}
if len(info.Checks) != 2 {
t.Fatalf("Bad: %v", info)
}
if info.Checks[0].CheckID != "db" {
t.Fatalf("Bad: %v", info)
}
if info.Checks[1].CheckID != SerfCheckID {
t.Fatalf("Bad: %v", info)
}
}
func TestNodeDump(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
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", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureNode(3, structs.Node{"baz", "127.0.0.2"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(4, "baz", &structs.NodeService{"db1", "db", []string{"master"}, 8000}); err != nil {
t.Fatalf("err: %v", err)
}
idx, dump := store.NodeDump()
if idx != 4 {
t.Fatalf("bad: %v", idx)
}
if len(dump) != 2 {
t.Fatalf("Bad: %v", dump)
}
info := dump[0]
if info.Node != "baz" {
t.Fatalf("Bad: %v", info)
}
if info.Services[0].ID != "db1" {
t.Fatalf("Bad: %v", info)
}
info = dump[1]
if info.Node != "foo" {
t.Fatalf("Bad: %v", info)
}
if info.Services[0].ID != "db1" {
t.Fatalf("Bad: %v", info)
}
}
func TestKVSSet_Get(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
// Should not exist
idx, d, err := store.KVSGet("/foo")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 0 {
t.Fatalf("bad: %v", idx)
}
if d != nil {
t.Fatalf("bad: %v", d)
}
// Create the entry
d = &structs.DirEntry{Key: "/foo", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1000, d); err != nil {
t.Fatalf("err: %v", err)
}
// Should exist exist
idx, d, err = store.KVSGet("/foo")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1000 {
t.Fatalf("bad: %v", idx)
}
if d.CreateIndex != 1000 {
t.Fatalf("bad: %v", d)
}
if d.ModifyIndex != 1000 {
t.Fatalf("bad: %v", d)
}
if d.Key != "/foo" {
t.Fatalf("bad: %v", d)
}
if d.Flags != 42 {
t.Fatalf("bad: %v", d)
}
if string(d.Value) != "test" {
t.Fatalf("bad: %v", d)
}
// Update the entry
d = &structs.DirEntry{Key: "/foo", Flags: 43, Value: []byte("zip")}
if err := store.KVSSet(1010, d); err != nil {
t.Fatalf("err: %v", err)
}
// Should update
idx, d, err = store.KVSGet("/foo")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1010 {
t.Fatalf("bad: %v", idx)
}
if d.CreateIndex != 1000 {
t.Fatalf("bad: %v", d)
}
if d.ModifyIndex != 1010 {
t.Fatalf("bad: %v", d)
}
if d.Key != "/foo" {
t.Fatalf("bad: %v", d)
}
if d.Flags != 43 {
t.Fatalf("bad: %v", d)
}
if string(d.Value) != "zip" {
t.Fatalf("bad: %v", d)
}
}
func TestKVSDelete(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
// Create the entry
d := &structs.DirEntry{Key: "/foo", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1000, d); err != nil {
t.Fatalf("err: %v", err)
}
// Delete the entry
if err := store.KVSDelete(1020, "/foo"); err != nil {
t.Fatalf("err: %v", err)
}
// Should not exist
idx, d, err := store.KVSGet("/foo")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1020 {
t.Fatalf("bad: %v", idx)
}
if d != nil {
t.Fatalf("bad: %v", d)
}
}
func TestKVSCheckAndSet(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
// CAS should fail, no entry
d := &structs.DirEntry{
ModifyIndex: 100,
Key: "/foo",
Flags: 42,
Value: []byte("test"),
}
ok, err := store.KVSCheckAndSet(1000, d)
if err != nil {
t.Fatalf("err: %v", err)
}
if ok {
t.Fatalf("unexpected commit")
}
// Constrain on not-exist, should work
d.ModifyIndex = 0
ok, err = store.KVSCheckAndSet(1001, d)
if err != nil {
t.Fatalf("err: %v", err)
}
if !ok {
t.Fatalf("expected commit")
}
// Constrain on not-exist, should fail
d.ModifyIndex = 0
ok, err = store.KVSCheckAndSet(1002, d)
if err != nil {
t.Fatalf("err: %v", err)
}
if ok {
t.Fatalf("unexpected commit")
}
// Constrain on a wrong modify time
d.ModifyIndex = 1000
ok, err = store.KVSCheckAndSet(1003, d)
if err != nil {
t.Fatalf("err: %v", err)
}
if ok {
t.Fatalf("unexpected commit")
}
// Constrain on a correct modify time
d.ModifyIndex = 1001
ok, err = store.KVSCheckAndSet(1004, d)
if err != nil {
t.Fatalf("err: %v", err)
}
if !ok {
t.Fatalf("expected commit")
}
}
func TestKVS_List(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
// Should not exist
idx, ents, err := store.KVSList("/web")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 0 {
t.Fatalf("bad: %v", idx)
}
if len(ents) != 0 {
t.Fatalf("bad: %v", ents)
}
// Create the entries
d := &structs.DirEntry{Key: "/web/a", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1000, d); err != nil {
t.Fatalf("err: %v", err)
}
d = &structs.DirEntry{Key: "/web/b", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1001, d); err != nil {
t.Fatalf("err: %v", err)
}
d = &structs.DirEntry{Key: "/web/sub/c", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1002, d); err != nil {
t.Fatalf("err: %v", err)
}
// Should list
idx, ents, err = store.KVSList("/web")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1002 {
t.Fatalf("bad: %v", idx)
}
if len(ents) != 3 {
t.Fatalf("bad: %v", ents)
}
if ents[0].Key != "/web/a" {
t.Fatalf("bad: %v", ents[0])
}
if ents[1].Key != "/web/b" {
t.Fatalf("bad: %v", ents[1])
}
if ents[2].Key != "/web/sub/c" {
t.Fatalf("bad: %v", ents[2])
}
}
func TestKVS_ListKeys(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
// Should not exist
idx, keys, err := store.KVSListKeys("", "/")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 0 {
t.Fatalf("bad: %v", keys)
}
// Create the entries
d := &structs.DirEntry{Key: "/web/a", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1000, d); err != nil {
t.Fatalf("err: %v", err)
}
d = &structs.DirEntry{Key: "/web/b", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1001, d); err != nil {
t.Fatalf("err: %v", err)
}
d = &structs.DirEntry{Key: "/web/sub/c", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1002, d); err != nil {
t.Fatalf("err: %v", err)
}
// Should list
idx, keys, err = store.KVSListKeys("", "/")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1002 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 1 {
t.Fatalf("bad: %v", keys)
}
if keys[0] != "/" {
t.Fatalf("bad: %v", keys)
}
// Should list just web
idx, keys, err = store.KVSListKeys("/", "/")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1002 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 1 {
t.Fatalf("bad: %v", keys)
}
if keys[0] != "/web/" {
t.Fatalf("bad: %v", keys)
}
// Should list a, b, sub/
idx, keys, err = store.KVSListKeys("/web/", "/")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1002 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 3 {
t.Fatalf("bad: %v", keys)
}
if keys[0] != "/web/a" {
t.Fatalf("bad: %v", keys)
}
if keys[1] != "/web/b" {
t.Fatalf("bad: %v", keys)
}
if keys[2] != "/web/sub/" {
t.Fatalf("bad: %v", keys)
}
// Should list c
idx, keys, err = store.KVSListKeys("/web/sub/", "/")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1002 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 1 {
t.Fatalf("bad: %v", keys)
}
if keys[0] != "/web/sub/c" {
t.Fatalf("bad: %v", keys)
}
// Should list all
idx, keys, err = store.KVSListKeys("/web/", "")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1002 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 3 {
t.Fatalf("bad: %v", keys)
}
if keys[2] != "/web/sub/c" {
t.Fatalf("bad: %v", keys)
}
}
func TestKVS_ListKeys_Index(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
// Create the entries
d := &structs.DirEntry{Key: "/foo/a", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1000, d); err != nil {
t.Fatalf("err: %v", err)
}
d = &structs.DirEntry{Key: "/bar/b", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1001, d); err != nil {
t.Fatalf("err: %v", err)
}
d = &structs.DirEntry{Key: "/baz/c", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1002, d); err != nil {
t.Fatalf("err: %v", err)
}
d = &structs.DirEntry{Key: "/other/d", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1003, d); err != nil {
t.Fatalf("err: %v", err)
}
idx, keys, err := store.KVSListKeys("/foo", "")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1000 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 1 {
t.Fatalf("bad: %v", keys)
}
idx, keys, err = store.KVSListKeys("/ba", "")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1002 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 2 {
t.Fatalf("bad: %v", keys)
}
idx, keys, err = store.KVSListKeys("/nope", "")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1003 {
t.Fatalf("bad: %v", idx)
}
if len(keys) != 0 {
t.Fatalf("bad: %v", keys)
}
}
func TestKVSDeleteTree(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
// Should not exist
err = store.KVSDeleteTree(1000, "/web")
if err != nil {
t.Fatalf("err: %v", err)
}
// Create the entries
d := &structs.DirEntry{Key: "/web/a", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1000, d); err != nil {
t.Fatalf("err: %v", err)
}
d = &structs.DirEntry{Key: "/web/b", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1001, d); err != nil {
t.Fatalf("err: %v", err)
}
d = &structs.DirEntry{Key: "/web/sub/c", Flags: 42, Value: []byte("test")}
if err := store.KVSSet(1002, d); err != nil {
t.Fatalf("err: %v", err)
}
// Nuke the web tree
err = store.KVSDeleteTree(1010, "/web")
if err != nil {
t.Fatalf("err: %v", err)
}
// Nothing should list
idx, ents, err := store.KVSList("/web")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1010 {
t.Fatalf("bad: %v", idx)
}
if len(ents) != 0 {
t.Fatalf("bad: %v", ents)
}
}
func TestSessionCreate(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(3, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "bar",
Status: structs.HealthPassing,
}
if err := store.EnsureCheck(13, check); err != nil {
t.Fatalf("err: %v", err)
}
session := &structs.Session{
ID: generateUUID(),
Node: "foo",
Checks: []string{"bar"},
}
if err := store.SessionCreate(1000, session); err != nil {
t.Fatalf("err: %v", err)
}
if session.CreateIndex != 1000 {
t.Fatalf("bad: %v", session)
}
}
func TestSessionCreate_Invalid(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
// No node registered
session := &structs.Session{
ID: generateUUID(),
Node: "foo",
Checks: []string{"bar"},
}
if err := store.SessionCreate(1000, session); err.Error() != "Missing node registration" {
t.Fatalf("err: %v", err)
}
// Check not registered
if err := store.EnsureNode(3, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.SessionCreate(1000, session); err.Error() != "Missing check 'bar' registration" {
t.Fatalf("err: %v", err)
}
// Unhealthy check
check := &structs.HealthCheck{
Node: "foo",
CheckID: "bar",
Status: structs.HealthCritical,
}
if err := store.EnsureCheck(13, check); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.SessionCreate(1000, session); err.Error() != "Check 'bar' is in critical state" {
t.Fatalf("err: %v", err)
}
}
func TestSession_Lookups(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
// Create a session
if err := store.EnsureNode(3, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
session := &structs.Session{
ID: generateUUID(),
Node: "foo",
Checks: []string{},
}
if err := store.SessionCreate(1000, session); err != nil {
t.Fatalf("err: %v", err)
}
// Lookup by ID
idx, s2, err := store.SessionGet(session.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1000 {
t.Fatalf("bad: %v", idx)
}
if !reflect.DeepEqual(s2, session) {
t.Fatalf("bad: %#v %#v", s2, session)
}
// Create many sessions
ids := []string{session.ID}
for i := 0; i < 10; i++ {
session := &structs.Session{
ID: generateUUID(),
Node: "foo",
}
if err := store.SessionCreate(uint64(1000+i), session); err != nil {
t.Fatalf("err: %v", err)
}
ids = append(ids, session.ID)
}
// List all
idx, all, err := store.SessionList()
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1009 {
t.Fatalf("bad: %v", idx)
}
// Retrieve the ids
var out []string
for _, s := range all {
out = append(out, s.ID)
}
sort.Strings(ids)
sort.Strings(out)
if !reflect.DeepEqual(ids, out) {
t.Fatalf("bad: %v %v", ids, out)
}
// List by node
idx, nodes, err := store.NodeSessions("foo")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 1009 {
t.Fatalf("bad: %v", idx)
}
// Check again for the node list
out = nil
for _, s := range nodes {
out = append(out, s.ID)
}
sort.Strings(out)
if !reflect.DeepEqual(ids, out) {
t.Fatalf("bad: %v %v", ids, out)
}
}
func TestSessionInvalidate_CriticalHealthCheck(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(3, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "bar",
Status: structs.HealthPassing,
}
if err := store.EnsureCheck(13, check); err != nil {
t.Fatalf("err: %v", err)
}
session := &structs.Session{
ID: generateUUID(),
Node: "foo",
Checks: []string{"bar"},
}
if err := store.SessionCreate(14, session); err != nil {
t.Fatalf("err: %v", err)
}
// Invalidate the check
check.Status = structs.HealthCritical
if err := store.EnsureCheck(15, check); err != nil {
t.Fatalf("err: %v", err)
}
// Lookup by ID, should be nil
_, s2, err := store.SessionGet(session.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if s2 != nil {
t.Fatalf("session should be invalidated")
}
}
func TestSessionInvalidate_DeleteHealthCheck(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(3, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "bar",
Status: structs.HealthPassing,
}
if err := store.EnsureCheck(13, check); err != nil {
t.Fatalf("err: %v", err)
}
session := &structs.Session{
ID: generateUUID(),
Node: "foo",
Checks: []string{"bar"},
}
if err := store.SessionCreate(14, session); err != nil {
t.Fatalf("err: %v", err)
}
// Delete the check
if err := store.DeleteNodeCheck(15, "foo", "bar"); err != nil {
t.Fatalf("err: %v", err)
}
// Lookup by ID, should be nil
_, s2, err := store.SessionGet(session.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if s2 != nil {
t.Fatalf("session should be invalidated")
}
}
func TestSessionInvalidate_DeleteNode(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(3, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
session := &structs.Session{
ID: generateUUID(),
Node: "foo",
}
if err := store.SessionCreate(14, session); err != nil {
t.Fatalf("err: %v", err)
}
// Delete the node
if err := store.DeleteNode(15, "foo"); err != nil {
t.Fatalf("err: %v", err)
}
// Lookup by ID, should be nil
_, s2, err := store.SessionGet(session.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if s2 != nil {
t.Fatalf("session should be invalidated")
}
}
func TestSessionInvalidate_DeleteNodeService(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(11, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.EnsureService(12, "foo", &structs.NodeService{"api", "api", nil, 5000}); err != nil {
t.Fatalf("err: %v", err)
}
check := &structs.HealthCheck{
Node: "foo",
CheckID: "api",
Name: "Can connect",
Status: structs.HealthPassing,
ServiceID: "api",
}
if err := store.EnsureCheck(13, check); err != nil {
t.Fatalf("err: %v", err)
}
session := &structs.Session{
ID: generateUUID(),
Node: "foo",
Checks: []string{"api"},
}
if err := store.SessionCreate(14, session); err != nil {
t.Fatalf("err: %v", err)
}
// Should invalidate the session
if err := store.DeleteNodeService(15, "foo", "api"); err != nil {
t.Fatalf("err: %v", err)
}
// Lookup by ID, should be nil
_, s2, err := store.SessionGet(session.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if s2 != nil {
t.Fatalf("session should be invalidated")
}
}
func TestKVSLock(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(3, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
session := &structs.Session{ID: generateUUID(), Node: "foo"}
if err := store.SessionCreate(4, session); err != nil {
t.Fatalf("err: %v", err)
}
// Lock with a non-existing keys should work
d := &structs.DirEntry{
Key: "/foo",
Flags: 42,
Value: []byte("test"),
Session: session.ID,
}
ok, err := store.KVSLock(5, d)
if err != nil {
t.Fatalf("err: %v", err)
}
if !ok {
t.Fatalf("unexpected fail")
}
if d.LockIndex != 1 {
t.Fatalf("bad: %v", d)
}
// Re-locking should fail
ok, err = store.KVSLock(6, d)
if err != nil {
t.Fatalf("err: %v", err)
}
if ok {
t.Fatalf("expected fail")
}
// Set a normal key
k1 := &structs.DirEntry{
Key: "/bar",
Flags: 0,
Value: []byte("asdf"),
}
if err := store.KVSSet(7, k1); err != nil {
t.Fatalf("err: %v", err)
}
// Should acquire the lock
k1.Session = session.ID
ok, err = store.KVSLock(8, k1)
if err != nil {
t.Fatalf("err: %v", err)
}
if !ok {
t.Fatalf("unexpected fail")
}
// Re-acquire should fail
ok, err = store.KVSLock(9, k1)
if err != nil {
t.Fatalf("err: %v", err)
}
if ok {
t.Fatalf("expected fail")
}
}
func TestKVSUnlock(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(3, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
session := &structs.Session{ID: generateUUID(), Node: "foo"}
if err := store.SessionCreate(4, session); err != nil {
t.Fatalf("err: %v", err)
}
// Unlock with a non-existing keys should fail
d := &structs.DirEntry{
Key: "/foo",
Flags: 42,
Value: []byte("test"),
Session: session.ID,
}
ok, err := store.KVSUnlock(5, d)
if err != nil {
t.Fatalf("err: %v", err)
}
if ok {
t.Fatalf("expected fail")
}
// Lock should work
d.Session = session.ID
if ok, _ := store.KVSLock(6, d); !ok {
t.Fatalf("expected lock")
}
// Unlock should work
d.Session = session.ID
ok, err = store.KVSUnlock(7, d)
if err != nil {
t.Fatalf("err: %v", err)
}
if !ok {
t.Fatalf("unexpected fail")
}
// Re-lock should work
d.Session = session.ID
if ok, err := store.KVSLock(8, d); err != nil {
t.Fatalf("err: %v", err)
} else if !ok {
t.Fatalf("expected lock")
}
if d.LockIndex != 2 {
t.Fatalf("bad: %v", d)
}
}
func TestSessionInvalidate_KeyUnlock(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(3, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
session := &structs.Session{
ID: generateUUID(),
Node: "foo",
LockDelay: 50 * time.Millisecond,
}
if err := store.SessionCreate(4, session); err != nil {
t.Fatalf("err: %v", err)
}
// Lock a key with the session
d := &structs.DirEntry{
Key: "/foo",
Flags: 42,
Value: []byte("test"),
Session: session.ID,
}
ok, err := store.KVSLock(5, d)
if err != nil {
t.Fatalf("err: %v", err)
}
if !ok {
t.Fatalf("unexpected fail")
}
// Delete the node
if err := store.DeleteNode(6, "foo"); err != nil {
t.Fatalf("err: %v", err)
}
// Key should be unlocked
idx, d2, err := store.KVSGet("/foo")
if idx != 6 {
t.Fatalf("bad: %v", idx)
}
if d2.LockIndex != 1 {
t.Fatalf("bad: %v", *d2)
}
if d2.Session != "" {
t.Fatalf("bad: %v", *d2)
}
// Key should have a lock delay
expires := store.KVSLockDelay("/foo")
if expires.Before(time.Now().Add(30 * time.Millisecond)) {
t.Fatalf("Bad: %v", expires)
}
}
func TestSessionInvalidate_KeyDelete(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
if err := store.EnsureNode(3, structs.Node{"foo", "127.0.0.1"}); err != nil {
t.Fatalf("err: %v", err)
}
session := &structs.Session{
ID: generateUUID(),
Node: "foo",
LockDelay: 50 * time.Millisecond,
Behavior: structs.SessionKeysDelete,
}
if err := store.SessionCreate(4, session); err != nil {
t.Fatalf("err: %v", err)
}
// Lock a key with the session
d := &structs.DirEntry{
Key: "/bar",
Flags: 42,
Value: []byte("test"),
Session: session.ID,
}
ok, err := store.KVSLock(5, d)
if err != nil {
t.Fatalf("err: %v", err)
}
if !ok {
t.Fatalf("unexpected fail")
}
// Delete the node
if err := store.DeleteNode(6, "foo"); err != nil {
t.Fatalf("err: %v", err)
}
// Key should be deleted
_, d2, err := store.KVSGet("/bar")
if d2 != nil {
t.Fatalf("unexpected undeleted key")
}
}
func TestACLSet_Get(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
idx, out, err := store.ACLGet("1234")
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 0 {
t.Fatalf("bad: %v", idx)
}
if out != nil {
t.Fatalf("bad: %v", out)
}
a := &structs.ACL{
ID: generateUUID(),
Name: "User token",
Type: structs.ACLTypeClient,
Rules: "",
}
if err := store.ACLSet(50, a); err != nil {
t.Fatalf("err: %v", err)
}
if a.CreateIndex != 50 {
t.Fatalf("Bad: %v", a)
}
if a.ModifyIndex != 50 {
t.Fatalf("Bad: %v", a)
}
if a.ID == "" {
t.Fatalf("Bad: %v", a)
}
idx, out, err = store.ACLGet(a.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 50 {
t.Fatalf("bad: %v", idx)
}
if !reflect.DeepEqual(out, a) {
t.Fatalf("bad: %v", out)
}
// Update
a.Rules = "foo bar baz"
if err := store.ACLSet(52, a); err != nil {
t.Fatalf("err: %v", err)
}
if a.CreateIndex != 50 {
t.Fatalf("Bad: %v", a)
}
if a.ModifyIndex != 52 {
t.Fatalf("Bad: %v", a)
}
idx, out, err = store.ACLGet(a.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 52 {
t.Fatalf("bad: %v", idx)
}
if !reflect.DeepEqual(out, a) {
t.Fatalf("bad: %v", out)
}
}
func TestACLDelete(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
a := &structs.ACL{
ID: generateUUID(),
Name: "User token",
Type: structs.ACLTypeClient,
Rules: "",
}
if err := store.ACLSet(50, a); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.ACLDelete(52, a.ID); err != nil {
t.Fatalf("err: %v", err)
}
if err := store.ACLDelete(53, a.ID); err != nil {
t.Fatalf("err: %v", err)
}
idx, out, err := store.ACLGet(a.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 52 {
t.Fatalf("bad: %v", idx)
}
if out != nil {
t.Fatalf("bad: %v", out)
}
}
func TestACLList(t *testing.T) {
store, err := testStateStore()
if err != nil {
t.Fatalf("err: %v", err)
}
defer store.Close()
a1 := &structs.ACL{
ID: generateUUID(),
Name: "User token",
Type: structs.ACLTypeClient,
}
if err := store.ACLSet(50, a1); err != nil {
t.Fatalf("err: %v", err)
}
a2 := &structs.ACL{
ID: generateUUID(),
Name: "User token",
Type: structs.ACLTypeClient,
}
if err := store.ACLSet(51, a2); err != nil {
t.Fatalf("err: %v", err)
}
idx, out, err := store.ACLList()
if err != nil {
t.Fatalf("err: %v", err)
}
if idx != 51 {
t.Fatalf("bad: %v", idx)
}
if len(out) != 2 {
t.Fatalf("bad: %v", out)
}
}