Skip to content
This repository has been archived by the owner on Feb 1, 2021. It is now read-only.

Commit

Permalink
Extract AWS preparation
Browse files Browse the repository at this point in the history
  • Loading branch information
rslifka committed Apr 25, 2015
1 parent c241f67 commit 176b7fb
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 99 deletions.
1 change: 1 addition & 0 deletions lib/elasticity.rb
Expand Up @@ -5,6 +5,7 @@
require 'nokogiri'
require 'fog'

require 'elasticity/aws_utils'
require 'elasticity/aws_session'
require 'elasticity/aws_request_v2'
require 'elasticity/emr'
Expand Down
57 changes: 4 additions & 53 deletions lib/elasticity/aws_request_v2.rb
Expand Up @@ -17,75 +17,26 @@ def headers
}
end

def payload
payload_v2(@ruby_service_hash)
end

private

# (Used from RightScale's right_aws gem.)
# EC2, SQS, SDB and EMR requests must be signed by this guy.
# See: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?REST_RESTAuth.html
# http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1928
def payload_v2(service_hash)
service_hash = AwsRequestV2.convert_ruby_to_aws(service_hash)
def payload
service_hash = AwsUtils.convert_ruby_to_aws(@ruby_service_hash)
service_hash.merge!({
'AWSAccessKeyId' => @aws_session.access_key,
'Timestamp' => Time.now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z'),
'SignatureVersion' => '2',
'SignatureMethod' => 'HmacSHA256'
})
canonical_string = service_hash.keys.sort.map do |key|
"#{AwsRequestV2.aws_escape(key)}=#{AwsRequestV2.aws_escape(service_hash[key])}"
"#{AwsUtils.aws_escape(key)}=#{AwsUtils.aws_escape(service_hash[key])}"
end.join('&')
string_to_sign = "POST\n#{@aws_session.host.downcase}\n/\n#{canonical_string}"
signature = AwsRequestV2.aws_escape(Base64.encode64(OpenSSL::HMAC.digest('sha256', @aws_session.secret_key, string_to_sign)).strip)
signature = AwsUtils.aws_escape(Base64.encode64(OpenSSL::HMAC.digest('sha256', @aws_session.secret_key, string_to_sign)).strip)
"#{canonical_string}&Signature=#{signature}"
end

# (Used from RightScale's right_aws gem)
# Escape a string according to Amazon's rules.
# See: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?REST_RESTAuth.html
def self.aws_escape(param)
param.to_s.gsub(/([^a-zA-Z0-9._~-]+)/n) do
'%' + $1.unpack('H2' * $1.size).join('%').upcase
end
end

# Since we use the same structure as AWS, we can generate AWS param names
# from the Ruby versions of those names (and the param nesting).
def self.convert_ruby_to_aws(params)
result = {}
params.each do |key, value|
case value
when Array
prefix = "#{camelize(key.to_s)}.member"
value.each_with_index do |item, index|
if item.is_a?(String)
result["#{prefix}.#{index+1}"] = item
else
convert_ruby_to_aws(item).each do |nested_key, nested_value|
result["#{prefix}.#{index+1}.#{nested_key}"] = nested_value
end
end
end
when Hash
prefix = "#{camelize(key.to_s)}"
convert_ruby_to_aws(value).each do |nested_key, nested_value|
result["#{prefix}.#{nested_key}"] = nested_value
end
else
result[camelize(key.to_s)] = value
end
end
result
end

# (Used from Rails' ActiveSupport)
def self.camelize(word)
word.to_s.gsub(/\/(.?)/) { '::' + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
end

end

end
48 changes: 48 additions & 0 deletions lib/elasticity/aws_utils.rb
@@ -0,0 +1,48 @@
module Elasticity

class AwsUtils

# Escape a string according to Amazon's rules.
# See: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?REST_RESTAuth.html
def self.aws_escape(param)
param.to_s.gsub(/([^a-zA-Z0-9._~-]+)/n) do
'%' + $1.unpack('H2' * $1.size).join('%').upcase
end
end

# Since we use the same structure as AWS, we can generate AWS param names
# from the Ruby versions of those names (and the param nesting).
def self.convert_ruby_to_aws(params)
result = {}
params.each do |key, value|
case value
when Array
prefix = "#{camelize(key.to_s)}.member"
value.each_with_index do |item, index|
if item.is_a?(String)
result["#{prefix}.#{index+1}"] = item
else
convert_ruby_to_aws(item).each do |nested_key, nested_value|
result["#{prefix}.#{index+1}.#{nested_key}"] = nested_value
end
end
end
when Hash
prefix = "#{camelize(key.to_s)}"
convert_ruby_to_aws(value).each do |nested_key, nested_value|
result["#{prefix}.#{nested_key}"] = nested_value
end
else
result[camelize(key.to_s)] = value
end
end
result
end

def self.camelize(word)
word.to_s.gsub(/\/(.?)/) { '::' + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
end

end

end
46 changes: 0 additions & 46 deletions spec/lib/elasticity/aws_request_v2_spec.rb
Expand Up @@ -35,50 +35,4 @@
end
end

describe '.convert_ruby_to_aws' do
it 'should convert the params' do
add_jobflow_steps_params = {
:job_flow_id => 'j-1',
:steps => [
{
:action_on_failure => 'CONTINUE',
:name => 'First New Job Step',
:hadoop_jar_step => {
:args => %w(arg1 arg2 arg3),
:jar => 'first_step.jar',
:main_class => 'first_class.jar'
}
},
{
:action_on_failure => 'CANCEL_AND_WAIT',
:name => 'Second New Job Step',
:hadoop_jar_step => {
:args => %w(arg4 arg5 arg6),
:jar => 'second_step.jar',
:main_class => 'second_class.jar'
}
}
]
}
expected_result = {
'JobFlowId' => 'j-1',
'Steps.member.1.Name' => 'First New Job Step',
'Steps.member.1.ActionOnFailure' => 'CONTINUE',
'Steps.member.1.HadoopJarStep.Jar' => 'first_step.jar',
'Steps.member.1.HadoopJarStep.MainClass' => 'first_class.jar',
'Steps.member.1.HadoopJarStep.Args.member.1' => 'arg1',
'Steps.member.1.HadoopJarStep.Args.member.2' => 'arg2',
'Steps.member.1.HadoopJarStep.Args.member.3' => 'arg3',
'Steps.member.2.Name' => 'Second New Job Step',
'Steps.member.2.ActionOnFailure' => 'CANCEL_AND_WAIT',
'Steps.member.2.HadoopJarStep.Jar' => 'second_step.jar',
'Steps.member.2.HadoopJarStep.MainClass' => 'second_class.jar',
'Steps.member.2.HadoopJarStep.Args.member.1' => 'arg4',
'Steps.member.2.HadoopJarStep.Args.member.2' => 'arg5',
'Steps.member.2.HadoopJarStep.Args.member.3' => 'arg6'
}
Elasticity::AwsRequestV2.send(:convert_ruby_to_aws, add_jobflow_steps_params).should == expected_result
end
end

end
49 changes: 49 additions & 0 deletions spec/lib/elasticity/aws_utils_spec.rb
@@ -0,0 +1,49 @@
describe Elasticity::AwsUtils do

describe '.convert_ruby_to_aws' do
it 'should convert the params' do
add_jobflow_steps_params = {
:job_flow_id => 'j-1',
:steps => [
{
:action_on_failure => 'CONTINUE',
:name => 'First New Job Step',
:hadoop_jar_step => {
:args => %w(arg1 arg2 arg3),
:jar => 'first_step.jar',
:main_class => 'first_class.jar'
}
},
{
:action_on_failure => 'CANCEL_AND_WAIT',
:name => 'Second New Job Step',
:hadoop_jar_step => {
:args => %w(arg4 arg5 arg6),
:jar => 'second_step.jar',
:main_class => 'second_class.jar'
}
}
]
}
expected_result = {
'JobFlowId' => 'j-1',
'Steps.member.1.Name' => 'First New Job Step',
'Steps.member.1.ActionOnFailure' => 'CONTINUE',
'Steps.member.1.HadoopJarStep.Jar' => 'first_step.jar',
'Steps.member.1.HadoopJarStep.MainClass' => 'first_class.jar',
'Steps.member.1.HadoopJarStep.Args.member.1' => 'arg1',
'Steps.member.1.HadoopJarStep.Args.member.2' => 'arg2',
'Steps.member.1.HadoopJarStep.Args.member.3' => 'arg3',
'Steps.member.2.Name' => 'Second New Job Step',
'Steps.member.2.ActionOnFailure' => 'CANCEL_AND_WAIT',
'Steps.member.2.HadoopJarStep.Jar' => 'second_step.jar',
'Steps.member.2.HadoopJarStep.MainClass' => 'second_class.jar',
'Steps.member.2.HadoopJarStep.Args.member.1' => 'arg4',
'Steps.member.2.HadoopJarStep.Args.member.2' => 'arg5',
'Steps.member.2.HadoopJarStep.Args.member.3' => 'arg6'
}
Elasticity::AwsUtils.send(:convert_ruby_to_aws, add_jobflow_steps_params).should == expected_result
end
end

end

0 comments on commit 176b7fb

Please sign in to comment.