knife-windows 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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