staugaard-cloudmaster 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION.yml +4 -0
- data/bin/cloudmaster +45 -0
- data/lib/AWS/AWS.rb +3 -0
- data/lib/AWS/EC2.rb +14 -0
- data/lib/AWS/S3.rb +14 -0
- data/lib/AWS/SQS.rb +14 -0
- data/lib/AWS/SimpleDB.rb +14 -0
- data/lib/MockAWS/EC2.rb +119 -0
- data/lib/MockAWS/S3.rb +39 -0
- data/lib/MockAWS/SQS.rb +82 -0
- data/lib/MockAWS/SimpleDB.rb +46 -0
- data/lib/MockAWS/clock.rb +67 -0
- data/lib/OriginalAWS/AWS.rb +475 -0
- data/lib/OriginalAWS/EC2.rb +783 -0
- data/lib/OriginalAWS/S3.rb +559 -0
- data/lib/OriginalAWS/SQS.rb +159 -0
- data/lib/OriginalAWS/SimpleDB.rb +460 -0
- data/lib/RetryAWS/EC2.rb +88 -0
- data/lib/RetryAWS/S3.rb +77 -0
- data/lib/RetryAWS/SQS.rb +109 -0
- data/lib/RetryAWS/SimpleDB.rb +118 -0
- data/lib/SafeAWS/EC2.rb +63 -0
- data/lib/SafeAWS/S3.rb +56 -0
- data/lib/SafeAWS/SQS.rb +75 -0
- data/lib/SafeAWS/SimpleDB.rb +88 -0
- data/lib/aws_context.rb +165 -0
- data/lib/basic_configuration.rb +120 -0
- data/lib/clock.rb +10 -0
- data/lib/factory.rb +14 -0
- data/lib/file_logger.rb +36 -0
- data/lib/inifile.rb +148 -0
- data/lib/instance_logger.rb +25 -0
- data/lib/logger_factory.rb +38 -0
- data/lib/periodic.rb +29 -0
- data/lib/string_logger.rb +29 -0
- data/lib/sys_logger.rb +40 -0
- data/lib/user_data.rb +30 -0
- data/test/aws-config.ini +9 -0
- data/test/cloudmaster-tests.rb +329 -0
- data/test/configuration-test.rb +62 -0
- data/test/daytime-policy-tests.rb +47 -0
- data/test/enumerator-test.rb +47 -0
- data/test/fixed-policy-tests.rb +50 -0
- data/test/instance-pool-test.rb +359 -0
- data/test/instance-test.rb +98 -0
- data/test/job-policy-test.rb +95 -0
- data/test/manual-policy-tests.rb +63 -0
- data/test/named-queue-test.rb +90 -0
- data/test/resource-policy-tests.rb +126 -0
- data/test/suite +17 -0
- data/test/test-config.ini +47 -0
- metadata +111 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
# Sample Ruby code for the O'Reilly book "Programming Amazon Web
|
2
|
+
# Services" by James Murty.
|
3
|
+
#
|
4
|
+
# This code was written for Ruby version 1.8.6 or greater.
|
5
|
+
#
|
6
|
+
# The SQS module implements the Query API of the Amazon Simple Queue
|
7
|
+
# Service.
|
8
|
+
require 'AWS'
|
9
|
+
|
10
|
+
class SQS
|
11
|
+
include AWS # Include the AWS module as a mixin
|
12
|
+
|
13
|
+
ENDPOINT_URI = URI.parse("https://queue.amazonaws.com/")
|
14
|
+
API_VERSION = '2008-01-01'
|
15
|
+
SIGNATURE_VERSION = '1'
|
16
|
+
|
17
|
+
HTTP_METHOD = 'POST' # 'GET'
|
18
|
+
|
19
|
+
|
20
|
+
def list_queues(queue_name_prefix=nil)
|
21
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
22
|
+
{
|
23
|
+
'Action' => 'ListQueues',
|
24
|
+
'QueueNamePrefix' => queue_name_prefix
|
25
|
+
})
|
26
|
+
|
27
|
+
response = do_query(HTTP_METHOD, ENDPOINT_URI, parameters)
|
28
|
+
|
29
|
+
queue_names = []
|
30
|
+
xml_doc = REXML::Document.new(response.body)
|
31
|
+
|
32
|
+
xml_doc.elements.each('//QueueUrl') do |queue_url|
|
33
|
+
queue_names << queue_url.text
|
34
|
+
end
|
35
|
+
|
36
|
+
return queue_names
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_queue(queue_name, visibility_timeout_secs=nil)
|
40
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
41
|
+
{
|
42
|
+
'Action' => 'CreateQueue',
|
43
|
+
'QueueName' => queue_name,
|
44
|
+
'DefaultVisibilityTimeout' => visibility_timeout_secs
|
45
|
+
})
|
46
|
+
|
47
|
+
response = do_query(HTTP_METHOD, ENDPOINT_URI, parameters)
|
48
|
+
|
49
|
+
xml_doc = REXML::Document.new(response.body)
|
50
|
+
return xml_doc.elements['//QueueUrl'].text
|
51
|
+
end
|
52
|
+
|
53
|
+
def delete_queue(queue_url)
|
54
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
55
|
+
{
|
56
|
+
'Action' => 'DeleteQueue'
|
57
|
+
})
|
58
|
+
|
59
|
+
response = do_query(HTTP_METHOD, URI.parse(queue_url), parameters)
|
60
|
+
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_queue_attributes(queue_url, attribute='All')
|
65
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
66
|
+
{
|
67
|
+
'Action' => 'GetQueueAttributes',
|
68
|
+
'AttributeName' => attribute
|
69
|
+
})
|
70
|
+
|
71
|
+
response = do_query(HTTP_METHOD, URI.parse(queue_url), parameters)
|
72
|
+
|
73
|
+
attributes = {}
|
74
|
+
xml_doc = REXML::Document.new(response.body)
|
75
|
+
|
76
|
+
xml_doc.elements.each('//Attribute') do |att|
|
77
|
+
name = att.elements['Name'].text
|
78
|
+
# All currently supported attributes have integer values
|
79
|
+
value = att.elements['Value'].text.to_i
|
80
|
+
|
81
|
+
attributes[name] = value
|
82
|
+
end
|
83
|
+
|
84
|
+
return attributes
|
85
|
+
end
|
86
|
+
|
87
|
+
def set_queue_attribute(queue_url, value, attribute='VisibilityTimeout')
|
88
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
89
|
+
{
|
90
|
+
'Action' => 'SetQueueAttributes',
|
91
|
+
'Attribute.Name' => attribute,
|
92
|
+
'Attribute.Value' => value
|
93
|
+
})
|
94
|
+
|
95
|
+
response = do_query(HTTP_METHOD, URI.parse(queue_url), parameters)
|
96
|
+
return true
|
97
|
+
end
|
98
|
+
|
99
|
+
def send_message(queue_url, message_body, encode=false)
|
100
|
+
message_body = encode_base64(message_body) if encode
|
101
|
+
|
102
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
103
|
+
{
|
104
|
+
'Action' => 'SendMessage',
|
105
|
+
'MessageBody' => message_body
|
106
|
+
})
|
107
|
+
|
108
|
+
response = do_query(HTTP_METHOD, URI.parse(queue_url), parameters)
|
109
|
+
|
110
|
+
xml_doc = REXML::Document.new(response.body)
|
111
|
+
return xml_doc.elements['//MessageId'].text
|
112
|
+
end
|
113
|
+
|
114
|
+
def receive_messages(queue_url, maximum=1, visibility_timeout=nil)
|
115
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
116
|
+
{
|
117
|
+
'Action' => 'ReceiveMessage',
|
118
|
+
'MaxNumberOfMessages' => maximum,
|
119
|
+
'VisibilityTimeout' => visibility_timeout
|
120
|
+
})
|
121
|
+
|
122
|
+
response = do_query(HTTP_METHOD, URI.parse(queue_url), parameters)
|
123
|
+
msgs = []
|
124
|
+
|
125
|
+
xml_doc = REXML::Document.new(response.body)
|
126
|
+
|
127
|
+
xml_doc.elements.each('//Message') do |msg|
|
128
|
+
msgs << {
|
129
|
+
:id => msg.elements['MessageId'].text,
|
130
|
+
:receipt_handle => msg.elements['ReceiptHandle'].text,
|
131
|
+
:md5_of_body => msg.elements['MD5OfBody'].text,
|
132
|
+
:body => msg.elements['Body'].text
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
return msgs
|
137
|
+
end
|
138
|
+
|
139
|
+
def delete_message(queue_url, receipt_handle)
|
140
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
141
|
+
{
|
142
|
+
'Action' => 'DeleteMessage',
|
143
|
+
'ReceiptHandle' => receipt_handle
|
144
|
+
})
|
145
|
+
|
146
|
+
response = do_query(HTTP_METHOD, URI.parse(queue_url), parameters)
|
147
|
+
|
148
|
+
return true
|
149
|
+
end
|
150
|
+
|
151
|
+
def receive_new_messages(msgs, queue_url, max_count=1)
|
152
|
+
new_msgs = receive_messages(queue_url, max_count)
|
153
|
+
new_msgs.each do |new|
|
154
|
+
msgs << new unless msgs.find{|old| old[:id] == new[:id]}
|
155
|
+
end
|
156
|
+
return msgs
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
@@ -0,0 +1,460 @@
|
|
1
|
+
# Sample Ruby code for the O'Reilly book "Using AWS Infrastructure
|
2
|
+
# Services" by James Murty.
|
3
|
+
#
|
4
|
+
# This code was written for Ruby version 1.8.6 or greater.
|
5
|
+
#
|
6
|
+
# The SimpleDB module implements the Query API of the Amazon SimpleDB
|
7
|
+
# Service.
|
8
|
+
|
9
|
+
require 'AWS'
|
10
|
+
require 'bigdecimal'
|
11
|
+
|
12
|
+
class SimpleDB
|
13
|
+
include AWS # Include the AWS module as a mixin
|
14
|
+
|
15
|
+
ENDPOINT_URI = URI.parse("https://sdb.amazonaws.com/")
|
16
|
+
API_VERSION = '2007-11-07'
|
17
|
+
SIGNATURE_VERSION = '1'
|
18
|
+
|
19
|
+
HTTP_METHOD = 'POST' # 'GET'
|
20
|
+
|
21
|
+
attr_reader :prior_box_usage
|
22
|
+
attr_reader :total_box_usage
|
23
|
+
|
24
|
+
|
25
|
+
def do_sdb_query(parameters)
|
26
|
+
response = do_query(HTTP_METHOD, ENDPOINT_URI, parameters)
|
27
|
+
xml_doc = REXML::Document.new(response.body)
|
28
|
+
|
29
|
+
@total_box_usage = 0 if @total_box_usage.nil?
|
30
|
+
|
31
|
+
@prior_box_usage = xml_doc.elements['//BoxUsage'].text.to_f
|
32
|
+
@total_box_usage += @prior_box_usage
|
33
|
+
|
34
|
+
return xml_doc
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def encode_boolean(value)
|
39
|
+
if value
|
40
|
+
return '!b'
|
41
|
+
else
|
42
|
+
return '!B'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def decode_boolean(value_str)
|
48
|
+
if value_str == '!B'
|
49
|
+
return false
|
50
|
+
elsif value_str == '!b'
|
51
|
+
return true
|
52
|
+
else
|
53
|
+
raise "Cannot decode boolean from string: #{value_str}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def encode_date(value)
|
59
|
+
return "!d" + value.getutc.iso8601
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def decode_date(value_str)
|
64
|
+
if value_str[0..1] == '!d'
|
65
|
+
return Time.parse(value_str[2..-1])
|
66
|
+
else
|
67
|
+
raise "Cannot decode date from string: #{value_str}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def encode_integer(value, max_digits=18)
|
73
|
+
upper_bound = (10 ** max_digits)
|
74
|
+
|
75
|
+
if value >= upper_bound or value < -upper_bound
|
76
|
+
raise "Integer #{value} is outside encoding range (-#{upper_bound} " +
|
77
|
+
"to #{upper_bound - 1})"
|
78
|
+
end
|
79
|
+
|
80
|
+
if value < 0
|
81
|
+
return "!I" + format("%0#{max_digits}d", upper_bound + value)
|
82
|
+
else
|
83
|
+
return "!i" + format("%0#{max_digits}d", value)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
def decode_integer(value_str)
|
89
|
+
if value_str[0..1] == '!I'
|
90
|
+
# Encoded value is a negative integer
|
91
|
+
max_digits = value_str.size - 2
|
92
|
+
upper_bound = (10 ** max_digits)
|
93
|
+
|
94
|
+
return value_str[2..-1].to_i - upper_bound
|
95
|
+
elsif value_str[0..1] == '!i'
|
96
|
+
# Encoded value is a positive integer
|
97
|
+
return value_str[2..-1].to_i
|
98
|
+
else
|
99
|
+
raise "Cannot decode integer from string: #{value_str}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
def encode_float(value, max_exp_digits=2, max_precision_digits=15)
|
105
|
+
exp_midpoint = (10 ** max_exp_digits) / 2
|
106
|
+
|
107
|
+
sign, fraction, base, exponent = BigDecimal(value.to_s).split
|
108
|
+
|
109
|
+
if exponent >= exp_midpoint or exponent < -exp_midpoint
|
110
|
+
raise "Exponent #{exponent} is outside encoding range " +
|
111
|
+
"(-#{exp_midpoint} " + "to #{exp_midpoint - 1})"
|
112
|
+
end
|
113
|
+
|
114
|
+
if fraction.size > max_precision_digits
|
115
|
+
# Round fraction value if it exceeds allowed precision.
|
116
|
+
fraction_str = fraction[0...max_precision_digits] + '.' +
|
117
|
+
fraction[max_precision_digits..-1]
|
118
|
+
fraction = BigDecimal(fraction_str).round(0).split[1]
|
119
|
+
elsif fraction.size < max_precision_digits
|
120
|
+
# Right-pad fraction with zeros if it is too short.
|
121
|
+
fraction = fraction + ('0' * (max_precision_digits - fraction.size))
|
122
|
+
end
|
123
|
+
|
124
|
+
# The zero value is a special case, for which the exponent must be 0
|
125
|
+
exponent = -exp_midpoint if value == 0
|
126
|
+
|
127
|
+
if sign == 1
|
128
|
+
return format("!f%0#{max_exp_digits}d", exp_midpoint + exponent) +
|
129
|
+
format("!%0#{max_precision_digits}d", fraction.to_i)
|
130
|
+
else
|
131
|
+
fraction_upper_bound = (10 ** max_precision_digits)
|
132
|
+
diff_fraction = fraction_upper_bound - BigDecimal(fraction)
|
133
|
+
return format("!F%0#{max_exp_digits}d", exp_midpoint - exponent) +
|
134
|
+
format("!%0#{max_precision_digits}d", diff_fraction)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
def decode_float(value_str)
|
140
|
+
prefix = value_str[0..1]
|
141
|
+
|
142
|
+
if prefix != '!f' and prefix != '!F'
|
143
|
+
raise "Cannot decode float from string: #{value_str}"
|
144
|
+
end
|
145
|
+
|
146
|
+
value_str =~ /![fF]([0-9]+)!([0-9]+)/
|
147
|
+
exp_str = $1
|
148
|
+
fraction_str = $2
|
149
|
+
|
150
|
+
max_exp_digits = exp_str.size
|
151
|
+
exp_midpoint = (10 ** max_exp_digits) / 2
|
152
|
+
max_precision_digits = fraction_str.size
|
153
|
+
|
154
|
+
if prefix == '!F'
|
155
|
+
sign = -1
|
156
|
+
exp = exp_midpoint - exp_str.to_i
|
157
|
+
|
158
|
+
fraction_upper_bound = (10 ** max_precision_digits)
|
159
|
+
fraction = fraction_upper_bound - BigDecimal(fraction_str)
|
160
|
+
else
|
161
|
+
sign = 1
|
162
|
+
exp = exp_str.to_i - exp_midpoint
|
163
|
+
|
164
|
+
fraction = BigDecimal(fraction_str)
|
165
|
+
end
|
166
|
+
|
167
|
+
return sign * "0.#{fraction.to_i}".to_f * (10 ** exp)
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
def encode_attribute_value(value)
|
172
|
+
if value == true or value == false
|
173
|
+
return encode_boolean(value)
|
174
|
+
elsif value.is_a? Time
|
175
|
+
return encode_date(value)
|
176
|
+
elsif value.is_a? Integer
|
177
|
+
return encode_integer(value)
|
178
|
+
elsif value.is_a? Numeric
|
179
|
+
return encode_float(value)
|
180
|
+
else
|
181
|
+
# No type-specific encoding is available, so we simply convert
|
182
|
+
# the value to a string.
|
183
|
+
return value.to_s
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
def decode_attribute_value(value_str)
|
189
|
+
return '' if value_str.nil?
|
190
|
+
|
191
|
+
# Check whether the '!' flag is present to indicate an encoded value
|
192
|
+
return value_str if value_str[0..0] != '!'
|
193
|
+
|
194
|
+
prefix = value_str[0..1].downcase
|
195
|
+
if prefix == '!b'
|
196
|
+
return decode_boolean(value_str)
|
197
|
+
elsif prefix == '!d'
|
198
|
+
return decode_date(value_str)
|
199
|
+
elsif prefix == '!i'
|
200
|
+
return decode_integer(value_str)
|
201
|
+
elsif prefix == '!f'
|
202
|
+
return decode_float(value_str)
|
203
|
+
else
|
204
|
+
return value_str
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
def list_domains(max_domains=100)
|
210
|
+
more_domains = true
|
211
|
+
next_token = nil
|
212
|
+
domain_names = []
|
213
|
+
|
214
|
+
while more_domains
|
215
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
216
|
+
{
|
217
|
+
'Action' => 'ListDomains',
|
218
|
+
'MaxNumberOfDomains' => max_domains,
|
219
|
+
'NextToken' => next_token
|
220
|
+
})
|
221
|
+
|
222
|
+
xml_doc = do_sdb_query(parameters)
|
223
|
+
|
224
|
+
xml_doc.elements.each('//DomainName') do |name|
|
225
|
+
domain_names << name.text
|
226
|
+
end
|
227
|
+
|
228
|
+
# If we receive a NextToken element, perform a follow-up operation
|
229
|
+
# to retrieve the next set of domain names.
|
230
|
+
next_token = xml_doc.elements['//NextToken/text()']
|
231
|
+
more_domains = !next_token.nil?
|
232
|
+
end
|
233
|
+
|
234
|
+
return domain_names
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
def create_domain(domain_name)
|
239
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
240
|
+
{
|
241
|
+
'Action' => 'CreateDomain',
|
242
|
+
'DomainName' => domain_name
|
243
|
+
})
|
244
|
+
|
245
|
+
do_sdb_query(parameters)
|
246
|
+
return true
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
def delete_domain(domain_name)
|
251
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
252
|
+
{
|
253
|
+
'Action' => 'DeleteDomain',
|
254
|
+
'DomainName' => domain_name
|
255
|
+
})
|
256
|
+
|
257
|
+
do_sdb_query(parameters)
|
258
|
+
return true
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
def build_attribute_params(attributes={}, replace=false)
|
263
|
+
attribute_params = {}
|
264
|
+
index = 0
|
265
|
+
|
266
|
+
attributes.each do |attrib_name, attrib_value|
|
267
|
+
attrib_value = [attrib_value] if not attrib_value.is_a? Array
|
268
|
+
|
269
|
+
attrib_value.each do |value|
|
270
|
+
attribute_params["Attribute.#{index}.Name"] = attrib_name
|
271
|
+
if not value.nil?
|
272
|
+
if respond_to? :encode_attribute_value
|
273
|
+
# Automatically encode attribute values if the method
|
274
|
+
# encode_attribute_value is available in this class
|
275
|
+
value = encode_attribute_value(value)
|
276
|
+
end
|
277
|
+
attribute_params["Attribute.#{index}.Value"] = value
|
278
|
+
end
|
279
|
+
# Add a Replace parameter for the attribute if the replace flag is set
|
280
|
+
attribute_params["Attribute.#{index}.Replace"] = 'true' if replace
|
281
|
+
index += 1
|
282
|
+
end if attrib_value
|
283
|
+
end
|
284
|
+
|
285
|
+
return attribute_params
|
286
|
+
end
|
287
|
+
|
288
|
+
|
289
|
+
def put_attributes(domain_name, item_name, attributes, replace=false)
|
290
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
291
|
+
{
|
292
|
+
'Action' => 'PutAttributes',
|
293
|
+
'DomainName' => domain_name,
|
294
|
+
'ItemName' => item_name
|
295
|
+
})
|
296
|
+
|
297
|
+
parameters.merge!(build_attribute_params(attributes, replace))
|
298
|
+
|
299
|
+
do_sdb_query(parameters)
|
300
|
+
return true
|
301
|
+
end
|
302
|
+
|
303
|
+
|
304
|
+
def delete_attributes(domain_name, item_name, attributes={})
|
305
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
306
|
+
{
|
307
|
+
'Action' => 'DeleteAttributes',
|
308
|
+
'DomainName' => domain_name,
|
309
|
+
'ItemName' => item_name
|
310
|
+
})
|
311
|
+
|
312
|
+
parameters.merge!(build_attribute_params(attributes))
|
313
|
+
|
314
|
+
do_sdb_query(parameters)
|
315
|
+
return true
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
def get_attributes(domain_name, item_name, attribute_name=nil)
|
320
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
321
|
+
{
|
322
|
+
'Action' => 'GetAttributes',
|
323
|
+
'DomainName' => domain_name,
|
324
|
+
'ItemName' => item_name,
|
325
|
+
'AttributeName' => attribute_name
|
326
|
+
})
|
327
|
+
|
328
|
+
xml_doc = do_sdb_query(parameters)
|
329
|
+
|
330
|
+
attributes = {}
|
331
|
+
xml_doc.elements.each('//Attribute') do |attribute_node|
|
332
|
+
attr_name = attribute_node.elements['Name'].text
|
333
|
+
value = attribute_node.elements['Value'].text
|
334
|
+
|
335
|
+
if respond_to? :decode_attribute_value
|
336
|
+
# Automatically decode attribute values if the method
|
337
|
+
# decode_attribute_value is available in this class
|
338
|
+
value = decode_attribute_value(value)
|
339
|
+
end
|
340
|
+
|
341
|
+
# An empty attribute value is an empty string, not nil.
|
342
|
+
value = '' if value.nil?
|
343
|
+
|
344
|
+
if attributes.has_key?(attr_name)
|
345
|
+
attributes[attr_name] << value
|
346
|
+
else
|
347
|
+
attributes[attr_name] = [value]
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
if not attribute_name.nil?
|
352
|
+
# If a specific attribute was requested, return only the values array
|
353
|
+
# for this attribute.
|
354
|
+
if not attributes[attribute_name]
|
355
|
+
return []
|
356
|
+
else
|
357
|
+
return attributes[attribute_name]
|
358
|
+
end
|
359
|
+
else
|
360
|
+
return attributes
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
|
365
|
+
def query(domain_name, query_expression=nil, options={:fetch_all=>true})
|
366
|
+
more_items = true
|
367
|
+
next_token = nil
|
368
|
+
item_names = []
|
369
|
+
|
370
|
+
while more_items
|
371
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
372
|
+
{
|
373
|
+
'Action' => 'Query',
|
374
|
+
'DomainName' => domain_name,
|
375
|
+
'QueryExpression' => query_expression,
|
376
|
+
'MaxNumberOfItems' => options[:max_items],
|
377
|
+
'NextToken' => next_token
|
378
|
+
})
|
379
|
+
|
380
|
+
xml_doc = do_sdb_query(parameters)
|
381
|
+
|
382
|
+
xml_doc.elements.each('//ItemName') do |item_name|
|
383
|
+
item_names << item_name.text
|
384
|
+
end
|
385
|
+
|
386
|
+
if xml_doc.elements['//NextToken']
|
387
|
+
next_token = xml_doc.elements['//NextToken'].text.gsub("\n","")
|
388
|
+
more_items = options[:fetch_all]
|
389
|
+
else
|
390
|
+
more_items = false
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
return item_names
|
395
|
+
end
|
396
|
+
|
397
|
+
# The query with attributes feature was added to the S3 API after the
|
398
|
+
# release of "Programming Amazon Web Services" so it is not discussed in
|
399
|
+
# the book's text. For more details, see:
|
400
|
+
# http://www.jamesmurty.com/2008/09/07/samples-for-simpledb-querywithattributes/
|
401
|
+
def query_with_attributes(domain_name, query_expression=nil,
|
402
|
+
attribute_names=[], options={:fetch_all=>true})
|
403
|
+
more_items = true
|
404
|
+
next_token = nil
|
405
|
+
items = []
|
406
|
+
|
407
|
+
while more_items
|
408
|
+
parameters = build_query_params(API_VERSION, SIGNATURE_VERSION,
|
409
|
+
{
|
410
|
+
'Action' => 'QueryWithAttributes',
|
411
|
+
'DomainName' => domain_name,
|
412
|
+
'QueryExpression' => query_expression,
|
413
|
+
'MaxNumberOfItems' => options[:max_items],
|
414
|
+
'NextToken' => next_token
|
415
|
+
},{
|
416
|
+
'AttributeName' => attribute_names,
|
417
|
+
})
|
418
|
+
|
419
|
+
xml_doc = do_sdb_query(parameters)
|
420
|
+
|
421
|
+
xml_doc.elements.each('//Item') do |item_node|
|
422
|
+
item = {'name' => item_node.elements['Name'].text}
|
423
|
+
|
424
|
+
attributes = {}
|
425
|
+
item_node.elements.each('Attribute') do |attribute_node|
|
426
|
+
attr_name = attribute_node.elements['Name'].text
|
427
|
+
value = attribute_node.elements['Value'].text
|
428
|
+
|
429
|
+
if respond_to? :decode_attribute_value
|
430
|
+
# Automatically decode attribute values if the method
|
431
|
+
# decode_attribute_value is available in this class
|
432
|
+
value = decode_attribute_value(value)
|
433
|
+
end
|
434
|
+
|
435
|
+
# An empty attribute value is an empty string, not nil.
|
436
|
+
value = '' if value.nil?
|
437
|
+
|
438
|
+
if attributes.has_key?(attr_name)
|
439
|
+
attributes[attr_name] << value
|
440
|
+
else
|
441
|
+
attributes[attr_name] = [value]
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
item['attributes'] = attributes
|
446
|
+
items << item
|
447
|
+
end
|
448
|
+
|
449
|
+
if xml_doc.elements['//NextToken']
|
450
|
+
next_token = xml_doc.elements['//NextToken'].text.gsub("\n","")
|
451
|
+
more_items = options[:fetch_all]
|
452
|
+
else
|
453
|
+
more_items = false
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
return items
|
458
|
+
end
|
459
|
+
|
460
|
+
end
|
data/lib/RetryAWS/EC2.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'AWS/EC2'
|
2
|
+
|
3
|
+
# RetryAWS wraps the AWS module in exception catcher blocks, so that any
|
4
|
+
# exceptions that are thrown do not affect the caller.
|
5
|
+
#
|
6
|
+
# The RetryEC2, RetrySQS, and RetryS3 log any errors that they encounter, so
|
7
|
+
# that they can be examined later.
|
8
|
+
module RetryAWS
|
9
|
+
# Wrap EC2 functions that we use.
|
10
|
+
# Catch errors and do something reasonable.
|
11
|
+
class EC2
|
12
|
+
def initialize(access_key, secret_key)
|
13
|
+
@ec2 = AWS::EC2.new(access_key, secret_key)
|
14
|
+
@@log = STDOUT
|
15
|
+
@retry_limit = 16
|
16
|
+
end
|
17
|
+
|
18
|
+
def logger=(logger)
|
19
|
+
@@log = logger
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def report_error(res)
|
25
|
+
@@log.puts "error #{$!}"
|
26
|
+
$@.each {|line| @@log.puts " #{line}"}
|
27
|
+
res
|
28
|
+
end
|
29
|
+
|
30
|
+
def retry?(err, retry_time)
|
31
|
+
if err.response.code.to_i >= 500 && retry_time < @retry_limit
|
32
|
+
sleep retry_time
|
33
|
+
return retry_time * 2
|
34
|
+
end
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
public
|
39
|
+
|
40
|
+
def describe_images(options={})
|
41
|
+
retry_time = 1
|
42
|
+
begin
|
43
|
+
@ec2.describe_images(options)
|
44
|
+
rescue AWS::ServiceError => err
|
45
|
+
retry if retry_time = retry?(err, retry_time)
|
46
|
+
report_error []
|
47
|
+
rescue
|
48
|
+
report_error []
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def describe_instances(*instance_ids)
|
53
|
+
retry_time = 1
|
54
|
+
begin
|
55
|
+
@ec2.describe_instances(*instance_ids)
|
56
|
+
rescue AWS::ServiceError => err
|
57
|
+
retry if retry_time = retry?(err, retry_time)
|
58
|
+
report_error []
|
59
|
+
rescue
|
60
|
+
report_error []
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def run_instances(image_id, min_count=1, max_count=min_count, options={})
|
65
|
+
retry_time = 1
|
66
|
+
begin
|
67
|
+
@ec2.run_instances(image_id, min_count, max_count, options)
|
68
|
+
rescue AWS::ServiceError => err
|
69
|
+
retry if retry_time = retry?(err, retry_time)
|
70
|
+
report_error []
|
71
|
+
rescue
|
72
|
+
report_error []
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def terminate_instances(*instance_ids)
|
77
|
+
retry_time = 1
|
78
|
+
begin
|
79
|
+
@ec2.terminate_instances(*instance_ids)
|
80
|
+
rescue AWS::ServiceError => err
|
81
|
+
retry if retry_time = retry?(err, retry_time)
|
82
|
+
report_error []
|
83
|
+
rescue
|
84
|
+
report_error []
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|