knife-windows 0.8.6 → 1.0.0.rc.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +17 -3
- data/CHANGELOG.md +25 -6
- data/DOC_CHANGES.md +323 -0
- data/Gemfile +2 -1
- data/README.md +160 -29
- data/RELEASE_NOTES.md +59 -6
- data/appveyor.yml +42 -0
- data/ci.gemfile +15 -0
- data/knife-windows.gemspec +4 -2
- data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +35 -21
- data/lib/chef/knife/bootstrap_windows_base.rb +155 -31
- data/lib/chef/knife/bootstrap_windows_ssh.rb +1 -1
- data/lib/chef/knife/bootstrap_windows_winrm.rb +17 -10
- data/lib/chef/knife/core/windows_bootstrap_context.rb +67 -16
- data/lib/chef/knife/windows_cert_generate.rb +155 -0
- data/lib/chef/knife/windows_cert_install.rb +62 -0
- data/lib/chef/knife/windows_helper.rb +3 -1
- data/lib/chef/knife/windows_listener_create.rb +100 -0
- data/lib/chef/knife/winrm.rb +84 -208
- data/lib/chef/knife/winrm_base.rb +36 -10
- data/lib/chef/knife/winrm_knife_base.rb +201 -0
- data/lib/chef/knife/winrm_session.rb +72 -0
- data/lib/chef/knife/winrm_shared_options.rb +47 -0
- data/lib/chef/knife/wsman_endpoint.rb +44 -0
- data/lib/chef/knife/wsman_test.rb +96 -0
- data/lib/knife-windows/path_helper.rb +77 -0
- data/lib/knife-windows/version.rb +1 -1
- data/spec/functional/bootstrap_download_spec.rb +41 -23
- data/spec/spec_helper.rb +11 -1
- data/spec/unit/knife/bootstrap_template_spec.rb +27 -27
- data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +67 -23
- data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +47 -0
- data/spec/unit/knife/windows_cert_generate_spec.rb +90 -0
- data/spec/unit/knife/windows_cert_install_spec.rb +35 -0
- data/spec/unit/knife/windows_listener_create_spec.rb +61 -0
- data/spec/unit/knife/winrm_session_spec.rb +47 -0
- data/spec/unit/knife/winrm_spec.rb +222 -56
- data/spec/unit/knife/wsman_test_spec.rb +176 -0
- metadata +51 -20
data/RELEASE_NOTES.md
CHANGED
@@ -6,14 +6,67 @@ Example Note:
|
|
6
6
|
## Example Heading
|
7
7
|
Details about the thing that changed that needs to get included in the Release Notes in markdown.
|
8
8
|
-->
|
9
|
-
# knife-windows 0.
|
10
|
-
This release of knife-windows
|
9
|
+
# knife-windows 1.0.0.rc.0 release notes:
|
10
|
+
This release of knife-windows includes new features to improve authentication,
|
11
|
+
simplify use of the WinRM SSL transport, and addresses compatibility issues with Chef Client 12.0.
|
11
12
|
|
12
|
-
|
13
|
-
None.
|
13
|
+
You can install the new features using the `gem` command:
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
gem install knife-windows --pre
|
16
|
+
|
17
|
+
## Reporting issues and contributing
|
18
|
+
|
19
|
+
`knife-windows` issues like those addressed in this release should be reported in the ticketing system at https://github.com/chef/knife-windows/issues. You can learn more about how to contribute features and bug fixes to `knife-windows` in the [Chef Contributions document](http://docs.chef.io/community_contributions.html).
|
20
|
+
|
21
|
+
## Breaking changes
|
22
|
+
|
23
|
+
### Negotiate as the default authentication protocol
|
24
|
+
With this release, the default authentication protocol for WinRM
|
25
|
+
communication is negotiate, which is the same as that for tools built-in to
|
26
|
+
the Windows operating system. Prior to this release, the protocol depended
|
27
|
+
on the format of the `--winrm-user` option -- the basic authentication
|
28
|
+
protocol would be assumed unless that option had the format `domain\user`.
|
29
|
+
|
30
|
+
To revert to the behavior of previous releases or otherwise force `knife-windows` to use a specific authentication protocol such as
|
31
|
+
basic, use the `--winrm-authentication-protocol` option.
|
32
|
+
|
33
|
+
### Default WinRM port depends on the transport
|
34
|
+
The default port for WinRM communication is now **5986** when the SSL transport is used (the transport is
|
35
|
+
configured by the `winrm_transport` option), otherwise it is **5985**. In
|
36
|
+
previous releases, if the port was not specified, it was always 5985.
|
37
|
+
|
38
|
+
To override this behavior, explicitly specify the desired port using the
|
39
|
+
`winrm_port` (`-p`) option.
|
40
|
+
|
41
|
+
### Kerberos Keytab short option is now -T
|
42
|
+
The short option flag for --keytab-file is now -T to fix a conflict with the --identity-file option.
|
43
|
+
|
44
|
+
## Features added in knife-windows 1.0.0.rc.0
|
45
|
+
* New `--winrm-authentication-protocol` option for explicit control of WinRM authentication
|
46
|
+
* `knife windows cert generate` subcommand:
|
47
|
+
Generates a certificate and related public key file for use in configuring a WinRM listener and validating communication involving it.
|
48
|
+
* `knife windows cert install` subcommand:
|
49
|
+
Installs a certificate such as one generated by the `cert generate`
|
50
|
+
subcommand into the Windows certificate store's LocalMachine personal store
|
51
|
+
so that it can be used as part of the configuration for a WinRM SSL listener
|
52
|
+
* `knife windows listener create` subcommand:
|
53
|
+
Creates a WinRM SSL listener on a Windows system
|
54
|
+
* Added `--hint` option for creating Ohai hints on bootstrap
|
55
|
+
* Validatorless bootstrapping is now supported
|
56
|
+
* New `--install-as-service` option will have Chef Client be installed as a service on bootstrap
|
57
|
+
* Added `--msi_url` option for providing an alternate URL to the Chef Client installation package
|
58
|
+
* `knife wsman test` subcommaned:
|
59
|
+
Verifies winrm functionality on a remote system, e.g. `knife wsman test 192.168.1.10 -m --winrm-transport ssl`
|
60
|
+
|
61
|
+
## Issues fixed in knife-windows 1.0.0.rc.0
|
62
|
+
* [knife-windows #159](https://github.com/chef/knife-windows/issues/159) `winrm_port` option should default to 5986 if `winrm_transport` option is `ssl`
|
63
|
+
* [knife-windows #139](https://github.com/chef/knife-windows/issues/139) Force dev dependency on Chef 11 for test scenarios to avoid Ohai 8 conflict on Ruby 1.9.x
|
64
|
+
* [knife-windows #133](https://github.com/chef/knife-windows/issues/133) Bootstrap failure -- unable to validate SSL chef server endpoints
|
65
|
+
* [knife-windows #125](https://github.com/chef/knife-windows/issues/125) knife-windows should use PowerShell first before cscript to download the Chef Client msi
|
66
|
+
* [knife-windows #92](https://github.com/chef/knife-windows/issues/92) EventMachine issue: knife bootstrap windows winrm error
|
67
|
+
* [knife-windows #94](https://github.com/chef/knife-windows/issues/94) Remove Eventmachine dependency
|
68
|
+
* [knife-windows #213](https://github.com/chef/knife-windows/pull/213) Search possibilities of HOME for bootstrap templates
|
69
|
+
* [knife-windows #227](https://github.com/chef/knife-windows/issues/227) Exception: NoMethodError: undefined method 'gsub' for false:FalseClass
|
17
70
|
|
18
71
|
## knife-windows on RubyGems and Github
|
19
72
|
https://rubygems.org/gems/knife-windows
|
data/appveyor.yml
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
version: "master-{build}"
|
2
|
+
|
3
|
+
os: Windows Server 2012
|
4
|
+
platform:
|
5
|
+
- x64
|
6
|
+
|
7
|
+
environment:
|
8
|
+
bundle_gemfile: ci.gemfile
|
9
|
+
|
10
|
+
matrix:
|
11
|
+
- ruby_version: "193"
|
12
|
+
chef_version: "< 12"
|
13
|
+
|
14
|
+
- ruby_version: "200"
|
15
|
+
chef_version: "< 12"
|
16
|
+
|
17
|
+
- ruby_version: "200"
|
18
|
+
chef_version: "~> 12.0"
|
19
|
+
|
20
|
+
- ruby_version: "200"
|
21
|
+
chef_version: "master"
|
22
|
+
|
23
|
+
clone_folder: c:\projects\knife-windows
|
24
|
+
clone_depth: 1
|
25
|
+
branches:
|
26
|
+
only:
|
27
|
+
- master
|
28
|
+
|
29
|
+
install:
|
30
|
+
- winrm quickconfig -q
|
31
|
+
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
|
32
|
+
- echo %PATH%
|
33
|
+
- ruby --version
|
34
|
+
- gem --version
|
35
|
+
- gem install bundler --quiet --no-ri --no-rdoc
|
36
|
+
- bundler --version
|
37
|
+
|
38
|
+
build_script:
|
39
|
+
- bundle install || bundle install || bundle install
|
40
|
+
|
41
|
+
test_script:
|
42
|
+
- bundle exec rake spec
|
data/ci.gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in knife-windows.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
if ENV['CHEF_VERSION'] == 'master'
|
7
|
+
gem 'chef', github: 'chef/chef'
|
8
|
+
else
|
9
|
+
gem 'chef', ENV['CHEF_VERSION']
|
10
|
+
end
|
11
|
+
|
12
|
+
gem "rspec", '~> 3.0'
|
13
|
+
gem "ruby-wmi"
|
14
|
+
gem "httpclient"
|
15
|
+
gem 'rake'
|
data/knife-windows.gemspec
CHANGED
@@ -14,8 +14,9 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.description = s.summary
|
15
15
|
|
16
16
|
s.required_ruby_version = ">= 1.9.1"
|
17
|
-
s.add_dependency "winrm
|
18
|
-
s.add_dependency "
|
17
|
+
s.add_dependency "winrm", "~> 1.3"
|
18
|
+
s.add_dependency "winrm-s", "~> 0.3.0.dev.0"
|
19
|
+
s.add_dependency "nokogiri"
|
19
20
|
|
20
21
|
s.add_development_dependency 'pry'
|
21
22
|
|
@@ -24,3 +25,4 @@ Gem::Specification.new do |s|
|
|
24
25
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
26
|
s.require_paths = ["lib"]
|
26
27
|
end
|
28
|
+
|
@@ -107,12 +107,8 @@ goto install
|
|
107
107
|
:install
|
108
108
|
@rem Install Chef using chef-client MSI installer
|
109
109
|
|
110
|
-
<% url="https://www.opscode.com/chef/download?p=windows&pv=%MACHINE_OS%&m=%MACHINE_ARCH%" -%>
|
111
|
-
<% url += latest_current_windows_chef_version_query -%>
|
112
|
-
@set "REMOTE_SOURCE_MSI_URL=<%= url %>"
|
113
110
|
@set "LOCAL_DESTINATION_MSI_PATH=<%= local_download_path %>"
|
114
111
|
@set "CHEF_CLIENT_MSI_LOG_PATH=%TEMP%\chef-client-msi%RANDOM%.log"
|
115
|
-
@set "FALLBACK_QUERY_STRING=&DownloadContext=PowerShell"
|
116
112
|
|
117
113
|
@rem Clear any pre-existing downloads
|
118
114
|
@echo Checking for existing downloaded package at "%LOCAL_DESTINATION_MSI_PATH%"
|
@@ -129,15 +125,16 @@ goto install
|
|
129
125
|
@rem If there is somehow a name collision, remove pre-existing log
|
130
126
|
@if EXIST "%CHEF_CLIENT_MSI_LOG_PATH%" del /f /q "%CHEF_CLIENT_MSI_LOG_PATH%"
|
131
127
|
|
132
|
-
@echo Attempting to download client package using
|
133
|
-
|
128
|
+
@echo Attempting to download client package using PowerShell if available...
|
129
|
+
@set "REMOTE_SOURCE_MSI_URL=<%= msi_url('%MACHINE_OS%', '%MACHINE_ARCH%', 'PowerShell') %>"
|
130
|
+
@set powershell_download=powershell.exe -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -File <%= bootstrap_directory %>\wget.ps1 "%REMOTE_SOURCE_MSI_URL%" "%LOCAL_DESTINATION_MSI_PATH%"
|
131
|
+
@echo !powershell_download!
|
132
|
+
@call !powershell_download!
|
134
133
|
|
135
|
-
@rem Work around issues found in Windows Server 2012 around job objects not respecting WSMAN memory quotas
|
136
|
-
@rem that cause the MSI download process to exceed the quota even when it is increased by administrators.
|
137
|
-
@rem Retry the download using a more memory-efficient mechanism that only works if PowerShell is available.
|
138
134
|
@set DOWNLOAD_ERROR_STATUS=!ERRORLEVEL!
|
135
|
+
|
139
136
|
@if ERRORLEVEL 1 (
|
140
|
-
@echo Failed
|
137
|
+
@echo Failed PowerShell download with status code !DOWNLOAD_ERROR_STATUS! > "&2"
|
141
138
|
@if !DOWNLOAD_ERROR_STATUS!==0 set DOWNLOAD_ERROR_STATUS=2
|
142
139
|
) else (
|
143
140
|
@rem Sometimes the error level is not set even when the download failed,
|
@@ -146,21 +143,29 @@ cscript /nologo <%= bootstrap_directory %>\wget.vbs /url:"%REMOTE_SOURCE_MSI_URL
|
|
146
143
|
echo Failed download: download completed, but downloaded file not found > "&2"
|
147
144
|
set DOWNLOAD_ERROR_STATUS=2
|
148
145
|
) else (
|
149
|
-
echo Download via
|
146
|
+
echo Download via PowerShell succeeded.
|
150
147
|
)
|
151
148
|
)
|
152
149
|
|
153
150
|
@if NOT %DOWNLOAD_ERROR_STATUS%==0 (
|
154
151
|
@echo Warning: Failed to download "%REMOTE_SOURCE_MSI_URL%" to "%LOCAL_DESTINATION_MSI_PATH%"
|
155
|
-
@echo Warning: Retrying download with
|
152
|
+
@echo Warning: Retrying download with cscript ...
|
156
153
|
|
157
154
|
@if EXIST "%LOCAL_DESTINATION_MSI_PATH%" del /f /q "%LOCAL_DESTINATION_MSI_PATH%"
|
158
155
|
|
159
|
-
@set
|
160
|
-
|
161
|
-
|
156
|
+
@set "REMOTE_SOURCE_MSI_URL=<%= msi_url('%MACHINE_OS%', '%MACHINE_ARCH%') %>"
|
157
|
+
cscript /nologo <%= bootstrap_directory %>\wget.vbs /url:"%REMOTE_SOURCE_MSI_URL%" /path:"%LOCAL_DESTINATION_MSI_PATH%"
|
158
|
+
|
162
159
|
@if NOT ERRORLEVEL 1 (
|
163
|
-
|
160
|
+
@rem Sometimes the error level is not set even when the download failed,
|
161
|
+
@rem so check for the file to be sure it is there.
|
162
|
+
@if NOT EXIST "%LOCAL_DESTINATION_MSI_PATH%" (
|
163
|
+
echo Failed download: download completed, but downloaded file not found > "&2"
|
164
|
+
echo Exiting without bootstrapping due to download failure. > "&2"
|
165
|
+
exit /b 1
|
166
|
+
) else (
|
167
|
+
echo Download via cscript succeeded.
|
168
|
+
)
|
164
169
|
) else (
|
165
170
|
echo Failed to download "%REMOTE_SOURCE_MSI_URL%" with status code !ERRORLEVEL!. > "&2"
|
166
171
|
echo Exiting without bootstrapping due to download failure. > "&2"
|
@@ -183,24 +188,33 @@ cscript /nologo <%= bootstrap_directory %>\wget.vbs /url:"%REMOTE_SOURCE_MSI_URL
|
|
183
188
|
@endlocal
|
184
189
|
|
185
190
|
@echo off
|
191
|
+
|
192
|
+
<% if client_pem -%>
|
193
|
+
> <%= bootstrap_directory %>\client.pem (
|
194
|
+
<%= escape_and_echo(::File.read(::File.expand_path(client_pem))) %>
|
195
|
+
)
|
196
|
+
<% end -%>
|
197
|
+
|
186
198
|
echo Writing validation key...
|
187
199
|
|
200
|
+
<% if validation_key -%>
|
188
201
|
> <%= bootstrap_directory %>\validation.pem (
|
189
|
-
<%= validation_key %>
|
202
|
+
<%= escape_and_echo(validation_key) %>
|
190
203
|
)
|
204
|
+
<% end -%>
|
191
205
|
|
192
206
|
echo Validation key written.
|
193
207
|
@echo on
|
194
208
|
|
195
|
-
<% if @config[:
|
209
|
+
<% if @config[:secret] -%>
|
196
210
|
> <%= bootstrap_directory %>\encrypted_data_bag_secret (
|
197
|
-
<%=
|
211
|
+
<%= secret %>
|
198
212
|
)
|
199
213
|
<% end -%>
|
200
214
|
|
201
|
-
<% unless
|
215
|
+
<% unless trusted_certs_script.empty? -%>
|
202
216
|
mkdir <%= bootstrap_directory %>\trusted_certs
|
203
|
-
<%=
|
217
|
+
<%= trusted_certs_script %>
|
204
218
|
<% end -%>
|
205
219
|
|
206
220
|
<%# Generate Ohai Hints -%>
|
@@ -20,6 +20,8 @@ require 'chef/knife'
|
|
20
20
|
require 'chef/knife/bootstrap'
|
21
21
|
require 'chef/encrypted_data_bag_item'
|
22
22
|
require 'chef/knife/core/windows_bootstrap_context'
|
23
|
+
# Chef 11 PathHelper doesn't have #home
|
24
|
+
#require 'chef/util/path_helper'
|
23
25
|
|
24
26
|
class Chef
|
25
27
|
class Knife
|
@@ -60,16 +62,28 @@ class Chef
|
|
60
62
|
:description => "Avoid a proxy server for the given addresses",
|
61
63
|
:proc => Proc.new { |np| Chef::Config[:knife][:bootstrap_no_proxy] = np }
|
62
64
|
|
65
|
+
# DEPR: Remove this option in Chef 13
|
63
66
|
option :distro,
|
64
67
|
:short => "-d DISTRO",
|
65
68
|
:long => "--distro DISTRO",
|
66
|
-
:description => "Bootstrap a distro using a template",
|
67
|
-
:
|
69
|
+
:description => "Bootstrap a distro using a template. [DEPRECATED] Use --bootstrap-template option instead.",
|
70
|
+
:proc => Proc.new { |v|
|
71
|
+
Chef::Log.warn("[DEPRECATED] -d / --distro option is deprecated. Use --bootstrap-template option instead.")
|
72
|
+
v
|
73
|
+
}
|
74
|
+
|
75
|
+
option :bootstrap_template,
|
76
|
+
:long => "--bootstrap-template TEMPLATE",
|
77
|
+
: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."
|
68
78
|
|
79
|
+
# DEPR: Remove this option in Chef 13
|
69
80
|
option :template_file,
|
70
81
|
:long => "--template-file TEMPLATE",
|
71
|
-
:description => "Full path to location of template to use",
|
72
|
-
:
|
82
|
+
:description => "Full path to location of template to use. [DEPRECATED] Use --bootstrap-template option instead.",
|
83
|
+
:proc => Proc.new { |v|
|
84
|
+
Chef::Log.warn("[DEPRECATED] --template-file option is deprecated. Use --bootstrap-template option instead.")
|
85
|
+
v
|
86
|
+
}
|
73
87
|
|
74
88
|
option :run_list,
|
75
89
|
:short => "-r RUN_LIST",
|
@@ -78,6 +92,15 @@ class Chef
|
|
78
92
|
:proc => lambda { |o| o.split(",") },
|
79
93
|
:default => []
|
80
94
|
|
95
|
+
option :hint,
|
96
|
+
:long => "--hint HINT_NAME[=HINT_FILE]",
|
97
|
+
:description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
|
98
|
+
:proc => Proc.new { |h|
|
99
|
+
Chef::Config[:knife][:hints] ||= Hash.new
|
100
|
+
name, path = h.split("=")
|
101
|
+
Chef::Config[:knife][:hints][name] = path ? Chef::JSONCompat.parse(::File.read(path)) : Hash.new
|
102
|
+
}
|
103
|
+
|
81
104
|
option :first_boot_attributes,
|
82
105
|
:short => "-j JSON_ATTRIBS",
|
83
106
|
:long => "--json-attributes",
|
@@ -85,12 +108,14 @@ class Chef
|
|
85
108
|
:proc => lambda { |o| JSON.parse(o) },
|
86
109
|
:default => {}
|
87
110
|
|
111
|
+
# Mismatch between option 'encrypted_data_bag_secret' and it's long value '--secret' is by design for compatibility
|
88
112
|
option :encrypted_data_bag_secret,
|
89
113
|
:short => "-s SECRET",
|
90
114
|
:long => "--secret ",
|
91
115
|
:description => "The secret key to use to decrypt data bag item values. Will be rendered on the node at c:/chef/encrypted_data_bag_secret and set in the rendered client config.",
|
92
116
|
:default => false
|
93
117
|
|
118
|
+
# Mismatch between option 'encrypted_data_bag_secret_file' and it's long value '--secret-file' is by design for compatibility
|
94
119
|
option :encrypted_data_bag_secret_file,
|
95
120
|
:long => "--secret-file SECRET_FILE",
|
96
121
|
:description => "A file containing the secret key to use to encrypt data bag item values. Will be rendered on the node at c:/chef/encrypted_data_bag_secret and set in the rendered client config."
|
@@ -114,23 +139,51 @@ class Chef
|
|
114
139
|
:long => "--[no-]node-verify-api-cert",
|
115
140
|
:description => "Verify the SSL cert for HTTPS requests to the Chef server API.",
|
116
141
|
:boolean => true
|
142
|
+
|
143
|
+
option :msi_url,
|
144
|
+
:short => "-u URL",
|
145
|
+
:long => "--msi_url URL",
|
146
|
+
:description => "Location of the Chef Client MSI. The default templates will prefer to download from this location. The MSI will be downloaded from chef.io if not provided.",
|
147
|
+
:default => ''
|
148
|
+
|
149
|
+
option :install_as_service,
|
150
|
+
:long => "--install-as-service",
|
151
|
+
:description => "Install chef-client as service in windows machine",
|
152
|
+
:default => false
|
117
153
|
end
|
118
154
|
end
|
119
155
|
|
120
|
-
|
156
|
+
def default_bootstrap_template
|
157
|
+
"windows-chef-client-msi"
|
158
|
+
end
|
159
|
+
|
160
|
+
def bootstrap_template
|
161
|
+
# The order here is important. We want to check if we have the new Chef 12 option is set first.
|
162
|
+
# Knife cloud plugins unfortunately all set a default option for the :distro so it should be at
|
163
|
+
# the end.
|
164
|
+
config[:bootstrap_template] || config[:template_file] || config[:distro] || default_bootstrap_template
|
165
|
+
end
|
166
|
+
|
167
|
+
# TODO: This should go away when CHEF-2193 is fixed
|
121
168
|
def load_template(template=nil)
|
122
169
|
# Are we bootstrapping using an already shipped template?
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
bootstrap_files << Gem.find_files(File.join("chef","knife","bootstrap","#{config[:distro]}.erb"))
|
131
|
-
bootstrap_files.flatten!
|
170
|
+
|
171
|
+
template = bootstrap_template
|
172
|
+
|
173
|
+
# Use the template directly if it's a path to an actual file
|
174
|
+
if File.exists?(template)
|
175
|
+
Chef::Log.debug("Using the specified bootstrap template: #{File.dirname(template)}")
|
176
|
+
return IO.read(template).chomp
|
132
177
|
end
|
133
178
|
|
179
|
+
# Otherwise search the template directories until we find the right one
|
180
|
+
bootstrap_files = []
|
181
|
+
bootstrap_files << File.join(File.dirname(__FILE__), 'bootstrap/templates', "#{template}.erb")
|
182
|
+
bootstrap_files << File.join(Knife.chef_config_dir, "bootstrap", "#{template}.erb") if Chef::Knife.chef_config_dir
|
183
|
+
::Knife::Windows::PathHelper.all_homes('.chef', 'bootstrap', "#{template}.erb") { |p| bootstrap_files << p }
|
184
|
+
bootstrap_files << Gem.find_files(File.join("chef","knife","bootstrap","#{template}.erb"))
|
185
|
+
bootstrap_files.flatten!
|
186
|
+
|
134
187
|
template = Array(bootstrap_files).find do |bootstrap_template|
|
135
188
|
Chef::Log.debug("Looking for bootstrap template in #{File.dirname(bootstrap_template)}")
|
136
189
|
::File.exists?(bootstrap_template)
|
@@ -146,15 +199,24 @@ class Chef
|
|
146
199
|
IO.read(template).chomp
|
147
200
|
end
|
148
201
|
|
202
|
+
def bootstrap_context
|
203
|
+
@bootstrap_context ||= Knife::Core::WindowsBootstrapContext.new(config, config[:run_list], Chef::Config)
|
204
|
+
end
|
205
|
+
|
149
206
|
def render_template(template=nil)
|
150
|
-
if config[:
|
151
|
-
config[:
|
207
|
+
if config[:secret_file]
|
208
|
+
config[:secret] = Chef::EncryptedDataBagItem.load_secret(config[:secret_file])
|
152
209
|
end
|
153
|
-
|
154
|
-
Erubis::Eruby.new(template).evaluate(context)
|
210
|
+
Erubis::Eruby.new(template).evaluate(bootstrap_context)
|
155
211
|
end
|
156
212
|
|
157
213
|
def bootstrap(proto=nil)
|
214
|
+
if Chef::Config[:knife][:encrypted_data_bag_secret_file] || Chef::Config[:knife][:encrypted_data_bag_secret]
|
215
|
+
warn_chef_config_secret_key
|
216
|
+
config[:secret_file] ||= Chef::Config[:knife][:encrypted_data_bag_secret_file]
|
217
|
+
config[:secret] ||= Chef::Config[:knife][:encrypted_data_bag_secret]
|
218
|
+
end
|
219
|
+
|
158
220
|
validate_name_args!
|
159
221
|
|
160
222
|
@node_name = Array(@name_args).first
|
@@ -163,13 +225,32 @@ class Chef
|
|
163
225
|
|
164
226
|
STDOUT.sync = STDERR.sync = true
|
165
227
|
|
228
|
+
if (Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key])))
|
229
|
+
if Chef::VERSION.split('.').first.to_i == 11
|
230
|
+
ui.error("Unable to find validation key. Please verify your configuration file for validation_key config value.")
|
231
|
+
exit 1
|
232
|
+
end
|
233
|
+
|
234
|
+
unless locate_config_value(:chef_node_name)
|
235
|
+
ui.error("You must pass a node name with -N when bootstrapping with user credentials")
|
236
|
+
exit 1
|
237
|
+
end
|
238
|
+
|
239
|
+
client_builder.run
|
240
|
+
bootstrap_context.client_pem = client_builder.client_path
|
241
|
+
else
|
242
|
+
ui.info("Doing old-style registration with the validation key at #{Chef::Config[:validation_key]}...")
|
243
|
+
ui.info("Delete your validation key in order to use your user credentials instead")
|
244
|
+
ui.info("")
|
245
|
+
end
|
246
|
+
|
166
247
|
wait_for_remote_response( config[:auth_timeout].to_i )
|
167
248
|
ui.info("Bootstrapping Chef on #{ui.color(@node_name, :bold)}")
|
168
249
|
# create a bootstrap.bat file on the node
|
169
250
|
# we have to run the remote commands in 2047 char chunks
|
170
|
-
create_bootstrap_bat_command do |command_chunk
|
251
|
+
create_bootstrap_bat_command do |command_chunk|
|
171
252
|
begin
|
172
|
-
render_command_result = run_command(
|
253
|
+
render_command_result = run_command(command_chunk)
|
173
254
|
ui.error("Batch render command returned #{render_command_result}") if render_command_result != 0
|
174
255
|
render_command_result
|
175
256
|
rescue SystemExit => e
|
@@ -180,6 +261,7 @@ class Chef
|
|
180
261
|
# execute the bootstrap.bat file
|
181
262
|
bootstrap_command_result = run_command(bootstrap_command)
|
182
263
|
ui.error("Bootstrap command returned #{bootstrap_command_result}") if bootstrap_command_result != 0
|
264
|
+
|
183
265
|
bootstrap_command_result
|
184
266
|
end
|
185
267
|
|
@@ -193,20 +275,48 @@ class Chef
|
|
193
275
|
@bootstrap_command ||= "cmd.exe /C #{bootstrap_bat_file}"
|
194
276
|
end
|
195
277
|
|
196
|
-
def
|
197
|
-
|
278
|
+
def bootstrap_render_banner_command(chunk_num)
|
279
|
+
"cmd.exe /C echo Rendering #{bootstrap_bat_file} chunk #{chunk_num}"
|
280
|
+
end
|
281
|
+
|
282
|
+
def escape_windows_batch_characters(line)
|
283
|
+
# TODO: The commands are going to get redirected - do we need to escape &?
|
284
|
+
line.gsub!(/[(<|>)^]/).each{|m| "^#{m}"}
|
285
|
+
end
|
286
|
+
|
287
|
+
def create_bootstrap_bat_command()
|
198
288
|
chunk_num = 0
|
289
|
+
bootstrap_bat = ""
|
290
|
+
banner = bootstrap_render_banner_command(chunk_num += 1)
|
199
291
|
render_template(load_template(config[:bootstrap_template])).each_line do |line|
|
200
|
-
|
201
|
-
|
202
|
-
#
|
203
|
-
|
204
|
-
|
205
|
-
|
292
|
+
escape_windows_batch_characters(line)
|
293
|
+
# We are guaranteed to have a prefix "banner" command that echo's chunk number. We can
|
294
|
+
# confidently prefix every actual command with &&.
|
295
|
+
# TODO: Why does ^\n&& work directly through the commandline but not through SOAP?
|
296
|
+
render_line = " && >> #{bootstrap_bat_file} (echo.#{line.chomp.strip})"
|
297
|
+
# Windows commands are limited to 8191 characters for machines running XP or higher but
|
298
|
+
# this includes the length of environment variables after they have been expanded.
|
299
|
+
# Since we don't actually know how long %TEMP% (and it's used twice - once in the banner
|
300
|
+
# and once in every command redirection), we simply guess and set the max to 5000.
|
301
|
+
# TODO: When a more accurate method is available, fix this.
|
302
|
+
if bootstrap_bat.length + render_line.length + banner.length > 5000
|
303
|
+
# Can't fit it into this chunk? - flush (if necessary) and then try.
|
304
|
+
# Do this first because banner.length might change (e.g. due to an extra digit) and
|
305
|
+
# prevent a fit.
|
306
|
+
unless bootstrap_bat.empty?
|
307
|
+
yield banner + bootstrap_bat
|
308
|
+
bootstrap_bat = ""
|
309
|
+
banner = bootstrap_render_banner_command(chunk_num += 1)
|
310
|
+
end
|
311
|
+
# Will this ever fit?
|
312
|
+
if render_line.length + banner.length > 5000
|
313
|
+
raise "Command in bootstrap template too long by #{render_line.length + banner.length - 5000} characters : #{line}"
|
314
|
+
end
|
206
315
|
end
|
207
|
-
bootstrap_bat <<
|
316
|
+
bootstrap_bat << render_line
|
208
317
|
end
|
209
|
-
|
318
|
+
raise "Bootstrap template was empty! Check #{config[:bootstrap_template]}" if bootstrap_bat.empty?
|
319
|
+
yield banner + bootstrap_bat
|
210
320
|
end
|
211
321
|
|
212
322
|
def bootstrap_bat_file
|
@@ -215,7 +325,21 @@ class Chef
|
|
215
325
|
|
216
326
|
def locate_config_value(key)
|
217
327
|
key = key.to_sym
|
218
|
-
Chef::Config[:knife][key]
|
328
|
+
config[key] || Chef::Config[:knife][key]
|
329
|
+
end
|
330
|
+
|
331
|
+
def warn_chef_config_secret_key
|
332
|
+
ui.info "* " * 40
|
333
|
+
ui.warn(<<-WARNING)
|
334
|
+
\nSpecifying the encrypted data bag secret key using an 'encrypted_data_bag_secret'
|
335
|
+
entry in 'knife.rb' is deprecated. Please use the '--secret' or '--secret-file'
|
336
|
+
options of this command instead.
|
337
|
+
|
338
|
+
#{ui.color('IMPORTANT:', :red, :bold)} In a future version of Chef, this
|
339
|
+
behavior will be removed and any 'encrypted_data_bag_secret' entries in
|
340
|
+
'knife.rb' will be ignored completely.
|
341
|
+
WARNING
|
342
|
+
ui.info "* " * 40
|
219
343
|
end
|
220
344
|
end
|
221
345
|
end
|