chef 15.3.14-universal-mingw32 → 15.4.45-universal-mingw32

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/chef.gemspec +2 -2
  3. data/lib/chef/application/solo.rb +1 -1
  4. data/lib/chef/event_dispatch/dispatcher.rb +9 -2
  5. data/lib/chef/formatters/doc.rb +3 -3
  6. data/lib/chef/knife.rb +13 -3
  7. data/lib/chef/knife/bootstrap.rb +28 -4
  8. data/lib/chef/knife/bootstrap/templates/chef-full.erb +7 -8
  9. data/lib/chef/knife/data_bag_secret_options.rb +11 -4
  10. data/lib/chef/knife/download.rb +2 -2
  11. data/lib/chef/knife/exec.rb +9 -1
  12. data/lib/chef/knife/ssh.rb +1 -1
  13. data/lib/chef/knife/ssl_check.rb +1 -1
  14. data/lib/chef/knife/supermarket_list.rb +19 -7
  15. data/lib/chef/knife/supermarket_search.rb +3 -2
  16. data/lib/chef/node/attribute.rb +2 -0
  17. data/lib/chef/node/attribute_collections.rb +8 -0
  18. data/lib/chef/node/immutable_collections.rb +12 -0
  19. data/lib/chef/node/mixin/immutablize_array.rb +1 -0
  20. data/lib/chef/node/mixin/immutablize_hash.rb +1 -0
  21. data/lib/chef/provider.rb +14 -8
  22. data/lib/chef/provider/package/chocolatey.rb +11 -3
  23. data/lib/chef/provider/package/dnf/python_helper.rb +8 -3
  24. data/lib/chef/provider/package/windows/exe.rb +2 -2
  25. data/lib/chef/provider/package/windows/msi.rb +3 -3
  26. data/lib/chef/provider/package/yum/python_helper.rb +8 -3
  27. data/lib/chef/provider/service/windows.rb +1 -1
  28. data/lib/chef/resource/apt_repository.rb +19 -13
  29. data/lib/chef/resource/apt_update.rb +15 -1
  30. data/lib/chef/resource/archive_file.rb +10 -1
  31. data/lib/chef/resource/build_essential.rb +14 -1
  32. data/lib/chef/resource/chocolatey_config.rb +17 -1
  33. data/lib/chef/resource/chocolatey_feature.rb +15 -0
  34. data/lib/chef/resource/chocolatey_package.rb +31 -1
  35. data/lib/chef/resource/chocolatey_source.rb +17 -1
  36. data/lib/chef/resource/cookbook_file.rb +1 -1
  37. data/lib/chef/resource/cron_access.rb +22 -1
  38. data/lib/chef/resource/cron_d.rb +46 -1
  39. data/lib/chef/resource/dmg_package.rb +28 -0
  40. data/lib/chef/resource/kernel_module.rb +61 -0
  41. data/lib/chef/resource/sudo.rb +2 -2
  42. data/lib/chef/resource/windows_ad_join.rb +72 -3
  43. data/lib/chef/resource/windows_service.rb +1 -1
  44. data/lib/chef/resource/windows_share.rb +2 -1
  45. data/lib/chef/shell.rb +4 -4
  46. data/lib/chef/shell/ext.rb +2 -2
  47. data/lib/chef/train_transport.rb +1 -1
  48. data/lib/chef/version.rb +1 -1
  49. data/spec/functional/resource/ifconfig_spec.rb +0 -2
  50. data/spec/functional/resource/mount_spec.rb +0 -4
  51. data/spec/functional/util/powershell/cmdlet_spec.rb +2 -2
  52. data/spec/integration/knife/chef_repo_path_spec.rb +4 -2
  53. data/spec/integration/recipes/resource_converge_if_changed_spec.rb +19 -19
  54. data/spec/spec_helper.rb +2 -0
  55. data/spec/unit/formatters/doc_spec.rb +18 -0
  56. data/spec/unit/knife/bootstrap_spec.rb +46 -10
  57. data/spec/unit/knife/supermarket_list_spec.rb +70 -0
  58. data/spec/unit/knife/supermarket_search_spec.rb +85 -0
  59. data/spec/unit/node/attribute_spec.rb +22 -0
  60. data/spec/unit/node/immutable_collections_spec.rb +72 -144
  61. data/spec/unit/provider/package/chocolatey_spec.rb +50 -35
  62. data/spec/unit/provider/package/windows/exe_spec.rb +1 -1
  63. data/spec/unit/provider/service/windows_spec.rb +23 -3
  64. data/spec/unit/resource/chocolatey_package_spec.rb +17 -2
  65. data/spec/unit/resource/windows_ad_join_spec.rb +4 -0
  66. data/spec/unit/resource/windows_service_spec.rb +5 -0
  67. data/spec/unit/resource/windows_share_spec.rb +7 -0
  68. data/tasks/docs.rb +4 -1
  69. metadata +10 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d697d8e4f6cda468d0586b03e8c1eddda7a72ab33338c4012fc35ceca6b6f389
4
- data.tar.gz: 8c6f5f5797b7ca69023b0b29db9b76e76f60b8c6c5469603fbc82815037a991f
3
+ metadata.gz: 21b19428acc0503074938fb4e5dc93cb5cd70542f35625634b205d74b662bf48
4
+ data.tar.gz: 9e75d184a6494c67c334198d6cc55c60a9441d9bf18bece3598d8476d95adc8f
5
5
  SHA512:
6
- metadata.gz: 45977ec05519331f10644ed4ac549554d2330078dc4ea587c48c4dbb22575caaf4efc96e05f2df2108f4f578484b7e0e041d119cf15d6e1f6ee1111e6e0dcc4a
7
- data.tar.gz: e640ff48fd1aac419652fba2c8fbe3a613a15bb1e88d09b24ef2e2549a2815ebddc8838ef05f234c56b01f3081c83a8256d5883352f0267ee4a4cb53fd6e2ea6
6
+ metadata.gz: 6877efd745673cb51b0879c8e78abb0ea88049e320e07d69052bc3616f4c53077f039f47310b90fb469e13f90f9f5df3b8935aee3ea45ba5aa26984829d6a849
7
+ data.tar.gz: ffe577ee8b5b6f36e0a75930a9878bb659c60488c4ea9471e2596294a9590c6db0887f5be1d8266d26052b55702b89cf5ee15d432cee54aee63d9b3da65aab8d
data/chef.gemspec CHANGED
@@ -16,8 +16,8 @@ Gem::Specification.new do |s|
16
16
  s.required_ruby_version = ">= 2.5.0"
17
17
 
18
18
  s.add_dependency "chef-config", "= #{Chef::VERSION}"
19
- s.add_dependency "train-core", "~> 3.0"
20
- s.add_dependency "train-winrm"
19
+ s.add_dependency "train-core", "~> 3.1"
20
+ s.add_dependency "train-winrm", ">= 0.2.5"
21
21
 
22
22
  s.add_dependency "license-acceptance", "~> 1.0", ">= 1.0.5"
23
23
  s.add_dependency "mixlib-cli", ">= 2.1.1", "< 3.0"
@@ -52,13 +52,13 @@ class Chef::Application::Solo < Chef::Application::Base
52
52
  # Get this party started
53
53
  def run(enforce_license: false)
54
54
  setup_signal_handlers
55
- setup_application
56
55
  reconfigure
57
56
  check_license_acceptance if enforce_license
58
57
  for_ezra if Chef::Config[:ez]
59
58
  if !Chef::Config[:solo_legacy_mode]
60
59
  Chef::Application::Client.new.run
61
60
  else
61
+ setup_application
62
62
  run_application
63
63
  end
64
64
  end
@@ -10,11 +10,18 @@ class Chef
10
10
  class Dispatcher < Base
11
11
 
12
12
  attr_reader :subscribers
13
- attr_reader :event_list
14
13
 
15
14
  def initialize(*subscribers)
16
15
  @subscribers = subscribers
17
- @event_list = []
16
+ end
17
+
18
+ # Since the cookbook synchronizer will call this object from threads, we
19
+ # have to deal with concurrent access to this object. Since we don't want
20
+ # threads to handle events from other threads, we just use thread local
21
+ # storage.
22
+ #
23
+ def event_list
24
+ Thread.current[:chef_client_event_list] ||= []
18
25
  end
19
26
 
20
27
  # Add a new subscriber to the list of registered subscribers
@@ -236,11 +236,11 @@ class Chef
236
236
  end
237
237
 
238
238
  def resource_update_progress(resource, current, total, interval)
239
- @progress[resource] ||= 0
239
+ @progress[resource] ||= -1
240
240
 
241
- percent_complete = (current.to_f / total.to_f * 100).to_i
241
+ percent_complete = (current.to_f / total.to_f * 100).to_i unless total.to_f == 0.0
242
242
 
243
- if percent_complete > @progress[resource]
243
+ if percent_complete && percent_complete > @progress[resource]
244
244
 
245
245
  @progress[resource] = percent_complete
246
246
 
data/lib/chef/knife.rb CHANGED
@@ -64,6 +64,12 @@ class Chef
64
64
  attr_accessor :name_args
65
65
  attr_accessor :ui
66
66
 
67
+ # knife acl subcommands are grouped in this category using this constant to verify.
68
+ OPSCODE_HOSTED_CHEF_ACCESS_CONTROL = %w{acl group user}.freeze
69
+
70
+ # knife opc subcommands are grouped in this category using this constant to verify.
71
+ CHEF_ORGANIZATION_MANAGEMENT = %w{opc}.freeze
72
+
67
73
  # Configure mixlib-cli to always separate defaults from user-supplied CLI options
68
74
  def self.use_separate_defaults?
69
75
  true
@@ -234,7 +240,7 @@ class Chef
234
240
  dependency_loaders.each(&:call)
235
241
  end
236
242
 
237
- OFFICIAL_PLUGINS = %w{ec2 rackspace windows openstack azure google linode push vcenter lpar}.freeze
243
+ OFFICIAL_PLUGINS = %w{lpar openstack push rackspace vcenter}.freeze
238
244
 
239
245
  class << self
240
246
  def list_commands(preferred_category = nil)
@@ -270,10 +276,14 @@ class Chef
270
276
  ui.info("If this is a recently installed plugin, please run 'knife rehash' to update the subcommands cache.")
271
277
  end
272
278
 
273
- if category_commands = guess_category(args)
279
+ if CHEF_ORGANIZATION_MANAGEMENT.include?(args[0])
280
+ list_commands("CHEF ORGANIZATION MANAGEMENT")
281
+ elsif OPSCODE_HOSTED_CHEF_ACCESS_CONTROL.include?(args[0])
282
+ list_commands("OPSCODE HOSTED CHEF ACCESS CONTROL")
283
+ elsif category_commands = guess_category(args)
274
284
  list_commands(category_commands)
275
285
  elsif OFFICIAL_PLUGINS.include?(args[0]) # command was an uninstalled official chef knife plugin
276
- ui.info("Use `#{Chef::Dist::EXEC} gem install knife-#{args[0]}` to install the plugin into ChefDK")
286
+ ui.info("Use `#{Chef::Dist::EXEC} gem install knife-#{args[0]}` to install the plugin into ChefDK / Chef Workstation")
277
287
  else
278
288
  list_commands
279
289
  end
@@ -616,7 +616,7 @@ class Chef
616
616
 
617
617
  def connect!
618
618
  ui.info("Connecting to #{ui.color(server_name, :bold)}")
619
- opts = connection_opts.dup
619
+ opts ||= connection_opts.dup
620
620
  do_connect(opts)
621
621
  rescue Train::Error => e
622
622
  # We handle these by message text only because train only loads the
@@ -638,8 +638,10 @@ class Chef
638
638
  EOM
639
639
  # FIXME: this should save the key to known_hosts but doesn't appear to be
640
640
  config[:ssh_verify_host_key] = :accept_new
641
- do_connect(connection_opts(reset: true))
642
- elsif ssh? && e.cause && e.cause.class == Net::SSH::AuthenticationFailed
641
+ conn_opts = connection_opts(reset: true)
642
+ opts.merge! conn_opts
643
+ retry
644
+ elsif (ssh? && e.cause && e.cause.class == Net::SSH::AuthenticationFailed) || (ssh? && e.class == Train::ClientError && e.reason == :no_ssh_password_or_key_available)
643
645
  if connection.password_auth?
644
646
  raise
645
647
  else
@@ -650,7 +652,23 @@ class Chef
650
652
  end
651
653
 
652
654
  opts.merge! force_ssh_password_opts(password)
653
- do_connect(opts)
655
+ retry
656
+ else
657
+ raise
658
+ end
659
+ rescue RuntimeError => e
660
+ if winrm? && e.message == "password is a required option"
661
+ if connection.password_auth?
662
+ raise
663
+ else
664
+ ui.warn("Failed to authenticate #{opts[:user]} to #{server_name} - trying password auth")
665
+ password = ui.ask("Enter password for #{opts[:user]}@#{server_name}.") do |q|
666
+ q.echo = false
667
+ end
668
+ end
669
+
670
+ opts.merge! force_winrm_password_opts(password)
671
+ retry
654
672
  else
655
673
  raise
656
674
  end
@@ -1023,6 +1041,12 @@ class Chef
1023
1041
  }
1024
1042
  end
1025
1043
 
1044
+ def force_winrm_password_opts(password)
1045
+ {
1046
+ password: password,
1047
+ }
1048
+ end
1049
+
1026
1050
  # Looks up configuration entries, first in the class member
1027
1051
  # `config` which contains options populated from CLI flags.
1028
1052
  # If the entry is not found there, Chef::Config[:knife][KEY]
@@ -1,4 +1,3 @@
1
- sh -c '
2
1
  <%= "https_proxy=\"#{knife_config[:bootstrap_proxy]}\" export https_proxy" if knife_config[:bootstrap_proxy] %>
3
2
  <%= "no_proxy=\"#{knife_config[:bootstrap_no_proxy]}\" export no_proxy" if knife_config[:bootstrap_no_proxy] %>
4
3
 
@@ -189,24 +188,24 @@ fi
189
188
  mkdir -p <%= Chef::Dist::CONF_DIR %>
190
189
 
191
190
  <% if client_pem -%>
192
- cat > <%= Chef::Dist::CONF_DIR %>/client.pem <<'EOP'
191
+ (umask 077 && (cat > <%= Chef::Dist::CONF_DIR %>/client.pem <<'EOP'
193
192
  <%= ::File.read(::File.expand_path(client_pem)) %>
194
193
  EOP
195
- chmod 0600 <%= Chef::Dist::CONF_DIR %>/client.pem
194
+ )) || exit 1
196
195
  <% end -%>
197
196
 
198
197
  <% if validation_key -%>
199
- cat > <%= Chef::Dist::CONF_DIR %>/validation.pem <<'EOP'
198
+ (umask 077 && (cat > <%= Chef::Dist::CONF_DIR %>/validation.pem <<'EOP'
200
199
  <%= validation_key %>
201
200
  EOP
202
- chmod 0600 <%= Chef::Dist::CONF_DIR %>/validation.pem
201
+ )) || exit 1
203
202
  <% end -%>
204
203
 
205
204
  <% if encrypted_data_bag_secret -%>
206
- cat > <%= Chef::Dist::CONF_DIR %>/encrypted_data_bag_secret <<'EOP'
205
+ (umask 077 && (cat > <%= Chef::Dist::CONF_DIR %>/encrypted_data_bag_secret <<'EOP'
207
206
  <%= encrypted_data_bag_secret %>
208
207
  EOP
209
- chmod 0600 <%= Chef::Dist::CONF_DIR %>/encrypted_data_bag_secret
208
+ )) || exit 1
210
209
  <% end -%>
211
210
 
212
211
  <% unless trusted_certs.empty? -%>
@@ -240,4 +239,4 @@ mkdir -p <%= Chef::Dist::CONF_DIR %>/client.d
240
239
 
241
240
  echo "Starting the first <%= Chef::Dist::PRODUCT %> Client run..."
242
241
 
243
- <%= start_chef %>'
242
+ <%= start_chef %>
@@ -36,7 +36,7 @@ class Chef
36
36
  def self.included(base)
37
37
  base.option :secret,
38
38
  short: "-s SECRET",
39
- long: "--secret ",
39
+ long: "--secret SECRET",
40
40
  description: "The secret key to use to encrypt data bag item values. Can also be defaulted in your config with the key 'secret'.",
41
41
  # Need to store value from command line in separate variable - knife#merge_configs populates same keys
42
42
  # on config object from
@@ -80,9 +80,16 @@ class Chef
80
80
  end
81
81
 
82
82
  def validate_secrets
83
- if has_cl_secret? && has_cl_secret_file?
84
- ui.fatal("Please specify only one of --secret, --secret-file")
85
- exit(1)
83
+ if has_cl_secret?
84
+ if opt_parser.default_argv.include?("-s")
85
+ ui.warn("Secret short option -s is deprecated and will remove in the future. Please use --secret instead.
86
+ ")
87
+ end
88
+
89
+ if has_cl_secret_file?
90
+ ui.fatal("Please specify only one of --secret, --secret-file")
91
+ exit(1)
92
+ end
86
93
  end
87
94
 
88
95
  if knife_config[:secret] && knife_config[:secret_file]
@@ -43,7 +43,7 @@ class Chef
43
43
  long: "--[no-]force",
44
44
  boolean: true,
45
45
  default: false,
46
- description: "Force upload of files even if they match (quicker and harmless, but doesn't print out what it changed)."
46
+ description: "Force download of files even if they match (quicker and harmless, but doesn't print out what it changed)."
47
47
 
48
48
  option :dry_run,
49
49
  long: "--dry-run",
@@ -56,7 +56,7 @@ class Chef
56
56
  long: "--[no-]diff",
57
57
  boolean: true,
58
58
  default: true,
59
- description: "Turn off to avoid uploading existing files; only new (and possibly deleted) files with --no-diff."
59
+ description: "Turn off to avoid downloading existing files; only new (and possibly deleted) files with --no-diff."
60
60
 
61
61
  option :cookbook_version,
62
62
  long: "--cookbook-version VERSION",
@@ -40,7 +40,7 @@ class Chef::Knife::Exec < Chef::Knife
40
40
  end
41
41
 
42
42
  def run
43
- config[:script_path] ||= Array(Chef::Config[:script_path])
43
+ config[:script_path] = Array(config[:script_path] || Chef::Config[:script_path])
44
44
 
45
45
  # Default script paths are chef-repo/.chef/scripts and ~/.chef/scripts
46
46
  config[:script_path] << File.join(Chef::Knife.chef_config_dir, "scripts") if Chef::Knife.chef_config_dir
@@ -57,6 +57,14 @@ class Chef::Knife::Exec < Chef::Knife
57
57
  context.instance_eval(IO.read(file), file, 0)
58
58
  end
59
59
  else
60
+ puts "An interactive shell is opened"
61
+ puts
62
+ puts "Type your script and do:"
63
+ puts
64
+ puts "1. To run the script, use 'Ctrl D'"
65
+ puts "2. To exit, use 'Ctrl/Shift C'"
66
+ puts
67
+ puts "Type here a script..."
60
68
  script = STDIN.read
61
69
  context.instance_eval(script, "STDIN", 0)
62
70
  end
@@ -623,7 +623,7 @@ class Chef
623
623
  end
624
624
 
625
625
  session.close
626
- if exit_status != 0
626
+ if exit_status && exit_status != 0
627
627
  exit exit_status
628
628
  else
629
629
  exit_status
@@ -190,7 +190,7 @@ class Chef
190
190
  #{ui.color("TO FIX THIS ERROR:", :bold)}
191
191
 
192
192
  If the server you are connecting to uses a self-signed certificate, you must
193
- configure chef to trust that server's certificate.
193
+ configure #{Chef::Dist::PRODUCT} to trust that server's certificate.
194
194
 
195
195
  By default, the certificate is stored in the following location on the host
196
196
  where your chef-server runs:
@@ -37,23 +37,35 @@ class Chef
37
37
  default: "https://supermarket.chef.io",
38
38
  proc: Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
39
39
 
40
+ option :sort_by,
41
+ long: "--sort-by SORT",
42
+ description: "Use to sort the records",
43
+ in: %w{recently_updated recently_added most_downloaded most_followed}
44
+
45
+ option :owned_by,
46
+ short: "-o USER",
47
+ long: "--owned-by USER",
48
+ description: "Show cookbooks that are owned by the USER"
49
+
40
50
  def run
41
51
  if config[:with_uri]
42
- cookbooks = {}
43
- get_cookbook_list.each { |k, v| cookbooks[k] = v["cookbook"] }
44
- ui.output(format_for_display(cookbooks))
52
+ ui.output(format_for_display(get_cookbook_list))
45
53
  else
46
- ui.msg(ui.list(get_cookbook_list.keys.sort, :columns_down))
54
+ ui.msg(ui.list(get_cookbook_list.keys, :columns_down))
47
55
  end
48
56
  end
49
57
 
50
- def get_cookbook_list(items = 10, start = 0, cookbook_collection = {})
58
+ # In order to avoid pagination items limit set to 9999999
59
+ def get_cookbook_list(items = 9999999, start = 0, cookbook_collection = {})
51
60
  cookbooks_url = "#{config[:supermarket_site]}/api/v1/cookbooks?items=#{items}&start=#{start}"
61
+ cookbooks_url << "&order=#{config[:sort_by]}" if config[:sort_by]
62
+ cookbooks_url << "&user=#{config[:owned_by]}" if config[:owned_by]
52
63
  cr = noauth_rest.get(cookbooks_url)
64
+
53
65
  cr["items"].each do |cookbook|
54
- cookbook_collection[cookbook["cookbook_name"]] = cookbook
66
+ cookbook_collection[cookbook["cookbook_name"]] = cookbook["cookbook"]
55
67
  end
56
- new_start = start + cr["items"].length
68
+ new_start = start + items
57
69
  if new_start < cr["total"]
58
70
  get_cookbook_list(items, new_start, cookbook_collection)
59
71
  else
@@ -35,13 +35,14 @@ class Chef
35
35
  output(search_cookbook(name_args[0]))
36
36
  end
37
37
 
38
- def search_cookbook(query, items = 10, start = 0, cookbook_collection = {})
38
+ # In order to avoid pagination items limit set to 9999999
39
+ def search_cookbook(query, items = 9999999, start = 0, cookbook_collection = {})
39
40
  cookbooks_url = "#{config[:supermarket_site]}/api/v1/search?q=#{query}&items=#{items}&start=#{start}"
40
41
  cr = noauth_rest.get(cookbooks_url)
41
42
  cr["items"].each do |cookbook|
42
43
  cookbook_collection[cookbook["cookbook_name"]] = cookbook
43
44
  end
44
- new_start = start + cr["items"].length
45
+ new_start = start + items
45
46
  if new_start < cr["total"]
46
47
  search_cookbook(query, items, new_start, cookbook_collection)
47
48
  else
@@ -148,7 +148,9 @@ class Chef
148
148
  to_a
149
149
  to_h
150
150
  to_hash
151
+ to_json
151
152
  to_set
153
+ to_yaml
152
154
  value?
153
155
  values
154
156
  values_at
@@ -65,6 +65,10 @@ class Chef
65
65
  Array.new(map { |e| safe_dup(e) })
66
66
  end
67
67
 
68
+ def to_yaml(*opts)
69
+ to_a.to_yaml(*opts)
70
+ end
71
+
68
72
  private
69
73
 
70
74
  def convert_value(value)
@@ -172,6 +176,10 @@ class Chef
172
176
  Mash.new(self)
173
177
  end
174
178
 
179
+ def to_yaml(*opts)
180
+ to_h.to_yaml(*opts)
181
+ end
182
+
175
183
  prepend Chef::Node::Mixin::StateTracking
176
184
  end
177
185
  end
@@ -96,6 +96,12 @@ class Chef
96
96
 
97
97
  alias_method :to_array, :to_a
98
98
 
99
+ # As Psych module, not respecting ImmutableArray object
100
+ # So first convert it to Hash/Array then parse it to `.to_yaml`
101
+ def to_yaml(*opts)
102
+ to_a.to_yaml(*opts)
103
+ end
104
+
99
105
  private
100
106
 
101
107
  # needed for __path__
@@ -168,6 +174,12 @@ class Chef
168
174
 
169
175
  alias_method :to_hash, :to_h
170
176
 
177
+ # As Psych module, not respecting ImmutableMash object
178
+ # So first convert it to Hash/Array then parse it to `.to_yaml`
179
+ def to_yaml(*opts)
180
+ to_h.to_yaml(*opts)
181
+ end
182
+
171
183
  # For elements like Fixnums, true, nil...
172
184
  def safe_dup(e)
173
185
  e.dup
@@ -119,6 +119,7 @@ class Chef
119
119
  to_h
120
120
  to_plist
121
121
  to_set
122
+ to_yaml
122
123
  transpose
123
124
  union
124
125
  uniq
@@ -116,6 +116,7 @@ class Chef
116
116
  to_proc
117
117
  to_set
118
118
  to_xml_attributes
119
+ to_yaml
119
120
  transform_keys
120
121
  transform_values
121
122
  uniq