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 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 = options[:region] ? "elasticmapreduce.#{options[:region]}.amazonaws.com" : 'elasticmapreduce.amazonaws.com'
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 @options == other.options
34
+ return false unless @host == other.host
35
+ return false unless @protocol == other.protocol
36
36
  true
37
37
  end
38
38
 
@@ -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
- @emr = Elasticity::EMR.new(access, secret)
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, 'Instance count cannot be set to less than 2 (requested 1)' unless count > 1
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
- @emr.add_jobflow_steps(@jobflow_id, {:steps => jobflow_steps})
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 = @emr.run_job_flow(jobflow_config)
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
- @emr.terminate_jobflows(@jobflow_id)
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
- @emr.describe_jobflow(@jobflow_id)
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
@@ -1,3 +1,3 @@
1
1
  module Elasticity
2
- VERSION = '2.2'
2
+ VERSION = '2.3'
3
3
  end
@@ -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=QwQIiizWrfvWuLNnzmCMfaeXFfh9IQTvOix5MNVTh2s%3D'
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
- <<-ERROR
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
- ERROR
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
- 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
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.should_receive(:new).with('access', 'secret').and_return(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.should_receive(:new).with('STEP_TEST_ACCESS', 'STEP_TEST_SECRET').and_return(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).with('STEP_TEST_ACCESS', 'STEP_TEST_SECRET').and_return(emr)
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).with('STEP_TEST_ACCESS', 'STEP_TEST_SECRET').and_return(emr)
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
- it 'should create a jobflow with the specified credentials' do
494
- Elasticity::EMR.should_receive(:new).with('ACCESS', 'SECRET')
495
- Elasticity::JobFlow.from_jobflow_id('ACCESS', 'SECRET', '_')
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.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-23 00:00:00.000000000 Z
12
+ date: 2012-07-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client