knife-ec2 0.5.6 → 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
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