knife-windows 1.5.0 → 1.6.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +26 -26
  3. data/CHANGELOG.md +131 -121
  4. data/DOC_CHANGES.md +22 -14
  5. data/Gemfile +14 -13
  6. data/README.md +400 -392
  7. data/RELEASE_NOTES.md +2 -26
  8. data/appveyor.yml +39 -39
  9. data/ci.gemfile +16 -16
  10. data/knife-windows.gemspec +25 -25
  11. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +246 -233
  12. data/lib/chef/knife/bootstrap_windows_base.rb +443 -454
  13. data/lib/chef/knife/bootstrap_windows_ssh.rb +116 -115
  14. data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -95
  15. data/lib/chef/knife/core/windows_bootstrap_context.rb +378 -378
  16. data/lib/chef/knife/knife_windows_base.rb +33 -33
  17. data/lib/chef/knife/windows_cert_generate.rb +155 -155
  18. data/lib/chef/knife/windows_cert_install.rb +68 -68
  19. data/lib/chef/knife/windows_helper.rb +36 -36
  20. data/lib/chef/knife/windows_listener_create.rb +107 -107
  21. data/lib/chef/knife/winrm.rb +122 -122
  22. data/lib/chef/knife/winrm_base.rb +123 -117
  23. data/lib/chef/knife/winrm_knife_base.rb +306 -305
  24. data/lib/chef/knife/winrm_session.rb +97 -91
  25. data/lib/chef/knife/winrm_shared_options.rb +47 -47
  26. data/lib/chef/knife/wsman_endpoint.rb +44 -44
  27. data/lib/chef/knife/wsman_test.rb +118 -118
  28. data/lib/knife-windows/path_helper.rb +234 -234
  29. data/lib/knife-windows/version.rb +6 -6
  30. data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +223 -223
  31. data/spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt +223 -223
  32. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +335 -335
  33. data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt +335 -335
  34. data/spec/dummy_winrm_connection.rb +21 -0
  35. data/spec/functional/bootstrap_download_spec.rb +236 -241
  36. data/spec/spec_helper.rb +94 -94
  37. data/spec/unit/knife/bootstrap_options_spec.rb +157 -155
  38. data/spec/unit/knife/bootstrap_template_spec.rb +98 -98
  39. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +423 -426
  40. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +177 -177
  41. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -90
  42. data/spec/unit/knife/windows_cert_install_spec.rb +51 -51
  43. data/spec/unit/knife/windows_listener_create_spec.rb +76 -76
  44. data/spec/unit/knife/winrm_session_spec.rb +71 -76
  45. data/spec/unit/knife/winrm_spec.rb +500 -508
  46. data/spec/unit/knife/wsman_test_spec.rb +209 -209
  47. metadata +16 -17
  48. data/spec/dummy_winrm_service.rb +0 -24
@@ -1,115 +1,116 @@
1
- #
2
- # 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
- bootstrap
96
- end
97
-
98
- def run_command(command = '')
99
- ssh = Chef::Knife::Ssh.new
100
- ssh.name_args = [ server_name, command ]
101
- ssh.config[:ssh_user] = locate_config_value(:ssh_user)
102
- ssh.config[:ssh_password] = locate_config_value(:ssh_password)
103
- ssh.config[:ssh_port] = locate_config_value(:ssh_port)
104
- ssh.config[:ssh_gateway] = locate_config_value(:ssh_gateway)
105
- ssh.config[:identity_file] = config[:identity_file]
106
- ssh.config[:ssh_identity_file] = config[:ssh_identity_file] || config[:identity_file]
107
- ssh.config[:forward_agent] = config[:forward_agent]
108
- ssh.config[:manual] = true
109
- ssh.config[:host_key_verify] = config[:host_key_verify]
110
- ssh.run
111
- end
112
-
113
- end
114
- end
115
- end
1
+ #
2
+ # 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
+
114
+ end
115
+ end
116
+ end
@@ -1,95 +1,102 @@
1
- #
2
- # 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
- 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
+ #
2
+ # 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
@@ -1,378 +1,378 @@
1
- #
2
- # 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
-
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
- if Chef::Config[:fips]
140
- client_rb << <<-CONFIG
141
- fips true
142
- chef_version = ::Chef::VERSION.split(".")
143
- unless chef_version[0].to_i > 12 || (chef_version[0].to_i == 12 && chef_version[1].to_i >= 8)
144
- raise "FIPS Mode requested but not supported by this client"
145
- end
146
- CONFIG
147
- end
148
-
149
- escape_and_echo(client_rb)
150
- end
151
-
152
- def start_chef
153
- bootstrap_environment_option = bootstrap_environment.nil? ? '' : " -E #{bootstrap_environment}"
154
- start_chef = "SET \"PATH=%PATH%;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin\"\n"
155
- start_chef << "chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json#{bootstrap_environment_option}\n"
156
- end
157
-
158
- def latest_current_windows_chef_version_query
159
- installer_version_string = nil
160
- if @config[:prerelease]
161
- installer_version_string = "&prerelease=true"
162
- else
163
- chef_version_string = if knife_config[:bootstrap_version]
164
- knife_config[:bootstrap_version]
165
- else
166
- Chef::VERSION.split(".").first
167
- end
168
-
169
- installer_version_string = "&v=#{chef_version_string}"
170
-
171
- # If bootstrapping a pre-release version add the prerelease query string
172
- if chef_version_string.split(".").length > 3
173
- installer_version_string << "&prerelease=true"
174
- end
175
- end
176
-
177
- installer_version_string
178
- end
179
-
180
- def win_wget
181
- # I tried my best to figure out how to properly url decode and switch / to \
182
- # but this is VBScript - so I don't really care that badly.
183
- win_wget = <<-WGET
184
- url = WScript.Arguments.Named("url")
185
- path = WScript.Arguments.Named("path")
186
- proxy = null
187
- '* Vaguely attempt to handle file:// scheme urls by url unescaping and switching all
188
- '* / into \. Also assume that file:/// is a local absolute path and that file://<foo>
189
- '* is possibly a network file path.
190
- If InStr(url, "file://") = 1 Then
191
- url = Unescape(url)
192
- If InStr(url, "file:///") = 1 Then
193
- sourcePath = Mid(url, Len("file:///") + 1)
194
- Else
195
- sourcePath = Mid(url, Len("file:") + 1)
196
- End If
197
- sourcePath = Replace(sourcePath, "/", "\\")
198
-
199
- Set objFSO = CreateObject("Scripting.FileSystemObject")
200
- If objFSO.Fileexists(path) Then objFSO.DeleteFile path
201
- objFSO.CopyFile sourcePath, path, true
202
- Set objFSO = Nothing
203
-
204
- Else
205
- Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP")
206
- Set wshShell = CreateObject( "WScript.Shell" )
207
- Set objUserVariables = wshShell.Environment("USER")
208
-
209
- rem http proxy is optional
210
- rem attempt to read from HTTP_PROXY env var first
211
- On Error Resume Next
212
-
213
- If NOT (objUserVariables("HTTP_PROXY") = "") Then
214
- proxy = objUserVariables("HTTP_PROXY")
215
-
216
- rem fall back to named arg
217
- ElseIf NOT (WScript.Arguments.Named("proxy") = "") Then
218
- proxy = WScript.Arguments.Named("proxy")
219
- End If
220
-
221
- If NOT isNull(proxy) Then
222
- rem setProxy method is only available on ServerXMLHTTP 6.0+
223
- Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0")
224
- objXMLHTTP.setProxy 2, proxy
225
- End If
226
-
227
- On Error Goto 0
228
-
229
- objXMLHTTP.open "GET", url, false
230
- objXMLHTTP.send()
231
- If objXMLHTTP.Status = 200 Then
232
- Set objADOStream = CreateObject("ADODB.Stream")
233
- objADOStream.Open
234
- objADOStream.Type = 1
235
- objADOStream.Write objXMLHTTP.ResponseBody
236
- objADOStream.Position = 0
237
- Set objFSO = Createobject("Scripting.FileSystemObject")
238
- If objFSO.Fileexists(path) Then objFSO.DeleteFile path
239
- Set objFSO = Nothing
240
- objADOStream.SaveToFile path
241
- objADOStream.Close
242
- Set objADOStream = Nothing
243
- End If
244
- Set objXMLHTTP = Nothing
245
- End If
246
- WGET
247
- escape_and_echo(win_wget)
248
- end
249
-
250
- def win_wget_ps
251
- win_wget_ps = <<-WGET_PS
252
- param(
253
- [String] $remoteUrl,
254
- [String] $localPath
255
- )
256
-
257
- $ProxyUrl = $env:http_proxy;
258
- $webClient = new-object System.Net.WebClient;
259
-
260
- if ($ProxyUrl -ne '') {
261
- $WebProxy = New-Object System.Net.WebProxy($ProxyUrl,$true)
262
- $WebClient.Proxy = $WebProxy
263
- }
264
-
265
- $webClient.DownloadFile($remoteUrl, $localPath);
266
- WGET_PS
267
-
268
- escape_and_echo(win_wget_ps)
269
- end
270
-
271
- def install_chef
272
- # The normal install command uses regular double quotes in
273
- # the install command, so request such a string from install_command
274
- install_chef = install_command('"') + "\n" + fallback_install_task_command
275
- end
276
-
277
- def bootstrap_directory
278
- bootstrap_directory = "C:\\chef"
279
- end
280
-
281
- def local_download_path
282
- local_download_path = "%TEMP%\\chef-client-latest.msi"
283
- end
284
-
285
- def msi_url(machine_os=nil, machine_arch=nil, download_context=nil)
286
- # The default msi path has a number of url query parameters - we attempt to substitute
287
- # such parameters in as long as they are provided by the template.
288
-
289
- if @config[:msi_url].nil? || @config[:msi_url].empty?
290
- url = "https://www.chef.io/chef/download?p=windows"
291
- url += "&pv=#{machine_os}" unless machine_os.nil?
292
- url += "&m=#{machine_arch}" unless machine_arch.nil?
293
- url += "&DownloadContext=#{download_context}" unless download_context.nil?
294
- url += latest_current_windows_chef_version_query
295
- else
296
- @config[:msi_url]
297
- end
298
- end
299
-
300
- def first_boot
301
- escape_and_echo(super.to_json)
302
- end
303
-
304
- # escape WIN BATCH special chars
305
- # and prefixes each line with an
306
- # echo
307
- def escape_and_echo(file_contents)
308
- file_contents.gsub(/^(.*)$/, 'echo.\1').gsub(/([(<|>)^])/, '^\1')
309
- end
310
-
311
- private
312
-
313
- def install_command(executor_quote)
314
- if @config[:install_as_service]
315
- "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}"
316
- else
317
- "msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote}"
318
- end
319
- end
320
-
321
- # Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
322
- # This string should contain both the commands necessary to both create the files, as well as their content
323
- def trusted_certs_content
324
- content = ""
325
- if @chef_config[:trusted_certs_dir]
326
- Dir.glob(File.join(PathHelper.escape_glob(@chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert|
327
- content << "> #{bootstrap_directory}/trusted_certs/#{File.basename(cert)} (\n" +
328
- escape_and_echo(IO.read(File.expand_path(cert))) + "\n)\n"
329
- end
330
- end
331
- content
332
- end
333
-
334
- def fallback_install_task_command
335
- # This command will be executed by schtasks.exe in the batch
336
- # code below. To handle tasks that contain arguments that
337
- # need to be double quoted, schtasks allows the use of single
338
- # quotes that will later be converted to double quotes
339
- command = install_command('\'')
340
- <<-EOH
341
- @set MSIERRORCODE=!ERRORLEVEL!
342
- @if ERRORLEVEL 1 (
343
- @echo WARNING: Failed to install Chef Client MSI package in remote context with status code !MSIERRORCODE!.
344
- @echo WARNING: This may be due to a defect in operating system update KB2918614: http://support.microsoft.com/kb/2918614
345
- @set OLDLOGLOCATION="%CHEF_CLIENT_MSI_LOG_PATH%-fail.log"
346
- @move "%CHEF_CLIENT_MSI_LOG_PATH%" "!OLDLOGLOCATION!" > NUL
347
- @echo WARNING: Saving installation log of failure at !OLDLOGLOCATION!
348
- @echo WARNING: Retrying installation with local context...
349
- @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\"
350
-
351
- @if ERRORLEVEL 1 (
352
- @echo ERROR: Failed to create Chef Client installation scheduled task with status code !ERRORLEVEL! > "&2"
353
- ) else (
354
- @echo Successfully created scheduled task to install Chef Client.
355
- @schtasks /run /tn chefclientbootstraptask
356
- @if ERRORLEVEL 1 (
357
- @echo ERROR: Failed to execut Chef Client installation scheduled task with status code !ERRORLEVEL!. > "&2"
358
- ) else (
359
- @echo Successfully started Chef Client installation scheduled task.
360
- @echo Waiting for installation to complete -- this may take a few minutes...
361
- waitfor chefclientinstalldone /t 600
362
- if ERRORLEVEL 1 (
363
- @echo ERROR: Timed out waiting for Chef Client package to install
364
- ) else (
365
- @echo Finished waiting for Chef Client package to install.
366
- )
367
- @schtasks /delete /f /tn chefclientbootstraptask > NUL
368
- )
369
- )
370
- ) else (
371
- @echo Successfully installed Chef Client package.
372
- )
373
- EOH
374
- end
375
- end
376
- end
377
- end
378
- end
1
+ #
2
+ # 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
+
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
+ if Chef::Config[:fips]
140
+ client_rb << <<-CONFIG
141
+ fips true
142
+ chef_version = ::Chef::VERSION.split(".")
143
+ unless chef_version[0].to_i > 12 || (chef_version[0].to_i == 12 && chef_version[1].to_i >= 8)
144
+ raise "FIPS Mode requested but not supported by this client"
145
+ end
146
+ CONFIG
147
+ end
148
+
149
+ escape_and_echo(client_rb)
150
+ end
151
+
152
+ def start_chef
153
+ bootstrap_environment_option = bootstrap_environment.nil? ? '' : " -E #{bootstrap_environment}"
154
+ start_chef = "SET \"PATH=%PATH%;C:\\ruby\\bin;C:\\opscode\\chef\\bin;C:\\opscode\\chef\\embedded\\bin\"\n"
155
+ start_chef << "chef-client -c c:/chef/client.rb -j c:/chef/first-boot.json#{bootstrap_environment_option}\n"
156
+ end
157
+
158
+ def latest_current_windows_chef_version_query
159
+ installer_version_string = nil
160
+ if @config[:prerelease]
161
+ installer_version_string = "&prerelease=true"
162
+ else
163
+ chef_version_string = if knife_config[:bootstrap_version]
164
+ knife_config[:bootstrap_version]
165
+ else
166
+ Chef::VERSION.split(".").first
167
+ end
168
+
169
+ installer_version_string = "&v=#{chef_version_string}"
170
+
171
+ # If bootstrapping a pre-release version add the prerelease query string
172
+ if chef_version_string.split(".").length > 3
173
+ installer_version_string << "&prerelease=true"
174
+ end
175
+ end
176
+
177
+ installer_version_string
178
+ end
179
+
180
+ def win_wget
181
+ # I tried my best to figure out how to properly url decode and switch / to \
182
+ # but this is VBScript - so I don't really care that badly.
183
+ win_wget = <<-WGET
184
+ url = WScript.Arguments.Named("url")
185
+ path = WScript.Arguments.Named("path")
186
+ proxy = null
187
+ '* Vaguely attempt to handle file:// scheme urls by url unescaping and switching all
188
+ '* / into \. Also assume that file:/// is a local absolute path and that file://<foo>
189
+ '* is possibly a network file path.
190
+ If InStr(url, "file://") = 1 Then
191
+ url = Unescape(url)
192
+ If InStr(url, "file:///") = 1 Then
193
+ sourcePath = Mid(url, Len("file:///") + 1)
194
+ Else
195
+ sourcePath = Mid(url, Len("file:") + 1)
196
+ End If
197
+ sourcePath = Replace(sourcePath, "/", "\\")
198
+
199
+ Set objFSO = CreateObject("Scripting.FileSystemObject")
200
+ If objFSO.Fileexists(path) Then objFSO.DeleteFile path
201
+ objFSO.CopyFile sourcePath, path, true
202
+ Set objFSO = Nothing
203
+
204
+ Else
205
+ Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP")
206
+ Set wshShell = CreateObject( "WScript.Shell" )
207
+ Set objUserVariables = wshShell.Environment("USER")
208
+
209
+ rem http proxy is optional
210
+ rem attempt to read from HTTP_PROXY env var first
211
+ On Error Resume Next
212
+
213
+ If NOT (objUserVariables("HTTP_PROXY") = "") Then
214
+ proxy = objUserVariables("HTTP_PROXY")
215
+
216
+ rem fall back to named arg
217
+ ElseIf NOT (WScript.Arguments.Named("proxy") = "") Then
218
+ proxy = WScript.Arguments.Named("proxy")
219
+ End If
220
+
221
+ If NOT isNull(proxy) Then
222
+ rem setProxy method is only available on ServerXMLHTTP 6.0+
223
+ Set objXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP.6.0")
224
+ objXMLHTTP.setProxy 2, proxy
225
+ End If
226
+
227
+ On Error Goto 0
228
+
229
+ objXMLHTTP.open "GET", url, false
230
+ objXMLHTTP.send()
231
+ If objXMLHTTP.Status = 200 Then
232
+ Set objADOStream = CreateObject("ADODB.Stream")
233
+ objADOStream.Open
234
+ objADOStream.Type = 1
235
+ objADOStream.Write objXMLHTTP.ResponseBody
236
+ objADOStream.Position = 0
237
+ Set objFSO = Createobject("Scripting.FileSystemObject")
238
+ If objFSO.Fileexists(path) Then objFSO.DeleteFile path
239
+ Set objFSO = Nothing
240
+ objADOStream.SaveToFile path
241
+ objADOStream.Close
242
+ Set objADOStream = Nothing
243
+ End If
244
+ Set objXMLHTTP = Nothing
245
+ End If
246
+ WGET
247
+ escape_and_echo(win_wget)
248
+ end
249
+
250
+ def win_wget_ps
251
+ win_wget_ps = <<-WGET_PS
252
+ param(
253
+ [String] $remoteUrl,
254
+ [String] $localPath
255
+ )
256
+
257
+ $ProxyUrl = $env:http_proxy;
258
+ $webClient = new-object System.Net.WebClient;
259
+
260
+ if ($ProxyUrl -ne '') {
261
+ $WebProxy = New-Object System.Net.WebProxy($ProxyUrl,$true)
262
+ $WebClient.Proxy = $WebProxy
263
+ }
264
+
265
+ $webClient.DownloadFile($remoteUrl, $localPath);
266
+ WGET_PS
267
+
268
+ escape_and_echo(win_wget_ps)
269
+ end
270
+
271
+ def install_chef
272
+ # The normal install command uses regular double quotes in
273
+ # the install command, so request such a string from install_command
274
+ install_chef = install_command('"') + "\n" + fallback_install_task_command
275
+ end
276
+
277
+ def bootstrap_directory
278
+ bootstrap_directory = "C:\\chef"
279
+ end
280
+
281
+ def local_download_path
282
+ local_download_path = "%TEMP%\\chef-client-latest.msi"
283
+ end
284
+
285
+ def msi_url(machine_os=nil, machine_arch=nil, download_context=nil)
286
+ # The default msi path has a number of url query parameters - we attempt to substitute
287
+ # such parameters in as long as they are provided by the template.
288
+
289
+ if @config[:msi_url].nil? || @config[:msi_url].empty?
290
+ url = "https://www.chef.io/chef/download?p=windows"
291
+ url += "&pv=#{machine_os}" unless machine_os.nil?
292
+ url += "&m=#{machine_arch}" unless machine_arch.nil?
293
+ url += "&DownloadContext=#{download_context}" unless download_context.nil?
294
+ url += latest_current_windows_chef_version_query
295
+ else
296
+ @config[:msi_url]
297
+ end
298
+ end
299
+
300
+ def first_boot
301
+ escape_and_echo(super.to_json)
302
+ end
303
+
304
+ # escape WIN BATCH special chars
305
+ # and prefixes each line with an
306
+ # echo
307
+ def escape_and_echo(file_contents)
308
+ file_contents.gsub(/^(.*)$/, 'echo.\1').gsub(/([(<|>)^])/, '^\1')
309
+ end
310
+
311
+ private
312
+
313
+ def install_command(executor_quote)
314
+ if @config[:install_as_service]
315
+ "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}"
316
+ else
317
+ "msiexec /qn /log #{executor_quote}%CHEF_CLIENT_MSI_LOG_PATH%#{executor_quote} /i #{executor_quote}%LOCAL_DESTINATION_MSI_PATH%#{executor_quote}"
318
+ end
319
+ end
320
+
321
+ # Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
322
+ # This string should contain both the commands necessary to both create the files, as well as their content
323
+ def trusted_certs_content
324
+ content = ""
325
+ if @chef_config[:trusted_certs_dir]
326
+ Dir.glob(File.join(PathHelper.escape_glob(@chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert|
327
+ content << "> #{bootstrap_directory}/trusted_certs/#{File.basename(cert)} (\n" +
328
+ escape_and_echo(IO.read(File.expand_path(cert))) + "\n)\n"
329
+ end
330
+ end
331
+ content
332
+ end
333
+
334
+ def fallback_install_task_command
335
+ # This command will be executed by schtasks.exe in the batch
336
+ # code below. To handle tasks that contain arguments that
337
+ # need to be double quoted, schtasks allows the use of single
338
+ # quotes that will later be converted to double quotes
339
+ command = install_command('\'')
340
+ <<-EOH
341
+ @set MSIERRORCODE=!ERRORLEVEL!
342
+ @if ERRORLEVEL 1 (
343
+ @echo WARNING: Failed to install Chef Client MSI package in remote context with status code !MSIERRORCODE!.
344
+ @echo WARNING: This may be due to a defect in operating system update KB2918614: http://support.microsoft.com/kb/2918614
345
+ @set OLDLOGLOCATION="%CHEF_CLIENT_MSI_LOG_PATH%-fail.log"
346
+ @move "%CHEF_CLIENT_MSI_LOG_PATH%" "!OLDLOGLOCATION!" > NUL
347
+ @echo WARNING: Saving installation log of failure at !OLDLOGLOCATION!
348
+ @echo WARNING: Retrying installation with local context...
349
+ @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\"
350
+
351
+ @if ERRORLEVEL 1 (
352
+ @echo ERROR: Failed to create Chef Client installation scheduled task with status code !ERRORLEVEL! > "&2"
353
+ ) else (
354
+ @echo Successfully created scheduled task to install Chef Client.
355
+ @schtasks /run /tn chefclientbootstraptask
356
+ @if ERRORLEVEL 1 (
357
+ @echo ERROR: Failed to execut Chef Client installation scheduled task with status code !ERRORLEVEL!. > "&2"
358
+ ) else (
359
+ @echo Successfully started Chef Client installation scheduled task.
360
+ @echo Waiting for installation to complete -- this may take a few minutes...
361
+ waitfor chefclientinstalldone /t 600
362
+ if ERRORLEVEL 1 (
363
+ @echo ERROR: Timed out waiting for Chef Client package to install
364
+ ) else (
365
+ @echo Finished waiting for Chef Client package to install.
366
+ )
367
+ @schtasks /delete /f /tn chefclientbootstraptask > NUL
368
+ )
369
+ )
370
+ ) else (
371
+ @echo Successfully installed Chef Client package.
372
+ )
373
+ EOH
374
+ end
375
+ end
376
+ end
377
+ end
378
+ end