knife-windows 1.9.0 → 1.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +10 -6
- data/CHANGELOG.md +4 -0
- data/appveyor.yml +3 -3
- data/ci.gemfile +1 -1
- data/knife-windows.gemspec +1 -1
- data/lib/chef/knife/core/windows_bootstrap_context.rb +2 -2
- data/lib/knife-windows/version.rb +1 -1
- data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +1 -1
- data/spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt +1 -1
- data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +1 -1
- data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt +1 -1
- data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_13_client.txt +332 -0
- data/spec/functional/bootstrap_download_spec.rb +13 -10
- data/spec/spec_helper.rb +23 -14
- data/spec/unit/knife/bootstrap_options_spec.rb +11 -6
- data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +16 -29
- data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +1 -1
- metadata +5 -7
- data/features/knife_help.feature +0 -20
- data/features/support/env.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 52d0760c6f7d545c38caddf396afcc46e6eb139a83c6bc08c4176971234b3a25
|
4
|
+
data.tar.gz: 10864d611e1fbc3f2aff60b25d16557582069bad004a2c7c3885346f6486d642
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 953c7ad5d2d9bf66b79385cf6d2dae322704a5fae7dba12f502dca1b71fbfef7648e5a0b8a6558b3ca2507456c16f18b164147f47fcb114c3d7d051109cc1d6b
|
7
|
+
data.tar.gz: e6e50af2ca602f30130701984c19d365531c5aacfa24232ad606300c678fc1a279eafa5987416dda186788ac2a489ff0a666dcc0f59719bb057331283946b6c5
|
data/.travis.yml
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
language: ruby
|
2
|
+
cache: bundler
|
3
|
+
dist: trusty
|
4
|
+
sudo: false
|
2
5
|
|
3
6
|
rvm:
|
4
|
-
- 2.
|
5
|
-
- 2.
|
7
|
+
- 2.3.4
|
8
|
+
- 2.4.1
|
9
|
+
- ruby-head
|
6
10
|
|
7
11
|
before_install:
|
8
12
|
- gem install bundler
|
@@ -11,14 +15,14 @@ gemfile: ci.gemfile
|
|
11
15
|
|
12
16
|
env:
|
13
17
|
- CHEF_VERSION="master"
|
14
|
-
- CHEF_VERSION="~>
|
18
|
+
- CHEF_VERSION="~> 13.0"
|
15
19
|
|
16
20
|
matrix:
|
17
21
|
exclude:
|
18
|
-
- rvm: 2.
|
22
|
+
- rvm: 2.3.4
|
19
23
|
env: CHEF_VERSION="master"
|
20
|
-
|
21
|
-
|
24
|
+
allow_failures:
|
25
|
+
- rvm: ruby-head
|
22
26
|
|
23
27
|
branches:
|
24
28
|
only:
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# knife-windows Change Log
|
2
2
|
|
3
|
+
## Release 1.9.1 (2018-03-07)
|
4
|
+
|
5
|
+
* [knife-windows #444](https://github.com/chef/knife-windows/pull/444) Fixes issue when bootstrapping windows systems failing with the message: The input line is too long.
|
6
|
+
|
3
7
|
## Release 1.9.0
|
4
8
|
|
5
9
|
* [knife-windows #416](https://github.com/chef/knife-windows/pull/416) Add concurrency support via the `--concurrency` flag
|
data/appveyor.yml
CHANGED
@@ -8,10 +8,10 @@ environment:
|
|
8
8
|
bundle_gemfile: ci.gemfile
|
9
9
|
|
10
10
|
matrix:
|
11
|
-
- ruby_version: "
|
12
|
-
chef_version: "~>
|
11
|
+
- ruby_version: "24"
|
12
|
+
chef_version: "~> 13.0"
|
13
13
|
|
14
|
-
- ruby_version: "
|
14
|
+
- ruby_version: "24"
|
15
15
|
chef_version: "master"
|
16
16
|
|
17
17
|
clone_folder: c:\projects\knife-windows
|
data/ci.gemfile
CHANGED
@@ -5,6 +5,7 @@ gemspec
|
|
5
5
|
|
6
6
|
if ENV['CHEF_VERSION'] == 'master'
|
7
7
|
gem 'chef', github: 'chef/chef'
|
8
|
+
gem 'ohai', github: 'chef/ohai'
|
8
9
|
else
|
9
10
|
gem 'chef', ENV['CHEF_VERSION']
|
10
11
|
end
|
@@ -13,4 +14,3 @@ gem "rspec", '~> 3.0'
|
|
13
14
|
gem "ruby-wmi"
|
14
15
|
gem "httpclient"
|
15
16
|
gem 'rake'
|
16
|
-
gem "rack", "< 2.0" # 2.0 requires Ruby 2.2+
|
data/knife-windows.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.add_development_dependency 'pry'
|
21
21
|
|
22
22
|
s.files = `git ls-files`.split("\n")
|
23
|
-
s.test_files = `git ls-files --
|
23
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
24
24
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
25
|
s.require_paths = ["lib"]
|
26
26
|
end
|
@@ -170,7 +170,7 @@ CONFIG
|
|
170
170
|
|
171
171
|
def start_chef
|
172
172
|
bootstrap_environment_option = bootstrap_environment.nil? ? '' : " -E #{bootstrap_environment}"
|
173
|
-
start_chef = "SET \"PATH=%
|
173
|
+
start_chef = "SET \"PATH=%SystemRoot%\\system32;%SystemRoot%;%SystemRoot%\\System32\\Wbem;%SYSTEMROOT%\\System32\\WindowsPowerShell\\v1.0\\;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin\"\n"
|
174
174
|
start_chef << "chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json#{bootstrap_environment_option}\n"
|
175
175
|
end
|
176
176
|
|
@@ -394,4 +394,4 @@ EOH
|
|
394
394
|
end
|
395
395
|
end
|
396
396
|
end
|
397
|
-
end
|
397
|
+
end
|
@@ -215,6 +215,6 @@ echo.log_location STDOUT
|
|
215
215
|
)
|
216
216
|
|
217
217
|
@echo Starting chef to bootstrap the node...
|
218
|
-
SET "PATH=%
|
218
|
+
SET "PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;C:\ruby\bin;C:\opscode\chef\bin;C:\opscode\chef\embedded\bin"
|
219
219
|
chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json
|
220
220
|
|
@@ -215,6 +215,6 @@ echo.log_location STDOUT
|
|
215
215
|
)
|
216
216
|
|
217
217
|
@echo Starting chef to bootstrap the node...
|
218
|
-
SET "PATH=%
|
218
|
+
SET "PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;C:\ruby\bin;C:\opscode\chef\bin;C:\opscode\chef\embedded\bin"
|
219
219
|
chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json
|
220
220
|
|
@@ -327,6 +327,6 @@ echo.log_location STDOUT
|
|
327
327
|
)
|
328
328
|
|
329
329
|
@echo Starting chef to bootstrap the node...
|
330
|
-
SET "PATH=%
|
330
|
+
SET "PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;C:\ruby\bin;C:\opscode\chef\bin;C:\opscode\chef\embedded\bin"
|
331
331
|
chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json
|
332
332
|
|
@@ -327,6 +327,6 @@ echo.log_location STDOUT
|
|
327
327
|
)
|
328
328
|
|
329
329
|
@echo Starting chef to bootstrap the node...
|
330
|
-
SET "PATH=%
|
330
|
+
SET "PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;C:\ruby\bin;C:\opscode\chef\bin;C:\opscode\chef\embedded\bin"
|
331
331
|
chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json
|
332
332
|
|
@@ -0,0 +1,332 @@
|
|
1
|
+
@rem
|
2
|
+
@rem Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
+
@rem Copyright:: Copyright (c) 2011 Opscode, Inc.
|
4
|
+
@rem License:: Apache License, Version 2.0
|
5
|
+
@rem
|
6
|
+
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
@rem you may not use this file except in compliance with the License.
|
8
|
+
@rem You may obtain a copy of the License at
|
9
|
+
@rem
|
10
|
+
@rem http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
@rem
|
12
|
+
@rem Unless required by applicable law or agreed to in writing, software
|
13
|
+
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
@rem See the License for the specific language governing permissions and
|
16
|
+
@rem limitations under the License.
|
17
|
+
@rem
|
18
|
+
|
19
|
+
@rem Use delayed environment expansion so that ERRORLEVEL can be evaluated with the
|
20
|
+
@rem !ERRORLEVEL! syntax which evaluates at execution of the line of script, not when
|
21
|
+
@rem the line is read. See help for the /E switch from cmd.exe /? .
|
22
|
+
@setlocal ENABLEDELAYEDEXPANSION
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
@set BOOTSTRAP_DIRECTORY=C:\chef
|
27
|
+
@echo Checking for existing directory "%BOOTSTRAP_DIRECTORY%"...
|
28
|
+
@if NOT EXIST %BOOTSTRAP_DIRECTORY% (
|
29
|
+
@echo Existing directory not found, creating.
|
30
|
+
@mkdir %BOOTSTRAP_DIRECTORY%
|
31
|
+
) else (
|
32
|
+
@echo Existing directory found, skipping creation.
|
33
|
+
)
|
34
|
+
|
35
|
+
> C:\chef\wget.vbs (
|
36
|
+
echo.url = WScript.Arguments.Named^("url"^)
|
37
|
+
echo.path = WScript.Arguments.Named^("path"^)
|
38
|
+
echo.proxy = null
|
39
|
+
echo.'* Vaguely attempt to handle file:// scheme urls by url unescaping and switching all
|
40
|
+
echo.'* / into . Also assume that file:/// is a local absolute path and that file://^<foo^>
|
41
|
+
echo.'* is possibly a network file path.
|
42
|
+
echo.If InStr^(url, "file://"^) = 1 Then
|
43
|
+
echo.url = Unescape^(url^)
|
44
|
+
echo.If InStr^(url, "file:///"^) = 1 Then
|
45
|
+
echo.sourcePath = Mid^(url, Len^("file:///"^) + 1^)
|
46
|
+
echo.Else
|
47
|
+
echo.sourcePath = Mid^(url, Len^("file:"^) + 1^)
|
48
|
+
echo.End If
|
49
|
+
echo.sourcePath = Replace^(sourcePath, "/", "\"^)
|
50
|
+
echo.
|
51
|
+
echo.Set objFSO = CreateObject^("Scripting.FileSystemObject"^)
|
52
|
+
echo.If objFSO.Fileexists^(path^) Then objFSO.DeleteFile path
|
53
|
+
echo.objFSO.CopyFile sourcePath, path, true
|
54
|
+
echo.Set objFSO = Nothing
|
55
|
+
echo.
|
56
|
+
echo.Else
|
57
|
+
echo.Set objXMLHTTP = CreateObject^("MSXML2.ServerXMLHTTP"^)
|
58
|
+
echo.Set wshShell = CreateObject^( "WScript.Shell" ^)
|
59
|
+
echo.Set objUserVariables = wshShell.Environment^("USER"^)
|
60
|
+
echo.
|
61
|
+
echo.rem http proxy is optional
|
62
|
+
echo.rem attempt to read from HTTP_PROXY env var first
|
63
|
+
echo.On Error Resume Next
|
64
|
+
echo.
|
65
|
+
echo.If NOT ^(objUserVariables^("HTTP_PROXY"^) = ""^) Then
|
66
|
+
echo.proxy = objUserVariables^("HTTP_PROXY"^)
|
67
|
+
echo.
|
68
|
+
echo.rem fall back to named arg
|
69
|
+
echo.ElseIf NOT ^(WScript.Arguments.Named^("proxy"^) = ""^) Then
|
70
|
+
echo.proxy = WScript.Arguments.Named^("proxy"^)
|
71
|
+
echo.End If
|
72
|
+
echo.
|
73
|
+
echo.If NOT isNull^(proxy^) Then
|
74
|
+
echo.rem setProxy method is only available on ServerXMLHTTP 6.0+
|
75
|
+
echo.Set objXMLHTTP = CreateObject^("MSXML2.ServerXMLHTTP.6.0"^)
|
76
|
+
echo.objXMLHTTP.setProxy 2, proxy
|
77
|
+
echo.End If
|
78
|
+
echo.
|
79
|
+
echo.On Error Goto 0
|
80
|
+
echo.
|
81
|
+
echo.objXMLHTTP.open "GET", url, false
|
82
|
+
echo.objXMLHTTP.send^(^)
|
83
|
+
echo.If objXMLHTTP.Status = 200 Then
|
84
|
+
echo.Set objADOStream = CreateObject^("ADODB.Stream"^)
|
85
|
+
echo.objADOStream.Open
|
86
|
+
echo.objADOStream.Type = 1
|
87
|
+
echo.objADOStream.Write objXMLHTTP.ResponseBody
|
88
|
+
echo.objADOStream.Position = 0
|
89
|
+
echo.Set objFSO = Createobject^("Scripting.FileSystemObject"^)
|
90
|
+
echo.If objFSO.Fileexists^(path^) Then objFSO.DeleteFile path
|
91
|
+
echo.Set objFSO = Nothing
|
92
|
+
echo.objADOStream.SaveToFile path
|
93
|
+
echo.objADOStream.Close
|
94
|
+
echo.Set objADOStream = Nothing
|
95
|
+
echo.End If
|
96
|
+
echo.Set objXMLHTTP = Nothing
|
97
|
+
echo.End If
|
98
|
+
|
99
|
+
)
|
100
|
+
|
101
|
+
> C:\chef\wget.ps1 (
|
102
|
+
echo.param^(
|
103
|
+
echo. [String] $remoteUrl,
|
104
|
+
echo. [String] $localPath
|
105
|
+
echo.^)
|
106
|
+
echo.
|
107
|
+
echo.$ProxyUrl = $env:http_proxy;
|
108
|
+
echo.$webClient = new-object System.Net.WebClient;
|
109
|
+
echo.
|
110
|
+
echo.if ^($ProxyUrl -ne ''^) {
|
111
|
+
echo. $WebProxy = New-Object System.Net.WebProxy^($ProxyUrl,$true^)
|
112
|
+
echo. $WebClient.Proxy = $WebProxy
|
113
|
+
echo.}
|
114
|
+
echo.
|
115
|
+
echo.$webClient.DownloadFile^($remoteUrl, $localPath^);
|
116
|
+
|
117
|
+
)
|
118
|
+
|
119
|
+
@rem Determine the version and the architecture
|
120
|
+
|
121
|
+
@FOR /F "usebackq tokens=1-8 delims=.[] " %%A IN (`ver`) DO (
|
122
|
+
@set WinMajor=%%D
|
123
|
+
@set WinMinor=%%E
|
124
|
+
@set WinBuild=%%F
|
125
|
+
)
|
126
|
+
|
127
|
+
@echo Detected Windows Version %WinMajor%.%WinMinor% Build %WinBuild%
|
128
|
+
|
129
|
+
@set LATEST_OS_VERSION_MAJOR=6
|
130
|
+
@set LATEST_OS_VERSION_MINOR=3
|
131
|
+
|
132
|
+
@if /i %WinMajor% GTR %LATEST_OS_VERSION_MAJOR% goto VersionUnknown
|
133
|
+
@if /i %WinMajor% EQU %LATEST_OS_VERSION_MAJOR% (
|
134
|
+
@if /i %WinMinor% GTR %LATEST_OS_VERSION_MINOR% goto VersionUnknown
|
135
|
+
)
|
136
|
+
|
137
|
+
goto Version%WinMajor%.%WinMinor%
|
138
|
+
|
139
|
+
:VersionUnknown
|
140
|
+
@rem If this is an unknown version of windows set the default
|
141
|
+
@set MACHINE_OS=2008r2
|
142
|
+
@echo Warning: Unknown version of Windows, assuming default of Windows %MACHINE_OS%
|
143
|
+
goto architecture_select
|
144
|
+
|
145
|
+
:Version6.0
|
146
|
+
@set MACHINE_OS=2008
|
147
|
+
goto architecture_select
|
148
|
+
|
149
|
+
:Version5.2
|
150
|
+
@set MACHINE_OS=2003r2
|
151
|
+
goto architecture_select
|
152
|
+
|
153
|
+
:Version6.1
|
154
|
+
@set MACHINE_OS=2008r2
|
155
|
+
goto architecture_select
|
156
|
+
|
157
|
+
:Version6.2
|
158
|
+
@set MACHINE_OS=2012
|
159
|
+
goto architecture_select
|
160
|
+
|
161
|
+
@rem Currently Windows Server 2012 R2 is treated as equivalent to Windows Server 2012
|
162
|
+
:Version6.3
|
163
|
+
goto Version6.2
|
164
|
+
|
165
|
+
:architecture_select
|
166
|
+
goto Architecture%PROCESSOR_ARCHITEW6432%
|
167
|
+
|
168
|
+
:Architecture
|
169
|
+
goto Architecture%PROCESSOR_ARCHITECTURE%
|
170
|
+
|
171
|
+
@rem If this is an unknown architecture set the default
|
172
|
+
@set MACHINE_ARCH=i686
|
173
|
+
goto install
|
174
|
+
|
175
|
+
:Architecturex86
|
176
|
+
@set MACHINE_ARCH=i686
|
177
|
+
goto install
|
178
|
+
|
179
|
+
:Architectureamd64
|
180
|
+
@set MACHINE_ARCH=x86_64
|
181
|
+
goto install
|
182
|
+
|
183
|
+
:install
|
184
|
+
@rem If user has provided the custom installation command for chef-client then execute it
|
185
|
+
@rem Install Chef using chef-client MSI installer
|
186
|
+
|
187
|
+
@set "LOCAL_DESTINATION_MSI_PATH=%TEMP%\chef-client-latest.msi"
|
188
|
+
@set "CHEF_CLIENT_MSI_LOG_PATH=%TEMP%\chef-client-msi%RANDOM%.log"
|
189
|
+
|
190
|
+
@rem Clear any pre-existing downloads
|
191
|
+
@echo Checking for existing downloaded package at "%LOCAL_DESTINATION_MSI_PATH%"
|
192
|
+
@if EXIST "%LOCAL_DESTINATION_MSI_PATH%" (
|
193
|
+
@echo Found existing downloaded package, deleting.
|
194
|
+
@del /f /q "%LOCAL_DESTINATION_MSI_PATH%"
|
195
|
+
@if ERRORLEVEL 1 (
|
196
|
+
echo Warning: Failed to delete pre-existing package with status code !ERRORLEVEL! > "&2"
|
197
|
+
)
|
198
|
+
) else (
|
199
|
+
echo No existing downloaded packages to delete.
|
200
|
+
)
|
201
|
+
|
202
|
+
@rem If there is somehow a name collision, remove pre-existing log
|
203
|
+
@if EXIST "%CHEF_CLIENT_MSI_LOG_PATH%" del /f /q "%CHEF_CLIENT_MSI_LOG_PATH%"
|
204
|
+
|
205
|
+
@echo Attempting to download client package using PowerShell if available...
|
206
|
+
@set "REMOTE_SOURCE_MSI_URL=https://www.chef.io/chef/download?p=windows&pv=%MACHINE_OS%&m=%MACHINE_ARCH%&DownloadContext=PowerShell&v=13"
|
207
|
+
@set powershell_download=powershell.exe -ExecutionPolicy Unrestricted -NoProfile -NonInteractive -File C:\chef\wget.ps1 "%REMOTE_SOURCE_MSI_URL%" "%LOCAL_DESTINATION_MSI_PATH%"
|
208
|
+
@echo !powershell_download!
|
209
|
+
@call !powershell_download!
|
210
|
+
|
211
|
+
@set DOWNLOAD_ERROR_STATUS=!ERRORLEVEL!
|
212
|
+
|
213
|
+
@if ERRORLEVEL 1 (
|
214
|
+
@echo Failed PowerShell download with status code !DOWNLOAD_ERROR_STATUS! > "&2"
|
215
|
+
@if !DOWNLOAD_ERROR_STATUS!==0 set DOWNLOAD_ERROR_STATUS=2
|
216
|
+
) else (
|
217
|
+
@rem Sometimes the error level is not set even when the download failed,
|
218
|
+
@rem so check for the file to be sure it is there -- if it is not, we will retry
|
219
|
+
@if NOT EXIST "%LOCAL_DESTINATION_MSI_PATH%" (
|
220
|
+
echo Failed download: download completed, but downloaded file not found > "&2"
|
221
|
+
set DOWNLOAD_ERROR_STATUS=2
|
222
|
+
) else (
|
223
|
+
echo Download via PowerShell succeeded.
|
224
|
+
)
|
225
|
+
)
|
226
|
+
|
227
|
+
@if NOT %DOWNLOAD_ERROR_STATUS%==0 (
|
228
|
+
@echo Warning: Failed to download "%REMOTE_SOURCE_MSI_URL%" to "%LOCAL_DESTINATION_MSI_PATH%"
|
229
|
+
@echo Warning: Retrying download with cscript ...
|
230
|
+
|
231
|
+
@if EXIST "%LOCAL_DESTINATION_MSI_PATH%" del /f /q "%LOCAL_DESTINATION_MSI_PATH%"
|
232
|
+
|
233
|
+
@set "REMOTE_SOURCE_MSI_URL=https://www.chef.io/chef/download?p=windows&pv=%MACHINE_OS%&m=%MACHINE_ARCH%&v=13"
|
234
|
+
cscript /nologo C:\chef\wget.vbs /url:"%REMOTE_SOURCE_MSI_URL%" /path:"%LOCAL_DESTINATION_MSI_PATH%"
|
235
|
+
|
236
|
+
@if NOT ERRORLEVEL 1 (
|
237
|
+
@rem Sometimes the error level is not set even when the download failed,
|
238
|
+
@rem so check for the file to be sure it is there.
|
239
|
+
@if NOT EXIST "%LOCAL_DESTINATION_MSI_PATH%" (
|
240
|
+
echo Failed download: download completed, but downloaded file not found > "&2"
|
241
|
+
echo Exiting without bootstrapping due to download failure. > "&2"
|
242
|
+
exit /b 1
|
243
|
+
) else (
|
244
|
+
echo Download via cscript succeeded.
|
245
|
+
)
|
246
|
+
) else (
|
247
|
+
echo Failed to download "%REMOTE_SOURCE_MSI_URL%" with status code !ERRORLEVEL!. > "&2"
|
248
|
+
echo Exiting without bootstrapping due to download failure. > "&2"
|
249
|
+
exit /b 1
|
250
|
+
)
|
251
|
+
)
|
252
|
+
|
253
|
+
@echo Installing downloaded client package...
|
254
|
+
|
255
|
+
msiexec /qn /log "%CHEF_CLIENT_MSI_LOG_PATH%" /i "%LOCAL_DESTINATION_MSI_PATH%"
|
256
|
+
@set MSIERRORCODE=!ERRORLEVEL!
|
257
|
+
@if ERRORLEVEL 1 (
|
258
|
+
@echo WARNING: Failed to install Chef Client MSI package in remote context with status code !MSIERRORCODE!.
|
259
|
+
@echo WARNING: This may be due to a defect in operating system update KB2918614: http://support.microsoft.com/kb/2918614
|
260
|
+
@set OLDLOGLOCATION="%CHEF_CLIENT_MSI_LOG_PATH%-fail.log"
|
261
|
+
@move "%CHEF_CLIENT_MSI_LOG_PATH%" "!OLDLOGLOCATION!" > NUL
|
262
|
+
@echo WARNING: Saving installation log of failure at !OLDLOGLOCATION!
|
263
|
+
@echo WARNING: Retrying installation with local context...
|
264
|
+
@schtasks /create /f /sc once /st 00:00:00 /tn chefclientbootstraptask /ru SYSTEM /rl HIGHEST /tr "cmd /c msiexec /qn /log '%CHEF_CLIENT_MSI_LOG_PATH%' /i '%LOCAL_DESTINATION_MSI_PATH%' & sleep 2 & waitfor /s %computername% /si chefclientinstalldone"
|
265
|
+
|
266
|
+
@if ERRORLEVEL 1 (
|
267
|
+
@echo ERROR: Failed to create Chef Client installation scheduled task with status code !ERRORLEVEL! > "&2"
|
268
|
+
) else (
|
269
|
+
@echo Successfully created scheduled task to install Chef Client.
|
270
|
+
@schtasks /run /tn chefclientbootstraptask
|
271
|
+
@if ERRORLEVEL 1 (
|
272
|
+
@echo ERROR: Failed to execut Chef Client installation scheduled task with status code !ERRORLEVEL!. > "&2"
|
273
|
+
) else (
|
274
|
+
@echo Successfully started Chef Client installation scheduled task.
|
275
|
+
@echo Waiting for installation to complete -- this may take a few minutes...
|
276
|
+
waitfor chefclientinstalldone /t 600
|
277
|
+
if ERRORLEVEL 1 (
|
278
|
+
@echo ERROR: Timed out waiting for Chef Client package to install
|
279
|
+
) else (
|
280
|
+
@echo Finished waiting for Chef Client package to install.
|
281
|
+
)
|
282
|
+
@schtasks /delete /f /tn chefclientbootstraptask > NUL
|
283
|
+
)
|
284
|
+
)
|
285
|
+
) else (
|
286
|
+
@echo Successfully installed Chef Client package.
|
287
|
+
)
|
288
|
+
|
289
|
+
|
290
|
+
@if ERRORLEVEL 1 (
|
291
|
+
echo Chef-client package failed to install with status code !ERRORLEVEL!. > "&2"
|
292
|
+
echo See installation log for additional detail: %CHEF_CLIENT_MSI_LOG_PATH%. > "&2"
|
293
|
+
) else (
|
294
|
+
@echo Installation completed successfully
|
295
|
+
del /f /q "%CHEF_CLIENT_MSI_LOG_PATH%"
|
296
|
+
)
|
297
|
+
|
298
|
+
|
299
|
+
@endlocal
|
300
|
+
|
301
|
+
@echo off
|
302
|
+
|
303
|
+
|
304
|
+
echo Writing validation key...
|
305
|
+
|
306
|
+
|
307
|
+
echo Validation key written.
|
308
|
+
@echo on
|
309
|
+
|
310
|
+
|
311
|
+
|
312
|
+
|
313
|
+
> C:\chef\client.rb (
|
314
|
+
echo.chef_server_url "https://localhost:443"
|
315
|
+
echo.validation_client_name "chef-validator"
|
316
|
+
echo.file_cache_path "c:/chef/cache"
|
317
|
+
echo.file_backup_path "c:/chef/backup"
|
318
|
+
echo.cache_options ^({:path =^> "c:/chef/cache/checksums", :skip_expires =^> true}^)
|
319
|
+
echo.# Using default node name ^(fqdn^)
|
320
|
+
echo.log_level :info
|
321
|
+
echo.log_location STDOUT
|
322
|
+
|
323
|
+
)
|
324
|
+
|
325
|
+
> C:\chef\first-boot.json (
|
326
|
+
echo.{"run_list":null}
|
327
|
+
)
|
328
|
+
|
329
|
+
@echo Starting chef to bootstrap the node...
|
330
|
+
SET "PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;C:\ruby\bin;C:\opscode\chef\bin;C:\opscode\chef\embedded\bin"
|
331
|
+
chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json
|
332
|
+
|
@@ -126,12 +126,7 @@ describe 'Knife::Windows::Core msi download functionality for knife Windows winr
|
|
126
126
|
clean_test_case
|
127
127
|
|
128
128
|
winrm_bootstrapper = Chef::Knife::BootstrapWindowsWinrm.new([ "127.0.0.1" ])
|
129
|
-
|
130
|
-
if chef_gte_12?
|
131
|
-
winrm_bootstrapper.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
|
132
|
-
elsif chef_lt_12?
|
133
|
-
allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(true)
|
134
|
-
end
|
129
|
+
winrm_bootstrapper.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
|
135
130
|
|
136
131
|
allow(WinRM::Connection).to receive(:new).and_return(Dummy::Connection.new)
|
137
132
|
allow(winrm_bootstrapper).to receive(:wait_for_remote_response)
|
@@ -151,7 +146,7 @@ describe 'Knife::Windows::Core msi download functionality for knife Windows winr
|
|
151
146
|
end
|
152
147
|
end
|
153
148
|
|
154
|
-
describe "bootstrap_install_command functionality through WinRM protocol"
|
149
|
+
describe "bootstrap_install_command functionality through WinRM protocol" do
|
155
150
|
context "bootstrap_install_command option is not specified" do
|
156
151
|
let(:bootstrap) { Chef::Knife::BootstrapWindowsWinrm.new([]) }
|
157
152
|
before do
|
@@ -164,13 +159,21 @@ describe "bootstrap_install_command functionality through WinRM protocol", :if_c
|
|
164
159
|
@template_output)
|
165
160
|
end
|
166
161
|
|
167
|
-
context "when running chef-client 12.5
|
162
|
+
context "when running chef-client ~12.5", :chef_gte_12_5_only => true, :chef_lt_13_only => true do
|
168
163
|
let(:template_12_5_output) { sample_data('win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt') }
|
169
164
|
it "bootstrap_install_command option is not rendered in the windows-chef-client-msi.erb template as its value is nil" do
|
170
165
|
expect(bootstrap.send(:render_template,@template_input)).to eq(
|
171
166
|
template_12_5_output)
|
172
167
|
end
|
173
168
|
end
|
169
|
+
|
170
|
+
context "when running chef-client 13.0 or greater", :chef_gte_13_only => true, :chef_lt_14_only => true do
|
171
|
+
let(:template_13_output) { sample_data('win_template_rendered_without_bootstrap_install_command_on_13_client.txt') }
|
172
|
+
it "bootstrap_install_command option is not rendered in the windows-chef-client-msi.erb template as its value is nil" do
|
173
|
+
expect(bootstrap.send(:render_template,@template_input)).to eq(
|
174
|
+
template_13_output)
|
175
|
+
end
|
176
|
+
end
|
174
177
|
end
|
175
178
|
|
176
179
|
context "bootstrap_install_command option is specified" do
|
@@ -186,7 +189,7 @@ describe "bootstrap_install_command functionality through WinRM protocol", :if_c
|
|
186
189
|
@template_output)
|
187
190
|
end
|
188
191
|
|
189
|
-
context "when running chef-client 12.5.0 or greater", :chef_gte_12_5_only => true do
|
192
|
+
context "when running chef-client 12.5.0 or greater", :chef_gte_12_5_only => true, :chef_lt_14_only => true do
|
190
193
|
let(:template_12_5_output) { sample_data('win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt') }
|
191
194
|
it "bootstrap_install_command option is rendered in the windows-chef-client-msi.erb template" do
|
192
195
|
expect(bootstrap.send(:render_template,@template_input)).to eq(
|
@@ -201,7 +204,7 @@ describe "bootstrap_install_command functionality through WinRM protocol", :if_c
|
|
201
204
|
end
|
202
205
|
end
|
203
206
|
|
204
|
-
describe "bootstrap_install_command functionality through SSH protocol", :
|
207
|
+
describe "bootstrap_install_command functionality through SSH protocol", :chef_lt_12_5_only => true do
|
205
208
|
context "bootstrap_install_command option is not specified" do
|
206
209
|
let(:bootstrap) { Chef::Knife::BootstrapWindowsSsh.new([]) }
|
207
210
|
before do
|
data/spec/spec_helper.rb
CHANGED
@@ -55,24 +55,32 @@ def windows2012?
|
|
55
55
|
is_win2k12
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
59
|
-
Chef::VERSION.
|
58
|
+
def chef_lt_12_5?
|
59
|
+
Gem::Version.new(Chef::VERSION) < Gem::Version.new('12.5')
|
60
60
|
end
|
61
61
|
|
62
62
|
def chef_gte_12_5?
|
63
|
-
Chef::VERSION
|
63
|
+
Gem::Version.new(Chef::VERSION) >= Gem::Version.new('12.5')
|
64
64
|
end
|
65
65
|
|
66
|
-
def
|
67
|
-
Chef::VERSION
|
66
|
+
def chef_gte_12_7?
|
67
|
+
Gem::Version.new(Chef::VERSION) >= Gem::Version.new('12.7')
|
68
68
|
end
|
69
69
|
|
70
|
-
def
|
71
|
-
Chef::VERSION.
|
70
|
+
def chef_gte_13?
|
71
|
+
Gem::Version.new(Chef::VERSION) >= Gem::Version.new('13')
|
72
72
|
end
|
73
73
|
|
74
|
-
def
|
75
|
-
Chef::VERSION.
|
74
|
+
def chef_lt_13?
|
75
|
+
Gem::Version.new(Chef::VERSION) < Gem::Version.new('13')
|
76
|
+
end
|
77
|
+
|
78
|
+
def chef_lt_14?
|
79
|
+
Gem::Version.new(Chef::VERSION) < Gem::Version.new('14')
|
80
|
+
end
|
81
|
+
|
82
|
+
def chef_gte_14?
|
83
|
+
Gem::Version.new(Chef::VERSION) >= Gem::Version.new('14')
|
76
84
|
end
|
77
85
|
|
78
86
|
def sample_data(file_name)
|
@@ -85,10 +93,11 @@ RSpec.configure do |config|
|
|
85
93
|
config.filter_run :focus => true
|
86
94
|
config.filter_run_excluding :windows_only => true unless windows?
|
87
95
|
config.filter_run_excluding :windows_2012_only => true unless windows2012?
|
88
|
-
config.filter_run_excluding :chef_gte_12_only => true unless chef_gte_12?
|
89
96
|
config.filter_run_excluding :chef_gte_12_5_only => true unless chef_gte_12_5?
|
90
|
-
config.filter_run_excluding :
|
91
|
-
config.filter_run_excluding :
|
92
|
-
config.filter_run_excluding :
|
93
|
-
config.filter_run_excluding :
|
97
|
+
config.filter_run_excluding :chef_gte_12_7_only => true unless chef_gte_12_7?
|
98
|
+
config.filter_run_excluding :chef_gte_13_only => true unless chef_gte_13?
|
99
|
+
config.filter_run_excluding :chef_lt_12_5_only => true unless chef_lt_12_5?
|
100
|
+
config.filter_run_excluding :chef_lt_13_only => true unless chef_lt_13?
|
101
|
+
config.filter_run_excluding :chef_lt_14_only => true unless chef_lt_14?
|
102
|
+
config.filter_run_excluding :chef_gte_14_only => true unless chef_gte_14?
|
94
103
|
end
|
@@ -18,7 +18,7 @@
|
|
18
18
|
|
19
19
|
require 'spec_helper'
|
20
20
|
|
21
|
-
describe Chef::Knife::Bootstrap, :
|
21
|
+
describe Chef::Knife::Bootstrap, :chef_gte_12_7_only do
|
22
22
|
before(:all) do
|
23
23
|
Chef::Config.reset
|
24
24
|
end
|
@@ -44,7 +44,7 @@ expected: #{expected}
|
|
44
44
|
end
|
45
45
|
|
46
46
|
shared_examples 'compare_options' do
|
47
|
-
it 'contains the option flags' do
|
47
|
+
it 'contains the option flags', :chef_lt_14_only do
|
48
48
|
opt_map.default_proc = proc { |map, key| key }
|
49
49
|
filtered_keys = (win_bootstrap.options.keys - win_ignore).map! { |key| opt_map[key] }
|
50
50
|
|
@@ -98,7 +98,9 @@ expected: #{expected}
|
|
98
98
|
:encrypt, # irrelevant during bootstrap
|
99
99
|
:identity_file,
|
100
100
|
:ssh_identity_file,
|
101
|
-
:
|
101
|
+
:ssh_gateway_identity,
|
102
|
+
:bootstrap_proxy_user,
|
103
|
+
:bootstrap_proxy_pass,
|
102
104
|
]}
|
103
105
|
|
104
106
|
# win_ignore: Options in windows that aren't relevant to core.
|
@@ -115,7 +117,9 @@ expected: #{expected}
|
|
115
117
|
:ssl_peer_fingerprint,
|
116
118
|
:winrm_authentication_protocol,
|
117
119
|
:winrm_transport,
|
118
|
-
:
|
120
|
+
:winrm_codepage,
|
121
|
+
:concurrency,
|
122
|
+
:winrm_shell,
|
119
123
|
] }
|
120
124
|
|
121
125
|
include_examples 'compare_options'
|
@@ -141,14 +145,15 @@ expected: #{expected}
|
|
141
145
|
:use_sudo,
|
142
146
|
:use_sudo_password,
|
143
147
|
:encrypt, # irrelevant during bootstrap
|
144
|
-
:
|
148
|
+
:ssh_gateway_identity,
|
149
|
+
:bootstrap_proxy_user,
|
150
|
+
:bootstrap_proxy_pass,
|
145
151
|
]}
|
146
152
|
# win_ignore: Options in windows that aren't relevant to core.
|
147
153
|
let(:win_ignore) { [
|
148
154
|
:auth_timeout,
|
149
155
|
:install_as_service,
|
150
156
|
:host_key_verification, # Deprecated - remove this when the flag is removed.
|
151
|
-
:fips, #until chef 12.7 is released
|
152
157
|
] }
|
153
158
|
|
154
159
|
include_examples 'compare_options'
|
@@ -267,41 +267,28 @@ describe Chef::Knife::BootstrapWindowsWinrm do
|
|
267
267
|
end
|
268
268
|
|
269
269
|
context "when validation_key is not present" do
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
it 'raises an exception if validation_key is not present in chef 11' do
|
276
|
-
expect(bootstrap.ui).to receive(:error)
|
277
|
-
expect { bootstrap.bootstrap }.to raise_error(SystemExit)
|
278
|
-
end
|
270
|
+
before do
|
271
|
+
allow(File).to receive(:exist?).with(File.expand_path(Chef::Config[:validation_key])).and_return(false)
|
272
|
+
bootstrap.client_builder = instance_double("Chef::Knife::Bootstrap::ClientBuilder", :run => nil, :client_path => nil)
|
273
|
+
Chef::Config[:knife] = {:chef_node_name => 'foo.example.com'}
|
279
274
|
end
|
280
275
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
end
|
287
|
-
|
288
|
-
it 'raises an exception if winrm_authentication_protocol is basic and transport is plaintext' do
|
289
|
-
Chef::Config[:knife] = {:winrm_authentication_protocol => 'basic', :winrm_transport => 'plaintext', :chef_node_name => 'foo.example.com'}
|
290
|
-
expect(bootstrap.ui).to receive(:error)
|
291
|
-
expect { bootstrap.run }.to raise_error(SystemExit)
|
292
|
-
end
|
276
|
+
it 'raises an exception if winrm_authentication_protocol is basic and transport is plaintext' do
|
277
|
+
Chef::Config[:knife] = {:winrm_authentication_protocol => 'basic', :winrm_transport => 'plaintext', :chef_node_name => 'foo.example.com'}
|
278
|
+
expect(bootstrap.ui).to receive(:error)
|
279
|
+
expect { bootstrap.run }.to raise_error(SystemExit)
|
280
|
+
end
|
293
281
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
end
|
282
|
+
it 'raises an exception if chef_node_name is not present ' do
|
283
|
+
Chef::Config[:knife] = {:chef_node_name => nil}
|
284
|
+
expect(bootstrap.client_builder).not_to receive(:run)
|
285
|
+
expect(bootstrap.client_builder).not_to receive(:client_path)
|
286
|
+
expect(bootstrap.ui).to receive(:error)
|
287
|
+
expect { bootstrap.bootstrap }.to raise_error(SystemExit)
|
301
288
|
end
|
302
289
|
end
|
303
290
|
|
304
|
-
context "when doing chef vault"
|
291
|
+
context "when doing chef vault" do
|
305
292
|
let(:vault_handler) { double('vault_handler', :doing_chef_vault? => true) }
|
306
293
|
let(:node_name) { 'foo.example.com' }
|
307
294
|
before do
|
@@ -87,7 +87,7 @@ describe Chef::Knife::Core::WindowsBootstrapContext do
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
describe "validation_key"
|
90
|
+
describe "validation_key" do
|
91
91
|
before do
|
92
92
|
mock_bootstrap_context.instance_variable_set(:@config, Mash.new(:validation_key => "C:\\chef\\key.pem"))
|
93
93
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-windows
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.9.
|
4
|
+
version: 1.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Seth Chisamore
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: winrm
|
@@ -72,8 +72,6 @@ files:
|
|
72
72
|
- Rakefile
|
73
73
|
- appveyor.yml
|
74
74
|
- ci.gemfile
|
75
|
-
- features/knife_help.feature
|
76
|
-
- features/support/env.rb
|
77
75
|
- knife-windows.gemspec
|
78
76
|
- lib/chef/knife/bootstrap/windows-chef-client-msi.erb
|
79
77
|
- lib/chef/knife/bootstrap_windows_base.rb
|
@@ -102,6 +100,7 @@ files:
|
|
102
100
|
- spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt
|
103
101
|
- spec/assets/win_template_rendered_without_bootstrap_install_command.txt
|
104
102
|
- spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt
|
103
|
+
- spec/assets/win_template_rendered_without_bootstrap_install_command_on_13_client.txt
|
105
104
|
- spec/assets/win_template_unrendered.txt
|
106
105
|
- spec/dummy_winrm_connection.rb
|
107
106
|
- spec/functional/bootstrap_download_spec.rb
|
@@ -136,14 +135,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
136
135
|
version: '0'
|
137
136
|
requirements: []
|
138
137
|
rubyforge_project:
|
139
|
-
rubygems_version: 2.
|
138
|
+
rubygems_version: 2.7.5
|
140
139
|
signing_key:
|
141
140
|
specification_version: 4
|
142
141
|
summary: Plugin that adds functionality to Chef's Knife CLI for configuring/interacting
|
143
142
|
with nodes running Microsoft Windows
|
144
143
|
test_files:
|
145
|
-
- features/knife_help.feature
|
146
|
-
- features/support/env.rb
|
147
144
|
- spec/assets/fake_trusted_certs/excluded.txt
|
148
145
|
- spec/assets/fake_trusted_certs/github.pem
|
149
146
|
- spec/assets/fake_trusted_certs/google.crt
|
@@ -152,6 +149,7 @@ test_files:
|
|
152
149
|
- spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt
|
153
150
|
- spec/assets/win_template_rendered_without_bootstrap_install_command.txt
|
154
151
|
- spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt
|
152
|
+
- spec/assets/win_template_rendered_without_bootstrap_install_command_on_13_client.txt
|
155
153
|
- spec/assets/win_template_unrendered.txt
|
156
154
|
- spec/dummy_winrm_connection.rb
|
157
155
|
- spec/functional/bootstrap_download_spec.rb
|
data/features/knife_help.feature
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
Feature: Ensure that the help works as designed
|
2
|
-
In order to test the help via CLI
|
3
|
-
As an Operator
|
4
|
-
I want to run the CLI with different arguments
|
5
|
-
|
6
|
-
Scenario: Running the windows sub-command shows available commands
|
7
|
-
When I run `knife windows`
|
8
|
-
And the output should contain "Available windows subcommands: (for details, knife SUB-COMMAND --help)\n\n** WINDOWS COMMANDS **\nknife bootstrap windows winrm FQDN (options)\nknife bootstrap windows ssh FQDN (options)\nknife winrm QUERY COMMAND (options)"
|
9
|
-
|
10
|
-
Scenario: Running the windows sub-command shows available commands
|
11
|
-
When I run `knife windows --help`
|
12
|
-
And the output should contain "Available windows subcommands: (for details, knife SUB-COMMAND --help)\n\n** WINDOWS COMMANDS **\nknife bootstrap windows winrm FQDN (options)\nknife bootstrap windows ssh FQDN (options)\nknife winrm QUERY COMMAND (options)"
|
13
|
-
|
14
|
-
Scenario: Running the windows sub-command shows available commands
|
15
|
-
When I run `knife windows help`
|
16
|
-
And the output should contain "Available windows subcommands: (for details, knife SUB-COMMAND --help)\n\n** WINDOWS COMMANDS **\nknife bootstrap windows winrm FQDN (options)\nknife bootstrap windows ssh FQDN (options)\nknife winrm QUERY COMMAND (options)"
|
17
|
-
|
18
|
-
Scenario: Running the knife command shows available windows command"
|
19
|
-
When I run `knife`
|
20
|
-
And the output should contain "** WINDOWS COMMANDS **\nknife bootstrap windows winrm FQDN (options)\nknife bootstrap windows ssh FQDN (options)\nknife winrm QUERY COMMAND (options)"
|