func TestFSM_KVSDeleteCheckAndSet(t *testing.T) { path, err := ioutil.TempDir("", "fsm") if err != nil { t.Fatalf("err: %v", err) } defer os.RemoveAll(path) fsm, err := NewFSM(nil, path, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() req := structs.KVSRequest{ Datacenter: "dc1", Op: structs.KVSSet, DirEnt: structs.DirEntry{ Key: "/test/path", Flags: 0, Value: []byte("test"), }, } buf, err := structs.Encode(structs.KVSRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify key is set _, d, err := fsm.state.KVSGet("/test/path") if err != nil { t.Fatalf("err: %v", err) } if d == nil { t.Fatalf("key missing") } // Run the check-and-set req.Op = structs.KVSDeleteCAS req.DirEnt.ModifyIndex = d.ModifyIndex buf, err = structs.Encode(structs.KVSRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp = fsm.Apply(makeLog(buf)) if resp.(bool) != true { t.Fatalf("resp: %v", resp) } // Verify key is gone _, d, err = fsm.state.KVSGet("/test/path") if err != nil { t.Fatalf("err: %v", err) } if d != nil { t.Fatalf("bad: %v", d) } }
func TestFSM_DeregisterCheck(t *testing.T) { fsm, err := NewFSM(nil, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } req := structs.RegisterRequest{ Datacenter: "dc1", Node: "foo", Address: "127.0.0.1", Check: &structs.HealthCheck{ Node: "foo", CheckID: "mem", Name: "memory util", Status: structs.HealthPassing, }, } buf, err := structs.Encode(structs.RegisterRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } dereg := structs.DeregisterRequest{ Datacenter: "dc1", Node: "foo", CheckID: "mem", } buf, err = structs.Encode(structs.DeregisterRequestType, dereg) if err != nil { t.Fatalf("err: %v", err) } resp = fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify we are registered _, node, err := fsm.state.GetNode("foo") if err != nil { t.Fatalf("err: %s", err) } if node == nil { t.Fatalf("not found!") } // Verify check not registered _, checks, err := fsm.state.NodeChecks("foo") if err != nil { t.Fatalf("err: %s", err) } if len(checks) != 0 { t.Fatalf("check registered!") } }
func TestFSM_DeregisterService(t *testing.T) { fsm, err := NewFSM(nil, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } req := structs.RegisterRequest{ Datacenter: "dc1", Node: "foo", Address: "127.0.0.1", Service: &structs.NodeService{ ID: "db", Service: "db", Tags: []string{"master"}, Port: 8000, }, } buf, err := structs.Encode(structs.RegisterRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } dereg := structs.DeregisterRequest{ Datacenter: "dc1", Node: "foo", ServiceID: "db", } buf, err = structs.Encode(structs.DeregisterRequestType, dereg) if err != nil { t.Fatalf("err: %v", err) } resp = fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify we are registered _, node, err := fsm.state.GetNode("foo") if err != nil { t.Fatalf("err: %s", err) } if node == nil { t.Fatalf("not found!") } // Verify service not registered _, services, err := fsm.state.NodeServices("foo") if err != nil { t.Fatalf("err: %s", err) } if _, ok := services.Services["db"]; ok { t.Fatalf("db registered!") } }
func TestFSM_DeregisterService(t *testing.T) { path, err := ioutil.TempDir("", "fsm") if err != nil { t.Fatalf("err: %v", err) } defer os.RemoveAll(path) fsm, err := NewFSM(nil, path, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() req := structs.RegisterRequest{ Datacenter: "dc1", Node: "foo", Address: "127.0.0.1", Service: &structs.NodeService{ ID: "db", Service: "db", Tags: []string{"master"}, Port: 8000, }, } buf, err := structs.Encode(structs.RegisterRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } dereg := structs.DeregisterRequest{ Datacenter: "dc1", Node: "foo", ServiceID: "db", } buf, err = structs.Encode(structs.DeregisterRequestType, dereg) if err != nil { t.Fatalf("err: %v", err) } resp = fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify we are registered if _, found, _ := fsm.state.GetNode("foo"); !found { t.Fatalf("not found!") } // Verify service not registered _, services := fsm.state.NodeServices("foo") if _, ok := services.Services["db"]; ok { t.Fatalf("db registered!") } }
func TestFSM_DeregisterCheck(t *testing.T) { path, err := ioutil.TempDir("", "fsm") if err != nil { t.Fatalf("err: %v", err) } defer os.RemoveAll(path) fsm, err := NewFSM(nil, path, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() req := structs.RegisterRequest{ Datacenter: "dc1", Node: "foo", Address: "127.0.0.1", Check: &structs.HealthCheck{ Node: "foo", CheckID: "mem", Name: "memory util", Status: structs.HealthPassing, }, } buf, err := structs.Encode(structs.RegisterRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } dereg := structs.DeregisterRequest{ Datacenter: "dc1", Node: "foo", CheckID: "mem", } buf, err = structs.Encode(structs.DeregisterRequestType, dereg) if err != nil { t.Fatalf("err: %v", err) } resp = fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify we are registered if _, found, _ := fsm.state.GetNode("foo"); !found { t.Fatalf("not found!") } // Verify check not registered _, checks := fsm.state.NodeChecks("foo") if len(checks) != 0 { t.Fatalf("check registered!") } }
func TestFSM_KVSCheckAndSet(t *testing.T) { fsm, err := NewFSM(os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() req := structs.KVSRequest{ Datacenter: "dc1", Op: structs.KVSSet, DirEnt: structs.DirEntry{ Key: "/test/path", Flags: 0, Value: []byte("test"), }, } buf, err := structs.Encode(structs.KVSRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify key is set _, d, err := fsm.state.KVSGet("/test/path") if err != nil { t.Fatalf("err: %v", err) } if d == nil { t.Fatalf("key missing") } // Run the check-and-set req.Op = structs.KVSCAS req.DirEnt.ModifyIndex = d.ModifyIndex req.DirEnt.Value = []byte("zip") buf, err = structs.Encode(structs.KVSRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp = fsm.Apply(makeLog(buf)) if resp.(bool) != true { t.Fatalf("resp: %v", resp) } // Verify key is updated _, d, err = fsm.state.KVSGet("/test/path") if err != nil { t.Fatalf("err: %v", err) } if string(d.Value) != "zip" { t.Fatalf("bad: %v", d) } }
func TestFSM_KVSDeleteTree(t *testing.T) { path, err := ioutil.TempDir("", "fsm") if err != nil { t.Fatalf("err: %v", err) } defer os.RemoveAll(path) fsm, err := NewFSM(nil, path, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() req := structs.KVSRequest{ Datacenter: "dc1", Op: structs.KVSSet, DirEnt: structs.DirEntry{ Key: "/test/path", Flags: 0, Value: []byte("test"), }, } buf, err := structs.Encode(structs.KVSRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Run the delete tree req.Op = structs.KVSDeleteTree req.DirEnt.Key = "/test" buf, err = structs.Encode(structs.KVSRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp = fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify key is not set _, d, err := fsm.state.KVSGet("/test/path") if err != nil { t.Fatalf("err: %v", err) } if d != nil { t.Fatalf("key present") } }
func TestFSM_IgnoreUnknown(t *testing.T) { path, err := ioutil.TempDir("", "fsm") if err != nil { t.Fatalf("err: %v", err) } defer os.RemoveAll(path) fsm, err := NewFSM(nil, path, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() // Create a new reap request type UnknownRequest struct { Foo string } req := UnknownRequest{Foo: "bar"} msgType := structs.IgnoreUnknownTypeFlag | 64 buf, err := structs.Encode(msgType, req) if err != nil { t.Fatalf("err: %v", err) } // Apply should work, even though not supported resp := fsm.Apply(makeLog(buf)) if err, ok := resp.(error); ok { t.Fatalf("resp: %v", err) } }
func TestFSM_RegisterNode(t *testing.T) { fsm, err := NewFSM(os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() req := structs.RegisterRequest{ Datacenter: "dc1", Node: "foo", Address: "127.0.0.1", } buf, err := structs.Encode(structs.RegisterRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify we are registered if idx, found, _ := fsm.state.GetNode("foo"); !found { t.Fatalf("not found!") } else if idx != 1 { t.Fatalf("bad index: %d", idx) } // Verify service registered _, services := fsm.state.NodeServices("foo") if len(services.Services) != 0 { t.Fatalf("Services: %v", services) } }
func TestFSM_KVSSet(t *testing.T) { fsm, err := NewFSM(os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() req := structs.KVSRequest{ Datacenter: "dc1", Op: structs.KVSSet, DirEnt: structs.DirEntry{ Key: "/test/path", Flags: 0, Value: []byte("test"), }, } buf, err := structs.Encode(structs.KVSRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify key is set _, d, err := fsm.state.KVSGet("/test/path") if err != nil { t.Fatalf("err: %v", err) } if d == nil { t.Fatalf("missing") } }
func TestFSM_RegisterNode_Service(t *testing.T) { path, err := ioutil.TempDir("", "fsm") if err != nil { t.Fatalf("err: %v", err) } defer os.RemoveAll(path) fsm, err := NewFSM(nil, path, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() req := structs.RegisterRequest{ Datacenter: "dc1", Node: "foo", Address: "127.0.0.1", Service: &structs.NodeService{ ID: "db", Service: "db", Tags: []string{"master"}, Port: 8000, }, Check: &structs.HealthCheck{ Node: "foo", CheckID: "db", Name: "db connectivity", Status: structs.HealthPassing, ServiceID: "db", }, } buf, err := structs.Encode(structs.RegisterRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify we are registered if _, found, _ := fsm.state.GetNode("foo"); !found { t.Fatalf("not found!") } // Verify service registered _, services := fsm.state.NodeServices("foo") if _, ok := services.Services["db"]; !ok { t.Fatalf("not registered!") } // Verify check _, checks := fsm.state.NodeChecks("foo") if checks[0].CheckID != "db" { t.Fatalf("not registered!") } }
func TestFSM_KVSDelete(t *testing.T) { fsm, err := NewFSM(nil, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } req := structs.KVSRequest{ Datacenter: "dc1", Op: structs.KVSSet, DirEnt: structs.DirEntry{ Key: "/test/path", Flags: 0, Value: []byte("test"), }, } buf, err := structs.Encode(structs.KVSRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Run the delete req.Op = structs.KVSDelete buf, err = structs.Encode(structs.KVSRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp = fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify key is not set _, d, err := fsm.state.KVSGet("/test/path") if err != nil { t.Fatalf("err: %v", err) } if d != nil { t.Fatalf("key present") } }
// raftApply is used to encode a message, run it through raft, and return // the FSM response along with any errors func (s *Server) raftApply(t structs.MessageType, msg interface{}) (interface{}, error) { buf, err := structs.Encode(t, msg) if err != nil { return nil, fmt.Errorf("Failed to encode request: %v", err) } future := s.raft.Apply(buf, 0) if err := future.Error(); err != nil { return nil, err } return future.Response(), nil }
func TestFSM_KVSLock(t *testing.T) { path, err := ioutil.TempDir("", "fsm") if err != nil { t.Fatalf("err: %v", err) } defer os.RemoveAll(path) fsm, err := NewFSM(nil, path, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() fsm.state.EnsureNode(1, structs.Node{"foo", "127.0.0.1"}) session := &structs.Session{ID: generateUUID(), Node: "foo"} fsm.state.SessionCreate(2, session) req := structs.KVSRequest{ Datacenter: "dc1", Op: structs.KVSLock, DirEnt: structs.DirEntry{ Key: "/test/path", Value: []byte("test"), Session: session.ID, }, } buf, err := structs.Encode(structs.KVSRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != true { t.Fatalf("resp: %v", resp) } // Verify key is locked _, d, err := fsm.state.KVSGet("/test/path") if err != nil { t.Fatalf("err: %v", err) } if d == nil { t.Fatalf("missing") } if d.LockIndex != 1 { t.Fatalf("bad: %v", *d) } if d.Session != session.ID { t.Fatalf("bad: %v", *d) } }
func TestFSM_TombstoneReap(t *testing.T) { fsm, err := NewFSM(nil, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } // Create some tombstones fsm.state.KVSSet(11, &structs.DirEntry{ Key: "/remove", Value: []byte("foo"), }) fsm.state.KVSDelete(12, "/remove") idx, _, err := fsm.state.KVSList("/remove") if err != nil { t.Fatalf("err: %s", err) } if idx != 12 { t.Fatalf("bad index: %d", idx) } // Create a new reap request req := structs.TombstoneRequest{ Datacenter: "dc1", Op: structs.TombstoneReap, ReapIndex: 12, } buf, err := structs.Encode(structs.TombstoneRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if err, ok := resp.(error); ok { t.Fatalf("resp: %v", err) } // Verify the tombstones are gone snap := fsm.state.Snapshot() defer snap.Close() stones, err := snap.Tombstones() if err != nil { t.Fatalf("err: %s", err) } if stones.Next() != nil { t.Fatalf("unexpected extra tombstones") } }
// raftApply is used to encode a message, run it through raft, and return // the FSM response along with any errors func (s *Server) raftApply(t structs.MessageType, msg interface{}) (interface{}, error) { buf, err := structs.Encode(t, msg) if err != nil { return nil, fmt.Errorf("Failed to encode request: %v", err) } // Warn if the command is very large if n := len(buf); n > raftWarnSize { s.logger.Printf("[WARN] consul: Attempting to apply large raft entry (%d bytes)", n) } future := s.raft.Apply(buf, 0) if err := future.Error(); err != nil { return nil, err } return future.Response(), nil }
func TestFSM_TombstoneReap(t *testing.T) { path, err := ioutil.TempDir("", "fsm") if err != nil { t.Fatalf("err: %v", err) } defer os.RemoveAll(path) fsm, err := NewFSM(nil, path, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() // Create some tombstones fsm.state.KVSSet(11, &structs.DirEntry{ Key: "/remove", Value: []byte("foo"), }) fsm.state.KVSDelete(12, "/remove") // Create a new reap request req := structs.TombstoneRequest{ Datacenter: "dc1", Op: structs.TombstoneReap, ReapIndex: 12, } buf, err := structs.Encode(structs.TombstoneRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if err, ok := resp.(error); ok { t.Fatalf("resp: %v", err) } // Verify the tombstones are gone _, res, err := fsm.state.tombstoneTable.Get("id") if err != nil { t.Fatalf("err: %v", err) } if len(res) != 0 { t.Fatalf("bad: %v", res) } }
func TestFSM_RegisterNode(t *testing.T) { fsm, err := NewFSM(nil, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } req := structs.RegisterRequest{ Datacenter: "dc1", Node: "foo", Address: "127.0.0.1", } buf, err := structs.Encode(structs.RegisterRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify we are registered _, node, err := fsm.state.GetNode("foo") if err != nil { t.Fatalf("err: %s", err) } if node == nil { t.Fatalf("not found!") } if node.ModifyIndex != 1 { t.Fatalf("bad index: %d", node.ModifyIndex) } // Verify service registered _, services, err := fsm.state.NodeServices("foo") if err != nil { t.Fatalf("err: %s", err) } if len(services.Services) != 0 { t.Fatalf("Services: %v", services) } }
func TestFSM_Txn(t *testing.T) { fsm, err := NewFSM(nil, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } // Set a key using a transaction. req := structs.TxnRequest{ Datacenter: "dc1", Ops: structs.TxnOps{ &structs.TxnOp{ KV: &structs.TxnKVOp{ Verb: structs.KVSSet, DirEnt: structs.DirEntry{ Key: "/test/path", Flags: 0, Value: []byte("test"), }, }, }, }, } buf, err := structs.Encode(structs.TxnRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if _, ok := resp.(structs.TxnResponse); !ok { t.Fatalf("bad response type: %T", resp) } // Verify key is set directly in the state store. _, d, err := fsm.state.KVSGet("/test/path") if err != nil { t.Fatalf("err: %v", err) } if d == nil { t.Fatalf("missing") } }
func TestFSM_CoordinateUpdate(t *testing.T) { fsm, err := NewFSM(nil, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } // Register some nodes. fsm.state.EnsureNode(1, &structs.Node{Node: "node1", Address: "127.0.0.1"}) fsm.state.EnsureNode(2, &structs.Node{Node: "node2", Address: "127.0.0.1"}) // Write a batch of two coordinates. updates := structs.Coordinates{ &structs.Coordinate{ Node: "node1", Coord: generateRandomCoordinate(), }, &structs.Coordinate{ Node: "node2", Coord: generateRandomCoordinate(), }, } buf, err := structs.Encode(structs.CoordinateBatchUpdateType, updates) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Read back the two coordinates to make sure they got updated. _, coords, err := fsm.state.Coordinates() if err != nil { t.Fatalf("err: %s", err) } if !reflect.DeepEqual(coords, updates) { t.Fatalf("bad: %#v", coords) } }
func TestFSM_ACL_Set_Delete(t *testing.T) { fsm, err := NewFSM(os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() // Create a new ACL req := structs.ACLRequest{ Datacenter: "dc1", Op: structs.ACLSet, ACL: structs.ACL{ Name: "User token", Type: structs.ACLTypeClient, }, } buf, err := structs.Encode(structs.ACLRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if err, ok := resp.(error); ok { t.Fatalf("resp: %v", err) } // Get the ACL id := resp.(string) _, acl, err := fsm.state.ACLGet(id) if err != nil { t.Fatalf("err: %v", err) } if acl == nil { t.Fatalf("missing") } // Verify the ACL if acl.ID != id { t.Fatalf("bad: %v", *acl) } if acl.Name != "User token" { t.Fatalf("bad: %v", *acl) } if acl.Type != structs.ACLTypeClient { t.Fatalf("bad: %v", *acl) } // Try to destroy destroy := structs.ACLRequest{ Datacenter: "dc1", Op: structs.ACLDelete, ACL: structs.ACL{ ID: id, }, } buf, err = structs.Encode(structs.ACLRequestType, destroy) if err != nil { t.Fatalf("err: %v", err) } resp = fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } _, acl, err = fsm.state.ACLGet(id) if err != nil { t.Fatalf("err: %v", err) } if acl != nil { t.Fatalf("should be destroyed") } }
func TestFSM_SessionCreate_Destroy(t *testing.T) { fsm, err := NewFSM(os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() fsm.state.EnsureNode(1, structs.Node{"foo", "127.0.0.1"}) fsm.state.EnsureCheck(2, &structs.HealthCheck{ Node: "foo", CheckID: "web", Status: structs.HealthPassing, }) // Create a new session req := structs.SessionRequest{ Datacenter: "dc1", Op: structs.SessionCreate, Session: structs.Session{ Node: "foo", Checks: []string{"web"}, }, } buf, err := structs.Encode(structs.SessionRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if err, ok := resp.(error); ok { t.Fatalf("resp: %v", err) } // Get the session id := resp.(string) _, session, err := fsm.state.SessionGet(id) if err != nil { t.Fatalf("err: %v", err) } if session == nil { t.Fatalf("missing") } // Verify the session if session.ID != id { t.Fatalf("bad: %v", *session) } if session.Node != "foo" { t.Fatalf("bad: %v", *session) } if session.Checks[0] != "web" { t.Fatalf("bad: %v", *session) } // Try to destroy destroy := structs.SessionRequest{ Datacenter: "dc1", Op: structs.SessionDestroy, Session: structs.Session{ ID: id, }, } buf, err = structs.Encode(structs.SessionRequestType, destroy) if err != nil { t.Fatalf("err: %v", err) } resp = fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } _, session, err = fsm.state.SessionGet(id) if err != nil { t.Fatalf("err: %v", err) } if session != nil { t.Fatalf("should be destroyed") } }
func TestFSM_PreparedQuery_CRUD(t *testing.T) { fsm, err := NewFSM(nil, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } // Register a service to query on. fsm.state.EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}) fsm.state.EnsureService(2, "foo", &structs.NodeService{ID: "web", Service: "web", Tags: nil, Address: "127.0.0.1", Port: 80}) // Create a new query. query := structs.PreparedQueryRequest{ Op: structs.PreparedQueryCreate, Query: &structs.PreparedQuery{ ID: generateUUID(), Service: structs.ServiceQuery{ Service: "web", }, }, } { buf, err := structs.Encode(structs.PreparedQueryRequestType, query) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } } // Verify it's in the state store. { _, actual, err := fsm.state.PreparedQueryGet(query.Query.ID) if err != nil { t.Fatalf("err: %s", err) } actual.CreateIndex, actual.ModifyIndex = 0, 0 if !reflect.DeepEqual(actual, query.Query) { t.Fatalf("bad: %v", actual) } } // Make an update to the query. query.Op = structs.PreparedQueryUpdate query.Query.Name = "my-query" { buf, err := structs.Encode(structs.PreparedQueryRequestType, query) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } } // Verify the update. { _, actual, err := fsm.state.PreparedQueryGet(query.Query.ID) if err != nil { t.Fatalf("err: %s", err) } actual.CreateIndex, actual.ModifyIndex = 0, 0 if !reflect.DeepEqual(actual, query.Query) { t.Fatalf("bad: %v", actual) } } // Delete the query. query.Op = structs.PreparedQueryDelete { buf, err := structs.Encode(structs.PreparedQueryRequestType, query) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } } // Make sure it's gone. { _, actual, err := fsm.state.PreparedQueryGet(query.Query.ID) if err != nil { t.Fatalf("err: %s", err) } if actual != nil { t.Fatalf("bad: %v", actual) } } }
func TestFSM_DeregisterNode(t *testing.T) { fsm, err := NewFSM(os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() req := structs.RegisterRequest{ Datacenter: "dc1", Node: "foo", Address: "127.0.0.1", Service: &structs.NodeService{ ID: "db", Service: "db", Tags: []string{"master"}, Port: 8000, }, Check: &structs.HealthCheck{ Node: "foo", CheckID: "db", Name: "db connectivity", Status: structs.HealthPassing, ServiceID: "db", }, } buf, err := structs.Encode(structs.RegisterRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } dereg := structs.DeregisterRequest{ Datacenter: "dc1", Node: "foo", } buf, err = structs.Encode(structs.DeregisterRequestType, dereg) if err != nil { t.Fatalf("err: %v", err) } resp = fsm.Apply(makeLog(buf)) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify we are registered if _, found, _ := fsm.state.GetNode("foo"); found { t.Fatalf("found!") } // Verify service not registered _, services := fsm.state.NodeServices("foo") if services != nil { t.Fatalf("Services: %v", services) } // Verify checks not registered _, checks := fsm.state.NodeChecks("foo") if len(checks) != 0 { t.Fatalf("Services: %v", services) } }
// Testing for GH-300 and GH-279 func TestHealthCheckRace(t *testing.T) { fsm, err := NewFSM(os.Stderr) if err != nil { t.Fatalf("err: %v", err) } defer fsm.Close() state := fsm.State() req := structs.RegisterRequest{ Datacenter: "dc1", Node: "foo", Address: "127.0.0.1", Service: &structs.NodeService{ ID: "db", Service: "db", }, Check: &structs.HealthCheck{ Node: "foo", CheckID: "db", Name: "db connectivity", Status: structs.HealthPassing, ServiceID: "db", }, } buf, err := structs.Encode(structs.RegisterRequestType, req) if err != nil { t.Fatalf("err: %v", err) } log := makeLog(buf) log.Index = 10 resp := fsm.Apply(log) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify the index idx, out1 := state.CheckServiceNodes("db") if idx != 10 { t.Fatalf("Bad index") } // Update the check state req.Check.Status = structs.HealthCritical buf, err = structs.Encode(structs.RegisterRequestType, req) if err != nil { t.Fatalf("err: %v", err) } log = makeLog(buf) log.Index = 20 resp = fsm.Apply(log) if resp != nil { t.Fatalf("resp: %v", resp) } // Verify the index changed idx, out2 := state.CheckServiceNodes("db") if idx != 20 { t.Fatalf("Bad index") } if reflect.DeepEqual(out1, out2) { t.Fatalf("match: %#v %#v", *out1[0].Checks[0], *out2[0].Checks[0]) } }
// initialize is used to setup the store for use func (s *StateStore) initialize() error { // Setup the Env first if err := s.env.SetMaxDBs(mdb.DBI(32)); err != nil { return err } // Set the maximum db size based on 32/64bit. Since we are // doing an mmap underneath, we need to limit our use of virtual // address space on 32bit, but don't have to care on 64bit. dbSize := dbMaxMapSize32bit if runtime.GOARCH == "amd64" { dbSize = dbMaxMapSize64bit } // Increase the maximum map size if err := s.env.SetMapSize(dbSize); err != nil { return err } // Optimize our flags for speed over safety, since the Raft log + snapshots // are durable. We treat this as an ephemeral in-memory DB, since we nuke // the data anyways. var flags uint = mdb.NOMETASYNC | mdb.NOSYNC | mdb.NOTLS if err := s.env.Open(s.path, flags, 0755); err != nil { return err } // Tables use a generic struct encoder encoder := func(obj interface{}) []byte { buf, err := structs.Encode(255, obj) if err != nil { panic(err) } return buf[1:] } // Setup our tables s.nodeTable = &MDBTable{ Name: dbNodes, Indexes: map[string]*MDBIndex{ "id": &MDBIndex{ Unique: true, Fields: []string{"Node"}, }, }, Decoder: func(buf []byte) interface{} { out := new(structs.Node) if err := structs.Decode(buf, out); err != nil { panic(err) } return out }, } s.serviceTable = &MDBTable{ Name: dbServices, Indexes: map[string]*MDBIndex{ "id": &MDBIndex{ Unique: true, Fields: []string{"Node", "ServiceID"}, }, "service": &MDBIndex{ AllowBlank: true, Fields: []string{"ServiceName"}, }, }, Decoder: func(buf []byte) interface{} { out := new(structs.ServiceNode) if err := structs.Decode(buf, out); err != nil { panic(err) } return out }, } s.checkTable = &MDBTable{ Name: dbChecks, Indexes: map[string]*MDBIndex{ "id": &MDBIndex{ Unique: true, Fields: []string{"Node", "CheckID"}, }, "status": &MDBIndex{ Fields: []string{"Status"}, }, "service": &MDBIndex{ AllowBlank: true, Fields: []string{"ServiceName"}, }, "node": &MDBIndex{ AllowBlank: true, Fields: []string{"Node", "ServiceID"}, }, }, Decoder: func(buf []byte) interface{} { out := new(structs.HealthCheck) if err := structs.Decode(buf, out); err != nil { panic(err) } return out }, } s.kvsTable = &MDBTable{ Name: dbKVS, Indexes: map[string]*MDBIndex{ "id": &MDBIndex{ Unique: true, Fields: []string{"Key"}, }, "id_prefix": &MDBIndex{ Virtual: true, RealIndex: "id", Fields: []string{"Key"}, IdxFunc: DefaultIndexPrefixFunc, }, }, Decoder: func(buf []byte) interface{} { out := new(structs.DirEntry) if err := structs.Decode(buf, out); err != nil { panic(err) } return out }, } // Store the set of tables s.tables = []*MDBTable{s.nodeTable, s.serviceTable, s.checkTable, s.kvsTable} for _, table := range s.tables { table.Env = s.env table.Encoder = encoder if err := table.Init(); err != nil { return err } // Setup a notification group per table s.watch[table] = &NotifyGroup{} } // Setup the query tables s.queryTables = map[string]MDBTables{ "Nodes": MDBTables{s.nodeTable}, "Services": MDBTables{s.serviceTable}, "ServiceNodes": MDBTables{s.nodeTable, s.serviceTable}, "NodeServices": MDBTables{s.nodeTable, s.serviceTable}, "ChecksInState": MDBTables{s.checkTable}, "NodeChecks": MDBTables{s.checkTable}, "ServiceChecks": MDBTables{s.checkTable}, "CheckServiceNodes": MDBTables{s.nodeTable, s.serviceTable, s.checkTable}, "NodeInfo": MDBTables{s.nodeTable, s.serviceTable, s.checkTable}, "NodeDump": MDBTables{s.nodeTable, s.serviceTable, s.checkTable}, "KVSGet": MDBTables{s.kvsTable}, "KVSList": MDBTables{s.kvsTable}, "KVSListKeys": MDBTables{s.kvsTable}, } return nil }
func TestFSM_KVSUnlock(t *testing.T) { fsm, err := NewFSM(nil, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } fsm.state.EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}) session := &structs.Session{ID: generateUUID(), Node: "foo"} fsm.state.SessionCreate(2, session) req := structs.KVSRequest{ Datacenter: "dc1", Op: structs.KVSLock, DirEnt: structs.DirEntry{ Key: "/test/path", Value: []byte("test"), Session: session.ID, }, } buf, err := structs.Encode(structs.KVSRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp := fsm.Apply(makeLog(buf)) if resp != true { t.Fatalf("resp: %v", resp) } req = structs.KVSRequest{ Datacenter: "dc1", Op: structs.KVSUnlock, DirEnt: structs.DirEntry{ Key: "/test/path", Value: []byte("test"), Session: session.ID, }, } buf, err = structs.Encode(structs.KVSRequestType, req) if err != nil { t.Fatalf("err: %v", err) } resp = fsm.Apply(makeLog(buf)) if resp != true { t.Fatalf("resp: %v", resp) } // Verify key is unlocked _, d, err := fsm.state.KVSGet("/test/path") if err != nil { t.Fatalf("err: %v", err) } if d == nil { t.Fatalf("missing") } if d.LockIndex != 1 { t.Fatalf("bad: %v", *d) } if d.Session != "" { t.Fatalf("bad: %v", *d) } }