chef 12.1.2 → 12.2.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +7 -0
  2. data/lib/chef/application/client.rb +2 -2
  3. data/lib/chef/audit/audit_reporter.rb +1 -1
  4. data/lib/chef/audit/runner.rb +15 -2
  5. data/lib/chef/client.rb +1 -1
  6. data/lib/chef/config.rb +6 -4
  7. data/lib/chef/dsl/powershell.rb +29 -0
  8. data/lib/chef/exceptions.rb +18 -3
  9. data/lib/chef/formatters/doc.rb +2 -2
  10. data/lib/chef/knife/bootstrap.rb +2 -1
  11. data/lib/chef/knife/bootstrap/templates/chef-full.erb +1 -1
  12. data/lib/chef/knife/core/subcommand_loader.rb +13 -5
  13. data/lib/chef/knife/exec.rb +2 -1
  14. data/lib/chef/knife/ssh.rb +12 -2
  15. data/lib/chef/mixin/params_validate.rb +42 -19
  16. data/lib/chef/mixin/powershell_type_coercions.rb +82 -0
  17. data/lib/chef/mixin/windows_architecture_helper.rb +8 -0
  18. data/lib/chef/node.rb +1 -1
  19. data/lib/chef/platform/provider_mapping.rb +0 -107
  20. data/lib/chef/platform/query_helpers.rb +7 -0
  21. data/lib/chef/provider/batch.rb +2 -0
  22. data/lib/chef/provider/cron.rb +2 -0
  23. data/lib/chef/provider/cron/aix.rb +2 -0
  24. data/lib/chef/provider/cron/unix.rb +2 -0
  25. data/lib/chef/provider/deploy.rb +104 -87
  26. data/lib/chef/provider/dsc_resource.rb +157 -0
  27. data/lib/chef/provider/env.rb +2 -0
  28. data/lib/chef/provider/env/windows.rb +2 -0
  29. data/lib/chef/provider/git.rb +4 -0
  30. data/lib/chef/provider/group.rb +5 -5
  31. data/lib/chef/provider/group/dscl.rb +2 -0
  32. data/lib/chef/provider/group/groupmod.rb +2 -0
  33. data/lib/chef/provider/group/usermod.rb +2 -0
  34. data/lib/chef/provider/group/windows.rb +2 -0
  35. data/lib/chef/provider/mdadm.rb +2 -0
  36. data/lib/chef/provider/mount/windows.rb +2 -0
  37. data/lib/chef/provider/package/homebrew.rb +1 -1
  38. data/lib/chef/provider/package/openbsd.rb +49 -18
  39. data/lib/chef/provider/package/rubygems.rb +7 -2
  40. data/lib/chef/provider/powershell_script.rb +2 -0
  41. data/lib/chef/provider/service/macosx.rb +1 -2
  42. data/lib/chef/provider/user/dscl.rb +7 -1
  43. data/lib/chef/provider/user/windows.rb +2 -0
  44. data/lib/chef/providers.rb +1 -0
  45. data/lib/chef/recipe.rb +2 -0
  46. data/lib/chef/resource.rb +9 -0
  47. data/lib/chef/resource/batch.rb +2 -0
  48. data/lib/chef/resource/cron.rb +3 -3
  49. data/lib/chef/resource/deploy.rb +52 -217
  50. data/lib/chef/resource/dsc_resource.rb +83 -0
  51. data/lib/chef/resource/env.rb +2 -0
  52. data/lib/chef/resource/git.rb +1 -1
  53. data/lib/chef/resource/group.rb +2 -0
  54. data/lib/chef/resource/homebrew_package.rb +1 -1
  55. data/lib/chef/resource/lwrp_base.rb +0 -8
  56. data/lib/chef/resource/mdadm.rb +2 -0
  57. data/lib/chef/resource/mount.rb +2 -0
  58. data/lib/chef/resource/powershell_script.rb +2 -0
  59. data/lib/chef/resource/user.rb +2 -0
  60. data/lib/chef/resources.rb +1 -0
  61. data/lib/chef/run_context.rb +1 -1
  62. data/lib/chef/shell.rb +7 -5
  63. data/lib/chef/util/dsc/resource_store.rb +110 -0
  64. data/lib/chef/util/path_helper.rb +76 -0
  65. data/lib/chef/util/powershell/cmdlet.rb +41 -7
  66. data/lib/chef/util/powershell/cmdlet_result.rb +18 -3
  67. data/lib/chef/util/powershell/ps_credential.rb +38 -0
  68. data/lib/chef/version.rb +1 -1
  69. data/lib/chef/win32/api.rb +2 -0
  70. data/lib/chef/win32/api/crypto.rb +63 -0
  71. data/lib/chef/win32/api/installer.rb +1 -1
  72. data/lib/chef/win32/crypto.rb +49 -0
  73. data/lib/chef/workstation_config_loader.rb +4 -3
  74. data/spec/functional/file_content_management/deploy_strategies_spec.rb +1 -1
  75. data/spec/functional/resource/cookbook_file_spec.rb +1 -1
  76. data/spec/functional/resource/deploy_revision_spec.rb +35 -0
  77. data/spec/functional/resource/directory_spec.rb +1 -1
  78. data/spec/functional/resource/dsc_resource_spec.rb +93 -0
  79. data/spec/functional/resource/env_spec.rb +4 -3
  80. data/spec/functional/resource/file_spec.rb +1 -1
  81. data/spec/functional/resource/powershell_spec.rb +2 -1
  82. data/spec/functional/resource/remote_directory_spec.rb +1 -1
  83. data/spec/functional/resource/remote_file_spec.rb +1 -1
  84. data/spec/functional/resource/template_spec.rb +1 -1
  85. data/spec/functional/resource/user/dscl_spec.rb +1 -2
  86. data/spec/functional/resource/user/useradd_spec.rb +27 -13
  87. data/spec/functional/util/powershell/cmdlet_spec.rb +3 -3
  88. data/spec/functional/win32/crypto_spec.rb +57 -0
  89. data/spec/spec_helper.rb +3 -0
  90. data/spec/support/platform_helpers.rb +14 -0
  91. data/spec/support/shared/functional/securable_resource_with_reporting.rb +5 -5
  92. data/spec/unit/application/client_spec.rb +4 -4
  93. data/spec/unit/audit/audit_reporter_spec.rb +1 -1
  94. data/spec/unit/audit/runner_spec.rb +10 -0
  95. data/spec/unit/config_spec.rb +2 -8
  96. data/spec/unit/knife/bootstrap_spec.rb +20 -8
  97. data/spec/unit/knife/core/subcommand_loader_spec.rb +29 -29
  98. data/spec/unit/mixin/params_validate_spec.rb +75 -61
  99. data/spec/unit/mixin/powershell_type_coercions_spec.rb +72 -0
  100. data/spec/unit/platform/query_helpers_spec.rb +22 -0
  101. data/spec/unit/platform_spec.rb +0 -5
  102. data/spec/unit/provider/dsc_resource_spec.rb +84 -0
  103. data/spec/unit/provider/package/openbsd_spec.rb +105 -17
  104. data/spec/unit/provider/service/macosx_spec.rb +3 -3
  105. data/spec/unit/provider_resolver_spec.rb +132 -0
  106. data/spec/unit/recipe_spec.rb +4 -0
  107. data/spec/unit/resource/deploy_spec.rb +27 -0
  108. data/spec/unit/resource/dsc_resource_spec.rb +85 -0
  109. data/spec/unit/shell_spec.rb +1 -1
  110. data/spec/unit/util/dsc/resource_store.rb +76 -0
  111. data/spec/unit/util/powershell/ps_credential_spec.rb +37 -0
  112. data/spec/unit/workstation_config_loader_spec.rb +1 -1
  113. metadata +159 -186
@@ -0,0 +1,83 @@
1
+ #
2
+ # Author:: Adam Edwards (<adamed@getchef.com>)
3
+ #
4
+ # Copyright:: 2014, Opscode, Inc.
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
+ require 'chef/dsl/powershell'
19
+
20
+ class Chef
21
+ class Resource
22
+ class DscResource < Chef::Resource
23
+
24
+ provides :dsc_resource, os: "windows"
25
+
26
+ include Chef::DSL::Powershell
27
+
28
+ def initialize(name, run_context)
29
+ super
30
+ @properties = {}
31
+ @resource_name = :dsc_resource
32
+ @resource = nil
33
+ @allowed_actions.push(:run)
34
+ @action = :run
35
+ end
36
+
37
+ def resource(value=nil)
38
+ if value
39
+ @resource = value
40
+ else
41
+ @resource
42
+ end
43
+ end
44
+
45
+ def module_name(value=nil)
46
+ if value
47
+ @module_name = value
48
+ else
49
+ @module_name
50
+ end
51
+ end
52
+
53
+ def property(property_name, value=nil)
54
+ if not property_name.is_a?(Symbol)
55
+ raise TypeError, "A property name of type Symbol must be specified, '#{property_name.to_s}' of type #{property_name.class.to_s} was given"
56
+ end
57
+
58
+ if value.nil?
59
+ value_of(@properties[property_name])
60
+ else
61
+ @properties[property_name] = value
62
+ end
63
+ end
64
+
65
+ def properties
66
+ @properties.reduce({}) do |memo, (k, v)|
67
+ memo[k] = value_of(v)
68
+ memo
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def value_of(value)
75
+ if value.is_a?(DelayedEvaluator)
76
+ value.call
77
+ else
78
+ value
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -25,6 +25,8 @@ class Chef
25
25
 
26
26
  state_attrs :value
27
27
 
28
+ provides :env, os: "windows"
29
+
28
30
  def initialize(name, run_context=nil)
29
31
  super
30
32
  @resource_name = :env
@@ -27,7 +27,7 @@ class Chef
27
27
  def initialize(name, run_context=nil)
28
28
  super
29
29
  @resource_name = :git
30
- @additional_remotes = Hash[]
30
+ @additional_remotes = {}
31
31
  end
32
32
 
33
33
  def additional_remotes(arg=nil)
@@ -25,6 +25,8 @@ class Chef
25
25
 
26
26
  state_attrs :members
27
27
 
28
+ provides :group
29
+
28
30
  def initialize(name, run_context=nil)
29
31
  super
30
32
  @resource_name = :group
@@ -26,7 +26,7 @@ class Chef
26
26
  class HomebrewPackage < Chef::Resource::Package
27
27
 
28
28
  provides :homebrew_package
29
- provides :package, os: ["mac_os_x", "darwin"]
29
+ provides :package, os: "darwin"
30
30
 
31
31
  def initialize(name, run_context=nil)
32
32
  super
@@ -70,14 +70,6 @@ class Chef
70
70
  alias_method :resource_name=, :resource_name
71
71
  end
72
72
 
73
- # Define an attribute on this resource, including optional validation
74
- # parameters.
75
- def self.attribute(attr_name, validation_opts={})
76
- define_method(attr_name) do |arg=nil|
77
- set_or_return(attr_name.to_sym, arg, validation_opts)
78
- end
79
- end
80
-
81
73
  # Sets the default action
82
74
  def self.default_action(action_name=NULL_ARG)
83
75
  unless action_name.equal?(NULL_ARG)
@@ -27,6 +27,8 @@ class Chef
27
27
 
28
28
  state_attrs :devices, :level, :chunk
29
29
 
30
+ provides :mdadm
31
+
30
32
  def initialize(name, run_context=nil)
31
33
  super
32
34
  @resource_name = :mdadm
@@ -27,6 +27,8 @@ class Chef
27
27
 
28
28
  state_attrs :mount_point, :device_type, :fstype, :username, :password, :domain
29
29
 
30
+ provides :mount
31
+
30
32
  def initialize(name, run_context=nil)
31
33
  super
32
34
  @resource_name = :mount
@@ -21,6 +21,8 @@ class Chef
21
21
  class Resource
22
22
  class PowershellScript < Chef::Resource::WindowsScript
23
23
 
24
+ provides :powershell_script, os: "windows"
25
+
24
26
  def initialize(name, run_context=nil)
25
27
  super(name, run_context, :powershell_script, "powershell.exe")
26
28
  @convert_boolean_return = false
@@ -26,6 +26,8 @@ class Chef
26
26
 
27
27
  state_attrs :uid, :gid, :home
28
28
 
29
+ provides :user
30
+
29
31
  def initialize(name, run_context=nil)
30
32
  super
31
33
  @resource_name = :user
@@ -29,6 +29,7 @@ require 'chef/resource/deploy_revision'
29
29
  require 'chef/resource/directory'
30
30
  require 'chef/resource/dpkg_package'
31
31
  require 'chef/resource/dsc_script'
32
+ require 'chef/resource/dsc_resource'
32
33
  require 'chef/resource/easy_install_package'
33
34
  require 'chef/resource/env'
34
35
  require 'chef/resource/erl_call'
@@ -50,7 +50,7 @@ class Chef
50
50
  # recipes, which is triggered by #load. (See also: CookbookCompiler)
51
51
  attr_accessor :resource_collection
52
52
 
53
- # The list of audits (control groups) to execute during the audit phase
53
+ # The list of control groups to execute during the audit phase
54
54
  attr_accessor :audits
55
55
 
56
56
  # A Hash containing the immediate notifications triggered by resources
@@ -29,6 +29,7 @@ require 'chef/config_fetcher'
29
29
  require 'chef/shell/shell_session'
30
30
  require 'chef/shell/ext'
31
31
  require 'chef/json_compat'
32
+ require 'chef/util/path_helper'
32
33
 
33
34
  # = Shell
34
35
  # Shell is Chef in an IRB session. Shell can interact with a Chef server via the
@@ -101,7 +102,7 @@ module Shell
101
102
  end
102
103
 
103
104
  def self.configure_irb
104
- irb_conf[:HISTORY_FILE] = "~/.chef/chef_shell_history"
105
+ irb_conf[:HISTORY_FILE] = Chef::Util::PathHelper.home(".chef", "chef_shell_history")
105
106
  irb_conf[:SAVE_HISTORY] = 1000
106
107
 
107
108
  irb_conf[:IRB_RC] = lambda do |conf|
@@ -295,18 +296,19 @@ FOOTER
295
296
  private
296
297
 
297
298
  def config_file_for_shell_mode(environment)
299
+ dot_chef_dir = Chef::Util::PathHelper.home('.chef')
298
300
  if config[:config_file]
299
301
  config[:config_file]
300
- elsif environment && ENV['HOME']
302
+ elsif environment
301
303
  Shell.env = environment
302
- config_file_to_try = ::File.join(ENV['HOME'], '.chef', environment, 'chef_shell.rb')
304
+ config_file_to_try = ::File.join(dot_chef_dir, environment, 'chef_shell.rb')
303
305
  unless ::File.exist?(config_file_to_try)
304
306
  puts "could not find chef-shell config for environment #{environment} at #{config_file_to_try}"
305
307
  exit 1
306
308
  end
307
309
  config_file_to_try
308
- elsif ENV['HOME'] && ::File.exist?(File.join(ENV['HOME'], '.chef', 'chef_shell.rb'))
309
- File.join(ENV['HOME'], '.chef', 'chef_shell.rb')
310
+ elsif dot_chef_dir && ::File.exist?(File.join(dot_chef_dir, 'chef_shell.rb'))
311
+ File.join(dot_chef_dir, 'chef_shell.rb')
310
312
  elsif config[:solo]
311
313
  Chef::Config.platform_specific_path("/etc/chef/solo.rb")
312
314
  elsif config[:client]
@@ -0,0 +1,110 @@
1
+ #
2
+ # Author:: Jay Mundrawala (<jdm@chef.io>)
3
+ #
4
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
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/util/powershell/cmdlet'
20
+ require 'chef/util/powershell/cmdlet_result'
21
+ require 'chef/exceptions'
22
+
23
+ class Chef
24
+ class Util
25
+ class DSC
26
+ class ResourceStore
27
+
28
+ def self.instance
29
+ @@instance ||= ResourceStore.new.tap do |store|
30
+ store.send(:populate_cache)
31
+ end
32
+ end
33
+
34
+ def resources
35
+ @resources ||= []
36
+ end
37
+
38
+ def find(name, module_name=nil)
39
+ found = find_resources(name, module_name, resources)
40
+
41
+ # We don't have it, query for the resource...it might
42
+ # have been added since we last queried
43
+ if found.length == 0
44
+ rs = query_resource(name)
45
+ add_resources(rs)
46
+ found = find_resources(name, module_name, rs)
47
+ end
48
+
49
+ found
50
+ end
51
+
52
+ private
53
+
54
+ def add_resource(new_r)
55
+ count = resources.count do |r|
56
+ r['ResourceType'].casecmp(new_r['ResourceType']) == 0
57
+ end
58
+ if count == 0
59
+ resources << new_r
60
+ end
61
+ end
62
+
63
+ def add_resources(rs)
64
+ rs.each do |r|
65
+ add_resource(r)
66
+ end
67
+ end
68
+
69
+ def populate_cache
70
+ @resources = query_resources
71
+ end
72
+
73
+ def find_resources(name, module_name, rs)
74
+ found = rs.find_all do |r|
75
+ name_matches = r['Name'].casecmp(name) == 0
76
+ if name_matches
77
+ module_name == nil || (r['Module'] and r['Module']['Name'].casecmp(module_name) == 0)
78
+ else
79
+ false
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ # Returns a list of dsc resources
86
+ def query_resources
87
+ cmdlet = Chef::Util::Powershell::Cmdlet.new(nil, 'get-dscresource',
88
+ :object)
89
+ result = cmdlet.run
90
+ result.return_value
91
+ end
92
+
93
+ # Returns a list of dsc resources matching the provided name
94
+ def query_resource(resource_name)
95
+ cmdlet = Chef::Util::Powershell::Cmdlet.new(nil, "get-dscresource #{resource_name}",
96
+ :object)
97
+ result = cmdlet.run
98
+ ret_val = result.return_value
99
+ if ret_val.nil?
100
+ []
101
+ elsif ret_val.is_a? Array
102
+ ret_val
103
+ else
104
+ [ret_val]
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -142,6 +142,82 @@ class Chef
142
142
  def self.relative_path_from(from, to)
143
143
  pathname = Pathname.new(Chef::Util::PathHelper.cleanpath(to)).relative_path_from(Pathname.new(Chef::Util::PathHelper.cleanpath(from)))
144
144
  end
145
+
146
+ # Retrieves the "home directory" of the current user while trying to ascertain the existence
147
+ # of said directory. The path returned uses / for all separators (the ruby standard format).
148
+ # If the home directory doesn't exist or an error is otherwise encountered, nil is returned.
149
+ #
150
+ # If a set of path elements is provided, they are appended as-is to the home path if the
151
+ # homepath exists.
152
+ #
153
+ # If an optional block is provided, the joined path is passed to that block if the home path is
154
+ # valid and the result of the block is returned instead.
155
+ #
156
+ # Home-path discovery is performed once. If a path is discovered, that value is memoized so
157
+ # that subsequent calls to home_dir don't bounce around.
158
+ #
159
+ # See self.all_homes.
160
+ def self.home(*args)
161
+ @@home_dir ||= self.all_homes { |p| break p }
162
+ if @@home_dir
163
+ path = File.join(@@home_dir, *args)
164
+ block_given? ? (yield path) : path
165
+ end
166
+ end
167
+
168
+ # See self.home. This method performs a similar operation except that it yields all the different
169
+ # possible values of 'HOME' that one could have on this platform. Hence, on windows, if
170
+ # HOMEDRIVE\HOMEPATH and USERPROFILE are different, the provided block will be called twice.
171
+ # This method goes out and checks the existence of each location at the time of the call.
172
+ #
173
+ # The return is a list of all the returned values from each block invocation or a list of paths
174
+ # if no block is provided.
175
+ def self.all_homes(*args)
176
+ paths = []
177
+ if Chef::Platform.windows?
178
+ # By default, Ruby uses the the following environment variables to determine Dir.home:
179
+ # HOME
180
+ # HOMEDRIVE HOMEPATH
181
+ # USERPROFILE
182
+ # Ruby only checks to see if the variable is specified - not if the directory actually exists.
183
+ # On Windows, HOMEDRIVE HOMEPATH can point to a different location (such as an unavailable network mounted drive)
184
+ # while USERPROFILE points to the location where the user application settings and profile are stored. HOME
185
+ # is not defined as an environment variable (usually). If the home path actually uses UNC, then the prefix is
186
+ # HOMESHARE instead of HOMEDRIVE.
187
+ #
188
+ # We instead walk down the following and only include paths that actually exist.
189
+ # HOME
190
+ # HOMEDRIVE HOMEPATH
191
+ # HOMESHARE HOMEPATH
192
+ # USERPROFILE
193
+
194
+ paths << ENV['HOME']
195
+ paths << ENV['HOMEDRIVE'] + ENV['HOMEPATH'] if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
196
+ paths << ENV['HOMESHARE'] + ENV['HOMEPATH'] if ENV['HOMESHARE'] && ENV['HOMEPATH']
197
+ paths << ENV['USERPROFILE']
198
+ end
199
+ paths << Dir.home
200
+
201
+ # Depending on what environment variables we're using, the slashes can go in any which way.
202
+ # Just change them all to / to keep things consistent.
203
+ # Note: Maybe this is a bad idea on some unixy systems where \ might be a valid character depending on
204
+ # the particular brand of kool-aid you consume. This code assumes that \ and / are both
205
+ # path separators on any system being used.
206
+ paths = paths.map { |home_path| home_path.gsub(path_separator, ::File::SEPARATOR) if home_path }
207
+
208
+ # Filter out duplicate paths and paths that don't exist.
209
+ valid_paths = paths.select { |home_path| home_path && Dir.exists?(home_path) }
210
+ valid_paths = valid_paths.uniq
211
+
212
+ # Join all optional path elements at the end.
213
+ # If a block is provided, invoke it - otherwise just return what we've got.
214
+ joined_paths = valid_paths.map { |home_path| File.join(home_path, *args) }
215
+ if block_given?
216
+ joined_paths.each { |p| yield p }
217
+ else
218
+ joined_paths
219
+ end
220
+ end
145
221
  end
146
222
  end
147
223
  end
@@ -20,7 +20,9 @@ require 'mixlib/shellout'
20
20
  require 'chef/mixin/windows_architecture_helper'
21
21
  require 'chef/util/powershell/cmdlet_result'
22
22
 
23
- class Chef::Util::Powershell
23
+ class Chef
24
+ class Util
25
+ class Powershell
24
26
  class Cmdlet
25
27
  def initialize(node, cmdlet, output_format=nil, output_format_options={})
26
28
  @output_format = output_format
@@ -46,6 +48,10 @@ class Chef::Util::Powershell
46
48
  attr_reader :output_format
47
49
 
48
50
  def run(switches={}, execution_options={}, *arguments)
51
+ streams = { :json => CmdletStream.new('json'),
52
+ :verbose => CmdletStream.new('verbose'),
53
+ }
54
+
49
55
  arguments_string = arguments.join(' ')
50
56
 
51
57
  switches_string = command_switches_string(switches)
@@ -56,21 +62,25 @@ class Chef::Util::Powershell
56
62
  json_depth = @output_format_options[:depth]
57
63
  end
58
64
 
59
- json_command = @json_format ? " | convertto-json -compress -depth #{json_depth}" : ""
60
- command_string = "powershell.exe -executionpolicy bypass -noprofile -noninteractive -command \"trap [Exception] {write-error -exception ($_.Exception.Message);exit 1};#{@cmdlet} #{switches_string} #{arguments_string}#{json_command}\";if ( ! $? ) { exit 1 }"
65
+ json_command = @json_format ? " | convertto-json -compress -depth #{json_depth} "\
66
+ "> #{streams[:json].path}" : ""
67
+ redirections = "4> '#{streams[:verbose].path}'"
68
+ command_string = "powershell.exe -executionpolicy bypass -noprofile -noninteractive "\
69
+ "-command \"trap [Exception] {write-error -exception "\
70
+ "($_.Exception.Message);exit 1};#{@cmdlet} #{switches_string} "\
71
+ "#{arguments_string} #{redirections}"\
72
+ "#{json_command}\";if ( ! $? ) { exit 1 }"
61
73
 
62
74
  augmented_options = {:returns => [0], :live_stream => false}.merge(execution_options)
63
75
  command = Mixlib::ShellOut.new(command_string, augmented_options)
64
76
 
65
- os_architecture = "#{ENV['PROCESSOR_ARCHITEW6432']}" == 'AMD64' ? :x86_64 : :i386
66
-
67
77
  status = nil
68
78
 
69
79
  with_os_architecture(@node) do
70
80
  status = command.run_command
71
81
  end
72
82
 
73
- CmdletResult.new(status, @output_format)
83
+ CmdletResult.new(status, streams, @output_format)
74
84
  end
75
85
 
76
86
  def run!(switches={}, execution_options={}, *arguments)
@@ -131,6 +141,30 @@ class Chef::Util::Powershell
131
141
 
132
142
  command_switches.join(' ')
133
143
  end
144
+
145
+ class CmdletStream
146
+ def initialize(name)
147
+ @filename = Dir::Tmpname.create(name) {}
148
+ ObjectSpace.define_finalizer(self, self.class.destroy(@filename))
149
+ end
150
+
151
+ def path
152
+ @filename
153
+ end
154
+
155
+ def read
156
+ if File.exist? @filename
157
+ File.open(@filename, 'rb:bom|UTF-16LE') do |f|
158
+ f.read.encode('UTF-8')
159
+ end
160
+ end
161
+ end
162
+
163
+ def self.destroy(name)
164
+ proc { File.delete(name) if File.exists? name }
165
+ end
166
+ end
134
167
  end
135
168
  end
136
-
169
+ end
170
+ end