knife-windows 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +5 -5
- data/.travis.yml +20 -20
- data/CHANGELOG.md +87 -83
- data/DOC_CHANGES.md +20 -20
- data/Gemfile +12 -12
- data/LICENSE +201 -201
- data/README.md +396 -396
- data/RELEASE_NOTES.md +34 -34
- data/Rakefile +21 -21
- data/appveyor.yml +42 -42
- data/ci.gemfile +15 -15
- data/features/knife_help.feature +20 -20
- data/features/support/env.rb +5 -5
- data/knife-windows.gemspec +28 -28
- data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +247 -247
- data/lib/chef/knife/bootstrap_windows_base.rb +407 -401
- data/lib/chef/knife/bootstrap_windows_ssh.rb +110 -110
- data/lib/chef/knife/bootstrap_windows_winrm.rb +95 -102
- data/lib/chef/knife/core/windows_bootstrap_context.rb +362 -362
- data/lib/chef/knife/knife_windows_base.rb +33 -33
- data/lib/chef/knife/windows_cert_generate.rb +155 -155
- data/lib/chef/knife/windows_cert_install.rb +68 -68
- data/lib/chef/knife/windows_helper.rb +36 -36
- data/lib/chef/knife/windows_listener_create.rb +107 -107
- data/lib/chef/knife/winrm.rb +122 -212
- data/lib/chef/knife/winrm_base.rb +118 -118
- data/lib/chef/knife/winrm_knife_base.rb +309 -218
- data/lib/chef/knife/winrm_session.rb +82 -82
- data/lib/chef/knife/winrm_shared_options.rb +47 -47
- data/lib/chef/knife/wsman_endpoint.rb +44 -44
- data/lib/chef/knife/wsman_test.rb +95 -95
- data/lib/knife-windows/path_helper.rb +234 -234
- data/lib/knife-windows/version.rb +6 -6
- data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +217 -217
- data/spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt +217 -217
- data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +329 -329
- data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt +329 -329
- data/spec/assets/win_template_unrendered.txt +246 -246
- data/spec/functional/bootstrap_download_spec.rb +234 -233
- data/spec/spec_helper.rb +88 -88
- data/spec/unit/knife/bootstrap_options_spec.rb +148 -146
- data/spec/unit/knife/bootstrap_template_spec.rb +92 -92
- data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +259 -243
- data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +151 -151
- data/spec/unit/knife/windows_cert_generate_spec.rb +90 -90
- data/spec/unit/knife/windows_cert_install_spec.rb +51 -51
- data/spec/unit/knife/windows_listener_create_spec.rb +76 -76
- data/spec/unit/knife/winrm_session_spec.rb +73 -73
- data/spec/unit/knife/winrm_spec.rb +551 -504
- data/spec/unit/knife/wsman_test_spec.rb +178 -175
- metadata +3 -23
@@ -1,110 +1,110 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
-
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
4
|
-
# License:: Apache License, Version 2.0
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
|
19
|
-
require 'chef/knife/bootstrap_windows_base'
|
20
|
-
|
21
|
-
class Chef
|
22
|
-
class Knife
|
23
|
-
class BootstrapWindowsSsh < Bootstrap
|
24
|
-
|
25
|
-
include Chef::Knife::BootstrapWindowsBase
|
26
|
-
|
27
|
-
deps do
|
28
|
-
require 'chef/knife/core/windows_bootstrap_context'
|
29
|
-
require 'chef/json_compat'
|
30
|
-
require 'tempfile'
|
31
|
-
require 'highline'
|
32
|
-
require 'net/ssh'
|
33
|
-
require 'net/ssh/multi'
|
34
|
-
Chef::Knife::Ssh.load_deps
|
35
|
-
end
|
36
|
-
|
37
|
-
banner "knife bootstrap windows ssh FQDN (options)"
|
38
|
-
|
39
|
-
option :ssh_user,
|
40
|
-
:short => "-x USERNAME",
|
41
|
-
:long => "--ssh-user USERNAME",
|
42
|
-
:description => "The ssh username",
|
43
|
-
:default => "root"
|
44
|
-
|
45
|
-
option :ssh_password,
|
46
|
-
:short => "-P PASSWORD",
|
47
|
-
:long => "--ssh-password PASSWORD",
|
48
|
-
:description => "The ssh password"
|
49
|
-
|
50
|
-
option :ssh_port,
|
51
|
-
:short => "-p PORT",
|
52
|
-
:long => "--ssh-port PORT",
|
53
|
-
:description => "The ssh port",
|
54
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key.strip }
|
55
|
-
|
56
|
-
option :ssh_gateway,
|
57
|
-
:short => "-G GATEWAY",
|
58
|
-
:long => "--ssh-gateway GATEWAY",
|
59
|
-
:description => "The ssh gateway",
|
60
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
|
61
|
-
|
62
|
-
option :forward_agent,
|
63
|
-
:short => "-A",
|
64
|
-
:long => "--forward-agent",
|
65
|
-
:description => "Enable SSH agent forwarding",
|
66
|
-
:boolean => true
|
67
|
-
|
68
|
-
option :identity_file,
|
69
|
-
:short => "-i IDENTITY_FILE",
|
70
|
-
:long => "--identity-file IDENTITY_FILE",
|
71
|
-
:description => "The SSH identity file used for authentication"
|
72
|
-
|
73
|
-
# DEPR: Remove this option for the next release.
|
74
|
-
option :host_key_verification,
|
75
|
-
:long => "--[no-]host-key-verify",
|
76
|
-
:description => "Verify host key, enabled by default. [DEPRECATED] Use --host-key-verify option instead.",
|
77
|
-
:boolean => true,
|
78
|
-
:default => true,
|
79
|
-
:proc => Proc.new { |key|
|
80
|
-
Chef::Log.warn("[DEPRECATED] --host-key-verification option is deprecated. Use --host-key-verify option instead.")
|
81
|
-
config[:host_key_verify] = key
|
82
|
-
}
|
83
|
-
|
84
|
-
option :host_key_verify,
|
85
|
-
:long => "--[no-]host-key-verify",
|
86
|
-
:description => "Verify host key, enabled by default.",
|
87
|
-
:boolean => true,
|
88
|
-
:default => true
|
89
|
-
|
90
|
-
def run
|
91
|
-
bootstrap
|
92
|
-
end
|
93
|
-
|
94
|
-
def run_command(command = '')
|
95
|
-
ssh = Chef::Knife::Ssh.new
|
96
|
-
ssh.name_args = [ server_name, command ]
|
97
|
-
ssh.config[:ssh_user] = locate_config_value(:ssh_user)
|
98
|
-
ssh.config[:ssh_password] = locate_config_value(:ssh_password)
|
99
|
-
ssh.config[:ssh_port] = locate_config_value(:ssh_port)
|
100
|
-
ssh.config[:ssh_gateway] = locate_config_value(:ssh_gateway)
|
101
|
-
ssh.config[:identity_file] = config[:identity_file]
|
102
|
-
ssh.config[:forward_agent] = config[:forward_agent]
|
103
|
-
ssh.config[:manual] = true
|
104
|
-
ssh.config[:host_key_verify] = config[:host_key_verify]
|
105
|
-
ssh.run
|
106
|
-
end
|
107
|
-
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef/knife/bootstrap_windows_base'
|
20
|
+
|
21
|
+
class Chef
|
22
|
+
class Knife
|
23
|
+
class BootstrapWindowsSsh < Bootstrap
|
24
|
+
|
25
|
+
include Chef::Knife::BootstrapWindowsBase
|
26
|
+
|
27
|
+
deps do
|
28
|
+
require 'chef/knife/core/windows_bootstrap_context'
|
29
|
+
require 'chef/json_compat'
|
30
|
+
require 'tempfile'
|
31
|
+
require 'highline'
|
32
|
+
require 'net/ssh'
|
33
|
+
require 'net/ssh/multi'
|
34
|
+
Chef::Knife::Ssh.load_deps
|
35
|
+
end
|
36
|
+
|
37
|
+
banner "knife bootstrap windows ssh FQDN (options)"
|
38
|
+
|
39
|
+
option :ssh_user,
|
40
|
+
:short => "-x USERNAME",
|
41
|
+
:long => "--ssh-user USERNAME",
|
42
|
+
:description => "The ssh username",
|
43
|
+
:default => "root"
|
44
|
+
|
45
|
+
option :ssh_password,
|
46
|
+
:short => "-P PASSWORD",
|
47
|
+
:long => "--ssh-password PASSWORD",
|
48
|
+
:description => "The ssh password"
|
49
|
+
|
50
|
+
option :ssh_port,
|
51
|
+
:short => "-p PORT",
|
52
|
+
:long => "--ssh-port PORT",
|
53
|
+
:description => "The ssh port",
|
54
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key.strip }
|
55
|
+
|
56
|
+
option :ssh_gateway,
|
57
|
+
:short => "-G GATEWAY",
|
58
|
+
:long => "--ssh-gateway GATEWAY",
|
59
|
+
:description => "The ssh gateway",
|
60
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
|
61
|
+
|
62
|
+
option :forward_agent,
|
63
|
+
:short => "-A",
|
64
|
+
:long => "--forward-agent",
|
65
|
+
:description => "Enable SSH agent forwarding",
|
66
|
+
:boolean => true
|
67
|
+
|
68
|
+
option :identity_file,
|
69
|
+
:short => "-i IDENTITY_FILE",
|
70
|
+
:long => "--identity-file IDENTITY_FILE",
|
71
|
+
:description => "The SSH identity file used for authentication"
|
72
|
+
|
73
|
+
# DEPR: Remove this option for the next release.
|
74
|
+
option :host_key_verification,
|
75
|
+
:long => "--[no-]host-key-verify",
|
76
|
+
:description => "Verify host key, enabled by default. [DEPRECATED] Use --host-key-verify option instead.",
|
77
|
+
:boolean => true,
|
78
|
+
:default => true,
|
79
|
+
:proc => Proc.new { |key|
|
80
|
+
Chef::Log.warn("[DEPRECATED] --host-key-verification option is deprecated. Use --host-key-verify option instead.")
|
81
|
+
config[:host_key_verify] = key
|
82
|
+
}
|
83
|
+
|
84
|
+
option :host_key_verify,
|
85
|
+
:long => "--[no-]host-key-verify",
|
86
|
+
:description => "Verify host key, enabled by default.",
|
87
|
+
:boolean => true,
|
88
|
+
:default => true
|
89
|
+
|
90
|
+
def run
|
91
|
+
bootstrap
|
92
|
+
end
|
93
|
+
|
94
|
+
def run_command(command = '')
|
95
|
+
ssh = Chef::Knife::Ssh.new
|
96
|
+
ssh.name_args = [ server_name, command ]
|
97
|
+
ssh.config[:ssh_user] = locate_config_value(:ssh_user)
|
98
|
+
ssh.config[:ssh_password] = locate_config_value(:ssh_password)
|
99
|
+
ssh.config[:ssh_port] = locate_config_value(:ssh_port)
|
100
|
+
ssh.config[:ssh_gateway] = locate_config_value(:ssh_gateway)
|
101
|
+
ssh.config[:identity_file] = config[:identity_file]
|
102
|
+
ssh.config[:forward_agent] = config[:forward_agent]
|
103
|
+
ssh.config[:manual] = true
|
104
|
+
ssh.config[:host_key_verify] = config[:host_key_verify]
|
105
|
+
ssh.run
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -1,102 +1,95 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
-
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
4
|
-
# License:: Apache License, Version 2.0
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
|
19
|
-
require 'chef/knife/bootstrap_windows_base'
|
20
|
-
require 'chef/knife/winrm'
|
21
|
-
require 'chef/knife/winrm_base'
|
22
|
-
require 'chef/knife/winrm_knife_base'
|
23
|
-
|
24
|
-
|
25
|
-
class Chef
|
26
|
-
class Knife
|
27
|
-
class BootstrapWindowsWinrm < Bootstrap
|
28
|
-
|
29
|
-
include Chef::Knife::BootstrapWindowsBase
|
30
|
-
include Chef::Knife::WinrmBase
|
31
|
-
include Chef::Knife::WinrmCommandSharedFunctions
|
32
|
-
|
33
|
-
deps do
|
34
|
-
require 'chef/knife/core/windows_bootstrap_context'
|
35
|
-
require 'chef/json_compat'
|
36
|
-
require 'tempfile'
|
37
|
-
Chef::Knife::Winrm.load_deps
|
38
|
-
end
|
39
|
-
|
40
|
-
banner 'knife bootstrap windows winrm FQDN (options)'
|
41
|
-
|
42
|
-
def run
|
43
|
-
if (Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key])))
|
44
|
-
if !negotiate_auth? && !(locate_config_value(:winrm_transport) == 'ssl')
|
45
|
-
ui.error('Validatorless bootstrap over unsecure winrm channels could expose your key to network sniffing')
|
46
|
-
exit 1
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
def elapsed_time_in_minutes(start_time)
|
98
|
-
((Time.now - start_time) / 60).round(2)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef/knife/bootstrap_windows_base'
|
20
|
+
require 'chef/knife/winrm'
|
21
|
+
require 'chef/knife/winrm_base'
|
22
|
+
require 'chef/knife/winrm_knife_base'
|
23
|
+
|
24
|
+
|
25
|
+
class Chef
|
26
|
+
class Knife
|
27
|
+
class BootstrapWindowsWinrm < Bootstrap
|
28
|
+
|
29
|
+
include Chef::Knife::BootstrapWindowsBase
|
30
|
+
include Chef::Knife::WinrmBase
|
31
|
+
include Chef::Knife::WinrmCommandSharedFunctions
|
32
|
+
|
33
|
+
deps do
|
34
|
+
require 'chef/knife/core/windows_bootstrap_context'
|
35
|
+
require 'chef/json_compat'
|
36
|
+
require 'tempfile'
|
37
|
+
Chef::Knife::Winrm.load_deps
|
38
|
+
end
|
39
|
+
|
40
|
+
banner 'knife bootstrap windows winrm FQDN (options)'
|
41
|
+
|
42
|
+
def run
|
43
|
+
if (Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key])))
|
44
|
+
if !negotiate_auth? && !(locate_config_value(:winrm_transport) == 'ssl')
|
45
|
+
ui.error('Validatorless bootstrap over unsecure winrm channels could expose your key to network sniffing')
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
config[:manual] = true
|
51
|
+
configure_session
|
52
|
+
|
53
|
+
bootstrap
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def wait_for_remote_response(wait_max_minutes)
|
59
|
+
wait_max_seconds = wait_max_minutes * 60
|
60
|
+
retry_interval_seconds = 10
|
61
|
+
retries_left = wait_max_seconds / retry_interval_seconds
|
62
|
+
print(ui.color("\nWaiting for remote response before bootstrap", :magenta))
|
63
|
+
wait_start_time = Time.now
|
64
|
+
begin
|
65
|
+
print(".")
|
66
|
+
# Return status of the command is non-zero, typically nil,
|
67
|
+
# for our simple echo command in cases where run_command
|
68
|
+
# swallows the exception, such as 401's. Treat such cases
|
69
|
+
# the same as the case where we encounter an exception.
|
70
|
+
status = run_command("echo . & echo Response received.")
|
71
|
+
raise RuntimeError, 'Command execution failed.' if status != 0
|
72
|
+
ui.info(ui.color("Remote node responded after #{elapsed_time_in_minutes(wait_start_time)} minutes.", :magenta))
|
73
|
+
return
|
74
|
+
rescue Errno::ECONNREFUSED => e
|
75
|
+
ui.error("Connection refused connecting to #{locate_config_value(:server_name)}:#{locate_config_value(:winrm_port)}.")
|
76
|
+
raise
|
77
|
+
rescue Exception => e
|
78
|
+
retries_left -= 1
|
79
|
+
if retries_left <= 0 || (elapsed_time_in_minutes(wait_start_time) > wait_max_minutes)
|
80
|
+
ui.error("No response received from remote node after #{elapsed_time_in_minutes(wait_start_time)} minutes, giving up.")
|
81
|
+
ui.error("Exception: #{e.message}")
|
82
|
+
raise
|
83
|
+
end
|
84
|
+
print '.'
|
85
|
+
sleep retry_interval_seconds
|
86
|
+
retry
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def elapsed_time_in_minutes(start_time)
|
91
|
+
((Time.now - start_time) / 60).round(2)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -1,362 +1,362 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
-
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
4
|
-
# License:: Apache License, Version 2.0
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
|
19
|
-
require 'chef/knife/core/bootstrap_context'
|
20
|
-
|
21
|
-
# Chef::Util::PathHelper in Chef 11 is a bit juvenile still
|
22
|
-
require 'knife-windows/path_helper'
|
23
|
-
# require 'chef/util/path_helper'
|
24
|
-
|
25
|
-
|
26
|
-
class Chef
|
27
|
-
class Knife
|
28
|
-
module Core
|
29
|
-
# Instances of BootstrapContext are the context objects (i.e., +self+) for
|
30
|
-
# bootstrap templates. For backwards compatability, they +must+ set the
|
31
|
-
# following instance variables:
|
32
|
-
# * @config - a hash of knife's config values
|
33
|
-
# * @run_list - the run list for the node to boostrap
|
34
|
-
#
|
35
|
-
class WindowsBootstrapContext < BootstrapContext
|
36
|
-
PathHelper = ::Knife::Windows::PathHelper
|
37
|
-
|
38
|
-
attr_accessor :client_pem
|
39
|
-
|
40
|
-
def initialize(config, run_list, chef_config, secret=nil)
|
41
|
-
@config = config
|
42
|
-
@run_list = run_list
|
43
|
-
@chef_config = chef_config
|
44
|
-
@secret = secret
|
45
|
-
# Compatibility with Chef 12 and Chef 11 versions
|
46
|
-
begin
|
47
|
-
# Pass along the secret parameter for Chef 12
|
48
|
-
super(config, run_list, chef_config, secret)
|
49
|
-
rescue ArgumentError
|
50
|
-
# The Chef 11 base class only has parameters for initialize
|
51
|
-
super(config, run_list, chef_config)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def validation_key
|
56
|
-
if File.exist?(File.expand_path(@chef_config[:validation_key]))
|
57
|
-
IO.read(File.expand_path(@chef_config[:validation_key]))
|
58
|
-
else
|
59
|
-
false
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def secret
|
64
|
-
escape_and_echo(@config[:secret])
|
65
|
-
end
|
66
|
-
|
67
|
-
def trusted_certs_script
|
68
|
-
@trusted_certs_script ||= trusted_certs_content
|
69
|
-
end
|
70
|
-
|
71
|
-
def config_content
|
72
|
-
client_rb = <<-CONFIG
|
73
|
-
log_level :info
|
74
|
-
log_location STDOUT
|
75
|
-
|
76
|
-
chef_server_url "#{@chef_config[:chef_server_url]}"
|
77
|
-
validation_client_name "#{@chef_config[:validation_client_name]}"
|
78
|
-
|
79
|
-
file_cache_path "c:/chef/cache"
|
80
|
-
file_backup_path "c:/chef/backup"
|
81
|
-
cache_options ({:path => "c:/chef/cache/checksums", :skip_expires => true})
|
82
|
-
|
83
|
-
CONFIG
|
84
|
-
if @config[:chef_node_name]
|
85
|
-
client_rb << %Q{node_name "#{@config[:chef_node_name]}"\n}
|
86
|
-
else
|
87
|
-
client_rb << "# Using default node name (fqdn)\n"
|
88
|
-
end
|
89
|
-
|
90
|
-
# We configure :verify_api_cert only when it's overridden on the CLI
|
91
|
-
# or when specified in the knife config.
|
92
|
-
if !@config[:node_verify_api_cert].nil? || knife_config.has_key?(:verify_api_cert)
|
93
|
-
value = @config[:node_verify_api_cert].nil? ? knife_config[:verify_api_cert] : @config[:node_verify_api_cert]
|
94
|
-
client_rb << %Q{verify_api_cert #{value}\n}
|
95
|
-
end
|
96
|
-
|
97
|
-
# We configure :ssl_verify_mode only when it's overridden on the CLI
|
98
|
-
# or when specified in the knife config.
|
99
|
-
if @config[:node_ssl_verify_mode] || knife_config.has_key?(:ssl_verify_mode)
|
100
|
-
value = case @config[:node_ssl_verify_mode]
|
101
|
-
when "peer"
|
102
|
-
:verify_peer
|
103
|
-
when "none"
|
104
|
-
:verify_none
|
105
|
-
when nil
|
106
|
-
knife_config[:ssl_verify_mode]
|
107
|
-
else
|
108
|
-
nil
|
109
|
-
end
|
110
|
-
|
111
|
-
if value
|
112
|
-
client_rb << %Q{ssl_verify_mode :#{value}\n}
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
if @config[:ssl_verify_mode]
|
117
|
-
client_rb << %Q{ssl_verify_mode :#{knife_config[:ssl_verify_mode]}\n}
|
118
|
-
end
|
119
|
-
|
120
|
-
if knife_config[:bootstrap_proxy]
|
121
|
-
client_rb << "\n"
|
122
|
-
client_rb << %Q{http_proxy "#{knife_config[:bootstrap_proxy]}"\n}
|
123
|
-
client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n}
|
124
|
-
client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n} if knife_config[:bootstrap_no_proxy]
|
125
|
-
end
|
126
|
-
|
127
|
-
if knife_config[:bootstrap_no_proxy]
|
128
|
-
client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n}
|
129
|
-
end
|
130
|
-
|
131
|
-
if @config[:secret]
|
132
|
-
client_rb << %Q{encrypted_data_bag_secret "c:/chef/encrypted_data_bag_secret"\n}
|
133
|
-
end
|
134
|
-
|
135
|
-
unless trusted_certs_script.empty?
|
136
|
-
client_rb << %Q{trusted_certs_dir "c:/chef/trusted_certs"\n}
|
137
|
-
end
|
138
|
-
|
139
|
-
escape_and_echo(client_rb)
|
140
|
-
end
|
141
|
-
|
142
|
-
def start_chef
|
143
|
-
bootstrap_environment_option = bootstrap_environment.nil? ? '' : " -E #{bootstrap_environment}"
|
144
|
-
start_chef = "SET \"PATH=%PATH%;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin\"\n"
|
145
|
-
start_chef << "chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json#{bootstrap_environment_option}\n"
|
146
|
-
end
|
147
|
-
|
148
|
-
def latest_current_windows_chef_version_query
|
149
|
-
installer_version_string = nil
|
150
|
-
if @config[:prerelease]
|
151
|
-
installer_version_string = "&prerelease=true"
|
152
|
-
else
|
153
|
-
chef_version_string = if knife_config[:bootstrap_version]
|
154
|
-
knife_config[:bootstrap_version]
|
155
|
-
else
|
156
|
-
Chef::VERSION.split(".").first
|
157
|
-
end
|
158
|
-
|
159
|
-
installer_version_string = "&v=#{chef_version_string}"
|
160
|
-
|
161
|
-
# If bootstrapping a pre-release version add the prerelease query string
|
162
|
-
if chef_version_string.split(".").length > 3
|
163
|
-
installer_version_string << "&prerelease=true"
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
installer_version_string
|
168
|
-
end
|
169
|
-
|
170
|
-
def win_wget
|
171
|
-
# I tried my best to figure out how to properly url decode and switch / to \
|
172
|
-
# but this is VBScript - so I don't really care that badly.
|
173
|
-
win_wget = <<-WGET
|
174
|
-
url = WScript.Arguments.Named("url")
|
175
|
-
path = WScript.Arguments.Named("path")
|
176
|
-
proxy = null
|
177
|
-
'* Vaguely attempt to handle file:// scheme urls by url unescaping and switching all
|
178
|
-
'* / into \. Also assume that file:/// is a local absolute path and that file://<foo>
|
179
|
-
'* is possibly a network file path.
|
180
|
-
If InStr(url, "file://") = 1 Then
|
181
|
-
url = Unescape(url)
|
182
|
-
If InStr(url, "file:///") = 1 Then
|
183
|
-
sourcePath = Mid(url, Len("file:///") + 1)
|
184
|
-
Else
|
185
|
-
sourcePath = Mid(url, Len("file:") + 1)
|
186
|
-
End If
|
187
|
-
sourcePath = Replace(sourcePath, "/", "\\")
|
188
|
-
|
189
|
-
Set objFSO = CreateObject("Scripting.FileSystemObject")
|
190
|
-
If objFSO.Fileexists(path) Then objFSO.DeleteFile path
|
191
|
-
objFSO.CopyFile sourcePath, path, true
|
192
|
-
Set objFSO = Nothing
|
193
|
-
|
194
|
-
Else
|
195
|
-
Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP")
|
196
|
-
Set wshShell = CreateObject( "WScript.Shell" )
|
197
|
-
Set objUserVariables = wshShell.Environment("USER")
|
198
|
-
|
199
|
-
rem http proxy is optional
|
200
|
-
rem attempt to read from HTTP_PROXY env var first
|
201
|
-
On Error Resume Next
|
202
|
-
|
203
|
-
If NOT (objUserVariables("HTTP_PROXY") = "") Then
|
204
|
-
proxy = objUserVariables("HTTP_PROXY")
|
205
|
-
|
206
|
-
rem fall back to named arg
|
207
|
-
ElseIf NOT (WScript.Arguments.Named("proxy") = "") Then
|
208
|
-
proxy = WScript.Arguments.Named("proxy")
|
209
|
-
End If
|
210
|
-
|
211
|
-
If NOT isNull(proxy) Then
|
212
|
-
rem setProxy method is only available on ServerXMLHTTP 6.0+
|
213
|
-
Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0")
|
214
|
-
objXMLHTTP.setProxy 2, proxy
|
215
|
-
End If
|
216
|
-
|
217
|
-
On Error Goto 0
|
218
|
-
|
219
|
-
objXMLHTTP.open "GET", url, false
|
220
|
-
objXMLHTTP.send()
|
221
|
-
If objXMLHTTP.Status = 200 Then
|
222
|
-
Set objADOStream = CreateObject("ADODB.Stream")
|
223
|
-
objADOStream.Open
|
224
|
-
objADOStream.Type = 1
|
225
|
-
objADOStream.Write objXMLHTTP.ResponseBody
|
226
|
-
objADOStream.Position = 0
|
227
|
-
Set objFSO = Createobject("Scripting.FileSystemObject")
|
228
|
-
If objFSO.Fileexists(path) Then objFSO.DeleteFile path
|
229
|
-
Set objFSO = Nothing
|
230
|
-
objADOStream.SaveToFile path
|
231
|
-
objADOStream.Close
|
232
|
-
Set objADOStream = Nothing
|
233
|
-
End If
|
234
|
-
Set objXMLHTTP = Nothing
|
235
|
-
End If
|
236
|
-
WGET
|
237
|
-
escape_and_echo(win_wget)
|
238
|
-
end
|
239
|
-
|
240
|
-
def win_wget_ps
|
241
|
-
win_wget_ps = <<-WGET_PS
|
242
|
-
param(
|
243
|
-
[String] $remoteUrl,
|
244
|
-
[String] $localPath
|
245
|
-
)
|
246
|
-
|
247
|
-
$webClient = new-object System.Net.WebClient;
|
248
|
-
|
249
|
-
$webClient.DownloadFile($remoteUrl, $localPath);
|
250
|
-
WGET_PS
|
251
|
-
|
252
|
-
escape_and_echo(win_wget_ps)
|
253
|
-
end
|
254
|
-
|
255
|
-
def install_chef
|
256
|
-
# The normal install command uses regular double quotes in
|
257
|
-
# the install command, so request such a string from install_command
|
258
|
-
install_chef = install_command('"') + "\n" + fallback_install_task_command
|
259
|
-
end
|
260
|
-
|
261
|
-
def bootstrap_directory
|
262
|
-
bootstrap_directory = "C:\\chef"
|
263
|
-
end
|
264
|
-
|
265
|
-
def local_download_path
|
266
|
-
local_download_path = "%TEMP%\\chef-client-latest.msi"
|
267
|
-
end
|
268
|
-
|
269
|
-
def msi_url(machine_os=nil, machine_arch=nil, download_context=nil)
|
270
|
-
# The default msi path has a number of url query parameters - we attempt to substitute
|
271
|
-
# such parameters in as long as they are provided by the template.
|
272
|
-
|
273
|
-
if @config[:msi_url].nil? || @config[:msi_url].empty?
|
274
|
-
url = "https://www.chef.io/chef/download?p=windows"
|
275
|
-
url += "&pv=#{machine_os}" unless machine_os.nil?
|
276
|
-
url += "&m=#{machine_arch}" unless machine_arch.nil?
|
277
|
-
url += "&DownloadContext=#{download_context}" unless download_context.nil?
|
278
|
-
url += latest_current_windows_chef_version_query
|
279
|
-
else
|
280
|
-
@config[:msi_url]
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
def first_boot
|
285
|
-
escape_and_echo(super.to_json)
|
286
|
-
end
|
287
|
-
|
288
|
-
# escape WIN BATCH special chars
|
289
|
-
# and prefixes each line with an
|
290
|
-
# echo
|
291
|
-
def escape_and_echo(file_contents)
|
292
|
-
file_contents.gsub(/^(.*)$/, 'echo.\1').gsub(/([(<|>)^])/, '^\1')
|
293
|
-
end
|
294
|
-
|
295
|
-
private
|
296
|
-
|
297
|
-
def install_command(executor_quote)
|
298
|
-
if @config[:install_as_service]
|
299
|
-
"msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote} ADDLOCAL=#{executor_quote}ChefClientFeature,ChefServiceFeature#{executor_quote}"
|
300
|
-
else
|
301
|
-
"msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote}"
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
# Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
|
306
|
-
# This string should contain both the commands necessary to both create the files, as well as their content
|
307
|
-
def trusted_certs_content
|
308
|
-
content = ""
|
309
|
-
if @chef_config[:trusted_certs_dir]
|
310
|
-
Dir.glob(File.join(PathHelper.escape_glob(@chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert|
|
311
|
-
content << "> #{bootstrap_directory}/trusted_certs/#{File.basename(cert)} (\n" +
|
312
|
-
escape_and_echo(IO.read(File.expand_path(cert))) + "\n)\n"
|
313
|
-
end
|
314
|
-
end
|
315
|
-
content
|
316
|
-
end
|
317
|
-
|
318
|
-
def fallback_install_task_command
|
319
|
-
# This command will be executed by schtasks.exe in the batch
|
320
|
-
# code below. To handle tasks that contain arguments that
|
321
|
-
# need to be double quoted, schtasks allows the use of single
|
322
|
-
# quotes that will later be converted to double quotes
|
323
|
-
command = install_command('\'')
|
324
|
-
<<-EOH
|
325
|
-
@set MSIERRORCODE=!ERRORLEVEL!
|
326
|
-
@if ERRORLEVEL 1 (
|
327
|
-
@echo WARNING: Failed to install Chef Client MSI package in remote context with status code !MSIERRORCODE!.
|
328
|
-
@echo WARNING: This may be due to a defect in operating system update KB2918614: http://support.microsoft.com/kb/2918614
|
329
|
-
@set OLDLOGLOCATION="%CHEF_CLIENT_MSI_LOG_PATH%-fail.log"
|
330
|
-
@move "%CHEF_CLIENT_MSI_LOG_PATH%" "!OLDLOGLOCATION!" > NUL
|
331
|
-
@echo WARNING: Saving installation log of failure at !OLDLOGLOCATION!
|
332
|
-
@echo WARNING: Retrying installation with local context...
|
333
|
-
@schtasks /create /f /sc once /st 00:00:00 /tn chefclientbootstraptask /ru SYSTEM /rl HIGHEST /tr \"cmd /c #{command} & sleep 2 & waitfor /s %computername% /si chefclientinstalldone\"
|
334
|
-
|
335
|
-
@if ERRORLEVEL 1 (
|
336
|
-
@echo ERROR: Failed to create Chef Client installation scheduled task with status code !ERRORLEVEL! > "&2"
|
337
|
-
) else (
|
338
|
-
@echo Successfully created scheduled task to install Chef Client.
|
339
|
-
@schtasks /run /tn chefclientbootstraptask
|
340
|
-
@if ERRORLEVEL 1 (
|
341
|
-
@echo ERROR: Failed to execut Chef Client installation scheduled task with status code !ERRORLEVEL!. > "&2"
|
342
|
-
) else (
|
343
|
-
@echo Successfully started Chef Client installation scheduled task.
|
344
|
-
@echo Waiting for installation to complete -- this may take a few minutes...
|
345
|
-
waitfor chefclientinstalldone /t 600
|
346
|
-
if ERRORLEVEL 1 (
|
347
|
-
@echo ERROR: Timed out waiting for Chef Client package to install
|
348
|
-
) else (
|
349
|
-
@echo Finished waiting for Chef Client package to install.
|
350
|
-
)
|
351
|
-
@schtasks /delete /f /tn chefclientbootstraptask > NUL
|
352
|
-
)
|
353
|
-
)
|
354
|
-
) else (
|
355
|
-
@echo Successfully installed Chef Client package.
|
356
|
-
)
|
357
|
-
EOH
|
358
|
-
end
|
359
|
-
end
|
360
|
-
end
|
361
|
-
end
|
362
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Seth Chisamore (<schisamo@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef/knife/core/bootstrap_context'
|
20
|
+
|
21
|
+
# Chef::Util::PathHelper in Chef 11 is a bit juvenile still
|
22
|
+
require 'knife-windows/path_helper'
|
23
|
+
# require 'chef/util/path_helper'
|
24
|
+
|
25
|
+
|
26
|
+
class Chef
|
27
|
+
class Knife
|
28
|
+
module Core
|
29
|
+
# Instances of BootstrapContext are the context objects (i.e., +self+) for
|
30
|
+
# bootstrap templates. For backwards compatability, they +must+ set the
|
31
|
+
# following instance variables:
|
32
|
+
# * @config - a hash of knife's config values
|
33
|
+
# * @run_list - the run list for the node to boostrap
|
34
|
+
#
|
35
|
+
class WindowsBootstrapContext < BootstrapContext
|
36
|
+
PathHelper = ::Knife::Windows::PathHelper
|
37
|
+
|
38
|
+
attr_accessor :client_pem
|
39
|
+
|
40
|
+
def initialize(config, run_list, chef_config, secret=nil)
|
41
|
+
@config = config
|
42
|
+
@run_list = run_list
|
43
|
+
@chef_config = chef_config
|
44
|
+
@secret = secret
|
45
|
+
# Compatibility with Chef 12 and Chef 11 versions
|
46
|
+
begin
|
47
|
+
# Pass along the secret parameter for Chef 12
|
48
|
+
super(config, run_list, chef_config, secret)
|
49
|
+
rescue ArgumentError
|
50
|
+
# The Chef 11 base class only has parameters for initialize
|
51
|
+
super(config, run_list, chef_config)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def validation_key
|
56
|
+
if File.exist?(File.expand_path(@chef_config[:validation_key]))
|
57
|
+
IO.read(File.expand_path(@chef_config[:validation_key]))
|
58
|
+
else
|
59
|
+
false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def secret
|
64
|
+
escape_and_echo(@config[:secret])
|
65
|
+
end
|
66
|
+
|
67
|
+
def trusted_certs_script
|
68
|
+
@trusted_certs_script ||= trusted_certs_content
|
69
|
+
end
|
70
|
+
|
71
|
+
def config_content
|
72
|
+
client_rb = <<-CONFIG
|
73
|
+
log_level :info
|
74
|
+
log_location STDOUT
|
75
|
+
|
76
|
+
chef_server_url "#{@chef_config[:chef_server_url]}"
|
77
|
+
validation_client_name "#{@chef_config[:validation_client_name]}"
|
78
|
+
|
79
|
+
file_cache_path "c:/chef/cache"
|
80
|
+
file_backup_path "c:/chef/backup"
|
81
|
+
cache_options ({:path => "c:/chef/cache/checksums", :skip_expires => true})
|
82
|
+
|
83
|
+
CONFIG
|
84
|
+
if @config[:chef_node_name]
|
85
|
+
client_rb << %Q{node_name "#{@config[:chef_node_name]}"\n}
|
86
|
+
else
|
87
|
+
client_rb << "# Using default node name (fqdn)\n"
|
88
|
+
end
|
89
|
+
|
90
|
+
# We configure :verify_api_cert only when it's overridden on the CLI
|
91
|
+
# or when specified in the knife config.
|
92
|
+
if !@config[:node_verify_api_cert].nil? || knife_config.has_key?(:verify_api_cert)
|
93
|
+
value = @config[:node_verify_api_cert].nil? ? knife_config[:verify_api_cert] : @config[:node_verify_api_cert]
|
94
|
+
client_rb << %Q{verify_api_cert #{value}\n}
|
95
|
+
end
|
96
|
+
|
97
|
+
# We configure :ssl_verify_mode only when it's overridden on the CLI
|
98
|
+
# or when specified in the knife config.
|
99
|
+
if @config[:node_ssl_verify_mode] || knife_config.has_key?(:ssl_verify_mode)
|
100
|
+
value = case @config[:node_ssl_verify_mode]
|
101
|
+
when "peer"
|
102
|
+
:verify_peer
|
103
|
+
when "none"
|
104
|
+
:verify_none
|
105
|
+
when nil
|
106
|
+
knife_config[:ssl_verify_mode]
|
107
|
+
else
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
if value
|
112
|
+
client_rb << %Q{ssl_verify_mode :#{value}\n}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
if @config[:ssl_verify_mode]
|
117
|
+
client_rb << %Q{ssl_verify_mode :#{knife_config[:ssl_verify_mode]}\n}
|
118
|
+
end
|
119
|
+
|
120
|
+
if knife_config[:bootstrap_proxy]
|
121
|
+
client_rb << "\n"
|
122
|
+
client_rb << %Q{http_proxy "#{knife_config[:bootstrap_proxy]}"\n}
|
123
|
+
client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n}
|
124
|
+
client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n} if knife_config[:bootstrap_no_proxy]
|
125
|
+
end
|
126
|
+
|
127
|
+
if knife_config[:bootstrap_no_proxy]
|
128
|
+
client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n}
|
129
|
+
end
|
130
|
+
|
131
|
+
if @config[:secret]
|
132
|
+
client_rb << %Q{encrypted_data_bag_secret "c:/chef/encrypted_data_bag_secret"\n}
|
133
|
+
end
|
134
|
+
|
135
|
+
unless trusted_certs_script.empty?
|
136
|
+
client_rb << %Q{trusted_certs_dir "c:/chef/trusted_certs"\n}
|
137
|
+
end
|
138
|
+
|
139
|
+
escape_and_echo(client_rb)
|
140
|
+
end
|
141
|
+
|
142
|
+
def start_chef
|
143
|
+
bootstrap_environment_option = bootstrap_environment.nil? ? '' : " -E #{bootstrap_environment}"
|
144
|
+
start_chef = "SET \"PATH=%PATH%;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin\"\n"
|
145
|
+
start_chef << "chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json#{bootstrap_environment_option}\n"
|
146
|
+
end
|
147
|
+
|
148
|
+
def latest_current_windows_chef_version_query
|
149
|
+
installer_version_string = nil
|
150
|
+
if @config[:prerelease]
|
151
|
+
installer_version_string = "&prerelease=true"
|
152
|
+
else
|
153
|
+
chef_version_string = if knife_config[:bootstrap_version]
|
154
|
+
knife_config[:bootstrap_version]
|
155
|
+
else
|
156
|
+
Chef::VERSION.split(".").first
|
157
|
+
end
|
158
|
+
|
159
|
+
installer_version_string = "&v=#{chef_version_string}"
|
160
|
+
|
161
|
+
# If bootstrapping a pre-release version add the prerelease query string
|
162
|
+
if chef_version_string.split(".").length > 3
|
163
|
+
installer_version_string << "&prerelease=true"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
installer_version_string
|
168
|
+
end
|
169
|
+
|
170
|
+
def win_wget
|
171
|
+
# I tried my best to figure out how to properly url decode and switch / to \
|
172
|
+
# but this is VBScript - so I don't really care that badly.
|
173
|
+
win_wget = <<-WGET
|
174
|
+
url = WScript.Arguments.Named("url")
|
175
|
+
path = WScript.Arguments.Named("path")
|
176
|
+
proxy = null
|
177
|
+
'* Vaguely attempt to handle file:// scheme urls by url unescaping and switching all
|
178
|
+
'* / into \. Also assume that file:/// is a local absolute path and that file://<foo>
|
179
|
+
'* is possibly a network file path.
|
180
|
+
If InStr(url, "file://") = 1 Then
|
181
|
+
url = Unescape(url)
|
182
|
+
If InStr(url, "file:///") = 1 Then
|
183
|
+
sourcePath = Mid(url, Len("file:///") + 1)
|
184
|
+
Else
|
185
|
+
sourcePath = Mid(url, Len("file:") + 1)
|
186
|
+
End If
|
187
|
+
sourcePath = Replace(sourcePath, "/", "\\")
|
188
|
+
|
189
|
+
Set objFSO = CreateObject("Scripting.FileSystemObject")
|
190
|
+
If objFSO.Fileexists(path) Then objFSO.DeleteFile path
|
191
|
+
objFSO.CopyFile sourcePath, path, true
|
192
|
+
Set objFSO = Nothing
|
193
|
+
|
194
|
+
Else
|
195
|
+
Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP")
|
196
|
+
Set wshShell = CreateObject( "WScript.Shell" )
|
197
|
+
Set objUserVariables = wshShell.Environment("USER")
|
198
|
+
|
199
|
+
rem http proxy is optional
|
200
|
+
rem attempt to read from HTTP_PROXY env var first
|
201
|
+
On Error Resume Next
|
202
|
+
|
203
|
+
If NOT (objUserVariables("HTTP_PROXY") = "") Then
|
204
|
+
proxy = objUserVariables("HTTP_PROXY")
|
205
|
+
|
206
|
+
rem fall back to named arg
|
207
|
+
ElseIf NOT (WScript.Arguments.Named("proxy") = "") Then
|
208
|
+
proxy = WScript.Arguments.Named("proxy")
|
209
|
+
End If
|
210
|
+
|
211
|
+
If NOT isNull(proxy) Then
|
212
|
+
rem setProxy method is only available on ServerXMLHTTP 6.0+
|
213
|
+
Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0")
|
214
|
+
objXMLHTTP.setProxy 2, proxy
|
215
|
+
End If
|
216
|
+
|
217
|
+
On Error Goto 0
|
218
|
+
|
219
|
+
objXMLHTTP.open "GET", url, false
|
220
|
+
objXMLHTTP.send()
|
221
|
+
If objXMLHTTP.Status = 200 Then
|
222
|
+
Set objADOStream = CreateObject("ADODB.Stream")
|
223
|
+
objADOStream.Open
|
224
|
+
objADOStream.Type = 1
|
225
|
+
objADOStream.Write objXMLHTTP.ResponseBody
|
226
|
+
objADOStream.Position = 0
|
227
|
+
Set objFSO = Createobject("Scripting.FileSystemObject")
|
228
|
+
If objFSO.Fileexists(path) Then objFSO.DeleteFile path
|
229
|
+
Set objFSO = Nothing
|
230
|
+
objADOStream.SaveToFile path
|
231
|
+
objADOStream.Close
|
232
|
+
Set objADOStream = Nothing
|
233
|
+
End If
|
234
|
+
Set objXMLHTTP = Nothing
|
235
|
+
End If
|
236
|
+
WGET
|
237
|
+
escape_and_echo(win_wget)
|
238
|
+
end
|
239
|
+
|
240
|
+
def win_wget_ps
|
241
|
+
win_wget_ps = <<-WGET_PS
|
242
|
+
param(
|
243
|
+
[String] $remoteUrl,
|
244
|
+
[String] $localPath
|
245
|
+
)
|
246
|
+
|
247
|
+
$webClient = new-object System.Net.WebClient;
|
248
|
+
|
249
|
+
$webClient.DownloadFile($remoteUrl, $localPath);
|
250
|
+
WGET_PS
|
251
|
+
|
252
|
+
escape_and_echo(win_wget_ps)
|
253
|
+
end
|
254
|
+
|
255
|
+
def install_chef
|
256
|
+
# The normal install command uses regular double quotes in
|
257
|
+
# the install command, so request such a string from install_command
|
258
|
+
install_chef = install_command('"') + "\n" + fallback_install_task_command
|
259
|
+
end
|
260
|
+
|
261
|
+
def bootstrap_directory
|
262
|
+
bootstrap_directory = "C:\\chef"
|
263
|
+
end
|
264
|
+
|
265
|
+
def local_download_path
|
266
|
+
local_download_path = "%TEMP%\\chef-client-latest.msi"
|
267
|
+
end
|
268
|
+
|
269
|
+
def msi_url(machine_os=nil, machine_arch=nil, download_context=nil)
|
270
|
+
# The default msi path has a number of url query parameters - we attempt to substitute
|
271
|
+
# such parameters in as long as they are provided by the template.
|
272
|
+
|
273
|
+
if @config[:msi_url].nil? || @config[:msi_url].empty?
|
274
|
+
url = "https://www.chef.io/chef/download?p=windows"
|
275
|
+
url += "&pv=#{machine_os}" unless machine_os.nil?
|
276
|
+
url += "&m=#{machine_arch}" unless machine_arch.nil?
|
277
|
+
url += "&DownloadContext=#{download_context}" unless download_context.nil?
|
278
|
+
url += latest_current_windows_chef_version_query
|
279
|
+
else
|
280
|
+
@config[:msi_url]
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def first_boot
|
285
|
+
escape_and_echo(super.to_json)
|
286
|
+
end
|
287
|
+
|
288
|
+
# escape WIN BATCH special chars
|
289
|
+
# and prefixes each line with an
|
290
|
+
# echo
|
291
|
+
def escape_and_echo(file_contents)
|
292
|
+
file_contents.gsub(/^(.*)$/, 'echo.\1').gsub(/([(<|>)^])/, '^\1')
|
293
|
+
end
|
294
|
+
|
295
|
+
private
|
296
|
+
|
297
|
+
def install_command(executor_quote)
|
298
|
+
if @config[:install_as_service]
|
299
|
+
"msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote} ADDLOCAL=#{executor_quote}ChefClientFeature,ChefServiceFeature#{executor_quote}"
|
300
|
+
else
|
301
|
+
"msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote}"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
|
306
|
+
# This string should contain both the commands necessary to both create the files, as well as their content
|
307
|
+
def trusted_certs_content
|
308
|
+
content = ""
|
309
|
+
if @chef_config[:trusted_certs_dir]
|
310
|
+
Dir.glob(File.join(PathHelper.escape_glob(@chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert|
|
311
|
+
content << "> #{bootstrap_directory}/trusted_certs/#{File.basename(cert)} (\n" +
|
312
|
+
escape_and_echo(IO.read(File.expand_path(cert))) + "\n)\n"
|
313
|
+
end
|
314
|
+
end
|
315
|
+
content
|
316
|
+
end
|
317
|
+
|
318
|
+
def fallback_install_task_command
|
319
|
+
# This command will be executed by schtasks.exe in the batch
|
320
|
+
# code below. To handle tasks that contain arguments that
|
321
|
+
# need to be double quoted, schtasks allows the use of single
|
322
|
+
# quotes that will later be converted to double quotes
|
323
|
+
command = install_command('\'')
|
324
|
+
<<-EOH
|
325
|
+
@set MSIERRORCODE=!ERRORLEVEL!
|
326
|
+
@if ERRORLEVEL 1 (
|
327
|
+
@echo WARNING: Failed to install Chef Client MSI package in remote context with status code !MSIERRORCODE!.
|
328
|
+
@echo WARNING: This may be due to a defect in operating system update KB2918614: http://support.microsoft.com/kb/2918614
|
329
|
+
@set OLDLOGLOCATION="%CHEF_CLIENT_MSI_LOG_PATH%-fail.log"
|
330
|
+
@move "%CHEF_CLIENT_MSI_LOG_PATH%" "!OLDLOGLOCATION!" > NUL
|
331
|
+
@echo WARNING: Saving installation log of failure at !OLDLOGLOCATION!
|
332
|
+
@echo WARNING: Retrying installation with local context...
|
333
|
+
@schtasks /create /f /sc once /st 00:00:00 /tn chefclientbootstraptask /ru SYSTEM /rl HIGHEST /tr \"cmd /c #{command} & sleep 2 & waitfor /s %computername% /si chefclientinstalldone\"
|
334
|
+
|
335
|
+
@if ERRORLEVEL 1 (
|
336
|
+
@echo ERROR: Failed to create Chef Client installation scheduled task with status code !ERRORLEVEL! > "&2"
|
337
|
+
) else (
|
338
|
+
@echo Successfully created scheduled task to install Chef Client.
|
339
|
+
@schtasks /run /tn chefclientbootstraptask
|
340
|
+
@if ERRORLEVEL 1 (
|
341
|
+
@echo ERROR: Failed to execut Chef Client installation scheduled task with status code !ERRORLEVEL!. > "&2"
|
342
|
+
) else (
|
343
|
+
@echo Successfully started Chef Client installation scheduled task.
|
344
|
+
@echo Waiting for installation to complete -- this may take a few minutes...
|
345
|
+
waitfor chefclientinstalldone /t 600
|
346
|
+
if ERRORLEVEL 1 (
|
347
|
+
@echo ERROR: Timed out waiting for Chef Client package to install
|
348
|
+
) else (
|
349
|
+
@echo Finished waiting for Chef Client package to install.
|
350
|
+
)
|
351
|
+
@schtasks /delete /f /tn chefclientbootstraptask > NUL
|
352
|
+
)
|
353
|
+
)
|
354
|
+
) else (
|
355
|
+
@echo Successfully installed Chef Client package.
|
356
|
+
)
|
357
|
+
EOH
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|