knife-windows 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -5
  3. data/.travis.yml +26 -23
  4. data/CHANGELOG.md +108 -104
  5. data/DOC_CHANGES.md +14 -14
  6. data/Gemfile +12 -12
  7. data/LICENSE +201 -201
  8. data/README.md +385 -376
  9. data/RELEASE_NOTES.md +34 -34
  10. data/Rakefile +21 -21
  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 +25 -25
  16. data/lib/chef/knife/bootstrap/windows-chef-client-msi.erb +247 -247
  17. data/lib/chef/knife/bootstrap_windows_base.rb +415 -415
  18. data/lib/chef/knife/bootstrap_windows_ssh.rb +115 -115
  19. data/lib/chef/knife/bootstrap_windows_winrm.rb +95 -95
  20. data/lib/chef/knife/core/windows_bootstrap_context.rb +366 -366
  21. data/lib/chef/knife/knife_windows_base.rb +33 -33
  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 +122 -122
  27. data/lib/chef/knife/winrm_base.rb +117 -113
  28. data/lib/chef/knife/winrm_knife_base.rb +303 -298
  29. data/lib/chef/knife/winrm_session.rb +86 -86
  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 +117 -117
  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 -217
  36. data/spec/assets/win_template_rendered_with_bootstrap_install_command_on_12_5_client.txt +217 -217
  37. data/spec/assets/win_template_rendered_without_bootstrap_install_command.txt +329 -329
  38. data/spec/assets/win_template_rendered_without_bootstrap_install_command_on_12_5_client.txt +329 -329
  39. data/spec/assets/win_template_unrendered.txt +246 -246
  40. data/spec/functional/bootstrap_download_spec.rb +234 -234
  41. data/spec/spec_helper.rb +93 -93
  42. data/spec/unit/knife/bootstrap_options_spec.rb +155 -154
  43. data/spec/unit/knife/bootstrap_template_spec.rb +92 -92
  44. data/spec/unit/knife/bootstrap_windows_winrm_spec.rb +295 -295
  45. data/spec/unit/knife/core/windows_bootstrap_context_spec.rb +177 -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 +64 -64
  50. data/spec/unit/knife/winrm_spec.rb +516 -516
  51. data/spec/unit/knife/wsman_test_spec.rb +201 -201
  52. metadata +4 -4
@@ -1,234 +1,234 @@
1
- #
2
- # Author:: Bryan McLellan <btm@loftninjas.org>
3
- # Copyright:: Copyright (c) 2014 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 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'