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 +34 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/README.rdoc +4 -0
- data/Rakefile +56 -0
- data/knife-ec2.gemspec +22 -0
- data/lib/chef/knife/ec2_base.rb +98 -0
- data/lib/chef/knife/ec2_server_create.rb +129 -128
- data/lib/chef/knife/ec2_server_delete.rb +28 -52
- data/lib/chef/knife/ec2_server_list.rb +18 -34
- data/lib/knife-ec2/version.rb +5 -2
- data/spec/spec_helper.rb +6 -0
- data/spec/unit/ec2_server_create_spec.rb +130 -0
- metadata +20 -30
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
data/Gemfile
ADDED
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
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
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
|
261
|
-
|
262
|
-
|
263
|
-
|
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
|
-
|
207
|
+
msg_pair("Private IP Address", server.private_ip_address)
|
266
208
|
|
267
209
|
print "\n#{ui.color("Waiting for sshd", :magenta)}"
|
268
|
-
|
269
|
-
|
270
|
-
|
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
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
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
|
-
|
296
|
-
|
297
|
-
|
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
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
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
|
-
|
308
|
-
|
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 = [
|
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
|
-
#
|
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
|
-
|
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
|
-
|
54
|
-
|
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
|
-
|
64
|
-
|
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
|
-
|
75
|
-
|
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
|
-
|
52
|
+
puts "\n"
|
53
|
+
confirm("Do you really want to delete this server")
|
78
54
|
|
79
|
-
|
80
|
-
|
81
|
-
|
55
|
+
server.destroy
|
56
|
+
|
57
|
+
ui.warn("Deleted server #{server.id}")
|
82
58
|
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
|
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 <<
|
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,
|
64
|
+
puts ui.list(server_list, :columns_across, 8)
|
81
65
|
|
82
66
|
end
|
83
67
|
end
|
data/lib/knife-ec2/version.rb
CHANGED
data/spec/spec_helper.rb
ADDED
@@ -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.
|
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-
|
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.
|
25
|
+
version: 0.8.2
|
25
26
|
type: :runtime
|
26
27
|
version_requirements: *id001
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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:
|
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
|