vagrant-bosh 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.gitmodules +6 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE +20 -0
  6. data/README.md +73 -0
  7. data/Rakefile +2 -0
  8. data/dev/.gitignore +1 -0
  9. data/dev/Vagrantfile +30 -0
  10. data/dev/example-bosh-manifest.yml +188 -0
  11. data/dev/example-winston-manifest.yml +70 -0
  12. data/go/.gitignore +11 -0
  13. data/go/bin/build +10 -0
  14. data/go/bin/build-linux-amd64 +9 -0
  15. data/go/bin/env +13 -0
  16. data/go/bin/go +5 -0
  17. data/go/bin/golint +19 -0
  18. data/go/bin/test +37 -0
  19. data/go/src/boshprovisioner/agent/client/client_interface.go +73 -0
  20. data/go/src/boshprovisioner/agent/client/fakes/fake_client.go +81 -0
  21. data/go/src/boshprovisioner/agent/client/http_client.go +299 -0
  22. data/go/src/boshprovisioner/agent/client/http_client_envelope.go +107 -0
  23. data/go/src/boshprovisioner/deployment/deployment.go +221 -0
  24. data/go/src/boshprovisioner/deployment/instance.go +54 -0
  25. data/go/src/boshprovisioner/deployment/manifest/deployment.go +80 -0
  26. data/go/src/boshprovisioner/deployment/manifest/ips.go +23 -0
  27. data/go/src/boshprovisioner/deployment/manifest/manifest.go +143 -0
  28. data/go/src/boshprovisioner/deployment/manifest/manifest_suite_test.go +13 -0
  29. data/go/src/boshprovisioner/deployment/manifest/manifest_test.go +86 -0
  30. data/go/src/boshprovisioner/deployment/manifest/syntax_validator.go +186 -0
  31. data/go/src/boshprovisioner/deployment/manifest/watch_time.go +47 -0
  32. data/go/src/boshprovisioner/deployment/manifest_reader.go +46 -0
  33. data/go/src/boshprovisioner/deployment/reader_factory.go +25 -0
  34. data/go/src/boshprovisioner/deployment/semantic_validator.go +111 -0
  35. data/go/src/boshprovisioner/downloader/blobstore_downloader.go +55 -0
  36. data/go/src/boshprovisioner/downloader/default_mux_downloader.go +22 -0
  37. data/go/src/boshprovisioner/downloader/downloader_interface.go +6 -0
  38. data/go/src/boshprovisioner/downloader/http_downloader.go +53 -0
  39. data/go/src/boshprovisioner/downloader/local_fs_downloader.go +48 -0
  40. data/go/src/boshprovisioner/downloader/mux_downloader.go +69 -0
  41. data/go/src/boshprovisioner/eventlog/log.go +72 -0
  42. data/go/src/boshprovisioner/eventlog/stage.go +39 -0
  43. data/go/src/boshprovisioner/eventlog/task.go +58 -0
  44. data/go/src/boshprovisioner/index/file_index.go +289 -0
  45. data/go/src/boshprovisioner/index/file_index_test.go +296 -0
  46. data/go/src/boshprovisioner/index/index_interface.go +18 -0
  47. data/go/src/boshprovisioner/index/index_suite_test.go +13 -0
  48. data/go/src/boshprovisioner/instance/templatescompiler/concrete_templates_compiler.go +273 -0
  49. data/go/src/boshprovisioner/instance/templatescompiler/erbrenderer/erb_renderer.go +117 -0
  50. data/go/src/boshprovisioner/instance/templatescompiler/erbrenderer/erbrenderer_suite_test.go +13 -0
  51. data/go/src/boshprovisioner/instance/templatescompiler/erbrenderer/render_properties.go +77 -0
  52. data/go/src/boshprovisioner/instance/templatescompiler/erbrenderer/render_properties_test.go +142 -0
  53. data/go/src/boshprovisioner/instance/templatescompiler/erbrenderer/template_evaluation_context.go +85 -0
  54. data/go/src/boshprovisioner/instance/templatescompiler/erbrenderer/template_evaluation_context_rb.go +155 -0
  55. data/go/src/boshprovisioner/instance/templatescompiler/jobsrepo/concrete_jobs_repository.go +64 -0
  56. data/go/src/boshprovisioner/instance/templatescompiler/jobsrepo/concrete_runtime_packages_repository.go +105 -0
  57. data/go/src/boshprovisioner/instance/templatescompiler/jobsrepo/concrete_template_to_job_repository.go +76 -0
  58. data/go/src/boshprovisioner/instance/templatescompiler/jobsrepo/jobs_repository_interface.go +31 -0
  59. data/go/src/boshprovisioner/instance/templatescompiler/rendered_archives_compiler.go +81 -0
  60. data/go/src/boshprovisioner/instance/templatescompiler/templates_compiler_interface.go +20 -0
  61. data/go/src/boshprovisioner/instance/templatescompiler/templatesrepo/ct_repository.go +54 -0
  62. data/go/src/boshprovisioner/instance/templatescompiler/templatesrepo/templates_repository_interface.go +16 -0
  63. data/go/src/boshprovisioner/instance/updater/applier/applier.go +93 -0
  64. data/go/src/boshprovisioner/instance/updater/applier/empty_state.go +66 -0
  65. data/go/src/boshprovisioner/instance/updater/applier/job_state.go +178 -0
  66. data/go/src/boshprovisioner/instance/updater/drainer.go +72 -0
  67. data/go/src/boshprovisioner/instance/updater/preparer.go +39 -0
  68. data/go/src/boshprovisioner/instance/updater/starter.go +36 -0
  69. data/go/src/boshprovisioner/instance/updater/stopper.go +36 -0
  70. data/go/src/boshprovisioner/instance/updater/updater.go +102 -0
  71. data/go/src/boshprovisioner/instance/updater/updater_factory.go +83 -0
  72. data/go/src/boshprovisioner/instance/updater/updater_suite_test.go +13 -0
  73. data/go/src/boshprovisioner/instance/updater/waiter.go +77 -0
  74. data/go/src/boshprovisioner/instance/updater/waiter_test.go +103 -0
  75. data/go/src/boshprovisioner/main/config.go +77 -0
  76. data/go/src/boshprovisioner/main/main.go +183 -0
  77. data/go/src/boshprovisioner/main/repos_factory.go +96 -0
  78. data/go/src/boshprovisioner/packagescompiler/compiledpackagesrepo/compiled_packages_repository_interface.go +17 -0
  79. data/go/src/boshprovisioner/packagescompiler/compiledpackagesrepo/concrete_compiled_packages_repository.go +61 -0
  80. data/go/src/boshprovisioner/packagescompiler/concrete_packages_compiler.go +179 -0
  81. data/go/src/boshprovisioner/packagescompiler/concrete_packages_compiler_factory.go +48 -0
  82. data/go/src/boshprovisioner/packagescompiler/packages_compiler_interface.go +20 -0
  83. data/go/src/boshprovisioner/packagescompiler/packagesrepo/concrete_packages_repository.go +65 -0
  84. data/go/src/boshprovisioner/packagescompiler/packagesrepo/packages_repository_interface.go +16 -0
  85. data/go/src/boshprovisioner/provisioner/blobstore_config.go +65 -0
  86. data/go/src/boshprovisioner/provisioner/blobstore_provisioner.go +38 -0
  87. data/go/src/boshprovisioner/provisioner/deployment_provisioner.go +97 -0
  88. data/go/src/boshprovisioner/provisioner/instance_provisioner.go +48 -0
  89. data/go/src/boshprovisioner/provisioner/release_compiler.go +133 -0
  90. data/go/src/boshprovisioner/release/job/job.go +86 -0
  91. data/go/src/boshprovisioner/release/job/manifest/manifest.go +79 -0
  92. data/go/src/boshprovisioner/release/job/manifest/manifest_suite_test.go +13 -0
  93. data/go/src/boshprovisioner/release/job/manifest/manifest_test.go +42 -0
  94. data/go/src/boshprovisioner/release/job/manifest/syntax_validator.go +43 -0
  95. data/go/src/boshprovisioner/release/job/reader_factory.go +34 -0
  96. data/go/src/boshprovisioner/release/job/tar_reader.go +133 -0
  97. data/go/src/boshprovisioner/release/manifest/manifest.go +96 -0
  98. data/go/src/boshprovisioner/release/manifest_reader.go +29 -0
  99. data/go/src/boshprovisioner/release/reader_factory.go +34 -0
  100. data/go/src/boshprovisioner/release/release.go +144 -0
  101. data/go/src/boshprovisioner/release/release_suite_test.go +13 -0
  102. data/go/src/boshprovisioner/release/release_test.go +129 -0
  103. data/go/src/boshprovisioner/release/tar_reader.go +139 -0
  104. data/go/src/boshprovisioner/releasesrepo/blobstore_releases_repository.go +114 -0
  105. data/go/src/boshprovisioner/releasesrepo/releases_repository_interface.go +15 -0
  106. data/go/src/boshprovisioner/tar/cmd_compressor.go +68 -0
  107. data/go/src/boshprovisioner/tar/cmd_extractor.go +47 -0
  108. data/go/src/boshprovisioner/tar/compressor_interface.go +6 -0
  109. data/go/src/boshprovisioner/tar/extractor_interface.go +6 -0
  110. data/go/src/boshprovisioner/util/string_keyed.go +70 -0
  111. data/go/src/boshprovisioner/vm/agent_provisioner.go +266 -0
  112. data/go/src/boshprovisioner/vm/asset_manager.go +61 -0
  113. data/go/src/boshprovisioner/vm/deps_provisioner.go +92 -0
  114. data/go/src/boshprovisioner/vm/monit_provisioner.go +83 -0
  115. data/go/src/boshprovisioner/vm/runit_provisioner.go +225 -0
  116. data/go/src/boshprovisioner/vm/simple_cmds.go +54 -0
  117. data/go/src/boshprovisioner/vm/vcap_user_provisioner.go +120 -0
  118. data/go/src/boshprovisioner/vm/vm.go +19 -0
  119. data/go/src/boshprovisioner/vm/vm_provisioner.go +57 -0
  120. data/go/src/boshprovisioner/vm/vm_provisioner_factory.go +97 -0
  121. data/lib/vagrant-bosh/asset_uploader.rb +53 -0
  122. data/lib/vagrant-bosh/assets/agent/agent-log +5 -0
  123. data/lib/vagrant-bosh/assets/agent/agent-run +12 -0
  124. data/lib/vagrant-bosh/assets/agent/agent.cert +18 -0
  125. data/lib/vagrant-bosh/assets/agent/agent.json +9 -0
  126. data/lib/vagrant-bosh/assets/agent/agent.key +27 -0
  127. data/lib/vagrant-bosh/assets/agent/bosh-agent +0 -0
  128. data/lib/vagrant-bosh/assets/agent/bosh-agent-rc +18 -0
  129. data/lib/vagrant-bosh/assets/agent/bosh-blobstore-dav +0 -0
  130. data/lib/vagrant-bosh/assets/monit/monit +0 -0
  131. data/lib/vagrant-bosh/assets/monit/monit-log +5 -0
  132. data/lib/vagrant-bosh/assets/monit/monit-run +9 -0
  133. data/lib/vagrant-bosh/assets/monit/monitrc +8 -0
  134. data/lib/vagrant-bosh/assets/provisioner +0 -0
  135. data/lib/vagrant-bosh/bootstrapper.rb +59 -0
  136. data/lib/vagrant-bosh/communicator.rb +50 -0
  137. data/lib/vagrant-bosh/config.rb +15 -0
  138. data/lib/vagrant-bosh/errors.rb +11 -0
  139. data/lib/vagrant-bosh/plugin.rb +25 -0
  140. data/lib/vagrant-bosh/provisioner.rb +46 -0
  141. data/lib/vagrant-bosh/provisioner_tracker.rb +41 -0
  142. data/lib/vagrant-bosh/ui.rb +77 -0
  143. data/lib/vagrant-bosh/version.rb +5 -0
  144. data/lib/vagrant-bosh.rb +15 -0
  145. data/templates/locales/en.yml +15 -0
  146. data/vagrant-bosh.gemspec +20 -0
  147. metadata +191 -0
@@ -0,0 +1,289 @@
1
+ package index
2
+
3
+ import (
4
+ "encoding/json"
5
+ "reflect"
6
+
7
+ bosherr "bosh/errors"
8
+ boshsys "bosh/system"
9
+ )
10
+
11
+ type FileIndex struct {
12
+ path string
13
+ fs boshsys.FileSystem
14
+ }
15
+
16
+ type indexEntry struct {
17
+ Key map[string]interface{}
18
+ Value json.RawMessage
19
+ }
20
+
21
+ func NewFileIndex(path string, fs boshsys.FileSystem) FileIndex {
22
+ return FileIndex{path: path, fs: fs}
23
+ }
24
+
25
+ func (ri FileIndex) List(entries interface{}) error {
26
+ rawEntries, err := ri.readRawEntries()
27
+ if err != nil {
28
+ return err
29
+ }
30
+
31
+ var rawEntryValues []json.RawMessage
32
+
33
+ for _, rawEntry := range rawEntries {
34
+ rawEntryValues = append(rawEntryValues, rawEntry.Value)
35
+ }
36
+
37
+ // todo avoid serializing already collected entries
38
+ rawEntryValuesBytes, err := json.Marshal(rawEntryValues)
39
+ if err != nil {
40
+ return err
41
+ }
42
+
43
+ err = json.Unmarshal(rawEntryValuesBytes, entries)
44
+ if err != nil {
45
+ return err
46
+ }
47
+
48
+ return nil
49
+ }
50
+
51
+ func (ri FileIndex) ListKeys(keys interface{}) error {
52
+ rawEntries, err := ri.readRawEntries()
53
+ if err != nil {
54
+ return err
55
+ }
56
+
57
+ keysElem := reflect.ValueOf(keys).Elem()
58
+
59
+ for _, rawEntry := range rawEntries {
60
+ key, err := ri.mapToStructFromSlice(rawEntry.Key, keys)
61
+ if err != nil {
62
+ return err
63
+ }
64
+
65
+ keysElem.Set(reflect.Append(keysElem, key))
66
+ }
67
+
68
+ return nil
69
+ }
70
+
71
+ func (ri FileIndex) Find(key interface{}, entry interface{}) error {
72
+ rawEntries, err := ri.readRawEntries()
73
+ if err != nil {
74
+ return err
75
+ }
76
+
77
+ rawKey, err := ri.structToMap(key)
78
+ if err != nil {
79
+ return err
80
+ }
81
+
82
+ for _, rawEntry := range rawEntries {
83
+ if reflect.DeepEqual(rawEntry.Key, rawKey) {
84
+ err := json.Unmarshal(rawEntry.Value, entry)
85
+ if err != nil {
86
+ return err
87
+ }
88
+
89
+ return nil
90
+ }
91
+ }
92
+
93
+ return ErrNotFound
94
+ }
95
+
96
+ func (ri FileIndex) Save(key interface{}, entry interface{}) error {
97
+ rawEntries, err := ri.readRawEntries()
98
+ if err != nil {
99
+ return err
100
+ }
101
+
102
+ rawKey, err := ri.structToMap(key)
103
+ if err != nil {
104
+ return err
105
+ }
106
+
107
+ rawValue, err := json.Marshal(entry)
108
+ if err != nil {
109
+ return err
110
+ }
111
+
112
+ foundI := -1
113
+
114
+ for i, rawEntry := range rawEntries {
115
+ if reflect.DeepEqual(rawEntry.Key, rawKey) {
116
+ foundI = i
117
+ break
118
+ }
119
+ }
120
+
121
+ if foundI >= 0 {
122
+ rawEntries[foundI].Value = rawValue
123
+ } else {
124
+ rawEntries = append(rawEntries, indexEntry{
125
+ Key: rawKey,
126
+ Value: rawValue,
127
+ })
128
+ }
129
+
130
+ err = ri.writeRawEntries(rawEntries)
131
+ if err != nil {
132
+ return err
133
+ }
134
+
135
+ return nil
136
+ }
137
+
138
+ func (ri FileIndex) Remove(key interface{}) error {
139
+ rawEntries, err := ri.readRawEntries()
140
+ if err != nil {
141
+ return err
142
+ }
143
+
144
+ rawKey, err := ri.structToMap(key)
145
+ if err != nil {
146
+ return err
147
+ }
148
+
149
+ for i, rawEntry := range rawEntries {
150
+ if reflect.DeepEqual(rawEntry.Key, rawKey) {
151
+ rawEntries = append(rawEntries[:i], rawEntries[i+1:]...)
152
+ break
153
+ }
154
+ }
155
+
156
+ err = ri.writeRawEntries(rawEntries)
157
+ if err != nil {
158
+ return err
159
+ }
160
+
161
+ return nil
162
+ }
163
+
164
+ func (ri FileIndex) readRawEntries() ([]indexEntry, error) {
165
+ var entries []indexEntry
166
+
167
+ if ri.fs.FileExists(ri.path) {
168
+ bytes, err := ri.fs.ReadFile(ri.path)
169
+ if err != nil {
170
+ return entries, bosherr.WrapError(err, "Reading index file %s", ri.path)
171
+ }
172
+
173
+ err = json.Unmarshal(bytes, &entries)
174
+ if err != nil {
175
+ return entries, bosherr.WrapError(err, "Unmarshalling index entries")
176
+ }
177
+ }
178
+
179
+ return entries, nil
180
+ }
181
+
182
+ func (ri FileIndex) writeRawEntries(entries []indexEntry) error {
183
+ bytes, err := json.Marshal(entries)
184
+ if err != nil {
185
+ return bosherr.WrapError(err, "Marshalling index entries")
186
+ }
187
+
188
+ err = ri.fs.WriteFile(ri.path, bytes)
189
+ if err != nil {
190
+ return bosherr.WrapError(err, "Writing index file %s", ri.path)
191
+ }
192
+
193
+ return nil
194
+ }
195
+
196
+ // structToMap extracts fields from a struct and populates a map
197
+ func (ri FileIndex) structToMap(s interface{}) (map[string]interface{}, error) {
198
+ res := map[string]interface{}{}
199
+ st := reflect.TypeOf(s)
200
+ stv := reflect.ValueOf(s)
201
+
202
+ if stv.Kind() != reflect.Struct {
203
+ return res, bosherr.New(
204
+ "Must be reflect.Struct: %#v (%#v)", stv, ri.kindToStr(stv.Kind()))
205
+ }
206
+
207
+ for i := 0; i < st.NumField(); i++ {
208
+ res[st.Field(i).Name] = stv.Field(i).Interface()
209
+ }
210
+
211
+ return res, nil
212
+ }
213
+
214
+ // mapToStruct returns new struct value with data from a map
215
+ func (ri FileIndex) mapToStruct(m map[string]interface{}, t interface{}) (reflect.Value, error) {
216
+ return ri.mapToNewStruct(m, reflect.ValueOf(t).Elem().Type())
217
+ }
218
+
219
+ // mapToStructFromSlice returns new struct value with data from a map
220
+ func (ri FileIndex) mapToStructFromSlice(m map[string]interface{}, t interface{}) (reflect.Value, error) {
221
+ slice := reflect.ValueOf(t).Elem()
222
+
223
+ if slice.Kind() != reflect.Slice {
224
+ return reflect.Value{}, bosherr.New(
225
+ "Must be reflect.Slice: %#v (%#v)",
226
+ slice, ri.kindToStr(slice.Kind()),
227
+ )
228
+ }
229
+
230
+ return ri.mapToNewStruct(m, slice.Type().Elem())
231
+ }
232
+
233
+ // mapToNewStruct returns new struct of type t with data from a map
234
+ func (ri FileIndex) mapToNewStruct(m map[string]interface{}, t reflect.Type) (reflect.Value, error) {
235
+ if t.Kind() != reflect.Struct {
236
+ return reflect.Value{}, bosherr.New(
237
+ "Must be reflect.Struct: %#v (%#v)",
238
+ t, ri.kindToStr(t.Kind()),
239
+ )
240
+ }
241
+
242
+ newStruct := reflect.New(t).Elem()
243
+
244
+ for k, v := range m {
245
+ f := newStruct.FieldByName(k)
246
+ if f.IsValid() && f.CanSet() {
247
+ // todo float64 -> int
248
+ // todo pointer values
249
+ // todo slices
250
+ f.Set(reflect.ValueOf(v))
251
+ }
252
+ }
253
+
254
+ return newStruct, nil
255
+ }
256
+
257
+ var kindToStrMap = map[reflect.Kind]string{
258
+ reflect.Invalid: "Invalid",
259
+ reflect.Bool: "Bool",
260
+ reflect.Int: "Int",
261
+ reflect.Int8: "Int8",
262
+ reflect.Int16: "Int16",
263
+ reflect.Int32: "Int32",
264
+ reflect.Int64: "Int64",
265
+ reflect.Uint: "Uint",
266
+ reflect.Uint8: "Uint8",
267
+ reflect.Uint16: "Uint16",
268
+ reflect.Uint32: "Uint32",
269
+ reflect.Uint64: "Uint64",
270
+ reflect.Uintptr: "Uintptr",
271
+ reflect.Float32: "Float32",
272
+ reflect.Float64: "Float64",
273
+ reflect.Complex64: "Complex64",
274
+ reflect.Complex128: "Complex128",
275
+ reflect.Array: "Array",
276
+ reflect.Chan: "Chan",
277
+ reflect.Func: "Func",
278
+ reflect.Interface: "Interface",
279
+ reflect.Map: "Map",
280
+ reflect.Ptr: "Ptr",
281
+ reflect.Slice: "Slice",
282
+ reflect.String: "String",
283
+ reflect.Struct: "Struct",
284
+ reflect.UnsafePointer: "UnsafePointer",
285
+ }
286
+
287
+ func (ri FileIndex) kindToStr(k reflect.Kind) string {
288
+ return kindToStrMap[k]
289
+ }
@@ -0,0 +1,296 @@
1
+ package index_test
2
+
3
+ import (
4
+ boshlog "bosh/logger"
5
+ boshsys "bosh/system"
6
+ . "github.com/onsi/ginkgo"
7
+ . "github.com/onsi/gomega"
8
+
9
+ . "boshprovisioner/index"
10
+ )
11
+
12
+ type Key struct {
13
+ Key string
14
+ }
15
+
16
+ type Value struct {
17
+ Name string
18
+ Count float64
19
+ }
20
+
21
+ type ArrayValue struct{ Names []string }
22
+
23
+ type StructValue struct{ Name Name }
24
+
25
+ type Name struct {
26
+ First string
27
+ Last string
28
+ }
29
+
30
+ var _ = Describe("FileIndex", func() {
31
+ var (
32
+ index FileIndex
33
+ )
34
+
35
+ BeforeEach(func() {
36
+ logger := boshlog.NewLogger(boshlog.LevelNone)
37
+ fs := boshsys.NewOsFileSystem(logger)
38
+
39
+ file, err := fs.TempFile("file-index")
40
+ Expect(err).ToNot(HaveOccurred())
41
+
42
+ err = fs.RemoveAll(file.Name())
43
+ Expect(err).ToNot(HaveOccurred())
44
+
45
+ index = NewFileIndex(file.Name(), fs)
46
+ })
47
+
48
+ Describe("Save/List", func() {
49
+ It("returns list of saved items", func() {
50
+ k1 := Key{Key: "key-1"}
51
+ v1 := Value{Name: "value-1", Count: 1}
52
+ err := index.Save(k1, v1)
53
+ Expect(err).ToNot(HaveOccurred())
54
+
55
+ k2 := Key{Key: "key-2"}
56
+ v2 := Value{Name: "value-2", Count: 2}
57
+ err = index.Save(k2, v2)
58
+ Expect(err).ToNot(HaveOccurred())
59
+
60
+ var values []Value
61
+
62
+ err = index.List(&values)
63
+ Expect(err).ToNot(HaveOccurred())
64
+ Expect(values).To(Equal([]Value{v1, v2}))
65
+ })
66
+
67
+ Describe("array values", func() {
68
+ It("returns list of saved items that have nil, 0, 1, and more items in an array", func() {
69
+ k1 := Key{Key: "key-1"}
70
+ v1 := ArrayValue{Names: []string{"name-1-1", "name-1-2"}} // multiple
71
+ err := index.Save(k1, v1)
72
+ Expect(err).ToNot(HaveOccurred())
73
+
74
+ k2 := Key{Key: "key-2"}
75
+ v2 := ArrayValue{Names: []string{"name-2-1"}} // single
76
+ err = index.Save(k2, v2)
77
+ Expect(err).ToNot(HaveOccurred())
78
+
79
+ k3 := Key{Key: "key-3"}
80
+ v3 := ArrayValue{Names: []string{}} // empty slice
81
+ err = index.Save(k3, v3)
82
+ Expect(err).ToNot(HaveOccurred())
83
+
84
+ k4 := Key{Key: "key-4"}
85
+ v4 := ArrayValue{} // nil
86
+ err = index.Save(k4, v4)
87
+ Expect(err).ToNot(HaveOccurred())
88
+
89
+ var values []ArrayValue
90
+
91
+ err = index.List(&values)
92
+ Expect(err).ToNot(HaveOccurred())
93
+ Expect(values).To(Equal([]ArrayValue{v1, v2, v3, v4}))
94
+ })
95
+ })
96
+
97
+ Describe("struct values", func() {
98
+ It("returns list of saved items that have nil or more item", func() {
99
+ k1 := Key{Key: "key-1"}
100
+ v1 := StructValue{Name: Name{First: "first-name-1", Last: "last-name-1"}} // struct
101
+ err := index.Save(k1, v1)
102
+ Expect(err).ToNot(HaveOccurred())
103
+
104
+ k2 := Key{Key: "key-2"}
105
+ v2 := StructValue{Name: Name{First: "first-name-1"}} // struct incomplete
106
+ err = index.Save(k2, v2)
107
+ Expect(err).ToNot(HaveOccurred())
108
+
109
+ k3 := Key{Key: "key-3"}
110
+ v3 := StructValue{} // zero value
111
+ err = index.Save(k3, v3)
112
+ Expect(err).ToNot(HaveOccurred())
113
+
114
+ var values []StructValue
115
+
116
+ err = index.List(&values)
117
+ Expect(err).ToNot(HaveOccurred())
118
+ Expect(values).To(Equal([]StructValue{v1, v2, v3}))
119
+ })
120
+ })
121
+ })
122
+
123
+ Describe("Save/ListKeys", func() {
124
+ It("returns list of saved keys", func() {
125
+ k1 := Key{Key: "key-1"}
126
+ v1 := Value{Name: "value-1", Count: 1}
127
+ err := index.Save(k1, v1)
128
+ Expect(err).ToNot(HaveOccurred())
129
+
130
+ k2 := Key{Key: "key-2"}
131
+ v2 := Value{Name: "value-2", Count: 2}
132
+ err = index.Save(k2, v2)
133
+ Expect(err).ToNot(HaveOccurred())
134
+
135
+ var keys []Key
136
+
137
+ err = index.ListKeys(&keys)
138
+ Expect(err).ToNot(HaveOccurred())
139
+ Expect(keys).To(Equal([]Key{k1, k2}))
140
+ })
141
+ })
142
+
143
+ Describe("Save/Find", func() {
144
+ It("returns true if item is found by key", func() {
145
+ k1 := Key{Key: "key-1"}
146
+ v1 := Value{Name: "value-1", Count: 1}
147
+ err := index.Save(k1, v1)
148
+ Expect(err).ToNot(HaveOccurred())
149
+
150
+ var value Value
151
+
152
+ err = index.Find(k1, &value)
153
+ Expect(err).ToNot(HaveOccurred())
154
+ Expect(err).ToNot(Equal(ErrNotFound))
155
+
156
+ Expect(value).To(Equal(v1))
157
+ })
158
+
159
+ It("returns false if item is not found by key", func() {
160
+ k1 := Key{Key: "key-1"}
161
+ v1 := Value{Name: "value-1", Count: 1}
162
+ err := index.Save(k1, v1)
163
+ Expect(err).ToNot(HaveOccurred())
164
+
165
+ var value Value
166
+
167
+ err = index.Find(Key{Key: "key-2"}, &value)
168
+ Expect(err).To(HaveOccurred())
169
+ Expect(err).To(Equal(ErrNotFound))
170
+
171
+ Expect(value).To(Equal(Value{}))
172
+ })
173
+
174
+ Describe("array values", func() {
175
+ It("returns true and correctly deserializes item with nil", func() {
176
+ k1 := Key{Key: "key-1"}
177
+ v1 := ArrayValue{} // nil
178
+ err := index.Save(k1, v1)
179
+ Expect(err).ToNot(HaveOccurred())
180
+
181
+ var value ArrayValue
182
+
183
+ err = index.Find(k1, &value)
184
+ Expect(err).ToNot(HaveOccurred())
185
+ Expect(err).ToNot(Equal(ErrNotFound))
186
+
187
+ Expect(value).To(Equal(v1))
188
+ })
189
+
190
+ It("returns true and correctly deserializes item with empty slice", func() {
191
+ k1 := Key{Key: "key-1"}
192
+ v1 := ArrayValue{Names: []string{}} // empty slice
193
+ err := index.Save(k1, v1)
194
+ Expect(err).ToNot(HaveOccurred())
195
+
196
+ var value ArrayValue
197
+
198
+ err = index.Find(k1, &value)
199
+ Expect(err).ToNot(HaveOccurred())
200
+ Expect(err).ToNot(Equal(ErrNotFound))
201
+
202
+ Expect(value).To(Equal(v1))
203
+ })
204
+
205
+ It("returns true and correctly deserializes item with multiple items", func() {
206
+ k1 := Key{Key: "key-1"}
207
+ v1 := ArrayValue{Names: []string{"name-1-1", "name-1-2"}} // multiple
208
+ err := index.Save(k1, v1)
209
+ Expect(err).ToNot(HaveOccurred())
210
+
211
+ var value ArrayValue
212
+
213
+ err = index.Find(k1, &value)
214
+ Expect(err).ToNot(HaveOccurred())
215
+ Expect(err).ToNot(Equal(ErrNotFound))
216
+
217
+ Expect(value).To(Equal(v1))
218
+ })
219
+ })
220
+
221
+ Describe("struct values", func() {
222
+ It("returns true and correctly deserializes item with zero value", func() {
223
+ k1 := Key{Key: "key-1"}
224
+ v1 := StructValue{} // zero value
225
+ err := index.Save(k1, v1)
226
+ Expect(err).ToNot(HaveOccurred())
227
+
228
+ var value StructValue
229
+
230
+ err = index.Find(k1, &value)
231
+ Expect(err).ToNot(HaveOccurred())
232
+ Expect(err).ToNot(Equal(ErrNotFound))
233
+
234
+ Expect(value).To(Equal(v1))
235
+ })
236
+
237
+ It("returns true and correctly deserializes item with filled struct", func() {
238
+ k1 := Key{Key: "key-1"}
239
+ v1 := StructValue{Name: Name{First: "first-name-1", Last: "last-name-1"}} // struct
240
+ err := index.Save(k1, v1)
241
+ Expect(err).ToNot(HaveOccurred())
242
+
243
+ var value StructValue
244
+
245
+ err = index.Find(k1, &value)
246
+ Expect(err).ToNot(HaveOccurred())
247
+ Expect(err).ToNot(Equal(ErrNotFound))
248
+
249
+ Expect(value).To(Equal(v1))
250
+ })
251
+ })
252
+ })
253
+
254
+ Describe("Save/Remove", func() {
255
+ var (
256
+ k1 Key
257
+ v1 Value
258
+ )
259
+
260
+ BeforeEach(func() {
261
+ k1 = Key{Key: "key-1"}
262
+ v1 = Value{Name: "value-1", Count: 1}
263
+ err := index.Save(k1, v1)
264
+ Expect(err).ToNot(HaveOccurred())
265
+ })
266
+
267
+ It("removes matching value if found", func() {
268
+ k2 := Key{Key: "key-2"}
269
+ v2 := Value{Name: "value-2", Count: 2}
270
+ err := index.Save(k2, v2)
271
+ Expect(err).ToNot(HaveOccurred())
272
+
273
+ err = index.Remove(k1)
274
+ Expect(err).ToNot(HaveOccurred())
275
+
276
+ var values []Value
277
+
278
+ err = index.List(&values)
279
+ Expect(err).ToNot(HaveOccurred())
280
+ Expect(values).To(Equal([]Value{v2}))
281
+ })
282
+
283
+ It("does not remove non-matching value if not found", func() {
284
+ k2 := Key{Key: "key-2"}
285
+
286
+ err := index.Remove(k2)
287
+ Expect(err).ToNot(HaveOccurred())
288
+
289
+ var values []Value
290
+
291
+ err = index.List(&values)
292
+ Expect(err).ToNot(HaveOccurred())
293
+ Expect(values).To(Equal([]Value{v1}))
294
+ })
295
+ })
296
+ })
@@ -0,0 +1,18 @@
1
+ package index
2
+
3
+ import (
4
+ "errors"
5
+ )
6
+
7
+ var (
8
+ ErrNotFound = errors.New("Record is not found")
9
+ )
10
+
11
+ type Index interface {
12
+ ListKeys(interface{}) error
13
+ List(interface{}) error
14
+
15
+ Find(interface{}, interface{}) error
16
+ Save(interface{}, interface{}) error
17
+ Remove(interface{}) error
18
+ }
@@ -0,0 +1,13 @@
1
+ package index_test
2
+
3
+ import (
4
+ . "github.com/onsi/ginkgo"
5
+ . "github.com/onsi/gomega"
6
+
7
+ "testing"
8
+ )
9
+
10
+ func TestIndex(t *testing.T) {
11
+ RegisterFailHandler(Fail)
12
+ RunSpecs(t, "Index Suite")
13
+ }