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