elasticity 1.5 → 2.0

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 (57) hide show
  1. data/.rspec +2 -1
  2. data/.rvmrc +1 -1
  3. data/HISTORY.md +47 -24
  4. data/LICENSE +1 -1
  5. data/README.md +165 -317
  6. data/Rakefile +4 -3
  7. data/elasticity.gemspec +3 -5
  8. data/lib/elasticity.rb +10 -5
  9. data/lib/elasticity/aws_request.rb +81 -20
  10. data/lib/elasticity/custom_jar_step.rb +33 -0
  11. data/lib/elasticity/emr.rb +45 -117
  12. data/lib/elasticity/hadoop_bootstrap_action.rb +27 -0
  13. data/lib/elasticity/hive_step.rb +57 -0
  14. data/lib/elasticity/job_flow.rb +109 -39
  15. data/lib/elasticity/job_flow_status.rb +53 -0
  16. data/lib/elasticity/job_flow_status_step.rb +35 -0
  17. data/lib/elasticity/job_flow_step.rb +17 -25
  18. data/lib/elasticity/pig_step.rb +82 -0
  19. data/lib/elasticity/support/conditional_raise.rb +23 -0
  20. data/lib/elasticity/version.rb +1 -1
  21. data/spec/lib/elasticity/aws_request_spec.rb +159 -51
  22. data/spec/lib/elasticity/custom_jar_step_spec.rb +59 -0
  23. data/spec/lib/elasticity/emr_spec.rb +231 -762
  24. data/spec/lib/elasticity/hadoop_bootstrap_action_spec.rb +26 -0
  25. data/spec/lib/elasticity/hive_step_spec.rb +74 -0
  26. data/spec/lib/elasticity/job_flow_integration_spec.rb +197 -0
  27. data/spec/lib/elasticity/job_flow_spec.rb +369 -138
  28. data/spec/lib/elasticity/job_flow_status_spec.rb +147 -0
  29. data/spec/lib/elasticity/job_flow_status_step_spec.rb +73 -0
  30. data/spec/lib/elasticity/job_flow_step_spec.rb +26 -64
  31. data/spec/lib/elasticity/pig_step_spec.rb +104 -0
  32. data/spec/lib/elasticity/support/conditional_raise_spec.rb +35 -0
  33. data/spec/spec_helper.rb +1 -50
  34. data/spec/support/be_a_hash_including_matcher.rb +35 -0
  35. metadata +101 -119
  36. data/.autotest +0 -2
  37. data/lib/elasticity/custom_jar_job.rb +0 -38
  38. data/lib/elasticity/hive_job.rb +0 -69
  39. data/lib/elasticity/pig_job.rb +0 -109
  40. data/lib/elasticity/simple_job.rb +0 -51
  41. data/spec/fixtures/vcr_cassettes/add_instance_groups/one_group_successful.yml +0 -44
  42. data/spec/fixtures/vcr_cassettes/add_instance_groups/one_group_unsuccessful.yml +0 -41
  43. data/spec/fixtures/vcr_cassettes/add_jobflow_steps/add_multiple_steps.yml +0 -266
  44. data/spec/fixtures/vcr_cassettes/custom_jar_job/cloudburst.yml +0 -41
  45. data/spec/fixtures/vcr_cassettes/describe_jobflows/all_jobflows.yml +0 -75
  46. data/spec/fixtures/vcr_cassettes/direct/terminate_jobflow.yml +0 -38
  47. data/spec/fixtures/vcr_cassettes/hive_job/hive_ads.yml +0 -41
  48. data/spec/fixtures/vcr_cassettes/modify_instance_groups/set_instances_to_3.yml +0 -38
  49. data/spec/fixtures/vcr_cassettes/pig_job/apache_log_reports.yml +0 -41
  50. data/spec/fixtures/vcr_cassettes/pig_job/apache_log_reports_with_bootstrap.yml +0 -41
  51. data/spec/fixtures/vcr_cassettes/run_jobflow/word_count.yml +0 -41
  52. data/spec/fixtures/vcr_cassettes/set_termination_protection/nonexistent_job_flows.yml +0 -41
  53. data/spec/fixtures/vcr_cassettes/set_termination_protection/protect_multiple_job_flows.yml +0 -38
  54. data/spec/fixtures/vcr_cassettes/terminate_jobflows/one_jobflow.yml +0 -38
  55. data/spec/lib/elasticity/custom_jar_job_spec.rb +0 -118
  56. data/spec/lib/elasticity/hive_job_spec.rb +0 -90
  57. data/spec/lib/elasticity/pig_job_spec.rb +0 -226
@@ -0,0 +1,23 @@
1
+ module Elasticity
2
+
3
+ module ConditionalRaising
4
+
5
+ def raise_if(conditional, error_class, message)
6
+ raise error_class, message if conditional
7
+ end
8
+
9
+ def raise_unless(conditional, error_class, message)
10
+ raise error_class, message unless conditional
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+
17
+ module Kernel
18
+ include Elasticity::ConditionalRaising
19
+ end
20
+
21
+ class Object
22
+ include Kernel
23
+ end
@@ -1,3 +1,3 @@
1
1
  module Elasticity
2
- VERSION = "1.5"
2
+ VERSION = "2.0"
3
3
  end
@@ -1,72 +1,180 @@
1
- require 'spec_helper'
2
-
3
1
  describe Elasticity::AwsRequest do
4
2
 
5
- describe ".aws_escape" do
6
- it "should escape according to Amazon's rules" do
7
- # Don't encode reserved characters
8
- Elasticity::AwsRequest.aws_escape("foo-_.~bar").should == "foo-_.~bar"
9
- # Encode as %20, not as +
10
- Elasticity::AwsRequest.aws_escape("foo bar").should == "foo%20bar"
11
- # Percent encode all other characters with %XY, where X and Y are hex characters 0-9 and uppercase A-F.
12
- Elasticity::AwsRequest.aws_escape("foo$&+,/:;=?@bar").should == "foo%24%26%2B%2C%2F%3A%3B%3D%3F%40bar"
13
- end
3
+ before do
4
+ Time.stub(:now).and_return(Time.at(1302461096))
5
+ end
6
+
7
+ subject do
8
+ Elasticity::AwsRequest.new('access', 'secret')
14
9
  end
15
10
 
16
- describe "#sign_params" do
17
- before do
18
- Time.stub(:now).and_return(Time.at(1302461096))
11
+ its(:access_key) { should == 'access' }
12
+ its(:secret_key) { should == 'secret' }
13
+
14
+ describe '#host' do
15
+
16
+ context 'when the region is not specified' do
17
+ its(:host) { should == 'elasticmapreduce.amazonaws.com' }
19
18
  end
20
- it "should sign according to Amazon's rules" do
21
- request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_access_key")
22
- signed_params = request.send(:sign_params, {}, "GET", "example.com", "/")
23
- signed_params.should == "AWSAccessKeyId=aws_access_key_id&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2011-04-10T18%3A44%3A56.000Z&Signature=jVLfPS056dNmjpCcikBnPmRHJNZ8YGaI7zdmHWUk658%3D"
19
+
20
+ context 'when the region is specified' do
21
+ let(:request_with_region) do
22
+ Elasticity::AwsRequest.new('_', '_', {:region => 'us-west-1'})
23
+ end
24
+ it 'should incorporate the region into the hostname' do
25
+ request_with_region.host.should == 'elasticmapreduce.us-west-1.amazonaws.com'
26
+ end
24
27
  end
28
+
25
29
  end
26
30
 
27
- describe "#aws_emr_request" do
28
- before do
29
- Time.stub(:now).and_return(Time.at(1302461096))
31
+ describe '#protocol' do
32
+
33
+ context 'when :secure is not specified' do
34
+ let(:default_request) { Elasticity::AwsRequest.new('_', '_') }
35
+ it 'should be https by default' do
36
+ default_request.protocol.should == 'https'
37
+ end
30
38
  end
31
39
 
32
- describe "options" do
40
+ context 'when :secure is specified' do
33
41
 
34
- describe "region" do
35
- context "when :region is specified" do
36
- it "should request against that region" do
37
- request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_access_key",
38
- :region => "eu-west-1")
39
- RestClient.should_receive(:get).with(/elasticmapreduce\.eu\-west\-1\.amazonaws\.com/)
40
- request.aws_emr_request({})
41
- end
42
- end
43
- context "when :region is not specified" do
44
- it "should use the default request url" do
45
- request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_access_key")
46
- RestClient.should_receive(:get).with(/elasticmapreduce\.amazonaws\.com/)
47
- request.aws_emr_request({})
48
- end
42
+ context 'when :secure is truthy' do
43
+ let(:secure_request) { Elasticity::AwsRequest.new('_', '_', {:secure => true}) }
44
+ it 'should be https' do
45
+ secure_request.protocol.should == 'https'
49
46
  end
50
47
  end
51
48
 
52
- describe ":secure" do
53
- context "when :secure is specified" do
54
- it "should use the value to determine the request type" do
55
- request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_access_key",
56
- :secure => false)
57
- RestClient.should_receive(:get).with(/^http:/)
58
- request.aws_emr_request({})
59
- end
49
+ context 'when :secure is falsey' do
50
+ let(:insecure_request) { Elasticity::AwsRequest.new('_', '_', {:secure => false}) }
51
+ it 'should be http' do
52
+ insecure_request.protocol.should == 'http'
60
53
  end
61
- context "when :secure is not specified" do
62
- it "should default to secure connection" do
63
- request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_access_key")
64
- RestClient.should_receive(:get).with(/^https:/)
65
- request.aws_emr_request({})
66
- end
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
60
+ describe '#sign_params' do
61
+ it 'should sign according to AWS rules' do
62
+ signed_params = subject.send(:sign_params, {})
63
+ signed_params.should == 'AWSAccessKeyId=access&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2011-04-10T18%3A44%3A56.000Z&Signature=QwQIiizWrfvWuLNnzmCMfaeXFfh9IQTvOix5MNVTh2s%3D'
64
+ end
65
+ end
66
+
67
+ describe '#submit' do
68
+
69
+ let(:request) do
70
+ Elasticity::AwsRequest.new('_', '_').tap do |r|
71
+ r.instance_variable_set(:@host, 'HOSTNAME')
72
+ r.instance_variable_set(:@protocol, 'PROTOCOL')
73
+ end
74
+ end
75
+
76
+ it 'should POST a properly assembled request' do
77
+ ruby_params = {}
78
+ aws_params = {}
79
+ Elasticity::AwsRequest.should_receive(:convert_ruby_to_aws).with(ruby_params).and_return(ruby_params)
80
+ request.should_receive(:sign_params).with(aws_params).and_return('SIGNED_PARAMS')
81
+ RestClient.should_receive(:post).with('PROTOCOL://HOSTNAME', 'SIGNED_PARAMS', :content_type => 'application/x-www-form-urlencoded; charset=utf-8')
82
+ request.submit(ruby_params)
83
+ end
84
+
85
+ context 'when there is an EMR error with the request' do
86
+ let(:error_message) { 'ERROR_MESSAGE' }
87
+ let(:error_xml) do
88
+ <<-ERROR
89
+ <ErrorResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
90
+ <Error>
91
+ <Message>#{error_message}</Message>
92
+ </Error>
93
+ </ErrorResponse>
94
+ ERROR
95
+ end
96
+ let(:error) do
97
+ RestClient::BadRequest.new.tap do |error|
98
+ error.stub(:http_body => error_xml)
67
99
  end
68
100
  end
69
101
 
102
+ it 'should raise an Argument error with the body of the error' do
103
+ RestClient.should_receive(:post).and_raise(error)
104
+ expect {
105
+ request.submit({})
106
+ }.to raise_error(ArgumentError, error_message)
107
+ end
108
+ end
109
+
110
+ end
111
+
112
+ describe '#==' do
113
+ let(:same_object) { subject }
114
+ let(:same_values) { Elasticity::AwsRequest.new('access', 'secret', {}) }
115
+ let(:diff_type) { Object.new }
116
+
117
+ it { should == same_object }
118
+ it { should == same_values }
119
+ it { should_not == diff_type }
120
+
121
+ it 'should be false on deep comparison' do
122
+ {
123
+ :@access_key => '_',
124
+ :@secret_key => '_',
125
+ :@options => {:foo => :bar}
126
+ }.each do |variable, value|
127
+ other = Elasticity::AwsRequest.new('aws_access_key_id', 'aws_secret_access_key', {})
128
+ other.instance_variable_set(variable, value)
129
+ subject.should_not == other
130
+ end
131
+ end
132
+
133
+ end
134
+
135
+ describe '.convert_ruby_to_aws' do
136
+ it 'should convert the params' do
137
+ add_jobflow_steps_params = {
138
+ :job_flow_id => 'j-1',
139
+ :steps => [
140
+ {
141
+ :action_on_failure => 'CONTINUE',
142
+ :name => 'First New Job Step',
143
+ :hadoop_jar_step => {
144
+ :args => ['arg1', 'arg2', 'arg3',],
145
+ :jar => 'first_step.jar',
146
+ :main_class => 'first_class.jar'
147
+ }
148
+ },
149
+ {
150
+ :action_on_failure => 'CANCEL_AND_WAIT',
151
+ :name => 'Second New Job Step',
152
+ :hadoop_jar_step => {
153
+ :args => ['arg4', 'arg5', 'arg6',],
154
+ :jar => 'second_step.jar',
155
+ :main_class => 'second_class.jar'
156
+ }
157
+ }
158
+ ]
159
+ }
160
+ expected_result = {
161
+ 'JobFlowId' => 'j-1',
162
+ 'Steps.member.1.Name' => 'First New Job Step',
163
+ 'Steps.member.1.ActionOnFailure' => 'CONTINUE',
164
+ 'Steps.member.1.HadoopJarStep.Jar' => 'first_step.jar',
165
+ 'Steps.member.1.HadoopJarStep.MainClass' => 'first_class.jar',
166
+ 'Steps.member.1.HadoopJarStep.Args.member.1' => 'arg1',
167
+ 'Steps.member.1.HadoopJarStep.Args.member.2' => 'arg2',
168
+ 'Steps.member.1.HadoopJarStep.Args.member.3' => 'arg3',
169
+ 'Steps.member.2.Name' => 'Second New Job Step',
170
+ 'Steps.member.2.ActionOnFailure' => 'CANCEL_AND_WAIT',
171
+ 'Steps.member.2.HadoopJarStep.Jar' => 'second_step.jar',
172
+ 'Steps.member.2.HadoopJarStep.MainClass' => 'second_class.jar',
173
+ 'Steps.member.2.HadoopJarStep.Args.member.1' => 'arg4',
174
+ 'Steps.member.2.HadoopJarStep.Args.member.2' => 'arg5',
175
+ 'Steps.member.2.HadoopJarStep.Args.member.3' => 'arg6'
176
+ }
177
+ Elasticity::AwsRequest.send(:convert_ruby_to_aws, add_jobflow_steps_params).should == expected_result
70
178
  end
71
179
  end
72
180
 
@@ -0,0 +1,59 @@
1
+ describe Elasticity::CustomJarStep do
2
+
3
+ subject do
4
+ Elasticity::CustomJarStep.new('jar')
5
+ end
6
+
7
+ it { should be_a Elasticity::JobFlowStep }
8
+
9
+ its(:name) { should == 'Elasticity Custom Jar Step (jar)' }
10
+ its(:jar) { should == 'jar' }
11
+ its(:arguments) { should == [] }
12
+ its(:action_on_failure) { should == 'TERMINATE_JOB_FLOW' }
13
+
14
+ describe '#to_aws_step' do
15
+
16
+ it { should respond_to(:to_aws_step).with(1).argument }
17
+
18
+ context 'when there are no arguments provided' do
19
+ let(:cjs_with_no_args) { Elasticity::CustomJarStep.new('jar') }
20
+
21
+ it 'should convert to aws step format' do
22
+ cjs_with_no_args.to_aws_step(Elasticity::JobFlow.new('access', 'secret')).should == {
23
+ :action_on_failure => 'TERMINATE_JOB_FLOW',
24
+ :hadoop_jar_step => {
25
+ :jar => 'jar'
26
+ },
27
+ :name => 'Elasticity Custom Jar Step (jar)'
28
+ }
29
+ end
30
+ end
31
+
32
+ context 'when there are arguments provided' do
33
+ let(:cjs_with_args) do
34
+ Elasticity::CustomJarStep.new('jar').tap do |cjs|
35
+ cjs.arguments = ['arg1', 'arg2']
36
+ end
37
+ end
38
+
39
+ it 'should convert to aws step format' do
40
+ cjs_with_args.to_aws_step(Elasticity::JobFlow.new('access', 'secret')).should == {
41
+ :action_on_failure => 'TERMINATE_JOB_FLOW',
42
+ :hadoop_jar_step => {
43
+ :jar => 'jar',
44
+ :args => ['arg1', 'arg2',],
45
+ },
46
+ :name => 'Elasticity Custom Jar Step (jar)'
47
+ }
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ describe '.requires_installation?' do
54
+ it 'should not require installation' do
55
+ Elasticity::CustomJarStep.requires_installation?.should be_false
56
+ end
57
+ end
58
+
59
+ end
@@ -1,879 +1,348 @@
1
- require 'spec_helper'
2
-
3
1
  describe Elasticity::EMR do
4
2
 
5
- describe "#add_instance_groups" do
6
-
7
- describe "integration happy path" do
8
-
9
- context "when properly specified" do
10
- use_vcr_cassette "add_instance_groups/one_group_successful", :record => :none
11
- it "should add the instance groups" do
12
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
13
- instance_group_config = {
14
- :instance_count => 1,
15
- :instance_role => "TASK",
16
- :instance_type => "m1.small",
17
- :market => "ON_DEMAND",
18
- :name => "Go Canucks Go!"
19
- }
20
- instance_group_ids = emr.add_instance_groups("j-OALI7TZTQMHX", [instance_group_config])
21
- instance_group_ids.should == ["ig-2GOVEN6HVJZID"]
22
- end
23
- end
24
-
25
- context "when improperly specified" do
26
- use_vcr_cassette "add_instance_groups/one_group_unsuccessful", :record => :none
27
- it "should add the instance groups" do
28
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
29
- instance_group_config = {
30
- :bid_price => 0,
31
- :instance_count => 1,
32
- :instance_role => "TASK",
33
- :instance_type => "m1.small",
34
- :market => "ON_DEMAND",
35
- :name => "Go Canucks Go!"
36
- }
37
- lambda {
38
- emr.add_instance_groups("j-19WDDS68ZUENP", [instance_group_config])
39
- }.should raise_error(ArgumentError, "Task instance group already exists in the job flow, cannot add more task groups")
40
- end
41
- end
3
+ subject do
4
+ Elasticity::EMR.new('ACCESS', 'SECRET')
5
+ end
42
6
 
43
- end
7
+ its(:aws_request) { should == Elasticity::AwsRequest.new('ACCESS', 'SECRET', {}) }
44
8
 
45
- describe "unit tests" do
46
-
47
- context "when multiple instance groups are specified" do
48
- before do
49
- @add_instance_groups_xml = <<-ADD_GROUPS
50
- <AddInstanceGroupsResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
51
- <AddInstanceGroupsResult>
52
- <JobFlowId>j-OALI7TZTQMHX</JobFlowId>
53
- <InstanceGroupIds>
54
- <member>ig-1</member>
55
- <member>ig-2</member>
56
- <member>ig-3</member>
57
- </InstanceGroupIds>
58
- </AddInstanceGroupsResult>
59
- </AddInstanceGroupsResponse>
60
- ADD_GROUPS
61
- end
9
+ describe '#add_instance_groups' do
62
10
 
63
- it "should iterate over them and send the correct params to AWS" do
64
- instance_group_configs = [
65
- {:instance_type=>"m1.small", :instance_role=>"CORE", :market=>"ON_DEMAND", :instance_count=>1, :name=>"Go Canucks Go!", :bid_price=>0},
66
- {:instance_type=>"m1.small", :instance_role=>"CORE", :market=>"ON_DEMAND", :instance_count=>1, :name=>"Go Canucks Go!", :bid_price=>0},
67
- ]
68
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
69
- aws_request.should_receive(:aws_emr_request).with({
70
- "Operation" => "AddInstanceGroups",
71
- "InstanceGroups.member.1.Name"=>"Go Canucks Go!",
72
- "InstanceGroups.member.1.InstanceRole"=>"CORE",
73
- "InstanceGroups.member.1.InstanceCount"=>1,
74
- "InstanceGroups.member.1.BidPrice"=>0,
75
- "InstanceGroups.member.1.InstanceType"=>"m1.small",
76
- "InstanceGroups.member.1.Market"=>"ON_DEMAND",
77
- "InstanceGroups.member.2.Name"=>"Go Canucks Go!",
78
- "InstanceGroups.member.2.InstanceRole"=>"CORE",
79
- "InstanceGroups.member.2.InstanceCount"=>1,
80
- "InstanceGroups.member.2.BidPrice"=>0,
81
- "InstanceGroups.member.2.InstanceType"=>"m1.small",
82
- "InstanceGroups.member.2.Market"=>"ON_DEMAND",
83
- "JobFlowId"=>"j-19WDDS68ZUENP"
84
- })
85
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
86
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
87
- emr.add_instance_groups("j-19WDDS68ZUENP", instance_group_configs)
88
- end
11
+ it 'should send the correct params to AWS' do
12
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).with({
13
+ :operation => 'AddInstanceGroups',
14
+ :job_flow_id => 'JOBFLOW_ID',
15
+ :instance_groups => ['INSTANCE_GROUP_CONFIGS']
16
+ })
17
+ subject.add_instance_groups('JOBFLOW_ID', ['INSTANCE_GROUP_CONFIGS'])
18
+ end
89
19
 
90
- it "should return an array of the instance groups created" do
91
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
92
- aws_request.should_receive(:aws_emr_request).and_return(@add_instance_groups_xml)
93
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
94
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
95
- emr.add_instance_groups("", []).should == ["ig-1", "ig-2", "ig-3"]
96
- end
20
+ describe 'return values' do
21
+ let(:aws_response) do
22
+ <<-XML
23
+ <AddInstanceGroupsResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
24
+ <AddInstanceGroupsResult>
25
+ <JobFlowId>j-OALI7TZTQMHX</JobFlowId>
26
+ <InstanceGroupIds>
27
+ <member>ig-1</member>
28
+ <member>ig-2</member>
29
+ <member>ig-3</member>
30
+ </InstanceGroupIds>
31
+ </AddInstanceGroupsResult>
32
+ </AddInstanceGroupsResponse>
33
+ XML
34
+ end
35
+
36
+ it 'should return an array of the new instance groups IDs' do
37
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).and_return(aws_response)
38
+ subject.add_instance_groups('', []).should == ['ig-1', 'ig-2', 'ig-3']
97
39
  end
40
+ end
98
41
 
99
- context "when a block is provided" do
100
- it "should yield the XML result" do
101
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
102
- aws_request.should_receive(:aws_emr_request).and_return("AWS XML")
103
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
104
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
105
- xml_result = nil
106
- emr.add_instance_groups("", []) do |xml|
107
- xml_result = xml
108
- end
109
- xml_result.should == "AWS XML"
42
+ context 'when a block is given' do
43
+ let(:result) { 'RESULT' }
44
+ it 'should yield the submission results' do
45
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).and_return(result)
46
+ subject.add_instance_groups('', []) do |xml|
47
+ xml.should == 'RESULT'
110
48
  end
111
49
  end
112
-
113
50
  end
114
51
 
115
52
  end
116
53
 
117
- describe "#add_jobflow_steps" do
118
-
119
- describe "integration happy path" do
120
- use_vcr_cassette "add_jobflow_steps/add_multiple_steps", :record => :none
121
-
122
- before do
123
- @setup_pig_step = {
124
- :action_on_failure => "TERMINATE_JOB_FLOW",
125
- :hadoop_jar_step => {
126
- :args => [
127
- "s3://elasticmapreduce/libs/pig/pig-script",
128
- "--base-path",
129
- "s3://elasticmapreduce/libs/pig/",
130
- "--install-pig"
131
- ],
132
- :jar => "s3://elasticmapreduce/libs/script-runner/script-runner.jar"
133
- },
134
- :name => "Setup Pig"
135
- }
136
- @emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
137
- @jobflow_id = @emr.run_job_flow({
138
- :name => "Elasticity Test Flow (EMR Pig Script)",
139
- :instances => {
140
- :ec2_key_name => "sharethrough-dev",
141
- :instance_count => 2,
142
- :master_instance_type => "m1.small",
143
- :slave_instance_type => "m1.small",
144
- },
145
- :steps => [@setup_pig_step]
146
- })
147
- end
148
-
149
- it "should add a job flow step to the specified job flow" do
150
- @emr.add_jobflow_steps(@jobflow_id, {
151
- :steps => [
152
- @setup_pig_step.merge(:name => "Setup Pig 2"),
153
- @setup_pig_step.merge(:name => "Setup Pig 3")
154
- ]
155
- })
156
- jobflow = @emr.describe_jobflows.select { |jf| jf.jobflow_id = @jobflow_id }.first
157
- jobflow.steps.map(&:name).should == ["Setup Pig", "Setup Pig 2", "Setup Pig 3"]
158
- end
54
+ describe '#add_jobflow_steps' do
159
55
 
56
+ it 'should add the specified steps to the job flow' do
57
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).with({
58
+ :operation => 'AddJobFlowSteps',
59
+ :job_flow_id => 'JOBFLOW_ID',
60
+ :steps => ['_']
61
+ })
62
+ subject.add_jobflow_steps('JOBFLOW_ID', {:steps => ['_']})
160
63
  end
161
64
 
162
- describe "unit tests" do
163
-
164
- it "should add the specified steps to the job flow" do
165
- aws_request = Elasticity::AwsRequest.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
166
- aws_request.should_receive(:aws_emr_request).with({
167
- "Operation" => "AddJobFlowSteps",
168
- "JobFlowId" => "j-1",
169
- "Steps.member.1.Name" => "Step 1",
170
- "Steps.member.1.ActionOnFailure" => "TERMINATE_JOB_FLOW",
171
- "Steps.member.1.HadoopJarStep.Jar" => "jar1",
172
- "Steps.member.1.HadoopJarStep.Args.member.1" => "arg1-1",
173
- "Steps.member.1.HadoopJarStep.Args.member.2" => "arg1-2",
174
- "Steps.member.2.Name" => "Step 2",
175
- "Steps.member.2.ActionOnFailure" => "CONTINUE",
176
- "Steps.member.2.HadoopJarStep.Jar" => "jar2",
177
- "Steps.member.2.HadoopJarStep.Args.member.1" => "arg2-1",
178
- "Steps.member.2.HadoopJarStep.Args.member.2" => "arg2-2",
179
- })
180
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
181
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
182
- emr.add_jobflow_steps("j-1", {
183
- :steps => [
184
- {
185
- :action_on_failure => "TERMINATE_JOB_FLOW",
186
- :name => "Step 1",
187
- :hadoop_jar_step => {
188
- :args => ["arg1-1", "arg1-2"],
189
- :jar => "jar1",
190
- }
191
- },
192
- {
193
- :action_on_failure => "CONTINUE",
194
- :name => "Step 2",
195
- :hadoop_jar_step => {
196
- :args => ["arg2-1", "arg2-2"],
197
- :jar => "jar2",
198
- }
199
- }
200
- ]
201
- })
202
- end
203
-
204
- context "when there is an error" do
205
- before do
206
- @error_message = "2 validation errors detected: Value null at 'steps' failed to satisfy constraint: Member must not be null; Value null at 'jobFlowId' failed to satisfy constraint: Member must not be null"
207
- @error_xml = <<-ERROR
208
- <ErrorResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
209
- <Error>
210
- <Message>#{@error_message}</Message>
211
- </Error>
212
- </ErrorResponse>
213
- ERROR
214
- end
215
-
216
- it "should raise an ArgumentError with the error message" do
217
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
218
- @exception = RestClient::BadRequest.new
219
- @exception.should_receive(:http_body).and_return(@error_xml)
220
- aws_request.should_receive(:aws_emr_request).and_raise(@exception)
221
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
222
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
223
- lambda {
224
- emr.add_jobflow_steps("", {})
225
- }.should raise_error(ArgumentError, @error_message)
226
- end
227
- end
228
-
229
- context "when a block is given" do
230
- it "should yield the XML result" do
231
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
232
- aws_request.should_receive(:aws_emr_request).and_return("xml_response")
233
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
234
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
235
- xml_result = nil
236
- emr.add_jobflow_steps("", {}) do |xml|
237
- xml_result = xml
238
- end
239
- xml_result.should == "xml_response"
65
+ context 'when a block is given' do
66
+ let(:result) { 'RESULT' }
67
+ it 'should yield the submission results' do
68
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).and_return(result)
69
+ subject.add_jobflow_steps('', {}) do |xml|
70
+ xml.should == 'RESULT'
240
71
  end
241
72
  end
242
-
243
-
244
73
  end
245
74
 
246
75
  end
247
76
 
248
- describe "#describe_jobflows" do
77
+ describe '#describe_jobflows' do
249
78
 
250
- describe "integration happy path" do
251
- use_vcr_cassette "describe_jobflows/all_jobflows", :record => :none
252
- it "should return the names of all running job flows" do
253
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
254
- jobflows = emr.describe_jobflows
255
- jobflows.map(&:name).should == ["WM+RS", "Interactive Audience Hive Test", "Audience (Hive)", "Audience Reporting"]
256
- jobflows.map(&:jobflow_id).should == ["j-1MZ5TVWFJRSKN", "j-38EU2XZQP9KJ4", "j-2TDCVGEEHOFI9", "j-NKKQ429D858I"]
257
- jobflows.map(&:state).should == ["TERMINATED", "TERMINATED", "TERMINATED", "TERMINATED"]
258
- end
79
+ let(:describe_jobflows_xml) do
80
+ <<-XML
81
+ <DescribeJobFlowsResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
82
+ <DescribeJobFlowsResult>
83
+ <JobFlows>
84
+ <member>
85
+ <ExecutionStatusDetail>
86
+ <CreationDateTime>2011-04-04T17:41:51Z</CreationDateTime>
87
+ <State>TERMINATED</State>
88
+ </ExecutionStatusDetail>
89
+ <JobFlowId>j-p</JobFlowId>
90
+ <Name>Pig Job</Name>
91
+ </member>
92
+ <member>
93
+ <ExecutionStatusDetail>
94
+ <State>TERMINATED</State>
95
+ <CreationDateTime>2011-04-04T17:41:51Z</CreationDateTime>
96
+ </ExecutionStatusDetail>
97
+ <JobFlowId>j-h</JobFlowId>
98
+ <Name>Hive Job</Name>
99
+ </member>
100
+ </JobFlows>
101
+ </DescribeJobFlowsResult>
102
+ </DescribeJobFlowsResponse>
103
+ XML
259
104
  end
260
105
 
261
- describe "unit tests" do
262
- before do
263
- @describe_jobflows_xml = <<-JOBFLOWS
264
- <DescribeJobFlowsResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
265
- <DescribeJobFlowsResult>
266
- <JobFlows>
267
- <member>
268
- <ExecutionStatusDetail>
269
- <CreationDateTime>2011-04-04T17:41:51Z</CreationDateTime>
270
- <State>TERMINATED</State>
271
- </ExecutionStatusDetail>
272
- <JobFlowId>j-p</JobFlowId>
273
- <Name>Pig Job</Name>
274
- </member>
275
- <member>
276
- <ExecutionStatusDetail>
277
- <State>TERMINATED</State>
278
- <CreationDateTime>2011-04-04T17:41:51Z</CreationDateTime>
279
- </ExecutionStatusDetail>
280
- <JobFlowId>j-h</JobFlowId>
281
- <Name>Hive Job</Name>
282
- </member>
283
- </JobFlows>
284
- </DescribeJobFlowsResult>
285
- </DescribeJobFlowsResponse>
286
- JOBFLOWS
287
- end
106
+ it 'should return an array of properly populated JobFlowStatusES' do
107
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).and_return(describe_jobflows_xml)
108
+ jobflow_statuses = subject.describe_jobflows
109
+ jobflow_statuses.map(&:name).should == ['Pig Job', 'Hive Job']
110
+ jobflow_statuses.map(&:class).should == [Elasticity::JobFlowStatus, Elasticity::JobFlowStatus]
111
+ end
288
112
 
289
- it "should return the names of all running job flows" do
290
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
291
- aws_request.should_receive(:aws_emr_request).with({"Operation" => "DescribeJobFlows"}).and_return(@describe_jobflows_xml)
292
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
293
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
294
- jobflows = emr.describe_jobflows
295
- jobflows.map(&:name).should == ["Pig Job", "Hive Job"]
296
- end
113
+ it 'should describe all jobflows' do
114
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).with({
115
+ :operation => 'DescribeJobFlows'
116
+ })
117
+ subject.describe_jobflows
118
+ end
297
119
 
298
- it "should accept additional parameters" do
299
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
300
- aws_request.should_receive(:aws_emr_request).with({"Operation" => "DescribeJobFlows","CreatedBefore" => "2011-10-04"}).and_return(@describe_jobflows_xml)
301
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
302
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
303
- emr.describe_jobflows(:CreatedBefore => "2011-10-04")
120
+ context 'when additional parameters are provided' do
121
+ it 'should pass them through' do
122
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).with({
123
+ :CreatedBefore => '2011-10-04',
124
+ :operation => 'DescribeJobFlows'
125
+ })
126
+ subject.describe_jobflows(:CreatedBefore => '2011-10-04')
304
127
  end
128
+ end
305
129
 
306
- context "when a block is provided" do
307
- it "should yield the XML result" do
308
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
309
- aws_request.should_receive(:aws_emr_request).and_return("describe!")
310
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
311
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
312
- xml_result = nil
313
- emr.describe_jobflows do |xml|
314
- xml_result = xml
315
- end
316
- xml_result.should == "describe!"
130
+ context 'when a block is given' do
131
+ let(:result) { 'RESULT' }
132
+ it 'should yield the submission results' do
133
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).and_return(result)
134
+ subject.describe_jobflows do |xml|
135
+ xml.should == 'RESULT'
317
136
  end
318
137
  end
319
138
  end
320
139
 
321
140
  end
322
141
 
323
- describe "#describe_jobflow" do
324
- before do
325
- @describe_jobflows_xml = <<-JOBFLOWS
142
+ describe '#describe_jobflow' do
143
+
144
+ let(:describe_jobflows_xml) {
145
+ <<-XML
326
146
  <DescribeJobFlowsResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
327
147
  <DescribeJobFlowsResult>
328
148
  <JobFlows>
329
149
  <member>
150
+ <JobFlowId>j-3UN6WX5RRO2AG</JobFlowId>
151
+ <Name>The One Job Flow</Name>
330
152
  <ExecutionStatusDetail>
331
153
  <State>TERMINATED</State>
332
154
  <CreationDateTime>2011-04-04T17:41:51Z</CreationDateTime>
333
155
  </ExecutionStatusDetail>
334
- <JobFlowId>j-3UN6WX5RRO2AG</JobFlowId>
335
- <Name>The One Job Flow</Name>
336
156
  </member>
337
157
  </JobFlows>
338
158
  </DescribeJobFlowsResult>
339
159
  </DescribeJobFlowsResponse>
340
- JOBFLOWS
341
- end
160
+ XML
161
+ }
342
162
 
343
- it "should ask AWS about the specified job flow" do
344
- aws_request = Elasticity::AwsRequest.new("","")
345
- aws_request.should_receive(:aws_emr_request).with({
346
- "Operation" => "DescribeJobFlows",
347
- "JobFlowIds.member.1" => "j-3UN6WX5RRO2AG"
163
+ it 'should describe the specified jobflow' do
164
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).with({
165
+ :operation => 'DescribeJobFlows',
166
+ :job_flow_ids => ['j-3UN6WX5RRO2AG']
348
167
  })
349
- Elasticity::AwsRequest.stub(:new).and_return(aws_request)
350
- emr = Elasticity::EMR.new("", "")
351
- emr.describe_jobflow("j-3UN6WX5RRO2AG")
352
- end
353
-
354
- context "when the job flow ID exists" do
355
- it "should return a JobFlow" do
356
- aws_request = Elasticity::AwsRequest.new("","")
357
- aws_request.stub(:aws_emr_request).with({
358
- "Operation" => "DescribeJobFlows",
359
- "JobFlowIds.member.1" => "j-3UN6WX5RRO2AG"
360
- }).and_return(@describe_jobflows_xml)
361
- Elasticity::AwsRequest.stub(:new).and_return(aws_request)
362
- emr = Elasticity::EMR.new("", "")
363
- jobflow = emr.describe_jobflow("j-3UN6WX5RRO2AG")
364
- jobflow.jobflow_id.should == "j-3UN6WX5RRO2AG"
365
- end
168
+ subject.describe_jobflow('j-3UN6WX5RRO2AG')
366
169
  end
367
170
 
368
- context "when there is an error" do
369
- before do
370
- @error_xml = <<-ERROR
371
- <ErrorResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
372
- <Error>
373
- <Message>Specified job flow ID not valid</Message>
374
- </Error>
375
- </ErrorResponse>
376
- ERROR
377
- end
378
-
379
- it "should raise an ArgumentError with the error message" do
380
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
381
- @exception = RestClient::BadRequest.new
382
- @exception.should_receive(:http_body).and_return(@error_xml)
383
- aws_request.should_receive(:aws_emr_request).and_raise(@exception)
384
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
385
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
386
- lambda {
387
- emr.describe_jobflow("bad_jobflow_id")
388
- }.should raise_error(ArgumentError, "Specified job flow ID not valid")
389
- end
171
+ it 'should return a properly populated JobFlowStatus' do
172
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).and_return(describe_jobflows_xml)
173
+ jobflow_status = subject.describe_jobflow('_')
174
+ jobflow_status.should be_a Elasticity::JobFlowStatus
175
+ jobflow_status.jobflow_id.should == 'j-3UN6WX5RRO2AG'
390
176
  end
391
177
 
392
- context "when a block is provided" do
393
- it "should yield to the block" do
394
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
395
- aws_request.should_receive(:aws_emr_request).and_return("describe!")
396
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
397
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
398
- xml_result = nil
399
- emr.describe_jobflow("_") do |xml|
400
- xml_result = xml
178
+ context 'when a block is given' do
179
+ let(:result) { 'RESULT' }
180
+ it 'should yield the submission results' do
181
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).and_return(result)
182
+ subject.describe_jobflow('') do |xml|
183
+ xml.should == 'RESULT'
401
184
  end
402
- xml_result.should == "describe!"
403
185
  end
404
186
  end
405
187
  end
406
188
 
407
- describe "#modify_instance_groups" do
189
+ describe '#modify_instance_groups' do
408
190
 
409
- describe "integration happy path" do
410
- context "when the instance group exists" do
411
- use_vcr_cassette "modify_instance_groups/set_instances_to_3", :record => :none
412
- it "should terminate the specified jobflow" do
413
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
414
- instance_group_config = {"ig-2T1HNUO61BG3O" => 2}
415
- emr.modify_instance_groups(instance_group_config)
416
- end
417
- end
191
+ it 'should modify the specified instance groups' do
192
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).with({
193
+ :operation => 'ModifyInstanceGroups',
194
+ :instance_groups => [{
195
+ :instance_group_id => 'ig-2T1HNUO61BG3O',
196
+ :instance_count => 2
197
+ }]
198
+ })
199
+ subject.modify_instance_groups({'ig-2T1HNUO61BG3O' => 2})
418
200
  end
419
201
 
420
- describe "unit tests" do
421
-
422
- context "when the instance group exists" do
423
- it "should modify the specified instance group" do
424
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
425
- aws_request.should_receive(:aws_emr_request).with({
426
- "Operation" => "ModifyInstanceGroups",
427
- "InstanceGroups.member.1.InstanceGroupId" => "ig-1",
428
- "InstanceGroups.member.1.InstanceCount" => 2
429
- })
430
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
431
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
432
- emr.modify_instance_groups({"ig-1" => 2})
202
+ context 'when a block is given' do
203
+ let(:result) { '_' }
204
+ it 'should yield the submission results' do
205
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).and_return(result)
206
+ subject.modify_instance_groups({}) do |xml|
207
+ xml.should == '_'
433
208
  end
434
209
  end
435
-
436
- context "when a block is given" do
437
- it "should yield the XML result" do
438
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
439
- aws_request.should_receive(:aws_emr_request).and_return("xml result!")
440
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
441
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
442
- xml_result = nil
443
- emr.modify_instance_groups({"ig-1" => 2}) do |xml|
444
- xml_result = xml
445
- end
446
- xml_result.should == "xml result!"
447
- end
448
- end
449
-
450
-
451
- context "when there is an error" do
452
-
453
- before do
454
- @error_message = "1 validation error detected: Value null at 'instanceGroups.1.member.instanceCount' failed to satisfy constraint: Member must not be null"
455
- @error_xml = <<-ERROR
456
- <ErrorResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
457
- <Error>
458
- <Message>#{@error_message}</Message>
459
- </Error>
460
- </ErrorResponse>
461
- ERROR
462
- end
463
-
464
- it "should raise an ArgumentError with the error message" do
465
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
466
- @exception = RestClient::BadRequest.new
467
- @exception.should_receive(:http_body).and_return(@error_xml)
468
- aws_request.should_receive(:aws_emr_request).and_raise(@exception)
469
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
470
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
471
- lambda {
472
- emr.modify_instance_groups({"ig-1" => 2})
473
- }.should raise_error(ArgumentError, @error_message)
474
- end
475
-
476
- end
477
-
478
210
  end
479
211
 
480
212
  end
481
213
 
482
- describe "#run_jobflow" do
483
-
484
- describe "integration happy path" do
485
-
486
- context "when the job flow is properly specified" do
487
- use_vcr_cassette "run_jobflow/word_count", :record => :none
488
- it "should start the specified job flow" do
489
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
490
- jobflow_id = emr.run_job_flow({
491
- :name => "Elasticity Test Flow (EMR Pig Script)",
492
- :instances => {
493
- :ec2_key_name => "sharethrough-dev",
494
- :hadoop_version => "0.20",
495
- :instance_count => 2,
496
- :master_instance_type => "m1.small",
497
- :placement => {
498
- :availability_zone => "us-east-1a"
499
- },
500
- :slave_instance_type => "m1.small",
501
- },
502
- :steps => [
503
- {
504
- :action_on_failure => "TERMINATE_JOB_FLOW",
505
- :hadoop_jar_step => {
506
- :args => [
507
- "s3://elasticmapreduce/libs/pig/pig-script",
508
- "--base-path",
509
- "s3://elasticmapreduce/libs/pig/",
510
- "--install-pig"
511
- ],
512
- :jar => "s3://elasticmapreduce/libs/script-runner/script-runner.jar"
513
- },
514
- :name => "Setup Pig"
515
- },
516
- {
517
- :action_on_failure => "TERMINATE_JOB_FLOW",
518
- :hadoop_jar_step => {
519
- :args => [
520
- "s3://elasticmapreduce/libs/pig/pig-script",
521
- "--run-pig-script",
522
- "--args",
523
- "-p",
524
- "INPUT=s3n://elasticmapreduce/samples/pig-apache/input",
525
- "-p",
526
- "OUTPUT=s3n://slif-elasticity/pig-apache/output/2011-04-19",
527
- "s3n://elasticmapreduce/samples/pig-apache/do-reports.pig"
528
- ],
529
- :jar => "s3://elasticmapreduce/libs/script-runner/script-runner.jar"
530
- },
531
- :name => "Run Pig Script"
532
- }
533
- ]
534
- })
535
- jobflow_id.should == "j-G6N5HA528AD4"
536
- end
537
- end
214
+ describe '#run_jobflow' do
215
+
216
+ it 'should start the specified job flow' do
217
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).with({
218
+ :operation => 'RunJobFlow',
219
+ :jobflow_params => '_'
220
+ })
221
+ subject.run_job_flow({:jobflow_params => '_'})
538
222
  end
539
223
 
540
- describe "unit tests" do
541
- it "should return the job flow ID of the new job" do
542
- run_jobflow_response = <<-RESPONSE
224
+ describe 'jobflow response handling' do
225
+ let(:jobflow_xml_response) do
226
+ <<-XML
543
227
  <RunJobFlowResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
544
228
  <RunJobFlowResult>
545
- <JobFlowId>j-N500G8Y8U7ZQ</JobFlowId>
229
+ <JobFlowId>j-G6N5HA528AD4</JobFlowId>
546
230
  </RunJobFlowResult>
547
231
  <ResponseMetadata>
548
- <RequestId>a6dddf4c-6a49-11e0-b6c0-e9580d1f7304</RequestId>
232
+ <RequestId>b22f4aea-6a4b-11e0-9ddc-a168e244afdb</RequestId>
549
233
  </ResponseMetadata>
550
234
  </RunJobFlowResponse>
551
- RESPONSE
552
- aws_request = Elasticity::AwsRequest.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
553
- aws_request.should_receive(:aws_emr_request).and_return(run_jobflow_response)
554
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
555
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
556
- jobflow_id = emr.run_job_flow({})
557
- jobflow_id.should == "j-N500G8Y8U7ZQ"
558
- end
559
-
560
- it "should run the specified job flow" do
561
- aws_request = Elasticity::AwsRequest.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
562
- aws_request.should_receive(:aws_emr_request).with({
563
- "Operation" => "RunJobFlow",
564
- "Name" => "Job flow name",
565
- "Instances.MasterInstanceType" => "m1.small",
566
- "Instances.Placement.AvailabilityZone" => "us-east-1a",
567
- "Steps.member.1.Name" => "Streaming Job",
568
- "Steps.member.1.ActionOnFailure" => "TERMINATE_JOB_FLOW",
569
- "Steps.member.1.HadoopJarStep.Jar" => "/home/hadoop/contrib/streaming/hadoop-streaming.jar",
570
- "Steps.member.1.HadoopJarStep.Args.member.1" => "-input",
571
- "Steps.member.1.HadoopJarStep.Args.member.2" => "s3n://elasticmapreduce/samples/wordcount/input"
572
- })
573
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
574
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
575
- emr.run_job_flow({
576
- :name => "Job flow name",
577
- :instances => {
578
- :master_instance_type => "m1.small",
579
- :placement => {
580
- :availability_zone => "us-east-1a"
581
- }
582
- },
583
- :steps => [
584
- {
585
- :action_on_failure => "TERMINATE_JOB_FLOW",
586
- :name => "Streaming Job",
587
- :hadoop_jar_step => {
588
- :args => ["-input", "s3n://elasticmapreduce/samples/wordcount/input"],
589
- :jar => "/home/hadoop/contrib/streaming/hadoop-streaming.jar",
590
- }
591
- }
592
- ]
593
- })
235
+ XML
594
236
  end
595
237
 
596
- context "when there is an error" do
597
- before do
598
- @error_message = "1 validation error detected: Value null at 'instanceGroups.1.member.instanceCount' failed to satisfy constraint: Member must not be null"
599
- @error_xml = <<-ERROR
600
- <ErrorResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
601
- <Error>
602
- <Message>#{@error_message}</Message>
603
- </Error>
604
- </ErrorResponse>
605
- ERROR
606
- end
607
-
608
- it "should raise an ArgumentError with the error message" do
609
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
610
- @exception = RestClient::BadRequest.new
611
- @exception.should_receive(:http_body).and_return(@error_xml)
612
- aws_request.should_receive(:aws_emr_request).and_raise(@exception)
613
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
614
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
615
- lambda {
616
- emr.run_job_flow({})
617
- }.should raise_error(ArgumentError, @error_message)
618
- end
238
+ it 'should return the ID of the running job flow' do
239
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).and_return(jobflow_xml_response)
240
+ subject.run_job_flow({}).should == 'j-G6N5HA528AD4'
619
241
  end
242
+ end
620
243
 
621
- context "when a block is given" do
622
- it "should yield the XML result" do
623
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
624
- aws_request.should_receive(:aws_emr_request).and_return("jobflow_id!")
625
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
626
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
627
- xml_result = nil
628
- emr.run_job_flow({}) do |xml|
629
- xml_result = xml
630
- end
631
- xml_result.should == "jobflow_id!"
244
+ context 'when a block is given' do
245
+ let(:result) { '_' }
246
+ it 'should yield the submission results' do
247
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).and_return(result)
248
+ subject.run_job_flow({}) do |xml|
249
+ xml.should == '_'
632
250
  end
633
251
  end
634
252
  end
635
253
 
636
254
  end
637
255
 
638
- describe "#terminate_jobflows" do
256
+ describe '#terminate_jobflows' do
639
257
 
640
- describe "integration happy path" do
641
- context "when the job flow exists" do
642
- use_vcr_cassette "terminate_jobflows/one_jobflow", :record => :none
643
- it "should terminate the specified jobflow" do
644
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
645
- emr.terminate_jobflows("j-1MZ5TVWFJRSKN")
646
- end
647
- end
258
+ it 'should terminate the specific jobflow' do
259
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).with({
260
+ :operation => 'TerminateJobFlows',
261
+ :job_flow_ids => ['j-1']
262
+ })
263
+ subject.terminate_jobflows('j-1')
648
264
  end
649
265
 
650
- describe "unit tests" do
651
-
652
- context "when the jobflow exists" do
653
- before do
654
- @terminate_jobflows_xml = <<-RESPONSE
655
- <TerminateJobFlowsResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
656
- <ResponseMetadata>
657
- <RequestId>2690d7eb-ed86-11dd-9877-6fad448a8419</RequestId>
658
- </ResponseMetadata>
659
- </TerminateJobFlowsResponse>
660
- RESPONSE
661
- end
662
- it "should terminate the specific jobflow" do
663
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
664
- aws_request.should_receive(:aws_emr_request).with({
665
- "Operation" => "TerminateJobFlows",
666
- "JobFlowIds.member.1" => "j-1"
667
- }).and_return(@terminate_jobflows_xml)
668
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
669
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
670
- emr.terminate_jobflows("j-1")
671
- end
672
- end
673
-
674
- context "when the jobflow does not exist" do
675
- it "should raise an ArgumentError" do
676
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
677
- aws_request.should_receive(:aws_emr_request).and_raise(RestClient::BadRequest)
678
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
679
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
680
- lambda {
681
- emr.terminate_jobflows("invalid_jobflow_id")
682
- }.should raise_error(ArgumentError)
683
- end
684
- end
685
-
686
- context "when a block is given" do
687
- it "should yield the XML result" do
688
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
689
- aws_request.should_receive(:aws_emr_request).and_return("terminated!")
690
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
691
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
692
- xml_result = nil
693
- emr.terminate_jobflows("j-1") do |xml|
694
- xml_result = xml
695
- end
696
- xml_result.should == "terminated!"
266
+ context 'when a block is given' do
267
+ let(:result) { '_' }
268
+ it 'should yield the termination results' do
269
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).and_return(result)
270
+ subject.terminate_jobflows('j-1') do |xml|
271
+ xml.should == '_'
697
272
  end
698
273
  end
699
-
700
274
  end
701
- end
702
-
703
- describe "#set_termination_protection" do
704
-
705
- describe "integration happy path" do
706
-
707
- context "when protecting multiple job flows" do
708
- use_vcr_cassette "set_termination_protection/protect_multiple_job_flows", :record => :none
709
- it "should protect the specified job flows" do
710
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
711
- emr.set_termination_protection(["j-1B4D1XP0C0A35", "j-1YG2MYL0HVYS5"], true)
712
- end
713
- end
714
275
 
715
- context "when specifying a job flow that doesn't exist" do
716
- use_vcr_cassette "set_termination_protection/nonexistent_job_flows", :record => :none
717
- it "should have an error" do
718
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
719
- lambda {
720
- emr.set_termination_protection(["j-1B4D1XP0C0A35", "j-2"], true)
721
- }.should raise_error(ArgumentError, "Specified job flow ID not valid")
722
- end
723
- end
276
+ end
724
277
 
725
- end
278
+ describe '#set_termination_protection' do
726
279
 
727
- describe "unit tests" do
728
- it "should enable protection on the specified job flows" do
729
- aws_request = Elasticity::AwsRequest.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
730
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
731
- aws_request.should_receive(:aws_emr_request).with({
732
- "Operation" => "SetTerminationProtection",
733
- "JobFlowIds.member.1" => "jobflow1",
734
- "JobFlowIds.member.2" => "jobflow2",
735
- "TerminationProtected" => true
280
+ context 'when protection is enabled' do
281
+ it 'should enable protection on the specified jobflows' do
282
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).with({
283
+ :operation => 'SetTerminationProtection',
284
+ :termination_protected => true,
285
+ :job_flow_ids => ['jobflow1', 'jobflow2']
736
286
  })
737
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
738
- emr.set_termination_protection(["jobflow1", "jobflow2"], true)
287
+ subject.set_termination_protection(['jobflow1', 'jobflow2'], true)
739
288
  end
289
+ end
740
290
 
741
- it "should disable protection on the specified job flows" do
742
- aws_request = Elasticity::AwsRequest.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
743
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
744
- aws_request.should_receive(:aws_emr_request).with({
745
- "Operation" => "SetTerminationProtection",
746
- "JobFlowIds.member.1" => "jobflow1",
747
- "JobFlowIds.member.2" => "jobflow2",
748
- "TerminationProtected" => false
291
+ context 'when protection is disabled' do
292
+ it 'should disable protection on the specified jobflows' do
293
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).with({
294
+ :operation => 'SetTerminationProtection',
295
+ :termination_protected => false,
296
+ :job_flow_ids => ['jobflow1', 'jobflow2']
749
297
  })
750
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
751
- emr.set_termination_protection(["jobflow1", "jobflow2"], false)
298
+ subject.set_termination_protection(['jobflow1', 'jobflow2'], false)
752
299
  end
300
+ end
753
301
 
754
- it "should enable protection when not specified" do
755
- aws_request = Elasticity::AwsRequest.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
756
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
757
- aws_request.should_receive(:aws_emr_request).with({
758
- "Operation" => "SetTerminationProtection",
759
- "JobFlowIds.member.1" => "jobflow1",
760
- "JobFlowIds.member.2" => "jobflow2",
761
- "TerminationProtected" => true
302
+ context 'when protection is not specified' do
303
+ it 'should enable protection on the specified jobflows' do
304
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).with({
305
+ :operation => 'SetTerminationProtection',
306
+ :termination_protected => true,
307
+ :job_flow_ids => ['jobflow1', 'jobflow2']
762
308
  })
763
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
764
- emr.set_termination_protection(["jobflow1", "jobflow2"])
309
+ subject.set_termination_protection(['jobflow1', 'jobflow2'])
765
310
  end
311
+ end
766
312
 
767
- context "when a block is given" do
768
- before do
769
- @xml_response = <<-RESPONSE
770
- <SetTerminationProtectionResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
771
- <ResponseMetadata>
772
- <RequestId>755ebe8a-6923-11e0-a9c2-c126f1bb4493</RequestId>
773
- </ResponseMetadata>
774
- </SetTerminationProtectionResponse>
775
- RESPONSE
776
- end
777
- it "should yield the XML result" do
778
- aws_request = Elasticity::AwsRequest.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
779
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
780
- aws_request.should_receive(:aws_emr_request).and_return(@xml_response)
781
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
782
- xml = nil
783
- emr.set_termination_protection([]) do |aws_response|
784
- xml = aws_response
785
- end
786
- xml.should == @xml_response
313
+ context 'when a block is given' do
314
+ let(:result) { '_' }
315
+ it 'should yield the termination results' do
316
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).and_return(result)
317
+ subject.set_termination_protection([]) do |xml|
318
+ xml.should == '_'
787
319
  end
788
320
  end
789
321
  end
790
322
 
791
323
  end
792
324
 
793
- describe "#direct" do
794
-
795
- describe "integration happy path" do
796
- use_vcr_cassette "direct/terminate_jobflow", :record => :none
797
- it "should terminate the specified jobflow" do
798
- emr = Elasticity::EMR.new(AWS_ACCESS_KEY_ID, AWS_SECRET_KEY)
799
- params = {
800
- "Operation" => "TerminateJobFlows",
801
- "JobFlowIds.member.1" => "j-1MZ5TVWFJRSKN"
802
- }
803
- emr.direct(params)
804
- end
805
- end
325
+ describe '#direct' do
326
+ let(:params) { {:foo => 'bar'} }
806
327
 
807
- describe "unit tests" do
808
- before do
809
- @terminate_jobflows_xml = <<-RESPONSE
810
- <TerminateJobFlowsResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
811
- <ResponseMetadata>
812
- <RequestId>2690d7eb-ed86-11dd-9877-6fad448a8419</RequestId>
813
- </ResponseMetadata>
814
- </TerminateJobFlowsResponse>
815
- RESPONSE
816
- end
817
- it "should pass through directly to the request" do
818
- aws_request = Elasticity::AwsRequest.new("aws_access_key_id", "aws_secret_key")
819
- aws_request.should_receive(:aws_emr_request).with({
820
- "Operation" => "TerminateJobFlows",
821
- "JobFlowIds.member.1" => "j-1"
822
- }).and_return(@terminate_jobflows_xml)
823
- Elasticity::AwsRequest.should_receive(:new).and_return(aws_request)
824
- emr = Elasticity::EMR.new("aws_access_key_id", "aws_secret_key")
825
- params = {
826
- "Operation" => "TerminateJobFlows",
827
- "JobFlowIds.member.1" => "j-1"
828
- }
829
- emr.direct(params).should == @terminate_jobflows_xml
830
- end
328
+ it 'should pass through directly to the request and return the results of the request' do
329
+ Elasticity::AwsRequest.any_instance.should_receive(:submit).with(params).and_return('RESULT')
330
+ subject.direct(params).should == 'RESULT'
831
331
  end
832
332
  end
833
333
 
834
- describe ".convert_ruby_to_aws" do
835
- it "should convert the params" do
836
- add_jobflow_steps_params = {
837
- :job_flow_id => "j-1",
838
- :steps => [
839
- {
840
- :action_on_failure => "CONTINUE",
841
- :name => "First New Job Step",
842
- :hadoop_jar_step => {
843
- :args => ["arg1", "arg2", "arg3",],
844
- :jar => "first_step.jar",
845
- :main_class => "first_class.jar"
846
- }
847
- },
848
- {
849
- :action_on_failure => "CANCEL_AND_WAIT",
850
- :name => "Second New Job Step",
851
- :hadoop_jar_step => {
852
- :args => ["arg4", "arg5", "arg6",],
853
- :jar => "second_step.jar",
854
- :main_class => "second_class.jar"
855
- }
856
- }
857
- ]
858
- }
859
- expected_result = {
860
- "JobFlowId" => "j-1",
861
- "Steps.member.1.Name" => "First New Job Step",
862
- "Steps.member.1.ActionOnFailure" => "CONTINUE",
863
- "Steps.member.1.HadoopJarStep.Jar" => "first_step.jar",
864
- "Steps.member.1.HadoopJarStep.MainClass" => "first_class.jar",
865
- "Steps.member.1.HadoopJarStep.Args.member.1" => "arg1",
866
- "Steps.member.1.HadoopJarStep.Args.member.2" => "arg2",
867
- "Steps.member.1.HadoopJarStep.Args.member.3" => "arg3",
868
- "Steps.member.2.Name" => "Second New Job Step",
869
- "Steps.member.2.ActionOnFailure" => "CANCEL_AND_WAIT",
870
- "Steps.member.2.HadoopJarStep.Jar" => "second_step.jar",
871
- "Steps.member.2.HadoopJarStep.MainClass" => "second_class.jar",
872
- "Steps.member.2.HadoopJarStep.Args.member.1" => "arg4",
873
- "Steps.member.2.HadoopJarStep.Args.member.2" => "arg5",
874
- "Steps.member.2.HadoopJarStep.Args.member.3" => "arg6"
875
- }
876
- Elasticity::EMR.send(:convert_ruby_to_aws, add_jobflow_steps_params).should == expected_result
334
+ describe '#==' do
335
+ let(:emr1) { Elasticity::EMR.new('ACCESS1', 'SECRET1') }
336
+ let(:emr2) { Elasticity::EMR.new('ACCESS2', 'SECRET2') }
337
+
338
+ let(:same_object) { emr1 }
339
+ let(:same_values) { Elasticity::EMR.new('ACCESS1', 'SECRET1') }
340
+ let(:diff_type) { Object.new }
341
+
342
+ it 'should pass comparison checks' do
343
+ emr1.should == same_object
344
+ emr1.should == same_values
345
+ emr1.should_not == diff_type
877
346
  end
878
347
  end
879
348