knife-windows 1.7.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +26 -26
  3. data/CHANGELOG.md +139 -135
  4. data/DOC_CHANGES.md +22 -22
  5. data/Gemfile +13 -13
  6. data/README.md +404 -404
  7. data/RELEASE_NOTES.md +9 -9
  8. data/appveyor.yml +39 -39
  9. data/ci.gemfile +16 -16
  10. data/knife-windows.gemspec +26 -26
  11. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +246 -246
  12. data/lib/chef/knife/bootstrap_windows_base.rb +443 -443
  13. data/lib/chef/knife/bootstrap_windows_ssh.rb +116 -116
  14. data/lib/chef/knife/bootstrap_windows_winrm.rb +102 -102
  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 +128 -128
  23. data/lib/chef/knife/winrm_knife_base.rb +307 -307
  24. data/lib/chef/knife/winrm_session.rb +98 -98
  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 +242 -234
  29. data/lib/knife-windows/version.rb +6 -6
  30. data/spec/assets/fake_trusted_certs/excluded.txt +2 -0
  31. data/spec/assets/fake_trusted_certs/github.pem +42 -0
  32. data/spec/assets/fake_trusted_certs/google.crt +41 -0
  33. data/spec/assets/win_fake_trusted_cert_script.txt +89 -0
  34. data/spec/assets/win_template_rendered_with_bootstrap_install_command.txt +223 -223
  35. data/spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt +223 -223
  36. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +335 -335
  37. data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt +335 -335
  38. data/spec/assets/win_template_unrendered.txt +246 -246
  39. data/spec/dummy_winrm_connection.rb +21 -21
  40. data/spec/functional/bootstrap_download_spec.rb +236 -236
  41. data/spec/spec_helper.rb +94 -94
  42. data/spec/unit/knife/bootstrap_options_spec.rb +157 -157
  43. data/spec/unit/knife/bootstrap_template_spec.rb +98 -98
  44. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +423 -423
  45. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +213 -177
  46. data/spec/unit/knife/windows_cert_generate_spec.rb +90 -90
  47. data/spec/unit/knife/windows_cert_install_spec.rb +51 -51
  48. data/spec/unit/knife/windows_listener_create_spec.rb +76 -76
  49. data/spec/unit/knife/winrm_session_spec.rb +95 -95
  50. data/spec/unit/knife/winrm_spec.rb +500 -500
  51. data/spec/unit/knife/wsman_test_spec.rb +209 -209
  52. metadata +7 -3
@@ -1,234 +1,242 @@
1
- #
2
- # Author:: Bryan McLellan <btm@loftninjas.org>
3
- # Copyright:: Copyright (c) 2014-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
- # Sourced from Chef::Util::PathHelper.
20
- # Should be removed when Chef 11 catches up or we stop supporting Chef 11
21
-
22
- module Knife
23
- module Windows
24
- class PathHelper
25
- # Maximum characters in a standard Windows path (260 including drive letter and NUL)
26
- WIN_MAX_PATH = 259
27
-
28
- def self.dirname(path)
29
- if Chef::Platform.windows?
30
- # Find the first slash, not counting trailing slashes
31
- end_slash = path.size
32
- while true
33
- slash = path.rindex(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]/, end_slash - 1)
34
- if !slash
35
- return end_slash == path.size ? '.' : path_separator
36
- elsif slash == end_slash - 1
37
- end_slash = slash
38
- else
39
- return path[0..slash-1]
40
- end
41
- end
42
- else
43
- ::File.dirname(path)
44
- end
45
- end
46
-
47
- BACKSLASH = '\\'.freeze
48
-
49
- def self.path_separator
50
- if Chef::Platform.windows?
51
- File::ALT_SEPARATOR || BACKSLASH
52
- else
53
- File::SEPARATOR
54
- end
55
- end
56
-
57
- def self.join(*args)
58
- args.flatten.inject do |joined_path, component|
59
- # Joined path ends with /
60
- joined_path = joined_path.sub(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]+$/, '')
61
- component = component.sub(/^[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]+/, '')
62
- joined_path += "#{path_separator}#{component}"
63
- end
64
- end
65
-
66
- def self.validate_path(path)
67
- if Chef::Platform.windows?
68
- unless printable?(path)
69
- msg = "Path '#{path}' contains non-printable characters. Check that backslashes are escaped with another backslash (e.g. C:\\\\Windows) in double-quoted strings."
70
- Chef::Log.error(msg)
71
- raise Chef::Exceptions::ValidationFailed, msg
72
- end
73
-
74
- if windows_max_length_exceeded?(path)
75
- Chef::Log.debug("Path '#{path}' is longer than #{WIN_MAX_PATH}, prefixing with'\\\\?\\'")
76
- path.insert(0, "\\\\?\\")
77
- end
78
- end
79
-
80
- path
81
- end
82
-
83
- def self.windows_max_length_exceeded?(path)
84
- # Check to see if paths without the \\?\ prefix are over the maximum allowed length for the Windows API
85
- # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
86
- unless path =~ /^\\\\?\\/
87
- if path.length > WIN_MAX_PATH
88
- return true
89
- end
90
- end
91
-
92
- false
93
- end
94
-
95
- def self.printable?(string)
96
- # returns true if string is free of non-printable characters (escape sequences)
97
- # this returns false for whitespace escape sequences as well, e.g. \n\t
98
- if string =~ /[^[:print:]]/
99
- false
100
- else
101
- true
102
- end
103
- end
104
-
105
- # Produces a comparable path.
106
- def self.canonical_path(path, add_prefix=true)
107
- # Rather than find an equivalent for File.absolute_path on 1.8.7, just bail out
108
- raise NotImplementedError, "This feature is not supported on Ruby versions < 1.9" if RUBY_VERSION.to_f < 1.9
109
-
110
- # First remove extra separators and resolve any relative paths
111
- abs_path = File.absolute_path(path)
112
-
113
- if Chef::Platform.windows?
114
- # Add the \\?\ API prefix on Windows unless add_prefix is false
115
- # Downcase on Windows where paths are still case-insensitive
116
- abs_path.gsub!(::File::SEPARATOR, path_separator)
117
- if add_prefix && abs_path !~ /^\\\\?\\/
118
- abs_path.insert(0, "\\\\?\\")
119
- end
120
-
121
- abs_path.downcase!
122
- end
123
-
124
- abs_path
125
- end
126
-
127
- def self.cleanpath(path)
128
- path = Pathname.new(path).cleanpath.to_s
129
- # ensure all forward slashes are backslashes
130
- if Chef::Platform.windows?
131
- path = path.gsub(File::SEPARATOR, path_separator)
132
- end
133
- path
134
- end
135
-
136
- def self.paths_eql?(path1, path2)
137
- canonical_path(path1) == canonical_path(path2)
138
- end
139
-
140
- # Paths which may contain glob-reserved characters need
141
- # to be escaped before globbing can be done.
142
- # http://stackoverflow.com/questions/14127343
143
- def self.escape_glob(*parts)
144
- path = cleanpath(join(*parts))
145
- path.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\"+x }
146
- end
147
-
148
- def self.relative_path_from(from, to)
149
- pathname = Pathname.new(Chef::Util::PathHelper.cleanpath(to)).relative_path_from(Pathname.new(Chef::Util::PathHelper.cleanpath(from)))
150
- end
151
-
152
- # Retrieves the "home directory" of the current user while trying to ascertain the existence
153
- # of said directory. The path returned uses / for all separators (the ruby standard format).
154
- # If the home directory doesn't exist or an error is otherwise encountered, nil is returned.
155
- #
156
- # If a set of path elements is provided, they are appended as-is to the home path if the
157
- # homepath exists.
158
- #
159
- # If an optional block is provided, the joined path is passed to that block if the home path is
160
- # valid and the result of the block is returned instead.
161
- #
162
- # Home-path discovery is performed once. If a path is discovered, that value is memoized so
163
- # that subsequent calls to home_dir don't bounce around.
164
- #
165
- # See self.all_homes.
166
- def self.home(*args)
167
- @@home_dir ||= self.all_homes { |p| break p }
168
- if @@home_dir
169
- path = File.join(@@home_dir, *args)
170
- block_given? ? (yield path) : path
171
- end
172
- end
173
-
174
- # See self.home. This method performs a similar operation except that it yields all the different
175
- # possible values of 'HOME' that one could have on this platform. Hence, on windows, if
176
- # HOMEDRIVE\HOMEPATH and USERPROFILE are different, the provided block will be called twice.
177
- # This method goes out and checks the existence of each location at the time of the call.
178
- #
179
- # The return is a list of all the returned values from each block invocation or a list of paths
180
- # if no block is provided.
181
- def self.all_homes(*args)
182
- paths = []
183
- if Chef::Platform.windows?
184
- # By default, Ruby uses the the following environment variables to determine Dir.home:
185
- # HOME
186
- # HOMEDRIVE HOMEPATH
187
- # USERPROFILE
188
- # Ruby only checks to see if the variable is specified - not if the directory actually exists.
189
- # On Windows, HOMEDRIVE HOMEPATH can point to a different location (such as an unavailable network mounted drive)
190
- # while USERPROFILE points to the location where the user application settings and profile are stored. HOME
191
- # is not defined as an environment variable (usually). If the home path actually uses UNC, then the prefix is
192
- # HOMESHARE instead of HOMEDRIVE.
193
- #
194
- # We instead walk down the following and only include paths that actually exist.
195
- # HOME
196
- # HOMEDRIVE HOMEPATH
197
- # HOMESHARE HOMEPATH
198
- # USERPROFILE
199
-
200
- paths << ENV['HOME']
201
- paths << ENV['HOMEDRIVE'] + ENV['HOMEPATH'] if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
202
- paths << ENV['HOMESHARE'] + ENV['HOMEPATH'] if ENV['HOMESHARE'] && ENV['HOMEPATH']
203
- paths << ENV['USERPROFILE']
204
- end
205
- paths << Dir.home if ENV['HOME']
206
-
207
- # Depending on what environment variables we're using, the slashes can go in any which way.
208
- # Just change them all to / to keep things consistent.
209
- # Note: Maybe this is a bad idea on some unixy systems where \ might be a valid character depending on
210
- # the particular brand of kool-aid you consume. This code assumes that \ and / are both
211
- # path separators on any system being used.
212
- paths = paths.map { |home_path| home_path.gsub(path_separator, ::File::SEPARATOR) if home_path }
213
-
214
- # Filter out duplicate paths and paths that don't exist.
215
- valid_paths = paths.select { |home_path| home_path && Dir.exists?(home_path) }
216
- valid_paths = valid_paths.uniq
217
-
218
- # Join all optional path elements at the end.
219
- # If a block is provided, invoke it - otherwise just return what we've got.
220
- joined_paths = valid_paths.map { |home_path| File.join(home_path, *args) }
221
- if block_given?
222
- joined_paths.each { |p| yield p }
223
- else
224
- joined_paths
225
- end
226
- end
227
-
228
- end
229
- end
230
- end
231
-
232
- # Break a require loop when require chef/util/path_helper
233
- require 'chef/platform'
234
- require 'chef/exceptions'
1
+ #
2
+ # Author:: Bryan McLellan <btm@loftninjas.org>
3
+ # Copyright:: Copyright (c) 2014-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
+ # Sourced from Chef::Util::PathHelper.
20
+ # Should be removed when Chef 11 catches up or we stop supporting Chef 11
21
+
22
+ module Knife
23
+ module Windows
24
+ class PathHelper
25
+ # Maximum characters in a standard Windows path (260 including drive letter and NUL)
26
+ WIN_MAX_PATH = 259
27
+
28
+ def self.dirname(path)
29
+ if Chef::Platform.windows?
30
+ # Find the first slash, not counting trailing slashes
31
+ end_slash = path.size
32
+ while true
33
+ slash = path.rindex(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]/, end_slash - 1)
34
+ if !slash
35
+ return end_slash == path.size ? '.' : path_separator
36
+ elsif slash == end_slash - 1
37
+ end_slash = slash
38
+ else
39
+ return path[0..slash-1]
40
+ end
41
+ end
42
+ else
43
+ ::File.dirname(path)
44
+ end
45
+ end
46
+
47
+ BACKSLASH = '\\'.freeze
48
+
49
+ def self.path_separator
50
+ if Chef::Platform.windows?
51
+ File::ALT_SEPARATOR || BACKSLASH
52
+ else
53
+ File::SEPARATOR
54
+ end
55
+ end
56
+
57
+ def self.join(*args)
58
+ args.flatten.inject do |joined_path, component|
59
+ # Joined path ends with /
60
+ joined_path = joined_path.sub(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]+$/, '')
61
+ component = component.sub(/^[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]+/, '')
62
+ joined_path += "#{path_separator}#{component}"
63
+ end
64
+ end
65
+
66
+ def self.validate_path(path)
67
+ if Chef::Platform.windows?
68
+ unless printable?(path)
69
+ msg = "Path '#{path}' contains non-printable characters. Check that backslashes are escaped with another backslash (e.g. C:\\\\Windows) in double-quoted strings."
70
+ Chef::Log.error(msg)
71
+ raise Chef::Exceptions::ValidationFailed, msg
72
+ end
73
+
74
+ if windows_max_length_exceeded?(path)
75
+ Chef::Log.debug("Path '#{path}' is longer than #{WIN_MAX_PATH}, prefixing with'\\\\?\\'")
76
+ path.insert(0, "\\\\?\\")
77
+ end
78
+ end
79
+
80
+ path
81
+ end
82
+
83
+ def self.windows_max_length_exceeded?(path)
84
+ # Check to see if paths without the \\?\ prefix are over the maximum allowed length for the Windows API
85
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
86
+ unless path =~ /^\\\\?\\/
87
+ if path.length > WIN_MAX_PATH
88
+ return true
89
+ end
90
+ end
91
+
92
+ false
93
+ end
94
+
95
+ def self.printable?(string)
96
+ # returns true if string is free of non-printable characters (escape sequences)
97
+ # this returns false for whitespace escape sequences as well, e.g. \n\t
98
+ if string =~ /[^[:print:]]/
99
+ false
100
+ else
101
+ true
102
+ end
103
+ end
104
+
105
+ # Produces a comparable path.
106
+ def self.canonical_path(path, add_prefix=true)
107
+ # Rather than find an equivalent for File.absolute_path on 1.8.7, just bail out
108
+ raise NotImplementedError, "This feature is not supported on Ruby versions < 1.9" if RUBY_VERSION.to_f < 1.9
109
+
110
+ # First remove extra separators and resolve any relative paths
111
+ abs_path = File.absolute_path(path)
112
+
113
+ if Chef::Platform.windows?
114
+ # Add the \\?\ API prefix on Windows unless add_prefix is false
115
+ # Downcase on Windows where paths are still case-insensitive
116
+ abs_path.gsub!(::File::SEPARATOR, path_separator)
117
+ if add_prefix && abs_path !~ /^\\\\?\\/
118
+ abs_path.insert(0, "\\\\?\\")
119
+ end
120
+
121
+ abs_path.downcase!
122
+ end
123
+
124
+ abs_path
125
+ end
126
+
127
+ def self.cleanpath(path)
128
+ path = Pathname.new(path).cleanpath.to_s
129
+ # ensure all forward slashes are backslashes
130
+ if Chef::Platform.windows?
131
+ path = path.gsub(File::SEPARATOR, path_separator)
132
+ end
133
+ path
134
+ end
135
+
136
+ def self.paths_eql?(path1, path2)
137
+ canonical_path(path1) == canonical_path(path2)
138
+ end
139
+
140
+ # Note: this method is deprecated. Please use escape_glob_dirs
141
+ # Paths which may contain glob-reserved characters need
142
+ # to be escaped before globbing can be done.
143
+ # http://stackoverflow.com/questions/14127343
144
+ def self.escape_glob(*parts)
145
+ path = cleanpath(join(*parts))
146
+ path.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\"+x }
147
+ end
148
+
149
+ # This function does not switch to backslashes for windows
150
+ # This is because only forwardslashes should be used with dir (even for windows)
151
+ def self.escape_glob_dir(*parts)
152
+ path = Pathname.new(join(*parts)).cleanpath.to_s
153
+ path.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\" + x }
154
+ end
155
+
156
+ def self.relative_path_from(from, to)
157
+ pathname = Pathname.new(Chef::Util::PathHelper.cleanpath(to)).relative_path_from(Pathname.new(Chef::Util::PathHelper.cleanpath(from)))
158
+ end
159
+
160
+ # Retrieves the "home directory" of the current user while trying to ascertain the existence
161
+ # of said directory. The path returned uses / for all separators (the ruby standard format).
162
+ # If the home directory doesn't exist or an error is otherwise encountered, nil is returned.
163
+ #
164
+ # If a set of path elements is provided, they are appended as-is to the home path if the
165
+ # homepath exists.
166
+ #
167
+ # If an optional block is provided, the joined path is passed to that block if the home path is
168
+ # valid and the result of the block is returned instead.
169
+ #
170
+ # Home-path discovery is performed once. If a path is discovered, that value is memoized so
171
+ # that subsequent calls to home_dir don't bounce around.
172
+ #
173
+ # See self.all_homes.
174
+ def self.home(*args)
175
+ @@home_dir ||= self.all_homes { |p| break p }
176
+ if @@home_dir
177
+ path = File.join(@@home_dir, *args)
178
+ block_given? ? (yield path) : path
179
+ end
180
+ end
181
+
182
+ # See self.home. This method performs a similar operation except that it yields all the different
183
+ # possible values of 'HOME' that one could have on this platform. Hence, on windows, if
184
+ # HOMEDRIVE\HOMEPATH and USERPROFILE are different, the provided block will be called twice.
185
+ # This method goes out and checks the existence of each location at the time of the call.
186
+ #
187
+ # The return is a list of all the returned values from each block invocation or a list of paths
188
+ # if no block is provided.
189
+ def self.all_homes(*args)
190
+ paths = []
191
+ if Chef::Platform.windows?
192
+ # By default, Ruby uses the the following environment variables to determine Dir.home:
193
+ # HOME
194
+ # HOMEDRIVE HOMEPATH
195
+ # USERPROFILE
196
+ # Ruby only checks to see if the variable is specified - not if the directory actually exists.
197
+ # On Windows, HOMEDRIVE HOMEPATH can point to a different location (such as an unavailable network mounted drive)
198
+ # while USERPROFILE points to the location where the user application settings and profile are stored. HOME
199
+ # is not defined as an environment variable (usually). If the home path actually uses UNC, then the prefix is
200
+ # HOMESHARE instead of HOMEDRIVE.
201
+ #
202
+ # We instead walk down the following and only include paths that actually exist.
203
+ # HOME
204
+ # HOMEDRIVE HOMEPATH
205
+ # HOMESHARE HOMEPATH
206
+ # USERPROFILE
207
+
208
+ paths << ENV['HOME']
209
+ paths << ENV['HOMEDRIVE'] + ENV['HOMEPATH'] if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
210
+ paths << ENV['HOMESHARE'] + ENV['HOMEPATH'] if ENV['HOMESHARE'] && ENV['HOMEPATH']
211
+ paths << ENV['USERPROFILE']
212
+ end
213
+ paths << Dir.home if ENV['HOME']
214
+
215
+ # Depending on what environment variables we're using, the slashes can go in any which way.
216
+ # Just change them all to / to keep things consistent.
217
+ # Note: Maybe this is a bad idea on some unixy systems where \ might be a valid character depending on
218
+ # the particular brand of kool-aid you consume. This code assumes that \ and / are both
219
+ # path separators on any system being used.
220
+ paths = paths.map { |home_path| home_path.gsub(path_separator, ::File::SEPARATOR) if home_path }
221
+
222
+ # Filter out duplicate paths and paths that don't exist.
223
+ valid_paths = paths.select { |home_path| home_path && Dir.exists?(home_path) }
224
+ valid_paths = valid_paths.uniq
225
+
226
+ # Join all optional path elements at the end.
227
+ # If a block is provided, invoke it - otherwise just return what we've got.
228
+ joined_paths = valid_paths.map { |home_path| File.join(home_path, *args) }
229
+ if block_given?
230
+ joined_paths.each { |p| yield p }
231
+ else
232
+ joined_paths
233
+ end
234
+ end
235
+
236
+ end
237
+ end
238
+ end
239
+
240
+ # Break a require loop when require chef/util/path_helper
241
+ require 'chef/platform'
242
+ require 'chef/exceptions'