| Class | AWS::Base |
| In: |
lib/AWS.rb
|
| Parent: | Object |
This class provides all the methods for using the EC2 or ELB service including the handling of header signing and other security concerns. This class uses the Net::HTTP library to interface with the AWS Query API interface. You should not instantiate this directly, instead you should setup an instance of ‘AWS::EC2::Base’ or ‘AWS::ELB::Base’.
| port | [R] | |
| proxy_server | [R] | |
| server | [R] | |
| use_ssl | [R] |
@option options [String] :access_key_id ("") The user‘s AWS Access Key ID @option options [String] :secret_access_key ("") The user‘s AWS Secret Access Key @option options [Boolean] :use_ssl (true) Connect using SSL? @option options [String] :server ("ec2.amazonaws.com") The server API endpoint host @option options [String] :proxy_server (nil) An HTTP proxy server FQDN @return [Object] the object.
# File lib/AWS.rb, line 123
123: def initialize( options = {} )
124:
125: options = { :access_key_id => "",
126: :secret_access_key => "",
127: :use_ssl => true,
128: :server => default_host,
129: :proxy_server => nil
130: }.merge(options)
131:
132: @server = options[:server]
133: @proxy_server = options[:proxy_server]
134: @use_ssl = options[:use_ssl]
135:
136: raise ArgumentError, "No :access_key_id provided" if options[:access_key_id].nil? || options[:access_key_id].empty?
137: raise ArgumentError, "No :secret_access_key provided" if options[:secret_access_key].nil? || options[:secret_access_key].empty?
138: raise ArgumentError, "No :use_ssl value provided" if options[:use_ssl].nil?
139: raise ArgumentError, "Invalid :use_ssl value provided, only 'true' or 'false' allowed" unless options[:use_ssl] == true || options[:use_ssl] == false
140: raise ArgumentError, "No :server provided" if options[:server].nil? || options[:server].empty?
141:
142: if options[:port]
143: # user-specified port
144: @port = options[:port]
145: elsif @use_ssl
146: # https
147: @port = 443
148: else
149: # http
150: @port = 80
151: end
152:
153: @access_key_id = options[:access_key_id]
154: @secret_access_key = options[:secret_access_key]
155:
156: # Use proxy server if defined
157: # Based on patch by Mathias Dalheimer. 20070217
158: proxy = @proxy_server ? URI.parse(@proxy_server) : OpenStruct.new
159: @http = Net::HTTP::Proxy( proxy.host,
160: proxy.port,
161: proxy.user,
162: proxy.password).new(options[:server], @port)
163:
164: @http.use_ssl = @use_ssl
165:
166: # Don't verify the SSL certificates. Avoids SSL Cert warning in log on every GET.
167: @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
168:
169: end
If :user_data is passed in then URL escape and Base64 encode it as needed. Need for URL Escape + Base64 encoding is determined by :base64_encoded param.
# File lib/AWS.rb, line 174
174: def extract_user_data( options = {} )
175: return unless options[:user_data]
176: if options[:user_data]
177: if options[:base64_encoded]
178: Base64.encode64(options[:user_data]).gsub(/\n/, "").strip()
179: else
180: options[:user_data]
181: end
182: end
183: end
Raises the appropriate error if the specified Net::HTTPResponse object contains an AWS error; returns false otherwise.
# File lib/AWS.rb, line 290
290: def aws_error?(response)
291:
292: # return false if we got a HTTP 200 code,
293: # otherwise there is some type of error (40x,50x) and
294: # we should try to raise an appropriate exception
295: # from one of our exception classes defined in
296: # exceptions.rb
297: return false if response.is_a?(Net::HTTPSuccess)
298:
299: # parse the XML document so we can walk through it
300: doc = REXML::Document.new(response.body)
301:
302: # Check that the Error element is in the place we would expect.
303: # and if not raise a generic error exception
304: unless doc.root.elements['Errors'].elements['Error'].name == 'Error'
305: raise Error, "Unexpected error format. response.body is: #{response.body}"
306: end
307:
308: # An valid error response looks like this:
309: # <?xml version="1.0"?><Response><Errors><Error><Code>InvalidParameterCombination</Code><Message>Unknown parameter: foo</Message></Error></Errors><RequestID>291cef62-3e86-414b-900e-17246eccfae8</RequestID></Response>
310: # AWS throws some exception codes that look like Error.SubError. Since we can't name classes this way
311: # we need to strip out the '.' in the error 'Code' and we name the error exceptions with this
312: # non '.' name as well.
313: error_code = doc.root.elements['Errors'].elements['Error'].elements['Code'].text.gsub('.', '')
314: error_message = doc.root.elements['Errors'].elements['Error'].elements['Message'].text
315:
316: # Raise one of our specific error classes if it exists.
317: # otherwise, throw a generic EC2 Error with a few details.
318: if AWS.const_defined?(error_code)
319: raise AWS.const_get(error_code), error_message
320: else
321: raise AWS::Error, error_message
322: end
323:
324: end
Set the Authorization header using AWS signed header authentication
# File lib/AWS.rb, line 267
267: def get_aws_auth_param(params, secret_access_key, server)
268: canonical_string = AWS.canonical_string(params, server)
269: encoded_canonical = AWS.encode(secret_access_key, canonical_string)
270: end
Make the connection to AWS EC2 passing in our request. This is generally called from within a ‘Response’ class object or one of its sub-classes so the response is interpreted in its proper context. See lib/EC2/responses.rb
# File lib/AWS.rb, line 231
231: def make_request(action, params, data='')
232:
233: @http.start do
234:
235: # remove any keys that have nil or empty values
236: params.reject! { |key, value| value.nil? or value.empty?}
237:
238: params.merge!( {"Action" => action,
239: "SignatureVersion" => "2",
240: "SignatureMethod" => 'HmacSHA1',
241: "AWSAccessKeyId" => @access_key_id,
242: "Version" => api_version,
243: "Timestamp"=>Time.now.getutc.iso8601} )
244:
245: sig = get_aws_auth_param(params, @secret_access_key, @server)
246:
247: query = params.sort.collect do |param|
248: CGI::escape(param[0]) + "=" + CGI::escape(param[1])
249: end.join("&") + "&Signature=" + sig
250:
251: req = Net::HTTP::Post.new("/")
252: req.content_type = 'application/x-www-form-urlencoded'
253: req['User-Agent'] = "github-amazon-ec2-ruby-gem"
254:
255: response = @http.request(req, query)
256:
257: # Make a call to see if we need to throw an error based on the response given by EC2
258: # All error classes are defined in EC2/exceptions.rb
259: aws_error?(response)
260: return response
261:
262: end
263:
264: end
Same as pathlist except it deals with arrays of hashes. So if you pass in args ("People", [{:name=>’jon’, :age=>’22’}, {:name=>’chris’}], {:name => ‘Name’, :age => ‘Age’}) you should get {"People.1.Name"=>"jon", "People.1.Age"=>’22’, ‘People.2.Name’=>’chris’}
# File lib/AWS.rb, line 214
214: def pathhashlist(key, arr_of_hashes, mappings)
215: raise ArgumentError, "expected a key that is a String" unless key.is_a? String
216: raise ArgumentError, "expected a arr_of_hashes that is an Array" unless arr_of_hashes.is_a? Array
217: arr_of_hashes.each{|h| raise ArgumentError, "expected each element of arr_of_hashes to be a Hash" unless h.is_a?(Hash)}
218: raise ArgumentError, "expected a mappings that is an Hash" unless mappings.is_a? Hash
219: params = {}
220: arr_of_hashes.each_with_index do |hash, i|
221: hash.each do |attribute, value|
222: params["#{key}.#{i+1}.#{mappings[attribute]}"] = value.to_s
223: end
224: end
225: params
226: end
pathlist is a utility method which takes a key string and and array as input. It converts the array into a Hash with the hash key being ‘Key.n’ where ‘n’ increments by 1 for each iteration. So if you pass in args ("ImageId", ["123", "456"]) you should get {"ImageId.1"=>"123", "ImageId.2"=>"456"} returned.
# File lib/AWS.rb, line 193
193: def pathlist(key, arr)
194: params = {}
195:
196: # ruby 1.9 will barf if we pass in a string instead of the array expected.
197: # it will fail on each_with_index below since string is not enumerable.
198: if arr.is_a? String
199: new_arr = []
200: new_arr << arr
201: arr = new_arr
202: end
203:
204: arr.each_with_index do |value, i|
205: params["#{key}.#{i+1}"] = value
206: end
207: params
208: end
allow us to have a one line call in each method which will do all of the work in making the actual request to AWS.
# File lib/AWS.rb, line 274
274: def response_generator( options = {} )
275:
276: options = {
277: :action => "",
278: :params => {}
279: }.merge(options)
280:
281: raise ArgumentError, ":action must be provided to response_generator" if options[:action].nil? || options[:action].empty?
282:
283: http_response = make_request(options[:action], options[:params])
284: http_xml = http_response.body
285: return Response.parse(:xml => http_xml)
286: end