qurd 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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +367 -0
  7. data/Rakefile +9 -0
  8. data/bin/qurd +10 -0
  9. data/lib/hash.rb +6 -0
  10. data/lib/qurd.rb +49 -0
  11. data/lib/qurd/action.rb +92 -0
  12. data/lib/qurd/action/chef.rb +128 -0
  13. data/lib/qurd/action/dummy.rb +27 -0
  14. data/lib/qurd/action/route53.rb +168 -0
  15. data/lib/qurd/configuration.rb +182 -0
  16. data/lib/qurd/listener.rb +207 -0
  17. data/lib/qurd/message.rb +231 -0
  18. data/lib/qurd/mixins.rb +8 -0
  19. data/lib/qurd/mixins/aws_clients.rb +37 -0
  20. data/lib/qurd/mixins/configuration.rb +29 -0
  21. data/lib/qurd/mixins/configuration_helpers.rb +79 -0
  22. data/lib/qurd/processor.rb +97 -0
  23. data/lib/qurd/version.rb +5 -0
  24. data/lib/string.rb +12 -0
  25. data/qurd.gemspec +32 -0
  26. data/test/action_test.rb +115 -0
  27. data/test/chef_test.rb +206 -0
  28. data/test/configuration_test.rb +333 -0
  29. data/test/dummy_action_test.rb +51 -0
  30. data/test/inputs/foo.pem +27 -0
  31. data/test/inputs/knife.rb +9 -0
  32. data/test/inputs/qurd.yml +32 -0
  33. data/test/inputs/qurd_chef.yml +35 -0
  34. data/test/inputs/qurd_chef_route53.yml +43 -0
  35. data/test/inputs/qurd_route53.yml +39 -0
  36. data/test/inputs/qurd_route53_wrong.yml +37 -0
  37. data/test/inputs/validator.pem +27 -0
  38. data/test/listener_test.rb +135 -0
  39. data/test/message_test.rb +187 -0
  40. data/test/mixin_aws_clients_test.rb +28 -0
  41. data/test/mixin_configuration_test.rb +36 -0
  42. data/test/processor_test.rb +41 -0
  43. data/test/responses/aws/ec2-describe-instances-0.xml +2 -0
  44. data/test/responses/aws/ec2-describe-instances-1.xml +127 -0
  45. data/test/responses/aws/error-response.xml +1 -0
  46. data/test/responses/aws/route53-change-resource-record-sets.xml +2 -0
  47. data/test/responses/aws/route53-list-hosted-zones-by-name-0.xml +3 -0
  48. data/test/responses/aws/route53-list-hosted-zones-by-name-1.xml +4 -0
  49. data/test/responses/aws/route53-list-hosted-zones-by-name-n.xml +5 -0
  50. data/test/responses/aws/route53-list-resource-record-sets-0.xml +2 -0
  51. data/test/responses/aws/route53-list-resource-record-sets-1.xml +4 -0
  52. data/test/responses/aws/route53-list-resource-record-sets-n.xml +6 -0
  53. data/test/responses/aws/sqs-list-queues-0.xml +1 -0
  54. data/test/responses/aws/sqs-list-queues-n.xml +4 -0
  55. data/test/responses/aws/sqs-receive-message-1-launch.xml +6 -0
  56. data/test/responses/aws/sqs-receive-message-1-launch_error.xml +6 -0
  57. data/test/responses/aws/sqs-receive-message-1-other.xml +12 -0
  58. data/test/responses/aws/sqs-receive-message-1-terminate.xml +6 -0
  59. data/test/responses/aws/sqs-receive-message-1-terminate_error.xml +6 -0
  60. data/test/responses/aws/sqs-receive-message-1-test.xml +12 -0
  61. data/test/responses/aws/sqs-set-queue-attributes.xml +1 -0
  62. data/test/responses/aws/sts-assume-role.xml +17 -0
  63. data/test/responses/chef/search-client-name-0.json +6 -0
  64. data/test/responses/chef/search-client-name-1.json +7 -0
  65. data/test/responses/chef/search-client-name-n.json +8 -0
  66. data/test/responses/chef/search-node-instance-0.json +5 -0
  67. data/test/responses/chef/search-node-instance-1.json +784 -0
  68. data/test/responses/chef/search-node-instance-n.json +1565 -0
  69. data/test/responses/ec2/latest-meta-data-iam-security-credentials-client.txt +9 -0
  70. data/test/responses/ec2/latest-meta-data-iam-security-credentials.txt +1 -0
  71. data/test/route53_test.rb +231 -0
  72. data/test/support/web_mock_stubs.rb +109 -0
  73. data/test/test_helper.rb +10 -0
  74. metadata +307 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cb531c8fce5f427730dc00640ab95c9efc9766bf
4
+ data.tar.gz: 7eaee5c8c805f610cd2e0962cb7785347f33b476
5
+ SHA512:
6
+ metadata.gz: 20500894f93834ad793f274ca2865183c6313547fd700bfea25c96852741f562e56d1b289ba655f79f0be95153d9aaf989f21b73150f55213ca235e82da1383b
7
+ data.tar.gz: cd3332095e49af044260a9aa026081aad942f93c1b8d2c46bbd565a5e311bc04374c9786485bcac043cb5f1dddea70e16b12bed82181353d1d53fb55a86e2ac3
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *.sw[a-z]
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.2
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in qurd.gemspec
4
+ gem 'rubocop', group: :development, require: false
5
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Philip Champon
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,367 @@
1
+ # QURD - QUeue Resource Daemon
2
+
3
+ The Queue Resource Daemon is an extensible SQS monitoring service, which can be
4
+ configured to react to or ignore AutoScaling messages. Qurd can be configured to
5
+ monitor multiple accounts, any number of queues, and any type of auto scaling
6
+ event.
7
+
8
+ When the daemon starts up, it finds the queues it's meant to monitor and sets
9
+ the `visibility_timeout` and `waittimeseconds` for each queue. Qurd uses long
10
+ polling to monitor the queues, by default.
11
+
12
+ This daemon makes extensive use of threads, you should really consider running
13
+ this with Ruby version 2.0 or higher.
14
+
15
+ ## Plugin architecture
16
+
17
+ It is possible to provide your own actions, aside from Chef, Route53, and Dummy.
18
+ Dummy is provided as a simple example, but, in a nutshell, inherit from
19
+ Qurd::Action and override the actions you respond to.
20
+
21
+ Your action class can configure itself, by overriding the class method
22
+ `configure`. Instances must override the action methods launch, launch_error,
23
+ terminate, terminate_error, and test. Action instances have two attributes,
24
+ `message` and `context`. Message is a `Qurd::Message` instance. Context is a
25
+ `Cabin::Context`, used for logging. Callbacks, to interact with the action
26
+ before and after the instance are executed, can be overridden.
27
+
28
+ The mixins for AwsClients and Configuration are also available at the class and
29
+ instance level.
30
+
31
+ ```ruby
32
+ # This contrived example creates a file in s3 when an instance launches
33
+ # It can be triggered by adding the class name to the list of actions in the
34
+ # configuration file, ie
35
+ # bucket: example-bucket
36
+ # actions:
37
+ # launch:
38
+ # - "Foo"
39
+ class Foo < Qurd::Action
40
+ def self.configure(_action)
41
+ qurd_configuration.bucket || qurd_logger!("Missing bucket")
42
+ end
43
+
44
+ def run_before
45
+ aws_retryable do
46
+ aws_client(:S3).delete_object(
47
+ bucket: qurd_configuration.bucket,
48
+ key: message.instance_id
49
+ )
50
+ end
51
+ end
52
+
53
+ def launch
54
+ aws_retryable do
55
+ aws_client(:S3).put_object(
56
+ bucket: qurd_configuration.bucket,
57
+ key: message.instance_id,
58
+ body: message.instance.private_ip_address
59
+ )
60
+ end
61
+ end
62
+
63
+ def run_after
64
+ aws_retryable do
65
+ o = aws_client(:S3).get_object(
66
+ bucket: qurd_configuration.bucket,
67
+ key: message.instance_id
68
+ )
69
+ qurd_logger.debug("Found #{o.body}")
70
+ end
71
+ end
72
+
73
+ end
74
+ ```
75
+
76
+ ## AWS IAM Policy
77
+
78
+ QURD requires, at a minimum, SQS privileges and EC2 privileges, ie
79
+
80
+ ```json
81
+ {
82
+ "Version": "2012-10-17",
83
+ "Statement": [
84
+ {
85
+ "Sid": "qurd_sqs",
86
+ "Effect": "Allow",
87
+ "Action": [
88
+ "sqs:DeleteMessage",
89
+ "sqs:ListQueues",
90
+ "sqs:ReceiveMessage",
91
+ "sqs:SetQueueAttributes"
92
+ ],
93
+ "Resource": [
94
+ "*"
95
+ ]
96
+ },
97
+ {
98
+ "Sid": "qurd_ec2",
99
+ "Effect": "Allow",
100
+ "Action": [
101
+ "ec2:DescribeInstances"
102
+ ],
103
+ "Resource": [
104
+ "*"
105
+ ]
106
+ }
107
+ ]
108
+ }
109
+ ```
110
+
111
+ If you are using the route53 action, you will also need
112
+
113
+ ```json
114
+ {
115
+ "Version": "2012-10-17",
116
+ "Statement": [
117
+ {
118
+ "Sid": "Stmt1428424119000",
119
+ "Effect": "Allow",
120
+ "Action": [
121
+ "route53:ChangeResourceRecordSets",
122
+ "route53:DeleteHostedZone",
123
+ "route53:GetHostedZone",
124
+ "route53:ListHostedZones",
125
+ "route53:ListResourceRecordSets"
126
+ ],
127
+ "Resource": [
128
+ "*"
129
+ ]
130
+ }
131
+ ]
132
+ }
133
+ ```
134
+
135
+ ## Configuration
136
+
137
+ To configure the daemon, edit the YAML configuration file. The default path is
138
+ `/etc/qurd/config.yml`. An alternate path can be specified on the command line.
139
+
140
+ <table>
141
+ <tr>
142
+ <th>Option</th>
143
+ <th>Description</th>
144
+ <th>Type</th>
145
+ <th>Default</th>
146
+ </tr>
147
+
148
+ <tr>
149
+ <td><tt><a href="#aws_credentials">aws_credentials</a></tt></td>
150
+ <td>AWS credentials</td>
151
+ <td><tt>[Array&lt;Hash&gt;]</tt></td>
152
+ <td>&nbsp;</td>
153
+ </tr>
154
+
155
+ <tr>
156
+ <td><tt><a href="#auto_scaling_queues">auto_scaling_queues</a></tt></td>
157
+ <td>Describe the queues to be monitored</td>
158
+ <td><tt>[Hash&lt;Hash&gt;]</tt></td>
159
+ <td>&nbsp;</td>
160
+ </tr>
161
+
162
+ <tr>
163
+ <td><tt><a href="#actions">actions</a></tt></td>
164
+ <td>describe actions to take, for any autoscaling event</td>
165
+ <td><tt>[Hash&lt;Array&gt;]</tt></td>
166
+ <td>&nbsp;</td>
167
+ </tr>
168
+
169
+ <tr>
170
+ <td><tt>daemonize</tt></td>
171
+ <td>Force qurd to log to a file, even if <tt>log_file</tt> is not defined.</td>
172
+ <td><tt>Boolean</tt></td>
173
+ <td><tt>false</tt></td>
174
+ </tr>
175
+
176
+ <tr>
177
+ <td><tt>dry_run</tt></td>
178
+ <td>Log what qurd would have done</td>
179
+ <td><tt>Boolean</tt></td>
180
+ <td><tt>false</tt></td>
181
+ </tr>
182
+
183
+ <tr>
184
+ <td><tt>listen_timeout</tt></td>
185
+ <td>Defines the timeout, in seconds, for a thread to process a message</td>
186
+ <td><tt>Float</tt></td>
187
+ <td><tt>visibility_timeout</tt></td>
188
+ </tr>
189
+
190
+ <tr>
191
+ <td><tt>log_file</tt></td>
192
+ <td>The path to qurd's log file</td>
193
+ <td><tt>String</tt></td>
194
+ <td><tt>/var/log/qurd/qurd.log</tt></td>
195
+ </tr>
196
+
197
+ <tr>
198
+ <td><tt>log_level</tt></td>
199
+ <td>The log level to catch</td>
200
+ <td><tt>String</tt></td>
201
+ <td><tt>info</tt></td>
202
+ </tr>
203
+
204
+ <tr>
205
+ <td><tt>pid_file</tt></td>
206
+ <td>The path of qurd's pid file</td>
207
+ <td><tt>String</tt></td>
208
+ <td><tt>/var/run/qurd/qurd.pid</tt></td>
209
+ </tr>
210
+
211
+ <tr>
212
+ <td><tt>save_failures</tt></td>
213
+ <td>Save messages if any action fails</td>
214
+ <td><tt>Boolean</tt></td>
215
+ <td><tt>true</tt></td>
216
+ </tr>
217
+
218
+ <tr>
219
+ <td><tt>sqs_set_attributes_timeout</tt></td>
220
+ <td>Defines the timeout, in seconds, for a thread setting SQS attributes</td>
221
+ <td><tt>Float</tt></td>
222
+ <td><tt>10</tt></td>
223
+ </tr>
224
+
225
+ <tr>
226
+ <td><tt>visibility_timeout</tt></td>
227
+ <td>Set the SQS <a href="http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html">visibility timeout</a></td>
228
+ <td><tt>Integer</tt></td>
229
+ <td><tt>300</tt></td>
230
+ </tr>
231
+
232
+ <tr>
233
+ <td><tt>wait_time</tt></td>
234
+ <td>Set the SQS <a href="http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-long-polling.html">wait time seconds</a></td>
235
+ <td><tt>Integer</tt></td>
236
+ <td><tt>20</tt></td>
237
+ </tr>
238
+
239
+ </table>
240
+
241
+ ### aws_credentials
242
+ <a name="aws_credentials">
243
+
244
+ Qurd supports [AssumeRoleCredentials][1], [Credentials][2],
245
+ [InstanceProfileCredentials][3], and [SharedCredentials][4]. Each credential
246
+ must be named and must have a type defined. The options key allows the caller to
247
+ define keys and values, mirroring the options for each of the credential types.
248
+
249
+ If no aws_credentials are defined in the configuration file, the key `default`
250
+ is created and it will attempt to use `Aws::InstanceProfileCredentials`. Each
251
+ auto_scaling_queues will have its credentials automatically set to `default`.
252
+
253
+ ```yaml
254
+ aws_credentials:
255
+ - name: prod
256
+ type: assume_role_credentials
257
+ options:
258
+ role_arn: "arn:aws:iam::1:user/bob@example.com"
259
+ role_session_name: foo
260
+ - name: staging
261
+ type: credentials
262
+ options:
263
+ access_key_id: abc123
264
+ secret_access_key: 123abc
265
+ - name: dev
266
+ type: instance_profile_credentials
267
+ - name: test
268
+ type: shared_credentials
269
+ options:
270
+ profile_name: default
271
+ ```
272
+
273
+ ### auto_scaling_queues
274
+ <a name="auto_scaling_queues">
275
+
276
+ A hash of hashes, which describe the queues to be monitored. The outer key is
277
+ the name of the group of queues, ie production, staging, etc. The inner keys
278
+ `credentials`, `region`, and `queues` are required. Credentials should refer to the
279
+ `name` of an `aws_credential`. The `region` is the region of the queues. The
280
+ `queues` key is an array of queue names and regular expressions. Regular
281
+ expressions are strings, which begin and end with forward slash. Regular
282
+ expressions can also have [modifiers][5] applied to them.
283
+
284
+ The optional keys `wait_time` and `visibility_timeout` override the global
285
+ options of the same name.
286
+
287
+ The `credentials` key will be overridden, and set to `default`, if no
288
+ `aws_credentials` are defined.
289
+
290
+ ```yaml
291
+ auto_scaling_queues:
292
+ dev:
293
+ credentials: dev
294
+ region: us-east-1
295
+ queues: "/scalingnotificationsqueue/i"
296
+ staging:
297
+ credentials: staging
298
+ region: us-west-2
299
+ visibility_timeout: 100
300
+ wait_time: 20
301
+ queues:
302
+ - FooQueue
303
+ - BarQueue
304
+ - "/ScalingNotificationsQueue/"
305
+ ```
306
+
307
+ ### actions
308
+ <a name="actions">
309
+
310
+ A hash of arrays, describing actions to take, for any autoscaling event. To test
311
+ the various options, you could configure the dummy action for each event.
312
+
313
+ ```yaml
314
+ actions:
315
+ launch:
316
+ - "Qurd::Action::Dummy"
317
+ launch_error:
318
+ - "Qurd::Action::Dummy"
319
+ terminate:
320
+ - "Qurd::Action::Dummy"
321
+ terminate_error:
322
+ - "Qurd::Action::Dummy"
323
+ test:
324
+ - "Qurd::Action::Dummy"
325
+ ```
326
+
327
+ ## Installation
328
+
329
+ Add this line to your application's Gemfile:
330
+
331
+ ```ruby
332
+ gem 'qurd'
333
+ ```
334
+
335
+ And then execute:
336
+
337
+ $ bundle
338
+
339
+ Or install it yourself as:
340
+
341
+ $ gem install qurd
342
+
343
+ ## Usage
344
+
345
+ `qurd [/PATH/TO/CONFIG.yml]`
346
+
347
+ ## Tests
348
+
349
+ `bundle exec rake`
350
+
351
+ WebMock stubs can be found in test/support/web_mock_stubs.rb, responses are in
352
+ test/responses.
353
+
354
+ ## Contributing
355
+
356
+ 1. Fork it ( https://github.com/Adaptly/qurd/fork )
357
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
358
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
359
+ 1. Write some tests!
360
+ 4. Push to the branch (`git push origin my-new-feature`)
361
+ 5. Create a new Pull Request
362
+
363
+ [1]: http://docs.aws.amazon.com/sdkforruby/api/Aws/AssumeRoleCredentials.html
364
+ [2]: http://docs.aws.amazon.com/sdkforruby/api/Aws/Credentials.html
365
+ [3]: http://docs.aws.amazon.com/sdkforruby/api/Aws/InstanceProfileCredentials.html
366
+ [4]: http://docs.aws.amazon.com/sdkforruby/api/Aws/SharedCredentials.html
367
+ [5]: http://ruby-doc.org/core-2.1.1/Regexp.html#class-Regexp-label-Options
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = "test/*_test.rb"
6
+ t.libs << "test"
7
+ end
8
+
9
+ task default: [:test]
data/bin/qurd ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.dirname(__FILE__) + '/../lib'
3
+ require 'qurd'
4
+ if ARGV[0] && !File.exist?(ARGV[0])
5
+ STDERR.puts "File does not exist: '#{ARGV[0]}'"
6
+ STDERR.puts "Usage: qurd [/path/to/config.yml]"
7
+ exit 1
8
+ end
9
+ Qurd.start(ARGV[0])
10
+ # vim:ft=ruby: