knife-windows 1.0.0.rc.1 → 1.0.0.rc.2

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