knife-ec2 0.6.2 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -23,7 +23,7 @@ Bundler::GemHelper.install_tasks
23
23
 
24
24
  # require 'rubygems'
25
25
  # require 'rake/gempackagetask'
26
- require 'rake/rdoctask'
26
+ require 'rdoc/task'
27
27
 
28
28
  begin
29
29
  require 'sdoc'
@@ -62,6 +62,10 @@ class Chef
62
62
  :description => "The security group ids for this server; required when using VPC",
63
63
  :proc => Proc.new { |security_group_ids| security_group_ids.split(',') }
64
64
 
65
+ option :associate_eip,
66
+ :long => "--associate-eip IP_ADDRESS",
67
+ :description => "Associate existing elastic IP address with instance after launch"
68
+
65
69
  option :tags,
66
70
  :short => "-T T=V[,T=V,...]",
67
71
  :long => "--tags Tag=Value[,Tag=Value...]",
@@ -110,7 +114,6 @@ class Chef
110
114
  :description => "The ssh gateway server",
111
115
  :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
112
116
 
113
-
114
117
  option :identity_file,
115
118
  :short => "-i IDENTITY_FILE",
116
119
  :long => "--identity-file IDENTITY_FILE",
@@ -167,6 +170,11 @@ class Chef
167
170
  :description => "create node in this Virtual Private Cloud Subnet ID (implies VPC mode)",
168
171
  :proc => Proc.new { |key| Chef::Config[:knife][:subnet_id] = key }
169
172
 
173
+ option :private_ip_address,
174
+ :long => "--private-ip-address IP-ADDRESS",
175
+ :description => "allows to specify the private IP address of the instance in VPC mode",
176
+ :proc => Proc.new { |ip| Chef::Config[:knife][:private_ip_address] = ip }
177
+
170
178
  option :host_key_verify,
171
179
  :long => "--[no-]host-key-verify",
172
180
  :description => "Verify host key, enabled by default.",
@@ -225,6 +233,11 @@ class Chef
225
233
 
226
234
  validate!
227
235
 
236
+ requested_elastic_ip = config[:associate_eip] if config[:associate_eip]
237
+
238
+ # For VPC EIP assignment we need the allocation ID so fetch full EIP details
239
+ elastic_ip = connection.addresses.detect{|addr| addr if addr.public_ip == requested_elastic_ip}
240
+
228
241
  @server = connection.servers.create(create_server_def)
229
242
 
230
243
  hashed_tags={}
@@ -235,10 +248,6 @@ class Chef
235
248
  hashed_tags["Name"] = locate_config_value(:chef_node_name) || @server.id
236
249
  end
237
250
 
238
- hashed_tags.each_pair do |key,val|
239
- connection.tags.create :key => key, :value => val, :resource_id => @server.id
240
- end
241
-
242
251
  msg_pair("Instance ID", @server.id)
243
252
  msg_pair("Flavor", @server.flavor_id)
244
253
  msg_pair("Image", @server.image_id)
@@ -260,15 +269,30 @@ class Chef
260
269
  msg_pair("Tags", hashed_tags)
261
270
  msg_pair("SSH Key", @server.key_name)
262
271
 
263
- print "\n#{ui.color("Waiting for server", :magenta)}"
272
+ print "\n#{ui.color("Waiting for instance", :magenta)}"
264
273
 
265
- # wait for it to be ready to do stuff
274
+ # wait for instance to come up before acting against it
266
275
  @server.wait_for { print "."; ready? }
267
276
 
268
277
  puts("\n")
269
278
 
279
+ # occasionally 'ready?' isn't, so retry a couple times if needed.
280
+ tries = 6
281
+ begin
282
+ create_tags(hashed_tags) unless hashed_tags.empty?
283
+ associate_eip(elastic_ip) if config[:associate_eip]
284
+ rescue Fog::Compute::AWS::NotFound => e
285
+ raise if (tries -= 1) <= 0
286
+ ui.warn("server not ready, retrying tag application (retries left: #{tries})")
287
+ sleep 5
288
+ retry
289
+ end
290
+
270
291
  if vpc_mode?
271
292
  msg_pair("Subnet ID", @server.subnet_id)
293
+ if elastic_ip
294
+ msg_pair("Public IP Address", @server.public_ip_address)
295
+ end
272
296
  else
273
297
  msg_pair("Public DNS Name", @server.dns_name)
274
298
  msg_pair("Public IP Address", @server.public_ip_address)
@@ -373,7 +397,19 @@ class Chef
373
397
  ui.error("You are using a VPC, security groups specified with '-G' are not allowed, specify one or more security group ids with '-g' instead.")
374
398
  exit 1
375
399
  end
400
+ if !vpc_mode? and !!config[:private_ip_address]
401
+ ui.error("You can only specify a private IP address if you are using VPC.")
402
+ exit 1
403
+ end
404
+
405
+ if config[:associate_eip]
406
+ eips = connection.addresses.collect{|addr| addr if addr.domain == eip_scope}.compact
376
407
 
408
+ unless eips.detect{|addr| addr.public_ip == config[:associate_eip] && addr.server_id == nil}
409
+ ui.error("Elastic IP requested is not available.")
410
+ exit 1
411
+ end
412
+ end
377
413
  end
378
414
 
379
415
  def tags
@@ -385,6 +421,14 @@ class Chef
385
421
  tags
386
422
  end
387
423
 
424
+ def eip_scope
425
+ if vpc_mode?
426
+ "vpc"
427
+ else
428
+ "standard"
429
+ end
430
+ end
431
+
388
432
  def create_server_def
389
433
  server_def = {
390
434
  :image_id => locate_config_value(:image),
@@ -395,6 +439,7 @@ class Chef
395
439
  :availability_zone => locate_config_value(:availability_zone)
396
440
  }
397
441
  server_def[:subnet_id] = locate_config_value(:subnet_id) if vpc_mode?
442
+ server_def[:private_ip_address] = locate_config_value(:private_ip_address) if vpc_mode?
398
443
 
399
444
  if Chef::Config[:knife][:aws_user_data]
400
445
  begin
@@ -486,6 +531,17 @@ class Chef
486
531
  vpc_mode? ? server.private_ip_address : server.dns_name
487
532
  end
488
533
  end
534
+
535
+ def create_tags(hashed_tags)
536
+ hashed_tags.each_pair do |key,val|
537
+ connection.tags.create :key => key, :value => val, :resource_id => @server.id
538
+ end
539
+ end
540
+
541
+ def associate_eip(elastic_ip)
542
+ connection.associate_address(server.id, elastic_ip.public_ip, nil, elastic_ip.allocation_id)
543
+ @server.wait_for { public_ip_address == elastic_ip.public_ip }
544
+ end
489
545
  end
490
546
  end
491
547
  end
@@ -1,6 +1,6 @@
1
1
  module Knife
2
2
  module Ec2
3
- VERSION = "0.6.2"
3
+ VERSION = "0.6.4"
4
4
  MAJOR, MINOR, TINY = VERSION.split('.')
5
5
  end
6
6
  end
@@ -38,6 +38,13 @@ describe Chef::Knife::Ec2ServerCreate do
38
38
  @ec2_connection = mock(Fog::Compute::AWS)
39
39
  @ec2_connection.stub_chain(:tags).and_return mock('create', :create => true)
40
40
  @ec2_connection.stub_chain(:images, :get).and_return mock('ami', :root_device_type => 'not_ebs')
41
+ @ec2_connection.stub_chain(:addresses).and_return [mock('addesses', {
42
+ :domain => 'standard',
43
+ :public_ip => '111.111.111.111',
44
+ :server_id => nil,
45
+ :allocation_id => ''})]
46
+
47
+
41
48
  @ec2_servers = mock()
42
49
  @new_ec2_server = mock()
43
50
 
@@ -60,30 +67,56 @@ describe Chef::Knife::Ec2ServerCreate do
60
67
  end
61
68
 
62
69
  describe "run" do
63
- it "creates an EC2 instance and bootstraps it" do
64
- @new_ec2_server.should_receive(:wait_for).and_return(true)
70
+ before do
65
71
  @ec2_servers.should_receive(:create).and_return(@new_ec2_server)
66
72
  @ec2_connection.should_receive(:servers).and_return(@ec2_servers)
73
+ @ec2_connection.should_receive(:addresses)
67
74
 
75
+ @eip = "111.111.111.111"
68
76
  Fog::Compute::AWS.should_receive(:new).and_return(@ec2_connection)
69
77
 
70
78
  @knife_ec2_create.stub!(:puts)
71
79
  @knife_ec2_create.stub!(:print)
72
80
  @knife_ec2_create.config[:image] = '12345'
73
81
 
74
-
75
82
  @bootstrap = Chef::Knife::Bootstrap.new
76
83
  Chef::Knife::Bootstrap.stub!(:new).and_return(@bootstrap)
77
84
  @bootstrap.should_receive(:run)
85
+ end
86
+
87
+ it "creates an EC2 instance and bootstraps it" do
88
+ @new_ec2_server.should_receive(:wait_for).and_return(true)
89
+ @knife_ec2_create.run
90
+ @knife_ec2_create.server.should_not == nil
91
+ end
92
+
93
+ it "creates an EC2 instance, assigns existing EIP and bootstraps it" do
94
+ @knife_ec2_create.config[:associate_eip] = @eip
95
+
96
+ @new_ec2_server.stub!(:public_ip_address).and_return(@eip)
97
+ @ec2_connection.should_receive(:associate_address).with(@ec2_server_attribs[:id], @eip, nil, '')
98
+ @new_ec2_server.should_receive(:wait_for).at_least(:twice).and_return(true)
99
+
78
100
  @knife_ec2_create.run
79
101
  @knife_ec2_create.server.should_not == nil
80
102
  end
103
+
104
+ it "retries if it receives Fog::Compute::AWS::NotFound" do
105
+ @new_ec2_server.should_receive(:wait_for).and_return(true)
106
+ @knife_ec2_create.should_receive(:create_tags).and_raise(Fog::Compute::AWS::NotFound)
107
+ @knife_ec2_create.should_receive(:create_tags).and_return(true)
108
+ @knife_ec2_create.should_receive(:sleep).and_return(true)
109
+ @knife_ec2_create.ui.should_receive(:warn).with(/retrying/)
110
+ @knife_ec2_create.run
111
+ end
81
112
  end
113
+
82
114
  describe "when setting tags" do
83
115
  before do
84
116
  Fog::Compute::AWS.should_receive(:new).and_return(@ec2_connection)
85
117
  @knife_ec2_create.stub!(:bootstrap_for_node).and_return mock("bootstrap", :run => true)
86
118
  @ec2_connection.stub!(:servers).and_return(@ec2_servers)
119
+ @ec2_connection.should_receive(:addresses)
87
120
  @new_ec2_server.stub!(:wait_for).and_return(true)
88
121
  @ec2_servers.stub!(:create).and_return(@new_ec2_server)
89
122
  @knife_ec2_create.stub!(:puts)
@@ -216,6 +249,12 @@ describe Chef::Knife::Ec2ServerCreate do
216
249
 
217
250
  lambda { @knife_ec2_create.validate! }.should raise_error SystemExit
218
251
  end
252
+
253
+ it "disallows private ips when not using a VPC" do
254
+ @knife_ec2_create.config[:private_ip_address] = '10.0.0.10'
255
+
256
+ lambda { @knife_ec2_create.validate! }.should raise_error SystemExit
257
+ end
219
258
  end
220
259
 
221
260
  describe "when creating the server definition" do
@@ -270,6 +309,15 @@ describe Chef::Knife::Ec2ServerCreate do
270
309
  { "VirtualName" => "ephemeral2", "DeviceName" => "/dev/sdd" },
271
310
  { "VirtualName" => "ephemeral3", "DeviceName" => "/dev/sde" }]
272
311
  end
312
+
313
+ it "sets the specified private ip address" do
314
+ @knife_ec2_create.config[:subnet_id] = 'subnet-1a2b3c4d'
315
+ @knife_ec2_create.config[:private_ip_address] = '10.0.0.10'
316
+ server_def = @knife_ec2_create.create_server_def
317
+
318
+ server_def[:subnet_id].should == 'subnet-1a2b3c4d'
319
+ server_def[:private_ip_address].should == '10.0.0.10'
320
+ end
273
321
  end
274
322
 
275
323
  describe "ssh_connect_host" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-ec2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.6.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-10-24 00:00:00.000000000 Z
13
+ date: 2013-04-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: fog