elasticity 5.0.3 → 6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/HISTORY.md +26 -0
  3. data/README.md +35 -28
  4. data/elasticity.gemspec +2 -2
  5. data/lib/elasticity.rb +5 -3
  6. data/lib/elasticity/aws_request_v4.rb +15 -3
  7. data/lib/elasticity/aws_session.rb +4 -23
  8. data/lib/elasticity/aws_utils.rb +0 -29
  9. data/lib/elasticity/cluster_status.rb +38 -0
  10. data/lib/elasticity/cluster_step_status.rb +51 -0
  11. data/lib/elasticity/emr.rb +208 -78
  12. data/lib/elasticity/job_flow.rb +16 -17
  13. data/lib/elasticity/version.rb +1 -1
  14. data/spec/factories/cluster_status_factory.rb +12 -0
  15. data/spec/factories/cluster_step_status_factory.rb +17 -0
  16. data/spec/lib/elasticity/aws_request_v4_spec.rb +54 -4
  17. data/spec/lib/elasticity/aws_session_spec.rb +22 -88
  18. data/spec/lib/elasticity/aws_utils_spec.rb +0 -46
  19. data/spec/lib/elasticity/bootstrap_action_spec.rb +7 -3
  20. data/spec/lib/elasticity/cluster_status_spec.rb +98 -0
  21. data/spec/lib/elasticity/cluster_step_status_spec.rb +80 -0
  22. data/spec/lib/elasticity/custom_jar_step_spec.rb +10 -7
  23. data/spec/lib/elasticity/emr_spec.rb +422 -132
  24. data/spec/lib/elasticity/ganglia_bootstrap_action_spec.rb +8 -3
  25. data/spec/lib/elasticity/hadoop_bootstrap_action_spec.rb +8 -3
  26. data/spec/lib/elasticity/hadoop_file_bootstrap_action_spec.rb +7 -3
  27. data/spec/lib/elasticity/hive_step_spec.rb +21 -17
  28. data/spec/lib/elasticity/instance_group_spec.rb +9 -5
  29. data/spec/lib/elasticity/job_flow_integration_spec.rb +4 -4
  30. data/spec/lib/elasticity/job_flow_spec.rb +102 -76
  31. data/spec/lib/elasticity/job_flow_step_spec.rb +1 -1
  32. data/spec/lib/elasticity/looper_spec.rb +1 -1
  33. data/spec/lib/elasticity/pig_step_spec.rb +13 -9
  34. data/spec/lib/elasticity/s3distcp_step_spec.rb +7 -5
  35. data/spec/lib/elasticity/script_step_spec.rb +11 -6
  36. data/spec/lib/elasticity/setup_hadoop_debugging_step_spec.rb +9 -5
  37. data/spec/lib/elasticity/streaming_step_spec.rb +13 -9
  38. data/spec/spec_helper.rb +8 -0
  39. data/spec/support/factory_girl.rb +8 -0
  40. metadata +24 -21
  41. data/lib/elasticity/aws_request_v2.rb +0 -42
  42. data/lib/elasticity/job_flow_status.rb +0 -91
  43. data/lib/elasticity/job_flow_status_step.rb +0 -38
  44. data/spec/lib/elasticity/aws_request_v2_spec.rb +0 -38
  45. data/spec/lib/elasticity/job_flow_status_spec.rb +0 -265
  46. data/spec/lib/elasticity/job_flow_status_step_spec.rb +0 -80
@@ -4,49 +4,8 @@ module Elasticity
4
4
 
5
5
  attr_reader :aws_request
6
6
 
7
- def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, options = {})
8
- @aws_request = Elasticity::AwsSession.new(aws_access_key_id, aws_secret_access_key, options)
9
- end
10
-
11
- # Describe a specific jobflow.
12
- #
13
- # describe_jobflow("j-3UN6WX5RRO2AG")
14
- #
15
- # Raises ArgumentError if the specified jobflow does not exist.
16
- def describe_jobflow(jobflow_id)
17
- aws_result = @aws_request.submit({
18
- :operation => 'DescribeJobFlows',
19
- :job_flow_ids => [jobflow_id]
20
- })
21
- xml_doc = Nokogiri::XML(aws_result)
22
- xml_doc.remove_namespaces!
23
- yield aws_result if block_given?
24
- JobFlowStatus.from_members_nodeset(xml_doc.xpath('/DescribeJobFlowsResponse/DescribeJobFlowsResult/JobFlows/member')).first
25
- end
26
-
27
- # This is primarily for debugging purposes, providing insight into how
28
- # Amazon internally represents jobs. It's used to reverse-engineer
29
- # how API calls construct jobflows.
30
- def describe_jobflow_xml(jobflow_id)
31
- describe_jobflow(jobflow_id) do |xml|
32
- return xml
33
- end
34
- end
35
-
36
- # Lists all jobflows in all states.
37
- #
38
- # To override this behaviour, pass additional filters as specified in the AWS
39
- # documentation - http://docs.amazonwebservices.com/ElasticMapReduce/latest/API/index.html?API_DescribeJobFlows.html.
40
- #
41
- # describe_jobflows(:CreatedBefore => "2011-10-04")
42
- def describe_jobflows(params = {})
43
- aws_result = @aws_request.submit(
44
- params.merge({:operation => 'DescribeJobFlows'})
45
- )
46
- xml_doc = Nokogiri::XML(aws_result)
47
- xml_doc.remove_namespaces!
48
- yield aws_result if block_given?
49
- JobFlowStatus.from_members_nodeset(xml_doc.xpath('/DescribeJobFlowsResponse/DescribeJobFlowsResult/JobFlows/member'))
7
+ def initialize(options = {})
8
+ @aws_request = Elasticity::AwsSession.new(options)
50
9
  end
51
10
 
52
11
  # Adds a new group of instances to the specified jobflow. Elasticity maps a
@@ -56,11 +15,11 @@ module Elasticity
56
15
  #
57
16
  # instance_group_config = {
58
17
  # :bid_price => 5,
18
+ # :market => "SPOT",
19
+ # :name => "Go Canucks Go!",
59
20
  # :instance_count => 1,
60
21
  # :instance_role => "TASK",
61
- # :market => "SPOT",
62
- # :name => "Go Canucks Go!"
63
- # :type => "m1.small",
22
+ # :instance_type => "m1.small"
64
23
  # }
65
24
  #
66
25
  # add_instance_groups takes an array of {}. Returns an array of the instance IDs
@@ -74,42 +33,179 @@ module Elasticity
74
33
  :instance_groups => instance_group_configs
75
34
  }
76
35
  aws_result = @aws_request.submit(params)
77
- xml_doc = Nokogiri::XML(aws_result)
78
- xml_doc.remove_namespaces!
79
- instance_group_ids = []
80
- xml_doc.xpath('/AddInstanceGroupsResponse/AddInstanceGroupsResult/InstanceGroupIds/member').each do |member|
81
- instance_group_ids << member.text
82
- end
83
36
  yield aws_result if block_given?
84
- instance_group_ids
37
+ JSON.parse(aws_result)['InstanceGroupIds']
85
38
  end
86
39
 
87
40
  # Add a step (or steps) to the specified job flow.
88
41
  #
89
- # emr.add_jobflow_step("j-123", {
90
- # :steps => [
91
- # {
92
- # :action_on_failure => "TERMINATE_JOB_FLOW",
93
- # :hadoop_jar_step => {
94
- # :args => [
95
- # "s3://elasticmapreduce/libs/pig/pig-script",
96
- # "--base-path",
97
- # "s3://elasticmapreduce/libs/pig/",
98
- # "--install-pig"
99
- # ],
100
- # :jar => "s3://elasticmapreduce/libs/script-runner/script-runner.jar"
101
- # },
102
- # :name => "Setup Pig"
103
- # }
104
- # ]
105
- # })
42
+ # emr.add_jobflow_step("j-123", [
43
+ # {
44
+ # :action_on_failure => "TERMINATE_JOB_FLOW",
45
+ # :hadoop_jar_step => {
46
+ # :args => [
47
+ # "s3://elasticmapreduce/libs/pig/pig-script",
48
+ # "--base-path",
49
+ # "s3://elasticmapreduce/libs/pig/",
50
+ # "--install-pig"
51
+ # ],
52
+ # :jar => "s3://elasticmapreduce/libs/script-runner/script-runner.jar"
53
+ # },
54
+ # :name => "Setup Pig"
55
+ # }
56
+ # ])
106
57
  def add_jobflow_steps(jobflow_id, steps_config)
107
58
  params = {
108
59
  :operation => 'AddJobFlowSteps',
109
- :job_flow_id => jobflow_id
110
- }.merge!(steps_config)
60
+ :job_flow_id => jobflow_id,
61
+ :steps => steps_config
62
+ }
63
+ aws_result = @aws_request.submit(params)
64
+ yield aws_result if block_given?
65
+ JSON.parse(aws_result)
66
+ end
67
+
68
+ # Sets the specified tags on all instances in the specified jobflow
69
+ #
70
+ # emr.add_tags('j-123', [{:key => 'key1', :value => 'value1'}, {:key => 'key_only2'}])
71
+ #
72
+ # See http://docs.aws.amazon.com/ElasticMapReduce/latest/API/API_AddTags.html
73
+ def add_tags(jobflow_id, tags)
74
+ params = {
75
+ :operation => 'AddTags',
76
+ :resource_id => jobflow_id,
77
+ :tags => tags
78
+ }
79
+ aws_result = @aws_request.submit(params)
80
+ yield aws_result if block_given?
81
+ end
82
+
83
+ # Provides details about the specified jobflow
84
+ #
85
+ # emr.describe_cluster('j-123')
86
+ #
87
+ # http://docs.aws.amazon.com/ElasticMapReduce/latest/API/API_DescribeCluster.html
88
+ def describe_cluster(jobflow_id)
89
+ params = {
90
+ :operation => 'DescribeCluster',
91
+ :cluster_id => jobflow_id,
92
+ }
93
+ aws_result = @aws_request.submit(params)
94
+ yield aws_result if block_given?
95
+ JSON.parse(aws_result)
96
+ end
97
+
98
+ # Provides details about the specified step within an existing jobflow
99
+ #
100
+ # emr.describe_step('j-123', 'step-456')
101
+ #
102
+ # http://docs.aws.amazon.com/ElasticMapReduce/latest/API/API_DescribeStep.html
103
+ def describe_step(jobflow_id, step_id)
104
+ params = {
105
+ :operation => 'DescribeStep',
106
+ :cluster_id => jobflow_id,
107
+ :step_id => step_id
108
+ }
109
+ aws_result = @aws_request.submit(params)
110
+ yield aws_result if block_given?
111
+ JSON.parse(aws_result)
112
+ end
113
+
114
+ # List the clusters given specified filtering
115
+ #
116
+ # emr.list_clusters({
117
+ # :states => ['status1', 'status2', ...],
118
+ # :created_before => Time, # Amazon times are in UTC
119
+ # :created_after => Time, # Amazon times are in UTC
120
+ # :marker => 'marker' # Retrieve from a prior call to list_clusters
121
+ # })
122
+ #
123
+ # http://docs.aws.amazon.com/ElasticMapReduce/latest/API/API_ListClusters.html
124
+ def list_clusters(options={})
125
+ params = {
126
+ :operation => 'ListClusters'
127
+ }
128
+ params.merge!(:cluster_states => options[:states]) if options[:states]
129
+ params.merge!(:created_before => options[:created_before].to_i) if options[:created_before]
130
+ params.merge!(:created_after => options[:created_after].to_i) if options[:created_after]
131
+ params.merge!(:marker => options[:marker]) if options[:marker]
132
+ aws_result = @aws_request.submit(params)
133
+ yield aws_result if block_given?
134
+ JSON.parse(aws_result)
135
+ end
136
+
137
+ # List the instances in a cluster given specified filtering
138
+ #
139
+ # emr.list_instances('j-123', {
140
+ # :types => ['MASTER', 'CORE', 'TASK'],
141
+ # :marker => 'marker' # Retrieve from a prior call to list_clusters
142
+ # })
143
+ #
144
+ # http://docs.aws.amazon.com/ElasticMapReduce/latest/API/API_ListInstances.html
145
+ def list_instances(jobflow_id, options={})
146
+ params = {
147
+ :operation => 'ListInstances',
148
+ :cluster_id => jobflow_id
149
+ }
150
+ params.merge!(:instance_group_id => options[:instance_group_id]) if options[:instance_group_id]
151
+ params.merge!(:instance_group_types => options[:instance_group_types]) if options[:instance_group_types]
152
+ params.merge!(:marker => options[:marker]) if options[:marker]
153
+ aws_result = @aws_request.submit(params)
154
+ yield aws_result if block_given?
155
+ JSON.parse(aws_result)
156
+ end
157
+
158
+ # List the instance groups in the specified jobflow
159
+ #
160
+ # emr.list_instance_groups('j-123')
161
+ #
162
+ # http://docs.aws.amazon.com/ElasticMapReduce/latest/API/API_ListInstanceGroups.html
163
+ def list_instance_groups(jobflow_id)
164
+ params = {
165
+ :operation => 'ListInstanceGroups',
166
+ :cluster_id => jobflow_id,
167
+ }
168
+ aws_result = @aws_request.submit(params)
169
+ yield aws_result if block_given?
170
+ JSON.parse(aws_result)
171
+ end
172
+
173
+ # List the bootstrap actions in the specified jobflow
174
+ #
175
+ # emr.list_bootstrap_actions('j-123')
176
+ #
177
+ # http://docs.aws.amazon.com/ElasticMapReduce/latest/API/API_ListBootstrapActions.html
178
+ def list_bootstrap_actions(jobflow_id)
179
+ params = {
180
+ :operation => 'ListBootstrapActions',
181
+ :cluster_id => jobflow_id,
182
+ }
183
+ aws_result = @aws_request.submit(params)
184
+ yield aws_result if block_given?
185
+ JSON.parse(aws_result)
186
+ end
187
+
188
+ # List the steps in a job flow given specified filtering
189
+ #
190
+ # emr.list_steps('j-123', {
191
+ # :types => ['MASTER', 'CORE', 'TASK'],
192
+ # :step_ids => ['ID-1', 'ID-2']
193
+ # :step_states => ['PENDING', 'RUNNING', ...]
194
+ # :marker => 'marker' # Retrieve from a prior call to list_steps
195
+ # })
196
+ #
197
+ # http://docs.aws.amazon.com/ElasticMapReduce/latest/API/API_ListSteps.html
198
+ def list_steps(jobflow_id, options={})
199
+ params = {
200
+ :operation => 'ListSteps',
201
+ :cluster_id => jobflow_id,
202
+ }
203
+ params.merge!(:step_ids => options[:step_ids]) if options[:step_ids]
204
+ params.merge!(:step_states => options[:step_states]) if options[:step_states]
205
+ params.merge!(:marker => options[:marker]) if options[:marker]
111
206
  aws_result = @aws_request.submit(params)
112
207
  yield aws_result if block_given?
208
+ JSON.parse(aws_result)
113
209
  end
114
210
 
115
211
  # Set the number of instances in the specified instance groups to the
@@ -129,6 +225,21 @@ module Elasticity
129
225
  yield aws_result if block_given?
130
226
  end
131
227
 
228
+ # Remove the specified tags on all instances in the specified jobflow
229
+ #
230
+ # emr.remove_tags('j-123', ['key1','key_only2'])
231
+ #
232
+ # See http://docs.aws.amazon.com/ElasticMapReduce/latest/API/API_RemoveTags.html
233
+ def remove_tags(jobflow_id, keys)
234
+ params = {
235
+ :operation => 'RemoveTags',
236
+ :resource_id => jobflow_id,
237
+ :tag_keys => keys
238
+ }
239
+ aws_result = @aws_request.submit(params)
240
+ yield aws_result if block_given?
241
+ end
242
+
132
243
  # Start a job flow with the specified configuration. This is a very thin
133
244
  # wrapper around the AWS API, so in order to use it directly you'll need
134
245
  # to have the PDF API reference handy, which can be found here:
@@ -211,14 +322,33 @@ module Elasticity
211
322
  yield aws_result if block_given?
212
323
  end
213
324
 
214
- # Terminate the specified jobflow. Amazon does not define a return value
215
- # for this operation, so you'll need to poll #describe_jobflows to see
216
- # the state of the jobflow. Raises ArgumentError if the specified job
217
- # flow does not exist.
218
- def terminate_jobflows(jobflow_id)
325
+ # Whether or not all IAM users in this account can access the job flows.
326
+ #
327
+ # Takes an [] of job flow IDs.
328
+ #
329
+ # ["j-1B4D1XP0C0A35", "j-1YG2MYL0HVYS5", ...]
330
+ #
331
+ # http://docs.aws.amazon.com/ElasticMapReduce/latest/API/API_SetVisibleToAllUsers.html
332
+ def set_visible_to_all_users(jobflow_ids, visible=true)
333
+ params = {
334
+ :operation => 'SetVisibleToAllUsers',
335
+ :visible_to_all_users => visible,
336
+ :job_flow_ids => jobflow_ids
337
+ }
338
+ aws_result = @aws_request.submit(params)
339
+ yield aws_result if block_given?
340
+ end
341
+
342
+ # Terminate the specified jobflows. Amazon does not define a return value
343
+ # for this operation, so you'll need to poll to see the state of the jobflow.
344
+ #
345
+ # Takes an [] of job flow IDs.
346
+ #
347
+ # ["j-1B4D1XP0C0A35", "j-1YG2MYL0HVYS5", ...]
348
+ def terminate_jobflows(jobflow_ids)
219
349
  params = {
220
350
  :operation => 'TerminateJobFlows',
221
- :job_flow_ids => [jobflow_id]
351
+ :job_flow_ids => jobflow_ids
222
352
  }
223
353
  aws_result = @aws_request.submit(params)
224
354
  yield aws_result if block_given?
@@ -25,10 +25,7 @@ module Elasticity
25
25
  attr_accessor :job_flow_role
26
26
  attr_accessor :service_role
27
27
 
28
- attr_reader :access_key
29
- attr_reader :secret_key
30
-
31
- def initialize(access=nil, secret=nil)
28
+ def initialize
32
29
  @action_on_failure = 'TERMINATE_JOB_FLOW'
33
30
  @name = 'Elasticity Job Flow'
34
31
  @ami_version = 'latest'
@@ -36,8 +33,6 @@ module Elasticity
36
33
  self.placement = 'us-east-1a'
37
34
  @enable_debugging = false
38
35
 
39
- @access_key = access
40
- @secret_key = secret
41
36
  @visible_to_all_users = false
42
37
 
43
38
  @bootstrap_actions = []
@@ -50,16 +45,13 @@ module Elasticity
50
45
  @instance_count = 2
51
46
  @master_instance_type = 'm1.small'
52
47
  @slave_instance_type = 'm1.small'
53
-
54
- @access_key = access
55
- @secret_key = secret
56
48
  end
57
49
 
58
- def self.from_jobflow_id(access, secret, jobflow_id, region = 'us-east-1')
59
- JobFlow.new(access, secret).tap do |j|
50
+ def self.from_jobflow_id(jobflow_id, region = 'us-east-1')
51
+ JobFlow.new.tap do |j|
60
52
  j.instance_variable_set(:@region, region)
61
53
  j.instance_variable_set(:@jobflow_id, jobflow_id)
62
- j.instance_variable_set(:@installed_steps, j.status.installed_steps)
54
+ j.instance_variable_set(:@installed_steps, ClusterStepStatus.installed_steps(j.cluster_step_status))
63
55
  end
64
56
  end
65
57
 
@@ -154,11 +146,18 @@ module Elasticity
154
146
  emr.terminate_jobflows(@jobflow_id)
155
147
  end
156
148
 
157
- def status
149
+ def cluster_status
150
+ if !is_jobflow_running?
151
+ raise JobFlowNotStartedError, 'Please #run this job flow before attempting to retrieve status.'
152
+ end
153
+ ClusterStatus.from_aws_data(emr.describe_cluster(@jobflow_id))
154
+ end
155
+
156
+ def cluster_step_status
158
157
  if !is_jobflow_running?
159
158
  raise JobFlowNotStartedError, 'Please #run this job flow before attempting to retrieve status.'
160
159
  end
161
- emr.describe_jobflow(@jobflow_id)
160
+ ClusterStepStatus.from_aws_list_data(emr.list_steps(@jobflow_id))
162
161
  end
163
162
 
164
163
  def wait_for_completion(&on_wait)
@@ -169,12 +168,12 @@ module Elasticity
169
168
  private
170
169
 
171
170
  def retry_check
172
- jf_status = status
173
- return status.active?, jf_status
171
+ jf_status = cluster_status
172
+ return cluster_status.active?, jf_status
174
173
  end
175
174
 
176
175
  def emr
177
- @emr ||= Elasticity::EMR.new(@access_key, @secret_key, :region => @region)
176
+ @emr ||= Elasticity::EMR.new(:region => @region)
178
177
  end
179
178
 
180
179
  def is_jobflow_running?
@@ -1,3 +1,3 @@
1
1
  module Elasticity
2
- VERSION = '5.0.3'
2
+ VERSION = '6.0'
3
3
  end
@@ -0,0 +1,12 @@
1
+ FactoryGirl.define do
2
+ factory :cluster_status, class: Elasticity::ClusterStatus do
3
+ cluster_id 'CLUSTER_ID'
4
+ state 'TERMINATED'
5
+ created_at Time.at(1436788464.415)
6
+ ready_at Time.at(1436788842.195)
7
+ ended_at Time.at(1436791032.097)
8
+ last_state_change_reason 'ALL_STEPS_COMPLETED'
9
+ master_public_dns_name 'ec2-54-81-173-103.compute-1.amazonaws.com'
10
+ normalized_instance_hours 999
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ FactoryGirl.define do
2
+ factory :cluster_step_status, class: Elasticity::ClusterStepStatus do
3
+ action_on_failure 'TERMINATE_CLUSTER'
4
+ args ['36', '3', '0',]
5
+ jar 's3n://elasticmapreduce/samples/cloudburst/cloudburst.jar'
6
+ main_class 'MAIN_CLASS'
7
+ step_id 's-OYPPAC4XPPUC'
8
+ properties 'Key1' => 'Value1', 'Key2' => 'Value2'
9
+ name 'Elasticity Custom Jar Step'
10
+ state 'COMPLETED'
11
+ state_change_reason 'ALL_STEPS_COMPLETED'
12
+ state_change_reason_message 'Steps completed'
13
+ created_at Time.at(1436788464.416)
14
+ started_at Time.at(1436788841.237)
15
+ ended_at Time.at(1436790944.162)
16
+ end
17
+ end
@@ -2,6 +2,11 @@ describe Elasticity::AwsRequestV4 do
2
2
 
3
3
  before do
4
4
  Timecop.freeze(Time.at(1315611360))
5
+
6
+ Elasticity.configure do |c|
7
+ c.access_key = 'access'
8
+ c.secret_key = 'secret'
9
+ end
5
10
  end
6
11
 
7
12
  after do
@@ -10,11 +15,41 @@ describe Elasticity::AwsRequestV4 do
10
15
 
11
16
  subject do
12
17
  Elasticity::AwsRequestV4.new(
13
- Elasticity::AwsSession.new('access', 'secret'),
18
+ Elasticity::AwsSession.new,
14
19
  {:operation => 'DescribeJobFlows', :job_flow_ids => ['TEST_JOBFLOW_ID']}
15
20
  )
16
21
  end
17
22
 
23
+ describe '.initialize' do
24
+
25
+ describe 'access key' do
26
+ context 'when not provided' do
27
+ it 'should be an error' do
28
+ Elasticity.configure do |c|
29
+ c.access_key = nil
30
+ end
31
+ expect {
32
+ Elasticity::AwsRequestV4.new(nil, {})
33
+ }.to raise_error(ArgumentError, '.access_key must be set in the configuration block')
34
+ end
35
+ end
36
+ end
37
+
38
+ describe 'secret key' do
39
+ context 'when not provided' do
40
+ it 'should be an error' do
41
+ Elasticity.configure do |c|
42
+ c.secret_key = nil
43
+ end
44
+ expect {
45
+ Elasticity::AwsRequestV4.new(nil, {})
46
+ }.to raise_error(ArgumentError, '.secret_key must be set in the configuration block')
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+
18
53
  describe '#url' do
19
54
  it 'should construct a proper endpoint' do
20
55
  subject.url.should == 'https://elasticmapreduce.us-east-1.amazonaws.com'
@@ -22,16 +57,31 @@ describe Elasticity::AwsRequestV4 do
22
57
  end
23
58
 
24
59
  describe '#headers' do
25
- it 'should create the proper headers' do
26
- subject.headers.should == {
60
+
61
+ let(:base_headers) {
62
+ {
27
63
  'Authorization' => "AWS4-HMAC-SHA256 Credential=access/20110909/us-east-1/elasticmapreduce/aws4_request, SignedHeaders=content-type;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-target, Signature=#{subject.send(:aws_v4_signature)}",
28
64
  'Content-Type' => 'application/x-amz-json-1.1',
29
65
  'Host' => 'elasticmapreduce.us-east-1.amazonaws.com',
30
66
  'User-Agent' => "elasticity/#{Elasticity::VERSION}",
31
67
  'X-Amz-Content-SHA256' => Digest::SHA256.hexdigest(subject.payload),
32
68
  'X-Amz-Date' => '20110909T233600Z',
33
- 'X-Amz-Target' => 'ElasticMapReduce.DescribeJobFlows',
69
+ 'X-Amz-Target' => 'ElasticMapReduce.DescribeJobFlows'
34
70
  }
71
+ }
72
+
73
+ context 'when a security token is specified' do
74
+ it 'should create the proper headers' do
75
+ Elasticity.configure {|c| c.security_token = 'SECURITY_TOKEN' }
76
+ subject.headers.should == base_headers.merge('X-Amz-Security-Token' => 'SECURITY_TOKEN')
77
+ end
78
+ end
79
+
80
+ context 'when a security token is not specified' do
81
+ it 'should create the proper headers' do
82
+ Elasticity.configure {|c| c.security_token = nil }
83
+ subject.headers.should == base_headers
84
+ end
35
85
  end
36
86
  end
37
87