elasticity 1.5 → 2.0

Sign up to get free protection for your applications and to get access to all the features.
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