elasticity 2.2 → 2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.md +5 -0
- data/README.md +3 -2
- data/lib/elasticity/aws_request.rb +3 -3
- data/lib/elasticity/job_flow.rb +19 -8
- data/lib/elasticity/version.rb +1 -1
- data/spec/lib/elasticity/aws_request_spec.rb +36 -21
- data/spec/lib/elasticity/job_flow_integration_spec.rb +4 -1
- data/spec/lib/elasticity/job_flow_spec.rb +24 -12
- metadata +2 -2
data/HISTORY.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## 2.3 - July 28, 2012
|
2
|
+
|
3
|
+
+ ```JobFlow``` now supports specifying availbility zone instance specification via ```JobFlow#placement=```.
|
4
|
+
+ ```JobFlow::from_jobflow_id``` now supports region specification so that jobs created in regions other than us-east-1 can be recreated.
|
5
|
+
|
1
6
|
## 2.2 - July 23, 2012
|
2
7
|
|
3
8
|
+ Hadoop streaming jobs are now supported via ```Elasticity::StreamingStep```.
|
data/README.md
CHANGED
@@ -70,10 +70,10 @@ jobflow = Elasticity::JobFlow.new('AWS access key', 'AWS secret key')
|
|
70
70
|
If you want to access a job flow that's already running:
|
71
71
|
|
72
72
|
```ruby
|
73
|
-
jobflow = Elasticity::JobFlow.from_jobflow_id('AWS access key', 'AWS secret key', 'jobflow ID')
|
73
|
+
jobflow = Elasticity::JobFlow.from_jobflow_id('AWS access key', 'AWS secret key', 'jobflow ID', 'region')
|
74
74
|
```
|
75
75
|
|
76
|
-
This is useful if you'd like to attach to a running job flow and add more steps, etc.
|
76
|
+
This is useful if you'd like to attach to a running job flow and add more steps, etc. The ```region``` parameter is necessary because job flows are only accessible from the the API when you connect to the same endpoint that created them (e.g. us-west-1). If you don't specify the ```region``` parameter, us-east-1 is assumed.
|
77
77
|
|
78
78
|
## 2 - Specifying Job Flow Options
|
79
79
|
|
@@ -90,6 +90,7 @@ jobflow.hadoop_version = '0.20.205'
|
|
90
90
|
jobflow.keep_job_flow_alive_when_no_steps = true
|
91
91
|
jobflow.log_uri = nil
|
92
92
|
jobflow.name = 'Elasticity Job Flow'
|
93
|
+
jobflow.placement = 'us-east-1a'
|
93
94
|
jobflow.instance_count = 2
|
94
95
|
jobflow.master_instance_type = 'm1.small'
|
95
96
|
jobflow.slave_instance_type = 'm1.small'
|
@@ -4,7 +4,6 @@ module Elasticity
|
|
4
4
|
|
5
5
|
attr_reader :access_key
|
6
6
|
attr_reader :secret_key
|
7
|
-
attr_reader :options
|
8
7
|
attr_reader :host
|
9
8
|
attr_reader :protocol
|
10
9
|
|
@@ -14,7 +13,7 @@ module Elasticity
|
|
14
13
|
def initialize(access, secret, options = {})
|
15
14
|
@access_key = access
|
16
15
|
@secret_key = secret
|
17
|
-
@host =
|
16
|
+
@host = "elasticmapreduce.#{{:region => 'us-east-1'}.merge(options)[:region]}.amazonaws.com"
|
18
17
|
@protocol = {:secure => true}.merge(options)[:secure] ? 'https' : 'http'
|
19
18
|
end
|
20
19
|
|
@@ -32,7 +31,8 @@ module Elasticity
|
|
32
31
|
return false unless other.is_a? AwsRequest
|
33
32
|
return false unless @access_key == other.access_key
|
34
33
|
return false unless @secret_key == other.secret_key
|
35
|
-
return false unless @
|
34
|
+
return false unless @host == other.host
|
35
|
+
return false unless @protocol == other.protocol
|
36
36
|
true
|
37
37
|
end
|
38
38
|
|
data/lib/elasticity/job_flow.rb
CHANGED
@@ -17,6 +17,7 @@ module Elasticity
|
|
17
17
|
attr_accessor :ami_version
|
18
18
|
attr_accessor :keep_job_flow_alive_when_no_steps
|
19
19
|
attr_accessor :ec2_subnet_id
|
20
|
+
attr_accessor :placement
|
20
21
|
|
21
22
|
def initialize(access, secret)
|
22
23
|
@action_on_failure = 'TERMINATE_JOB_FLOW'
|
@@ -25,6 +26,10 @@ module Elasticity
|
|
25
26
|
@name = 'Elasticity Job Flow'
|
26
27
|
@ami_version = 'latest'
|
27
28
|
@keep_job_flow_alive_when_no_steps = false
|
29
|
+
@placement = 'us-east-1a'
|
30
|
+
|
31
|
+
@access = access
|
32
|
+
@secret = secret
|
28
33
|
|
29
34
|
@bootstrap_actions = []
|
30
35
|
@jobflow_steps = []
|
@@ -33,23 +38,24 @@ module Elasticity
|
|
33
38
|
@instance_groups = {}
|
34
39
|
set_master_instance_group(Elasticity::InstanceGroup.new)
|
35
40
|
set_core_instance_group(Elasticity::InstanceGroup.new)
|
36
|
-
|
37
41
|
@instance_count = 2
|
38
42
|
@master_instance_type = 'm1.small'
|
39
43
|
@slave_instance_type = 'm1.small'
|
40
44
|
|
41
|
-
@
|
45
|
+
@access = access
|
46
|
+
@secret = secret
|
42
47
|
end
|
43
48
|
|
44
|
-
def self.from_jobflow_id(access, secret, jobflow_id)
|
49
|
+
def self.from_jobflow_id(access, secret, jobflow_id, region = 'us-east-1')
|
45
50
|
JobFlow.new(access, secret).tap do |j|
|
51
|
+
j.instance_variable_set(:@region, region)
|
46
52
|
j.instance_variable_set(:@jobflow_id, jobflow_id)
|
47
53
|
j.instance_variable_set(:@installed_steps, j.status.installed_steps)
|
48
54
|
end
|
49
55
|
end
|
50
56
|
|
51
57
|
def instance_count=(count)
|
52
|
-
raise ArgumentError,
|
58
|
+
raise ArgumentError, "Instance count cannot be set to less than 2 (requested #{count})" unless count > 1
|
53
59
|
@instance_groups[:core].count = count - 1
|
54
60
|
@instance_count = count
|
55
61
|
end
|
@@ -91,7 +97,7 @@ module Elasticity
|
|
91
97
|
jobflow_steps << jobflow_step.aws_installation_step
|
92
98
|
end
|
93
99
|
jobflow_steps << jobflow_step.to_aws_step(self)
|
94
|
-
|
100
|
+
emr.add_jobflow_steps(@jobflow_id, {:steps => jobflow_steps})
|
95
101
|
else
|
96
102
|
@jobflow_steps << jobflow_step
|
97
103
|
end
|
@@ -100,21 +106,26 @@ module Elasticity
|
|
100
106
|
def run
|
101
107
|
raise_if @jobflow_steps.empty?, JobFlowMissingStepsError, 'Cannot run a job flow without adding steps. Please use #add_step.'
|
102
108
|
raise_if is_jobflow_running?, JobFlowRunningError, 'Cannot run a job flow multiple times. To do more with this job flow, please use #add_step.'
|
103
|
-
@jobflow_id =
|
109
|
+
@jobflow_id = emr.run_job_flow(jobflow_config)
|
104
110
|
end
|
105
111
|
|
106
112
|
def shutdown
|
107
113
|
raise_unless is_jobflow_running?, JobFlowNotStartedError, 'Cannot #shutdown a job flow that has not yet been #run.'
|
108
|
-
|
114
|
+
emr.terminate_jobflows(@jobflow_id)
|
109
115
|
end
|
110
116
|
|
111
117
|
def status
|
112
118
|
raise_unless is_jobflow_running?, JobFlowNotStartedError, 'Please #run this job flow before attempting to retrieve status.'
|
113
|
-
|
119
|
+
emr.describe_jobflow(@jobflow_id)
|
114
120
|
end
|
115
121
|
|
116
122
|
private
|
117
123
|
|
124
|
+
def emr
|
125
|
+
@region ||= @placement.match(/(\w+-\w+-\d+)/)[0]
|
126
|
+
@emr ||= Elasticity::EMR.new(@access, @secret, :region => @region)
|
127
|
+
end
|
128
|
+
|
118
129
|
def is_jobflow_running?
|
119
130
|
!@jobflow_id.nil?
|
120
131
|
end
|
data/lib/elasticity/version.rb
CHANGED
@@ -14,7 +14,7 @@ describe Elasticity::AwsRequest do
|
|
14
14
|
describe '#host' do
|
15
15
|
|
16
16
|
context 'when the region is not specified' do
|
17
|
-
its(:host) { should == 'elasticmapreduce.amazonaws.com' }
|
17
|
+
its(:host) { should == 'elasticmapreduce.us-east-1.amazonaws.com' }
|
18
18
|
end
|
19
19
|
|
20
20
|
context 'when the region is specified' do
|
@@ -60,7 +60,7 @@ describe Elasticity::AwsRequest do
|
|
60
60
|
describe '#sign_params' do
|
61
61
|
it 'should sign according to AWS rules' do
|
62
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=
|
63
|
+
signed_params.should == 'AWSAccessKeyId=access&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2011-04-10T18%3A44%3A56.000Z&Signature=t%2BccC38VxCKyk2ROTKo9vnECsntKoU0RBAFklHWP5bE%3D'
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
@@ -85,13 +85,13 @@ describe Elasticity::AwsRequest do
|
|
85
85
|
context 'when there is an EMR error with the request' do
|
86
86
|
let(:error_message) { 'ERROR_MESSAGE' }
|
87
87
|
let(:error_xml) do
|
88
|
-
<<-
|
88
|
+
<<-XML
|
89
89
|
<ErrorResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
|
90
90
|
<Error>
|
91
91
|
<Message>#{error_message}</Message>
|
92
92
|
</Error>
|
93
93
|
</ErrorResponse>
|
94
|
-
|
94
|
+
XML
|
95
95
|
end
|
96
96
|
let(:error) do
|
97
97
|
RestClient::BadRequest.new.tap do |error|
|
@@ -110,24 +110,39 @@ describe Elasticity::AwsRequest do
|
|
110
110
|
end
|
111
111
|
|
112
112
|
describe '#==' do
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
113
|
+
|
114
|
+
describe 'basic equality checks with subject' do
|
115
|
+
let(:same_object) { subject }
|
116
|
+
let(:same_values) { Elasticity::AwsRequest.new('access', 'secret', {}) }
|
117
|
+
let(:diff_type) { Object.new }
|
118
|
+
|
119
|
+
it { should == same_object }
|
120
|
+
it { should == same_values }
|
121
|
+
it { should_not == diff_type }
|
122
|
+
end
|
123
|
+
|
124
|
+
describe 'deep comparisons' do
|
125
|
+
|
126
|
+
it 'should fail on access key check' do
|
127
|
+
Elasticity::AwsRequest.new('access', '_').should_not == Elasticity::AwsRequest.new('_', '_')
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should fail on secret key check' do
|
131
|
+
Elasticity::AwsRequest.new('_', 'secret').should_not == Elasticity::AwsRequest.new('_', '_')
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should fail on host check' do
|
135
|
+
aws1 = Elasticity::AwsRequest.new('_', '_', :region => 'us-east-1')
|
136
|
+
aws2 = Elasticity::AwsRequest.new('_', '_', :region => 'us-west-1')
|
137
|
+
aws1.should_not == aws2
|
130
138
|
end
|
139
|
+
|
140
|
+
it 'should fail on protocol check' do
|
141
|
+
aws1 = Elasticity::AwsRequest.new('_', '_', :secure => true)
|
142
|
+
aws2 = Elasticity::AwsRequest.new('_', '_', :secure => false)
|
143
|
+
aws1.should_not == aws2
|
144
|
+
end
|
145
|
+
|
131
146
|
end
|
132
147
|
|
133
148
|
end
|
@@ -3,7 +3,7 @@ describe 'Elasticity::JobFlow Integration Examples' do
|
|
3
3
|
let(:emr) { double('Elasticity::EMR') }
|
4
4
|
|
5
5
|
before do
|
6
|
-
Elasticity::EMR.should_receive(:new).with('access', 'secret').and_return(emr)
|
6
|
+
Elasticity::EMR.should_receive(:new).with('access', 'secret', :region => 'us-west-1').and_return(emr)
|
7
7
|
end
|
8
8
|
|
9
9
|
describe 'Hive' do
|
@@ -17,6 +17,7 @@ describe 'Elasticity::JobFlow Integration Examples' do
|
|
17
17
|
|
18
18
|
let(:hive_jobflow) do
|
19
19
|
Elasticity::JobFlow.new('access', 'secret').tap do |jf|
|
20
|
+
jf.placement = 'us-west-1b'
|
20
21
|
jf.log_uri = 's3n://slif-test/output/logs'
|
21
22
|
jf.add_step(hive_step)
|
22
23
|
end
|
@@ -93,6 +94,7 @@ describe 'Elasticity::JobFlow Integration Examples' do
|
|
93
94
|
|
94
95
|
let(:pig_jobflow) do
|
95
96
|
Elasticity::JobFlow.new('access', 'secret').tap do |jf|
|
97
|
+
jf.placement = 'us-west-1b'
|
96
98
|
jf.instance_count = 8
|
97
99
|
jf.slave_instance_type = 'm1.xlarge'
|
98
100
|
jf.log_uri = 's3n://slif-test/output/logs'
|
@@ -177,6 +179,7 @@ describe 'Elasticity::JobFlow Integration Examples' do
|
|
177
179
|
|
178
180
|
let(:custom_jar_jobflow) do
|
179
181
|
Elasticity::JobFlow.new('access', 'secret').tap do |jf|
|
182
|
+
jf.placement = 'us-west-1b'
|
180
183
|
jf.log_uri = 's3n://slif-test/output/logs'
|
181
184
|
jf.add_step(custom_jar_step)
|
182
185
|
end
|
@@ -1,10 +1,5 @@
|
|
1
1
|
describe Elasticity::JobFlow do
|
2
2
|
|
3
|
-
before do
|
4
|
-
# Ensure we don't accidentally submit to EMR for all of these examples
|
5
|
-
Elasticity::EMR.stub(:new).and_return(double('Elasticity::EMR', :run_job_flow => '_'))
|
6
|
-
end
|
7
|
-
|
8
3
|
subject do
|
9
4
|
Elasticity::JobFlow.new('access', 'secret')
|
10
5
|
end
|
@@ -19,6 +14,7 @@ describe Elasticity::JobFlow do
|
|
19
14
|
its(:slave_instance_type) { should == 'm1.small' }
|
20
15
|
its(:ami_version) { should == 'latest' }
|
21
16
|
its(:keep_job_flow_alive_when_no_steps) { should == false }
|
17
|
+
its(:placement) { should == 'us-east-1a' }
|
22
18
|
|
23
19
|
describe '#instance_count=' do
|
24
20
|
|
@@ -102,6 +98,7 @@ describe Elasticity::JobFlow do
|
|
102
98
|
|
103
99
|
context 'when the jobflow is already started' do
|
104
100
|
before do
|
101
|
+
Elasticity::EMR.any_instance.stub(:run_job_flow => '_')
|
105
102
|
subject.add_step(Elasticity::CustomJarStep.new('_'))
|
106
103
|
subject.run
|
107
104
|
end
|
@@ -127,7 +124,7 @@ describe Elasticity::JobFlow do
|
|
127
124
|
end
|
128
125
|
|
129
126
|
before do
|
130
|
-
Elasticity::EMR.
|
127
|
+
Elasticity::EMR.stub(:new).and_return(emr)
|
131
128
|
running_jobflow.run
|
132
129
|
end
|
133
130
|
|
@@ -380,20 +377,20 @@ describe Elasticity::JobFlow do
|
|
380
377
|
let(:emr) { double('Elasticity::EMR', :run_job_flow => 'JOBFLOW_ID') }
|
381
378
|
|
382
379
|
it 'should run the job with the supplied EMR credentials' do
|
383
|
-
Elasticity::EMR.
|
380
|
+
Elasticity::EMR.stub(:new).with('STEP_TEST_ACCESS', 'STEP_TEST_SECRET', :region => 'us-east-1').and_return(emr)
|
384
381
|
emr.should_receive(:run_job_flow)
|
385
382
|
jobflow_with_steps.run
|
386
383
|
end
|
387
384
|
|
388
385
|
it 'should run the job with the jobflow config' do
|
389
|
-
Elasticity::EMR.stub(:new).
|
386
|
+
Elasticity::EMR.stub(:new).and_return(emr)
|
390
387
|
jobflow_with_steps.stub(:jobflow_config).and_return('JOBFLOW_CONFIG')
|
391
388
|
emr.should_receive(:run_job_flow).with('JOBFLOW_CONFIG')
|
392
389
|
jobflow_with_steps.run
|
393
390
|
end
|
394
391
|
|
395
392
|
it 'should return the jobflow ID' do
|
396
|
-
Elasticity::EMR.stub(:new).
|
393
|
+
Elasticity::EMR.stub(:new).and_return(emr)
|
397
394
|
jobflow_with_steps.run.should == 'JOBFLOW_ID'
|
398
395
|
end
|
399
396
|
|
@@ -401,6 +398,7 @@ describe Elasticity::JobFlow do
|
|
401
398
|
|
402
399
|
context 'when the jobflow has already been run' do
|
403
400
|
before do
|
401
|
+
Elasticity::EMR.any_instance.stub(:run_job_flow => '_')
|
404
402
|
jobflow_with_steps.run
|
405
403
|
end
|
406
404
|
it 'should raise an error' do
|
@@ -484,15 +482,29 @@ describe Elasticity::JobFlow do
|
|
484
482
|
end
|
485
483
|
|
486
484
|
describe '.from_jobflow_id' do
|
485
|
+
|
487
486
|
before do
|
488
487
|
Elasticity::JobFlow.any_instance.stub_chain(:status, :installed_steps => [])
|
489
488
|
end
|
490
489
|
|
491
490
|
let(:jobflow) { Elasticity::JobFlow.from_jobflow_id('ACCESS', 'SECRET', 'JOBFLOW_ID') }
|
492
491
|
|
493
|
-
|
494
|
-
|
495
|
-
|
492
|
+
describe 'creating a jobflow with the specified credentials' do
|
493
|
+
|
494
|
+
context 'when the region is not specified' do
|
495
|
+
it 'should use the default of us-east-1a' do
|
496
|
+
j = Elasticity::JobFlow.from_jobflow_id('ACCESS', 'SECRET', '_')
|
497
|
+
j.send(:emr).should == Elasticity::EMR.new('ACCESS', 'SECRET', :region => 'us-east-1')
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
context 'when the region is specified' do
|
502
|
+
it 'should use the specified region' do
|
503
|
+
j = Elasticity::JobFlow.from_jobflow_id('ACCESS', 'SECRET', '_', 'us-west-1')
|
504
|
+
j.send(:emr).should == Elasticity::EMR.new('ACCESS', 'SECRET', :region => 'us-west-1')
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
496
508
|
end
|
497
509
|
|
498
510
|
it 'should create a jobflow' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elasticity
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '2.
|
4
|
+
version: '2.3'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|