elastic-mapreduce 0.0.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.
Files changed (60) hide show
  1. data/CHANGELOG +51 -0
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +16 -0
  4. data/LICENSE.txt +393 -0
  5. data/NOTICE.txt +26 -0
  6. data/README +1007 -0
  7. data/Rakefile +35 -0
  8. data/VERSION +1 -0
  9. data/bin/elastic-mapreduce +27 -0
  10. data/cacert.pem +280 -0
  11. data/elastic-mapreduce.gemspec +104 -0
  12. data/lib/amazon/aws/exceptions.rb +211 -0
  13. data/lib/amazon/coral/awsquery.rb +128 -0
  14. data/lib/amazon/coral/awsquerychainhelper.rb +92 -0
  15. data/lib/amazon/coral/awsqueryhandler.rb +170 -0
  16. data/lib/amazon/coral/awsqueryurihandler.rb +34 -0
  17. data/lib/amazon/coral/call.rb +68 -0
  18. data/lib/amazon/coral/dispatcher.rb +33 -0
  19. data/lib/amazon/coral/ec2client.rb +91 -0
  20. data/lib/amazon/coral/elasticmapreduceclient.rb +198 -0
  21. data/lib/amazon/coral/handler.rb +20 -0
  22. data/lib/amazon/coral/httpdelegationhelper.rb +27 -0
  23. data/lib/amazon/coral/httpdestinationhandler.rb +36 -0
  24. data/lib/amazon/coral/httphandler.rb +124 -0
  25. data/lib/amazon/coral/identityhandler.rb +32 -0
  26. data/lib/amazon/coral/job.rb +25 -0
  27. data/lib/amazon/coral/logfactory.rb +35 -0
  28. data/lib/amazon/coral/option.rb +70 -0
  29. data/lib/amazon/coral/orchestrator.rb +49 -0
  30. data/lib/amazon/coral/querystringmap.rb +93 -0
  31. data/lib/amazon/coral/service.rb +130 -0
  32. data/lib/amazon/coral/simplelog.rb +98 -0
  33. data/lib/amazon/coral/urlencoding.rb +19 -0
  34. data/lib/amazon/coral/v0signaturehandler.rb +33 -0
  35. data/lib/amazon/coral/v0signaturehelper.rb +83 -0
  36. data/lib/amazon/coral/v1signaturehandler.rb +32 -0
  37. data/lib/amazon/coral/v1signaturehelper.rb +58 -0
  38. data/lib/amazon/coral/v2signaturehandler.rb +46 -0
  39. data/lib/amazon/coral/v2signaturehelper.rb +76 -0
  40. data/lib/amazon/retry_delegator.rb +66 -0
  41. data/lib/amazon/stderr_logger.rb +23 -0
  42. data/lib/client.rb +117 -0
  43. data/lib/commands.rb +1690 -0
  44. data/lib/credentials.rb +86 -0
  45. data/lib/ec2_client_wrapper.rb +73 -0
  46. data/lib/json/lexer.rb +294 -0
  47. data/lib/json/objects.rb +200 -0
  48. data/lib/json.rb +58 -0
  49. data/lib/simple_executor.rb +11 -0
  50. data/lib/simple_logger.rb +38 -0
  51. data/lib/uuidtools/version.rb +32 -0
  52. data/lib/uuidtools.rb +655 -0
  53. data/run_tests.rb +8 -0
  54. data/samples/freebase/code/freebase_jobflow.json +44 -0
  55. data/samples/similarity/lastfm_jobflow.json +78 -0
  56. data/samples/wordSplitter.py +18 -0
  57. data/tests/commands_test.rb +587 -0
  58. data/tests/credentials.json +7 -0
  59. data/tests/example.json +14 -0
  60. metadata +154 -0
@@ -0,0 +1,93 @@
1
+ #
2
+ # Copyright 2008-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ require 'amazon/coral/urlencoding'
5
+
6
+ module Amazon
7
+ module Coral
8
+
9
+ # A hash containing query string parameters that produces a query
10
+ # string via to_s. Also consumes hashes representing hierarchies of
11
+ # data to encode as query parameters.
12
+ class QueryStringMap < Hash
13
+
14
+ # Instantiate a QueryStringMap with the contents of the specified
15
+ # hash. If no hash is provided, an empty map is created.
16
+ def initialize(hash = {})
17
+ add_flattened(hash)
18
+ end
19
+
20
+ # Returns the query string representation of this map by collapsing
21
+ # its key-value pairs into URL parameters.
22
+ def to_s
23
+ qstr = ''
24
+ isFirst = true
25
+ each_pair { |k,v|
26
+ if isFirst then
27
+ isFirst = false
28
+ else
29
+ qstr << '&'
30
+ end
31
+ qstr << UrlEncoding.encode(k.to_s)
32
+ unless(v.nil?) then
33
+ qstr << '='
34
+ qstr << UrlEncoding.encode(v.to_s)
35
+ end
36
+ }
37
+ return qstr
38
+ end
39
+
40
+ private
41
+ def add_flattened(hash)
42
+ stack = []
43
+ add_flattened_helper(stack, hash)
44
+ end
45
+
46
+ def add_flattened_helper(stack, obj)
47
+ return if obj.nil?
48
+
49
+ case obj
50
+ when Hash
51
+
52
+ obj.each_pair { |k,v|
53
+ stack.push(k)
54
+ add_flattened_helper(stack, v)
55
+ stack.pop
56
+ }
57
+
58
+ when Array
59
+
60
+ # Do artificial list member wrapping (Coral requires this
61
+ # level of indirection, but doesn't validate the member name)
62
+ stack.push("member")
63
+
64
+ obj.each_index { |i|
65
+ v = obj[i]
66
+ stack.push(i + 1) # query string arrays are 1-based
67
+ add_flattened_helper(stack, v)
68
+ stack.pop
69
+ }
70
+
71
+ stack.pop
72
+
73
+ else
74
+
75
+ # this works for symbols also, because sym.id2name == sym.to_s
76
+ self[get_key(stack)] = obj.to_s
77
+
78
+ end
79
+ end
80
+
81
+ def get_key(stack)
82
+ key = ''
83
+ stack.each_index { |i|
84
+ key << '.' if(i > 0)
85
+ key << stack[i].to_s
86
+ }
87
+ return key
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,130 @@
1
+ #
2
+ # Copyright 2008-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ require 'amazon/coral/option'
5
+ require 'amazon/coral/orchestrator'
6
+ require 'amazon/coral/dispatcher'
7
+ require 'amazon/coral/call'
8
+ require 'amazon/coral/awsquery'
9
+ require 'amazon/coral/simplelog'
10
+
11
+ module Amazon
12
+ module Coral
13
+
14
+ # Provides a simple command-line interface to call remote services.
15
+ class Service
16
+
17
+ @@command_arguments = [
18
+ Option.new({:long => 'help', :short => 'h'}),
19
+ Option.new({:long => 'url', :short => 'u', :parameters => 1}),
20
+ Option.new({:long => 'awsAccessKey', :short => 'a', :parameters => 1}),
21
+ Option.new({:long => 'awsSecretKey', :short => 's', :parameters => 1}),
22
+ Option.new({:long => 'v0'}),
23
+ Option.new({:long => 'v1'}),
24
+ Option.new({:long => 'timeout', :parameters => 1}),
25
+ Option.new({:long => 'connect_timeout', :parameters => 1}),
26
+ Option.new({:long => 'input', :short => 'i', :parameters => 1}),
27
+ Option.new({:long => 'operation', :short => 'o', :parameters => 1}),
28
+ Option.new({:long => 'verbose', :short => 'v'})];
29
+
30
+ # Initializes a Service object with the specified arguments.
31
+ # Possible arguments include:
32
+ # [:orchestrator_helper]
33
+ # A class that responds to self.new_orchestrator create the necessary orchestrator.
34
+ # By default the AwsQueryChainHelper is used.
35
+ # [:service]
36
+ # The name of the service to be called.
37
+ # [:operations]
38
+ # A list naming the operations available on the remote service.
39
+ def initialize(args)
40
+ @orchestrator_helper_class = args[:orchestrator_helper]
41
+ @orchestrator_helper_class = AwsQuery if @orchestrator_helper_class.nil?
42
+
43
+ @service_name = args[:service]
44
+ @operation_names = args[:operations]
45
+ end
46
+
47
+ # Runs the command line client application.
48
+ def main
49
+
50
+ if ARGV.length == 0 then
51
+ print_usage
52
+ exit
53
+ end
54
+
55
+ args = Option.parse(@@command_arguments, ARGV)
56
+ if(args.length == 0 || !args['help'].nil?) then
57
+ print_usage
58
+ exit
59
+ end
60
+
61
+
62
+
63
+ raise "the 'url' parameter is required" if(args['url'].nil?)
64
+ url = args['url'][0]
65
+
66
+ input = nil
67
+ input = eval(args['input'][0]) unless args['input'].nil?
68
+
69
+ raise "the 'operation' parameter is required" if(args['operation'].nil?)
70
+ operation = args['operation'][0]
71
+ raise "operation '#{operation}' is not valid for this service" unless @operation_names.include?(operation)
72
+
73
+ verbose = !args['verbose'].nil?
74
+
75
+ timeout = Float(args['timeout'][0]) unless args['timeout'].nil?
76
+ connect_timeout = Float(args['connect_timeout'][0]) unless args['connect_timeout'].nil?
77
+
78
+ aws_access_key = nil
79
+ aws_secret_key = nil
80
+ signature_algorithm = nil
81
+
82
+ if(!args['awsAccessKey'].nil? && !args['awsSecretKey'].nil?) then
83
+ aws_access_key = args['awsAccessKey'][0]
84
+ aws_secret_key = args['awsSecretKey'][0]
85
+ signature_algorithm = :V2
86
+ signature_algorithm = :V0 if !args['v0'].nil?
87
+ signature_algorithm = :V1 if !args['v1'].nil?
88
+ end
89
+
90
+ helper_args = {:endpoint => url, :signature_algorithm => signature_algorithm, :verbose => verbose, :timeout => timeout, :connect_timeout => connect_timeout}
91
+
92
+ orchestrator = @orchestrator_helper_class.new_orchestrator(helper_args)
93
+ dispatcher = Dispatcher.new(orchestrator, @service_name, operation)
94
+ call = Call.new(dispatcher)
95
+
96
+ call.identity[:aws_access_key] = aws_access_key
97
+ call.identity[:aws_secret_key] = aws_secret_key
98
+
99
+ output = call.call(input)
100
+
101
+ puts output.inspect
102
+ end
103
+
104
+
105
+
106
+ # Prints to STDOUT a help message describing how to use the application.
107
+ def print_usage
108
+ puts "#{@service_name} ruby client"
109
+ puts "Usage:"
110
+ puts " -h --help"
111
+ puts " -u --url"
112
+ puts " -o --operation OPERATION"
113
+ puts " -i --input INPUT"
114
+ puts " -a --awsAccessKey KEY"
115
+ puts " -s --awsSecretKey SECRET_KEY"
116
+ puts " --v0"
117
+ puts " --v1"
118
+ puts " -v --verbose"
119
+ puts ""
120
+ puts "Available operations:"
121
+ @operation_names.each { |name|
122
+ puts(" #{name}")
123
+ } unless @operation_names.nil?
124
+ end
125
+
126
+ end
127
+
128
+ end
129
+ end
130
+
@@ -0,0 +1,98 @@
1
+ #
2
+ # Copyright 2008-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ require 'amazon/coral/logfactory'
5
+ require 'logger'
6
+
7
+ module Amazon
8
+ module Coral
9
+
10
+ # Wraps Ruby's built in Logger to prepend a context string to log messages.
11
+ # This is useful to prefix log messages with the name of the originating class, etc.
12
+ class WrappedLogger
13
+ def initialize(key, logger)
14
+ @key = key
15
+ @logger = logger
16
+ end
17
+
18
+ def debug(s)
19
+ @logger.debug(format(s))
20
+ end
21
+ def info(s)
22
+ @logger.info(format(s))
23
+ end
24
+ def warn(s)
25
+ @logger.warn(format(s))
26
+ end
27
+ def error(s)
28
+ @logger.error(format(s))
29
+ end
30
+ def fatal(s)
31
+ @logger.fatal(format(s))
32
+ end
33
+
34
+ def debug?
35
+ @logger.debug?
36
+ end
37
+ def info?
38
+ @logger.info?
39
+ end
40
+ def warn?
41
+ @logger.warn?
42
+ end
43
+ def error?
44
+ @logger.error?
45
+ end
46
+ def fatal?
47
+ @logger.fatal?
48
+ end
49
+
50
+ def format(s)
51
+ return "#{@key}: #{s}"
52
+ end
53
+ end
54
+
55
+ #
56
+ # Provides a LogFactory implementation that supplies WrappedLogger objects to requestors.
57
+ # The key provided to getLog is prepended to log messages from each Logger.
58
+ #
59
+ # Copyright:: Copyright (c) 2008 Amazon.com, Inc. or its affiliates. All Rights Reserved.
60
+ #
61
+ class SimpleLogFactory < LogFactory
62
+ def initialize(output, level)
63
+ @output = output
64
+ @level = level
65
+ end
66
+
67
+ def getLog(key)
68
+ logger = Logger.new(@output)
69
+ logger.level = @level
70
+
71
+ return WrappedLogger.new(key, logger)
72
+ end
73
+ end
74
+
75
+ #
76
+ # Provides a straightforward facility to configure SimpleLog as the active logging mechanism.
77
+ #
78
+ # Copyright:: Copyright (c) 2008 Amazon.com, Inc. or its affiliates. All Rights Reserved.
79
+ #
80
+ class SimpleLog
81
+ # Registers a SimpleLogFactory with the specified output IO object and logging level.
82
+ #
83
+ # To set logging to its highest level and send output to the console, use:
84
+ # SimpleLog.install(STDOUT, Logger:DEBUG)
85
+ #
86
+ # To send logging output to a file:
87
+ # SimpleLog.install(File.new('/tmp/simplelog.log', 'r'), Logger::INFO)
88
+ #
89
+ # Installing a new LogFactory will not affect objects that have already retrieved their log instances,
90
+ # it's best to initialize logging as early as possible in your code to ensure that all your code gets
91
+ # the proper configuration.
92
+ def SimpleLog.install(output, level)
93
+ LogFactory.setInstance(SimpleLogFactory.new(output, level))
94
+ end
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,19 @@
1
+ #
2
+ # Copyright 2008-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ require 'cgi'
4
+
5
+ module Amazon
6
+ module Coral
7
+
8
+ # Performs AWS's preferred method of URLEncoding.
9
+ class UrlEncoding
10
+
11
+ # Convert a string into URL encoded form.
12
+ def UrlEncoding.encode(plaintext)
13
+ CGI.escape(plaintext.to_s).gsub("+", "%20").gsub("%7E", "~")
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+
@@ -0,0 +1,33 @@
1
+ #
2
+ # Copyright 2008-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ require 'openssl'
5
+ require 'base64'
6
+ require 'time'
7
+ require 'amazon/coral/handler'
8
+ require 'amazon/coral/v0signaturehelper'
9
+
10
+ module Amazon
11
+ module Coral
12
+
13
+ # Applies an AWS version 0 signature to the outgoing request.
14
+ class V0SignatureHandler < Handler
15
+
16
+ def before(job)
17
+ request = job.request
18
+ identity = request[:identity]
19
+ aws_access_key = identity[:aws_access_key]
20
+ aws_secret_key = identity[:aws_secret_key]
21
+
22
+ query_string_map = request[:query_string_map]
23
+
24
+ return if aws_access_key.nil? || aws_secret_key.nil? || query_string_map.nil?;
25
+
26
+ V0SignatureHelper.new(aws_access_key, aws_secret_key).sign({:query_string_map => query_string_map})
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
33
+
@@ -0,0 +1,83 @@
1
+ #
2
+ # Copyright 2008-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ require 'openssl'
5
+ require 'base64'
6
+ require 'time'
7
+
8
+ module Amazon
9
+ module Coral
10
+
11
+ # Performs AWS V0 Signatures on QueryStringMap objects.
12
+ class V0SignatureHelper
13
+ def initialize(aws_access_key_id, aws_secret_key)
14
+ @aws_access_key_id = aws_access_key_id.to_s
15
+ @aws_secret_key = aws_secret_key.to_s
16
+ end
17
+
18
+ def sign(args)
19
+ signT(Time.now.iso8601, args)
20
+ end
21
+
22
+ def signT(time, args)
23
+ query_string_map = args[:query_string_map]
24
+ add_fields(query_string_map, time)
25
+ query_string_map['Signature'] = compute_signature(canonicalize(args))
26
+ end
27
+
28
+ def canonicalize(args)
29
+ query_string_map = args[:query_string_map]
30
+
31
+ service_name = query_string_map['ServiceName']
32
+ action = query_string_map['Action']
33
+ operation = query_string_map['Operation']
34
+ timestamp = query_string_map['Timestamp']
35
+ expires = query_string_map['Expires']
36
+ credential = query_string_map['Credential']
37
+
38
+ canonical = ''
39
+
40
+ canonical << service_name unless service_name.nil?
41
+
42
+ if !action.nil? then
43
+ canonical << action
44
+ elsif !operation.nil? then
45
+ canonical << operation
46
+ else
47
+ raise 'Query string must contain Action or Operation'
48
+ end
49
+
50
+ if timestamp.nil? and expires.nil? then
51
+ raise 'Query string must contain Timestamp or Expires'
52
+ elsif !timestamp.nil? and !expires.nil? then
53
+ raise 'Query string may contain only one of Timestamp or Expires'
54
+ elsif !timestamp.nil? then
55
+ canonical << timestamp
56
+ else
57
+ canonical << expires
58
+ end
59
+
60
+ canonical << credential unless credential.nil?
61
+
62
+ return canonical
63
+ end
64
+
65
+ def compute_signature(canonical)
66
+ digest = OpenSSL::Digest::Digest.new('sha1')
67
+ return Base64.encode64(OpenSSL::HMAC.digest(digest, @aws_secret_key, canonical)).strip
68
+ end
69
+
70
+ def add_fields(query_string_map, time)
71
+ query_string_map['AWSAccessKeyId'] = @aws_access_key_id
72
+ query_string_map['SignatureVersion'] = '0'
73
+ query_string_map['SignatureMethod'] = 'HmacSHA1'
74
+ query_string_map['Timestamp'] = time.to_s
75
+ end
76
+
77
+ def sort(hash)
78
+ hash.sort { |a,b| a[0].downcase <=> b[0].downcase }
79
+ end
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,32 @@
1
+ #
2
+ # Copyright 2008-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ require 'openssl'
5
+ require 'base64'
6
+ require 'time'
7
+ require 'amazon/coral/handler'
8
+ require 'amazon/coral/v1signaturehelper'
9
+
10
+ module Amazon
11
+ module Coral
12
+
13
+ # Applies an AWS version 1 signature to the outgoing request.
14
+ class V1SignatureHandler < Handler
15
+
16
+ def before(job)
17
+ request = job.request
18
+ identity = request[:identity]
19
+ aws_access_key = identity[:aws_access_key]
20
+ aws_secret_key = identity[:aws_secret_key]
21
+
22
+ query_string_map = request[:query_string_map]
23
+
24
+ return if aws_access_key.nil? || aws_secret_key.nil? || query_string_map.nil?;
25
+
26
+ V1SignatureHelper.new(aws_access_key, aws_secret_key).sign({:query_string_map => query_string_map})
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,58 @@
1
+ #
2
+ # Copyright 2008-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ require 'openssl'
5
+ require 'base64'
6
+ require 'time'
7
+
8
+ module Amazon
9
+ module Coral
10
+ class V1SignatureHelper
11
+ def initialize(aws_access_key_id, aws_secret_key)
12
+ @aws_access_key_id = aws_access_key_id.to_s
13
+ @aws_secret_key = aws_secret_key.to_s
14
+ end
15
+
16
+ def sign(args)
17
+ signT(Time.now.iso8601, args)
18
+ end
19
+
20
+ def signT(time, args)
21
+ query_string_map = args[:query_string_map]
22
+ add_fields(query_string_map, time)
23
+ query_string_map['Signature'] = compute_signature(canonicalize(args))
24
+ end
25
+
26
+ def canonicalize(args)
27
+ query_string_map = args[:query_string_map]
28
+
29
+ # exclude any existing Signature parameter from the canonical string
30
+ sorted = sort(query_string_map.reject { |k, v| k == 'Signature' })
31
+
32
+ canonical = ''
33
+ sorted.each do |v|
34
+ canonical << v[0]
35
+ canonical << v[1] unless(v[1].nil?)
36
+ end
37
+
38
+ return canonical
39
+ end
40
+
41
+ def compute_signature(canonical)
42
+ digest = OpenSSL::Digest::Digest.new('sha1')
43
+ return Base64.encode64(OpenSSL::HMAC.digest(digest, @aws_secret_key, canonical)).strip
44
+ end
45
+
46
+ def add_fields(query_string_map, time)
47
+ query_string_map['AWSAccessKeyId'] = @aws_access_key_id
48
+ query_string_map['SignatureVersion'] = '1'
49
+ query_string_map['SignatureMethod'] = 'HmacSHA1'
50
+ query_string_map['Timestamp'] = time.to_s
51
+ end
52
+
53
+ def sort(hash)
54
+ hash.sort { |a,b| a[0].downcase <=> b[0].downcase }
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,46 @@
1
+ #
2
+ # Copyright 2008-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ require 'openssl'
5
+ require 'base64'
6
+ require 'time'
7
+ require 'amazon/coral/handler'
8
+ require 'amazon/coral/v2signaturehelper'
9
+ require 'amazon/coral/logfactory'
10
+
11
+ module Amazon
12
+ module Coral
13
+ class V2SignatureHandler < Handler
14
+
15
+ def initialize
16
+ @log = LogFactory.getLog('Amazon::Coral::V2SignatureHandler')
17
+ end
18
+
19
+ def before(job)
20
+ request = job.request
21
+ identity = request[:identity]
22
+ aws_access_key = identity[:aws_access_key]
23
+ aws_secret_key = identity[:aws_secret_key]
24
+
25
+ query_string_map = request[:query_string_map]
26
+ http_uri = request[:http_uri]
27
+ uri = http_uri.path
28
+ verb = request[:http_verb]
29
+
30
+ host = "#{http_uri.host}"
31
+ host << ":#{http_uri.port}" unless http_uri.port.nil?
32
+
33
+ @log.debug("uri: #{uri} verb: #{verb} host: #{host}")
34
+
35
+ return if aws_access_key.nil? || aws_secret_key.nil? || query_string_map.nil? ||
36
+ uri.nil? || verb.nil? || host.nil?;
37
+
38
+ V2SignatureHelper.new(aws_access_key, aws_secret_key).sign({
39
+ :query_string_map => query_string_map, :uri => uri, :verb => verb, :host => host
40
+ })
41
+
42
+ request[:http_host] = host
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,76 @@
1
+ #
2
+ # Copyright 2008-2010 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ require 'openssl'
5
+ require 'base64'
6
+ require 'time'
7
+
8
+ module Amazon
9
+ module Coral
10
+
11
+ # Performs AWS V2 signatures on QueryStringMap objects.
12
+ class V2SignatureHelper
13
+ def initialize(aws_access_key_id, aws_secret_key)
14
+ @aws_access_key_id = aws_access_key_id.to_s
15
+ @aws_secret_key = aws_secret_key.to_s
16
+ end
17
+
18
+ def sign(args)
19
+ signT(Time.now.iso8601, args)
20
+ end
21
+
22
+ def signT(time, args)
23
+ query_string_map = args[:query_string_map]
24
+ add_fields(query_string_map, time)
25
+ query_string_map['Signature'] = compute_signature(canonicalize(args))
26
+ end
27
+
28
+ def canonicalize(args)
29
+ query_string_map = args[:query_string_map]
30
+ uri = args[:uri]
31
+ verb = args[:verb]
32
+ host = args[:host].downcase
33
+
34
+ # exclude any existing Signature parameter from the canonical string
35
+ sorted = sort(query_string_map.reject { |k, v| k == 'Signature' })
36
+
37
+ canonical = "#{verb}\n#{host}\n#{uri}\n"
38
+ isFirst = true
39
+
40
+ sorted.each { |v|
41
+ if(isFirst) then
42
+ isFirst = false
43
+ else
44
+ canonical << '&'
45
+ end
46
+
47
+ canonical << UrlEncoding.encode(v[0])
48
+ unless(v[1].nil?) then
49
+ canonical << '='
50
+ canonical << UrlEncoding.encode(v[1])
51
+ end
52
+ }
53
+
54
+ return canonical
55
+ end
56
+
57
+ def compute_signature(canonical)
58
+ digest = OpenSSL::Digest::Digest.new('sha256')
59
+ return Base64.encode64(OpenSSL::HMAC.digest(digest, @aws_secret_key, canonical)).strip
60
+ end
61
+
62
+ def add_fields(query_string_map, time)
63
+ query_string_map['AWSAccessKeyId'] = @aws_access_key_id
64
+ query_string_map['SignatureVersion'] = '2'
65
+ query_string_map['SignatureMethod'] = 'HmacSHA256'
66
+ query_string_map['Timestamp'] = time.to_s
67
+ end
68
+
69
+ def sort(hash)
70
+ hash.sort
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+ end