chef 16.2.73-universal-mingw32 → 16.3.38-universal-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -4
  3. data/Rakefile +1 -1
  4. data/chef-universal-mingw32.gemspec +2 -2
  5. data/chef.gemspec +2 -1
  6. data/lib/chef/application.rb +12 -0
  7. data/lib/chef/{whitelist.rb → attribute_allowlist.rb} +11 -11
  8. data/lib/chef/{blacklist.rb → attribute_blocklist.rb} +9 -9
  9. data/lib/chef/chef_fs/data_handler/organization_data_handler.rb +1 -2
  10. data/lib/chef/chef_fs/file_system/chef_server/cookbooks_dir.rb +1 -5
  11. data/lib/chef/chef_fs/file_system/repository/base_file.rb +1 -0
  12. data/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb +1 -1
  13. data/lib/chef/client.rb +3 -3
  14. data/lib/chef/cookbook/remote_file_vendor.rb +1 -3
  15. data/lib/chef/cookbook/syntax_check.rb +1 -2
  16. data/lib/chef/cookbook_loader.rb +15 -29
  17. data/lib/chef/data_bag.rb +1 -2
  18. data/lib/chef/deprecated.rb +8 -0
  19. data/lib/chef/dsl/platform_introspection.rb +2 -0
  20. data/lib/chef/environment.rb +1 -2
  21. data/lib/chef/exceptions.rb +3 -0
  22. data/lib/chef/http/authenticator.rb +1 -1
  23. data/lib/chef/knife.rb +4 -4
  24. data/lib/chef/knife/bootstrap.rb +4 -10
  25. data/lib/chef/knife/bootstrap/train_connector.rb +1 -0
  26. data/lib/chef/knife/config_get.rb +1 -0
  27. data/lib/chef/knife/config_list_profiles.rb +4 -1
  28. data/lib/chef/knife/configure.rb +1 -1
  29. data/lib/chef/knife/cookbook_upload.rb +5 -10
  30. data/lib/chef/knife/core/gem_glob_loader.rb +1 -1
  31. data/lib/chef/knife/core/hashed_command_loader.rb +1 -0
  32. data/lib/chef/knife/core/subcommand_loader.rb +20 -1
  33. data/lib/chef/knife/core/ui.rb +8 -2
  34. data/lib/chef/knife/core/windows_bootstrap_context.rb +1 -2
  35. data/lib/chef/knife/rehash.rb +3 -21
  36. data/lib/chef/knife/ssh.rb +5 -1
  37. data/lib/chef/log.rb +7 -2
  38. data/lib/chef/mixin/chef_utils_wiring.rb +40 -0
  39. data/{spec/unit/log_spec.rb → lib/chef/mixin/default_paths.rb} +13 -5
  40. data/lib/chef/mixin/openssl_helper.rb +27 -5
  41. data/lib/chef/mixin/path_sanity.rb +5 -4
  42. data/lib/chef/mixin/shell_out.rb +4 -188
  43. data/lib/chef/mixin/template.rb +1 -0
  44. data/lib/chef/mixin/which.rb +6 -3
  45. data/lib/chef/mixins.rb +1 -0
  46. data/lib/chef/node.rb +36 -12
  47. data/lib/chef/node_map.rb +21 -18
  48. data/lib/chef/platform/service_helpers.rb +31 -28
  49. data/lib/chef/provider/git.rb +12 -4
  50. data/lib/chef/provider/mount/solaris.rb +0 -1
  51. data/lib/chef/provider/package/snap.rb +2 -3
  52. data/lib/chef/provider/package/windows.rb +9 -4
  53. data/lib/chef/provider/package/zypper.rb +0 -1
  54. data/lib/chef/provider/service.rb +2 -2
  55. data/lib/chef/provider/yum_repository.rb +1 -1
  56. data/lib/chef/provider/zypper_repository.rb +1 -1
  57. data/lib/chef/resource.rb +2 -0
  58. data/lib/chef/resource/build_essential.rb +2 -2
  59. data/lib/chef/resource/chef_client_scheduled_task.rb +1 -1
  60. data/lib/chef/resource/chocolatey_feature.rb +1 -2
  61. data/lib/chef/resource/cron/cron_d.rb +1 -1
  62. data/lib/chef/resource/cron_access.rb +2 -2
  63. data/lib/chef/resource/execute.rb +2 -2
  64. data/lib/chef/resource/lwrp_base.rb +1 -0
  65. data/lib/chef/resource/macos_userdefaults.rb +176 -61
  66. data/lib/chef/resource/openssl_x509_certificate.rb +11 -14
  67. data/lib/chef/resource/openssl_x509_crl.rb +1 -2
  68. data/lib/chef/resource/service.rb +2 -2
  69. data/lib/chef/resource/ssh_known_hosts_entry.rb +1 -1
  70. data/lib/chef/resource/sudo.rb +1 -1
  71. data/lib/chef/resource/user_ulimit.rb +1 -1
  72. data/lib/chef/resource/windows_dns_record.rb +17 -0
  73. data/lib/chef/resource/windows_firewall_profile.rb +197 -0
  74. data/lib/chef/resource/windows_security_policy.rb +49 -20
  75. data/lib/chef/resource_inspector.rb +7 -1
  76. data/lib/chef/resources.rb +1 -0
  77. data/lib/chef/role.rb +1 -2
  78. data/lib/chef/shell/shell_session.rb +2 -0
  79. data/lib/chef/util/diff.rb +0 -1
  80. data/lib/chef/version.rb +2 -2
  81. data/lib/chef/win32/registry.rb +1 -2
  82. data/spec/functional/knife/ssh_spec.rb +5 -16
  83. data/spec/functional/resource/aix_service_spec.rb +0 -2
  84. data/spec/functional/resource/aixinit_service_spec.rb +0 -1
  85. data/spec/functional/resource/apt_package_spec.rb +0 -1
  86. data/spec/functional/resource/cron_spec.rb +0 -1
  87. data/spec/functional/resource/git_spec.rb +23 -1
  88. data/spec/functional/resource/group_spec.rb +6 -2
  89. data/spec/functional/resource/insserv_spec.rb +0 -1
  90. data/spec/functional/resource/remote_file_spec.rb +1 -7
  91. data/spec/functional/resource/windows_user_privilege_spec.rb +1 -1
  92. data/spec/functional/run_lock_spec.rb +2 -1
  93. data/spec/functional/shell_spec.rb +5 -5
  94. data/spec/functional/util/powershell/cmdlet_spec.rb +1 -1
  95. data/spec/functional/version_spec.rb +1 -1
  96. data/spec/integration/knife/config_list_profiles_spec.rb +30 -2
  97. data/spec/integration/knife/cookbook_upload_spec.rb +27 -0
  98. data/spec/integration/recipes/accumulator_spec.rb +1 -1
  99. data/spec/integration/recipes/lwrp_inline_resources_spec.rb +1 -1
  100. data/spec/integration/recipes/lwrp_spec.rb +1 -1
  101. data/spec/integration/recipes/notifies_spec.rb +1 -1
  102. data/spec/integration/recipes/notifying_block_spec.rb +1 -1
  103. data/spec/integration/recipes/recipe_dsl_spec.rb +1 -1
  104. data/spec/integration/recipes/resource_converge_if_changed_spec.rb +2 -0
  105. data/spec/integration/recipes/resource_load_spec.rb +2 -0
  106. data/spec/integration/recipes/unified_mode_spec.rb +1 -1
  107. data/spec/integration/recipes/use_partial_spec.rb +1 -1
  108. data/spec/scripts/ssl-serve.rb +1 -1
  109. data/spec/spec_helper.rb +10 -4
  110. data/spec/support/chef_helpers.rb +1 -20
  111. data/spec/support/platform_helpers.rb +0 -2
  112. data/spec/support/shared/functional/file_resource.rb +0 -1
  113. data/spec/support/shared/integration/knife_support.rb +2 -9
  114. data/spec/support/shared/unit/application_dot_d.rb +0 -1
  115. data/spec/unit/application_spec.rb +4 -2
  116. data/spec/unit/chef_fs/file_system/operation_failed_error_spec.rb +2 -4
  117. data/spec/unit/chef_fs/{parallelizer.rb → parallelizer_spec.rb} +1 -1
  118. data/spec/unit/cookbook/gem_installer_spec.rb +2 -1
  119. data/spec/unit/data_collector_spec.rb +1 -1
  120. data/spec/unit/dsl/platform_introspection_spec.rb +1 -0
  121. data/spec/unit/event_dispatch/dispatcher_spec.rb +3 -0
  122. data/spec/unit/json_compat_spec.rb +1 -1
  123. data/spec/unit/knife/bootstrap_spec.rb +2 -6
  124. data/spec/unit/knife/cookbook_upload_spec.rb +7 -10
  125. data/spec/unit/log/syslog_spec.rb +6 -10
  126. data/spec/unit/log/winevt_spec.rb +21 -13
  127. data/spec/unit/lwrp_spec.rb +4 -4
  128. data/spec/unit/mixin/{path_sanity_spec.rb → default_paths_spec.rb} +14 -14
  129. data/spec/unit/mixin/powershell_exec_spec.rb +1 -1
  130. data/spec/unit/mixin/securable_spec.rb +0 -1
  131. data/spec/unit/mixin/shell_out_spec.rb +25 -26
  132. data/spec/unit/mixin/which.rb +8 -0
  133. data/spec/unit/node_spec.rb +98 -11
  134. data/spec/unit/property_spec.rb +5 -5
  135. data/spec/unit/provider/execute_spec.rb +0 -7
  136. data/spec/unit/provider/ifconfig_spec.rb +0 -1
  137. data/spec/unit/provider/package/dnf/python_helper_spec.rb +1 -1
  138. data/spec/unit/provider/package/rubygems_spec.rb +5 -10
  139. data/spec/unit/provider/package/smartos_spec.rb +1 -1
  140. data/spec/unit/provider/package/windows_spec.rb +30 -53
  141. data/spec/unit/provider/service/redhat_spec.rb +1 -1
  142. data/spec/unit/provider/service/windows_spec.rb +2 -6
  143. data/spec/unit/provider/systemd_unit_spec.rb +28 -24
  144. data/spec/unit/provider_spec.rb +1 -0
  145. data/spec/unit/resource/execute_spec.rb +10 -0
  146. data/spec/unit/resource/macos_user_defaults_spec.rb +103 -2
  147. data/spec/unit/resource/windows_firewall_profile_spec.rb +77 -0
  148. data/spec/unit/resource/windows_package_spec.rb +1 -0
  149. data/spec/unit/resource_reporter_spec.rb +1 -1
  150. data/spec/unit/run_context/cookbook_compiler_spec.rb +1 -1
  151. data/spec/unit/run_lock_spec.rb +1 -1
  152. data/spec/unit/scan_access_control_spec.rb +1 -1
  153. data/spec/unit/util/diff_spec.rb +1 -15
  154. data/spec/unit/win32/security_spec.rb +4 -3
  155. metadata +38 -15
@@ -154,6 +154,11 @@ class Chef
154
154
  sha_hash?(result) ? result : nil
155
155
  end
156
156
 
157
+ def already_on_target_branch?
158
+ current_branch = git("rev-parse", "--abbrev-ref", "HEAD", cwd: cwd, returns: [0, 128]).stdout.strip
159
+ current_branch == (new_resource.checkout_branch || new_resource.revision)
160
+ end
161
+
157
162
  def add_remotes
158
163
  if new_resource.additional_remotes.length > 0
159
164
  new_resource.additional_remotes.each_pair do |remote_name, remote_url|
@@ -193,6 +198,9 @@ class Chef
193
198
  # detached head
194
199
  git("checkout", target_revision, cwd: cwd)
195
200
  logger.info "#{new_resource} checked out reference: #{target_revision}"
201
+ elsif already_on_target_branch?
202
+ # we are already on the proper branch
203
+ git("reset", "--hard", target_revision, cwd: cwd)
196
204
  else
197
205
  # need a branch with a tracking branch
198
206
  git("branch", "-f", new_resource.revision, target_revision, cwd: cwd)
@@ -222,13 +230,13 @@ class Chef
222
230
  logger.trace "Fetching updates from #{new_resource.remote} and resetting to revision #{target_revision}"
223
231
  git("fetch", "--prune", new_resource.remote, cwd: cwd)
224
232
  git("fetch", new_resource.remote, "--tags", cwd: cwd)
225
- if new_resource.checkout_branch
233
+ if sha_hash?(new_resource.revision) || is_tag? || already_on_target_branch?
234
+ # detached head or if we are already on the proper branch
235
+ git("reset", "--hard", target_revision, cwd: cwd)
236
+ elsif new_resource.checkout_branch
226
237
  # check out to a local branch
227
238
  git("branch", "-f", new_resource.checkout_branch, target_revision, cwd: cwd)
228
239
  git("checkout", new_resource.checkout_branch, cwd: cwd)
229
- elsif sha_hash?(new_resource.revision) || is_tag?
230
- # detached head
231
- git("reset", "--hard", target_revision, cwd: cwd)
232
240
  else
233
241
  # need a branch with a tracking branch
234
242
  git("branch", "-f", new_resource.revision, target_revision, cwd: cwd)
@@ -1,4 +1,3 @@
1
- # Encoding: utf-8
2
1
  # Author:: Hugo Fichter
3
2
  # Author:: Lamont Granquist (<lamont@chef.io>)
4
3
  # Author:: Joshua Timberman (<joshua@chef.io>)
@@ -137,7 +137,7 @@ class Chef
137
137
 
138
138
  # while it is expected to allow clients to connect using https over
139
139
  # a tcp socket, at this point only a unix socket is supported. the
140
- # socket is /run/snapd.socket note - unix socket is not defined on
140
+ # socket is /run/snapd.socket note - UNIXSocket is not defined on
141
141
  # windows systems
142
142
  if defined?(::UNIXSocket)
143
143
  UNIXSocket.open("/run/snapd.socket") do |socket|
@@ -304,7 +304,7 @@ class Chef
304
304
  SNAP_OPTION
305
305
  end
306
306
 
307
- multipart_form_data = <<~SNAP_S
307
+ <<~SNAP_S
308
308
  Host:
309
309
  Content-Type: multipart/form-data; boundary=#{snap_name}
310
310
  Content-Length: #{content_length}
@@ -320,7 +320,6 @@ class Chef
320
320
  <#{content_length} bytes of snap file data>
321
321
  --#{snap_name}
322
322
  SNAP_S
323
- multipart_form_data
324
323
  end
325
324
 
326
325
  # Constructs json to post for snap changes
@@ -242,22 +242,27 @@ class Chef
242
242
  end
243
243
 
244
244
  def download_source_file
245
+ source_resource.run_action(:create)
246
+ logger.trace("#{new_resource} fetched source file to #{default_download_cache_path}")
247
+ end
248
+
249
+ def source_resource
245
250
  # It seems correct that this is a build_resource rather than declare_resource/DSL use since updating should not trigger a notification
246
251
  # unless the downloaded file is actually installed. The case where the remote_file downloads the package but the package is already
247
252
  # installed on the target should not trigger a notification since the running state did not change.
248
- build_resource(:remote_file, default_download_cache_path) do
253
+ @source_resource ||= build_resource(:remote_file, default_download_cache_path) do
249
254
  source(new_resource.source)
250
255
  cookbook_name = new_resource.cookbook_name
251
256
  checksum(new_resource.checksum)
252
257
  backup(false)
253
258
 
259
+ # since the source_resource can mutate here, we save off the @source_resource so we can inspect the actual state later
254
260
  if new_resource.remote_file_attributes
255
261
  new_resource.remote_file_attributes.each do |(k, v)|
256
262
  send(k.to_sym, v)
257
263
  end
258
264
  end
259
- end.run_action(:create)
260
- logger.trace("#{new_resource} fetched source file to #{default_download_cache_path}")
265
+ end
261
266
  end
262
267
 
263
268
  def default_download_cache_path
@@ -271,7 +276,7 @@ class Chef
271
276
  if new_resource.source.nil?
272
277
  nil
273
278
  elsif uri_scheme?(new_resource.source)
274
- default_download_cache_path
279
+ source_resource.path
275
280
  else
276
281
  new_source = Chef::Util::PathHelper.cleanpath(new_resource.source)
277
282
  ::File.exist?(new_source) ? new_source : nil
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  #
3
2
  # Authors:: Adam Jacob (<adam@chef.io>)
4
3
  # Ionuț Arțăriși (<iartarisi@suse.cz>)
@@ -23,8 +23,8 @@ require "chef-utils" unless defined?(ChefUtils::CANARY)
23
23
  class Chef
24
24
  class Provider
25
25
  class Service < Chef::Provider
26
- include ChefUtils::DSL::Service
27
- extend ChefUtils::DSL::Service
26
+ include Chef::Platform::ServiceHelpers
27
+ extend Chef::Platform::ServiceHelpers
28
28
 
29
29
  def supports
30
30
  @supports ||= new_resource.supports.dup
@@ -37,7 +37,7 @@ class Chef
37
37
  if template_available?(new_resource.source)
38
38
  source new_resource.source
39
39
  else
40
- source ::File.expand_path("../support/yum_repo.erb", __FILE__)
40
+ source ::File.expand_path("support/yum_repo.erb", __dir__)
41
41
  local true
42
42
  end
43
43
  sensitive new_resource.sensitive
@@ -41,7 +41,7 @@ class Chef
41
41
  if template_available?(new_resource.source)
42
42
  source new_resource.source
43
43
  else
44
- source ::File.expand_path("../support/zypper_repo.erb", __FILE__)
44
+ source ::File.expand_path("support/zypper_repo.erb", __dir__)
45
45
  local true
46
46
  end
47
47
  sensitive new_resource.sensitive
@@ -638,6 +638,7 @@ class Chef
638
638
  # Do NOT use this. It may be removed. It is for internal purposes only.
639
639
  # @api private
640
640
  attr_reader :resource_initializing
641
+
641
642
  def resource_initializing=(value)
642
643
  if value
643
644
  @resource_initializing = true
@@ -888,6 +889,7 @@ class Chef
888
889
  # have.
889
890
  #
890
891
  attr_writer :allowed_actions
892
+
891
893
  def allowed_actions(value = NOT_PASSED)
892
894
  if value != NOT_PASSED
893
895
  self.allowed_actions = value
@@ -146,8 +146,8 @@ class Chef
146
146
  def install_xcode_cli_tools(label)
147
147
  # This script was graciously borrowed and modified from Tim Sutton's
148
148
  # osx-vm-templates at https://github.com/timsutton/osx-vm-templates/blob/b001475df54a9808d3d56d06e71b8fa3001fff42/scripts/xcode-cli-tools.sh
149
- execute "install Xcode Command Line tools" do
150
- command <<-EOH
149
+ bash "install Xcode Command Line Tools" do
150
+ code <<-EOH
151
151
  # create the placeholder file that's checked by CLI updates' .dist code
152
152
  # in Apple's SUS catalog
153
153
  touch /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
@@ -172,7 +172,7 @@ class Chef
172
172
  # Fetch path of cmd.exe through environment variable comspec
173
173
  cmd_path = ENV["COMSPEC"]
174
174
 
175
- "#{cmd_path} /c \'#{client_cmd}\'"
175
+ "#{cmd_path} /c \"#{client_cmd}\""
176
176
  end
177
177
 
178
178
  # Build command line to pass to cmd.exe
@@ -89,8 +89,7 @@ class Chef
89
89
  # @param [String] action the name of the action to perform
90
90
  # @return [String] the choco feature command string
91
91
  def choco_cmd(action)
92
- cmd = "#{ENV["ALLUSERSPROFILE"]}\\chocolatey\\bin\\choco feature #{action} --name #{new_resource.feature_name}"
93
- cmd
92
+ "#{ENV["ALLUSERSPROFILE"]}\\chocolatey\\bin\\choco feature #{action} --name #{new_resource.feature_name}"
94
93
  end
95
94
  end
96
95
  end
@@ -158,7 +158,7 @@ class Chef
158
158
 
159
159
  # @todo this is Chef 12 era cleanup. Someday we should remove it all
160
160
  template "/etc/cron.d/#{sanitized_name}" do
161
- source ::File.expand_path("../../support/cron.d.erb", __FILE__)
161
+ source ::File.expand_path("../support/cron.d.erb", __dir__)
162
162
  local true
163
163
  mode new_resource.mode
164
164
  variables(
@@ -70,7 +70,7 @@ class Chef
70
70
 
71
71
  with_run_context :root do
72
72
  edit_resource(:template, allow_path) do |new_resource|
73
- source ::File.expand_path("../support/cron_access.erb", __FILE__)
73
+ source ::File.expand_path("support/cron_access.erb", __dir__)
74
74
  local true
75
75
  mode "0600"
76
76
  variables["users"] ||= []
@@ -87,7 +87,7 @@ class Chef
87
87
 
88
88
  with_run_context :root do
89
89
  edit_resource(:template, deny_path) do |new_resource|
90
- source ::File.expand_path("../support/cron_access.erb", __FILE__)
90
+ source ::File.expand_path("support/cron_access.erb", __dir__)
91
91
  local true
92
92
  mode "0600"
93
93
  variables["users"] ||= []
@@ -630,11 +630,11 @@ class Chef
630
630
  end
631
631
 
632
632
  # if domain is provided in both username and domain
633
- if specified_user && ((specified_user.include? '\\') || (specified_user.include? "@")) && specified_domain
633
+ if specified_user.is_a?(String) && ((specified_user.include? '\\') || (specified_user.include? "@")) && specified_domain
634
634
  raise ArgumentError, "The domain is provided twice. Username: `#{specified_user}`, Domain: `#{specified_domain}`. Please specify domain only once."
635
635
  end
636
636
 
637
- if ! specified_user.nil? && specified_domain.nil?
637
+ if specified_user.is_a?(String) && specified_domain.nil?
638
638
  # Splitting username of format: Domain\Username
639
639
  domain_and_user = user.split('\\')
640
640
 
@@ -103,6 +103,7 @@ class Chef
103
103
  protected
104
104
 
105
105
  attr_writer :loaded_lwrps
106
+
106
107
  def loaded_lwrps
107
108
  @loaded_lwrps ||= {}
108
109
  end
@@ -16,6 +16,8 @@
16
16
  #
17
17
 
18
18
  require_relative "../resource"
19
+ require_relative "../dist"
20
+ require "plist"
19
21
 
20
22
  class Chef
21
23
  class Resource
@@ -28,99 +30,210 @@ class Chef
28
30
 
29
31
  description "Use the **macos_userdefaults** resource to manage the macOS user defaults system. The properties of this resource are passed to the defaults command, and the parameters follow the convention of that command. See the defaults(1) man page for details on how the tool works."
30
32
  introduced "14.0"
33
+ examples <<~DOC
34
+ **Specify a global domain value**
35
+
36
+ ```ruby
37
+ macos_userdefaults 'Full keyboard access to all controls' do
38
+ key 'AppleKeyboardUIMode'
39
+ value 2
40
+ end
41
+ ```
42
+
43
+ **Setting a value on a specific domain**
44
+
45
+ ```ruby
46
+ macos_userdefaults 'Enable macOS firewall' do
47
+ domain '/Library/Preferences/com.apple.alf'
48
+ key 'globalstate'
49
+ value 1
50
+ end
51
+ ```
52
+
53
+ **Specifying the type of a key to skip automatic type detection**
54
+
55
+ ```ruby
56
+ macos_userdefaults 'Finder expanded save dialogs' do
57
+ key 'NSNavPanelExpandedStateForSaveMode'
58
+ value 'TRUE'
59
+ type 'bool'
60
+ end
61
+ ```
62
+ DOC
31
63
 
32
64
  property :domain, String,
33
65
  description: "The domain that the user defaults belong to.",
34
- required: true
66
+ default: "NSGlobalDomain",
67
+ default_description: "NSGlobalDomain: the global domain.",
68
+ desired_state: false
35
69
 
36
70
  property :global, [TrueClass, FalseClass],
37
71
  description: "Determines whether or not the domain is global.",
38
- default: false
72
+ deprecated: true,
73
+ default: false,
74
+ desired_state: false
39
75
 
40
76
  property :key, String,
41
- description: "The preference key."
77
+ description: "The preference key.",
78
+ required: true
79
+
80
+ property :host, [String, Symbol],
81
+ description: "Set either :current or a hostname to set the user default at the host level.",
82
+ desired_state: false,
83
+ introduced: "16.3"
42
84
 
43
85
  property :value, [Integer, Float, String, TrueClass, FalseClass, Hash, Array],
44
- description: "The value of the key.",
45
- required: true
86
+ description: "The value of the key. Note: With the `type` property set to `bool`, `String` forms of Boolean true/false values that Apple accepts in the defaults command will be coerced: 0/1, 'TRUE'/'FALSE,' 'true'/false', 'YES'/'NO', or 'yes'/'no'.",
87
+ required: [:write],
88
+ coerce: proc { |v| v.is_a?(Hash) ? v.transform_keys(&:to_s) : v } # make sure keys are all strings for comparison
46
89
 
47
90
  property :type, String,
48
91
  description: "The value type of the preference key.",
49
- default: ""
92
+ equal_to: %w{bool string int float array dict},
93
+ desired_state: false
50
94
 
51
95
  property :user, String,
52
- description: "The system user that the default will be applied to."
96
+ description: "The system user that the default will be applied to.",
97
+ desired_state: false
53
98
 
54
99
  property :sudo, [TrueClass, FalseClass],
55
- description: "Set to true if the setting you wish to modify requires privileged access.",
100
+ description: "Set to true if the setting you wish to modify requires privileged access. This requires passwordless sudo for the '/usr/bin/defaults' command to be setup for the user running #{Chef::Dist::PRODUCT}.",
56
101
  default: false,
57
102
  desired_state: false
58
103
 
59
- # @todo this should get refactored away: https://github.com/chef/chef/issues/7622
60
- property :is_set, [TrueClass, FalseClass],
61
- default: false,
62
- desired_state: false,
63
- skip_docs: true
104
+ load_current_value do |desired|
105
+ Chef::Log.debug "#load_current_value: shelling out \"#{defaults_export_cmd(desired).join(" ")}\" to determine state"
106
+ state = shell_out(defaults_export_cmd(desired), user: desired.user)
64
107
 
65
- # coerce various ways of representing a boolean into either 0 (false) or 1 (true)
66
- # which is what the defaults CLI expects. Why? Well defaults itself accepts a few
67
- # different formats, but when you do a read command it all comes back as 1 or 0.
68
- def coerce_booleans(val)
69
- return 1 if [true, "TRUE", "1", "true", "YES", "yes"].include?(val)
70
- return 0 if [false, "FALSE", "0", "false", "NO", "no"].include?(val)
108
+ if state.error? || state.stdout.empty?
109
+ Chef::Log.debug "#load_current_value: #{defaults_export_cmd(desired).join(" ")} returned stdout: #{state.stdout} and stderr: #{state.stderr}"
110
+ current_value_does_not_exist!
111
+ end
112
+
113
+ plist_data = ::Plist.parse_xml(state.stdout)
114
+
115
+ # handle the situation where the key doesn't exist in the domain
116
+ if plist_data.key?(desired.key)
117
+ key desired.key
118
+ else
119
+ current_value_does_not_exist!
120
+ end
71
121
 
72
- val
122
+ value plist_data[desired.key]
73
123
  end
74
124
 
75
- load_current_value do |desired|
76
- value = coerce_booleans(desired.value)
77
- cmd = "defaults read '#{desired.domain}' "
78
- cmd << "'#{desired.key}' " if desired.key
79
- cmd << " | grep -qx '#{value}'"
80
-
81
- vc = if desired.user.nil?
82
- shell_out(cmd)
83
- else
84
- shell_out(cmd, user: desired.user)
85
- end
86
-
87
- is_set !vc.error?
125
+ #
126
+ # The defaults command to export a domain
127
+ #
128
+ # @return [Array] defaults command
129
+ #
130
+ def defaults_export_cmd(resource)
131
+ state_cmd = ["/usr/bin/defaults"]
132
+
133
+ if resource.host == "current"
134
+ state_cmd.concat(["-currentHost"])
135
+ elsif resource.host # they specified a non-nil value, which is a hostname
136
+ state_cmd.concat(["-host", resource.host])
137
+ end
138
+
139
+ state_cmd.concat(["export", resource.domain, "-"])
140
+ state_cmd
88
141
  end
89
142
 
90
143
  action :write do
91
- description "Write the setting to the specified domain"
92
-
93
- unless current_resource.is_set
94
- cmd = ["defaults write"]
95
- cmd.unshift("sudo") if new_resource.sudo
96
-
97
- cmd << if new_resource.global
98
- "NSGlobalDomain"
99
- else
100
- "'#{new_resource.domain}'"
101
- end
102
-
103
- cmd << "'#{new_resource.key}'" if new_resource.key
104
- value = new_resource.value
105
- type = new_resource.type.empty? ? value_type(value) : new_resource.type
106
- # creates a string of Key1 Value1 Key2 Value2...
107
- value = value.map { |k, v| "\"#{k}\" \"#{v}\"" }.join(" ") if type == "dict"
108
- if type == "array"
109
- value = value.join("' '")
110
- value = "'#{value}'"
111
- end
112
- cmd << "-#{type}" if type
113
- cmd << value
144
+ description "Write the value to the specified domain/key."
114
145
 
115
- # FIXME: this should use cmd directly as an array argument, but then the quoting
116
- # of individual args above needs to be removed as well.
117
- execute cmd.join(" ") do
118
- user new_resource.user unless new_resource.user.nil?
119
- end
146
+ converge_if_changed do
147
+ cmd = defaults_modify_cmd
148
+ Chef::Log.debug("Updating defaults value by shelling out: #{cmd.join(" ")}")
149
+
150
+ shell_out!(cmd, user: new_resource.user)
151
+ end
152
+ end
153
+
154
+ action :delete do
155
+ description "Delete a key from a domain."
156
+
157
+ # if it's not there there's nothing to remove
158
+ return unless current_resource
159
+
160
+ converge_by("delete domain:#{new_resource.domain} key:#{new_resource.key}") do
161
+
162
+ cmd = defaults_modify_cmd
163
+ Chef::Log.debug("Removing defaults key by shelling out: #{cmd.join(" ")}")
164
+
165
+ shell_out!(cmd, user: new_resource.user)
120
166
  end
121
167
  end
122
168
 
123
169
  action_class do
170
+ #
171
+ # The command used to write or delete delete values from domains
172
+ #
173
+ # @return [Array] Array representation of defaults command to run
174
+ #
175
+ def defaults_modify_cmd
176
+ cmd = ["/usr/bin/defaults"]
177
+
178
+ if new_resource.host == :current
179
+ cmd.concat(["-currentHost"])
180
+ elsif new_resource.host # they specified a non-nil value, which is a hostname
181
+ cmd.concat(["-host", new_resource.host])
182
+ end
183
+
184
+ cmd.concat([action.to_s, new_resource.domain, new_resource.key])
185
+ cmd.concat(processed_value) if action == :write
186
+ cmd.prepend("sudo") if new_resource.sudo
187
+ cmd
188
+ end
189
+
190
+ #
191
+ # convert the provided value into the format defaults expects
192
+ #
193
+ # @return [array] array of values starting with the type if applicable
194
+ #
195
+ def processed_value
196
+ type = new_resource.type || value_type(new_resource.value)
197
+
198
+ # when dict this creates an array of values ["Key1", "Value1", "Key2", "Value2" ...]
199
+ cmd_values = ["-#{type}"]
200
+
201
+ case type
202
+ when "dict"
203
+ cmd_values.concat(new_resource.value.flatten)
204
+ when "array"
205
+ cmd_values.concat(new_resource.value)
206
+ when "bool"
207
+ cmd_values.concat(bool_to_defaults_bool(new_resource.value))
208
+ else
209
+ cmd_values.concat([new_resource.value])
210
+ end
211
+
212
+ cmd_values
213
+ end
214
+
215
+ #
216
+ # defaults booleans on the CLI must be 'TRUE' or 'FALSE' so convert various inputs to that
217
+ #
218
+ # @param [String, Integer, Boolean] input <description>
219
+ #
220
+ # @return [String] TRUE or FALSE
221
+ #
222
+ def bool_to_defaults_bool(input)
223
+ return ["TRUE"] if [true, "TRUE", "1", "true", "YES", "yes"].include?(input)
224
+ return ["FALSE"] if [false, "FALSE", "0", "false", "NO", "no"].include?(input)
225
+
226
+ # make sure it's very clear bad input was given
227
+ raise ArgumentError, "#{input} cannot be converted to a boolean value for use with Apple's defaults command. Acceptable values are: 'TRUE', 'YES', 'true, 'yes', '0', true, 'FALSE', 'false', 'NO', 'no', '1', or false."
228
+ end
229
+
230
+ #
231
+ # convert ruby type to defaults type
232
+ #
233
+ # @param [Integer, Float, String, TrueClass, FalseClass, Hash, Array] value The value being set
234
+ #
235
+ # @return [string, nil] the type value used by defaults or nil if not applicable
236
+ #
124
237
  def value_type(value)
125
238
  case value
126
239
  when true, false
@@ -133,6 +246,8 @@ class Chef
133
246
  "dict"
134
247
  when Array
135
248
  "array"
249
+ when String
250
+ "string"
136
251
  end
137
252
  end
138
253
  end