github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/state/state_store_test.go (about) 1 package state 2 3 import ( 4 crand "crypto/rand" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/hashicorp/consul/agent/structs" 10 "github.com/hashicorp/consul/types" 11 "github.com/hashicorp/go-memdb" 12 ) 13 14 func testUUID() string { 15 buf := make([]byte, 16) 16 if _, err := crand.Read(buf); err != nil { 17 panic(fmt.Errorf("failed to read random bytes: %v", err)) 18 } 19 20 return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x", 21 buf[0:4], 22 buf[4:6], 23 buf[6:8], 24 buf[8:10], 25 buf[10:16]) 26 } 27 28 func testStateStore(t *testing.T) *Store { 29 s, err := NewStateStore(nil) 30 if err != nil { 31 t.Fatalf("err: %s", err) 32 } 33 if s == nil { 34 t.Fatalf("missing state store") 35 } 36 return s 37 } 38 39 func testRegisterNode(t *testing.T, s *Store, idx uint64, nodeID string) { 40 testRegisterNodeWithMeta(t, s, idx, nodeID, nil) 41 } 42 43 // testRegisterNodeWithChange registers a node and ensures it gets different from previous registration 44 func testRegisterNodeWithChange(t *testing.T, s *Store, idx uint64, nodeID string) { 45 testRegisterNodeWithMeta(t, s, idx, nodeID, map[string]string{ 46 "version": string(idx), 47 }) 48 } 49 50 func testRegisterNodeWithMeta(t *testing.T, s *Store, idx uint64, nodeID string, meta map[string]string) { 51 node := &structs.Node{Node: nodeID, Meta: meta} 52 if err := s.EnsureNode(idx, node); err != nil { 53 t.Fatalf("err: %s", err) 54 } 55 56 tx := s.db.Txn(false) 57 defer tx.Abort() 58 n, err := tx.First("nodes", "id", nodeID) 59 if err != nil { 60 t.Fatalf("err: %s", err) 61 } 62 if result, ok := n.(*structs.Node); !ok || result.Node != nodeID { 63 t.Fatalf("bad node: %#v", result) 64 } 65 } 66 67 // testRegisterServiceWithChange registers a service and allow ensuring the consul index is updated 68 // even if service already exists if using `modifyAccordingIndex`. 69 // This is done by setting the transaction ID in "version" meta so service will be updated if it already exists 70 func testRegisterServiceWithChange(t *testing.T, s *Store, idx uint64, nodeID, serviceID string, modifyAccordingIndex bool) { 71 meta := make(map[string]string) 72 if modifyAccordingIndex { 73 meta["version"] = string(idx) 74 } 75 svc := &structs.NodeService{ 76 ID: serviceID, 77 Service: serviceID, 78 Address: "1.1.1.1", 79 Port: 1111, 80 Meta: meta, 81 } 82 if err := s.EnsureService(idx, nodeID, svc); err != nil { 83 t.Fatalf("err: %s", err) 84 } 85 86 tx := s.db.Txn(false) 87 defer tx.Abort() 88 service, err := tx.First("services", "id", nodeID, serviceID) 89 if err != nil { 90 t.Fatalf("err: %s", err) 91 } 92 if result, ok := service.(*structs.ServiceNode); !ok || 93 result.Node != nodeID || 94 result.ServiceID != serviceID { 95 t.Fatalf("bad service: %#v", result) 96 } 97 } 98 99 // testRegisterService register a service with given transaction idx 100 // If the service already exists, transaction number might not be increased 101 // Use `testRegisterServiceWithChange()` if you want perform a registration that 102 // ensures the transaction is updated by setting idx in Meta of Service 103 func testRegisterService(t *testing.T, s *Store, idx uint64, nodeID, serviceID string) { 104 testRegisterServiceWithChange(t, s, idx, nodeID, serviceID, false) 105 } 106 107 func testRegisterCheck(t *testing.T, s *Store, idx uint64, 108 nodeID string, serviceID string, checkID types.CheckID, state string) { 109 chk := &structs.HealthCheck{ 110 Node: nodeID, 111 CheckID: checkID, 112 ServiceID: serviceID, 113 Status: state, 114 } 115 if err := s.EnsureCheck(idx, chk); err != nil { 116 t.Fatalf("err: %s", err) 117 } 118 119 tx := s.db.Txn(false) 120 defer tx.Abort() 121 c, err := tx.First("checks", "id", nodeID, string(checkID)) 122 if err != nil { 123 t.Fatalf("err: %s", err) 124 } 125 if result, ok := c.(*structs.HealthCheck); !ok || 126 result.Node != nodeID || 127 result.ServiceID != serviceID || 128 result.CheckID != checkID { 129 t.Fatalf("bad check: %#v", result) 130 } 131 } 132 133 func testSetKey(t *testing.T, s *Store, idx uint64, key, value string) { 134 entry := &structs.DirEntry{Key: key, Value: []byte(value)} 135 if err := s.KVSSet(idx, entry); err != nil { 136 t.Fatalf("err: %s", err) 137 } 138 139 tx := s.db.Txn(false) 140 defer tx.Abort() 141 e, err := tx.First("kvs", "id", key) 142 if err != nil { 143 t.Fatalf("err: %s", err) 144 } 145 if result, ok := e.(*structs.DirEntry); !ok || result.Key != key { 146 t.Fatalf("bad kvs entry: %#v", result) 147 } 148 } 149 150 // watchFired is a helper for unit tests that returns if the given watch set 151 // fired (it doesn't care which watch actually fired). This uses a fixed 152 // timeout since we already expect the event happened before calling this and 153 // just need to distinguish a fire from a timeout. We do need a little time to 154 // allow the watch to set up any goroutines, though. 155 func watchFired(ws memdb.WatchSet) bool { 156 timedOut := ws.Watch(time.After(50 * time.Millisecond)) 157 return !timedOut 158 } 159 160 func TestStateStore_Restore_Abort(t *testing.T) { 161 s := testStateStore(t) 162 163 // The detailed restore functions are tested below, this just checks 164 // that abort works. 165 restore := s.Restore() 166 entry := &structs.DirEntry{ 167 Key: "foo", 168 Value: []byte("bar"), 169 RaftIndex: structs.RaftIndex{ 170 ModifyIndex: 5, 171 }, 172 } 173 if err := restore.KVS(entry); err != nil { 174 t.Fatalf("err: %s", err) 175 } 176 restore.Abort() 177 178 idx, entries, err := s.KVSList(nil, "") 179 if err != nil { 180 t.Fatalf("err: %s", err) 181 } 182 if idx != 0 { 183 t.Fatalf("bad index: %d", idx) 184 } 185 if len(entries) != 0 { 186 t.Fatalf("bad: %#v", entries) 187 } 188 } 189 190 func TestStateStore_Abandon(t *testing.T) { 191 s := testStateStore(t) 192 abandonCh := s.AbandonCh() 193 s.Abandon() 194 select { 195 case <-abandonCh: 196 default: 197 t.Fatalf("bad") 198 } 199 } 200 201 func TestStateStore_maxIndex(t *testing.T) { 202 s := testStateStore(t) 203 204 testRegisterNode(t, s, 0, "foo") 205 testRegisterNode(t, s, 1, "bar") 206 testRegisterService(t, s, 2, "foo", "consul") 207 208 if max := s.maxIndex("nodes", "services"); max != 2 { 209 t.Fatalf("bad max: %d", max) 210 } 211 } 212 213 func TestStateStore_indexUpdateMaxTxn(t *testing.T) { 214 s := testStateStore(t) 215 216 testRegisterNode(t, s, 0, "foo") 217 testRegisterNode(t, s, 1, "bar") 218 219 tx := s.db.Txn(true) 220 if err := indexUpdateMaxTxn(tx, 3, "nodes"); err != nil { 221 t.Fatalf("err: %s", err) 222 } 223 tx.Commit() 224 225 if max := s.maxIndex("nodes"); max != 3 { 226 t.Fatalf("bad max: %d", max) 227 } 228 }