datastore_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. package datastore
  2. import (
  3. "encoding/json"
  4. "testing"
  5. "github.com/docker/docker/libnetwork/options"
  6. "gotest.tools/v3/assert"
  7. is "gotest.tools/v3/assert/cmp"
  8. )
  9. const dummyKey = "dummy"
  10. // NewTestDataStore can be used by other Tests in order to use custom datastore
  11. func NewTestDataStore() *Store {
  12. return &Store{scope: LocalScope, store: NewMockStore()}
  13. }
  14. func TestKey(t *testing.T) {
  15. sKey := Key("hello", "world")
  16. const expected = "docker/network/v1.0/hello/world/"
  17. assert.Check(t, is.Equal(sKey, expected))
  18. }
  19. func TestInvalidDataStore(t *testing.T) {
  20. _, err := New(ScopeCfg{
  21. Client: ScopeClientCfg{
  22. Provider: "invalid",
  23. Address: "localhost:8500",
  24. },
  25. })
  26. assert.Check(t, is.Error(err, "unsupported KV store"))
  27. }
  28. func TestKVObjectFlatKey(t *testing.T) {
  29. store := NewTestDataStore()
  30. expected := dummyKVObject("1000", true)
  31. err := store.PutObjectAtomic(expected)
  32. assert.Check(t, err)
  33. n := dummyObject{ID: "1000"} // GetObject uses KVObject.Key() for cache lookup.
  34. err = store.GetObject(Key(dummyKey, "1000"), &n)
  35. assert.Check(t, err)
  36. assert.Check(t, is.Equal(n.Name, expected.Name))
  37. }
  38. func TestAtomicKVObjectFlatKey(t *testing.T) {
  39. store := NewTestDataStore()
  40. expected := dummyKVObject("1111", true)
  41. assert.Check(t, !expected.Exists())
  42. err := store.PutObjectAtomic(expected)
  43. assert.Check(t, err)
  44. assert.Check(t, expected.Exists())
  45. // PutObjectAtomic automatically sets the Index again. Hence the following must pass.
  46. err = store.PutObjectAtomic(expected)
  47. assert.Check(t, err, "Atomic update should succeed.")
  48. // Get the latest index and try PutObjectAtomic again for the same Key
  49. // This must succeed as well
  50. n := dummyObject{ID: "1111"} // GetObject uses KVObject.Key() for cache lookup.
  51. err = store.GetObject(Key(expected.Key()...), &n)
  52. assert.Check(t, err)
  53. n.ReturnValue = true
  54. err = store.PutObjectAtomic(&n)
  55. assert.Check(t, err)
  56. // Get the Object using GetObject, then set again.
  57. newObj := dummyObject{ID: "1111"} // GetObject uses KVObject.Key() for cache lookup.
  58. err = store.GetObject(Key(expected.Key()...), &newObj)
  59. assert.Check(t, err)
  60. assert.Check(t, newObj.Exists())
  61. err = store.PutObjectAtomic(&n)
  62. assert.Check(t, err)
  63. }
  64. // dummy data used to test the datastore
  65. type dummyObject struct {
  66. Name string `kv:"leaf"`
  67. NetworkType string `kv:"leaf"`
  68. EnableIPv6 bool `kv:"leaf"`
  69. Rec *recStruct `kv:"recursive"`
  70. Dict map[string]*recStruct `kv:"iterative"`
  71. Generic options.Generic `kv:"iterative"`
  72. ID string
  73. DBIndex uint64
  74. DBExists bool
  75. SkipSave bool
  76. ReturnValue bool
  77. }
  78. func (n *dummyObject) Key() []string {
  79. return []string{dummyKey, n.ID}
  80. }
  81. func (n *dummyObject) KeyPrefix() []string {
  82. return []string{dummyKey}
  83. }
  84. func (n *dummyObject) Value() []byte {
  85. if !n.ReturnValue {
  86. return nil
  87. }
  88. b, err := json.Marshal(n)
  89. if err != nil {
  90. return nil
  91. }
  92. return b
  93. }
  94. func (n *dummyObject) SetValue(value []byte) error {
  95. return json.Unmarshal(value, n)
  96. }
  97. func (n *dummyObject) Index() uint64 {
  98. return n.DBIndex
  99. }
  100. func (n *dummyObject) SetIndex(index uint64) {
  101. n.DBIndex = index
  102. n.DBExists = true
  103. }
  104. func (n *dummyObject) Exists() bool {
  105. return n.DBExists
  106. }
  107. func (n *dummyObject) Skip() bool {
  108. return n.SkipSave
  109. }
  110. func (n *dummyObject) DataScope() string {
  111. return LocalScope
  112. }
  113. func (n *dummyObject) MarshalJSON() ([]byte, error) {
  114. return json.Marshal(map[string]interface{}{
  115. "name": n.Name,
  116. "networkType": n.NetworkType,
  117. "enableIPv6": n.EnableIPv6,
  118. "generic": n.Generic,
  119. })
  120. }
  121. func (n *dummyObject) UnmarshalJSON(b []byte) error {
  122. var netMap map[string]interface{}
  123. if err := json.Unmarshal(b, &netMap); err != nil {
  124. return err
  125. }
  126. n.Name = netMap["name"].(string)
  127. n.NetworkType = netMap["networkType"].(string)
  128. n.EnableIPv6 = netMap["enableIPv6"].(bool)
  129. n.Generic = netMap["generic"].(map[string]interface{})
  130. return nil
  131. }
  132. // dummy structure to test "recursive" cases
  133. type recStruct struct {
  134. Name string `kv:"leaf"`
  135. Field1 int `kv:"leaf"`
  136. Dict map[string]string `kv:"iterative"`
  137. DBIndex uint64
  138. DBExists bool
  139. SkipSave bool
  140. }
  141. func (r *recStruct) Key() []string {
  142. return []string{"recStruct"}
  143. }
  144. func (r *recStruct) Value() []byte {
  145. b, err := json.Marshal(r)
  146. if err != nil {
  147. return nil
  148. }
  149. return b
  150. }
  151. func (r *recStruct) SetValue(value []byte) error {
  152. return json.Unmarshal(value, r)
  153. }
  154. func (r *recStruct) Index() uint64 {
  155. return r.DBIndex
  156. }
  157. func (r *recStruct) SetIndex(index uint64) {
  158. r.DBIndex = index
  159. r.DBExists = true
  160. }
  161. func (r *recStruct) Exists() bool {
  162. return r.DBExists
  163. }
  164. func (r *recStruct) Skip() bool {
  165. return r.SkipSave
  166. }
  167. func dummyKVObject(id string, retValue bool) *dummyObject {
  168. cDict := map[string]string{
  169. "foo": "bar",
  170. "hello": "world",
  171. }
  172. return &dummyObject{
  173. Name: "testNw",
  174. NetworkType: "bridge",
  175. EnableIPv6: true,
  176. Rec: &recStruct{Name: "gen", Field1: 5, Dict: cDict},
  177. ID: id,
  178. DBIndex: 0,
  179. ReturnValue: retValue,
  180. DBExists: false,
  181. SkipSave: false,
  182. Generic: map[string]interface{}{
  183. "label1": &recStruct{Name: "value1", Field1: 1, Dict: cDict},
  184. "label2": "subnet=10.1.1.0/16",
  185. },
  186. }
  187. }