knife-ec2 0.18.0 → 0.18.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.expeditor/config.yml +41 -0
- data/.expeditor/update_version.sh +12 -0
- data/.gitignore +41 -41
- data/CHANGELOG.md +132 -117
- data/CONTRIBUTING.md +245 -216
- data/DOC_CHANGES.md +17 -17
- data/Gemfile +4 -4
- data/LICENSE +201 -201
- data/RELEASE_NOTES.md +17 -17
- data/Rakefile +56 -56
- data/VERSION +1 -0
- data/lib/chef/knife/ec2_server_create.rb +1588 -1577
- data/lib/chef/knife/ec2_server_delete.rb +140 -140
- data/lib/chef/knife/s3_source.rb +49 -49
- data/lib/knife-ec2/version.rb +6 -6
- data/spec/unit/ec2_server_create_spec.rb +2728 -2703
- data/spec/unit/ec2_server_delete_spec.rb +141 -141
- data/spec/unit/s3_source_deps_spec.rb +24 -24
- data/spec/unit/s3_source_spec.rb +75 -75
- metadata +6 -3
data/Rakefile
CHANGED
@@ -1,56 +1,56 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Adam Jacob (<adam@chef.io>)
|
3
|
-
# Author:: Daniel DeLeo (<dan@chef.io>)
|
4
|
-
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
5
|
-
# Copyright:: Copyright (c) 2008-2015 Chef Software, 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 'rdoc/task'
|
27
|
-
|
28
|
-
begin
|
29
|
-
require 'sdoc'
|
30
|
-
require 'rdoc/task'
|
31
|
-
|
32
|
-
RDoc::Task.new do |rdoc|
|
33
|
-
rdoc.title = 'Chef Ruby API Documentation'
|
34
|
-
rdoc.main = 'README.rdoc'
|
35
|
-
rdoc.options << '--fmt' << 'shtml' # explictly set shtml generator
|
36
|
-
rdoc.template = 'direct' # lighter template
|
37
|
-
rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'spec/tiny_server.rb', 'lib/**/*.rb')
|
38
|
-
rdoc.rdoc_dir = 'rdoc'
|
39
|
-
end
|
40
|
-
rescue LoadError
|
41
|
-
puts 'sdoc is not available. (sudo) gem install sdoc to generate rdoc documentation.'
|
42
|
-
end
|
43
|
-
|
44
|
-
begin
|
45
|
-
require 'rspec/core/rake_task'
|
46
|
-
|
47
|
-
task :default => :spec
|
48
|
-
|
49
|
-
desc 'Run all specs in spec directory'
|
50
|
-
RSpec::Core::RakeTask.new(:spec) do |t|
|
51
|
-
t.pattern = 'spec/unit/**/*_spec.rb'
|
52
|
-
end
|
53
|
-
|
54
|
-
rescue LoadError
|
55
|
-
STDERR.puts "\n*** RSpec not available. (sudo) gem install rspec to run unit tests. ***\n\n"
|
56
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Adam Jacob (<adam@chef.io>)
|
3
|
+
# Author:: Daniel DeLeo (<dan@chef.io>)
|
4
|
+
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
5
|
+
# Copyright:: Copyright (c) 2008-2015 Chef Software, 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 'rdoc/task'
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'sdoc'
|
30
|
+
require 'rdoc/task'
|
31
|
+
|
32
|
+
RDoc::Task.new do |rdoc|
|
33
|
+
rdoc.title = 'Chef Ruby API Documentation'
|
34
|
+
rdoc.main = 'README.rdoc'
|
35
|
+
rdoc.options << '--fmt' << 'shtml' # explictly set shtml generator
|
36
|
+
rdoc.template = 'direct' # lighter template
|
37
|
+
rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'spec/tiny_server.rb', 'lib/**/*.rb')
|
38
|
+
rdoc.rdoc_dir = 'rdoc'
|
39
|
+
end
|
40
|
+
rescue LoadError
|
41
|
+
puts 'sdoc is not available. (sudo) gem install sdoc to generate rdoc documentation.'
|
42
|
+
end
|
43
|
+
|
44
|
+
begin
|
45
|
+
require 'rspec/core/rake_task'
|
46
|
+
|
47
|
+
task :default => :spec
|
48
|
+
|
49
|
+
desc 'Run all specs in spec directory'
|
50
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
51
|
+
t.pattern = 'spec/unit/**/*_spec.rb'
|
52
|
+
end
|
53
|
+
|
54
|
+
rescue LoadError
|
55
|
+
STDERR.puts "\n*** RSpec not available. (sudo) gem install rspec to run unit tests. ***\n\n"
|
56
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.18.2
|
@@ -1,1577 +1,1588 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Adam Jacob (<adam@chef.io>)
|
3
|
-
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
4
|
-
# Copyright:: Copyright (c) 2010-2015 Chef Software, Inc.
|
5
|
-
# License:: Apache License, Version 2.0
|
6
|
-
#
|
7
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
-
# you may not use this file except in compliance with the License.
|
9
|
-
# You may obtain a copy of the License at
|
10
|
-
#
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
-
#
|
13
|
-
# Unless required by applicable law or agreed to in writing, software
|
14
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
-
# See the License for the specific language governing permissions and
|
17
|
-
# limitations under the License.
|
18
|
-
#
|
19
|
-
|
20
|
-
require 'chef/knife/ec2_base'
|
21
|
-
require 'chef/knife/s3_source'
|
22
|
-
require 'chef/knife/winrm_base'
|
23
|
-
require 'chef/knife/bootstrap_windows_base'
|
24
|
-
|
25
|
-
class Chef
|
26
|
-
class Knife
|
27
|
-
class Ec2ServerCreate < Knife
|
28
|
-
|
29
|
-
include Knife::Ec2Base
|
30
|
-
include Knife::WinrmBase
|
31
|
-
include Knife::BootstrapWindowsBase
|
32
|
-
deps do
|
33
|
-
require 'tempfile'
|
34
|
-
require 'fog/aws'
|
35
|
-
require 'uri'
|
36
|
-
require 'readline'
|
37
|
-
require 'chef/json_compat'
|
38
|
-
require 'chef/knife/bootstrap'
|
39
|
-
Chef::Knife::Bootstrap.load_deps
|
40
|
-
end
|
41
|
-
|
42
|
-
banner "knife ec2 server create (options)"
|
43
|
-
|
44
|
-
attr_accessor :initial_sleep_delay
|
45
|
-
attr_reader :server
|
46
|
-
|
47
|
-
option :flavor,
|
48
|
-
:short => "-f FLAVOR",
|
49
|
-
:long => "--flavor FLAVOR",
|
50
|
-
:description => "The flavor of server (m1.small, m1.medium, etc)",
|
51
|
-
:proc => Proc.new { |f| Chef::Config[:knife][:flavor] = f }
|
52
|
-
|
53
|
-
option :image,
|
54
|
-
:short => "-I IMAGE",
|
55
|
-
:long => "--image IMAGE",
|
56
|
-
:description => "The AMI for the server",
|
57
|
-
:proc => Proc.new { |i| Chef::Config[:knife][:image] = i }
|
58
|
-
|
59
|
-
option :iam_instance_profile,
|
60
|
-
:long => "--iam-profile NAME",
|
61
|
-
:description => "The IAM instance profile to apply to this instance."
|
62
|
-
|
63
|
-
option :security_groups,
|
64
|
-
:short => "-G X,Y,Z",
|
65
|
-
:long => "--groups X,Y,Z",
|
66
|
-
:description => "The security groups for this server; not allowed when using VPC",
|
67
|
-
:proc => Proc.new { |groups| groups.split(',') }
|
68
|
-
|
69
|
-
option :security_group_ids,
|
70
|
-
:long => "--security-group-ids 'X,Y,Z'",
|
71
|
-
:description => "The security group ids for this server; required when using VPC. Provide values in format --security-group-ids 'X,Y,Z'. [DEPRECATED] This option will be removed in future release. Use the new --security-group-id option. ",
|
72
|
-
:proc => Proc.new { |security_group_ids|
|
73
|
-
ui.warn('[DEPRECATED] This option will be removed in future release. Use the new --security-group-id option multiple times when specifying multiple groups for e.g. -g sg-e985168d -g sg-e7f06383 -g sg-ec1b7e88.')
|
74
|
-
if security_group_ids.gsub(' ', '').split(',').size > 1
|
75
|
-
Chef::Config[:knife][:security_group_ids] = security_group_ids.gsub(' ', '').split(',')
|
76
|
-
else
|
77
|
-
Chef::Config[:knife][:security_group_ids] ||= []
|
78
|
-
Chef::Config[:knife][:security_group_ids].push(security_group_ids)
|
79
|
-
Chef::Config[:knife][:security_group_ids]
|
80
|
-
end
|
81
|
-
}
|
82
|
-
|
83
|
-
option :security_group_id,
|
84
|
-
:short => "-g SECURITY_GROUP_ID",
|
85
|
-
:long => "--security-group-id ID",
|
86
|
-
:description => "The security group id for this server; required when using VPC. Use the --security-group-id option multiple times when specifying multiple groups for e.g. -g sg-e985168d -g sg-e7f06383 -g sg-ec1b7e88.",
|
87
|
-
:proc => Proc.new { |security_group_id|
|
88
|
-
Chef::Config[:knife][:security_group_ids] ||= []
|
89
|
-
Chef::Config[:knife][:security_group_ids].push(security_group_id)
|
90
|
-
Chef::Config[:knife][:security_group_ids]
|
91
|
-
}
|
92
|
-
|
93
|
-
option :associate_eip,
|
94
|
-
:long => "--associate-eip IP_ADDRESS",
|
95
|
-
:description => "Associate existing elastic IP address with instance after launch"
|
96
|
-
|
97
|
-
option :dedicated_instance,
|
98
|
-
:long => "--dedicated_instance",
|
99
|
-
:description => "Launch as a Dedicated instance (VPC ONLY)"
|
100
|
-
|
101
|
-
option :placement_group,
|
102
|
-
:long => "--placement-group PLACEMENT_GROUP",
|
103
|
-
:description => "The placement group to place a cluster compute instance",
|
104
|
-
:proc => Proc.new { |pg| Chef::Config[:knife][:placement_group] = pg }
|
105
|
-
|
106
|
-
option :primary_eni,
|
107
|
-
:long => "--primary-eni ENI_ID",
|
108
|
-
:description => "Specify a pre-existing eni to use when building the instance."
|
109
|
-
|
110
|
-
option :tags,
|
111
|
-
:short => "-T T=V[,T=V,...]",
|
112
|
-
:long => "--tags Tag=Value[,Tag=Value...]",
|
113
|
-
:description => "The tags for this server. [DEPRECATED] Use --aws-tag instead.",
|
114
|
-
:proc => Proc.new { |
|
115
|
-
Chef::Log.warn("[DEPRECATED] --tags option is deprecated. Use --aws-tag option instead.")
|
116
|
-
|
117
|
-
}
|
118
|
-
|
119
|
-
option :availability_zone,
|
120
|
-
:short => "-Z ZONE",
|
121
|
-
:long => "--availability-zone ZONE",
|
122
|
-
:description => "The Availability Zone",
|
123
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:availability_zone] = key }
|
124
|
-
|
125
|
-
option :chef_node_name,
|
126
|
-
:short => "-N NAME",
|
127
|
-
:long => "--node-name NAME",
|
128
|
-
:description => "The Chef node name for your new node",
|
129
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:chef_node_name] = key }
|
130
|
-
|
131
|
-
option :ssh_key_name,
|
132
|
-
:short => "-S KEY",
|
133
|
-
:long => "--ssh-key KEY",
|
134
|
-
:description => "The AWS SSH key id",
|
135
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_key_name] = key }
|
136
|
-
|
137
|
-
option :ssh_user,
|
138
|
-
:short => "-x USERNAME",
|
139
|
-
:long => "--ssh-user USERNAME",
|
140
|
-
:description => "The ssh username",
|
141
|
-
:default => "root"
|
142
|
-
|
143
|
-
option :ssh_password,
|
144
|
-
:short => "-P PASSWORD",
|
145
|
-
:long => "--ssh-password PASSWORD",
|
146
|
-
:description => "The ssh password"
|
147
|
-
|
148
|
-
option :ssh_port,
|
149
|
-
:short => "-p PORT",
|
150
|
-
:long => "--ssh-port PORT",
|
151
|
-
:description => "The ssh port",
|
152
|
-
:default => "22",
|
153
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
|
154
|
-
|
155
|
-
option :ssh_gateway,
|
156
|
-
:short => "-w GATEWAY",
|
157
|
-
:long => "--ssh-gateway GATEWAY",
|
158
|
-
:description => "The ssh gateway server. Any proxies configured in your ssh config are automatically used by default.",
|
159
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
|
160
|
-
|
161
|
-
option :ssh_gateway_identity,
|
162
|
-
:long => "--ssh-gateway-identity IDENTITY_FILE",
|
163
|
-
:description => "The private key for ssh gateway server",
|
164
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway_identity] = key }
|
165
|
-
|
166
|
-
option :identity_file,
|
167
|
-
:short => "-i IDENTITY_FILE",
|
168
|
-
:long => "--identity-file IDENTITY_FILE",
|
169
|
-
:description => "The SSH identity file used for authentication"
|
170
|
-
|
171
|
-
option :prerelease,
|
172
|
-
:long => "--prerelease",
|
173
|
-
:description => "Install the pre-release chef gems"
|
174
|
-
|
175
|
-
option :bootstrap_version,
|
176
|
-
:long => "--bootstrap-version VERSION",
|
177
|
-
:description => "The version of Chef to install",
|
178
|
-
:proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
|
179
|
-
|
180
|
-
option :bootstrap_proxy,
|
181
|
-
:long => "--bootstrap-proxy PROXY_URL",
|
182
|
-
:description => "The proxy server for the node being bootstrapped",
|
183
|
-
:proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
|
184
|
-
|
185
|
-
option :distro,
|
186
|
-
:short => "-d DISTRO",
|
187
|
-
:long => "--distro DISTRO",
|
188
|
-
:description => "Bootstrap a distro using a template. [DEPRECATED] Use --bootstrap-template option instead.",
|
189
|
-
:proc => Proc.new { |v|
|
190
|
-
Chef::Log.warn("[DEPRECATED] -d / --distro option is deprecated. Use --bootstrap-template option instead.")
|
191
|
-
v
|
192
|
-
}
|
193
|
-
|
194
|
-
option :template_file,
|
195
|
-
:long => "--template-file TEMPLATE",
|
196
|
-
:description => "Full path to location of template to use. [DEPRECATED] Use -t / --bootstrap-template option instead.",
|
197
|
-
:proc => Proc.new { |v|
|
198
|
-
Chef::Log.warn("[DEPRECATED] --template-file option is deprecated. Use -t / --bootstrap-template option instead.")
|
199
|
-
v
|
200
|
-
}
|
201
|
-
|
202
|
-
option :bootstrap_template,
|
203
|
-
:long => "--bootstrap-template TEMPLATE",
|
204
|
-
:description => "Bootstrap Chef using a built-in or custom template. Set to the full path of an erb template or use one of the built-in templates."
|
205
|
-
|
206
|
-
option :ebs_size,
|
207
|
-
:long => "--ebs-size SIZE",
|
208
|
-
:description => "The size of the EBS volume in GB, for EBS-backed instances"
|
209
|
-
|
210
|
-
option :ebs_optimized,
|
211
|
-
:long => "--ebs-optimized",
|
212
|
-
:description => "Enabled optimized EBS I/O"
|
213
|
-
|
214
|
-
option :ebs_no_delete_on_term,
|
215
|
-
:long => "--ebs-no-delete-on-term",
|
216
|
-
:description => "Do not delete EBS volume on instance termination"
|
217
|
-
|
218
|
-
option :run_list,
|
219
|
-
:short => "-r RUN_LIST",
|
220
|
-
:long => "--run-list RUN_LIST",
|
221
|
-
:description => "Comma separated list of roles/recipes to apply",
|
222
|
-
:proc => lambda { |o| o.split(/[\s,]+/) },
|
223
|
-
:default => []
|
224
|
-
|
225
|
-
option :secret,
|
226
|
-
:long => "--secret ",
|
227
|
-
:description => "The secret key to use to encrypt data bag item values",
|
228
|
-
:proc => lambda { |s| Chef::Config[:knife][:secret] = s }
|
229
|
-
|
230
|
-
option :secret_file,
|
231
|
-
:long => "--secret-file SECRET_FILE",
|
232
|
-
:description => "A file containing the secret key to use to encrypt data bag item values",
|
233
|
-
:proc => lambda { |sf| Chef::Config[:knife][:secret_file] = sf }
|
234
|
-
|
235
|
-
option :s3_secret,
|
236
|
-
:long => '--s3-secret S3_SECRET_URL',
|
237
|
-
:description => 'S3 URL (e.g. s3://bucket/file) for the encrypted_data_bag_secret_file',
|
238
|
-
:proc => lambda { |url| Chef::Config[:knife][:s3_secret] = url }
|
239
|
-
|
240
|
-
option :subnet_id,
|
241
|
-
:long => "--subnet SUBNET-ID",
|
242
|
-
:description => "create node in this Virtual Private Cloud Subnet ID (implies VPC mode)",
|
243
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:subnet_id] = key }
|
244
|
-
|
245
|
-
option :private_ip_address,
|
246
|
-
:long => "--private-ip-address IP-ADDRESS",
|
247
|
-
:description => "allows to specify the private IP address of the instance in VPC mode",
|
248
|
-
:proc => Proc.new { |ip| Chef::Config[:knife][:private_ip_address] = ip }
|
249
|
-
|
250
|
-
option :host_key_verify,
|
251
|
-
:long => "--[no-]host-key-verify",
|
252
|
-
:description => "Verify host key, enabled by default.",
|
253
|
-
:boolean => true,
|
254
|
-
:default => true
|
255
|
-
|
256
|
-
option :bootstrap_protocol,
|
257
|
-
:long => "--bootstrap-protocol protocol",
|
258
|
-
:description => "protocol to bootstrap windows servers. options: winrm/ssh",
|
259
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:bootstrap_protocol] = key },
|
260
|
-
:default => nil
|
261
|
-
|
262
|
-
option :fqdn,
|
263
|
-
:long => "--fqdn FQDN",
|
264
|
-
:description => "Pre-defined FQDN. This is used for Kerberos Authentication purpose only",
|
265
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:fqdn] = key },
|
266
|
-
:default => nil
|
267
|
-
|
268
|
-
option :aws_user_data,
|
269
|
-
:long => "--user-data USER_DATA_FILE",
|
270
|
-
:short => "-u USER_DATA_FILE",
|
271
|
-
:description => "The EC2 User Data file to provision the instance with",
|
272
|
-
:proc => Proc.new { |m| Chef::Config[:knife][:aws_user_data] = m },
|
273
|
-
:default => nil
|
274
|
-
|
275
|
-
option :hint,
|
276
|
-
:long => "--hint HINT_NAME[=HINT_FILE]",
|
277
|
-
:description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
|
278
|
-
:proc => Proc.new { |h|
|
279
|
-
Chef::Config[:knife][:hints] ||= {}
|
280
|
-
name, path = h.split("=")
|
281
|
-
Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new
|
282
|
-
}
|
283
|
-
|
284
|
-
option :ephemeral,
|
285
|
-
:long => "--ephemeral EPHEMERAL_DEVICES",
|
286
|
-
:description => "Comma separated list of device locations (eg - /dev/sdb) to map ephemeral devices",
|
287
|
-
:proc => lambda { |o| o.split(/[\s,]+/) },
|
288
|
-
:default => []
|
289
|
-
|
290
|
-
option :server_connect_attribute,
|
291
|
-
:long => "--server-connect-attribute ATTRIBUTE",
|
292
|
-
:short => "-a ATTRIBUTE",
|
293
|
-
:description => "The EC2 server attribute to use for the SSH connection if necessary, e.g. public_ip_address or private_ip_address.",
|
294
|
-
:default => nil
|
295
|
-
|
296
|
-
option :associate_public_ip,
|
297
|
-
:long => "--associate-public-ip",
|
298
|
-
:description => "Associate public ip to VPC instance.",
|
299
|
-
:boolean => true,
|
300
|
-
:default => false
|
301
|
-
|
302
|
-
option :ebs_volume_type,
|
303
|
-
:long => "--ebs-volume-type TYPE",
|
304
|
-
:description => "Possible values are standard (magnetic) | io1 | gp2 | sc1 | st1. Default is gp2",
|
305
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:ebs_volume_type] = key },
|
306
|
-
:default => "gp2"
|
307
|
-
|
308
|
-
option :ebs_provisioned_iops,
|
309
|
-
:long => "--provisioned-iops IOPS",
|
310
|
-
:description => "IOPS rate, only used when ebs volume type is 'io1'",
|
311
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:provisioned_iops] = key },
|
312
|
-
:default => nil
|
313
|
-
|
314
|
-
option :auth_timeout,
|
315
|
-
:long => "--windows-auth-timeout MINUTES",
|
316
|
-
:description => "The maximum time in minutes to wait to for authentication over the transport to the node to succeed. The default value is 25 minutes.",
|
317
|
-
:default => 25
|
318
|
-
|
319
|
-
option :validation_key_url,
|
320
|
-
:long => "--validation-key-url URL",
|
321
|
-
:description => "Path to the validation key",
|
322
|
-
:proc => proc { |m| Chef::Config[:validation_key_url] = m }
|
323
|
-
|
324
|
-
option :ebs_encrypted,
|
325
|
-
:long => "--ebs-encrypted",
|
326
|
-
:description => "Enables EBS volume encryption",
|
327
|
-
:boolean => true,
|
328
|
-
:default => false
|
329
|
-
|
330
|
-
option :spot_price,
|
331
|
-
:long => "--spot-price PRICE",
|
332
|
-
:description => "The maximum hourly USD price for the instance",
|
333
|
-
:default => nil
|
334
|
-
|
335
|
-
option :spot_request_type,
|
336
|
-
:long => "--spot-request-type TYPE",
|
337
|
-
:description => "The Spot Instance request type. Possible values are 'one-time' and 'persistent', default value is 'one-time'",
|
338
|
-
:default => "one-time"
|
339
|
-
|
340
|
-
option :spot_wait_mode,
|
341
|
-
:long => "--spot-wait-mode MODE",
|
342
|
-
:description =>
|
343
|
-
"Whether we should wait for spot request fulfillment. Could be 'wait', 'exit', or " \
|
344
|
-
"'prompt' (default). For any of the above mentioned choices, ('wait') - if the " \
|
345
|
-
"instance does not get allocated before the command itself times-out or ('exit') the " \
|
346
|
-
"user needs to manually bootstrap the instance in the future after it gets allocated.",
|
347
|
-
:default => "prompt"
|
348
|
-
|
349
|
-
option :aws_connection_timeout,
|
350
|
-
:long => "--aws-connection-timeout MINUTES",
|
351
|
-
:description => "The maximum time in minutes to wait to for aws connection. Default is 10 min",
|
352
|
-
:proc => proc {|t| t = t.to_i * 60; Chef::Config[:aws_connection_timeout] = t},
|
353
|
-
:default => 600
|
354
|
-
|
355
|
-
option :node_ssl_verify_mode,
|
356
|
-
:long => "--node-ssl-verify-mode [peer|none]",
|
357
|
-
:description => "Whether or not to verify the SSL cert for all HTTPS requests.",
|
358
|
-
:proc => Proc.new { |v|
|
359
|
-
valid_values = ["none", "peer"]
|
360
|
-
unless valid_values.include?(v)
|
361
|
-
raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}"
|
362
|
-
end
|
363
|
-
}
|
364
|
-
|
365
|
-
option :node_verify_api_cert,
|
366
|
-
:long => "--[no-]node-verify-api-cert",
|
367
|
-
:description => "Verify the SSL cert for HTTPS requests to the Chef server API.",
|
368
|
-
:boolean => true
|
369
|
-
|
370
|
-
option :bootstrap_no_proxy,
|
371
|
-
:long => "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]",
|
372
|
-
:description => "Do not proxy locations for the node being bootstrapped; this option is used internally by Opscode",
|
373
|
-
:proc => Proc.new { |np| Chef::Config[:knife][:bootstrap_no_proxy] = np }
|
374
|
-
|
375
|
-
option :bootstrap_url,
|
376
|
-
:long => "--bootstrap-url URL",
|
377
|
-
:description => "URL to a custom installation script",
|
378
|
-
:proc => Proc.new { |u| Chef::Config[:knife][:bootstrap_url] = u }
|
379
|
-
|
380
|
-
option :bootstrap_install_command,
|
381
|
-
:long => "--bootstrap-install-command COMMANDS",
|
382
|
-
:description => "Custom command to install chef-client",
|
383
|
-
:proc => Proc.new { |ic| Chef::Config[:knife][:bootstrap_install_command] = ic }
|
384
|
-
|
385
|
-
option :bootstrap_wget_options,
|
386
|
-
:long => "--bootstrap-wget-options OPTIONS",
|
387
|
-
:description => "Add options to wget when installing chef-client",
|
388
|
-
:proc => Proc.new { |wo| Chef::Config[:knife][:bootstrap_wget_options] = wo }
|
389
|
-
|
390
|
-
option :bootstrap_curl_options,
|
391
|
-
:long => "--bootstrap-curl-options OPTIONS",
|
392
|
-
:description => "Add options to curl when install chef-client",
|
393
|
-
:proc => Proc.new { |co| Chef::Config[:knife][:bootstrap_curl_options] = co }
|
394
|
-
|
395
|
-
option :bootstrap_vault_file,
|
396
|
-
:long => '--bootstrap-vault-file VAULT_FILE',
|
397
|
-
:description => 'A JSON file with a list of vault(s) and item(s) to be updated'
|
398
|
-
|
399
|
-
option :bootstrap_vault_json,
|
400
|
-
:long => '--bootstrap-vault-json VAULT_JSON',
|
401
|
-
:description => 'A JSON string with the vault(s) and item(s) to be updated'
|
402
|
-
|
403
|
-
option :bootstrap_vault_item,
|
404
|
-
:long => '--bootstrap-vault-item VAULT_ITEM',
|
405
|
-
:description => 'A single vault and item to update as "vault:item"',
|
406
|
-
:proc => Proc.new { |i|
|
407
|
-
(vault, item) = i.split(/:/)
|
408
|
-
Chef::Config[:knife][:bootstrap_vault_item] ||= {}
|
409
|
-
Chef::Config[:knife][:bootstrap_vault_item][vault] ||= []
|
410
|
-
Chef::Config[:knife][:bootstrap_vault_item][vault].push(item)
|
411
|
-
Chef::Config[:knife][:bootstrap_vault_item]
|
412
|
-
}
|
413
|
-
|
414
|
-
option :use_sudo_password,
|
415
|
-
:long => "--use-sudo-password",
|
416
|
-
:description => "Execute the bootstrap via sudo with password",
|
417
|
-
:boolean => false
|
418
|
-
|
419
|
-
option :forward_agent,
|
420
|
-
:short => "-A",
|
421
|
-
:long => "--forward-agent",
|
422
|
-
:description => "Enable SSH agent forwarding",
|
423
|
-
:boolean => true
|
424
|
-
|
425
|
-
option :create_ssl_listener,
|
426
|
-
:long => "--[no-]create-ssl-listener",
|
427
|
-
:description => "Create ssl listener, enabled by default.",
|
428
|
-
:boolean => true,
|
429
|
-
:default => true
|
430
|
-
|
431
|
-
option :network_interfaces,
|
432
|
-
:short => '-n',
|
433
|
-
:long => '--attach-network-interface ENI1,ENI2',
|
434
|
-
:description => 'Attach additional network interfaces during bootstrap',
|
435
|
-
:proc => proc { |nics| nics.split(',') }
|
436
|
-
|
437
|
-
option :classic_link_vpc_id,
|
438
|
-
:long => "--classic-link-vpc-id VPC_ID",
|
439
|
-
:description => "Enable ClassicLink connection with a VPC"
|
440
|
-
|
441
|
-
option :classic_link_vpc_security_group_ids,
|
442
|
-
:long => "--classic-link-vpc-security-groups-ids X,Y,Z",
|
443
|
-
:description => "Comma-separated list of security group ids for ClassicLink",
|
444
|
-
:proc => Proc.new { |groups| groups.split(',') }
|
445
|
-
|
446
|
-
option :disable_api_termination,
|
447
|
-
:long => "--disable-api-termination",
|
448
|
-
:description => "Disable termination of the instance using the Amazon EC2 console, CLI and API.",
|
449
|
-
:boolean => true,
|
450
|
-
:default => false
|
451
|
-
|
452
|
-
option :volume_tags,
|
453
|
-
:long => "--volume-tags Tag=Value[,Tag=Value...]",
|
454
|
-
:description => "Tag the Root volume",
|
455
|
-
:proc => Proc.new { |volume_tags| volume_tags.split(',') }
|
456
|
-
|
457
|
-
option :tag_node_in_chef,
|
458
|
-
:long => "--tag-node-in-chef",
|
459
|
-
:description => "Flag for tagging node in ec2 and chef both. [DEPRECATED] Use --chef-tag instead.",
|
460
|
-
:proc => Proc.new { |v|
|
461
|
-
Chef::Log.warn("[DEPRECATED] --tag-node-in-chef option is deprecated. Use --chef-tag option instead.")
|
462
|
-
v
|
463
|
-
},
|
464
|
-
:boolean => true,
|
465
|
-
:default => false
|
466
|
-
|
467
|
-
option :instance_initiated_shutdown_behavior,
|
468
|
-
:long => "--instance-initiated-shutdown-behavior SHUTDOWN_BEHAVIOR",
|
469
|
-
:description => "Indicates whether an instance stops or terminates when you initiate shutdown from the instance. Possible values are 'stop' and 'terminate', default is 'stop'."
|
470
|
-
|
471
|
-
option :chef_tag,
|
472
|
-
:long => "--chef-tag CHEF_TAG",
|
473
|
-
:description => "Use to tag the node in chef server; Provide --chef-tag option multiple times when specifying multiple tags e.g. --chef-tag tag1 --chef-tag tag2.",
|
474
|
-
:proc => Proc.new { |chef_tag|
|
475
|
-
Chef::Config[:knife][:chef_tag] ||= []
|
476
|
-
Chef::Config[:knife][:chef_tag].push(chef_tag)
|
477
|
-
Chef::Config[:knife][:chef_tag]
|
478
|
-
}
|
479
|
-
|
480
|
-
option :aws_tag,
|
481
|
-
:long => "--aws-tag AWS_TAG",
|
482
|
-
:description => "AWS tag for this server; Use the --aws-tag option multiple times when specifying multiple tags e.g. --aws-tag key1=value1 --aws-tag key2=value2.",
|
483
|
-
:proc => Proc.new { |aws_tag|
|
484
|
-
Chef::Config[:knife][:aws_tag] ||= []
|
485
|
-
Chef::Config[:knife][:aws_tag].push(aws_tag)
|
486
|
-
Chef::Config[:knife][:aws_tag]
|
487
|
-
}
|
488
|
-
|
489
|
-
def run
|
490
|
-
$stdout.sync = true
|
491
|
-
validate!
|
492
|
-
|
493
|
-
requested_elastic_ip = config[:associate_eip] if config[:associate_eip]
|
494
|
-
|
495
|
-
# For VPC EIP assignment we need the allocation ID so fetch full EIP details
|
496
|
-
elastic_ip = connection.addresses.detect{|addr| addr if addr.public_ip == requested_elastic_ip}
|
497
|
-
|
498
|
-
if locate_config_value(:spot_price)
|
499
|
-
server_def = create_server_def
|
500
|
-
server_def[:groups] = server_def[:security_group_ids] if vpc_mode?
|
501
|
-
spot_request = connection.spot_requests.create(server_def)
|
502
|
-
msg_pair("Spot Request ID", spot_request.id)
|
503
|
-
msg_pair("Spot Request Type", spot_request.request_type)
|
504
|
-
msg_pair("Spot Price", spot_request.price)
|
505
|
-
|
506
|
-
case config[:spot_wait_mode]
|
507
|
-
when 'prompt', '', nil
|
508
|
-
wait_msg = "Do you want to wait for Spot Instance Request fulfillment? (Y/N) \n"
|
509
|
-
wait_msg += "Y - Wait for Spot Instance request fulfillment\n"
|
510
|
-
wait_msg += "N - Do not wait for Spot Instance request fulfillment. "
|
511
|
-
wait_msg += ui.color("[WARN :: Request would be alive on AWS ec2 side but execution of Chef Bootstrap on the target instance will get skipped.]\n", :red, :bold)
|
512
|
-
wait_msg += ui.color("\n[WARN :: For any of the above mentioned choices, (Y) - if the instance does not get allocated before the command itself times-out or (N) - user decides to exit, then in both cases user needs to manually bootstrap the instance in the future after it gets allocated.]\n\n", :cyan, :bold)
|
513
|
-
confirm(wait_msg)
|
514
|
-
when 'wait'
|
515
|
-
# wait for the node and run Chef bootstrap
|
516
|
-
when 'exit'
|
517
|
-
ui.color("The 'exit' option was specified for --spot-wait-mode, exiting.", :cyan)
|
518
|
-
exit
|
519
|
-
else
|
520
|
-
raise "Invalid value for --spot-wait-mode: '#{config[:spot_wait_mode]}', " \
|
521
|
-
"valid values: wait, exit, prompt"
|
522
|
-
end
|
523
|
-
|
524
|
-
print ui.color("Waiting for Spot Request fulfillment: ", :cyan)
|
525
|
-
spot_request.wait_for do
|
526
|
-
@spinner ||= %w{| / - \\}
|
527
|
-
print "\b" + @spinner.rotate!.first
|
528
|
-
ready?
|
529
|
-
end
|
530
|
-
puts("\n")
|
531
|
-
@server = connection.servers.get(spot_request.instance_id)
|
532
|
-
else
|
533
|
-
begin
|
534
|
-
@server = connection.servers.create(create_server_def)
|
535
|
-
rescue => error
|
536
|
-
error.message.sub("download completed, but downloaded file not found", "Verify that you have public internet access.")
|
537
|
-
ui.error error.message
|
538
|
-
Chef::Log.debug("#{error.backtrace.join("\n")}")
|
539
|
-
exit
|
540
|
-
end
|
541
|
-
end
|
542
|
-
|
543
|
-
hashed_tags={}
|
544
|
-
tags.map{ |t| key,val=t.split('='); hashed_tags[key]=val} unless tags.nil?
|
545
|
-
|
546
|
-
# Always set the Name tag
|
547
|
-
unless hashed_tags.keys.include? "Name"
|
548
|
-
if locate_config_value(:chef_node_name)
|
549
|
-
hashed_tags["Name"] = evaluate_node_name(locate_config_value(:chef_node_name))
|
550
|
-
else
|
551
|
-
hashed_tags["Name"] = server.id
|
552
|
-
end
|
553
|
-
end
|
554
|
-
|
555
|
-
printed_aws_tags = hashed_tags.map{ |tag, val| "#{tag}: #{val}" }.join(", ")
|
556
|
-
|
557
|
-
hashed_volume_tags={}
|
558
|
-
volume_tags = locate_config_value(:volume_tags)
|
559
|
-
volume_tags.map{ |t| key,val=t.split('='); hashed_volume_tags[key]=val} unless volume_tags.nil?
|
560
|
-
printed_volume_tags = hashed_volume_tags.map{ |tag, val| "#{tag}: #{val}" }.join(", ")
|
561
|
-
|
562
|
-
msg_pair("Instance ID", @server.id)
|
563
|
-
msg_pair("Flavor", @server.flavor_id)
|
564
|
-
msg_pair("Image", @server.image_id)
|
565
|
-
msg_pair("Region", connection.instance_variable_get(:@region))
|
566
|
-
msg_pair("Availability Zone", @server.availability_zone)
|
567
|
-
|
568
|
-
# If we don't specify a security group or security group id, Fog will
|
569
|
-
# pick the appropriate default one. In case of a VPC we don't know the
|
570
|
-
# default security group id at this point unless we look it up, hence
|
571
|
-
# 'default' is printed if no id was specified.
|
572
|
-
printed_security_groups = "default"
|
573
|
-
printed_security_groups = @server.groups.join(", ") if @server.groups
|
574
|
-
msg_pair("Security Groups", printed_security_groups) unless vpc_mode? or (@server.groups.nil? and @server.security_group_ids)
|
575
|
-
|
576
|
-
printed_security_group_ids = "default"
|
577
|
-
printed_security_group_ids = @server.security_group_ids.join(", ") if @server.security_group_ids
|
578
|
-
msg_pair("Security Group Ids", printed_security_group_ids) if vpc_mode? or @server.security_group_ids
|
579
|
-
|
580
|
-
msg_pair("IAM Profile", locate_config_value(:iam_instance_profile))
|
581
|
-
|
582
|
-
msg_pair("AWS Tags", printed_aws_tags)
|
583
|
-
msg_pair("Volume Tags", printed_volume_tags)
|
584
|
-
msg_pair("SSH Key", @server.key_name)
|
585
|
-
|
586
|
-
print "\n#{ui.color("Waiting for EC2 to create the instance", :magenta)}"
|
587
|
-
|
588
|
-
# wait for instance to come up before acting against it
|
589
|
-
@server.wait_for(locate_config_value(:aws_connection_timeout)) { print "."; ready? }
|
590
|
-
|
591
|
-
puts("\n")
|
592
|
-
|
593
|
-
# occasionally 'ready?' isn't, so retry a couple times if needed.
|
594
|
-
tries = 6
|
595
|
-
begin
|
596
|
-
create_tags(hashed_tags) unless hashed_tags.empty?
|
597
|
-
create_volume_tags(hashed_volume_tags) unless hashed_volume_tags.empty?
|
598
|
-
associate_eip(elastic_ip) if config[:associate_eip]
|
599
|
-
enable_classic_link(config[:classic_link_vpc_id], config[:classic_link_vpc_security_group_ids]) if config[:classic_link_vpc_id]
|
600
|
-
rescue Fog::Compute::AWS::NotFound, Fog::Errors::Error
|
601
|
-
raise if (tries -= 1) <= 0
|
602
|
-
ui.warn("server not ready, retrying tag application (retries left: #{tries})")
|
603
|
-
sleep 5
|
604
|
-
retry
|
605
|
-
end
|
606
|
-
|
607
|
-
attach_nics if config[:network_interfaces]
|
608
|
-
|
609
|
-
if vpc_mode?
|
610
|
-
msg_pair("Subnet ID", @server.subnet_id)
|
611
|
-
msg_pair("Tenancy", @server.tenancy)
|
612
|
-
if config[:associate_public_ip]
|
613
|
-
msg_pair("Public DNS Name", @server.dns_name)
|
614
|
-
end
|
615
|
-
if elastic_ip
|
616
|
-
msg_pair("Public IP Address", @server.public_ip_address)
|
617
|
-
end
|
618
|
-
else
|
619
|
-
msg_pair("Public DNS Name", @server.dns_name)
|
620
|
-
msg_pair("Public IP Address", @server.public_ip_address)
|
621
|
-
msg_pair("Private DNS Name", @server.private_dns_name)
|
622
|
-
end
|
623
|
-
msg_pair("Private IP Address", @server.private_ip_address)
|
624
|
-
|
625
|
-
if Chef::Config[:knife][:validation_key_url]
|
626
|
-
download_validation_key(validation_key_path)
|
627
|
-
Chef::Config[:validation_key] = validation_key_path
|
628
|
-
end
|
629
|
-
|
630
|
-
#Check if Server is Windows or Linux
|
631
|
-
if is_image_windows?
|
632
|
-
protocol = locate_config_value(:bootstrap_protocol)
|
633
|
-
protocol ||= 'winrm'
|
634
|
-
if protocol == 'winrm'
|
635
|
-
load_winrm_deps
|
636
|
-
print "\n#{ui.color("Waiting for winrm access to become available", :magenta)}"
|
637
|
-
print(".") until tcp_test_winrm(ssh_connect_host, locate_config_value(:winrm_port)) {
|
638
|
-
sleep 10
|
639
|
-
puts("done")
|
640
|
-
}
|
641
|
-
else
|
642
|
-
print "\n#{ui.color("Waiting for sshd access to become available", :magenta)}"
|
643
|
-
#If FreeSSHd, winsshd etc are available
|
644
|
-
print(".") until tcp_test_ssh(ssh_connect_host, config[:ssh_port]) {
|
645
|
-
sleep @initial_sleep_delay ||= (vpc_mode? ? 40 : 10)
|
646
|
-
puts("done")
|
647
|
-
}
|
648
|
-
ssh_override_winrm
|
649
|
-
end
|
650
|
-
bootstrap_for_windows_node(@server, ssh_connect_host).run
|
651
|
-
else
|
652
|
-
print "\n#{ui.color("Waiting for sshd access to become available", :magenta)}"
|
653
|
-
wait_for_sshd(ssh_connect_host)
|
654
|
-
ssh_override_winrm
|
655
|
-
bootstrap_for_linux_node(@server, ssh_connect_host).run
|
656
|
-
end
|
657
|
-
|
658
|
-
puts "\n"
|
659
|
-
msg_pair("Instance ID", @server.id)
|
660
|
-
msg_pair("Flavor", @server.flavor_id)
|
661
|
-
msg_pair("Placement Group", @server.placement_group) unless @server.placement_group.nil?
|
662
|
-
msg_pair("Image", @server.image_id)
|
663
|
-
msg_pair("Region", connection.instance_variable_get(:@region))
|
664
|
-
msg_pair("Availability Zone", @server.availability_zone)
|
665
|
-
msg_pair("Security Groups", printed_security_groups) unless vpc_mode? or (@server.groups.nil? and @server.security_group_ids)
|
666
|
-
msg_pair("Security Group Ids", printed_security_group_ids) if vpc_mode? or @server.security_group_ids
|
667
|
-
msg_pair("IAM Profile", locate_config_value(:iam_instance_profile)) if locate_config_value(:iam_instance_profile)
|
668
|
-
msg_pair("Primary ENI", locate_config_value(:primary_eni)) if locate_config_value(:primary_eni)
|
669
|
-
msg_pair("AWS Tags", printed_aws_tags)
|
670
|
-
msg_pair("Chef Tags", locate_config_value(:chef_tag)) if locate_config_value(:chef_tag)
|
671
|
-
msg_pair("SSH Key", @server.key_name)
|
672
|
-
msg_pair("Root Device Type", @server.root_device_type)
|
673
|
-
msg_pair("Root Volume Tags", printed_volume_tags)
|
674
|
-
if @server.root_device_type == "ebs"
|
675
|
-
device_map = @server.block_device_mapping.first
|
676
|
-
msg_pair("Root Volume ID", device_map['volumeId'])
|
677
|
-
msg_pair("Root Device Name", device_map['deviceName'])
|
678
|
-
msg_pair("Root Device Delete on Terminate", device_map['deleteOnTermination'])
|
679
|
-
msg_pair("Standard or Provisioned IOPS", device_map['volumeType'])
|
680
|
-
msg_pair("IOPS rate", device_map['iops'])
|
681
|
-
|
682
|
-
print "\n#{ui.color("Block devices", :magenta)}\n"
|
683
|
-
print "#{ui.color("===========================", :magenta)}\n"
|
684
|
-
@server.block_device_mapping.each do |device_map|
|
685
|
-
msg_pair("Device Name", device_map['deviceName'])
|
686
|
-
msg_pair("Volume ID", device_map['volumeId'])
|
687
|
-
msg_pair("Delete on Terminate", device_map['deleteOnTermination'].to_s)
|
688
|
-
msg_pair("Standard or Provisioned IOPS", device_map['volumeType'])
|
689
|
-
msg_pair("IOPS rate", device_map['iops'])
|
690
|
-
print "\n"
|
691
|
-
end
|
692
|
-
print "#{ui.color("===========================", :magenta)}\n"
|
693
|
-
|
694
|
-
if config[:ebs_size]
|
695
|
-
if ami.block_device_mapping.first['volumeSize'].to_i < config[:ebs_size].to_i
|
696
|
-
volume_too_large_warning = "#{config[:ebs_size]}GB " +
|
697
|
-
"EBS volume size is larger than size set in AMI of " +
|
698
|
-
"#{ami.block_device_mapping.first['volumeSize']}GB.\n" +
|
699
|
-
"Use file system tools to make use of the increased volume size."
|
700
|
-
msg_pair("Warning", volume_too_large_warning, :yellow)
|
701
|
-
end
|
702
|
-
end
|
703
|
-
end
|
704
|
-
if config[:ebs_optimized]
|
705
|
-
msg_pair("EBS is Optimized", @server.ebs_optimized.to_s)
|
706
|
-
end
|
707
|
-
if vpc_mode?
|
708
|
-
msg_pair("Subnet ID", @server.subnet_id)
|
709
|
-
msg_pair("Tenancy", @server.tenancy)
|
710
|
-
if config[:associate_public_ip]
|
711
|
-
msg_pair("Public DNS Name", @server.dns_name)
|
712
|
-
end
|
713
|
-
else
|
714
|
-
msg_pair("Public DNS Name", @server.dns_name)
|
715
|
-
msg_pair("Public IP Address", @server.public_ip_address)
|
716
|
-
msg_pair("Private DNS Name", @server.private_dns_name)
|
717
|
-
end
|
718
|
-
msg_pair("Private IP Address", @server.private_ip_address)
|
719
|
-
msg_pair("Environment", config[:environment] || '_default')
|
720
|
-
msg_pair("Run List", (config[:run_list] || []).join(', '))
|
721
|
-
if config[:first_boot_attributes] || config[:first_boot_attributes_from_file]
|
722
|
-
msg_pair("JSON Attributes",config[:first_boot_attributes] || config[:first_boot_attributes_from_file])
|
723
|
-
end
|
724
|
-
end
|
725
|
-
|
726
|
-
def default_bootstrap_template
|
727
|
-
is_image_windows? ? 'windows-chef-client-msi' : 'chef-full'
|
728
|
-
end
|
729
|
-
|
730
|
-
def validation_key_path
|
731
|
-
@validation_key_path ||= begin
|
732
|
-
if URI(Chef::Config[:knife][:validation_key_url]).scheme == 'file'
|
733
|
-
URI(Chef::Config[:knife][:validation_key_url]).path
|
734
|
-
else
|
735
|
-
validation_key_tmpfile.path
|
736
|
-
end
|
737
|
-
end
|
738
|
-
end
|
739
|
-
|
740
|
-
def validation_key_tmpfile
|
741
|
-
@validation_key_tmpfile ||= Tempfile.new('validation_key')
|
742
|
-
end
|
743
|
-
|
744
|
-
def download_validation_key(tempfile)
|
745
|
-
Chef::Log.debug 'Downloading validation key ' \
|
746
|
-
"<#{Chef::Config[:knife][:validation_key_url]}> to file " \
|
747
|
-
"<#{tempfile}>"
|
748
|
-
|
749
|
-
case URI(Chef::Config[:knife][:validation_key_url]).scheme
|
750
|
-
when 's3'
|
751
|
-
File.open(tempfile, 'w') { |f| f.write(s3_validation_key) }
|
752
|
-
end
|
753
|
-
end
|
754
|
-
|
755
|
-
def s3_validation_key
|
756
|
-
@s3_validation_key ||= begin
|
757
|
-
Chef::Knife::S3Source.fetch(Chef::Config[:knife][:validation_key_url])
|
758
|
-
end
|
759
|
-
end
|
760
|
-
|
761
|
-
def s3_secret
|
762
|
-
@s3_secret ||= begin
|
763
|
-
return false unless locate_config_value(:s3_secret)
|
764
|
-
Chef::Knife::S3Source.fetch(locate_config_value(:s3_secret))
|
765
|
-
end
|
766
|
-
end
|
767
|
-
|
768
|
-
def bootstrap_common_params(bootstrap)
|
769
|
-
bootstrap.config[:run_list] = config[:run_list]
|
770
|
-
bootstrap.config[:policy_group] = locate_config_value(:policy_group)
|
771
|
-
bootstrap.config[:policy_name] = locate_config_value(:policy_name)
|
772
|
-
bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
|
773
|
-
bootstrap.config[:distro] = locate_config_value(:distro) || default_bootstrap_template
|
774
|
-
# setting bootstrap_template value to template_file for backward compatibility
|
775
|
-
bootstrap.config[:template_file] = locate_config_value(:template_file) || locate_config_value(:bootstrap_template)
|
776
|
-
bootstrap.config[:environment] = locate_config_value(:environment)
|
777
|
-
bootstrap.config[:prerelease] = config[:prerelease]
|
778
|
-
bootstrap.config[:first_boot_attributes] = locate_config_value(:first_boot_attributes)
|
779
|
-
bootstrap.config[:first_boot_attributes_from_file] = locate_config_value(:first_boot_attributes_from_file)
|
780
|
-
bootstrap.config[:encrypted_data_bag_secret] = s3_secret || locate_config_value(:secret)
|
781
|
-
bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:secret_file)
|
782
|
-
# retrieving the secret from S3 is unique to knife-ec2, so we need to set "command line secret" to the value fetched from S3
|
783
|
-
# When linux vm is spawned, the chef's secret option proc function sets the value "command line secret" and this value is used by
|
784
|
-
# chef's code to check if secret option is passed through command line or not
|
785
|
-
Chef::Knife::DataBagSecretOptions.set_cl_secret(s3_secret) if locate_config_value(:s3_secret)
|
786
|
-
bootstrap.config[:secret] = s3_secret || locate_config_value(:secret)
|
787
|
-
bootstrap.config[:secret_file] = locate_config_value(:secret_file)
|
788
|
-
bootstrap.config[:node_ssl_verify_mode] = locate_config_value(:node_ssl_verify_mode)
|
789
|
-
bootstrap.config[:node_verify_api_cert] = locate_config_value(:node_verify_api_cert)
|
790
|
-
bootstrap.config[:bootstrap_no_proxy] = locate_config_value(:bootstrap_no_proxy)
|
791
|
-
bootstrap.config[:bootstrap_url] = locate_config_value(:bootstrap_url)
|
792
|
-
bootstrap.config[:bootstrap_install_command] = locate_config_value(:bootstrap_install_command)
|
793
|
-
bootstrap.config[:bootstrap_wget_options] = locate_config_value(:bootstrap_wget_options)
|
794
|
-
bootstrap.config[:bootstrap_curl_options] = locate_config_value(:bootstrap_curl_options)
|
795
|
-
bootstrap.config[:bootstrap_vault_file] = locate_config_value(:bootstrap_vault_file)
|
796
|
-
bootstrap.config[:bootstrap_vault_json] = locate_config_value(:bootstrap_vault_json)
|
797
|
-
bootstrap.config[:bootstrap_vault_item] = locate_config_value(:bootstrap_vault_item)
|
798
|
-
bootstrap.config[:use_sudo_password] = locate_config_value(:use_sudo_password)
|
799
|
-
bootstrap.config[:yes] = locate_config_value(:yes)
|
800
|
-
|
801
|
-
#
|
802
|
-
#
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
bootstrap
|
827
|
-
bootstrap.config[:
|
828
|
-
bootstrap.config[:
|
829
|
-
bootstrap.config[:
|
830
|
-
|
831
|
-
bootstrap =
|
832
|
-
bootstrap.config[:
|
833
|
-
bootstrap.config[:
|
834
|
-
bootstrap.config[:
|
835
|
-
bootstrap.config[:
|
836
|
-
bootstrap.config[:
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
bootstrap
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
bootstrap
|
864
|
-
bootstrap.
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
if
|
904
|
-
ui.error("You
|
905
|
-
exit 1
|
906
|
-
end
|
907
|
-
|
908
|
-
if
|
909
|
-
ui.error("You
|
910
|
-
exit 1
|
911
|
-
end
|
912
|
-
|
913
|
-
if config[:
|
914
|
-
ui.error("You can only specify a
|
915
|
-
exit 1
|
916
|
-
end
|
917
|
-
|
918
|
-
if
|
919
|
-
ui.error("
|
920
|
-
exit 1
|
921
|
-
end
|
922
|
-
|
923
|
-
if config[:
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
end
|
936
|
-
|
937
|
-
if config[:
|
938
|
-
ui.error("--provisioned-iops option is
|
939
|
-
exit 1
|
940
|
-
end
|
941
|
-
|
942
|
-
if config[:ebs_volume_type]
|
943
|
-
ui.error("--
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
exit 1
|
951
|
-
end
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
if
|
960
|
-
ui.error("
|
961
|
-
exit 1
|
962
|
-
end
|
963
|
-
|
964
|
-
if
|
965
|
-
ui.error("
|
966
|
-
exit 1
|
967
|
-
end
|
968
|
-
|
969
|
-
if
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
end
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
end
|
1010
|
-
|
1011
|
-
if locate_config_value(:spot_price)
|
1012
|
-
ui.error(
|
1013
|
-
exit 1
|
1014
|
-
end
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
if
|
1023
|
-
ui.
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
end
|
1047
|
-
|
1048
|
-
def
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
end
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
}
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
$
|
1083
|
-
$
|
1084
|
-
$
|
1085
|
-
|
1086
|
-
|
1087
|
-
$
|
1088
|
-
$
|
1089
|
-
$
|
1090
|
-
$
|
1091
|
-
$
|
1092
|
-
$
|
1093
|
-
$
|
1094
|
-
$
|
1095
|
-
$
|
1096
|
-
$
|
1097
|
-
$
|
1098
|
-
$
|
1099
|
-
$
|
1100
|
-
$
|
1101
|
-
$
|
1102
|
-
$
|
1103
|
-
$
|
1104
|
-
$
|
1105
|
-
|
1106
|
-
$
|
1107
|
-
$
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
if
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
gw_user
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
#
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
end
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
end
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
false
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
end
|
1526
|
-
|
1527
|
-
def
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
end
|
1535
|
-
|
1536
|
-
def
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1
|
+
#
|
2
|
+
# Author:: Adam Jacob (<adam@chef.io>)
|
3
|
+
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
4
|
+
# Copyright:: Copyright (c) 2010-2015 Chef Software, Inc.
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'chef/knife/ec2_base'
|
21
|
+
require 'chef/knife/s3_source'
|
22
|
+
require 'chef/knife/winrm_base'
|
23
|
+
require 'chef/knife/bootstrap_windows_base'
|
24
|
+
|
25
|
+
class Chef
|
26
|
+
class Knife
|
27
|
+
class Ec2ServerCreate < Knife
|
28
|
+
|
29
|
+
include Knife::Ec2Base
|
30
|
+
include Knife::WinrmBase
|
31
|
+
include Knife::BootstrapWindowsBase
|
32
|
+
deps do
|
33
|
+
require 'tempfile'
|
34
|
+
require 'fog/aws'
|
35
|
+
require 'uri'
|
36
|
+
require 'readline'
|
37
|
+
require 'chef/json_compat'
|
38
|
+
require 'chef/knife/bootstrap'
|
39
|
+
Chef::Knife::Bootstrap.load_deps
|
40
|
+
end
|
41
|
+
|
42
|
+
banner "knife ec2 server create (options)"
|
43
|
+
|
44
|
+
attr_accessor :initial_sleep_delay
|
45
|
+
attr_reader :server
|
46
|
+
|
47
|
+
option :flavor,
|
48
|
+
:short => "-f FLAVOR",
|
49
|
+
:long => "--flavor FLAVOR",
|
50
|
+
:description => "The flavor of server (m1.small, m1.medium, etc)",
|
51
|
+
:proc => Proc.new { |f| Chef::Config[:knife][:flavor] = f }
|
52
|
+
|
53
|
+
option :image,
|
54
|
+
:short => "-I IMAGE",
|
55
|
+
:long => "--image IMAGE",
|
56
|
+
:description => "The AMI for the server",
|
57
|
+
:proc => Proc.new { |i| Chef::Config[:knife][:image] = i }
|
58
|
+
|
59
|
+
option :iam_instance_profile,
|
60
|
+
:long => "--iam-profile NAME",
|
61
|
+
:description => "The IAM instance profile to apply to this instance."
|
62
|
+
|
63
|
+
option :security_groups,
|
64
|
+
:short => "-G X,Y,Z",
|
65
|
+
:long => "--groups X,Y,Z",
|
66
|
+
:description => "The security groups for this server; not allowed when using VPC",
|
67
|
+
:proc => Proc.new { |groups| groups.split(',') }
|
68
|
+
|
69
|
+
option :security_group_ids,
|
70
|
+
:long => "--security-group-ids 'X,Y,Z'",
|
71
|
+
:description => "The security group ids for this server; required when using VPC. Provide values in format --security-group-ids 'X,Y,Z'. [DEPRECATED] This option will be removed in future release. Use the new --security-group-id option. ",
|
72
|
+
:proc => Proc.new { |security_group_ids|
|
73
|
+
ui.warn('[DEPRECATED] This option will be removed in future release. Use the new --security-group-id option multiple times when specifying multiple groups for e.g. -g sg-e985168d -g sg-e7f06383 -g sg-ec1b7e88.')
|
74
|
+
if security_group_ids.gsub(' ', '').split(',').size > 1
|
75
|
+
Chef::Config[:knife][:security_group_ids] = security_group_ids.gsub(' ', '').split(',')
|
76
|
+
else
|
77
|
+
Chef::Config[:knife][:security_group_ids] ||= []
|
78
|
+
Chef::Config[:knife][:security_group_ids].push(security_group_ids)
|
79
|
+
Chef::Config[:knife][:security_group_ids]
|
80
|
+
end
|
81
|
+
}
|
82
|
+
|
83
|
+
option :security_group_id,
|
84
|
+
:short => "-g SECURITY_GROUP_ID",
|
85
|
+
:long => "--security-group-id ID",
|
86
|
+
:description => "The security group id for this server; required when using VPC. Use the --security-group-id option multiple times when specifying multiple groups for e.g. -g sg-e985168d -g sg-e7f06383 -g sg-ec1b7e88.",
|
87
|
+
:proc => Proc.new { |security_group_id|
|
88
|
+
Chef::Config[:knife][:security_group_ids] ||= []
|
89
|
+
Chef::Config[:knife][:security_group_ids].push(security_group_id)
|
90
|
+
Chef::Config[:knife][:security_group_ids]
|
91
|
+
}
|
92
|
+
|
93
|
+
option :associate_eip,
|
94
|
+
:long => "--associate-eip IP_ADDRESS",
|
95
|
+
:description => "Associate existing elastic IP address with instance after launch"
|
96
|
+
|
97
|
+
option :dedicated_instance,
|
98
|
+
:long => "--dedicated_instance",
|
99
|
+
:description => "Launch as a Dedicated instance (VPC ONLY)"
|
100
|
+
|
101
|
+
option :placement_group,
|
102
|
+
:long => "--placement-group PLACEMENT_GROUP",
|
103
|
+
:description => "The placement group to place a cluster compute instance",
|
104
|
+
:proc => Proc.new { |pg| Chef::Config[:knife][:placement_group] = pg }
|
105
|
+
|
106
|
+
option :primary_eni,
|
107
|
+
:long => "--primary-eni ENI_ID",
|
108
|
+
:description => "Specify a pre-existing eni to use when building the instance."
|
109
|
+
|
110
|
+
option :tags,
|
111
|
+
:short => "-T T=V[,T=V,...]",
|
112
|
+
:long => "--tags Tag=Value[,Tag=Value...]",
|
113
|
+
:description => "The tags for this server. [DEPRECATED] Use --aws-tag instead.",
|
114
|
+
:proc => Proc.new { |tags|
|
115
|
+
Chef::Log.warn("[DEPRECATED] --tags option is deprecated. Use --aws-tag option instead.")
|
116
|
+
tags.split(',')
|
117
|
+
}
|
118
|
+
|
119
|
+
option :availability_zone,
|
120
|
+
:short => "-Z ZONE",
|
121
|
+
:long => "--availability-zone ZONE",
|
122
|
+
:description => "The Availability Zone",
|
123
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:availability_zone] = key }
|
124
|
+
|
125
|
+
option :chef_node_name,
|
126
|
+
:short => "-N NAME",
|
127
|
+
:long => "--node-name NAME",
|
128
|
+
:description => "The Chef node name for your new node",
|
129
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:chef_node_name] = key }
|
130
|
+
|
131
|
+
option :ssh_key_name,
|
132
|
+
:short => "-S KEY",
|
133
|
+
:long => "--ssh-key KEY",
|
134
|
+
:description => "The AWS SSH key id",
|
135
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_key_name] = key }
|
136
|
+
|
137
|
+
option :ssh_user,
|
138
|
+
:short => "-x USERNAME",
|
139
|
+
:long => "--ssh-user USERNAME",
|
140
|
+
:description => "The ssh username",
|
141
|
+
:default => "root"
|
142
|
+
|
143
|
+
option :ssh_password,
|
144
|
+
:short => "-P PASSWORD",
|
145
|
+
:long => "--ssh-password PASSWORD",
|
146
|
+
:description => "The ssh password"
|
147
|
+
|
148
|
+
option :ssh_port,
|
149
|
+
:short => "-p PORT",
|
150
|
+
:long => "--ssh-port PORT",
|
151
|
+
:description => "The ssh port",
|
152
|
+
:default => "22",
|
153
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
|
154
|
+
|
155
|
+
option :ssh_gateway,
|
156
|
+
:short => "-w GATEWAY",
|
157
|
+
:long => "--ssh-gateway GATEWAY",
|
158
|
+
:description => "The ssh gateway server. Any proxies configured in your ssh config are automatically used by default.",
|
159
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
|
160
|
+
|
161
|
+
option :ssh_gateway_identity,
|
162
|
+
:long => "--ssh-gateway-identity IDENTITY_FILE",
|
163
|
+
:description => "The private key for ssh gateway server",
|
164
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway_identity] = key }
|
165
|
+
|
166
|
+
option :identity_file,
|
167
|
+
:short => "-i IDENTITY_FILE",
|
168
|
+
:long => "--identity-file IDENTITY_FILE",
|
169
|
+
:description => "The SSH identity file used for authentication"
|
170
|
+
|
171
|
+
option :prerelease,
|
172
|
+
:long => "--prerelease",
|
173
|
+
:description => "Install the pre-release chef gems"
|
174
|
+
|
175
|
+
option :bootstrap_version,
|
176
|
+
:long => "--bootstrap-version VERSION",
|
177
|
+
:description => "The version of Chef to install",
|
178
|
+
:proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
|
179
|
+
|
180
|
+
option :bootstrap_proxy,
|
181
|
+
:long => "--bootstrap-proxy PROXY_URL",
|
182
|
+
:description => "The proxy server for the node being bootstrapped",
|
183
|
+
:proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
|
184
|
+
|
185
|
+
option :distro,
|
186
|
+
:short => "-d DISTRO",
|
187
|
+
:long => "--distro DISTRO",
|
188
|
+
:description => "Bootstrap a distro using a template. [DEPRECATED] Use --bootstrap-template option instead.",
|
189
|
+
:proc => Proc.new { |v|
|
190
|
+
Chef::Log.warn("[DEPRECATED] -d / --distro option is deprecated. Use --bootstrap-template option instead.")
|
191
|
+
v
|
192
|
+
}
|
193
|
+
|
194
|
+
option :template_file,
|
195
|
+
:long => "--template-file TEMPLATE",
|
196
|
+
:description => "Full path to location of template to use. [DEPRECATED] Use -t / --bootstrap-template option instead.",
|
197
|
+
:proc => Proc.new { |v|
|
198
|
+
Chef::Log.warn("[DEPRECATED] --template-file option is deprecated. Use -t / --bootstrap-template option instead.")
|
199
|
+
v
|
200
|
+
}
|
201
|
+
|
202
|
+
option :bootstrap_template,
|
203
|
+
:long => "--bootstrap-template TEMPLATE",
|
204
|
+
:description => "Bootstrap Chef using a built-in or custom template. Set to the full path of an erb template or use one of the built-in templates."
|
205
|
+
|
206
|
+
option :ebs_size,
|
207
|
+
:long => "--ebs-size SIZE",
|
208
|
+
:description => "The size of the EBS volume in GB, for EBS-backed instances"
|
209
|
+
|
210
|
+
option :ebs_optimized,
|
211
|
+
:long => "--ebs-optimized",
|
212
|
+
:description => "Enabled optimized EBS I/O"
|
213
|
+
|
214
|
+
option :ebs_no_delete_on_term,
|
215
|
+
:long => "--ebs-no-delete-on-term",
|
216
|
+
:description => "Do not delete EBS volume on instance termination"
|
217
|
+
|
218
|
+
option :run_list,
|
219
|
+
:short => "-r RUN_LIST",
|
220
|
+
:long => "--run-list RUN_LIST",
|
221
|
+
:description => "Comma separated list of roles/recipes to apply",
|
222
|
+
:proc => lambda { |o| o.split(/[\s,]+/) },
|
223
|
+
:default => []
|
224
|
+
|
225
|
+
option :secret,
|
226
|
+
:long => "--secret ",
|
227
|
+
:description => "The secret key to use to encrypt data bag item values",
|
228
|
+
:proc => lambda { |s| Chef::Config[:knife][:secret] = s }
|
229
|
+
|
230
|
+
option :secret_file,
|
231
|
+
:long => "--secret-file SECRET_FILE",
|
232
|
+
:description => "A file containing the secret key to use to encrypt data bag item values",
|
233
|
+
:proc => lambda { |sf| Chef::Config[:knife][:secret_file] = sf }
|
234
|
+
|
235
|
+
option :s3_secret,
|
236
|
+
:long => '--s3-secret S3_SECRET_URL',
|
237
|
+
:description => 'S3 URL (e.g. s3://bucket/file) for the encrypted_data_bag_secret_file',
|
238
|
+
:proc => lambda { |url| Chef::Config[:knife][:s3_secret] = url }
|
239
|
+
|
240
|
+
option :subnet_id,
|
241
|
+
:long => "--subnet SUBNET-ID",
|
242
|
+
:description => "create node in this Virtual Private Cloud Subnet ID (implies VPC mode)",
|
243
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:subnet_id] = key }
|
244
|
+
|
245
|
+
option :private_ip_address,
|
246
|
+
:long => "--private-ip-address IP-ADDRESS",
|
247
|
+
:description => "allows to specify the private IP address of the instance in VPC mode",
|
248
|
+
:proc => Proc.new { |ip| Chef::Config[:knife][:private_ip_address] = ip }
|
249
|
+
|
250
|
+
option :host_key_verify,
|
251
|
+
:long => "--[no-]host-key-verify",
|
252
|
+
:description => "Verify host key, enabled by default.",
|
253
|
+
:boolean => true,
|
254
|
+
:default => true
|
255
|
+
|
256
|
+
option :bootstrap_protocol,
|
257
|
+
:long => "--bootstrap-protocol protocol",
|
258
|
+
:description => "protocol to bootstrap windows servers. options: winrm/ssh",
|
259
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:bootstrap_protocol] = key },
|
260
|
+
:default => nil
|
261
|
+
|
262
|
+
option :fqdn,
|
263
|
+
:long => "--fqdn FQDN",
|
264
|
+
:description => "Pre-defined FQDN. This is used for Kerberos Authentication purpose only",
|
265
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:fqdn] = key },
|
266
|
+
:default => nil
|
267
|
+
|
268
|
+
option :aws_user_data,
|
269
|
+
:long => "--user-data USER_DATA_FILE",
|
270
|
+
:short => "-u USER_DATA_FILE",
|
271
|
+
:description => "The EC2 User Data file to provision the instance with",
|
272
|
+
:proc => Proc.new { |m| Chef::Config[:knife][:aws_user_data] = m },
|
273
|
+
:default => nil
|
274
|
+
|
275
|
+
option :hint,
|
276
|
+
:long => "--hint HINT_NAME[=HINT_FILE]",
|
277
|
+
:description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
|
278
|
+
:proc => Proc.new { |h|
|
279
|
+
Chef::Config[:knife][:hints] ||= {}
|
280
|
+
name, path = h.split("=")
|
281
|
+
Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new
|
282
|
+
}
|
283
|
+
|
284
|
+
option :ephemeral,
|
285
|
+
:long => "--ephemeral EPHEMERAL_DEVICES",
|
286
|
+
:description => "Comma separated list of device locations (eg - /dev/sdb) to map ephemeral devices",
|
287
|
+
:proc => lambda { |o| o.split(/[\s,]+/) },
|
288
|
+
:default => []
|
289
|
+
|
290
|
+
option :server_connect_attribute,
|
291
|
+
:long => "--server-connect-attribute ATTRIBUTE",
|
292
|
+
:short => "-a ATTRIBUTE",
|
293
|
+
:description => "The EC2 server attribute to use for the SSH connection if necessary, e.g. public_ip_address or private_ip_address.",
|
294
|
+
:default => nil
|
295
|
+
|
296
|
+
option :associate_public_ip,
|
297
|
+
:long => "--associate-public-ip",
|
298
|
+
:description => "Associate public ip to VPC instance.",
|
299
|
+
:boolean => true,
|
300
|
+
:default => false
|
301
|
+
|
302
|
+
option :ebs_volume_type,
|
303
|
+
:long => "--ebs-volume-type TYPE",
|
304
|
+
:description => "Possible values are standard (magnetic) | io1 | gp2 | sc1 | st1. Default is gp2",
|
305
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:ebs_volume_type] = key },
|
306
|
+
:default => "gp2"
|
307
|
+
|
308
|
+
option :ebs_provisioned_iops,
|
309
|
+
:long => "--provisioned-iops IOPS",
|
310
|
+
:description => "IOPS rate, only used when ebs volume type is 'io1'",
|
311
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:provisioned_iops] = key },
|
312
|
+
:default => nil
|
313
|
+
|
314
|
+
option :auth_timeout,
|
315
|
+
:long => "--windows-auth-timeout MINUTES",
|
316
|
+
:description => "The maximum time in minutes to wait to for authentication over the transport to the node to succeed. The default value is 25 minutes.",
|
317
|
+
:default => 25
|
318
|
+
|
319
|
+
option :validation_key_url,
|
320
|
+
:long => "--validation-key-url URL",
|
321
|
+
:description => "Path to the validation key",
|
322
|
+
:proc => proc { |m| Chef::Config[:validation_key_url] = m }
|
323
|
+
|
324
|
+
option :ebs_encrypted,
|
325
|
+
:long => "--ebs-encrypted",
|
326
|
+
:description => "Enables EBS volume encryption",
|
327
|
+
:boolean => true,
|
328
|
+
:default => false
|
329
|
+
|
330
|
+
option :spot_price,
|
331
|
+
:long => "--spot-price PRICE",
|
332
|
+
:description => "The maximum hourly USD price for the instance",
|
333
|
+
:default => nil
|
334
|
+
|
335
|
+
option :spot_request_type,
|
336
|
+
:long => "--spot-request-type TYPE",
|
337
|
+
:description => "The Spot Instance request type. Possible values are 'one-time' and 'persistent', default value is 'one-time'",
|
338
|
+
:default => "one-time"
|
339
|
+
|
340
|
+
option :spot_wait_mode,
|
341
|
+
:long => "--spot-wait-mode MODE",
|
342
|
+
:description =>
|
343
|
+
"Whether we should wait for spot request fulfillment. Could be 'wait', 'exit', or " \
|
344
|
+
"'prompt' (default). For any of the above mentioned choices, ('wait') - if the " \
|
345
|
+
"instance does not get allocated before the command itself times-out or ('exit') the " \
|
346
|
+
"user needs to manually bootstrap the instance in the future after it gets allocated.",
|
347
|
+
:default => "prompt"
|
348
|
+
|
349
|
+
option :aws_connection_timeout,
|
350
|
+
:long => "--aws-connection-timeout MINUTES",
|
351
|
+
:description => "The maximum time in minutes to wait to for aws connection. Default is 10 min",
|
352
|
+
:proc => proc {|t| t = t.to_i * 60; Chef::Config[:aws_connection_timeout] = t},
|
353
|
+
:default => 600
|
354
|
+
|
355
|
+
option :node_ssl_verify_mode,
|
356
|
+
:long => "--node-ssl-verify-mode [peer|none]",
|
357
|
+
:description => "Whether or not to verify the SSL cert for all HTTPS requests.",
|
358
|
+
:proc => Proc.new { |v|
|
359
|
+
valid_values = ["none", "peer"]
|
360
|
+
unless valid_values.include?(v)
|
361
|
+
raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}"
|
362
|
+
end
|
363
|
+
}
|
364
|
+
|
365
|
+
option :node_verify_api_cert,
|
366
|
+
:long => "--[no-]node-verify-api-cert",
|
367
|
+
:description => "Verify the SSL cert for HTTPS requests to the Chef server API.",
|
368
|
+
:boolean => true
|
369
|
+
|
370
|
+
option :bootstrap_no_proxy,
|
371
|
+
:long => "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]",
|
372
|
+
:description => "Do not proxy locations for the node being bootstrapped; this option is used internally by Opscode",
|
373
|
+
:proc => Proc.new { |np| Chef::Config[:knife][:bootstrap_no_proxy] = np }
|
374
|
+
|
375
|
+
option :bootstrap_url,
|
376
|
+
:long => "--bootstrap-url URL",
|
377
|
+
:description => "URL to a custom installation script",
|
378
|
+
:proc => Proc.new { |u| Chef::Config[:knife][:bootstrap_url] = u }
|
379
|
+
|
380
|
+
option :bootstrap_install_command,
|
381
|
+
:long => "--bootstrap-install-command COMMANDS",
|
382
|
+
:description => "Custom command to install chef-client",
|
383
|
+
:proc => Proc.new { |ic| Chef::Config[:knife][:bootstrap_install_command] = ic }
|
384
|
+
|
385
|
+
option :bootstrap_wget_options,
|
386
|
+
:long => "--bootstrap-wget-options OPTIONS",
|
387
|
+
:description => "Add options to wget when installing chef-client",
|
388
|
+
:proc => Proc.new { |wo| Chef::Config[:knife][:bootstrap_wget_options] = wo }
|
389
|
+
|
390
|
+
option :bootstrap_curl_options,
|
391
|
+
:long => "--bootstrap-curl-options OPTIONS",
|
392
|
+
:description => "Add options to curl when install chef-client",
|
393
|
+
:proc => Proc.new { |co| Chef::Config[:knife][:bootstrap_curl_options] = co }
|
394
|
+
|
395
|
+
option :bootstrap_vault_file,
|
396
|
+
:long => '--bootstrap-vault-file VAULT_FILE',
|
397
|
+
:description => 'A JSON file with a list of vault(s) and item(s) to be updated'
|
398
|
+
|
399
|
+
option :bootstrap_vault_json,
|
400
|
+
:long => '--bootstrap-vault-json VAULT_JSON',
|
401
|
+
:description => 'A JSON string with the vault(s) and item(s) to be updated'
|
402
|
+
|
403
|
+
option :bootstrap_vault_item,
|
404
|
+
:long => '--bootstrap-vault-item VAULT_ITEM',
|
405
|
+
:description => 'A single vault and item to update as "vault:item"',
|
406
|
+
:proc => Proc.new { |i|
|
407
|
+
(vault, item) = i.split(/:/)
|
408
|
+
Chef::Config[:knife][:bootstrap_vault_item] ||= {}
|
409
|
+
Chef::Config[:knife][:bootstrap_vault_item][vault] ||= []
|
410
|
+
Chef::Config[:knife][:bootstrap_vault_item][vault].push(item)
|
411
|
+
Chef::Config[:knife][:bootstrap_vault_item]
|
412
|
+
}
|
413
|
+
|
414
|
+
option :use_sudo_password,
|
415
|
+
:long => "--use-sudo-password",
|
416
|
+
:description => "Execute the bootstrap via sudo with password",
|
417
|
+
:boolean => false
|
418
|
+
|
419
|
+
option :forward_agent,
|
420
|
+
:short => "-A",
|
421
|
+
:long => "--forward-agent",
|
422
|
+
:description => "Enable SSH agent forwarding",
|
423
|
+
:boolean => true
|
424
|
+
|
425
|
+
option :create_ssl_listener,
|
426
|
+
:long => "--[no-]create-ssl-listener",
|
427
|
+
:description => "Create ssl listener, enabled by default.",
|
428
|
+
:boolean => true,
|
429
|
+
:default => true
|
430
|
+
|
431
|
+
option :network_interfaces,
|
432
|
+
:short => '-n',
|
433
|
+
:long => '--attach-network-interface ENI1,ENI2',
|
434
|
+
:description => 'Attach additional network interfaces during bootstrap',
|
435
|
+
:proc => proc { |nics| nics.split(',') }
|
436
|
+
|
437
|
+
option :classic_link_vpc_id,
|
438
|
+
:long => "--classic-link-vpc-id VPC_ID",
|
439
|
+
:description => "Enable ClassicLink connection with a VPC"
|
440
|
+
|
441
|
+
option :classic_link_vpc_security_group_ids,
|
442
|
+
:long => "--classic-link-vpc-security-groups-ids X,Y,Z",
|
443
|
+
:description => "Comma-separated list of security group ids for ClassicLink",
|
444
|
+
:proc => Proc.new { |groups| groups.split(',') }
|
445
|
+
|
446
|
+
option :disable_api_termination,
|
447
|
+
:long => "--disable-api-termination",
|
448
|
+
:description => "Disable termination of the instance using the Amazon EC2 console, CLI and API.",
|
449
|
+
:boolean => true,
|
450
|
+
:default => false
|
451
|
+
|
452
|
+
option :volume_tags,
|
453
|
+
:long => "--volume-tags Tag=Value[,Tag=Value...]",
|
454
|
+
:description => "Tag the Root volume",
|
455
|
+
:proc => Proc.new { |volume_tags| volume_tags.split(',') }
|
456
|
+
|
457
|
+
option :tag_node_in_chef,
|
458
|
+
:long => "--tag-node-in-chef",
|
459
|
+
:description => "Flag for tagging node in ec2 and chef both. [DEPRECATED] Use --chef-tag instead.",
|
460
|
+
:proc => Proc.new { |v|
|
461
|
+
Chef::Log.warn("[DEPRECATED] --tag-node-in-chef option is deprecated. Use --chef-tag option instead.")
|
462
|
+
v
|
463
|
+
},
|
464
|
+
:boolean => true,
|
465
|
+
:default => false
|
466
|
+
|
467
|
+
option :instance_initiated_shutdown_behavior,
|
468
|
+
:long => "--instance-initiated-shutdown-behavior SHUTDOWN_BEHAVIOR",
|
469
|
+
:description => "Indicates whether an instance stops or terminates when you initiate shutdown from the instance. Possible values are 'stop' and 'terminate', default is 'stop'."
|
470
|
+
|
471
|
+
option :chef_tag,
|
472
|
+
:long => "--chef-tag CHEF_TAG",
|
473
|
+
:description => "Use to tag the node in chef server; Provide --chef-tag option multiple times when specifying multiple tags e.g. --chef-tag tag1 --chef-tag tag2.",
|
474
|
+
:proc => Proc.new { |chef_tag|
|
475
|
+
Chef::Config[:knife][:chef_tag] ||= []
|
476
|
+
Chef::Config[:knife][:chef_tag].push(chef_tag)
|
477
|
+
Chef::Config[:knife][:chef_tag]
|
478
|
+
}
|
479
|
+
|
480
|
+
option :aws_tag,
|
481
|
+
:long => "--aws-tag AWS_TAG",
|
482
|
+
:description => "AWS tag for this server; Use the --aws-tag option multiple times when specifying multiple tags e.g. --aws-tag key1=value1 --aws-tag key2=value2.",
|
483
|
+
:proc => Proc.new { |aws_tag|
|
484
|
+
Chef::Config[:knife][:aws_tag] ||= []
|
485
|
+
Chef::Config[:knife][:aws_tag].push(aws_tag)
|
486
|
+
Chef::Config[:knife][:aws_tag]
|
487
|
+
}
|
488
|
+
|
489
|
+
def run
|
490
|
+
$stdout.sync = true
|
491
|
+
validate!
|
492
|
+
|
493
|
+
requested_elastic_ip = config[:associate_eip] if config[:associate_eip]
|
494
|
+
|
495
|
+
# For VPC EIP assignment we need the allocation ID so fetch full EIP details
|
496
|
+
elastic_ip = connection.addresses.detect{|addr| addr if addr.public_ip == requested_elastic_ip}
|
497
|
+
|
498
|
+
if locate_config_value(:spot_price)
|
499
|
+
server_def = create_server_def
|
500
|
+
server_def[:groups] = server_def[:security_group_ids] if vpc_mode?
|
501
|
+
spot_request = connection.spot_requests.create(server_def)
|
502
|
+
msg_pair("Spot Request ID", spot_request.id)
|
503
|
+
msg_pair("Spot Request Type", spot_request.request_type)
|
504
|
+
msg_pair("Spot Price", spot_request.price)
|
505
|
+
|
506
|
+
case config[:spot_wait_mode]
|
507
|
+
when 'prompt', '', nil
|
508
|
+
wait_msg = "Do you want to wait for Spot Instance Request fulfillment? (Y/N) \n"
|
509
|
+
wait_msg += "Y - Wait for Spot Instance request fulfillment\n"
|
510
|
+
wait_msg += "N - Do not wait for Spot Instance request fulfillment. "
|
511
|
+
wait_msg += ui.color("[WARN :: Request would be alive on AWS ec2 side but execution of Chef Bootstrap on the target instance will get skipped.]\n", :red, :bold)
|
512
|
+
wait_msg += ui.color("\n[WARN :: For any of the above mentioned choices, (Y) - if the instance does not get allocated before the command itself times-out or (N) - user decides to exit, then in both cases user needs to manually bootstrap the instance in the future after it gets allocated.]\n\n", :cyan, :bold)
|
513
|
+
confirm(wait_msg)
|
514
|
+
when 'wait'
|
515
|
+
# wait for the node and run Chef bootstrap
|
516
|
+
when 'exit'
|
517
|
+
ui.color("The 'exit' option was specified for --spot-wait-mode, exiting.", :cyan)
|
518
|
+
exit
|
519
|
+
else
|
520
|
+
raise "Invalid value for --spot-wait-mode: '#{config[:spot_wait_mode]}', " \
|
521
|
+
"valid values: wait, exit, prompt"
|
522
|
+
end
|
523
|
+
|
524
|
+
print ui.color("Waiting for Spot Request fulfillment: ", :cyan)
|
525
|
+
spot_request.wait_for do
|
526
|
+
@spinner ||= %w{| / - \\}
|
527
|
+
print "\b" + @spinner.rotate!.first
|
528
|
+
ready?
|
529
|
+
end
|
530
|
+
puts("\n")
|
531
|
+
@server = connection.servers.get(spot_request.instance_id)
|
532
|
+
else
|
533
|
+
begin
|
534
|
+
@server = connection.servers.create(create_server_def)
|
535
|
+
rescue => error
|
536
|
+
error.message.sub("download completed, but downloaded file not found", "Verify that you have public internet access.")
|
537
|
+
ui.error error.message
|
538
|
+
Chef::Log.debug("#{error.backtrace.join("\n")}")
|
539
|
+
exit
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
hashed_tags={}
|
544
|
+
tags.map{ |t| key,val=t.split('='); hashed_tags[key]=val} unless tags.nil?
|
545
|
+
|
546
|
+
# Always set the Name tag
|
547
|
+
unless hashed_tags.keys.include? "Name"
|
548
|
+
if locate_config_value(:chef_node_name)
|
549
|
+
hashed_tags["Name"] = evaluate_node_name(locate_config_value(:chef_node_name))
|
550
|
+
else
|
551
|
+
hashed_tags["Name"] = server.id
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
printed_aws_tags = hashed_tags.map{ |tag, val| "#{tag}: #{val}" }.join(", ")
|
556
|
+
|
557
|
+
hashed_volume_tags={}
|
558
|
+
volume_tags = locate_config_value(:volume_tags)
|
559
|
+
volume_tags.map{ |t| key,val=t.split('='); hashed_volume_tags[key]=val} unless volume_tags.nil?
|
560
|
+
printed_volume_tags = hashed_volume_tags.map{ |tag, val| "#{tag}: #{val}" }.join(", ")
|
561
|
+
|
562
|
+
msg_pair("Instance ID", @server.id)
|
563
|
+
msg_pair("Flavor", @server.flavor_id)
|
564
|
+
msg_pair("Image", @server.image_id)
|
565
|
+
msg_pair("Region", connection.instance_variable_get(:@region))
|
566
|
+
msg_pair("Availability Zone", @server.availability_zone)
|
567
|
+
|
568
|
+
# If we don't specify a security group or security group id, Fog will
|
569
|
+
# pick the appropriate default one. In case of a VPC we don't know the
|
570
|
+
# default security group id at this point unless we look it up, hence
|
571
|
+
# 'default' is printed if no id was specified.
|
572
|
+
printed_security_groups = "default"
|
573
|
+
printed_security_groups = @server.groups.join(", ") if @server.groups
|
574
|
+
msg_pair("Security Groups", printed_security_groups) unless vpc_mode? or (@server.groups.nil? and @server.security_group_ids)
|
575
|
+
|
576
|
+
printed_security_group_ids = "default"
|
577
|
+
printed_security_group_ids = @server.security_group_ids.join(", ") if @server.security_group_ids
|
578
|
+
msg_pair("Security Group Ids", printed_security_group_ids) if vpc_mode? or @server.security_group_ids
|
579
|
+
|
580
|
+
msg_pair("IAM Profile", locate_config_value(:iam_instance_profile))
|
581
|
+
|
582
|
+
msg_pair("AWS Tags", printed_aws_tags)
|
583
|
+
msg_pair("Volume Tags", printed_volume_tags)
|
584
|
+
msg_pair("SSH Key", @server.key_name)
|
585
|
+
|
586
|
+
print "\n#{ui.color("Waiting for EC2 to create the instance", :magenta)}"
|
587
|
+
|
588
|
+
# wait for instance to come up before acting against it
|
589
|
+
@server.wait_for(locate_config_value(:aws_connection_timeout)) { print "."; ready? }
|
590
|
+
|
591
|
+
puts("\n")
|
592
|
+
|
593
|
+
# occasionally 'ready?' isn't, so retry a couple times if needed.
|
594
|
+
tries = 6
|
595
|
+
begin
|
596
|
+
create_tags(hashed_tags) unless hashed_tags.empty?
|
597
|
+
create_volume_tags(hashed_volume_tags) unless hashed_volume_tags.empty?
|
598
|
+
associate_eip(elastic_ip) if config[:associate_eip]
|
599
|
+
enable_classic_link(config[:classic_link_vpc_id], config[:classic_link_vpc_security_group_ids]) if config[:classic_link_vpc_id]
|
600
|
+
rescue Fog::Compute::AWS::NotFound, Fog::Errors::Error
|
601
|
+
raise if (tries -= 1) <= 0
|
602
|
+
ui.warn("server not ready, retrying tag application (retries left: #{tries})")
|
603
|
+
sleep 5
|
604
|
+
retry
|
605
|
+
end
|
606
|
+
|
607
|
+
attach_nics if config[:network_interfaces]
|
608
|
+
|
609
|
+
if vpc_mode?
|
610
|
+
msg_pair("Subnet ID", @server.subnet_id)
|
611
|
+
msg_pair("Tenancy", @server.tenancy)
|
612
|
+
if config[:associate_public_ip]
|
613
|
+
msg_pair("Public DNS Name", @server.dns_name)
|
614
|
+
end
|
615
|
+
if elastic_ip
|
616
|
+
msg_pair("Public IP Address", @server.public_ip_address)
|
617
|
+
end
|
618
|
+
else
|
619
|
+
msg_pair("Public DNS Name", @server.dns_name)
|
620
|
+
msg_pair("Public IP Address", @server.public_ip_address)
|
621
|
+
msg_pair("Private DNS Name", @server.private_dns_name)
|
622
|
+
end
|
623
|
+
msg_pair("Private IP Address", @server.private_ip_address)
|
624
|
+
|
625
|
+
if Chef::Config[:knife][:validation_key_url]
|
626
|
+
download_validation_key(validation_key_path)
|
627
|
+
Chef::Config[:validation_key] = validation_key_path
|
628
|
+
end
|
629
|
+
|
630
|
+
#Check if Server is Windows or Linux
|
631
|
+
if is_image_windows?
|
632
|
+
protocol = locate_config_value(:bootstrap_protocol)
|
633
|
+
protocol ||= 'winrm'
|
634
|
+
if protocol == 'winrm'
|
635
|
+
load_winrm_deps
|
636
|
+
print "\n#{ui.color("Waiting for winrm access to become available", :magenta)}"
|
637
|
+
print(".") until tcp_test_winrm(ssh_connect_host, locate_config_value(:winrm_port)) {
|
638
|
+
sleep 10
|
639
|
+
puts("done")
|
640
|
+
}
|
641
|
+
else
|
642
|
+
print "\n#{ui.color("Waiting for sshd access to become available", :magenta)}"
|
643
|
+
#If FreeSSHd, winsshd etc are available
|
644
|
+
print(".") until tcp_test_ssh(ssh_connect_host, config[:ssh_port]) {
|
645
|
+
sleep @initial_sleep_delay ||= (vpc_mode? ? 40 : 10)
|
646
|
+
puts("done")
|
647
|
+
}
|
648
|
+
ssh_override_winrm
|
649
|
+
end
|
650
|
+
bootstrap_for_windows_node(@server, ssh_connect_host).run
|
651
|
+
else
|
652
|
+
print "\n#{ui.color("Waiting for sshd access to become available", :magenta)}"
|
653
|
+
wait_for_sshd(ssh_connect_host)
|
654
|
+
ssh_override_winrm
|
655
|
+
bootstrap_for_linux_node(@server, ssh_connect_host).run
|
656
|
+
end
|
657
|
+
|
658
|
+
puts "\n"
|
659
|
+
msg_pair("Instance ID", @server.id)
|
660
|
+
msg_pair("Flavor", @server.flavor_id)
|
661
|
+
msg_pair("Placement Group", @server.placement_group) unless @server.placement_group.nil?
|
662
|
+
msg_pair("Image", @server.image_id)
|
663
|
+
msg_pair("Region", connection.instance_variable_get(:@region))
|
664
|
+
msg_pair("Availability Zone", @server.availability_zone)
|
665
|
+
msg_pair("Security Groups", printed_security_groups) unless vpc_mode? or (@server.groups.nil? and @server.security_group_ids)
|
666
|
+
msg_pair("Security Group Ids", printed_security_group_ids) if vpc_mode? or @server.security_group_ids
|
667
|
+
msg_pair("IAM Profile", locate_config_value(:iam_instance_profile)) if locate_config_value(:iam_instance_profile)
|
668
|
+
msg_pair("Primary ENI", locate_config_value(:primary_eni)) if locate_config_value(:primary_eni)
|
669
|
+
msg_pair("AWS Tags", printed_aws_tags)
|
670
|
+
msg_pair("Chef Tags", locate_config_value(:chef_tag)) if locate_config_value(:chef_tag)
|
671
|
+
msg_pair("SSH Key", @server.key_name)
|
672
|
+
msg_pair("Root Device Type", @server.root_device_type)
|
673
|
+
msg_pair("Root Volume Tags", printed_volume_tags)
|
674
|
+
if @server.root_device_type == "ebs"
|
675
|
+
device_map = @server.block_device_mapping.first
|
676
|
+
msg_pair("Root Volume ID", device_map['volumeId'])
|
677
|
+
msg_pair("Root Device Name", device_map['deviceName'])
|
678
|
+
msg_pair("Root Device Delete on Terminate", device_map['deleteOnTermination'])
|
679
|
+
msg_pair("Standard or Provisioned IOPS", device_map['volumeType'])
|
680
|
+
msg_pair("IOPS rate", device_map['iops'])
|
681
|
+
|
682
|
+
print "\n#{ui.color("Block devices", :magenta)}\n"
|
683
|
+
print "#{ui.color("===========================", :magenta)}\n"
|
684
|
+
@server.block_device_mapping.each do |device_map|
|
685
|
+
msg_pair("Device Name", device_map['deviceName'])
|
686
|
+
msg_pair("Volume ID", device_map['volumeId'])
|
687
|
+
msg_pair("Delete on Terminate", device_map['deleteOnTermination'].to_s)
|
688
|
+
msg_pair("Standard or Provisioned IOPS", device_map['volumeType'])
|
689
|
+
msg_pair("IOPS rate", device_map['iops'])
|
690
|
+
print "\n"
|
691
|
+
end
|
692
|
+
print "#{ui.color("===========================", :magenta)}\n"
|
693
|
+
|
694
|
+
if config[:ebs_size]
|
695
|
+
if ami.block_device_mapping.first['volumeSize'].to_i < config[:ebs_size].to_i
|
696
|
+
volume_too_large_warning = "#{config[:ebs_size]}GB " +
|
697
|
+
"EBS volume size is larger than size set in AMI of " +
|
698
|
+
"#{ami.block_device_mapping.first['volumeSize']}GB.\n" +
|
699
|
+
"Use file system tools to make use of the increased volume size."
|
700
|
+
msg_pair("Warning", volume_too_large_warning, :yellow)
|
701
|
+
end
|
702
|
+
end
|
703
|
+
end
|
704
|
+
if config[:ebs_optimized]
|
705
|
+
msg_pair("EBS is Optimized", @server.ebs_optimized.to_s)
|
706
|
+
end
|
707
|
+
if vpc_mode?
|
708
|
+
msg_pair("Subnet ID", @server.subnet_id)
|
709
|
+
msg_pair("Tenancy", @server.tenancy)
|
710
|
+
if config[:associate_public_ip]
|
711
|
+
msg_pair("Public DNS Name", @server.dns_name)
|
712
|
+
end
|
713
|
+
else
|
714
|
+
msg_pair("Public DNS Name", @server.dns_name)
|
715
|
+
msg_pair("Public IP Address", @server.public_ip_address)
|
716
|
+
msg_pair("Private DNS Name", @server.private_dns_name)
|
717
|
+
end
|
718
|
+
msg_pair("Private IP Address", @server.private_ip_address)
|
719
|
+
msg_pair("Environment", config[:environment] || '_default')
|
720
|
+
msg_pair("Run List", (config[:run_list] || []).join(', '))
|
721
|
+
if config[:first_boot_attributes] || config[:first_boot_attributes_from_file]
|
722
|
+
msg_pair("JSON Attributes",config[:first_boot_attributes] || config[:first_boot_attributes_from_file])
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|
726
|
+
def default_bootstrap_template
|
727
|
+
is_image_windows? ? 'windows-chef-client-msi' : 'chef-full'
|
728
|
+
end
|
729
|
+
|
730
|
+
def validation_key_path
|
731
|
+
@validation_key_path ||= begin
|
732
|
+
if URI(Chef::Config[:knife][:validation_key_url]).scheme == 'file'
|
733
|
+
URI(Chef::Config[:knife][:validation_key_url]).path
|
734
|
+
else
|
735
|
+
validation_key_tmpfile.path
|
736
|
+
end
|
737
|
+
end
|
738
|
+
end
|
739
|
+
|
740
|
+
def validation_key_tmpfile
|
741
|
+
@validation_key_tmpfile ||= Tempfile.new('validation_key')
|
742
|
+
end
|
743
|
+
|
744
|
+
def download_validation_key(tempfile)
|
745
|
+
Chef::Log.debug 'Downloading validation key ' \
|
746
|
+
"<#{Chef::Config[:knife][:validation_key_url]}> to file " \
|
747
|
+
"<#{tempfile}>"
|
748
|
+
|
749
|
+
case URI(Chef::Config[:knife][:validation_key_url]).scheme
|
750
|
+
when 's3'
|
751
|
+
File.open(tempfile, 'w') { |f| f.write(s3_validation_key) }
|
752
|
+
end
|
753
|
+
end
|
754
|
+
|
755
|
+
def s3_validation_key
|
756
|
+
@s3_validation_key ||= begin
|
757
|
+
Chef::Knife::S3Source.fetch(Chef::Config[:knife][:validation_key_url])
|
758
|
+
end
|
759
|
+
end
|
760
|
+
|
761
|
+
def s3_secret
|
762
|
+
@s3_secret ||= begin
|
763
|
+
return false unless locate_config_value(:s3_secret)
|
764
|
+
Chef::Knife::S3Source.fetch(locate_config_value(:s3_secret))
|
765
|
+
end
|
766
|
+
end
|
767
|
+
|
768
|
+
def bootstrap_common_params(bootstrap)
|
769
|
+
bootstrap.config[:run_list] = config[:run_list]
|
770
|
+
bootstrap.config[:policy_group] = locate_config_value(:policy_group)
|
771
|
+
bootstrap.config[:policy_name] = locate_config_value(:policy_name)
|
772
|
+
bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
|
773
|
+
bootstrap.config[:distro] = locate_config_value(:distro) || default_bootstrap_template
|
774
|
+
# setting bootstrap_template value to template_file for backward compatibility
|
775
|
+
bootstrap.config[:template_file] = locate_config_value(:template_file) || locate_config_value(:bootstrap_template)
|
776
|
+
bootstrap.config[:environment] = locate_config_value(:environment)
|
777
|
+
bootstrap.config[:prerelease] = config[:prerelease]
|
778
|
+
bootstrap.config[:first_boot_attributes] = locate_config_value(:first_boot_attributes)
|
779
|
+
bootstrap.config[:first_boot_attributes_from_file] = locate_config_value(:first_boot_attributes_from_file)
|
780
|
+
bootstrap.config[:encrypted_data_bag_secret] = s3_secret || locate_config_value(:secret)
|
781
|
+
bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:secret_file)
|
782
|
+
# retrieving the secret from S3 is unique to knife-ec2, so we need to set "command line secret" to the value fetched from S3
|
783
|
+
# When linux vm is spawned, the chef's secret option proc function sets the value "command line secret" and this value is used by
|
784
|
+
# chef's code to check if secret option is passed through command line or not
|
785
|
+
Chef::Knife::DataBagSecretOptions.set_cl_secret(s3_secret) if locate_config_value(:s3_secret)
|
786
|
+
bootstrap.config[:secret] = s3_secret || locate_config_value(:secret)
|
787
|
+
bootstrap.config[:secret_file] = locate_config_value(:secret_file)
|
788
|
+
bootstrap.config[:node_ssl_verify_mode] = locate_config_value(:node_ssl_verify_mode)
|
789
|
+
bootstrap.config[:node_verify_api_cert] = locate_config_value(:node_verify_api_cert)
|
790
|
+
bootstrap.config[:bootstrap_no_proxy] = locate_config_value(:bootstrap_no_proxy)
|
791
|
+
bootstrap.config[:bootstrap_url] = locate_config_value(:bootstrap_url)
|
792
|
+
bootstrap.config[:bootstrap_install_command] = locate_config_value(:bootstrap_install_command)
|
793
|
+
bootstrap.config[:bootstrap_wget_options] = locate_config_value(:bootstrap_wget_options)
|
794
|
+
bootstrap.config[:bootstrap_curl_options] = locate_config_value(:bootstrap_curl_options)
|
795
|
+
bootstrap.config[:bootstrap_vault_file] = locate_config_value(:bootstrap_vault_file)
|
796
|
+
bootstrap.config[:bootstrap_vault_json] = locate_config_value(:bootstrap_vault_json)
|
797
|
+
bootstrap.config[:bootstrap_vault_item] = locate_config_value(:bootstrap_vault_item)
|
798
|
+
bootstrap.config[:use_sudo_password] = locate_config_value(:use_sudo_password)
|
799
|
+
bootstrap.config[:yes] = locate_config_value(:yes)
|
800
|
+
# If --chef-tag is provided then it will be set in chef as single value e.g. --chef-tag "myTag"
|
801
|
+
# Otherwise if --tag-node-in-chef is provided then it will tag the chef in key=value pair of --tags option
|
802
|
+
# e.g. --tags "key=value"
|
803
|
+
if locate_config_value(:chef_tag)
|
804
|
+
bootstrap.config[:tags] = locate_config_value(:chef_tag)
|
805
|
+
elsif locate_config_value(:tag_node_in_chef)
|
806
|
+
bootstrap.config[:tags] = config[:tags]
|
807
|
+
end
|
808
|
+
# Modify global configuration state to ensure hint gets set by
|
809
|
+
# knife-bootstrap
|
810
|
+
Chef::Config[:knife][:hints] ||= {}
|
811
|
+
Chef::Config[:knife][:hints]["ec2"] ||= {}
|
812
|
+
bootstrap
|
813
|
+
end
|
814
|
+
|
815
|
+
def fetch_server_fqdn(ip_addr)
|
816
|
+
require 'resolv'
|
817
|
+
Resolv.getname(ip_addr)
|
818
|
+
end
|
819
|
+
|
820
|
+
def bootstrap_for_windows_node(server, fqdn)
|
821
|
+
if locate_config_value(:bootstrap_protocol) == 'winrm' || locate_config_value(:bootstrap_protocol) == nil
|
822
|
+
if locate_config_value(:kerberos_realm)
|
823
|
+
#Fetch AD/WINS based fqdn if any for Kerberos-based Auth
|
824
|
+
fqdn = locate_config_value(:fqdn) || fetch_server_fqdn(server.private_ip_address)
|
825
|
+
end
|
826
|
+
bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
|
827
|
+
bootstrap.config[:winrm_user] = locate_config_value(:winrm_user)
|
828
|
+
bootstrap.config[:winrm_password] = windows_password
|
829
|
+
bootstrap.config[:winrm_transport] = locate_config_value(:winrm_transport)
|
830
|
+
bootstrap.config[:kerberos_keytab_file] = locate_config_value(:kerberos_keytab_file)
|
831
|
+
bootstrap.config[:kerberos_realm] = locate_config_value(:kerberos_realm)
|
832
|
+
bootstrap.config[:kerberos_service] = locate_config_value(:kerberos_service)
|
833
|
+
bootstrap.config[:ca_trust_file] = locate_config_value(:ca_trust_file)
|
834
|
+
bootstrap.config[:winrm_port] = locate_config_value(:winrm_port)
|
835
|
+
bootstrap.config[:auth_timeout] = locate_config_value(:auth_timeout)
|
836
|
+
bootstrap.config[:winrm_ssl_verify_mode] = locate_config_value(:winrm_ssl_verify_mode)
|
837
|
+
elsif locate_config_value(:bootstrap_protocol) == 'ssh'
|
838
|
+
bootstrap = Chef::Knife::BootstrapWindowsSsh.new
|
839
|
+
bootstrap.config[:ssh_user] = locate_config_value(:ssh_user)
|
840
|
+
bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
|
841
|
+
bootstrap.config[:ssh_port] = locate_config_value(:ssh_port)
|
842
|
+
bootstrap.config[:identity_file] = locate_config_value(:identity_file)
|
843
|
+
bootstrap.config[:no_host_key_verify] = locate_config_value(:no_host_key_verify)
|
844
|
+
bootstrap.config[:forward_agent] = locate_config_value(:forward_agent)
|
845
|
+
else
|
846
|
+
ui.error("Unsupported Bootstrapping Protocol. Supported : winrm, ssh")
|
847
|
+
exit 1
|
848
|
+
end
|
849
|
+
bootstrap.name_args = [fqdn]
|
850
|
+
bootstrap.config[:msi_url] = locate_config_value(:msi_url)
|
851
|
+
bootstrap.config[:install_as_service] = locate_config_value(:install_as_service)
|
852
|
+
bootstrap.config[:session_timeout] = locate_config_value(:session_timeout)
|
853
|
+
|
854
|
+
if locate_config_value(:chef_node_name)
|
855
|
+
bootstrap.config[:chef_node_name] = evaluate_node_name(locate_config_value(:chef_node_name))
|
856
|
+
else
|
857
|
+
bootstrap.config[:chef_node_name] = server.id
|
858
|
+
end
|
859
|
+
bootstrap_common_params(bootstrap)
|
860
|
+
end
|
861
|
+
|
862
|
+
def bootstrap_for_linux_node(server,ssh_host)
|
863
|
+
bootstrap = Chef::Knife::Bootstrap.new
|
864
|
+
bootstrap.name_args = [ssh_host]
|
865
|
+
bootstrap.config[:ssh_user] = config[:ssh_user]
|
866
|
+
bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
|
867
|
+
bootstrap.config[:ssh_port] = config[:ssh_port]
|
868
|
+
bootstrap.config[:ssh_gateway] = config[:ssh_gateway]
|
869
|
+
bootstrap.config[:identity_file] = config[:identity_file]
|
870
|
+
|
871
|
+
if locate_config_value(:chef_node_name)
|
872
|
+
bootstrap.config[:chef_node_name] = evaluate_node_name(locate_config_value(:chef_node_name))
|
873
|
+
else
|
874
|
+
bootstrap.config[:chef_node_name] = server.id
|
875
|
+
end
|
876
|
+
bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
|
877
|
+
# may be needed for vpc_mode
|
878
|
+
bootstrap.config[:host_key_verify] = config[:host_key_verify]
|
879
|
+
bootstrap_common_params(bootstrap)
|
880
|
+
end
|
881
|
+
|
882
|
+
def vpc_mode?
|
883
|
+
# Amazon Virtual Private Cloud requires a subnet_id. If
|
884
|
+
# present, do a few things differently
|
885
|
+
!!locate_config_value(:subnet_id)
|
886
|
+
end
|
887
|
+
|
888
|
+
def ami
|
889
|
+
@ami ||= connection.images.get(locate_config_value(:image))
|
890
|
+
end
|
891
|
+
|
892
|
+
def validate!
|
893
|
+
if Chef::Config[:knife].keys.include? :aws_ssh_key_id
|
894
|
+
Chef::Config[:knife][:ssh_key_name] = Chef::Config[:knife][:aws_ssh_key_id] if !Chef::Config[:knife][:ssh_key_name]
|
895
|
+
Chef::Config[:knife].delete(:aws_ssh_key_id)
|
896
|
+
ui.warn("Use of aws_ssh_key_id option in knife.rb config is deprecated, use ssh_key_name option instead.")
|
897
|
+
end
|
898
|
+
|
899
|
+
super([:image, :ssh_key_name, :aws_access_key_id, :aws_secret_access_key])
|
900
|
+
|
901
|
+
validate_nics! if locate_config_value(:network_interfaces)
|
902
|
+
|
903
|
+
if ami.nil?
|
904
|
+
ui.error("You have not provided a valid image (AMI) value.")
|
905
|
+
exit 1
|
906
|
+
end
|
907
|
+
|
908
|
+
if vpc_mode? and !!config[:security_groups]
|
909
|
+
ui.error("You are using a VPC, security groups specified with '-G' are not allowed, specify one or more security group ids with '-g' instead.")
|
910
|
+
exit 1
|
911
|
+
end
|
912
|
+
|
913
|
+
if !vpc_mode? and !!config[:private_ip_address]
|
914
|
+
ui.error("You can only specify a private IP address if you are using VPC.")
|
915
|
+
exit 1
|
916
|
+
end
|
917
|
+
|
918
|
+
if config[:dedicated_instance] and !vpc_mode?
|
919
|
+
ui.error("You can only specify a Dedicated Instance if you are using VPC.")
|
920
|
+
exit 1
|
921
|
+
end
|
922
|
+
|
923
|
+
if !vpc_mode? and config[:associate_public_ip]
|
924
|
+
ui.error("--associate-public-ip option only applies to VPC instances, and you have not specified a subnet id.")
|
925
|
+
exit 1
|
926
|
+
end
|
927
|
+
|
928
|
+
if config[:associate_eip]
|
929
|
+
eips = connection.addresses.collect{|addr| addr if addr.domain == eip_scope}.compact
|
930
|
+
|
931
|
+
unless eips.detect{|addr| addr.public_ip == config[:associate_eip] && addr.server_id == nil}
|
932
|
+
ui.error("Elastic IP requested is not available.")
|
933
|
+
exit 1
|
934
|
+
end
|
935
|
+
end
|
936
|
+
|
937
|
+
if config[:ebs_provisioned_iops] and config[:ebs_volume_type] != 'io1'
|
938
|
+
ui.error("--provisioned-iops option is only supported for volume type of 'io1'")
|
939
|
+
exit 1
|
940
|
+
end
|
941
|
+
|
942
|
+
if config[:ebs_volume_type] == 'io1' and config[:ebs_provisioned_iops].nil?
|
943
|
+
ui.error("--provisioned-iops option is required when using volume type of 'io1'")
|
944
|
+
exit 1
|
945
|
+
end
|
946
|
+
|
947
|
+
if config[:ebs_volume_type] and ! %w(gp2 io1 standard).include?(config[:ebs_volume_type])
|
948
|
+
ui.error("--ebs-volume-type must be 'standard' or 'io1' or 'gp2'")
|
949
|
+
msg opt_parser
|
950
|
+
exit 1
|
951
|
+
end
|
952
|
+
|
953
|
+
if config[:security_groups] && config[:security_groups].class == String
|
954
|
+
ui.error("Invalid value type for knife[:security_groups] in knife configuration file (i.e knife.rb). Type should be array. e.g - knife[:security_groups] = ['sgroup1']")
|
955
|
+
exit 1
|
956
|
+
end
|
957
|
+
|
958
|
+
# Validation for security_group_ids passed through knife.rb. It will raise error if values are not provided in Array.
|
959
|
+
if locate_config_value(:security_group_ids) && locate_config_value(:security_group_ids).class == String
|
960
|
+
ui.error("Invalid value type for knife[:security_group_ids] in knife configuration file (i.e knife.rb). Type should be array. e.g - knife[:security_group_ids] = ['sgroup1']")
|
961
|
+
exit 1
|
962
|
+
end
|
963
|
+
|
964
|
+
if config[:classic_link_vpc_id].nil? ^ config[:classic_link_vpc_security_group_ids].nil?
|
965
|
+
ui.error("--classic-link-vpc-id and --classic-link-vpc-security-group-ids must be used together")
|
966
|
+
exit 1
|
967
|
+
end
|
968
|
+
|
969
|
+
if vpc_mode? and config[:classic_link_vpc_id]
|
970
|
+
ui.error("You can only use ClassicLink if you are not using a VPC")
|
971
|
+
exit 1
|
972
|
+
end
|
973
|
+
|
974
|
+
if locate_config_value(:ebs_encrypted)
|
975
|
+
error_message = ""
|
976
|
+
errors = []
|
977
|
+
# validation for flavor and ebs_encrypted
|
978
|
+
if !locate_config_value(:flavor)
|
979
|
+
ui.error("--ebs-encrypted option requires valid flavor to be specified.")
|
980
|
+
exit 1
|
981
|
+
elsif (locate_config_value(:ebs_encrypted) and ! %w(m3.medium m3.large m3.xlarge m3.2xlarge m4.large m4.xlarge
|
982
|
+
m4.2xlarge m4.4xlarge m4.10xlarge m4.16xlarge t2.nano t2.micro t2.small
|
983
|
+
t2.medium t2.large t2.xlarge t2.2xlarge d2.xlarge d2.2xlarge d2.4xlarge
|
984
|
+
d2.8xlarge c4.large c4.xlarge c4.2xlarge c4.4xlarge c4.8xlarge c3.large
|
985
|
+
c3.xlarge c3.2xlarge c3.4xlarge c3.8xlarge cr1.8xlarge r3.large r3.xlarge
|
986
|
+
r3.2xlarge r3.4xlarge r3.8xlarge r4.large r4.xlarge r4.2xlarge r4.4xlarge
|
987
|
+
r4.8xlarge r4.16xlarge x1.16xlarge x1.32xlarge i2.xlarge i2.2xlarge i2.4xlarge
|
988
|
+
i2.8xlarge i3.large i3.xlarge i3.2xlarge i3.4xlarge i3.8xlarge i3.16xlarge
|
989
|
+
f1.2xlarge f1.16xlarge g2.2xlarge g2.8xlarge p2.xlarge p2.8xlarge p2.16xlarge).include?(locate_config_value(:flavor)))
|
990
|
+
ui.error("--ebs-encrypted option is not supported for #{locate_config_value(:flavor)} flavor.")
|
991
|
+
exit 1
|
992
|
+
end
|
993
|
+
|
994
|
+
# validation for ebs_size and ebs_volume_type and ebs_encrypted
|
995
|
+
if !locate_config_value(:ebs_size)
|
996
|
+
errors << "--ebs-encrypted option requires valid --ebs-size to be specified."
|
997
|
+
elsif locate_config_value(:ebs_volume_type) == "gp2" and ! locate_config_value(:ebs_size).to_i.between?(1, 16384)
|
998
|
+
errors << "--ebs-size should be in between 1-16384 for 'gp2' ebs volume type."
|
999
|
+
elsif locate_config_value(:ebs_volume_type) == "io1" and ! locate_config_value(:ebs_size).to_i.between?(4, 16384)
|
1000
|
+
errors << "--ebs-size should be in between 4-16384 for 'io1' ebs volume type."
|
1001
|
+
elsif locate_config_value(:ebs_volume_type) == "standard" and ! locate_config_value(:ebs_size).to_i.between?(1, 1024)
|
1002
|
+
errors << "--ebs-size should be in between 1-1024 for 'standard' ebs volume type."
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
if errors.each{|e| error_message = "#{error_message} #{e}"}.any?
|
1006
|
+
ui.error(error_message)
|
1007
|
+
exit 1
|
1008
|
+
end
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
if locate_config_value(:spot_price) && locate_config_value(:disable_api_termination)
|
1012
|
+
ui.error("spot-price and disable-api-termination options cannot be passed together as 'Termination Protection' cannot be enabled for spot instances.")
|
1013
|
+
exit 1
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
if locate_config_value(:spot_price).nil? && locate_config_value(:spot_wait_mode).downcase != 'prompt'
|
1017
|
+
ui.error('spot-wait-mode option requires that a spot-price option is set.')
|
1018
|
+
exit 1
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
volume_tags = locate_config_value(:volume_tags)
|
1022
|
+
if !volume_tags.nil? and volume_tags.length != volume_tags.to_s.count('=')
|
1023
|
+
ui.error("Volume Tags should be entered in a key = value pair")
|
1024
|
+
exit 1
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
if (locate_config_value(:winrm_password).to_s.length > 14 )
|
1028
|
+
ui.warn("The password provided is longer than 14 characters. Computers with Windows prior to Windows 2000 will not be able to use this account. Do you want to continue this operation? (Y/N):")
|
1029
|
+
password_promt = STDIN.gets.chomp.upcase
|
1030
|
+
if (password_promt == "N")
|
1031
|
+
raise "Exiting as operation with password greater than 14 characters not accepted"
|
1032
|
+
elsif (password_promt == "Y")
|
1033
|
+
@allow_long_password = "/yes"
|
1034
|
+
else
|
1035
|
+
raise "The input provided is incorrect."
|
1036
|
+
end
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
if locate_config_value(:tag_node_in_chef)
|
1040
|
+
ui.warn("[DEPRECATED] --tag-node-in-chef option is deprecated. Use --chef-tag option instead.")
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
if locate_config_value(:tags)
|
1044
|
+
ui.warn("[DEPRECATED] --tags option is deprecated. Use --aws-tag option instead.")
|
1045
|
+
end
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
def tags
|
1049
|
+
tags = locate_config_value(:tags) || locate_config_value(:aws_tag)
|
1050
|
+
if !tags.nil? and tags.length != tags.to_s.count('=')
|
1051
|
+
ui.error("AWS Tags should be entered in a key = value pair")
|
1052
|
+
exit 1
|
1053
|
+
end
|
1054
|
+
tags
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
def eip_scope
|
1058
|
+
if vpc_mode?
|
1059
|
+
"vpc"
|
1060
|
+
else
|
1061
|
+
"standard"
|
1062
|
+
end
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
def ssl_config_user_data
|
1066
|
+
user_related_commands = ""
|
1067
|
+
winrm_user = locate_config_value(:winrm_user).split("\\")
|
1068
|
+
if (winrm_user[0] == ".") || (winrm_user[0] == "") ||(winrm_user.length == 1)
|
1069
|
+
user_related_commands = <<-EOH
|
1070
|
+
net user /add #{locate_config_value(:winrm_user).delete('.\\')} #{windows_password} #{@allow_long_password};
|
1071
|
+
net localgroup Administrators /add #{locate_config_value(:winrm_user).delete('.\\')};
|
1072
|
+
EOH
|
1073
|
+
end
|
1074
|
+
<<-EOH
|
1075
|
+
#{user_related_commands}
|
1076
|
+
If (-Not (Get-Service WinRM | Where-Object {$_.status -eq "Running"})) {
|
1077
|
+
winrm quickconfig -q
|
1078
|
+
}
|
1079
|
+
If (winrm e winrm/config/listener | Select-String -Pattern " Transport = HTTP\\b" -Quiet) {
|
1080
|
+
winrm delete winrm/config/listener?Address=*+Transport=HTTP
|
1081
|
+
}
|
1082
|
+
$vm_name = invoke-restmethod -uri http://169.254.169.254/latest/meta-data/public-ipv4
|
1083
|
+
If (-Not $vm_name) {
|
1084
|
+
$vm_name = invoke-restmethod -uri http://169.254.169.254/latest/meta-data/local-ipv4
|
1085
|
+
}
|
1086
|
+
|
1087
|
+
$name = new-object -com "X509Enrollment.CX500DistinguishedName.1"
|
1088
|
+
$name.Encode("CN=$vm_name", 0)
|
1089
|
+
$key = new-object -com "X509Enrollment.CX509PrivateKey.1"
|
1090
|
+
$key.ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
|
1091
|
+
$key.KeySpec = 1
|
1092
|
+
$key.Length = 2048
|
1093
|
+
$key.SecurityDescriptor = "D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)"
|
1094
|
+
$key.MachineContext = 1
|
1095
|
+
$key.Create()
|
1096
|
+
$serverauthoid = new-object -com "X509Enrollment.CObjectId.1"
|
1097
|
+
$serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1")
|
1098
|
+
$ekuoids = new-object -com "X509Enrollment.CObjectIds.1"
|
1099
|
+
$ekuoids.add($serverauthoid)
|
1100
|
+
$ekuext = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
|
1101
|
+
$ekuext.InitializeEncode($ekuoids)
|
1102
|
+
$cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate.1"
|
1103
|
+
$cert.InitializeFromPrivateKey(2, $key, "")
|
1104
|
+
$cert.Subject = $name
|
1105
|
+
$cert.Issuer = $cert.Subject
|
1106
|
+
$cert.NotBefore = get-date
|
1107
|
+
$cert.NotAfter = $cert.NotBefore.AddYears(10)
|
1108
|
+
$cert.X509Extensions.Add($ekuext)
|
1109
|
+
$cert.Encode()
|
1110
|
+
$enrollment = new-object -com "X509Enrollment.CX509Enrollment.1"
|
1111
|
+
$enrollment.InitializeFromRequest($cert)
|
1112
|
+
$certdata = $enrollment.CreateRequest(0)
|
1113
|
+
$enrollment.InstallResponse(2, $certdata, 0, "")
|
1114
|
+
|
1115
|
+
$thumbprint = (Get-ChildItem -Path cert:\\localmachine\\my | Where-Object {$_.Subject -match "$vm_name"}).Thumbprint;
|
1116
|
+
$create_listener_cmd = "winrm create winrm/config/Listener?Address=*+Transport=HTTPS '@{Hostname=`"$vm_name`";CertificateThumbprint=`"$thumbprint`"}'"
|
1117
|
+
iex $create_listener_cmd
|
1118
|
+
netsh advfirewall firewall add rule name="WinRM HTTPS" protocol=TCP dir=in Localport=5986 remoteport=any action=allow localip=any remoteip=any profile=any enable=yes
|
1119
|
+
EOH
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
def ssl_config_data_already_exist?
|
1123
|
+
File.read(locate_config_value(:aws_user_data)).gsub(/\\\\/,"\\").include? ssl_config_user_data.strip
|
1124
|
+
end
|
1125
|
+
|
1126
|
+
def process_user_data(script_lines)
|
1127
|
+
if !ssl_config_data_already_exist?
|
1128
|
+
ps_start_tag = "<powershell>\n"
|
1129
|
+
ps_end_tag = "</powershell>\n"
|
1130
|
+
ps_start_tag_index = script_lines.index(ps_start_tag) || script_lines.index(ps_start_tag.strip)
|
1131
|
+
ps_end_tag_index = script_lines.index(ps_end_tag) || script_lines.index(ps_end_tag.strip)
|
1132
|
+
case
|
1133
|
+
when ( ps_start_tag_index && !ps_end_tag_index ) || ( !ps_start_tag_index && ps_end_tag_index )
|
1134
|
+
ui.error("Provided user_data file is invalid.")
|
1135
|
+
exit 1
|
1136
|
+
when ps_start_tag_index && ps_end_tag_index
|
1137
|
+
script_lines[ps_end_tag_index] = ssl_config_user_data + ps_end_tag
|
1138
|
+
when !ps_start_tag_index && !ps_end_tag_index
|
1139
|
+
script_lines.insert(-1,"\n\n" + ps_start_tag + ssl_config_user_data + ps_end_tag)
|
1140
|
+
end
|
1141
|
+
end
|
1142
|
+
script_lines
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
def create_server_def
|
1146
|
+
server_def = {
|
1147
|
+
:image_id => locate_config_value(:image),
|
1148
|
+
:groups => config[:security_groups],
|
1149
|
+
:flavor_id => locate_config_value(:flavor),
|
1150
|
+
:key_name => locate_config_value(:ssh_key_name),
|
1151
|
+
:availability_zone => locate_config_value(:availability_zone),
|
1152
|
+
:price => locate_config_value(:spot_price),
|
1153
|
+
:request_type => locate_config_value(:spot_request_type)
|
1154
|
+
}
|
1155
|
+
|
1156
|
+
if primary_eni = locate_config_value(:primary_eni)
|
1157
|
+
server_def[:network_interfaces] = [
|
1158
|
+
{
|
1159
|
+
:NetworkInterfaceId => primary_eni,
|
1160
|
+
:DeviceIndex => "0"
|
1161
|
+
}
|
1162
|
+
]
|
1163
|
+
else
|
1164
|
+
server_def[:security_group_ids] = locate_config_value(:security_group_ids)
|
1165
|
+
server_def[:subnet_id] = locate_config_value(:subnet_id) if vpc_mode?
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
server_def[:private_ip_address] = locate_config_value(:private_ip_address) if vpc_mode?
|
1169
|
+
server_def[:placement_group] = locate_config_value(:placement_group)
|
1170
|
+
server_def[:iam_instance_profile_name] = locate_config_value(:iam_instance_profile)
|
1171
|
+
server_def[:tenancy] = "dedicated" if vpc_mode? and locate_config_value(:dedicated_instance)
|
1172
|
+
server_def[:associate_public_ip] = locate_config_value(:associate_public_ip) if vpc_mode? and config[:associate_public_ip]
|
1173
|
+
|
1174
|
+
if locate_config_value(:winrm_transport) == 'ssl'
|
1175
|
+
if locate_config_value(:aws_user_data)
|
1176
|
+
begin
|
1177
|
+
user_data = File.readlines(locate_config_value(:aws_user_data))
|
1178
|
+
if config[:create_ssl_listener]
|
1179
|
+
user_data = process_user_data(user_data)
|
1180
|
+
end
|
1181
|
+
user_data = user_data.join
|
1182
|
+
server_def.merge!(:user_data => user_data)
|
1183
|
+
rescue
|
1184
|
+
ui.warn("Cannot read #{locate_config_value(:aws_user_data)}: #{$!.inspect}. Ignoring option.")
|
1185
|
+
end
|
1186
|
+
else
|
1187
|
+
if config[:create_ssl_listener]
|
1188
|
+
server_def.merge!(:user_data => "<powershell>\n" + ssl_config_user_data + "</powershell>\n")
|
1189
|
+
end
|
1190
|
+
end
|
1191
|
+
else
|
1192
|
+
if locate_config_value(:aws_user_data)
|
1193
|
+
begin
|
1194
|
+
server_def.merge!(:user_data => File.read(locate_config_value(:aws_user_data)))
|
1195
|
+
rescue
|
1196
|
+
ui.warn("Cannot read #{locate_config_value(:aws_user_data)}: #{$!.inspect}. Ignoring option.")
|
1197
|
+
end
|
1198
|
+
end
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
if config[:ebs_optimized]
|
1202
|
+
server_def[:ebs_optimized] = "true"
|
1203
|
+
else
|
1204
|
+
server_def[:ebs_optimized] = "false"
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
if ami.root_device_type == "ebs"
|
1208
|
+
if locate_config_value(:ebs_encrypted)
|
1209
|
+
ami_map = ami.block_device_mapping[1]
|
1210
|
+
else
|
1211
|
+
ami_map = ami.block_device_mapping.first
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
ebs_size = begin
|
1215
|
+
if config[:ebs_size]
|
1216
|
+
Integer(config[:ebs_size]).to_s
|
1217
|
+
else
|
1218
|
+
ami_map["volumeSize"].to_s
|
1219
|
+
end
|
1220
|
+
rescue ArgumentError
|
1221
|
+
puts "--ebs-size must be an integer"
|
1222
|
+
msg opt_parser
|
1223
|
+
exit 1
|
1224
|
+
end
|
1225
|
+
delete_term = if config[:ebs_no_delete_on_term]
|
1226
|
+
"false"
|
1227
|
+
else
|
1228
|
+
ami_map["deleteOnTermination"]
|
1229
|
+
end
|
1230
|
+
iops_rate = begin
|
1231
|
+
if config[:ebs_provisioned_iops]
|
1232
|
+
Integer(config[:ebs_provisioned_iops]).to_s
|
1233
|
+
else
|
1234
|
+
ami_map["iops"].to_s
|
1235
|
+
end
|
1236
|
+
rescue ArgumentError
|
1237
|
+
puts "--provisioned-iops must be an integer"
|
1238
|
+
msg opt_parser
|
1239
|
+
exit 1
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
server_def[:block_device_mapping] =
|
1243
|
+
[{
|
1244
|
+
'DeviceName' => ami_map["deviceName"],
|
1245
|
+
'Ebs.VolumeSize' => ebs_size,
|
1246
|
+
'Ebs.DeleteOnTermination' => delete_term,
|
1247
|
+
'Ebs.VolumeType' => config[:ebs_volume_type],
|
1248
|
+
}]
|
1249
|
+
server_def[:block_device_mapping].first['Ebs.Iops'] = iops_rate unless iops_rate.empty?
|
1250
|
+
server_def[:block_device_mapping].first['Ebs.Encrypted'] = true if locate_config_value(:ebs_encrypted)
|
1251
|
+
end
|
1252
|
+
|
1253
|
+
(config[:ephemeral] || []).each_with_index do |device_name, i|
|
1254
|
+
server_def[:block_device_mapping] = (server_def[:block_device_mapping] || []) << {'VirtualName' => "ephemeral#{i}", 'DeviceName' => device_name}
|
1255
|
+
end
|
1256
|
+
|
1257
|
+
## cannot pass disable_api_termination option to the API when using spot instances ##
|
1258
|
+
server_def[:disable_api_termination] = locate_config_value(:disable_api_termination) if locate_config_value(:spot_price).nil?
|
1259
|
+
|
1260
|
+
server_def[:instance_initiated_shutdown_behavior] = locate_config_value(:instance_initiated_shutdown_behavior)
|
1261
|
+
server_def[:chef_tag] = locate_config_value(:chef_tag)
|
1262
|
+
server_def
|
1263
|
+
end
|
1264
|
+
|
1265
|
+
def wait_for_sshd(hostname)
|
1266
|
+
ssh_gateway = get_ssh_gateway_for(hostname)
|
1267
|
+
ssh_gateway ? wait_for_tunnelled_sshd(ssh_gateway, hostname) : wait_for_direct_sshd(hostname, config[:ssh_port])
|
1268
|
+
end
|
1269
|
+
|
1270
|
+
def get_ssh_gateway_for(hostname)
|
1271
|
+
if config[:ssh_gateway]
|
1272
|
+
# The ssh_gateway specified in the knife config (if any) takes
|
1273
|
+
# precedence over anything in the SSH configuration
|
1274
|
+
Chef::Log.debug("Using ssh gateway #{config[:ssh_gateway]} from knife config")
|
1275
|
+
config[:ssh_gateway]
|
1276
|
+
else
|
1277
|
+
# Next, check if the SSH configuration has a ProxyCommand
|
1278
|
+
# directive for this host. If there is one, parse out the
|
1279
|
+
# host from the proxy command
|
1280
|
+
ssh_proxy = Net::SSH::Config.for(hostname)[:proxy]
|
1281
|
+
if ssh_proxy.respond_to?(:command_line_template)
|
1282
|
+
# ssh gateway_hostname nc %h %p
|
1283
|
+
proxy_pattern = /ssh\s+(\S+)\s+nc/
|
1284
|
+
matchdata = proxy_pattern.match(ssh_proxy.command_line_template)
|
1285
|
+
if matchdata.nil?
|
1286
|
+
Chef::Log.debug("Unable to determine ssh gateway for '#{hostname}' from ssh config template: #{ssh_proxy.command_line_template}")
|
1287
|
+
nil
|
1288
|
+
else
|
1289
|
+
# Return hostname extracted from command line template
|
1290
|
+
Chef::Log.debug("Using ssh gateway #{matchdata[1]} from ssh config")
|
1291
|
+
matchdata[1]
|
1292
|
+
end
|
1293
|
+
else
|
1294
|
+
# Return nil if we cannot find an ssh_gateway
|
1295
|
+
Chef::Log.debug("No ssh gateway found, making a direct connection")
|
1296
|
+
nil
|
1297
|
+
end
|
1298
|
+
end
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
def wait_for_tunnelled_sshd(ssh_gateway, hostname)
|
1302
|
+
initial = true
|
1303
|
+
print(".") until tunnel_test_ssh(ssh_gateway, hostname) {
|
1304
|
+
if initial
|
1305
|
+
initial = false
|
1306
|
+
sleep (vpc_mode? ? 40 : 10)
|
1307
|
+
else
|
1308
|
+
sleep 10
|
1309
|
+
end
|
1310
|
+
puts("done")
|
1311
|
+
}
|
1312
|
+
end
|
1313
|
+
|
1314
|
+
def tunnel_test_ssh(ssh_gateway, hostname, &block)
|
1315
|
+
status = false
|
1316
|
+
gateway = configure_ssh_gateway(ssh_gateway)
|
1317
|
+
gateway.open(hostname, config[:ssh_port]) do |local_tunnel_port|
|
1318
|
+
status = tcp_test_ssh('localhost', local_tunnel_port, &block)
|
1319
|
+
end
|
1320
|
+
status
|
1321
|
+
rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
|
1322
|
+
sleep 2
|
1323
|
+
false
|
1324
|
+
rescue Errno::EPERM, Errno::ETIMEDOUT
|
1325
|
+
false
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
def configure_ssh_gateway(ssh_gateway)
|
1329
|
+
gw_host, gw_user = ssh_gateway.split('@').reverse
|
1330
|
+
gw_host, gw_port = gw_host.split(':')
|
1331
|
+
gateway_options = { :port => gw_port || 22 }
|
1332
|
+
|
1333
|
+
# Load the SSH config for the SSH gateway host.
|
1334
|
+
# Set the gateway user if it was not part of the
|
1335
|
+
# SSH gateway string, and use any configured
|
1336
|
+
# SSH keys.
|
1337
|
+
ssh_gateway_config = Net::SSH::Config.for(gw_host)
|
1338
|
+
gw_user ||= ssh_gateway_config[:user]
|
1339
|
+
|
1340
|
+
# Always use the gateway keys from the SSH Config
|
1341
|
+
gateway_keys = ssh_gateway_config[:keys]
|
1342
|
+
|
1343
|
+
# Use the keys specificed on the command line if available (overrides SSH Config)
|
1344
|
+
if config[:ssh_gateway_identity]
|
1345
|
+
gateway_keys = Array(locate_config_value(:ssh_gateway_identity))
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
unless gateway_keys.nil?
|
1349
|
+
gateway_options[:keys] = gateway_keys
|
1350
|
+
end
|
1351
|
+
|
1352
|
+
Net::SSH::Gateway.new(gw_host, gw_user, gateway_options)
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
def wait_for_direct_sshd(hostname, ssh_port)
|
1356
|
+
initial = true
|
1357
|
+
print(".") until tcp_test_ssh(hostname, ssh_port) {
|
1358
|
+
if initial
|
1359
|
+
initial = false
|
1360
|
+
sleep (vpc_mode? ? 40 : 10)
|
1361
|
+
else
|
1362
|
+
sleep 10
|
1363
|
+
end
|
1364
|
+
puts("done")
|
1365
|
+
}
|
1366
|
+
end
|
1367
|
+
|
1368
|
+
def subnet_public_ip_on_launch?
|
1369
|
+
connection.subnets.get(server.subnet_id).map_public_ip_on_launch
|
1370
|
+
end
|
1371
|
+
|
1372
|
+
def ssh_connect_host
|
1373
|
+
unless @ssh_connect_host
|
1374
|
+
if config[:server_connect_attribute]
|
1375
|
+
connect_attribute = config[:server_connect_attribute]
|
1376
|
+
server.send(config[:server_connect_attribute])
|
1377
|
+
elsif vpc_mode? && !(subnet_public_ip_on_launch? || config[:associate_public_ip] || config[:associate_eip])
|
1378
|
+
connect_attribute = "private_ip_address"
|
1379
|
+
server.private_ip_address
|
1380
|
+
else
|
1381
|
+
connect_attribute = server.dns_name ? "dns_name" : "public_ip_address"
|
1382
|
+
server.send(connect_attribute)
|
1383
|
+
end
|
1384
|
+
@ssh_connect_host = server.send(connect_attribute)
|
1385
|
+
end
|
1386
|
+
|
1387
|
+
puts "\nSSH Target Address: #{@ssh_connect_host}(#{connect_attribute})"
|
1388
|
+
@ssh_connect_host
|
1389
|
+
end
|
1390
|
+
|
1391
|
+
def create_tags(hashed_tags)
|
1392
|
+
hashed_tags.each_pair do |key,val|
|
1393
|
+
connection.tags.create :key => key, :value => val, :resource_id => @server.id
|
1394
|
+
end
|
1395
|
+
end
|
1396
|
+
|
1397
|
+
def associate_eip(elastic_ip)
|
1398
|
+
connection.associate_address(server.id, elastic_ip.public_ip, nil, elastic_ip.allocation_id)
|
1399
|
+
@server.wait_for(locate_config_value(:aws_connection_timeout)) { public_ip_address == elastic_ip.public_ip }
|
1400
|
+
end
|
1401
|
+
|
1402
|
+
def validate_nics!
|
1403
|
+
valid_nic_ids = connection.network_interfaces.all(
|
1404
|
+
vpc_mode? ? { 'vpc-id' => vpc_id } : {}
|
1405
|
+
).map(&:network_interface_id)
|
1406
|
+
invalid_nic_ids =
|
1407
|
+
locate_config_value(:network_interfaces) - valid_nic_ids
|
1408
|
+
return true if invalid_nic_ids.empty?
|
1409
|
+
ui.error 'The following network interfaces are invalid: ' \
|
1410
|
+
"#{invalid_nic_ids.join(', ')}"
|
1411
|
+
exit 1
|
1412
|
+
end
|
1413
|
+
|
1414
|
+
def vpc_id
|
1415
|
+
@vpc_id ||= begin
|
1416
|
+
connection.subnets.get(locate_config_value(:subnet_id)).vpc_id
|
1417
|
+
end
|
1418
|
+
end
|
1419
|
+
|
1420
|
+
def wait_for_nic_attachment
|
1421
|
+
attached_nics_count = 0
|
1422
|
+
until attached_nics_count ==
|
1423
|
+
locate_config_value(:network_interfaces).count
|
1424
|
+
attachment_nics =
|
1425
|
+
locate_config_value(:network_interfaces).map do |nic_id|
|
1426
|
+
connection.network_interfaces.get(nic_id).attachment['status']
|
1427
|
+
end
|
1428
|
+
attached_nics_count = attachment_nics.grep('attached').count
|
1429
|
+
end
|
1430
|
+
end
|
1431
|
+
|
1432
|
+
def attach_nics
|
1433
|
+
attachments = []
|
1434
|
+
config[:network_interfaces].each_with_index do |nic_id, index|
|
1435
|
+
attachments << connection.attach_network_interface(nic_id,
|
1436
|
+
server.id,
|
1437
|
+
index + 1).body
|
1438
|
+
end
|
1439
|
+
wait_for_nic_attachment
|
1440
|
+
# rubocop:disable Style/RedundantReturn
|
1441
|
+
return attachments
|
1442
|
+
# rubocop:enable Style/RedundantReturn
|
1443
|
+
end
|
1444
|
+
|
1445
|
+
def enable_classic_link(vpc_id, security_group_ids)
|
1446
|
+
connection.attach_classic_link_vpc(server.id, vpc_id, security_group_ids)
|
1447
|
+
end
|
1448
|
+
|
1449
|
+
def ssh_override_winrm
|
1450
|
+
# unchanged ssh_user and changed winrm_user, override ssh_user
|
1451
|
+
if locate_config_value(:ssh_user).eql?(options[:ssh_user][:default]) &&
|
1452
|
+
!locate_config_value(:winrm_user).eql?(options[:winrm_user][:default])
|
1453
|
+
config[:ssh_user] = locate_config_value(:winrm_user)
|
1454
|
+
end
|
1455
|
+
# unchanged ssh_port and changed winrm_port, override ssh_port
|
1456
|
+
if locate_config_value(:ssh_port).eql?(options[:ssh_port][:default]) &&
|
1457
|
+
!locate_config_value(:winrm_port).eql?(options[:winrm_port][:default])
|
1458
|
+
config[:ssh_port] = locate_config_value(:winrm_port)
|
1459
|
+
end
|
1460
|
+
# unset ssh_password and set winrm_password, override ssh_password
|
1461
|
+
if locate_config_value(:ssh_password).nil? &&
|
1462
|
+
!locate_config_value(:winrm_password).nil?
|
1463
|
+
config[:ssh_password] = locate_config_value(:winrm_password)
|
1464
|
+
end
|
1465
|
+
# unset identity_file and set kerberos_keytab_file, override identity_file
|
1466
|
+
if locate_config_value(:identity_file).nil? &&
|
1467
|
+
!locate_config_value(:kerberos_keytab_file).nil?
|
1468
|
+
config[:identity_file] = locate_config_value(:kerberos_keytab_file)
|
1469
|
+
end
|
1470
|
+
end
|
1471
|
+
|
1472
|
+
def tcp_test_winrm(ip_addr, port)
|
1473
|
+
tcp_socket = TCPSocket.new(ip_addr, port)
|
1474
|
+
yield
|
1475
|
+
true
|
1476
|
+
rescue SocketError
|
1477
|
+
sleep 2
|
1478
|
+
false
|
1479
|
+
rescue Errno::ETIMEDOUT
|
1480
|
+
false
|
1481
|
+
rescue Errno::EPERM
|
1482
|
+
false
|
1483
|
+
rescue Errno::ECONNREFUSED
|
1484
|
+
sleep 2
|
1485
|
+
false
|
1486
|
+
rescue Errno::EHOSTUNREACH
|
1487
|
+
sleep 2
|
1488
|
+
false
|
1489
|
+
rescue Errno::ENETUNREACH
|
1490
|
+
sleep 2
|
1491
|
+
false
|
1492
|
+
ensure
|
1493
|
+
tcp_socket && tcp_socket.close
|
1494
|
+
end
|
1495
|
+
|
1496
|
+
def tcp_test_ssh(hostname, ssh_port)
|
1497
|
+
tcp_socket = TCPSocket.new(hostname, ssh_port)
|
1498
|
+
readable = IO.select([tcp_socket], nil, nil, 5)
|
1499
|
+
if readable
|
1500
|
+
ssh_banner = tcp_socket.gets
|
1501
|
+
if ssh_banner.nil? or ssh_banner.empty?
|
1502
|
+
false
|
1503
|
+
else
|
1504
|
+
Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{ssh_banner}")
|
1505
|
+
yield
|
1506
|
+
true
|
1507
|
+
end
|
1508
|
+
else
|
1509
|
+
false
|
1510
|
+
end
|
1511
|
+
rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::ENOTCONN, IOError
|
1512
|
+
Chef::Log.debug("ssh failed to connect: #{hostname}")
|
1513
|
+
sleep 2
|
1514
|
+
false
|
1515
|
+
rescue Errno::EPERM, Errno::ETIMEDOUT
|
1516
|
+
Chef::Log.debug("ssh timed out: #{hostname}")
|
1517
|
+
false
|
1518
|
+
# This happens on some mobile phone networks
|
1519
|
+
rescue Errno::ECONNRESET
|
1520
|
+
Chef::Log.debug("ssh reset its connection: #{hostname}")
|
1521
|
+
sleep 2
|
1522
|
+
false
|
1523
|
+
ensure
|
1524
|
+
tcp_socket && tcp_socket.close
|
1525
|
+
end
|
1526
|
+
|
1527
|
+
def decrypt_admin_password(encoded_password, key)
|
1528
|
+
require 'base64'
|
1529
|
+
require 'openssl'
|
1530
|
+
private_key = OpenSSL::PKey::RSA.new(key)
|
1531
|
+
encrypted_password = Base64.decode64(encoded_password)
|
1532
|
+
password = private_key.private_decrypt(encrypted_password)
|
1533
|
+
password
|
1534
|
+
end
|
1535
|
+
|
1536
|
+
def check_windows_password_available(server_id)
|
1537
|
+
sleep 10
|
1538
|
+
response = connection.get_password_data(server_id)
|
1539
|
+
if not response.body["passwordData"]
|
1540
|
+
return false
|
1541
|
+
end
|
1542
|
+
response.body["passwordData"]
|
1543
|
+
end
|
1544
|
+
|
1545
|
+
def windows_password
|
1546
|
+
if not locate_config_value(:winrm_password)
|
1547
|
+
if locate_config_value(:identity_file)
|
1548
|
+
if @server
|
1549
|
+
print "\n#{ui.color("Waiting for Windows Admin password to be available: ", :magenta)}"
|
1550
|
+
print(".") until check_windows_password_available(@server.id) { puts("done") }
|
1551
|
+
response = connection.get_password_data(@server.id)
|
1552
|
+
data = File.read(locate_config_value(:identity_file))
|
1553
|
+
config[:winrm_password] = decrypt_admin_password(response.body["passwordData"], data)
|
1554
|
+
else
|
1555
|
+
print "\n#{ui.color("Fetchig instance details: \n", :magenta)}"
|
1556
|
+
end
|
1557
|
+
else
|
1558
|
+
ui.error("Cannot find SSH Identity file, required to fetch dynamically generated password")
|
1559
|
+
exit 1
|
1560
|
+
end
|
1561
|
+
else
|
1562
|
+
locate_config_value(:winrm_password)
|
1563
|
+
end
|
1564
|
+
end
|
1565
|
+
|
1566
|
+
def load_winrm_deps
|
1567
|
+
require 'winrm'
|
1568
|
+
require 'chef/knife/winrm'
|
1569
|
+
require 'chef/knife/bootstrap_windows_winrm'
|
1570
|
+
require 'chef/knife/bootstrap_windows_ssh'
|
1571
|
+
require 'chef/knife/core/windows_bootstrap_context'
|
1572
|
+
end
|
1573
|
+
|
1574
|
+
#Returns the name of node after evaluation of server id if %s is present.
|
1575
|
+
#Eg: "Test-%s" will return "Test-i-12345" in case the instance id is i-12345
|
1576
|
+
def evaluate_node_name(node_name)
|
1577
|
+
return node_name%server.id
|
1578
|
+
end
|
1579
|
+
|
1580
|
+
def create_volume_tags(hashed_volume_tags)
|
1581
|
+
hashed_volume_tags.each_pair do |key,val|
|
1582
|
+
connection.tags.create :key => key, :value => val, :resource_id => @server.block_device_mapping.first['volumeId']
|
1583
|
+
end
|
1584
|
+
end
|
1585
|
+
|
1586
|
+
end
|
1587
|
+
end
|
1588
|
+
end
|