store_test.go 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118
  1. // Copyright 2015 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package store
  15. import (
  16. "testing"
  17. "time"
  18. etcdErr "github.com/coreos/etcd/error"
  19. "github.com/jonboulle/clockwork"
  20. "github.com/stretchr/testify/assert"
  21. )
  22. func TestNewStoreWithNamespaces(t *testing.T) {
  23. s := newStore("/0", "/1")
  24. _, err := s.Get("/0", false, false)
  25. assert.Nil(t, err, "")
  26. _, err = s.Get("/1", false, false)
  27. assert.Nil(t, err, "")
  28. }
  29. // Ensure that the store can retrieve an existing value.
  30. func TestStoreGetValue(t *testing.T) {
  31. s := newStore()
  32. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  33. var eidx uint64 = 1
  34. e, err := s.Get("/foo", false, false)
  35. assert.Nil(t, err, "")
  36. assert.Equal(t, e.EtcdIndex, eidx, "")
  37. assert.Equal(t, e.Action, "get", "")
  38. assert.Equal(t, e.Node.Key, "/foo", "")
  39. assert.Equal(t, *e.Node.Value, "bar", "")
  40. }
  41. // Ensure that any TTL <= minExpireTime becomes Permanent
  42. func TestMinExpireTime(t *testing.T) {
  43. s := newStore()
  44. fc := clockwork.NewFakeClock()
  45. s.clock = fc
  46. // FakeClock starts at 0, so minExpireTime should be far in the future.. but just in case
  47. assert.True(t, minExpireTime.After(fc.Now()), "minExpireTime should be ahead of FakeClock!")
  48. s.Create("/foo", false, "Y", false, TTLOptionSet{ExpireTime: fc.Now().Add(3 * time.Second)})
  49. fc.Advance(5 * time.Second)
  50. // Ensure it hasn't expired
  51. s.DeleteExpiredKeys(fc.Now())
  52. var eidx uint64 = 1
  53. e, err := s.Get("/foo", true, false)
  54. assert.Nil(t, err, "")
  55. assert.Equal(t, e.EtcdIndex, eidx, "")
  56. assert.Equal(t, e.Action, "get", "")
  57. assert.Equal(t, e.Node.Key, "/foo", "")
  58. assert.Equal(t, e.Node.TTL, int64(0))
  59. }
  60. // Ensure that the store can recursively retrieve a directory listing.
  61. // Note that hidden files should not be returned.
  62. func TestStoreGetDirectory(t *testing.T) {
  63. s := newStore()
  64. fc := newFakeClock()
  65. s.clock = fc
  66. s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  67. s.Create("/foo/bar", false, "X", false, TTLOptionSet{ExpireTime: Permanent})
  68. s.Create("/foo/_hidden", false, "*", false, TTLOptionSet{ExpireTime: Permanent})
  69. s.Create("/foo/baz", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  70. s.Create("/foo/baz/bat", false, "Y", false, TTLOptionSet{ExpireTime: Permanent})
  71. s.Create("/foo/baz/_hidden", false, "*", false, TTLOptionSet{ExpireTime: Permanent})
  72. s.Create("/foo/baz/ttl", false, "Y", false, TTLOptionSet{ExpireTime: fc.Now().Add(time.Second * 3)})
  73. var eidx uint64 = 7
  74. e, err := s.Get("/foo", true, false)
  75. assert.Nil(t, err, "")
  76. assert.Equal(t, e.EtcdIndex, eidx, "")
  77. assert.Equal(t, e.Action, "get", "")
  78. assert.Equal(t, e.Node.Key, "/foo", "")
  79. assert.Equal(t, len(e.Node.Nodes), 2, "")
  80. var bazNodes NodeExterns
  81. for _, node := range e.Node.Nodes {
  82. switch node.Key {
  83. case "/foo/bar":
  84. assert.Equal(t, *node.Value, "X", "")
  85. assert.Equal(t, node.Dir, false, "")
  86. case "/foo/baz":
  87. assert.Equal(t, node.Dir, true, "")
  88. assert.Equal(t, len(node.Nodes), 2, "")
  89. bazNodes = node.Nodes
  90. default:
  91. t.Errorf("key = %s, not matched", node.Key)
  92. }
  93. }
  94. for _, node := range bazNodes {
  95. switch node.Key {
  96. case "/foo/baz/bat":
  97. assert.Equal(t, *node.Value, "Y", "")
  98. assert.Equal(t, node.Dir, false, "")
  99. case "/foo/baz/ttl":
  100. assert.Equal(t, *node.Value, "Y", "")
  101. assert.Equal(t, node.Dir, false, "")
  102. assert.Equal(t, node.TTL, int64(3), "")
  103. default:
  104. t.Errorf("key = %s, not matched", node.Key)
  105. }
  106. }
  107. }
  108. // Ensure that the store can retrieve a directory in sorted order.
  109. func TestStoreGetSorted(t *testing.T) {
  110. s := newStore()
  111. s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  112. s.Create("/foo/x", false, "0", false, TTLOptionSet{ExpireTime: Permanent})
  113. s.Create("/foo/z", false, "0", false, TTLOptionSet{ExpireTime: Permanent})
  114. s.Create("/foo/y", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  115. s.Create("/foo/y/a", false, "0", false, TTLOptionSet{ExpireTime: Permanent})
  116. s.Create("/foo/y/b", false, "0", false, TTLOptionSet{ExpireTime: Permanent})
  117. var eidx uint64 = 6
  118. e, err := s.Get("/foo", true, true)
  119. assert.Nil(t, err, "")
  120. assert.Equal(t, e.EtcdIndex, eidx, "")
  121. var yNodes NodeExterns
  122. sortedStrings := []string{"/foo/x", "/foo/y", "/foo/z"}
  123. for i := range e.Node.Nodes {
  124. node := e.Node.Nodes[i]
  125. if node.Key != sortedStrings[i] {
  126. t.Errorf("expect key = %s, got key = %s", sortedStrings[i], node.Key)
  127. }
  128. if node.Key == "/foo/y" {
  129. yNodes = node.Nodes
  130. }
  131. }
  132. sortedStrings = []string{"/foo/y/a", "/foo/y/b"}
  133. for i := range yNodes {
  134. node := yNodes[i]
  135. if node.Key != sortedStrings[i] {
  136. t.Errorf("expect key = %s, got key = %s", sortedStrings[i], node.Key)
  137. }
  138. }
  139. }
  140. func TestSet(t *testing.T) {
  141. s := newStore()
  142. // Set /foo=""
  143. var eidx uint64 = 1
  144. e, err := s.Set("/foo", false, "", TTLOptionSet{ExpireTime: Permanent})
  145. assert.Nil(t, err, "")
  146. assert.Equal(t, e.EtcdIndex, eidx, "")
  147. assert.Equal(t, e.Action, "set", "")
  148. assert.Equal(t, e.Node.Key, "/foo", "")
  149. assert.False(t, e.Node.Dir, "")
  150. assert.Equal(t, *e.Node.Value, "", "")
  151. assert.Nil(t, e.Node.Nodes, "")
  152. assert.Nil(t, e.Node.Expiration, "")
  153. assert.Equal(t, e.Node.TTL, int64(0), "")
  154. assert.Equal(t, e.Node.ModifiedIndex, uint64(1), "")
  155. // Set /foo="bar"
  156. eidx = 2
  157. e, err = s.Set("/foo", false, "bar", TTLOptionSet{ExpireTime: Permanent})
  158. assert.Nil(t, err, "")
  159. assert.Equal(t, e.EtcdIndex, eidx, "")
  160. assert.Equal(t, e.Action, "set", "")
  161. assert.Equal(t, e.Node.Key, "/foo", "")
  162. assert.False(t, e.Node.Dir, "")
  163. assert.Equal(t, *e.Node.Value, "bar", "")
  164. assert.Nil(t, e.Node.Nodes, "")
  165. assert.Nil(t, e.Node.Expiration, "")
  166. assert.Equal(t, e.Node.TTL, int64(0), "")
  167. assert.Equal(t, e.Node.ModifiedIndex, uint64(2), "")
  168. // check prevNode
  169. assert.NotNil(t, e.PrevNode, "")
  170. assert.Equal(t, e.PrevNode.Key, "/foo", "")
  171. assert.Equal(t, *e.PrevNode.Value, "", "")
  172. assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "")
  173. // Set /foo="baz" (for testing prevNode)
  174. eidx = 3
  175. e, err = s.Set("/foo", false, "baz", TTLOptionSet{ExpireTime: Permanent})
  176. assert.Nil(t, err, "")
  177. assert.Equal(t, e.EtcdIndex, eidx, "")
  178. assert.Equal(t, e.Action, "set", "")
  179. assert.Equal(t, e.Node.Key, "/foo", "")
  180. assert.False(t, e.Node.Dir, "")
  181. assert.Equal(t, *e.Node.Value, "baz", "")
  182. assert.Nil(t, e.Node.Nodes, "")
  183. assert.Nil(t, e.Node.Expiration, "")
  184. assert.Equal(t, e.Node.TTL, int64(0), "")
  185. assert.Equal(t, e.Node.ModifiedIndex, uint64(3), "")
  186. // check prevNode
  187. assert.NotNil(t, e.PrevNode, "")
  188. assert.Equal(t, e.PrevNode.Key, "/foo", "")
  189. assert.Equal(t, *e.PrevNode.Value, "bar", "")
  190. assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(2), "")
  191. // Set /dir as a directory
  192. eidx = 4
  193. e, err = s.Set("/dir", true, "", TTLOptionSet{ExpireTime: Permanent})
  194. assert.Nil(t, err, "")
  195. assert.Equal(t, e.EtcdIndex, eidx, "")
  196. assert.Equal(t, e.Action, "set", "")
  197. assert.Equal(t, e.Node.Key, "/dir", "")
  198. assert.True(t, e.Node.Dir, "")
  199. assert.Nil(t, e.Node.Value)
  200. assert.Nil(t, e.Node.Nodes, "")
  201. assert.Nil(t, e.Node.Expiration, "")
  202. assert.Equal(t, e.Node.TTL, int64(0), "")
  203. assert.Equal(t, e.Node.ModifiedIndex, uint64(4), "")
  204. }
  205. // Ensure that the store can create a new key if it doesn't already exist.
  206. func TestStoreCreateValue(t *testing.T) {
  207. s := newStore()
  208. // Create /foo=bar
  209. var eidx uint64 = 1
  210. e, err := s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  211. assert.Nil(t, err, "")
  212. assert.Equal(t, e.EtcdIndex, eidx, "")
  213. assert.Equal(t, e.Action, "create", "")
  214. assert.Equal(t, e.Node.Key, "/foo", "")
  215. assert.False(t, e.Node.Dir, "")
  216. assert.Equal(t, *e.Node.Value, "bar", "")
  217. assert.Nil(t, e.Node.Nodes, "")
  218. assert.Nil(t, e.Node.Expiration, "")
  219. assert.Equal(t, e.Node.TTL, int64(0), "")
  220. assert.Equal(t, e.Node.ModifiedIndex, uint64(1), "")
  221. // Create /empty=""
  222. eidx = 2
  223. e, err = s.Create("/empty", false, "", false, TTLOptionSet{ExpireTime: Permanent})
  224. assert.Nil(t, err, "")
  225. assert.Equal(t, e.EtcdIndex, eidx, "")
  226. assert.Equal(t, e.Action, "create", "")
  227. assert.Equal(t, e.Node.Key, "/empty", "")
  228. assert.False(t, e.Node.Dir, "")
  229. assert.Equal(t, *e.Node.Value, "", "")
  230. assert.Nil(t, e.Node.Nodes, "")
  231. assert.Nil(t, e.Node.Expiration, "")
  232. assert.Equal(t, e.Node.TTL, int64(0), "")
  233. assert.Equal(t, e.Node.ModifiedIndex, uint64(2), "")
  234. }
  235. // Ensure that the store can create a new directory if it doesn't already exist.
  236. func TestStoreCreateDirectory(t *testing.T) {
  237. s := newStore()
  238. var eidx uint64 = 1
  239. e, err := s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  240. assert.Nil(t, err, "")
  241. assert.Equal(t, e.EtcdIndex, eidx, "")
  242. assert.Equal(t, e.Action, "create", "")
  243. assert.Equal(t, e.Node.Key, "/foo", "")
  244. assert.True(t, e.Node.Dir, "")
  245. }
  246. // Ensure that the store fails to create a key if it already exists.
  247. func TestStoreCreateFailsIfExists(t *testing.T) {
  248. s := newStore()
  249. // create /foo as dir
  250. s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  251. // create /foo as dir again
  252. e, _err := s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  253. err := _err.(*etcdErr.Error)
  254. assert.Equal(t, err.ErrorCode, etcdErr.EcodeNodeExist, "")
  255. assert.Equal(t, err.Message, "Key already exists", "")
  256. assert.Equal(t, err.Cause, "/foo", "")
  257. assert.Equal(t, err.Index, uint64(1), "")
  258. assert.Nil(t, e, 0, "")
  259. }
  260. // Ensure that the store can update a key if it already exists.
  261. func TestStoreUpdateValue(t *testing.T) {
  262. s := newStore()
  263. // create /foo=bar
  264. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  265. // update /foo="bzr"
  266. var eidx uint64 = 2
  267. e, err := s.Update("/foo", "baz", TTLOptionSet{ExpireTime: Permanent})
  268. assert.Nil(t, err, "")
  269. assert.Equal(t, e.EtcdIndex, eidx, "")
  270. assert.Equal(t, e.Action, "update", "")
  271. assert.Equal(t, e.Node.Key, "/foo", "")
  272. assert.False(t, e.Node.Dir, "")
  273. assert.Equal(t, *e.Node.Value, "baz", "")
  274. assert.Equal(t, e.Node.TTL, int64(0), "")
  275. assert.Equal(t, e.Node.ModifiedIndex, uint64(2), "")
  276. // check prevNode
  277. assert.Equal(t, e.PrevNode.Key, "/foo", "")
  278. assert.Equal(t, *e.PrevNode.Value, "bar", "")
  279. assert.Equal(t, e.PrevNode.TTL, int64(0), "")
  280. assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "")
  281. e, _ = s.Get("/foo", false, false)
  282. assert.Equal(t, *e.Node.Value, "baz", "")
  283. assert.Equal(t, e.EtcdIndex, eidx, "")
  284. // update /foo=""
  285. eidx = 3
  286. e, err = s.Update("/foo", "", TTLOptionSet{ExpireTime: Permanent})
  287. assert.Nil(t, err, "")
  288. assert.Equal(t, e.EtcdIndex, eidx, "")
  289. assert.Equal(t, e.Action, "update", "")
  290. assert.Equal(t, e.Node.Key, "/foo", "")
  291. assert.False(t, e.Node.Dir, "")
  292. assert.Equal(t, *e.Node.Value, "", "")
  293. assert.Equal(t, e.Node.TTL, int64(0), "")
  294. assert.Equal(t, e.Node.ModifiedIndex, uint64(3), "")
  295. // check prevNode
  296. assert.Equal(t, e.PrevNode.Key, "/foo", "")
  297. assert.Equal(t, *e.PrevNode.Value, "baz", "")
  298. assert.Equal(t, e.PrevNode.TTL, int64(0), "")
  299. assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(2), "")
  300. e, _ = s.Get("/foo", false, false)
  301. assert.Equal(t, e.EtcdIndex, eidx, "")
  302. assert.Equal(t, *e.Node.Value, "", "")
  303. }
  304. // Ensure that the store cannot update a directory.
  305. func TestStoreUpdateFailsIfDirectory(t *testing.T) {
  306. s := newStore()
  307. s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  308. e, _err := s.Update("/foo", "baz", TTLOptionSet{ExpireTime: Permanent})
  309. err := _err.(*etcdErr.Error)
  310. assert.Equal(t, err.ErrorCode, etcdErr.EcodeNotFile, "")
  311. assert.Equal(t, err.Message, "Not a file", "")
  312. assert.Equal(t, err.Cause, "/foo", "")
  313. assert.Nil(t, e, "")
  314. }
  315. // Ensure that the store can update the TTL on a value.
  316. func TestStoreUpdateValueTTL(t *testing.T) {
  317. s := newStore()
  318. fc := newFakeClock()
  319. s.clock = fc
  320. var eidx uint64 = 2
  321. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  322. _, err := s.Update("/foo", "baz", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
  323. e, _ := s.Get("/foo", false, false)
  324. assert.Equal(t, *e.Node.Value, "baz", "")
  325. assert.Equal(t, e.EtcdIndex, eidx, "")
  326. fc.Advance(600 * time.Millisecond)
  327. s.DeleteExpiredKeys(fc.Now())
  328. e, err = s.Get("/foo", false, false)
  329. assert.Nil(t, e, "")
  330. assert.Equal(t, err.(*etcdErr.Error).ErrorCode, etcdErr.EcodeKeyNotFound, "")
  331. }
  332. // Ensure that the store can update the TTL on a directory.
  333. func TestStoreUpdateDirTTL(t *testing.T) {
  334. s := newStore()
  335. fc := newFakeClock()
  336. s.clock = fc
  337. var eidx uint64 = 3
  338. s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  339. s.Create("/foo/bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
  340. e, err := s.Update("/foo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
  341. assert.Equal(t, e.Node.Dir, true, "")
  342. assert.Equal(t, e.EtcdIndex, eidx, "")
  343. e, _ = s.Get("/foo/bar", false, false)
  344. assert.Equal(t, *e.Node.Value, "baz", "")
  345. assert.Equal(t, e.EtcdIndex, eidx, "")
  346. fc.Advance(600 * time.Millisecond)
  347. s.DeleteExpiredKeys(fc.Now())
  348. e, err = s.Get("/foo/bar", false, false)
  349. assert.Nil(t, e, "")
  350. assert.Equal(t, err.(*etcdErr.Error).ErrorCode, etcdErr.EcodeKeyNotFound, "")
  351. }
  352. // Ensure that the store can delete a value.
  353. func TestStoreDeleteValue(t *testing.T) {
  354. s := newStore()
  355. var eidx uint64 = 2
  356. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  357. e, err := s.Delete("/foo", false, false)
  358. assert.Nil(t, err, "")
  359. assert.Equal(t, e.EtcdIndex, eidx, "")
  360. assert.Equal(t, e.Action, "delete", "")
  361. // check prevNode
  362. assert.NotNil(t, e.PrevNode, "")
  363. assert.Equal(t, e.PrevNode.Key, "/foo", "")
  364. assert.Equal(t, *e.PrevNode.Value, "bar", "")
  365. }
  366. // Ensure that the store can delete a directory if recursive is specified.
  367. func TestStoreDeleteDiretory(t *testing.T) {
  368. s := newStore()
  369. // create directory /foo
  370. var eidx uint64 = 2
  371. s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  372. // delete /foo with dir = true and recursive = false
  373. // this should succeed, since the directory is empty
  374. e, err := s.Delete("/foo", true, false)
  375. assert.Nil(t, err, "")
  376. assert.Equal(t, e.EtcdIndex, eidx, "")
  377. assert.Equal(t, e.Action, "delete", "")
  378. // check prevNode
  379. assert.NotNil(t, e.PrevNode, "")
  380. assert.Equal(t, e.PrevNode.Key, "/foo", "")
  381. assert.Equal(t, e.PrevNode.Dir, true, "")
  382. // create directory /foo and directory /foo/bar
  383. s.Create("/foo/bar", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  384. // delete /foo with dir = true and recursive = false
  385. // this should fail, since the directory is not empty
  386. _, err = s.Delete("/foo", true, false)
  387. assert.NotNil(t, err, "")
  388. // delete /foo with dir=false and recursive = true
  389. // this should succeed, since recursive implies dir=true
  390. // and recursively delete should be able to delete all
  391. // items under the given directory
  392. e, err = s.Delete("/foo", false, true)
  393. assert.Nil(t, err, "")
  394. assert.Equal(t, e.Action, "delete", "")
  395. }
  396. // Ensure that the store cannot delete a directory if both of recursive
  397. // and dir are not specified.
  398. func TestStoreDeleteDiretoryFailsIfNonRecursiveAndDir(t *testing.T) {
  399. s := newStore()
  400. s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  401. e, _err := s.Delete("/foo", false, false)
  402. err := _err.(*etcdErr.Error)
  403. assert.Equal(t, err.ErrorCode, etcdErr.EcodeNotFile, "")
  404. assert.Equal(t, err.Message, "Not a file", "")
  405. assert.Nil(t, e, "")
  406. }
  407. func TestRootRdOnly(t *testing.T) {
  408. s := newStore("/0")
  409. for _, tt := range []string{"/", "/0"} {
  410. _, err := s.Set(tt, true, "", TTLOptionSet{ExpireTime: Permanent})
  411. assert.NotNil(t, err, "")
  412. _, err = s.Delete(tt, true, true)
  413. assert.NotNil(t, err, "")
  414. _, err = s.Create(tt, true, "", false, TTLOptionSet{ExpireTime: Permanent})
  415. assert.NotNil(t, err, "")
  416. _, err = s.Update(tt, "", TTLOptionSet{ExpireTime: Permanent})
  417. assert.NotNil(t, err, "")
  418. _, err = s.CompareAndSwap(tt, "", 0, "", TTLOptionSet{ExpireTime: Permanent})
  419. assert.NotNil(t, err, "")
  420. }
  421. }
  422. func TestStoreCompareAndDeletePrevValue(t *testing.T) {
  423. s := newStore()
  424. var eidx uint64 = 2
  425. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  426. e, err := s.CompareAndDelete("/foo", "bar", 0)
  427. assert.Nil(t, err, "")
  428. assert.Equal(t, e.EtcdIndex, eidx, "")
  429. assert.Equal(t, e.Action, "compareAndDelete", "")
  430. assert.Equal(t, e.Node.Key, "/foo", "")
  431. // check prevNode
  432. assert.NotNil(t, e.PrevNode, "")
  433. assert.Equal(t, e.PrevNode.Key, "/foo", "")
  434. assert.Equal(t, *e.PrevNode.Value, "bar", "")
  435. assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "")
  436. assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "")
  437. }
  438. func TestStoreCompareAndDeletePrevValueFailsIfNotMatch(t *testing.T) {
  439. s := newStore()
  440. var eidx uint64 = 1
  441. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  442. e, _err := s.CompareAndDelete("/foo", "baz", 0)
  443. err := _err.(*etcdErr.Error)
  444. assert.Equal(t, err.ErrorCode, etcdErr.EcodeTestFailed, "")
  445. assert.Equal(t, err.Message, "Compare failed", "")
  446. assert.Nil(t, e, "")
  447. e, _ = s.Get("/foo", false, false)
  448. assert.Equal(t, e.EtcdIndex, eidx, "")
  449. assert.Equal(t, *e.Node.Value, "bar", "")
  450. }
  451. func TestStoreCompareAndDeletePrevIndex(t *testing.T) {
  452. s := newStore()
  453. var eidx uint64 = 2
  454. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  455. e, err := s.CompareAndDelete("/foo", "", 1)
  456. assert.Nil(t, err, "")
  457. assert.Equal(t, e.EtcdIndex, eidx, "")
  458. assert.Equal(t, e.Action, "compareAndDelete", "")
  459. // check prevNode
  460. assert.NotNil(t, e.PrevNode, "")
  461. assert.Equal(t, e.PrevNode.Key, "/foo", "")
  462. assert.Equal(t, *e.PrevNode.Value, "bar", "")
  463. assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "")
  464. assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "")
  465. }
  466. func TestStoreCompareAndDeletePrevIndexFailsIfNotMatch(t *testing.T) {
  467. s := newStore()
  468. var eidx uint64 = 1
  469. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  470. e, _err := s.CompareAndDelete("/foo", "", 100)
  471. assert.NotNil(t, _err, "")
  472. err := _err.(*etcdErr.Error)
  473. assert.Equal(t, err.ErrorCode, etcdErr.EcodeTestFailed, "")
  474. assert.Equal(t, err.Message, "Compare failed", "")
  475. assert.Nil(t, e, "")
  476. e, _ = s.Get("/foo", false, false)
  477. assert.Equal(t, e.EtcdIndex, eidx, "")
  478. assert.Equal(t, *e.Node.Value, "bar", "")
  479. }
  480. // Ensure that the store cannot delete a directory.
  481. func TestStoreCompareAndDeleteDiretoryFail(t *testing.T) {
  482. s := newStore()
  483. s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  484. _, _err := s.CompareAndDelete("/foo", "", 0)
  485. assert.NotNil(t, _err, "")
  486. err := _err.(*etcdErr.Error)
  487. assert.Equal(t, err.ErrorCode, etcdErr.EcodeNotFile, "")
  488. }
  489. // Ensure that the store can conditionally update a key if it has a previous value.
  490. func TestStoreCompareAndSwapPrevValue(t *testing.T) {
  491. s := newStore()
  492. var eidx uint64 = 2
  493. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  494. e, err := s.CompareAndSwap("/foo", "bar", 0, "baz", TTLOptionSet{ExpireTime: Permanent})
  495. assert.Nil(t, err, "")
  496. assert.Equal(t, e.EtcdIndex, eidx, "")
  497. assert.Equal(t, e.Action, "compareAndSwap", "")
  498. assert.Equal(t, *e.Node.Value, "baz", "")
  499. // check prevNode
  500. assert.NotNil(t, e.PrevNode, "")
  501. assert.Equal(t, e.PrevNode.Key, "/foo", "")
  502. assert.Equal(t, *e.PrevNode.Value, "bar", "")
  503. assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "")
  504. assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "")
  505. e, _ = s.Get("/foo", false, false)
  506. assert.Equal(t, *e.Node.Value, "baz", "")
  507. }
  508. // Ensure that the store cannot conditionally update a key if it has the wrong previous value.
  509. func TestStoreCompareAndSwapPrevValueFailsIfNotMatch(t *testing.T) {
  510. s := newStore()
  511. var eidx uint64 = 1
  512. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  513. e, _err := s.CompareAndSwap("/foo", "wrong_value", 0, "baz", TTLOptionSet{ExpireTime: Permanent})
  514. err := _err.(*etcdErr.Error)
  515. assert.Equal(t, err.ErrorCode, etcdErr.EcodeTestFailed, "")
  516. assert.Equal(t, err.Message, "Compare failed", "")
  517. assert.Nil(t, e, "")
  518. e, _ = s.Get("/foo", false, false)
  519. assert.Equal(t, *e.Node.Value, "bar", "")
  520. assert.Equal(t, e.EtcdIndex, eidx, "")
  521. }
  522. // Ensure that the store can conditionally update a key if it has a previous index.
  523. func TestStoreCompareAndSwapPrevIndex(t *testing.T) {
  524. s := newStore()
  525. var eidx uint64 = 2
  526. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  527. e, err := s.CompareAndSwap("/foo", "", 1, "baz", TTLOptionSet{ExpireTime: Permanent})
  528. assert.Nil(t, err, "")
  529. assert.Equal(t, e.EtcdIndex, eidx, "")
  530. assert.Equal(t, e.Action, "compareAndSwap", "")
  531. assert.Equal(t, *e.Node.Value, "baz", "")
  532. // check prevNode
  533. assert.NotNil(t, e.PrevNode, "")
  534. assert.Equal(t, e.PrevNode.Key, "/foo", "")
  535. assert.Equal(t, *e.PrevNode.Value, "bar", "")
  536. assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "")
  537. assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "")
  538. e, _ = s.Get("/foo", false, false)
  539. assert.Equal(t, *e.Node.Value, "baz", "")
  540. assert.Equal(t, e.EtcdIndex, eidx, "")
  541. }
  542. // Ensure that the store cannot conditionally update a key if it has the wrong previous index.
  543. func TestStoreCompareAndSwapPrevIndexFailsIfNotMatch(t *testing.T) {
  544. s := newStore()
  545. var eidx uint64 = 1
  546. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  547. e, _err := s.CompareAndSwap("/foo", "", 100, "baz", TTLOptionSet{ExpireTime: Permanent})
  548. err := _err.(*etcdErr.Error)
  549. assert.Equal(t, err.ErrorCode, etcdErr.EcodeTestFailed, "")
  550. assert.Equal(t, err.Message, "Compare failed", "")
  551. assert.Nil(t, e, "")
  552. e, _ = s.Get("/foo", false, false)
  553. assert.Equal(t, e.EtcdIndex, eidx, "")
  554. assert.Equal(t, *e.Node.Value, "bar", "")
  555. }
  556. // Ensure that the store can watch for key creation.
  557. func TestStoreWatchCreate(t *testing.T) {
  558. s := newStore()
  559. var eidx uint64 = 0
  560. w, _ := s.Watch("/foo", false, false, 0)
  561. c := w.EventChan()
  562. assert.Equal(t, w.StartIndex(), eidx, "")
  563. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  564. eidx = 1
  565. e := nbselect(c)
  566. assert.Equal(t, e.EtcdIndex, eidx, "")
  567. assert.Equal(t, e.Action, "create", "")
  568. assert.Equal(t, e.Node.Key, "/foo", "")
  569. e = nbselect(c)
  570. assert.Nil(t, e, "")
  571. }
  572. // Ensure that the store can watch for recursive key creation.
  573. func TestStoreWatchRecursiveCreate(t *testing.T) {
  574. s := newStore()
  575. var eidx uint64 = 0
  576. w, _ := s.Watch("/foo", true, false, 0)
  577. assert.Equal(t, w.StartIndex(), eidx, "")
  578. eidx = 1
  579. s.Create("/foo/bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
  580. e := nbselect(w.EventChan())
  581. assert.Equal(t, e.EtcdIndex, eidx, "")
  582. assert.Equal(t, e.Action, "create", "")
  583. assert.Equal(t, e.Node.Key, "/foo/bar", "")
  584. }
  585. // Ensure that the store can watch for key updates.
  586. func TestStoreWatchUpdate(t *testing.T) {
  587. s := newStore()
  588. var eidx uint64 = 1
  589. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  590. w, _ := s.Watch("/foo", false, false, 0)
  591. assert.Equal(t, w.StartIndex(), eidx, "")
  592. eidx = 2
  593. s.Update("/foo", "baz", TTLOptionSet{ExpireTime: Permanent})
  594. e := nbselect(w.EventChan())
  595. assert.Equal(t, e.EtcdIndex, eidx, "")
  596. assert.Equal(t, e.Action, "update", "")
  597. assert.Equal(t, e.Node.Key, "/foo", "")
  598. }
  599. // Ensure that the store can watch for recursive key updates.
  600. func TestStoreWatchRecursiveUpdate(t *testing.T) {
  601. s := newStore()
  602. var eidx uint64 = 1
  603. s.Create("/foo/bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
  604. w, _ := s.Watch("/foo", true, false, 0)
  605. assert.Equal(t, w.StartIndex(), eidx, "")
  606. eidx = 2
  607. s.Update("/foo/bar", "baz", TTLOptionSet{ExpireTime: Permanent})
  608. e := nbselect(w.EventChan())
  609. assert.Equal(t, e.EtcdIndex, eidx, "")
  610. assert.Equal(t, e.Action, "update", "")
  611. assert.Equal(t, e.Node.Key, "/foo/bar", "")
  612. }
  613. // Ensure that the store can watch for key deletions.
  614. func TestStoreWatchDelete(t *testing.T) {
  615. s := newStore()
  616. var eidx uint64 = 1
  617. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  618. w, _ := s.Watch("/foo", false, false, 0)
  619. assert.Equal(t, w.StartIndex(), eidx, "")
  620. eidx = 2
  621. s.Delete("/foo", false, false)
  622. e := nbselect(w.EventChan())
  623. assert.Equal(t, e.EtcdIndex, eidx, "")
  624. assert.Equal(t, e.Action, "delete", "")
  625. assert.Equal(t, e.Node.Key, "/foo", "")
  626. }
  627. // Ensure that the store can watch for recursive key deletions.
  628. func TestStoreWatchRecursiveDelete(t *testing.T) {
  629. s := newStore()
  630. var eidx uint64 = 1
  631. s.Create("/foo/bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
  632. w, _ := s.Watch("/foo", true, false, 0)
  633. assert.Equal(t, w.StartIndex(), eidx, "")
  634. eidx = 2
  635. s.Delete("/foo/bar", false, false)
  636. e := nbselect(w.EventChan())
  637. assert.Equal(t, e.EtcdIndex, eidx, "")
  638. assert.Equal(t, e.Action, "delete", "")
  639. assert.Equal(t, e.Node.Key, "/foo/bar", "")
  640. }
  641. // Ensure that the store can watch for CAS updates.
  642. func TestStoreWatchCompareAndSwap(t *testing.T) {
  643. s := newStore()
  644. var eidx uint64 = 1
  645. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  646. w, _ := s.Watch("/foo", false, false, 0)
  647. assert.Equal(t, w.StartIndex(), eidx, "")
  648. eidx = 2
  649. s.CompareAndSwap("/foo", "bar", 0, "baz", TTLOptionSet{ExpireTime: Permanent})
  650. e := nbselect(w.EventChan())
  651. assert.Equal(t, e.EtcdIndex, eidx, "")
  652. assert.Equal(t, e.Action, "compareAndSwap", "")
  653. assert.Equal(t, e.Node.Key, "/foo", "")
  654. }
  655. // Ensure that the store can watch for recursive CAS updates.
  656. func TestStoreWatchRecursiveCompareAndSwap(t *testing.T) {
  657. s := newStore()
  658. var eidx uint64 = 1
  659. s.Create("/foo/bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
  660. w, _ := s.Watch("/foo", true, false, 0)
  661. assert.Equal(t, w.StartIndex(), eidx, "")
  662. eidx = 2
  663. s.CompareAndSwap("/foo/bar", "baz", 0, "bat", TTLOptionSet{ExpireTime: Permanent})
  664. e := nbselect(w.EventChan())
  665. assert.Equal(t, e.EtcdIndex, eidx, "")
  666. assert.Equal(t, e.Action, "compareAndSwap", "")
  667. assert.Equal(t, e.Node.Key, "/foo/bar", "")
  668. }
  669. // Ensure that the store can watch for key expiration.
  670. func TestStoreWatchExpire(t *testing.T) {
  671. s := newStore()
  672. fc := newFakeClock()
  673. s.clock = fc
  674. var eidx uint64 = 3
  675. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(400 * time.Millisecond)})
  676. s.Create("/foofoo", false, "barbarbar", false, TTLOptionSet{ExpireTime: fc.Now().Add(450 * time.Millisecond)})
  677. s.Create("/foodir", true, "", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
  678. w, _ := s.Watch("/", true, false, 0)
  679. assert.Equal(t, w.StartIndex(), eidx, "")
  680. c := w.EventChan()
  681. e := nbselect(c)
  682. assert.Nil(t, e, "")
  683. fc.Advance(600 * time.Millisecond)
  684. s.DeleteExpiredKeys(fc.Now())
  685. eidx = 4
  686. e = nbselect(c)
  687. assert.Equal(t, e.EtcdIndex, eidx, "")
  688. assert.Equal(t, e.Action, "expire", "")
  689. assert.Equal(t, e.Node.Key, "/foo", "")
  690. w, _ = s.Watch("/", true, false, 5)
  691. eidx = 6
  692. assert.Equal(t, w.StartIndex(), eidx, "")
  693. e = nbselect(w.EventChan())
  694. assert.Equal(t, e.EtcdIndex, eidx, "")
  695. assert.Equal(t, e.Action, "expire", "")
  696. assert.Equal(t, e.Node.Key, "/foofoo", "")
  697. w, _ = s.Watch("/", true, false, 6)
  698. e = nbselect(w.EventChan())
  699. assert.Equal(t, e.EtcdIndex, eidx, "")
  700. assert.Equal(t, e.Action, "expire", "")
  701. assert.Equal(t, e.Node.Key, "/foodir", "")
  702. assert.Equal(t, e.Node.Dir, true, "")
  703. }
  704. // Ensure that the store can watch for key expiration when refreshing.
  705. func TestStoreWatchExpireRefresh(t *testing.T) {
  706. s := newStore()
  707. fc := newFakeClock()
  708. s.clock = fc
  709. var eidx uint64 = 2
  710. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
  711. s.Create("/foofoo", false, "barbarbar", false, TTLOptionSet{ExpireTime: fc.Now().Add(1200 * time.Millisecond), Refresh: true})
  712. // Make sure we set watch updates when Refresh is true for newly created keys
  713. w, _ := s.Watch("/", true, false, 0)
  714. assert.Equal(t, w.StartIndex(), eidx, "")
  715. c := w.EventChan()
  716. e := nbselect(c)
  717. assert.Nil(t, e, "")
  718. fc.Advance(600 * time.Millisecond)
  719. s.DeleteExpiredKeys(fc.Now())
  720. eidx = 3
  721. e = nbselect(c)
  722. assert.Equal(t, e.EtcdIndex, eidx, "")
  723. assert.Equal(t, e.Action, "expire", "")
  724. assert.Equal(t, e.Node.Key, "/foo", "")
  725. s.Update("/foofoo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
  726. w, _ = s.Watch("/", true, false, 4)
  727. fc.Advance(700 * time.Millisecond)
  728. s.DeleteExpiredKeys(fc.Now())
  729. eidx = 5 // We should skip 4 because a TTL update should occur with no watch notification if set `TTLOptionSet.Refresh` to true
  730. assert.Equal(t, w.StartIndex(), eidx-1, "")
  731. e = nbselect(w.EventChan())
  732. assert.Equal(t, e.EtcdIndex, eidx, "")
  733. assert.Equal(t, e.Action, "expire", "")
  734. assert.Equal(t, e.Node.Key, "/foofoo", "")
  735. }
  736. // Ensure that the store can watch for key expiration when refreshing with an empty value.
  737. func TestStoreWatchExpireEmptyRefresh(t *testing.T) {
  738. s := newStore()
  739. fc := newFakeClock()
  740. s.clock = fc
  741. var eidx uint64 = 1
  742. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
  743. // Should be no-op
  744. fc.Advance(200 * time.Millisecond)
  745. s.DeleteExpiredKeys(fc.Now())
  746. s.Update("/foo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
  747. w, _ := s.Watch("/", true, false, 2)
  748. fc.Advance(700 * time.Millisecond)
  749. s.DeleteExpiredKeys(fc.Now())
  750. eidx = 3 // We should skip 2 because a TTL update should occur with no watch notification if set `TTLOptionSet.Refresh` to true
  751. assert.Equal(t, w.StartIndex(), eidx-1, "")
  752. e := nbselect(w.EventChan())
  753. assert.Equal(t, e.EtcdIndex, eidx, "")
  754. assert.Equal(t, e.Action, "expire", "")
  755. assert.Equal(t, e.Node.Key, "/foo", "")
  756. assert.Equal(t, *e.PrevNode.Value, "bar", "")
  757. }
  758. // Update TTL of a key (set TTLOptionSet.Refresh to false) and send notification
  759. func TestStoreWatchNoRefresh(t *testing.T) {
  760. s := newStore()
  761. fc := newFakeClock()
  762. s.clock = fc
  763. var eidx uint64 = 1
  764. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
  765. // Should be no-op
  766. fc.Advance(200 * time.Millisecond)
  767. s.DeleteExpiredKeys(fc.Now())
  768. // Update key's TTL with setting `TTLOptionSet.Refresh` to false will cause an update event
  769. s.Update("/foo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: false})
  770. w, _ := s.Watch("/", true, false, 2)
  771. fc.Advance(700 * time.Millisecond)
  772. s.DeleteExpiredKeys(fc.Now())
  773. eidx = 2
  774. assert.Equal(t, w.StartIndex(), eidx, "")
  775. e := nbselect(w.EventChan())
  776. assert.Equal(t, e.EtcdIndex, eidx, "")
  777. assert.Equal(t, e.Action, "update", "")
  778. assert.Equal(t, e.Node.Key, "/foo", "")
  779. assert.Equal(t, *e.PrevNode.Value, "bar", "")
  780. }
  781. // Ensure that the store can update the TTL on a value with refresh.
  782. func TestStoreRefresh(t *testing.T) {
  783. s := newStore()
  784. fc := newFakeClock()
  785. s.clock = fc
  786. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
  787. s.Create("/bar", true, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
  788. _, err := s.Update("/foo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
  789. assert.Nil(t, err, "")
  790. _, err = s.Set("/foo", false, "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
  791. assert.Nil(t, err, "")
  792. _, err = s.Update("/bar", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
  793. assert.Nil(t, err, "")
  794. _, err = s.CompareAndSwap("/foo", "bar", 0, "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
  795. assert.Nil(t, err, "")
  796. }
  797. // Ensure that the store can watch in streaming mode.
  798. func TestStoreWatchStream(t *testing.T) {
  799. s := newStore()
  800. var eidx uint64 = 1
  801. w, _ := s.Watch("/foo", false, true, 0)
  802. // first modification
  803. s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  804. e := nbselect(w.EventChan())
  805. assert.Equal(t, e.EtcdIndex, eidx, "")
  806. assert.Equal(t, e.Action, "create", "")
  807. assert.Equal(t, e.Node.Key, "/foo", "")
  808. assert.Equal(t, *e.Node.Value, "bar", "")
  809. e = nbselect(w.EventChan())
  810. assert.Nil(t, e, "")
  811. // second modification
  812. eidx = 2
  813. s.Update("/foo", "baz", TTLOptionSet{ExpireTime: Permanent})
  814. e = nbselect(w.EventChan())
  815. assert.Equal(t, e.EtcdIndex, eidx, "")
  816. assert.Equal(t, e.Action, "update", "")
  817. assert.Equal(t, e.Node.Key, "/foo", "")
  818. assert.Equal(t, *e.Node.Value, "baz", "")
  819. e = nbselect(w.EventChan())
  820. assert.Nil(t, e, "")
  821. }
  822. // Ensure that the store can recover from a previously saved state.
  823. func TestStoreRecover(t *testing.T) {
  824. s := newStore()
  825. var eidx uint64 = 4
  826. s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  827. s.Create("/foo/x", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  828. s.Update("/foo/x", "barbar", TTLOptionSet{ExpireTime: Permanent})
  829. s.Create("/foo/y", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
  830. b, err := s.Save()
  831. s2 := newStore()
  832. s2.Recovery(b)
  833. e, err := s.Get("/foo/x", false, false)
  834. assert.Equal(t, e.Node.CreatedIndex, uint64(2), "")
  835. assert.Equal(t, e.Node.ModifiedIndex, uint64(3), "")
  836. assert.Equal(t, e.EtcdIndex, eidx, "")
  837. assert.Nil(t, err, "")
  838. assert.Equal(t, *e.Node.Value, "barbar", "")
  839. e, err = s.Get("/foo/y", false, false)
  840. assert.Equal(t, e.EtcdIndex, eidx, "")
  841. assert.Nil(t, err, "")
  842. assert.Equal(t, *e.Node.Value, "baz", "")
  843. }
  844. // Ensure that the store can recover from a previously saved state that includes an expiring key.
  845. func TestStoreRecoverWithExpiration(t *testing.T) {
  846. s := newStore()
  847. s.clock = newFakeClock()
  848. fc := newFakeClock()
  849. var eidx uint64 = 4
  850. s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  851. s.Create("/foo/x", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  852. s.Create("/foo/y", false, "baz", false, TTLOptionSet{ExpireTime: fc.Now().Add(5 * time.Millisecond)})
  853. b, err := s.Save()
  854. time.Sleep(10 * time.Millisecond)
  855. s2 := newStore()
  856. s2.clock = fc
  857. s2.Recovery(b)
  858. fc.Advance(600 * time.Millisecond)
  859. s.DeleteExpiredKeys(fc.Now())
  860. e, err := s.Get("/foo/x", false, false)
  861. assert.Nil(t, err, "")
  862. assert.Equal(t, e.EtcdIndex, eidx, "")
  863. assert.Equal(t, *e.Node.Value, "bar", "")
  864. e, err = s.Get("/foo/y", false, false)
  865. assert.NotNil(t, err, "")
  866. assert.Nil(t, e, "")
  867. }
  868. // Ensure that the store can watch for hidden keys as long as it's an exact path match.
  869. func TestStoreWatchCreateWithHiddenKey(t *testing.T) {
  870. s := newStore()
  871. var eidx uint64 = 1
  872. w, _ := s.Watch("/_foo", false, false, 0)
  873. s.Create("/_foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  874. e := nbselect(w.EventChan())
  875. assert.Equal(t, e.EtcdIndex, eidx, "")
  876. assert.Equal(t, e.Action, "create", "")
  877. assert.Equal(t, e.Node.Key, "/_foo", "")
  878. e = nbselect(w.EventChan())
  879. assert.Nil(t, e, "")
  880. }
  881. // Ensure that the store doesn't see hidden key creates without an exact path match in recursive mode.
  882. func TestStoreWatchRecursiveCreateWithHiddenKey(t *testing.T) {
  883. s := newStore()
  884. w, _ := s.Watch("/foo", true, false, 0)
  885. s.Create("/foo/_bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
  886. e := nbselect(w.EventChan())
  887. assert.Nil(t, e, "")
  888. w, _ = s.Watch("/foo", true, false, 0)
  889. s.Create("/foo/_baz", true, "", false, TTLOptionSet{ExpireTime: Permanent})
  890. e = nbselect(w.EventChan())
  891. assert.Nil(t, e, "")
  892. s.Create("/foo/_baz/quux", false, "quux", false, TTLOptionSet{ExpireTime: Permanent})
  893. e = nbselect(w.EventChan())
  894. assert.Nil(t, e, "")
  895. }
  896. // Ensure that the store doesn't see hidden key updates.
  897. func TestStoreWatchUpdateWithHiddenKey(t *testing.T) {
  898. s := newStore()
  899. s.Create("/_foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  900. w, _ := s.Watch("/_foo", false, false, 0)
  901. s.Update("/_foo", "baz", TTLOptionSet{ExpireTime: Permanent})
  902. e := nbselect(w.EventChan())
  903. assert.Equal(t, e.Action, "update", "")
  904. assert.Equal(t, e.Node.Key, "/_foo", "")
  905. e = nbselect(w.EventChan())
  906. assert.Nil(t, e, "")
  907. }
  908. // Ensure that the store doesn't see hidden key updates without an exact path match in recursive mode.
  909. func TestStoreWatchRecursiveUpdateWithHiddenKey(t *testing.T) {
  910. s := newStore()
  911. s.Create("/foo/_bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
  912. w, _ := s.Watch("/foo", true, false, 0)
  913. s.Update("/foo/_bar", "baz", TTLOptionSet{ExpireTime: Permanent})
  914. e := nbselect(w.EventChan())
  915. assert.Nil(t, e, "")
  916. }
  917. // Ensure that the store can watch for key deletions.
  918. func TestStoreWatchDeleteWithHiddenKey(t *testing.T) {
  919. s := newStore()
  920. var eidx uint64 = 2
  921. s.Create("/_foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
  922. w, _ := s.Watch("/_foo", false, false, 0)
  923. s.Delete("/_foo", false, false)
  924. e := nbselect(w.EventChan())
  925. assert.Equal(t, e.EtcdIndex, eidx, "")
  926. assert.Equal(t, e.Action, "delete", "")
  927. assert.Equal(t, e.Node.Key, "/_foo", "")
  928. e = nbselect(w.EventChan())
  929. assert.Nil(t, e, "")
  930. }
  931. // Ensure that the store doesn't see hidden key deletes without an exact path match in recursive mode.
  932. func TestStoreWatchRecursiveDeleteWithHiddenKey(t *testing.T) {
  933. s := newStore()
  934. s.Create("/foo/_bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
  935. w, _ := s.Watch("/foo", true, false, 0)
  936. s.Delete("/foo/_bar", false, false)
  937. e := nbselect(w.EventChan())
  938. assert.Nil(t, e, "")
  939. }
  940. // Ensure that the store doesn't see expirations of hidden keys.
  941. func TestStoreWatchExpireWithHiddenKey(t *testing.T) {
  942. s := newStore()
  943. fc := newFakeClock()
  944. s.clock = fc
  945. s.Create("/_foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
  946. s.Create("/foofoo", false, "barbarbar", false, TTLOptionSet{ExpireTime: fc.Now().Add(1000 * time.Millisecond)})
  947. w, _ := s.Watch("/", true, false, 0)
  948. c := w.EventChan()
  949. e := nbselect(c)
  950. assert.Nil(t, e, "")
  951. fc.Advance(600 * time.Millisecond)
  952. s.DeleteExpiredKeys(fc.Now())
  953. e = nbselect(c)
  954. assert.Nil(t, e, "")
  955. fc.Advance(600 * time.Millisecond)
  956. s.DeleteExpiredKeys(fc.Now())
  957. e = nbselect(c)
  958. assert.Equal(t, e.Action, "expire", "")
  959. assert.Equal(t, e.Node.Key, "/foofoo", "")
  960. }
  961. // Ensure that the store does see hidden key creates if watching deeper than a hidden key in recursive mode.
  962. func TestStoreWatchRecursiveCreateDeeperThanHiddenKey(t *testing.T) {
  963. s := newStore()
  964. var eidx uint64 = 1
  965. w, _ := s.Watch("/_foo/bar", true, false, 0)
  966. s.Create("/_foo/bar/baz", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
  967. e := nbselect(w.EventChan())
  968. assert.NotNil(t, e, "")
  969. assert.Equal(t, e.EtcdIndex, eidx, "")
  970. assert.Equal(t, e.Action, "create", "")
  971. assert.Equal(t, e.Node.Key, "/_foo/bar/baz", "")
  972. }
  973. // Ensure that slow consumers are handled properly.
  974. //
  975. // Since Watcher.EventChan() has a buffer of size 100 we can only queue 100
  976. // event per watcher. If the consumer cannot consume the event on time and
  977. // another event arrives, the channel is closed and event is discarded.
  978. // This test ensures that after closing the channel, the store can continue
  979. // to operate correctly.
  980. func TestStoreWatchSlowConsumer(t *testing.T) {
  981. s := newStore()
  982. s.Watch("/foo", true, true, 0) // stream must be true
  983. // Fill watch channel with 100 events
  984. for i := 1; i <= 100; i++ {
  985. s.Set("/foo", false, string(i), TTLOptionSet{ExpireTime: Permanent}) // ok
  986. }
  987. assert.Equal(t, s.WatcherHub.count, int64(1), "")
  988. s.Set("/foo", false, "101", TTLOptionSet{ExpireTime: Permanent}) // ok
  989. // remove watcher
  990. assert.Equal(t, s.WatcherHub.count, int64(0), "")
  991. s.Set("/foo", false, "102", TTLOptionSet{ExpireTime: Permanent}) // must not panic
  992. }
  993. // Performs a non-blocking select on an event channel.
  994. func nbselect(c <-chan *Event) *Event {
  995. select {
  996. case e := <-c:
  997. return e
  998. default:
  999. return nil
  1000. }
  1001. }
  1002. // newFakeClock creates a new FakeClock that has been advanced to at least minExpireTime
  1003. func newFakeClock() clockwork.FakeClock {
  1004. fc := clockwork.NewFakeClock()
  1005. for minExpireTime.After(fc.Now()) {
  1006. fc.Advance((0x1 << 62) * time.Nanosecond)
  1007. }
  1008. return fc
  1009. }