Ports over session invalidation tests (and fixes some bugs).
This commit is contained in:
parent
8072138c9a
commit
87ff8d031c
|
@ -461,7 +461,8 @@ func (s *StateStore) deleteNodeTxn(tx *memdb.Txn, idx uint64, nodeID string) err
|
|||
}
|
||||
}
|
||||
|
||||
// Delete all checks associated with the node and update the check index.
|
||||
// Delete all checks associated with the node. This will invalidate
|
||||
// sessions as necessary.
|
||||
checks, err := tx.Get("checks", "node", nodeID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed check lookup: %s", err)
|
||||
|
@ -634,7 +635,7 @@ func (s *StateStore) DeleteService(idx uint64, nodeID, serviceID string) error {
|
|||
// deleteServiceTxn is the inner method called to remove a service
|
||||
// registration within an existing transaction.
|
||||
func (s *StateStore) deleteServiceTxn(tx *memdb.Txn, idx uint64, watches *DumbWatchManager, nodeID, serviceID string) error {
|
||||
// Look up the service
|
||||
// Look up the service.
|
||||
service, err := tx.First("services", "id", nodeID, serviceID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed service lookup: %s", err)
|
||||
|
@ -643,16 +644,17 @@ func (s *StateStore) deleteServiceTxn(tx *memdb.Txn, idx uint64, watches *DumbWa
|
|||
return nil
|
||||
}
|
||||
|
||||
// Delete any checks associated with the service
|
||||
// Delete any checks associated with the service. This will invalidate
|
||||
// sessions as necessary.
|
||||
checks, err := tx.Get("checks", "node_service", nodeID, serviceID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed service check lookup: %s", err)
|
||||
}
|
||||
for check := checks.Next(); check != nil; check = checks.Next() {
|
||||
if err := tx.Delete("checks", check); err != nil {
|
||||
return fmt.Errorf("failed deleting service check: %s", err)
|
||||
hc := check.(*structs.HealthCheck)
|
||||
if err := s.deleteCheckTxn(tx, idx, watches, nodeID, hc.CheckID); err != nil {
|
||||
return err
|
||||
}
|
||||
watches.Arm("checks")
|
||||
}
|
||||
if err := tx.Insert("index", &IndexEntry{"checks", idx}); err != nil {
|
||||
return fmt.Errorf("failed updating index: %s", err)
|
||||
|
@ -1585,8 +1587,8 @@ func (s *StateStore) sessionCreateTxn(tx *memdb.Txn, idx uint64, sess *structs.S
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetSession is used to retrieve an active session from the state store.
|
||||
func (s *StateStore) GetSession(sessionID string) (*structs.Session, error) {
|
||||
// SessionGet is used to retrieve an active session from the state store.
|
||||
func (s *StateStore) SessionGet(sessionID string) (*structs.Session, error) {
|
||||
tx := s.db.Txn(false)
|
||||
defer tx.Abort()
|
||||
|
||||
|
|
|
@ -2782,11 +2782,11 @@ func TestStateStore_Tombstone_Snapshot_Restore(t *testing.T) {
|
|||
}()
|
||||
}
|
||||
|
||||
func TestStateStore_SessionCreate_GetSession(t *testing.T) {
|
||||
func TestStateStore_SessionCreate_SessionGet(t *testing.T) {
|
||||
s := testStateStore(t)
|
||||
|
||||
// GetSession returns nil if the session doesn't exist
|
||||
sess, err := s.GetSession("session1")
|
||||
// SessionGet returns nil if the session doesn't exist
|
||||
sess, err := s.SessionGet("session1")
|
||||
if sess != nil || err != nil {
|
||||
t.Fatalf("expected (nil, nil), got: (%#v, %#v)", sess, err)
|
||||
}
|
||||
|
@ -2832,7 +2832,7 @@ func TestStateStore_SessionCreate_GetSession(t *testing.T) {
|
|||
}
|
||||
|
||||
// Retrieve the session again
|
||||
session, err := s.GetSession("foo")
|
||||
session, err := s.SessionGet("foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
@ -3016,12 +3016,12 @@ func TestStateStore_SessionDestroy(t *testing.T) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Ensure the index was not updated if nothing was destroyed
|
||||
// Ensure the index was not updated if nothing was destroyed.
|
||||
if idx := s.maxIndex("sessions"); idx != 0 {
|
||||
t.Fatalf("bad index: %d", idx)
|
||||
}
|
||||
|
||||
// Register a node
|
||||
// Register a node.
|
||||
testRegisterNode(t, s, 1, "node1")
|
||||
|
||||
// Register a new session
|
||||
|
@ -3033,24 +3033,23 @@ func TestStateStore_SessionDestroy(t *testing.T) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Destroy the session
|
||||
// Destroy the session.
|
||||
if err := s.SessionDestroy(3, "session1"); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
tx := s.db.Txn(false)
|
||||
defer tx.Abort()
|
||||
|
||||
// Make sure the session is really gone
|
||||
sessions, err := tx.Get("sessions", "id")
|
||||
if err != nil || sessions.Next() != nil {
|
||||
t.Fatalf("session should not exist")
|
||||
}
|
||||
|
||||
// Check that the index was updated
|
||||
if idx := s.maxIndex("sessions"); idx != 3 {
|
||||
t.Fatalf("bad index: %d", idx)
|
||||
}
|
||||
|
||||
// Make sure the session is really gone.
|
||||
tx := s.db.Txn(false)
|
||||
sessions, err := tx.Get("sessions", "id")
|
||||
if err != nil || sessions.Next() != nil {
|
||||
t.Fatalf("session should not exist")
|
||||
}
|
||||
tx.Abort()
|
||||
}
|
||||
|
||||
func TestStateStore_Session_Snapshot_Restore(t *testing.T) {
|
||||
|
@ -3189,6 +3188,329 @@ func TestStateStore_Session_Watches(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestStateStore_Session_Invalidate_DeleteNode(t *testing.T) {
|
||||
s := testStateStore(t)
|
||||
|
||||
// Set up our test environment.
|
||||
if err := s.EnsureNode(3, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
session := &structs.Session{
|
||||
ID: "session1",
|
||||
Node: "foo",
|
||||
}
|
||||
if err := s.SessionCreate(14, session); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Delete the node and make sure the watches fire.
|
||||
verifyWatch(t, s.GetTableWatch("sessions"), func() {
|
||||
verifyWatch(t, s.GetTableWatch("nodes"), func() {
|
||||
if err := s.DeleteNode(15, "foo"); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Lookup by ID, should be nil.
|
||||
s2, err := s.SessionGet(session.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if s2 != nil {
|
||||
t.Fatalf("session should be invalidated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_Session_Invalidate_DeleteService(t *testing.T) {
|
||||
s := testStateStore(t)
|
||||
|
||||
// Set up our test environment.
|
||||
if err := s.EnsureNode(11, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if err := s.EnsureService(12, "foo", &structs.NodeService{ID: "api", Service: "api", Tags: nil, Address: "", Port: 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 := s.EnsureCheck(13, check); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
session := &structs.Session{
|
||||
ID: "session1",
|
||||
Node: "foo",
|
||||
Checks: []string{"api"},
|
||||
}
|
||||
if err := s.SessionCreate(14, session); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Delete the service and make sure the watches fire.
|
||||
verifyWatch(t, s.GetTableWatch("sessions"), func() {
|
||||
verifyWatch(t, s.GetTableWatch("services"), func() {
|
||||
verifyWatch(t, s.GetTableWatch("checks"), func() {
|
||||
if err := s.DeleteService(15, "foo", "api"); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Lookup by ID, should be nil.
|
||||
s2, err := s.SessionGet(session.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if s2 != nil {
|
||||
t.Fatalf("session should be invalidated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_Session_Invalidate_Critical_Check(t *testing.T) {
|
||||
s := testStateStore(t)
|
||||
|
||||
// Set up our test environment.
|
||||
if err := s.EnsureNode(3, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
check := &structs.HealthCheck{
|
||||
Node: "foo",
|
||||
CheckID: "bar",
|
||||
Status: structs.HealthPassing,
|
||||
}
|
||||
if err := s.EnsureCheck(13, check); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
session := &structs.Session{
|
||||
ID: "session1",
|
||||
Node: "foo",
|
||||
Checks: []string{"bar"},
|
||||
}
|
||||
if err := s.SessionCreate(14, session); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Invalidate the check and make sure the watches fire.
|
||||
verifyWatch(t, s.GetTableWatch("sessions"), func() {
|
||||
verifyWatch(t, s.GetTableWatch("checks"), func() {
|
||||
check.Status = structs.HealthCritical
|
||||
if err := s.EnsureCheck(15, check); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Lookup by ID, should be nil.
|
||||
s2, err := s.SessionGet(session.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if s2 != nil {
|
||||
t.Fatalf("session should be invalidated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_Session_Invalidate_DeleteCheck(t *testing.T) {
|
||||
s := testStateStore(t)
|
||||
|
||||
// Set up our test environment.
|
||||
if err := s.EnsureNode(3, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
check := &structs.HealthCheck{
|
||||
Node: "foo",
|
||||
CheckID: "bar",
|
||||
Status: structs.HealthPassing,
|
||||
}
|
||||
if err := s.EnsureCheck(13, check); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
session := &structs.Session{
|
||||
ID: "session1",
|
||||
Node: "foo",
|
||||
Checks: []string{"bar"},
|
||||
}
|
||||
if err := s.SessionCreate(14, session); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Delete the check and make sure the watches fire.
|
||||
verifyWatch(t, s.GetTableWatch("sessions"), func() {
|
||||
verifyWatch(t, s.GetTableWatch("checks"), func() {
|
||||
if err := s.DeleteCheck(15, "foo", "bar"); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Lookup by ID, should be nil.
|
||||
s2, err := s.SessionGet(session.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if s2 != nil {
|
||||
t.Fatalf("session should be invalidated")
|
||||
}
|
||||
|
||||
// Manually make sure the session checks mapping is clear.
|
||||
tx := s.db.Txn(false)
|
||||
mapping, err := tx.First("session_checks", "session", session.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if mapping != nil {
|
||||
t.Fatalf("unexpected session check")
|
||||
}
|
||||
tx.Abort()
|
||||
}
|
||||
|
||||
func TestStateStore_Session_Invalidate_Key_Unlock_Behavior(t *testing.T) {
|
||||
s := testStateStore(t)
|
||||
|
||||
// Set up our test environment.
|
||||
if err := s.EnsureNode(3, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
session := &structs.Session{
|
||||
ID: "session1",
|
||||
Node: "foo",
|
||||
LockDelay: 50 * time.Millisecond,
|
||||
}
|
||||
if err := s.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 := s.KVSLock(5, d)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("unexpected fail")
|
||||
}
|
||||
|
||||
// Delete the node and make sure the watches fire.
|
||||
verifyWatch(t, s.GetTableWatch("sessions"), func() {
|
||||
verifyWatch(t, s.GetTableWatch("nodes"), func() {
|
||||
verifyWatch(t, s.GetKVSWatch("/f"), func() {
|
||||
if err := s.DeleteNode(6, "foo"); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Lookup by ID, should be nil.
|
||||
s2, err := s.SessionGet(session.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if s2 != nil {
|
||||
t.Fatalf("session should be invalidated")
|
||||
}
|
||||
|
||||
// Key should be unlocked.
|
||||
d2, err := s.KVSGet("/foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if d2.ModifyIndex != 6 {
|
||||
t.Fatalf("bad index: %v", d2.ModifyIndex)
|
||||
}
|
||||
if d2.LockIndex != 1 {
|
||||
t.Fatalf("bad: %v", *d2)
|
||||
}
|
||||
if d2.Session != "" {
|
||||
t.Fatalf("bad: %v", *d2)
|
||||
}
|
||||
|
||||
// Key should have a lock delay.
|
||||
expires := s.KVSLockDelay("/foo")
|
||||
if expires.Before(time.Now().Add(30 * time.Millisecond)) {
|
||||
t.Fatalf("Bad: %v", expires)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_Session_Invalidate_Key_Delete_Behavior(t *testing.T) {
|
||||
s := testStateStore(t)
|
||||
|
||||
// Set up our test environment.
|
||||
if err := s.EnsureNode(3, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
session := &structs.Session{
|
||||
ID: "session1",
|
||||
Node: "foo",
|
||||
LockDelay: 50 * time.Millisecond,
|
||||
Behavior: structs.SessionKeysDelete,
|
||||
}
|
||||
if err := s.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 := s.KVSLock(5, d)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("unexpected fail")
|
||||
}
|
||||
|
||||
// Delete the node and make sure the watches fire.
|
||||
verifyWatch(t, s.GetTableWatch("sessions"), func() {
|
||||
verifyWatch(t, s.GetTableWatch("nodes"), func() {
|
||||
verifyWatch(t, s.GetKVSWatch("/b"), func() {
|
||||
if err := s.DeleteNode(6, "foo"); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Lookup by ID, should be nil.
|
||||
s2, err := s.SessionGet(session.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if s2 != nil {
|
||||
t.Fatalf("session should be invalidated")
|
||||
}
|
||||
|
||||
// Key should be deleted.
|
||||
d2, err := s.KVSGet("/bar")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if d2 != nil {
|
||||
t.Fatalf("unexpected undeleted key")
|
||||
}
|
||||
|
||||
// Key should have a lock delay.
|
||||
expires := s.KVSLockDelay("/bar")
|
||||
if expires.Before(time.Now().Add(30 * time.Millisecond)) {
|
||||
t.Fatalf("Bad: %v", expires)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_ACLSet_ACLGet(t *testing.T) {
|
||||
s := testStateStore(t)
|
||||
|
||||
|
|
Loading…
Reference in New Issue