elasticity 2.2 → 2.3

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.
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