knife-ec2 0.5.6 → 0.5.8

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/.gitignore ADDED
@@ -0,0 +1,34 @@
1
+ .rake_tasks~
2
+ tags
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ test/tmp
7
+ test/version_tmp
8
+ tmp
9
+ pkg
10
+ *.gem
11
+ *.rbc
12
+ lib/bundler/man
13
+ spec/reports
14
+ .config
15
+ InstalledFiles
16
+ .bundle
17
+
18
+ # YARD artifacts
19
+ .yardoc
20
+ _yardoc
21
+ doc/
22
+
23
+ .DS_Store
24
+ Icon?
25
+
26
+ # Thumbnails
27
+ ._*
28
+
29
+ # Files that might appear on external disk
30
+ .Spotlight-V100
31
+ .Trashes
32
+
33
+ *.swp
34
+ *.swo
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ -fs
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in knife-rackspace.gemspec
4
+ gemspec
data/README.rdoc CHANGED
@@ -6,6 +6,10 @@ This is the official Opscode Knife plugin for EC2. This plugin gives knife the a
6
6
 
7
7
  = INSTALLATION:
8
8
 
9
+ Be sure you are running the latest version Chef. Versions earlier than 0.10.0 don't support plugins:
10
+
11
+ gem install chef
12
+
9
13
  This plugin is distributed as a Ruby Gem. To install it, run:
10
14
 
11
15
  gem install knife-ec2
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Daniel DeLeo (<dan@opscode.com>)
4
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
5
+ # Copyright:: Copyright (c) 2008, 2010 Opscode, Inc.
6
+ # License:: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ require 'bundler'
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ # require 'rubygems'
25
+ # require 'rake/gempackagetask'
26
+ require 'rake/rdoctask'
27
+
28
+ begin
29
+ require 'sdoc'
30
+
31
+ Rake::RDocTask.new do |rdoc|
32
+ rdoc.title = "Chef Ruby API Documentation"
33
+ rdoc.main = "README.rdoc"
34
+ rdoc.options << '--fmt' << 'shtml' # explictly set shtml generator
35
+ rdoc.template = 'direct' # lighter template
36
+ rdoc.rdoc_files.include("README.rdoc", "LICENSE", "spec/tiny_server.rb", "lib/**/*.rb")
37
+ rdoc.rdoc_dir = "rdoc"
38
+ end
39
+ rescue LoadError
40
+ puts "sdoc is not available. (sudo) gem install sdoc to generate rdoc documentation."
41
+ end
42
+
43
+ begin
44
+ require 'rspec/core/rake_task'
45
+
46
+ task :default => :spec
47
+
48
+ desc "Run all specs in spec directory"
49
+ RSpec::Core::RakeTask.new(:spec) do |t|
50
+ t.pattern = 'spec/unit/**/*_spec.rb'
51
+ end
52
+
53
+ rescue LoadError
54
+ STDERR.puts "\n*** RSpec not available. (sudo) gem install rspec to run unit tests. ***\n\n"
55
+ end
56
+
data/knife-ec2.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "knife-ec2/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "knife-ec2"
7
+ s.version = Knife::Ec2::VERSION
8
+ s.has_rdoc = true
9
+ s.authors = ["Adam Jacob","Seth Chisamore"]
10
+ s.email = ["adam@opscode.com","schisamo@opscode.com"]
11
+ s.homepage = "http://wiki.opscode.com/display/chef"
12
+ s.summary = "Rackspace Support for Chef's Knife Command"
13
+ s.description = s.summary
14
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE" ]
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.add_dependency "fog", "~> 0.8.2"
20
+ s.require_paths = ["lib"]
21
+
22
+ end
@@ -0,0 +1,98 @@
1
+ #
2
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
3
+ # Copyright:: Copyright (c) 2011 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/knife'
20
+
21
+ class Chef
22
+ class Knife
23
+ module Ec2Base
24
+
25
+ # :nodoc:
26
+ # Would prefer to do this in a rational way, but can't be done b/c of
27
+ # Mixlib::CLI's design :(
28
+ def self.included(includer)
29
+ includer.class_eval do
30
+
31
+ deps do
32
+ require 'fog'
33
+ require 'readline'
34
+ require 'chef/json_compat'
35
+ end
36
+
37
+ option :aws_access_key_id,
38
+ :short => "-A ID",
39
+ :long => "--aws-access-key-id KEY",
40
+ :description => "Your AWS Access Key ID",
41
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_access_key_id] = key }
42
+
43
+ option :aws_secret_access_key,
44
+ :short => "-K SECRET",
45
+ :long => "--aws-secret-access-key SECRET",
46
+ :description => "Your AWS API Secret Access Key",
47
+ :proc => Proc.new { |key| Chef::Config[:knife][:aws_secret_access_key] = key }
48
+
49
+ option :region,
50
+ :long => "--region REGION",
51
+ :description => "Your AWS region",
52
+ :default => "us-east-1",
53
+ :proc => Proc.new { |key| Chef::Config[:knife][:region] = key }
54
+ end
55
+ end
56
+
57
+ def connection
58
+ @connection ||= begin
59
+ connection = Fog::Compute.new(
60
+ :provider => 'AWS',
61
+ :aws_access_key_id => Chef::Config[:knife][:aws_access_key_id],
62
+ :aws_secret_access_key => Chef::Config[:knife][:aws_secret_access_key],
63
+ :region => locate_config_value(:region)
64
+ )
65
+ end
66
+ end
67
+
68
+ def locate_config_value(key)
69
+ key = key.to_sym
70
+ Chef::Config[:knife][key] || config[key]
71
+ end
72
+
73
+ def msg_pair(label, value, color=:cyan)
74
+ if value && !value.empty?
75
+ puts "#{ui.color(label, color)}: #{value}"
76
+ end
77
+ end
78
+
79
+ def validate!(keys=[:aws_access_key_id, :aws_secret_access_key])
80
+ errors = []
81
+
82
+ keys.each do |k|
83
+ pretty_key = k.to_s.gsub(/_/, ' ').gsub(/\w+/){ |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
84
+ if Chef::Config[:knife][k].nil?
85
+ errors << "You did not provided a valid '#{pretty_key}' value."
86
+ end
87
+ end
88
+
89
+ if errors.each{|e| ui.error(e)}.any?
90
+ exit 1
91
+ end
92
+ end
93
+
94
+ end
95
+ end
96
+ end
97
+
98
+
@@ -1,6 +1,7 @@
1
1
  #
2
2
  # Author:: Adam Jacob (<adam@opscode.com>)
3
- # Copyright:: Copyright (c) 2010 Opscode, Inc.
3
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
4
+ # Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
4
5
  # License:: Apache License, Version 2.0
5
6
  #
6
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,20 +17,20 @@
16
17
  # limitations under the License.
17
18
  #
18
19
 
19
- require 'chef/knife'
20
+ require 'chef/knife/ec2_base'
20
21
 
21
22
  class Chef
22
23
  class Knife
23
24
  class Ec2ServerCreate < Knife
24
25
 
26
+ include Knife::Ec2Base
27
+
25
28
  deps do
26
- require 'chef/knife/bootstrap'
27
- Chef::Knife::Bootstrap.load_deps
28
29
  require 'fog'
29
- require 'socket'
30
- require 'net/ssh/multi'
31
30
  require 'readline'
32
31
  require 'chef/json_compat'
32
+ require 'chef/knife/bootstrap'
33
+ Chef::Knife::Bootstrap.load_deps
33
34
  end
34
35
 
35
36
  banner "knife ec2 server create (options)"
@@ -90,18 +91,6 @@ class Chef
90
91
  :long => "--identity-file IDENTITY_FILE",
91
92
  :description => "The SSH identity file used for authentication"
92
93
 
93
- option :aws_access_key_id,
94
- :short => "-A ID",
95
- :long => "--aws-access-key-id KEY",
96
- :description => "Your AWS Access Key ID",
97
- :proc => Proc.new { |key| Chef::Config[:knife][:aws_access_key_id] = key }
98
-
99
- option :aws_secret_access_key,
100
- :short => "-K SECRET",
101
- :long => "--aws-secret-access-key SECRET",
102
- :description => "Your AWS API Secret Access Key",
103
- :proc => Proc.new { |key| Chef::Config[:knife][:aws_secret_access_key] = key }
104
-
105
94
  option :prerelease,
106
95
  :long => "--prerelease",
107
96
  :description => "Install the pre-release chef gems"
@@ -111,12 +100,6 @@ class Chef
111
100
  :description => "The version of Chef to install",
112
101
  :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
113
102
 
114
- option :region,
115
- :long => "--region REGION",
116
- :description => "Your AWS region",
117
- :default => "us-east-1",
118
- :proc => Proc.new { |key| Chef::Config[:knife][:region] = key }
119
-
120
103
  option :distro,
121
104
  :short => "-d DISTRO",
122
105
  :long => "--distro DISTRO",
@@ -157,6 +140,13 @@ class Chef
157
140
  :boolean => true,
158
141
  :default => false
159
142
 
143
+ option :aws_user_data,
144
+ :long => "--user-data USER_DATA_FILE",
145
+ :short => "-u USER_DATA_FILE",
146
+ :description => "The EC2 User Data file to provision the instance with",
147
+ :proc => Proc.new { |m| Chef::Config[:knife][:aws_user_data] = m },
148
+ :default => nil
149
+
160
150
  def tcp_test_ssh(hostname)
161
151
  tcp_socket = TCPSocket.new(hostname, 22)
162
152
  readable = IO.select([tcp_socket], nil, nil, 5)
@@ -167,6 +157,9 @@ class Chef
167
157
  else
168
158
  false
169
159
  end
160
+ rescue SocketError
161
+ sleep 2
162
+ false
170
163
  rescue Errno::ETIMEDOUT
171
164
  false
172
165
  rescue Errno::EPERM
@@ -183,134 +176,87 @@ class Chef
183
176
  end
184
177
 
185
178
  def run
186
-
187
179
  $stdout.sync = true
188
180
 
189
- connection = Fog::Compute.new(
190
- :provider => 'AWS',
191
- :aws_access_key_id => Chef::Config[:knife][:aws_access_key_id],
192
- :aws_secret_access_key => Chef::Config[:knife][:aws_secret_access_key],
193
- :region => locate_config_value(:region)
194
- )
195
-
196
- ami = connection.images.get(locate_config_value(:image))
197
-
198
- if ami.nil?
199
- ui.error("You have not provided a valid image (AMI) value. Please note the short option for this value recently changed from '-i' to '-I'.")
200
- exit 1
201
- end
202
-
203
- server_def = {
204
- :image_id => locate_config_value(:image),
205
- :groups => config[:security_groups],
206
- :flavor_id => locate_config_value(:flavor),
207
- :key_name => Chef::Config[:knife][:aws_ssh_key_id],
208
- :availability_zone => Chef::Config[:knife][:availability_zone]
209
- }
210
- server_def[:subnet_id] = config[:subnet_id] if config[:subnet_id]
181
+ validate!
211
182
 
212
- if ami.root_device_type == "ebs"
213
- ami_map = ami.block_device_mapping.first
214
- ebs_size = begin
215
- if config[:ebs_size]
216
- Integer(config[:ebs_size]).to_s
217
- else
218
- ami_map["volumeSize"].to_s
219
- end
220
- rescue ArgumentError
221
- puts "--ebs-size must be an integer"
222
- msg opt_parser
223
- exit 1
224
- end
225
- delete_term = if config[:ebs_no_delete_on_term]
226
- "false"
227
- else
228
- ami_map["deleteOnTermination"]
229
- end
230
- server_def[:block_device_mapping] =
231
- [{
232
- 'DeviceName' => ami_map["deviceName"],
233
- 'Ebs.VolumeSize' => ebs_size,
234
- 'Ebs.DeleteOnTermination' => delete_term
235
- }]
236
- end
237
- server = connection.servers.create(server_def)
183
+ server = connection.servers.create(create_server_def)
238
184
 
239
- puts "#{ui.color("Instance ID", :cyan)}: #{server.id}"
240
- puts "#{ui.color("Flavor", :cyan)}: #{server.flavor_id}"
241
- puts "#{ui.color("Image", :cyan)}: #{server.image_id}"
242
- puts "#{ui.color("Availability Zone", :cyan)}: #{server.availability_zone}"
243
- puts "#{ui.color("Security Groups", :cyan)}: #{server.groups.join(", ")}"
244
- puts "#{ui.color("SSH Key", :cyan)}: #{server.key_name}"
245
- puts "#{ui.color("Subnet ID", :cyan)}: #{server.subnet_id}" if vpc_mode?
185
+ msg_pair("Instance ID", server.id)
186
+ msg_pair("Flavor", server.flavor_id)
187
+ msg_pair("Image", server.image_id)
188
+ msg_pair("Region", connection.instance_variable_get(:@region))
189
+ msg_pair("Availability Zone", server.availability_zone)
190
+ msg_pair("Security Groups", server.groups.join(", "))
191
+ msg_pair("SSH Key", server.key_name)
246
192
 
247
193
  print "\n#{ui.color("Waiting for server", :magenta)}"
248
194
 
249
- display_name = if vpc_mode?
250
- server.private_ip_address
251
- else
252
- server.dns_name
253
- end
254
-
255
195
  # wait for it to be ready to do stuff
256
196
  server.wait_for { print "."; ready? }
257
197
 
258
198
  puts("\n")
259
-
260
- if !vpc_mode?
261
- puts "#{ui.color("Public DNS Name", :cyan)}: #{server.dns_name}"
262
- puts "#{ui.color("Public IP Address", :cyan)}: #{server.public_ip_address}"
263
- puts "#{ui.color("Private DNS Name", :cyan)}: #{server.private_dns_name}"
199
+
200
+ if vpc_mode?
201
+ msg_pair("Subnet ID", server.subnet_id)
202
+ else
203
+ msg_pair("Public DNS Name", server.dns_name)
204
+ msg_pair("Public IP Address", server.public_ip_address)
205
+ msg_pair("Private DNS Name", server.private_dns_name)
264
206
  end
265
- puts "#{ui.color("Private IP Address", :cyan)}: #{server.private_ip_address}"
207
+ msg_pair("Private IP Address", server.private_ip_address)
266
208
 
267
209
  print "\n#{ui.color("Waiting for sshd", :magenta)}"
268
-
269
- ip_to_test = vpc_mode? ? server.private_ip_address : server.public_ip_address
270
- print(".") until tcp_test_ssh(ip_to_test) {
210
+
211
+ fqdn = vpc_mode? ? server.private_ip_address : server.dns_name
212
+
213
+ print(".") until tcp_test_ssh(fqdn) {
271
214
  sleep @initial_sleep_delay ||= (vpc_mode? ? 40 : 10)
272
215
  puts("done")
273
216
  }
274
217
 
275
- bootstrap_for_node(server).run
218
+ bootstrap_for_node(server,fqdn).run
276
219
 
277
220
  puts "\n"
278
- puts "#{ui.color("Instance ID", :cyan)}: #{server.id}"
279
- puts "#{ui.color("Flavor", :cyan)}: #{server.flavor_id}"
280
- puts "#{ui.color("Image", :cyan)}: #{server.image_id}"
281
- puts "#{ui.color("Availability Zone", :cyan)}: #{server.availability_zone}"
282
- puts "#{ui.color("Security Groups", :cyan)}: #{server.groups.join(", ")}"
283
- if vpc_mode?
284
- puts "#{ui.color("Subnet ID", :cyan)}: #{server.subnet_id}"
285
- else
286
- puts "#{ui.color("Public DNS Name", :cyan)}: #{server.dns_name}"
287
- puts "#{ui.color("Public IP Address", :cyan)}: #{server.public_ip_address}"
288
- puts "#{ui.color("Private DNS Name", :cyan)}: #{server.private_dns_name}"
289
- end
290
- puts "#{ui.color("SSH Key", :cyan)}: #{server.key_name}"
291
- puts "#{ui.color("Private IP Address", :cyan)}: #{server.private_ip_address}"
292
- puts "#{ui.color("Root Device Type", :cyan)}: #{server.root_device_type}"
221
+ msg_pair("Instance ID", server.id)
222
+ msg_pair("Flavor", server.flavor_id)
223
+ msg_pair("Image", server.image_id)
224
+ msg_pair("Region", connection.instance_variable_get(:@region))
225
+ msg_pair("Availability Zone", server.availability_zone)
226
+ msg_pair("Security Groups", server.groups.join(", "))
227
+ msg_pair("SSH Key", server.key_name)
228
+ msg_pair("Root Device Type", server.root_device_type)
293
229
  if server.root_device_type == "ebs"
294
230
  device_map = server.block_device_mapping.first
295
- puts "#{ui.color("Root Volume ID", :cyan)}: #{device_map['volumeId']}"
296
- puts "#{ui.color("Root Device Name", :cyan)}: #{device_map['deviceName']}"
297
- puts "#{ui.color("Root Device Delete on Terminate", :cyan)}: #{device_map['deleteOnTermination']}"
231
+ msg_pair("Root Volume ID", device_map['volumeId'])
232
+ msg_pair("Root Device Name", device_map['deviceName'])
233
+ msg_pair("Root Device Delete on Terminate", device_map['deleteOnTermination'])
234
+
298
235
  if config[:ebs_size]
299
236
  if ami.block_device_mapping.first['volumeSize'].to_i < config[:ebs_size].to_i
300
- puts ("#{ui.color("Warning", :yellow)}: #{config[:ebs_size]}GB " +
301
- "EBS volume size is larger than size set in AMI of " +
302
- "#{ami.block_device_mapping.first['volumeSize']}GB.\n" +
303
- "Use file system tools to make use of the increased volume size.")
237
+ volume_too_large_warning = "#{config[:ebs_size]}GB " +
238
+ "EBS volume size is larger than size set in AMI of " +
239
+ "#{ami.block_device_mapping.first['volumeSize']}GB.\n" +
240
+ "Use file system tools to make use of the increased volume size."
241
+ msg_pair("Warning", volume_too_large_warning, :yellow)
304
242
  end
305
243
  end
306
244
  end
307
- puts "#{ui.color("Environment", :cyan)}: #{config[:environment] || '_default'}"
308
- puts "#{ui.color("Run List", :cyan)}: #{config[:run_list].join(', ')}"
245
+ if vpc_mode?
246
+ msg_pair("Subnet ID", server.subnet_id)
247
+ else
248
+ msg_pair("Public DNS Name", server.dns_name)
249
+ msg_pair("Public IP Address", server.public_ip_address)
250
+ msg_pair("Private DNS Name", server.private_dns_name)
251
+ end
252
+ msg_pair("Private IP Address", server.private_ip_address)
253
+ msg_pair("Environment", config[:environment] || '_default')
254
+ msg_pair("Run List", config[:run_list].join(', '))
309
255
  end
310
256
 
311
- def bootstrap_for_node(server)
257
+ def bootstrap_for_node(server,fqdn)
312
258
  bootstrap = Chef::Knife::Bootstrap.new
313
- bootstrap.name_args = [vpc_mode? ? server.private_ip_address : server.dns_name ]
259
+ bootstrap.name_args = [fqdn]
314
260
  bootstrap.config[:run_list] = config[:run_list]
315
261
  bootstrap.config[:ssh_user] = config[:ssh_user]
316
262
  bootstrap.config[:identity_file] = config[:identity_file]
@@ -318,7 +264,7 @@ class Chef
318
264
  bootstrap.config[:prerelease] = config[:prerelease]
319
265
  bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
320
266
  bootstrap.config[:distro] = locate_config_value(:distro)
321
- bootstrap.config[:use_sudo] = true
267
+ bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
322
268
  bootstrap.config[:template_file] = locate_config_value(:template_file)
323
269
  bootstrap.config[:environment] = config[:environment]
324
270
  # may be needed for vpc_mode
@@ -326,17 +272,72 @@ class Chef
326
272
  bootstrap
327
273
  end
328
274
 
329
- def locate_config_value(key)
330
- key = key.to_sym
331
- Chef::Config[:knife][key] || config[key]
332
- end
333
-
334
275
  def vpc_mode?
335
276
  # Amazon Virtual Private Cloud requires a subnet_id. If
336
277
  # present, do a few things differently
337
278
  !!config[:subnet_id]
338
279
  end
339
280
 
281
+ def ami
282
+ @ami ||= connection.images.get(locate_config_value(:image))
283
+ end
284
+
285
+ def validate!
286
+
287
+ super([:image, :aws_ssh_key_id, :aws_access_key_id, :aws_secret_access_key])
288
+
289
+ if ami.nil?
290
+ ui.error("You have not provided a valid image (AMI) value. Please note the short option for this value recently changed from '-i' to '-I'.")
291
+ exit 1
292
+ end
293
+ end
294
+
295
+ def create_server_def
296
+ server_def = {
297
+ :image_id => locate_config_value(:image),
298
+ :groups => config[:security_groups],
299
+ :flavor_id => locate_config_value(:flavor),
300
+ :key_name => Chef::Config[:knife][:aws_ssh_key_id],
301
+ :availability_zone => locate_config_value(:availability_zone)
302
+ }
303
+ server_def[:subnet_id] = config[:subnet_id] if config[:subnet_id]
304
+
305
+ if Chef::Config[:knife][:aws_user_data]
306
+ begin
307
+ server_def.merge!(:user_data => File.read(Chef::Config[:knife][:aws_user_data]))
308
+ rescue
309
+ ui.warn("Cannot read #{Chef::Config[:knife][:aws_user_data]}: #{$!.inspect}. Ignoring option.")
310
+ end
311
+ end
312
+
313
+ if ami.root_device_type == "ebs"
314
+ ami_map = ami.block_device_mapping.first
315
+ ebs_size = begin
316
+ if config[:ebs_size]
317
+ Integer(config[:ebs_size]).to_s
318
+ else
319
+ ami_map["volumeSize"].to_s
320
+ end
321
+ rescue ArgumentError
322
+ puts "--ebs-size must be an integer"
323
+ msg opt_parser
324
+ exit 1
325
+ end
326
+ delete_term = if config[:ebs_no_delete_on_term]
327
+ "false"
328
+ else
329
+ ami_map["deleteOnTermination"]
330
+ end
331
+ server_def[:block_device_mapping] =
332
+ [{
333
+ 'DeviceName' => ami_map["deviceName"],
334
+ 'Ebs.VolumeSize' => ebs_size,
335
+ 'Ebs.DeleteOnTermination' => delete_term
336
+ }]
337
+ end
338
+
339
+ server_def
340
+ end
340
341
  end
341
342
  end
342
343
  end
@@ -1,6 +1,7 @@
1
1
  #
2
2
  # Author:: Adam Jacob (<adam@opscode.com>)
3
- # Copyright:: Copyright (c) 2009 Opscode, Inc.
3
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
4
+ # Copyright:: Copyright (c) 2009-2011 Opscode, Inc.
4
5
  # License:: Apache License, Version 2.0
5
6
  #
6
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,73 +17,48 @@
16
17
  # limitations under the License.
17
18
  #
18
19
 
19
- require 'chef/knife'
20
+ require 'chef/knife/ec2_base'
20
21
 
21
22
  class Chef
22
23
  class Knife
23
24
  class Ec2ServerDelete < Knife
24
25
 
25
- deps do
26
- require 'fog'
27
- require 'net/ssh/multi'
28
- require 'readline'
29
- require 'chef/json_compat'
30
- end
26
+ include Knife::Ec2Base
31
27
 
32
28
  banner "knife ec2 server delete SERVER [SERVER] (options)"
33
29
 
34
- option :aws_access_key_id,
35
- :short => "-A ID",
36
- :long => "--aws-access-key-id KEY",
37
- :description => "Your AWS Access Key ID",
38
- :proc => Proc.new { |key| Chef::Config[:knife][:aws_access_key_id] = key }
39
-
40
- option :aws_secret_access_key,
41
- :short => "-K SECRET",
42
- :long => "--aws-secret-access-key SECRET",
43
- :description => "Your AWS API Secret Access Key",
44
- :proc => Proc.new { |key| Chef::Config[:knife][:aws_secret_access_key] = key }
45
-
46
- option :region,
47
- :long => "--region REGION",
48
- :description => "Your AWS region",
49
- :default => "us-east-1",
50
- :proc => Proc.new { |key| Chef::Config[:knife][:region] = key }
51
-
52
30
  def run
53
- connection = Fog::Compute.new(
54
- :provider => 'AWS',
55
- :aws_access_key_id => Chef::Config[:knife][:aws_access_key_id],
56
- :aws_secret_access_key => Chef::Config[:knife][:aws_secret_access_key],
57
- :region => Chef::Config[:knife][:region] || config[:region]
58
- )
31
+
32
+ validate!
59
33
 
60
34
  @name_args.each do |instance_id|
61
- server = connection.servers.get(instance_id)
62
35
 
63
- msg("Instance ID", server.id)
64
- msg("Flavor", server.flavor_id)
65
- msg("Image", server.image_id)
66
- msg("Availability Zone", server.availability_zone)
67
- msg("Security Groups", server.groups.join(", "))
68
- msg("SSH Key", server.key_name)
69
- msg("Public DNS Name", server.dns_name)
70
- msg("Public IP Address", server.public_ip_address)
71
- msg("Private DNS Name", server.private_dns_name)
72
- msg("Private IP Address", server.private_ip_address)
36
+ begin
37
+ server = connection.servers.get(instance_id)
73
38
 
74
- puts "\n"
75
- confirm("Do you really want to delete this server")
39
+ msg_pair("Instance ID", server.id)
40
+ msg_pair("Flavor", server.flavor_id)
41
+ msg_pair("Image", server.image_id)
42
+ msg_pair("Region", connection.instance_variable_get(:@region))
43
+ msg_pair("Availability Zone", server.availability_zone)
44
+ msg_pair("Security Groups", server.groups.join(", "))
45
+ msg_pair("SSH Key", server.key_name)
46
+ msg_pair("Root Device Type", server.root_device_type)
47
+ msg_pair("Public DNS Name", server.dns_name)
48
+ msg_pair("Public IP Address", server.public_ip_address)
49
+ msg_pair("Private DNS Name", server.private_dns_name)
50
+ msg_pair("Private IP Address", server.private_ip_address)
76
51
 
77
- server.destroy
52
+ puts "\n"
53
+ confirm("Do you really want to delete this server")
78
54
 
79
- ui.warn("Deleted server #{server.id}")
80
- end
81
- end
55
+ server.destroy
56
+
57
+ ui.warn("Deleted server #{server.id}")
82
58
 
83
- def msg(label, value)
84
- if value && !value.empty?
85
- puts "#{ui.color(label, :cyan)}: #{value}"
59
+ rescue NoMethodError
60
+ ui.error("Could not locate server '#{instance_id}'. Please verify it was provisioned in the '#{locate_config_value(:region)}' region.")
61
+ end
86
62
  end
87
63
  end
88
64
 
@@ -1,6 +1,7 @@
1
1
  #
2
2
  # Author:: Adam Jacob (<adam@opscode.com>)
3
- # Copyright:: Copyright (c) 2010 Opscode, Inc.
3
+ # Author:: Seth Chisamore (<schisamo@opscode.com>)
4
+ # Copyright:: Copyright (c) 2010-2011 Opscode, Inc.
4
5
  # License:: Apache License, Version 2.0
5
6
  #
6
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,48 +17,20 @@
16
17
  # limitations under the License.
17
18
  #
18
19
 
19
- require 'chef/knife'
20
+ require 'chef/knife/ec2_base'
20
21
 
21
22
  class Chef
22
23
  class Knife
23
24
  class Ec2ServerList < Knife
24
25
 
25
- deps do
26
- require 'fog'
27
- require 'net/ssh/multi'
28
- require 'readline'
29
- require 'chef/json_compat'
30
- end
26
+ include Knife::Ec2Base
31
27
 
32
28
  banner "knife ec2 server list (options)"
33
29
 
34
- option :aws_access_key_id,
35
- :short => "-A ID",
36
- :long => "--aws-access-key-id KEY",
37
- :description => "Your AWS Access Key ID",
38
- :proc => Proc.new { |key| Chef::Config[:knife][:aws_access_key_id] = key }
39
-
40
- option :aws_secret_access_key,
41
- :short => "-K SECRET",
42
- :long => "--aws-secret-access-key SECRET",
43
- :description => "Your AWS API Secret Access Key",
44
- :proc => Proc.new { |key| Chef::Config[:knife][:aws_secret_access_key] = key }
45
-
46
- option :region,
47
- :long => "--region REGION",
48
- :description => "Your AWS region",
49
- :default => "us-east-1",
50
- :proc => Proc.new { |key| Chef::Config[:knife][:region] = key }
51
-
52
30
  def run
53
31
  $stdout.sync = true
54
32
 
55
- connection = Fog::Compute.new(
56
- :provider => 'AWS',
57
- :aws_access_key_id => Chef::Config[:knife][:aws_access_key_id],
58
- :aws_secret_access_key => Chef::Config[:knife][:aws_secret_access_key],
59
- :region => Chef::Config[:knife][:region] || config[:region]
60
- )
33
+ validate!
61
34
 
62
35
  server_list = [
63
36
  ui.color('Instance ID', :bold),
@@ -65,6 +38,7 @@ class Chef
65
38
  ui.color('Private IP', :bold),
66
39
  ui.color('Flavor', :bold),
67
40
  ui.color('Image', :bold),
41
+ ui.color('SSH Key', :bold),
68
42
  ui.color('Security Groups', :bold),
69
43
  ui.color('State', :bold)
70
44
  ]
@@ -74,10 +48,20 @@ class Chef
74
48
  server_list << (server.private_ip_address == nil ? "" : server.private_ip_address)
75
49
  server_list << (server.flavor_id == nil ? "" : server.flavor_id)
76
50
  server_list << (server.image_id == nil ? "" : server.image_id)
51
+ server_list << server.key_name
77
52
  server_list << server.groups.join(", ")
78
- server_list << (server.state == nil ? "" : server.state)
53
+ server_list << begin
54
+ case server.state.downcase
55
+ when 'shutting-down','terminated','stopping','stopped'
56
+ ui.color(server.state.downcase, :red)
57
+ when 'pending'
58
+ ui.color(server.state.downcase, :yellow)
59
+ else
60
+ ui.color(server.state.downcase, :green)
61
+ end
62
+ end
79
63
  end
80
- puts ui.list(server_list, :columns_across, 7)
64
+ puts ui.list(server_list, :columns_across, 8)
81
65
 
82
66
  end
83
67
  end
@@ -1,4 +1,7 @@
1
- module KnifeEC2
2
- VERSION = "0.5.6"
1
+ module Knife
2
+ module Ec2
3
+ VERSION = "0.5.8"
4
+ MAJOR, MINOR, TINY = VERSION.split('.')
5
+ end
3
6
  end
4
7
 
@@ -0,0 +1,6 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'chef'
3
+ require 'chef/knife/ec2_server_create'
4
+ require 'chef/knife/ec2_instance_data'
5
+ require 'chef/knife/ec2_server_delete'
6
+ require 'chef/knife/ec2_server_list'
@@ -0,0 +1,130 @@
1
+ #
2
+ # Author:: Thomas Bishop (<bishop.thomas@gmail.com>)
3
+ # Copyright:: Copyright (c) 2010 Thomas Bishop
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require File.expand_path('../../spec_helper', __FILE__)
20
+
21
+ describe Chef::Knife::Ec2ServerCreate do
22
+ before do
23
+ @knife_ec2_create = Chef::Knife::Ec2ServerCreate.new()
24
+ @knife_ec2_create.name_args = ['role[base]']
25
+ @knife_ec2_create.initial_sleep_delay = 0
26
+ @knife_ec2_create.stub!(:tcp_test_ssh).and_return(true)
27
+
28
+ @ec2_connection = mock()
29
+ @ec2_servers = mock()
30
+ @new_ec2_server = mock()
31
+
32
+ @ec2_server_attribs = { :id => 'i-39382318',
33
+ :flavor_id => 'm1.small',
34
+ :image_id => 'ami-47241231',
35
+ :availability_zone => 'us-west-1',
36
+ :key_name => 'my_ssh_key',
37
+ :groups => ['group1', 'group2'],
38
+ :dns_name => 'ec2-75.101.253.10.compute-1.amazonaws.com',
39
+ :ip_address => '75.101.253.10',
40
+ :private_dns_name => 'ip-10-251-75-20.ec2.internal',
41
+ :private_ip_address => '10.251.75.20' }
42
+
43
+ @ec2_server_attribs.each_pair do |attrib, value|
44
+ @new_ec2_server.stub!(attrib).and_return(value)
45
+ end
46
+ end
47
+
48
+ describe "run" do
49
+
50
+ it "creates an EC2 instance and bootstraps it" do
51
+ @new_ec2_server.should_receive(:wait_for).and_return(true)
52
+ @ec2_servers.should_receive(:create).and_return(@new_ec2_server)
53
+ @ec2_connection.should_receive(:servers).and_return(@ec2_servers)
54
+
55
+ Fog::AWS::Compute.should_receive(:new).and_return(@ec2_connection)
56
+
57
+ @knife_ec2_create.stub!(:puts)
58
+ @knife_ec2_create.stub!(:print)
59
+ @knife_ec2_create.config[:image] = '12345'
60
+
61
+
62
+ @bootstrap = Chef::Knife::Bootstrap.new
63
+ Chef::Knife::Bootstrap.stub!(:new).and_return(@bootstrap)
64
+ @bootstrap.should_receive(:run)
65
+ @knife_ec2_create.run
66
+ end
67
+
68
+ end
69
+
70
+ describe "when configuring the bootstrap process" do
71
+ before do
72
+ @knife_ec2_create.config[:ssh_user] = "ubuntu"
73
+ @knife_ec2_create.config[:identity_file] = "~/.ssh/aws-key.pem"
74
+ @knife_ec2_create.config[:chef_node_name] = "blarf"
75
+ @knife_ec2_create.config[:template_file] = '~/.chef/templates/my-bootstrap.sh.erb'
76
+ @knife_ec2_create.config[:distro] = 'ubuntu-10.04-magic-sparkles'
77
+
78
+ @bootstrap = @knife_ec2_create.bootstrap_for_node(@new_ec2_server)
79
+ end
80
+
81
+ it "should set the bootstrap 'name argument' to the hostname of the EC2 server" do
82
+ @bootstrap.name_args.should == ['ec2-75.101.253.10.compute-1.amazonaws.com']
83
+ end
84
+
85
+ it "configures sets the bootstrap's run_list" do
86
+ @bootstrap.config[:run_list].should == ['role[base]']
87
+ end
88
+
89
+ it "configures the bootstrap to use the correct ssh_user login" do
90
+ @bootstrap.config[:ssh_user].should == 'ubuntu'
91
+ end
92
+
93
+ it "configures the bootstrap to use the correct ssh identity file" do
94
+ @bootstrap.config[:identity_file].should == "~/.ssh/aws-key.pem"
95
+ end
96
+
97
+ it "configures the bootstrap to use the configured node name if provided" do
98
+ @bootstrap.config[:chef_node_name].should == 'blarf'
99
+ end
100
+
101
+ it "configures the bootstrap to use the EC2 server id if no explicit node name is set" do
102
+ @knife_ec2_create.config[:chef_node_name] = nil
103
+
104
+ bootstrap = @knife_ec2_create.bootstrap_for_node(@new_ec2_server)
105
+ bootstrap.config[:chef_node_name].should == @new_ec2_server.id
106
+ end
107
+
108
+ it "configures the bootstrap to use prerelease versions of chef if specified" do
109
+ @bootstrap.config[:prerelease].should be_false
110
+
111
+ @knife_ec2_create.config[:prerelease] = true
112
+
113
+ bootstrap = @knife_ec2_create.bootstrap_for_node(@new_ec2_server)
114
+ bootstrap.config[:prerelease].should be_true
115
+ end
116
+
117
+ it "configures the bootstrap to use the desired distro-specific bootstrap script" do
118
+ @bootstrap.config[:distro].should == 'ubuntu-10.04-magic-sparkles'
119
+ end
120
+
121
+ it "configures the bootstrap to use sudo" do
122
+ @bootstrap.config[:use_sudo].should be_true
123
+ end
124
+
125
+ it "configured the bootstrap to use the desired template" do
126
+ @bootstrap.config[:template_file].should == '~/.chef/templates/my-bootstrap.sh.erb'
127
+ end
128
+ end
129
+
130
+ end
metadata CHANGED
@@ -2,15 +2,16 @@
2
2
  name: knife-ec2
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.5.6
5
+ version: 0.5.8
6
6
  platform: ruby
7
7
  authors:
8
8
  - Adam Jacob
9
+ - Seth Chisamore
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
13
 
13
- date: 2011-05-13 00:00:00 -07:00
14
+ date: 2011-08-15 00:00:00 -04:00
14
15
  default_executable:
15
16
  dependencies:
16
17
  - !ruby/object:Gem::Dependency
@@ -21,33 +22,13 @@ dependencies:
21
22
  requirements:
22
23
  - - ~>
23
24
  - !ruby/object:Gem::Version
24
- version: 0.7.2
25
+ version: 0.8.2
25
26
  type: :runtime
26
27
  version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
28
- name: net-ssh
29
- prerelease: false
30
- requirement: &id002 !ruby/object:Gem::Requirement
31
- none: false
32
- requirements:
33
- - - ~>
34
- - !ruby/object:Gem::Version
35
- version: 2.1.3
36
- type: :runtime
37
- version_requirements: *id002
38
- - !ruby/object:Gem::Dependency
39
- name: net-ssh-multi
40
- prerelease: false
41
- requirement: &id003 !ruby/object:Gem::Requirement
42
- none: false
43
- requirements:
44
- - - ~>
45
- - !ruby/object:Gem::Version
46
- version: 1.0.1
47
- type: :runtime
48
- version_requirements: *id003
49
- description: EC2 Support for Chef's Knife Command
50
- email: adam@opscode.com
28
+ description: Rackspace Support for Chef's Knife Command
29
+ email:
30
+ - adam@opscode.com
31
+ - schisamo@opscode.com
51
32
  executables: []
52
33
 
53
34
  extensions: []
@@ -56,13 +37,21 @@ extra_rdoc_files:
56
37
  - README.rdoc
57
38
  - LICENSE
58
39
  files:
40
+ - .gitignore
41
+ - .rspec
42
+ - Gemfile
59
43
  - LICENSE
60
44
  - README.rdoc
45
+ - Rakefile
46
+ - knife-ec2.gemspec
47
+ - lib/chef/knife/ec2_base.rb
61
48
  - lib/chef/knife/ec2_instance_data.rb
62
49
  - lib/chef/knife/ec2_server_create.rb
63
50
  - lib/chef/knife/ec2_server_delete.rb
64
51
  - lib/chef/knife/ec2_server_list.rb
65
52
  - lib/knife-ec2/version.rb
53
+ - spec/spec_helper.rb
54
+ - spec/unit/ec2_server_create_spec.rb
66
55
  has_rdoc: true
67
56
  homepage: http://wiki.opscode.com/display/chef
68
57
  licenses: []
@@ -90,6 +79,7 @@ rubyforge_project:
90
79
  rubygems_version: 1.6.2
91
80
  signing_key:
92
81
  specification_version: 3
93
- summary: EC2 Support for Chef's Knife Command
94
- test_files: []
95
-
82
+ summary: Rackspace Support for Chef's Knife Command
83
+ test_files:
84
+ - spec/spec_helper.rb
85
+ - spec/unit/ec2_server_create_spec.rb