vayacondios-server 0.2.11 → 0.3.0
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.
- data/.gitignore +3 -1
- data/.travis.yml +2 -0
- data/Gemfile +15 -9
- data/LICENSE.md +2 -6
- data/Procfile +1 -1
- data/README.md +656 -111
- data/Rakefile +89 -6
- data/bin/vcd +10 -0
- data/bin/vcd-server +8 -0
- data/config/database.yml +6 -0
- data/config/spec.example.yml +18 -0
- data/config/vayacondios.example.yml +15 -0
- data/config/vcd-server.rb +37 -0
- data/examples/configuration.rb +56 -0
- data/examples/event_stream.rb +19 -0
- data/examples/simple.rb +61 -0
- data/features/event.feature +319 -0
- data/features/events.feature +208 -0
- data/features/stash.feature +840 -0
- data/features/stashes.feature +492 -0
- data/features/step_definitions/stash_steps.rb +113 -0
- data/features/stream.feature +30 -0
- data/features/support/em.rb +14 -0
- data/features/support/env.rb +13 -0
- data/lib/vayacondios/configuration.rb +63 -0
- data/lib/vayacondios/server/api.rb +126 -0
- data/lib/vayacondios/server/api_options.rb +56 -0
- data/lib/vayacondios/server/configuration.rb +23 -0
- data/lib/vayacondios/server/driver.rb +71 -0
- data/lib/vayacondios/server/drivers/mongo.rb +126 -0
- data/lib/vayacondios/server/handlers/document_handler.rb +81 -0
- data/lib/vayacondios/server/handlers/event_handler.rb +31 -26
- data/lib/vayacondios/server/handlers/events_handler.rb +31 -0
- data/lib/vayacondios/server/handlers/stash_handler.rb +69 -0
- data/lib/vayacondios/server/handlers/stashes_handler.rb +49 -0
- data/lib/vayacondios/server/handlers/stream_handler.rb +39 -0
- data/lib/vayacondios/server/models/document.rb +87 -0
- data/lib/vayacondios/server/models/event.rb +198 -0
- data/lib/vayacondios/server/models/stash.rb +100 -0
- data/lib/vayacondios/server.rb +35 -0
- data/lib/vayacondios-server.rb +19 -13
- data/lib/vayacondios.rb +22 -0
- data/pom.xml +124 -4
- data/spec/configuration_spec.rb +41 -0
- data/spec/server/api_options_spec.rb +32 -0
- data/spec/server/api_spec.rb +279 -0
- data/spec/server/configuration_spec.rb +27 -0
- data/spec/server/drivers/mongo_spec.rb +107 -0
- data/spec/server/handlers/event_handler_spec.rb +62 -0
- data/spec/server/handlers/events_handler_spec.rb +51 -0
- data/spec/server/handlers/stash_handler_spec.rb +68 -0
- data/spec/server/handlers/stashes_handler_spec.rb +50 -0
- data/spec/server/handlers/stream_handler_spec.rb +5 -0
- data/spec/server/models/document_spec.rb +9 -0
- data/spec/server/models/event_spec.rb +185 -0
- data/spec/server/models/stash_spec.rb +95 -0
- data/spec/spec_helper.rb +23 -3
- data/spec/support/database_helper.rb +42 -0
- data/spec/support/log_helper.rb +19 -0
- data/spec/support/shared_context_for_events.rb +22 -0
- data/spec/support/shared_context_for_stashes.rb +24 -0
- data/spec/support/shared_examples_for_handlers.rb +32 -0
- data/src/main/java/com/infochimps/vayacondios/BaseClient.java +342 -0
- data/src/main/java/com/infochimps/vayacondios/HTTPClient.java +426 -0
- data/src/main/java/com/infochimps/vayacondios/VayacondiosClient.java +487 -65
- data/src/main/java/com/infochimps/vayacondios/test/IntegrationTest.java +3 -0
- data/src/test/java/com/infochimps/vayacondios/BaseClientTest.java +50 -0
- data/src/test/java/com/infochimps/vayacondios/HTTPClientIT.java +267 -0
- data/vayacondios-server.gemspec +9 -9
- metadata +127 -122
- checksums.yaml +0 -15
- data/.rspec +0 -2
- data/.yardopts +0 -10
- data/Guardfile +0 -41
- data/app/http_shim.rb +0 -71
- data/bin/vcd.sh +0 -27
- data/config/http_shim.rb +0 -43
- data/config/vayacondios.example.yaml +0 -7
- data/config/vayacondios.yaml +0 -7
- data/examples/java/ItemSetTest.java +0 -76
- data/lib/tasks/publish.rake +0 -23
- data/lib/tasks/spec.rake +0 -11
- data/lib/tasks/yard.rake +0 -2
- data/lib/vayacondios/client/config.rb +0 -7
- data/lib/vayacondios/client/configliere.rb +0 -38
- data/lib/vayacondios/client/cube_client.rb +0 -39
- data/lib/vayacondios/client/http_client.rb +0 -49
- data/lib/vayacondios/client/itemset.rb +0 -130
- data/lib/vayacondios/client/legacy_switch.rb +0 -43
- data/lib/vayacondios/client/notifier.rb +0 -123
- data/lib/vayacondios/client/zabbix_client.rb +0 -148
- data/lib/vayacondios/legacy_switch.rb +0 -43
- data/lib/vayacondios/server/errors/bad_request.rb +0 -6
- data/lib/vayacondios/server/errors/not_found.rb +0 -6
- data/lib/vayacondios/server/handlers/config_handler.rb +0 -32
- data/lib/vayacondios/server/handlers/itemset_handler.rb +0 -60
- data/lib/vayacondios/server/legacy_switch.rb +0 -43
- data/lib/vayacondios/server/model/config_document.rb +0 -89
- data/lib/vayacondios/server/model/document.rb +0 -25
- data/lib/vayacondios/server/model/event_document.rb +0 -94
- data/lib/vayacondios/server/model/itemset_document.rb +0 -126
- data/lib/vayacondios/server/rack/extract_methods.rb +0 -35
- data/lib/vayacondios/server/rack/jsonize.rb +0 -43
- data/lib/vayacondios/server/rack/params.rb +0 -50
- data/lib/vayacondios/server/rack/path.rb +0 -23
- data/lib/vayacondios/server/rack/path_validation.rb +0 -22
- data/lib/vayacondios/version.rb +0 -3
- data/lib/vayacondios-client.rb +0 -22
- data/scripts/hadoop_monitor/configurable.rb +0 -66
- data/scripts/hadoop_monitor/hadoop_attempt_scraper.rb +0 -45
- data/scripts/hadoop_monitor/hadoop_client.rb +0 -273
- data/scripts/hadoop_monitor/hadoop_monitor.rb +0 -101
- data/scripts/hadoop_monitor/hadoopable.rb +0 -65
- data/scripts/hadoop_monitor/machine_monitor.rb +0 -115
- data/scripts/s3_cataloger/buckets +0 -33
- data/scripts/s3_cataloger/foreach_bucket +0 -88
- data/scripts/s3_cataloger/parse_ls.py +0 -391
- data/spec/client/itemset_legacy_spec.rb +0 -55
- data/spec/client/itemset_spec.rb +0 -60
- data/spec/client/notifier_spec.rb +0 -120
- data/spec/server/config_spec.rb +0 -113
- data/spec/server/event_spec.rb +0 -103
- data/spec/server/itemset_legacy_spec.rb +0 -320
- data/spec/server/itemset_spec.rb +0 -317
- data/spec/server/rack/extract_methods_spec.rb +0 -60
- data/spec/server/rack/path_spec.rb +0 -36
- data/spec/server/rack/path_validation_spec.rb +0 -22
- data/spec/server/server_spec.rb +0 -20
- data/spec/support/mongo_cleaner.rb +0 -32
- data/src/main/java/ItemSetTest.java +0 -76
- data/src/main/java/com/infochimps/util/CurrentClass.java +0 -26
- data/src/main/java/com/infochimps/util/DebugUtil.java +0 -38
- data/src/main/java/com/infochimps/util/HttpHelper.java +0 -181
- data/src/main/java/com/infochimps/vayacondios/ItemSets.java +0 -373
- data/src/main/java/com/infochimps/vayacondios/LinkToVCD.java +0 -18
- data/src/main/java/com/infochimps/vayacondios/MemoryVCDShim.java +0 -84
- data/src/main/java/com/infochimps/vayacondios/Organization.java +0 -62
- data/src/main/java/com/infochimps/vayacondios/PathBuilder.java +0 -13
- data/src/main/java/com/infochimps/vayacondios/StandardVCDLink.java +0 -218
- data/src/main/java/com/infochimps/vayacondios/VCDIntegrationTest.java +0 -108
- data/src/test/java/com/infochimps/vayacondios/TestVayacondiosInMemory.java +0 -78
- data/vayacondios-client.gemspec +0 -25
data/README.md
CHANGED
@@ -1,189 +1,734 @@
|
|
1
|
-
#
|
1
|
+
# Vayacondios
|
2
2
|
|
3
|
-
|
3
|
+
Vayacondios is a server-client program designed to make it simple to
|
4
|
+
collect and centralize information and metrics from a large number of
|
5
|
+
disparate sources from multiple application domains.
|
4
6
|
|
5
|
-
|
6
|
-
Vayacondios. Until this document is updated, please see the specs for
|
7
|
-
the Ruby code or compile the javadocs for the Java API.
|
7
|
+
Vayacondios has the following design goals:
|
8
8
|
|
9
|
-
|
9
|
+
The client is simple enough to use in a shell script and the server is
|
10
|
+
performant enough to support ubiquitous use across a large
|
11
|
+
installation with many clients.
|
10
12
|
|
11
|
-
|
13
|
+
* *Decentralized* -- Any client can dispatch stashes or events from anywhere
|
14
|
+
* *Dynamic* -- No data types or schemas need to be created in advance
|
15
|
+
* *Ubiquitous* -- Clients require minimal dependencies because the API is simple to use and access
|
16
|
+
* *Simple* -- Clients can write data in whatever way is natural for them
|
17
|
+
* *Scalable* -- Server and storage can be scaled horizontally to allow for ever-increasing loads
|
18
|
+
* *Fast* -- No client should have to worry that sending data to Vayacondios will affect its performance
|
12
19
|
|
13
|
-
|
20
|
+
The basic objects of Vayacondios are **stash** and the **event**:
|
14
21
|
|
15
|
-
*
|
16
|
-
*
|
17
|
-
* *count* -- 'I just served a 404 response code'
|
18
|
-
* *fact* -- 'here's everything to know about the coffee machine: `{"cups_remaining":3,"uptime":127,room:"hyrule"}`' -- an arbitrary JSON hash
|
22
|
+
* a **stash** is an "object", a "configuration", or "setting" designed to be shared among many services
|
23
|
+
* an **event** is a "fact", "measurement", or "metric" announced by an arbitrary service, possibly related to some stash
|
19
24
|
|
20
|
-
|
25
|
+
Stashes and events are each documents which can contain arbitrary
|
26
|
+
JSON-serializable data: hashes/maps/dictionarys, arrays/lists,
|
27
|
+
strings, numbers, floats, null, &c.
|
21
28
|
|
22
|
-
|
23
|
-
|
24
|
-
* *Fast* -- UDP clients are non-blocking and happily dispatch thousands of requests per second.
|
25
|
-
* *Minimal Dependency* -- Ruby clients uses nothing outside of the standard libraries.
|
26
|
-
* *Ubiquitous* -- Can send facts from the shell (with nothing besides `curl`)
|
27
|
-
* *writes are simple, reads are clever* -- A writer gets to chuck things in according to its conception of the world.
|
29
|
+
The client and server communicate over a RESTful, HTTP-based API which
|
30
|
+
speaks JSON.
|
28
31
|
|
29
32
|
See also [Coda Hale's metrics](https://github.com/codahale/metrics/).
|
30
33
|
|
31
|
-
|
34
|
+
<a name="architecture" />
|
35
|
+
## Architecture
|
32
36
|
|
33
|
-
|
34
|
-
|
37
|
+
<a name="architecture-database" />
|
38
|
+
### Database
|
35
39
|
|
36
|
-
|
40
|
+
Vayacondios stores all its data in a database. Access to the database
|
41
|
+
within Vayacondios is strictly contained within model classes within
|
42
|
+
`lib/vayacondios/server/models`. This is so that the backend database
|
43
|
+
can one day be changed easily without affecting the rest of the
|
44
|
+
application.
|
37
45
|
|
38
|
-
|
46
|
+
[MongoDB](http://www.mongodb.org/) is currently the only supported
|
47
|
+
database. MongoDB is a natural choice because it exposes atomic query
|
48
|
+
primitives which map very closely to the operations exposed by the
|
49
|
+
Vayacondios API.
|
39
50
|
|
40
|
-
|
41
|
-
|
42
|
-
* `topic` is the materialized path we referred to that specifies its unique location.
|
43
|
-
* `format` will change the serialization format for the data. Only JSON is currently supported.
|
51
|
+
<a name="architecture-server" />
|
52
|
+
### Server
|
44
53
|
|
54
|
+
The Vayacondios server process is a
|
55
|
+
[Goliath](https://github.com/postrank-labs/goliath) web server which
|
56
|
+
implements the Vayacondios API over HTTP using JSON.
|
57
|
+
|
58
|
+
A single server process can easily handle hundreds of client requests
|
59
|
+
per second. Multiple Vayacondios servers can easily be deployed
|
60
|
+
behind a load-balancer.
|
61
|
+
|
62
|
+
Each running server process reads and writes all its data in a single
|
63
|
+
MongoDB database specified at runtime.
|
64
|
+
|
65
|
+
<a name="architecture-client" />
|
66
|
+
### Client
|
67
|
+
|
68
|
+
Clients communicate with the Vayacondios server via the HTTP API it
|
69
|
+
exposes. This makes it extremely simply for applications in any
|
70
|
+
language to communicate with the server.
|
71
|
+
|
72
|
+
Vayacondios comes with several clients:
|
73
|
+
|
74
|
+
* a Ruby-language client (`Vayacondios::HttpClient`)
|
75
|
+
* a Java-language client (`com.infochimps.vayacondios.HTTPClient`)
|
76
|
+
* a command-line client (the `vcd` program)
|
77
|
+
|
78
|
+
The Ruby-language client and the command-line client are bundled with
|
79
|
+
the `vayacondios-client` Ruby gem. The Java-language client is part
|
80
|
+
of the `com.infochimps.vayacondios` package.
|
81
|
+
|
82
|
+
<a name="datamodel" />
|
83
|
+
## Data Model
|
84
|
+
|
85
|
+
Vayacondios uses a two-level hierarchical data model to organize
|
86
|
+
events and stashes.
|
87
|
+
|
88
|
+
The top-level is the **organization**. Data from multiple
|
89
|
+
organizations is stored together but accessed separately by a running
|
90
|
+
Vayacondios server. An organization could be the name of a user,
|
91
|
+
workgroup, application, or service using Vayacondios.
|
92
|
+
|
93
|
+
The next level is the **topic**. Each topic within Vayacondios has a
|
94
|
+
single stash and can have multiple events. An "object" like a server,
|
95
|
+
a database, an application, a service, or a user maps to the concept
|
96
|
+
of "topic".
|
97
|
+
|
98
|
+
Topics and organizations are strings which can only contain letters,
|
99
|
+
digits, underscores, periods, and hypens, though periods cannot be the
|
100
|
+
first or last character. Organizations cannot begin with the string
|
101
|
+
`system.`.
|
102
|
+
|
103
|
+
<a name="datamodel-events" />
|
45
104
|
### Events
|
46
105
|
|
47
|
-
Events
|
106
|
+
Events belong to a topic within an organization. Each event
|
107
|
+
additionally has
|
108
|
+
|
109
|
+
* an ID which is automatically set by the server to a random, unique value if none is provided when the event is announced. Provided IDs cannot contain periods or dollar signs.
|
110
|
+
* a timestamp which is automatically set by the server to the current UTC time if none is provided when the event is announced. Provided timestamps will attempt to be parsed either from a string or from an integer UNIX timestamp.
|
111
|
+
* arbitrary key/value data. Keys cannot contain periods or dollar signs.
|
112
|
+
|
113
|
+
Events are used for storing facts, measurements, metrics, errors,
|
114
|
+
occurrences, &c. If you anticipate wanting to see a time series or a
|
115
|
+
histogram of a certain kind of data then you should consider writing
|
116
|
+
that data into Vayacondios as events on some topic.
|
117
|
+
|
118
|
+
Events are stored in MongoDB in a collection named after their
|
119
|
+
organization and topic: an event on the `ci` topic for the `example`
|
120
|
+
organization would be stored in the MongoDB collection
|
121
|
+
`example.ci.events`. The ID of the event, whether auto-generated by
|
122
|
+
the server or specified by the client, will be used as the `_id` field
|
123
|
+
of the resulting document within this collection.
|
48
124
|
|
125
|
+
Here are some examples of data that it would make sense to store as
|
126
|
+
events (in JSON format):
|
49
127
|
|
50
|
-
|
128
|
+
* the output of a build from a CI system might be written to topic `ci`
|
51
129
|
```
|
52
130
|
{
|
53
|
-
"
|
54
|
-
"
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
"
|
131
|
+
"environment": "Jenkins CI v. 1.519",
|
132
|
+
"project": {
|
133
|
+
"name": "website",
|
134
|
+
"version": "0b4d99ded50a19e495d2472477bbb0784d8a18d8",
|
135
|
+
"url": "https://github.com/mycompany/website.git",
|
136
|
+
},
|
137
|
+
"build": {
|
138
|
+
"time": 182,
|
139
|
+
"status": "success"
|
140
|
+
},
|
141
|
+
"test": {
|
142
|
+
"time": 97,
|
143
|
+
"ran": 102,
|
144
|
+
"passed": 102,
|
145
|
+
"failed": 0
|
146
|
+
}
|
147
|
+
}
|
148
|
+
```
|
149
|
+
* an intrusion event picked up by the firewall might be written to topic `firewall.intrusions`
|
150
|
+
```
|
151
|
+
{
|
152
|
+
"ip": "74.210.29.117",
|
153
|
+
"port": 22,
|
154
|
+
"type": "ssh",
|
155
|
+
"reason": "blacklisted"
|
60
156
|
}
|
61
157
|
```
|
158
|
+
* some performance statistics for a running server might be written topic `phoenix.servers.webserver-16`
|
159
|
+
```
|
160
|
+
{
|
161
|
+
"data_center": "Phoenix",
|
162
|
+
"rack": "14",
|
163
|
+
"server": "webserver-16",
|
164
|
+
"cpu": {
|
165
|
+
user: 3.17,
|
166
|
+
nice: 0.01,
|
167
|
+
system: 0.27,
|
168
|
+
iowait: 0.18,
|
169
|
+
steal: 0.00,
|
170
|
+
idle: 96.38
|
171
|
+
},
|
172
|
+
"mem": {
|
173
|
+
"total": 12304632,
|
174
|
+
"used": 10335900,
|
175
|
+
"free": 1968732
|
176
|
+
},
|
177
|
+
"net": {
|
178
|
+
"out": 2.25,
|
179
|
+
"in": 10.28,
|
180
|
+
},
|
181
|
+
"disk": {
|
182
|
+
"write": 16.182,
|
183
|
+
"read": 0.11
|
184
|
+
}
|
185
|
+
```
|
186
|
+
|
187
|
+
<a name="datamodel-stashes" />
|
188
|
+
### Stashes
|
189
|
+
|
190
|
+
Stashes belong to a topic within an organization. Each stash
|
191
|
+
additionally has arbitrary key/value data that it can store. Keys
|
192
|
+
cannot contain dollar signs or periods.
|
193
|
+
|
194
|
+
Stashes are used for storing objects, configuration, settings, &c. If
|
195
|
+
you anticipate wanting to lookup a value by name then you should
|
196
|
+
consider writing that data into Vayacondios as (or within) a stash on
|
197
|
+
some topic.
|
198
|
+
|
199
|
+
The names of top-level keys within a stash can be used as the "ID"
|
200
|
+
when retrieving/setting/deleting values via the API.
|
201
|
+
|
202
|
+
Stashes are stored in MongoDB in a collection named after their
|
203
|
+
organization: a stash for the `example` organization would be stored
|
204
|
+
in the MongoDB collection `example.stash`. The topic of the stash
|
205
|
+
will be used as the `_id` field of the resulting document within this
|
206
|
+
collection.
|
207
|
+
|
208
|
+
Here are some examples of data that it would make sense to store as
|
209
|
+
stashes (in JSON format):
|
210
|
+
|
211
|
+
* a collection of projects to run through a CI system might be stored on topic `ci`
|
212
|
+
```
|
213
|
+
{
|
214
|
+
"projects": {
|
215
|
+
{
|
216
|
+
"name": "website",
|
217
|
+
"url": "https://github.com/mycompany/website.git",
|
218
|
+
},
|
219
|
+
{
|
220
|
+
"name": "client_tool",
|
221
|
+
"url": "https://github.com/mycompany/client_tool.git",
|
222
|
+
},
|
223
|
+
...
|
224
|
+
}
|
225
|
+
}
|
226
|
+
```
|
227
|
+
* firewall settings might be stored on topic `firewall`
|
228
|
+
```
|
229
|
+
{
|
230
|
+
"firewall": {
|
231
|
+
"rules": [
|
232
|
+
{
|
233
|
+
"range": "0.0.0.0",
|
234
|
+
"port": 80,
|
235
|
+
"protocol": "tcp"
|
236
|
+
},
|
237
|
+
{
|
238
|
+
"range": "10.0.0.0",
|
239
|
+
"port"; 22,
|
240
|
+
"protocol": "ssh"
|
241
|
+
}
|
242
|
+
]
|
243
|
+
}
|
244
|
+
}
|
245
|
+
```
|
246
|
+
* a mapping of servers within some data center might be stored on topic `data_centers.phoenix`
|
247
|
+
```
|
248
|
+
{
|
249
|
+
"name": "PHX",
|
250
|
+
"location": "Phoenix, AZ",
|
251
|
+
"servers": [
|
252
|
+
"webserver-0",
|
253
|
+
"webserver-1",
|
254
|
+
"webserver-2",
|
255
|
+
...
|
256
|
+
]
|
257
|
+
}
|
258
|
+
```
|
259
|
+
|
260
|
+
<a name="installation" />
|
261
|
+
## Installation & Configuration
|
262
|
+
|
263
|
+
<a name="installation-database" />
|
264
|
+
### Database
|
265
|
+
|
266
|
+
Vayacondios server depends on a database to store all its data.
|
267
|
+
Currently, only MongoDB is supported: here are some
|
268
|
+
[installation instructions](http://docs.mongodb.org/manual/installation/).
|
269
|
+
|
270
|
+
<a name="installation-server" />
|
271
|
+
### Server
|
272
|
+
|
273
|
+
Vayacondios server is distributed via Rubygems:
|
274
|
+
|
275
|
+
```
|
276
|
+
$ sudo gem install vayacondios-server
|
277
|
+
```
|
278
|
+
|
279
|
+
Once installed, you can launch a copy of the server from the
|
280
|
+
command-line running locally on port 9000:
|
281
|
+
```
|
282
|
+
$ vcd-server --verbose --stdout
|
283
|
+
```
|
284
|
+
|
285
|
+
Ports, logging, the location of MongoDB, and much more can be
|
286
|
+
configured via command-line options. Try `vcd-server --help` for more
|
287
|
+
details.
|
288
|
+
|
289
|
+
<a name="installation-client" />
|
290
|
+
### Client
|
291
|
+
|
292
|
+
The server exposes its API via HTTP so all sorts of clients can talk
|
293
|
+
to Vayacondios server. Most simply, a command like
|
294
|
+
|
295
|
+
```
|
296
|
+
$ curl -X POST http://localhost:9000/v2/my_organization/event/some_topic -d '{"event": "data"}'
|
297
|
+
```
|
298
|
+
|
299
|
+
will work "right out of the box".
|
300
|
+
|
301
|
+
You can also install some pre-written clients that are aware of the
|
302
|
+
Vayacondios API.
|
303
|
+
|
304
|
+
<a name="installation-client-cli" />
|
305
|
+
#### Command-Line
|
306
|
+
|
307
|
+
The `vcd` command-line client is installed via Rubygems:
|
308
|
+
|
309
|
+
```
|
310
|
+
$ sudo gem install vayacondios-client
|
311
|
+
```
|
312
|
+
|
313
|
+
You can now run the `vcd` program. The equivalent to the above `curl`
|
314
|
+
command would be
|
315
|
+
|
316
|
+
```
|
317
|
+
$ vcd announce 'some_topic' '{"event": "data"}'
|
318
|
+
```
|
319
|
+
|
320
|
+
The `vcd` program looks for its configuration (where is the
|
321
|
+
Vayacondios server? what organization am I in?) in the files
|
322
|
+
`/etc/vayacondios/vayacondios.yml` and `~/.vayacondios.yml`. The
|
323
|
+
following can be put in either location to customize the behavior of
|
324
|
+
`vcd` for a given server or user.
|
325
|
+
|
326
|
+
```yml
|
327
|
+
---
|
328
|
+
host: vcd.example.com
|
329
|
+
port: 9000
|
330
|
+
organization: my_company
|
331
|
+
```
|
332
|
+
|
333
|
+
<a name="installation-client-ruby" />
|
334
|
+
#### Ruby Client
|
335
|
+
|
336
|
+
A Ruby client is also avialable via Rubygems:
|
337
|
+
|
338
|
+
```
|
339
|
+
$ sudo gem install vayacondios-client
|
340
|
+
```
|
341
|
+
|
342
|
+
You can now use the `Vayacondios::HttpClient` class in your code:
|
343
|
+
|
344
|
+
```ruby
|
345
|
+
require 'vayacondios-client'
|
346
|
+
client = Vayacondios::HttpClient.new(organization: 'my_company')
|
347
|
+
client.announce('some_topic', foo: 'bar')
|
348
|
+
```
|
349
|
+
|
350
|
+
The Ruby client exposes several API requests as named methods (like
|
351
|
+
`announce` above, which maps to a <a
|
352
|
+
href="#api-events-announce">announce event</a> API endpoint).
|
353
|
+
|
354
|
+
<a name="installation-client-java" />
|
355
|
+
#### Java Client
|
356
|
+
|
357
|
+
A Java client is also available. Put the following into your
|
358
|
+
`pom.xml`:
|
359
|
+
|
360
|
+
```xml
|
361
|
+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
362
|
+
...
|
363
|
+
<repositories>
|
364
|
+
...
|
365
|
+
<repository>
|
366
|
+
<id>infochimps.releases</id>
|
367
|
+
<name>Infochimps Internal Repository</name>
|
368
|
+
<url>https://s3.amazonaws.com/artifacts.chimpy.us/maven-s3p/releases</url>
|
369
|
+
</repository>
|
370
|
+
...
|
371
|
+
</repositories>
|
372
|
+
...
|
373
|
+
<dependencies>
|
374
|
+
...
|
375
|
+
<dependency>
|
376
|
+
<groupId>com.infochimps</groupId>
|
377
|
+
<artifactId>vayacondios</artifactId>
|
378
|
+
<version>2.0.0</version>
|
379
|
+
</dependency>
|
380
|
+
...
|
381
|
+
</dependencies>
|
382
|
+
...
|
383
|
+
</project>
|
384
|
+
```
|
385
|
+
|
386
|
+
You can now use the `com.infochimps.vayacondios.HTTPClient` class in
|
387
|
+
your code:
|
388
|
+
|
389
|
+
```java
|
390
|
+
import com.infochimps.vayacondios.VayacondiosClient;
|
391
|
+
import com.infochimps.vayacondios.HTTPClient;
|
392
|
+
|
393
|
+
class public HelloVayacondios {
|
394
|
+
public static void main(String[] args) throws Exception {
|
395
|
+
VayacondiosClient client = new HTTPClient("my_organization");
|
396
|
+
Map event = new HashMap();
|
397
|
+
event.put("foo", "bar");
|
398
|
+
client.announce("my_topic", event);
|
399
|
+
Thread.sleep(50) // ensures async HTTP request finishes
|
400
|
+
client.close();
|
401
|
+
}
|
402
|
+
}
|
403
|
+
```
|
404
|
+
|
405
|
+
The Java client exposes several API requests as named methods (like
|
406
|
+
`announce` above, which maps to a <a
|
407
|
+
href="#api-events-announce">announce event</a> API endpoint).
|
408
|
+
|
409
|
+
<a name="api" />
|
410
|
+
## API (v2)
|
411
|
+
|
412
|
+
All HTTP endpoints defined by the Vayacondios server API share a
|
413
|
+
common structure: `/:version/:organization/:type/[:topic]/[:id]/...`
|
414
|
+
|
415
|
+
| Parameter | Required | Definition | Examples |
|
416
|
+
| ------------ | -------- | ------------------------------------- | ------------------------------------- |
|
417
|
+
| version | required | Vayacondios API version | v1, v2 (current) |
|
418
|
+
| organization | required | Name of organization, service, or app | `security`, `accounting`, `customerX` |
|
419
|
+
| type | required | Request type | `event`, `stash`, `events`, `stashes` |
|
420
|
+
| topic | varies | Topic for event or stash | `firewall`, `servers.webserver-3` |
|
421
|
+
| id | varies | ID of event or field within stash | `cpu`, `liua38923u2389f` |
|
422
|
+
|
423
|
+
The `version`, `organization`, and `type` parameters are always
|
424
|
+
required. Other parameters are required depending on the endpoint.
|
425
|
+
|
426
|
+
Vayacondios server only listens for a single value of the top-level
|
427
|
+
`version` parameter (curently: `v2`). A frontend webserver (Apache,
|
428
|
+
nginx, &c.) can be used to split traffic to backend Vayacondios
|
429
|
+
servers running different versions of the Vayacondios API by routing
|
430
|
+
based on this parameter.
|
431
|
+
|
432
|
+
The `type` parameter is fixed and defines the type of a Vayacondios
|
433
|
+
request: `event`, `stash`, `events`, or `stashes`.
|
434
|
+
|
435
|
+
All other parameters are completely free for clients to specify under
|
436
|
+
the following constraints:
|
437
|
+
|
438
|
+
* the `organization` parameter can only contain letters, digits,
|
439
|
+
hyphens, and underscores and it must begin with a letter
|
440
|
+
|
441
|
+
* the `topic` parameter can only contain letters, digits, hyphens,
|
442
|
+
underscores, and periods and it cannot start or end with a period
|
443
|
+
|
444
|
+
* the `id` parameter cannot contain the dollar sign or period
|
445
|
+
|
446
|
+
The `type`, `organization`, `topic`, and `id` parameter together
|
447
|
+
constitue the *vayacondios route*.
|
448
|
+
|
449
|
+
The *document* is the request body sent to the server with a given
|
450
|
+
request. Requests to Vayacondios should have JSON-encoded bodies but
|
451
|
+
the body can be any JSON datatype: Hash, Array, String, Integer,
|
452
|
+
Float, Boolean, or `null`.
|
453
|
+
|
454
|
+
The *response* is the JSON-encoded response body sent back to the
|
455
|
+
client from the server. If an error occurred, in addition to the
|
456
|
+
appropriate HTTP response code, the response will be a Hash containing
|
457
|
+
the key `error` with a message detailing the error.
|
458
|
+
|
459
|
+
In the case of a record which is not found, the response may be empty
|
460
|
+
but the HTTP response code will be 404.
|
461
|
+
|
462
|
+
In the case of a successful request, the response code will be 200 and
|
463
|
+
the response body will the requested/written object.
|
464
|
+
|
465
|
+
<a name="api-events" />
|
466
|
+
### Events
|
467
|
+
|
468
|
+
A topic within an organization can have many events.
|
469
|
+
|
470
|
+
Events are Hash-like data structures which have an associated
|
471
|
+
timestamp and ID.
|
472
|
+
|
473
|
+
Events can be announced, retrieved, and searched. Events cannot be
|
474
|
+
updated or deleted, though announcing an event with the same ID as an
|
475
|
+
existing event overwrites the existing event.
|
476
|
+
|
477
|
+
<a name="api-events-announce" />
|
478
|
+
#### Announce a new event
|
479
|
+
|
480
|
+
An event can be created without an ID. The server will generate a
|
481
|
+
random, unique ID and include it with the event in the response. This
|
482
|
+
is the most common way to write an event. If you don't intend to ever
|
483
|
+
retrieve this specific event (as opposed to searching across events)
|
484
|
+
then this is the right choice.
|
485
|
+
|
486
|
+
Events can also be created with an explicit ID. This is less common
|
487
|
+
but can be useful if your events naturally contain a unique
|
488
|
+
identifier.
|
489
|
+
|
490
|
+
| Method | Path | Request | Response | Action |
|
491
|
+
| ------ | ---------------------------------- | ------- | -------- | ----------------------------------------------- |
|
492
|
+
| POST | /v2/:organization/event/:topic | Hash | Hash | Stores a new event with an auto-generated ID |
|
493
|
+
| POST | /v2/:organization/event/:topic/:id | Hash | Hash | Stores/overwrites a new event with the given ID |
|
494
|
+
|
495
|
+
All requests to announce a new event accept a Hash-like request body.
|
496
|
+
Key/value pairs in this request body constitute the body of the event.
|
497
|
+
The following parameters have special meaning:
|
498
|
+
|
499
|
+
| Parameter | Description | Default | Example Values |
|
500
|
+
| --------- | ------------------------------ | ------------ | ---------------------------------------- |
|
501
|
+
| time | Set the timestamp of the event | current time |`2013-06-20 16:20:48 -0500`, `1371763237` |
|
502
|
+
|
503
|
+
The response body will contain a Hash that is the original request
|
504
|
+
Hash but with the (possibly auto-generated) ID and timestamp included.
|
505
|
+
|
506
|
+
<a name="api-events-get" />
|
507
|
+
#### Get an existing event
|
508
|
+
|
509
|
+
Events can be retrieved if their ID is known.
|
510
|
+
|
511
|
+
| Method | Path | Request | Response | Action |
|
512
|
+
| ------ | ---------------------------------- | ------- | -------- | ----------------------------------------------- |
|
513
|
+
| GET | /v2/:organization/event/:topic/:id | N/A | Hash | Retrieve an existing event given its ID |
|
514
|
+
|
515
|
+
The response will contain the event Hash if found or will be empty if
|
516
|
+
not.
|
517
|
+
|
518
|
+
<a name="api-events-search" />
|
519
|
+
#### Search for events
|
520
|
+
|
521
|
+
You can search for events matching a query.
|
522
|
+
|
523
|
+
| Method | Path | Request | Response | Action |
|
524
|
+
| ------ | ------------------------------- | ------- | ----------- | ----------------------------------------------- |
|
525
|
+
| GET | /v2/:organization/events/:topic | Hash | Array<Hash> | Search for events on the given topic. |
|
526
|
+
|
527
|
+
The default behavior (which will occur with an empty request body) is
|
528
|
+
to return the most recent 50 events on the given `topic` sorted in
|
529
|
+
descending order by their timestamps.
|
530
|
+
|
531
|
+
Each key in the query body will be interpeted as a condition that the
|
532
|
+
data of each event must match in order to be returned. Keys with
|
533
|
+
periods are interpreted as nested fields. The following parameters
|
534
|
+
have special meaning and can be used to adjust the time window, number
|
535
|
+
of returned events, the sort behavior, and the fields within each
|
536
|
+
event to return:
|
62
537
|
|
63
|
-
|
538
|
+
| Parameter | Description | Default | Example Values |
|
539
|
+
| --------- | -------------------------------------------- | -------------------- | ------------------------------------------------------ |
|
540
|
+
| from | Occurred after this time | 1 hour ago | `2013-06-20 Thu 00:00:00 -0500`, 1371704400 |
|
541
|
+
| upto | Occurred before this time | current time | `2013-06-20 Thu 23:59:59 -0500`, 1371790799 |
|
542
|
+
| limit | Return up to this many events | 50 | 100, 200 |
|
543
|
+
| fields | Return only these fields from the event body | all fields | `["account_id", "ip_address"]` |
|
544
|
+
| sort | Sort returned events by this field | descending by time | `["time", "ascending"]`, `["ip_address", "ascending"]` |
|
545
|
+
| id | Regular expression search on event ID | N/A | `sensor-data-.*`, `2013-06-20-.*` |
|
64
546
|
|
65
|
-
The
|
547
|
+
The response will be an Array of the matching events, possibly an
|
548
|
+
empty Array if no events were found.
|
66
549
|
|
67
|
-
|
68
|
-
|
69
|
-
* `_path` -- `name.spaced.path.fact_name`, omits the collection part
|
550
|
+
<a name="api-stashes" />
|
551
|
+
### Stashes
|
70
552
|
|
553
|
+
A topic within an organization can have a stash.
|
71
554
|
|
72
|
-
|
555
|
+
Stashes are Hash-like data structures. Each key/value pair with the
|
556
|
+
stash can be accessed directly by using the name of its key as the ID
|
557
|
+
in requests.
|
73
558
|
|
74
|
-
|
75
|
-
* count
|
76
|
-
* timing
|
77
|
-
* fact
|
559
|
+
Stashes can be set, merged, retrieved, searched, and destroyed.
|
78
560
|
|
79
|
-
|
561
|
+
<a name="api-stashes-set" />
|
562
|
+
#### Set a value
|
80
563
|
|
81
|
-
|
564
|
+
You can set a value for a stash or one of the fields within a stash.
|
565
|
+
Your value will override whatever value is currently stored for that
|
566
|
+
stash or for that ID within the stash.
|
82
567
|
|
83
|
-
|
568
|
+
| Method | Path | Request | Response | Action |
|
569
|
+
| ------ | ---------------------------------- | ------- | -------- | ---------------------------------------------------------- |
|
570
|
+
| POST | /v2/:organization/stash/:topic | Hash | Hash | Overwrites the stash with the given topic. |
|
571
|
+
| POST | /v2/:organization/stash/:topic/:id | varies | varies | Overwrites the ID field of the stash with the given topic. |
|
84
572
|
|
85
|
-
|
573
|
+
When setting the stash itself, your value must be Hash-like. When
|
574
|
+
setting an ID within a stash, your value can have any datatype.
|
86
575
|
|
87
|
-
|
576
|
+
The response for setting a stash will be the (Hash-like) stash you
|
577
|
+
just set. When setting an ID within a stash, the response will be of
|
578
|
+
the same datatype as the request.
|
88
579
|
|
89
|
-
|
580
|
+
#### Merge a value
|
90
581
|
|
91
|
-
|
582
|
+
You can merge a value for a stash or one of the fields within a stash.
|
92
583
|
|
93
|
-
|
584
|
+
| Method | Path | Request | Response | Action |
|
585
|
+
| ------ | ---------------------------------- | ------- | -------- | ----------------------------------------------------------- |
|
586
|
+
| PUT | /v2/:organization/stash/:topic | Hash | Hash | Merges into the stash with the given topic. |
|
587
|
+
| PUT | /v2/:organization/stash/:topic/:id | varies | varies | Merges into the ID field of the stash with the given topic. |
|
94
588
|
|
95
|
-
|
96
|
-
|
97
|
-
- val1 + val2 = val2
|
589
|
+
When merging the stash itself, your value must be Hash-like and will
|
590
|
+
be merged on top of the existing (Hash-like) stash's value.
|
98
591
|
|
99
|
-
|
100
|
-
|
101
|
-
- val1 + nil = val1
|
592
|
+
When merging one of the ID fields within the stash, your value can
|
593
|
+
have any datatype and it will be intelligently merged:
|
102
594
|
|
103
|
-
|
104
|
-
|
105
|
-
|
595
|
+
* if your value is Hash-like and the existing value is Hash-like , your new value will be merged on top of the existing value
|
596
|
+
* if your value is Array-like and the existing value is Array-like , your new value will be concatenated to the end of the existing value
|
597
|
+
* if your value is String-like and the existing value is String-like , your new value will be concatenated to the end of the existing value
|
598
|
+
* if your value is Numeric-like and the existing value is Numeric-like , your new value will be added to the existing value
|
106
599
|
|
107
|
-
|
600
|
+
The response for merging a stash will be the Hash-like combination of
|
601
|
+
your old and new value. The response for merging an ID within a stash
|
602
|
+
will be of the same type as the request.
|
108
603
|
|
109
|
-
|
604
|
+
<a name="api-stashes-get" />
|
605
|
+
#### Get a value
|
110
606
|
|
111
|
-
|
112
|
-
|
607
|
+
You can get the value of an existing stash or one of the fields within
|
608
|
+
that stash.
|
113
609
|
|
114
|
-
|
610
|
+
| Method | Path | Request | Response | Action |
|
611
|
+
| ------ | ---------------------------------- | ------- | -------- | ------------------------------------------------------ |
|
612
|
+
| GET | /v2/:organization/stash/:topic | N/A | Hash | Return the stash with the given topic. |
|
613
|
+
| GET | /v2/:organization/stash/:topic/:id | N/A | varies | Return the ID field of the stash with the given topic. |
|
115
614
|
|
116
|
-
|
615
|
+
The response for retreiving a stash will be the Hash-like stash while
|
616
|
+
the response for retreving an ID field within a stash will vary based
|
617
|
+
on the datatype of that value.
|
117
618
|
|
118
|
-
|
619
|
+
<a name="api-stashes-delete" />
|
620
|
+
#### Delete a value
|
119
621
|
|
622
|
+
You can get delete a stash or one of the fields within a stash.
|
120
623
|
|
121
|
-
|
624
|
+
| Method | Path | Request | Response | Action |
|
625
|
+
| ------ | ---------------------------------- | ------- | -------- | ------------------------------------------------------- |
|
626
|
+
| DELETE | /v2/:organization/stash/:topic | N/A | Hash | Deletes the stash with the given topic. |
|
627
|
+
| DELETE | /v2/:organization/stash/:topic/:id | N/A | Hash | Deletes the ID field of the stash with the given topic. |
|
122
628
|
|
629
|
+
The response for deleting a stash or an ID within a stash will be a
|
630
|
+
Hash naming the topic (and ID if given in the request) deleted.
|
123
631
|
|
124
|
-
|
632
|
+
<a name="api-stashes-search" />
|
633
|
+
#### Search for stashes
|
125
634
|
|
126
|
-
|
635
|
+
You can search for stashes.
|
127
636
|
|
128
|
-
|
637
|
+
| Method | Path | Request | Response | Action |
|
638
|
+
| ------ | --------------------------- | ------- | -------- | ------------------------------------------------------- |
|
639
|
+
| GET | /v2/:organization/stashes | Hash | Array<Hash> | Search for stashes matching the given query. |
|
129
640
|
|
130
|
-
|
641
|
+
The default behavior (which will occur with an empty request body) is
|
642
|
+
to return 50 stashes in sorted in ascending order by their topic.
|
131
643
|
|
132
|
-
|
644
|
+
Each key in the query body will be interpreted as a condition that the
|
645
|
+
data of each stash must match in order to be returned. Keys with
|
646
|
+
periods are interpreted as nested fields. The following parameters
|
647
|
+
have special meaning and can be used to adjust the number of returned
|
648
|
+
stashes, the fields within each stash, and the the sort behavior.
|
133
649
|
|
134
|
-
|
650
|
+
| Parameter | Description | Default | Example Values |
|
651
|
+
| ------------- | ---------------------------------------------- | -------------------- | ---------------------------------- |
|
652
|
+
| limit | Return up to this many stashes | 50 | 100, 200 |
|
653
|
+
| sort | Sort returned stashes by this field | ascending by topic | `["ip_address", "ascending"]` |
|
654
|
+
| fields | Array of fields to include in the response | all fields | `["name", "address", "phone"]` |
|
655
|
+
| topic | Regular expression search on the stash's topic | N/A | `servers-.*`, `firewall\..*\.rule` |
|
656
|
+
| topic_in | List of explicit topics to match | N/A | `["servers.bob", "servers.alan"]` |
|
657
|
+
| topic_not_in | List of explicit topics to **not** match | N/A | `["servers.bob", "servers.alan"]` |
|
135
658
|
|
136
|
-
|
137
|
-
|
138
|
-
* can only auth at first or second level?
|
139
|
-
* or by wildcard?
|
140
|
-
* access is read or read_write; by default allows read_write
|
659
|
+
The response will be an Array of the matching stashes, possibly an
|
660
|
+
empty Array if no events were found.
|
141
661
|
|
142
|
-
|
662
|
+
#### Update Multiple Stashes
|
143
663
|
|
144
|
-
|
145
|
-
|
146
|
-
GET next
|
664
|
+
There are two related methods that let you update multiple stashes in
|
665
|
+
place according to the same rule.
|
147
666
|
|
148
|
-
|
667
|
+
| Method | Path | Request | Response | Action |
|
668
|
+
| ------ | ------------------------- | ------- | -------- | --------------------------------------------------- |
|
669
|
+
| PUT | /v2/:organization/stashes | Hash | Hash | Apply an update to all stashes matching a query |
|
670
|
+
| POST | /v2/:organization/stashes | Hash | Hash | Apply a replacement to all stashes matching a query |
|
149
671
|
|
150
|
-
|
672
|
+
Each of these methods accepts a Hash request body that is supposed to have the following parameters:
|
151
673
|
|
152
|
-
|
674
|
+
| Parameter | Description | Default | Example Values |
|
675
|
+
| --------- | -------------------------------------------------- | ------- | ----------------------------------------------- |
|
676
|
+
| query | A Hash of matching criteria the stash must satisfy | N/A | `{"region": "dakota"}` |
|
677
|
+
| update | An update to apply to each matched stash | N/A | `{"status": "disabled", "service.ftp": "down"}` |
|
153
678
|
|
154
|
-
|
679
|
+
The methods differ in the way they process the `update`. The `POST`
|
680
|
+
method will always replace the existing values of the named fields
|
681
|
+
(top-level or nested) in the update Hash with their new, updated
|
682
|
+
values. This is useful when you want to explicitly set a property to
|
683
|
+
some fixed value for all stashes which match some criteria.
|
155
684
|
|
156
|
-
|
685
|
+
The `PUT` method (like the `PUT /v2/:organization/stash/:topic`
|
686
|
+
method) tries to be more clever and attempts to *merge* the update
|
687
|
+
into the record according to the following rules
|
157
688
|
|
158
|
-
|
689
|
+
* if your value is Numeric-like, your new value will be added to the existing value
|
690
|
+
* if your value is Array-like, your new value will be concatenated to the end of the existing value
|
691
|
+
* if your value is Hash-like, it will replace the existing value (**Note::** this is different than the behavior of `PUT /v2:organization/stash/:stopic`.)
|
692
|
+
* if your value is String-like, it will replace the existing value (**Note::** this is different than the behavior of `PUT /v2:organization/stash/:stopic`.)
|
159
693
|
|
160
|
-
|
694
|
+
Notice that the behavior for Hashes and Strings isn't as nice as it is
|
695
|
+
for `PUT /v2:organization/stash/:stopic`. Still, this method is
|
696
|
+
useful for when you want to increment some value across all stashes
|
697
|
+
which match some criteria.
|
161
698
|
|
162
|
-
|
699
|
+
For both of these methods, the query Hash also contains parameters
|
700
|
+
that are very similar to when searching for a stash:
|
163
701
|
|
164
|
-
|
165
|
-
|
702
|
+
| Parameter | Description | Default | Example Values |
|
703
|
+
| ------------- | ---------------------------------------------- | -------------------- | ---------------------------------- |
|
704
|
+
| topic | Regular expression search on the stash's topic | N/A | `servers-.*`, `firewall\..*\.rule` |
|
705
|
+
| topic_in | List of explicit topics to match | N/A | `["servers.bob", "servers.alan"]` |
|
706
|
+
| topic_not_in | List of explicit topics to **not** match | N/A | `["servers.bob", "servers.alan"]` |
|
166
707
|
|
167
|
-
|
708
|
+
Other fields in the query Hash are interpreted as requirements on the
|
709
|
+
data in the stash itself.
|
168
710
|
|
169
|
-
|
711
|
+
#### Deleting Multiple Stashes
|
170
712
|
|
171
|
-
|
713
|
+
The following method can be used to delete multiple stashes which all
|
714
|
+
match some criteria:
|
172
715
|
|
173
|
-
|
716
|
+
| Method | Path | Request | Response | Action |
|
717
|
+
| ------ | ------------------------- | ------- | -------- | ----------------------------------- |
|
718
|
+
| DELETE | /v2/:organization/stashes | Hash | Hash | Delete all stashes matching a query |
|
174
719
|
|
175
|
-
|
720
|
+
The following request parameters define the query which is used to
|
721
|
+
match stashes to be deleted:
|
176
722
|
|
177
|
-
|
723
|
+
| Parameter | Description | Default | Example Values |
|
724
|
+
| ------------- | ---------------------------------------------- | -------------------- | ---------------------------------- |
|
725
|
+
| topic | Regular expression search on the stash's topic | N/A | `servers-.*`, `firewall\..*\.rule` |
|
726
|
+
| topic_in | List of explicit topics to match | N/A | `["servers.bob", "servers.alan"]` |
|
727
|
+
| topic_not_in | List of explicit topics to **not** match | N/A | `["servers.bob", "servers.alan"]` |
|
178
728
|
|
179
|
-
|
180
|
-
|
181
|
-
* Fork the project
|
182
|
-
* Start a feature/bugfix branch
|
183
|
-
* Commit and push until you are happy with your contribution
|
184
|
-
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
185
|
-
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
729
|
+
Other fields in the query Hash are interpreted as requirements on the
|
730
|
+
data in the stash itself in order for it to match and be deleted.
|
186
731
|
|
187
732
|
### Copyright
|
188
733
|
|
189
|
-
Copyright (c) 2011
|
734
|
+
Copyright (c) 2011 - 2013 Infochimps. See [LICENSE.md](LICENSE.md) for further details.
|