leeroy_app 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +40 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +103 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/Rakefile +5 -0
- data/bin/leeroy +18 -0
- data/hierarchy.md +39 -0
- data/leeroy.gemspec +42 -0
- data/lib/leeroy/app.rb +132 -0
- data/lib/leeroy/env.rb +116 -0
- data/lib/leeroy/helpers/aws.rb +403 -0
- data/lib/leeroy/helpers/dumpable.rb +72 -0
- data/lib/leeroy/helpers/env.rb +42 -0
- data/lib/leeroy/helpers/logging.rb +36 -0
- data/lib/leeroy/helpers/polling.rb +67 -0
- data/lib/leeroy/helpers/state.rb +59 -0
- data/lib/leeroy/helpers.rb +9 -0
- data/lib/leeroy/state.rb +70 -0
- data/lib/leeroy/task/base.rb +61 -0
- data/lib/leeroy/task/image.rb +126 -0
- data/lib/leeroy/task/instantiate.rb +200 -0
- data/lib/leeroy/task/sleep.rb +29 -0
- data/lib/leeroy/task/stub.rb +40 -0
- data/lib/leeroy/task/terminate.rb +49 -0
- data/lib/leeroy/task.rb +17 -0
- data/lib/leeroy/types/dash.rb +17 -0
- data/lib/leeroy/types/image.rb +53 -0
- data/lib/leeroy/types/instance.rb +67 -0
- data/lib/leeroy/types/mash.rb +17 -0
- data/lib/leeroy/types/packedstring.rb +25 -0
- data/lib/leeroy/types/phase.rb +20 -0
- data/lib/leeroy/types/semaphore.rb +30 -0
- data/lib/leeroy/types/statedata.rb +44 -0
- data/lib/leeroy/types/statemetadata.rb +29 -0
- data/lib/leeroy/types/userdata.rb +13 -0
- data/lib/leeroy/version.rb +3 -0
- data/lib/leeroy.rb +7 -0
- data/spec/spec_helper.rb +105 -0
- data/spec/support/aruba.rb +1 -0
- data/spec/use_aruba_with_rspec_spec.rb +11 -0
- metadata +333 -0
@@ -0,0 +1,403 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
require 'leeroy/helpers'
|
5
|
+
require 'leeroy/helpers/env'
|
6
|
+
|
7
|
+
require 'leeroy/types/instance'
|
8
|
+
require 'leeroy/types/mash'
|
9
|
+
require 'leeroy/types/semaphore'
|
10
|
+
|
11
|
+
module Leeroy
|
12
|
+
module Helpers
|
13
|
+
module AWS
|
14
|
+
include Leeroy::Helpers
|
15
|
+
|
16
|
+
attr :ec2, :s3
|
17
|
+
|
18
|
+
def initialize(*args, &block)
|
19
|
+
super(*args, &block)
|
20
|
+
|
21
|
+
logger.debug "initializing AWS helpers"
|
22
|
+
|
23
|
+
@ec2 = Aws::EC2::Client.new
|
24
|
+
@s3 = Aws::S3::Client.new
|
25
|
+
|
26
|
+
logger.debug "AWS helpers initialized"
|
27
|
+
end
|
28
|
+
|
29
|
+
# EC2
|
30
|
+
|
31
|
+
def ec2Request(method, params = {}, ec2 = self.ec2, options = self.options, global_options = self.global_options)
|
32
|
+
begin
|
33
|
+
logger.debug "constructing EC2 request for '#{method}'"
|
34
|
+
logger.debug "params: #{params.inspect}"
|
35
|
+
|
36
|
+
params_mash = Leeroy::Types::Mash.new(params)
|
37
|
+
params = params_mash
|
38
|
+
|
39
|
+
dry_run = global_options[:op] ? false : true
|
40
|
+
|
41
|
+
params.dry_run = dry_run
|
42
|
+
|
43
|
+
logger.debug "params: #{params.inspect}"
|
44
|
+
|
45
|
+
resp = ec2.send(method.to_sym, params)
|
46
|
+
|
47
|
+
logger.debug "resp: #{resp.inspect}"
|
48
|
+
|
49
|
+
resp
|
50
|
+
|
51
|
+
rescue StandardError => e
|
52
|
+
raise e
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def getVpcId(vpcname, ec2 = self.ec2)
|
57
|
+
begin
|
58
|
+
logger.debug "getting VPC ID for '#{vpcname}'"
|
59
|
+
|
60
|
+
resp = ec2Request(:describe_vpcs, {:filters => [{name: 'tag:Name', values: [vpcname]}]})
|
61
|
+
vpcs = resp.vpcs
|
62
|
+
logger.debug "vpcs: #{vpcs.inspect}"
|
63
|
+
|
64
|
+
if vpcs.length < 1
|
65
|
+
raise "No VPC found with the name '#{vpcname}'."
|
66
|
+
elsif vpcs.length > 1
|
67
|
+
raise "Multiple VPCs found with the name '#{vpcname}'."
|
68
|
+
else
|
69
|
+
vpcid = vpcs[0].vpc_id
|
70
|
+
end
|
71
|
+
|
72
|
+
logger.debug "vpcid: #{vpcid}"
|
73
|
+
vpcid
|
74
|
+
|
75
|
+
rescue Aws::EC2::Errors::DryRunOperation => e
|
76
|
+
logger.info e.message
|
77
|
+
"DRYRUN_DUMMY_VALUE: #{self.class.to_s}"
|
78
|
+
|
79
|
+
rescue StandardError => e
|
80
|
+
raise e
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def getSgId(sgname, vpcname, vpcid, ec2 = self.ec2)
|
85
|
+
begin
|
86
|
+
logger.debug "getting SG ID for '#{sgname}'"
|
87
|
+
|
88
|
+
resp = ec2Request(:describe_security_groups, {:filters => [{name: 'vpc-id', values: [vpcid]}]})
|
89
|
+
security_groups = resp.security_groups
|
90
|
+
|
91
|
+
# now filter by sgname
|
92
|
+
sgmatcher = %r{#{vpcname}-#{sgname}-.*}
|
93
|
+
security_group = security_groups.select { |sg| sg.group_name =~ sgmatcher}
|
94
|
+
logger.debug "security_group: #{security_group.inspect}"
|
95
|
+
|
96
|
+
if security_group.length < 1
|
97
|
+
raise "No SG found with the name '#{sgname}'."
|
98
|
+
elsif security_group.length > 1
|
99
|
+
raise "Multiple SGs found with the name '#{sgname}'."
|
100
|
+
else
|
101
|
+
sgid = security_group[0].group_id
|
102
|
+
end
|
103
|
+
|
104
|
+
logger.debug "sgid: #{sgid}"
|
105
|
+
sgid
|
106
|
+
|
107
|
+
rescue Aws::EC2::Errors::DryRunOperation => e
|
108
|
+
logger.info e.message
|
109
|
+
"DRYRUN_DUMMY_VALUE: #{self.class.to_s}"
|
110
|
+
|
111
|
+
rescue StandardError => e
|
112
|
+
raise e
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def getSubnetId(subnetname, vpcid, ec2 = self.ec2)
|
117
|
+
begin
|
118
|
+
logger.debug "getting Subnet ID for '#{subnetname}'"
|
119
|
+
|
120
|
+
resp = ec2Request(:describe_subnets, {:filters => [{name: 'vpc-id', values: [vpcid]}, {name: 'tag:Name', values: [subnetname]}]})
|
121
|
+
subnets = resp.subnets
|
122
|
+
logger.debug "subnets: #{subnets.inspect}"
|
123
|
+
|
124
|
+
if subnets.length < 1
|
125
|
+
raise "No Subnet found with the name '#{subnetname}'."
|
126
|
+
elsif subnets.length > 1
|
127
|
+
raise "Multiple Subnets found with the name '#{subnetname}'."
|
128
|
+
else
|
129
|
+
subnetid = subnets[0].subnet_id
|
130
|
+
end
|
131
|
+
|
132
|
+
logger.debug "subnetid: #{subnetid}"
|
133
|
+
subnetid
|
134
|
+
|
135
|
+
rescue Aws::EC2::Errors::DryRunOperation => e
|
136
|
+
logger.info e.message
|
137
|
+
"DRYRUN_DUMMY_VALUE: #{self.class.to_s}"
|
138
|
+
|
139
|
+
rescue StandardError => e
|
140
|
+
raise e
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def destroyInstance(state = self.state, env = self.env, ec2 = self.ec2, options = self.options)
|
145
|
+
begin
|
146
|
+
# did we get instance ID(s)?
|
147
|
+
instanceids = options.fetch(:instance, nil)
|
148
|
+
if instanceids.nil?
|
149
|
+
instanceids = Array(state.instanceid)
|
150
|
+
end
|
151
|
+
|
152
|
+
logger.debug "instanceids: #{instanceids}"
|
153
|
+
|
154
|
+
run_params = Leeroy::Types::Mash.new
|
155
|
+
run_params.instance_ids = instanceids
|
156
|
+
|
157
|
+
resp = ec2Request(:terminate_instances, run_params)
|
158
|
+
|
159
|
+
resp.terminating_instances.collect { |i| i.instance_id }.sort
|
160
|
+
|
161
|
+
rescue Aws::EC2::Errors::DryRunOperation => e
|
162
|
+
logger.info e.message
|
163
|
+
"DRYRUN_DUMMY_VALUE: #{self.class.to_s}"
|
164
|
+
|
165
|
+
rescue StandardError => e
|
166
|
+
raise e
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
def createTags(tags = {}, resourceids = [], state = self.state, env = self.env, ec2 = self.ec2, options = self.options)
|
172
|
+
begin
|
173
|
+
if resourceids.length == 0
|
174
|
+
if state.instanceid?
|
175
|
+
logger.debug "no resourceids provided for tagging, defaulting to instanceid #{state.instanceid} from state"
|
176
|
+
resourceids.push(state.instanceid.to_s)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
run_params = Leeroy::Types::Mash.new
|
181
|
+
|
182
|
+
logger.debug "resourceids: #{resourceids}"
|
183
|
+
run_params.resources = resourceids
|
184
|
+
|
185
|
+
tag_array = tags.collect {|key,value| {'key' => key, 'value' => value}}
|
186
|
+
|
187
|
+
logger.debug "tags: #{tags}"
|
188
|
+
logger.debug "tag_array: #{tag_array}"
|
189
|
+
run_params.tags = tag_array
|
190
|
+
|
191
|
+
resp = ec2Request(:create_tags, run_params)
|
192
|
+
|
193
|
+
rescue Aws::EC2::Errors::DryRunOperation => e
|
194
|
+
logger.info e.message
|
195
|
+
"DRYRUN_DUMMY_VALUE: #{self.class.to_s}"
|
196
|
+
|
197
|
+
rescue StandardError => e
|
198
|
+
raise e
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def filterImages(selector, collector = lambda { |x| x }, state = self.state, env = self.env, ec2 = self.ec2, options = self.options)
|
203
|
+
begin
|
204
|
+
run_params = Leeroy::Types::Mash.new
|
205
|
+
|
206
|
+
run_params.owners = ['self']
|
207
|
+
|
208
|
+
resp = ec2Request(:describe_images, run_params)
|
209
|
+
|
210
|
+
# now filter based on callback
|
211
|
+
resp.images.select {|x| selector.call(x)}.collect {|x| collector.call(x)}
|
212
|
+
|
213
|
+
rescue Aws::EC2::Errors::DryRunOperation => e
|
214
|
+
logger.info e.message
|
215
|
+
"DRYRUN_DUMMY_VALUE: #{self.class.to_s}"
|
216
|
+
|
217
|
+
rescue StandardError => e
|
218
|
+
raise e
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def getGoldMasterInstanceName(env_name = 'LEEROY_GOLD_MASTER_NAME')
|
223
|
+
checkEnv(env_name)
|
224
|
+
end
|
225
|
+
|
226
|
+
def getApplicationInstanceName(index, env_app = 'LEEROY_APP_NAME', env_name = 'LEEROY_BUILD_TARGET')
|
227
|
+
name_prefix = [checkEnv(env_app), checkEnv(env_name), index].join('-')
|
228
|
+
logger.debug "name_prefix: #{name_prefix}"
|
229
|
+
|
230
|
+
if index.nil?
|
231
|
+
# determine the index by looking at existing images
|
232
|
+
selector = lambda {|image| image.name =~ /^#{name_prefix}/}
|
233
|
+
# and extract the names
|
234
|
+
collector = lambda {|image| image.name}
|
235
|
+
|
236
|
+
image_names = filterImages(selector, collector)
|
237
|
+
logger.debug image_names.awesome_inspect
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
def getGoldMasterImageIndex(env_prefix = 'LEEROY_GOLD_MASTER_IMAGE_PREFIX')
|
243
|
+
name_prefix = checkEnv(env_prefix)
|
244
|
+
logger.debug "name_prefix: #{name_prefix}"
|
245
|
+
|
246
|
+
# determine the index by looking at existing images
|
247
|
+
selector = lambda {|image| image.name =~ /^#{name_prefix}/}
|
248
|
+
# and extract the names
|
249
|
+
collector = lambda {|image| image.name}
|
250
|
+
|
251
|
+
image_names = filterImages(selector, collector)
|
252
|
+
image_numbers = image_names.collect do |name|
|
253
|
+
if name =~ /(\d+)$/
|
254
|
+
image_number = $1.to_i
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
latest_image = image_numbers.sort.compact.uniq.pop
|
259
|
+
logger.debug "latest_image: #{latest_image}"
|
260
|
+
|
261
|
+
latest_image
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
# S3
|
266
|
+
|
267
|
+
def s3Request(method, params = {}, s3 = self.s3, options = self.options, global_options = self.global_options)
|
268
|
+
begin
|
269
|
+
logger.debug "constructing S3 request for '#{method}'"
|
270
|
+
|
271
|
+
params_mash = Leeroy::Types::Mash.new(params)
|
272
|
+
params = params_mash
|
273
|
+
|
274
|
+
logger.debug "params: #{params.inspect}"
|
275
|
+
|
276
|
+
resp = s3.send(method.to_sym, params)
|
277
|
+
|
278
|
+
logger.debug "resp: #{resp.inspect}"
|
279
|
+
|
280
|
+
resp
|
281
|
+
|
282
|
+
rescue StandardError => e
|
283
|
+
raise e
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def buildS3ObjectName(key, type, prefixes = Leeroy::Env::S3_PREFIXES)
|
288
|
+
begin
|
289
|
+
logger.debug "building S3 prefix (key: #{key}, type: #{type})"
|
290
|
+
pfx = Leeroy::Types::Mash.new(prefixes)
|
291
|
+
root = pfx.jenkins
|
292
|
+
prefix = pfx.fetch(type,type)
|
293
|
+
|
294
|
+
# FIXME i should do this with URI
|
295
|
+
[root, prefix, key].join('/')
|
296
|
+
|
297
|
+
rescue StandardError => e
|
298
|
+
raise e
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def genSemaphore(object, payload = '', bucket = checkEnv('LEEROY_S3_BUCKET'))
|
303
|
+
begin
|
304
|
+
logger.debug "creating a semaphore"
|
305
|
+
|
306
|
+
semaphore = Leeroy::Types::Semaphore.new(bucket: bucket, object: object, payload: payload)
|
307
|
+
logger.debug "semaphore: #{semaphore}"
|
308
|
+
|
309
|
+
semaphore
|
310
|
+
|
311
|
+
rescue StandardError => e
|
312
|
+
raise e
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def setSemaphore(semaphore)
|
317
|
+
begin
|
318
|
+
unless semaphore.kind_of?(Leeroy::Types::Semaphore)
|
319
|
+
semaphore = Leeroy::Types::Semaphore.new(semaphore)
|
320
|
+
end
|
321
|
+
|
322
|
+
logger.debug "setting a semaphore"
|
323
|
+
|
324
|
+
run_params = Leeroy::Types::Mash.new
|
325
|
+
|
326
|
+
run_params.body = semaphore.payload
|
327
|
+
run_params.bucket = semaphore.bucket
|
328
|
+
run_params.key = semaphore.object
|
329
|
+
|
330
|
+
resp = s3Request(:put_object, run_params)
|
331
|
+
|
332
|
+
semaphore
|
333
|
+
|
334
|
+
rescue StandardError => e
|
335
|
+
raise e
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def clearSemaphore(semaphore)
|
340
|
+
begin
|
341
|
+
logger.debug "semaphore.class: #{semaphore.class}"
|
342
|
+
|
343
|
+
if semaphore.kind_of?(Leeroy::Types::Semaphore)
|
344
|
+
logger.debug "received a semaphore, continuing"
|
345
|
+
else
|
346
|
+
logger.debug "did not receive a semaphore, initializing"
|
347
|
+
semaphore = Leeroy::Types::Semaphore.new(semaphore)
|
348
|
+
end
|
349
|
+
|
350
|
+
run_params = Leeroy::Types::Mash.new
|
351
|
+
run_params.bucket = semaphore.bucket
|
352
|
+
run_params.key = semaphore.object
|
353
|
+
|
354
|
+
# is the object present in S3?
|
355
|
+
resp = checkSemaphore(semaphore)
|
356
|
+
|
357
|
+
if checkSemaphore(semaphore)
|
358
|
+
logger.debug "#{semaphore} present, deleting"
|
359
|
+
resp = s3Request(:delete_object, run_params)
|
360
|
+
else
|
361
|
+
logger.debug "#{semaphore} not present, continuing"
|
362
|
+
end
|
363
|
+
|
364
|
+
semaphore
|
365
|
+
|
366
|
+
rescue StandardError => e
|
367
|
+
raise e
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def checkSemaphore(semaphore)
|
372
|
+
begin
|
373
|
+
unless semaphore.kind_of?(Leeroy::Types::Semaphore)
|
374
|
+
semaphore = Leeroy::Types::Semaphore.new(semaphore)
|
375
|
+
end
|
376
|
+
|
377
|
+
run_params = Leeroy::Types::Mash.new
|
378
|
+
run_params.bucket = semaphore.bucket
|
379
|
+
run_params.key = semaphore.object
|
380
|
+
|
381
|
+
# is the object present in S3?
|
382
|
+
logger.debug "checking for presence of #{semaphore}"
|
383
|
+
resp = s3Request(:head_object, run_params)
|
384
|
+
|
385
|
+
if resp.delete_marker.nil?
|
386
|
+
resp
|
387
|
+
else
|
388
|
+
logger.debug "#{semaphore} already deleted"
|
389
|
+
nil
|
390
|
+
end
|
391
|
+
|
392
|
+
rescue Aws::S3::Errors::NotFound => e
|
393
|
+
logger.debug "#{semaphore} not found"
|
394
|
+
nil
|
395
|
+
|
396
|
+
rescue StandardError => e
|
397
|
+
raise e
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
require 'leeroy/types/mash'
|
4
|
+
require 'leeroy/helpers/logging'
|
5
|
+
|
6
|
+
module Leeroy
|
7
|
+
module Helpers
|
8
|
+
module Dumpable
|
9
|
+
|
10
|
+
attr_accessor :dump_properties
|
11
|
+
|
12
|
+
def dump
|
13
|
+
begin
|
14
|
+
logger.debug "beginning dump"
|
15
|
+
|
16
|
+
dump_mash = Leeroy::Types::Mash.new
|
17
|
+
|
18
|
+
dump_properties = self.dump_properties
|
19
|
+
logger.debug "dump_properties: #{dump_properties.inspect}"
|
20
|
+
|
21
|
+
if dump_properties.length == 0
|
22
|
+
logger.warn "dumping an object with no dump_properties set, this is unlikely to end well"
|
23
|
+
end
|
24
|
+
|
25
|
+
self.dump_properties.each do |property|
|
26
|
+
logger.debug "dumping property '#{property.to_s}'"
|
27
|
+
if self.respond_to?(property.to_sym)
|
28
|
+
begin
|
29
|
+
raw = self.fetch(property.to_s)
|
30
|
+
|
31
|
+
# logger.debug "raw: #{raw.inspect}"
|
32
|
+
|
33
|
+
cooked = raw.respond_to?(:dumper) ? raw.dumper : raw
|
34
|
+
|
35
|
+
# logger.debug "cooked: #{cooked.inspect}"
|
36
|
+
|
37
|
+
dump_mash.store(property.to_s, cooked)
|
38
|
+
|
39
|
+
rescue KeyError => e
|
40
|
+
logger.debug e.message
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
logger.debug "dump_mash: #{dump_mash.inspect}"
|
46
|
+
|
47
|
+
MultiJson.dump(dump_mash.to_hash)
|
48
|
+
|
49
|
+
rescue StandardError => e
|
50
|
+
raise e
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def dumper
|
55
|
+
begin
|
56
|
+
dump_hash = {}
|
57
|
+
|
58
|
+
self.dump_properties.each do |property|
|
59
|
+
the_prop = self.send(property.to_sym)
|
60
|
+
dump_hash[property] = the_prop.respond_to?(:dumper) ? the_prop.dumper : the_prop
|
61
|
+
end
|
62
|
+
|
63
|
+
dump_hash
|
64
|
+
|
65
|
+
rescue StandardError => e
|
66
|
+
raise e
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'awesome_print'
|
2
|
+
|
3
|
+
require 'leeroy/env'
|
4
|
+
require 'leeroy/helpers'
|
5
|
+
|
6
|
+
module Leeroy
|
7
|
+
module Helpers
|
8
|
+
module Env
|
9
|
+
include Leeroy::Helpers
|
10
|
+
|
11
|
+
attr_reader :env
|
12
|
+
|
13
|
+
def checkEnv(param, check = lambda { |x| ! x.nil? }, errmsg = "You must provide #{param} in the environment.", env = self.env)
|
14
|
+
begin
|
15
|
+
logger.debug "checking for '#{param}' in environment"
|
16
|
+
|
17
|
+
# get param from env
|
18
|
+
candidate = env.fetch(param, nil)
|
19
|
+
logger.debug "candidate: #{candidate}"
|
20
|
+
|
21
|
+
# check it against the check
|
22
|
+
check_passed = check.call(candidate)
|
23
|
+
logger.debug "check_passed: #{check_passed}"
|
24
|
+
|
25
|
+
if check_passed
|
26
|
+
candidate
|
27
|
+
else
|
28
|
+
raise errmsg
|
29
|
+
end
|
30
|
+
|
31
|
+
rescue NoMethodError => e
|
32
|
+
logger.error "unable to read environment! env: #{env.inspect}"
|
33
|
+
raise e
|
34
|
+
|
35
|
+
rescue StandardError => e
|
36
|
+
raise e
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'yell'
|
2
|
+
require 'yell-adapters-syslog'
|
3
|
+
|
4
|
+
require 'leeroy/helpers'
|
5
|
+
require 'leeroy/helpers/env'
|
6
|
+
|
7
|
+
module Leeroy
|
8
|
+
module Helpers
|
9
|
+
module Logging
|
10
|
+
include Leeroy::Helpers
|
11
|
+
|
12
|
+
# constants
|
13
|
+
TRUNCATE_THRESHOLD = 60
|
14
|
+
|
15
|
+
TRACE_FORMAT = "%d [%5L] %p (%M): %m"
|
16
|
+
TRACE_LEVELS = [:debug]
|
17
|
+
|
18
|
+
# define a logger
|
19
|
+
# Yell.new :stderr, name: self.class.to_s, format: TRACE_FORMAT, trace: TRACE_LEVELS, level: :debug
|
20
|
+
if ENV['ENVIRONMENT'] == 'production'
|
21
|
+
Yell.new :syslog, name: self.class.to_s, format: TRACE_FORMAT, trace: TRACE_LEVELS, level: :info, facility: :user
|
22
|
+
else
|
23
|
+
Yell.new :stderr, name: self.class.to_s, format: TRACE_FORMAT, trace: TRACE_LEVELS, level: :debug
|
24
|
+
end
|
25
|
+
|
26
|
+
# make this class loggable
|
27
|
+
self.class.send :include, Yell::Loggable
|
28
|
+
|
29
|
+
# make logger an instance method
|
30
|
+
def logger
|
31
|
+
self.class.logger
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'smart_polling'
|
2
|
+
|
3
|
+
require 'leeroy/helpers'
|
4
|
+
require 'leeroy/helpers/env'
|
5
|
+
require 'leeroy/types/mash'
|
6
|
+
|
7
|
+
module Leeroy
|
8
|
+
module Helpers
|
9
|
+
module Polling
|
10
|
+
include Leeroy::Helpers
|
11
|
+
include Leeroy::Helpers::Env
|
12
|
+
|
13
|
+
POLL_CALLBACK = lambda {|x| raise 'this is the default callback, did you forget to set the poll_callback attribute?'}
|
14
|
+
POLL_TIMEOUT = 600 # seconds
|
15
|
+
POLL_INTERVAL = 10 # seconds
|
16
|
+
|
17
|
+
attr_accessor :poll_callback, :poll_timeout, :poll_interval, :poll_response
|
18
|
+
|
19
|
+
def poll(*args)
|
20
|
+
begin
|
21
|
+
logger.debug "beginning to poll"
|
22
|
+
|
23
|
+
callback = self.poll_callback
|
24
|
+
raise "callback must be a Proc" unless callback.kind_of?(Proc)
|
25
|
+
|
26
|
+
timeout = self.poll_timeout
|
27
|
+
interval = self.poll_interval
|
28
|
+
|
29
|
+
logger.debug "callback: #{callback.inspect}"
|
30
|
+
logger.debug "polling every #{interval} seconds for #{timeout} seconds"
|
31
|
+
|
32
|
+
SmartPolling.poll(timeout: timeout, interval: interval) do
|
33
|
+
poll_arg = args[0]
|
34
|
+
logger.debug "poll_arg: #{poll_arg.inspect}"
|
35
|
+
self.poll_response = callback.call(poll_arg)
|
36
|
+
end
|
37
|
+
|
38
|
+
response = self.poll_response
|
39
|
+
logger.debug "response: #{response.inspect}"
|
40
|
+
|
41
|
+
response
|
42
|
+
|
43
|
+
rescue Interrupt => e
|
44
|
+
logger.fatal "Keyboard interrupt"
|
45
|
+
raise e
|
46
|
+
|
47
|
+
rescue StandardError => e
|
48
|
+
raise e
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(*args, &block)
|
53
|
+
begin
|
54
|
+
super
|
55
|
+
|
56
|
+
@poll_callback = POLL_CALLBACK
|
57
|
+
@poll_timeout = POLL_TIMEOUT
|
58
|
+
@poll_interval = POLL_INTERVAL
|
59
|
+
|
60
|
+
rescue StandardError => e
|
61
|
+
raise e
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'fcntl'
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
require 'leeroy/helpers'
|
5
|
+
|
6
|
+
module Leeroy
|
7
|
+
module Helpers
|
8
|
+
module State
|
9
|
+
include Leeroy::Helpers
|
10
|
+
|
11
|
+
attr_accessor :state
|
12
|
+
|
13
|
+
def state_from_pipe(state = {}, global_options = self.global_options)
|
14
|
+
begin
|
15
|
+
state.merge(load_state)
|
16
|
+
|
17
|
+
rescue StandardError => e
|
18
|
+
raise e
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_state
|
23
|
+
begin
|
24
|
+
logger.debug "loading state from stdin if available"
|
25
|
+
|
26
|
+
_stdin? ? MultiJson.load($stdin.read, :symbolize_keys => true) : {}
|
27
|
+
|
28
|
+
rescue StandardError => e
|
29
|
+
raise e
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def dump_state
|
34
|
+
logger.debug "dumping state to stdout"
|
35
|
+
$stdout.puts self.state.dump
|
36
|
+
end
|
37
|
+
|
38
|
+
def rotate_task_metadata
|
39
|
+
logger.debug "rotating task metadata"
|
40
|
+
if self.state.metadata.task?
|
41
|
+
self.state.metadata.previous = self.state.metadata.task
|
42
|
+
end
|
43
|
+
self.state.metadata.task = self.class.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
"#{self.metadata},#{self.data}"
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# this is preposterous BS and doubtless not portable to Windows
|
53
|
+
def _stdin?
|
54
|
+
$stdin.fcntl(Fcntl::F_GETFL, 0) == 0
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|