knife-winops 2.0.0
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 +7 -0
- data/.gitignore +5 -0
- data/.rspec +3 -0
- data/.travis.yml +30 -0
- data/CHANGELOG.md +147 -0
- data/DOC_CHANGES.md +22 -0
- data/Gemfile +13 -0
- data/LICENSE +201 -0
- data/README.md +430 -0
- data/RELEASE_NOTES.md +17 -0
- data/Rakefile +21 -0
- data/appveyor.yml +36 -0
- data/ci.gemfile +15 -0
- data/knife-winops.gemspec +26 -0
- data/lib/chef/knife/bootstrap/Chef_bootstrap.erb +44 -0
- data/lib/chef/knife/bootstrap/bootstrap.ps1 +134 -0
- data/lib/chef/knife/bootstrap/tail.cmd +15 -0
- data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +302 -0
- data/lib/chef/knife/bootstrap_windows_base.rb +473 -0
- data/lib/chef/knife/bootstrap_windows_ssh.rb +115 -0
- data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -0
- data/lib/chef/knife/core/windows_bootstrap_context.rb +356 -0
- data/lib/chef/knife/knife_windows_base.rb +33 -0
- data/lib/chef/knife/windows_cert_generate.rb +155 -0
- data/lib/chef/knife/windows_cert_install.rb +68 -0
- data/lib/chef/knife/windows_helper.rb +36 -0
- data/lib/chef/knife/windows_listener_create.rb +107 -0
- data/lib/chef/knife/winrm.rb +127 -0
- data/lib/chef/knife/winrm_base.rb +128 -0
- data/lib/chef/knife/winrm_knife_base.rb +315 -0
- data/lib/chef/knife/winrm_session.rb +101 -0
- data/lib/chef/knife/winrm_shared_options.rb +54 -0
- data/lib/chef/knife/wsman_endpoint.rb +44 -0
- data/lib/chef/knife/wsman_test.rb +118 -0
- data/lib/knife-winops/path_helper.rb +242 -0
- data/lib/knife-winops/version.rb +6 -0
- data/spec/assets/fake_trusted_certs/excluded.txt +2 -0
- data/spec/assets/fake_trusted_certs/github.pem +42 -0
- data/spec/assets/fake_trusted_certs/google.crt +41 -0
- data/spec/assets/win_fake_trusted_cert_script.txt +89 -0
- data/spec/dummy_winrm_connection.rb +21 -0
- data/spec/functional/bootstrap_download_spec.rb +229 -0
- data/spec/spec_helper.rb +93 -0
- data/spec/unit/knife/bootstrap_options_spec.rb +164 -0
- data/spec/unit/knife/bootstrap_template_spec.rb +98 -0
- data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +410 -0
- data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +292 -0
- data/spec/unit/knife/windows_cert_generate_spec.rb +90 -0
- data/spec/unit/knife/windows_cert_install_spec.rb +51 -0
- data/spec/unit/knife/windows_listener_create_spec.rb +76 -0
- data/spec/unit/knife/winrm_session_spec.rb +101 -0
- data/spec/unit/knife/winrm_spec.rb +494 -0
- data/spec/unit/knife/wsman_test_spec.rb +209 -0
- metadata +157 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
#
|
2
|
+
# Original knife-windows author:: Seth Chisamore (<schisamo@chef.io>)
|
3
|
+
# Copyright:: Copyright (c) 2011-2016 Chef Software, 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
|
+
:long => "--identity-file IDENTITY_FILE",
|
70
|
+
:description => "The SSH identity file used for authentication. [DEPRECATED] Use --ssh-identity-file instead."
|
71
|
+
|
72
|
+
option :ssh_identity_file,
|
73
|
+
:short => "-i IDENTITY_FILE",
|
74
|
+
:long => "--ssh-identity-file IDENTITY_FILE",
|
75
|
+
:description => "The SSH identity file used for authentication"
|
76
|
+
|
77
|
+
# DEPR: Remove this option for the next release.
|
78
|
+
option :host_key_verification,
|
79
|
+
:long => "--[no-]host-key-verify",
|
80
|
+
:description => "Verify host key, enabled by default. [DEPRECATED] Use --host-key-verify option instead.",
|
81
|
+
:boolean => true,
|
82
|
+
:default => true,
|
83
|
+
:proc => Proc.new { |key|
|
84
|
+
Chef::Log.warn("[DEPRECATED] --host-key-verification option is deprecated. Use --host-key-verify option instead.")
|
85
|
+
config[:host_key_verify] = key
|
86
|
+
}
|
87
|
+
|
88
|
+
option :host_key_verify,
|
89
|
+
:long => "--[no-]host-key-verify",
|
90
|
+
:description => "Verify host key, enabled by default.",
|
91
|
+
:boolean => true,
|
92
|
+
:default => true
|
93
|
+
|
94
|
+
def run
|
95
|
+
validate_name_args!
|
96
|
+
bootstrap
|
97
|
+
end
|
98
|
+
|
99
|
+
def run_command(command = '')
|
100
|
+
ssh = Chef::Knife::Ssh.new
|
101
|
+
ssh.name_args = [ server_name, command ]
|
102
|
+
ssh.config[:ssh_user] = locate_config_value(:ssh_user)
|
103
|
+
ssh.config[:ssh_password] = locate_config_value(:ssh_password)
|
104
|
+
ssh.config[:ssh_port] = locate_config_value(:ssh_port)
|
105
|
+
ssh.config[:ssh_gateway] = locate_config_value(:ssh_gateway)
|
106
|
+
ssh.config[:identity_file] = config[:identity_file]
|
107
|
+
ssh.config[:ssh_identity_file] = config[:ssh_identity_file] || config[:identity_file]
|
108
|
+
ssh.config[:forward_agent] = config[:forward_agent]
|
109
|
+
ssh.config[:manual] = true
|
110
|
+
ssh.config[:host_key_verify] = config[:host_key_verify]
|
111
|
+
ssh.run
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
#
|
2
|
+
# Original knife-windows author:: Seth Chisamore (<schisamo@chef.io>)
|
3
|
+
# Copyright:: Copyright (c) 2011-2016 Chef Software, 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
|
+
validate_name_args!
|
44
|
+
|
45
|
+
if (Chef::Config[:validation_key] && !File.exist?(File.expand_path(Chef::Config[:validation_key])))
|
46
|
+
if !negotiate_auth? && !(locate_config_value(:winrm_transport) == 'ssl')
|
47
|
+
ui.error('Validatorless bootstrap over unsecure winrm channels could expose your key to network sniffing')
|
48
|
+
exit 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
unless locate_config_value(:winrm_shell) == :cmd
|
53
|
+
ui.warn("The cmd shell is the only valid winrm-shell for 'knife bootstrap windows winrm'. Switching to the cmd shell.")
|
54
|
+
config[:winrm_shell] = :cmd
|
55
|
+
end
|
56
|
+
|
57
|
+
config[:manual] = true
|
58
|
+
configure_session
|
59
|
+
|
60
|
+
bootstrap
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def wait_for_remote_response(wait_max_minutes)
|
66
|
+
wait_max_seconds = wait_max_minutes * 60
|
67
|
+
retry_interval_seconds = 10
|
68
|
+
retries_left = wait_max_seconds / retry_interval_seconds
|
69
|
+
print(ui.color("\nWaiting for remote response before bootstrap", :magenta))
|
70
|
+
wait_start_time = Time.now
|
71
|
+
begin
|
72
|
+
print(".")
|
73
|
+
# Return status of the command is non-zero, typically nil,
|
74
|
+
# for our simple echo command in cases where run_command
|
75
|
+
# swallows the exception, such as 401's. Treat such cases
|
76
|
+
# the same as the case where we encounter an exception.
|
77
|
+
status = run_command("echo . & echo Response received.")
|
78
|
+
raise RuntimeError, 'Command execution failed.' if status != 0
|
79
|
+
ui.info(ui.color("Remote node responded after #{elapsed_time_in_minutes(wait_start_time)} minutes.", :magenta))
|
80
|
+
return
|
81
|
+
rescue Errno::ECONNREFUSED => e
|
82
|
+
ui.error("Connection refused connecting to #{locate_config_value(:server_name)}:#{locate_config_value(:winrm_port)}.")
|
83
|
+
raise
|
84
|
+
rescue Exception => e
|
85
|
+
retries_left -= 1
|
86
|
+
if retries_left <= 0 || (elapsed_time_in_minutes(wait_start_time) > wait_max_minutes)
|
87
|
+
ui.error("No response received from remote node after #{elapsed_time_in_minutes(wait_start_time)} minutes, giving up.")
|
88
|
+
ui.error("Exception: #{e.message}")
|
89
|
+
raise
|
90
|
+
end
|
91
|
+
print '.'
|
92
|
+
sleep retry_interval_seconds
|
93
|
+
retry
|
94
|
+
end
|
95
|
+
end
|
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
|
@@ -0,0 +1,356 @@
|
|
1
|
+
#
|
2
|
+
# Original knife-windows author:: Seth Chisamore (<schisamo@chef.io>)
|
3
|
+
# Copyright:: Copyright (c) 2011-2016 Chef Software, 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
|
+
# Chef::Util::PathHelper in Chef 11 is a bit juvenile still
|
21
|
+
require 'knife-winops/path_helper'
|
22
|
+
# require 'chef/util/path_helper'
|
23
|
+
require 'chef/knife/core/windows_bootstrap_context'
|
24
|
+
|
25
|
+
class Chef
|
26
|
+
class Knife
|
27
|
+
module Core
|
28
|
+
# Instances of BootstrapContext are the context objects (i.e., +self+) for
|
29
|
+
# bootstrap templates. For backwards compatability, they +must+ set the
|
30
|
+
# following instance variables:
|
31
|
+
# * @config - a hash of knife's config values
|
32
|
+
# * @run_list - the run list for the node to boostrap
|
33
|
+
#
|
34
|
+
class WindowsBootstrapContext < BootstrapContext
|
35
|
+
PathHelper = ::Knife::Windows::PathHelper
|
36
|
+
|
37
|
+
attr_accessor :client_pem
|
38
|
+
|
39
|
+
def initialize(config, run_list, chef_config, secret=nil)
|
40
|
+
@config = config
|
41
|
+
@run_list = run_list
|
42
|
+
@chef_config = chef_config
|
43
|
+
@secret = secret
|
44
|
+
# Compatibility with Chef 12 and Chef 11 versions
|
45
|
+
begin
|
46
|
+
# Pass along the secret parameter for Chef 12
|
47
|
+
super(config, run_list, chef_config, secret)
|
48
|
+
rescue ArgumentError
|
49
|
+
# The Chef 11 base class only has parameters for initialize
|
50
|
+
super(config, run_list, chef_config)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def validation_key
|
55
|
+
if File.exist?(File.expand_path(@chef_config[:validation_key]))
|
56
|
+
IO.read(File.expand_path(@chef_config[:validation_key]))
|
57
|
+
else
|
58
|
+
false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def secret
|
63
|
+
if @config[:secret].nil?
|
64
|
+
nil
|
65
|
+
else
|
66
|
+
escape_and_echo(@config[:secret])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def trusted_certs_script
|
71
|
+
@trusted_certs_script ||= trusted_certs_content
|
72
|
+
end
|
73
|
+
|
74
|
+
def config_content
|
75
|
+
client_rb = <<-CONFIG
|
76
|
+
chef_server_url "#{@chef_config[:chef_server_url]}"
|
77
|
+
validation_client_name "#{@chef_config[:validation_client_name]}"
|
78
|
+
file_cache_path "c:/chef/cache"
|
79
|
+
file_backup_path "c:/chef/backup"
|
80
|
+
cache_options ({:path => "c:/chef/cache/checksums", :skip_expires => true})
|
81
|
+
CONFIG
|
82
|
+
if @config[:chef_node_name]
|
83
|
+
client_rb << %Q{node_name "#{@config[:chef_node_name]}"\n}
|
84
|
+
else
|
85
|
+
client_rb << "# Using default node name (fqdn)\n"
|
86
|
+
end
|
87
|
+
|
88
|
+
if @chef_config[:config_log_level]
|
89
|
+
client_rb << %Q{log_level :#{@chef_config[:config_log_level]}\n}
|
90
|
+
else
|
91
|
+
client_rb << "log_level :info\n"
|
92
|
+
end
|
93
|
+
|
94
|
+
client_rb << "log_location #{get_log_location}"
|
95
|
+
|
96
|
+
# We configure :verify_api_cert only when it's overridden on the CLI
|
97
|
+
# or when specified in the knife config.
|
98
|
+
if !@config[:node_verify_api_cert].nil? || knife_config.has_key?(:verify_api_cert)
|
99
|
+
value = @config[:node_verify_api_cert].nil? ? knife_config[:verify_api_cert] : @config[:node_verify_api_cert]
|
100
|
+
client_rb << %Q{verify_api_cert #{value}\n}
|
101
|
+
end
|
102
|
+
|
103
|
+
# We configure :ssl_verify_mode only when it's overridden on the CLI
|
104
|
+
# or when specified in the knife config.
|
105
|
+
if @config[:node_ssl_verify_mode] || knife_config.has_key?(:ssl_verify_mode)
|
106
|
+
value = case @config[:node_ssl_verify_mode]
|
107
|
+
when "peer"
|
108
|
+
:verify_peer
|
109
|
+
when "none"
|
110
|
+
:verify_none
|
111
|
+
when nil
|
112
|
+
knife_config[:ssl_verify_mode]
|
113
|
+
else
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
|
117
|
+
if value
|
118
|
+
client_rb << %Q{ssl_verify_mode :#{value}\n}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
if @config[:ssl_verify_mode]
|
123
|
+
client_rb << %Q{ssl_verify_mode :#{knife_config[:ssl_verify_mode]}\n}
|
124
|
+
end
|
125
|
+
|
126
|
+
if knife_config[:bootstrap_proxy]
|
127
|
+
client_rb << "\n"
|
128
|
+
client_rb << %Q{http_proxy "#{knife_config[:bootstrap_proxy]}"\n}
|
129
|
+
client_rb << %Q{https_proxy "#{knife_config[:bootstrap_proxy]}"\n}
|
130
|
+
client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n} if knife_config[:bootstrap_no_proxy]
|
131
|
+
end
|
132
|
+
|
133
|
+
if knife_config[:bootstrap_no_proxy]
|
134
|
+
client_rb << %Q{no_proxy "#{knife_config[:bootstrap_no_proxy]}"\n}
|
135
|
+
end
|
136
|
+
|
137
|
+
if @config[:secret]
|
138
|
+
client_rb << %Q{encrypted_data_bag_secret "c:/chef/encrypted_data_bag_secret"\n}
|
139
|
+
end
|
140
|
+
|
141
|
+
unless trusted_certs_script.empty?
|
142
|
+
client_rb << %Q{trusted_certs_dir "c:/chef/trusted_certs"\n}
|
143
|
+
end
|
144
|
+
|
145
|
+
if Chef::Config[:fips]
|
146
|
+
client_rb << <<-CONFIG
|
147
|
+
fips true
|
148
|
+
chef_version = ::Chef::VERSION.split(".")
|
149
|
+
unless chef_version[0].to_i > 12 || (chef_version[0].to_i == 12 && chef_version[1].to_i >= 8)
|
150
|
+
raise "FIPS Mode requested but not supported by this client"
|
151
|
+
end
|
152
|
+
CONFIG
|
153
|
+
end
|
154
|
+
|
155
|
+
escape_and_echo(client_rb)
|
156
|
+
end
|
157
|
+
|
158
|
+
def get_log_location
|
159
|
+
if @chef_config[:config_log_location].equal?(:win_evt)
|
160
|
+
%Q{:#{@chef_config[:config_log_location]}\n}
|
161
|
+
elsif @chef_config[:config_log_location].equal?(:syslog)
|
162
|
+
raise "syslog is not supported for log_location on Windows OS\n"
|
163
|
+
elsif (@chef_config[:config_log_location].equal?(STDOUT))
|
164
|
+
"STDOUT\n"
|
165
|
+
elsif (@chef_config[:config_log_location].equal?(STDERR))
|
166
|
+
"STDERR\n"
|
167
|
+
elsif @chef_config[:config_log_location].nil? || @chef_config[:config_log_location].empty?
|
168
|
+
"STDOUT\n"
|
169
|
+
elsif @chef_config[:config_log_location]
|
170
|
+
%Q{"#{@chef_config[:config_log_location]}"\n}
|
171
|
+
else
|
172
|
+
"STDOUT\n"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def chef_version_in_url
|
177
|
+
installer_version_string = nil
|
178
|
+
if @config[:bootstrap_version]
|
179
|
+
installer_version_string = "&v=#{@config[:bootstrap_version]}"
|
180
|
+
elsif @config[:prerelease]
|
181
|
+
installer_version_string = "&prerelease=true"
|
182
|
+
else
|
183
|
+
chef_version_string = if knife_config[:bootstrap_version]
|
184
|
+
knife_config[:bootstrap_version]
|
185
|
+
else
|
186
|
+
Chef::VERSION.split(".").first
|
187
|
+
end
|
188
|
+
|
189
|
+
installer_version_string = "&v=#{chef_version_string}"
|
190
|
+
|
191
|
+
# If bootstrapping a pre-release version add the prerelease query string
|
192
|
+
if chef_version_string.split(".").length > 3
|
193
|
+
installer_version_string << "&prerelease=true"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
installer_version_string
|
198
|
+
end
|
199
|
+
|
200
|
+
def win_ps_write_filechunk
|
201
|
+
win_ps_write_filechunk = <<-PS_WRITEFILECHUNK
|
202
|
+
$data=$args[0]
|
203
|
+
$filename=$args[1]
|
204
|
+
$bytes = @()
|
205
|
+
if (Test-Path $filename) { $bytes = [System.IO.File]::ReadAllBytes($filename) }
|
206
|
+
$bytes += $args[0]
|
207
|
+
[io.file]::WriteAllBytes($filename,$bytes)
|
208
|
+
PS_WRITEFILECHUNK
|
209
|
+
escape_and_echo(win_ps_write_filechunk)
|
210
|
+
end
|
211
|
+
|
212
|
+
def win_cmd_wait_for_file(filename_in_envvar)
|
213
|
+
win_cmd_wait_for_file = <<-filename_in_envvar
|
214
|
+
:waitfor#{filename_in_envvar}
|
215
|
+
@if NOT EXIST "%#{filename_in_envvar}%" (
|
216
|
+
@powershell.exe -command Start-Sleep 1
|
217
|
+
@echo %#{filename_in_envvar}% does not exist yet.
|
218
|
+
@goto waitfor#{filename_in_envvar}
|
219
|
+
) else (
|
220
|
+
@echo Logfile %#{filename_in_envvar}% found.
|
221
|
+
)
|
222
|
+
filename_in_envvar
|
223
|
+
win_cmd_wait_for_file
|
224
|
+
end
|
225
|
+
|
226
|
+
def win_cmd_tail(target_filename)
|
227
|
+
cmd_tail_file = Gem.find_files(File.join('chef', 'knife', 'bootstrap', 'tail.cmd')).first
|
228
|
+
cmd_tail_content = IO.read(cmd_tail_file)
|
229
|
+
win_parse_file_content(cmd_tail_content, target_filename)
|
230
|
+
end
|
231
|
+
|
232
|
+
def bootstrap_context
|
233
|
+
@bootstrap_context ||= Knife::Core::WindowsBootstrapContext.new(@config, @config[:run_list], Chef::Config)
|
234
|
+
end
|
235
|
+
|
236
|
+
def win_ps_bootstrap(target_filename)
|
237
|
+
ps_bootstrap_file = Gem.find_files(File.join('chef', 'knife', 'bootstrap', 'bootstrap.ps1')).first
|
238
|
+
ps_bootstrap_content = IO.read(ps_bootstrap_file)
|
239
|
+
win_parse_file_content(ps_bootstrap_content, target_filename)
|
240
|
+
end
|
241
|
+
|
242
|
+
def win_schedtask_xml(target_filename)
|
243
|
+
sched_xml_file = Gem.find_files(File.join('chef', 'knife', 'bootstrap', 'Chef_bootstrap.erb')).first
|
244
|
+
sched_xml_file_content = IO.read(sched_xml_file).chomp
|
245
|
+
win_parse_file_content(Erubis::Eruby.new(sched_xml_file_content).evaluate(bootstrap_context), target_filename)
|
246
|
+
end
|
247
|
+
|
248
|
+
def win_folder_cp(folder_src,folder_dest, folder_src_original = nil)
|
249
|
+
win_folder_cp = ''
|
250
|
+
Dir.foreach(folder_src) do |item|
|
251
|
+
next if item == '.' or item == '..'
|
252
|
+
folder_src_original = folder_src_original ? folder_src_original : folder_src
|
253
|
+
if File.file?(File.join(folder_src,item))
|
254
|
+
filename_target = File.join(folder_src,item).gsub(folder_src_original, folder_dest).gsub("/","\\")
|
255
|
+
win_folder_cp << win_parse_file_content(IO.read(File.join(folder_src,item)).force_encoding('binary'), filename_target)
|
256
|
+
else
|
257
|
+
folder = File.join(folder_src,item).gsub(folder_src_original, folder_dest).gsub("/","\\")
|
258
|
+
win_folder_cp << "mkdir #{folder}\n"
|
259
|
+
win_folder_cp << win_folder_cp(File.join(folder_src,item), folder_dest, folder_src_original)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
win_folder_cp
|
263
|
+
end
|
264
|
+
|
265
|
+
def win_parse_file_content(content, target_filename)
|
266
|
+
byte_chunks = data_to_byte_chunks(content, 2000)
|
267
|
+
win_parse_file_content = ''
|
268
|
+
win_parse_file_content << "del \"#{target_filename}\" /Q 2> nul\n"
|
269
|
+
byte_chunks.each { |file_chunk|
|
270
|
+
if file_chunk.length > 0
|
271
|
+
win_parse_file_content << write_file_chunk(file_chunk, target_filename)
|
272
|
+
end
|
273
|
+
}
|
274
|
+
win_parse_file_content
|
275
|
+
end
|
276
|
+
|
277
|
+
def write_file_chunk(byte_chunk, target_filename)
|
278
|
+
"@powershell.exe -command #{bootstrap_directory}\\writefile.ps1 #{byte_chunk} #{target_filename}\n"
|
279
|
+
end
|
280
|
+
|
281
|
+
def data_to_byte_chunks(data, max_chuck_size)
|
282
|
+
chunk = ''
|
283
|
+
chunk_buffer = ''
|
284
|
+
data_to_byte_chunks = []
|
285
|
+
data.split('').each { |char|
|
286
|
+
chunk_buffer << char.ord.to_s << ','
|
287
|
+
if (chunk_buffer.length) >= max_chuck_size
|
288
|
+
data_to_byte_chunks.push(chunk_buffer.chomp(','))
|
289
|
+
chunk_buffer = ''
|
290
|
+
end
|
291
|
+
}
|
292
|
+
data_to_byte_chunks.push(chunk_buffer.chomp(','))
|
293
|
+
data_to_byte_chunks
|
294
|
+
end
|
295
|
+
|
296
|
+
|
297
|
+
def win_ps_exitcheck
|
298
|
+
<<-ps_exitcheck
|
299
|
+
@powershell.exe -command Start-Sleep 1
|
300
|
+
@echo off
|
301
|
+
REM Wait for the file to exist
|
302
|
+
:wff
|
303
|
+
if NOT EXIST "%CHEF_PS_EXITCODE%" (
|
304
|
+
goto wff
|
305
|
+
)
|
306
|
+
if EXIST %CHEF_PS_EXITCODE% (
|
307
|
+
setlocal disabledelayedexpansion
|
308
|
+
for /f "tokens=1* delims=]" %%A in ('type "%CHEF_PS_EXITCODE%"^|find /v /n ""') do (
|
309
|
+
set psexitcode=%%B
|
310
|
+
setlocal enabledelayedexpansion
|
311
|
+
)
|
312
|
+
) else (
|
313
|
+
echo %CHEF_PS_EXITCODE% not found. This should never happen
|
314
|
+
exit 328
|
315
|
+
)
|
316
|
+
ps_exitcheck
|
317
|
+
end
|
318
|
+
|
319
|
+
def bootstrap_directory
|
320
|
+
bootstrap_directory = "C:\\chef"
|
321
|
+
end
|
322
|
+
|
323
|
+
def local_download_path
|
324
|
+
local_download_path = "%TEMP%\\chef-client-latest.msi"
|
325
|
+
end
|
326
|
+
|
327
|
+
def first_boot
|
328
|
+
escape_and_echo(super.to_json)
|
329
|
+
end
|
330
|
+
|
331
|
+
# escape WIN BATCH special chars
|
332
|
+
# and prefixes each line with an
|
333
|
+
# echo
|
334
|
+
def escape_and_echo(file_contents)
|
335
|
+
file_contents.gsub(/^(.*)$/, 'echo.\1').gsub(/([(<|>)^])/, '^\1').gsub(/(!)/, '^^\1').gsub(/(%)/,'%\1')
|
336
|
+
end
|
337
|
+
|
338
|
+
private
|
339
|
+
|
340
|
+
# Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
|
341
|
+
# This string should contain both the commands necessary to both create the files, as well as their content
|
342
|
+
def trusted_certs_content
|
343
|
+
content = ""
|
344
|
+
if @chef_config[:trusted_certs_dir]
|
345
|
+
Dir.glob(File.join(PathHelper.escape_glob_dir(@chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert|
|
346
|
+
content << "> #{bootstrap_directory}/trusted_certs/#{File.basename(cert)} (\n" +
|
347
|
+
escape_and_echo(IO.read(File.expand_path(cert))) + "\n)\n"
|
348
|
+
end
|
349
|
+
end
|
350
|
+
content
|
351
|
+
end
|
352
|
+
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|