staugaard-cloudmaster 0.1.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.
- 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 =~ /!([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
|