[DRE-commits] [chef] 01/01: New upstream version 12.14.75

Antonio Terceiro terceiro at moszumanska.debian.org
Tue Nov 1 13:50:06 UTC 2016


This is an automated email from the git hooks/post-receive script.

terceiro pushed a commit to annotated tag upstream/12.14.75
in repository chef.

commit 8c63f4f0735e3859c759b805da9190d3bb47a1c0
Author: Antonio Terceiro <terceiro at debian.org>
Date:   Mon Sep 19 11:19:37 2016 -0300

    New upstream version 12.14.75
---
 .kitchen.yml                                       |  82 ---
 .travis.yml                                        |  63 +-
 CHANGELOG.md                                       | 158 ++++-
 CONTRIBUTING.md                                    | 143 ++---
 DOC_CHANGES.md                                     |  13 +-
 Gemfile                                            |  13 +-
 Gemfile.lock                                       | 416 ++++++++++----
 MAINTAINERS.md                                     |  13 +-
 MAINTAINERS.toml                                   |   9 +-
 README.md                                          |  20 +-
 RELEASE_NOTES.md                                   |  42 +-
 Rakefile                                           |   5 +-
 VERSION                                            |   2 +-
 acceptance/.gitignore                              |   2 +
 .../.shared/kitchen_acceptance/.kitchen.ec2.yml    |  14 +-
 .../kitchen_acceptance/libraries/kitchen.rb        |  41 +-
 acceptance/Gemfile                                 |   8 +-
 acceptance/Gemfile.lock                            | 139 ++---
 .../test/integration/helpers/serverspec/Gemfile    |   8 +
 acceptance/data-collector/Berksfile.lock           |   6 -
 .../integration/default/serverspec/default_spec.rb |  43 +-
 .../test/integration/helpers/serverspec/Gemfile    |   8 +
 .../fips-integration/serverspec/Gemfile            |   6 +
 .../fips-unit-functional/serverspec/Gemfile        |   4 +
 .../acceptance-cookbook/libraries/top_cookbooks.rb |   4 +-
 acceptance/trivial/.kitchen.yml                    |   2 +-
 .../acceptance-cookbook/recipes/destroy.rb         |   2 +-
 .../acceptance-cookbook/recipes/provision.rb       |   2 +-
 .../acceptance-cookbook/recipes/verify.rb          |   2 +-
 .../inspec/chef_windows_service_spec.rb            |   8 +-
 appveyor.yml                                       |   5 +-
 chef-config/Rakefile                               |  14 +-
 chef-config/chef-config.gemspec                    |   3 +-
 chef-config/lib/chef-config/config.rb              |  60 +-
 .../chef-config/mixin/fuzzy_hostname_matcher.rb    |   6 +-
 chef-config/lib/chef-config/package_task.rb        |  56 +-
 chef-config/lib/chef-config/version.rb             |   2 +-
 chef-config/spec/unit/config_spec.rb               |  10 +
 chef.gemspec                                       |  10 +-
 ci/bundle_install.sh                               |   3 +-
 ci/dependency_update.sh                            |   2 +
 ci/version_bump.sh                                 |   2 +
 ci/version_show.sh                                 |   6 +-
 kitchen-tests/.kitchen.travis.yml                  |  21 +-
 kitchen-tests/.kitchen.yml                         |   1 +
 kitchen-tests/Berksfile                            |   2 +-
 kitchen-tests/Berksfile.lock                       |  62 +-
 kitchen-tests/Gemfile.lock                         | 184 +++---
 kitchen-tests/cookbooks/base/metadata.rb           |   1 -
 kitchen-tests/cookbooks/base/recipes/default.rb    |  14 +-
 kitchen-tests/cookbooks/base/recipes/packages.rb   |   8 +
 kitchen-tests/cookbooks/base/recipes/tests.rb      |  21 +
 lib/chef/application.rb                            |  23 +
 lib/chef/application/client.rb                     |  20 +-
 lib/chef/application/knife.rb                      |   8 +
 lib/chef/application/solo.rb                       |  23 +-
 lib/chef/audit/audit_reporter.rb                   |   6 +-
 lib/chef/audit/runner.rb                           |   2 +-
 lib/chef/chef_fs/chef_fs_data_store.rb             |   1 +
 lib/chef/chef_fs/command_line.rb                   |  70 +--
 lib/chef/chef_fs/data_handler/data_handler_base.rb |   4 +-
 lib/chef/chef_fs/file_pattern.rb                   |   2 +-
 lib/chef/chef_fs/file_system.rb                    | 276 ++++-----
 lib/chef/chef_fs/file_system/multiplexed_dir.rb    |   2 +-
 .../chef_fs/file_system/repository/acls_dir.rb     |   6 +-
 .../chef_fs/file_system/repository/base_file.rb    |   3 +
 .../chef_repository_file_system_cookbook_entry.rb  |   1 +
 .../chef_fs/file_system/repository/directory.rb    |  13 +-
 lib/chef/chef_fs/file_system_cache.rb              |  80 +++
 .../chef_fs/parallelizer/parallel_enumerable.rb    |   8 +-
 lib/chef/cookbook/cookbook_version_loader.rb       |  49 +-
 lib/chef/cookbook/metadata.rb                      |   2 +-
 lib/chef/cookbook/synchronizer.rb                  |   6 +-
 lib/chef/cookbook_site_streaming_uploader.rb       |   6 +-
 lib/chef/cookbook_uploader.rb                      |   2 +-
 lib/chef/cookbook_version.rb                       |  12 +-
 lib/chef/data_collector.rb                         | 141 +++--
 lib/chef/data_collector/messages.rb                |  37 +-
 lib/chef/data_collector/messages/helpers.rb        |   4 +-
 lib/chef/data_collector/resource_report.rb         |  33 +-
 lib/chef/decorator/unchain.rb                      |  59 ++
 lib/chef/dsl/cheffish.rb                           |   1 +
 lib/chef/dsl/declare_resource.rb                   |  10 +-
 lib/chef/dsl/powershell.rb                         |   2 +-
 lib/chef/event_dispatch/base.rb                    |   4 +-
 lib/chef/exceptions.rb                             |   7 +-
 lib/chef/file_access_control/windows.rb            |   6 +-
 lib/chef/file_cache.rb                             |   2 +-
 lib/chef/file_content_management/tempfile.rb       |  14 +-
 lib/chef/formatters/base.rb                        |   4 +-
 lib/chef/formatters/doc.rb                         |   4 +-
 lib/chef/http.rb                                   |  76 ++-
 lib/chef/http/auth_credentials.rb                  |   2 +-
 lib/chef/http/basic_client.rb                      |  13 +-
 lib/chef/http/socketless_chef_zero_client.rb       |   4 +-
 lib/chef/key.rb                                    | 111 ++--
 lib/chef/knife.rb                                  | 131 +++--
 lib/chef/knife/bootstrap.rb                        |   9 +
 lib/chef/knife/client_delete.rb                    |   4 +-
 lib/chef/knife/cookbook_bulk_delete.rb             |   2 +-
 lib/chef/knife/cookbook_create.rb                  |   4 +
 lib/chef/knife/cookbook_show.rb                    |   2 +-
 lib/chef/knife/cookbook_site_download.rb           |   9 +-
 lib/chef/knife/cookbook_site_install.rb            |  21 +-
 lib/chef/knife/cookbook_site_list.rb               |   9 +-
 lib/chef/knife/cookbook_site_search.rb             |   9 +-
 lib/chef/knife/cookbook_site_share.rb              |  29 +-
 lib/chef/knife/cookbook_site_show.rb               |  17 +-
 lib/chef/knife/cookbook_site_unshare.rb            |   9 +-
 lib/chef/knife/core/bootstrap_context.rb           |  12 +-
 lib/chef/knife/core/gem_glob_loader.rb             |   6 +-
 lib/chef/knife/search.rb                           |   4 +-
 lib/chef/knife/ssh.rb                              |  40 +-
 lib/chef/knife/status.rb                           |   4 +-
 .../supermarket_download.rb}                       |  20 +-
 .../powershell.rb => knife/supermarket_install.rb} |  20 +-
 .../powershell.rb => knife/supermarket_list.rb}    |  20 +-
 .../powershell.rb => knife/supermarket_search.rb}  |  20 +-
 .../powershell.rb => knife/supermarket_share.rb}   |  20 +-
 .../powershell.rb => knife/supermarket_show.rb}    |  20 +-
 .../powershell.rb => knife/supermarket_unshare.rb} |  20 +-
 lib/chef/mixin/command/unix.rb                     |   4 +-
 lib/chef/mixin/powershell_type_coercions.rb        |   2 +-
 lib/chef/mixin/properties.rb                       |   3 +
 lib/chef/mixin/securable.rb                        |   4 +-
 lib/chef/mixin/shell_out.rb                        |  30 +
 lib/chef/mixin/uris.rb                             |   3 +-
 lib/chef/mixin/windows_architecture_helper.rb      |   7 +-
 lib/chef/monkey_patches/webrick-utils.rb           |   4 +-
 lib/chef/monologger.rb                             |   2 +-
 lib/chef/node.rb                                   |  49 +-
 lib/chef/node/attribute.rb                         | 199 ++++---
 lib/chef/node/attribute_collections.rb             | 139 +----
 lib/chef/node/common_api.rb                        | 129 +++++
 lib/chef/node/immutable_collections.rb             |  29 +-
 lib/chef/platform/provider_mapping.rb              |   7 +-
 lib/chef/property.rb                               |  25 +-
 lib/chef/provider.rb                               |   9 +-
 lib/chef/provider/apt_repository.rb                |  12 +-
 lib/chef/provider/apt_update.rb                    |  13 +-
 lib/chef/provider/batch.rb                         |   2 +-
 lib/chef/provider/cron.rb                          |   5 +-
 lib/chef/provider/directory.rb                     |   4 +-
 lib/chef/provider/dsc_script.rb                    |   8 +-
 lib/chef/provider/group/usermod.rb                 |   2 +-
 lib/chef/provider/link.rb                          |  13 +-
 lib/chef/provider/log.rb                           |   2 +-
 lib/chef/provider/osx_profile.rb                   |  20 +-
 lib/chef/provider/package.rb                       |  14 +-
 lib/chef/provider/package/aix.rb                   |   6 +-
 lib/chef/provider/package/openbsd.rb               |   2 +-
 lib/chef/provider/package/rubygems.rb              |  45 +-
 lib/chef/provider/package/windows.rb               |   2 +
 lib/chef/provider/package/windows/exe.rb           |   9 +-
 .../package/windows/registry_uninstall_entry.rb    |   2 -
 lib/chef/provider/package/yum/rpm_utils.rb         |  16 +-
 lib/chef/provider/package/yum/yum_cache.rb         |   2 +-
 lib/chef/provider/package/zypper.rb                |  14 +-
 lib/chef/provider/powershell_script.rb             |   2 +-
 lib/chef/provider/registry_key.rb                  |   3 +
 lib/chef/provider/remote_directory.rb              |   2 +
 lib/chef/provider/remote_file/ftp.rb               |   4 +-
 lib/chef/provider/remote_file/sftp.rb              |   4 +-
 lib/chef/provider/service/debian.rb                |   4 +-
 lib/chef/provider/service/simple.rb                |   7 +-
 lib/chef/provider/service/solaris.rb               |   2 +-
 lib/chef/provider/service/systemd.rb               |   4 +-
 lib/chef/provider/service/upstart.rb               |  13 +-
 lib/chef/provider/support/yum_repo.erb             | 125 ++++
 lib/chef/provider/systemd_unit.rb                  |  13 +-
 lib/chef/provider/template_finder.rb               |   2 +-
 lib/chef/provider/user.rb                          |  36 +-
 lib/chef/provider/user/aix.rb                      |   7 +-
 lib/chef/provider/user/dscl.rb                     |   1 +
 lib/chef/provider/user/linux.rb                    | 138 +++++
 lib/chef/provider/user/pw.rb                       |   3 +-
 lib/chef/provider/user/solaris.rb                  |   5 +-
 lib/chef/provider/user/useradd.rb                  |   9 +-
 lib/chef/provider/user/windows.rb                  |   2 +-
 lib/chef/provider/windows_script.rb                |   7 +-
 lib/chef/provider/yum_repository.rb                | 121 ++++
 lib/chef/providers.rb                              |   6 +-
 lib/chef/recipe.rb                                 |   9 -
 lib/chef/resource.rb                               |  67 ++-
 lib/chef/resource/action_class.rb                  |   4 +
 lib/chef/resource/conditional.rb                   |  10 +-
 lib/chef/resource/file.rb                          |   2 +-
 lib/chef/resource/freebsd_package.rb               |   2 +-
 lib/chef/resource/launchd.rb                       |   2 +-
 lib/chef/resource/user.rb                          |  18 +-
 .../powershell.rb => resource/user/aix_user.rb}    |  16 +-
 .../powershell.rb => resource/user/dscl_user.rb}   |  16 +-
 .../powershell.rb => resource/user/linux_user.rb}  |  26 +-
 .../powershell.rb => resource/user/pw_user.rb}     |  16 +-
 .../user/solaris_user.rb}                          |  16 +-
 .../user/windows_user.rb}                          |  16 +-
 lib/chef/resource/yum_package.rb                   |  25 +-
 lib/chef/resource/yum_repository.rb                |  79 +++
 lib/chef/resource_builder.rb                       |  15 +-
 lib/chef/resource_collection/stepable_iterator.rb  |   4 +-
 lib/chef/resource_reporter.rb                      |  10 +-
 lib/chef/resources.rb                              |   7 +
 lib/chef/run_context.rb                            |  65 +--
 lib/chef/run_context/cookbook_compiler.rb          |   5 +-
 lib/chef/run_list.rb                               |   4 +-
 lib/chef/search/query.rb                           |  19 +-
 lib/chef/shell.rb                                  |   2 +-
 lib/chef/shell/ext.rb                              |   4 +-
 lib/chef/shell/shell_session.rb                    |   2 +-
 lib/chef/util/dsc/configuration_generator.rb       |   2 +-
 lib/chef/util/powershell/cmdlet.rb                 |   7 +-
 lib/chef/util/selinux.rb                           |   2 +-
 lib/chef/version.rb                                |   2 +-
 lib/chef/version_class.rb                          |  11 +-
 lib/chef/version_constraint.rb                     |   4 +-
 lib/chef/win32/api/error.rb                        |   4 +-
 lib/chef/win32/api/net.rb                          |   2 +-
 lib/chef/win32/eventlog.rb                         |   2 +-
 lib/chef/win32/file.rb                             |   8 +-
 lib/chef/win32/net.rb                              |   2 +-
 lib/chef/win32/security/sid.rb                     |   2 +-
 omnibus/.kitchen.yml                               |  38 +-
 omnibus/Gemfile                                    |  11 +-
 omnibus/Gemfile.lock                               | 182 +++---
 omnibus/config/software/chef-appbundle.rb          |   1 +
 omnibus/config/software/chef-complete.rb           |   3 +-
 .../config/software/chef-gem-binding_of_caller.rb  |   1 +
 omnibus/config/software/chef-gem-byebug.rb         |   1 +
 .../config/software/chef-gem-debug_inspector.rb    |   1 +
 omnibus/config/software/chef-gem-ffi-yajl.rb       |   1 +
 omnibus/config/software/chef-gem-ffi.rb            |   3 +-
 omnibus/config/software/chef-gem-json.rb           |   1 +
 omnibus/config/software/chef-gem-libyajl2.rb       |   1 +
 omnibus/config/software/chef-gem-mini_portile2.rb  |   1 +
 omnibus/config/software/chef-gem-nokogiri.rb       |   2 +
 ...binding_of_caller.rb => chef-gem-pkg-config.rb} |   5 +-
 omnibus/config/software/chef-gem-ruby-prof.rb      |   3 +-
 omnibus/config/software/chef-gem-ruby-shadow.rb    |   1 +
 omnibus/config/software/chef-remove-docs.rb        |   1 +
 omnibus/config/software/chef.rb                    |   7 -
 omnibus/files/mapfiles/solaris                     |  18 -
 omnibus/omnibus.rb                                 |   8 +-
 omnibus_overrides.rb                               |  11 +-
 pkg.rb                                             |   1 -
 rubygems-pkg/rubygems-update-2.4.6.gem             | Bin 451072 -> 0 bytes
 .../cookbooks/circular-dep1/attributes/default.rb  |   6 +-
 .../cookbooks/circular-dep2/attributes/default.rb  |   5 +-
 .../cookbooks/dependency1/attributes/aa_first.rb   |   4 +-
 .../cookbooks/dependency1/attributes/default.rb    |   4 +-
 .../cookbooks/dependency1/attributes/zz_last.rb    |   5 +-
 .../cookbooks/dependency2/attributes/default.rb    |   5 +-
 .../cookbooks/no-default-attr/attributes/server.rb |   5 +-
 .../test-with-circular-deps/attributes/default.rb  |   5 +-
 .../cookbooks/test-with-deps/attributes/default.rb |   5 +-
 .../assets/chocolatey_feed/test-A.1.0.nupkg        | Bin 2667 -> 2678 bytes
 .../assets/chocolatey_feed/test-A.1.5.nupkg        | Bin 2669 -> 2679 bytes
 .../assets/chocolatey_feed/test-A.2.0.nupkg        | Bin 2667 -> 2678 bytes
 .../assets/chocolatey_feed/test-B.1.0.nupkg        | Bin 2667 -> 2678 bytes
 spec/functional/assets/testchefsubsys              |   5 +-
 .../event_loggers/windows_eventlog_spec.rb         |  19 +-
 .../deploy_strategies_spec.rb                      |   2 +-
 spec/functional/http/simple_spec.rb                |   4 +-
 spec/functional/knife/cookbook_delete_spec.rb      |  10 +-
 spec/functional/knife/exec_spec.rb                 |  12 +-
 spec/functional/knife/ssh_spec.rb                  |   8 +-
 spec/functional/mixin/powershell_out_spec.rb       |   4 +-
 spec/functional/notifications_spec.rb              |   4 +-
 .../provider/whyrun_safe_ruby_block_spec.rb        |   4 +-
 spec/functional/rebooter_spec.rb                   |  10 +-
 spec/functional/resource/bash_spec.rb              |   4 +-
 spec/functional/resource/cron_spec.rb              |   9 +-
 spec/functional/resource/deploy_revision_spec.rb   |   2 +-
 spec/functional/resource/dpkg_package_spec.rb      |   4 +-
 spec/functional/resource/dsc_resource_spec.rb      |  10 +-
 spec/functional/resource/dsc_script_spec.rb        |  50 +-
 spec/functional/resource/env_spec.rb               |   8 +-
 spec/functional/resource/execute_spec.rb           |   8 +-
 spec/functional/resource/group_spec.rb             |  28 +-
 spec/functional/resource/link_spec.rb              |  41 +-
 spec/functional/resource/ohai_spec.rb              |  12 +-
 spec/functional/resource/package_spec.rb           |   2 +-
 spec/functional/resource/reboot_spec.rb            |   4 +-
 spec/functional/resource/registry_spec.rb          |  10 +
 spec/functional/resource/remote_file_spec.rb       |  23 +-
 spec/functional/resource/template_spec.rb          |   6 +-
 spec/functional/resource/user/dscl_spec.rb         |   6 +-
 spec/functional/resource/user/useradd_spec.rb      |  58 +-
 spec/functional/resource/windows_service_spec.rb   |  30 +-
 spec/functional/rest_spec.rb                       |   4 +-
 spec/functional/run_lock_spec.rb                   |  45 +-
 spec/functional/shell_spec.rb                      |   2 +-
 spec/functional/tiny_server_spec.rb                |  17 +-
 spec/functional/win32/crypto_spec.rb               |   2 +-
 spec/functional/win32/security_spec.rb             |  16 +-
 spec/integration/client/client_spec.rb             |  10 +-
 spec/integration/knife/chef_repo_path_spec.rb      |  71 ++-
 spec/integration/knife/client_bulk_delete_spec.rb  | 130 +++++
 spec/integration/knife/client_create_spec.rb       |  69 +++
 spec/integration/knife/client_delete_spec.rb       |  63 ++
 spec/integration/knife/client_key_create_spec.rb   |  65 +++
 spec/integration/knife/client_key_delete_spec.rb   |  42 ++
 spec/integration/knife/client_key_list_spec.rb     |  60 ++
 spec/integration/knife/client_key_show_spec.rb     |  44 ++
 spec/integration/knife/client_list_spec.rb         |  48 ++
 .../integration/knife/client_show_spec.rb          |  27 +-
 .../integration/knife/cookbook_bulk_delete_spec.rb |  64 +++
 spec/integration/knife/cookbook_download_spec.rb   |  95 +++
 spec/integration/knife/cookbook_list_spec.rb       |  54 ++
 spec/integration/knife/cookbook_show_spec.rb       | 159 ++++++
 spec/integration/knife/cookbook_upload_spec.rb     |  90 +++
 spec/integration/knife/data_bag_create_spec.rb     |  58 ++
 spec/integration/knife/data_bag_delete_spec.rb     |  58 ++
 spec/integration/knife/data_bag_from_file_spec.rb  | 115 ++++
 spec/integration/knife/data_bag_list_spec.rb       |  43 ++
 spec/integration/knife/data_bag_show_spec.rb       |  53 ++
 spec/integration/knife/diff_spec.rb                |   4 +-
 spec/integration/knife/download_spec.rb            |   8 +-
 spec/integration/knife/environment_compare_spec.rb |  74 +++
 spec/integration/knife/environment_create_spec.rb  |  40 ++
 .../integration/knife/environment_delete_spec.rb   |  27 +-
 .../knife/environment_from_file_spec.rb            | 115 ++++
 .../integration/knife/environment_list_spec.rb     |  32 +-
 spec/integration/knife/environment_show_spec.rb    |  56 ++
 spec/integration/knife/node_bulk_delete_spec.rb    |  51 ++
 spec/integration/knife/node_create_spec.rb         |  46 ++
 spec/integration/knife/node_delete_spec.rb         |  47 ++
 .../integration/knife/node_environment_set_spec.rb |  42 ++
 spec/integration/knife/node_from_file_spec.rb      |  58 ++
 .../integration/knife/node_list_spec.rb            |  35 +-
 spec/integration/knife/node_run_list_add_spec.rb   |  53 ++
 .../integration/knife/node_run_list_remove_spec.rb |  26 +-
 spec/integration/knife/node_run_list_set_spec.rb   |  40 ++
 .../integration/knife/node_show_spec.rb            |  26 +-
 spec/integration/knife/raw_spec.rb                 |   2 +
 spec/integration/knife/role_bulk_delete_spec.rb    |  51 ++
 spec/integration/knife/role_create_spec.rb         |  40 ++
 spec/integration/knife/role_delete_spec.rb         |  47 ++
 spec/integration/knife/role_from_file_spec.rb      |  95 +++
 .../integration/knife/role_list_spec.rb            |  35 +-
 spec/integration/knife/role_show_spec.rb           |  50 ++
 spec/integration/knife/upload_spec.rb              |   8 +-
 .../recipes/lwrp_inline_resources_spec.rb          |  12 +-
 spec/integration/recipes/noop_resource_spec.rb     |   8 +-
 spec/integration/recipes/provider_choice.rb        |   6 +-
 spec/integration/recipes/recipe_dsl_spec.rb        | 635 +++++++++++----------
 spec/integration/recipes/resource_action_spec.rb   | 208 +++----
 .../recipes/resource_converge_if_changed_spec.rb   |  68 +--
 spec/integration/recipes/resource_load_spec.rb     |  48 +-
 spec/integration/solo/solo_spec.rb                 |  51 +-
 spec/scripts/ssl-serve.rb                          |   2 +-
 spec/spec_helper.rb                                |  13 +-
 spec/stress/win32/security_spec.rb                 |   8 +-
 spec/support/chef_helpers.rb                       |  22 +
 spec/support/platform_helpers.rb                   |   6 +-
 spec/support/shared/context/client.rb              |   5 +-
 spec/support/shared/functional/file_resource.rb    |   9 +-
 spec/support/shared/functional/http.rb             |  32 +-
 .../shared/functional/securable_resource.rb        |  10 +-
 spec/support/shared/functional/win32_service.rb    |  12 +-
 .../shared/integration/app_server_support.rb       |   4 +-
 spec/support/shared/integration/knife_support.rb   |  19 +-
 spec/support/shared/shared_examples.rb             |   4 +-
 spec/support/shared/unit/api_versioning.rb         |   4 +-
 spec/support/shared/unit/application_dot_d.rb      |  15 +-
 spec/support/shared/unit/provider/file.rb          |   4 +-
 .../unit/provider/useradd_based_user_provider.rb   | 129 ++---
 .../unit/resource/static_provider_resolution.rb    |   4 +-
 spec/support/shared/unit/script_resource.rb        |   8 +-
 spec/support/shared/unit/user_and_client_shared.rb |   8 +-
 spec/tiny_server.rb                                | 117 ++--
 spec/unit/api_client_v1_spec.rb                    |   4 +-
 spec/unit/application/client_spec.rb               |  35 ++
 spec/unit/application/solo_spec.rb                 |  19 +-
 spec/unit/application_spec.rb                      |   2 +-
 spec/unit/audit/audit_event_proxy_spec.rb          |  61 +-
 spec/unit/audit/audit_reporter_spec.rb             |  55 +-
 spec/unit/audit/control_group_data_spec.rb         |  33 +-
 spec/unit/chef_class_spec.rb                       |   2 +-
 .../chef_fs/data_handler/group_handler_spec.rb     |   2 +-
 spec/unit/chef_fs/diff_spec.rb                     |   8 +-
 .../file_system/operation_failed_error_spec.rb     |   8 +-
 .../file_system/repository/directory_spec.rb       |   1 +
 spec/unit/chef_fs/file_system_spec.rb              |   4 +-
 spec/unit/chef_fs/parallelizer.rb                  |  12 +-
 spec/unit/cookbook/metadata_spec.rb                | 196 +++----
 spec/unit/cookbook/synchronizer_spec.rb            |   7 +
 spec/unit/cookbook_loader_spec.rb                  |  48 +-
 .../unit/cookbook_version_file_specificity_spec.rb |  16 +-
 spec/unit/cookbook_version_spec.rb                 |   8 +-
 spec/unit/daemon_spec.rb                           |   2 +-
 spec/unit/data_bag_item_spec.rb                    |  36 +-
 spec/unit/data_bag_spec.rb                         |   4 +-
 spec/unit/data_collector/messages/helpers_spec.rb  |  28 +-
 spec/unit/data_collector/messages_spec.rb          |  77 +--
 spec/unit/data_collector_spec.rb                   | 159 ++++--
 spec/unit/dsl/audit_spec.rb                        |   6 +-
 spec/unit/dsl/data_query_spec.rb                   |   5 +-
 spec/unit/dsl/declare_resource_spec.rb             |  50 +-
 .../check_encrypted_spec.rb                        |   5 +-
 spec/unit/encrypted_data_bag_item_spec.rb          |   5 +-
 spec/unit/environment_spec.rb                      |  12 +-
 spec/unit/event_dispatch/dispatcher_spec.rb        |   4 +-
 spec/unit/file_access_control_spec.rb              |   6 +-
 spec/unit/file_content_management/tempfile_spec.rb |  48 +-
 .../error_inspectors/api_error_formatting_spec.rb  |   4 +-
 .../resource_guard_interpreter_spec.rb             |   4 +-
 spec/unit/http/basic_client_spec.rb                |  20 +
 spec/unit/http/validate_content_length_spec.rb     |  16 +-
 spec/unit/http_spec.rb                             |  14 +
 .../knife/bootstrap/chef_vault_handler_spec.rb     |   4 +-
 spec/unit/knife/bootstrap/client_builder_spec.rb   |   4 +-
 spec/unit/knife/bootstrap_spec.rb                  |  14 +-
 spec/unit/knife/client_bulk_delete_spec.rb         |  16 +-
 spec/unit/knife/client_edit_spec.rb                |   4 +-
 spec/unit/knife/configure_client_spec.rb           |   4 +-
 spec/unit/knife/cookbook_create_spec.rb            |   1 +
 spec/unit/knife/cookbook_metadata_spec.rb          |   8 +-
 spec/unit/knife/cookbook_site_download_spec.rb     |   1 +
 spec/unit/knife/cookbook_site_install_spec.rb      |  13 +-
 spec/unit/knife/cookbook_site_share_spec.rb        |  12 +-
 spec/unit/knife/core/hashed_command_loader_spec.rb |   9 +-
 spec/unit/knife/core/node_editor_spec.rb           |   6 +-
 spec/unit/knife/core/ui_spec.rb                    |   4 +-
 spec/unit/knife/data_bag_from_file_spec.rb         |   7 +-
 spec/unit/knife/data_bag_show_spec.rb              |   5 +-
 spec/unit/knife/key_create_spec.rb                 |  28 +-
 spec/unit/knife/key_delete_spec.rb                 |   4 +-
 spec/unit/knife/key_edit_spec.rb                   |  32 +-
 spec/unit/knife/key_list_spec.rb                   |  12 +-
 spec/unit/knife/key_show_spec.rb                   |   8 +-
 spec/unit/knife/node_environment_set_spec.rb       |  24 -
 spec/unit/knife/node_run_list_set_spec.rb          |  25 -
 spec/unit/knife/status_spec.rb                     |   5 +-
 spec/unit/knife/user_create_spec.rb                |   8 +-
 spec/unit/knife_spec.rb                            |  13 +-
 spec/unit/lwrp_spec.rb                             |  12 +-
 .../mixin/api_version_request_handling_spec.rb     |   4 +-
 spec/unit/mixin/command_spec.rb                    |  31 +-
 spec/unit/mixin/deprecation_spec.rb                |   2 +-
 spec/unit/mixin/homebrew_user_spec.rb              |   4 +-
 spec/unit/mixin/params_validate_spec.rb            | 105 ++--
 spec/unit/mixin/powershell_out_spec.rb             |   4 +-
 spec/unit/mixin/powershell_type_coercions_spec.rb  |   4 +-
 spec/unit/mixin/shell_out_spec.rb                  |   2 +-
 spec/unit/mixin/xml_escape_spec.rb                 |   6 +-
 spec/unit/node/attribute_spec.rb                   |  16 +-
 spec/unit/node/immutable_collections_spec.rb       |   4 +
 spec/unit/node/vivid_mash_spec.rb                  | 377 ++++++++++++
 spec/unit/node_spec.rb                             | 175 +++++-
 spec/unit/platform/query_helpers_spec.rb           |   6 +-
 spec/unit/property_spec.rb                         |   8 +-
 spec/unit/provider/cookbook_file_spec.rb           |   8 +-
 spec/unit/provider/cron/unix_spec.rb               |   8 +-
 spec/unit/provider/cron_spec.rb                    |  36 +-
 spec/unit/provider/deploy_spec.rb                  |  16 +-
 spec/unit/provider/directory_spec.rb               |  12 +-
 spec/unit/provider/dsc_resource_spec.rb            |  21 +-
 spec/unit/provider/dsc_script_spec.rb              |  18 +-
 spec/unit/provider/env/windows_spec.rb             |  16 +-
 spec/unit/provider/file/content_spec.rb            |  12 +-
 spec/unit/provider/file_spec.rb                    |   8 +-
 spec/unit/provider/launchd_spec.rb                 |   3 +-
 spec/unit/provider/link_spec.rb                    | 155 +++++
 spec/unit/provider/log_spec.rb                     |  14 +
 spec/unit/provider/mount/mount_spec.rb             |  10 +-
 spec/unit/provider/mount/solaris_spec.rb           |  96 ++--
 spec/unit/provider/osx_profile_spec.rb             |   6 +
 spec/unit/provider/package/aix_spec.rb             |  13 +
 spec/unit/provider/package/chocolatey_spec.rb      |   8 +-
 spec/unit/provider/package/freebsd/port_spec.rb    |   8 +-
 spec/unit/provider/package/rubygems_spec.rb        |  94 +--
 spec/unit/provider/package/windows/exe_spec.rb     |  32 +-
 spec/unit/provider/package/yum_spec.rb             | 121 ++--
 spec/unit/provider/package/zypper_spec.rb          |  20 +-
 spec/unit/provider/package_spec.rb                 |   4 +-
 spec/unit/provider/powershell_script_spec.rb       |  16 +-
 spec/unit/provider/registry_key_spec.rb            |  52 +-
 spec/unit/provider/remote_directory_spec.rb        |  17 +-
 spec/unit/provider/remote_file/ftp_spec.rb         |   8 +-
 spec/unit/provider/remote_file/local_file_spec.rb  |   6 +-
 spec/unit/provider/remote_file/sftp_spec.rb        |   8 +-
 spec/unit/provider/remote_file_spec.rb             |   8 +-
 spec/unit/provider/script_spec.rb                  |   8 +-
 spec/unit/provider/service/debian_service_spec.rb  |   8 +-
 spec/unit/provider/service/freebsd_service_spec.rb |  20 +-
 spec/unit/provider/service/openbsd_service_spec.rb |  20 +-
 spec/unit/provider/service/systemd_service_spec.rb |   8 +-
 spec/unit/provider/service/upstart_service_spec.rb |  39 +-
 spec/unit/provider/service/windows_spec.rb         |  20 +-
 spec/unit/provider/systemd_unit_spec.rb            |   4 +-
 spec/unit/provider/template/content_spec.rb        |  16 +-
 spec/unit/provider/template_spec.rb                |  16 +-
 spec/unit/provider/user/dscl_spec.rb               | 106 ++--
 spec/unit/provider/user/linux_spec.rb              | 106 ++++
 spec/unit/provider/user/pw_spec.rb                 |  11 +-
 spec/unit/provider/user/solaris_spec.rb            |  10 +-
 spec/unit/provider/user/useradd_spec.rb            |  51 --
 spec/unit/provider/user/windows_spec.rb            |   6 +-
 spec/unit/provider/user_spec.rb                    |   4 +-
 .../yum_repository_spec.rb}                        |  21 +-
 spec/unit/provider_resolver_spec.rb                | 570 +++++++++---------
 spec/unit/recipe_spec.rb                           |  52 +-
 spec/unit/resource/apt_repository_spec.rb          |  16 +-
 spec/unit/resource/apt_update_spec.rb              |  16 +-
 spec/unit/resource/conditional_spec.rb             |  46 ++
 spec/unit/resource/dsc_resource_spec.rb            |  16 +-
 spec/unit/resource/dsc_script_spec.rb              |   8 +-
 spec/unit/resource/freebsd_package_spec.rb         |  12 +-
 spec/unit/resource/launchd_spec.rb                 |   5 +-
 spec/unit/resource/osx_profile_spec.rb             |   5 +-
 spec/unit/resource/remote_file_spec.rb             |  12 +-
 spec/unit/resource/service_spec.rb                 |  28 +-
 spec/unit/resource/user_spec.rb                    |   2 +-
 spec/unit/resource/yum_repository_spec.rb          |  49 ++
 spec/unit/resource_collection_spec.rb              |   8 +-
 spec/unit/resource_definition_spec.rb              |  22 +-
 spec/unit/resource_reporter_spec.rb                |   8 +-
 spec/unit/resource_resolver_spec.rb                |  12 +-
 spec/unit/resource_spec.rb                         |  52 +-
 spec/unit/run_context/child_run_context_spec.rb    |   8 +-
 spec/unit/run_context/cookbook_compiler_spec.rb    |  44 +-
 spec/unit/run_context_spec.rb                      |  20 +-
 spec/unit/run_list/run_list_expansion_spec.rb      |   4 +-
 spec/unit/runner_spec.rb                           |   4 +-
 spec/unit/search/query_spec.rb                     |  29 +-
 spec/unit/shell/shell_session_spec.rb              |   2 +-
 spec/unit/user_v1_spec.rb                          |  16 +-
 spec/unit/util/dsc/configuration_generator_spec.rb |  24 +-
 .../util/dsc/local_configuration_manager_spec.rb   |  21 +-
 spec/unit/util/dsc/resource_store.rb               |  14 +-
 spec/unit/util/editor_spec.rb                      |  12 +-
 spec/unit/util/powershell/cmdlet_spec.rb           |  20 +-
 tasks/bin/bundle-platform                          |   1 +
 tasks/bin/bundler_patch.rb                         |  27 +
 tasks/bin/gem-version-diff                         |  37 ++
 tasks/bundle.rb                                    |   4 +-
 tasks/cbgb.rb                                      |   4 +-
 tasks/dependencies.rb                              |   2 -
 tasks/maintainers.rb                               |   4 +-
 version_policy.rb                                  |  26 +-
 540 files changed, 9986 insertions(+), 4745 deletions(-)

diff --git a/.kitchen.yml b/.kitchen.yml
deleted file mode 100644
index ed49eb3..0000000
--- a/.kitchen.yml
+++ /dev/null
@@ -1,82 +0,0 @@
-driver:
-  name: vagrant
-  forward_agent: yes
-  customize:
-    cpus: 4
-    memory: 4096
-  synced_folders:
-    - ['.', '/home/vagrant/chef']
-
-provisioner:
-  name: chef_zero
-  require_chef_omnibus: 12.0.0.rc.1
-
-platforms:
-  - name: centos-5.10
-    run_list:
-  - name: centos-6.5
-    run_list:
-  - name: debian-7.2.0
-    run_list:
-  - name: debian-7.4
-    run_list:
-  - name: debian-6.0.8
-    run_list:
-  - name: freebsd-9.2
-    run_list:
-  - name: freebsd-10.0
-    run_list:
-  - name: ubuntu-10.04
-    run_list:
-  - name: ubuntu-12.04
-    run_list:
-  - name: ubuntu-12.10
-    run_list:
-  - name: ubuntu-13.04
-    run_list:
-  - name: ubuntu-13.10
-    run_list:
-  - name: ubuntu-14.04
-    run_list:
-  # The following boxes are shared via VagrantCloud. Until kitchen-vagrant
-  # is updated you'll need to add the box manually:
-  #
-  #   vagrant box add chef/windows-8.1-professional
-  #
-  # Please note this may require a `vagrant login` if the box is private.
-  #
-  # The following boxes are VMware only also. You can enable VMware Fusion
-  # as the default provider by copying `.kitchen.local.yml.vmware.example`
-  # over to `.kitchen.local.yml`.
-  #
-  - name: macosx-10.8
-    driver:
-      box: chef/macosx-10.8 # private
-  - name: macosx-10.9
-    driver:
-      box: chef/macosx-10.9 # private
-  - name: macosx-10.10
-    driver:
-      box: chef/macosx-10.10 # private
-  # - name: windows-7-professional
-  #   provisioner:
-  #     name: windows_chef_zero
-  #     require_chef_omnibus: 11.12.4
-  #   driver:
-  #     box: chef/windows-7-professional # private
-  # - name: windows-8.1-professional
-  #   provisioner:
-  #     name: windows_chef_zero
-  #     require_chef_omnibus: 11.12.4
-  #   driver:
-  #     box: chef/windows-8.1-professional # private
-  # - name: windows-2008r2-standard
-  #   provisioner:
-  #     name: windows_chef_zero
-  #     require_chef_omnibus: 11.12.4
-  #   driver:
-  #     box: chef/windows-server-2008r2-standard # private
-
-suites:
-  - name: chef
-    run_list:
diff --git a/.travis.yml b/.travis.yml
index b81c538..95b69ed 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,20 +27,16 @@ branches:
 env:
   global:
     - FORCE_FFI_YAJL=ext
+    - BUNDLE_ENABLE_TRAMPOLINE=1
 
 matrix:
   include:
-  - rvm: 2.1
+  - rvm: 2.2.5
     sudo: true
     script: sudo -E $(which bundle) exec rake spec;
     # also remove integration / external tests
     bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
-  - rvm: 2.2
-    sudo: true
-    script: sudo -E $(which bundle) exec rake spec;
-    # also remove integration / external tests
-    bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
-  - rvm: 2.3.0
+  - rvm: 2.3.1
     sudo: true
     script: sudo -E $(which bundle) exec rake spec;
     # also remove integration / external tests
@@ -52,13 +48,13 @@ matrix:
     bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
   - env:
       CHEFSTYLE: 1
-    rvm: 2.1
+    rvm: 2.3.1
     script: bundle exec rake style
     # also remove integration / external tests
     bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
   - env:
       AUDIT_CHECK: 1
-    rvm: 2.1
+    rvm: 2.3.1
     script: bundle exec bundle-audit check --update
     # also remove integration / external tests
     bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
@@ -68,23 +64,23 @@ matrix:
   - env:
       TEST_GEM: chef-provisioning
     script: tasks/bin/run_external_test $TEST_GEM rake spec
-    rvm: 2.2
+    rvm: 2.3.1
   - env:
       TEST_GEM: chef-provisioning-aws
     script: tasks/bin/run_external_test $TEST_GEM rake spec
-    rvm: 2.2
+    rvm: 2.3.1
   - env:
       TEST_GEM: chef-sugar
     script: tasks/bin/run_external_test $TEST_GEM rake
-    rvm: 2.2
+    rvm: 2.3.1
   - env:
       - TEST_GEM: chef-zero
     script: tasks/bin/run_external_test $TEST_GEM rake spec cheffs
-    rvm: 2.2
+    rvm: 2.3.1
   - env:
       TEST_GEM: cheffish
     script: tasks/bin/run_external_test $TEST_GEM rake spec
-    rvm: 2.2
+    rvm: 2.3.1
   - env:
       TEST_GEM: chefspec
     # The chefspec tests + bundler cache + "gem update --system" interact badly :/
@@ -93,32 +89,33 @@ matrix:
       - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
       - bundle config --local without server:docgen:maintenance:omnibus_package:development:ruby_prof:pry
     script: tasks/bin/run_external_test $TEST_GEM rake
-    rvm: 2.2.0
+    rvm: 2.3.1
   - env:
       TEST_GEM: foodcritic
     script: tasks/bin/run_external_test $TEST_GEM rake test
-    rvm: 2.2
+    rvm: 2.3.1
   - env:
       TEST_GEM: halite
     script: tasks/bin/run_external_test $TEST_GEM rake spec
-    rvm: 2.2
+    rvm: 2.3.1
   - env:
       TEST_GEM: knife-windows
     script: tasks/bin/run_external_test $TEST_GEM rake unit_spec
-    rvm: 2.2
+    rvm: 2.3.1
   - env:
       TEST_GEM: poise
     script: tasks/bin/run_external_test $TEST_GEM rake spec
-    rvm: 2.2
+    rvm: 2.3.1
   ### START TEST KITCHEN ONLY ###
   #
-  - rvm: 2.2
+  - rvm: 2.3.1
     services: docker
     sudo: required
     gemfile: kitchen-tests/Gemfile
     before_install:
       - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
       - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
+    bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
     before_script:
       - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
       - cd kitchen-tests
@@ -129,13 +126,14 @@ matrix:
     env:
       - UBUNTU=12.04
       - KITCHEN_YAML=.kitchen.travis.yml
-  - rvm: 2.2
+  - rvm: 2.3.1
     services: docker
     sudo: required
     gemfile: kitchen-tests/Gemfile
     before_install:
       - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
       - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
+    bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
     before_script:
       - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
       - cd kitchen-tests
@@ -146,13 +144,14 @@ matrix:
     env:
       - UBUNTU=14.04
       - KITCHEN_YAML=.kitchen.travis.yml
-  - rvm: 2.2
+  - rvm: 2.3.1
     services: docker
     sudo: required
     gemfile: kitchen-tests/Gemfile
     before_install:
       - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
       - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
+    bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
     before_script:
       - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
       - cd kitchen-tests
@@ -163,13 +162,14 @@ matrix:
     env:
       - UBUNTU=16.04
       - KITCHEN_YAML=.kitchen.travis.yml
-  - rvm: 2.2
+  - rvm: 2.3.1
     services: docker
     sudo: required
     gemfile: kitchen-tests/Gemfile
     before_install:
       - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
       - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
+    bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
     before_script:
       - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
       - cd kitchen-tests
@@ -180,30 +180,33 @@ matrix:
     env:
       - DEBIAN=7
       - KITCHEN_YAML=.kitchen.travis.yml
-  - rvm: 2.2
+  - rvm: 2.3.1
     services: docker
     sudo: required
     gemfile: kitchen-tests/Gemfile
     before_install:
       - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
       - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
+    bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
     before_script:
       - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
       - cd kitchen-tests
     script:
       - bundle exec kitchen test debian-8
+    bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
     after_failure:
       - cat .kitchen/logs/kitchen.log
     env:
       - DEBIAN=8
       - KITCHEN_YAML=.kitchen.travis.yml
-  - rvm: 2.2
+  - rvm: 2.3.1
     services: docker
     sudo: required
     gemfile: kitchen-tests/Gemfile
     before_install:
       - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
       - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
+    bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
     before_script:
       - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
       - cd kitchen-tests
@@ -214,13 +217,14 @@ matrix:
     env:
       - CENTOS=6
       - KITCHEN_YAML=.kitchen.travis.yml
-  - rvm: 2.2
+  - rvm: 2.3.1
     services: docker
     sudo: required
     gemfile: kitchen-tests/Gemfile
     before_install:
       - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
       - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
+    bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
     before_script:
       - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
       - cd kitchen-tests
@@ -231,13 +235,14 @@ matrix:
     env:
       - CENTOS=7
       - KITCHEN_YAML=.kitchen.travis.yml
-  - rvm: 2.2
+  - rvm: 2.3.1
     services: docker
     sudo: required
     gemfile: kitchen-tests/Gemfile
     before_install:
       - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2)
       - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
+    bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
     before_script:
       - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER )
       - cd kitchen-tests
@@ -249,7 +254,7 @@ matrix:
       - FEDORA=23
       - KITCHEN_YAML=.kitchen.travis.yml
     ### END TEST KITCHEN ONLY ###
-  - rvm: 2.2
+  - rvm: 2.3.1
     sudo: required
     dist: trusty
     before_install:
@@ -257,6 +262,7 @@ matrix:
       - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
       - sudo apt-get update
       - sudo apt-get -y install squid3 git curl
+    bundler_args: --without changelog development docgen guard integration maintenance omnibus_package tools aix bsd mac_os_x solaris windows --frozen
     env:
       - PROXY_TESTS_DIR=proxy_tests/files/default/scripts
       - PROXY_TESTS_REPO=$PROXY_TESTS_DIR/repo
@@ -270,7 +276,6 @@ matrix:
       - sudo cat /var/log/squid3/access.log
 
   allow_failures:
-    - rvm: 2.3.0
     - rvm: rbx
 
 notifications:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6846ff5..f761845 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,161 @@
 # Change Log
 
+## [v12.14.60](https://github.com/chef/chef/tree/v12.14.60) (2016-09-09)
+[Full Changelog](https://github.com/chef/chef/compare/v12.13.37...v12.14.60)
+
+**Enhancements:**
+
+- Only support Solaris 10u11 and newer [\#5264](https://github.com/chef/chef/pull/5264) ([rhass](https://github.com/rhass))
+- Added code to handle deletion of directories on Windows that are symlinks. [\#5234](https://github.com/chef/chef/pull/5234) ([Aliasgar16](https://github.com/Aliasgar16))
+- Readability improvements to options parsing code [\#5289](https://github.com/chef/chef/pull/5289) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add Hash type to launchd:keep\_alive [\#5182](https://github.com/chef/chef/pull/5182) ([erikng](https://github.com/erikng))
+- Added timeout during removing of windows package [\#5250](https://github.com/chef/chef/pull/5250) ([dheerajd-msys](https://github.com/dheerajd-msys))
+- Bump openssl to 1.0.2h [\#5260](https://github.com/chef/chef/pull/5260) ([lamont-granquist](https://github.com/lamont-granquist))
+- Rewrite linux\_user provider check\_lock [\#5248](https://github.com/chef/chef/pull/5248) ([lamont-granquist](https://github.com/lamont-granquist))
+- Allow flagging a resource property as sensitive [\#5185](https://github.com/chef/chef/pull/5185) ([adamleff](https://github.com/adamleff))
+- Rewrite linux useradd provider [\#5243](https://github.com/chef/chef/pull/5243) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add yum_repository resource from the yum cookbook [\#5187](https://github.com/chef/chef/pull/5187) ([thommay](https://github.com/thommay))
+- Verify systemd\_unit file during create [\#5210](https://github.com/chef/chef/pull/5210) ([mal](https://github.com/mal))
+- Add a warning for guard blocks that return a non-empty string [\#5233](https://github.com/chef/chef/pull/5233) ([coderanger](https://github.com/coderanger))
+- Forward package cookbook\_name to underlying remote\_file [\#5128](https://github.com/chef/chef/pull/5128) ([Annih](https://github.com/Annih))
+- Fix "URI.escape is obsolete" warnings [\#5230](https://github.com/chef/chef/pull/5230) ([jkeiser](https://github.com/jkeiser))
+- Remove ruby 2.1 support [\#5220](https://github.com/chef/chef/pull/5220) ([lamont-granquist](https://github.com/lamont-granquist))
+- User provider manage\_home behavior and refactor [\#5122](https://github.com/chef/chef/pull/5122) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix Style/BlockDelimiters, Style/MultilineBlockLayout and 0.42.0 engine upgrade [\#5218](https://github.com/chef/chef/pull/5218) ([lamont-granquist](https://github.com/lamont-granquist))
+- Switch from Ruby 2.1.9 to Ruby 2.3.1 [\#5190](https://github.com/chef/chef/pull/5190) ([jkeiser](https://github.com/jkeiser))
+- Update to latest chefstyle [\#5217](https://github.com/chef/chef/pull/5217) ([jkeiser](https://github.com/jkeiser))
+- Rubygems memory performance improvement [\#5203](https://github.com/chef/chef/pull/5203) ([lamont-granquist](https://github.com/lamont-granquist))
+- HTTP 1.1 keepalives for cookbook synchronization [\#5151](https://github.com/chef/chef/pull/5151) ([lamont-granquist](https://github.com/lamont-granquist))
+
+**Fixed Bugs:**
+
+- Fixes GH-4955, allowing local gems with remote dependencies [\#5098](https://github.com/chef/chef/pull/5098) ([jyaworski](https://github.com/jyaworski))
+- Hook up the recipe\_file\_loaded event which was defined but not actually called [\#5281](https://github.com/chef/chef/pull/5281) ([coderanger](https://github.com/coderanger))
+- fix gem\_package regression in master [\#5262](https://github.com/chef/chef/pull/5262) ([lamont-granquist](https://github.com/lamont-granquist))
+- Added fix for spaces in profile identifiers [\#5159](https://github.com/chef/chef/pull/5159) ([natewalck](https://github.com/natewalck))
+- Add a hook for compat\_resource [\#5259](https://github.com/chef/chef/pull/5259) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix flush\_cache issues in yum\_package [\#5258](https://github.com/chef/chef/pull/5258) ([jaymzh](https://github.com/jaymzh))
+- Use symbols instead of strings as keys for systemd user property [\#5241](https://github.com/chef/chef/pull/5241) ([joshuamiller01](https://github.com/joshuamiller01))
+- Use upstart goal state as service status [\#5249](https://github.com/chef/chef/pull/5249) ([evan2645](https://github.com/evan2645))
+- Fix the useradd test filters [\#5236](https://github.com/chef/chef/pull/5236) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix specify members of group on suse/openbsd/solaris2/hpux [\#5152](https://github.com/chef/chef/pull/5152) ([tas50](https://github.com/tas50))
+- Fix cookbook upload of symlinked cookbooks in Ruby 2.3 on Windows [\#5216](https://github.com/chef/chef/pull/5216) ([jkeiser](https://github.com/jkeiser))
+- Don't use relative\_path\_from on glob results [\#5215](https://github.com/chef/chef/pull/5215) ([jkeiser](https://github.com/jkeiser))
+
+## [v12.13.37](https://github.com/chef/chef/tree/v12.13.37) (2016-08-12)
+[Full Changelog](https://github.com/chef/chef/compare/v12.13.30...v12.13.37)
+
+**Enhancements:**
+
+- Bumping ohai and mixlib-log to fix regression [\#5197](https://github.com/chef/chef/pull/5197) ([mwrock](https://github.com/mwrock))
+- Remove requires in Chef::Recipe that are no longer necessary [\#5189](https://github.com/chef/chef/pull/5189) ([lamont-granquist](https://github.com/lamont-granquist))
+
+## [v12.13.30](https://github.com/chef/chef/tree/v12.13.30) (2016-08-05)
+[Full Changelog](https://github.com/chef/chef/compare/v12.12.15...v12.13.30)
+
+**Enhancements:**
+
+- noop apt_update similar to apt_repository [\#5173](https://github.com/chef/chef/pull/5173) ([lamont-granquist](https://github.com/lamont-granquist))
+- Bump dependencies to bring in Ohai 8.18 [\#5168](https://github.com/chef/chef/pull/5168) ([tas50](https://github.com/tas50))
+- Make Chef work with Ruby 2.3, update Ruby to 2.1.9 [\#5165](https://github.com/chef/chef/pull/5165) ([jkeiser](https://github.com/jkeiser))
+- Log cause chain for exceptions [\#3354](https://github.com/chef/chef/pull/3354) ([jaym](https://github.com/jaym))
+- First pass on --config-option handling. [\#5045](https://github.com/chef/chef/pull/5045) ([coderanger](https://github.com/coderanger))
+- Add bootstrap proxy authentication support. [\#4059](https://github.com/chef/chef/pull/4059) ([yossigo](https://github.com/yossigo))
+- Support setting an empty string for cron attrs [\#5127](https://github.com/chef/chef/pull/5127) ([thommay](https://github.com/thommay))
+- Also clear notifications when deleting a resource. [\#5146](https://github.com/chef/chef/pull/5146) ([coderanger](https://github.com/coderanger))
+- Clean up subscribes internals and notification storage. [\#5145](https://github.com/chef/chef/pull/5145) ([coderanger](https://github.com/coderanger))
+- Cache ChefFS children [\#5131](https://github.com/chef/chef/pull/5131) ([thommay](https://github.com/thommay))
+- Update to rspec 3.5 [\#5126](https://github.com/chef/chef/pull/5126) ([thommay](https://github.com/thommay))
+- Add `chef\_data\_bag\_item` to Cheffish DSL methods [\#5125](https://github.com/chef/chef/pull/5125) ([danielsdeleo](https://github.com/danielsdeleo))
+- replace glibc resolver with ruby resolver [\#5123](https://github.com/chef/chef/pull/5123) ([lamont-granquist](https://github.com/lamont-granquist))
+- The user must specify a category for a new cookbook [\#5091](https://github.com/chef/chef/pull/5091) ([thommay](https://github.com/thommay))
+- Warn if not installing an individual bff fileset [\#5093](https://github.com/chef/chef/pull/5093) ([mwrock](https://github.com/mwrock))
+- Use Mixlib::Archive to extract tarballs [\#5080](https://github.com/chef/chef/pull/5080) ([thommay](https://github.com/thommay))
+- Data Collector server URL validation, and disable on host down [\#5076](https://github.com/chef/chef/pull/5076) ([adamleff](https://github.com/adamleff))
+
+**Fixed Bugs:**
+
+- Don't log error for reporting audit data in when in chef-zero [\#5016](https://github.com/chef/chef/pull/5016) ([erichelgeson](https://github.com/erichelgeson))
+- Invalidate the file system cache on deletion [\#5154](https://github.com/chef/chef/pull/5154) ([thommay](https://github.com/thommay))
+- Root ACLs are a top level json file not a sub-directory [\#5155](https://github.com/chef/chef/pull/5155) ([thommay](https://github.com/thommay))
+- Install nokogiri and pin mixlib-cli [\#5118](https://github.com/chef/chef/pull/5118) ([ksubrama](https://github.com/ksubrama))
+- Ensure that the valid option is given back to the option parser [\#5114](https://github.com/chef/chef/pull/5114) ([dldinternet](https://github.com/dldinternet))
+- Fixed regex for zypper version 1.13.\*.  [\#5109](https://github.com/chef/chef/pull/5109) ([yeoldegrove](https://github.com/yeoldegrove))
+- add back method\_missing support to set\_unless [\#5103](https://github.com/chef/chef/pull/5103) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix \#5094 node.default\_unless issue in 12.12.13 [\#5097](https://github.com/chef/chef/pull/5097) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix \#5078 using cwd parameter instead of Dir.pwd [\#5079](https://github.com/chef/chef/pull/5079) ([Tensibai](https://github.com/Tensibai))
+
+## [v12.12.15](https://github.com/chef/chef/tree/v12.12.15) (2016-07-08)
+[Full Changelog](https://github.com/chef/chef/compare/v12.12.13...v12.12.15)
+
+**Fixed Bugs:**
+
+- Fix for #5094 12.12.13 node.default_unless issue [\#5097](https://github.com/chef/chef/pull/5097) ([lamont-granquist](https://github.com/lamont-granquist))
+
+## [v12.12.13](https://github.com/chef/chef/tree/v12.12.13) (2016-07-01)
+[Full Changelog](https://github.com/chef/chef/compare/v12.11.18...v12.12.13)
+
+**Implemented Enhancements:**
+
+- Tweak 3694 warnings [\#5075](https://github.com/chef/chef/pull/5075) ([lamont-granquist](https://github.com/lamont-granquist))
+- Adding node object to Data collector run\_converge message [\#5065](https://github.com/chef/chef/pull/5065) ([adamleff](https://github.com/adamleff))
+- Attribute API improvements [\#5029](https://github.com/chef/chef/pull/5029) ([lamont-granquist](https://github.com/lamont-granquist))
+- Remove deprecated Thread.exclusive around require call. [\#5068](https://github.com/chef/chef/pull/5068) ([maxlazio](https://github.com/maxlazio))
+- Ensure that chef-solo uses the expected repo dir [\#5059](https://github.com/chef/chef/pull/5059) ([thommay](https://github.com/thommay))
+- Expand data\_collector resource list to include all resources [\#5058](https://github.com/chef/chef/pull/5058) ([adamleff](https://github.com/adamleff))
+- Turn off fips with an empty environment var [\#5048](https://github.com/chef/chef/pull/5048) ([mwrock](https://github.com/mwrock))
+- Deprecate knife-supermarket gem [\#4896](https://github.com/chef/chef/pull/4896) ([thommay](https://github.com/thommay))
+- Update Nokogiri [\#5042](https://github.com/chef/chef/pull/5042) ([mwrock](https://github.com/mwrock))
+- Remote resource should respect sensitive flag [\#5025](https://github.com/chef/chef/pull/5025) ([PrajaktaPurohit](https://github.com/PrajaktaPurohit))
+- Convert the 3694 warning to a deprecation so it will be subject to the usual deprecation formatting \(collected at the bottom, can be made an error, etc\). [\#5022](https://github.com/chef/chef/pull/5022) ([coderanger](https://github.com/coderanger))
+- Deprecate `knife cookbook create` in favor of `chef generate cookbook`. [\#5021](https://github.com/chef/chef/pull/5021) ([tylercloke](https://github.com/tylercloke))
+
+**Fixed Bugs:**
+
+- Fixes windows_package uninstall scenarios by calling uninstall string directly [\#5050](https://github.com/chef/chef/pull/5050) ([mwrock](https://github.com/mwrock))
+- Fix gem_package idempotency [\#5046](https://github.com/chef/chef/pull/5046) ([thommay](https://github.com/thommay))
+- Undefined local variable lookup in multiplexed_dir.rb [\#5027](https://github.com/chef/chef/issues/5027) ([robdimarco](https://github.com/robdimarco))
+- Correctly write out data collector metadata file [\#5019](https://github.com/chef/chef/pull/5019) ([adamleff](https://github.com/adamleff))
+- Eliminate missing constant errors for LWRP class [\#5000](https://github.com/chef/chef/pull/5000) ([PrajaktaPurohit](https://github.com/PrajaktaPurohit))
+- Updated_resource_count to data collector should only include updated resources [\#5006](https://github.com/chef/chef/pull/5006) ([adamleff](https://github.com/adamleff))
+- Don't mask directory deletion errors [\#4991](https://github.com/chef/chef/pull/4991) ([jaymzh](https://github.com/jaymzh))
+
+## [v12.11.18](https://github.com/chef/chef/tree/v12.11.18) (2016-06-02)
+[Full Changelog](https://github.com/chef/chef/compare/v12.11.17...v12.11.18)
+
+**Implemented Enhancements:**
+
+- Creation of the new DataCollector reporter [\#4973](https://github.com/chef/chef/pull/4973) ([adamleff](https://github.com/adamleff))
+- Add systemd\_unit try-restart, reload-or-restart, reload-or-try-restart actions [\#4908](https://github.com/chef/chef/pull/4908) ([nathwill](https://github.com/nathwill))
+- RFC062 exit status chef client [\#4611](https://github.com/chef/chef/pull/4611) ([smurawski](https://github.com/smurawski))
+- Create 'universal' DSL [\#4942](https://github.com/chef/chef/pull/4942) ([lamont-granquist](https://github.com/lamont-granquist))
+- Handle numeric id for the user value in the git resource [\#4902](https://github.com/chef/chef/pull/4902) ([MichaelPereira](https://github.com/MichaelPereira))
+- RFC 31 - Default solo to local mode [\#4919](https://github.com/chef/chef/pull/4919) ([thommay](https://github.com/thommay))
+- Wire up chef handlers directly from libraries [\#4933](https://github.com/chef/chef/pull/4933) ([lamont-granquist](https://github.com/lamont-granquist))
+- Reject malformed ini content in systemd\_unit resource [\#4907](https://github.com/chef/chef/pull/4907) ([nathwill](https://github.com/nathwill))
+- Update usage of @new\_resource.destination to `cwd` within the git hwrp [\#4898](https://github.com/chef/chef/pull/4898) ([joshburt](https://github.com/joshburt))
+- Support Ruby Files in ChefFS [\#4887](https://github.com/chef/chef/pull/4887) ([thommay](https://github.com/thommay))
+- Adds a system check for fips enablement and runs in fips mode if enabled [\#4880](https://github.com/chef/chef/pull/4880) ([mwrock](https://github.com/mwrock))
+- Lazy'ing candidate\_version in package provider [\#4869](https://github.com/chef/chef/pull/4869) ([lamont-granquist](https://github.com/lamont-granquist))
+- Add systemd\_unit resource [\#4700](https://github.com/chef/chef/pull/4700) ([nathwill](https://github.com/nathwill))
+- Bump chef-zero to avoid aggressive logging [\#4878](https://github.com/chef/chef/pull/4878) ([stevendanna](https://github.com/stevendanna))
+
+**Fixed Bugs:**
+
+- Fix \#4949 and Avoid Errno::EBUSY on docker containers [\#4979](https://github.com/chef/chef/pull/4979) ([andrewjamesbrown](https://github.com/andrewjamesbrown))
+- Ensure recipe-url works right in solo [\#4957](https://github.com/chef/chef/pull/4957) ([thommay](https://github.com/thommay))
+- Fix portage provider to support version with character [\#4966](https://github.com/chef/chef/pull/4966) ([crigor](https://github.com/crigor))
+- Fixes \#4968 and only retrieves the latest version of packages from chocolatey [\#4977](https://github.com/chef/chef/pull/4977) ([mwrock](https://github.com/mwrock))
+- Update contributing doc to better reflect reality [\#4962](https://github.com/chef/chef/pull/4962) ([tas50](https://github.com/tas50))
+- Load cookbook versions correctly for knife [\#4936](https://github.com/chef/chef/pull/4936) ([thommay](https://github.com/thommay))
+- Gem metadata command needs Gem.clear\_paths [\#4929](https://github.com/chef/chef/pull/4929) ([lamont-granquist](https://github.com/lamont-granquist))
+- Fix os x profile provider for nil [\#4921](https://github.com/chef/chef/pull/4921) ([achand](https://github.com/achand))
+- Cookbook site install : tar error on windows [\#4867](https://github.com/chef/chef/pull/4867) ([willoucom](https://github.com/willoucom))
+- Fix yum\_package breakage \(the =~ operator in ruby is awful\) [\#4912](https://github.com/chef/chef/pull/4912) ([lamont-granquist](https://github.com/lamont-granquist))
+- Encode registry enumerated values and keys to utf8 instead of the local codepage [\#4906](https://github.com/chef/chef/pull/4906) ([mwrock](https://github.com/mwrock))
+- Chocolatey Package Provider chomps nil object [\#4760](https://github.com/chef/chef/pull/4760) ([svmastersamurai](https://github.com/svmastersamurai))
+- Fixes knife ssl check on windows [\#4886](https://github.com/chef/chef/pull/4886) ([mwrock](https://github.com/mwrock))
+
 ## [v12.10.24](https://github.com/chef/chef/tree/v12.10.24) (2016-04-27)
 [Full Changelog](https://github.com/chef/chef/compare/v12.10.23...v12.10.24)
 
@@ -23,7 +179,7 @@
 - Update rubygems provider to support local install of gems if so specified [\#4847](https://github.com/chef/chef/pull/4847) ([PrajaktaPurohit](https://github.com/PrajaktaPurohit))
 - fix details in with\_run\_context [\#4839](https://github.com/chef/chef/pull/4839) ([lamont-granquist](https://github.com/lamont-granquist))
 - Lock dependencies of chef through a `Gemfile.lock` [\#4820](https://github.com/chef/chef/pull/4820) ([jkeiser](https://github.com/jkeiser))
-- add better resource manipulation API [\#4834](https://github.com/chef/chef/pull/4834) ([lamont-granquist](https://github.com/lamont-granquist)) 
+- add better resource manipulation API [\#4834](https://github.com/chef/chef/pull/4834) ([lamont-granquist](https://github.com/lamont-granquist))
 - add nillable apt\_repository and nillable properties [\#4832](https://github.com/chef/chef/pull/4832) ([lamont-granquist](https://github.com/lamont-granquist))
 
 ## [v12.9](https://github.com/chef/chef/tree/v12.9.38) (2016-04-09)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 23dd85d..4f9f527 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -15,84 +15,54 @@ We have a 3 step process that utilizes **Github Issues**:
 2. Create a Github Pull Request.
 3. Do [Code Review](#cr) with the **Chef Engineering Team** or **Chef Core Committers** on the pull request.
 
-### <a name="pulls"></a> Chef Pull Requests
+### Chef Pull Requests
 
-Chef is built to last. We strive to ensure high quality throughout the Chef experience. In order to ensure
-  this, we require a couple of things for all pull requests to Chef:
+Chef is built to last. We strive to ensure high quality throughout the Chef experience. In order to ensure this, we require a couple of things for all pull requests to Chef:
 
-1. **Tests:** To ensure high quality code and protect against future regressions, we require all the
-  code in Chef to have at least unit test coverage. See the [spec/unit](https://github.com/chef/chef/tree/master/spec/unit)
-  directory for the existing tests and use ```bundle exec rake spec``` to run them.
-2. **Green Travis Run:** We use [Travis CI](https://travis-ci.org/) in order to run our tests
-  continuously on all the pull requests. We require the Travis runs to succeed on every pull
-  request before being merged.
+1. **Tests:** To ensure high quality code and protect against future regressions, we require all the code in Chef to have at least unit test coverage. See the [spec/unit](https://github.com/chef/chef/tree/master/spec/unit) directory for the existing tests and use `bundle exec rake spec` to run them.
+2. **Green Travis Run:** We use [Travis CI](https://travis-ci.org/) in order to run our tests continuously on all the pull requests. We require the Travis runs to succeed on every pull request before being merged.
 
+### Chef Code Review Process
 
-### <a name="cr"></a> Chef Code Review Process
+The Chef Code Review process happens on Github pull requests. See [this article](https://help.github.com/articles/using-pull-requests) if you're not familiar with Github Pull Requests.
 
-The Chef Code Review process happens on Github pull requests. See
-  [this article](https://help.github.com/articles/using-pull-requests) if you're not
-  familiar with Github Pull Requests.
-
-Once you a pull request, the **Chef Engineering Team** or **Chef Core Committers** will review your code
-  and respond to you with any feedback they might have. The process at this point is as follows:
+Once you open a pull request, the **Chef Engineering Team** or **Chef Core Committers** will review your code and respond to you with any feedback they might have. The process at this point is as follows:
 
 1. 2 thumbs-ups are required from the **Chef Engineering Team** or **Chef Core Committers** for all merges.
 2. When ready, your pull request will be tagged with label `Ready For Merge`.
-3. Your patch will be merged into `master` including necessary documentation updates
-  and you will be included in `CHANGELOG.md`. Our goal is to have patches merged in 2 weeks
-  after they are marked to be merged.
-
-If you would like to learn about when your code will be available in a release of Chef, read more about
-  [Chef Release Process](#release).
+3. Your patch will be merged into `master` including necessary documentation updates and you will be included in `CHANGELOG.md`. Our goal is to have patches merged in 2 weeks after they are marked to be merged.
 
+If you would like to learn about when your code will be available in a release of Chef, read more about [Chef Release Cycles](#chef-release-cycles).
 
 ### Contributor License Agreement (CLA)
-Licensing is very important to open source projects. It helps ensure the
-  software continues to be available under the terms that the author desired.
 
-Chef uses [the Apache 2.0 license](https://github.com/chef/chef/blob/master/LICENSE)
-  to strike a balance between open contribution and allowing you to use the
-  software however you would like to.
+Licensing is very important to open source projects. It helps ensure the software continues to be available under the terms that the author desired.
+
+Chef uses [the Apache 2.0 license](https://github.com/chef/chef/blob/master/LICENSE) to strike a balance between open contribution and allowing you to use the software however you would like to.
 
-The license tells you what rights you have that are provided by the copyright holder.
-  It is important that the contributor fully understands what rights they are
-  licensing and agrees to them. Sometimes the copyright holder isn't the contributor,
-  such as when the contributor is doing work for a company.
+The license tells you what rights you have that are provided by the copyright holder. It is important that the contributor fully understands what rights they are licensing and agrees to them. Sometimes the copyright holder isn't the contributor, such as when the contributor is doing work for a company.
 
-To make a good faith effort to ensure these criteria are met, Chef requires an Individual CLA
-  or a Corporate CLA for contributions. This agreement helps ensure you are aware of the
-  terms of the license you are contributing your copyrighted works under, which helps to
-  prevent the inclusion of works in the projects that the contributor does not hold the rights
-  to share.
+To make a good faith effort to ensure these criteria are met, Chef requires an Individual CLA or a Corporate CLA for contributions. This agreement helps ensure you are aware of the terms of the license you are contributing your copyrighted works under, which helps to prevent the inclusion of works in the projects that the contributor does not hold the rights to share.
 
 It only takes a few minutes to complete a CLA, and you retain the copyright to your contribution.
 
-You can complete our
-  [Individual CLA](https://supermarket.chef.io/icla-signatures/new) online.
-  If you're contributing on behalf of your employer and they retain the copyright for your works,
-  have your employer fill out our
-  [Corporate CLA](https://supermarket.chef.io/ccla-signatures/new) instead.
+You can complete our [Individual CLA](https://supermarket.chef.io/icla-signatures/new) online. If you're contributing on behalf of your employer and they retain the copyright for your works, have your employer fill out our [Corporate CLA](https://supermarket.chef.io/ccla-signatures/new) instead.
 
 ### Chef Obvious Fix Policy
 
-Small contributions such as fixing spelling errors, where the content is small enough
-  to not be considered intellectual property, can be submitted by a contributor as a patch,
-  without a CLA.
+Small contributions such as fixing spelling errors, where the content is small enough to not be considered intellectual property, can be submitted by a contributor as a patch, without a CLA.
 
-As a rule of thumb, changes are obvious fixes if they do not introduce any new functionality
-  or creative thinking. As long as the change does not affect functionality, some likely
-  examples include the following:
+As a rule of thumb, changes are obvious fixes if they do not introduce any new functionality or creative thinking. As long as the change does not affect functionality, some likely examples include the following:
 
-* Spelling / grammar fixes
-* Typo correction, white space and formatting changes
-* Comment clean up
-* Bug fixes that change default return values or error codes stored in constants
-* Adding logging messages or debugging output
-* Changes to ‘metadata’ files like Gemfile, .gitignore, build scripts, etc.
-* Moving source files from one directory or package to another
+- Spelling / grammar fixes
+- Typo correction, white space and formatting changes
+- Comment clean up
+- Bug fixes that change default return values or error codes stored in constants
+- Adding logging messages or debugging output
+- Changes to 'metadata' files like Gemfile, .gitignore, build scripts, etc.
+- Moving source files from one directory or package to another
 
-**Whenever you invoke the “obvious fix” rule, please say so in your commit message:**
+**Whenever you invoke the "obvious fix" rule, please say so in your commit message:**
 
 ```
 ------------------------------------------------------------------------
@@ -107,67 +77,50 @@ Date:   Wed Sep 18 11:44:40 2013 -0700
 ------------------------------------------------------------------------
 ```
 
-## <a name="issues"></a> Chef Issue Tracking
+## Chef Issue Tracking
 
 Chef Issue Tracking is handled using Github Issues.
 
-If you are familiar with Chef and know the component that is causing you a problem or if you
-  have a feature request on a specific component you can file an issue in the corresponding
-  Github project. All of our Open Source Software can be found in our
-  [Github organization](https://github.com/chef/).
+If you are familiar with Chef and know the component that is causing you a problem or if you have a feature request on a specific component you can file an issue in the corresponding Github project. All of our Open Source Software can be found in our [Github organization](https://github.com/chef/).
 
-There is also a listing of the various Chef products and where to file issues that can be
-  found in the Chef docs in the [community contributions section](https://docs.chef.io/community_contributions.html#issues-and-bug-reports).
-
-Otherwise you can file your issue in the [Chef project](https://github.com/chef/chef/issues)
-  and we will make sure it gets filed against the appropriate project.
+There is also a listing of the various Chef products and where to file issues that can be found in the Chef docs in the [community contributions section](https://docs.chef.io/community_contributions.html#issues-and-bug-reports).
 
+Otherwise you can file your issue in the [Chef project](https://github.com/chef/chef/issues) and we will make sure it gets filed against the appropriate project.
 
 ### Useful Github Queries
 
 Contributions go through a review process to improve code quality and avoid regressions. Managing a large number of contributions requires a workflow to provide queues for work such as triage, code review, and merging. A semi-formal process has evolved over the life of the project. Chef maintains this process pending community development and acceptance of an [RFC](https://github.com/chef/chef-rfc). These queries will help track contributions through this process:
 
-* [Issues that are not assigned to a team](https://github.com/chef/chef/issues?q=is%3Aopen+-label%3AAIX+-label%3ABSD+-label%3Awindows+-label%3A%22Chef+Core%22++-label%3A%22Dev+Tools%22+-label%3AUbuntu+-label%3A%22Enterprise+Linux%22+-label%3A%22Ready+For+Merge%22+-label%3AMac+-label%3ASolaris+)
-* [Untriaged Issues](https://github.com/chef/chef/issues?q=is%3Aopen+is%3Aissue+-label%3ABug+-label%3AEnhancement+-label%3A%22Tech+Cleanup%22+-label%3A%22Ready+For+Merge%22)
-* [PRs to be Reviewed](https://github.com/chef/chef/labels/Pending%20Maintainer%20Review)
-* [Suitable for First Contribution](https://github.com/chef/chef/labels/Easy)
-
-## <a name="release"></a> Chef Release Cycles
+- [Issues that are not assigned to a team](https://github.com/chef/chef/issues?q=is%3Aopen+-label%3AAIX+-label%3ABSD+-label%3Awindows+-label%3A%22Chef+Core%22++-label%3A%22Dev+Tools%22+-label%3AUbuntu+-label%3A%22Enterprise+Linux%22+-label%3A%22Ready+For+Merge%22+-label%3AMac+-label%3ASolaris+)
+- [Untriaged Issues](https://github.com/chef/chef/issues?q=is%3Aopen+is%3Aissue+-label%3ABug+-label%3AEnhancement+-label%3A%22Tech+Cleanup%22+-label%3A%22Ready+For+Merge%22)
+- [PRs to be Reviewed](https://github.com/chef/chef/labels/Pending%20Maintainer%20Review)
+- [Suitable for First Contribution](https://github.com/chef/chef/labels/Easy)
 
-Our primary shipping vehicle is operating system specific packages that includes
-  all the requirements of Chef. We call these [Omnibus packages](https://github.com/chef/omnibus)
+## Chef Release Cycles
 
-We also release our software as gems to [Rubygems](https://rubygems.org/) but we strongly
-  recommend using Chef packages since they are the only combination of native libraries &
-  gems required by Chef that we test throughly.
+Our primary shipping vehicle is operating system specific packages that includes all the requirements of Chef. We call these [Omnibus packages](https://github.com/chef/omnibus)
 
-Our version numbering closely follows [Semantic Versioning](http://semver.org/) standard. Our
-  standard version numbers look like X.Y.Z which mean:
+We also release our software as gems to [Rubygems](https://rubygems.org/) but we strongly recommend using Chef packages since they are the only combination of native libraries & gems required by Chef that we test throughly.
 
-* X is a major release, which may not be fully compatible with prior major releases
-* Y is a minor release, which adds both new features and bug fixes
-* Z is a patch release, which adds just bug fixes
+Our version numbering roughly follows [Semantic Versioning](http://semver.org/) standard. Our standard version numbers look like X.Y.Z which mean:
 
-We frequently make `alpha` and `beta` releases with version numbers that look like
-  `X.Y.Z.alpha.0` or `X.Y.Z.beta.1`. These releases are still well tested but not as
-  throughly as **Minor** or **Patch** releases.
+- X is a major release, which may not be fully compatible with prior major releases
+- Y is a minor release, which adds both new features and bug fixes
+- Z is a patch release, which adds just bug fixes
 
-We do a `Minor` release approximately every 3 months and `Patch` releases on a when-needed
-  basis for regressions, significant bugs, and security issues.
+After shipping a release of Chef we bump the `Minor` version by one to start development of the next minor releaae. All merges to master trigger an increment of the `Patch` version, and a build through our internal testing pipeline. We do a `Minor` release approximately every month, which consist of shipping one of the already auto-incremented and tested `Patch` versions. For example after shiping 12.10.24, we incremented Chef to 12.11.0\. From there 18 commits where merged bringing the  [...]
 
-Announcements of releases are available on [Chef Blog](https://www.chef.io/blog/) when they are
-  available.
+Announcements of releases are made to the [chef mailing list](https://discourse.chef.io/c/chef-release) when they are available.
 
 ## Chef Community
 
-Chef is made possible by a strong community of developers and system administrators. If you have
-  any questions or if you would like to get involved in the Chef community you can check out:
+Chef is made possible by a strong community of developers and system administrators. If you have any questions or if you would like to get involved in the Chef community you can check out:
 
-* [chef](https://discourse.chef.io/) mailing list
-* [\#chef](https://botbot.me/freenode/chef/) and [\#chef-hacking](https://botbot.me/freenode/chef-hacking/) IRC channels on irc.freenode.net
+- [Chef Mailing List](https://discourse.chef.io/)
+- [Chef Community Slack](https://community-slack.chef.io/)
 
 Also here are some additional pointers to some awesome Chef content:
 
-* [Chef Docs](https://docs.chef.io/)
-* [Learn Chef](https://learn.chef.io/)
-* [Chef Inc](https://www.chef.io/)
+- [Chef Docs](https://docs.chef.io/)
+- [Learn Chef](https://learn.chef.io/)
+- [Chef Inc.](https://www.chef.io/)
diff --git a/DOC_CHANGES.md b/DOC_CHANGES.md
index 069cdc6..fadd382 100644
--- a/DOC_CHANGES.md
+++ b/DOC_CHANGES.md
@@ -6,15 +6,4 @@ Example Doc Change:
 Description of the required change.
 -->
 
-## Doc changes for Chef 12.11
-
-### RFC 062 Exit Status Support
-
-Starting with Chef Client 12.11, there is support for the consistent, standard exit codes as defined in [Chef RFC 062](https://github.com/chef/chef-rfc/blob/master/rfc062-exit-status.md).
-
-With no additional configuration when Chef Client exits with a non-standard exit code a deprecation warning will be issued advising users of the upcoming change in behavior.
-
-To enable the standardized exit code behavior, there is a new setting in client.rb.  The `exit_status` setting, when set to `:enabled` will enforce standarized exit codes.  In a future release, this will become the default behavior.
-
-If you need to maintain the previous exit code behavior to support your current workflow, you can disable this (and the deprecation warnings) by setting `exit_status` to `:disabled`.
-
+## Doc changes for Chef 12.13
diff --git a/Gemfile b/Gemfile
index c205490..003df8e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -13,7 +13,7 @@ gem "chef", path: "."
 
 gem "chef-config", path: File.expand_path("../chef-config", __FILE__) if File.exist?(File.expand_path("../chef-config", __FILE__))
 # Ensure that we can always install rake, regardless of gem groups
-gem "rake"
+gem "rake", group: [ :default, :omnibus_package, :development ]
 gem "bundler"
 gem "cheffish"
 
@@ -22,6 +22,7 @@ group(:omnibus_package) do
   gem "rb-readline"
   gem "nokogiri"
 end
+
 group(:omnibus_package, :pry) do
   gem "pry"
   gem "pry-byebug"
@@ -37,8 +38,14 @@ group(:integration) do
   gem "chefspec"
   gem "halite"
   gem "poise"
+  gem "poise-boiler", git: "https://github.com/poise/poise-boiler"
   gem "knife-windows"
   gem "foodcritic"
+
+  # We pin this so nobody brings in a cucumber-core incompatible with cucumber latest
+  gem "cucumber", ">= 2.4.0"
+  # We pin oc-chef-pedant to prevent it from updating out of lockstep with chef-zero
+  gem "oc-chef-pedant", git: "https://github.com/chef/chef-server"
 end
 
 group(:docgen) do
@@ -65,7 +72,6 @@ end
 
 group(:development, :test) do
   gem "simplecov"
-  gem "rack"
 
   # for testing new chefstyle rules
   # gem 'chefstyle', github: 'chef/chefstyle'
@@ -78,7 +84,8 @@ end
 
 group(:travis) do
   # See `bundler-audit` in .travis.yml
-  gem "bundler-audit", git: "https://github.com/rubysec/bundler-audit.git", ref: "4e32fca"
+  gem "bundler-audit", git: "https://github.com/rubysec/bundler-audit.git"
+  gem "travis"
 end
 
 instance_eval(ENV["GEMFILE_MOD"]) if ENV["GEMFILE_MOD"]
diff --git a/Gemfile.lock b/Gemfile.lock
index b933528..a23bc64 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,34 +1,84 @@
 GIT
+  remote: https://github.com/chef/chef-server
+  revision: 8165dfd0524ee6076eda32727cbd0005c9348150
+  specs:
+    oc-chef-pedant (2.2.0)
+      activesupport (~> 5.0)
+      erubis (~> 2.7)
+      mixlib-authentication (~> 1.4)
+      mixlib-config (~> 2.0)
+      mixlib-shellout (>= 1.1)
+      net-http-spy (~> 0.2)
+      rest-client (>= 1.6)
+      rspec (~> 3.2)
+      rspec-rerun (~> 1.0)
+      rspec_junit_formatter (~> 0.2)
+
+GIT
   remote: https://github.com/chef/chefstyle.git
-  revision: 52a0d55a9e8fb30d067c0a66371db4ae781a0269
+  revision: c36dcbd6c2c21d2e19db77d9fbdf2402d0bacccf
   branch: master
   specs:
-    chefstyle (0.3.1)
-      rubocop (= 0.39.0)
+    chefstyle (0.4.0)
+      rubocop (= 0.42.0)
+
+GIT
+  remote: https://github.com/poise/poise-boiler
+  revision: b2faa2bcfd128d5fcd781c2b727d05f6b818d74b
+  specs:
+    poise-boiler (1.12.1.pre)
+      bundler
+      chefspec (~> 5.0)
+      codeclimate-test-reporter (~> 0.4)
+      codecov (~> 0.0, >= 0.0.2)
+      foodcritic (~> 7.0)
+      fuubar (~> 2.0)
+      git (~> 1.2)
+      halite (~> 1.2)
+      kitchen-docker (>= 2.6.0.rc.0)
+      kitchen-ec2 (~> 1.0)
+      kitchen-sync (~> 2.1)
+      kitchen-vagrant
+      mixlib-shellout (>= 1.4, < 3.0)
+      poise-profiler (~> 1.0)
+      pry
+      pry-byebug
+      rake (>= 10.4, < 12.0)
+      rspec (~> 3.2)
+      rspec-its (~> 1.2)
+      simplecov (~> 0.9)
+      test-kitchen (~> 1.7, >= 1.7.1)
+      travis (~> 1.8, >= 1.8.1)
+      vagrant-wrapper
+      winrm (~> 2.0)
+      winrm-fs (~> 1.0)
+      yard (~> 0.8)
+      yard-classmethods (~> 1.0)
 
 GIT
   remote: https://github.com/rubysec/bundler-audit.git
-  revision: 4e32fca89d75f0e249671431ff38aadc02bfb28b
-  ref: 4e32fca
+  revision: b7123d7b294f244165d9469f22b37a559e235fc2
   specs:
-    bundler-audit (0.4.0)
+    bundler-audit (0.5.0)
       bundler (~> 1.2)
       thor (~> 0.18)
 
 PATH
   remote: .
   specs:
-    chef (12.11.18)
+    chef (12.14.75)
+      addressable
       bundler (>= 1.10)
-      chef-config (= 12.11.18)
-      chef-zero (~> 4.5)
+      chef-config (= 12.14.75)
+      chef-zero (>= 4.8)
       diff-lcs (~> 1.2, >= 1.2.4)
       erubis (~> 2.7)
       ffi-yajl (~> 2.2)
       highline (~> 1.6, >= 1.6.9)
       iniparse (~> 1.4)
+      mixlib-archive (>= 0.2.0)
       mixlib-authentication (~> 1.4)
-      mixlib-cli (~> 1.4)
+      mixlib-cli (~> 1.7)
       mixlib-log (~> 1.3)
       mixlib-shellout (~> 2.0)
       net-sftp (~> 2.1, >= 2.1.2)
@@ -37,26 +87,28 @@ PATH
       ohai (>= 8.6.0.alpha.1, < 9)
       plist (~> 3.2)
       proxifier (~> 1.0)
-      rspec-core (~> 3.4)
-      rspec-expectations (~> 3.4)
-      rspec-mocks (~> 3.4)
+      rspec-core (~> 3.5)
+      rspec-expectations (~> 3.5)
+      rspec-mocks (~> 3.5)
       rspec_junit_formatter (~> 0.2.0)
       serverspec (~> 2.7)
       specinfra (~> 2.10)
       syslog-logger (~> 1.6)
       uuidtools (~> 2.1.5)
-    chef (12.11.18-universal-mingw32)
+    chef (12.14.75-universal-mingw32)
+      addressable
       bundler (>= 1.10)
-      chef-config (= 12.11.18)
-      chef-zero (~> 4.5)
+      chef-config (= 12.14.75)
+      chef-zero (>= 4.8)
       diff-lcs (~> 1.2, >= 1.2.4)
       erubis (~> 2.7)
       ffi (~> 1.9)
       ffi-yajl (~> 2.2)
       highline (~> 1.6, >= 1.6.9)
       iniparse (~> 1.4)
+      mixlib-archive (>= 0.2.0)
       mixlib-authentication (~> 1.4)
-      mixlib-cli (~> 1.4)
+      mixlib-cli (~> 1.7)
       mixlib-log (~> 1.3)
       mixlib-shellout (~> 2.0)
       net-sftp (~> 2.1, >= 2.1.2)
@@ -65,9 +117,9 @@ PATH
       ohai (>= 8.6.0.alpha.1, < 9)
       plist (~> 3.2)
       proxifier (~> 1.0)
-      rspec-core (~> 3.4)
-      rspec-expectations (~> 3.4)
-      rspec-mocks (~> 3.4)
+      rspec-core (~> 3.5)
+      rspec-expectations (~> 3.5)
+      rspec-mocks (~> 3.5)
       rspec_junit_formatter (~> 0.2.0)
       serverspec (~> 2.7)
       specinfra (~> 2.10)
@@ -87,84 +139,115 @@ PATH
 PATH
   remote: chef-config
   specs:
-    chef-config (12.11.18)
-      fuzzyurl (~> 0.8.0)
+    chef-config (12.14.75)
+      addressable
+      fuzzyurl
       mixlib-config (~> 2.0)
       mixlib-shellout (~> 2.0)
 
 GEM
   remote: https://rubygems.org/
   specs:
+    activesupport (5.0.0.1)
+      concurrent-ruby (~> 1.0, >= 1.0.2)
+      i18n (~> 0.7)
+      minitest (~> 5.1)
+      tzinfo (~> 1.1)
     addressable (2.4.0)
     appbundler (0.9.0)
       mixlib-cli (~> 1.4)
-    artifactory (2.3.2)
-    ast (2.2.0)
-    aws-sdk (2.3.9)
-      aws-sdk-resources (= 2.3.9)
-    aws-sdk-core (2.3.9)
+    artifactory (2.3.3)
+    ast (2.3.0)
+    aws-sdk (2.5.11)
+      aws-sdk-resources (= 2.5.11)
+    aws-sdk-core (2.5.11)
       jmespath (~> 1.0)
-    aws-sdk-resources (2.3.9)
-      aws-sdk-core (= 2.3.9)
+    aws-sdk-resources (2.5.11)
+      aws-sdk-core (= 2.5.11)
     aws-sdk-v1 (1.66.0)
       json (~> 1.4)
       nokogiri (>= 1.4.4)
+    backports (3.6.8)
     binding_of_caller (0.7.2)
       debug_inspector (>= 0.0.1)
     builder (3.2.2)
     byebug (9.0.5)
-    chef-api (0.6.0)
+    chef-api (0.7.0)
       logify (~> 0.1)
       mime-types
-    chef-provisioning (1.7.1)
-      cheffish (>= 1.3.1, < 3.0)
+    chef-provisioning (2.0.1)
+      cheffish (~> 4.0)
       inifile (>= 2.0.2)
       mixlib-install (~> 1.0)
       net-scp (~> 1.0)
       net-ssh (>= 2.9, < 4.0)
       net-ssh-gateway (~> 1.2.0)
-      winrm (~> 1.3)
-    chef-provisioning-aws (1.9.0)
+      winrm-fs (~> 1.0)
+    chef-provisioning-aws (2.0.0)
       aws-sdk (>= 2.1.26, < 3.0)
       aws-sdk-v1 (>= 1.59.0)
-      chef-provisioning (~> 1.4)
+      chef-provisioning (~> 2.0)
       retryable (~> 2.0, >= 2.0.1)
       ubuntu_ami (~> 0.4, >= 0.4.1)
     chef-rewind (0.0.9)
-    chef-sugar (3.3.0)
-    chef-zero (4.6.2)
+    chef-sugar (3.4.0)
+    chef-zero (5.1.0)
       ffi-yajl (~> 2.2)
       hashie (>= 2.0, < 4.0)
       mixlib-log (~> 1.3)
-      rack
+      rack (~> 2.0)
       uuidtools (~> 2.1)
-    cheffish (2.0.4)
-      chef-zero (~> 4.3)
-      compat_resource
-    chefspec (4.7.0)
-      chef (>= 11.14)
-      fauxhai (~> 3.2)
+    cheffish (4.0.0)
+      chef-zero (~> 5.0)
+      net-ssh
+    chefspec (5.0.0)
+      chef (>= 12.0)
+      fauxhai (~> 3.6)
       rspec (~> 3.0)
+    codeclimate-test-reporter (0.6.0)
+      simplecov (>= 0.7.1, < 1.0.0)
+    codecov (0.1.5)
+      json
+      simplecov
+      url
     coderay (1.1.1)
-    colorize (0.7.7)
-    compat_resource (12.10.4)
-    cucumber-core (1.4.0)
-      gherkin (~> 3.2.0)
+    colorize (0.8.1)
+    concurrent-ruby (1.0.2)
+    cucumber (2.4.0)
+      builder (>= 2.1.2)
+      cucumber-core (~> 1.5.0)
+      cucumber-wire (~> 0.0.1)
+      diff-lcs (>= 1.1.3)
+      gherkin (~> 4.0)
+      multi_json (>= 1.7.5, < 2.0)
+      multi_test (>= 0.1.2)
+    cucumber-core (1.5.0)
+      gherkin (~> 4.0)
+    cucumber-wire (0.0.1)
     debug_inspector (0.0.2)
     descendants_tracker (0.0.4)
       thread_safe (~> 0.3, >= 0.3.1)
     diff-lcs (1.2.5)
     docile (1.1.5)
+    domain_name (0.5.20160826)
+      unf (>= 0.0.5, < 1.0.0)
     erubis (2.7.0)
+    ethon (0.9.0)
+      ffi (>= 1.3.0)
+    excon (0.52.0)
     faraday (0.9.2)
       multipart-post (>= 1.2, < 3)
-    fauxhai (3.5.0)
+    faraday_middleware (0.10.0)
+      faraday (>= 0.7.4, < 0.10)
+    fauxhai (3.8.0)
       net-ssh
-    ffi (1.9.10)
-    ffi (1.9.10-x86-mingw32)
-    ffi-yajl (2.2.3)
+    ffi (1.9.14)
+    ffi (1.9.14-x86-mingw32)
+    ffi-win32-extensions (1.0.3)
+      ffi
+    ffi-yajl (2.3.0)
       libyajl2 (~> 1.2)
-    foodcritic (6.3.0)
+    foodcritic (7.1.0)
       cucumber-core (>= 1.3)
       erubis
       nokogiri (>= 1.5, < 2.0)
@@ -172,15 +255,26 @@ GEM
       rufus-lru (~> 1.0)
       treetop (~> 1.4)
       yajl-ruby (~> 1.1)
-    fuzzyurl (0.8.0)
-    gherkin (3.2.0)
-    github_api (0.14.0)
+    fuubar (2.2.0)
+      rspec-core (~> 3.0)
+      ruby-progressbar (~> 1.4)
+    fuzzyurl (0.9.0)
+    gh (0.14.0)
+      addressable
+      backports
+      faraday (~> 0.8)
+      multi_json (~> 1.0)
+      net-http-persistent (>= 2.7)
+      net-http-pipeline
+    gherkin (4.0.0)
+    git (1.3.0)
+    github_api (0.14.5)
       addressable (~> 2.4.0)
       descendants_tracker (~> 0.0.4)
       faraday (~> 0.8, < 0.10)
       hashie (>= 3.4)
-      oauth2
-    github_changelog_generator (1.12.1)
+      oauth2 (~> 1.0)
+    github_changelog_generator (1.13.1)
       colorize (~> 0.7)
       github_api (~> 0.12)
       rake (>= 10.0)
@@ -188,24 +282,40 @@ GEM
       ffi (>= 1.0.1)
     gyoku (1.3.1)
       builder (>= 2.1.2)
-    halite (1.2.1)
+    halite (1.3.0)
       bundler
       chef (~> 12.0)
-      stove (~> 3.2, >= 3.2.3)
+      stove (~> 4.0)
       thor
     hashie (3.4.4)
     highline (1.7.8)
-    httpclient (2.8.0)
+    http-cookie (1.0.2)
+      domain_name (~> 0.5)
+    httpclient (2.8.2.4)
+    i18n (0.7.0)
     inifile (3.0.0)
     iniparse (1.4.2)
     ipaddress (0.8.3)
-    jmespath (1.2.4)
-      json_pure (>= 1.8.1)
+    jmespath (1.3.1)
     json (1.8.3)
-    json_pure (1.8.3)
-    jwt (1.5.1)
-    knife-windows (1.4.1)
-      winrm (~> 1.7)
+    jwt (1.5.4)
+    kitchen-docker (2.6.0.rc.0)
+      test-kitchen (>= 1.0.0)
+    kitchen-ec2 (1.1.0)
+      aws-sdk (~> 2)
+      excon
+      multi_json
+      retryable (~> 2.0)
+      test-kitchen (~> 1.4, >= 1.4.1)
+    kitchen-sync (2.1.1)
+      net-sftp
+      test-kitchen (>= 1.0.0)
+    kitchen-vagrant (0.20.0)
+      test-kitchen (~> 1.4)
+    knife-windows (1.6.0)
+      winrm-elevated (~> 1.0)
+    launchy (2.4.3)
+      addressable (~> 2.3)
     libyajl2 (1.2.0)
     little-plugger (1.1.4)
     logging (2.1.0)
@@ -216,32 +326,36 @@ GEM
     mime-types (3.1)
       mime-types-data (~> 3.2015)
     mime-types-data (3.2016.0521)
-    mini_portile2 (2.0.0)
-    mixlib-authentication (1.4.0)
+    mini_portile2 (2.1.0)
+    minitest (5.9.0)
+    mixlib-archive (0.2.0)
       mixlib-log
-      rspec-core (~> 3.2)
-      rspec-expectations (~> 3.2)
-      rspec-mocks (~> 3.2)
-    mixlib-cli (1.6.0)
-    mixlib-config (2.2.1)
-    mixlib-install (1.0.12)
+    mixlib-authentication (1.4.1)
+      mixlib-log
+    mixlib-cli (1.7.0)
+    mixlib-config (2.2.4)
+    mixlib-install (1.1.0)
       artifactory
       mixlib-shellout
       mixlib-versioning
-    mixlib-log (1.6.0)
-    mixlib-shellout (2.2.6)
-    mixlib-shellout (2.2.6-universal-mingw32)
+    mixlib-log (1.7.1)
+    mixlib-shellout (2.2.7)
+    mixlib-shellout (2.2.7-universal-mingw32)
       win32-process (~> 0.8.2)
       wmi-lite (~> 1.0)
     mixlib-versioning (1.1.0)
     multi_json (1.12.1)
+    multi_test (0.1.2)
     multi_xml (0.5.5)
     multipart-post (2.0.0)
+    net-http-persistent (2.9.4)
+    net-http-pipeline (1.0.1)
+    net-http-spy (0.2.1)
     net-scp (1.2.1)
       net-ssh (>= 2.6.5)
     net-sftp (2.1.2)
       net-ssh (>= 2.6.5)
-    net-ssh (3.1.1)
+    net-ssh (3.2.0)
     net-ssh-gateway (1.2.0)
       net-ssh (>= 2.6.5)
     net-ssh-multi (1.2.1)
@@ -249,40 +363,45 @@ GEM
       net-ssh-gateway (>= 1.2.0)
     net-telnet (0.1.1)
     netrc (0.11.0)
-    nokogiri (1.6.7.2)
-      mini_portile2 (~> 2.0.0.rc2)
-    nokogiri (1.6.7.2-x86-mingw32)
-      mini_portile2 (~> 2.0.0.rc2)
+    nokogiri (1.6.8)
+      mini_portile2 (~> 2.1.0)
+      pkg-config (~> 1.1.7)
+    nokogiri (1.6.8-x86-mingw32)
+      mini_portile2 (~> 2.1.0)
+      pkg-config (~> 1.1.7)
     nori (2.6.0)
-    oauth2 (1.1.0)
+    oauth2 (1.2.0)
       faraday (>= 0.8, < 0.10)
-      jwt (~> 1.0, < 1.5.2)
+      jwt (~> 1.0)
       multi_json (~> 1.3)
       multi_xml (~> 0.5)
       rack (>= 1.2, < 3)
     octokit (4.3.0)
       sawyer (~> 0.7.0, >= 0.5.3)
-    ohai (8.16.0)
+    ohai (8.20.0)
       chef-config (>= 12.5.0.alpha.1, < 13)
       ffi (~> 1.9)
       ffi-yajl (~> 2.2)
       ipaddress
       mixlib-cli
       mixlib-config (~> 2.0)
-      mixlib-log
+      mixlib-log (>= 1.7.1, < 2.0)
       mixlib-shellout (~> 2.0)
       plist (~> 3.1)
       systemu (~> 2.6.4)
       wmi-lite (~> 1.0)
     parser (2.3.1.2)
       ast (~> 2.2)
+    pkg-config (1.1.7)
     plist (3.2.0)
-    poise (2.7.0)
+    poise (2.7.1)
+      halite (~> 1.0)
+    poise-profiler (1.0.1)
       halite (~> 1.0)
     polyglot (0.3.5)
     powerpack (0.1.1)
     proxifier (1.0.3)
-    pry (0.10.3)
+    pry (0.10.4)
       coderay (~> 1.1.0)
       method_source (~> 0.8.1)
       slop (~> 3.4)
@@ -295,74 +414,118 @@ GEM
     pry-stack_explorer (0.4.9.2)
       binding_of_caller (>= 0.7)
       pry (>= 0.9.11)
-    rack (1.6.4)
+    pusher-client (0.6.2)
+      json
+      websocket (~> 1.0)
+    rack (2.0.1)
     rainbow (2.1.0)
-    rake (11.1.2)
+    rake (11.2.2)
     rb-readline (0.5.3)
-    retryable (2.0.3)
-    rspec (3.4.0)
-      rspec-core (~> 3.4.0)
-      rspec-expectations (~> 3.4.0)
-      rspec-mocks (~> 3.4.0)
-    rspec-core (3.4.4)
-      rspec-support (~> 3.4.0)
-    rspec-expectations (3.4.0)
+    rest-client (2.0.0)
+      http-cookie (>= 1.0.2, < 2.0)
+      mime-types (>= 1.16, < 4.0)
+      netrc (~> 0.8)
+    rest-client (2.0.0-x86-mingw32)
+      ffi (~> 1.9)
+      http-cookie (>= 1.0.2, < 2.0)
+      mime-types (>= 1.16, < 4.0)
+      netrc (~> 0.8)
+    retryable (2.0.4)
+    rspec (3.5.0)
+      rspec-core (~> 3.5.0)
+      rspec-expectations (~> 3.5.0)
+      rspec-mocks (~> 3.5.0)
+    rspec-core (3.5.3)
+      rspec-support (~> 3.5.0)
+    rspec-expectations (3.5.0)
       diff-lcs (>= 1.2.0, < 2.0)
-      rspec-support (~> 3.4.0)
+      rspec-support (~> 3.5.0)
     rspec-its (1.2.0)
       rspec-core (>= 3.0.0)
       rspec-expectations (>= 3.0.0)
-    rspec-mocks (3.4.1)
+    rspec-mocks (3.5.0)
       diff-lcs (>= 1.2.0, < 2.0)
-      rspec-support (~> 3.4.0)
-    rspec-support (3.4.1)
+      rspec-support (~> 3.5.0)
+    rspec-rerun (1.1.0)
+      rspec (~> 3.0)
+    rspec-support (3.5.0)
     rspec_junit_formatter (0.2.3)
       builder (< 4)
       rspec-core (>= 2, < 4, != 2.12.0)
-    rubocop (0.39.0)
-      parser (>= 2.3.0.7, < 3.0)
+    rubocop (0.42.0)
+      parser (>= 2.3.1.1, < 3.0)
       powerpack (~> 0.1)
       rainbow (>= 1.99.1, < 3.0)
       ruby-progressbar (~> 1.7)
       unicode-display_width (~> 1.0, >= 1.0.1)
-    ruby-prof (0.15.9)
+    ruby-prof (0.16.2)
     ruby-progressbar (1.8.1)
     ruby-shadow (2.5.0)
     rubyntlm (0.6.0)
+    rubyzip (1.2.0)
     rufus-lru (1.1.0)
+    safe_yaml (1.0.4)
     sawyer (0.7.0)
       addressable (>= 2.3.5, < 2.5)
       faraday (~> 0.8, < 0.10)
-    serverspec (2.36.0)
+    serverspec (2.36.1)
       multi_json
       rspec (~> 3.0)
       rspec-its
       specinfra (~> 2.53)
     sfl (2.2)
-    simplecov (0.11.2)
+    simplecov (0.12.0)
       docile (~> 1.1.0)
-      json (~> 1.8)
+      json (>= 1.8, < 3)
       simplecov-html (~> 0.10.0)
     simplecov-html (0.10.0)
     slop (3.6.0)
-    specinfra (2.57.5)
+    specinfra (2.61.3)
       net-scp
       net-ssh (>= 2.7, < 4.0)
       net-telnet
       sfl
-    stove (3.2.8)
+    stove (4.1.1)
       chef-api (~> 0.5)
       logify (~> 0.2)
     syslog-logger (1.6.8)
     systemu (2.6.5)
+    test-kitchen (1.12.0)
+      mixlib-install (~> 1.0, >= 1.0.4)
+      mixlib-shellout (>= 1.2, < 3.0)
+      net-scp (~> 1.1)
+      net-ssh (>= 2.9, < 4.0)
+      net-ssh-gateway (~> 1.2.0)
+      safe_yaml (~> 1.0)
+      thor (~> 0.18)
     thor (0.19.1)
     thread_safe (0.3.5)
-    tomlrb (1.2.1)
-    treetop (1.6.5)
+    tomlrb (1.2.3)
+    travis (1.8.2)
+      backports
+      faraday (~> 0.9)
+      faraday_middleware (~> 0.9, >= 0.9.1)
+      gh (~> 0.13)
+      highline (~> 1.6)
+      launchy (~> 2.1)
+      pusher-client (~> 0.4)
+      typhoeus (~> 0.6, >= 0.6.8)
+    treetop (1.6.8)
       polyglot (~> 0.3)
+    typhoeus (0.8.0)
+      ethon (>= 0.8.0)
+    tzinfo (1.2.2)
+      thread_safe (~> 0.1)
     ubuntu_ami (0.4.1)
-    unicode-display_width (1.0.5)
+    unf (0.1.4)
+      unf_ext
+    unf_ext (0.0.7.2)
+    unf_ext (0.0.7.2-x86-mingw32)
+    unicode-display_width (1.1.1)
+    url (0.3.2)
     uuidtools (2.1.5)
+    vagrant-wrapper (2.0.3)
+    websocket (1.2.3)
     win32-api (1.5.3-universal-mingw32)
     win32-dir (0.5.1)
       ffi (>= 1.0.0)
@@ -370,7 +533,7 @@ GEM
       win32-ipc (>= 0.6.0)
     win32-eventlog (0.6.3)
       ffi
-    win32-ipc (0.6.6)
+    win32-ipc (0.7.0)
       ffi
     win32-mmap (0.4.2)
       ffi
@@ -378,21 +541,33 @@ GEM
       win32-ipc (>= 0.6.0)
     win32-process (0.8.3)
       ffi (>= 1.0.0)
-    win32-service (0.8.7)
+    win32-service (0.8.9)
       ffi
+      ffi-win32-extensions
     windows-api (0.4.4)
       win32-api (>= 1.4.5)
-    winrm (1.8.1)
+    winrm (2.0.1)
       builder (>= 2.1.2)
+      erubis (~> 2.7)
       gssapi (~> 1.2)
       gyoku (~> 1.0)
       httpclient (~> 2.2, >= 2.2.0.2)
       logging (>= 1.6.1, < 3.0)
       nori (~> 2.0)
       rubyntlm (~> 0.6.0)
+    winrm-elevated (1.0.0)
+      winrm (~> 2.0)
+      winrm-fs (~> 1.0)
+    winrm-fs (1.0.0)
+      erubis (~> 2.7)
+      logging (>= 1.6.1, < 3.0)
+      rubyzip (~> 1.1)
+      winrm (~> 2.0)
     wmi-lite (1.0.0)
     yajl-ruby (1.2.1)
-    yard (0.8.7.6)
+    yard (0.9.5)
+    yard-classmethods (1.0.0)
+      yard
 
 PLATFORMS
   ruby
@@ -411,25 +586,28 @@ DEPENDENCIES
   cheffish
   chefspec
   chefstyle!
+  cucumber (>= 2.4.0)
   foodcritic
   github_changelog_generator
   halite
   knife-windows
   netrc
   nokogiri
+  oc-chef-pedant!
   octokit
   poise
+  poise-boiler!
   pry
   pry-byebug
   pry-remote
   pry-stack_explorer
-  rack
   rake
   rb-readline
   ruby-prof
   ruby-shadow
   simplecov
   tomlrb
+  travis
   yard
 
 BUNDLED WITH
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index b0474b5..d8fc022 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -7,7 +7,7 @@ file tells you who needs to review your patch - you need a simple majority of ma
 for the relevant subsystems to provide a :+1: on your pull request. Additionally, you need
 to not receive a veto from a Lieutenant or the Project Lead.
 
-Check out [How Chef is Maintained](https://github.com/opscode/chef-rfc/blob/master/rfc030-maintenance-policy.md#how-the-project-is-maintained) for details on the process, how to become
+Check out [How Chef is Maintained](https://github.com/chef/chef-rfc/blob/master/rfc030-maintenance-policy.md#how-the-project-is-maintained) for details on the process, how to become
 a maintainer, lieutenant, or the project lead.
 
 # Project Lead
@@ -36,6 +36,7 @@ To mention the team, use @chef/client-core
 * [AJ Christensen](https://github.com/fujin)
 * [Phil Dibowitz](https://github.com/jaymzh)
 * [Jay Mundrawala](https://github.com/jaym)
+* [John Keiser](https://github.com/jkeiser)
 * [Jon Cowie](https://github.com/jonlives)
 * [Lamont Granquist](https://github.com/lamont-granquist)
 * [Claire McQuin](https://github.com/mcquin)
@@ -65,6 +66,7 @@ To mention the team, use @chef/client-dev-tools
 ### Maintainers
 
 * [Daniel DeLeo](https://github.com/danielsdeleo)
+* [John Keiser](https://github.com/jkeiser)
 * [Joshua Timberman](https://github.com/jtimberman)
 * [Lamont Granquist](https://github.com/lamont-granquist)
 * [Steven Danna](https://github.com/stevendanna)
@@ -100,6 +102,7 @@ To mention the team, use @chef/provisioning
 * [Stuart Preston](https://github.com/stuartpreston)
 * [JJ Asghar](https://github.com/jjasghar)
 * [João Cravo](https://github.com/joaogbcravo)
+* [Harley Alaniz](https://github.com/thehar)
 
 ## Platform Specific Components
 
@@ -197,11 +200,11 @@ To mention the team, use @chef/client-nxos
 
 ### Lieutenant
 
-* [Carl Perry](https://github.com/edolnx)
+* [Adam Leff](https://github.com/adamleff)
 
 ### Maintainers
 
-* [Carl Perry](https://github.com/edolnx)
+* [Adam Leff](https://github.com/adamleff)
 
 ## Cisco IOS XR
 
@@ -209,11 +212,11 @@ To mention the team, use @chef/client-iosxr
 
 ### Lieutenant
 
-* [Carl Perry](https://github.com/edolnx)
+* [Adam Leff](https://github.com/adamleff)
 
 ### Maintainers
 
-* [Carl Perry](https://github.com/edolnx)
+* [Adam Leff](https://github.com/adamleff)
 
 ## Fedora
 
diff --git a/MAINTAINERS.toml b/MAINTAINERS.toml
index 9501c6d..ca90a72 100644
--- a/MAINTAINERS.toml
+++ b/MAINTAINERS.toml
@@ -12,7 +12,7 @@ file tells you who needs to review your patch - you need a simple majority of ma
 for the relevant subsystems to provide a :+1: on your pull request. Additionally, you need
 to not receive a veto from a Lieutenant or the Project Lead.
 
-Check out [How Chef is Maintained](https://github.com/opscode/chef-rfc/blob/master/rfc030-maintenance-policy.md#how-the-project-is-maintained) for details on the process, how to become
+Check out [How Chef is Maintained](https://github.com/chef/chef-rfc/blob/master/rfc030-maintenance-policy.md#how-the-project-is-maintained) for details on the process, how to become
 a maintainer, lieutenant, or the project lead.
 """
 
@@ -42,6 +42,7 @@ another component.
         "fujin",
         "jaymzh",
         "jaym",
+        "jkeiser",
         "jonlives",
         "lamont-granquist",
         "mcquin",
@@ -77,6 +78,7 @@ another component.
 
       maintainers = [
         "danielsdeleo",
+        "jkeiser",
         "jtimberman",
         "lamont-granquist",
         "stevendanna"
@@ -109,6 +111,7 @@ Chef Provisioning and Drivers.  Supported Drivers are listed in the [README](htt
         "stuartpreston",
         "jjasghar",
         "joaogbcravo",
+        "thehar"
       ]
 
     [Org.Components.Subsystems]
@@ -410,3 +413,7 @@ The specific components of Chef related to a given platform - including (but not
     Name = "João Cravo"
     GitHub = "joaogbcravo"
     Twitter = "joaogbcravo"
+
+  [people.thehar]
+    Name = "Harley Alaniz"
+    GitHub = "thehar"
diff --git a/README.md b/README.md
index 1e1e4ad..6eb0c27 100644
--- a/README.md
+++ b/README.md
@@ -8,9 +8,7 @@ Want to try Chef? Get started with [learnchef](https://learn.chef.io)
 * Documentation: [http://docs.chef.io](http://docs.chef.io)
 * Source: [http://github.com/chef/chef/tree/master](http://github.com/chef/chef/tree/master)
 * Tickets/Issues: [https://github.com/chef/chef/issues](https://github.com/chef/chef/issues)
-* IRC: `#chef` and `#chef-hacking` on Freenode
-  - Join via browser: [#chef](https://webchat.freenode.net/?channels=chef), [#chef-hacking](https://webchat.freenode.net/?channels=chef-hacking)
-  - View logs: [#chef](https://botbot.me/freenode/chef/), [#chef-hacking](https://botbot.me/freenode/chef-hacking/)
+* Slack: [Chef Community Slack](https://community-slack.chef.io/)
 * Mailing list: [https://discourse.chef.io](https://discourse.chef.io)
 
 Chef is a configuration management tool designed to bring automation to your
@@ -38,9 +36,9 @@ Install these via your platform's preferred method (`apt`, `yum`, `ports`,
 * git
 * C compiler, header files, etc. On Ubuntu/Debian, use the
   `build-essential` package.
-* ruby 2.0.0 or later
+* ruby 2.1.0 or later
 * rubygems
-* bundler
+* bundler gem
 
 ### Chef Installation
 
@@ -77,10 +75,10 @@ The general development process is:
 4. Push your feature branch to github and open a pull request against
    master.
 
-Once your repository is set up, you can start working on the code. We do use
-TDD with RSpec, so you'll need to get a development environment running.
-Follow the above procedure ("Installing from Git") to get your local
-copy of the source running.
+Once your repository is set up, you can start working on the code. We do utilize
+RSpec for test driven development, so you'll need to get a development 
+environment running. Follow the above procedure ("Installing from Git") to get
+your local copy of the source running.
 
 ## Reporting Issues
 
@@ -142,12 +140,12 @@ Chef is an amalgam of many components. These components update all the time, nec
 
 ## Chef Packages
 
-Chef is distributed as packages for debian, rhel, ubuntu, windows and os/x. It includes a large number of components from various sources, and these are versioned and maintained separately from chef project, which bundles them all together conveniently for the user.
+Chef is distributed as packages for debian, rhel, ubuntu, windows, solaris, aix, and os x. It includes a large number of components from various sources, and these are versioned and maintained separately from chef project, which bundles them all together conveniently for the user.
 
 These packages go through several milestones:
 - `master`: When code is checked in to master, the patch version of chef is bumped (e.g. 0.9.10 -> 0.9.11) and a build is kicked off automatically to create and test the packages in Chef's Jenkins cluster.
 - `unstable`: When a package is built, it enters the unstable channel. When all packages for all OS's have successfully built, the test phase is kicked off in Jenkins across all supported OS's. These builds are password-protected and generally only available to the test systems.
-- `current`: If the packages pass all the tests on all supported OS's, it is promoted as a unit to `current`, and is available via Chef's artifactory by running `curl https://omnitruck.chef.io/install.sh | sudo bash -s -- -c current -P chef`
+- `current`: If the packages pass all the tests on all supported OS's, it is promoted as a unit to `current`, and is available via Chef's artifactory by running `curl https://www.chef.io/chef/install.sh | sudo bash -s -- -c current -P chef`
 - `stable`: Periodically, Chef will pick a release to "bless" for folks who would like a slower update schedule than "every time a build passes the tests." When this happens, it is manually promoted to stable and an announcement is sent to the list. It can be reached at https://downloads.chef.io or installed using the `curl` command without specifying `-c current`. Packages in `stable` are no longer available in `current`.
 
 Additionally, periodically Chef will update the desired versions of chef components and check that in to `master`, triggering a new build with the updated components in it.
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index b25aa52..8010770 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,4 +1,44 @@
 *This file holds "in progress" release notes for the current release under development and is intended for consumption by the Chef Documentation team.
 Please see `https://docs.chef.io/release/<major>-<minor>/release_notes.html` for the official Chef release notes.*
 
-# Chef Client Release Notes 12.10:
+# Chef Client Release Notes 12.14:
+
+## Highlighted enhancements for this release:
+
+* Upgraded Ruby version from 2.1.9 to 2.3.1 which adds several performance and functionality enhancements.
+* Added a small patch to Ruby 2.3.1 and improvements to the Ohai Network plugin in order to support chef client runs on Windows Nano Server.
+* Added the ability to mark a property of a custom resource as "sensitive." This will suppress the property's value when it's used in other outputs, such as messages used by the [Data Collector](https://github.com/chef/chef-rfc/blob/master/rfc077-mode-agnostic-data-collection.md). To use, add `sensitive: true` when definine the property. Example:
+
+  ```ruby
+  property :db_password, String, sensitive: true
+  ```
+
+* Ported the yum_repository resource from the yum cookbook to core chef. With this change you can create and remove repositories without depending on the yum cookbook. Example:
+
+  ```ruby
+  yum_repository 'OurCo' do
+    description 'OurCo yum repository'
+    mirrorlist 'http://artifacts.ourco.org/mirrorlist?repo=ourco-6&arch=$basearch'
+    gpgkey 'http://artifacts.ourco.org/pub/yum/RPM-GPG-KEY-OURCO-6'
+    action :create
+  end
+
+  yum 'Oldrepo' do
+    action :delete
+  end
+  ```
+
+* Support for Solaris releases before 10u11 has been removed
+* Upgraded Ohai to 8.20 with new / enhanced plugins. See the [ohai changelog](https://github.com/chef-cookbooks/ohai/blob/master/CHANGELOG.md)
+
+## Highlighted bug fixes for this release:
+
+Fixed `chef_gem` for local gems with remote dependencies. A recent chef release introduced a breaking change which added the `--local` argument to `gem installs` for local gems prohibiting remote dependencies from being installed. Users who want to ensure that gem installs remain completely local should add `--local` to the `options` property:
+
+```
+chef_gem 'my-gem' do
+  source '/tmp/gems/my-gem.gem'
+  options '--local'
+  action :install
+end
+```
diff --git a/Rakefile b/Rakefile
index 7a9ff83..74a30d4 100644
--- a/Rakefile
+++ b/Rakefile
@@ -29,12 +29,13 @@ require_relative "tasks/cbgb"
 require_relative "tasks/dependencies"
 require_relative "tasks/changelog"
 
-ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "Chef") do |package|
+ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "Chef", "chef") do |package|
   package.component_paths = ["chef-config"]
   package.generate_version_class = true
 end
 # Add a conservative dependency update to version:bump (which was created by PackageTask)
-task "version:bump" => %w{version:bump_patch version:update bundle:install}
+task "version:bump" => %w{version:bump_patch version:update}
+task "version:bump" => %w{version:bump_patch version:update}
 
 task :pedant, :chef_zero_spec
 
diff --git a/VERSION b/VERSION
index e161e86..6a0c5ba 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-12.11.18
\ No newline at end of file
+12.14.75
\ No newline at end of file
diff --git a/acceptance/.gitignore b/acceptance/.gitignore
index c2ab707..4b0b151 100644
--- a/acceptance/.gitignore
+++ b/acceptance/.gitignore
@@ -1 +1,3 @@
 .acceptance_logs
+.acceptance_data
+data-collector/Berksfile.lock
diff --git a/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml b/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml
index 4022882..209b7fa 100644
--- a/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml
+++ b/acceptance/.shared/kitchen_acceptance/.kitchen.ec2.yml
@@ -14,7 +14,6 @@ driver:
   aws_ssh_key_id: <%= ENV['AWS_SSH_KEY_ID'] || ENV['USER'] || ENV['USERNAME'] %>
   # test-specific stuff
   region: us-west-2
-  availability_zone: a
   subnet_id: subnet-19ac017c
   security_group_ids: ["sg-e401eb83", "sg-96274af3"]
   instance_type: m3.large
@@ -213,18 +212,7 @@ platforms:
         image-type: machine
       user_data: |
         <powershell>
-        $logfile="C:\\Program Files\\Amazon\\Ec2ConfigService\\Logs\\kitchen-ec2.log"
-        #PS Remoting and & winrm.cmd basic config
-        Enable-PSRemoting -Force -SkipNetworkProfileCheck
-        & winrm.cmd set winrm/config '@{MaxTimeoutms="1800000"}' >> $logfile
-        & winrm.cmd set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}' >> $logfile
-        & winrm.cmd set winrm/config/winrs '@{MaxShellsPerUser="50"}' >> $logfile
-        #Server settings - support username/password login
-        & winrm.cmd set winrm/config/service/auth '@{Basic="true"}' >> $logfile
-        & winrm.cmd set winrm/config/service '@{AllowUnencrypted="true"}' >> $logfile
-        & winrm.cmd set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}' >> $logfile
-        #Firewall Config
-        & netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" profile=public protocol=tcp localport=5985 remoteip=localsubnet new remoteip=any  >> $logfile
+        & netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" profile=public protocol=tcp localport=5985 remoteip=localsubnet new remoteip=any
         #Set script execution to unrestricted
         & Set-ExecutionPolicy Unrestricted -Force
         </powershell>
diff --git a/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb b/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb
index d36909e..d5d2e13 100644
--- a/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb
+++ b/acceptance/.shared/kitchen_acceptance/libraries/kitchen.rb
@@ -1,3 +1,5 @@
+require 'chef/mixin/shell_out'
+
 module KitchenAcceptance
   class Kitchen < Chef::Resource
     resource_name :kitchen
@@ -33,18 +35,33 @@ module KitchenAcceptance
     property :kitchen_options, String, default: lazy { ENV["PROJECT_NAME"] ? "-c -l debug" : "-c" }
 
     action :run do
-      execute "bundle exec kitchen #{command}#{instances ? " #{instances}" : ""}#{kitchen_options ? " #{kitchen_options}" : ""}" do
-        cwd kitchen_dir
-        env({
-          "KITCHEN_DRIVER" => driver,
-          "KITCHEN_INSTANCES" => instances,
-          "KITCHEN_LOCAL_YAML" => ::File.expand_path("../../.kitchen.#{driver}.yml", __FILE__),
-          "KITCHEN_CHEF_PRODUCT" => chef_product,
-          "KITCHEN_CHEF_CHANNEL" => chef_channel,
-          "KITCHEN_CHEF_VERSION" => chef_version,
-          "ARTIFACTORY_USERNAME" => artifactory_username,
-          "ARTIFACTORY_PASSWORD" => artifactory_password
-        }.merge(new_resource.env))
+
+      ruby_block "copy_kitchen_logs_to_data_path" do
+        block do
+          cmd_env = {
+            "KITCHEN_DRIVER" => driver,
+            "KITCHEN_INSTANCES" => instances,
+            "KITCHEN_LOCAL_YAML" => ::File.expand_path("../../.kitchen.#{driver}.yml", __FILE__),
+            "KITCHEN_CHEF_PRODUCT" => chef_product,
+            "KITCHEN_CHEF_CHANNEL" => chef_channel,
+            "KITCHEN_CHEF_VERSION" => chef_version,
+            "ARTIFACTORY_USERNAME" => artifactory_username,
+            "ARTIFACTORY_PASSWORD" => artifactory_password
+          }.merge(new_resource.env)
+          suite = kitchen_dir.split("/").last
+          kitchen_log_path = ENV["WORKSPACE"] ? "#{ENV["WORKSPACE"]}/chef-acceptance-data/logs" : "#{kitchen_dir}/../.acceptance_data/logs/"
+
+          begin
+            shell_out!("bundle exec kitchen #{command}#{instances ? " #{instances}" : ""}#{kitchen_options ? " #{kitchen_options}" : ""}",
+                       env: cmd_env,
+                       timeout: 60 * 30,
+                       live_stream: STDOUT,
+                       cwd: kitchen_dir)
+          ensure
+            FileUtils.mkdir_p("#{kitchen_log_path}/#{suite}/#{command}")
+            FileUtils.cp_r("#{kitchen_dir}/.kitchen/logs/.", "#{kitchen_log_path}/#{suite}/#{command}")
+          end
+        end
       end
     end
   end
diff --git a/acceptance/Gemfile b/acceptance/Gemfile
index 185437e..83f9f7d 100644
--- a/acceptance/Gemfile
+++ b/acceptance/Gemfile
@@ -1,13 +1,13 @@
 source "https://rubygems.org"
 
 gem "chef-acceptance", github: "chef/chef-acceptance"
-gem "test-kitchen"
 gem "kitchen-ec2"
-gem "kitchen-inspec"
 gem "inspec"
 # Pinning to github for kitchen-vagrant because 0.19.0 incorrectly
 # puts in a box_url for bento when a vagrant box in atlas is specified
 gem "kitchen-vagrant"
 gem "windows_chef_zero"
-gem "winrm-fs"
-gem "berkshelf"
+gem "kitchen-inspec"
+gem "test-kitchen"
+gem "winrm-elevated"
+gem "berkshelf", "4.3.5"
diff --git a/acceptance/Gemfile.lock b/acceptance/Gemfile.lock
index 840e1a4..265f8ee 100644
--- a/acceptance/Gemfile.lock
+++ b/acceptance/Gemfile.lock
@@ -10,14 +10,14 @@ GEM
   remote: https://rubygems.org/
   specs:
     addressable (2.4.0)
-    artifactory (2.3.2)
-    aws-sdk (2.3.9)
-      aws-sdk-resources (= 2.3.9)
-    aws-sdk-core (2.3.9)
+    artifactory (2.5.0)
+    aws-sdk (2.6.1)
+      aws-sdk-resources (= 2.6.1)
+    aws-sdk-core (2.6.1)
       jmespath (~> 1.0)
-    aws-sdk-resources (2.3.9)
-      aws-sdk-core (= 2.3.9)
-    berkshelf (4.3.3)
+    aws-sdk-resources (2.6.1)
+      aws-sdk-core (= 2.6.1)
+    berkshelf (4.3.5)
       addressable (~> 2.3, >= 2.3.4)
       berkshelf-api-client (~> 2.0, >= 2.0.2)
       buff-config (~> 1.0)
@@ -29,6 +29,7 @@ GEM
       faraday (~> 0.9)
       httpclient (~> 2.7)
       minitar (~> 0.5, >= 0.5.4)
+      mixlib-archive (~> 0.1)
       octokit (~> 4.0)
       retryable (~> 2.0)
       ridley (~> 4.5)
@@ -52,51 +53,53 @@ GEM
     celluloid-io (0.16.2)
       celluloid (>= 0.16.0)
       nio4r (>= 1.1.0)
-    chef-config (12.10.24)
-      fuzzyurl (~> 0.8.0)
+    chef-config (12.14.60)
+      addressable
+      fuzzyurl
       mixlib-config (~> 2.0)
       mixlib-shellout (~> 2.0)
     cleanroom (1.0.0)
     coderay (1.1.1)
     diff-lcs (1.2.5)
-    docker-api (1.28.0)
+    docker-api (1.31.0)
       excon (>= 0.38.0)
       json
     erubis (2.7.0)
-    excon (0.49.0)
+    excon (0.52.0)
     faraday (0.9.2)
       multipart-post (>= 1.2, < 3)
-    ffi (1.9.10)
-    fuzzyurl (0.8.0)
+    ffi (1.9.14)
+    fuzzyurl (0.9.0)
     gssapi (1.2.0)
       ffi (>= 1.0.1)
     gyoku (1.3.1)
       builder (>= 2.1.2)
-    hashie (3.4.4)
+    hashie (3.4.6)
     hitimes (1.2.4)
     httpclient (2.7.2)
-    inspec (0.22.1)
+    inspec (0.34.1)
       hashie (~> 3.4)
-      json (~> 1.8)
+      json (>= 1.8, < 3.0)
       method_source (~> 0.8)
+      mixlib-log
+      parallel (~> 1.9)
       pry (~> 0)
-      r-train (~> 0.12)
       rainbow (~> 2)
       rspec (~> 3)
       rspec-its (~> 1.2)
       rubyzip (~> 1.1)
+      sslshake (~> 1)
       thor (~> 0.19)
-    jmespath (1.2.4)
-      json_pure (>= 1.8.1)
-    json (1.8.3)
-    json_pure (1.8.3)
-    kitchen-ec2 (1.0.0)
+      train (>= 0.19.0, < 1.0)
+    jmespath (1.3.1)
+    json (2.0.2)
+    kitchen-ec2 (1.2.0)
       aws-sdk (~> 2)
       excon
       multi_json
       retryable (~> 2.0)
       test-kitchen (~> 1.4, >= 1.4.1)
-    kitchen-inspec (0.14.0)
+    kitchen-inspec (0.15.1)
       inspec (>= 0.22.0, < 1.0.0)
       test-kitchen (~> 1.6)
     kitchen-vagrant (0.20.0)
@@ -107,48 +110,42 @@ GEM
       multi_json (~> 1.10)
     method_source (0.8.2)
     minitar (0.5.4)
-    mixlib-authentication (1.4.0)
+    mixlib-archive (0.2.0)
       mixlib-log
-      rspec-core (~> 3.2)
-      rspec-expectations (~> 3.2)
-      rspec-mocks (~> 3.2)
-    mixlib-config (2.2.1)
-    mixlib-install (1.0.12)
+    mixlib-authentication (1.4.1)
+      mixlib-log
+    mixlib-config (2.2.4)
+    mixlib-install (1.2.0)
       artifactory
       mixlib-shellout
       mixlib-versioning
-    mixlib-log (1.6.0)
-    mixlib-shellout (2.2.6)
+    mixlib-log (1.7.1)
+    mixlib-shellout (2.2.7)
     mixlib-versioning (1.1.0)
     molinillo (0.4.5)
     multi_json (1.12.1)
     multipart-post (2.0.0)
     net-scp (1.2.1)
       net-ssh (>= 2.6.5)
-    net-ssh (3.1.1)
+    net-ssh (3.2.0)
+    net-ssh-gateway (1.2.0)
+      net-ssh (>= 2.6.5)
     nio4r (1.2.1)
     nori (2.6.0)
     octokit (4.3.0)
       sawyer (~> 0.7.0, >= 0.5.3)
-    pry (0.10.3)
+    parallel (1.9.0)
+    pry (0.10.4)
       coderay (~> 1.1.0)
       method_source (~> 0.8.1)
       slop (~> 3.4)
-    r-train (0.12.1)
-      docker-api (~> 1.26)
-      json (~> 1.8)
-      mixlib-shellout (~> 2.0)
-      net-scp (~> 1.2)
-      net-ssh (>= 2.9, < 4.0)
-      winrm (~> 1.6)
-      winrm-fs (~> 0.3)
     rainbow (2.1.0)
-    retryable (2.0.3)
-    ridley (4.5.1)
+    retryable (2.0.4)
+    ridley (4.6.1)
       addressable
       buff-config (~> 1.0)
       buff-extensions (~> 1.0)
-      buff-ignore (~> 1.1)
+      buff-ignore (~> 1.1.1)
       buff-shell_out (~> 0.1)
       celluloid (~> 0.16.0)
       celluloid-io (~> 0.16.1)
@@ -162,23 +159,23 @@ GEM
       retryable (~> 2.0)
       semverse (~> 1.1)
       varia_model (~> 0.4.0)
-    rspec (3.4.0)
-      rspec-core (~> 3.4.0)
-      rspec-expectations (~> 3.4.0)
-      rspec-mocks (~> 3.4.0)
-    rspec-core (3.4.4)
-      rspec-support (~> 3.4.0)
-    rspec-expectations (3.4.0)
+    rspec (3.5.0)
+      rspec-core (~> 3.5.0)
+      rspec-expectations (~> 3.5.0)
+      rspec-mocks (~> 3.5.0)
+    rspec-core (3.5.3)
+      rspec-support (~> 3.5.0)
+    rspec-expectations (3.5.0)
       diff-lcs (>= 1.2.0, < 2.0)
-      rspec-support (~> 3.4.0)
+      rspec-support (~> 3.5.0)
     rspec-its (1.2.0)
       rspec-core (>= 3.0.0)
       rspec-expectations (>= 3.0.0)
-    rspec-mocks (3.4.1)
+    rspec-mocks (3.5.0)
       diff-lcs (>= 1.2.0, < 2.0)
-      rspec-support (~> 3.4.0)
-    rspec-support (3.4.1)
-    rubyntlm (0.6.0)
+      rspec-support (~> 3.5.0)
+    rspec-support (3.5.0)
+    rubyntlm (0.6.1)
     rubyzip (1.2.0)
     safe_yaml (1.0.4)
     sawyer (0.7.0)
@@ -189,40 +186,54 @@ GEM
     solve (2.0.3)
       molinillo (~> 0.4.2)
       semverse (~> 1.1)
-    test-kitchen (1.9.0)
-      mixlib-install (~> 1.0, >= 1.0.4)
+    sslshake (1.0.12)
+    test-kitchen (1.13.0)
+      mixlib-install (~> 1.2)
       mixlib-shellout (>= 1.2, < 3.0)
       net-scp (~> 1.1)
       net-ssh (>= 2.9, < 4.0)
+      net-ssh-gateway (~> 1.2.0)
       safe_yaml (~> 1.0)
       thor (~> 0.18)
     thor (0.19.1)
     timers (4.0.4)
       hitimes
+    train (0.19.1)
+      docker-api (~> 1.26)
+      json (>= 1.8, < 3.0)
+      mixlib-shellout (~> 2.0)
+      net-scp (~> 1.2)
+      net-ssh (>= 2.9, < 4.0)
+      winrm (~> 2.0)
+      winrm-fs (~> 1.0)
     varia_model (0.4.1)
       buff-extensions (~> 1.0)
       hashie (>= 2.0.2, < 4.0.0)
     windows_chef_zero (2.0.0)
       test-kitchen (>= 1.2.1)
-    winrm (1.8.1)
+    winrm (2.0.2)
       builder (>= 2.1.2)
+      erubis (~> 2.7)
       gssapi (~> 1.2)
       gyoku (~> 1.0)
       httpclient (~> 2.2, >= 2.2.0.2)
       logging (>= 1.6.1, < 3.0)
       nori (~> 2.0)
-      rubyntlm (~> 0.6.0)
-    winrm-fs (0.4.2)
+      rubyntlm (~> 0.6.0, >= 0.6.1)
+    winrm-elevated (1.0.0)
+      winrm (~> 2.0)
+      winrm-fs (~> 1.0)
+    winrm-fs (1.0.0)
       erubis (~> 2.7)
       logging (>= 1.6.1, < 3.0)
       rubyzip (~> 1.1)
-      winrm (~> 1.5)
+      winrm (~> 2.0)
 
 PLATFORMS
   ruby
 
 DEPENDENCIES
-  berkshelf
+  berkshelf (= 4.3.5)
   chef-acceptance!
   inspec
   kitchen-ec2
@@ -230,7 +241,7 @@ DEPENDENCIES
   kitchen-vagrant
   test-kitchen
   windows_chef_zero
-  winrm-fs
+  winrm-elevated
 
 BUNDLED WITH
    1.12.5
diff --git a/acceptance/basics/test/integration/helpers/serverspec/Gemfile b/acceptance/basics/test/integration/helpers/serverspec/Gemfile
new file mode 100644
index 0000000..b56d1e1
--- /dev/null
+++ b/acceptance/basics/test/integration/helpers/serverspec/Gemfile
@@ -0,0 +1,8 @@
+source "https://rubygems.org"
+
+# Until https://github.com/test-kitchen/busser-serverspec/pull/42 is merged and
+# released, we need to include rake and rspec-core in the Gemfile
+gem "rake"
+gem "rspec-core"
+gem "busser-serverspec"
+gem "serverspec"
diff --git a/acceptance/data-collector/Berksfile.lock b/acceptance/data-collector/Berksfile.lock
deleted file mode 100644
index 39f4ce3..0000000
--- a/acceptance/data-collector/Berksfile.lock
+++ /dev/null
@@ -1,6 +0,0 @@
-DEPENDENCIES
-  data-collector-test
-    path: .acceptance/data-collector-test
-
-GRAPH
-  data-collector-test (0.1.0)
diff --git a/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb b/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb
index be15b96..f9d365a 100644
--- a/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb
+++ b/acceptance/data-collector/test/integration/default/serverspec/default_spec.rb
@@ -110,6 +110,7 @@ shared_examples_for "run_converge.success payload check" do
         expanded_run_list
         message_type
         message_version
+        node
         node_name
         organization_name
         resources
@@ -150,6 +151,7 @@ shared_examples_for "run_converge.failure payload check" do
         expanded_run_list
         message_type
         message_version
+        node
         node_name
         organization_name
         resources
@@ -178,44 +180,6 @@ shared_examples_for "run_converge.failure payload check" do
   end
 end
 
-shared_examples_for "node-update payload check" do
-  describe "node update message" do
-    let(:required_fields) do
-      %w{
-        entity_name
-        entity_type
-        entity_uuid
-        id
-        message_type
-        message_version
-        organization_name
-        recorded_at
-        remote_hostname
-        requestor_name
-        requestor_type
-        run_id
-        service_hostname
-        source
-        task
-        user_agent
-      }
-    end
-    let(:optional_fields) { %{data} }
-
-    it "is not missing any required fields" do
-      payload = JSON.load(command("curl http://localhost:9292/cache/action").stdout)
-      missing_fields = required_fields.select { |key| !payload.key?(key) }
-      expect(missing_fields).to eq([])
-    end
-
-    it "does not have any extra fields" do
-      payload = JSON.load(command("curl http://localhost:9292/cache/action").stdout)
-      extra_fields = payload.keys.select { |key| !required_fields.include?(key) && !optional_fields.include?(key) }
-      expect(extra_fields).to eq([])
-    end
-  end
-end
-
 describe "CCR with no data collector URL configured" do
   include_examples "successful chef run", "chef-client -z -c /etc/chef/no-endpoint.rb"
   include_examples "counter checks", { "run_start" => nil, "run_converge.success" => nil, "run_converge.failure" => nil }
@@ -226,7 +190,6 @@ describe "CCR, local mode, config in solo mode" do
   include_examples "counter checks", { "run_start" => 1, "run_converge.success" => 1, "run_converge.failure" => nil }
   include_examples "run_start payload check"
   include_examples "run_converge.success payload check"
-  include_examples "node-update payload check"
 end
 
 describe "CCR, local mode, config in client mode" do
@@ -239,7 +202,6 @@ describe "CCR, local mode, config in both mode" do
   include_examples "counter checks", { "run_start" => 1, "run_converge.success" => 1, "run_converge.failure" => nil }
   include_examples "run_start payload check"
   include_examples "run_converge.success payload check"
-  include_examples "node-update payload check"
 end
 
 describe "CCR, local mode, config in solo mode, failed run" do
@@ -247,5 +209,4 @@ describe "CCR, local mode, config in solo mode, failed run" do
   include_examples "counter checks", { "run_start" => 1, "run_converge.success" => nil, "run_converge.failure" => 1 }
   include_examples "run_start payload check"
   include_examples "run_converge.failure payload check"
-  include_examples "node-update payload check"
 end
diff --git a/acceptance/data-collector/test/integration/helpers/serverspec/Gemfile b/acceptance/data-collector/test/integration/helpers/serverspec/Gemfile
new file mode 100644
index 0000000..b56d1e1
--- /dev/null
+++ b/acceptance/data-collector/test/integration/helpers/serverspec/Gemfile
@@ -0,0 +1,8 @@
+source "https://rubygems.org"
+
+# Until https://github.com/test-kitchen/busser-serverspec/pull/42 is merged and
+# released, we need to include rake and rspec-core in the Gemfile
+gem "rake"
+gem "rspec-core"
+gem "busser-serverspec"
+gem "serverspec"
diff --git a/acceptance/fips/test/integration/fips-integration/serverspec/Gemfile b/acceptance/fips/test/integration/fips-integration/serverspec/Gemfile
index 3921e6a..d297c43 100644
--- a/acceptance/fips/test/integration/fips-integration/serverspec/Gemfile
+++ b/acceptance/fips/test/integration/fips-integration/serverspec/Gemfile
@@ -1,3 +1,9 @@
 source "https://rubygems.org"
 
+# Until https://github.com/test-kitchen/busser-serverspec/pull/42 is merged and
+# released, we need to include rake and rspec-core in the Gemfile
+gem "rake"
+gem "rspec-core"
+gem "busser-serverspec"
+gem "serverspec"
 gem "mixlib-shellout"
diff --git a/acceptance/fips/test/integration/fips-unit-functional/serverspec/Gemfile b/acceptance/fips/test/integration/fips-unit-functional/serverspec/Gemfile
index 3921e6a..03c7a9e 100644
--- a/acceptance/fips/test/integration/fips-unit-functional/serverspec/Gemfile
+++ b/acceptance/fips/test/integration/fips-unit-functional/serverspec/Gemfile
@@ -1,3 +1,7 @@
 source "https://rubygems.org"
 
+# Until https://github.com/test-kitchen/busser-serverspec/pull/42 is merged and
+# released, we need to include rake and rspec-core in the Gemfile
+gem "rake"
+gem "rspec-core"
 gem "mixlib-shellout"
diff --git a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb b/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb
index 203ea98..73f5151 100644
--- a/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb
+++ b/acceptance/top-cookbooks/.acceptance/acceptance-cookbook/libraries/top_cookbooks.rb
@@ -3,6 +3,8 @@ class TopCookbooks < Chef::Resource
 
   property :command, String, name_property: true
 
+  # Disabling all windows tests until winrm issue is properly settled.
+  #
   action :run do
     cookbook_kitchen "#{command} docker" do
     end
@@ -33,8 +35,6 @@ class TopCookbooks < Chef::Resource
       repository "adamedx/winbox"
     end
 
-    # Temporarily disabling windows and chocolatey to eliminate 
-    # transient errors on the builders
     # cookbook_kitchen "#{command} windows" do
     # end
 
diff --git a/acceptance/trivial/.kitchen.yml b/acceptance/trivial/.kitchen.yml
index 1e0af03..0db67c4 100644
--- a/acceptance/trivial/.kitchen.yml
+++ b/acceptance/trivial/.kitchen.yml
@@ -3,5 +3,5 @@ verifier:
 
 suites:
   - name: chef-current-install
-    includes: [windows-2012r2]
+    includes: [ubuntu-14.04, windows-server-2012r2]
     run_list:
diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb
index e2d663a..e12f938 100644
--- a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb
+++ b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/destroy.rb
@@ -1 +1 @@
-kitchen "destroy"
+#kitchen "destroy"
diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb
index 5726c0e..cec9de4 100644
--- a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb
+++ b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/provision.rb
@@ -1 +1 @@
-kitchen "converge"
+#kitchen "converge"
diff --git a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb
index 05ac94c..52e3560 100644
--- a/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb
+++ b/acceptance/windows-service/.acceptance/acceptance-cookbook/recipes/verify.rb
@@ -1 +1 @@
-kitchen "verify"
+#kitchen "verify"
diff --git a/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb b/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb
index a791177..75383b6 100644
--- a/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb
+++ b/acceptance/windows-service/test/integration/chef-windows-service/inspec/chef_windows_service_spec.rb
@@ -13,7 +13,7 @@ describe service("chef-client") do
   it { should_not be_running }
 end
 
-describe command("chef-service-manager -a install") do
+describe command("/opscode/chef/bin/chef-service-manager.bat -a install") do
   its("exit_status") { should eq 0 }
   its(:stdout) { should match /Service 'chef-client' has successfully been installed./ }
 end
@@ -24,7 +24,7 @@ describe service("chef-client") do
   it { should_not be_running }
 end
 
-describe command("chef-service-manager -a start") do
+describe command("/opscode/chef/bin/chef-service-manager.bat -a start") do
   its("exit_status") { should eq 0 }
   its(:stdout) { should match /Service 'chef-client' is now 'running'/ }
 end
@@ -35,7 +35,7 @@ describe service("chef-client") do
   it { should be_running }
 end
 
-describe command("chef-service-manager -a stop") do
+describe command("/opscode/chef/bin/chef-service-manager.bat -a stop") do
   its("exit_status") { should eq 0 }
   its(:stdout) { should match /Service 'chef-client' is now 'stopped'/ }
 end
@@ -46,7 +46,7 @@ describe service("chef-client") do
   it { should_not be_running }
 end
 
-describe command("chef-service-manager -a uninstall") do
+describe command("/opscode/chef/bin/chef-service-manager.bat -a uninstall") do
   its("exit_status") { should eq 0 }
   its(:stdout) { should match /Service chef-client deleted/ }
 end
diff --git a/appveyor.yml b/appveyor.yml
index 7d89df1..831ecf6 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -6,9 +6,8 @@ platform:
 
 environment:
   matrix:
-    # 21-x64 is failing right now
-    #- ruby_version: "21-x64"
-    - ruby_version: "21"
+    - ruby_version: "23-x64"
+    - ruby_version: "23"
 
 clone_folder: c:\projects\chef
 clone_depth: 1
diff --git a/chef-config/Rakefile b/chef-config/Rakefile
index 151c275..fd6497a 100644
--- a/chef-config/Rakefile
+++ b/chef-config/Rakefile
@@ -1,13 +1,17 @@
-require "rspec/core/rake_task"
 require "chef-config/package_task"
 
-ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "ChefConfig") do |package|
+ChefConfig::PackageTask.new(File.expand_path("..", __FILE__), "ChefConfig", "chef-config") do |package|
   package.module_path = "chef-config"
 end
 
 task :default => :spec
 
-desc "Run standard specs"
-RSpec::Core::RakeTask.new(:spec) do |t|
-  t.pattern = FileList["spec/**/*_spec.rb"]
+begin
+  require "rspec/core/rake_task"
+  desc "Run standard specs"
+  RSpec::Core::RakeTask.new(:spec) do |t|
+    t.pattern = FileList["spec/**/*_spec.rb"]
+  end
+rescue LoadError
+  STDERR.puts "\n*** RSpec not available. (sudo) gem install rspec to run unit tests. ***\n\n"
 end
diff --git a/chef-config/chef-config.gemspec b/chef-config/chef-config.gemspec
index 3071121..9e40528 100644
--- a/chef-config/chef-config.gemspec
+++ b/chef-config/chef-config.gemspec
@@ -17,7 +17,8 @@ Gem::Specification.new do |spec|
 
   spec.add_dependency "mixlib-shellout", "~> 2.0"
   spec.add_dependency "mixlib-config", "~> 2.0"
-  spec.add_dependency "fuzzyurl", "~> 0.8.0"
+  spec.add_dependency "fuzzyurl"
+  spec.add_dependency "addressable"
 
   spec.add_development_dependency "rake", "~> 10.0"
 
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb
index 5684674..f2db54a 100644
--- a/chef-config/lib/chef-config/config.rb
+++ b/chef-config/lib/chef-config/config.rb
@@ -30,6 +30,7 @@ require "chef-config/mixin/fuzzy_hostname_matcher"
 
 require "mixlib/shellout"
 require "uri"
+require "addressable/uri"
 require "openssl"
 
 module ChefConfig
@@ -132,8 +133,8 @@ module ChefConfig
       until File.directory?(PathHelper.join(path, "cookbooks")) || File.directory?(PathHelper.join(path, "cookbook_artifacts"))
         new_path = File.expand_path("..", path)
         if new_path == path
-          ChefConfig.logger.warn("No cookbooks directory found at or above current directory.  Assuming #{Dir.pwd}.")
-          return Dir.pwd
+          ChefConfig.logger.warn("No cookbooks directory found at or above current directory.  Assuming #{cwd}.")
+          return cwd
         end
         path = new_path
       end
@@ -519,7 +520,16 @@ module ChefConfig
 
     # Set to true if Chef is to set OpenSSL to run in FIPS mode
     default(:fips) do
-      !ENV["CHEF_FIPS"].nil? || ChefConfig.fips?
+      # CHEF_FIPS is used in testing to override checking for system level
+      # enablement. There are 3 possible values that this variable may have:
+      # nil - no override and the system will be checked
+      # empty - FIPS is NOT enabled
+      # a non empty value - FIPS is enabled
+      if ENV["CHEF_FIPS"] == ""
+        false
+      else
+        !ENV["CHEF_FIPS"].nil? || ChefConfig.fips?
+      end
     end
 
     # Initialize openssl
@@ -649,8 +659,26 @@ module ChefConfig
       ENV.key?("CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS")
     end
 
+    # Whether the resource count should be updated for log resource
+    # on running chef-client
+    default :count_log_resource_updates, true
+
     # knife configuration data
     config_context :knife do
+      # XXX: none of these default values are applied to knife (and would create a backcompat
+      # break in knife if this bug was fixed since many of the defaults below are wrong).  this appears
+      # to be the start of an attempt to be able to use config_strict_mode true?  if so, this approach
+      # is fraught with peril because this namespace is used by every knife plugin in the wild and
+      # we would need to validate every cli option in every knife attribute out there and list them all here.
+      #
+      # based on the way that people may define `knife[:foobar] = "something"` for the knife-foobar
+      # gem plugin i'm pretty certain we can never turn on anything like config_string_mode since
+      # any config value may be a typo or it may be in some gem in some knife plugin we don't know about.
+      #
+      # we do still need to maintain at least one of these so that the knife config hash gets
+      # created.
+      #
+      # this whole situation is deeply unsatisfying.
       default :ssh_port, nil
       default :ssh_user, nil
       default :ssh_attribute, nil
@@ -782,6 +810,11 @@ module ChefConfig
     default :normal_attribute_whitelist, nil
     default :override_attribute_whitelist, nil
 
+    # Pull down all the rubygems versions from rubygems and cache them the first time we do a gem_package or
+    # chef_gem install.  This is memory-expensive and will grow without bounds, but will reduce network
+    # round trips.
+    default :rubygems_cache_enabled, false
+
     config_context :windows_service do
       # Set `watchdog_timeout` to the number of seconds to wait for a chef-client run
       # to finish
@@ -856,6 +889,13 @@ module ChefConfig
       export_no_proxy(no_proxy) if no_proxy
     end
 
+    # Character classes for Addressable
+    # See https://www.ietf.org/rfc/rfc3986.txt 3.2.1
+    # The user part may not have a : in it
+    USER = Addressable::URI::CharacterClasses::UNRESERVED + Addressable::URI::CharacterClasses::SUB_DELIMS
+    # The password part may have any valid USERINFO characters
+    PASSWORD = USER + "\\:"
+
     # Builds a proxy uri and exports it to the appropriate environment variables. Examples:
     #   http://username:password@hostname:port
     #   https://username@hostname:port
@@ -870,19 +910,17 @@ module ChefConfig
       path = "#{scheme}://#{path}" unless path.include?("://")
       # URI.split returns the following parts:
       # [scheme, userinfo, host, port, registry, path, opaque, query, fragment]
-      parts = URI.split(URI.encode(path))
-      # URI::Generic.build requires an integer for the port, but URI::split gives
-      # returns a string for the port.
-      parts[3] = parts[3].to_i if parts[3]
+      uri = Addressable::URI.encode(path, Addressable::URI)
+
       if user && !user.empty?
-        userinfo = URI.encode(URI.encode(user), "@:")
+        userinfo = Addressable::URI.encode_component(user, USER)
         if pass
-          userinfo << ":#{URI.encode(URI.encode(pass), '@:')}"
+          userinfo << ":#{Addressable::URI.encode_component(pass, PASSWORD)}"
         end
-        parts[1] = userinfo
+        uri.userinfo = userinfo
       end
 
-      path = URI::Generic.build(parts).to_s
+      path = uri.to_s
       ENV["#{scheme}_proxy".downcase] = path unless ENV["#{scheme}_proxy".downcase]
       ENV["#{scheme}_proxy".upcase] = path unless ENV["#{scheme}_proxy".upcase]
     end
diff --git a/chef-config/lib/chef-config/mixin/fuzzy_hostname_matcher.rb b/chef-config/lib/chef-config/mixin/fuzzy_hostname_matcher.rb
index c4d9185..6dd6788 100644
--- a/chef-config/lib/chef-config/mixin/fuzzy_hostname_matcher.rb
+++ b/chef-config/lib/chef-config/mixin/fuzzy_hostname_matcher.rb
@@ -21,9 +21,9 @@ module ChefConfig
     module FuzzyHostnameMatcher
 
       def fuzzy_hostname_match_any?(hostname, matches)
-        return matches.to_s.split(/\s*,\s*/).compact.any? {
-          |m| fuzzy_hostname_match?(hostname, m)
-        } if (hostname != nil) && (matches != nil)
+        return matches.to_s.split(/\s*,\s*/).compact.any? do |m|
+          fuzzy_hostname_match?(hostname, m)
+        end if (hostname != nil) && (matches != nil)
 
         false
       end
diff --git a/chef-config/lib/chef-config/package_task.rb b/chef-config/lib/chef-config/package_task.rb
index b984f60..de830c0 100644
--- a/chef-config/lib/chef-config/package_task.rb
+++ b/chef-config/lib/chef-config/package_task.rb
@@ -31,6 +31,10 @@ module ChefConfig
     # the top level module which contains VERSION and MODULE_ROOT.
     attr_accessor :module_name
 
+    # Name of the gem being built. This is used to find the lines to fix in
+    # Gemfile.lock.
+    attr_accessor :gem_name
+
     # Should the generated version.rb be in a class or module?  Default is false (module).
     attr_accessor :generate_version_class
 
@@ -55,15 +59,16 @@ module ChefConfig
     # Name of git remote used to push tags during a release.  Default is origin.
     attr_accessor :git_remote
 
-    def initialize(root_path = nil, module_name = nil)
-      init(root_path, module_name)
+    def initialize(root_path = nil, module_name = nil, gem_name = nil)
+      init(root_path, module_name, gem_name)
       yield self if block_given?
       define unless root_path.nil? || module_name.nil?
     end
 
-    def init(root_path, module_name)
+    def init(root_path, module_name, gem_name)
       @root_path = root_path
       @module_name = module_name
+      @gem_name = gem_name
       @component_paths = []
       @module_path = nil
       @package_dir = "pkg"
@@ -87,6 +92,10 @@ module ChefConfig
       File.join(chef_root_path, "VERSION")
     end
 
+    def gemfile_lock_path
+      File.join(root_path, "Gemfile.lock")
+    end
+
     def version
       IO.read(version_file_path).strip
     end
@@ -155,6 +164,26 @@ module ChefConfig
       namespace :version do
         desc 'Regenerate lib/#{@module_path}/version.rb from VERSION file'
         task :update => :update_components_versions do
+          update_version_rb
+          update_gemfile_lock
+        end
+
+        task :bump => %w{version:bump_patch version:update}
+
+        task :show do
+          puts version
+        end
+
+        # Add 1 to the current patch version in the VERSION file, and write it back out.
+        task :bump_patch do
+          current_version = version
+          new_version = current_version.sub(/^(\d+\.\d+\.)(\d+)/) { "#{$1}#{$2.to_i + 1}" }
+          puts "Updating version in #{version_rb_path} from #{current_version.chomp} to #{new_version.chomp}"
+          IO.write(version_file_path, new_version)
+        end
+
+        def update_version_rb # rubocop:disable Lint/NestedMethodDefinition
+          puts "Updating #{version_rb_path} to include version #{version} ..."
           contents = <<-VERSION_RB
 # Copyright:: Copyright 2010-2016, Chef Software, Inc.
 # License:: Apache License, Version 2.0
@@ -194,18 +223,15 @@ end
           IO.write(version_rb_path, contents)
         end
 
-        task :bump => %w{version:bump_patch version:update}
-
-        task :show do
-          puts version
-        end
-
-        # Add 1 to the current patch version in the VERSION file, and write it back out.
-        task :bump_patch do
-          current_version = version
-          new_version = current_version.sub(/^(\d+\.\d+\.)(\d+)/) { "#{$1}#{$2.to_i + 1}" }
-          puts "Updating version in #{version_rb_path} from #{current_version.chomp} to #{new_version.chomp}"
-          IO.write(version_file_path, new_version)
+        def update_gemfile_lock # rubocop:disable Lint/NestedMethodDefinition
+          if File.exist?(gemfile_lock_path)
+            puts "Updating #{gemfile_lock_path} to include version #{version} ..."
+            contents = IO.read(gemfile_lock_path)
+            contents.gsub!(/^\s*(chef|chef-config)\s*\((= )?\S+\)\s*$/) do |line|
+              line.gsub(/\((= )?\d+(\.\d+)+/) { "(#{$1}#{version}" }
+            end
+            IO.write(gemfile_lock_path, contents)
+          end
         end
       end
 
diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb
index 2962893..77a0001 100644
--- a/chef-config/lib/chef-config/version.rb
+++ b/chef-config/lib/chef-config/version.rb
@@ -21,7 +21,7 @@
 
 module ChefConfig
   CHEFCONFIG_ROOT = File.expand_path("../..", __FILE__)
-  VERSION = "12.11.18"
+  VERSION = "12.14.75"
 end
 
 #
diff --git a/chef-config/spec/unit/config_spec.rb b/chef-config/spec/unit/config_spec.rb
index f09dbb5..0ddb56c 100644
--- a/chef-config/spec/unit/config_spec.rb
+++ b/chef-config/spec/unit/config_spec.rb
@@ -186,6 +186,16 @@ RSpec.describe ChefConfig::Config do
             expect(ChefConfig::Config[:fips]).to eq(false)
           end
 
+          context "when ENV['CHEF_FIPS'] is empty" do
+            before do
+              ENV["CHEF_FIPS"] = ""
+            end
+
+            it "returns false" do
+              expect(ChefConfig::Config[:fips]).to eq(false)
+            end
+          end
+
           context "when ENV['CHEF_FIPS'] is set" do
             before do
               ENV["CHEF_FIPS"] = "1"
diff --git a/chef.gemspec b/chef.gemspec
index b88c899..367761f 100644
--- a/chef.gemspec
+++ b/chef.gemspec
@@ -13,14 +13,15 @@ Gem::Specification.new do |s|
   s.email = "adam at chef.io"
   s.homepage = "http://www.chef.io"
 
-  s.required_ruby_version = ">= 2.1.0"
+  s.required_ruby_version = ">= 2.2.0"
 
   s.add_dependency "chef-config", "= #{Chef::VERSION}"
 
-  s.add_dependency "mixlib-cli", "~> 1.4"
+  s.add_dependency "mixlib-cli", "~> 1.7"
   s.add_dependency "mixlib-log", "~> 1.3"
   s.add_dependency "mixlib-authentication", "~> 1.4"
   s.add_dependency "mixlib-shellout", "~> 2.0"
+  s.add_dependency "mixlib-archive", ">= 0.2.0"
   s.add_dependency "ohai", ">= 8.6.0.alpha.1", "< 9"
 
   s.add_dependency "ffi-yajl", "~> 2.2"
@@ -31,13 +32,14 @@ Gem::Specification.new do |s|
   s.add_dependency "erubis", "~> 2.7"
   s.add_dependency "diff-lcs", "~> 1.2", ">= 1.2.4"
 
-  s.add_dependency "chef-zero", "~> 4.5"
+  s.add_dependency "chef-zero", ">= 4.8"
 
   s.add_dependency "plist", "~> 3.2"
   s.add_dependency "iniparse", "~> 1.4"
+  s.add_dependency "addressable"
 
   # Audit mode requires these, so they are non-developmental dependencies now
-  %w{rspec-core rspec-expectations rspec-mocks}.each { |gem| s.add_dependency gem, "~> 3.4" }
+  %w{rspec-core rspec-expectations rspec-mocks}.each { |gem| s.add_dependency gem, "~> 3.5" }
   s.add_dependency "rspec_junit_formatter", "~> 0.2.0"
   s.add_dependency "serverspec", "~> 2.7"
   s.add_dependency "specinfra", "~> 2.10"
diff --git a/ci/bundle_install.sh b/ci/bundle_install.sh
index 519b0e4..6c6d76d 100755
--- a/ci/bundle_install.sh
+++ b/ci/bundle_install.sh
@@ -4,5 +4,6 @@ set -evx
 
 gem environment
 bundler_version=$(grep bundler omnibus_overrides.rb | cut -d'"' -f2)
-gem install bundler -v $bundler_version --user-install
+gem install bundler -v $bundler_version --user-install --conservative
+export BUNDLE_WITHOUT=default:omnibus_package:test:pry:integration:docgen:maintenance:changelog:travis:aix:bsd:linux:mac_os_x:solaris:windows
 bundle _${bundler_version}_ install
diff --git a/ci/dependency_update.sh b/ci/dependency_update.sh
index 3ab0c61..9588652 100755
--- a/ci/dependency_update.sh
+++ b/ci/dependency_update.sh
@@ -5,3 +5,5 @@ set -evx
 . ci/bundle_install.sh
 
 bundle exec rake dependencies
+
+git checkout .bundle/config
diff --git a/ci/version_bump.sh b/ci/version_bump.sh
index bd365c7..dd53ceb 100755
--- a/ci/version_bump.sh
+++ b/ci/version_bump.sh
@@ -5,3 +5,5 @@ set -evx
 . ci/bundle_install.sh
 
 bundle exec rake version:bump
+
+git checkout .bundle/config
diff --git a/ci/version_show.sh b/ci/version_show.sh
index c8917f2..5348f6f 100755
--- a/ci/version_show.sh
+++ b/ci/version_show.sh
@@ -1,7 +1,3 @@
 #!/bin/sh
 
-set -evx
-
-. ci/bundle_install.sh
-
-bundle exec rake version:show
+cat VERSION
diff --git a/kitchen-tests/.kitchen.travis.yml b/kitchen-tests/.kitchen.travis.yml
index 8bc41ad..9c5854d 100644
--- a/kitchen-tests/.kitchen.travis.yml
+++ b/kitchen-tests/.kitchen.travis.yml
@@ -10,7 +10,7 @@ transport:
 provisioner:
   name: chef_github
   root_path: /opt/kitchen
-  chef_version: latest
+  require_chef_omnibus: latest
   chef_omnibus_url: "https://omnitruck.chef.io/install.sh"
   chef_omnibus_install_options: "-c current"
   github_owner: "chef"
@@ -24,6 +24,7 @@ provisioner:
 
 verifier:
   name: inspec
+  format: progress
 
 platforms:
 - name: debian-7
@@ -32,7 +33,7 @@ platforms:
     pid_one_command: /sbin/init
     intermediate_instructions:
       - RUN /usr/bin/apt-get update
-      - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools
+      - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools wget ca-certificates
       - RUN /bin/mkdir /var/run/sshd
 
 - name: debian-8
@@ -41,7 +42,7 @@ platforms:
     pid_one_command: /bin/systemd
     intermediate_instructions:
       - RUN /usr/bin/apt-get update
-      - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools
+      - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools wget ca-certificates
 
 - name: centos-5
   driver:
@@ -50,7 +51,7 @@ platforms:
     run_command: /sbin/init
     intermediate_instructions:
       - RUN yum clean all
-      - RUN yum install -y which initscripts net-tools sudo
+      - RUN yum install -y which initscripts net-tools sudo wget
       - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults    !requiretty/g" /etc/sudoers
 
 - name: centos-6
@@ -59,7 +60,7 @@ platforms:
     run_command: /sbin/init
     intermediate_instructions:
       - RUN yum clean all
-      - RUN yum -y install which initscripts net-tools sudo
+      - RUN yum -y install which initscripts net-tools sudo wget
       - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults    !requiretty/g" /etc/sudoers
 
 - name: centos-7
@@ -68,7 +69,7 @@ platforms:
     pid_one_command: /usr/lib/systemd/systemd
     intermediate_instructions:
       - RUN yum clean all
-      - RUN yum -y install which initscripts net-tools sudo
+      - RUN yum -y install which initscripts net-tools sudo wget
       - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults    !requiretty/g" /etc/sudoers
 
 - name: fedora-23
@@ -76,7 +77,7 @@ platforms:
     image: fedora:23
     pid_one_command: /usr/lib/systemd/systemd
     intermediate_instructions:
-      - RUN dnf -y install yum which initscripts net-tools sudo
+      - RUN dnf -y install yum which initscripts rpm-build zlib-devel net-tools sudo wget
       - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults    !requiretty/g" /etc/sudoers
 
 - name: ubuntu-12.04
@@ -85,7 +86,7 @@ platforms:
     pid_one_command: /sbin/init
     intermediate_instructions:
       - RUN /usr/bin/apt-get update
-      - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools
+      - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools wget ca-certificates
 
 - name: ubuntu-14.04
   driver:
@@ -93,7 +94,7 @@ platforms:
     pid_one_command: /sbin/init
     intermediate_instructions:
       - RUN /usr/bin/apt-get update
-      - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools
+      - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools wget ca-certificates
 
 - name: ubuntu-16.04
   driver:
@@ -101,7 +102,7 @@ platforms:
     pid_one_command: /bin/systemd
     intermediate_instructions:
       - RUN /usr/bin/apt-get update
-      - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools
+      - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools wget ca-certificates
 
 - name: opensuse-13.2
   driver:
diff --git a/kitchen-tests/.kitchen.yml b/kitchen-tests/.kitchen.yml
index c02ea55..3148576 100644
--- a/kitchen-tests/.kitchen.yml
+++ b/kitchen-tests/.kitchen.yml
@@ -7,6 +7,7 @@ driver:
 
 verifier:
   name: inspec
+  format: progress
 
 provisioner:
   name: chef_github
diff --git a/kitchen-tests/Berksfile b/kitchen-tests/Berksfile
index 31e49b3..407b685 100644
--- a/kitchen-tests/Berksfile
+++ b/kitchen-tests/Berksfile
@@ -5,4 +5,4 @@ cookbook "base", path: "cookbooks/base"
 
 cookbook "php", "~> 1.5.0"
 
-cookbook "resolver", github: "chef-cookbooks/resolver", branch: "lcg/docker"
+cookbook "resolver", github: "chef-cookbooks/resolver"
diff --git a/kitchen-tests/Berksfile.lock b/kitchen-tests/Berksfile.lock
index f4a9de8..16d32a3 100644
--- a/kitchen-tests/Berksfile.lock
+++ b/kitchen-tests/Berksfile.lock
@@ -3,16 +3,16 @@ DEPENDENCIES
     path: cookbooks/base
   php (~> 1.5.0)
   resolver
-    git: git://github.com/chef-cookbooks/resolver.git
-    revision: dd65ab8e2346cc0739c13682c74868f5b939b06a
-    branch: lcg/docker
+    git: https://github.com/chef-cookbooks/resolver.git
+    revision: 8bf9034dabc47d29a07870e4059c32114f2c820a
   webapp
     path: cookbooks/webapp
 
 GRAPH
   apache2 (3.2.2)
-  apt (3.0.0)
-  aws (3.3.3)
+  apt (4.0.2)
+    compat_resource (>= 12.10)
+  aws (3.4.1)
     ohai (>= 2.1.0)
   base (0.1.0)
     apt (>= 0.0.0)
@@ -29,19 +29,18 @@ GRAPH
     sudo (>= 0.0.0)
     ubuntu (>= 0.0.0)
     users (>= 0.0.0)
-    yum-epel (>= 0.0.0)
-  build-essential (4.0.0)
-    mingw (>= 0.0.0)
+  build-essential (6.0.5)
+    compat_resource (>= 12.14)
+    mingw (>= 1.1)
     seven_zip (>= 0.0.0)
-  chef-client (4.5.2)
+  chef-client (5.0.0)
     cron (>= 1.7.0)
     logrotate (>= 1.9.0)
-    windows (>= 1.39.0)
-  chef-sugar (3.3.0)
-  chef_handler (1.4.0)
+    windows (>= 1.42.0)
+  chef-sugar (3.4.0)
   chef_hostname (0.4.1)
     compat_resource (>= 0.0.0)
-  compat_resource (12.10.4)
+  compat_resource (12.14.2)
   cron (1.7.6)
   database (2.3.1)
     aws (>= 0.0.0)
@@ -49,11 +48,12 @@ GRAPH
     mysql-chef_gem (~> 0.0)
     postgresql (>= 1.0.0)
     xfs (>= 0.0.0)
-  iis (4.1.7)
+  iis (5.0.0)
     windows (>= 1.34.6)
   iptables (2.2.0)
-  logrotate (1.9.2)
-  mingw (1.0.0)
+  logrotate (2.1.0)
+    compat_resource (>= 0.0.0)
+  mingw (1.2.4)
     compat_resource (>= 0.0.0)
     seven_zip (>= 0.0.0)
   multipackage (3.0.28)
@@ -63,12 +63,12 @@ GRAPH
   mysql-chef_gem (0.0.5)
     build-essential (>= 0.0.0)
     mysql (>= 0.0.0)
-  nscd (4.0.0)
+  nscd (4.1.0)
     compat_resource (>= 0.0.0)
-  ntp (2.0.0)
+  ntp (3.0.0)
     windows (>= 1.38.0)
-  ohai (4.0.2)
-    compat_resource (>= 12.9.0)
+  ohai (4.2.1)
+    compat_resource (>= 12.14)
   openssh (2.0.0)
     iptables (>= 1.0)
   openssl (4.4.0)
@@ -86,25 +86,23 @@ GRAPH
     openssl (~> 4.0)
   resolver (1.3.1)
   selinux (0.9.0)
-  seven_zip (2.0.1)
+  seven_zip (2.0.2)
     windows (>= 1.2.2)
-  sudo (2.9.0)
-  ubuntu (1.2.0)
+  sudo (3.0.0)
+  ubuntu (2.0.0)
     apt (>= 0.0.0)
-  users (2.0.3)
+  users (3.0.0)
   webapp (0.1.0)
     apache2 (~> 3.2.2)
     database (~> 2.3.1)
     mysql (~> 5.6.3)
     php (~> 1.5.0)
-  windows (1.41.0)
-    chef_handler (>= 0.0.0)
+  windows (2.0.2)
   xfs (2.0.1)
-  xml (2.0.0)
+  xml (3.0.0)
     build-essential (>= 0.0.0)
-    chef-sugar (>= 0.0.0)
-  yum (3.10.0)
-  yum-epel (0.7.0)
-    yum (>= 3.6.3)
-  yum-mysql-community (0.2.0)
+  yum (4.0.0)
+  yum-epel (1.0.1)
+    yum (>= 3.6)
+  yum-mysql-community (1.0.0)
     yum (>= 3.2)
diff --git a/kitchen-tests/Gemfile.lock b/kitchen-tests/Gemfile.lock
index 8ab7fbe..36571ec 100644
--- a/kitchen-tests/Gemfile.lock
+++ b/kitchen-tests/Gemfile.lock
@@ -2,39 +2,38 @@ GEM
   remote: https://rubygems.org/
   specs:
     addressable (2.4.0)
-    artifactory (2.3.2)
-    aws-sdk (2.3.9)
-      aws-sdk-resources (= 2.3.9)
-    aws-sdk-core (2.3.9)
+    artifactory (2.3.3)
+    aws-sdk (2.5.11)
+      aws-sdk-resources (= 2.5.11)
+    aws-sdk-core (2.5.11)
       jmespath (~> 1.0)
-    aws-sdk-resources (2.3.9)
-      aws-sdk-core (= 2.3.9)
-    berkshelf (4.3.3)
+    aws-sdk-resources (2.5.11)
+      aws-sdk-core (= 2.5.11)
+    berkshelf (5.0.0)
       addressable (~> 2.3, >= 2.3.4)
-      berkshelf-api-client (~> 2.0, >= 2.0.2)
-      buff-config (~> 1.0)
-      buff-extensions (~> 1.0)
+      berkshelf-api-client (>= 2.0.2, < 4.0)
+      buff-config (~> 2.0)
+      buff-extensions (~> 2.0)
       buff-shell_out (~> 0.1)
-      celluloid (= 0.16.0)
-      celluloid-io (~> 0.16.1)
       cleanroom (~> 1.0)
       faraday (~> 0.9)
       httpclient (~> 2.7)
       minitar (~> 0.5, >= 0.5.4)
+      mixlib-archive (~> 0.1)
       octokit (~> 4.0)
       retryable (~> 2.0)
-      ridley (~> 4.5)
-      solve (~> 2.0)
+      ridley (~> 5.0)
+      solve (> 2.0, < 4.0)
       thor (~> 0.19)
-    berkshelf-api-client (2.0.2)
-      faraday (~> 0.9.1)
-      httpclient (~> 2.7.0)
-      ridley (~> 4.5)
-    buff-config (1.0.1)
-      buff-extensions (~> 1.0)
-      varia_model (~> 0.4)
-    buff-extensions (1.0.0)
-    buff-ignore (1.1.1)
+    berkshelf-api-client (3.0.0)
+      faraday (~> 0.9)
+      httpclient (~> 2.7)
+      ridley (>= 4.5, < 6.0)
+    buff-config (2.0.0)
+      buff-extensions (~> 2.0)
+      varia_model (~> 0.6)
+    buff-extensions (2.0.0)
+    buff-ignore (1.2.0)
     buff-ruby_engine (0.1.0)
     buff-shell_out (0.2.0)
       buff-ruby_engine (~> 0.1.0)
@@ -44,22 +43,23 @@ GEM
     celluloid-io (0.16.2)
       celluloid (>= 0.16.0)
       nio4r (>= 1.1.0)
-    chef-config (12.10.24)
-      fuzzyurl (~> 0.8.0)
+    chef-config (12.13.37)
+      fuzzyurl
       mixlib-config (~> 2.0)
       mixlib-shellout (~> 2.0)
     cleanroom (1.0.0)
     coderay (1.1.1)
     diff-lcs (1.2.5)
-    docker-api (1.26.2)
+    docker-api (1.31.0)
       excon (>= 0.38.0)
       json
     erubis (2.7.0)
-    excon (0.49.0)
+    excon (0.52.0)
     faraday (0.9.2)
       multipart-post (>= 1.2, < 3)
-    ffi (1.9.10)
-    fuzzyurl (0.8.0)
+    ffi (1.9.14)
+    ffi (1.9.14-x86-mingw32)
+    fuzzyurl (0.9.0)
     gssapi (1.2.0)
       ffi (>= 1.0.1)
     gyoku (1.3.1)
@@ -67,33 +67,34 @@ GEM
     hashie (3.4.4)
     hitimes (1.2.4)
     hitimes (1.2.4-x86-mingw32)
-    httpclient (2.7.2)
-    inspec (0.22.1)
+    httpclient (2.8.2.4)
+    inspec (0.34.0)
       hashie (~> 3.4)
-      json (~> 1.8)
+      json (>= 1.8, < 3.0)
       method_source (~> 0.8)
+      mixlib-log
+      parallel (~> 1.9)
       pry (~> 0)
-      r-train (~> 0.12)
       rainbow (~> 2)
       rspec (~> 3)
       rspec-its (~> 1.2)
       rubyzip (~> 1.1)
+      sslshake (~> 1)
       thor (~> 0.19)
-    jmespath (1.2.4)
-      json_pure (>= 1.8.1)
-    json (1.8.3)
-    json_pure (1.8.3)
+      train (>= 0.19.0, < 1.0)
+    jmespath (1.3.1)
+    json (2.0.2)
     kitchen-appbundle-updater (0.1.2)
-    kitchen-dokken (0.0.29)
-      docker-api (~> 1.26.2)
+    kitchen-dokken (1.0.0)
+      docker-api (~> 1.29)
       test-kitchen (~> 1.5)
-    kitchen-ec2 (1.0.0)
+    kitchen-ec2 (1.1.0)
       aws-sdk (~> 2)
       excon
       multi_json
       retryable (~> 2.0)
       test-kitchen (~> 1.4, >= 1.4.1)
-    kitchen-inspec (0.14.0)
+    kitchen-inspec (0.15.1)
       inspec (>= 0.22.0, < 1.0.0)
       test-kitchen (~> 1.6)
     kitchen-vagrant (0.20.0)
@@ -104,51 +105,45 @@ GEM
       multi_json (~> 1.10)
     method_source (0.8.2)
     minitar (0.5.4)
-    mixlib-authentication (1.4.0)
+    mixlib-archive (0.2.0)
       mixlib-log
-      rspec-core (~> 3.2)
-      rspec-expectations (~> 3.2)
-      rspec-mocks (~> 3.2)
-    mixlib-config (2.2.1)
-    mixlib-install (1.0.12)
+    mixlib-authentication (1.4.1)
+      mixlib-log
+    mixlib-config (2.2.4)
+    mixlib-install (1.1.0)
       artifactory
       mixlib-shellout
       mixlib-versioning
-    mixlib-log (1.6.0)
-    mixlib-shellout (2.2.6)
-    mixlib-shellout (2.2.6-universal-mingw32)
+    mixlib-log (1.7.1)
+    mixlib-shellout (2.2.7)
+    mixlib-shellout (2.2.7-universal-mingw32)
       win32-process (~> 0.8.2)
       wmi-lite (~> 1.0)
     mixlib-versioning (1.1.0)
-    molinillo (0.4.5)
+    molinillo (0.5.0)
     multi_json (1.12.1)
     multipart-post (2.0.0)
     net-scp (1.2.1)
       net-ssh (>= 2.6.5)
-    net-ssh (3.1.1)
+    net-ssh (3.2.0)
+    net-ssh-gateway (1.2.0)
+      net-ssh (>= 2.6.5)
     nio4r (1.2.1)
     nori (2.6.0)
     octokit (4.3.0)
       sawyer (~> 0.7.0, >= 0.5.3)
-    pry (0.10.3)
+    parallel (1.9.0)
+    pry (0.10.4)
       coderay (~> 1.1.0)
       method_source (~> 0.8.1)
       slop (~> 3.4)
-    r-train (0.12.1)
-      docker-api (~> 1.26)
-      json (~> 1.8)
-      mixlib-shellout (~> 2.0)
-      net-scp (~> 1.2)
-      net-ssh (>= 2.9, < 4.0)
-      winrm (~> 1.6)
-      winrm-fs (~> 0.3)
     rainbow (2.1.0)
-    retryable (2.0.3)
-    ridley (4.5.1)
+    retryable (2.0.4)
+    ridley (5.0.0)
       addressable
-      buff-config (~> 1.0)
-      buff-extensions (~> 1.0)
-      buff-ignore (~> 1.1)
+      buff-config (~> 2.0)
+      buff-extensions (~> 2.0)
+      buff-ignore (~> 1.2)
       buff-shell_out (~> 0.1)
       celluloid (~> 0.16.0)
       celluloid-io (~> 0.16.1)
@@ -160,64 +155,75 @@ GEM
       json (>= 1.7.7)
       mixlib-authentication (>= 1.3.0)
       retryable (~> 2.0)
-      semverse (~> 1.1)
-      varia_model (~> 0.4.0)
-    rspec (3.4.0)
-      rspec-core (~> 3.4.0)
-      rspec-expectations (~> 3.4.0)
-      rspec-mocks (~> 3.4.0)
-    rspec-core (3.4.4)
-      rspec-support (~> 3.4.0)
-    rspec-expectations (3.4.0)
+      semverse (~> 2.0)
+      varia_model (~> 0.6)
+    rspec (3.5.0)
+      rspec-core (~> 3.5.0)
+      rspec-expectations (~> 3.5.0)
+      rspec-mocks (~> 3.5.0)
+    rspec-core (3.5.3)
+      rspec-support (~> 3.5.0)
+    rspec-expectations (3.5.0)
       diff-lcs (>= 1.2.0, < 2.0)
-      rspec-support (~> 3.4.0)
+      rspec-support (~> 3.5.0)
     rspec-its (1.2.0)
       rspec-core (>= 3.0.0)
       rspec-expectations (>= 3.0.0)
-    rspec-mocks (3.4.1)
+    rspec-mocks (3.5.0)
       diff-lcs (>= 1.2.0, < 2.0)
-      rspec-support (~> 3.4.0)
-    rspec-support (3.4.1)
+      rspec-support (~> 3.5.0)
+    rspec-support (3.5.0)
     rubyntlm (0.6.0)
     rubyzip (1.2.0)
     safe_yaml (1.0.4)
     sawyer (0.7.0)
       addressable (>= 2.3.5, < 2.5)
       faraday (~> 0.8, < 0.10)
-    semverse (1.2.1)
+    semverse (2.0.0)
     slop (3.6.0)
-    solve (2.0.3)
-      molinillo (~> 0.4.2)
-      semverse (~> 1.1)
-    test-kitchen (1.9.0)
+    solve (3.0.1)
+      molinillo (~> 0.4)
+      semverse (>= 1.1, < 3.0)
+    sslshake (1.0.12)
+    test-kitchen (1.12.0)
       mixlib-install (~> 1.0, >= 1.0.4)
       mixlib-shellout (>= 1.2, < 3.0)
       net-scp (~> 1.1)
       net-ssh (>= 2.9, < 4.0)
+      net-ssh-gateway (~> 1.2.0)
       safe_yaml (~> 1.0)
       thor (~> 0.18)
     thor (0.19.1)
     timers (4.0.4)
       hitimes
+    train (0.19.0)
+      docker-api (~> 1.26)
+      json (>= 1.8, < 3.0)
+      mixlib-shellout (~> 2.0)
+      net-scp (~> 1.2)
+      net-ssh (>= 2.9, < 4.0)
+      winrm (~> 2.0)
+      winrm-fs (~> 1.0)
     vagrant-wrapper (2.0.3)
-    varia_model (0.4.1)
-      buff-extensions (~> 1.0)
+    varia_model (0.6.0)
+      buff-extensions (~> 2.0)
       hashie (>= 2.0.2, < 4.0.0)
     win32-process (0.8.3)
       ffi (>= 1.0.0)
-    winrm (1.8.1)
+    winrm (2.0.1)
       builder (>= 2.1.2)
+      erubis (~> 2.7)
       gssapi (~> 1.2)
       gyoku (~> 1.0)
       httpclient (~> 2.2, >= 2.2.0.2)
       logging (>= 1.6.1, < 3.0)
       nori (~> 2.0)
       rubyntlm (~> 0.6.0)
-    winrm-fs (0.4.2)
+    winrm-fs (1.0.0)
       erubis (~> 2.7)
       logging (>= 1.6.1, < 3.0)
       rubyzip (~> 1.1)
-      winrm (~> 1.5)
+      winrm (~> 2.0)
     wmi-lite (1.0.0)
 
 PLATFORMS
diff --git a/kitchen-tests/cookbooks/base/metadata.rb b/kitchen-tests/cookbooks/base/metadata.rb
index 3811fe9..32ea039 100644
--- a/kitchen-tests/cookbooks/base/metadata.rb
+++ b/kitchen-tests/cookbooks/base/metadata.rb
@@ -22,4 +22,3 @@ depends          "selinux"
 depends          "sudo"
 depends          "ubuntu"
 depends          "users"
-depends          "yum-epel"
diff --git a/kitchen-tests/cookbooks/base/recipes/default.rb b/kitchen-tests/cookbooks/base/recipes/default.rb
index 053a689..397d50c 100644
--- a/kitchen-tests/cookbooks/base/recipes/default.rb
+++ b/kitchen-tests/cookbooks/base/recipes/default.rb
@@ -16,13 +16,19 @@ if %w{rhel fedora}.include?(node["platform_family"])
   include_recipe "selinux::disabled"
 end
 
-if node["platform_family"] == "rhel"
-  include_recipe "yum-epel"
+yum_repository "epel" do
+  enabled true
+  description "Extra Packages for Enterprise Linux #{node['platform_version'].to_i} - $basearch"
+  failovermethod "priority"
+  gpgkey "https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-#{node['platform_version'].to_i}"
+  gpgcheck true
+  mirrorlist "https://mirrors.fedoraproject.org/metalink?repo=epel-#{node['platform_version'].to_i}&arch=$basearch"
+  only_if { node["platform_family"] == "rhel" }
 end
 
 include_recipe "build-essential"
 
-include_recipe "#{cookbook_name}::packages"
+include_recipe "::packages"
 
 include_recipe "ntp"
 
@@ -44,3 +50,5 @@ include_recipe "openssh"
 include_recipe "nscd"
 
 include_recipe "logrotate"
+
+include_recipe "::tests"
diff --git a/kitchen-tests/cookbooks/base/recipes/packages.rb b/kitchen-tests/cookbooks/base/recipes/packages.rb
index c3a552b..1ca8d81 100644
--- a/kitchen-tests/cookbooks/base/recipes/packages.rb
+++ b/kitchen-tests/cookbooks/base/recipes/packages.rb
@@ -7,3 +7,11 @@ pkgs = %w{lsof tcpdump strace zsh dmidecode ltrace bc curl wget telnet subversio
 pkgs.each do |pkg|
   multipackage pkgs
 end
+
+gems = %w{fpm aws-sdk}
+
+gems.each do |gem|
+  chef_gem gem do
+    compile_time false
+  end
+end
diff --git a/kitchen-tests/cookbooks/base/recipes/tests.rb b/kitchen-tests/cookbooks/base/recipes/tests.rb
new file mode 100644
index 0000000..9d9d813
--- /dev/null
+++ b/kitchen-tests/cookbooks/base/recipes/tests.rb
@@ -0,0 +1,21 @@
+#
+# Cookbook Name:: webapp
+# Recipe:: default
+#
+# Copyright (C) 2014
+#
+
+#
+# this file is for random tests to check specific chef-client internal functionality
+#
+
+file "/tmp/chef-test-ümlauts" do
+  content "testing UTF-8 char in the filename"
+end
+
+# this caught a regression in 12.14.70 before it was released when i
+# ran it in lamont-ci, so added the test here so everyone else other than
+# me gets coverage for this as well.
+file "/tmp/chef-test-\xFDmlaut" do
+  content "testing illegal UTF-8 char in the filename"
+end
diff --git a/lib/chef/application.rb b/lib/chef/application.rb
index f8df71f..f9735a3 100644
--- a/lib/chef/application.rb
+++ b/lib/chef/application.rb
@@ -28,6 +28,7 @@ require "mixlib/cli"
 require "tmpdir"
 require "rbconfig"
 require "chef/application/exit_code"
+require "yaml"
 
 class Chef
   class Application
@@ -108,7 +109,22 @@ class Chef
         config_content = config_fetcher.read_config
         apply_config(config_content, config[:config_file])
       end
+      extra_config_options = config.delete(:config_option)
       Chef::Config.merge!(config)
+      if extra_config_options
+        extra_parsed_options = extra_config_options.inject({}) do |memo, option|
+          # Sanity check value.
+          Chef::Application.fatal!("Unparsable config option #{option.inspect}") if option.empty? || !option.include?("=")
+          # Split including whitespace if someone does truly odd like
+          # --config-option "foo = bar"
+          key, value = option.split(/\s*=\s*/, 2)
+          # Call to_sym because Chef::Config expects only symbol keys. Also
+          # runs a simple parse on the string for some common types.
+          memo[key.to_sym] = YAML.safe_load(value)
+          memo
+        end
+        Chef::Config.merge!(extra_parsed_options)
+      end
     end
 
     def set_specific_recipes
@@ -332,6 +348,13 @@ class Chef
     class << self
       def debug_stacktrace(e)
         message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
+
+        cause = e.cause if e.respond_to?(:cause)
+        while cause != nil
+          message << "\n\n>>>> Caused by #{cause.class}: #{cause}\n#{cause.backtrace.join("\n")}"
+          cause = cause.respond_to?(:cause) ? cause.cause : nil
+        end
+
         chef_stacktrace_out = "Generated at #{Time.now}\n"
         chef_stacktrace_out += message
 
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index 77c86ad..cbaa494 100644
--- a/lib/chef/application/client.rb
+++ b/lib/chef/application/client.rb
@@ -27,6 +27,7 @@ require "chef/handler/error_report"
 require "chef/workstation_config_loader"
 require "chef/mixin/shell_out"
 require "chef-config/mixin/dot_d"
+require "mixlib/archive"
 
 class Chef::Application::Client < Chef::Application
   include Chef::Mixin::ShellOut
@@ -40,6 +41,14 @@ class Chef::Application::Client < Chef::Application
     :long  => "--config CONFIG",
     :description => "The configuration file to use"
 
+  option :config_option,
+    :long         => "--config-option OPTION=VALUE",
+    :description  => "Override a single configuration option",
+    :proc         => lambda { |option, existing|
+      (existing ||= []) << option
+      existing
+    }
+
   option :formatter,
     :short        => "-F FORMATTER",
     :long         => "--format FORMATTER",
@@ -195,9 +204,9 @@ class Chef::Application::Client < Chef::Application
     :description  => "Replace current run list with specified items for a single run",
     :proc         => lambda {|items|
       items = items.split(",")
-      items.compact.map {|item|
+      items.compact.map do |item|
         Chef::RunList::RunListItem.new(item)
-      }
+      end
     }
 
   option :runlist,
@@ -206,9 +215,9 @@ class Chef::Application::Client < Chef::Application
     :description  => "Permanently replace current run list with specified items",
     :proc         => lambda {|items|
       items = items.split(",")
-      items.compact.map {|item|
+      items.compact.map do |item|
         Chef::RunList::RunListItem.new(item)
-      }
+      end
     }
   option :why_run,
     :short        => "-W",
@@ -334,8 +343,7 @@ class Chef::Application::Client < Chef::Application
         FileUtils.mkdir_p(Chef::Config.chef_repo_path)
         tarball_path = File.join(Chef::Config.chef_repo_path, "recipes.tgz")
         fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path)
-        result = shell_out!("tar zxvf #{tarball_path} -C #{Chef::Config.chef_repo_path}")
-        Chef::Log.debug "#{result.stdout}"
+        Mixlib::Archive.new(tarball_path).extract(Chef::Config.chef_repo_path, perms: false, ignore: /^\.$/)
       end
     end
 
diff --git a/lib/chef/application/knife.rb b/lib/chef/application/knife.rb
index 3459857..c80d024 100644
--- a/lib/chef/application/knife.rb
+++ b/lib/chef/application/knife.rb
@@ -33,6 +33,14 @@ class Chef::Application::Knife < Chef::Application
     :description => "The configuration file to use",
     :proc => lambda { |path| File.expand_path(path, Dir.pwd) }
 
+  option :config_option,
+    :long         => "--config-option OPTION=VALUE",
+    :description  => "Override a single configuration option",
+    :proc         => lambda { |option, existing|
+      (existing ||= []) << option
+      existing
+    }
+
   verbosity_level = 0
   option :verbosity,
     :short => "-V",
diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb
index ecac3f4..446a0f0 100644
--- a/lib/chef/application/solo.rb
+++ b/lib/chef/application/solo.rb
@@ -29,6 +29,7 @@ require "fileutils"
 require "chef/mixin/shell_out"
 require "pathname"
 require "chef-config/mixin/dot_d"
+require "mixlib/archive"
 
 class Chef::Application::Solo < Chef::Application
   include Chef::Mixin::ShellOut
@@ -40,6 +41,14 @@ class Chef::Application::Solo < Chef::Application
     :default => Chef::Config.platform_specific_path("/etc/chef/solo.rb"),
     :description => "The configuration file to use"
 
+  option :config_option,
+    :long         => "--config-option OPTION=VALUE",
+    :description  => "Override a single configuration option",
+    :proc         => lambda { |option, existing|
+      (existing ||= []) << option
+      existing
+    }
+
   option :formatter,
     :short        => "-F FORMATTER",
     :long         => "--format FORMATTER",
@@ -159,9 +168,9 @@ class Chef::Application::Solo < Chef::Application
     :description  => "Replace current run list with specified items",
     :proc         => lambda {|items|
       items = items.split(",")
-      items.compact.map {|item|
+      items.compact.map do |item|
         Chef::RunList::RunListItem.new(item)
-      }
+      end
     }
 
   option :client_fork,
@@ -241,6 +250,13 @@ class Chef::Application::Solo < Chef::Application
         ARGV[dash_r] = "--recipe-url"
       end
 
+      # For back compat reasons, we need to ensure that we try and use the cache_path as a repo first
+      Chef::Log.debug "Current chef_repo_path is #{Chef::Config.chef_repo_path}"
+
+      if !Chef::Config.has_key?(:cookbook_path) && !Chef::Config.has_key?(:chef_repo_path)
+        Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Chef::Config[:cache_path])
+      end
+
       Chef::Config[:local_mode] = true
     else
       configure_legacy_mode!
@@ -266,8 +282,7 @@ class Chef::Application::Solo < Chef::Application
       FileUtils.mkdir_p(recipes_path)
       tarball_path = File.join(recipes_path, "recipes.tgz")
       fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path)
-      result = shell_out!("tar zxvf #{tarball_path} -C #{recipes_path}")
-      Chef::Log.debug "#{result.stdout}"
+      Mixlib::Archive.new(tarball_path).extract(Chef::Config.chef_repo_path, perms: false, ignore: /^\.$/)
     end
 
     # json_attribs shuld be fetched after recipe_url tarball is unpacked.
diff --git a/lib/chef/audit/audit_reporter.rb b/lib/chef/audit/audit_reporter.rb
index a40cae9..8546a21 100644
--- a/lib/chef/audit/audit_reporter.rb
+++ b/lib/chef/audit/audit_reporter.rb
@@ -140,7 +140,11 @@ class Chef
               # Save the audit report to local disk
               error_file = "failed-audit-data.json"
               Chef::FileCache.store(error_file, Chef::JSONCompat.to_json_pretty(run_data), 0640)
-              Chef::Log.error("Failed to post audit report to server. Saving report to #{Chef::FileCache.load(error_file, false)}")
+              if Chef::Config.chef_zero.enabled
+                Chef::Log.debug("Saving audit report to #{Chef::FileCache.load(error_file, false)}")
+              else
+                Chef::Log.error("Failed to post audit report to server. Saving report to #{Chef::FileCache.load(error_file, false)}")
+              end
             end
           else
             Chef::Log.error("Failed to post audit report to server (#{e})")
diff --git a/lib/chef/audit/runner.rb b/lib/chef/audit/runner.rb
index 100a72d..8373463 100644
--- a/lib/chef/audit/runner.rb
+++ b/lib/chef/audit/runner.rb
@@ -165,7 +165,7 @@ class Chef
         add_example_group_methods
         run_context.audits.each do |name, group|
           ctl_grp = RSpec::Core::ExampleGroup.__control_group__(*group.args, &group.block)
-          RSpec.world.register(ctl_grp)
+          RSpec.world.record(ctl_grp)
         end
       end
 
diff --git a/lib/chef/chef_fs/chef_fs_data_store.rb b/lib/chef/chef_fs/chef_fs_data_store.rb
index aa5a6d5..6b3e830 100644
--- a/lib/chef/chef_fs/chef_fs_data_store.rb
+++ b/lib/chef/chef_fs/chef_fs_data_store.rb
@@ -458,6 +458,7 @@ class Chef
               # We want to delete just the ones that == POLICY
               next unless policy.name.rpartition("-")[0] == path[1]
               policy.delete(false)
+              FileSystemCache.instance.delete!(policy.file_path)
               found_policy = true
             end
             raise ChefZero::DataStore::DataNotFoundError.new(path) if !found_policy
diff --git a/lib/chef/chef_fs/command_line.rb b/lib/chef/chef_fs/command_line.rb
index c824bc9..2d887f4 100644
--- a/lib/chef/chef_fs/command_line.rb
+++ b/lib/chef/chef_fs/command_line.rb
@@ -242,48 +242,50 @@ class Chef
         return [ [ :error, old_entry, new_entry, nil, nil, e ] ]
       end
 
-      private
+      class << self
+        private
 
-      def self.sort_keys(json_object)
-        if json_object.is_a?(Array)
-          json_object.map { |o| sort_keys(o) }
-        elsif json_object.is_a?(Hash)
-          new_hash = {}
-          json_object.keys.sort.each { |key| new_hash[key] = sort_keys(json_object[key]) }
-          new_hash
-        else
-          json_object
+        def sort_keys(json_object)
+          if json_object.is_a?(Array)
+            json_object.map { |o| sort_keys(o) }
+          elsif json_object.is_a?(Hash)
+            new_hash = {}
+            json_object.keys.sort.each { |key| new_hash[key] = sort_keys(json_object[key]) }
+            new_hash
+          else
+            json_object
+          end
         end
-      end
 
-      def self.canonicalize_json(json_text)
-        parsed_json = Chef::JSONCompat.parse(json_text)
-        sorted_json = sort_keys(parsed_json)
-        Chef::JSONCompat.to_json_pretty(sorted_json)
-      end
-
-      def self.diff_text(old_path, new_path, old_value, new_value)
-        # Copy to tempfiles before diffing
-        # TODO don't copy things that are already in files!  Or find an in-memory diff algorithm
-        begin
-          new_tempfile = Tempfile.new("new")
-          new_tempfile.write(new_value)
-          new_tempfile.close
+        def canonicalize_json(json_text)
+          parsed_json = Chef::JSONCompat.parse(json_text)
+          sorted_json = sort_keys(parsed_json)
+          Chef::JSONCompat.to_json_pretty(sorted_json)
+        end
 
+        def diff_text(old_path, new_path, old_value, new_value)
+          # Copy to tempfiles before diffing
+          # TODO don't copy things that are already in files!  Or find an in-memory diff algorithm
           begin
-            old_tempfile = Tempfile.new("old")
-            old_tempfile.write(old_value)
-            old_tempfile.close
+            new_tempfile = Tempfile.new("new")
+            new_tempfile.write(new_value)
+            new_tempfile.close
 
-            result = Chef::Util::Diff.new.udiff(old_tempfile.path, new_tempfile.path)
-            result = result.gsub(/^--- #{old_tempfile.path}/, "--- #{old_path}")
-            result = result.gsub(/^\+\+\+ #{new_tempfile.path}/, "+++ #{new_path}")
-            result
+            begin
+              old_tempfile = Tempfile.new("old")
+              old_tempfile.write(old_value)
+              old_tempfile.close
+
+              result = Chef::Util::Diff.new.udiff(old_tempfile.path, new_tempfile.path)
+              result = result.gsub(/^--- #{old_tempfile.path}/, "--- #{old_path}")
+              result = result.gsub(/^\+\+\+ #{new_tempfile.path}/, "+++ #{new_path}")
+              result
+            ensure
+              old_tempfile.close!
+            end
           ensure
-            old_tempfile.close!
+            new_tempfile.close!
           end
-        ensure
-          new_tempfile.close!
         end
       end
     end
diff --git a/lib/chef/chef_fs/data_handler/data_handler_base.rb b/lib/chef/chef_fs/data_handler/data_handler_base.rb
index b34aff4..3668f77 100644
--- a/lib/chef/chef_fs/data_handler/data_handler_base.rb
+++ b/lib/chef/chef_fs/data_handler/data_handler_base.rb
@@ -93,7 +93,7 @@ class Chef
         # name to recipe[name].  Then calls uniq on the result.
         #
         def normalize_run_list(run_list)
-          run_list.map {|item|
+          run_list.map do |item|
             case item.to_s
             when /^recipe\[.*\]$/
               item # explicit recipe
@@ -102,7 +102,7 @@ class Chef
             else
               "recipe[#{item}]"
             end
-          }.uniq
+          end.uniq
         end
 
         #
diff --git a/lib/chef/chef_fs/file_pattern.rb b/lib/chef/chef_fs/file_pattern.rb
index a308a0f..9c12bd4 100644
--- a/lib/chef/chef_fs/file_pattern.rb
+++ b/lib/chef/chef_fs/file_pattern.rb
@@ -160,7 +160,7 @@ class Chef
         pattern
       end
 
-    private
+      private
 
       def regexp
         calculate
diff --git a/lib/chef/chef_fs/file_system.rb b/lib/chef/chef_fs/file_system.rb
index 69dce54..1a8da2f 100644
--- a/lib/chef/chef_fs/file_system.rb
+++ b/lib/chef/chef_fs/file_system.rb
@@ -68,7 +68,7 @@ class Chef
                 list_from(exact_child, &block)
               end
 
-            # Otherwise, go through all children and find any matches
+              # Otherwise, go through all children and find any matches
             elsif entry.dir?
               results = Parallelizer.parallelize(entry.children) { |child| Chef::ChefFS::FileSystem.list(child, pattern) }
               results.flatten(1).each(&block)
@@ -257,172 +257,174 @@ class Chef
         [ are_same, a_value, b_value ]
       end
 
-      private
-
-      # Copy two entries (could be files or dirs)
-      def self.copy_entries(src_entry, dest_entry, new_dest_parent, recurse_depth, options, ui, format_path)
-        # A NOTE about this algorithm:
-        # There are cases where this algorithm does too many network requests.
-        # knife upload with a specific filename will first check if the file
-        # exists (a "dir" in the parent) before deciding whether to POST or
-        # PUT it.  If we just tried PUT (or POST) and then tried the other if
-        # the conflict failed, we wouldn't need to check existence.
-        # On the other hand, we may already have DONE the request, in which
-        # case we shouldn't waste time trying PUT if we know the file doesn't
-        # exist.
-        # Will need to decide how that works with checksums, though.
-        error = false
-        begin
-          dest_path = format_path.call(dest_entry) if ui
-          src_path = format_path.call(src_entry) if ui
-          if !src_entry.exists?
-            if options[:purge]
-              # If we would not have uploaded it, we will not purge it.
-              if src_entry.parent.can_have_child?(dest_entry.name, dest_entry.dir?)
-                if options[:dry_run]
-                  ui.output "Would delete #{dest_path}" if ui
-                else
-                  begin
-                    dest_entry.delete(true)
-                    ui.output "Deleted extra entry #{dest_path} (purge is on)" if ui
-                  rescue Chef::ChefFS::FileSystem::NotFoundError
-                    ui.output "Entry #{dest_path} does not exist. Nothing to do. (purge is on)" if ui
+      class << self
+        private
+
+        # Copy two entries (could be files or dirs)
+        def copy_entries(src_entry, dest_entry, new_dest_parent, recurse_depth, options, ui, format_path)
+          # A NOTE about this algorithm:
+          # There are cases where this algorithm does too many network requests.
+          # knife upload with a specific filename will first check if the file
+          # exists (a "dir" in the parent) before deciding whether to POST or
+          # PUT it.  If we just tried PUT (or POST) and then tried the other if
+          # the conflict failed, we wouldn't need to check existence.
+          # On the other hand, we may already have DONE the request, in which
+          # case we shouldn't waste time trying PUT if we know the file doesn't
+          # exist.
+          # Will need to decide how that works with checksums, though.
+          error = false
+          begin
+            dest_path = format_path.call(dest_entry) if ui
+            src_path = format_path.call(src_entry) if ui
+            if !src_entry.exists?
+              if options[:purge]
+                # If we would not have uploaded it, we will not purge it.
+                if src_entry.parent.can_have_child?(dest_entry.name, dest_entry.dir?)
+                  if options[:dry_run]
+                    ui.output "Would delete #{dest_path}" if ui
+                  else
+                    begin
+                      dest_entry.delete(true)
+                      ui.output "Deleted extra entry #{dest_path} (purge is on)" if ui
+                    rescue Chef::ChefFS::FileSystem::NotFoundError
+                      ui.output "Entry #{dest_path} does not exist. Nothing to do. (purge is on)" if ui
+                    end
                   end
-                end
-              else
-                ui.output ("Not deleting extra entry #{dest_path} (purge is off)") if ui
-              end
-            end
-
-          elsif !dest_entry.exists?
-            if new_dest_parent.can_have_child?(src_entry.name, src_entry.dir?)
-              # If the entry can do a copy directly from filesystem, do that.
-              if new_dest_parent.respond_to?(:create_child_from)
-                if options[:dry_run]
-                  ui.output "Would create #{dest_path}" if ui
                 else
-                  new_dest_parent.create_child_from(src_entry)
-                  ui.output "Created #{dest_path}" if ui
+                  ui.output ("Not deleting extra entry #{dest_path} (purge is off)") if ui
                 end
-                return
               end
 
-              if src_entry.dir?
-                if options[:dry_run]
-                  ui.output "Would create #{dest_path}" if ui
-                  new_dest_dir = new_dest_parent.child(src_entry.name)
-                else
-                  new_dest_dir = new_dest_parent.create_child(src_entry.name, nil)
-                  ui.output "Created #{dest_path}" if ui
-                end
-                # Directory creation is recursive.
-                if recurse_depth != 0
-                  parallel_do(src_entry.children) do |src_child|
-                    new_dest_child = new_dest_dir.child(src_child.name)
-                    child_error = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
-                    error ||= child_error
+            elsif !dest_entry.exists?
+              if new_dest_parent.can_have_child?(src_entry.name, src_entry.dir?)
+                # If the entry can do a copy directly from filesystem, do that.
+                if new_dest_parent.respond_to?(:create_child_from)
+                  if options[:dry_run]
+                    ui.output "Would create #{dest_path}" if ui
+                  else
+                    new_dest_parent.create_child_from(src_entry)
+                    ui.output "Created #{dest_path}" if ui
                   end
+                  return
                 end
-              else
-                if options[:dry_run]
-                  ui.output "Would create #{dest_path}" if ui
-                else
-                  child = new_dest_parent.create_child(src_entry.name, src_entry.read)
-                  ui.output "Created #{format_path.call(child)}" if ui
-                end
-              end
-            end
-
-          else
-            # Both exist.
 
-            # If the entry can do a copy directly, do that.
-            if dest_entry.respond_to?(:copy_from)
-              if options[:force] || compare(src_entry, dest_entry)[0] == false
-                if options[:dry_run]
-                  ui.output "Would update #{dest_path}" if ui
+                if src_entry.dir?
+                  if options[:dry_run]
+                    ui.output "Would create #{dest_path}" if ui
+                    new_dest_dir = new_dest_parent.child(src_entry.name)
+                  else
+                    new_dest_dir = new_dest_parent.create_child(src_entry.name, nil)
+                    ui.output "Created #{dest_path}" if ui
+                  end
+                  # Directory creation is recursive.
+                  if recurse_depth != 0
+                    parallel_do(src_entry.children) do |src_child|
+                      new_dest_child = new_dest_dir.child(src_child.name)
+                      child_error = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
+                      error ||= child_error
+                    end
+                  end
                 else
-                  dest_entry.copy_from(src_entry, options)
-                  ui.output "Updated #{dest_path}" if ui
+                  if options[:dry_run]
+                    ui.output "Would create #{dest_path}" if ui
+                  else
+                    child = new_dest_parent.create_child(src_entry.name, src_entry.read)
+                    ui.output "Created #{format_path.call(child)}" if ui
+                  end
                 end
               end
-              return
-            end
 
-            # If they are different types, log an error.
-            if src_entry.dir?
-              if dest_entry.dir?
-                # If both are directories, recurse into their children
-                if recurse_depth != 0
-                  parallel_do(child_pairs(src_entry, dest_entry)) do |src_child, dest_child|
-                    child_error = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
-                    error ||= child_error
+            else
+              # Both exist.
+
+              # If the entry can do a copy directly, do that.
+              if dest_entry.respond_to?(:copy_from)
+                if options[:force] || compare(src_entry, dest_entry)[0] == false
+                  if options[:dry_run]
+                    ui.output "Would update #{dest_path}" if ui
+                  else
+                    dest_entry.copy_from(src_entry, options)
+                    ui.output "Updated #{dest_path}" if ui
                   end
                 end
-              else
-                # If they are different types.
-                ui.error("File #{src_path} is a directory while file #{dest_path} is a regular file\n") if ui
                 return
               end
-            else
-              if dest_entry.dir?
-                ui.error("File #{src_path} is a regular file while file #{dest_path} is a directory\n") if ui
-                return
-              else
 
-                # Both are files!  Copy them unless we're sure they are the same.'
-                if options[:diff] == false
-                  should_copy = false
-                elsif options[:force]
-                  should_copy = true
-                  src_value = nil
+              # If they are different types, log an error.
+              if src_entry.dir?
+                if dest_entry.dir?
+                  # If both are directories, recurse into their children
+                  if recurse_depth != 0
+                    parallel_do(child_pairs(src_entry, dest_entry)) do |src_child, dest_child|
+                      child_error = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
+                      error ||= child_error
+                    end
+                  end
                 else
-                  are_same, src_value, _dest_value = compare(src_entry, dest_entry)
-                  should_copy = !are_same
+                  # If they are different types.
+                  ui.error("File #{src_path} is a directory while file #{dest_path} is a regular file\n") if ui
+                  return
                 end
-                if should_copy
-                  if options[:dry_run]
-                    ui.output "Would update #{dest_path}" if ui
+              else
+                if dest_entry.dir?
+                  ui.error("File #{src_path} is a regular file while file #{dest_path} is a directory\n") if ui
+                  return
+                else
+
+                  # Both are files!  Copy them unless we're sure they are the same.'
+                  if options[:diff] == false
+                    should_copy = false
+                  elsif options[:force]
+                    should_copy = true
+                    src_value = nil
                   else
-                    src_value = src_entry.read if src_value.nil?
-                    dest_entry.write(src_value)
-                    ui.output "Updated #{dest_path}" if ui
+                    are_same, src_value, _dest_value = compare(src_entry, dest_entry)
+                    should_copy = !are_same
+                  end
+                  if should_copy
+                    if options[:dry_run]
+                      ui.output "Would update #{dest_path}" if ui
+                    else
+                      src_value = src_entry.read if src_value.nil?
+                      dest_entry.write(src_value)
+                      ui.output "Updated #{dest_path}" if ui
+                    end
                   end
                 end
               end
             end
+          rescue RubyFileError => e
+            ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui
+          rescue DefaultEnvironmentCannotBeModifiedError => e
+            ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui
+          rescue OperationFailedError => e
+            ui.error "#{format_path.call(e.entry)} failed to #{e.operation}: #{e.message}" if ui
+            error = true
+          rescue OperationNotAllowedError => e
+            ui.error "#{format_path.call(e.entry)} #{e.reason}." if ui
+            error = true
           end
-        rescue RubyFileError => e
-          ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui
-        rescue DefaultEnvironmentCannotBeModifiedError => e
-          ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui
-        rescue OperationFailedError => e
-          ui.error "#{format_path.call(e.entry)} failed to #{e.operation}: #{e.message}" if ui
-          error = true
-        rescue OperationNotAllowedError => e
-          ui.error "#{format_path.call(e.entry)} #{e.reason}." if ui
-          error = true
+          error
         end
-        error
-      end
 
-      def self.get_or_create_parent(entry, options, ui, format_path)
-        parent = entry.parent
-        if parent && !parent.exists?
-          parent_path = format_path.call(parent) if ui
-          parent_parent = get_or_create_parent(parent, options, ui, format_path)
-          if options[:dry_run]
-            ui.output "Would create #{parent_path}" if ui
-          else
-            parent = parent_parent.create_child(parent.name, nil)
-            ui.output "Created #{parent_path}" if ui
+        def get_or_create_parent(entry, options, ui, format_path)
+          parent = entry.parent
+          if parent && !parent.exists?
+            parent_path = format_path.call(parent) if ui
+            parent_parent = get_or_create_parent(parent, options, ui, format_path)
+            if options[:dry_run]
+              ui.output "Would create #{parent_path}" if ui
+            else
+              parent = parent_parent.create_child(parent.name, nil)
+              ui.output "Created #{parent_path}" if ui
+            end
           end
+          return parent
         end
-        return parent
-      end
 
-      def self.parallel_do(enum, options = {}, &block)
-        Chef::ChefFS::Parallelizer.parallel_do(enum, options, &block)
+        def parallel_do(enum, options = {}, &block)
+          Chef::ChefFS::Parallelizer.parallel_do(enum, options, &block)
+        end
       end
     end
   end
diff --git a/lib/chef/chef_fs/file_system/multiplexed_dir.rb b/lib/chef/chef_fs/file_system/multiplexed_dir.rb
index d3951ed..21abc01 100644
--- a/lib/chef/chef_fs/file_system/multiplexed_dir.rb
+++ b/lib/chef/chef_fs/file_system/multiplexed_dir.rb
@@ -41,7 +41,7 @@ class Chef
             child_entry = dir.child(name)
             if child_entry.exists?
               if result
-                Chef::Log.debug("Child with name '#{child_entry.name}' found in multiple directories: #{result.parent.path_for_printing} and #{child_entry.parent.path_for_printing}") unless seen[child.name].path_for_printing == child.path_for_printing
+                Chef::Log.debug("Child with name '#{child_entry.name}' found in multiple directories: #{result.parent.path_for_printing} and #{child_entry.parent.path_for_printing}")
               else
                 result = child_entry
               end
diff --git a/lib/chef/chef_fs/file_system/repository/acls_dir.rb b/lib/chef/chef_fs/file_system/repository/acls_dir.rb
index 619031a..110befd 100644
--- a/lib/chef/chef_fs/file_system/repository/acls_dir.rb
+++ b/lib/chef/chef_fs/file_system/repository/acls_dir.rb
@@ -28,14 +28,16 @@ class Chef
       module Repository
         class AclsDir < Repository::Directory
 
+          BARE_FILES = %w{ organization.json root }
+
           def can_have_child?(name, is_dir)
-            is_dir ? Chef::ChefFS::FileSystem::ChefServer::AclsDir::ENTITY_TYPES.include?(name) : name == "organization.json"
+            is_dir ? Chef::ChefFS::FileSystem::ChefServer::AclsDir::ENTITY_TYPES.include?(name) : BARE_FILES.include?(name)
           end
 
           protected
 
           def make_child_entry(child_name)
-            if child_name == "organization.json"
+            if BARE_FILES.include? child_name
               Acl.new(child_name, self)
             else
               AclsSubDir.new(child_name, self)
diff --git a/lib/chef/chef_fs/file_system/repository/base_file.rb b/lib/chef/chef_fs/file_system/repository/base_file.rb
index a768bcf..3e1edc8 100644
--- a/lib/chef/chef_fs/file_system/repository/base_file.rb
+++ b/lib/chef/chef_fs/file_system/repository/base_file.rb
@@ -16,6 +16,8 @@
 # limitations under the License.
 #
 
+require "chef/chef_fs/file_system_cache"
+
 class Chef
   module ChefFS
     module FileSystem
@@ -99,6 +101,7 @@ class Chef
           end
 
           def delete(_)
+            FileSystemCache.instance.delete!(file_path)
             File.delete(file_path)
           rescue Errno::ENOENT
             raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
diff --git a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb
index 9d1538e..4019c69 100644
--- a/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb
+++ b/lib/chef/chef_fs/file_system/repository/chef_repository_file_system_cookbook_entry.rb
@@ -120,6 +120,7 @@ class Chef
           end
 
           def delete(recurse)
+            FileSystemCache.instance.delete!(file_path)
             begin
               if dir?
                 if !recurse
diff --git a/lib/chef/chef_fs/file_system/repository/directory.rb b/lib/chef/chef_fs/file_system/repository/directory.rb
index dae4679..328cf92 100644
--- a/lib/chef/chef_fs/file_system/repository/directory.rb
+++ b/lib/chef/chef_fs/file_system/repository/directory.rb
@@ -16,6 +16,8 @@
 # limitations under the License.
 #
 
+require "chef/chef_fs/file_system_cache"
+
 class Chef
   module ChefFS
     module FileSystem
@@ -68,9 +70,11 @@ class Chef
           end
 
           def children
-            dir_ls.sort.
+            return FileSystemCache.instance.children(file_path) if FileSystemCache.instance.exist?(file_path)
+            children = dir_ls.sort.
               map { |child_name| make_child_entry(child_name) }.
               select { |new_child| new_child.fs_entry_valid? && can_have_child?(new_child.name, new_child.dir?) }
+            FileSystemCache.instance.set_children(file_path, children)
           rescue Errno::ENOENT => e
             raise Chef::ChefFS::FileSystem::NotFoundError.new(self, e)
           end
@@ -80,6 +84,7 @@ class Chef
             if child.exists?
               raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, child)
             end
+            FileSystemCache.instance.delete!(child.file_path)
             if file_contents
               child.write(file_contents)
             else
@@ -118,6 +123,7 @@ class Chef
               raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self)
             end
             begin
+              FileSystemCache.instance.delete!(file_path)
               Dir.mkdir(file_path)
             rescue Errno::EEXIST
               raise Chef::ChefFS::FileSystem::AlreadyExistsError.new(:create_child, self)
@@ -134,6 +140,7 @@ class Chef
                 raise MustDeleteRecursivelyError.new(self, $!)
               end
               FileUtils.rm_r(file_path)
+              FileSystemCache.instance.delete!(file_path)
             else
               raise Chef::ChefFS::FileSystem::NotFoundError.new(self, $!)
             end
@@ -145,6 +152,10 @@ class Chef
 
           protected
 
+          def write(data)
+            raise FileSystemError.new(self, nil, "attempted to write to a directory entry")
+          end
+
           def make_child_entry(child_name)
             raise "Not Implemented"
           end
diff --git a/lib/chef/chef_fs/file_system_cache.rb b/lib/chef/chef_fs/file_system_cache.rb
new file mode 100644
index 0000000..a9d8d8b
--- /dev/null
+++ b/lib/chef/chef_fs/file_system_cache.rb
@@ -0,0 +1,80 @@
+#
+# Copyright:: Copyright 2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "singleton"
+require "chef/client"
+
+class Chef
+  module ChefFS
+    class FileSystemCache
+      include Singleton
+
+      def initialize
+        @cache = {}
+
+        Chef::Client.when_run_starts do
+          FileSystemCache.instance.reset!
+        end
+      end
+
+      def reset!
+        @cache = {}
+      end
+
+      def exist?(path)
+        @cache.key?(path)
+      end
+
+      def children(path)
+        @cache[path]["children"]
+      end
+
+      def set_children(path, val)
+        @cache[path] ||= { "children" => [] }
+        @cache[path]["children"] = val
+        val
+      end
+
+      def delete!(path)
+        parent = _get_parent(path)
+        Chef::Log.debug("Deleting parent #{parent} and #{path} from FileSystemCache")
+        if @cache.key?(path)
+          @cache.delete(path)
+        end
+        if !parent.nil? && @cache.key?(parent)
+          @cache.delete(parent)
+        end
+      end
+
+      def fetch(path)
+        if @cache.key?(path)
+          @cache[path]
+        else
+          false
+        end
+      end
+
+      private
+
+      def _get_parent(path)
+        parts = ChefFS::PathUtils.split(path)
+        return nil if parts.nil? || parts.length < 2
+        ChefFS::PathUtils.join(*parts[0..-2])
+      end
+    end
+  end
+end
diff --git a/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb b/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb
index 9d02bba..ab578bd 100644
--- a/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb
+++ b/lib/chef/chef_fs/parallelizer/parallel_enumerable.rb
@@ -184,9 +184,7 @@ class Chef
                 sleep(0.01)
               end
 
-              until @unconsumed_output.empty?
-                yield @unconsumed_output.pop
-              end
+              yield @unconsumed_output.pop until @unconsumed_output.empty?
 
               # If no one is working on our tasks and we're allowed to
               # work on them in the main thread, process an input to
@@ -227,9 +225,7 @@ class Chef
 
         def stop
           @unconsumed_input.clear
-          while @in_process.size > 0
-            sleep(0.05)
-          end
+          sleep(0.05) while @in_process.size > 0
           @unconsumed_output.clear
         end
 
diff --git a/lib/chef/cookbook/cookbook_version_loader.rb b/lib/chef/cookbook/cookbook_version_loader.rb
index d9b027f..af8b2e0 100644
--- a/lib/chef/cookbook/cookbook_version_loader.rb
+++ b/lib/chef/cookbook/cookbook_version_loader.rb
@@ -3,6 +3,7 @@ require "chef/cookbook_version"
 require "chef/cookbook/chefignore"
 require "chef/cookbook/metadata"
 require "chef/util/path_helper"
+require "find"
 
 class Chef
   class Cookbook
@@ -168,7 +169,7 @@ class Chef
           when /\.json$/
             apply_json_metadata(metadata_file)
           else
-            raise RuntimeError, "Invalid metadata file: #{metadata_file} for cookbook: #{cookbook_version}"
+            raise "Invalid metadata file: #{metadata_file} for cookbook: #{cookbook_version}"
           end
         end
 
@@ -223,27 +224,31 @@ class Chef
       # however if the file is named ".uploaded-cookbook-version.json" it is
       # assumed to be managed by chef-zero and not part of the cookbook.
       def load_all_files
-        Dir.glob(File.join(Chef::Util::PathHelper.escape_glob_dir(cookbook_path), "*"), File::FNM_DOTMATCH).each do |fs_entry|
-          if File.directory?(fs_entry)
-            dir_relpath = Chef::Util::PathHelper.relative_path_from(@cookbook_path, fs_entry)
-
-            next if dir_relpath.to_s.start_with?(".")
-
-            Dir.glob(File.join(fs_entry, "**/*"), File::FNM_DOTMATCH).each do |file|
-              next if File.directory?(file)
-              file = Pathname.new(file).cleanpath.to_s
-              name = Chef::Util::PathHelper.relative_path_from(@cookbook_path, file)
-              cookbook_settings[:all_files][name] = file
-            end
-          elsif File.file?(fs_entry)
-            file = Pathname.new(fs_entry).cleanpath.to_s
-
-            next if File.basename(file) == UPLOADED_COOKBOOK_VERSION_FILE
-
-            name = Chef::Util::PathHelper.relative_path_from(@cookbook_path, file)
-            cookbook_settings[:all_files][name] = file
-          else # pipes, devices, other weirdness
-            next
+        return unless File.exist?(cookbook_path)
+
+        # If cookbook_path is a symlink, Find on Windows Ruby 2.3 will not traverse it.
+        # Dir.entries will do so on all platforms, so we iterate the top level using
+        # Dir.entries. Since we have different behavior at the top anyway (hidden
+        # directories at the top level are not included for backcompat), this
+        # actually keeps things a bit cleaner.
+        Dir.entries(cookbook_path).each do |top_filename|
+          # Skip top-level directories starting with "."
+          top_path = File.join(cookbook_path, top_filename)
+          next if File.directory?(top_path) && top_filename.start_with?(".")
+
+          # Use Find.find because it:
+          # (a) returns any children, recursively
+          # (b) includes top_path as well
+          # (c) skips symlinks, which is backcompat (no judgement on whether it was *right*)
+          Find.find(top_path) do |path|
+            # Only add files, not directories
+            next unless File.file?(path)
+            # Don't add .uploaded-cookbook-version.json
+            next if File.basename(path) == UPLOADED_COOKBOOK_VERSION_FILE
+
+            relative_path = Chef::Util::PathHelper.relative_path_from(cookbook_path, path)
+            path = Pathname.new(path).cleanpath.to_s
+            cookbook_settings[:all_files][relative_path] = path
           end
         end
       end
diff --git a/lib/chef/cookbook/metadata.rb b/lib/chef/cookbook/metadata.rb
index 603f807..ab83da9 100644
--- a/lib/chef/cookbook/metadata.rb
+++ b/lib/chef/cookbook/metadata.rb
@@ -722,7 +722,7 @@ class Chef
         end
       end
 
-    private
+      private
 
       # Helper to match a gem style version (ohai_version/chef_version) against a set of
       # Gem::Dependency version constraints.  If none are present, it always matches.  if
diff --git a/lib/chef/cookbook/synchronizer.rb b/lib/chef/cookbook/synchronizer.rb
index 1ee30ba..bb44bc3 100644
--- a/lib/chef/cookbook/synchronizer.rb
+++ b/lib/chef/cookbook/synchronizer.rb
@@ -152,11 +152,11 @@ class Chef
         queue << lambda do |lock|
           full_file_path = sync_file(file)
 
-          lock.synchronize {
+          lock.synchronize do
             # Save the full_path of the downloaded file to be restored in the manifest later
             save_full_file_path(file, full_file_path)
             mark_file_synced(file)
-          }
+          end
         end
       end
 
@@ -291,7 +291,7 @@ class Chef
     end
 
     def server_api
-      Chef::ServerAPI.new(Chef::Config[:chef_server_url])
+      Thread.current[:server_api] ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], keepalives: true)
     end
 
   end
diff --git a/lib/chef/cookbook_site_streaming_uploader.rb b/lib/chef/cookbook_site_streaming_uploader.rb
index 9fb8d0d..c0e85ff 100644
--- a/lib/chef/cookbook_site_streaming_uploader.rb
+++ b/lib/chef/cookbook_site_streaming_uploader.rb
@@ -31,7 +31,7 @@ class Chef
   # inspired by http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
   class CookbookSiteStreamingUploader
 
-    DefaultHeaders = { "accept" => "application/json", "x-chef-version" => ::Chef::VERSION }
+    DefaultHeaders = { "accept" => "application/json", "x-chef-version" => ::Chef::VERSION } # rubocop:disable Style/ConstantName
 
     class << self
 
@@ -149,11 +149,11 @@ class Chef
           alias :to_s :body
 
           # BUGBUG this makes the response compatible with what respsonse_steps expects to test headers (response.headers[] -> response[])
-          def headers
+          def headers # rubocop:disable Lint/NestedMethodDefinition
             self
           end
 
-          def status
+          def status  # rubocop:disable Lint/NestedMethodDefinition
             code.to_i
           end
         end
diff --git a/lib/chef/cookbook_uploader.rb b/lib/chef/cookbook_uploader.rb
index 95c2779..bb75234 100644
--- a/lib/chef/cookbook_uploader.rb
+++ b/lib/chef/cookbook_uploader.rb
@@ -55,7 +55,7 @@ class Chef
         checksum_files.merge!(cb.checksums)
       end
 
-      checksums = checksum_files.inject({}) { |memo, elt| memo[elt.first] = nil ; memo }
+      checksums = checksum_files.inject({}) { |memo, elt| memo[elt.first] = nil; memo }
       new_sandbox = rest.post("sandboxes", { :checksums => checksums })
 
       Chef::Log.info("Uploading files")
diff --git a/lib/chef/cookbook_version.rb b/lib/chef/cookbook_version.rb
index 1e90360..8de9cb2 100644
--- a/lib/chef/cookbook_version.rb
+++ b/lib/chef/cookbook_version.rb
@@ -316,13 +316,13 @@ class Chef
           error_message << error_locations.join("\n")
           existing_files = segment_filenames(segment)
           # Strip the root_dir prefix off all files for readability
-          pretty_existing_files = existing_files.map { |path|
+          pretty_existing_files = existing_files.map do |path|
             if root_dir
               path[root_dir.length + 1..-1]
             else
               path
             end
-          }
+          end
           # Show the files that the cookbook does have. If the user made a typo,
           # hopefully they'll see it here.
           unless pretty_existing_files.empty?
@@ -599,12 +599,12 @@ class Chef
       end
     end
 
-    def <=>(o)
-      raise Chef::Exceptions::CookbookVersionNameMismatch if self.name != o.name
+    def <=>(other)
+      raise Chef::Exceptions::CookbookVersionNameMismatch if self.name != other.name
       # FIXME: can we change the interface to the Metadata class such
       # that metadata.version returns a Chef::Version instance instead
       # of a string?
-      Chef::Version.new(self.version) <=> Chef::Version.new(o.version)
+      Chef::Version.new(self.version) <=> Chef::Version.new(other.version)
     end
 
     private
@@ -623,7 +623,7 @@ class Chef
     # For each filename, produce a mapping of base filename (i.e. recipe name
     # or attribute file) to on disk location
     def filenames_by_name(filenames)
-      filenames.select { |filename| filename =~ /\.rb$/ }.inject({}) { |memo, filename| memo[File.basename(filename, ".rb")] = filename ; memo }
+      filenames.select { |filename| filename =~ /\.rb$/ }.inject({}) { |memo, filename| memo[File.basename(filename, ".rb")] = filename; memo }
     end
 
     def file_vendor
diff --git a/lib/chef/data_collector.rb b/lib/chef/data_collector.rb
index e852d11..dbb0b37 100644
--- a/lib/chef/data_collector.rb
+++ b/lib/chef/data_collector.rb
@@ -22,6 +22,7 @@ require "uri"
 require "chef/event_dispatch/base"
 require "chef/data_collector/messages"
 require "chef/data_collector/resource_report"
+require "ostruct"
 
 class Chef
 
@@ -51,13 +52,14 @@ class Chef
     # and exports its data through a webhook-like mechanism to a configured
     # endpoint.
     class Reporter < EventDispatch::Base
-      attr_reader :updated_resources, :status, :exception, :error_descriptions,
-                  :expanded_run_list, :run_status, :http, :resource_count,
+      attr_reader :all_resource_reports, :status, :exception, :error_descriptions,
+                  :expanded_run_list, :run_context, :run_status, :http,
                   :current_resource_report, :enabled
 
       def initialize
-        @updated_resources       = []
-        @resource_count          = 0
+        validate_data_collector_server_url!
+
+        @all_resource_reports    = []
         @current_resource_loaded = nil
         @error_descriptions      = {}
         @expanded_run_list       = {}
@@ -94,6 +96,29 @@ class Chef
         send_run_completion(status: "failure")
       end
 
+      # see EventDispatch::Base#converge_start
+      # Upon receipt, we stash the run_context for use at the
+      # end of the run in order to determine what resource+action
+      # combinations have not yet fired so we can report on
+      # unprocessed resources.
+      def converge_start(run_context)
+        @run_context = run_context
+      end
+
+      # see EventDispatch::Base#converge_complete
+      # At the end of the converge, we add any unprocessed resources
+      # to our report list.
+      def converge_complete
+        detect_unprocessed_resources
+      end
+
+      # see EventDispatch::Base#converge_failed
+      # At the end of the converge, we add any unprocessed resources
+      # to our report list
+      def converge_failed(exception)
+        detect_unprocessed_resources
+      end
+
       # see EventDispatch::Base#resource_current_state_loaded
       # Create a new ResourceReport instance that we'll use to track
       # the state of this resource during the run. Nested resources are
@@ -101,56 +126,40 @@ class Chef
       # resource, and we only care about tracking top-level resources.
       def resource_current_state_loaded(new_resource, action, current_resource)
         return if nested_resource?(new_resource)
-        update_current_resource_report(
-          Chef::DataCollector::ResourceReport.new(
-            new_resource,
-            action,
-            current_resource
-          )
-        )
+        update_current_resource_report(create_resource_report(new_resource, action, current_resource))
       end
 
       # see EventDispatch::Base#resource_up_to_date
-      # Mark our ResourceReport status accordingly, and increment the total
-      # resource count.
+      # Mark our ResourceReport status accordingly
       def resource_up_to_date(new_resource, action)
         current_resource_report.up_to_date unless nested_resource?(new_resource)
-        increment_resource_count
       end
 
       # see EventDispatch::Base#resource_skipped
-      # Increment the total resource count. If this is a top-level resource,
-      # we also create a ResourceReport instance (because a skipped resource
-      # does not trigger the resource_current_state_loaded event), and flag
-      # it as skipped.
+      # If this is a top-level resource, we create a ResourceReport
+      # instance (because a skipped resource does not trigger the
+      # resource_current_state_loaded event), and flag it as skipped.
       def resource_skipped(new_resource, action, conditional)
-        increment_resource_count
         return if nested_resource?(new_resource)
 
-        update_current_resource_report(
-          Chef::DataCollector::ResourceReport.new(
-            new_resource,
-            action
-          )
-        )
-        current_resource_report.skipped(conditional)
+        resource_report = create_resource_report(new_resource, action)
+        resource_report.skipped(conditional)
+        update_current_resource_report(resource_report)
       end
 
       # see EventDispatch::Base#resource_updated
       # Flag the current ResourceReport instance as updated (as long as it's
-      # a top-level resource) and increment the total resource count.
+      # a top-level resource).
       def resource_updated(new_resource, action)
         current_resource_report.updated unless nested_resource?(new_resource)
-        increment_resource_count
       end
 
       # see EventDispatch::Base#resource_failed
       # Flag the current ResourceReport as failed and supply the exception as
-      # long as it's a top-level resource, increment the total resource count,
-      # and update the run error text with the proper Formatter.
+      # long as it's a top-level resource, and update the run error text
+      # with the proper Formatter.
       def resource_failed(new_resource, action, exception)
         current_resource_report.failed(exception) unless nested_resource?(new_resource)
-        increment_resource_count
         update_error_description(
           Formatters::ErrorMapper.resource_failed(
             new_resource,
@@ -161,13 +170,12 @@ class Chef
       end
 
       # see EventDispatch::Base#resource_completed
-      # Mark the ResourceReport instance as finished (for timing details)
-      # and add it to the list of resources encountered during this run.
+      # Mark the ResourceReport instance as finished (for timing details).
       # This marks the end of this resource during this run.
       def resource_completed(new_resource)
         if current_resource_report && !nested_resource?(new_resource)
           current_resource_report.finish
-          add_updated_resource(current_resource_report)
+          add_resource_report(current_resource_report)
           update_current_resource_report(nil)
         end
       end
@@ -230,7 +238,8 @@ class Chef
         yield
       rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
              Errno::ECONNREFUSED, EOFError, Net::HTTPBadResponse,
-             Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError => e
+             Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError,
+             Errno::EHOSTDOWN => e
         disable_data_collector_reporter
         code = if e.respond_to?(:response) && e.response.code
                  e.response.code.to_s
@@ -273,13 +282,11 @@ class Chef
         # we have nothing to report.
         return unless run_status
 
-        send_to_data_collector(Chef::DataCollector::Messages.node_update_message(run_status).to_json)
         send_to_data_collector(
           Chef::DataCollector::Messages.run_end_message(
             run_status: run_status,
             expanded_run_list: expanded_run_list,
-            total_resource_count: resource_count,
-            updated_resources: updated_resources,
+            resources: all_resource_reports,
             status: opts[:status],
             error_descriptions: error_descriptions
           ).to_json
@@ -305,12 +312,12 @@ class Chef
         Chef::Config[:data_collector][:token]
       end
 
-      def increment_resource_count
-        @resource_count += 1
-      end
-
-      def add_updated_resource(resource_report)
-        @updated_resources << resource_report
+      def add_resource_report(resource_report)
+        @all_resource_reports << OpenStruct.new(
+          resource: resource_report.new_resource,
+          action: resource_report.action,
+          report_data: resource_report.to_hash
+        )
       end
 
       def disable_data_collector_reporter
@@ -333,6 +340,38 @@ class Chef
         @error_descriptions = discription_hash
       end
 
+      def create_resource_report(new_resource, action, current_resource = nil)
+        Chef::DataCollector::ResourceReport.new(
+          new_resource,
+          action,
+          current_resource
+        )
+      end
+
+      def detect_unprocessed_resources
+        # create a Set containing all resource+action combinations from
+        # the Resource Collection
+        collection_resources = Set.new
+        run_context.resource_collection.all_resources.each do |resource|
+          Array(resource.action).each do |action|
+            collection_resources.add([resource, action])
+          end
+        end
+
+        # Delete from the Set any resource+action combination we have
+        # already processed.
+        all_resource_reports.each do |report|
+          collection_resources.delete([report.resource, report.action])
+        end
+
+        # The items remaining in the Set are unprocessed resource+actions,
+        # so we'll create new resource reports for them which default to
+        # a state of "unprocessed".
+        collection_resources.each do |resource, action|
+          add_resource_report(create_resource_report(resource, action))
+        end
+      end
+
       # If we are getting messages about a resource while we are in the middle of
       # another resource's update, we assume that the nested resource is just the
       # implementation of a provider, and we want to hide it from the reporting
@@ -340,6 +379,20 @@ class Chef
       def nested_resource?(new_resource)
         @current_resource_report && @current_resource_report.new_resource != new_resource
       end
+
+      def validate_data_collector_server_url!
+        raise Chef::Exceptions::ConfigurationError,
+          "Chef::Config[:data_collector][:server_url] is empty. Please supply a valid URL." if data_collector_server_url.empty?
+
+        begin
+          uri = URI(data_collector_server_url)
+        rescue URI::InvalidURIError
+          raise Chef::Exceptions::ConfigurationError, "Chef::Config[:data_collector][:server_url] (#{data_collector_server_url}) is not a valid URI."
+        end
+
+        raise Chef::Exceptions::ConfigurationError,
+          "Chef::Config[:data_collector][:server_url] (#{data_collector_server_url}) is a URI with no host. Please supply a valid URL." if uri.host.nil?
+      end
     end
   end
 end
diff --git a/lib/chef/data_collector/messages.rb b/lib/chef/data_collector/messages.rb
index b6114a8..8c2a84b 100644
--- a/lib/chef/data_collector/messages.rb
+++ b/lib/chef/data_collector/messages.rb
@@ -68,17 +68,18 @@ class Chef
           "id"                     => run_status.run_id,
           "message_version"        => "1.0.0",
           "message_type"           => "run_converge",
+          "node"                   => run_status.node,
           "node_name"              => run_status.node.name,
           "organization_name"      => organization,
-          "resources"              => reporter_data[:updated_resources].map(&:for_json),
+          "resources"              => reporter_data[:resources].map(&:report_data),
           "run_id"                 => run_status.run_id,
           "run_list"               => run_status.node.run_list.for_json,
           "start_time"             => run_status.start_time.utc.iso8601,
           "end_time"               => run_status.end_time.utc.iso8601,
           "source"                 => collector_source,
           "status"                 => reporter_data[:status],
-          "total_resource_count"   => reporter_data[:total_resource_count],
-          "updated_resource_count" => reporter_data[:updated_resources].count,
+          "total_resource_count"   => reporter_data[:resources].count,
+          "updated_resource_count" => reporter_data[:resources].select { |r| r.report_data["status"] == "updated" }.count,
         }
 
         message["error"] = {
@@ -90,36 +91,6 @@ class Chef
 
         message
       end
-
-      #
-      # Message payload that is sent to the DataCollector server at the
-      # end of a Chef run.
-      #
-      # @param run_status [Chef::RunStatus] The RunStatus instance for this node/run.
-      #
-      # @return [Hash] A hash containing the node object and related metadata.
-      #
-      def self.node_update_message(run_status)
-        {
-          "entity_name"       => run_status.node.name,
-          "entity_type"       => "node",
-          "entity_uuid"       => node_uuid,
-          "id"                => SecureRandom.uuid,
-          "message_version"   => "1.1.0",
-          "message_type"      => "action",
-          "organization_name" => organization,
-          "recorded_at"       => Time.now.utc.iso8601,
-          "remote_hostname"   => run_status.node["fqdn"],
-          "requestor_name"    => run_status.node.name,
-          "requestor_type"    => "client",
-          "run_id"            => run_status.run_id,
-          "service_hostname"  => chef_server_fqdn(run_status),
-          "source"            => collector_source,
-          "task"              => "update",
-          "user_agent"        => Chef::HTTP::HTTPRequest::DEFAULT_UA,
-          "data"              => run_status.node,
-        }
-      end
     end
   end
 end
diff --git a/lib/chef/data_collector/messages/helpers.rb b/lib/chef/data_collector/messages/helpers.rb
index 3e52f80..c0c700f 100644
--- a/lib/chef/data_collector/messages/helpers.rb
+++ b/lib/chef/data_collector/messages/helpers.rb
@@ -148,8 +148,8 @@ class Chef
         end
 
         def update_metadata(key, value)
-          metadata[key] = value
-          Chef::FileCache.store(metadata_filename, metadata.to_json, 0644)
+          updated_metadata = metadata.tap { |x| x[key] = value }
+          Chef::FileCache.store(metadata_filename, updated_metadata.to_json, 0644)
         end
 
         def metadata_filename
diff --git a/lib/chef/data_collector/resource_report.rb b/lib/chef/data_collector/resource_report.rb
index 1793fe2..dcaf9c8 100644
--- a/lib/chef/data_collector/resource_report.rb
+++ b/lib/chef/data_collector/resource_report.rb
@@ -22,13 +22,14 @@ class Chef
   class DataCollector
     class ResourceReport
 
-      attr_reader :action, :current_resource, :elapsed_time, :new_resource, :status
-      attr_accessor :conditional, :exception
+      attr_reader :action, :elapsed_time, :new_resource, :status
+      attr_accessor :conditional, :current_resource, :exception
 
       def initialize(new_resource, action, current_resource = nil)
         @new_resource     = new_resource
         @action           = action
         @current_resource = current_resource
+        @status           = "unprocessed"
       end
 
       def skipped(conditional)
@@ -54,22 +55,32 @@ class Chef
         @elapsed_time = new_resource.elapsed_time
       end
 
+      def elapsed_time_in_milliseconds
+        elapsed_time.nil? ? nil : (elapsed_time * 1000).to_i
+      end
+
+      def potentially_changed?
+        %w{updated failed}.include?(status)
+      end
+
       def to_hash
         hash = {
-          "type"     => new_resource.resource_name.to_sym,
-          "name"     => new_resource.name.to_s,
-          "id"       => new_resource.identity.to_s,
-          "after"    => new_resource.state_for_resource_reporter,
-          "before"   => current_resource ? current_resource.state_for_resource_reporter : {},
-          "duration" => (elapsed_time * 1000).to_i.to_s,
-          "delta"    => new_resource.respond_to?(:diff) ? new_resource.diff : "",
-          "result"   => action.to_s,
-          "status"   => status,
+          "type"           => new_resource.resource_name.to_sym,
+          "name"           => new_resource.name.to_s,
+          "id"             => new_resource.identity.to_s,
+          "after"          => new_resource.state_for_resource_reporter,
+          "before"         => current_resource ? current_resource.state_for_resource_reporter : {},
+          "duration"       => elapsed_time_in_milliseconds.to_s,
+          "delta"          => new_resource.respond_to?(:diff) && potentially_changed? ? new_resource.diff : "",
+          "ignore_failure" => new_resource.ignore_failure,
+          "result"         => action.to_s,
+          "status"         => status,
         }
 
         if new_resource.cookbook_name
           hash["cookbook_name"]    = new_resource.cookbook_name
           hash["cookbook_version"] = new_resource.cookbook_version.version
+          hash["recipe_name"]      = new_resource.recipe_name
         end
 
         hash["conditional"]   = conditional.to_text if status == "skipped"
diff --git a/lib/chef/decorator/unchain.rb b/lib/chef/decorator/unchain.rb
new file mode 100644
index 0000000..8093c70
--- /dev/null
+++ b/lib/chef/decorator/unchain.rb
@@ -0,0 +1,59 @@
+class Chef
+  class Decorator < SimpleDelegator
+    #
+    # This decorator unchains method call chains and turns them into method calls
+    # with variable args.  So this:
+    #
+    #   node.set_unless["foo"]["bar"] = "baz"
+    #
+    # Can become:
+    #
+    #   node.set_unless("foo", "bar", "baz")
+    #
+    # While this is a decorator it is not a Decorator and does not inherit because
+    # it deliberately does not need or want the method_missing magic.  It is not legal
+    # to call anything on the intermediate values and only supports method chaining with
+    # #[] until the chain comes to an end with #[]=, so does not behave like a hash or
+    # array...  e.g.
+    #
+    #   node.default['foo'].keys is legal
+    #   node.set_unless['foo'].keys is not legal now or ever
+    #
+    class Unchain
+      attr_accessor :__path__
+      attr_accessor :__method__
+
+      def initialize(obj, method)
+        @__path__        = []
+        @__method__      = method
+        @delegate_sd_obj = obj
+      end
+
+      def [](key)
+        __path__.push(key)
+        self
+      end
+
+      def []=(key, value)
+        __path__.push(key)
+        @delegate_sd_obj.public_send(__method__, *__path__, value)
+      end
+
+      # unfortunately we have to support method_missing for node.set_unless.foo.bar = 'baz' notation
+      def method_missing(symbol, *args)
+        if symbol == :to_ary
+          merged_attributes.send(symbol, *args)
+        elsif args.empty?
+          Chef.log_deprecation %q{method access to node attributes (node.foo.bar) is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]["bar"])}
+          self[symbol]
+        elsif symbol.to_s =~ /=$/
+          Chef.log_deprecation %q{method setting of node attributes (node.foo="bar") is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]="bar")}
+          key_to_set = symbol.to_s[/^(.+)=$/, 1]
+          self[key_to_set] = (args.length == 1 ? args[0] : args)
+        else
+          raise NoMethodError, "Undefined node attribute or method `#{symbol}' on `node'"
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chef/dsl/cheffish.rb b/lib/chef/dsl/cheffish.rb
index de052bb..03290b3 100644
--- a/lib/chef/dsl/cheffish.rb
+++ b/lib/chef/dsl/cheffish.rb
@@ -27,6 +27,7 @@ class Chef
         chef_acl
         chef_client
         chef_container
+        chef_data_bag_item
         chef_data_bag
         chef_environment
         chef_group
diff --git a/lib/chef/dsl/declare_resource.rb b/lib/chef/dsl/declare_resource.rb
index 8d76ddf..86227a0 100644
--- a/lib/chef/dsl/declare_resource.rb
+++ b/lib/chef/dsl/declare_resource.rb
@@ -71,7 +71,15 @@ class Chef
       #   delete_resource!(:template, '/x/y.txy')
       #
       def delete_resource!(type, name, run_context: self.run_context)
-        run_context.resource_collection.delete("#{type}[#{name}]")
+        run_context.resource_collection.delete("#{type}[#{name}]").tap do |resource|
+          # Purge any pending notifications too. This will not raise an exception
+          # if there are no notifications.
+          if resource
+            run_context.before_notification_collection.delete(resource.declared_key)
+            run_context.immediate_notification_collection.delete(resource.declared_key)
+            run_context.delayed_notification_collection.delete(resource.declared_key)
+          end
+        end
       end
 
       # Lookup a resource in the resource collection by name and delete it.  Returns
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/dsl/powershell.rb
index 1a900af..7dc7a9a 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/dsl/powershell.rb
@@ -21,7 +21,7 @@ require "chef/util/powershell/ps_credential"
 class Chef
   module DSL
     module Powershell
-      def ps_credential(username = "placeholder", password)
+      def ps_credential(username = "placeholder", password) # rubocop:disable Style/OptionalArguments
         Chef::Util::Powershell::PSCredential.new(username, password)
       end
     end
diff --git a/lib/chef/event_dispatch/base.rb b/lib/chef/event_dispatch/base.rb
index 6c6b2fa..04c960c 100644
--- a/lib/chef/event_dispatch/base.rb
+++ b/lib/chef/event_dispatch/base.rb
@@ -232,11 +232,11 @@ class Chef
       end
 
       # Called after the recipe has been loaded
-      def recipe_file_loaded(path)
+      def recipe_file_loaded(path, recipe)
       end
 
       # Called after a recipe file fails to load
-      def recipe_file_load_failed(path, exception)
+      def recipe_file_load_failed(path, exception, recipe)
       end
 
       # Called when a recipe cannot be resolved
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index ea90d80..a4d5ff6 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -106,7 +106,12 @@ class Chef
     # for back compat, need to raise an error that inherits from ArgumentError
     class CookbookNotFoundInRepo < ArgumentError; end
     class RecipeNotFound < ArgumentError; end
+    # AttributeNotFound really means the attribute file could not be found
     class AttributeNotFound < RuntimeError; end
+    # NoSuchAttribute is raised on access by node.read!("foo", "bar") when node["foo"]["bar"] does not exist.
+    class NoSuchAttribute < RuntimeError; end
+    # AttributeTypeMismatch is raised by node.write!("foo", "bar", "baz") when e.g. node["foo"] = "bar" (overwriting String with Hash)
+    class AttributeTypeMismatch < RuntimeError; end
     class MissingCookbookDependency < StandardError; end # CHEF-5120
     class InvalidCommandOption < RuntimeError; end
     class CommandTimeout < RuntimeError; end
@@ -179,7 +184,7 @@ class Chef
 
     # A different version of a cookbook was added to a
     # VersionedRecipeList than the one already there.
-    class CookbookVersionConflict < ArgumentError ; end
+    class CookbookVersionConflict < ArgumentError; end
 
     # does not follow X.Y.Z format. ArgumentError?
     class InvalidPlatformVersion < ArgumentError; end
diff --git a/lib/chef/file_access_control/windows.rb b/lib/chef/file_access_control/windows.rb
index 6b7184b..6f1ac5f 100644
--- a/lib/chef/file_access_control/windows.rb
+++ b/lib/chef/file_access_control/windows.rb
@@ -128,7 +128,7 @@ class Chef
       end
 
       def should_update_dacl?
-        return true unless ::File.exists?(file)
+        return true unless ::File.exists?(file) || ::File.symlink?(file)
         dacl = target_dacl
         existing_dacl = existing_descriptor.dacl
         inherits = target_inherits
@@ -161,7 +161,7 @@ class Chef
       end
 
       def should_update_group?
-        return true unless ::File.exists?(file)
+        return true unless ::File.exists?(file) || ::File.symlink?(file)
         (group = target_group) && (group != existing_descriptor.group)
       end
 
@@ -180,7 +180,7 @@ class Chef
       end
 
       def should_update_owner?
-        return true unless ::File.exists?(file)
+        return true unless ::File.exists?(file) || ::File.symlink?(file)
         (owner = target_owner) && (owner != existing_descriptor.owner)
       end
 
diff --git a/lib/chef/file_cache.rb b/lib/chef/file_cache.rb
index cefc9da..8e9bb1e 100644
--- a/lib/chef/file_cache.rb
+++ b/lib/chef/file_cache.rb
@@ -85,7 +85,7 @@ class Chef
             File.join(create_cache_path(File.join(file_path_array), true), file_name)
           )
         else
-          raise RuntimeError, "Cannot move #{file} to #{path}!"
+          raise "Cannot move #{file} to #{path}!"
         end
       end
 
diff --git a/lib/chef/file_content_management/tempfile.rb b/lib/chef/file_content_management/tempfile.rb
index bba4b34..cf59a87 100644
--- a/lib/chef/file_content_management/tempfile.rb
+++ b/lib/chef/file_content_management/tempfile.rb
@@ -40,7 +40,8 @@ class Chef
 
         tempfile_dirnames.each do |tempfile_dirname|
           begin
-            tf = ::Tempfile.open(tempfile_basename, tempfile_dirname)
+            # preserving the file extension of the target filename should be considered a public API
+            tf = ::Tempfile.open([tempfile_basename, tempfile_extension], tempfile_dirname)
             break
           rescue SystemCallError => e
             message = "Creating temp file under '#{tempfile_dirname}' failed with: '#{e.message}'"
@@ -63,12 +64,21 @@ class Chef
       # as the arguments to Tempfile.new() consistently.
       #
       def tempfile_basename
-        basename = ::File.basename(@new_resource.name)
+        basename = ::File.basename(@new_resource.path, tempfile_extension)
+        # the leading "[.]chef-" here should be considered a public API and should not be changed
         basename.insert 0, "chef-"
         basename.insert 0, "." unless Chef::Platform.windows? # dotfile if we're not on windows
         basename
       end
 
+      # this is similar to File.extname() but greedy about the extension (from the first dot, not the last dot)
+      def tempfile_extension
+        # complexity here is due to supporting mangling non-UTF8 strings (e.g. latin-1 filenames with characters that are illegal in UTF-8)
+        b = File.basename(@new_resource.path)
+        i = b.index(".")
+        i.nil? ? "" : b[i..-1]
+      end
+
       # Returns the possible directories for the tempfile to be created in.
       def tempfile_dirnames
         # in why-run mode we need to create a Tempfile to compare against, which we will never
diff --git a/lib/chef/formatters/base.rb b/lib/chef/formatters/base.rb
index 3641e61..536bf72 100644
--- a/lib/chef/formatters/base.rb
+++ b/lib/chef/formatters/base.rb
@@ -203,12 +203,12 @@ class Chef
       end
 
       # Delegates to #file_loaded
-      def recipe_file_loaded(path)
+      def recipe_file_loaded(path, recipe)
         file_loaded(path)
       end
 
       # Delegates to #file_load_failed
-      def recipe_file_load_failed(path, exception)
+      def recipe_file_load_failed(path, exception, recipe)
         file_load_failed(path, exception)
       end
 
diff --git a/lib/chef/formatters/doc.rb b/lib/chef/formatters/doc.rb
index d43f199..7dbbf1d 100644
--- a/lib/chef/formatters/doc.rb
+++ b/lib/chef/formatters/doc.rb
@@ -32,9 +32,9 @@ class Chef
 
       def pretty_elapsed_time
         time = elapsed_time
-        if time < 60 then
+        if time < 60
           message = Time.at(time).utc.strftime("%S seconds")
-        elsif time < 3600 then
+        elsif time < 3600
           message = Time.at(time).utc.strftime("%M minutes %S seconds")
         else
           message = Time.at(time).utc.strftime("%H hours %M minutes %S seconds")
diff --git a/lib/chef/http.rb b/lib/chef/http.rb
index c6afa97..924081b 100644
--- a/lib/chef/http.rb
+++ b/lib/chef/http.rb
@@ -77,6 +77,9 @@ class Chef
 
     attr_reader :middlewares
 
+    # [Boolean] if we're doing keepalives or not
+    attr_reader :keepalives
+
     # Create a HTTP client object. The supplied +url+ is used as the base for
     # all subsequent requests. For example, when initialized with a base url
     # http://localhost:4000, a call to +get+ with 'nodes' will make an
@@ -87,6 +90,7 @@ class Chef
       @sign_on_redirect = true
       @redirects_followed = 0
       @redirect_limit = 10
+      @keepalives = options[:keepalives] || false
       @options = options
 
       @middlewares = []
@@ -228,23 +232,49 @@ class Chef
 
     def http_client(base_url = nil)
       base_url ||= url
+      if keepalives && !base_url.nil?
+        # only reuse the http_client if we want keepalives and have a base_url
+        @http_client ||= {}
+        # the per-host per-port cache here gets peristent connections correct when
+        # redirecting to different servers
+        if base_url.is_a?(String) # sigh, this kind of abuse can't happen with strongly typed languages
+          @http_client[base_url] ||= build_http_client(base_url)
+        else
+          @http_client[base_url.host] ||= {}
+          @http_client[base_url.host][base_url.port] ||= build_http_client(base_url)
+        end
+      else
+        build_http_client(base_url)
+      end
+    end
+
+    # DEPRECATED: This is only kept around to provide access to cache control data in
+    # lib/chef/provider/remote_file/http.rb
+    # FIXME: Find a better API.
+    def last_response
+      @last_response
+    end
+
+    private
+
+    # @api private
+    def build_http_client(base_url)
       if chef_zero_uri?(base_url)
         # PERFORMANCE CRITICAL: *MUST* lazy require here otherwise we load up webrick
         # via chef-zero and that hits DNS (at *require* time) which may timeout,
         # when for most knife/chef-client work we never need/want this loaded.
-        Thread.exclusive {
-          unless defined?(SocketlessChefZeroClient)
-            require "chef/http/socketless_chef_zero_client"
-          end
-        }
+
+        unless defined?(SocketlessChefZeroClient)
+          require "chef/http/socketless_chef_zero_client"
+        end
+
         SocketlessChefZeroClient.new(base_url)
       else
-        BasicClient.new(base_url, :ssl_policy => Chef::HTTP::APISSLPolicy)
+        BasicClient.new(base_url, ssl_policy: Chef::HTTP::APISSLPolicy, keepalives: keepalives)
       end
     end
 
-    protected
-
+    # @api private
     def create_url(path)
       return path if path.is_a?(URI)
       if path =~ /^(http|https|chefzero):\/\//i
@@ -259,6 +289,7 @@ class Chef
       end
     end
 
+    # @api private
     def apply_request_middleware(method, url, headers, data)
       middlewares.inject([method, url, headers, data]) do |req_data, middleware|
         Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_request")
@@ -266,6 +297,7 @@ class Chef
       end
     end
 
+    # @api private
     def apply_response_middleware(response, rest_request, return_value)
       middlewares.reverse.inject([response, rest_request, return_value]) do |res_data, middleware|
         Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_response")
@@ -273,6 +305,7 @@ class Chef
       end
     end
 
+    # @api private
     def apply_stream_complete_middleware(response, rest_request, return_value)
       middlewares.reverse.inject([response, rest_request, return_value]) do |res_data, middleware|
         Chef::Log.debug("Chef::HTTP calling #{middleware.class}#handle_stream_complete")
@@ -280,6 +313,7 @@ class Chef
       end
     end
 
+    # @api private
     def log_failed_request(response, return_value)
       return_value ||= {}
       error_message = "HTTP Request Returned #{response.code} #{response.message}: "
@@ -287,12 +321,14 @@ class Chef
       Chef::Log.info(error_message)
     end
 
+    # @api private
     def success_response?(response)
       response.kind_of?(Net::HTTPSuccess) || response.kind_of?(Net::HTTPRedirection)
     end
 
     # Runs a synchronous HTTP request, with no middleware applied (use #request
     # to have the middleware applied). The entire response will be loaded into memory.
+    # @api private
     def send_http_request(method, url, headers, body, &response_handler)
       headers = build_headers(method, url, headers, body)
 
@@ -328,6 +364,7 @@ class Chef
     # Wraps an HTTP request with retry logic.
     # === Arguments
     # url:: URL of the request, used for error messages
+    # @api private
     def retrying_http_errors(url)
       http_attempts = 0
       begin
@@ -377,18 +414,22 @@ class Chef
       end
     end
 
+    # @api private
     def http_retry_delay
       config[:http_retry_delay]
     end
 
+    # @api private
     def http_retry_count
       config[:http_retry_count]
     end
 
+    # @api private
     def config
       Chef::Config
     end
 
+    # @api private
     def follow_redirect
       raise Chef::Exceptions::RedirectLimitExceeded if @redirects_followed >= redirect_limit
       @redirects_followed += 1
@@ -399,13 +440,13 @@ class Chef
       @redirects_followed = 0
     end
 
-    private
-
+    # @api private
     def chef_zero_uri?(uri)
       uri = URI.parse(uri) unless uri.respond_to?(:scheme)
       uri.scheme == "chefzero"
     end
 
+    # @api private
     def redirected_to(response)
       return nil  unless response.kind_of?(Net::HTTPRedirection)
       # Net::HTTPNotModified is undesired subclass of Net::HTTPRedirection so test for this
@@ -413,6 +454,7 @@ class Chef
       response["location"]
     end
 
+    # @api private
     def build_headers(method, url, headers = {}, json_body = false)
       headers = @default_headers.merge(headers)
       headers["Content-Length"] = json_body.bytesize.to_s if json_body
@@ -420,6 +462,7 @@ class Chef
       headers
     end
 
+    # @api private
     def stream_to_tempfile(url, response, &progress_block)
       content_length = response["Content-Length"]
       tf = Tempfile.open("chef-rest")
@@ -443,18 +486,5 @@ class Chef
       raise
     end
 
-    public
-
-    ############################################################################
-    # DEPRECATED
-    ############################################################################
-
-    # This is only kept around to provide access to cache control data in
-    # lib/chef/provider/remote_file/http.rb
-    # Find a better API.
-    def last_response
-      @last_response
-    end
-
   end
 end
diff --git a/lib/chef/http/auth_credentials.rb b/lib/chef/http/auth_credentials.rb
index d5dbff3..053b2c9 100644
--- a/lib/chef/http/auth_credentials.rb
+++ b/lib/chef/http/auth_credentials.rb
@@ -49,7 +49,7 @@ class Chef
 
         sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(request_params)
         signed = sign_obj.sign(key).merge({ :host => host })
-        signed.inject({}) { |memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1];memo }
+        signed.inject({}) { |memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1]; memo }
       end
 
     end
diff --git a/lib/chef/http/basic_client.rb b/lib/chef/http/basic_client.rb
index 3a87fe8..460744e 100644
--- a/lib/chef/http/basic_client.rb
+++ b/lib/chef/http/basic_client.rb
@@ -34,6 +34,7 @@ class Chef
       attr_reader :url
       attr_reader :http_client
       attr_reader :ssl_policy
+      attr_reader :keepalives
 
       # Instantiate a BasicClient.
       # === Arguments:
@@ -43,7 +44,11 @@ class Chef
       def initialize(url, opts = {})
         @url = url
         @ssl_policy = opts[:ssl_policy] || DefaultSSLPolicy
-        @http_client = build_http_client
+        @keepalives = opts[:keepalives] || false
+      end
+
+      def http_client
+        @http_client ||= build_http_client
       end
 
       def host
@@ -114,7 +119,11 @@ class Chef
 
         http_client.read_timeout = config[:rest_timeout]
         http_client.open_timeout = config[:rest_timeout]
-        http_client
+        if keepalives
+          http_client.start
+        else
+          http_client
+        end
       end
 
       def config
diff --git a/lib/chef/http/socketless_chef_zero_client.rb b/lib/chef/http/socketless_chef_zero_client.rb
index 1acac5e..d2f1f45 100644
--- a/lib/chef/http/socketless_chef_zero_client.rb
+++ b/lib/chef/http/socketless_chef_zero_client.rb
@@ -197,9 +197,9 @@ class Chef
       private
 
       def headers_extracted_from_options
-        options.reject { |name, _| KNOWN_OPTIONS.include?(name) }.map { |name, value|
+        options.reject { |name, _| KNOWN_OPTIONS.include?(name) }.map do |name, value|
           [name.to_s.split("_").map { |segment| segment.capitalize }.join("-"), value]
-        }
+        end
       end
 
     end
diff --git a/lib/chef/key.rb b/lib/chef/key.rb
index 1c25c5d..38822e8 100644
--- a/lib/chef/key.rb
+++ b/lib/chef/key.rb
@@ -201,72 +201,71 @@ class Chef
       chef_rest.delete("#{api_base}/#{@actor}/keys/#{@name}")
     end
 
-    # Class methods
-    def self.from_hash(key_hash)
-      if key_hash.has_key?("user")
-        key = Chef::Key.new(key_hash["user"], "user")
-      elsif key_hash.has_key?("client")
-        key = Chef::Key.new(key_hash["client"], "client")
-      else
-        raise Chef::Exceptions::MissingKeyAttribute, "The hash passed to from_hash does not contain the key 'user' or 'client'. Please pass a hash that defines one of those keys."
+    class << self
+      def from_hash(key_hash)
+        if key_hash.has_key?("user")
+          key = Chef::Key.new(key_hash["user"], "user")
+        elsif key_hash.has_key?("client")
+          key = Chef::Key.new(key_hash["client"], "client")
+        else
+          raise Chef::Exceptions::MissingKeyAttribute, "The hash passed to from_hash does not contain the key 'user' or 'client'. Please pass a hash that defines one of those keys."
+        end
+        key.name key_hash["name"] if key_hash.key?("name")
+        key.public_key key_hash["public_key"] if key_hash.key?("public_key")
+        key.private_key key_hash["private_key"] if key_hash.key?("private_key")
+        key.create_key key_hash["create_key"] if key_hash.key?("create_key")
+        key.expiration_date key_hash["expiration_date"] if key_hash.key?("expiration_date")
+        key
       end
-      key.name key_hash["name"] if key_hash.key?("name")
-      key.public_key key_hash["public_key"] if key_hash.key?("public_key")
-      key.private_key key_hash["private_key"] if key_hash.key?("private_key")
-      key.create_key key_hash["create_key"] if key_hash.key?("create_key")
-      key.expiration_date key_hash["expiration_date"] if key_hash.key?("expiration_date")
-      key
-    end
-
-    def self.from_json(json)
-      Chef::Key.from_hash(Chef::JSONCompat.from_json(json))
-    end
 
-    def self.json_create(json)
-      Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::Key#from_json or one of the load_by methods.")
-      Chef::Key.from_json(json)
-    end
+      def from_json(json)
+        Chef::Key.from_hash(Chef::JSONCompat.from_json(json))
+      end
 
-    def self.list_by_user(actor, inflate = false)
-      keys = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("users/#{actor}/keys")
-      self.list(keys, actor, :load_by_user, inflate)
-    end
+      def json_create(json)
+        Chef.log_deprecation("Auto inflation of JSON data is deprecated. Please use Chef::Key#from_json or one of the load_by methods.")
+        Chef::Key.from_json(json)
+      end
 
-    def self.list_by_client(actor, inflate = false)
-      keys = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("clients/#{actor}/keys")
-      self.list(keys, actor, :load_by_client, inflate)
-    end
+      def list_by_user(actor, inflate = false)
+        keys = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("users/#{actor}/keys")
+        self.list(keys, actor, :load_by_user, inflate)
+      end
 
-    def self.load_by_user(actor, key_name)
-      response = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("users/#{actor}/keys/#{key_name}")
-      Chef::Key.from_hash(response.merge({ "user" => actor }))
-    end
+      def list_by_client(actor, inflate = false)
+        keys = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("clients/#{actor}/keys")
+        self.list(keys, actor, :load_by_client, inflate)
+      end
 
-    def self.load_by_client(actor, key_name)
-      response = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("clients/#{actor}/keys/#{key_name}")
-      Chef::Key.from_hash(response.merge({ "client" => actor }))
-    end
+      def load_by_user(actor, key_name)
+        response = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get("users/#{actor}/keys/#{key_name}")
+        Chef::Key.from_hash(response.merge({ "user" => actor }))
+      end
 
-    def self.generate_fingerprint(public_key)
-      openssl_key_object = OpenSSL::PKey::RSA.new(public_key)
-      data_string = OpenSSL::ASN1::Sequence([
-                                              OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.n),
-                                              OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.e),
-                                            ])
-      OpenSSL::Digest::SHA1.hexdigest(data_string.to_der).scan(/../).join(":")
-    end
+      def load_by_client(actor, key_name)
+        response = Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("clients/#{actor}/keys/#{key_name}")
+        Chef::Key.from_hash(response.merge({ "client" => actor }))
+      end
 
-    private
+      def generate_fingerprint(public_key)
+        openssl_key_object = OpenSSL::PKey::RSA.new(public_key)
+        data_string = OpenSSL::ASN1::Sequence([
+          OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.n),
+          OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.e),
+        ])
+        OpenSSL::Digest::SHA1.hexdigest(data_string.to_der).scan(/../).join(":")
+      end
 
-    def self.list(keys, actor, load_method_symbol, inflate)
-      if inflate
-        keys.inject({}) do |key_map, result|
-          name = result["name"]
-          key_map[name] = Chef::Key.send(load_method_symbol, actor, name)
-          key_map
+      def list(keys, actor, load_method_symbol, inflate)
+        if inflate
+          keys.inject({}) do |key_map, result|
+            name = result["name"]
+            key_map[name] = Chef::Key.send(load_method_symbol, actor, name)
+            key_map
+          end
+        else
+          keys
         end
-      else
-        keys
       end
     end
   end
diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb
index 356bf47..c9ecfbf 100644
--- a/lib/chef/knife.rb
+++ b/lib/chef/knife.rb
@@ -233,61 +233,66 @@ class Chef
       end
     end
 
-    private
-
     OFFICIAL_PLUGINS = %w{ec2 rackspace windows openstack terremark bluebox}
 
-    def self.path_from_caller(caller_line)
-      caller_line.split(/:\d+/).first
-    end
-
-    # :nodoc:
-    # Error out and print usage. probably because the arguments given by the
-    # user could not be resolved to a subcommand.
-    def self.subcommand_not_found!(args)
-      ui.fatal("Cannot find subcommand for: '#{args.join(' ')}'")
+    class << self
+      private
 
-      # Mention rehash when the subcommands cache(plugin_manifest.json) is used
-      if subcommand_loader.is_a?(Chef::Knife::SubcommandLoader::HashedCommandLoader) ||
-          subcommand_loader.is_a?(Chef::Knife::SubcommandLoader::CustomManifestLoader)
-        ui.info("If this is a recently installed plugin, please run 'knife rehash' to update the subcommands cache.")
+      # @api private
+      def path_from_caller(caller_line)
+        caller_line.split(/:\d+/).first
       end
 
-      if category_commands = guess_category(args)
-        list_commands(category_commands)
-      elsif missing_plugin = ( OFFICIAL_PLUGINS.find { |plugin| plugin == args[0] } )
-        ui.info("The #{missing_plugin} commands were moved to plugins in Chef 0.10")
-        ui.info("You can install the plugin with `(sudo) gem install knife-#{missing_plugin}`")
-        ui.info("Use `chef gem install knife-#{missing_plugin}` instead if using ChefDK")
-      else
-        list_commands
-      end
+      # :nodoc:
+      # Error out and print usage. probably because the arguments given by the
+      # user could not be resolved to a subcommand.
+      # @api private
+      def subcommand_not_found!(args)
+        ui.fatal("Cannot find subcommand for: '#{args.join(' ')}'")
+
+        # Mention rehash when the subcommands cache(plugin_manifest.json) is used
+        if subcommand_loader.is_a?(Chef::Knife::SubcommandLoader::HashedCommandLoader) ||
+            subcommand_loader.is_a?(Chef::Knife::SubcommandLoader::CustomManifestLoader)
+          ui.info("If this is a recently installed plugin, please run 'knife rehash' to update the subcommands cache.")
+        end
 
-      exit 10
-    end
+        if category_commands = guess_category(args)
+          list_commands(category_commands)
+        elsif missing_plugin = ( OFFICIAL_PLUGINS.find { |plugin| plugin == args[0] } )
+          ui.info("The #{missing_plugin} commands were moved to plugins in Chef 0.10")
+          ui.info("You can install the plugin with `(sudo) gem install knife-#{missing_plugin}`")
+          ui.info("Use `chef gem install knife-#{missing_plugin}` instead if using ChefDK")
+        else
+          list_commands
+        end
+
+        exit 10
+      end
 
-    def self.list_commands(preferred_category = nil)
-      category_desc = preferred_category ? preferred_category + " " : ""
-      msg "Available #{category_desc}subcommands: (for details, knife SUB-COMMAND --help)\n\n"
-      subcommand_loader.list_commands(preferred_category).sort.each do |category, commands|
-        next if category =~ /deprecated/i
-        msg "** #{category.upcase} COMMANDS **"
-        commands.sort.each do |command|
-          subcommand_loader.load_command(command)
-          msg subcommands[command].banner if subcommands[command]
+      # @api private
+      def list_commands(preferred_category = nil)
+        category_desc = preferred_category ? preferred_category + " " : ""
+        msg "Available #{category_desc}subcommands: (for details, knife SUB-COMMAND --help)\n\n"
+        subcommand_loader.list_commands(preferred_category).sort.each do |category, commands|
+          next if category =~ /deprecated/i
+          msg "** #{category.upcase} COMMANDS **"
+          commands.sort.each do |command|
+            subcommand_loader.load_command(command)
+            msg subcommands[command].banner if subcommands[command]
+          end
+          msg
         end
-        msg
       end
-    end
 
-    def self.reset_config_path!
-      @@chef_config_dir = nil
+      # @api private
+      def reset_config_path!
+        @@chef_config_dir = nil
+      end
+
     end
 
     reset_config_path!
 
-    public
-
     # Create a new instance of the current class configured for the given
     # arguments and options
     def initialize(argv = [])
@@ -324,31 +329,35 @@ class Chef
       exit(1)
     end
 
-    # Returns a subset of the Chef::Config[:knife] Hash that is relevant to the
-    # currently executing knife command. This is used by #configure_chef to
-    # apply settings from knife.rb to the +config+ hash.
+    # keys from mixlib-cli options
+    def cli_keys
+      self.class.options.keys
+    end
+
+    # extracts the settings from the Chef::Config[:knife] sub-hash that correspond
+    # to knife cli options -- in preparation for merging config values with cli values
+    #
+    # NOTE: due to weirdness in mixlib-config #has_key? is only true if the value has
+    # been set by the user -- the Chef::Config defaults return #has_key?() of false and
+    # this code DEPENDS on that functionality since applying the default values in
+    # Chef::Config[:knife] would break the defaults in the cli that we would otherwise
+    # overwrite.
     def config_file_settings
-      config_file_settings = {}
-      self.class.options.keys.each do |key|
-        config_file_settings[key] = Chef::Config[:knife][key] if Chef::Config[:knife].has_key?(key)
+      cli_keys.each_with_object({}) do |key, memo|
+        memo[key] = Chef::Config[:knife][key] if Chef::Config[:knife].has_key?(key)
       end
-      config_file_settings
     end
 
-    # Apply Config in this order:
-    # defaults from mixlib-cli
-    # settings from config file, via Chef::Config[:knife]
-    # config from command line
+    # config is merged in this order (inverse of precedence)
+    #  default_config       - mixlib-cli defaults (accessor from the mixin)
+    #  config_file_settings - Chef::Config[:knife] sub-hash
+    #  config               - mixlib-cli settings (accessor from the mixin)
     def merge_configs
-      # Apply config file settings on top of mixlib-cli defaults
-      combined_config = default_config.merge(config_file_settings)
-      # Apply user-supplied options on top of the above combination
-      combined_config = combined_config.merge(config)
-      # replace the config hash from mixlib-cli with our own.
-      # Need to use the mutate-in-place #replace method instead of assigning to
-      # the instance variable because other code may have a reference to the
-      # original config hash object.
-      config.replace(combined_config)
+      # other code may have a handle to the config object, so use Hash#replace to deliberately
+      # update-in-place.
+      config.replace(
+        default_config.merge(config_file_settings).merge(config)
+      )
     end
 
     # Catch-all method that does any massaging needed for various config
diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb
index f5dc293..ee4d9ce 100644
--- a/lib/chef/knife/bootstrap.rb
+++ b/lib/chef/knife/bootstrap.rb
@@ -101,6 +101,14 @@ class Chef
         :description => "The proxy server for the node being bootstrapped",
         :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
 
+      option :bootstrap_proxy_user,
+        :long => "--bootstrap-proxy-user PROXY_USER",
+        :description => "The proxy authentication username for the node being bootstrapped"
+
+      option :bootstrap_proxy_pass,
+        :long => "--bootstrap-proxy-pass PROXY_PASS",
+        :description => "The proxy authentication password for the node being bootstrapped"
+
       option :bootstrap_no_proxy,
         :long => "--bootstrap-no-proxy [NO_PROXY_URL|NO_PROXY_IP]",
         :description => "Do not proxy locations for the node being bootstrapped; this option is used internally by Opscode",
@@ -224,6 +232,7 @@ class Chef
           unless valid_values.include?(v)
             raise "Invalid value '#{v}' for --node-ssl-verify-mode. Valid values are: #{valid_values.join(", ")}"
           end
+          v
         }
 
       option :node_verify_api_cert,
diff --git a/lib/chef/knife/client_delete.rb b/lib/chef/knife/client_delete.rb
index e1b2365..08cdf6c 100644
--- a/lib/chef/knife/client_delete.rb
+++ b/lib/chef/knife/client_delete.rb
@@ -43,7 +43,7 @@ class Chef
           exit 1
         end
 
-        delete_object(Chef::ApiClientV1, @client_name, "client") {
+        delete_object(Chef::ApiClientV1, @client_name, "client") do
           object = Chef::ApiClientV1.load(@client_name)
           if object.validator
             unless config[:delete_validators]
@@ -52,7 +52,7 @@ class Chef
             end
           end
           object.destroy
-        }
+        end
       end
 
     end
diff --git a/lib/chef/knife/cookbook_bulk_delete.rb b/lib/chef/knife/cookbook_bulk_delete.rb
index bd1c8a2..cdd1584 100644
--- a/lib/chef/knife/cookbook_bulk_delete.rb
+++ b/lib/chef/knife/cookbook_bulk_delete.rb
@@ -42,7 +42,7 @@ class Chef
 
         all_cookbooks = Chef::CookbookVersion.list
         cookbooks_names = all_cookbooks.keys.grep(regex)
-        cookbooks_to_delete = cookbooks_names.inject({}) { |hash, name| hash[name] = all_cookbooks[name];hash }
+        cookbooks_to_delete = cookbooks_names.inject({}) { |hash, name| hash[name] = all_cookbooks[name]; hash }
         ui.msg "All versions of the following cookbooks will be deleted:"
         ui.msg ""
         ui.msg ui.list(cookbooks_to_delete.keys.sort, :columns_down)
diff --git a/lib/chef/knife/cookbook_create.rb b/lib/chef/knife/cookbook_create.rb
index 950de38..ccb78bb 100644
--- a/lib/chef/knife/cookbook_create.rb
+++ b/lib/chef/knife/cookbook_create.rb
@@ -56,6 +56,10 @@ class Chef
         :description => "Email address of cookbook maintainer"
 
       def run
+        Chef::Log.deprecation <<EOF
+This command is being deprecated in favor of `chef generate cookbook` and will soon return an error.
+Please use `chef generate cookbook` instead of this command.
+EOF
         self.config = Chef::Config.merge!(config)
         if @name_args.length < 1
           show_usage
diff --git a/lib/chef/knife/cookbook_show.rb b/lib/chef/knife/cookbook_show.rb
index a20e62f..d0c930d 100644
--- a/lib/chef/knife/cookbook_show.rb
+++ b/lib/chef/knife/cookbook_show.rb
@@ -63,7 +63,7 @@ class Chef
           node[:platform_version] = config[:platform_version] if config.has_key?(:platform_version)
 
           class << node
-            def attribute?(name)
+            def attribute?(name) # rubocop:disable Lint/NestedMethodDefinition
               has_key?(name)
             end
           end
diff --git a/lib/chef/knife/cookbook_site_download.rb b/lib/chef/knife/cookbook_site_download.rb
index 7d0e217..43677cf 100644
--- a/lib/chef/knife/cookbook_site_download.rb
+++ b/lib/chef/knife/cookbook_site_download.rb
@@ -37,6 +37,13 @@ class Chef
         :long => "--force",
         :description => "Force download deprecated version"
 
+      option :supermarket_site,
+        :short => "-m SUPERMARKET_SITE",
+        :long => "--supermarket-site SUPERMARKET_SITE",
+        :description => "Supermarket Site",
+        :default => "https://supermarket.chef.io",
+        :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
       def run
         if current_cookbook_deprecated?
           message = "DEPRECATION: This cookbook has been deprecated. "
@@ -59,7 +66,7 @@ class Chef
       private
 
       def cookbooks_api_url
-        "https://supermarket.chef.io/api/v1/cookbooks"
+        "#{config[:supermarket_site]}/api/v1/cookbooks"
       end
 
       def current_cookbook_data
diff --git a/lib/chef/knife/cookbook_site_install.rb b/lib/chef/knife/cookbook_site_install.rb
index 45f3061..43d015d 100644
--- a/lib/chef/knife/cookbook_site_install.rb
+++ b/lib/chef/knife/cookbook_site_install.rb
@@ -19,6 +19,7 @@
 require "chef/knife"
 require "chef/exceptions"
 require "shellwords"
+require "mixlib/archive"
 
 class Chef
   class Knife
@@ -59,6 +60,13 @@ class Chef
         :boolean => true,
         :default => false
 
+      option :supermarket_site,
+        :short => "-m SUPERMARKET_SITE",
+        :long => "--supermarket-site SUPERMARKET_SITE",
+        :description => "Supermarket Site",
+        :default => "https://supermarket.chef.io",
+        :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
       attr_reader :cookbook_name
       attr_reader :vendor_path
 
@@ -134,6 +142,7 @@ class Chef
       def download_cookbook_to(download_path)
         downloader = Chef::Knife::CookbookSiteDownload.new
         downloader.config[:file] = download_path
+        downloader.config[:supermarket_site] = config[:supermarket_site]
         downloader.name_args = name_args
         downloader.run
         downloader
@@ -141,17 +150,7 @@ class Chef
 
       def extract_cookbook(upstream_file, version)
         ui.info("Uncompressing #{@cookbook_name} version #{version}.")
-        extract_command = "tar zxvf \"#{convert_path upstream_file}\""
-        if Chef::Platform.windows?
-          tar_version = shell_out("tar --version").stdout.tr("\n", " ")
-          if tar_version =~ /GNU tar/
-            Chef::Log.debug("GNU tar detected, adding --force-local")
-            extract_command << " --force-local"
-          else
-            Chef::Log.debug("non-GNU tar detected, not adding --force-local")
-          end
-        end
-        shell_out!(extract_command, :cwd => @install_path)
+        Mixlib::Archive.new(convert_path(upstream_file)).extract(@install_path, perms: false)
       end
 
       def clear_existing_files(cookbook_path)
diff --git a/lib/chef/knife/cookbook_site_list.rb b/lib/chef/knife/cookbook_site_list.rb
index abe48bf..3bdef8a 100644
--- a/lib/chef/knife/cookbook_site_list.rb
+++ b/lib/chef/knife/cookbook_site_list.rb
@@ -30,6 +30,13 @@ class Chef
         :long => "--with-uri",
         :description => "Show corresponding URIs"
 
+      option :supermarket_site,
+        :short => "-m SUPERMARKET_SITE",
+        :long => "--supermarket-site SUPERMARKET_SITE",
+        :description => "Supermarket Site",
+        :default => "https://supermarket.chef.io",
+        :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
       def run
         if config[:with_uri]
           cookbooks = Hash.new
@@ -41,7 +48,7 @@ class Chef
       end
 
       def get_cookbook_list(items = 10, start = 0, cookbook_collection = {})
-        cookbooks_url = "https://supermarket.chef.io/api/v1/cookbooks?items=#{items}&start=#{start}"
+        cookbooks_url = "#{config[:supermarket_site]}/api/v1/cookbooks?items=#{items}&start=#{start}"
         cr = noauth_rest.get(cookbooks_url)
         cr["items"].each do |cookbook|
           cookbook_collection[cookbook["cookbook_name"]] = cookbook
diff --git a/lib/chef/knife/cookbook_site_search.rb b/lib/chef/knife/cookbook_site_search.rb
index ba4b873..d401844 100644
--- a/lib/chef/knife/cookbook_site_search.rb
+++ b/lib/chef/knife/cookbook_site_search.rb
@@ -24,12 +24,19 @@ class Chef
       banner "knife cookbook site search QUERY (options)"
       category "cookbook site"
 
+      option :supermarket_site,
+        :short => "-m SUPERMARKET_SITE",
+        :long => "--supermarket-site SUPERMARKET_SITE",
+        :description => "Supermarket Site",
+        :default => "https://supermarket.chef.io",
+        :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
       def run
         output(search_cookbook(name_args[0]))
       end
 
       def search_cookbook(query, items = 10, start = 0, cookbook_collection = {})
-        cookbooks_url = "https://supermarket.chef.io/api/v1/search?q=#{query}&items=#{items}&start=#{start}"
+        cookbooks_url = "#{config[:supermarket_site]}/api/v1/search?q=#{query}&items=#{items}&start=#{start}"
         cr = noauth_rest.get(cookbooks_url)
         cr["items"].each do |cookbook|
           cookbook_collection[cookbook["cookbook_name"]] = cookbook
diff --git a/lib/chef/knife/cookbook_site_share.rb b/lib/chef/knife/cookbook_site_share.rb
index 6f37568..d55d6c1 100644
--- a/lib/chef/knife/cookbook_site_share.rb
+++ b/lib/chef/knife/cookbook_site_share.rb
@@ -50,6 +50,13 @@ class Chef
         :default => false,
         :description => "Don't take action, only print what files will be uploaded to Supermarket."
 
+      option :supermarket_site,
+        :short => "-m SUPERMARKET_SITE",
+        :long => "--supermarket-site SUPERMARKET_SITE",
+        :description => "Supermarket Site",
+        :default => "https://supermarket.chef.io",
+        :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
       def run
         config[:cookbook_path] ||= Chef::Config[:cookbook_path]
 
@@ -106,23 +113,17 @@ class Chef
       end
 
       def get_category(cookbook_name)
-        begin
-          data = noauth_rest.get("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}")
-          if !data["category"] && data["error_code"]
-            ui.fatal("Received an error from Supermarket: #{data["error_code"]}. On the first time you upload it, you are required to specify the category you want to share this cookbook to.")
-            exit(1)
-          else
-            data["category"]
-          end
-        rescue => e
-          ui.fatal("Unable to reach Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.")
-          Chef::Log.debug("\n#{e.backtrace.join("\n")}")
-          exit(1)
-        end
+        data = noauth_rest.get("#{config[:supermarket_site]}/api/v1/cookbooks/#{@name_args[0]}")
+        data["category"]
+      rescue => e
+        return "Other" if e.kind_of?(Net::HTTPServerException) && e.response.code == "404"
+        ui.fatal("Unable to reach Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.")
+        Chef::Log.debug("\n#{e.backtrace.join("\n")}")
+        exit(1)
       end
 
       def do_upload(cookbook_filename, cookbook_category, user_id, user_secret_filename)
-        uri = "https://supermarket.chef.io/api/v1/cookbooks"
+        uri = "#{config[:supermarket_site]}/api/v1/cookbooks"
 
         category_string = Chef::JSONCompat.to_json({ "category" => cookbook_category })
 
diff --git a/lib/chef/knife/cookbook_site_show.rb b/lib/chef/knife/cookbook_site_show.rb
index c0280cb..ce153ca 100644
--- a/lib/chef/knife/cookbook_site_show.rb
+++ b/lib/chef/knife/cookbook_site_show.rb
@@ -24,21 +24,32 @@ class Chef
       banner "knife cookbook site show COOKBOOK [VERSION] (options)"
       category "cookbook site"
 
+      option :supermarket_site,
+        :short => "-m SUPERMARKET_SITE",
+        :long => "--supermarket-site SUPERMARKET_SITE",
+        :description => "Supermarket Site",
+        :default => "https://supermarket.chef.io",
+        :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
       def run
         output(format_for_display(get_cookbook_data))
       end
 
+      def supermarket_uri
+        "#{config[:supermarket_site]}/api/v1"
+      end
+
       def get_cookbook_data
         case @name_args.length
         when 1
-          noauth_rest.get("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}")
+          noauth_rest.get("#{supermarket_uri}/cookbooks/#{@name_args[0]}")
         when 2
-          noauth_rest.get("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}/versions/#{name_args[1].tr('.', '_')}")
+          noauth_rest.get("#{supermarket_uri}/cookbooks/#{@name_args[0]}/versions/#{name_args[1].tr('.', '_')}")
         end
       end
 
       def get_cookbook_list(items = 10, start = 0, cookbook_collection = {})
-        cookbooks_url = "https://supermarket.chef.io/api/v1/cookbooks?items=#{items}&start=#{start}"
+        cookbooks_url = "#{supermarket_uri}/cookbooks?items=#{items}&start=#{start}"
         cr = noauth_rest.get(cookbooks_url)
         cr["items"].each do |cookbook|
           cookbook_collection[cookbook["cookbook_name"]] = cookbook
diff --git a/lib/chef/knife/cookbook_site_unshare.rb b/lib/chef/knife/cookbook_site_unshare.rb
index 310f6ac..bdabff0 100644
--- a/lib/chef/knife/cookbook_site_unshare.rb
+++ b/lib/chef/knife/cookbook_site_unshare.rb
@@ -30,6 +30,13 @@ class Chef
       banner "knife cookbook site unshare COOKBOOK"
       category "cookbook site"
 
+      option :supermarket_site,
+        :short => "-m SUPERMARKET_SITE",
+        :long => "--supermarket-site SUPERMARKET_SITE",
+        :description => "Supermarket Site",
+        :default => "https://supermarket.chef.io",
+        :proc => Proc.new { |supermarket| Chef::Config[:knife][:supermarket_site] = supermarket }
+
       def run
         @cookbook_name = @name_args[0]
         if @cookbook_name.nil?
@@ -41,7 +48,7 @@ class Chef
         confirm "Do you really want to unshare all versions of the cookbook #{@cookbook_name}"
 
         begin
-          rest.delete "https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}"
+          rest.delete "#{config[:supermarket_site]}/api/v1/cookbooks/#{@name_args[0]}"
         rescue Net::HTTPServerException => e
           raise e unless e.message =~ /Forbidden/
           ui.error "Forbidden: You must be the maintainer of #{@cookbook_name} to unshare it."
diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb
index 48d2cb9..b2670f1 100644
--- a/lib/chef/knife/core/bootstrap_context.rb
+++ b/lib/chef/knife/core/bootstrap_context.rb
@@ -54,7 +54,7 @@ class Chef
         end
 
         def client_d
-          @cliend_d ||= client_d_content
+          @client_d ||= client_d_content
         end
 
         def encrypted_data_bag_secret
@@ -114,6 +114,16 @@ validation_client_name "#{@chef_config[:validation_client_name]}"
             client_rb << %Q{https_proxy       "#{knife_config[:bootstrap_proxy]}"\n}
           end
 
+          if knife_config[:bootstrap_proxy_user]
+            client_rb << %Q{http_proxy_user   "#{knife_config[:bootstrap_proxy_user]}"\n}
+            client_rb << %Q{https_proxy_user  "#{knife_config[:bootstrap_proxy_user]}"\n}
+          end
+
+          if knife_config[:bootstrap_proxy_pass]
+            client_rb << %Q{http_proxy_pass   "#{knife_config[:bootstrap_proxy_pass]}"\n}
+            client_rb << %Q{https_proxy_pass  "#{knife_config[:bootstrap_proxy_pass]}"\n}
+          end
+
           if knife_config[:bootstrap_no_proxy]
             client_rb << %Q{no_proxy       "#{knife_config[:bootstrap_no_proxy]}"\n}
           end
diff --git a/lib/chef/knife/core/gem_glob_loader.rb b/lib/chef/knife/core/gem_glob_loader.rb
index f5eff26..34c5c53 100644
--- a/lib/chef/knife/core/gem_glob_loader.rb
+++ b/lib/chef/knife/core/gem_glob_loader.rb
@@ -81,9 +81,9 @@ class Chef
           files = []
 
           if check_load_path
-            files = $LOAD_PATH.map { |load_path|
+            files = $LOAD_PATH.map do |load_path|
               Dir["#{File.expand_path glob, Chef::Util::PathHelper.escape_glob_dir(load_path)}#{Gem.suffix_pattern}"]
-            }.flatten.select { |file| File.file? file.untaint }
+            end.flatten.select { |file| File.file? file.untaint }
           end
 
           gem_files = latest_gem_specs.map do |spec|
@@ -110,7 +110,7 @@ class Chef
         end
 
         def check_spec_for_glob(spec, glob)
-          dirs = if spec.require_paths.size > 1 then
+          dirs = if spec.require_paths.size > 1
                    "{#{spec.require_paths.join(',')}}"
                  else
                    spec.require_paths.first
diff --git a/lib/chef/knife/search.rb b/lib/chef/knife/search.rb
index 38d1ab3..d102c1e 100644
--- a/lib/chef/knife/search.rb
+++ b/lib/chef/knife/search.rb
@@ -18,6 +18,7 @@
 
 require "chef/knife"
 require "chef/knife/core/node_presenter"
+require "addressable/uri"
 
 class Chef
   class Knife
@@ -85,8 +86,7 @@ class Chef
         end
 
         q = Chef::Search::Query.new
-        escaped_query = URI.escape(@query,
-                           Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
+        escaped_query = Addressable::URI.encode_component(@query, Addressable::URI::CharacterClasses::QUERY)
 
         result_items = []
         result_count = 0
diff --git a/lib/chef/knife/ssh.rb b/lib/chef/knife/ssh.rb
index 2bbcbfc..6f266b2 100644
--- a/lib/chef/knife/ssh.rb
+++ b/lib/chef/knife/ssh.rb
@@ -164,9 +164,7 @@ class Chef
       end
 
       def configure_session
-        list = config[:manual] ?
-               @name_args[0].split(" ") :
-               search_nodes
+        list = config[:manual] ? @name_args[0].split(" ") : search_nodes
         if list.length == 0
           if @action_nodes.length == 0
             ui.fatal("No nodes returned from search")
@@ -548,24 +546,24 @@ class Chef
         configure_session
 
         exit_status =
-        case @name_args[1]
-        when "interactive"
-          interactive
-        when "screen"
-          screen
-        when "tmux"
-          tmux
-        when "macterm"
-          macterm
-        when "cssh"
-          cssh
-        when "csshx"
-          Chef::Log.warn("knife ssh csshx will be deprecated in a future release")
-          Chef::Log.warn("please use knife ssh cssh instead")
-          cssh
-        else
-          ssh_command(@name_args[1..-1].join(" "))
-        end
+          case @name_args[1]
+          when "interactive"
+            interactive
+          when "screen"
+            screen
+          when "tmux"
+            tmux
+          when "macterm"
+            macterm
+          when "cssh"
+            cssh
+          when "csshx"
+            Chef::Log.warn("knife ssh csshx will be deprecated in a future release")
+            Chef::Log.warn("please use knife ssh cssh instead")
+            cssh
+          else
+            ssh_command(@name_args[1..-1].join(" "))
+          end
 
         session.close
         if exit_status != 0
diff --git a/lib/chef/knife/status.rb b/lib/chef/knife/status.rb
index 551d5ad..0e3cd7e 100644
--- a/lib/chef/knife/status.rb
+++ b/lib/chef/knife/status.rb
@@ -96,13 +96,13 @@ class Chef
           all_nodes << node
         end
 
-        output(all_nodes.sort { |n1, n2|
+        output(all_nodes.sort do |n1, n2|
           if config[:sort_reverse] || Chef::Config[:knife][:sort_status_reverse]
             (n2["ohai_time"] || 0) <=> (n1["ohai_time"] || 0)
           else
             (n1["ohai_time"] || 0) <=> (n2["ohai_time"] || 0)
           end
-        })
+        end)
       end
 
     end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/knife/supermarket_download.rb
similarity index 55%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/knife/supermarket_download.rb
index 1a900af..5657558 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/knife/supermarket_download.rb
@@ -1,6 +1,6 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Author:: Christopher Webber (<cwebber at chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,18 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/knife"
+require "chef/knife/cookbook_site_download"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+  class Knife
+    class SupermarketDownload < Knife::CookbookSiteDownload
+      # Handle the subclassing (knife doesn't do this :()
+      dependency_loaders.concat(superclass.dependency_loaders)
+      options.merge!(superclass.options)
+
+      banner "knife supermarket download COOKBOOK [VERSION] (options)"
+      category "supermarket"
     end
   end
 end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/knife/supermarket_install.rb
similarity index 55%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/knife/supermarket_install.rb
index 1a900af..7642e68 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/knife/supermarket_install.rb
@@ -1,6 +1,6 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Author:: Christopher Webber (<cwebber at chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,18 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/knife"
+require "chef/knife/cookbook_site_install"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+  class Knife
+    class SupermarketInstall < Knife::CookbookSiteInstall
+      # Handle the subclassing (knife doesn't do this :()
+      dependency_loaders.concat(superclass.dependency_loaders)
+      options.merge!(superclass.options)
+
+      banner "knife supermarket install COOKBOOK [VERSION] (options)"
+      category "supermarket"
     end
   end
 end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/knife/supermarket_list.rb
similarity index 56%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/knife/supermarket_list.rb
index 1a900af..f2bc98b 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/knife/supermarket_list.rb
@@ -1,6 +1,6 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Author:: Christopher Webber (<cwebber at chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,18 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/knife"
+require "chef/knife/cookbook_site_list"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+  class Knife
+    class SupermarketList < Knife::CookbookSiteList
+      # Handle the subclassing (knife doesn't do this :()
+      dependency_loaders.concat(superclass.dependency_loaders)
+      options.merge!(superclass.options)
+
+      banner "knife supermarket list (options)"
+      category "supermarket"
     end
   end
 end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/knife/supermarket_search.rb
similarity index 56%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/knife/supermarket_search.rb
index 1a900af..3206b0c 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/knife/supermarket_search.rb
@@ -1,6 +1,6 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Author:: Christopher Webber (<cwebber at chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,18 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/knife"
+require "chef/knife/cookbook_site_search"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+  class Knife
+    class SupermarketSearch < Knife::CookbookSiteSearch
+      # Handle the subclassing (knife doesn't do this :()
+      dependency_loaders.concat(superclass.dependency_loaders)
+      options.merge!(superclass.options)
+
+      banner "knife supermarket search QUERY (options)"
+      category "supermarket"
     end
   end
 end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/knife/supermarket_share.rb
similarity index 55%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/knife/supermarket_share.rb
index 1a900af..3109b9e 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/knife/supermarket_share.rb
@@ -1,6 +1,6 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Author:: Christopher Webber (<cwebber at chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,18 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/knife"
+require "chef/knife/cookbook_site_share"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+  class Knife
+    class SupermarketShare < Knife::CookbookSiteShare
+      # Handle the subclassing (knife doesn't do this :()
+      dependency_loaders.concat(superclass.dependency_loaders)
+      options.merge!(superclass.options)
+
+      banner "knife supermarket share COOKBOOK [CATEGORY] (options)"
+      category "supermarket"
     end
   end
 end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/knife/supermarket_show.rb
similarity index 55%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/knife/supermarket_show.rb
index 1a900af..2ad1221 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/knife/supermarket_show.rb
@@ -1,6 +1,6 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Author:: Christopher Webber (<cwebber at chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,18 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/knife"
+require "chef/knife/cookbook_site_show"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+  class Knife
+    class SupermarketShow < Knife::CookbookSiteShow
+      # Handle the subclassing (knife doesn't do this :()
+      dependency_loaders.concat(superclass.dependency_loaders)
+      options.merge!(superclass.options)
+
+      banner "knife supermarket show COOKBOOK [VERSION] (options)"
+      category "supermarket"
     end
   end
 end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/knife/supermarket_unshare.rb
similarity index 55%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/knife/supermarket_unshare.rb
index 1a900af..fd48e17 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/knife/supermarket_unshare.rb
@@ -1,6 +1,6 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Author:: Christopher Webber (<cwebber at chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,14 +16,18 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/knife"
+require "chef/knife/cookbook_site_unshare"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+  class Knife
+    class SupermarketUnshare < Knife::CookbookSiteUnshare
+      # Handle the subclassing (knife doesn't do this :()
+      dependency_loaders.concat(superclass.dependency_loaders)
+      options.merge!(superclass.options)
+
+      banner "knife supermarket unshare COOKBOOK (options)"
+      category "supermarket"
     end
   end
 end
diff --git a/lib/chef/mixin/command/unix.rb b/lib/chef/mixin/command/unix.rb
index bfd5079..aa541c3 100644
--- a/lib/chef/mixin/command/unix.rb
+++ b/lib/chef/mixin/command/unix.rb
@@ -64,7 +64,7 @@ class Chef
             $VERBOSE = nil
             ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
 
-            cid = fork {
+            cid = fork do
               pw.last.close
               STDIN.reopen pw.first
               pw.first.close
@@ -111,7 +111,7 @@ class Chef
               end
               ps.last.close unless ps.last.closed?
               exit!
-            }
+            end
           ensure
             $VERBOSE = verbose
           end
diff --git a/lib/chef/mixin/powershell_type_coercions.rb b/lib/chef/mixin/powershell_type_coercions.rb
index 381cbed..6159c87 100644
--- a/lib/chef/mixin/powershell_type_coercions.rb
+++ b/lib/chef/mixin/powershell_type_coercions.rb
@@ -63,7 +63,7 @@ class Chef
       end
 
       def unsafe?(s)
-        ["'", '#', "`", '"'].any? do |x|
+        ["'", "#", "`", '"'].any? do |x|
           s.include? x
         end
       end
diff --git a/lib/chef/mixin/properties.rb b/lib/chef/mixin/properties.rb
index ae2406f..8ff2cc4 100644
--- a/lib/chef/mixin/properties.rb
+++ b/lib/chef/mixin/properties.rb
@@ -79,6 +79,9 @@ class Chef
         #     part of desired state. Defaults to `true`.
         #   @option options [Boolean] :identity `true` if this property
         #     is part of object identity. Defaults to `false`.
+        #   @option options [Boolean] :sensitive `true` if this property could
+        #     contain sensitive information and whose value should be redacted
+        #     in any resource reporting / auditing output. Defaults to `false`.
         #
         # @example Bare property
         #   property :x
diff --git a/lib/chef/mixin/securable.rb b/lib/chef/mixin/securable.rb
index a88d534..55b4e0a 100644
--- a/lib/chef/mixin/securable.rb
+++ b/lib/chef/mixin/securable.rb
@@ -43,7 +43,7 @@ class Chef
           :mode,
           arg,
           :callbacks => {
-            "not in valid numeric range" => lambda { |m|
+            "not in valid numeric range" => lambda do |m|
               if m.kind_of?(String)
                 m =~ /^0/ || m = "0#{m}"
               end
@@ -54,7 +54,7 @@ class Chef
               else
                 Integer(m) <= 07777 && Integer(m) >= 0
               end
-            },
+            end,
           }
         )
       end
diff --git a/lib/chef/mixin/shell_out.rb b/lib/chef/mixin/shell_out.rb
index 1f7deb2..a258a91 100644
--- a/lib/chef/mixin/shell_out.rb
+++ b/lib/chef/mixin/shell_out.rb
@@ -78,6 +78,36 @@ class Chef
         return my_command_args
       end
 
+      # Helper for sublcasses to convert an array of string args into a string.  It
+      # will compact nil or empty strings in the array and will join the array elements
+      # with spaces, without introducing any double spaces for nil/empty elements.
+      #
+      # @param args [String] variable number of string arguments
+      # @return [String] nicely concatenated string or empty string
+      def a_to_s(*args)
+        clean_array(*args).join(" ")
+      end
+
+      # Helper for sublcasses to reject nil and empty strings out of an array.  It allows
+      # using the array form of shell_out (which avoids the need to surround arguments with
+      # quote marks to deal with shells).
+      #
+      # Usage:
+      #   shell_out!(*clean_array("useradd", universal_options, useradd_options, new_resource.username))
+      #
+      # universal_options and useradd_options can be nil, empty array, empty string, strings or arrays
+      # and the result makes sense.
+      #
+      # keeping this separate from shell_out!() makes it a bit easier to write expectations against the
+      # shell_out args and be able to omit nils and such in the tests (and to test that the nils are
+      # being rejected correctly).
+      #
+      # @param args [String] variable number of string arguments
+      # @return [Array] array of strings with nil and null string rejection
+      def clean_array(*args)
+        args.flatten.reject { |i| i.nil? || i == "" }.map(&:to_s)
+      end
+
       private
 
       def shell_out_command(*command_args)
diff --git a/lib/chef/mixin/uris.rb b/lib/chef/mixin/uris.rb
index 24e8a4f..7dc04d6 100644
--- a/lib/chef/mixin/uris.rb
+++ b/lib/chef/mixin/uris.rb
@@ -17,6 +17,7 @@
 #
 
 require "uri"
+require "addressable/uri"
 
 class Chef
   module Mixin
@@ -34,7 +35,7 @@ class Chef
           URI.parse(source)
         rescue URI::InvalidURIError
           Chef::Log.warn("#{source} was an invalid URI. Trying to escape invalid characters")
-          URI.parse(URI.escape(source))
+          URI.parse(Addressable::URI.encode(source))
         end
       end
 
diff --git a/lib/chef/mixin/windows_architecture_helper.rb b/lib/chef/mixin/windows_architecture_helper.rb
index 11e1b55..49252af 100644
--- a/lib/chef/mixin/windows_architecture_helper.rb
+++ b/lib/chef/mixin/windows_architecture_helper.rb
@@ -74,16 +74,15 @@ class Chef
 
       def node_supports_windows_architecture?(node, desired_architecture)
         assert_valid_windows_architecture!(desired_architecture)
-        return (node_windows_architecture(node) == :x86_64 ||
-                desired_architecture == :i386) ? true : false
+        return ( node_windows_architecture(node) == :x86_64 ) || ( desired_architecture == :i386 )
       end
 
       def valid_windows_architecture?(architecture)
-        return (architecture == :x86_64) || (architecture == :i386)
+        return ( architecture == :x86_64 ) || ( architecture == :i386 )
       end
 
       def assert_valid_windows_architecture!(architecture)
-        if ! valid_windows_architecture?(architecture)
+        if !valid_windows_architecture?(architecture)
           raise Chef::Exceptions::Win32ArchitectureIncorrect,
           "The specified architecture was not valid. It must be one of :i386 or :x86_64"
         end
diff --git a/lib/chef/monkey_patches/webrick-utils.rb b/lib/chef/monkey_patches/webrick-utils.rb
index 7880adb..ccca582 100644
--- a/lib/chef/monkey_patches/webrick-utils.rb
+++ b/lib/chef/monkey_patches/webrick-utils.rb
@@ -31,7 +31,7 @@ module WEBrick
                                 Socket::AI_PASSIVE)  # flag
       last_error = nil
       sockets = []
-      res.each {|ai|
+      res.each do |ai|
         begin
           logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
           sock = TCPServer.new(ai[3], port)
@@ -42,7 +42,7 @@ module WEBrick
           logger.warn("TCPServer Error: #{ex}") if logger
           last_error = ex
         end
-      }
+      end
       raise last_error if sockets.empty?
       return sockets
     end
diff --git a/lib/chef/monologger.rb b/lib/chef/monologger.rb
index 8bcdae1..82816e8 100644
--- a/lib/chef/monologger.rb
+++ b/lib/chef/monologger.rb
@@ -61,7 +61,7 @@ class MonoLogger < Logger
       @dev.close rescue nil
     end
 
-  private
+    private
 
     def open_logfile(filename)
       if FileTest.exist?(filename)
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index 8d77bec..212b1ce 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -43,6 +43,8 @@ class Chef
     def_delegators :attributes, :keys, :each_key, :each_value, :key?, :has_key?
     def_delegators :attributes, :rm, :rm_default, :rm_normal, :rm_override
     def_delegators :attributes, :default!, :normal!, :override!, :force_default!, :force_override!
+    def_delegators :attributes, :default_unless, :normal_unless, :override_unless, :set_unless
+    def_delegators :attributes, :read, :read!, :write, :write!, :unlink, :unlink!
 
     attr_accessor :recipe_list, :run_state, :override_runlist
 
@@ -196,35 +198,18 @@ class Chef
     # might be missing
     def normal
       attributes.top_level_breadcrumb = nil
-      attributes.set_unless_value_present = false
       attributes.normal
     end
 
-    alias_method :set, :normal
-
-    # Set a normal attribute of this node, auto-vivifying any mashes that are
-    # missing, but if the final value already exists, don't set it
-    def normal_unless
-      attributes.top_level_breadcrumb = nil
-      attributes.set_unless_value_present = true
-      attributes.normal
+    def set
+      Chef.log_deprecation("node.set is deprecated and will be removed in Chef 14, please use node.default/node.override (or node.normal only if you really need persistence)")
+      normal
     end
 
-    alias_method :set_unless, :normal_unless
-
     # Set a default of this node, but auto-vivify any Mashes that might
     # be missing
     def default
       attributes.top_level_breadcrumb = nil
-      attributes.set_unless_value_present = false
-      attributes.default
-    end
-
-    # Set a default attribute of this node, auto-vivifying any mashes that are
-    # missing, but if the final value already exists, don't set it
-    def default_unless
-      attributes.top_level_breadcrumb = nil
-      attributes.set_unless_value_present = true
       attributes.default
     end
 
@@ -232,15 +217,6 @@ class Chef
     # might be missing
     def override
       attributes.top_level_breadcrumb = nil
-      attributes.set_unless_value_present = false
-      attributes.override
-    end
-
-    # Set an override attribute of this node, auto-vivifying any mashes that
-    # are missing, but if the final value already exists, don't set it
-    def override_unless
-      attributes.top_level_breadcrumb = nil
-      attributes.set_unless_value_present = true
       attributes.override
     end
 
@@ -262,7 +238,6 @@ class Chef
 
     def automatic_attrs
       attributes.top_level_breadcrumb = nil
-      attributes.set_unless_value_present = false
       attributes.automatic
     end
 
@@ -290,8 +265,14 @@ class Chef
     end
 
     # Only works for attribute fetches, setting is no longer supported
-    def method_missing(symbol, *args)
-      attributes.send(symbol, *args)
+    # XXX: this should be deprecated
+    def method_missing(method, *args, &block)
+      attributes.public_send(method, *args, &block)
+    end
+
+    # Fix respond_to + method so that it works with method_missing delegation
+    def respond_to_missing?(method, include_private = false)
+      attributes.respond_to?(method, false)
     end
 
     # Returns true if this Node expects a given recipe, false if not.
@@ -661,8 +642,8 @@ class Chef
       end
     end
 
-    def <=>(other_node)
-      self.name <=> other_node.name
+    def <=>(other)
+      self.name <=> other.name
     end
 
     private
diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb
index ab97cf9..95b3b09 100644
--- a/lib/chef/node/attribute.rb
+++ b/lib/chef/node/attribute.rb
@@ -19,6 +19,7 @@
 
 require "chef/node/immutable_collections"
 require "chef/node/attribute_collections"
+require "chef/decorator/unchain"
 require "chef/mixin/deep_merge"
 require "chef/log"
 
@@ -132,6 +133,7 @@ class Chef
        :take,
        :take_while,
        :to_a,
+       :to_h,
        :to_hash,
        :to_set,
        :value?,
@@ -187,8 +189,6 @@ class Chef
       attr_accessor :deep_merge_cache
 
       def initialize(normal, default, override, automatic)
-        @set_unless_present = false
-
         @default = VividMash.new(self, default)
         @env_default = VividMash.new(self, {})
         @role_default = VividMash.new(self, {})
@@ -214,15 +214,13 @@ class Chef
        # attribute you're interested in. For example, to debug where the value
        # of `node[:network][:default_interface]` is coming from, use:
        #   debug_value(:network, :default_interface).
-       # The return value is an Array of Arrays. The first element is
-       # `["set_unless_enabled?", Boolean]`, which describes whether the
-       # attribute collection is in "set_unless" mode. The rest of the Arrays
+       # The return value is an Array of Arrays.  The Arrays
        # are pairs of `["precedence_level", value]`, where precedence level is
        # the component, such as role default, normal, etc. and value is the
        # attribute value set at that precedence level. If there is no value at
        # that precedence level, +value+ will be the symbol +:not_present+.
       def debug_value(*args)
-        components = COMPONENTS.map do |component|
+        COMPONENTS.map do |component|
           ivar = instance_variable_get(component)
           value = args.inject(ivar) do |so_far, key|
             if so_far == :not_present
@@ -235,12 +233,6 @@ class Chef
           end
           [component.to_s.sub(/^@/, ""), value]
         end
-        [["set_unless_enabled?", @set_unless_present]] + components
-      end
-
-       # Enables or disables `||=`-like attribute setting. See, e.g., Node#set_unless
-      def set_unless_value_present=(setting)
-        @set_unless_present = setting
       end
 
        # Invalidate a key in the deep_merge_cache.  If called with nil, or no arg, this will invalidate
@@ -321,94 +313,134 @@ class Chef
 
        # clears attributes from all precedence levels
       def rm(*args)
-        reset(args[0])
-        # just easier to compute our retval, rather than collect+merge sub-retvals
-        ret = args.inject(merged_attributes) do |attr, arg|
-          if attr.nil? || !attr.respond_to?(:[])
-            nil
-          else
-            begin
-              attr[arg]
-            rescue TypeError
-              raise TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)"
-            end
-          end
+        with_deep_merged_return_value(self, *args) do
+          rm_default(*args)
+          rm_normal(*args)
+          rm_override(*args)
         end
-        rm_default(*args)
-        rm_normal(*args)
-        rm_override(*args)
-        ret
       end
 
-       # does <level>['foo']['bar'].delete('baz')
-      def remove_from_precedence_level(level, *args, key)
-        multimash = level.element(*args)
-        multimash.nil? ? nil : multimash.delete(key)
-      end
-
-      private :remove_from_precedence_level
-
        # clears attributes from all default precedence levels
        #
-       # equivalent to: force_default!['foo']['bar'].delete('baz')
+       # similar to: force_default!['foo']['bar'].delete('baz')
+       # - does not autovivify
+       # - does not trainwreck if interior keys do not exist
       def rm_default(*args)
-        reset(args[0])
-        remove_from_precedence_level(force_default!(autovivify: false), *args)
+        with_deep_merged_return_value(combined_default, *args) do
+          default.unlink(*args)
+          role_default.unlink(*args)
+          env_default.unlink(*args)
+          force_default.unlink(*args)
+        end
       end
 
        # clears attributes from normal precedence
        #
        # equivalent to: normal!['foo']['bar'].delete('baz')
+       # - does not autovivify
+       # - does not trainwreck if interior keys do not exist
       def rm_normal(*args)
-        reset(args[0])
-        remove_from_precedence_level(normal!(autovivify: false), *args)
+        normal.unlink(*args)
       end
 
        # clears attributes from all override precedence levels
        #
        # equivalent to: force_override!['foo']['bar'].delete('baz')
+       # - does not autovivify
+       # - does not trainwreck if interior keys do not exist
       def rm_override(*args)
-        reset(args[0])
-        remove_from_precedence_level(force_override!(autovivify: false), *args)
+        with_deep_merged_return_value(combined_override, *args) do
+          override.unlink(*args)
+          role_override.unlink(*args)
+          env_override.unlink(*args)
+          force_override.unlink(*args)
+        end
+      end
+
+      def with_deep_merged_return_value(obj, *path, last)
+        hash = obj.read(*path)
+        return nil unless hash.is_a?(Hash)
+        ret = hash[last]
+        yield
+        ret
       end
 
+      private :with_deep_merged_return_value
+
        #
        # Replacing attributes without merging
        #
 
        # sets default attributes without merging
-      def default!(opts = {})
-        # FIXME: do not flush whole cache
-        reset
-        MultiMash.new(self, @default, [], opts)
+       #
+       # - this API autovivifies (and cannot trainwreck)
+      def default!(*args)
+        return Decorator::Unchain.new(self, :default!) unless args.length > 0
+        write(:default, *args)
       end
 
        # sets normal attributes without merging
-      def normal!(opts = {})
-        # FIXME: do not flush whole cache
-        reset
-        MultiMash.new(self, @normal, [], opts)
+       #
+       # - this API autovivifies (and cannot trainwreck)
+      def normal!(*args)
+        return Decorator::Unchain.new(self, :normal!) unless args.length > 0
+        write(:normal, *args)
       end
 
        # sets override attributes without merging
-      def override!(opts = {})
-        # FIXME: do not flush whole cache
-        reset
-        MultiMash.new(self, @override, [], opts)
+       #
+       # - this API autovivifies (and cannot trainwreck)
+      def override!(*args)
+        return Decorator::Unchain.new(self, :override!) unless args.length > 0
+        write(:override, *args)
       end
 
        # clears from all default precedence levels and then sets force_default
-      def force_default!(opts = {})
-        # FIXME: do not flush whole cache
-        reset
-        MultiMash.new(self, @force_default, [@default, @env_default, @role_default], opts)
+       #
+       # - this API autovivifies (and cannot trainwreck)
+      def force_default!(*args)
+        return Decorator::Unchain.new(self, :force_default!) unless args.length > 0
+        value = args.pop
+        rm_default(*args)
+        write(:force_default, *args, value)
       end
 
        # clears from all override precedence levels and then sets force_override
-      def force_override!(opts = {})
-        # FIXME: do not flush whole cache
-        reset
-        MultiMash.new(self, @force_override, [@override, @env_override, @role_override], opts)
+      def force_override!(*args)
+        return Decorator::Unchain.new(self, :force_override!) unless args.length > 0
+        value = args.pop
+        rm_override(*args)
+        write(:force_override, *args, value)
+      end
+
+      # method-style access to attributes
+
+      def read(*path)
+        merged_attributes.read(*path)
+      end
+
+      def read!(*path)
+        merged_attributes.read!(*path)
+      end
+
+      def exist?(*path)
+        merged_attributes.exist?(*path)
+      end
+
+      def write(level, *args, &block)
+        self.send(level).write(*args, &block)
+      end
+
+      def write!(level, *args, &block)
+        self.send(level).write!(*args, &block)
+      end
+
+      def unlink(level, *path)
+        self.send(level).unlink(*path)
+      end
+
+      def unlink!(level, *path)
+        self.send(level).unlink!(*path)
       end
 
        #
@@ -420,9 +452,9 @@ class Chef
        #
 
       def merged_attributes(*path)
-       # immutablize(
+        # immutablize(
         merge_all(path)
-       # )
+        # )
       end
 
       def combined_override(*path)
@@ -433,6 +465,27 @@ class Chef
         immutablize(merge_defaults(path))
       end
 
+      def normal_unless(*args)
+        return Decorator::Unchain.new(self, :normal_unless) unless args.length > 0
+        write(:normal, *args) if read(*args[0...-1]).nil?
+      end
+
+      def default_unless(*args)
+        return Decorator::Unchain.new(self, :default_unless) unless args.length > 0
+        write(:default, *args) if read(*args[0...-1]).nil?
+      end
+
+      def override_unless(*args)
+        return Decorator::Unchain.new(self, :override_unless) unless args.length > 0
+        write(:override, *args) if read(*args[0...-1]).nil?
+      end
+
+      def set_unless(*args)
+        Chef.log_deprecation("node.set_unless is deprecated and will be removed in Chef 14, please use node.default_unless/node.override_unless (or node.normal_unless if you really need persistence)")
+        return Decorator::Unchain.new(self, :default_unless) unless args.length > 0
+        write(:normal, *args) if read(*args[0...-1]).nil?
+      end
+
       def [](key)
         if deep_merge_cache.has_key?(key.to_s)
           # return the cache of the deep merged values by top-level key
@@ -461,13 +514,17 @@ class Chef
       alias :each_attribute :each
 
       def method_missing(symbol, *args)
-        if args.empty?
+        if symbol == :to_ary
+          merged_attributes.send(symbol, *args)
+        elsif args.empty?
+          Chef.log_deprecation %q{method access to node attributes (node.foo.bar) is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]["bar"])}
           if key?(symbol)
             self[symbol]
           else
             raise NoMethodError, "Undefined method or attribute `#{symbol}' on `node'"
           end
         elsif symbol.to_s =~ /=$/
+          Chef.log_deprecation %q{method setting of node attributes (node.foo="bar") is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]="bar")}
           key_to_set = symbol.to_s[/^(.+)=$/, 1]
           self[key_to_set] = (args.length == 1 ? args[0] : args)
         else
@@ -480,16 +537,12 @@ class Chef
       end
 
       def inspect
-        "#<#{self.class} " << (COMPONENTS + [:@merged_attributes, :@properties]).map {|iv|
+        "#<#{self.class} " << (COMPONENTS + [:@merged_attributes, :@properties]).map do |iv|
           "#{iv}=#{instance_variable_get(iv).inspect}"
-        }.join(", ") << ">"
-      end
-
-      def set_unless?
-        @set_unless_present
+        end.join(", ") << ">"
       end
 
-       private
+      private
 
        # Helper method for merge_all/merge_defaults/merge_overrides.
        #
diff --git a/lib/chef/node/attribute_collections.rb b/lib/chef/node/attribute_collections.rb
index 68f3a69..b739ea8 100644
--- a/lib/chef/node/attribute_collections.rb
+++ b/lib/chef/node/attribute_collections.rb
@@ -16,15 +16,15 @@
 # limitations under the License.
 #
 
+require "chef/node/common_api"
+
 class Chef
   class Node
-
     # == AttrArray
     # AttrArray is identical to Array, except that it keeps a reference to the
     # "root" (Chef::Node::Attribute) object, and will trigger a cache
     # invalidation on that object when mutated.
     class AttrArray < Array
-
       MUTATOR_METHODS = [
         :<<,
         :[]=,
@@ -62,8 +62,9 @@ class Chef
       # Node::Attribute object.
       MUTATOR_METHODS.each do |mutator|
         define_method(mutator) do |*args, &block|
+          ret = super(*args, &block)
           root.reset_cache(root.top_level_breadcrumb)
-          super(*args, &block)
+          ret
         end
       end
 
@@ -96,14 +97,12 @@ class Chef
     #   in the creation of a new VividMash for that key. (This only works when
     #   using the element reference method, `[]` -- other methods, such as
     #   #fetch, work as normal).
-    # * It supports a set_unless flag (via the root Attribute object) which
-    #   allows `||=` style behavior (`||=` does not work with
-    #   auto-vivification). This is only implemented for #[]=; methods such as
-    #   #store work as normal.
     # * attr_accessor style element set and get are supported via method_missing
     class VividMash < Mash
       attr_reader :root
 
+      include CommonAPI
+
       # Methods that mutate a VividMash. Each of them is overridden so that it
       # also invalidates the cached merged_attributes on the root Attribute
       # object.
@@ -148,12 +147,9 @@ class Chef
 
       def []=(key, value)
         root.top_level_breadcrumb ||= key
-        if set_unless? && key?(key) && !self[key].nil?
-          self[key]
-        else
-          root.reset_cache(root.top_level_breadcrumb)
-          super
-        end
+        ret = super
+        root.reset_cache(root.top_level_breadcrumb)
+        ret
       end
 
       alias :attribute? :has_key?
@@ -176,10 +172,6 @@ class Chef
         end
       end
 
-      def set_unless?
-        @root.set_unless?
-      end
-
       def convert_key(key)
         super
       end
@@ -206,118 +198,5 @@ class Chef
       end
 
     end
-
-    # == MultiMash
-    # This is a Hash-like object that contains multiple VividMashes in it.  Its
-    # purpose is so that the user can descend into the mash and delete a subtree
-    # from all of the Mash objects (used to delete all values in a subtree from
-    # default, force_default, role_default and env_default at the same time).  The
-    # assignment operator strictly does assignment (does no merging) and works
-    # by deleting the subtree and then assigning to the last mash which passed in
-    # the initializer.
-    #
-    # A lot of the complexity of this class comes from the fact that at any key
-    # value some or all of the mashes may walk off their ends and become nil or
-    # true or something.  The schema may change so that one precidence leve may
-    # be 'true' object and another may be a VividMash.  It is also possible that
-    # one or many of them may transition from VividMashes to Hashes or Arrays.
-    #
-    # It also supports the case where you may be deleting a key using node.rm
-    # in which case if intermediate keys all walk off into nil then you don't want
-    # to be autovivifying keys as you go.  On the other hand you may be using
-    # node.force_default! in which case you'll wind up with a []= operator at the
-    # end and you want autovivification, so we conditionally have to support either
-    # operation.
-    #
-    # @todo: can we have an autovivify class that decorates a class that doesn't
-    # autovivify or something so that the code is less awful?
-    #
-    class MultiMash
-      attr_reader :root
-      attr_reader :mashes
-      attr_reader :opts
-      attr_reader :primary_mash
-
-      # Initialize with an array of mashes.  For the delete return value to work
-      # properly the mashes must come from the same attribute level (i.e. all
-      # override or all default, but not a mix of both).
-      def initialize(root, primary_mash, mashes, opts = {})
-        @root = root
-        @primary_mash = primary_mash
-        @mashes = mashes
-        @opts = opts
-        @opts[:autovivify] = true if @opts[:autovivify].nil?
-      end
-
-      def [](key)
-        # handle the secondary mashes
-        new_mashes = []
-        mashes.each do |mash|
-          new_mash = safe_evalute_key(mash, key)
-          # secondary mashes never autovivify so once they fall into nil, we just stop tracking them
-          new_mashes.push(new_mash) unless new_mash.nil?
-        end
-
-        new_primary_mash = safe_evalute_key(primary_mash, key)
-
-        if new_primary_mash.nil? && @opts[:autovivify]
-          primary_mash[key] = VividMash.new(root)
-          new_primary_mash = primary_mash[key]
-        end
-
-        MultiMash.new(root, new_primary_mash, new_mashes, opts)
-      end
-
-      def []=(key, value)
-        if primary_mash.nil?
-          # This theoretically should never happen since node#force_default! setter methods will autovivify and
-          # node#rm methods do not end in #[]= operators.
-          raise TypeError, "No autovivification was specified initially on a method chain ending in assignment"
-        end
-        ret = delete(key)
-        primary_mash[key] = value
-        ret
-      end
-
-      # mash.element('foo', 'bar') is the same as mash['foo']['bar']
-      def element(key = nil, *subkeys)
-        return self if key.nil?
-        submash = self[key]
-        subkeys.empty? ? submash : submash.element(*subkeys)
-      end
-
-      def delete(key)
-        # the return value is a deep merge which is correct semantics when
-        # merging between attributes on the same level (this would be incorrect
-        # if passed both override and default attributes which would need hash_only
-        # merging).
-        ret = mashes.inject(Mash.new) do |merged, mash|
-          Chef::Mixin::DeepMerge.merge(merged, mash)
-        end
-        ret = Chef::Mixin::DeepMerge.merge(ret, primary_mash)
-        mashes.each do |mash|
-          mash.delete(key) if mash.respond_to?(:delete)
-        end
-        primary_mash.delete(key) if primary_mash.respond_to?(:delete)
-        ret[key]
-      end
-
-      private
-
-      def safe_evalute_key(mash, key)
-        if mash.respond_to?(:[])
-          if mash.respond_to?(:has_key?)
-            if mash.has_key?(key)
-              return mash[key] if mash[key].respond_to?(:[])
-            end
-          elsif !mash[key].nil?
-            return mash[key] if mash[key].respond_to?(:[])
-          end
-        end
-        return nil
-      end
-
-    end
-
   end
 end
diff --git a/lib/chef/node/common_api.rb b/lib/chef/node/common_api.rb
new file mode 100644
index 0000000..ce2c6b6
--- /dev/null
+++ b/lib/chef/node/common_api.rb
@@ -0,0 +1,129 @@
+#--
+# Copyright:: Copyright 2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+  class Node
+    # shared API between VividMash and ImmutableMash, writer code can be
+    # 'shared' to keep it logically in this file by adding them to the
+    # block list in ImmutableMash.
+    module CommonAPI
+      # method-style access to attributes
+
+      def valid_container?(obj, key)
+        return obj.is_a?(Hash) || (obj.is_a?(Array) && key.is_a?(Fixnum))
+      end
+
+      private :valid_container?
+
+      # - autovivifying / autoreplacing writer
+      # - non-container-ey intermediate objects are replaced with hashes
+      def write(*args, &block)
+        root.top_level_breadcrumb = nil if respond_to?(:root)
+        value = block_given? ? yield : args.pop
+        last = args.pop
+        prev_memo = prev_key = nil
+        chain = args.inject(self) do |memo, key|
+          if !valid_container?(memo, key)
+            prev_memo[prev_key] = {}
+            memo = prev_memo[prev_key]
+          end
+          prev_memo = memo
+          prev_key = key
+          memo[key]
+        end
+        if !valid_container?(chain, last)
+          prev_memo[prev_key] = {}
+          chain = prev_memo[prev_key]
+        end
+        chain[last] = value
+      end
+
+      # this autovivifies, but can throw NoSuchAttribute when trying to access #[] on
+      # something that is not a container ("schema violation" issues).
+      #
+      def write!(*args, &block)
+        root.top_level_breadcrumb = nil if respond_to?(:root)
+        value = block_given? ? yield : args.pop
+        last = args.pop
+        obj = args.inject(self) do |memo, key|
+          raise Chef::Exceptions::AttributeTypeMismatch unless valid_container?(memo, key)
+          memo[key]
+        end
+        raise Chef::Exceptions::AttributeTypeMismatch unless valid_container?(obj, last)
+        obj[last] = value
+      end
+
+      # FIXME:(?) does anyone need a non-autovivifying writer for attributes that throws exceptions?
+
+      # return true or false based on if the attribute exists
+      def exist?(*path)
+        root.top_level_breadcrumb = nil if respond_to?(:root)
+        path.inject(self) do |memo, key|
+          return false unless valid_container?(memo, key)
+          if memo.is_a?(Hash)
+            if memo.key?(key)
+              memo[key]
+            else
+              return false
+            end
+          elsif memo.is_a?(Array)
+            if memo.length > key
+              memo[key]
+            else
+              return false
+            end
+          end
+        end
+        return true
+      end
+
+      # this is a safe non-autovivifying reader that returns nil if the attribute does not exist
+      def read(*path)
+        begin
+          read!(*path)
+        rescue Chef::Exceptions::NoSuchAttribute
+          nil
+        end
+      end
+
+      # non-autovivifying reader that throws an exception if the attribute does not exist
+      def read!(*path)
+        raise Chef::Exceptions::NoSuchAttribute unless exist?(*path)
+        root.top_level_breadcrumb = nil if respond_to?(:root)
+        path.inject(self) do |memo, key|
+          memo[key]
+        end
+      end
+
+      # FIXME:(?) does anyone really like the autovivifying reader that we have and wants the same behavior?  readers that write?  ugh...
+
+      def unlink(*path, last)
+        root.top_level_breadcrumb = nil if respond_to?(:root)
+        hash = path.empty? ? self : read(*path)
+        return nil unless hash.is_a?(Hash) || hash.is_a?(Array)
+        root.top_level_breadcrumb ||= last
+        hash.delete(last)
+      end
+
+      def unlink!(*path)
+        raise Chef::Exceptions::NoSuchAttribute unless exist?(*path)
+        unlink(*path)
+      end
+
+    end
+  end
+end
diff --git a/lib/chef/node/immutable_collections.rb b/lib/chef/node/immutable_collections.rb
index b5fd86f..d4623ac 100644
--- a/lib/chef/node/immutable_collections.rb
+++ b/lib/chef/node/immutable_collections.rb
@@ -1,3 +1,21 @@
+#--
+# Copyright:: Copyright 2012-2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/node/common_api"
 
 class Chef
   class Node
@@ -124,6 +142,7 @@ class Chef
     class ImmutableMash < Mash
 
       include Immutablize
+      include CommonAPI
 
       alias :internal_set :[]=
       private :internal_set
@@ -144,6 +163,10 @@ class Chef
         :replace,
         :select!,
         :shift,
+        :write,
+        :write!,
+        :unlink,
+        :unlink!,
       ]
 
       def initialize(mash_data)
@@ -167,13 +190,15 @@ class Chef
       end
 
       def method_missing(symbol, *args)
-        if args.empty?
+        if symbol == :to_ary
+          super
+        elsif args.empty?
           if key?(symbol)
             self[symbol]
           else
             raise NoMethodError, "Undefined method or attribute `#{symbol}' on `node'"
           end
-        # This will raise a ImmutableAttributeModification error:
+          # This will raise a ImmutableAttributeModification error:
         elsif symbol.to_s =~ /=$/
           key_to_set = symbol.to_s[/^(.+)=$/, 1]
           self[key_to_set] = (args.length == 1 ? args[0] : args)
diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb
index 4047424..bc565d9 100644
--- a/lib/chef/platform/provider_mapping.rb
+++ b/lib/chef/platform/provider_mapping.rb
@@ -197,8 +197,11 @@ class Chef
 
       def resource_matching_provider(platform, version, resource_type)
         if resource_type.kind_of?(Chef::Resource)
-          class_name = resource_type.class.name ? resource_type.class.name.split("::").last :
-            convert_to_class_name(resource_type.resource_name.to_s)
+          class_name = if resource_type.class.name
+                         resource_type.class.name.split("::").last
+                       else
+                         convert_to_class_name(resource_type.resource_name.to_s)
+                       end
 
           if Chef::Provider.const_defined?(class_name, false)
             Chef::Log.warn("Class Chef::Provider::#{class_name} does not declare 'provides #{convert_to_snake_case(class_name).to_sym.inspect}'.")
diff --git a/lib/chef/property.rb b/lib/chef/property.rb
index 45ab4dd..a357ba9 100644
--- a/lib/chef/property.rb
+++ b/lib/chef/property.rb
@@ -230,14 +230,25 @@ class Chef
     end
 
     #
+    # Whether this property is sensitive or not.
+    #
+    # Defaults to false.
+    #
+    # @return [Boolean]
+    #
+    def sensitive?
+      options.fetch(:sensitive, false)
+    end
+
+    #
     # Validation options.  (See Chef::Mixin::ParamsValidate#validate.)
     #
     # @return [Hash<Symbol,Object>]
     #
     def validation_options
-      @validation_options ||= options.reject { |k, v|
-        [:declared_in, :name, :instance_variable_name, :desired_state, :identity, :default, :name_property, :coerce, :required, :nillable].include?(k)
-      }
+      @validation_options ||= options.reject do |k, v|
+        [:declared_in, :name, :instance_variable_name, :desired_state, :identity, :default, :name_property, :coerce, :required, :nillable, :sensitive].include?(k)
+      end
     end
 
     #
@@ -531,8 +542,6 @@ class Chef
       end
     end
 
-    protected
-
     #
     # The options this Property will use for get/set behavior and validation.
     #
@@ -583,6 +592,7 @@ class Chef
         (options.has_key?(:is) && resource.send(:_pv_is, { name => nil }, name, options[:is], raise_error: false))
     end
 
+    # @api private
     def get_value(resource)
       if instance_variable_name
         resource.instance_variable_get(instance_variable_name)
@@ -591,6 +601,7 @@ class Chef
       end
     end
 
+    # @api private
     def set_value(resource, value)
       if instance_variable_name
         resource.instance_variable_set(instance_variable_name, value)
@@ -599,6 +610,7 @@ class Chef
       end
     end
 
+    # @api private
     def value_is_set?(resource)
       if instance_variable_name
         resource.instance_variable_defined?(instance_variable_name)
@@ -607,6 +619,7 @@ class Chef
       end
     end
 
+    # @api private
     def reset_value(resource)
       if instance_variable_name
         if value_is_set?(resource)
@@ -617,6 +630,8 @@ class Chef
       end
     end
 
+    private
+
     def exec_in_resource(resource, proc, *args)
       if resource
         if proc.arity > args.size
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
index 03b546c..7cfddba 100644
--- a/lib/chef/provider.rb
+++ b/lib/chef/provider.rb
@@ -423,9 +423,9 @@ class Chef
 
     module DeprecatedLWRPClass
       def const_missing(class_name)
-        if deprecated_constants[class_name.to_sym]
+        if Chef::Provider.deprecated_constants[class_name.to_sym]
           Chef.log_deprecation("Using an LWRP provider by its name (#{class_name}) directly is no longer supported in Chef 12 and will be removed.  Use Chef::ProviderResolver.new(node, resource, action) instead.")
-          deprecated_constants[class_name.to_sym]
+          Chef::Provider.deprecated_constants[class_name.to_sym]
         else
           raise NameError, "uninitialized constant Chef::Provider::#{class_name}"
         end
@@ -438,13 +438,12 @@ class Chef
         if Chef::Provider.const_defined?(class_name, false)
           Chef::Log.warn "Chef::Provider::#{class_name} already exists!  Cannot create deprecation class for #{provider_class}"
         else
-          deprecated_constants[class_name.to_sym] = provider_class
+          Chef::Provider.deprecated_constants[class_name.to_sym] = provider_class
         end
       end
 
-      private
-
       def deprecated_constants
+        raise "Deprecated constants should be called only on Chef::Provider" unless self == Chef::Provider
         @deprecated_constants ||= {}
       end
     end
diff --git a/lib/chef/provider/apt_repository.rb b/lib/chef/provider/apt_repository.rb
index 1e7db80..9e077c8 100644
--- a/lib/chef/provider/apt_repository.rb
+++ b/lib/chef/provider/apt_repository.rb
@@ -19,6 +19,7 @@
 require "chef/resource"
 require "chef/dsl/declare_resource"
 require "chef/mixin/shell_out"
+require "chef/mixin/which"
 require "chef/http/simple"
 require "chef/provider/noop"
 
@@ -28,9 +29,10 @@ class Chef
       use_inline_resources
 
       include Chef::Mixin::ShellOut
+      extend Chef::Mixin::Which
 
       provides :apt_repository do
-        uses_apt?
+        which("apt-get")
       end
 
       def whyrun_supported?
@@ -104,12 +106,6 @@ class Chef
         end
       end
 
-      def self.uses_apt?
-        ENV["PATH"] ||= ""
-        paths = %w{ /bin /usr/bin /sbin /usr/sbin } + ENV["PATH"].split(::File::PATH_SEPARATOR)
-        paths.any? { |path| ::File.executable?(::File.join(path, "apt-get")) }
-      end
-
       def is_key_id?(id)
         id = id[2..-1] if id.start_with?("0x")
         id =~ /^\h+$/ && [8, 16, 40].include?(id.length)
@@ -254,4 +250,4 @@ class Chef
   end
 end
 
-Chef::Provider::Noop.provides :apt_resource
+Chef::Provider::Noop.provides :apt_repository
diff --git a/lib/chef/provider/apt_update.rb b/lib/chef/provider/apt_update.rb
index d2dd5cf..0320e9a 100644
--- a/lib/chef/provider/apt_update.rb
+++ b/lib/chef/provider/apt_update.rb
@@ -16,15 +16,20 @@
 # limitations under the License.
 #
 
-require "chef/resource"
-require "chef/dsl/declare_resource"
+require "chef/provider"
+require "chef/provider/noop"
+require "chef/mixin/which"
 
 class Chef
   class Provider
     class AptUpdate < Chef::Provider
       use_inline_resources
 
-      provides :apt_update, os: "linux"
+      extend Chef::Mixin::Which
+
+      provides :apt_update do
+        which("apt-get")
+      end
 
       APT_CONF_DIR = "/etc/apt/apt.conf.d"
       STAMP_DIR = "/var/lib/apt/periodic"
@@ -78,3 +83,5 @@ class Chef
     end
   end
 end
+
+Chef::Provider::Noop.provides :apt_update
diff --git a/lib/chef/provider/batch.rb b/lib/chef/provider/batch.rb
index bb294af..0d857aa 100644
--- a/lib/chef/provider/batch.rb
+++ b/lib/chef/provider/batch.rb
@@ -29,7 +29,7 @@ class Chef
       end
 
       def command
-        basepath = is_forced_32bit ? wow64_directory : run_context.node.kernel.os_info.system_directory
+        basepath = is_forced_32bit ? wow64_directory : run_context.node["kernel"]["os_info"]["system_directory"]
 
         interpreter_path = Chef::Util::PathHelper.join(basepath, interpreter)
 
diff --git a/lib/chef/provider/cron.rb b/lib/chef/provider/cron.rb
index 36b67ab..7baaeec 100644
--- a/lib/chef/provider/cron.rb
+++ b/lib/chef/provider/cron.rb
@@ -187,8 +187,7 @@ class Chef
             end
             crontab << line
           end
-          description = cron_found ? "remove #{@new_resource.name} from crontab" :
-            "save unmodified crontab"
+          description = cron_found ? "remove #{@new_resource.name} from crontab" : "save unmodified crontab"
           converge_by(description) do
             write_crontab crontab
             Chef::Log.info("#{@new_resource} deleted crontab entry")
@@ -237,7 +236,7 @@ class Chef
         newcron = ""
         newcron << "# Chef Name: #{new_resource.name}\n"
         [ :mailto, :path, :shell, :home ].each do |v|
-          newcron << "#{v.to_s.upcase}=#{@new_resource.send(v)}\n" if @new_resource.send(v)
+          newcron << "#{v.to_s.upcase}=\"#{@new_resource.send(v)}\"\n" if @new_resource.send(v)
         end
         @new_resource.environment.each do |name, value|
           newcron << "#{name}=#{value}\n"
diff --git a/lib/chef/provider/directory.rb b/lib/chef/provider/directory.rb
index 7cc0525..619ab5d 100644
--- a/lib/chef/provider/directory.rb
+++ b/lib/chef/provider/directory.rb
@@ -145,7 +145,9 @@ class Chef
         if ::File.exists?(@new_resource.path)
           converge_by("delete existing directory #{@new_resource.path}") do
             if @new_resource.recursive == true
-              FileUtils.rm_rf(@new_resource.path)
+              # we don't use rm_rf here because it masks all errors, including
+              # IO errors or permission errors that would prvent the deletion
+              FileUtils.rm_r(@new_resource.path)
               Chef::Log.info("#{@new_resource} deleted #{@new_resource.path} recursively")
             else
               ::Dir.delete(@new_resource.path)
diff --git a/lib/chef/provider/dsc_script.rb b/lib/chef/provider/dsc_script.rb
index 79769d9..66783ce 100644
--- a/lib/chef/provider/dsc_script.rb
+++ b/lib/chef/provider/dsc_script.rb
@@ -32,12 +32,12 @@ class Chef
         @dsc_resource = dsc_resource
         @resource_converged = false
         @operations = {
-          :set => Proc.new { |config_manager, document, shellout_flags|
+          :set => Proc.new do |config_manager, document, shellout_flags|
             config_manager.set_configuration(document, shellout_flags)
-          },
-          :test => Proc.new { |config_manager, document, shellout_flags|
+          end,
+          :test => Proc.new do |config_manager, document, shellout_flags|
             config_manager.test_configuration(document, shellout_flags)
-          } }
+          end }
       end
 
       def action_run
diff --git a/lib/chef/provider/group/usermod.rb b/lib/chef/provider/group/usermod.rb
index e4b1918..bef4b66 100644
--- a/lib/chef/provider/group/usermod.rb
+++ b/lib/chef/provider/group/usermod.rb
@@ -57,7 +57,7 @@ class Chef
           # This provider only supports adding members with
           # append. Only if the action is create we will go
           # ahead and add members.
-          if @new_resource.action == :create
+          if @new_resource.action.include?(:create)
             members.each do |member|
               add_member(member)
             end
diff --git a/lib/chef/provider/link.rb b/lib/chef/provider/link.rb
index 5fce97e..16d3031 100644
--- a/lib/chef/provider/link.rb
+++ b/lib/chef/provider/link.rb
@@ -141,9 +141,16 @@ class Chef
 
       def action_delete
         if @current_resource.to # Exists
-          converge_by("delete link at #{@new_resource.target_file}") do
-            ::File.delete(@new_resource.target_file)
-            Chef::Log.info("#{@new_resource} deleted")
+          if Chef::Platform.windows? && ::File.directory?(@current_resource.target_file)
+            converge_by("delete link to dir at #{@new_resource.target_file}") do
+              ::Dir.delete(@new_resource.target_file)
+              Chef::Log.info("#{@new_resource} deleted")
+            end
+          else
+            converge_by("delete link to file at #{@new_resource.target_file}") do
+              ::File.delete(@new_resource.target_file)
+              Chef::Log.info("#{@new_resource} deleted")
+            end
           end
         end
       end
diff --git a/lib/chef/provider/log.rb b/lib/chef/provider/log.rb
index eef4077..567781c 100644
--- a/lib/chef/provider/log.rb
+++ b/lib/chef/provider/log.rb
@@ -45,7 +45,7 @@ class Chef
         # true:: Always return true
         def action_write
           Chef::Log.send(@new_resource.level, @new_resource.message)
-          @new_resource.updated_by_last_action(true)
+          @new_resource.updated_by_last_action(true) if Chef::Config[:count_log_resource_updates]
         end
 
       end
diff --git a/lib/chef/provider/osx_profile.rb b/lib/chef/provider/osx_profile.rb
index f2e71a6..69ecf2d 100644
--- a/lib/chef/provider/osx_profile.rb
+++ b/lib/chef/provider/osx_profile.rb
@@ -66,33 +66,33 @@ class Chef
       def define_resource_requirements
         requirements.assert(:remove) do |a|
           if @new_profile_identifier
-            a.assertion {
+            a.assertion do
               !@new_profile_identifier.nil? &&
                 !@new_profile_identifier.end_with?(".mobileconfig") &&
-                /^\w+(?:\.\w+)+$/.match(@new_profile_identifier)
-            }
+                /^\w+(?:(\.| )\w+)+$/.match(@new_profile_identifier)
+            end
             a.failure_message RuntimeError, "when removing using the identifier attribute, it must match the profile identifier"
           else
             new_profile_name = @new_resource.profile_name
-            a.assertion {
+            a.assertion do
               !new_profile_name.end_with?(".mobileconfig") &&
-                /^\w+(?:\.\w+)+$/.match(new_profile_name)
-            }
+                /^\w+(?:(\.| )\w+)+$/.match(new_profile_name)
+            end
             a.failure_message RuntimeError, "When removing by resource name, it must match the profile identifier "
           end
         end
 
         requirements.assert(:install) do |a|
           if @new_profile_hash.is_a?(Hash)
-            a.assertion {
+            a.assertion do
               @new_profile_hash.include?("PayloadIdentifier")
-            }
+            end
             a.failure_message RuntimeError, "The specified profile does not seem to be valid"
           end
           if @new_profile_hash.is_a?(String)
-            a.assertion {
+            a.assertion do
               @new_profile_hash.end_with?(".mobileconfig")
-            }
+            end
             a.failure_message RuntimeError, "#{new_profile_hash}' is not a valid profile"
           end
         end
diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb
index cac0fb6..3f64114 100644
--- a/lib/chef/provider/package.rb
+++ b/lib/chef/provider/package.rb
@@ -194,12 +194,12 @@ class Chef
       end
 
       def action_reconfig
-        if @current_resource.version == nil then
+        if @current_resource.version == nil
           Chef::Log.debug("#{@new_resource} is NOT installed - nothing to do")
           return
         end
 
-        unless @new_resource.response_file then
+        unless @new_resource.response_file
           Chef::Log.debug("#{@new_resource} no response_file provided - nothing to do")
           return
         end
@@ -588,16 +588,6 @@ class Chef
         end
         args
       end
-
-      # Helper for sublcasses to convert an array of string args into a string.  It
-      # will compact nil or empty strings in the array and will join the array elements
-      # with spaces, without introducing any double spaces for nil/empty elements.
-      #
-      # @param args [String] variable number of string arguments
-      # @return [String] nicely concatenated string or empty string
-      def a_to_s(*args)
-        args.flatten.reject { |i| i.nil? || i == "" }.join(" ")
-      end
     end
   end
 end
diff --git a/lib/chef/provider/package/aix.rb b/lib/chef/provider/package/aix.rb
index a1709c4..728f181 100644
--- a/lib/chef/provider/package/aix.rb
+++ b/lib/chef/provider/package/aix.rb
@@ -55,7 +55,11 @@ class Chef
               ret = shell_out_with_timeout("installp -L -d #{@new_resource.source}")
               ret.stdout.each_line do |line|
                 case line
-                when /#{@new_resource.package_name}:/
+                when /:#{@new_resource.package_name}:/
+                  fields = line.split(":")
+                  @new_resource.version(fields[2])
+                when /^#{@new_resource.package_name}:/
+                  Chef::Log.warn("You are installing a bff package by product name. For idempotent installs, please install individual filesets")
                   fields = line.split(":")
                   @new_resource.version(fields[2])
                 end
diff --git a/lib/chef/provider/package/openbsd.rb b/lib/chef/provider/package/openbsd.rb
index 2120b9a..8043c01 100644
--- a/lib/chef/provider/package/openbsd.rb
+++ b/lib/chef/provider/package/openbsd.rb
@@ -127,7 +127,7 @@ class Chef
         end
 
         def pkg_path
-          ENV["PKG_PATH"] || "http://ftp.OpenBSD.org/pub/#{node.kernel.name}/#{node.kernel.release}/packages/#{node.kernel.machine}/"
+          ENV["PKG_PATH"] || "http://ftp.OpenBSD.org/pub/#{node["kernel"]["name"]}/#{node["kernel"]["release"]}/packages/#{node["kernel"]["machine"]}/"
         end
 
       end
diff --git a/lib/chef/provider/package/rubygems.rb b/lib/chef/provider/package/rubygems.rb
index eb5a870..187197d 100644
--- a/lib/chef/provider/package/rubygems.rb
+++ b/lib/chef/provider/package/rubygems.rb
@@ -159,19 +159,22 @@ class Chef
           # Find the newest gem version available from Gem.sources that satisfies
           # the constraints of +gem_dependency+
           def find_newest_remote_version(gem_dependency, *sources)
-            available_gems = dependency_installer.find_gems_with_sources(gem_dependency)
-            spec, source = if available_gems.respond_to?(:last)
-                             # DependencyInstaller sorts the results such that the last one is
-                             # always the one it considers best.
-                             spec_with_source = available_gems.last
-                             spec_with_source && spec_with_source
-                           else
-                             # Rubygems 2.0 returns a Gem::Available set, which is a
-                             # collection of AvailableSet::Tuple structs
-                             available_gems.pick_best!
-                             best_gem = available_gems.set.first
-                             best_gem && [best_gem.spec, best_gem.source]
-                           end
+            spec, source =
+              if Chef::Config[:rubygems_cache_enabled]
+                # This code caches every gem on rubygems.org and uses lots of RAM
+                available_gems = dependency_installer.find_gems_with_sources(gem_dependency)
+                available_gems.pick_best!
+                best_gem = available_gems.set.first
+                best_gem && [best_gem.spec, best_gem.source]
+              else
+                # Use the API that 'gem install' calls which does not pull down the rubygems universe
+                begin
+                  rs = dependency_installer.resolve_dependencies gem_dependency.name, gem_dependency.requirement
+                  rs.specs.select { |s| s.name == gem_dependency.name }.first
+                rescue Gem::UnsatisfiableDependencyError
+                  nil
+                end
+              end
 
             version = spec && spec.version
             if version
@@ -431,17 +434,23 @@ class Chef
         end
 
         def current_version
-          #raise 'todo'
+          # rubygems 2.6.3 ensures that gem lists are sorted newest first
+          pos = if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.6.3")
+                  :first
+                else
+                  :last
+                end
+
           # If one or more matching versions are installed, the newest of them
           # is the current version
           if !matching_installed_versions.empty?
-            gemspec = matching_installed_versions.last
+            gemspec = matching_installed_versions.send(pos)
             logger.debug { "#{@new_resource} found installed gem #{gemspec.name} version #{gemspec.version} matching #{gem_dependency}" }
             gemspec
             # If no version matching the requirements exists, the latest installed
             # version is the current version.
           elsif !all_installed_versions.empty?
-            gemspec = all_installed_versions.last
+            gemspec = all_installed_versions.send(pos)
             logger.debug { "#{@new_resource} newest installed version of gem #{gemspec.name} is #{gemspec.version}" }
             gemspec
           else
@@ -513,9 +522,7 @@ class Chef
               install_via_gem_command(name, version)
             end
           elsif @new_resource.gem_binary.nil?
-            # domain is used by Gem::DependencyInstaller rather than by Chef code
-            # domain can be :local, :remote or :both
-            @gem_env.install(@new_resource.source, domain: :local)
+            @gem_env.install(@new_resource.source)
           else
             install_via_gem_command(name, version)
           end
diff --git a/lib/chef/provider/package/windows.rb b/lib/chef/provider/package/windows.rb
index a5f3ff7..753d3c2 100644
--- a/lib/chef/provider/package/windows.rb
+++ b/lib/chef/provider/package/windows.rb
@@ -201,6 +201,7 @@ class Chef
         def resource_for_provider
           @resource_for_provider = Chef::Resource::WindowsPackage.new(new_resource.name).tap do |r|
             r.source(Chef::Util::PathHelper.validate_path(source_location)) unless source_location.nil?
+            r.cookbook_name = new_resource.cookbook_name
             r.version(new_resource.version)
             r.timeout(new_resource.timeout)
             r.returns(new_resource.returns)
@@ -216,6 +217,7 @@ class Chef
         def source_resource
           @source_resource ||= Chef::Resource::RemoteFile.new(default_download_cache_path, run_context).tap do |r|
             r.source(new_resource.source)
+            r.cookbook_name = new_resource.cookbook_name
             r.checksum(new_resource.checksum)
             r.backup(false)
 
diff --git a/lib/chef/provider/package/windows/exe.rb b/lib/chef/provider/package/windows/exe.rb
index 70c9879..44a2f19 100644
--- a/lib/chef/provider/package/windows/exe.rb
+++ b/lib/chef/provider/package/windows/exe.rb
@@ -71,19 +71,16 @@ class Chef
             uninstall_entries.select { |entry| [uninstall_version].flatten.include?(entry.display_version) }
               .map { |version| version.uninstall_string }.uniq.each do |uninstall_string|
                 Chef::Log.debug("Registry provided uninstall string for #{new_resource} is '#{uninstall_string}'")
-                shell_out!(uninstall_command(uninstall_string), { returns: new_resource.returns })
+                shell_out!(uninstall_command(uninstall_string), { :timeout => new_resource.timeout, :returns => new_resource.returns })
               end
           end
 
           private
 
           def uninstall_command(uninstall_string)
-            uninstall_string.delete!('"')
+            uninstall_string = "\"#{uninstall_string}\"" if ::File.exist?(uninstall_string)
             uninstall_string = [
-              %q{/d"},
-              ::File.dirname(uninstall_string),
-              %q{" },
-              ::File.basename(uninstall_string),
+              uninstall_string,
               expand_options(new_resource.options),
               " ",
               unattended_flags,
diff --git a/lib/chef/provider/package/windows/registry_uninstall_entry.rb b/lib/chef/provider/package/windows/registry_uninstall_entry.rb
index 3fa00b6..a693558 100644
--- a/lib/chef/provider/package/windows/registry_uninstall_entry.rb
+++ b/lib/chef/provider/package/windows/registry_uninstall_entry.rb
@@ -79,8 +79,6 @@ class Chef
           attr_reader :uninstall_string
           attr_reader :data
 
-          private
-
           UNINSTALL_SUBKEY = 'Software\Microsoft\Windows\CurrentVersion\Uninstall'.freeze
         end
       end
diff --git a/lib/chef/provider/package/yum/rpm_utils.rb b/lib/chef/provider/package/yum/rpm_utils.rb
index a748c66..032597d 100644
--- a/lib/chef/provider/package/yum/rpm_utils.rb
+++ b/lib/chef/provider/package/yum/rpm_utils.rb
@@ -243,16 +243,16 @@ class Chef
             self.new(*args)
           end
 
-          def <=>(y)
-            compare_versions(y)
+          def <=>(other)
+            compare_versions(other)
           end
 
-          def compare(y)
-            compare_versions(y, false)
+          def compare(other)
+            compare_versions(other, false)
           end
 
-          def partial_compare(y)
-            compare_versions(y, true)
+          def partial_compare(other)
+            compare_versions(other, true)
           end
 
           # RPM::Version rpm_version_to_s equivalent
@@ -352,8 +352,8 @@ class Chef
           alias :name :n
           alias :arch :a
 
-          def <=>(y)
-            compare(y)
+          def <=>(other)
+            compare(other)
           end
 
           def compare(y)
diff --git a/lib/chef/provider/package/yum/yum_cache.rb b/lib/chef/provider/package/yum/yum_cache.rb
index fb25a91..7462529 100644
--- a/lib/chef/provider/package/yum/yum_cache.rb
+++ b/lib/chef/provider/package/yum/yum_cache.rb
@@ -197,7 +197,7 @@ class Chef
 
           def shabang?(file)
             ::File.open(file, "r") do |f|
-              f.read(2) == '#!'
+              f.read(2) == "#!"
             end
           rescue Errno::ENOENT
             false
diff --git a/lib/chef/provider/package/zypper.rb b/lib/chef/provider/package/zypper.rb
index 5ee1dbe..e20a733 100644
--- a/lib/chef/provider/package/zypper.rb
+++ b/lib/chef/provider/package/zypper.rb
@@ -38,15 +38,15 @@ class Chef
           status = shell_out_with_timeout!("zypper --non-interactive info #{package_name}")
           status.stdout.each_line do |line|
             case line
-            when /^Version: (.+)$/
-              candidate_version = $1
-              Chef::Log.debug("#{new_resource} version #{$1}")
-            when /^Installed: Yes$/
+            when /^Version *: (.+) *$/
+              candidate_version = $1.strip
+              Chef::Log.debug("#{new_resource} version #{candidate_version}")
+            when /^Installed *: Yes *$/
               is_installed = true
               Chef::Log.debug("#{new_resource} is installed")
-            when /^Status: out-of-date \(version (.+) installed\)$/
-              current_version = $1
-              Chef::Log.debug("#{new_resource} out of date version #{$1}")
+            when /^Status *: out-of-date \(version (.+) installed\) *$/
+              current_version = $1.strip
+              Chef::Log.debug("#{new_resource} out of date version #{current_version}")
             end
           end
           current_version = candidate_version if is_installed
diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb
index 6365f6a..ab85ec3 100644
--- a/lib/chef/provider/powershell_script.rb
+++ b/lib/chef/provider/powershell_script.rb
@@ -36,7 +36,7 @@ class Chef
       end
 
       def command
-        basepath = is_forced_32bit ? wow64_directory : run_context.node.kernel.os_info.system_directory
+        basepath = is_forced_32bit ? wow64_directory : run_context.node["kernel"]["os_info"]["system_directory"]
 
         # Powershell.exe is always in "v1.0" folder (for backwards compatibility)
         interpreter_path = Chef::Util::PathHelper.join(basepath, "WindowsPowerShell", "v1.0", interpreter)
diff --git a/lib/chef/provider/registry_key.rb b/lib/chef/provider/registry_key.rb
index e516433..5e8dbe9 100644
--- a/lib/chef/provider/registry_key.rb
+++ b/lib/chef/provider/registry_key.rb
@@ -102,6 +102,9 @@ class Chef
         @new_resource.unscrubbed_values.each do |value|
           if @name_hash.has_key?(value[:name].downcase)
             current_value = @name_hash[value[:name].downcase]
+            if [:dword, :dword_big_endian, :qword].include? value[:type]
+              value[:data] = value[:data].to_i
+            end
             unless current_value[:type] == value[:type] && current_value[:data] == value[:data]
               converge_by("set value #{value}") do
                 registry.set_value(@new_resource.key, value)
diff --git a/lib/chef/provider/remote_directory.rb b/lib/chef/provider/remote_directory.rb
index e3bc579..15b71c4 100644
--- a/lib/chef/provider/remote_directory.rb
+++ b/lib/chef/provider/remote_directory.rb
@@ -209,6 +209,8 @@ class Chef
       def cookbook_file_resource(target_path, relative_source_path)
         res = Chef::Resource::CookbookFile.new(target_path, run_context)
         res.cookbook_name = resource_cookbook
+        # Set the sensitivity level
+        res.sensitive(new_resource.sensitive)
         res.source(::File.join(source, relative_source_path))
         if Chef::Platform.windows? && files_rights
           files_rights.each_pair do |permission, *args|
diff --git a/lib/chef/provider/remote_file/ftp.rb b/lib/chef/provider/remote_file/ftp.rb
index 5935e83..b382c20 100644
--- a/lib/chef/provider/remote_file/ftp.rb
+++ b/lib/chef/provider/remote_file/ftp.rb
@@ -153,9 +153,9 @@ class Chef
         def parse_path
           path = uri.path.sub(%r{\A/}, "%2F") # re-encode the beginning slash because uri library decodes it.
           directories = path.split(%r{/}, -1)
-          directories.each {|d|
+          directories.each do |d|
             d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") }
-          }
+          end
           unless filename = directories.pop
             raise ArgumentError, "no filename: #{path.inspect}"
           end
diff --git a/lib/chef/provider/remote_file/sftp.rb b/lib/chef/provider/remote_file/sftp.rb
index 530977e..21c5c4c 100644
--- a/lib/chef/provider/remote_file/sftp.rb
+++ b/lib/chef/provider/remote_file/sftp.rb
@@ -68,9 +68,9 @@ class Chef
         def validate_path!
           path = uri.path.sub(%r{\A/}, "%2F") # re-encode the beginning slash because uri library decodes it.
           directories = path.split(%r{/}, -1)
-          directories.each {|d|
+          directories.each do |d|
             d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") }
-          }
+          end
           unless filename = directories.pop
             raise ArgumentError, "no filename: #{path.inspect}"
           end
diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb
index 67b7195..9d11032 100644
--- a/lib/chef/provider/service/debian.rb
+++ b/lib/chef/provider/service/debian.rb
@@ -106,13 +106,13 @@ class Chef
 
         def service_currently_enabled?(priority)
           enabled = false
-          priority.each { |runlevel, arguments|
+          priority.each do |runlevel, arguments|
             Chef::Log.debug("#{new_resource} runlevel #{runlevel}, action #{arguments[0]}, priority #{arguments[1]}")
             # if we are in a update-rc.d default startup runlevel && we start in this runlevel
             if %w{ 1 2 3 4 5 S }.include?(runlevel) && arguments[0] == :start
               enabled = true
             end
-          }
+          end
 
           enabled
         end
diff --git a/lib/chef/provider/service/simple.rb b/lib/chef/provider/service/simple.rb
index fe4768b..d75e859 100644
--- a/lib/chef/provider/service/simple.rb
+++ b/lib/chef/provider/service/simple.rb
@@ -76,8 +76,9 @@ class Chef
           end
 
           requirements.assert(:all_actions) do |a|
-            a.assertion { @new_resource.status_command || supports[:status] ||
-              (!ps_cmd.nil? && !ps_cmd.empty?) }
+            a.assertion do
+              @new_resource.status_command || supports[:status] ||
+                (!ps_cmd.nil? && !ps_cmd.empty?) end
             a.failure_message Chef::Exceptions::Service, "#{@new_resource} could not determine how to inspect the process table, please set this node's 'command.ps' attribute"
           end
           requirements.assert(:all_actions) do |a|
@@ -108,7 +109,7 @@ class Chef
           shell_out_with_systems_locale!(@new_resource.reload_command)
         end
 
-      protected
+        protected
 
         def determine_current_status!
           if @new_resource.status_command
diff --git a/lib/chef/provider/service/solaris.rb b/lib/chef/provider/service/solaris.rb
index 1e5398e..868b3e1 100644
--- a/lib/chef/provider/service/solaris.rb
+++ b/lib/chef/provider/service/solaris.rb
@@ -40,7 +40,7 @@ class Chef
           @current_resource.service_name(@new_resource.service_name)
 
           [@init_command, @status_command].each do |cmd|
-            unless ::File.executable? cmd then
+            unless ::File.executable? cmd
               raise Chef::Exceptions::Service, "#{cmd} not executable!"
             end
           end
diff --git a/lib/chef/provider/service/systemd.rb b/lib/chef/provider/service/systemd.rb
index 1597d46..712f0f6 100644
--- a/lib/chef/provider/service/systemd.rb
+++ b/lib/chef/provider/service/systemd.rb
@@ -78,10 +78,10 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
     if new_resource.user
       uid = node["etc"]["passwd"][new_resource.user]["uid"]
       options = {
-        "environment" => {
+        :environment => {
           "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/#{uid}/bus",
         },
-        "user" => new_resource.user,
+        :user => new_resource.user,
       }
       args = "--user"
     else
diff --git a/lib/chef/provider/service/upstart.rb b/lib/chef/provider/service/upstart.rb
index 3ac5ff5..6e2a3b6 100644
--- a/lib/chef/provider/service/upstart.rb
+++ b/lib/chef/provider/service/upstart.rb
@@ -80,8 +80,11 @@ class Chef
           shared_resource_requirements
           requirements.assert(:all_actions) do |a|
             if !@command_success
-              whyrun_msg = @new_resource.status_command ? "Provided status command #{@new_resource.status_command} failed." :
-                "Could not determine upstart state for service"
+              whyrun_msg = if @new_resource.status_command
+                             "Provided status command #{@new_resource.status_command} failed."
+                           else
+                             "Could not determine upstart state for service"
+                           end
             end
             a.assertion { @command_success }
             # no failure here, just document the assumptions made.
@@ -116,7 +119,7 @@ class Chef
             end
           else
             begin
-              if upstart_state == "running"
+              if upstart_goal_state == "start"
                 @current_resource.running true
               else
                 @current_resource.running false
@@ -220,7 +223,7 @@ class Chef
           conf.write_file
         end
 
-        def upstart_state
+        def upstart_goal_state
           command = "/sbin/status #{@job}"
           status = popen4(command) do |pid, stdin, stdout, stderr|
             stdout.each_line do |line|
@@ -231,7 +234,7 @@ class Chef
               # service (goal) state
               line =~ UPSTART_STATE_FORMAT
               data = Regexp.last_match
-              return data[2]
+              return data[1]
             end
           end
         end
diff --git a/lib/chef/provider/support/yum_repo.erb b/lib/chef/provider/support/yum_repo.erb
new file mode 100644
index 0000000..7d9a2d0
--- /dev/null
+++ b/lib/chef/provider/support/yum_repo.erb
@@ -0,0 +1,125 @@
+# This file was generated by Chef
+# Do NOT modify this file by hand.
+
+[<%= @config.repositoryid %>]
+name=<%= @config.description %>
+<% if @config.baseurl %>
+baseurl=<%= @config.baseurl %>
+<% end %>
+<% if @config.cost %>
+cost=<%= @config.cost %>
+<% end %>
+<% if @config.enabled %>
+enabled=1
+<% else %>
+enabled=0
+<% end %>
+<% if @config.enablegroups %>
+enablegroups=1
+<% end %>
+<% if @config.exclude %>
+exclude=<%= @config.exclude %>
+<% end %>
+<% if @config.failovermethod %>
+failovermethod=<%= @config.failovermethod %>
+<% end %>
+<% if @config.fastestmirror_enabled %>
+fastestmirror_enabled=<%= @config.fastestmirror_enabled %>
+<% end %>
+<% if @config.gpgcheck %>
+gpgcheck=1
+<% else %>
+gpgcheck=0
+<% end %>
+<% if @config.gpgkey %>
+gpgkey=<%= case @config.gpgkey
+     when Array
+       @config.gpgkey.join("\n       ")
+     else
+       @config.gpgkey
+     end %>
+<% end -%>
+<% if @config.http_caching %>
+http_caching=<%= @config.http_caching %>
+<% end %>
+<% if @config.include_config %>
+include=<%= @config.include_config %>
+<% end %>
+<% if @config.includepkgs %>
+includepkgs=<%= @config.includepkgs %>
+<% end %>
+<% if @config.keepalive %>
+keepalive=1
+<% end %>
+<% if @config.metadata_expire %>
+metadata_expire=<%= @config.metadata_expire %>
+<% end %>
+<% if @config.mirrorlist %>
+mirrorlist=<%= @config.mirrorlist %>
+<% end %>
+<% if @config.mirror_expire %>
+mirror_expire=<%= @config.mirror_expire %>
+<% end %>
+<% if @config.mirrorlist_expire %>
+mirrorlist_expire=<%= @config.mirrorlist_expire %>
+<% end %>
+<% if @config.priority %>
+priority=<%= @config.priority %>
+<% end %>
+<% if @config.proxy %>
+proxy=<%= @config.proxy %>
+<% end %>
+<% if @config.proxy_username %>
+proxy_username=<%= @config.proxy_username %>
+<% end %>
+<% if @config.proxy_password %>
+proxy_password=<%= @config.proxy_password %>
+<% end %>
+<% if @config.username %>
+username=<%= @config.username %>
+<% end %>
+<% if @config.password %>
+password=<%= @config.password %>
+<% end %>
+<% if @config.repo_gpgcheck %>
+repo_gpgcheck=1
+<% end %>
+<% if @config.max_retries %>
+retries=<%= @config.max_retries %>
+<% end %>
+<% if @config.report_instanceid %>
+report_instanceid=<%= @config.report_instanceid %>
+<% end %>
+<% if @config.skip_if_unavailable %>
+skip_if_unavailable=1
+<% end %>
+<% if @config.sslcacert %>
+sslcacert=<%= @config.sslcacert %>
+<% end %>
+<% if @config.sslclientcert %>
+sslclientcert=<%= @config.sslclientcert %>
+<% end %>
+<% if @config.sslclientkey %>
+sslclientkey=<%= @config.sslclientkey %>
+<% end %>
+<% unless @config.sslverify.nil? %>
+sslverify=<%= ( @config.sslverify ) ? 'true' : 'false' %>
+<% end %>
+<% if @config.timeout %>
+timeout=<%= @config.timeout %>
+<% end %>
+<% if @config.options -%>
+<%   @config.options.each do |key, value| -%>
+<%= key %>=<%=
+       case value
+       when Array
+         value.join("\n       ")
+       when TrueClass
+         '1'
+       when FalseClass
+         '0'
+       else
+         value
+       end %>
+<%   end -%>
+<% end -%>
diff --git a/lib/chef/provider/systemd_unit.rb b/lib/chef/provider/systemd_unit.rb
index b96a336..4aa4cd0 100644
--- a/lib/chef/provider/systemd_unit.rb
+++ b/lib/chef/provider/systemd_unit.rb
@@ -193,6 +193,7 @@ class Chef
           f.group "root"
           f.mode "0644"
           f.content new_resource.to_ini
+          f.verify systemd_analyze_cmd if systemd_analyze_path
         end.run_action(action)
       end
 
@@ -224,8 +225,8 @@ class Chef
         @systemctl_opts ||=
           if new_resource.user
             {
-              "user" => new_resource.user,
-              "environment" => {
+              :user => new_resource.user,
+              :environment => {
                 "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/#{node['etc']['passwd'][new_resource.user]['uid']}/bus",
               },
             }
@@ -233,6 +234,14 @@ class Chef
             {}
           end
       end
+
+      def systemd_analyze_cmd
+        @systemd_analyze_cmd ||= "#{systemd_analyze_path} verify %{path}"
+      end
+
+      def systemd_analyze_path
+        @systemd_analyze_path ||= which("systemd-analyze")
+      end
     end
   end
 end
diff --git a/lib/chef/provider/template_finder.rb b/lib/chef/provider/template_finder.rb
index 67342a8..1e8b925 100644
--- a/lib/chef/provider/template_finder.rb
+++ b/lib/chef/provider/template_finder.rb
@@ -40,7 +40,7 @@ class Chef
         cookbook.preferred_filename_on_disk_location(@node, :templates, template_name)
       end
 
-    protected
+      protected
 
       def template_source_name(name, options)
         if options[:source]
diff --git a/lib/chef/provider/user.rb b/lib/chef/provider/user.rb
index 85bd674..2bc4cc1 100644
--- a/lib/chef/provider/user.rb
+++ b/lib/chef/provider/user.rb
@@ -147,10 +147,6 @@ class Chef
         end
       end
 
-      def remove_user
-        raise NotImplementedError
-      end
-
       def action_manage
         if @user_exists && compare_user
           converge_by("manage user #{@new_resource.username}") do
@@ -160,10 +156,6 @@ class Chef
         end
       end
 
-      def manage_user
-        raise NotImplementedError
-      end
-
       def action_modify
         if compare_user
           converge_by("modify user #{@new_resource.username}") do
@@ -184,14 +176,6 @@ class Chef
         end
       end
 
-      def check_lock
-        raise NotImplementedError
-      end
-
-      def lock_user
-        raise NotImplementedError
-      end
-
       def action_unlock
         if check_lock() == true
           converge_by("unlock user #{@new_resource.username}") do
@@ -203,9 +187,29 @@ class Chef
         end
       end
 
+      def create_user
+        raise NotImplementedError
+      end
+
+      def remove_user
+        raise NotImplementedError
+      end
+
+      def manage_user
+        raise NotImplementedError
+      end
+
+      def lock_user
+        raise NotImplementedError
+      end
+
       def unlock_user
         raise NotImplementedError
       end
+
+      def check_lock
+        raise NotImplementedError
+      end
     end
   end
 end
diff --git a/lib/chef/provider/user/aix.rb b/lib/chef/provider/user/aix.rb
index 3f168b8..8ac229a 100644
--- a/lib/chef/provider/user/aix.rb
+++ b/lib/chef/provider/user/aix.rb
@@ -14,11 +14,14 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+require "chef/provider/user/useradd"
+
 class Chef
   class Provider
     class User
       class Aix < Chef::Provider::User::Useradd
-        provides :user, platform: %w{aix}
+        provides :user, os: "aix"
+        provides :aix_user
 
         UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]]
 
@@ -66,7 +69,7 @@ class Chef
           shell_out!("chuser account_locked=false #{new_resource.username}")
         end
 
-      private
+        private
 
         def add_password
           if @current_resource.password != @new_resource.password && @new_resource.password
diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb
index e933bf9..821fa8e 100644
--- a/lib/chef/provider/user/dscl.rb
+++ b/lib/chef/provider/user/dscl.rb
@@ -48,6 +48,7 @@ class Chef
         attr_accessor :authentication_authority
         attr_accessor :password_shadow_conversion_algorithm
 
+        provides :dscl_user
         provides :user, os: "darwin"
 
         def define_resource_requirements
diff --git a/lib/chef/provider/user/linux.rb b/lib/chef/provider/user/linux.rb
new file mode 100644
index 0000000..4a2491d
--- /dev/null
+++ b/lib/chef/provider/user/linux.rb
@@ -0,0 +1,138 @@
+#
+# Copyright:: Copyright 2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "chef/provider/user"
+
+class Chef
+  class Provider
+    class User
+      class Linux < Chef::Provider::User
+        provides :linux_user
+        provides :user, os: "linux"
+
+        def create_user
+          shell_out!(*clean_array("useradd", universal_options, useradd_options, new_resource.username))
+        end
+
+        def manage_user
+          shell_out!(*clean_array("usermod", universal_options, usermod_options, new_resource.username))
+        end
+
+        def remove_user
+          shell_out!(*clean_array("userdel", userdel_options, new_resource.username))
+        end
+
+        def lock_user
+          shell_out!(*clean_array("usermod", "-L", new_resource.username))
+        end
+
+        def unlock_user
+          shell_out!(*clean_array("usermod", "-U", new_resource.username))
+        end
+
+        # common to usermod and useradd
+        def universal_options
+          opts = []
+          opts << "-c" << new_resource.comment if should_set?(:comment)
+          opts << "-g" << new_resource.gid if should_set?(:gid)
+          opts << "-p" << new_resource.password if should_set?(:password)
+          opts << "-s" << new_resource.shell if should_set?(:shell)
+          opts << "-u" << new_resource.uid if should_set?(:uid)
+          opts << "-d" << new_resource.home if updating_home?
+          opts << "-o" if non_unique
+          opts
+        end
+
+        def usermod_options
+          opts = []
+          if updating_home?
+            if manage_home
+              opts << "-m"
+            end
+          end
+          opts
+        end
+
+        def useradd_options
+          opts = []
+          opts << "-r" if new_resource.system
+          if manage_home
+            opts << "-m"
+          else
+            opts << "-M"
+          end
+          opts
+        end
+
+        def userdel_options
+          opts = []
+          opts << "-r" if manage_home
+          opts << "-f" if new_resource.force
+          opts
+        end
+
+        def should_set?(sym)
+          current_resource.send(sym).to_s != new_resource.send(sym).to_s && new_resource.send(sym)
+        end
+
+        def updating_home?
+          return false unless new_resource.home
+          return true unless current_resource.home
+          new_resource.home && Pathname.new(current_resource.home).cleanpath != Pathname.new(new_resource.home).cleanpath
+        end
+
+        def check_lock
+          # there's an old bug in rhel (https://bugzilla.redhat.com/show_bug.cgi?id=578534)
+          # which means that both 0 and 1 can be success.
+          passwd_s = shell_out("passwd", "-S", new_resource.username, returns: [ 0, 1 ])
+
+          # checking "does not exist" has to come before exit code handling since centos and ubuntu differ in exit codes
+          if passwd_s.stderr =~ /does not exist/
+            if whyrun_mode?
+              return false
+            else
+              raise Chef::Exceptions::User, "User #{new_resource.username} does not exist when checking lock status for #{new_resource}"
+            end
+          end
+
+          # now raise if we didn't get a 0 or 1 (see above)
+          passwd_s.error!
+
+          # now the actual output parsing
+          @locked = nil
+          status_line = passwd_s.stdout.split(" ")
+          @locked = false if status_line[1] =~ /^[PN]/
+          @locked = true if status_line[1] =~ /^L/
+
+          raise Chef::Exceptions::User, "Cannot determine if user #{new_resource.username} is locked for #{new_resource}" if @locked.nil?
+
+          # FIXME: should probably go on the current_resource
+          @locked
+        end
+
+        def non_unique
+          # XXX: THIS GOES AWAY IN CHEF-13 AND BECOMES JUST new_resource.non_unique
+          new_resource.non_unique || new_resource.supports[:non_unique]
+        end
+
+        def manage_home
+          # XXX: THIS GOES AWAY IN CHEF-13 AND BECOMES JUST new_resource.manage_home
+          new_resource.manage_home || new_resource.supports[:manage_home]
+        end
+      end
+    end
+  end
+end
diff --git a/lib/chef/provider/user/pw.rb b/lib/chef/provider/user/pw.rb
index 949a217..a1d7671 100644
--- a/lib/chef/provider/user/pw.rb
+++ b/lib/chef/provider/user/pw.rb
@@ -22,7 +22,8 @@ class Chef
   class Provider
     class User
       class Pw < Chef::Provider::User
-        provides :user, platform: %w{freebsd}
+        provides :pw_user
+        provides :user, os: "freebsd"
 
         def load_current_resource
           super
diff --git a/lib/chef/provider/user/solaris.rb b/lib/chef/provider/user/solaris.rb
index 1f0cbb6..8d3df9e 100644
--- a/lib/chef/provider/user/solaris.rb
+++ b/lib/chef/provider/user/solaris.rb
@@ -24,7 +24,8 @@ class Chef
   class Provider
     class User
       class Solaris < Chef::Provider::User::Useradd
-        provides :user, platform: %w{omnios solaris2}
+        provides :solaris_user
+        provides :user, os: %w{omnios solaris2}
         UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]]
 
         attr_writer :password_file
@@ -70,7 +71,7 @@ class Chef
           shell_out!("passwd", "-u", new_resource.username)
         end
 
-      private
+        private
 
         def manage_password
           if @current_resource.password != @new_resource.password && @new_resource.password
diff --git a/lib/chef/provider/user/useradd.rb b/lib/chef/provider/user/useradd.rb
index 3fef8d3..68b6281 100644
--- a/lib/chef/provider/user/useradd.rb
+++ b/lib/chef/provider/user/useradd.rb
@@ -23,7 +23,7 @@ class Chef
   class Provider
     class User
       class Useradd < Chef::Provider::User
-        provides :user
+        # MAJOR XXX: this should become the base class of all Useradd providers instead of the linux implementation
 
         UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:password, "-p"], [:shell, "-s"], [:uid, "-u"]]
 
@@ -116,15 +116,15 @@ class Chef
                 update_options(field, option, opts)
               end
               if updating_home?
+                opts << "-d" << new_resource.home
                 if managing_home_dir?
                   Chef::Log.debug("#{new_resource} managing the users home directory")
-                  opts << "-d" << new_resource.home << "-m"
+                  opts << "-m"
                 else
                   Chef::Log.debug("#{new_resource} setting home to #{new_resource.home}")
-                  opts << "-d" << new_resource.home
                 end
               end
-              opts << "-o" if new_resource.non_unique || new_resource.supports[:non_unique]
+              opts << "-o" if new_resource.non_unique
               opts
             end
         end
@@ -141,6 +141,7 @@ class Chef
         def useradd_options
           opts = []
           opts << "-r" if new_resource.system
+          opts << "-M" unless managing_home_dir?
           opts
         end
 
diff --git a/lib/chef/provider/user/windows.rb b/lib/chef/provider/user/windows.rb
index 9545b1f..b086a1e 100644
--- a/lib/chef/provider/user/windows.rb
+++ b/lib/chef/provider/user/windows.rb
@@ -26,7 +26,7 @@ class Chef
   class Provider
     class User
       class Windows < Chef::Provider::User
-
+        provides :windows_user
         provides :user, os: "windows"
 
         def initialize(new_resource, run_context)
diff --git a/lib/chef/provider/windows_script.rb b/lib/chef/provider/windows_script.rb
index 2de127a..3b02027 100644
--- a/lib/chef/provider/windows_script.rb
+++ b/lib/chef/provider/windows_script.rb
@@ -33,8 +33,11 @@ class Chef
         super( new_resource, run_context )
         @script_extension = script_extension
 
-        target_architecture = new_resource.architecture.nil? ?
-          node_windows_architecture(run_context.node) : new_resource.architecture
+        target_architecture = if new_resource.architecture.nil?
+                                node_windows_architecture(run_context.node)
+                              else
+                                new_resource.architecture
+                              end
 
         @is_wow64 = wow64_architecture_override_required?(run_context.node, target_architecture)
 
diff --git a/lib/chef/provider/yum_repository.rb b/lib/chef/provider/yum_repository.rb
new file mode 100644
index 0000000..09ff2c5
--- /dev/null
+++ b/lib/chef/provider/yum_repository.rb
@@ -0,0 +1,121 @@
+#
+# Author:: Thom May (<thom at chef.io>)
+# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+require "chef/dsl/declare_resource"
+require "chef/mixin/shell_out"
+require "chef/mixin/which"
+require "chef/http/simple"
+require "chef/provider/noop"
+
+class Chef
+  class Provider
+    class YumRepository < Chef::Provider
+      use_inline_resources
+
+      extend Chef::Mixin::Which
+
+      provides :yum_repository do
+        which "yum"
+      end
+
+      def whyrun_supported?; true; end
+
+      def load_current_resource; end
+
+      action :create do
+        declare_resource(:template, "/etc/yum.repos.d/#{new_resource.repositoryid}.repo") do
+          if template_available?(new_resource.source)
+            source new_resource.source
+          else
+            source ::File.expand_path("../support/yum_repo.erb", __FILE__)
+            local true
+          end
+          sensitive new_resource.sensitive
+          variables(config: new_resource)
+          mode new_resource.mode
+          if new_resource.make_cache
+            notifies :run, "execute[yum clean metadata #{new_resource.repositoryid}]", :immediately if new_resource.clean_metadata || new_resource.clean_headers
+            notifies :run, "execute[yum-makecache-#{new_resource.repositoryid}]", :immediately
+            notifies :create, "ruby_block[yum-cache-reload-#{new_resource.repositoryid}]", :immediately
+          end
+        end
+
+        declare_resource(:execute, "yum clean metadata #{new_resource.repositoryid}") do
+          command "yum clean metadata --disablerepo=* --enablerepo=#{new_resource.repositoryid}"
+          action :nothing
+        end
+
+        # get the metadata for this repo only
+        declare_resource(:execute, "yum-makecache-#{new_resource.repositoryid}") do
+          command "yum -q -y makecache --disablerepo=* --enablerepo=#{new_resource.repositoryid}"
+          action :nothing
+          only_if { new_resource.enabled }
+        end
+
+        # reload internal Chef yum cache
+        declare_resource(:ruby_block, "yum-cache-reload-#{new_resource.repositoryid}") do
+          block { Chef::Provider::Package::Yum::YumCache.instance.reload }
+          action :nothing
+        end
+      end
+
+      action :delete do
+        declare_resource(:file, "/etc/yum.repos.d/#{new_resource.repositoryid}.repo") do
+          action :delete
+          notifies :run, "execute[yum clean all #{new_resource.repositoryid}]", :immediately
+          notifies :create, "ruby_block[yum-cache-reload-#{new_resource.repositoryid}]", :immediately
+        end
+
+        declare_resource(:execute, "yum clean all #{new_resource.repositoryid}") do
+          command "yum clean all --disablerepo=* --enablerepo=#{new_resource.repositoryid}"
+          only_if "yum repolist | grep -P '^#{new_resource.repositoryid}([ \t]|$)'"
+          action :nothing
+        end
+
+        declare_resource(:ruby_block, "yum-cache-reload-#{new_resource.repositoryid}") do
+          block { Chef::Provider::Package::Yum::YumCache.instance.reload }
+          action :nothing
+        end
+      end
+
+      action :makecache do
+        declare_resource(:execute, "yum-makecache-#{new_resource.repositoryid}") do
+          command "yum -q -y makecache --disablerepo=* --enablerepo=#{new_resource.repositoryid}"
+          action :run
+          only_if { new_resource.enabled }
+        end
+
+        declare_resource(:ruby_block, "yum-cache-reload-#{new_resource.repositoryid}") do
+          block { Chef::Provider::Package::Yum::YumCache.instance.reload }
+          action :run
+        end
+      end
+
+      alias_method :action_add, :action_create
+      alias_method :action_remove, :action_delete
+
+      def template_available?(path)
+        !path.nil? && run_context.has_template_in_cookbook?(new_resource.cookbook_name, path)
+      end
+
+    end
+  end
+end
+
+Chef::Provider::Noop.provides :yum_repository
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index 14c47df..affa5ca 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -58,6 +58,7 @@ require "chef/provider/systemd_unit"
 require "chef/provider/template"
 require "chef/provider/user"
 require "chef/provider/whyrun_safe_ruby_block"
+require "chef/provider/yum_repository"
 
 require "chef/provider/env/windows"
 
@@ -101,12 +102,13 @@ require "chef/provider/service/macosx"
 require "chef/provider/service/aixinit"
 require "chef/provider/service/aix"
 
+require "chef/provider/user/aix"
 require "chef/provider/user/dscl"
+require "chef/provider/user/linux"
 require "chef/provider/user/pw"
+require "chef/provider/user/solaris"
 require "chef/provider/user/useradd"
 require "chef/provider/user/windows"
-require "chef/provider/user/solaris"
-require "chef/provider/user/aix"
 
 require "chef/provider/group/aix"
 require "chef/provider/group/dscl"
diff --git a/lib/chef/recipe.rb b/lib/chef/recipe.rb
index 3cc5634..77d82f8 100644
--- a/lib/chef/recipe.rb
+++ b/lib/chef/recipe.rb
@@ -18,16 +18,7 @@
 #
 
 require "chef/dsl/recipe"
-require "chef/dsl/data_query"
-require "chef/dsl/platform_introspection"
-require "chef/dsl/include_recipe"
-require "chef/dsl/registry_helper"
-require "chef/dsl/reboot_pending"
-require "chef/dsl/audit"
-require "chef/dsl/powershell"
-
 require "chef/mixin/from_file"
-
 require "chef/mixin/deprecation"
 
 class Chef
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index 2633187..d11fa1c 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -260,6 +260,18 @@ class Chef
     end
 
     #
+    # Token class to hold an unresolved subscribes call with an associated
+    # run context.
+    #
+    # @api private
+    # @see Resource#subscribes
+    class UnresolvedSubscribes < self
+      # The full key ise given as the name in {Resource#subscribes}
+      alias_method :to_s, :name
+      alias_method :declared_key, :name
+    end
+
+    #
     # Subscribes to updates from other resources, causing a particular action to
     # run on *this* resource when the other resource is updated.
     #
@@ -326,7 +338,7 @@ class Chef
       resources = [resources].flatten
       resources.each do |resource|
         if resource.is_a?(String)
-          resource = Chef::Resource.new(resource, run_context)
+          resource = UnresolvedSubscribes.new(resource, run_context)
         end
         if resource.run_context.nil?
           resource.run_context = run_context
@@ -485,7 +497,7 @@ class Chef
       state_properties = self.class.state_properties
       state_properties.each do |property|
         if property.identity? || property.is_set?(self)
-          state[property.name] = send(property.name)
+          state[property.name] = property.sensitive? ? "*sensitive value suppressed*" : send(property.name)
         end
       end
       state
@@ -1264,15 +1276,15 @@ class Chef
     # resolve_resource_reference on each in turn, causing them to
     # resolve lazy/forward references.
     def resolve_notification_references
-      run_context.before_notifications(self).each { |n|
+      run_context.before_notifications(self).each do |n|
         n.resolve_resource_reference(run_context.resource_collection)
-      }
-      run_context.immediate_notifications(self).each { |n|
+      end
+      run_context.immediate_notifications(self).each do |n|
         n.resolve_resource_reference(run_context.resource_collection)
-      }
-      run_context.delayed_notifications(self).each {|n|
+      end
+      run_context.delayed_notifications(self).each do |n|
         n.resolve_resource_reference(run_context.resource_collection)
-      }
+      end
     end
 
     # Helper for #notifies
@@ -1530,23 +1542,6 @@ class Chef
     end
 
     # @api private
-    def self.register_deprecated_lwrp_class(resource_class, class_name)
-      if Chef::Resource.const_defined?(class_name, false)
-        Chef::Log.warn "#{class_name} already exists!  Deprecation class overwrites #{resource_class}"
-        Chef::Resource.send(:remove_const, class_name)
-      end
-
-      if !Chef::Config[:treat_deprecation_warnings_as_errors]
-        Chef::Resource.const_set(class_name, resource_class)
-        deprecated_constants[class_name.to_sym] = resource_class
-      end
-    end
-
-    def self.deprecated_constants
-      @deprecated_constants ||= {}
-    end
-
-    # @api private
     def lookup_provider_constant(name, action = :nothing)
       begin
         self.class.provider_base.const_get(convert_to_class_name(name.to_s))
@@ -1559,7 +1554,26 @@ class Chef
       end
     end
 
-    private
+    module DeprecatedLWRPClass
+
+      # @api private
+      def register_deprecated_lwrp_class(resource_class, class_name)
+        if Chef::Resource.const_defined?(class_name, false)
+          Chef::Log.warn "#{class_name} already exists!  Deprecation class overwrites #{resource_class}"
+          Chef::Resource.send(:remove_const, class_name)
+        end
+
+        if !Chef::Config[:treat_deprecation_warnings_as_errors]
+          Chef::Resource.const_set(class_name, resource_class)
+          Chef::Resource.deprecated_constants[class_name.to_sym] = resource_class
+        end
+      end
+
+      def deprecated_constants
+        raise "Deprecated constants should be called only on Chef::Resource" unless self == Chef::Resource
+        @deprecated_constants ||= {}
+      end
+    end
 
     def self.remove_canonical_dsl
       if @resource_name
@@ -1569,6 +1583,7 @@ class Chef
         end
       end
     end
+    extend DeprecatedLWRPClass
   end
 end
 
diff --git a/lib/chef/resource/action_class.rb b/lib/chef/resource/action_class.rb
index 89b2349..98b4d87 100644
--- a/lib/chef/resource/action_class.rb
+++ b/lib/chef/resource/action_class.rb
@@ -28,6 +28,10 @@ class Chef
         "#{new_resource || "<no resource>"} action #{action ? action.inspect : "<no action>"}"
       end
 
+      def whyrun_supported?
+        true
+      end
+
       #
       # If load_current_value! is defined on the resource, use that.
       #
diff --git a/lib/chef/resource/conditional.rb b/lib/chef/resource/conditional.rb
index cdb9f13..452718c 100644
--- a/lib/chef/resource/conditional.rb
+++ b/lib/chef/resource/conditional.rb
@@ -103,7 +103,15 @@ class Chef
       end
 
       def evaluate_block
-        @block.call
+        @block.call.tap do |rv|
+          if rv.is_a?(String) && !rv.empty?
+            # This is probably a mistake:
+            #   not_if { "command" }
+            sanitized_rv = @parent_resource.sensitive ? "a string" : rv.inspect
+            Chef::Log.warn("#{@positivity} block for #{@parent_resource} returned #{sanitized_rv}, did you mean to run a command?" +
+              (@parent_resource.sensitive ? "" : " If so use '#{@positivity} #{sanitized_rv}' in your code."))
+          end
+        end
       end
 
       def short_description
diff --git a/lib/chef/resource/file.rb b/lib/chef/resource/file.rb
index 7088e76..207de63 100644
--- a/lib/chef/resource/file.rb
+++ b/lib/chef/resource/file.rb
@@ -81,7 +81,7 @@ class Chef
       end
 
       def special_docker_files?(file)
-        %w{/etc/hosts /etc/hostname /etc/resolv.conf}.include?(Pathname(file).cleanpath.to_path)
+        %w{/etc/hosts /etc/hostname /etc/resolv.conf}.include?(Pathname(file.scrub).cleanpath.to_path)
       end
     end
   end
diff --git a/lib/chef/resource/freebsd_package.rb b/lib/chef/resource/freebsd_package.rb
index 540774e..a94dd0a 100644
--- a/lib/chef/resource/freebsd_package.rb
+++ b/lib/chef/resource/freebsd_package.rb
@@ -45,7 +45,7 @@ class Chef
       def ships_with_pkgng?
         # It was not until __FreeBSD_version 1000017 that pkgng became
         # the default binary package manager. See '/usr/ports/Mk/bsd.port.mk'.
-        node.automatic[:os_version].to_i >= 1000017
+        node[:os_version].to_i >= 1000017
       end
 
       def assign_provider
diff --git a/lib/chef/resource/launchd.rb b/lib/chef/resource/launchd.rb
index f3c378a..8dca90e 100644
--- a/lib/chef/resource/launchd.rb
+++ b/lib/chef/resource/launchd.rb
@@ -68,7 +68,7 @@ class Chef
       property :hard_resource_limits, Hash
       property :inetd_compatibility, Hash
       property :init_groups, [ TrueClass, FalseClass ]
-      property :keep_alive, [ TrueClass, FalseClass ]
+      property :keep_alive, [ TrueClass, FalseClass, Hash ]
       property :launch_only_once, [ TrueClass, FalseClass ]
       property :ld_group, String
       property :limit_load_from_hosts, Array
diff --git a/lib/chef/resource/user.rb b/lib/chef/resource/user.rb
index 012fa27..06dfe95 100644
--- a/lib/chef/resource/user.rb
+++ b/lib/chef/resource/user.rb
@@ -21,6 +21,7 @@ require "chef/resource"
 class Chef
   class Resource
     class User < Chef::Resource
+      resource_name :user_resource_abstract_base_class # this prevents magickal class name DSL wiring
       identity_attr :username
 
       state_attrs :uid, :gid, :home
@@ -42,8 +43,8 @@ class Chef
         @force = false
         @non_unique = false
         @supports = {
-          :manage_home => false,
-          :non_unique => false,
+          manage_home: false,
+          non_unique: false,
         }
         @iterations = 27855
         @salt = nil
@@ -155,6 +156,19 @@ class Chef
         )
       end
 
+      def supports(args = {})
+        if args.key?(:manage_home)
+          Chef.log_deprecation "supports { manage_home: #{args[:manage_home]} } on the user resource is deprecated and will be removed in Chef 13, set manage_home: #{args[:manage_home]} instead"
+        end
+        if args.key?(:non_unique)
+          Chef.log_deprecation "supports { non_unique: #{args[:non_unique]} } on the user resource is deprecated and will be removed in Chef 13, set non_unique: #{args[:non_unique]} instead"
+        end
+        super
+      end
+
+      def supports=(args)
+        supports(args)
+      end
     end
   end
 end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/resource/user/aix_user.rb
similarity index 67%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/resource/user/aix_user.rb
index 1a900af..7c07db2 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/resource/user/aix_user.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +15,16 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/resource/user"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
+  class Resource
+    class User
+      class AixUser < Chef::Resource::User
+        resource_name :aix_user
+
+        provides :aix_user
+        provides :user, os: "aix"
       end
     end
   end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/resource/user/dscl_user.rb
similarity index 67%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/resource/user/dscl_user.rb
index 1a900af..61517d8 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/resource/user/dscl_user.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +15,16 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/resource/user"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
+  class Resource
+    class User
+      class DsclUser < Chef::Resource::User
+        resource_name :dscl_user
+
+        provides :dscl_user
+        provides :user, os: "darwin"
       end
     end
   end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/resource/user/linux_user.rb
similarity index 57%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/resource/user/linux_user.rb
index 1a900af..ec60ac8 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/resource/user/linux_user.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +15,26 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/resource/user"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
+  class Resource
+    class User
+      class LinuxUser < Chef::Resource::User
+        resource_name :linux_user
+
+        provides :linux_user
+        provides :user, os: "linux"
+
+        def initialize(name, run_context = nil)
+          super
+          @supports = {
+            manage_home: false,
+            non_unique: false,
+          }
+          @manage_home = false
+        end
+
       end
     end
   end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/resource/user/pw_user.rb
similarity index 67%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/resource/user/pw_user.rb
index 1a900af..873be19 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/resource/user/pw_user.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +15,16 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/resource/user"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
+  class Resource
+    class User
+      class PwUser < Chef::Resource::User
+        resource_name :pw_user
+
+        provides :pw_user
+        provides :user, os: "freebsd"
       end
     end
   end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/resource/user/solaris_user.rb
similarity index 67%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/resource/user/solaris_user.rb
index 1a900af..bb89722 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/resource/user/solaris_user.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +15,16 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/resource/user"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
+  class Resource
+    class User
+      class SolarisUser < Chef::Resource::User
+        resource_name :solaris_user
+
+        provides :solaris_user
+        provides :user, os: %w{omnios solaris2}
       end
     end
   end
diff --git a/lib/chef/dsl/powershell.rb b/lib/chef/resource/user/windows_user.rb
similarity index 67%
copy from lib/chef/dsl/powershell.rb
copy to lib/chef/resource/user/windows_user.rb
index 1a900af..d1a249f 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/lib/chef/resource/user/windows_user.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,13 +15,16 @@
 # limitations under the License.
 #
 
-require "chef/util/powershell/ps_credential"
+require "chef/resource/user"
 
 class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
+  class Resource
+    class User
+      class WindowsUser < Chef::Resource::User
+        resource_name :windows_user
+
+        provides :windows_user
+        provides :user, os: "windows"
       end
     end
   end
diff --git a/lib/chef/resource/yum_package.rb b/lib/chef/resource/yum_package.rb
index 1f749b9..9d69897 100644
--- a/lib/chef/resource/yum_package.rb
+++ b/lib/chef/resource/yum_package.rb
@@ -27,18 +27,19 @@ class Chef
 
       # Install a specific arch
       property :arch, [ String, Array ]
-      property :flush_cache, Hash, default: { before: false, after: false }, coerce: proc { |v|
-        # TODO these append rather than set. This is probably wrong behavior, but we're preserving it until we know
-        if v.is_a?(Array)
-          v.each { |arg| flush_cache[arg] = true }
-          flush_cache
-        elsif v.any?
-          v
-        else
-          # TODO calling flush_cache({}) does a get instead of a set. This is probably wrong behavior, but we're preserving it until we know
-          flush_cache
-        end
-      }
+      # the {} on the proc here is because rspec chokes if it's do...end
+      property :flush_cache,
+               Hash,
+               default: { before: false, after: false },
+               coerce: proc { |v|
+                 if v.is_a?(Array)
+                   v.each_with_object({}) { |arg, obj| obj[arg] = true }
+                 elsif v.any?
+                   v
+                 else
+                   { before: v, after: v }
+                 end
+               }
       property :allow_downgrade, [ true, false ], default: false
       property :yum_binary, String
 
diff --git a/lib/chef/resource/yum_repository.rb b/lib/chef/resource/yum_repository.rb
new file mode 100644
index 0000000..3633f44
--- /dev/null
+++ b/lib/chef/resource/yum_repository.rb
@@ -0,0 +1,79 @@
+#
+# Author:: Thom May (<thom at chef.io>)
+# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/resource"
+
+class Chef
+  class Resource
+    class YumRepository < Chef::Resource
+      resource_name :yum_repository
+      provides :yum_repository
+
+      # http://linux.die.net/man/5/yum.conf
+      property :baseurl, String, regex: /.*/
+      property :cost, String, regex: /^\d+$/
+      property :clean_headers, [TrueClass, FalseClass], default: false # deprecated
+      property :clean_metadata, [TrueClass, FalseClass], default: true
+      property :description, String, regex: /.*/, default: "Yum Repository"
+      property :enabled, [TrueClass, FalseClass], default: true
+      property :enablegroups, [TrueClass, FalseClass]
+      property :exclude, String, regex: /.*/
+      property :failovermethod, String, equal_to: %w{priority roundrobin}
+      property :fastestmirror_enabled, [TrueClass, FalseClass]
+      property :gpgcheck, [TrueClass, FalseClass]
+      property :gpgkey, [String, Array], regex: /.*/
+      property :http_caching, String, equal_to: %w{packages all none}
+      property :include_config, String, regex: /.*/
+      property :includepkgs, String, regex: /.*/
+      property :keepalive, [TrueClass, FalseClass]
+      property :make_cache, [TrueClass, FalseClass], default: true
+      property :max_retries, [String, Integer]
+      property :metadata_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/, /never/]
+      property :mirrorexpire, String, regex: /.*/
+      property :mirrorlist, String, regex: /.*/
+      property :mirror_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/]
+      property :mirrorlist_expire, String, regex: [/^\d+$/, /^\d+[mhd]$/]
+      property :mode, default: "0644"
+      property :priority, String, regex: /^(\d?[0-9]|[0-9][0-9])$/
+      property :proxy, String, regex: /.*/
+      property :proxy_username, String, regex: /.*/
+      property :proxy_password, String, regex: /.*/
+      property :username, String, regex: /.*/
+      property :password, String, regex: /.*/
+      property :repo_gpgcheck, [TrueClass, FalseClass]
+      property :report_instanceid, [TrueClass, FalseClass]
+      property :repositoryid, String, regex: /.*/, name_attribute: true
+      property :sensitive, [TrueClass, FalseClass], default: false
+      property :skip_if_unavailable, [TrueClass, FalseClass]
+      property :source, String, regex: /.*/
+      property :sslcacert, String, regex: /.*/
+      property :sslclientcert, String, regex: /.*/
+      property :sslclientkey, String, regex: /.*/
+      property :sslverify, [TrueClass, FalseClass]
+      property :timeout, String, regex: /^\d+$/
+      property :options, Hash
+
+      default_action :create
+      allowed_actions :create, :remove, :make_cache, :add, :delete
+
+      # provide compatibility with the yum cookbook < 3.0 properties
+      alias_method :url, :baseurl
+      alias_method :keyurl, :gpgkey
+    end
+  end
+end
diff --git a/lib/chef/resource_builder.rb b/lib/chef/resource_builder.rb
index 138e401..57c57dd 100644
--- a/lib/chef/resource_builder.rb
+++ b/lib/chef/resource_builder.rb
@@ -31,7 +31,7 @@ class Chef
     attr_reader :resource
 
     # FIXME (ruby-2.1 syntax): most of these are mandatory
-    def initialize(type:nil, name:nil, created_at: nil, params: nil, run_context: nil, cookbook_name: nil, recipe_name: nil, enclosing_provider: nil)
+    def initialize(type: nil, name: nil, created_at: nil, params: nil, run_context: nil, cookbook_name: nil, recipe_name: nil, enclosing_provider: nil)
       @type               = type
       @name               = name
       @created_at         = created_at
@@ -104,7 +104,11 @@ class Chef
     end
 
     def is_trivial_resource?(resource)
-      identicalish_resources?(resource_class.new(name, run_context), resource)
+      trivial_resource = resource_class.new(name, run_context)
+      # force un-lazy the name property on the created trivial resource
+      name_property = resource_class.properties.find { |sym, p| p.name_property? }
+      trivial_resource.send(name_property[0]) unless name_property.nil?
+      identicalish_resources?(trivial_resource, resource)
     end
 
     # this is an equality test specific to checking for 3694 cloning warnings
@@ -124,9 +128,10 @@ class Chef
     end
 
     def emit_cloned_resource_warning
-      Chef::Log.warn("Cloning resource attributes for #{resource} from prior resource (CHEF-3694)")
-      Chef::Log.warn("Previous #{prior_resource}: #{prior_resource.source_line}") if prior_resource.source_line
-      Chef::Log.warn("Current  #{resource}: #{resource.source_line}") if resource.source_line
+      message = "Cloning resource attributes for #{resource} from prior resource (CHEF-3694)"
+      message << "\nPrevious #{prior_resource}: #{prior_resource.source_line}" if prior_resource.source_line
+      message << "\nCurrent  #{resource}: #{resource.source_line}" if resource.source_line
+      Chef.log_deprecation(message)
     end
 
     def emit_harmless_cloning_debug
diff --git a/lib/chef/resource_collection/stepable_iterator.rb b/lib/chef/resource_collection/stepable_iterator.rb
index d116576..958ffa2 100644
--- a/lib/chef/resource_collection/stepable_iterator.rb
+++ b/lib/chef/resource_collection/stepable_iterator.rb
@@ -100,9 +100,7 @@ class Chef
       end
 
       def iterate
-        while @position < size && !paused?
-          step
-        end
+        step while @position < size && !paused?
         collection
       end
 
diff --git a/lib/chef/resource_reporter.rb b/lib/chef/resource_reporter.rb
index 4135483..8422870 100644
--- a/lib/chef/resource_reporter.rb
+++ b/lib/chef/resource_reporter.rb
@@ -26,11 +26,11 @@ require "chef/event_dispatch/base"
 class Chef
   class ResourceReporter < EventDispatch::Base
 
-    class ResourceReport < Struct.new(:new_resource,
-                                      :current_resource,
-                                      :action,
-                                      :exception,
-                                      :elapsed_time)
+    ResourceReport = Struct.new(:new_resource,
+                                :current_resource,
+                                :action,
+                                :exception,
+                                :elapsed_time) do
 
       def self.new_with_current_state(new_resource, action, current_resource)
         report = new
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index af9c918..2afd47a 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -82,9 +82,16 @@ require "chef/resource/smartos_package"
 require "chef/resource/template"
 require "chef/resource/timestamped_deploy"
 require "chef/resource/user"
+require "chef/resource/user/aix_user"
+require "chef/resource/user/dscl_user"
+require "chef/resource/user/linux_user"
+require "chef/resource/user/pw_user"
+require "chef/resource/user/solaris_user"
+require "chef/resource/user/windows_user"
 require "chef/resource/whyrun_safe_ruby_block"
 require "chef/resource/windows_package"
 require "chef/resource/yum_package"
+require "chef/resource/yum_repository"
 require "chef/resource/lwrp_base"
 require "chef/resource/bff_package"
 require "chef/resource/zypper_package"
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index 29c936a..5d29f76 100644
--- a/lib/chef/run_context.rb
+++ b/lib/chef/run_context.rb
@@ -194,12 +194,10 @@ class Chef
     # @param [Chef::Resource::Notification] The notification to add.
     #
     def notifies_before(notification)
-      nr = notification.notifying_resource
-      if nr.instance_of?(Chef::Resource)
-        before_notification_collection[nr.name] << notification
-      else
-        before_notification_collection[nr.declared_key] << notification
-      end
+      # Note for the future, notification.notifying_resource may be an instance
+      # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
+      # with a string value.
+      before_notification_collection[notification.notifying_resource.declared_key] << notification
     end
 
     #
@@ -208,12 +206,10 @@ class Chef
     # @param [Chef::Resource::Notification] The notification to add.
     #
     def notifies_immediately(notification)
-      nr = notification.notifying_resource
-      if nr.instance_of?(Chef::Resource)
-        immediate_notification_collection[nr.name] << notification
-      else
-        immediate_notification_collection[nr.declared_key] << notification
-      end
+      # Note for the future, notification.notifying_resource may be an instance
+      # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
+      # with a string value.
+      immediate_notification_collection[notification.notifying_resource.declared_key] << notification
     end
 
     #
@@ -222,12 +218,10 @@ class Chef
     # @param [Chef::Resource::Notification] The notification to add.
     #
     def notifies_delayed(notification)
-      nr = notification.notifying_resource
-      if nr.instance_of?(Chef::Resource)
-        delayed_notification_collection[nr.name] << notification
-      else
-        delayed_notification_collection[nr.declared_key] << notification
-      end
+      # Note for the future, notification.notifying_resource may be an instance
+      # of Chef::Resource::UnresolvedSubscribes when calling {Resource#subscribes}
+      # with a string value.
+      delayed_notification_collection[notification.notifying_resource.declared_key] << notification
     end
 
     #
@@ -245,50 +239,29 @@ class Chef
     #
     # Get the list of before notifications sent by the given resource.
     #
-    # TODO seriously, this is actually wrong.  resource.name is not unique,
-    # you need the type as well.
-    #
     # @return [Array[Notification]]
     #
     def before_notifications(resource)
-      if resource.instance_of?(Chef::Resource)
-        return before_notification_collection[resource.name]
-      else
-        return before_notification_collection[resource.declared_key]
-      end
+      return before_notification_collection[resource.declared_key]
     end
 
     #
     # Get the list of immediate notifications sent by the given resource.
     #
-    # TODO seriously, this is actually wrong.  resource.name is not unique,
-    # you need the type as well.
-    #
     # @return [Array[Notification]]
     #
     def immediate_notifications(resource)
-      if resource.instance_of?(Chef::Resource)
-        return immediate_notification_collection[resource.name]
-      else
-        return immediate_notification_collection[resource.declared_key]
-      end
+      return immediate_notification_collection[resource.declared_key]
     end
 
     #
     # Get the list of delayed (end of run) notifications sent by the given
     # resource.
     #
-    # TODO seriously, this is actually wrong.  resource.name is not unique,
-    # you need the type as well.
-    #
     # @return [Array[Notification]]
     #
     def delayed_notifications(resource)
-      if resource.instance_of?(Chef::Resource)
-        return delayed_notification_collection[resource.name]
-      else
-        return delayed_notification_collection[resource.declared_key]
-      end
+      return delayed_notification_collection[resource.declared_key]
     end
 
     #
@@ -685,9 +658,11 @@ ERROR_MESSAGE
       }.map { |x| x.to_sym }
 
       # Verify that we didn't miss any methods
-      missing_methods = superclass.instance_methods(false) - instance_methods(false) - CHILD_STATE
-      if !missing_methods.empty?
-        raise "ERROR: not all methods of RunContext accounted for in ChildRunContext! All methods must be marked as child methods with CHILD_STATE or delegated to the parent_run_context. Missing #{missing_methods.join(", ")}."
+      unless @__skip_method_checking # hook specifically for compat_resource
+        missing_methods = superclass.instance_methods(false) - instance_methods(false) - CHILD_STATE
+        if !missing_methods.empty?
+          raise "ERROR: not all methods of RunContext accounted for in ChildRunContext! All methods must be marked as child methods with CHILD_STATE or delegated to the parent_run_context. Missing #{missing_methods.join(", ")}."
+        end
       end
     end
   end
diff --git a/lib/chef/run_context/cookbook_compiler.rb b/lib/chef/run_context/cookbook_compiler.rb
index bdf3a12..b2a8d23 100644
--- a/lib/chef/run_context/cookbook_compiler.rb
+++ b/lib/chef/run_context/cookbook_compiler.rb
@@ -137,13 +137,14 @@ class Chef
         @events.recipe_load_start(run_list_expansion.recipes.size)
         run_list_expansion.recipes.each do |recipe|
           begin
+            path = resolve_recipe(recipe)
             @run_context.load_recipe(recipe)
+            @events.recipe_file_loaded(path, recipe)
           rescue Chef::Exceptions::RecipeNotFound => e
             @events.recipe_not_found(e)
             raise
           rescue Exception => e
-            path = resolve_recipe(recipe)
-            @events.recipe_file_load_failed(path, e)
+            @events.recipe_file_load_failed(path, e, recipe)
             raise
           end
         end
diff --git a/lib/chef/run_list.rb b/lib/chef/run_list.rb
index 4dea938..3ac5fab 100644
--- a/lib/chef/run_list.rb
+++ b/lib/chef/run_list.rb
@@ -47,13 +47,13 @@ class Chef
     end
 
     def role_names
-      @run_list_items.inject([]) { |memo, run_list_item| memo << run_list_item.name if run_list_item.role? ; memo }
+      @run_list_items.inject([]) { |memo, run_list_item| memo << run_list_item.name if run_list_item.role?; memo }
     end
 
     alias :roles :role_names
 
     def recipe_names
-      @run_list_items.inject([]) { |memo, run_list_item| memo << run_list_item.name if run_list_item.recipe? ; memo }
+      @run_list_items.inject([]) { |memo, run_list_item| memo << run_list_item.name if run_list_item.recipe?; memo }
     end
 
     alias :recipes :recipe_names
diff --git a/lib/chef/search/query.rb b/lib/chef/search/query.rb
index ebf13be..bea8205 100644
--- a/lib/chef/search/query.rb
+++ b/lib/chef/search/query.rb
@@ -21,6 +21,7 @@ require "chef/exceptions"
 require "chef/server_api"
 
 require "uri"
+require "addressable/uri"
 
 class Chef
   class Search
@@ -29,7 +30,7 @@ class Chef
       attr_accessor :rest
       attr_reader :config
 
-      def initialize(url = nil, config:Chef::Config)
+      def initialize(url = nil, config: Chef::Config)
         @config = config
         @url = url
       end
@@ -134,19 +135,21 @@ WARNDEP
         args_h
       end
 
-      def escape(s)
-        s && URI.escape(s.to_s)
+      QUERY_PARAM_VALUE = Addressable::URI::CharacterClasses::QUERY + "\\&\\;"
+
+      def escape_value(s)
+        s && Addressable::URI.encode_component(s.to_s, QUERY_PARAM_VALUE)
       end
 
       def create_query_string(type, query, rows, start, sort)
-        qstr = "search/#{type}?q=#{escape(query)}"
-        qstr += "&sort=#{escape(sort)}" if sort
-        qstr += "&start=#{escape(start)}" if start
-        qstr += "&rows=#{escape(rows)}" if rows
+        qstr = "search/#{type}?q=#{escape_value(query)}"
+        qstr += "&sort=#{escape_value(sort)}" if sort
+        qstr += "&start=#{escape_value(start)}" if start
+        qstr += "&rows=#{escape_value(rows)}" if rows
         qstr
       end
 
-      def call_rest_service(type, query:"*:*", rows:nil, start:0, sort:"X_CHEF_id_CHEF_X asc", filter_result:nil)
+      def call_rest_service(type, query: "*:*", rows: nil, start: 0, sort: "X_CHEF_id_CHEF_X asc", filter_result: nil)
         query_string = create_query_string(type, query, rows, start, sort)
 
         if filter_result
diff --git a/lib/chef/shell.rb b/lib/chef/shell.rb
index aad5c49..26683cc 100644
--- a/lib/chef/shell.rb
+++ b/lib/chef/shell.rb
@@ -148,7 +148,7 @@ module Shell
   end
 
   def self.greeting
-    " #{Etc.getlogin}@#{Shell.session.node.fqdn}"
+    " #{Etc.getlogin}@#{Shell.session.node["fqdn"]}"
   rescue NameError, ArgumentError
     ""
   end
diff --git a/lib/chef/shell/ext.rb b/lib/chef/shell/ext.rb
index 40d7e10..0c10309 100644
--- a/lib/chef/shell/ext.rb
+++ b/lib/chef/shell/ext.rb
@@ -39,13 +39,13 @@ module Shell
         # irb breaks if you prematurely define IRB::JobMangager
         # so these methods need to be defined at the latest possible time.
         unless jobs.respond_to?(:select_session_by_context)
-          def jobs.select_session_by_context(&block)
+          def jobs.select_session_by_context(&block) # rubocop:disable Lint/NestedMethodDefinition
             @jobs.select { |job| block.call(job[1].context.main) }
           end
         end
 
         unless jobs.respond_to?(:session_select)
-          def jobs.select_shell_session(target_context)
+          def jobs.select_shell_session(target_context) # rubocop:disable Lint/NestedMethodDefinition
             session = if target_context.kind_of?(Class)
                         select_session_by_context { |main| main.kind_of?(target_context) }
                       else
diff --git a/lib/chef/shell/shell_session.rb b/lib/chef/shell/shell_session.rb
index 0a8cba5..a458286 100644
--- a/lib/chef/shell/shell_session.rb
+++ b/lib/chef/shell/shell_session.rb
@@ -126,7 +126,7 @@ module Shell
     end
 
     def shorten_node_inspect
-      def @node.inspect
+      def @node.inspect # rubocop:disable Lint/NestedMethodDefinition
         "<Chef::Node:0x#{self.object_id.to_s(16)} @name=\"#{self.name}\">"
       end
     end
diff --git a/lib/chef/util/dsc/configuration_generator.rb b/lib/chef/util/dsc/configuration_generator.rb
index 739a463..8b492d4 100644
--- a/lib/chef/util/dsc/configuration_generator.rb
+++ b/lib/chef/util/dsc/configuration_generator.rb
@@ -48,7 +48,7 @@ class Chef::Util::DSC
       configuration_document_location = find_configuration_document(configuration_name)
 
       if ! configuration_document_location
-        raise RuntimeError, "No DSC configuration for '#{configuration_name}' was generated from supplied DSC script"
+        raise "No DSC configuration for '#{configuration_name}' was generated from supplied DSC script"
       end
 
       configuration_document = get_configuration_document(configuration_document_location)
diff --git a/lib/chef/util/powershell/cmdlet.rb b/lib/chef/util/powershell/cmdlet.rb
index 6ab380c..e300266 100644
--- a/lib/chef/util/powershell/cmdlet.rb
+++ b/lib/chef/util/powershell/cmdlet.rb
@@ -62,8 +62,11 @@ class Chef
             json_depth = @output_format_options[:depth]
           end
 
-          json_command = @json_format ? " | convertto-json -compress -depth #{json_depth} "\
-                                        "> #{streams[:json].path}" : ""
+          json_command = if @json_format
+                           " | convertto-json -compress -depth #{json_depth} > #{streams[:json].path}"
+                         else
+                           ""
+                         end
           redirections = "4> '#{streams[:verbose].path}'"
           command_string = "powershell.exe -executionpolicy bypass -noprofile -noninteractive "\
                            "-command \"trap [Exception] {write-error -exception "\
diff --git a/lib/chef/util/selinux.rb b/lib/chef/util/selinux.rb
index 1aac7ee..edca589 100644
--- a/lib/chef/util/selinux.rb
+++ b/lib/chef/util/selinux.rb
@@ -78,7 +78,7 @@ class Chef
           when 0
             return true
           else
-            raise RuntimeError, "Unknown exit code from command #{selinuxenabled_path}: #{cmd.exitstatus}"
+            raise "Unknown exit code from command #{selinuxenabled_path}: #{cmd.exitstatus}"
           end
         else
           # We assume selinux is not enabled if selinux utils are not
diff --git a/lib/chef/version.rb b/lib/chef/version.rb
index 06450af..947cd9c 100644
--- a/lib/chef/version.rb
+++ b/lib/chef/version.rb
@@ -21,7 +21,7 @@
 
 class Chef
   CHEF_ROOT = File.expand_path("../..", __FILE__)
-  VERSION = "12.11.18"
+  VERSION = "12.14.75"
 end
 
 #
diff --git a/lib/chef/version_class.rb b/lib/chef/version_class.rb
index 35a9f32..f263689 100644
--- a/lib/chef/version_class.rb
+++ b/lib/chef/version_class.rb
@@ -32,10 +32,15 @@ class Chef
       "#{@major}.#{@minor}.#{@patch}"
     end
 
-    def <=>(v)
+    def <=>(other)
       [:major, :minor, :patch].each do |method|
-        ans = (self.send(method) <=> v.send(method))
-        return ans if ans != 0
+        version = self.send(method)
+        begin
+          ans = (version <=> other.send(method))
+        rescue NoMethodError # if the other thing isn't a version object, return nil
+          return nil
+        end
+        return ans unless ans == 0
       end
       0
     end
diff --git a/lib/chef/version_constraint.rb b/lib/chef/version_constraint.rb
index d4fa5df..f10325f 100644
--- a/lib/chef/version_constraint.rb
+++ b/lib/chef/version_constraint.rb
@@ -57,8 +57,8 @@ class Chef
       "#{@op} #{@raw_version}"
     end
 
-    def eql?(o)
-      o.class == self.class && @op == o.op && @version == o.version
+    def eql?(other)
+      other.class == self.class && @op == other.op && @version == other.version
     end
     alias_method :==, :eql?
 
diff --git a/lib/chef/win32/api/error.rb b/lib/chef/win32/api/error.rb
index dc83f9c..12ccdb5 100644
--- a/lib/chef/win32/api/error.rb
+++ b/lib/chef/win32/api/error.rb
@@ -194,12 +194,12 @@ class Chef
         ERROR_INVALID_EXE_SIGNATURE     = 191
         ERROR_EXE_MARKED_INVALID        = 192
         ERROR_BAD_EXE_FORMAT            = 193
-        ERROR_ITERATED_DATA_EXCEEDS_64k = 194
+        ERROR_ITERATED_DATA_EXCEEDS_64k = 194 # rubocop:disable Style/ConstantName
         ERROR_INVALID_MINALLOCSIZE      = 195
         ERROR_DYNLINK_FROM_INVALID_RING = 196
         ERROR_IOPL_NOT_ENABLED          = 197
         ERROR_INVALID_SEGDPL            = 198
-        ERROR_AUTODATASEG_EXCEEDS_64k   = 199
+        ERROR_AUTODATASEG_EXCEEDS_64k   = 199 # rubocop:disable Style/ConstantName
         ERROR_RING2SEG_MUST_BE_MOVABLE  = 200
         ERROR_RELOC_CHAIN_XEEDS_SEGLIM  = 201
         ERROR_INFLOOP_IN_RELOC_CHAIN    = 202
diff --git a/lib/chef/win32/api/net.rb b/lib/chef/win32/api/net.rb
index bec00f6..abf0dd8 100644
--- a/lib/chef/win32/api/net.rb
+++ b/lib/chef/win32/api/net.rb
@@ -45,7 +45,7 @@ class Chef
         USE_FORCE = 1
         USE_LOTS_OF_FORCE = 2 #every windows API should support this flag
 
-        NERR_Success = 0
+        NERR_Success = 0 # rubocop:disable Style/ConstantName
         ERROR_MORE_DATA = 234
 
         ffi_lib "netapi32"
diff --git a/lib/chef/win32/eventlog.rb b/lib/chef/win32/eventlog.rb
index 4254b8e..eae0ae4 100644
--- a/lib/chef/win32/eventlog.rb
+++ b/lib/chef/win32/eventlog.rb
@@ -26,6 +26,6 @@ if Chef::Platform.windows? && (not Chef::Platform.windows_server_2003?)
     end
 
     require "win32/eventlog"
-    Chef::Win32EventLogLoaded = true
+    Chef::Win32EventLogLoaded = true # rubocop:disable Style/ConstantName
   end
 end
diff --git a/lib/chef/win32/file.rb b/lib/chef/win32/file.rb
index 2a8f453..1009f8c 100644
--- a/lib/chef/win32/file.rb
+++ b/lib/chef/win32/file.rb
@@ -39,7 +39,7 @@ class Chef
       # returns nil as per MRI.
       #
       def self.link(old_name, new_name)
-        raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name)
+        raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name) || ::File.symlink?(old_name)
         # TODO do a check for CreateHardLinkW and
         # raise NotImplemented exception on older Windows
         old_name = encode_path(old_name)
@@ -56,7 +56,7 @@ class Chef
       # returns nil as per MRI.
       #
       def self.symlink(old_name, new_name)
-        # raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name)
+        # raise Errno::ENOENT, "(#{old_name}, #{new_name})" unless ::File.exist?(old_name) || ::File.symlink?(old_name)
         # TODO do a check for CreateSymbolicLinkW and
         # raise NotImplemented exception on older Windows
         flags = ::File.directory?(old_name) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0
@@ -75,7 +75,7 @@ class Chef
       def self.symlink?(file_name)
         is_symlink = false
         path = encode_path(file_name)
-        if ::File.exists?(file_name)
+        if ::File.exists?(file_name) || ::File.symlink?(file_name)
           if (GetFileAttributesW(path) & FILE_ATTRIBUTE_REPARSE_POINT) > 0
             file_search_handle(file_name) do |handle, find_data|
               if find_data[:dw_reserved_0] == IO_REPARSE_TAG_SYMLINK
@@ -93,7 +93,7 @@ class Chef
       # will raise a NotImplementedError, as per MRI.
       #
       def self.readlink(link_name)
-        raise Errno::ENOENT, link_name unless ::File.exists?(link_name)
+        raise Errno::ENOENT, link_name unless ::File.exists?(link_name) || ::File.symlink?(link_name)
         symlink_file_handle(link_name) do |handle|
           # Go to DeviceIoControl to get the symlink information
           # http://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx
diff --git a/lib/chef/win32/net.rb b/lib/chef/win32/net.rb
index 0454b17..09db2af 100644
--- a/lib/chef/win32/net.rb
+++ b/lib/chef/win32/net.rb
@@ -207,7 +207,7 @@ class Chef
 
       def self.members_to_lgrmi3(members)
         buf = FFI::MemoryPointer.new(LOCALGROUP_MEMBERS_INFO_3, members.size)
-        members.size.times.collect do |i|
+        Array.new(members.size) do |i|
           member_info = LOCALGROUP_MEMBERS_INFO_3.new(
             buf + i * LOCALGROUP_MEMBERS_INFO_3.size)
           member_info[:lgrmi3_domainandname] = FFI::MemoryPointer.from_string(wstring(members[i]))
diff --git a/lib/chef/win32/security/sid.rb b/lib/chef/win32/security/sid.rb
index 9951b93..f6b88c6 100644
--- a/lib/chef/win32/security/sid.rb
+++ b/lib/chef/win32/security/sid.rb
@@ -279,7 +279,7 @@ class Chef
               status = NetUserEnum(servername, level, filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle)
 
               if status == NERR_Success || status == ERROR_MORE_DATA
-                entriesread.read_long.times.collect do |i|
+                Array.new(entriesread.read_long) do |i|
                   user_info = USER_INFO_3.new(bufptr.read_pointer + i * USER_INFO_3.size)
                   # Check if the account is the Administrator account
                   # RID for the Administrator account is always 500 and it's privilage is set to USER_PRIV_ADMIN
diff --git a/omnibus/.kitchen.yml b/omnibus/.kitchen.yml
index 90283f8..a14d349 100644
--- a/omnibus/.kitchen.yml
+++ b/omnibus/.kitchen.yml
@@ -11,7 +11,7 @@ driver:
     cpus: 4
     memory: 4096
   synced_folders:
-    - ['..', '/home/vagrant/chef']
+    - ['../..', '/vagrant/code']
     - ['../../omnibus', '/home/vagrant/omnibus']
     - ['../../omnibus-software', '/home/vagrant/omnibus-software']
 
@@ -24,7 +24,12 @@ provisioner:
   attributes:
     vagrant:
       this_key_exists_so_we_have_a_vagrant_key: true
-  chef_omnibus_install_options: -P angrychef
+    omnibus:
+      build_user: vagrant
+      build_user_group: vagrant
+      build_user_password: vagrant
+  product_name: angrychef
+  product_version: latest
   chef_omnibus_root: /opt/angrychef
 
 platforms:
@@ -91,12 +96,30 @@ platforms:
       # at `C:\vagrant\code\chef`
       - ['../..', '/vagrant/code']
     provisioner:
+      attributes:
+        omnibus:
+          build_user:          vagrant
+          build_user_group:    Administrators
+          build_user_password: vagrant
+      chef_omnibus_root: /opscode/angrychef
+  # By adding an `i386` to the name the Omnibus cookbook's `load-omnibus-toolchain.bat`
+  # will load the 32-bit version of the MinGW toolchain.
+  - name: windows-2012r2-standard-i386
+    driver:
+      box: chef/windows-server-2012r2-standard # private
+      synced_folders:
+      # We have to mount this repos enclosing folder as the Omnibus build
+      # gets cranky if the mounted ChefDK source folder is a symlink. This
+      # mounts at `C:\vagrant\code` and the ChefDK source folder is available
+      # at `C:\vagrant\code\chef-dk`
+      - ['../..', '/vagrant/code']
+    provisioner:
+      attributes:
+        omnibus:
+          build_user:          vagrant
+          build_user_group:    Administrators
+          build_user_password: vagrant
       chef_omnibus_root: /opscode/angrychef
-
-attribute_defaults: &attribute_defaults
-  build_user:          vagrant
-  build_user_group:    vagrant
-  build_user_password: vagrant
 
 suites:
   # - name: angrychef
@@ -109,7 +132,6 @@ suites:
   - name: chef
     attributes:
       omnibus:
-        <<: *attribute_defaults
         install_dir: /opt/chef
     run_list:
       - omnibus::default
diff --git a/omnibus/Gemfile b/omnibus/Gemfile
index ef1dec3..6e9e9cc 100644
--- a/omnibus/Gemfile
+++ b/omnibus/Gemfile
@@ -1,7 +1,8 @@
 source "https://rubygems.org"
 
-gem "omnibus", git: "https://github.com/chef/omnibus.git"
-gem "omnibus-software", git: "https://github.com/chef/omnibus-software.git"
+gem "omnibus", github: "chef/omnibus", branch: "rhass/COOL-502_with_gcc_investigate"
+gem "omnibus-software", github: "chef/omnibus-software", branch: "lcg/ruby23"
+gem "license_scout", github: "chef/license_scout"
 
 # pedump pessimistically pins multipart-post to a version from 2013 which makes
 # bundler very unhappy. Remove this when upstream has merged zed-0xff/pedump#6 .
@@ -13,12 +14,12 @@ gem "pedump", git: "https://github.com/ksubrama/pedump.git", branch: "patch-1"
 # by running `bundle install --without development` to speed up build times.
 group :development do
   # Use Berkshelf for resolving cookbook dependencies
-  gem "berkshelf", "~> 3.0"
+  gem "berkshelf", "~> 4.0"
 
   # Use Test Kitchen with Vagrant for converging the build environment
-  gem "test-kitchen",    "~> 1.7.1"
+  gem "test-kitchen",    "~> 1.13"
   gem "kitchen-vagrant", "~> 0.19.0"
-  gem "winrm-fs", "~> 0.4.0"
+  gem "winrm-fs", "~> 1.0"
   gem "pry"
   gem "pry-byebug"
   gem "pry-stack_explorer"
diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock
index f165fa5..1512f36 100644
--- a/omnibus/Gemfile.lock
+++ b/omnibus/Gemfile.lock
@@ -1,19 +1,31 @@
 GIT
-  remote: https://github.com/chef/omnibus-software.git
-  revision: 2f04eff7dbec575cb2985d846dacd02a422cd36f
+  remote: git://github.com/chef/license_scout.git
+  revision: dbd17c0a99a12e16dd7af27798df863366efc289
+  specs:
+    license_scout (0.1.2)
+      ffi-yajl (~> 2.2)
+      mixlib-shellout (~> 2.2)
+
+GIT
+  remote: git://github.com/chef/omnibus-software.git
+  revision: f0f4bb4beab18a9b6adbc0a34bcd2f0caf10be5c
+  branch: lcg/ruby23
   specs:
     omnibus-software (4.0.0)
-      omnibus (>= 5.2.0)
+      chef-sugar (>= 3.4.0)
+      omnibus (>= 5.5.0)
 
 GIT
-  remote: https://github.com/chef/omnibus.git
-  revision: a36e70caedceadfcf0d85e2adef44ba0218a60a6
+  remote: git://github.com/chef/omnibus.git
+  revision: 98c9af20a0f79b6ac205613f509b4725eea47f7b
+  branch: rhass/COOL-502_with_gcc_investigate
   specs:
-    omnibus (5.4.0)
+    omnibus (5.5.0)
       aws-sdk (~> 2)
       chef-sugar (~> 3.3)
       cleanroom (~> 1.0)
       ffi-yajl (~> 2.2)
+      license_scout
       mixlib-shellout (~> 2.0)
       mixlib-versioning
       ohai (~> 8.0)
@@ -35,35 +47,37 @@ GIT
 GEM
   remote: https://rubygems.org/
   specs:
-    addressable (2.3.8)
-    artifactory (2.3.2)
-    awesome_print (1.6.1)
-    aws-sdk (2.3.9)
-      aws-sdk-resources (= 2.3.9)
-    aws-sdk-core (2.3.9)
+    addressable (2.4.0)
+    artifactory (2.5.0)
+    awesome_print (1.7.0)
+    aws-sdk (2.6.1)
+      aws-sdk-resources (= 2.6.1)
+    aws-sdk-core (2.6.1)
       jmespath (~> 1.0)
-    aws-sdk-resources (2.3.9)
-      aws-sdk-core (= 2.3.9)
-    berkshelf (3.3.0)
-      addressable (~> 2.3.4)
-      berkshelf-api-client (~> 1.2)
+    aws-sdk-resources (2.6.1)
+      aws-sdk-core (= 2.6.1)
+    berkshelf (4.3.5)
+      addressable (~> 2.3, >= 2.3.4)
+      berkshelf-api-client (~> 2.0, >= 2.0.2)
       buff-config (~> 1.0)
       buff-extensions (~> 1.0)
       buff-shell_out (~> 0.1)
-      celluloid (~> 0.16.0)
+      celluloid (= 0.16.0)
       celluloid-io (~> 0.16.1)
       cleanroom (~> 1.0)
-      faraday (~> 0.9.0)
-      httpclient (~> 2.6.0)
-      minitar (~> 0.5.4)
-      octokit (~> 3.0)
+      faraday (~> 0.9)
+      httpclient (~> 2.7)
+      minitar (~> 0.5, >= 0.5.4)
+      mixlib-archive (~> 0.1)
+      octokit (~> 4.0)
       retryable (~> 2.0)
-      ridley (~> 4.0)
-      solve (~> 1.1)
+      ridley (~> 4.5)
+      solve (~> 2.0)
       thor (~> 0.19)
-    berkshelf-api-client (1.3.1)
+    berkshelf-api-client (2.0.2)
       faraday (~> 0.9.1)
-      httpclient (~> 2.6.0)
+      httpclient (~> 2.7.0)
+      ridley (~> 4.5)
     binding_of_caller (0.7.2)
       debug_inspector (>= 0.0.1)
     buff-config (1.0.1)
@@ -75,47 +89,41 @@ GEM
     buff-shell_out (0.2.0)
       buff-ruby_engine (~> 0.1.0)
     builder (3.2.2)
-    byebug (9.0.4)
+    byebug (9.0.5)
     celluloid (0.16.0)
       timers (~> 4.0.0)
     celluloid-io (0.16.2)
       celluloid (>= 0.16.0)
       nio4r (>= 1.1.0)
-    chef-config (12.10.24)
-      fuzzyurl (~> 0.8.0)
+    chef-config (12.14.60)
+      addressable
+      fuzzyurl
       mixlib-config (~> 2.0)
       mixlib-shellout (~> 2.0)
-    chef-sugar (3.3.0)
+    chef-sugar (3.4.0)
     cleanroom (1.0.0)
     coderay (1.1.1)
     debug_inspector (0.0.2)
-    dep-selector-libgecode (1.2.0)
-    dep_selector (1.0.3)
-      dep-selector-libgecode (~> 1.0)
-      ffi (~> 1.9)
-    diff-lcs (1.2.5)
     erubis (2.7.0)
     faraday (0.9.2)
       multipart-post (>= 1.2, < 3)
-    ffi (1.9.10)
-    ffi (1.9.10-x86-mingw32)
-    ffi-yajl (2.2.3)
+    ffi (1.9.14)
+    ffi (1.9.14-x86-mingw32)
+    ffi-yajl (2.3.0)
       libyajl2 (~> 1.2)
-    fuzzyurl (0.8.0)
+    fuzzyurl (0.9.0)
     gssapi (1.2.0)
       ffi (>= 1.0.1)
     gyoku (1.3.1)
       builder (>= 2.1.2)
-    hashie (3.4.4)
+    hashie (3.4.6)
     hitimes (1.2.4)
     hitimes (1.2.4-x86-mingw32)
-    httpclient (2.6.0.1)
+    httpclient (2.7.2)
     iostruct (0.0.4)
     ipaddress (0.8.3)
-    jmespath (1.2.4)
-      json_pure (>= 1.8.1)
-    json (1.8.3)
-    json_pure (1.8.3)
+    jmespath (1.3.1)
+    json (2.0.2)
     kitchen-vagrant (0.19.0)
       test-kitchen (~> 1.4)
     libyajl2 (1.2.0)
@@ -125,47 +133,49 @@ GEM
       multi_json (~> 1.10)
     method_source (0.8.2)
     minitar (0.5.4)
-    mixlib-authentication (1.4.0)
+    mixlib-archive (0.2.0)
+      mixlib-log
+    mixlib-authentication (1.4.1)
       mixlib-log
-      rspec-core (~> 3.2)
-      rspec-expectations (~> 3.2)
-      rspec-mocks (~> 3.2)
-    mixlib-cli (1.6.0)
-    mixlib-config (2.2.1)
-    mixlib-install (1.0.12)
+    mixlib-cli (1.7.0)
+    mixlib-config (2.2.4)
+    mixlib-install (1.2.0)
       artifactory
       mixlib-shellout
       mixlib-versioning
-    mixlib-log (1.6.0)
-    mixlib-shellout (2.2.6)
-    mixlib-shellout (2.2.6-universal-mingw32)
+    mixlib-log (1.7.1)
+    mixlib-shellout (2.2.7)
+    mixlib-shellout (2.2.7-universal-mingw32)
       win32-process (~> 0.8.2)
       wmi-lite (~> 1.0)
     mixlib-versioning (1.1.0)
+    molinillo (0.4.5)
     multi_json (1.12.1)
     multipart-post (1.2.0)
     net-scp (1.2.1)
       net-ssh (>= 2.6.5)
-    net-ssh (3.1.1)
+    net-ssh (3.2.0)
+    net-ssh-gateway (1.2.0)
+      net-ssh (>= 2.6.5)
     nio4r (1.2.1)
     nori (2.6.0)
-    octokit (3.8.0)
-      sawyer (~> 0.6.0, >= 0.5.3)
-    ohai (8.16.0)
+    octokit (4.3.0)
+      sawyer (~> 0.7.0, >= 0.5.3)
+    ohai (8.20.0)
       chef-config (>= 12.5.0.alpha.1, < 13)
       ffi (~> 1.9)
       ffi-yajl (~> 2.2)
       ipaddress
       mixlib-cli
       mixlib-config (~> 2.0)
-      mixlib-log
+      mixlib-log (>= 1.7.1, < 2.0)
       mixlib-shellout (~> 2.0)
       plist (~> 3.1)
       systemu (~> 2.6.4)
       wmi-lite (~> 1.0)
     plist (3.2.0)
     progressbar (0.21.0)
-    pry (0.10.3)
+    pry (0.10.4)
       coderay (~> 1.1.0)
       method_source (~> 0.8.1)
       slop (~> 3.4)
@@ -175,52 +185,44 @@ GEM
     pry-stack_explorer (0.4.9.2)
       binding_of_caller (>= 0.7)
       pry (>= 0.9.11)
-    retryable (2.0.3)
-    ridley (4.4.2)
+    retryable (2.0.4)
+    ridley (4.6.1)
       addressable
       buff-config (~> 1.0)
       buff-extensions (~> 1.0)
-      buff-ignore (~> 1.1)
+      buff-ignore (~> 1.1.1)
       buff-shell_out (~> 0.1)
       celluloid (~> 0.16.0)
       celluloid-io (~> 0.16.1)
-      chef-config
+      chef-config (>= 12.5.0)
       erubis
       faraday (~> 0.9.0)
       hashie (>= 2.0.2, < 4.0.0)
-      httpclient (~> 2.6)
+      httpclient (~> 2.7)
       json (>= 1.7.7)
       mixlib-authentication (>= 1.3.0)
       retryable (~> 2.0)
       semverse (~> 1.1)
       varia_model (~> 0.4.0)
-    rspec-core (3.4.4)
-      rspec-support (~> 3.4.0)
-    rspec-expectations (3.4.0)
-      diff-lcs (>= 1.2.0, < 2.0)
-      rspec-support (~> 3.4.0)
-    rspec-mocks (3.4.1)
-      diff-lcs (>= 1.2.0, < 2.0)
-      rspec-support (~> 3.4.0)
-    rspec-support (3.4.1)
     ruby-progressbar (1.8.1)
-    rubyntlm (0.6.0)
+    rubyntlm (0.6.1)
     rubyzip (1.2.0)
     safe_yaml (1.0.4)
-    sawyer (0.6.0)
-      addressable (~> 2.3.5)
+    sawyer (0.7.0)
+      addressable (>= 2.3.5, < 2.5)
       faraday (~> 0.8, < 0.10)
     semverse (1.2.1)
     slop (3.6.0)
-    solve (1.2.1)
-      dep_selector (~> 1.0)
+    solve (2.0.3)
+      molinillo (~> 0.4.2)
       semverse (~> 1.1)
     systemu (2.6.5)
-    test-kitchen (1.7.3)
-      mixlib-install (~> 1.0, >= 1.0.4)
+    test-kitchen (1.13.0)
+      mixlib-install (~> 1.2)
       mixlib-shellout (>= 1.2, < 3.0)
       net-scp (~> 1.1)
       net-ssh (>= 2.9, < 4.0)
+      net-ssh-gateway (~> 1.2.0)
       safe_yaml (~> 1.0)
       thor (~> 0.18)
     thor (0.19.1)
@@ -231,19 +233,20 @@ GEM
       hashie (>= 2.0.2, < 4.0.0)
     win32-process (0.8.3)
       ffi (>= 1.0.0)
-    winrm (1.8.1)
+    winrm (2.0.2)
       builder (>= 2.1.2)
+      erubis (~> 2.7)
       gssapi (~> 1.2)
       gyoku (~> 1.0)
       httpclient (~> 2.2, >= 2.2.0.2)
       logging (>= 1.6.1, < 3.0)
       nori (~> 2.0)
-      rubyntlm (~> 0.6.0)
-    winrm-fs (0.4.2)
+      rubyntlm (~> 0.6.0, >= 0.6.1)
+    winrm-fs (1.0.0)
       erubis (~> 2.7)
       logging (>= 1.6.1, < 3.0)
       rubyzip (~> 1.1)
-      winrm (~> 1.5)
+      winrm (~> 2.0)
     wmi-lite (1.0.0)
     zhexdump (0.0.2)
 
@@ -252,16 +255,17 @@ PLATFORMS
   x86-mingw32
 
 DEPENDENCIES
-  berkshelf (~> 3.0)
+  berkshelf (~> 4.0)
   kitchen-vagrant (~> 0.19.0)
+  license_scout!
   omnibus!
   omnibus-software!
   pedump!
   pry
   pry-byebug
   pry-stack_explorer
-  test-kitchen (~> 1.7.1)
-  winrm-fs (~> 0.4.0)
+  test-kitchen (~> 1.13)
+  winrm-fs (~> 1.0)
 
 BUNDLED WITH
    1.12.5
diff --git a/omnibus/config/software/chef-appbundle.rb b/omnibus/config/software/chef-appbundle.rb
index 495f58b..8ea2110 100644
--- a/omnibus/config/software/chef-appbundle.rb
+++ b/omnibus/config/software/chef-appbundle.rb
@@ -2,6 +2,7 @@ name "chef-appbundle"
 default_version "local_source"
 
 license :project_license
+skip_transitive_dependency_licensing true
 
 source path: project.files_path
 
diff --git a/omnibus/config/software/chef-complete.rb b/omnibus/config/software/chef-complete.rb
index 2c2b1ca..8ca370c 100644
--- a/omnibus/config/software/chef-complete.rb
+++ b/omnibus/config/software/chef-complete.rb
@@ -1,6 +1,7 @@
 name "chef-complete"
 
 license :project_license
+skip_transitive_dependency_licensing true
 
 dependency "chef"
 dependency "chef-appbundle"
@@ -17,5 +18,3 @@ if windows?
   dependency "ruby-windows-devkit"
   dependency "ruby-windows-devkit-bash"
 end
-
-dependency "clean-static-libs"
diff --git a/omnibus/config/software/chef-gem-binding_of_caller.rb b/omnibus/config/software/chef-gem-binding_of_caller.rb
index ed9083f..3e7a9f9 100644
--- a/omnibus/config/software/chef-gem-binding_of_caller.rb
+++ b/omnibus/config/software/chef-gem-binding_of_caller.rb
@@ -7,3 +7,4 @@ BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
 
 license "MIT"
 license_file "https://github.com/banister/binding_of_caller/blob/master/LICENSE"
+skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-byebug.rb b/omnibus/config/software/chef-gem-byebug.rb
index f16daa2..3aef706 100644
--- a/omnibus/config/software/chef-gem-byebug.rb
+++ b/omnibus/config/software/chef-gem-byebug.rb
@@ -7,3 +7,4 @@ BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
 
 license "MIT"
 license_file "https://github.com/deivid-rodriguez/byebug/blob/master/LICENSE"
+skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-debug_inspector.rb b/omnibus/config/software/chef-gem-debug_inspector.rb
index a3432a8..ab81876 100644
--- a/omnibus/config/software/chef-gem-debug_inspector.rb
+++ b/omnibus/config/software/chef-gem-debug_inspector.rb
@@ -7,3 +7,4 @@ BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
 
 license "MIT"
 license_file "https://github.com/banister/debug_inspector/blob/master/README.md"
+skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-ffi-yajl.rb b/omnibus/config/software/chef-gem-ffi-yajl.rb
index bba55a3..44f9844 100644
--- a/omnibus/config/software/chef-gem-ffi-yajl.rb
+++ b/omnibus/config/software/chef-gem-ffi-yajl.rb
@@ -7,5 +7,6 @@ BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
 
 license "MIT"
 license_file "https://github.com/chef/ffi-yajl/blob/master/LICENSE"
+skip_transitive_dependency_licensing true
 
 dependency "chef-gem-libyajl2"
diff --git a/omnibus/config/software/chef-gem-ffi.rb b/omnibus/config/software/chef-gem-ffi.rb
index 4d54105..ea8879c 100644
--- a/omnibus/config/software/chef-gem-ffi.rb
+++ b/omnibus/config/software/chef-gem-ffi.rb
@@ -5,7 +5,8 @@
 require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
 BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
 
-license "BSD-3-CLAUSE"
+license "BSD-3-Clause"
 license_file "https://github.com/ffi/ffi/blob/master/LICENSE"
 license_file "https://github.com/ffi/ffi/blob/master/COPYING"
 license_file "https://github.com/ffi/ffi/blob/master/LICENSE.SPECS"
+skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-json.rb b/omnibus/config/software/chef-gem-json.rb
index b2d848c..9217359 100644
--- a/omnibus/config/software/chef-gem-json.rb
+++ b/omnibus/config/software/chef-gem-json.rb
@@ -8,3 +8,4 @@ BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
 license "Ruby"
 license_file "https://github.com/flori/json/blob/master/README.md"
 license_file "https://www.ruby-lang.org/en/about/license.txt"
+skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-libyajl2.rb b/omnibus/config/software/chef-gem-libyajl2.rb
index 609e3c3..47ef42e 100644
--- a/omnibus/config/software/chef-gem-libyajl2.rb
+++ b/omnibus/config/software/chef-gem-libyajl2.rb
@@ -7,3 +7,4 @@ BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
 
 license "Apache-2.0"
 license_file "https://github.com/chef/libyajl2-gem/blob/master/LICENSE"
+skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-mini_portile2.rb b/omnibus/config/software/chef-gem-mini_portile2.rb
index 9ffa040..36a2b83 100644
--- a/omnibus/config/software/chef-gem-mini_portile2.rb
+++ b/omnibus/config/software/chef-gem-mini_portile2.rb
@@ -7,3 +7,4 @@ BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
 
 license "MIT"
 license_file "https://github.com/flavorjones/mini_portile/blob/master/LICENSE.txt"
+skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-nokogiri.rb b/omnibus/config/software/chef-gem-nokogiri.rb
index 8bf7100..c6b8d03 100644
--- a/omnibus/config/software/chef-gem-nokogiri.rb
+++ b/omnibus/config/software/chef-gem-nokogiri.rb
@@ -7,5 +7,7 @@ BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
 
 license "MIT"
 license_file "https://github.com/ruby-prof/ruby-prof/blob/master/LICENSE"
+skip_transitive_dependency_licensing true
 
+dependency "chef-gem-pkg-config"
 dependency "chef-gem-mini_portile2"
diff --git a/omnibus/config/software/chef-gem-binding_of_caller.rb b/omnibus/config/software/chef-gem-pkg-config.rb
similarity index 73%
copy from omnibus/config/software/chef-gem-binding_of_caller.rb
copy to omnibus/config/software/chef-gem-pkg-config.rb
index ed9083f..051091b 100644
--- a/omnibus/config/software/chef-gem-binding_of_caller.rb
+++ b/omnibus/config/software/chef-gem-pkg-config.rb
@@ -5,5 +5,6 @@
 require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
 BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
 
-license "MIT"
-license_file "https://github.com/banister/binding_of_caller/blob/master/LICENSE"
+license "LGPL-2.1"
+license_file "https://github.com/ruby-gnome2/pkg-config/blob/master/LGPL-2.1"
+skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-ruby-prof.rb b/omnibus/config/software/chef-gem-ruby-prof.rb
index 47ecfea..af90212 100644
--- a/omnibus/config/software/chef-gem-ruby-prof.rb
+++ b/omnibus/config/software/chef-gem-ruby-prof.rb
@@ -5,5 +5,6 @@
 require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def"
 BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
 
-license "BSD-2-CLAUSE"
+license "BSD-2-Clause"
 license_file "https://github.com/ruby-prof/ruby-prof/blob/master/LICENSE"
+skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-gem-ruby-shadow.rb b/omnibus/config/software/chef-gem-ruby-shadow.rb
index 8091fd9..02fc906 100644
--- a/omnibus/config/software/chef-gem-ruby-shadow.rb
+++ b/omnibus/config/software/chef-gem-ruby-shadow.rb
@@ -8,3 +8,4 @@ BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__)
 license "Public-Domain"
 license_file "https://github.com/apalmblad/ruby-shadow/blob/master/LICENSE"
 license_file "http://creativecommons.org/licenses/publicdomain/"
+skip_transitive_dependency_licensing true
diff --git a/omnibus/config/software/chef-remove-docs.rb b/omnibus/config/software/chef-remove-docs.rb
index 2e71e63..31e2797 100644
--- a/omnibus/config/software/chef-remove-docs.rb
+++ b/omnibus/config/software/chef-remove-docs.rb
@@ -17,6 +17,7 @@
 name "chef-remove-docs"
 
 license :project_license
+skip_transitive_dependency_licensing true
 
 build do
   # This is where we get the definitions below
diff --git a/omnibus/config/software/chef.rb b/omnibus/config/software/chef.rb
index 49db136..c6ced7e 100644
--- a/omnibus/config/software/chef.rb
+++ b/omnibus/config/software/chef.rb
@@ -69,13 +69,6 @@ build do
   block { log.info(log_key) { "" } }
   bundle "install --verbose", env: project_env
 
-  # For whatever reason, nokogiri software def deletes this (rather small) directory
-  block { log.info(log_key) { "" } }
-  block "Remove mini_portile test dir" do
-    mini_portile = shellout!("#{bundle_bin} show mini_portile").stdout.chomp
-    remove_directory File.join(mini_portile, "test")
-  end
-
   # Check that it worked
   block { log.info(log_key) { "" } }
   bundle "check", env: project_env
diff --git a/omnibus/files/mapfiles/solaris b/omnibus/files/mapfiles/solaris
deleted file mode 100644
index c0ca5c1..0000000
--- a/omnibus/files/mapfiles/solaris
+++ /dev/null
@@ -1,18 +0,0 @@
-$mapfile_version 2
-DEPEND_VERSIONS libnsl.so {
-  ALLOW = SUNW_1.1;
-  ALLOW = SUNWprivate_1.1;
-};
-DEPEND_VERSIONS libsocket.so {
-  ALLOW = SUNW_1.4;
-  ALLOW = SUNWprivate_1.1;
-};
-DEPEND_VERSIONS libdl.so {
-  ALLOW = SUNW_1.4;
-  ALLOW = SUNWprivate_1.1;
-};
-DEPEND_VERSIONS libc.so {
-  ALLOW = SUNW_1.22.1;
-  ALLOW = SUNW_1.4;
-  ALLOW = SUNWprivate_1.1;
-};
diff --git a/omnibus/omnibus.rb b/omnibus/omnibus.rb
index 4eeee4a..c6f883a 100644
--- a/omnibus/omnibus.rb
+++ b/omnibus/omnibus.rb
@@ -26,8 +26,10 @@
 
 # Windows architecture defaults - set to x86 unless otherwise specified.
 # ------------------------------
-windows_arch   %w{x86 x64}.include?((ENV["OMNIBUS_WINDOWS_ARCH"] || "").downcase) ?
-                ENV["OMNIBUS_WINDOWS_ARCH"].downcase.to_sym : :x86
+env_omnibus_windows_arch = (ENV["OMNIBUS_WINDOWS_ARCH"] || "").downcase
+env_omnibus_windows_arch = :x86 unless %w{x86 x64}.include?(env_omnibus_windows_arch)
+
+windows_arch   env_omnibus_windows_arch
 
 # Disable git caching
 # ------------------------------
@@ -48,3 +50,5 @@ fetcher_read_timeout 120
 # ------------------------------
 # software_gems ['omnibus-software', 'my-company-software']
 # local_software_dirs ['/path/to/local/software']
+
+fatal_transitive_dependency_licensing_warnings true
diff --git a/omnibus_overrides.rb b/omnibus_overrides.rb
index d971dd9..44134bf 100644
--- a/omnibus_overrides.rb
+++ b/omnibus_overrides.rb
@@ -1,18 +1,19 @@
 # DO NOT EDIT. Generated by "rake dependencies". Edit version_policy.rb instead.
-override :rubygems, version: "2.6.4"
-override :bundler, version: "1.11.2"
+override :rubygems, version: "2.6.6"
+override :bundler, version: "1.12.5"
 override "libffi", version: "3.2.1"
 override "libiconv", version: "1.14"
 override "liblzma", version: "5.2.2"
 override "libtool", version: "2.4.2"
-override "libxml2", version: "2.9.3"
-override "libxslt", version: "1.1.28"
+override "libxml2", version: "2.9.4"
+override "libxslt", version: "1.1.29"
 override "libyaml", version: "0.1.6"
 override "makedepend", version: "1.0.5"
 override "ncurses", version: "5.9"
 override "pkg-config-lite", version: "0.28-1"
-override "ruby", version: "2.1.8"
+override "ruby", version: "2.3.1"
 override "ruby-windows-devkit-bash", version: "3.1.23-4-msys-1.0.18"
 override "util-macros", version: "1.19.0"
 override "xproto", version: "7.0.28"
 override "zlib", version: "1.2.8"
+override "openssl", version: "1.0.2h"
diff --git a/pkg.rb b/pkg.rb
deleted file mode 100644
index bb26f25..0000000
--- a/pkg.rb
+++ /dev/null
@@ -1 +0,0 @@
-package "chef-12.9.38-1.powerpc"
diff --git a/rubygems-pkg/rubygems-update-2.4.6.gem b/rubygems-pkg/rubygems-update-2.4.6.gem
deleted file mode 100644
index 97ebec6..0000000
Binary files a/rubygems-pkg/rubygems-update-2.4.6.gem and /dev/null differ
diff --git a/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb b/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb
index ef0967a..e45e7d9 100644
--- a/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb
+++ b/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb
@@ -1,4 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "circular-dep1::default"
-
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "circular-dep1::default"
diff --git a/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb b/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb
index f2ef012..37f396b 100644
--- a/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb
+++ b/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb
@@ -1,3 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "circular-dep2::default"
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "circular-dep2::default"
diff --git a/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb b/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb
index e818d36..3059494 100644
--- a/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb
+++ b/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb
@@ -1,2 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "dependency1::aa_first"
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "dependency1::aa_first"
diff --git a/spec/data/run_context/cookbooks/dependency1/attributes/default.rb b/spec/data/run_context/cookbooks/dependency1/attributes/default.rb
index 6875274..a65a334 100644
--- a/spec/data/run_context/cookbooks/dependency1/attributes/default.rb
+++ b/spec/data/run_context/cookbooks/dependency1/attributes/default.rb
@@ -1,2 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "dependency1::default"
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "dependency1::default"
diff --git a/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb b/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb
index 1a513b0..94ffb30 100644
--- a/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb
+++ b/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb
@@ -1,3 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "dependency1::zz_last"
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "dependency1::zz_last"
diff --git a/spec/data/run_context/cookbooks/dependency2/attributes/default.rb b/spec/data/run_context/cookbooks/dependency2/attributes/default.rb
index 526751f..8917bf9 100644
--- a/spec/data/run_context/cookbooks/dependency2/attributes/default.rb
+++ b/spec/data/run_context/cookbooks/dependency2/attributes/default.rb
@@ -1,3 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "dependency2::default"
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "dependency2::default"
diff --git a/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb b/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb
index 3ad2b92..0729466 100644
--- a/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb
+++ b/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb
@@ -1,3 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "no-default-attr::server"
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "no-default-attr::server"
diff --git a/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb b/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb
index cca56bc..7730946 100644
--- a/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb
+++ b/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb
@@ -1,3 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "test-with-circular-deps::default"
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "test-with-circular-deps::default"
diff --git a/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb b/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb
index 4d71cc3..c4cc815 100644
--- a/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb
+++ b/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb
@@ -1,3 +1,2 @@
-set_unless[:attr_load_order] = []
-set[:attr_load_order] << "test-with-deps::default"
-
+normal_unless[:attr_load_order] = []
+normal[:attr_load_order] << "test-with-deps::default"
diff --git a/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg b/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg
index a2d3e7b..0295507 100644
Binary files a/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg and b/spec/functional/assets/chocolatey_feed/test-A.1.0.nupkg differ
diff --git a/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg b/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg
index 9ce9445..1978221 100644
Binary files a/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg and b/spec/functional/assets/chocolatey_feed/test-A.1.5.nupkg differ
diff --git a/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg b/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg
index 250084d..360c084 100644
Binary files a/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg and b/spec/functional/assets/chocolatey_feed/test-A.2.0.nupkg differ
diff --git a/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg b/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg
index f275c78..d82b0d0 100644
Binary files a/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg and b/spec/functional/assets/chocolatey_feed/test-B.1.0.nupkg differ
diff --git a/spec/functional/assets/testchefsubsys b/spec/functional/assets/testchefsubsys
index e9ff30d..e5c2f8c 100755
--- a/spec/functional/assets/testchefsubsys
+++ b/spec/functional/assets/testchefsubsys
@@ -5,7 +5,6 @@ sleep 120 &
 
 pid="$!"
 
-trap 'echo I am going down, so killing off my processes..; kill $pid; exit' SIGHUP SIGINT
- SIGQUIT SIGTERM
+trap 'echo I am going down, so killing off my processes..; kill $pid; exit' SIGHUP SIGINT SIGQUIT SIGTERM
 
-wait
\ No newline at end of file
+wait
diff --git a/spec/functional/event_loggers/windows_eventlog_spec.rb b/spec/functional/event_loggers/windows_eventlog_spec.rb
index 78aed74..019595e 100644
--- a/spec/functional/event_loggers/windows_eventlog_spec.rb
+++ b/spec/functional/event_loggers/windows_eventlog_spec.rb
@@ -48,25 +48,28 @@ describe Chef::EventLoggers::WindowsEventLogger, :windows_only, :not_supported_o
   it "writes run_start event with event_id 10000 and contains version" do
     logger.run_start(version)
 
-    expect(event_log.read(flags, offset).any? { |e| e.source == "Chef" && e.event_id == 10000 &&
-                                               e.string_inserts[0].include?(version)}).to be_truthy
+    expect(event_log.read(flags, offset).any? do |e|
+             e.source == "Chef" && e.event_id == 10000 &&
+                                               e.string_inserts[0].include?(version) end).to be_truthy
   end
 
   it "writes run_started event with event_id 10001 and contains the run_id" do
     logger.run_started(run_status)
 
-    expect(event_log.read(flags, offset).any? { |e| e.source == "Chef" && e.event_id == 10001 &&
-                                               e.string_inserts[0].include?(run_id)}).to be_truthy
+    expect(event_log.read(flags, offset).any? do |e|
+             e.source == "Chef" && e.event_id == 10001 &&
+                                               e.string_inserts[0].include?(run_id) end).to be_truthy
   end
 
   it "writes run_completed event with event_id 10002 and contains the run_id and elapsed time" do
     logger.run_started(run_status)
     logger.run_completed(node)
 
-    expect(event_log.read(flags, offset).any? { |e| e.source == "Chef" && e.event_id == 10002 &&
-                                                e.string_inserts[0].include?(run_id) &&
-                                                e.string_inserts[1].include?(elapsed_time.to_s)
-    }).to be_truthy
+    expect(event_log.read(flags, offset).any? do |e|
+      e.source == "Chef" && e.event_id == 10002 &&
+                                         e.string_inserts[0].include?(run_id) &&
+                                         e.string_inserts[1].include?(elapsed_time.to_s)
+    end).to be_truthy
   end
 
   it "writes run_failed event with event_id 10003 and contains the run_id, elapsed time, and exception info" do
diff --git a/spec/functional/file_content_management/deploy_strategies_spec.rb b/spec/functional/file_content_management/deploy_strategies_spec.rb
index 7c54af3..9e21313 100644
--- a/spec/functional/file_content_management/deploy_strategies_spec.rb
+++ b/spec/functional/file_content_management/deploy_strategies_spec.rb
@@ -209,7 +209,7 @@ describe Chef::FileContentManagement::Deploy::MvUnix, :unix_only do
 end
 
 # On Unix we won't have loaded the file, avoid undefined constant errors:
-class Chef::FileContentManagement::Deploy::MvWindows ; end
+class Chef::FileContentManagement::Deploy::MvWindows; end
 
 describe Chef::FileContentManagement::Deploy::MvWindows, :windows_only do
 
diff --git a/spec/functional/http/simple_spec.rb b/spec/functional/http/simple_spec.rb
index aeb7be7..4210456 100644
--- a/spec/functional/http/simple_spec.rb
+++ b/spec/functional/http/simple_spec.rb
@@ -26,11 +26,11 @@ describe Chef::HTTP::Simple do
   let(:http_client) { described_class.new(source) }
   let(:http_client_disable_gzip) { described_class.new(source, { :disable_gzip => true } ) }
 
-  before(:all) do
+  before(:each) do
     start_tiny_server
   end
 
-  after(:all) do
+  after(:each) do
     stop_tiny_server
   end
 
diff --git a/spec/functional/knife/cookbook_delete_spec.rb b/spec/functional/knife/cookbook_delete_spec.rb
index a43e3a3..99f3309 100644
--- a/spec/functional/knife/cookbook_delete_spec.rb
+++ b/spec/functional/knife/cookbook_delete_spec.rb
@@ -20,11 +20,15 @@ require "spec_helper"
 require "tiny_server"
 
 describe Chef::Knife::CookbookDelete do
-  before(:all) do
+  before(:each) do
     @server = TinyServer::Manager.new
     @server.start
   end
 
+  after(:each) do
+    @server.stop
+  end
+
   before(:each) do
     @knife = Chef::Knife::CookbookDelete.new
     @api = TinyServer::API.instance
@@ -35,10 +39,6 @@ describe Chef::Knife::CookbookDelete do
     Chef::Config[:chef_server_url] = "http://localhost:9000"
   end
 
-  after(:all) do
-    @server.stop
-  end
-
   context "when the cookbook doesn't exist" do
     let(:log_output) { StringIO.new }
 
diff --git a/spec/functional/knife/exec_spec.rb b/spec/functional/knife/exec_spec.rb
index 838d156..ac8f617 100644
--- a/spec/functional/knife/exec_spec.rb
+++ b/spec/functional/knife/exec_spec.rb
@@ -20,11 +20,15 @@ require "spec_helper"
 require "tiny_server"
 
 describe Chef::Knife::Exec do
-  before(:all) do
-    @server = TinyServer::Manager.new#(:debug => true)
+  before(:each) do
+    @server = TinyServer::Manager.new #(:debug => true)
     @server.start
   end
 
+  after(:each) do
+    @server.stop
+  end
+
   before(:each) do
     @knife = Chef::Knife::Exec.new
     @api = TinyServer::API.instance
@@ -37,10 +41,6 @@ describe Chef::Knife::Exec do
     $output = StringIO.new
   end
 
-  after(:all) do
-    @server.stop
-  end
-
   it "executes a script in the context of the chef-shell main context" do
     @node = Chef::Node.new
     @node.name("ohai-world")
diff --git a/spec/functional/knife/ssh_spec.rb b/spec/functional/knife/ssh_spec.rb
index a23220e..065b646 100644
--- a/spec/functional/knife/ssh_spec.rb
+++ b/spec/functional/knife/ssh_spec.rb
@@ -21,13 +21,13 @@ require "tiny_server"
 
 describe Chef::Knife::Ssh do
 
-  before(:all) do
+  before(:each) do
     Chef::Knife::Ssh.load_deps
     @server = TinyServer::Manager.new
     @server.start
   end
 
-  after(:all) do
+  after(:each) do
     @server.stop
   end
 
@@ -276,9 +276,9 @@ describe Chef::Knife::Ssh do
     Chef::Config[:client_key] = nil
     Chef::Config[:chef_server_url] = "http://localhost:9000"
 
-    @api.get("/search/node?q=*:*&sort=X_CHEF_id_CHEF_X%20asc&start=0", 200) {
+    @api.get("/search/node?q=*:*&sort=X_CHEF_id_CHEF_X%20asc&start=0", 200) do
       %({"total":1, "start":0, "rows":[{"name":"i-xxxxxxxx", "json_class":"Chef::Node", "automatic":{"fqdn":"the.fqdn", "ec2":{"public_hostname":"the_public_hostname"}},"recipes":[]}]})
-    }
+    end
   end
 
 end
diff --git a/spec/functional/mixin/powershell_out_spec.rb b/spec/functional/mixin/powershell_out_spec.rb
index 293e955..66214cb 100644
--- a/spec/functional/mixin/powershell_out_spec.rb
+++ b/spec/functional/mixin/powershell_out_spec.rb
@@ -23,7 +23,7 @@ describe Chef::Mixin::PowershellOut, windows_only: true do
 
   describe "#powershell_out" do
     it "runs a powershell command and collects stdout" do
-      expect(powershell_out("get-process").run_command.stdout).to match /Handles\s+NPM\(K\)\s+PM\(K\)\s+WS\(K\)\s+VM\(M\)\s+CPU\(s\)\s+Id\s+ProcessName/
+      expect(powershell_out("get-process").run_command.stdout).to match /Handles\s+NPM\(K\)\s+PM\(K\)\s+WS\(K\)\s+VM\(M\)\s+CPU\(s\)\s+Id\s+/
     end
 
     it "does not raise exceptions when the command is invalid" do
@@ -33,7 +33,7 @@ describe Chef::Mixin::PowershellOut, windows_only: true do
 
   describe "#powershell_out!" do
     it "runs a powershell command and collects stdout" do
-      expect(powershell_out!("get-process").run_command.stdout).to match /Handles\s+NPM\(K\)\s+PM\(K\)\s+WS\(K\)\s+VM\(M\)\s+CPU\(s\)\s+Id\s+ProcessName/
+      expect(powershell_out!("get-process").run_command.stdout).to match /Handles\s+NPM\(K\)\s+PM\(K\)\s+WS\(K\)\s+VM\(M\)\s+CPU\(s\)\s+Id\s+/
     end
 
     it "raises exceptions when the command is invalid" do
diff --git a/spec/functional/notifications_spec.rb b/spec/functional/notifications_spec.rb
index 1b1ef83..8d8b2d9 100644
--- a/spec/functional/notifications_spec.rb
+++ b/spec/functional/notifications_spec.rb
@@ -13,11 +13,11 @@ describe "Notifications" do
 
   # We always pretend we are on OSx because that has a specific provider (HomebrewProvider) so it
   # tests the translation from Provider => HomebrewProvider
-  let(:node) {
+  let(:node) do
     n = Chef::Node.new
     n.override[:os] = "darwin"
     n
-  }
+  end
   let(:cookbook_collection) { double("Chef::CookbookCollection").as_null_object }
   let(:events) { double("Chef::EventDispatch::Dispatcher").as_null_object }
   let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) }
diff --git a/spec/functional/provider/whyrun_safe_ruby_block_spec.rb b/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
index 892e108..1bb36f2 100644
--- a/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
+++ b/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
@@ -21,10 +21,10 @@ require "spec_helper"
 describe Chef::Resource::WhyrunSafeRubyBlock do
   let(:node) { Chef::Node.new }
 
-  let(:run_context) {
+  let(:run_context) do
     events = Chef::EventDispatch::Dispatcher.new
     Chef::RunContext.new(node, {}, events)
-  }
+  end
 
   before do
     $evil_global_evil_laugh = :wahwah
diff --git a/spec/functional/rebooter_spec.rb b/spec/functional/rebooter_spec.rb
index 4b77c40..0006f3b 100644
--- a/spec/functional/rebooter_spec.rb
+++ b/spec/functional/rebooter_spec.rb
@@ -50,15 +50,15 @@ describe Chef::Platform::Rebooter do
 
   let(:rebooter) { Chef::Platform::Rebooter }
 
-  describe '#reboot_if_needed!' do
+  describe "#reboot_if_needed!" do
 
-    it 'should not call #shell_out! when reboot has not been requested' do
+    it "should not call #shell_out! when reboot has not been requested" do
       expect(rebooter).to receive(:shell_out!).exactly(0).times
       expect(rebooter).to receive(:reboot_if_needed!).once.and_call_original
       rebooter.reboot_if_needed!(run_context.node)
     end
 
-    describe 'calling #shell_out! to reboot' do
+    describe "calling #shell_out! to reboot" do
 
       before(:each) do
         run_context.request_reboot(reboot_info)
@@ -77,7 +77,7 @@ describe Chef::Platform::Rebooter do
         end
       end
 
-      describe 'when using #reboot_if_needed!' do
+      describe "when using #reboot_if_needed!" do
         include_context "test a reboot method"
 
         it "should produce the correct string on Windows" do
@@ -89,7 +89,7 @@ describe Chef::Platform::Rebooter do
         end
       end
 
-      describe 'when using #reboot!' do
+      describe "when using #reboot!" do
         include_context "test a reboot method"
 
         it "should produce the correct string on Windows" do
diff --git a/spec/functional/resource/bash_spec.rb b/spec/functional/resource/bash_spec.rb
index 87211ec..a2e174d 100644
--- a/spec/functional/resource/bash_spec.rb
+++ b/spec/functional/resource/bash_spec.rb
@@ -21,11 +21,11 @@ require "functional/resource/base"
 
 describe Chef::Resource::Bash, :unix_only do
   let(:code) { "echo hello" }
-  let(:resource) {
+  let(:resource) do
     resource = Chef::Resource::Bash.new("foo_resource", run_context)
     resource.code(code)
     resource
-  }
+  end
 
   describe "when setting the command attribute" do
     let (:command) { "wizard racket" }
diff --git a/spec/functional/resource/cron_spec.rb b/spec/functional/resource/cron_spec.rb
index 3380ecc..f594819 100644
--- a/spec/functional/resource/cron_spec.rb
+++ b/spec/functional/resource/cron_spec.rb
@@ -120,7 +120,7 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
       return if %w{aix solaris}.include?(ohai[:platform])
       # Test if the attribute exists on newly created cron
       cron_should_exists(cron_name, "")
-      expect(shell_out("crontab -l -u #{new_resource.user} | grep \"#{attribute.upcase}=#{value}\"").exitstatus).to eq(0)
+      expect(shell_out("crontab -l -u #{new_resource.user} | grep '#{attribute.upcase}=\"#{value}\"'").exitstatus).to eq(0)
     end
 
     after do
@@ -146,6 +146,13 @@ describe Chef::Resource::Cron, :requires_root, :unix_only do
       new_resource.home "/home/opscode"
       create_and_validate_with_attribute(new_resource, "home", "/home/opscode")
     end
+
+    %i{ home mailto path shell }.each do |attr|
+      it "supports an empty string for #{attr} attribute" do
+        new_resource.send(attr, "")
+        create_and_validate_with_attribute(new_resource, attr.to_s, "")
+      end
+    end
   end
 
   describe "negative tests for create action" do
diff --git a/spec/functional/resource/deploy_revision_spec.rb b/spec/functional/resource/deploy_revision_spec.rb
index 72eaea3..572609d 100644
--- a/spec/functional/resource/deploy_revision_spec.rb
+++ b/spec/functional/resource/deploy_revision_spec.rb
@@ -840,7 +840,7 @@ describe Chef::Resource::DeployRevision, :unix_only => true, :requires_git => tr
         end
 
         def run_action(action)
-          raise RuntimeError, "network error"
+          raise "network error"
         end
       end
 
diff --git a/spec/functional/resource/dpkg_package_spec.rb b/spec/functional/resource/dpkg_package_spec.rb
index d652562..1988fd0 100644
--- a/spec/functional/resource/dpkg_package_spec.rb
+++ b/spec/functional/resource/dpkg_package_spec.rb
@@ -27,11 +27,11 @@ describe Chef::Resource::DpkgPackage, :requires_root, :debian_family_only, arch:
   let(:test1_1) { File.join(apt_data, "chef-integration-test_1.1-1_amd64.deb") }
   let(:test2_0) { File.join(apt_data, "chef-integration-test2_1.0-1_amd64.deb") }
 
-  let(:run_context) {
+  let(:run_context) do
     node = TEST_NODE.dup
     events = Chef::EventDispatch::Dispatcher.new
     Chef::RunContext.new(node, {}, events)
-  }
+  end
 
   let(:dpkg_package) { Chef::Resource::DpkgPackage.new(test1_0, run_context) }
 
diff --git a/spec/functional/resource/dsc_resource_spec.rb b/spec/functional/resource/dsc_resource_spec.rb
index 8e4940d..bb3cf21 100644
--- a/spec/functional/resource/dsc_resource_spec.rb
+++ b/spec/functional/resource/dsc_resource_spec.rb
@@ -21,17 +21,17 @@ require "spec_helper"
 describe Chef::Resource::DscResource, :windows_powershell_dsc_only do
   let(:event_dispatch) { Chef::EventDispatch::Dispatcher.new }
 
-  let(:node) {
+  let(:node) do
     Chef::Node.new.tap do |n|
       n.consume_external_attrs(OHAI_SYSTEM.data, {})
     end
-  }
+  end
 
   let(:run_context) { Chef::RunContext.new(node, {}, event_dispatch) }
 
-  let(:new_resource) {
+  let(:new_resource) do
     Chef::Resource::DscResource.new("dsc_resource_test", run_context)
-  }
+  end
 
   context "when Powershell does not support Invoke-DscResource"
   context "when Powershell supports Invoke-DscResource" do
@@ -77,7 +77,7 @@ describe Chef::Resource::DscResource, :windows_powershell_dsc_only do
         new_resource.run_action(:run)
         expect(new_resource).to be_updated
         reresource =
-           Chef::Resource::DscResource.new("dsc_resource_retest", run_context)
+          Chef::Resource::DscResource.new("dsc_resource_retest", run_context)
         reresource.resource :File
         reresource.property :Contents, test_text
         reresource.property :DestinationPath, tmp_file_name
diff --git a/spec/functional/resource/dsc_script_spec.rb b/spec/functional/resource/dsc_script_spec.rb
index 42c23a6..e2b58f6 100644
--- a/spec/functional/resource/dsc_script_spec.rb
+++ b/spec/functional/resource/dsc_script_spec.rb
@@ -74,7 +74,7 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
   let(:dsc_env_variable) { "chefenvtest" }
   let(:dsc_env_value1) { "value1" }
   let(:env_value2) { "value2" }
-  let(:dsc_test_run_context) {
+  let(:dsc_test_run_context) do
     node = Chef::Node.new
     node.automatic["platform"] = "windows"
     node.automatic["platform_version"] = "6.1"
@@ -82,11 +82,11 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
     node.automatic[:languages][:powershell][:version] = "4.0"
     empty_events = Chef::EventDispatch::Dispatcher.new
     Chef::RunContext.new(node, {}, empty_events)
-  }
+  end
   let(:dsc_test_resource_name) { "DSCTest" }
-  let(:dsc_test_resource_base) {
+  let(:dsc_test_resource_base) do
     Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context)
-  }
+  end
   let(:test_registry_key) { 'HKEY_LOCAL_MACHINE\Software\Chef\Spec\Functional\Resource\dsc_script_spec' }
   let(:test_registry_value) { "Registration" }
   let(:test_registry_data1) { "LL927" }
@@ -94,7 +94,8 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
   let(:reg_key_name_param_name) { "testregkeyname" }
   let(:reg_key_value_param_name) { "testregvaluename" }
   let(:registry_embedded_parameters) { "$#{reg_key_name_param_name} = '#{test_registry_key}';$#{reg_key_value_param_name} = '#{test_registry_value}'" }
-  let(:dsc_reg_code) { <<-EOH
+  let(:dsc_reg_code) do
+    <<-EOH
   #{registry_embedded_parameters}
   Registry "ChefRegKey"
   {
@@ -104,14 +105,15 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
      Ensure = 'Present'
   }
 EOH
-  }
+  end
 
   let(:dsc_code) { dsc_reg_code }
-  let(:dsc_reg_script) { <<-EOH
+  let(:dsc_reg_script) do
+    <<-EOH
   param($testregkeyname, $testregvaluename)
   #{dsc_reg_code}
 EOH
-  }
+  end
 
   let(:dsc_user_prefix) { "dsc" }
   let(:dsc_user_suffix) { "chefx" }
@@ -128,7 +130,8 @@ EOH
   let(:dsc_user_param_code) { "\"$(#{dsc_user_prefix_param_code})_usr_$(#{dsc_user_suffix_param_code})\"" }
 
   let(:config_flags) { nil }
-  let(:config_params) { <<-EOH
+  let(:config_params) do
+    <<-EOH
 
     [CmdletBinding()]
     param
@@ -137,14 +140,15 @@ EOH
     $#{dsc_user_suffix_param_name}
     )
 EOH
-  }
+  end
 
   let(:config_param_section) { "" }
   let(:dsc_user_code) { "'#{dsc_user}'" }
   let(:dsc_user_prefix_code) { dsc_user_prefix }
   let(:dsc_user_suffix_code) { dsc_user_suffix }
   let(:dsc_script_environment_attribute) { nil }
-  let(:dsc_user_resources_code) { <<-EOH
+  let(:dsc_user_resources_code) do
+    <<-EOH
   #{config_param_section}
 node localhost
 {
@@ -164,9 +168,9 @@ User dsctestusercreate
 }
 }
 EOH
-  }
+  end
 
-  let(:dsc_user_config_data) {
+  let(:dsc_user_config_data) do
     <<-EOH
 @{
     AllNodes = @(
@@ -178,13 +182,14 @@ EOH
 }
 
 EOH
-  }
+  end
 
   let(:dsc_environment_env_var_name) { "dsc_test_cwd" }
   let(:dsc_environment_no_fail_not_etc_directory) { "#{ENV['systemroot']}\\system32" }
   let(:dsc_environment_fail_etc_directory) { "#{ENV['systemroot']}\\system32\\drivers\\etc" }
   let(:exception_message_signature) { "LL927-LL928" }
-  let(:dsc_environment_config) {<<-EOH
+  let(:dsc_environment_config) do
+    <<-EOH
 if (($pwd.path -eq '#{dsc_environment_fail_etc_directory}') -and (test-path('#{dsc_environment_fail_etc_directory}')))
 {
     throw 'Signature #{exception_message_signature}: Purposefully failing because cwd == #{dsc_environment_fail_etc_directory}'
@@ -196,21 +201,21 @@ environment "whatsmydir"
     Ensure = 'Present'
 }
 EOH
-  }
+  end
 
-  let(:dsc_config_name) {
+  let(:dsc_config_name) do
     dsc_test_resource_base.name
-  }
-  let(:dsc_resource_from_code) {
+  end
+  let(:dsc_resource_from_code) do
     dsc_test_resource_base.code(dsc_code)
     dsc_test_resource_base
-  }
+  end
   let(:config_name_value) { dsc_test_resource_base.name }
 
-  let(:dsc_resource_from_path) {
+  let(:dsc_resource_from_path) do
     dsc_test_resource_base.command(create_config_script_from_code(dsc_code, config_name_value))
     dsc_test_resource_base
-  }
+  end
 
   before(:each) do
     test_key_resource = Chef::Resource::RegistryKey.new(test_registry_key, dsc_test_run_context)
@@ -469,6 +474,7 @@ EOF
     end
 
     it "allows the use of ps_credential" do
+      skip("Skipped until we can adjust the test cert to meet the WMF 5 cert requirements.")
       expect(user_exists?(dsc_user)).to eq(false)
       powershell_script_resource.run_action(:run)
       expect(File).to exist(configuration_data_path)
diff --git a/spec/functional/resource/env_spec.rb b/spec/functional/resource/env_spec.rb
index 83328a6..4b0ff70 100755
--- a/spec/functional/resource/env_spec.rb
+++ b/spec/functional/resource/env_spec.rb
@@ -27,17 +27,17 @@ describe Chef::Resource::Env, :windows_only do
     let(:env_value2) { "value2" }
 
     let(:env_value_expandable) { "%SystemRoot%" }
-    let(:test_run_context) {
+    let(:test_run_context) do
       node = Chef::Node.new
       node.default["os"] = "windows"
       node.default["platform"] = "windows"
       node.default["platform_version"] = "6.1"
       empty_events = Chef::EventDispatch::Dispatcher.new
       Chef::RunContext.new(node, {}, empty_events)
-    }
-    let(:test_resource) {
+    end
+    let(:test_resource) do
       Chef::Resource::Env.new("unknown", test_run_context)
-    }
+    end
 
     before(:each) do
       resource_lower = Chef::Resource::Env.new(chef_env_test_lower_case, test_run_context)
diff --git a/spec/functional/resource/execute_spec.rb b/spec/functional/resource/execute_spec.rb
index c5978da..3c31537 100644
--- a/spec/functional/resource/execute_spec.rb
+++ b/spec/functional/resource/execute_spec.rb
@@ -21,11 +21,11 @@ require "functional/resource/base"
 require "timeout"
 
 describe Chef::Resource::Execute do
-  let(:resource) {
+  let(:resource) do
     resource = Chef::Resource::Execute.new("foo_resource", run_context)
     resource.command("echo hello")
     resource
-  }
+  end
 
   describe "when guard is ruby block" do
     it "guard can still run" do
@@ -41,10 +41,10 @@ describe Chef::Resource::Execute do
     end
 
     let(:guard) { "ruby -e 'exit 0'" }
-    let!(:guard_resource) {
+    let!(:guard_resource) do
       interpreter = Chef::GuardInterpreter::ResourceGuardInterpreter.new(resource, guard, nil)
       interpreter.send(:get_interpreter_resource, resource)
-    }
+    end
 
     it "executes the guard and not the regular resource" do
       expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:get_interpreter_resource).and_return(guard_resource)
diff --git a/spec/functional/resource/group_spec.rb b/spec/functional/resource/group_spec.rb
index a5de63b..aa5a29f 100644
--- a/spec/functional/resource/group_spec.rb
+++ b/spec/functional/resource/group_spec.rb
@@ -85,8 +85,14 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
     end
   end
 
+  def node
+    node = Chef::Node.new
+    node.consume_external_attrs(ohai.data, {})
+    node
+  end
+
   def user(username)
-    usr = Chef::Resource::User.new("#{username}", run_context)
+    usr = Chef::Resource.resource_for_node(:user, node).new(username, run_context)
     if ohai[:platform_family] == "windows"
       usr.password("ComplexPass11!")
     end
@@ -99,7 +105,11 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
   end
 
   def remove_user(username)
-    user(username).run_action(:remove) if ! windows_domain_user?(username)
+    if ! windows_domain_user?(username)
+      u = user(username)
+      u.manage_home false # jekins hosts throw mail spool file not owned by user if we use manage_home true
+      u.run_action(:remove)
+    end
     # TODO: User shouldn't exist
   end
 
@@ -282,12 +292,12 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
   let(:group_name) { "group#{SecureRandom.random_number(9999)}" }
   let(:included_members) { nil }
   let(:excluded_members) { nil }
-  let(:group_resource) {
+  let(:group_resource) do
     group = Chef::Resource::Group.new(group_name, run_context)
     group.members(included_members)
     group.excluded_members(excluded_members)
     group
-  }
+  end
 
   it "append should be false by default" do
     expect(group_resource.append).to eq(false)
@@ -305,10 +315,11 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
     end
 
     describe "when group name is length 256", :windows_only do
-      let!(:group_name) { "theoldmanwalkingdownthestreetalwayshadagood\
+      let!(:group_name) do
+        "theoldmanwalkingdownthestreetalwayshadagood\
 smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface\
 theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\
-downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree" }
+downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree" end
 
       it "should create a group" do
         group_resource.run_action(:create)
@@ -317,10 +328,11 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree" }
     end
 
     describe "when group name length is more than 256", :windows_only do
-      let!(:group_name) { "theoldmanwalkingdownthestreetalwayshadagood\
+      let!(:group_name) do
+        "theoldmanwalkingdownthestreetalwayshadagood\
 smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface\
 theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\
-downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" }
+downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" end
 
       it "should not create a group" do
         expect { group_resource.run_action(:create) }.to raise_error(ArgumentError)
diff --git a/spec/functional/resource/link_spec.rb b/spec/functional/resource/link_spec.rb
index 6dd6ad8..de69764 100644
--- a/spec/functional/resource/link_spec.rb
+++ b/spec/functional/resource/link_spec.rb
@@ -388,6 +388,14 @@ describe Chef::Resource::Link do
             symlink(other_dir, target_file)
           end
           include_context "create symbolic link succeeds"
+          include_context "delete succeeds"
+        end
+        context "and the link already exists and points at the target" do
+          before do
+            symlink(to, target_file)
+          end
+          include_context "create symbolic link is noop"
+          include_context "delete succeeds"
         end
       end
       context "when the link destination is a symbolic link" do
@@ -406,6 +414,19 @@ describe Chef::Resource::Link do
             include_context "create symbolic link succeeds"
             include_context "delete is noop"
           end
+          context "and the destination itself has another symbolic link" do
+            context "to a link that exist" do
+              before do
+                symlink(to, target_file)
+              end
+              include_context "create symbolic link is noop"
+              include_context "delete succeeds"
+            end
+            context "to a link that does not exist" do
+              include_context "create symbolic link succeeds"
+              include_context "delete is noop"
+            end
+          end
         end
         context "to a file that does not exist" do
           before(:each) do
@@ -418,6 +439,19 @@ describe Chef::Resource::Link do
             include_context "create symbolic link succeeds"
             include_context "delete is noop"
           end
+          context "and the destination itself has another symbolic link" do
+            context "to a link that exist" do
+              before do
+                symlink(to, target_file)
+              end
+              include_context "create symbolic link is noop"
+              include_context "delete succeeds"
+            end
+            context "to a link that does not exist" do
+              include_context "create symbolic link succeeds"
+              include_context "delete is noop"
+            end
+          end
         end
       end
       context "when the link destination does not exist" do
@@ -580,12 +614,7 @@ describe Chef::Resource::Link do
             it "links to the target file" do
               skip("OS X/FreeBSD/AIX fails to create hardlinks to broken symlinks") if os_x? || freebsd? || aix?
               resource.run_action(:create)
-              # Windows and Unix have different definitions of exists? here, and that's OK.
-              if windows?
-                expect(File.exists?(target_file)).to be_truthy
-              else
-                expect(File.exists?(target_file)).to be_falsey
-              end
+              expect(File.exists?(target_file) || File.symlink?(target_file)).to be_truthy
               expect(symlink?(target_file)).to be_truthy
               expect(readlink(target_file)).to eq(canonicalize(@other_target))
             end
diff --git a/spec/functional/resource/ohai_spec.rb b/spec/functional/resource/ohai_spec.rb
index 9ce989d..06bccfc 100644
--- a/spec/functional/resource/ohai_spec.rb
+++ b/spec/functional/resource/ohai_spec.rb
@@ -19,18 +19,18 @@
 require "spec_helper"
 
 describe Chef::Resource::Ohai do
-  let(:ohai) {
+  let(:ohai) do
     OHAI_SYSTEM
-  }
+  end
 
   let(:node) { Chef::Node.new }
 
-  let(:run_context) {
+  let(:run_context) do
     node.default[:platform] = ohai[:platform]
     node.default[:platform_version] = ohai[:platform_version]
     events = Chef::EventDispatch::Dispatcher.new
     Chef::RunContext.new(node, {}, events)
-  }
+  end
 
   shared_examples_for "reloaded :uptime" do
     it "should reload :uptime" do
@@ -51,11 +51,11 @@ describe Chef::Resource::Ohai do
   end
 
   describe "when reloading only uptime" do
-    let(:ohai_resource) {
+    let(:ohai_resource) do
       r = Chef::Resource::Ohai.new("reload all", run_context)
       r.plugin("uptime")
       r
-    }
+    end
 
     it_behaves_like "reloaded :uptime"
   end
diff --git a/spec/functional/resource/package_spec.rb b/spec/functional/resource/package_spec.rb
index 6dc55f7..0f01a75 100644
--- a/spec/functional/resource/package_spec.rb
+++ b/spec/functional/resource/package_spec.rb
@@ -260,7 +260,7 @@ describe Chef::Resource::Package, metadata do
           end
 
           before do
-            node.set[:preseed_value] = "FROM TEMPLATE"
+            node.normal[:preseed_value] = "FROM TEMPLATE"
           end
 
           it "preseeds the package, then installs it" do
diff --git a/spec/functional/resource/reboot_spec.rb b/spec/functional/resource/reboot_spec.rb
index 3cf7f58..c264b12 100644
--- a/spec/functional/resource/reboot_spec.rb
+++ b/spec/functional/resource/reboot_spec.rb
@@ -80,9 +80,9 @@ describe Chef::Resource::Reboot do
 
     it "should have modified the run context correctly" do
       # this doesn't actually test the flow of Chef::Client#do_run, unfortunately.
-      expect {
+      expect do
         resource.run_action(:reboot_now)
-      }.to throw_symbol(:end_client_run_early)
+      end.to throw_symbol(:end_client_run_early)
 
       test_reboot_action(resource)
     end
diff --git a/spec/functional/resource/registry_spec.rb b/spec/functional/resource/registry_spec.rb
index e64b669..8393e0a 100644
--- a/spec/functional/resource/registry_spec.rb
+++ b/spec/functional/resource/registry_spec.rb
@@ -153,6 +153,16 @@ describe Chef::Resource::RegistryKey, :windows_only, :broken => true do
       expect(@registry.data_exists?(reg_child, { :name => "Color", :type => :string, :data => "Orange" })).to eq(true)
     end
 
+    it "does not create the key if it already exists with same value and type but datatype of data differs" do
+      @new_resource.key(reg_child)
+      @new_resource.values([{ :name => "number", :type => :dword, :data => "12345" }])
+      @new_resource.run_action(:create)
+
+      expect(@new_resource).not_to be_updated_by_last_action
+      expect(@registry.key_exists?(reg_child)).to eq(true)
+      expect(@registry.data_exists?(reg_child, { :name => "number", :type => :dword, :data => 12344 })).to eq(true)
+    end
+
     it "creates a value if it does not exist" do
       @new_resource.key(reg_child)
       @new_resource.values([{ :name => "Mango", :type => :string, :data => "Yellow" }])
diff --git a/spec/functional/resource/remote_file_spec.rb b/spec/functional/resource/remote_file_spec.rb
index b394bd0..1f92a56 100644
--- a/spec/functional/resource/remote_file_spec.rb
+++ b/spec/functional/resource/remote_file_spec.rb
@@ -55,11 +55,11 @@ describe Chef::Resource::RemoteFile do
   let(:default_mode) { (0666 & ~File.umask).to_s(8) }
 
   context "when fetching files over HTTP" do
-    before(:all) do
+    before(:each) do
       start_tiny_server
     end
 
-    after(:all) do
+    after(:each) do
       stop_tiny_server
     end
 
@@ -97,7 +97,7 @@ describe Chef::Resource::RemoteFile do
 
   context "when fetching files over HTTPS" do
 
-    before(:all) do
+    before(:each) do
       cert_text = File.read(File.expand_path("ssl/chef-rspec.cert", CHEF_SPEC_DATA))
       cert = OpenSSL::X509::Certificate.new(cert_text)
       key_text = File.read(File.expand_path("ssl/chef-rspec.key", CHEF_SPEC_DATA))
@@ -111,7 +111,7 @@ describe Chef::Resource::RemoteFile do
       start_tiny_server(server_opts)
     end
 
-    after(:all) do
+    after(:each) do
       stop_tiny_server
     end
 
@@ -124,11 +124,11 @@ describe Chef::Resource::RemoteFile do
   end
 
   context "when dealing with content length checking" do
-    before(:all) do
+    before(:each) do
       start_tiny_server
     end
 
-    after(:all) do
+    after(:each) do
       stop_tiny_server
     end
 
@@ -232,7 +232,16 @@ describe Chef::Resource::RemoteFile do
       end
 
       it "should not create the file" do
-        expect { resource.run_action(:create) }.to raise_error
+        # This can legitimately raise either Errno::EADDRNOTAVAIL or Errno::ECONNREFUSED
+        # in different Ruby versions.
+        old_value = RSpec::Expectations.configuration.on_potential_false_positives
+        RSpec::Expectations.configuration.on_potential_false_positives = :nothing
+        begin
+          expect { resource.run_action(:create) }.to raise_error
+        ensure
+          RSpec::Expectations.configuration.on_potential_false_positives = old_value
+        end
+
         expect(File).not_to exist(path)
       end
     end
diff --git a/spec/functional/resource/template_spec.rb b/spec/functional/resource/template_spec.rb
index f270043..32529fb 100644
--- a/spec/functional/resource/template_spec.rb
+++ b/spec/functional/resource/template_spec.rb
@@ -110,7 +110,7 @@ describe Chef::Resource::Template do
 
     context "using single helper syntax referencing @node" do
       before do
-        node.set[:helper_test_attr] = "value from helper method"
+        node.normal[:helper_test_attr] = "value from helper method"
         resource.helper(:helper_method) { "#{@node[:helper_test_attr]}" }
       end
 
@@ -131,7 +131,7 @@ describe Chef::Resource::Template do
 
     context "using an inline block referencing @node" do
       before do
-        node.set[:helper_test_attr] = "value from helper method"
+        node.normal[:helper_test_attr] = "value from helper method"
 
         resource.helpers do
           def helper_method
@@ -168,7 +168,7 @@ describe Chef::Resource::Template do
       end
 
       before do
-        node.set[:helper_test_attr] = "value from helper method"
+        node.normal[:helper_test_attr] = "value from helper method"
 
         resource.helpers(ExampleModuleReferencingATNode)
       end
diff --git a/spec/functional/resource/user/dscl_spec.rb b/spec/functional/resource/user/dscl_spec.rb
index 5d904a9..bedb378 100644
--- a/spec/functional/resource/user/dscl_spec.rb
+++ b/spec/functional/resource/user/dscl_spec.rb
@@ -76,7 +76,7 @@ describe "Chef::Resource::User with Chef::Provider::User::Dscl provider", metada
   let(:iterations) { nil }
 
   let(:user_resource) do
-    r = Chef::Resource::User.new("TEST USER RESOURCE", run_context)
+    r = Chef::Resource::User::DsclUser.new("TEST USER RESOURCE", run_context)
     r.username(username)
     r.uid(uid)
     r.gid(gid)
@@ -123,7 +123,7 @@ describe "Chef::Resource::User with Chef::Provider::User::Dscl provider", metada
   end
 
   describe "when password is being set via shadow hash" do
-    let(:password) {
+    let(:password) do
       if node[:platform_version].start_with?("10.7.")
         # On Mac 10.7 we only need to set the password
         "c9b3bd1a0cde797eef0eff16c580dab996ba3a21961cccc\
@@ -139,7 +139,7 @@ b1d4880833aa7a190afc13e2bf0936b8\
 c5adbbac718b7eb99463a7b679571e0f\
 1c9fef2ef08d0b9e9c2bcf644eed2ffc"
       end
-    }
+    end
 
     let(:iterations) { 25000 }
     let(:salt) { "9e2e7d5ee473b496fd24cf0bbfcaedfcb291ee21740e570d1e917e874f8788ca" }
diff --git a/spec/functional/resource/user/useradd_spec.rb b/spec/functional/resource/user/useradd_spec.rb
index 43c26ac..79d6243 100644
--- a/spec/functional/resource/user/useradd_spec.rb
+++ b/spec/functional/resource/user/useradd_spec.rb
@@ -21,19 +21,26 @@ require "spec_helper"
 require "functional/resource/base"
 require "chef/mixin/shell_out"
 
-def user_provider_for_platform
-  case ohai[:platform]
+def resource_for_platform(username, run_context)
+  Chef::Resource.resource_for_node(:user, node).new(username, run_context)
+end
+
+# ideally we could somehow pass an array of [ ...::Aix, ...::Linux ] to the
+# filter, but we have to pick the right one for the O/S.
+def user_provider_filter
+  case ohai[:os]
   when "aix"
     Chef::Provider::User::Aix
-  else
-    Chef::Provider::User::Useradd
+  when "linux"
+    Chef::Provider::User::Linux
   end
 end
 
-metadata = { :unix_only => true,
-             :requires_root => true,
-             :not_supported_on_mac_osx => true,
-             :provider => { :user => user_provider_for_platform },
+metadata = {
+  :unix_only => true,
+  :requires_root => true,
+  :not_supported_on_mac_osx => true,
+  :provider => { :user => user_provider_filter },
 }
 
 describe Chef::Provider::User::Useradd, metadata do
@@ -81,12 +88,13 @@ describe Chef::Provider::User::Useradd, metadata do
   end
 
   def try_cleanup
-    ["/home/cheftestfoo", "/home/cheftestbar"].each do |f|
+    ["/home/cheftestfoo", "/home/cheftestbar", "/home/cf-test"].each do |f|
       FileUtils.rm_rf(f) if File.exists? f
     end
 
     ["cf-test"].each do |u|
-      r = Chef::Resource::User.new("DELETE USER", run_context)
+      r = resource_for_platform("DELETE USER", run_context)
+      r.manage_home true
       r.username("cf-test")
       r.run_action(:remove)
     end
@@ -134,10 +142,7 @@ describe Chef::Provider::User::Useradd, metadata do
     Chef::RunContext.new(node, {}, events)
   end
 
-  let(:username) do
-    "cf-test"
-  end
-
+  let(:username) { "cf-test" }
   let(:uid) { nil }
   let(:home) { nil }
   let(:manage_home) { false }
@@ -146,7 +151,7 @@ describe Chef::Provider::User::Useradd, metadata do
   let(:comment) { nil }
 
   let(:user_resource) do
-    r = Chef::Resource::User.new("TEST USER RESOURCE", run_context)
+    r = resource_for_platform("TEST USER RESOURCE", run_context)
     r.username(username)
     r.uid(uid)
     r.home(home)
@@ -242,15 +247,8 @@ describe Chef::Provider::User::Useradd, metadata do
           expect(pw_entry.home).to eq(home)
         end
 
-        if %w{rhel fedora wrlinux}.include?(OHAI_SYSTEM["platform_family"])
-          # Inconsistent behavior. See: CHEF-2205
-          it "creates the home dir when not explicitly asked to on RHEL (XXX)" do
-            expect(File).to exist(home)
-          end
-        else
-          it "does not create the home dir without `manage_home'" do
-            expect(File).not_to exist(home)
-          end
+        it "does not create the home dir without `manage_home'" do
+          expect(File).not_to exist(home)
         end
 
         context "and manage_home is enabled" do
@@ -260,6 +258,14 @@ describe Chef::Provider::User::Useradd, metadata do
             expect(File).to exist(home)
           end
         end
+
+        context "and manage_home is the default" do
+          let(:manage_home) { nil }
+
+          it "does not create the home dir without `manage_home'" do
+            expect(File).not_to exist(home)
+          end
+        end
       end
 
       context "when a password is specified" do
@@ -310,8 +316,8 @@ describe Chef::Provider::User::Useradd, metadata do
       let(:existing_comment) { nil }
 
       let(:existing_user) do
-        r = Chef::Resource::User.new("TEST USER RESOURCE", run_context)
-        # username is identity attr, must match.
+        r = resource_for_platform("TEST USER RESOURCE", run_context)
+          # username is identity attr, must match.
         r.username(username)
         r.uid(existing_uid)
         r.home(existing_home)
diff --git a/spec/functional/resource/windows_service_spec.rb b/spec/functional/resource/windows_service_spec.rb
index b4af1e9..531f9e9 100644
--- a/spec/functional/resource/windows_service_spec.rb
+++ b/spec/functional/resource/windows_service_spec.rb
@@ -27,19 +27,19 @@ describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_
   let(:qualified_username) { "#{ENV['COMPUTERNAME']}\\#{username}" }
   let(:password) { "1a2b3c4X!&narf" }
 
-  let(:user_resource) {
-    r = Chef::Resource::User.new(username, run_context)
+  let(:user_resource) do
+    r = Chef::Resource::User::WindowsUser.new(username, run_context)
     r.username(username)
     r.password(password)
     r.comment("temp spec user")
     r
-  }
+  end
 
-  let(:global_service_file_path) {
+  let(:global_service_file_path) do
     "#{ENV['WINDIR']}\\temp\\#{File.basename(test_service[:service_file_path])}"
-  }
+  end
 
-  let(:service_params) {
+  let(:service_params) do
 
     id = "#{$$}_#{rand(1000)}"
 
@@ -51,19 +51,19 @@ describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_
       service_description: "Test service for running the windows_service functional spec.",
       service_file_path: global_service_file_path,
       } )
-  }
+  end
 
-  let(:manager) {
+  let(:manager) do
     Chef::Application::WindowsServiceManager.new(service_params)
-  }
+  end
 
-  let(:service_resource) {
+  let(:service_resource) do
     r = Chef::Resource::WindowsService.new(service_params[:service_name], run_context)
     [:run_as_user, :run_as_password].each { |prop| r.send(prop, service_params[prop]) }
     r
-  }
+  end
 
-  before {
+  before do
     user_resource.run_action(:create)
 
     # the service executable has to be outside the current user's home
@@ -81,13 +81,13 @@ describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_
     file.run_action(:create)
 
     manager.run(%w{--action install})
-  }
+  end
 
-  after {
+  after do
     user_resource.run_action(:remove)
     manager.run(%w{--action uninstall})
     File.delete(global_service_file_path)
-  }
+  end
 
   describe "logon as a service" do
     it "successfully runs a service as another user" do
diff --git a/spec/functional/rest_spec.rb b/spec/functional/rest_spec.rb
index adafc18..14e7608 100644
--- a/spec/functional/rest_spec.rb
+++ b/spec/functional/rest_spec.rb
@@ -83,11 +83,11 @@ describe Chef::REST do
     Chef::Config[:treat_deprecation_warnings_as_errors] = false
   end
 
-  before(:all) do
+  before(:each) do
     start_tiny_server
   end
 
-  after(:all) do
+  after(:each) do
     stop_tiny_server
   end
 
diff --git a/spec/functional/run_lock_spec.rb b/spec/functional/run_lock_spec.rb
index f1e480c..d270803 100644
--- a/spec/functional/run_lock_spec.rb
+++ b/spec/functional/run_lock_spec.rb
@@ -46,11 +46,9 @@ describe Chef::RunLock do
     end
 
     WAIT_ON_LOCK_TIME = 1.0
-    def wait_on_lock
+    def wait_on_lock(from_fork)
       Timeout.timeout(WAIT_ON_LOCK_TIME) do
-        until File.exist?(lockfile)
-          sleep 0.1
-        end
+        from_fork.readline
       end
     rescue Timeout::Error
       raise "Lockfile never created, abandoning test"
@@ -258,37 +256,48 @@ describe Chef::RunLock do
 
     it "test returns true and acquires the lock" do
       run_lock = Chef::RunLock.new(lockfile)
+      from_tests, to_fork = IO.pipe
+      from_fork, to_tests = IO.pipe
       p1 = fork do
         expect(run_lock.test).to eq(true)
-        run_lock.save_pid
-        sleep 2
-        exit! 1
+        to_tests.puts "lock acquired"
+        # Wait for the test to tell us we can exit before exiting
+        from_tests.readline
+        exit! 0
       end
 
-      wait_on_lock
+      wait_on_lock(from_fork)
 
       p2 = fork do
         expect(run_lock.test).to eq(false)
         exit! 0
       end
 
-      Process.waitpid2(p2)
-      Process.waitpid2(p1)
+      pid, exit_status = Process.waitpid2(p2)
+      expect(exit_status).to eq(0)
+      to_fork.puts "you can exit now"
+      pid, exit_status = Process.waitpid2(p1)
+      expect(exit_status).to eq(0)
     end
 
     it "test returns without waiting when the lock is acquired" do
       run_lock = Chef::RunLock.new(lockfile)
+      from_tests, to_fork = IO.pipe
+      from_fork, to_tests = IO.pipe
       p1 = fork do
         run_lock.acquire
-        run_lock.save_pid
-        sleep 2
-        exit! 1
+        to_tests.puts "lock acquired"
+        # Wait for the test to tell us we can exit before exiting
+        from_tests.readline
+        exit! 0
       end
 
-      wait_on_lock
-
+      wait_on_lock(from_fork)
       expect(run_lock.test).to eq(false)
-      Process.waitpid2(p1)
+
+      to_fork.puts "you can exit now"
+      pid, exit_status = Process.waitpid2(p1)
+      expect(exit_status).to eq(0)
     end
 
   end
@@ -386,9 +395,7 @@ describe Chef::RunLock do
           # Send it the kill signal over and over until it dies
           Timeout.timeout(CLIENT_PROCESS_TIMEOUT) do
             Process.kill(:KILL, pid)
-            until Process.waitpid2(pid, Process::WNOHANG)
-              sleep(0.05)
-            end
+            sleep(0.05) until Process.waitpid2(pid, Process::WNOHANG)
           end
           example.log_event("#{name}.stop finished (stopped pid #{pid})")
         # Process not found is perfectly fine when we're trying to kill a process :)
diff --git a/spec/functional/shell_spec.rb b/spec/functional/shell_spec.rb
index fe2abdb..636162f 100644
--- a/spec/functional/shell_spec.rb
+++ b/spec/functional/shell_spec.rb
@@ -137,7 +137,7 @@ describe Shell do
 
     it "sets the override_runlist from the command line" do
       output, exitstatus = run_chef_shell_with("-o 'override::foo,override::bar'") do |out, keyboard|
-        show_recipes_code = %q[puts "#{node.recipes.inspect}"]
+        show_recipes_code = %q[puts "#{node["recipes"].inspect}"]
         keyboard.puts(show_recipes_code)
         read_until(out, show_recipes_code)
       end
diff --git a/spec/functional/tiny_server_spec.rb b/spec/functional/tiny_server_spec.rb
index 2a025a2..1ec56bd 100644
--- a/spec/functional/tiny_server_spec.rb
+++ b/spec/functional/tiny_server_spec.rb
@@ -65,14 +65,15 @@ end
 
 describe TinyServer::Manager do
   it "runs the server" do
-    @server = TinyServer::Manager.new
-    @server.start
+    server = TinyServer::Manager.new
+    server.start
+    begin
+      TinyServer::API.instance.get("/index", 200, "[\"hello\"]")
 
-    TinyServer::API.instance.get("/index", 200, "[\"hello\"]")
-
-    rest = Chef::HTTP.new("http://localhost:9000")
-    expect(rest.get("index")).to eq("[\"hello\"]")
-
-    @server.stop
+      rest = Chef::HTTP.new("http://localhost:9000")
+      expect(rest.get("index")).to eq("[\"hello\"]")
+    ensure
+      server.stop
+    end
   end
 end
diff --git a/spec/functional/win32/crypto_spec.rb b/spec/functional/win32/crypto_spec.rb
index 75a8bfb..145c988 100644
--- a/spec/functional/win32/crypto_spec.rb
+++ b/spec/functional/win32/crypto_spec.rb
@@ -22,7 +22,7 @@ if Chef::Platform.windows?
 end
 
 describe "Chef::ReservedNames::Win32::Crypto", :windows_only do
-  describe '#encrypt' do
+  describe "#encrypt" do
     before(:all) do
       new_node = Chef::Node.new
       new_node.consume_external_attrs(OHAI_SYSTEM.data, {})
diff --git a/spec/functional/win32/security_spec.rb b/spec/functional/win32/security_spec.rb
index c4951f3..40ae99b 100644
--- a/spec/functional/win32/security_spec.rb
+++ b/spec/functional/win32/security_spec.rb
@@ -45,27 +45,27 @@ describe "Chef::Win32::Security", :windows_only do
   end
 
   describe "access_check" do
-    let(:security_descriptor) {
+    let(:security_descriptor) do
       Chef::ReservedNames::Win32::Security.get_file_security(
         "C:\\Program Files")
-    }
+    end
 
     let(:token_rights) { Chef::ReservedNames::Win32::Security::TOKEN_ALL_ACCESS }
 
-    let(:token) {
+    let(:token) do
       Chef::ReservedNames::Win32::Security.open_process_token(
         Chef::ReservedNames::Win32::Process.get_current_process,
         token_rights).duplicate_token(:SecurityImpersonation)
-    }
+    end
 
-    let(:mapping) {
+    let(:mapping) do
       mapping = Chef::ReservedNames::Win32::Security::GENERIC_MAPPING.new
       mapping[:GenericRead] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_READ
       mapping[:GenericWrite] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_WRITE
       mapping[:GenericExecute] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_EXECUTE
       mapping[:GenericAll] = Chef::ReservedNames::Win32::Security::FILE_ALL_ACCESS
       mapping
-    }
+    end
 
     let(:desired_access) { Chef::ReservedNames::Win32::Security::FILE_GENERIC_READ }
 
@@ -76,11 +76,11 @@ describe "Chef::Win32::Security", :windows_only do
   end
 
   describe "Chef::Win32::Security::Token" do
-    let(:token) {
+    let(:token) do
       Chef::ReservedNames::Win32::Security.open_process_token(
         Chef::ReservedNames::Win32::Process.get_current_process,
         token_rights)
-    }
+    end
     context "with all rights" do
       let(:token_rights) { Chef::ReservedNames::Win32::Security::TOKEN_ALL_ACCESS }
 
diff --git a/spec/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb
index f6e5006..da3a2b9 100644
--- a/spec/integration/client/client_spec.rb
+++ b/spec/integration/client/client_spec.rb
@@ -19,11 +19,11 @@ describe "chef-client" do
     #
     # just a normal file
     # (expected_content should be uncompressed)
-    @api.get("/recipes.tgz", 200) {
+    @api.get("/recipes.tgz", 200) do
       File.open(recipes_filename, "rb") do |f|
         f.read
       end
-    }
+    end
   end
 
   def stop_tiny_server
@@ -376,6 +376,8 @@ EOM
     end
 
     it "the cheffish DSL tries to load but fails (because chef-provisioning is not there)" do
+      # we'd need to have a custom bundle to fix this that omitted chef-provisioning, but that would dig our crazy even deeper, so lets not
+      skip "but if chef-provisioning is in our bundle or in our gemset then this test, very annoyingly, always fails"
       command = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' --no-fork", :cwd => chef_dir)
       expect(command.exitstatus).to eql(1)
       expect(command.stdout).to match(/cannot load such file -- chef\/provisioning/)
@@ -474,11 +476,11 @@ end
 
   # Fails on appveyor, but works locally on windows and on windows hosts in Ci.
   context "when using recipe-url", :skip_appveyor do
-    before(:all) do
+    before(:each) do
       start_tiny_server
     end
 
-    after(:all) do
+    after(:each) do
       stop_tiny_server
     end
 
diff --git a/spec/integration/knife/chef_repo_path_spec.rb b/spec/integration/knife/chef_repo_path_spec.rb
index cf12b7d..1388aa8 100644
--- a/spec/integration/knife/chef_repo_path_spec.rb
+++ b/spec/integration/knife/chef_repo_path_spec.rb
@@ -48,7 +48,7 @@ describe "chef_repo_path tests", :workstation do
 
         directory "chef_repo2" do
           file "clients/client3.json", {}
-          file "cookbooks/cookbook3/metadata.rb", ""
+          file "cookbooks/cookbook3/metadata.rb", "name 'cookbook3'"
           file "data_bags/bag3/item3.json", {}
           file "environments/env3.json", {}
           file "nodes/node3.json", {}
@@ -79,6 +79,75 @@ describe "chef_repo_path tests", :workstation do
 EOM
       end
 
+      it "knife list --local -Rfp --chef-repo-path chef_r~1 / grabs chef_repo2 stuff", :windows_only do
+        Chef::Config.delete(:chef_repo_path)
+        knife("list --local -Rfp --chef-repo-path #{path_to('chef_r~1')} /").should_succeed <<EOM
+/clients/
+/clients/client3.json
+/cookbooks/
+/cookbooks/cookbook3/
+/cookbooks/cookbook3/metadata.rb
+/data_bags/
+/data_bags/bag3/
+/data_bags/bag3/item3.json
+/environments/
+/environments/env3.json
+/nodes/
+/nodes/node3.json
+/roles/
+/roles/role3.json
+/users/
+/users/user3.json
+EOM
+      end
+
+      it "knife list --local -Rfp --chef-repo-path chef_r~1 / grabs chef_repo2 stuff", :windows_only do
+        Chef::Config.delete(:chef_repo_path)
+        knife("list -z -Rfp --chef-repo-path #{path_to('chef_r~1')} /").should_succeed <<EOM
+/acls/
+/acls/clients/
+/acls/clients/client3.json
+/acls/containers/
+/acls/cookbook_artifacts/
+/acls/cookbooks/
+/acls/cookbooks/cookbook3.json
+/acls/data_bags/
+/acls/data_bags/bag3.json
+/acls/environments/
+/acls/environments/env3.json
+/acls/groups/
+/acls/nodes/
+/acls/nodes/node3.json
+/acls/organization.json
+/acls/policies/
+/acls/policy_groups/
+/acls/roles/
+/acls/roles/role3.json
+/clients/
+/clients/client3.json
+/containers/
+/cookbook_artifacts/
+/cookbooks/
+/cookbooks/cookbook3/
+/cookbooks/cookbook3/metadata.rb
+/data_bags/
+/data_bags/bag3/
+/data_bags/bag3/item3.json
+/environments/
+/environments/env3.json
+/groups/
+/invitations.json
+/members.json
+/nodes/
+/nodes/node3.json
+/org.json
+/policies/
+/policy_groups/
+/roles/
+/roles/role3.json
+EOM
+      end
+
       context "when all _paths are set to alternates" do
         before :each do
           %w{client cookbook data_bag environment node role user}.each do |object_name|
diff --git a/spec/integration/knife/client_bulk_delete_spec.rb b/spec/integration/knife/client_bulk_delete_spec.rb
new file mode 100644
index 0000000..a422401
--- /dev/null
+++ b/spec/integration/knife/client_bulk_delete_spec.rb
@@ -0,0 +1,130 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife client bulk delete", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some clients" do
+    before do
+      client "concat", {}
+      client "cons", {}
+      client "car", {}
+      client "cdr", {}
+      client "cat", {}
+    end
+
+    it "deletes all matching clients" do
+      knife("client bulk delete ^ca.*", input: "Y").should_succeed <<EOM
+The following clients will be deleted:
+
+car  cat
+
+Are you sure you want to delete these clients? (Y/N) Deleted client car
+Deleted client cat
+EOM
+
+      knife("client list").should_succeed <<EOM
+cdr
+chef-validator
+chef-webui
+concat
+cons
+EOM
+    end
+
+    it "deletes all matching clients when unanchored" do
+      knife("client bulk delete ca.*", input: "Y").should_succeed <<EOM
+The following clients will be deleted:
+
+car     cat     concat
+
+Are you sure you want to delete these clients? (Y/N) Deleted client car
+Deleted client cat
+Deleted client concat
+EOM
+
+      knife("client list").should_succeed <<EOM
+cdr
+chef-validator
+chef-webui
+cons
+EOM
+    end
+  end
+
+  when_the_chef_server "has a validator client" do
+    before do
+      client "cons", {}
+      client "car", {}
+      client "car-validator", { validator: true }
+      client "cdr", {}
+      client "cat", {}
+    end
+
+    it "refuses to delete a validator normally" do
+      knife("client bulk delete ^ca.*", input: "Y").should_succeed <<EOM
+The following clients are validators and will not be deleted:
+
+car-validator
+
+You must specify --delete-validators to delete the validator clients
+The following clients will be deleted:
+
+car  cat
+
+Are you sure you want to delete these clients? (Y/N) Deleted client car
+Deleted client cat
+EOM
+
+      knife("client list").should_succeed <<EOM
+car-validator
+cdr
+chef-validator
+chef-webui
+cons
+EOM
+    end
+
+    it "deletes a validator when told to" do
+      knife("client bulk delete ^ca.* -D", input: "Y\nY").should_succeed <<EOM
+The following validators will be deleted:
+
+car-validator
+
+Are you sure you want to delete these validators? (Y/N) Deleted client car-validator
+The following clients will be deleted:
+
+car  cat
+
+Are you sure you want to delete these clients? (Y/N) Deleted client car
+Deleted client cat
+EOM
+
+      knife("client list").should_succeed <<EOM
+cdr
+chef-validator
+chef-webui
+cons
+EOM
+    end
+  end
+end
diff --git a/spec/integration/knife/client_create_spec.rb b/spec/integration/knife/client_create_spec.rb
new file mode 100644
index 0000000..1017283
--- /dev/null
+++ b/spec/integration/knife/client_create_spec.rb
@@ -0,0 +1,69 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "openssl"
+
+describe "knife client create", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  let(:out) { "Created client[bah]\n" }
+
+  when_the_chef_server "is empty" do
+    it "creates a new client" do
+      knife("client create -k bah").should_succeed stderr: out
+    end
+
+    it "creates a new validator client" do
+      knife("client create -k --validator bah").should_succeed stderr: out
+      knife("client show bah").should_succeed <<EOM
+admin:     false
+chef_type: client
+name:      bah
+validator: true
+EOM
+    end
+
+    it "refuses to add an existing client" do
+      pending "Knife client create must not blindly overwrite an existing client"
+      knife("client create -k bah").should_succeed stderr: out
+      expect { knife("client create -k bah") }.to raise_error(Net::HTTPServerException)
+    end
+
+    it "saves the private key to a file" do
+      Dir.mktmpdir do |tgt|
+        knife("client create -f #{tgt}/bah.pem bah").should_succeed stderr: out
+        expect(File).to exist("#{tgt}/bah.pem")
+      end
+    end
+
+    it "reads the public key from a file" do
+      Dir.mktmpdir do |tgt|
+        key = OpenSSL::PKey::RSA.generate(1024)
+        File.open("#{tgt}/public.pem", "w") { |pub| pub.write(key.public_key.to_pem) }
+        knife("client create -p #{tgt}/public.pem bah").should_succeed stderr: out
+      end
+    end
+
+    it "refuses to run if conflicting options are passed" do
+      knife("client create -p public.pem --prevent-keygen blah").should_fail stderr: "FATAL: You cannot pass --public-key and --prevent-keygen\n", stdout: /^USAGE.*/
+    end
+  end
+end
diff --git a/spec/integration/knife/client_delete_spec.rb b/spec/integration/knife/client_delete_spec.rb
new file mode 100644
index 0000000..d135dd0
--- /dev/null
+++ b/spec/integration/knife/client_delete_spec.rb
@@ -0,0 +1,63 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife client delete", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some clients" do
+    before do
+      client "cons", {}
+      client "car", {}
+      client "car-validator", { validator: true }
+      client "cdr", {}
+      client "cat", {}
+    end
+
+    it "deletes a client" do
+      knife("client delete car", input: "Y").should_succeed <<EOM
+Do you really want to delete car? (Y/N) Deleted client[car]
+EOM
+
+      knife("client list").should_succeed <<EOM
+car-validator
+cat
+cdr
+chef-validator
+chef-webui
+cons
+EOM
+    end
+
+    it "refuses to delete a validator normally" do
+      knife("client delete car-validator", input: "Y").should_fail exit_code: 2, stdout: "Do you really want to delete car-validator? (Y/N) ", stderr: <<EOM
+FATAL: You must specify --delete-validators to delete the validator client car-validator
+EOM
+    end
+
+    it "deletes a validator correctly" do
+      knife("client delete car-validator -D", input: "Y").should_succeed <<EOM
+Do you really want to delete car-validator? (Y/N) Deleted client[car-validator]
+EOM
+    end
+
+  end
+end
diff --git a/spec/integration/knife/client_key_create_spec.rb b/spec/integration/knife/client_key_create_spec.rb
new file mode 100644
index 0000000..b588afb
--- /dev/null
+++ b/spec/integration/knife/client_key_create_spec.rb
@@ -0,0 +1,65 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "openssl"
+
+describe "knife client key create", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  let(:out) { "Created key: new" }
+
+  when_the_chef_server "has a client" do
+    before do
+      client "bah", {}
+    end
+
+    it "creates a new client key" do
+      knife("client key create -k new bah").should_succeed stderr: /^#{out}/, stdout: /.*BEGIN RSA PRIVATE KEY/
+    end
+
+    it "creates a new client key with an expiration date" do
+      date = "2017-12-31T23:59:59Z"
+      knife("client key create -k new -e #{date} bah").should_succeed stderr: /^#{out}/, stdout: /.*BEGIN RSA PRIVATE KEY/
+      knife("client key show bah new").should_succeed /expiration_date:.*#{date}/
+    end
+
+    it "refuses to add an already existing key" do
+      knife("client key create -k new bah")
+      expect { knife("client key create -k new bah") }.to raise_error(Net::HTTPServerException)
+    end
+
+    it "saves the private key to a file" do
+      Dir.mktmpdir do |tgt|
+        knife("client key create -f #{tgt}/bah.pem -k new bah").should_succeed stderr: /^#{out}/
+        expect(File).to exist("#{tgt}/bah.pem")
+      end
+    end
+
+    it "reads the public key from a file" do
+      Dir.mktmpdir do |tgt|
+        key = OpenSSL::PKey::RSA.generate(1024)
+        File.open("#{tgt}/public.pem", "w") { |pub| pub.write(key.public_key.to_pem) }
+        knife("client key create -p #{tgt}/public.pem -k new bah").should_succeed stderr: /^#{out}/
+      end
+    end
+
+  end
+end
diff --git a/spec/integration/knife/client_key_delete_spec.rb b/spec/integration/knife/client_key_delete_spec.rb
new file mode 100644
index 0000000..d5827aa
--- /dev/null
+++ b/spec/integration/knife/client_key_delete_spec.rb
@@ -0,0 +1,42 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife client key delete", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has a client" do
+    before do
+      client "car", {}
+    end
+
+    it "deletes a client" do
+      out = "Do you really want to delete the key named new for the client named car? (Y/N) "
+      knife("client key create -k new car")
+      knife("client key delete car new", input: "Y").should_succeed stdout: out, stderr: <<EOM
+Deleted key named new for the client named car
+EOM
+
+      knife("client key list car").should_succeed ""
+    end
+
+  end
+end
diff --git a/spec/integration/knife/client_key_list_spec.rb b/spec/integration/knife/client_key_list_spec.rb
new file mode 100644
index 0000000..de98946
--- /dev/null
+++ b/spec/integration/knife/client_key_list_spec.rb
@@ -0,0 +1,60 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "date"
+
+describe "knife client key list", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  let(:now) { DateTime.now }
+  let(:last_month) { (now << 1).strftime("%FT%TZ") }
+  let(:next_month) { (now >> 1).strftime("%FT%TZ") }
+
+  when_the_chef_server "has a client" do
+    before do
+      client "cons", {}
+      knife("client key create cons -k new")
+      knife("client key create cons -k next_month -e #{next_month}")
+      knife("client key create cons -k expired -e #{last_month}")
+    end
+
+    it "lists the keys for a client" do
+      knife("client key list cons").should_succeed "expired\nnew\nnext_month\n"
+    end
+
+    it "shows detailed output" do
+      knife("client key list -w cons").should_succeed <<EOM
+expired:    http://127.0.0.1:8900/clients/cons/keys/expired (expired)
+new:        http://127.0.0.1:8900/clients/cons/keys/new
+next_month: http://127.0.0.1:8900/clients/cons/keys/next_month
+EOM
+    end
+
+    it "lists the expired keys for a client" do
+      knife("client key list -e cons").should_succeed "expired\n"
+    end
+
+    it "lists the unexpired keys for a client" do
+      knife("client key list -n cons").should_succeed "new\nnext_month\n"
+    end
+
+  end
+end
diff --git a/spec/integration/knife/client_key_show_spec.rb b/spec/integration/knife/client_key_show_spec.rb
new file mode 100644
index 0000000..e96ff3b
--- /dev/null
+++ b/spec/integration/knife/client_key_show_spec.rb
@@ -0,0 +1,44 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "date"
+
+describe "knife client key show", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  let(:now) { DateTime.now }
+  let(:last_month) { (now << 1).strftime("%FT%TZ") }
+  let(:next_month) { (now >> 1).strftime("%FT%TZ") }
+
+  when_the_chef_server "has a client" do
+    before do
+      client "cons", {}
+      knife("client key create cons -k new")
+      knife("client key create cons -k next_month -e #{next_month}")
+      knife("client key create cons -k expired -e #{last_month}")
+    end
+
+    it "shows a key for a client" do
+      knife("client key show cons new").should_succeed stdout: /.*name:.*new/
+    end
+
+  end
+end
diff --git a/spec/integration/knife/client_list_spec.rb b/spec/integration/knife/client_list_spec.rb
new file mode 100644
index 0000000..4159df7
--- /dev/null
+++ b/spec/integration/knife/client_list_spec.rb
@@ -0,0 +1,48 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife client list", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some clients" do
+    before do
+      client "cons", {}
+      client "car", {}
+      client "car-validator", { validator: true }
+      client "cdr", {}
+      client "cat", {}
+    end
+
+    it "lists the clients" do
+      knife("client list").should_succeed <<EOM
+car
+car-validator
+cat
+cdr
+chef-validator
+chef-webui
+cons
+EOM
+    end
+
+  end
+end
diff --git a/lib/chef/dsl/powershell.rb b/spec/integration/knife/client_show_spec.rb
similarity index 55%
copy from lib/chef/dsl/powershell.rb
copy to spec/integration/knife/client_show_spec.rb
index 1a900af..23ac204 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/spec/integration/knife/client_show_spec.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,16 +13,24 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-require "chef/util/powershell/ps_credential"
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife client show", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
 
-class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+  when_the_chef_server "has a client" do
+    before do
+      client "cons", {}
     end
+
+    it "shows a client" do
+      knife("client show cons").should_succeed stdout: /.*name:.*cons/
+    end
+
   end
 end
diff --git a/spec/integration/knife/cookbook_bulk_delete_spec.rb b/spec/integration/knife/cookbook_bulk_delete_spec.rb
new file mode 100644
index 0000000..4740813
--- /dev/null
+++ b/spec/integration/knife/cookbook_bulk_delete_spec.rb
@@ -0,0 +1,64 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/cookbook_bulk_delete"
+
+describe "knife cookbook bulk delete", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has a cookbook" do
+    before do
+      cookbook "foo", "1.0.0"
+      cookbook "foo", "0.6.5"
+      cookbook "fox", "0.6.0"
+      cookbook "fox", "0.6.5"
+      cookbook "fax", "0.6.0"
+      cookbook "zfa", "0.6.5"
+    end
+
+    # rubocop:disable Style/TrailingWhitespace
+    it "knife cookbook bulk delete deletes all matching cookbooks" do
+      stdout = <<EOM
+All versions of the following cookbooks will be deleted:
+
+foo  fox
+
+Do you really want to delete these cookbooks? (Y/N) 
+EOM
+
+      stderr = <<EOM
+Deleted cookbook  foo                       [1.0.0]
+Deleted cookbook  foo                       [0.6.5]
+Deleted cookbook  fox                       [0.6.5]
+Deleted cookbook  fox                       [0.6.0]
+EOM
+
+      knife("cookbook bulk delete ^fo.*", input: "Y").should_succeed(stderr: stderr, stdout: stdout)
+
+      knife("cookbook list -a").should_succeed <<EOM
+fax   0.6.0
+zfa   0.6.5
+EOM
+    end
+    # rubocop:enable Style/TrailingWhitespace
+
+  end
+end
diff --git a/spec/integration/knife/cookbook_download_spec.rb b/spec/integration/knife/cookbook_download_spec.rb
new file mode 100644
index 0000000..2fbffb9
--- /dev/null
+++ b/spec/integration/knife/cookbook_download_spec.rb
@@ -0,0 +1,95 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/cookbook_download"
+require "tmpdir"
+
+describe "knife cookbook download", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  let(:tmpdir) { Dir.mktmpdir }
+
+  when_the_chef_server "has only one cookbook" do
+    before do
+      cookbook "x", "1.0.1"
+    end
+
+    it "knife cookbook download downloads the latest version" do
+      knife("cookbook download -d #{tmpdir} x").should_succeed stderr: <<EOM
+Downloading x cookbook version 1.0.1
+Downloading resources
+Downloading providers
+Downloading recipes
+Downloading definitions
+Downloading libraries
+Downloading attributes
+Downloading files
+Downloading templates
+Downloading root_files
+Cookbook downloaded to #{tmpdir}/x-1.0.1
+EOM
+    end
+
+    it "knife cookbook download with a version downloads the specified version" do
+      knife("cookbook download -d #{tmpdir} x 1.0.1").should_succeed stderr: <<EOM
+Downloading x cookbook version 1.0.1
+Downloading resources
+Downloading providers
+Downloading recipes
+Downloading definitions
+Downloading libraries
+Downloading attributes
+Downloading files
+Downloading templates
+Downloading root_files
+Cookbook downloaded to #{tmpdir}/x-1.0.1
+EOM
+    end
+
+    it "knife cookbook download with an unknown version raises an error" do
+      expect { knife("cookbook download -d #{tmpdir} x 1.0.0") }.to raise_error(Net::HTTPServerException)
+    end
+  end
+
+  when_the_chef_server "has multiple cookbook versions" do
+    before do
+      cookbook "x", "1.0.1"
+      cookbook "x", "1.0.0"
+    end
+
+    it "knife cookbook download with no version prompts" do
+      knife("cookbook download -d #{tmpdir} x", input: "2\n").should_succeed(stderr: <<EOM, stdout: "Which version do you want to download?\n1. x 1.0.0\n2. x 1.0.1\n\n"
+Downloading x cookbook version 1.0.1
+Downloading resources
+Downloading providers
+Downloading recipes
+Downloading definitions
+Downloading libraries
+Downloading attributes
+Downloading files
+Downloading templates
+Downloading root_files
+Cookbook downloaded to #{tmpdir}/x-1.0.1
+EOM
+)
+    end
+  end
+end
diff --git a/spec/integration/knife/cookbook_list_spec.rb b/spec/integration/knife/cookbook_list_spec.rb
new file mode 100644
index 0000000..6557869
--- /dev/null
+++ b/spec/integration/knife/cookbook_list_spec.rb
@@ -0,0 +1,54 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/cookbook_list"
+
+describe "knife cookbook list", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has a cookbook" do
+    before do
+      cookbook "x", "1.0.0"
+      cookbook "x", "0.6.5"
+      cookbook "x", "0.6.0"
+      cookbook "y", "0.6.5"
+      cookbook "y", "0.6.0"
+      cookbook "z", "0.6.5"
+    end
+
+    it "knife cookbook list shows all the cookbooks" do
+      knife("cookbook list").should_succeed <<EOM
+x   1.0.0
+y   0.6.5
+z   0.6.5
+EOM
+    end
+
+    it "knife cookbook list -a shows all the versions of all the cookbooks" do
+      knife("cookbook list -a").should_succeed <<EOM
+x   1.0.0  0.6.5  0.6.0
+y   0.6.5  0.6.0
+z   0.6.5
+EOM
+    end
+
+  end
+end
diff --git a/spec/integration/knife/cookbook_show_spec.rb b/spec/integration/knife/cookbook_show_spec.rb
new file mode 100644
index 0000000..c001d66
--- /dev/null
+++ b/spec/integration/knife/cookbook_show_spec.rb
@@ -0,0 +1,159 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/cookbook_show"
+
+describe "knife cookbook show", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has a cookbook" do
+    before do
+      cookbook "x", "1.0.0", { "recipes" => { "default.rb" => "file 'n'", "x.rb" => "" } }
+      cookbook "x", "0.6.5"
+    end
+
+    it "knife cookbook show x shows all the versions" do
+      knife("cookbook show x").should_succeed "x   1.0.0  0.6.5\n"
+    end
+
+    # rubocop:disable Style/TrailingWhitespace
+    it "knife cookbook show x 1.0.0 shows the correct version" do
+      knife("cookbook show x 1.0.0").should_succeed <<EOM
+attributes:
+chef_type:     cookbook_version
+cookbook_name: x
+definitions:
+files:
+frozen?:       false
+json_class:    Chef::CookbookVersion
+libraries:
+metadata:
+  attributes:
+  chef_versions:
+  conflicting:
+  dependencies:
+  description:      
+  gems:
+  groupings:
+  issues_url:       
+  license:          All rights reserved
+  long_description: 
+  maintainer:       
+  maintainer_email: 
+  name:             x
+  ohai_versions:
+  platforms:
+  privacy:          false
+  providing:
+  recipes:
+  recommendations:
+  replacing:
+  source_url:       
+  suggestions:
+  version:          1.0.0
+name:          x-1.0.0
+providers:
+recipes:
+  checksum:    4631b34cf58de10c5ef1304889941b2e
+  name:        default.rb
+  path:        recipes/default.rb
+  specificity: default
+  url:         http://127.0.0.1:8900/file_store/checksums/4631b34cf58de10c5ef1304889941b2e
+  
+  checksum:    d41d8cd98f00b204e9800998ecf8427e
+  name:        x.rb
+  path:        recipes/x.rb
+  specificity: default
+  url:         http://127.0.0.1:8900/file_store/checksums/d41d8cd98f00b204e9800998ecf8427e
+resources:
+root_files:
+  checksum:    8226671f751ba102dea6a6b6bd32fa8d
+  name:        metadata.rb
+  path:        metadata.rb
+  specificity: default
+  url:         http://127.0.0.1:8900/file_store/checksums/8226671f751ba102dea6a6b6bd32fa8d
+templates:
+version:       1.0.0
+EOM
+    end
+
+    it "knife cookbook show x 1.0.0 metadata shows the metadata" do
+      knife("cookbook show x 1.0.0 metadata").should_succeed <<EOM
+attributes:
+chef_versions:
+conflicting:
+dependencies:
+description:      
+gems:
+groupings:
+issues_url:       
+license:          All rights reserved
+long_description: 
+maintainer:       
+maintainer_email: 
+name:             x
+ohai_versions:
+platforms:
+privacy:          false
+providing:
+recipes:
+recommendations:
+replacing:
+source_url:       
+suggestions:
+version:          1.0.0
+EOM
+    end
+
+    it "knife cookbook show x 1.0.0 recipes shows all the recipes" do
+      knife("cookbook show x 1.0.0 recipes").should_succeed <<EOM
+checksum:    4631b34cf58de10c5ef1304889941b2e
+name:        default.rb
+path:        recipes/default.rb
+specificity: default
+url:         http://127.0.0.1:8900/file_store/checksums/4631b34cf58de10c5ef1304889941b2e
+
+checksum:    d41d8cd98f00b204e9800998ecf8427e
+name:        x.rb
+path:        recipes/x.rb
+specificity: default
+url:         http://127.0.0.1:8900/file_store/checksums/d41d8cd98f00b204e9800998ecf8427e
+EOM
+    end
+    # rubocop:enable Style/TrailingWhitespace
+
+    it "knife cookbook show x 1.0.0 recipes default.rb shows the default recipe" do
+      knife("cookbook show x 1.0.0 recipes default.rb").should_succeed "file 'n'\n"
+    end
+
+    it "knife cookbook show with a non-existent file displays an error" do
+      expect { knife("cookbook show x 1.0.0 recipes moose.rb") }.to raise_error(Chef::Exceptions::FileNotFound)
+    end
+
+    it "knife cookbook show with a non-existent version displays an error" do
+      expect { knife("cookbook show x 1.0.1") }.to raise_error(Net::HTTPServerException)
+    end
+
+    it "knife cookbook show with a non-existent cookbook displays an error" do
+      expect { knife("cookbook show y") }.to raise_error(Net::HTTPServerException)
+    end
+  end
+end
diff --git a/spec/integration/knife/cookbook_upload_spec.rb b/spec/integration/knife/cookbook_upload_spec.rb
new file mode 100644
index 0000000..a0de725
--- /dev/null
+++ b/spec/integration/knife/cookbook_upload_spec.rb
@@ -0,0 +1,90 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/cookbook_upload"
+
+describe "knife cookbook upload", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  let (:cb_dir) { "#{@repository_dir}/cookbooks" }
+
+  when_the_chef_server "is empty" do
+    when_the_repository "has a cookbook" do
+      before do
+        file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0")
+      end
+
+      it "knife cookbook upload uploads the cookbook" do
+        knife("cookbook upload x -o #{cb_dir}").should_succeed stderr: <<EOM
+Uploading x            [1.0.0]
+Uploaded 1 cookbook.
+EOM
+      end
+
+      it "knife cookbook upload --freeze uploads and freezes the cookbook" do
+        knife("cookbook upload x -o #{cb_dir} --freeze").should_succeed stderr: <<EOM
+Uploading x            [1.0.0]
+Uploaded 1 cookbook.
+EOM
+        # Modify the file, attempt to reupload
+        file "cookbooks/x/metadata.rb", 'name "x"; version "1.0.0"#different'
+        knife("cookbook upload x -o #{cb_dir} --freeze").should_fail stderr: <<EOM
+Uploading x              [1.0.0]
+ERROR: Version 1.0.0 of cookbook x is frozen. Use --force to override.
+WARNING: Not updating version constraints for x in the environment as the cookbook is frozen.
+ERROR: Failed to upload 1 cookbook.
+EOM
+      end
+    end
+
+    when_the_repository "has a cookbook that depends on another cookbook" do
+      before do
+        file "cookbooks/x/metadata.rb", cb_metadata("x", "1.0.0", "\ndepends 'y'")
+        file "cookbooks/y/metadata.rb", cb_metadata("y", "1.0.0")
+      end
+
+      it "knife cookbook upload --include-dependencies uploads both cookbooks" do
+        knife("cookbook upload --include-dependencies x -o #{cb_dir}").should_succeed stderr: <<EOM
+Uploading x            [1.0.0]
+Uploading y            [1.0.0]
+Uploaded 2 cookbooks.
+EOM
+      end
+
+      it "knife cookbook upload fails due to missing dependencies" do
+        knife("cookbook upload x -o #{cb_dir}").should_fail stderr: <<EOM
+Uploading x            [1.0.0]
+ERROR: Cookbook x depends on cookbooks which are not currently
+ERROR: being uploaded and cannot be found on the server.
+ERROR: The missing cookbook(s) are: 'y' version '>= 0.0.0'
+EOM
+      end
+
+      it "knife cookbook upload -a uploads both cookbooks" do
+        knife("cookbook upload -a -o #{cb_dir}").should_succeed stderr: <<EOM
+Uploading x            [1.0.0]
+Uploading y            [1.0.0]
+Uploaded all cookbooks.
+EOM
+      end
+    end
+  end
+end
diff --git a/spec/integration/knife/data_bag_create_spec.rb b/spec/integration/knife/data_bag_create_spec.rb
new file mode 100644
index 0000000..0a07792
--- /dev/null
+++ b/spec/integration/knife/data_bag_create_spec.rb
@@ -0,0 +1,58 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/data_bag_create"
+
+describe "knife data bag create", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  let(:err) { "Created data_bag[foo]\n" }
+  let(:out) { "Created data_bag_item[bar]\n" }
+  let(:exists) { "Data bag foo already exists\n" }
+
+  when_the_chef_server "is empty" do
+    it "creates a new data bag" do
+      knife("data bag create foo").should_succeed stderr: err
+    end
+
+    it "creates a new data bag and item" do
+      pending "Deprecation warning must get fixed"
+      knife("data bag create foo bar").should_succeed stdout: out, stderr: err
+    end
+
+    it "adds a new item to an existing bag" do
+      pending "Deprecation warning must get fixed"
+      knife("data bag create foo").should_succeed stderr: err
+      knife("data bag create foo bar").should_succeed stdout: out, stderr: exists
+    end
+
+    it "refuses to add an existing data bag" do
+      knife("data bag create foo").should_succeed stderr: err
+      knife("data bag create foo").should_succeed stderr: exists
+    end
+
+    it "fails to add an existing item" do
+      pending "Deprecation warning must get fixed"
+      knife("data bag create foo bar").should_succeed stdout: out, stderr: err
+      expect { knife("data bag create foo bar") }.to raise_error(Net::HTTPServerException)
+    end
+  end
+end
diff --git a/spec/integration/knife/data_bag_delete_spec.rb b/spec/integration/knife/data_bag_delete_spec.rb
new file mode 100644
index 0000000..96345b0
--- /dev/null
+++ b/spec/integration/knife/data_bag_delete_spec.rb
@@ -0,0 +1,58 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/data_bag_delete"
+
+describe "knife data bag delete", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some data bags" do
+    before do
+      data_bag "x", {}
+      data_bag "canteloupe", {}
+      data_bag "rocket", { "falcon9" => { heavy: "true" }, "atlas" => {}, "ariane" => {} }
+    end
+
+    it "with an empty data bag" do
+      knife("data bag delete canteloupe", input: "y").should_succeed <<EOM
+Do you really want to delete canteloupe? (Y/N) Deleted data_bag[canteloupe]
+EOM
+    end
+
+    it "with a bag with some items" do
+      knife("data bag delete rocket", input: "y").should_succeed <<EOM
+Do you really want to delete rocket? (Y/N) Deleted data_bag[rocket]
+EOM
+    end
+
+    it "with a single item" do
+      knife("data bag delete rocket falcon9", input: "y").should_succeed <<EOM
+Do you really want to delete falcon9? (Y/N) Deleted data_bag_item[falcon9]
+EOM
+    end
+
+    it "choosing not to delete" do
+      knife("data bag delete rocket falcon9", input: "n").should_succeed <<EOM, exit_code: 3
+Do you really want to delete falcon9? (Y/N) You said no, so I'm done here.
+EOM
+    end
+  end
+end
diff --git a/spec/integration/knife/data_bag_from_file_spec.rb b/spec/integration/knife/data_bag_from_file_spec.rb
new file mode 100644
index 0000000..ca8f743
--- /dev/null
+++ b/spec/integration/knife/data_bag_from_file_spec.rb
@@ -0,0 +1,115 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife data bag from file", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  let (:db_dir) { "#{@repository_dir}/data_bags" }
+
+  when_the_chef_server "has an empty data bag" do
+    before do
+      data_bag "foo", {}
+      data_bag "bar", {}
+    end
+
+    when_the_repository "has some data bag items" do
+      before do
+        file "data_bags/foo/bar.json", { "id" => "bar", "foo" => "bar " }
+        file "data_bags/foo/bzr.json", { "id" => "bzr", "foo" => "bar " }
+        file "data_bags/foo/cat.json", { "id" => "cat", "foo" => "bar " }
+        file "data_bags/foo/dog.json", { "id" => "dog", "foo" => "bar " }
+        file "data_bags/foo/encrypted.json", <<EOM
+{
+  "id": "encrypted",
+  "password": {
+    "encrypted_data": "H6ab5RY9a9JAkS8A0RCMspXtOJh0ai8cNeA4Q3gLO8s=\\n",
+    "iv": "uWKKKxrJgtELlGMCOLJdkA==\\n",
+    "version": 1,
+    "cipher": "aes-256-cbc"
+  }
+}
+EOM
+        file "data_bags/bar/round_trip.json", <<EOM
+{
+  "name": "data_bag_item_bar_round_trip",
+  "json_class": "Chef::DataBagItem",
+  "chef_type": "data_bag_item",
+  "data_bag": "bar",
+  "raw_data": {
+    "id": "round_trip",
+    "root_password": {
+      "encrypted_data": "noDOsTpsTAZlTU5sprhmYZzUDfr8du7hH/zRDOjRAmoTJHTZyfYoR221EOOW\\nXJ1D\\n",
+      "iv": "Bnqhfy6n0Hx1wCe9pxHLoA==\\n",
+      "version": 1,
+      "cipher": "aes-256-cbc"
+    },
+    "admin_password": {
+      "encrypted_data": "TcC7dU1gx6OnE5Ab4i/k42UEf0Nnr7cAyuTHId/LNjNOwpNf7XZc27DQSjuy\\nHPlt\\n",
+      "iv": "+TAWJuPWCI2+WB8lGJAyvw==\\n",
+      "version": 1,
+      "cipher": "aes-256-cbc"
+    }
+  }
+}
+EOM
+      end
+
+      it "uploads a single file" do
+        knife("data bag from file foo #{db_dir}/foo/bar.json").should_succeed stderr: <<EOM
+Updated data_bag_item[foo::bar]
+EOM
+      end
+
+      it "uploads a single encrypted file" do
+        knife("data bag from file foo #{db_dir}/foo/encrypted.json").should_succeed stderr: <<EOM
+Updated data_bag_item[foo::encrypted]
+EOM
+      end
+
+      it "uploads a file in chef's internal format" do
+        pending "chef/chef#4815"
+        knife("data bag from file bar #{db_dir}/bar/round_trip.json").should_succeed stderr: <<EOM
+Updated data_bag_item[bar::round_trip]
+EOM
+      end
+
+      it "uploads many files" do
+        knife("data bag from file foo #{db_dir}/foo/bar.json #{db_dir}/foo/bzr.json").should_succeed stderr: <<EOM
+Updated data_bag_item[foo::bar]
+Updated data_bag_item[foo::bzr]
+EOM
+      end
+
+      it "uploads a whole directory" do
+        knife("data bag from file foo #{db_dir}/foo")
+        knife("data bag show foo").should_succeed <<EOM
+bar
+bzr
+cat
+dog
+encrypted
+EOM
+      end
+
+    end
+  end
+end
diff --git a/spec/integration/knife/data_bag_list_spec.rb b/spec/integration/knife/data_bag_list_spec.rb
new file mode 100644
index 0000000..7db9638
--- /dev/null
+++ b/spec/integration/knife/data_bag_list_spec.rb
@@ -0,0 +1,43 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/data_bag_list"
+
+describe "knife data bag list", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some data bags" do
+    before do
+      data_bag "x", {}
+      data_bag "canteloupe", {}
+      data_bag "rocket", {}
+    end
+
+    it "knife data bag list shows all the cookbooks" do
+      knife("data bag list").should_succeed <<EOM
+canteloupe
+rocket
+x
+EOM
+    end
+
+  end
+end
diff --git a/spec/integration/knife/data_bag_show_spec.rb b/spec/integration/knife/data_bag_show_spec.rb
new file mode 100644
index 0000000..22381ad
--- /dev/null
+++ b/spec/integration/knife/data_bag_show_spec.rb
@@ -0,0 +1,53 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "chef/knife/data_bag_show"
+
+describe "knife data bag show", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some data bags" do
+    before do
+      data_bag "x", {}
+      data_bag "canteloupe", {}
+      data_bag "rocket", { "falcon9" => { heavy: "true" }, "atlas" => {}, "ariane" => {} }
+    end
+
+    it "with an empty data bag" do
+      knife("data bag show canteloupe").should_succeed "\n"
+    end
+
+    it "with a bag with some items" do
+      knife("data bag show rocket").should_succeed <<EOM
+ariane
+atlas
+falcon9
+EOM
+    end
+
+    it "with a single item" do
+      knife("data bag show rocket falcon9").should_succeed <<EOM, stderr: "WARNING: Unencrypted data bag detected, ignoring any provided secret options.\n"
+heavy: true
+id:    falcon9
+EOM
+    end
+  end
+end
diff --git a/spec/integration/knife/diff_spec.rb b/spec/integration/knife/diff_spec.rb
index b7d2f4d..b3bd23f 100644
--- a/spec/integration/knife/diff_spec.rb
+++ b/spec/integration/knife/diff_spec.rb
@@ -72,7 +72,7 @@ EOM
           file "data_bags/x/y.json", {}
           file "environments/_default.json", { "description" => "The default Chef environment" }
           file "environments/x.json", {}
-          file "nodes/x.json", {}
+          file "nodes/x.json", { "normal" => { "tags" => [] } }
           file "roles/x.json", {}
           file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
           file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
@@ -366,7 +366,7 @@ EOM
           file "data_bags/x/y.json", {}
           file "environments/_default.json", { "description" => "The default Chef environment" }
           file "environments/x.json", {}
-          file "nodes/x.json", {}
+          file "nodes/x.json", { "normal" => { "tags" => [] } }
           file "roles/x.json", {}
           file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
           file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
diff --git a/spec/integration/knife/download_spec.rb b/spec/integration/knife/download_spec.rb
index a924226..be0fc9d 100644
--- a/spec/integration/knife/download_spec.rb
+++ b/spec/integration/knife/download_spec.rb
@@ -76,7 +76,7 @@ EOM
           file "data_bags/x/y.json", {}
           file "environments/_default.json", { "description" => "The default Chef environment" }
           file "environments/x.json", {}
-          file "nodes/x.json", {}
+          file "nodes/x.json", { "normal" => { "tags" => [] } }
           file "roles/x.json", {}
           file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
           file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
@@ -645,7 +645,7 @@ EOM
           file "data_bags/x/y.json", {}
           file "environments/_default.json", { "description" => "The default Chef environment" }
           file "environments/x.json", {}
-          file "nodes/x.json", {}
+          file "nodes/x.json", { "normal" => { "tags" => [] } }
           file "roles/x.json", {}
           file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
           file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
@@ -1271,7 +1271,7 @@ EOM
             file "groups/x.json", {}
             file "invitations.json", [ "foo" ]
             file "members.json", [ "bar" ]
-            file "nodes/x.json", {}
+            file "nodes/x.json", { "normal" => { "tags" => [] } }
             file "org.json", { "full_name" => "Something" }
             file "policies/x-1.0.0.json", {}
             file "policies/blah-1.0.0.json", {}
@@ -1295,7 +1295,7 @@ EOM
             file "environments/x.json", { "description" => "foo" }
             file "groups/x.json", { "description" => "foo" }
             file "groups/x.json", { "groups" => [ "admin" ] }
-            file "nodes/x.json", { "run_list" => [ "blah" ] }
+            file "nodes/x.json", { "normal" => { "tags" => [] }, "run_list" => [ "blah" ] }
             file "org.json", { "full_name" => "Something Else " }
             file "policies/x-1.0.0.json", { "run_list" => [ "blah" ] }
             file "policy_groups/x.json", {
diff --git a/spec/integration/knife/environment_compare_spec.rb b/spec/integration/knife/environment_compare_spec.rb
new file mode 100644
index 0000000..3259b27
--- /dev/null
+++ b/spec/integration/knife/environment_compare_spec.rb
@@ -0,0 +1,74 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife environment compare", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some environments" do
+    before do
+      cookbook "blah", "1.0.1"
+      cookbook "blah", "1.1.1"
+      cookbook "krad", "1.1.1"
+      environment "x", {
+        "cookbook_versions" => {
+          "blah" => "= 1.0.0",
+          "krad" => ">= 1.0.0",
+        },
+      }
+      environment "y", {
+        "cookbook_versions" => {
+          "blah" => "= 1.1.0",
+          "krad" => ">= 1.0.0",
+        },
+      }
+    end
+
+    # rubocop:disable Style/TrailingWhitespace
+    it "displays the cookbooks for a single environment" do
+      knife("environment compare x").should_succeed <<EOM
+      x       
+blah  = 1.0.0 
+krad  >= 1.0.0
+
+EOM
+    end
+
+    it "compares the cookbooks for two environments" do
+      knife("environment compare x y").should_succeed <<EOM
+      x         y       
+blah  = 1.0.0   = 1.1.0 
+krad  >= 1.0.0  >= 1.0.0
+
+EOM
+    end
+
+    it "compares the cookbooks for all environments" do
+      knife("environment compare --all").should_succeed <<EOM
+      x         y       
+blah  = 1.0.0   = 1.1.0 
+krad  >= 1.0.0  >= 1.0.0
+
+EOM
+    end
+    # rubocop:enable Style/TrailingWhitespace
+  end
+end
diff --git a/spec/integration/knife/environment_create_spec.rb b/spec/integration/knife/environment_create_spec.rb
new file mode 100644
index 0000000..03fd4e6
--- /dev/null
+++ b/spec/integration/knife/environment_create_spec.rb
@@ -0,0 +1,40 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife environment create", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  let(:out) { "Created bah\n" }
+
+  when_the_chef_server "is empty" do
+    it "creates a new environment" do
+      knife("environment create bah").should_succeed out
+    end
+
+    it "refuses to add an existing environment" do
+      pending "Knife environment create must not blindly overwrite an existing environment"
+      knife("environment create bah").should_succeed out
+      expect { knife("environment create bah") }.to raise_error(Net::HTTPServerException)
+    end
+
+  end
+end
diff --git a/lib/chef/dsl/powershell.rb b/spec/integration/knife/environment_delete_spec.rb
similarity index 52%
copy from lib/chef/dsl/powershell.rb
copy to spec/integration/knife/environment_delete_spec.rb
index 1a900af..0f1fe5c 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/spec/integration/knife/environment_delete_spec.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,16 +13,24 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-require "chef/util/powershell/ps_credential"
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife environment delete", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
 
-class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+  when_the_chef_server "has an environment" do
+    before do
+      environment "y", {}
     end
+
+    it "deletes an environment" do
+      knife("environment delete y", input: "y").should_succeed "Do you really want to delete y? (Y/N) Deleted y\n"
+    end
+
   end
 end
diff --git a/spec/integration/knife/environment_from_file_spec.rb b/spec/integration/knife/environment_from_file_spec.rb
new file mode 100644
index 0000000..67d4373
--- /dev/null
+++ b/spec/integration/knife/environment_from_file_spec.rb
@@ -0,0 +1,115 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife environment from file", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  # include_context "default config options"
+
+  let (:env_dir) { "#{@repository_dir}/environments" }
+
+  when_the_chef_server "is empty" do
+    when_the_repository "has some environments" do
+      before do
+
+        file "environments/cons.json", <<EOM
+{
+  "name": "cons",
+  "description": "An environment",
+  "cookbook_versions": {
+
+  },
+  "json_class": "Chef::Environment",
+  "chef_type": "environment",
+  "default_attributes": {
+    "hola": "Amigos!"
+  },
+  "override_attributes": {
+
+  }
+}
+EOM
+
+        file "environments/car.json", <<EOM
+{
+  "name": "car",
+  "description": "An environment for list nodes",
+  "cookbook_versions": {
+
+  },
+  "json_class": "Chef::Environment",
+  "chef_type": "environment",
+  "default_attributes": {
+    "hola": "Amigos!"
+  },
+  "override_attributes": {
+
+  }
+}
+EOM
+
+        file "environments/cdr.json", <<EOM
+{
+  "name": "cdr",
+  "description": "An environment for last nodes",
+  "cookbook_versions": {
+
+  },
+  "json_class": "Chef::Environment",
+  "chef_type": "environment",
+  "default_attributes": {
+    "hola": "Amigos!"
+  },
+  "override_attributes": {
+
+  }
+}
+EOM
+
+      end
+
+      it "uploads a single file" do
+        knife("environment from file #{env_dir}/cons.json").should_succeed stderr: <<EOM
+Updated Environment cons
+EOM
+      end
+
+      it "uploads many files" do
+        knife("environment from file #{env_dir}/cons.json #{env_dir}/car.json #{env_dir}/cdr.json").should_succeed stderr: <<EOM
+Updated Environment cons
+Updated Environment car
+Updated Environment cdr
+EOM
+      end
+
+      it "uploads all environments in the repository" do
+        cwd(".")
+        knife("environment from file --all")
+        knife("environment list").should_succeed <<EOM
+_default
+car
+cdr
+cons
+EOM
+      end
+
+    end
+  end
+end
diff --git a/lib/chef/dsl/powershell.rb b/spec/integration/knife/environment_list_spec.rb
similarity index 53%
copy from lib/chef/dsl/powershell.rb
copy to spec/integration/knife/environment_list_spec.rb
index 1a900af..5e74453 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/spec/integration/knife/environment_list_spec.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,16 +13,29 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-require "chef/util/powershell/ps_credential"
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife environment list", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
 
-class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+  when_the_chef_server "has some environments" do
+    before do
+      environment "b", {}
+      environment "y", {}
     end
+
+    it "lists all the environments" do
+      knife("environment list").should_succeed <<EOM
+_default
+b
+y
+EOM
+    end
+
   end
 end
diff --git a/spec/integration/knife/environment_show_spec.rb b/spec/integration/knife/environment_show_spec.rb
new file mode 100644
index 0000000..e74bf6d
--- /dev/null
+++ b/spec/integration/knife/environment_show_spec.rb
@@ -0,0 +1,56 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife environment show", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some environments" do
+    before do
+      environment "b", {
+        "default_attributes" => { "foo" => "bar" },
+      }
+    end
+
+    # rubocop:disable Style/TrailingWhitespace
+    it "shows an environment" do
+      knife("environment show b").should_succeed <<EOM
+chef_type:           environment
+cookbook_versions:
+default_attributes:
+  foo: bar
+description:         
+json_class:          Chef::Environment
+name:                b
+override_attributes:
+EOM
+    end
+    # rubocop:enable Style/TrailingWhitespace
+
+    it "shows the requested attribute of an environment" do
+      pending "KnifeSupport doesn't appear to pass this through correctly"
+      knife("environment show b -a foo").should_succeed <<EOM
+b:
+  foo: bar
+EOM
+    end
+  end
+end
diff --git a/spec/integration/knife/node_bulk_delete_spec.rb b/spec/integration/knife/node_bulk_delete_spec.rb
new file mode 100644
index 0000000..fa706cb
--- /dev/null
+++ b/spec/integration/knife/node_bulk_delete_spec.rb
@@ -0,0 +1,51 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node bulk delete", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some nodes" do
+    before do
+      node "cons", {}
+      node "car", {}
+      node "cdr", {}
+      node "cat", {}
+    end
+
+    it "deletes all matching nodes" do
+      knife("node bulk delete ^ca.*", input: "Y").should_succeed <<EOM
+The following nodes will be deleted:
+
+car  cat
+
+Are you sure you want to delete these nodes? (Y/N) Deleted node car
+Deleted node cat
+EOM
+
+      knife("node list").should_succeed <<EOM
+cdr
+cons
+EOM
+    end
+  end
+
+end
diff --git a/spec/integration/knife/node_create_spec.rb b/spec/integration/knife/node_create_spec.rb
new file mode 100644
index 0000000..93a2f9c
--- /dev/null
+++ b/spec/integration/knife/node_create_spec.rb
@@ -0,0 +1,46 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+require "openssl"
+
+describe "knife node create", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  let(:out) { "Created node[bah]\n" }
+
+  when_the_chef_server "is empty" do
+    it "creates a new node" do
+      knife("node create bah").should_succeed out
+    end
+
+    it "creates a new validator node" do
+      knife("node create bah").should_succeed out
+      knife("node show bah").should_succeed /Node Name:   bah/
+    end
+
+    it "refuses to add an existing node" do
+      pending "Knife node create must not blindly overwrite an existing node"
+      knife("node create bah").should_succeed out
+      expect { knife("node create bah") }.to raise_error(Net::HTTPServerException)
+    end
+
+  end
+end
diff --git a/spec/integration/knife/node_delete_spec.rb b/spec/integration/knife/node_delete_spec.rb
new file mode 100644
index 0000000..5d88af6
--- /dev/null
+++ b/spec/integration/knife/node_delete_spec.rb
@@ -0,0 +1,47 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node delete", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some nodes" do
+    before do
+      node "cons", {}
+      node "car", {}
+      node "cdr", {}
+      node "cat", {}
+    end
+
+    it "deletes a node" do
+      knife("node delete car", input: "Y").should_succeed <<EOM
+Do you really want to delete car? (Y/N) Deleted node[car]
+EOM
+
+      knife("node list").should_succeed <<EOM
+cat
+cdr
+cons
+EOM
+    end
+
+  end
+end
diff --git a/spec/integration/knife/node_environment_set_spec.rb b/spec/integration/knife/node_environment_set_spec.rb
new file mode 100644
index 0000000..6dadecf
--- /dev/null
+++ b/spec/integration/knife/node_environment_set_spec.rb
@@ -0,0 +1,42 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node environment set", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has a node and an environment" do
+    before do
+      node "cons", {}
+      environment "lisp", {}
+    end
+
+    it "sets an environment on a node" do
+      knife("node environment set cons lisp").should_succeed /chef_environment:.*lisp/
+      knife("node show cons -a chef_environment").should_succeed /Environment:.*lisp/
+    end
+
+    it "with no environment" do
+      knife("node environment set adam").should_fail stderr: "FATAL: You must specify a node name and an environment.\n",
+                                                     stdout: /^USAGE: knife node environment set NODE ENVIRONMENT\n/
+    end
+  end
+end
diff --git a/spec/integration/knife/node_from_file_spec.rb b/spec/integration/knife/node_from_file_spec.rb
new file mode 100644
index 0000000..3430967
--- /dev/null
+++ b/spec/integration/knife/node_from_file_spec.rb
@@ -0,0 +1,58 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node from file", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  # include_context "default config options"
+
+  let (:node_dir) { "#{@repository_dir}/nodes" }
+
+  when_the_chef_server "is empty" do
+    when_the_repository "has some nodes" do
+      before do
+
+        file "nodes/cons.json", <<EOM
+{
+  "name": "cons",
+  "chef_environment": "_default",
+  "run_list": [
+  "recipe[cons]"
+]
+,
+  "normal": {
+    "tags": [
+
+    ]
+  }
+}
+EOM
+
+      end
+
+      it "uploads a single file" do
+        knife("node from file #{node_dir}/cons.json").should_succeed stderr: <<EOM
+Updated Node cons
+EOM
+      end
+
+    end
+  end
+end
diff --git a/lib/chef/dsl/powershell.rb b/spec/integration/knife/node_list_spec.rb
similarity index 52%
copy from lib/chef/dsl/powershell.rb
copy to spec/integration/knife/node_list_spec.rb
index 1a900af..76f5861 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/spec/integration/knife/node_list_spec.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,16 +13,32 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-require "chef/util/powershell/ps_credential"
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node list", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
 
-class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+  when_the_chef_server "has some nodes" do
+    before do
+      node "cons", {}
+      node "car", {}
+      node "cdr", {}
+      node "cat", {}
     end
+
+    it "lists all cookbooks" do
+      knife("node list").should_succeed <<EOM
+car
+cat
+cdr
+cons
+EOM
+    end
+
   end
 end
diff --git a/spec/integration/knife/node_run_list_add_spec.rb b/spec/integration/knife/node_run_list_add_spec.rb
new file mode 100644
index 0000000..87d08e1
--- /dev/null
+++ b/spec/integration/knife/node_run_list_add_spec.rb
@@ -0,0 +1,53 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node run list add", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has a node with no run_list" do
+    before do
+      node "cons", {}
+    end
+
+    it "sets the run list" do
+      knife("node run list add cons recipe[foo]").should_succeed /run_list:\s*recipe\[foo\]\n/
+    end
+  end
+
+  when_the_chef_server "has a node with a run_list" do
+    before do
+      node "cons", { run_list: ["recipe[bar]"] }
+    end
+
+    it "appends to the run list" do
+      knife("node run list add cons recipe[foo]").should_succeed /run_list:\n\s*recipe\[bar\]\n\s*recipe\[foo\]\n/m
+    end
+
+    it "adds to the run list before the specified item" do
+      knife("node run list add cons -b recipe[bar] recipe[foo]").should_succeed /run_list:\n\s*recipe\[foo\]\n\s*recipe\[bar\]\n/m
+    end
+
+    it "adds to the run list after the specified item" do
+      knife("node run list add cons -a recipe[bar] recipe[foo]").should_succeed /run_list:\n\s*recipe\[bar\]\n\s*recipe\[foo\]\n/m
+    end
+  end
+end
diff --git a/lib/chef/dsl/powershell.rb b/spec/integration/knife/node_run_list_remove_spec.rb
similarity index 50%
copy from lib/chef/dsl/powershell.rb
copy to spec/integration/knife/node_run_list_remove_spec.rb
index 1a900af..e85e3ed 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/spec/integration/knife/node_run_list_remove_spec.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,16 +13,23 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-require "chef/util/powershell/ps_credential"
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node run list remove", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has a node with a run_list" do
+    before do
+      node "cons", { run_list: ["recipe[bar]", "recipe[foo]"] }
+    end
 
-class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+    it "removes the item from the run list" do
+      knife("node run list remove cons recipe[bar]").should_succeed /run_list:\s*recipe\[foo\]\n/m
     end
   end
 end
diff --git a/spec/integration/knife/node_run_list_set_spec.rb b/spec/integration/knife/node_run_list_set_spec.rb
new file mode 100644
index 0000000..ec6b08f
--- /dev/null
+++ b/spec/integration/knife/node_run_list_set_spec.rb
@@ -0,0 +1,40 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node run list set", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has a node with a run_list" do
+    before do
+      node "cons", { run_list: ["recipe[bar]", "recipe[foo]"] }
+    end
+
+    it "sets the run list" do
+      knife("node run list set cons recipe[bar]").should_succeed /run_list:\s*recipe\[bar\]\n/m
+    end
+
+    it "with no role or recipe" do
+      knife("node run list set cons").should_fail stderr: "FATAL: You must supply both a node name and a run list.\n",
+                                                  stdout: /^USAGE: knife node run_list set NODE ENTRIES \(options\)/m
+    end
+  end
+end
diff --git a/lib/chef/dsl/powershell.rb b/spec/integration/knife/node_show_spec.rb
similarity index 52%
copy from lib/chef/dsl/powershell.rb
copy to spec/integration/knife/node_show_spec.rb
index 1a900af..dd890ae 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/spec/integration/knife/node_show_spec.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,16 +13,23 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-require "chef/util/powershell/ps_credential"
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife node show", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has a node with a run_list" do
+    before do
+      node "cons", { run_list: ["recipe[bar]", "recipe[foo]"] }
+    end
 
-class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+    it "shows the node" do
+      knife("node show cons").should_succeed /Run List:\s*recipe\[bar\], recipe\[foo\]\n/m
     end
   end
 end
diff --git a/spec/integration/knife/raw_spec.rb b/spec/integration/knife/raw_spec.rb
index 9078bf0..5e0d3a3 100644
--- a/spec/integration/knife/raw_spec.rb
+++ b/spec/integration/knife/raw_spec.rb
@@ -49,7 +49,9 @@ describe "knife raw", :workstation do
 
   },
   "normal": {
+    "tags": [
 
+    ]
   },
   "default": {
 
diff --git a/spec/integration/knife/role_bulk_delete_spec.rb b/spec/integration/knife/role_bulk_delete_spec.rb
new file mode 100644
index 0000000..0e7ff94
--- /dev/null
+++ b/spec/integration/knife/role_bulk_delete_spec.rb
@@ -0,0 +1,51 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife role bulk delete", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some roles" do
+    before do
+      role "cons", {}
+      role "car", {}
+      role "cdr", {}
+      role "cat", {}
+    end
+
+    it "deletes all matching roles" do
+      knife("role bulk delete ^ca.*", input: "Y").should_succeed <<EOM
+The following roles will be deleted:
+
+car  cat
+
+Are you sure you want to delete these roles? (Y/N) Deleted role car
+Deleted role cat
+EOM
+
+      knife("role list").should_succeed <<EOM
+cdr
+cons
+EOM
+    end
+
+  end
+end
diff --git a/spec/integration/knife/role_create_spec.rb b/spec/integration/knife/role_create_spec.rb
new file mode 100644
index 0000000..941eaf5
--- /dev/null
+++ b/spec/integration/knife/role_create_spec.rb
@@ -0,0 +1,40 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife role create", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  let(:out) { "Created role[bah]\n" }
+
+  when_the_chef_server "is empty" do
+    it "creates a new role" do
+      knife("role create bah").should_succeed out
+    end
+
+    it "refuses to add an existing role" do
+      pending "Knife role create must not blindly overwrite an existing role"
+      knife("role create bah").should_succeed out
+      expect { knife("role create bah") }.to raise_error(Net::HTTPServerException)
+    end
+
+  end
+end
diff --git a/spec/integration/knife/role_delete_spec.rb b/spec/integration/knife/role_delete_spec.rb
new file mode 100644
index 0000000..9fbd375
--- /dev/null
+++ b/spec/integration/knife/role_delete_spec.rb
@@ -0,0 +1,47 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife role delete", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some roles" do
+    before do
+      role "cons", {}
+      role "car", {}
+      role "cdr", {}
+      role "cat", {}
+    end
+
+    it "deletes a role" do
+      knife("role delete car", input: "Y").should_succeed <<EOM
+Do you really want to delete car? (Y/N) Deleted role[car]
+EOM
+
+      knife("role list").should_succeed <<EOM
+cat
+cdr
+cons
+EOM
+    end
+
+  end
+end
diff --git a/spec/integration/knife/role_from_file_spec.rb b/spec/integration/knife/role_from_file_spec.rb
new file mode 100644
index 0000000..60caa3f
--- /dev/null
+++ b/spec/integration/knife/role_from_file_spec.rb
@@ -0,0 +1,95 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife role from file", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  # include_context "default config options"
+
+  let (:role_dir) { "#{@repository_dir}/roles" }
+
+  when_the_chef_server "is empty" do
+    when_the_repository "has some roles" do
+      before do
+
+        file "roles/cons.json", <<EOM
+{
+  "name": "cons",
+  "description": "An role",
+  "json_class": "Chef::role",
+  "chef_type": "role",
+  "default_attributes": {
+    "hola": "Amigos!"
+  },
+  "override_attributes": {
+
+  }
+}
+EOM
+
+        file "roles/car.json", <<EOM
+{
+  "name": "car",
+  "description": "A role for list nodes",
+  "json_class": "Chef::Role",
+  "chef_type": "role",
+  "default_attributes": {
+    "hola": "Amigos!"
+  },
+  "override_attributes": {
+
+  }
+}
+EOM
+
+        file "roles/cdr.json", <<EOM
+{
+  "name": "cdr",
+  "description": "A role for last nodes",
+  "json_class": "Chef::Role",
+  "chef_type": "role",
+  "default_attributes": {
+    "hola": "Amigos!"
+  },
+  "override_attributes": {
+
+  }
+}
+EOM
+
+      end
+
+      it "uploads a single file" do
+        knife("role from file #{role_dir}/cons.json").should_succeed stderr: <<EOM
+Updated Role cons
+EOM
+      end
+
+      it "uploads many files" do
+        knife("role from file #{role_dir}/cons.json #{role_dir}/car.json #{role_dir}/cdr.json").should_succeed stderr: <<EOM
+Updated Role cons
+Updated Role car
+Updated Role cdr
+EOM
+      end
+
+    end
+  end
+end
diff --git a/lib/chef/dsl/powershell.rb b/spec/integration/knife/role_list_spec.rb
similarity index 52%
copy from lib/chef/dsl/powershell.rb
copy to spec/integration/knife/role_list_spec.rb
index 1a900af..36dc76b 100644
--- a/lib/chef/dsl/powershell.rb
+++ b/spec/integration/knife/role_list_spec.rb
@@ -1,6 +1,5 @@
 #
-# Author:: Jay Mundrawala (<jdm at chef.io>)
-# Copyright:: Copyright 2015-2016, Chef Software, Inc.
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
 # License:: Apache License, Version 2.0
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,16 +13,32 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-#
 
-require "chef/util/powershell/ps_credential"
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife role list", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
 
-class Chef
-  module DSL
-    module Powershell
-      def ps_credential(username = "placeholder", password)
-        Chef::Util::Powershell::PSCredential.new(username, password)
-      end
+  when_the_chef_server "has some roles" do
+    before do
+      role "cons", {}
+      role "car", {}
+      role "cdr", {}
+      role "cat", {}
     end
+
+    it "lists all cookbooks" do
+      knife("role list").should_succeed <<EOM
+car
+cat
+cdr
+cons
+EOM
+    end
+
   end
 end
diff --git a/spec/integration/knife/role_show_spec.rb b/spec/integration/knife/role_show_spec.rb
new file mode 100644
index 0000000..df25724
--- /dev/null
+++ b/spec/integration/knife/role_show_spec.rb
@@ -0,0 +1,50 @@
+#
+# Copyright:: Copyright 2013-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require "support/shared/integration/integration_helper"
+require "support/shared/context/config"
+
+describe "knife role show", :workstation do
+  include IntegrationSupport
+  include KnifeSupport
+
+  include_context "default config options"
+
+  when_the_chef_server "has some roles" do
+    before do
+      role "cons", {}
+      role "car", {}
+      role "cdr", {}
+      role "cat", {}
+    end
+
+    # rubocop:disable Style/TrailingWhitespace
+    it "shows a cookbook" do
+      knife("role show cons").should_succeed <<EOM
+chef_type:           role
+default_attributes:
+description:         
+env_run_lists:
+json_class:          Chef::Role
+name:                cons
+override_attributes:
+run_list:
+EOM
+    end
+    # rubocop:enable Style/TrailingWhitespace
+
+  end
+end
diff --git a/spec/integration/knife/upload_spec.rb b/spec/integration/knife/upload_spec.rb
index 55c95b5..d372a83 100644
--- a/spec/integration/knife/upload_spec.rb
+++ b/spec/integration/knife/upload_spec.rb
@@ -96,7 +96,7 @@ EOM
           file "data_bags/x/y.json", {}
           file "environments/_default.json", { "description" => "The default Chef environment" }
           file "environments/x.json", {}
-          file "nodes/x.json", {}
+          file "nodes/x.json", { "normal" => { "tags" => [] } }
           file "roles/x.json", {}
           file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
           file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
@@ -802,7 +802,7 @@ EOM
           file "data_bags/x/y.json", {}
           file "environments/_default.json", { "description" => "The default Chef environment" }
           file "environments/x.json", {}
-          file "nodes/x.json", {}
+          file "nodes/x.json", { "normal" => { "tags" => [] } }
           file "roles/x.json", {}
           file "users/admin.json", { "admin" => true, "public_key" => ChefZero::PUBLIC_KEY }
           file "users/x.json", { "public_key" => ChefZero::PUBLIC_KEY }
@@ -1064,7 +1064,7 @@ EOM
 
       when_the_repository "has a modified, extra and missing file for the cookbook" do
         before do
-          file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0", '#modified')
+          file "cookbooks/x-1.0.0/metadata.rb", cb_metadata("x", "1.0.0", "#modified")
           file "cookbooks/x-1.0.0/y.rb", "hi"
         end
 
@@ -1313,7 +1313,7 @@ EOM
           file "invitations.json", [ "foo" ]
           file "members.json", [ "bar" ]
           file "org.json", { "full_name" => "wootles" }
-          file "nodes/x.json", {}
+          file "nodes/x.json", { "normal" => { "tags" => [] } }
           file "policies/x-1.0.0.json", {}
           file "policies/blah-1.0.0.json", {}
           file "policy_groups/x.json", { "policies" => { "x" => { "revision_id" => "1.0.0" }, "blah" => { "revision_id" => "1.0.0" } } }
diff --git a/spec/integration/recipes/lwrp_inline_resources_spec.rb b/spec/integration/recipes/lwrp_inline_resources_spec.rb
index 46fd834..65931d4 100644
--- a/spec/integration/recipes/lwrp_inline_resources_spec.rb
+++ b/spec/integration/recipes/lwrp_inline_resources_spec.rb
@@ -38,9 +38,9 @@ describe "LWRPs with inline resources" do
 
     it "this is totally a bug, but for backcompat purposes, it adds the resources to the main resource collection and does not get marked updated" do
       r = nil
-      expect_recipe {
+      expect_recipe do
         r = lwrp_inline_resources_test "hi"
-      }.to have_updated("ruby_block[run a]", :run)
+      end.to have_updated("ruby_block[run a]", :run)
       expect(r.ran_a).to eq "ran a"
     end
   end
@@ -64,11 +64,11 @@ describe "LWRPs with inline resources" do
     # https://github.com/chef/chef/issues/4334
     it "does not warn spuriously" do
       expect(Chef::Log).to_not receive(:warn).with(/is declared in both/)
-      expect_recipe {
+      expect_recipe do
         lwrp_shadowed_property_test "fnord" do
           action :fiddle
         end
-      }
+      end
     end
   end
 
@@ -104,11 +104,11 @@ describe "LWRPs with inline resources" do
 
     it "resources declared in b are executed immediately inline" do
       r = nil
-      expect_recipe {
+      expect_recipe do
         r = lwrp_inline_resources_test2 "hi" do
           action :b
         end
-      }.to have_updated("lwrp_inline_resources_test2[hi]", :b).
+      end.to have_updated("lwrp_inline_resources_test2[hi]", :b).
         and have_updated("ruby_block[run a]", :run).
         and have_updated("ruby_block[run b]", :run)
       expect(r.ran_b).to eq "ran b: ran_a value was \"ran a\""
diff --git a/spec/integration/recipes/noop_resource_spec.rb b/spec/integration/recipes/noop_resource_spec.rb
index c8ff3e6..e0cf47c 100644
--- a/spec/integration/recipes/noop_resource_spec.rb
+++ b/spec/integration/recipes/noop_resource_spec.rb
@@ -4,20 +4,20 @@ describe "Resources with a no-op provider" do
   include IntegrationSupport
 
   context "with noop provider providing foo" do
-    before(:context) {
+    before(:context) do
       class NoOpFoo < Chef::Resource
         resource_name "hi_there"
         default_action :update
       end
       Chef::Provider::Noop.provides :hi_there
-    }
+    end
 
     it "does not blow up a run with a noop'd resource" do
-      recipe = converge {
+      recipe = converge do
         hi_there "blah" do
           action :update
         end
-      }
+      end
       expect(recipe.logged_warnings).to eq ""
     end
   end
diff --git a/spec/integration/recipes/provider_choice.rb b/spec/integration/recipes/provider_choice.rb
index a9fc060..1895d93 100644
--- a/spec/integration/recipes/provider_choice.rb
+++ b/spec/integration/recipes/provider_choice.rb
@@ -26,9 +26,9 @@ describe "Recipe DSL methods" do
       end
 
       it "provider_thingy 'blah' runs the provider and warns" do
-        recipe = converge {
-          provider_thingy "blah" do; end
-        }
+        recipe = converge do
+          provider_thingy("blah") {}
+        end
         expect(recipe.logged_warnings).to match /hello from Chef::Provider::ProviderThingy/
         expect(recipe.logged_warnings).to match /you must use 'provides' to provide DSL/i
       end
diff --git a/spec/integration/recipes/recipe_dsl_spec.rb b/spec/integration/recipes/recipe_dsl_spec.rb
index f96be7e..456319c 100644
--- a/spec/integration/recipes/recipe_dsl_spec.rb
+++ b/spec/integration/recipes/recipe_dsl_spec.rb
@@ -12,7 +12,7 @@ describe "Recipe DSL methods" do
   before { Namer.current_index += 1 }
 
   context "with resource 'base_thingy' declared as BaseThingy" do
-    before(:context) {
+    before(:context) do
 
       class BaseThingy < Chef::Resource
         resource_name "base_thingy"
@@ -43,7 +43,7 @@ describe "Recipe DSL methods" do
       module RecipeDSLSpecNamespace; end
       module RecipeDSLSpecNamespace::Bar; end
 
-    }
+    end
 
     before :each do
       BaseThingy.created_resource = nil
@@ -51,26 +51,26 @@ describe "Recipe DSL methods" do
     end
 
     it "creates base_thingy when you call base_thingy in a recipe" do
-      recipe = converge {
-        base_thingy "blah" do; end
-      }
+      recipe = converge do
+        base_thingy("blah") {}
+      end
       expect(recipe.logged_warnings).to eq ""
       expect(BaseThingy.created_name).to eq "blah"
       expect(BaseThingy.created_resource).to eq BaseThingy
     end
 
     it "errors out when you call base_thingy do ... end in a recipe" do
-      expect_converge {
-        base_thingy do; end
-      }.to raise_error(ArgumentError, "You must supply a name when declaring a base_thingy resource")
+      expect_converge do
+        base_thingy { ; }
+      end.to raise_error(ArgumentError, "You must supply a name when declaring a base_thingy resource")
     end
 
     it "emits a warning when you call base_thingy 'foo', 'bar' do ... end in a recipe" do
       Chef::Config[:treat_deprecation_warnings_as_errors] = false
-      recipe = converge {
+      recipe = converge do
         base_thingy "foo", "bar" do
         end
-      }
+      end
       expect(recipe.logged_warnings).to match(/Cannot create resource base_thingy with more than one argument. All arguments except the name \("foo"\) will be ignored. This will cause an error in Chef 13. Arguments: \["foo", "bar"\]/)
       expect(BaseThingy.created_name).to eq "foo"
       expect(BaseThingy.created_resource).to eq BaseThingy
@@ -82,7 +82,7 @@ describe "Recipe DSL methods" do
       end
 
       context "with a resource 'backcompat_thingy' declared in Chef::Resource and Chef::Provider" do
-        before(:context) {
+        before(:context) do
 
           class Chef::Resource::BackcompatThingy < Chef::Resource
             default_action :create
@@ -97,30 +97,30 @@ describe "Recipe DSL methods" do
             end
           end
 
-        }
+        end
 
         it "backcompat_thingy creates a Chef::Resource::BackcompatThingy" do
-          recipe = converge {
-            backcompat_thingy "blah" do; end
-          }
+          recipe = converge do
+            backcompat_thingy("blah") {}
+          end
           expect(BaseThingy.created_resource).to eq Chef::Resource::BackcompatThingy
           expect(BaseThingy.created_provider).to eq Chef::Provider::BackcompatThingy
         end
 
         context "and another resource 'backcompat_thingy' in BackcompatThingy with 'provides'" do
-          before(:context) {
+          before(:context) do
 
             class RecipeDSLSpecNamespace::BackcompatThingy < BaseThingy
               provides :backcompat_thingy
               resource_name :backcompat_thingy
             end
 
-          }
+          end
 
           it "backcompat_thingy creates a BackcompatThingy" do
-            recipe = converge {
-              backcompat_thingy "blah" do; end
-            }
+            recipe = converge do
+              backcompat_thingy("blah") {}
+            end
             expect(recipe.logged_warnings).to match(/Class Chef::Provider::BackcompatThingy does not declare 'provides :backcompat_thingy'./)
             expect(BaseThingy.created_resource).not_to be_nil
           end
@@ -128,86 +128,86 @@ describe "Recipe DSL methods" do
       end
 
       context "with a resource named RecipeDSLSpecNamespace::Bar::BarThingy" do
-        before(:context) {
+        before(:context) do
 
           class RecipeDSLSpecNamespace::Bar::BarThingy < BaseThingy
           end
 
-        }
+        end
 
         it "bar_thingy does not work" do
-          expect_converge {
-            bar_thingy "blah" do; end
-          }.to raise_error(NoMethodError)
+          expect_converge do
+            bar_thingy("blah") {}
+          end.to raise_error(NoMethodError)
         end
       end
 
       context "with a resource named Chef::Resource::NoNameThingy with resource_name nil" do
-        before(:context) {
+        before(:context) do
 
           class Chef::Resource::NoNameThingy < BaseThingy
             resource_name nil
           end
 
-        }
+        end
 
         it "no_name_thingy does not work" do
-          expect_converge {
-            no_name_thingy "blah" do; end
-          }.to raise_error(NoMethodError)
+          expect_converge do
+            no_name_thingy("blah") {}
+          end.to raise_error(NoMethodError)
         end
       end
 
       context "with a resource named AnotherNoNameThingy with resource_name :another_thingy_name" do
-        before(:context) {
+        before(:context) do
 
           class AnotherNoNameThingy < BaseThingy
             resource_name :another_thingy_name
           end
 
-        }
+        end
 
         it "another_no_name_thingy does not work" do
-          expect_converge {
-            another_no_name_thingy "blah" do; end
-          }.to raise_error(NoMethodError)
+          expect_converge do
+            another_no_name_thingy("blah") {}
+          end.to raise_error(NoMethodError)
         end
 
         it "another_thingy_name works" do
-          recipe = converge {
-            another_thingy_name "blah" do; end
-          }
+          recipe = converge do
+            another_thingy_name("blah") {}
+          end
           expect(recipe.logged_warnings).to eq ""
           expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy)
         end
       end
 
       context "with a resource named AnotherNoNameThingy2 with resource_name :another_thingy_name2; resource_name :another_thingy_name3" do
-        before(:context) {
+        before(:context) do
 
           class AnotherNoNameThingy2 < BaseThingy
             resource_name :another_thingy_name2
             resource_name :another_thingy_name3
           end
 
-        }
+        end
 
         it "another_no_name_thingy does not work" do
-          expect_converge {
-            another_no_name_thingy2 "blah" do; end
-          }.to raise_error(NoMethodError)
+          expect_converge do
+            another_no_name_thingy2("blah") {}
+          end.to raise_error(NoMethodError)
         end
 
         it "another_thingy_name2 does not work" do
-          expect_converge {
-            another_thingy_name2 "blah" do; end
-          }.to raise_error(NoMethodError)
+          expect_converge do
+            another_thingy_name2("blah") {}
+          end.to raise_error(NoMethodError)
         end
 
         it "yet_another_thingy_name3 works" do
-          recipe = converge {
-            another_thingy_name3 "blah" do; end
-          }
+          recipe = converge do
+            another_thingy_name3("blah") {}
+          end
           expect(recipe.logged_warnings).to eq ""
           expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy2)
         end
@@ -215,36 +215,36 @@ describe "Recipe DSL methods" do
 
       context "provides overriding resource_name" do
         context "with a resource named AnotherNoNameThingy3 with provides :another_no_name_thingy3, os: 'blarghle'" do
-          before(:context) {
+          before(:context) do
 
             class AnotherNoNameThingy3 < BaseThingy
               resource_name :another_no_name_thingy_3
               provides :another_no_name_thingy3, os: "blarghle"
             end
 
-          }
+          end
 
           it "and os = linux, another_no_name_thingy3 does not work" do
-            expect_converge {
+            expect_converge do
               # TODO this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "linux"
-              another_no_name_thingy3 "blah" do; end
-            }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+              another_no_name_thingy3("blah") {}
+            end.to raise_error(Chef::Exceptions::NoSuchResourceType)
           end
 
           it "and os = blarghle, another_no_name_thingy3 works" do
-            recipe = converge {
+            recipe = converge do
               # TODO this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "blarghle"
-              another_no_name_thingy3 "blah" do; end
-            }
+              another_no_name_thingy3("blah") {}
+            end
             expect(recipe.logged_warnings).to eq ""
             expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy3)
           end
         end
 
         context "with a resource named AnotherNoNameThingy4 with two provides" do
-          before(:context) {
+          before(:context) do
 
             class AnotherNoNameThingy4 < BaseThingy
               resource_name :another_no_name_thingy_4
@@ -252,183 +252,183 @@ describe "Recipe DSL methods" do
               provides :another_no_name_thingy4, platform_family: "foo"
             end
 
-          }
+          end
 
           it "and os = linux, another_no_name_thingy4 does not work" do
-            expect_converge {
+            expect_converge do
               # TODO this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "linux"
-              another_no_name_thingy4 "blah" do; end
-            }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+              another_no_name_thingy4("blah") {}
+            end.to raise_error(Chef::Exceptions::NoSuchResourceType)
           end
 
           it "and os = blarghle, another_no_name_thingy4 works" do
-            recipe = converge {
+            recipe = converge do
               # TODO this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "blarghle"
-              another_no_name_thingy4 "blah" do; end
-            }
+              another_no_name_thingy4("blah") {}
+            end
             expect(recipe.logged_warnings).to eq ""
             expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy4)
           end
 
           it "and platform_family = foo, another_no_name_thingy4 works" do
-            recipe = converge {
+            recipe = converge do
               # TODO this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:platform_family] = "foo"
-              another_no_name_thingy4 "blah" do; end
-            }
+              another_no_name_thingy4("blah") {}
+            end
             expect(recipe.logged_warnings).to eq ""
             expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy4)
           end
         end
 
         context "with a resource named AnotherNoNameThingy5, a different resource_name, and a provides with the original resource_name" do
-          before(:context) {
+          before(:context) do
 
             class AnotherNoNameThingy5 < BaseThingy
               resource_name :another_thingy_name_for_another_no_name_thingy5
               provides :another_no_name_thingy5, os: "blarghle"
             end
 
-          }
+          end
 
           it "and os = linux, another_no_name_thingy5 does not work" do
-            expect_converge {
+            expect_converge do
               # this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "linux"
-              another_no_name_thingy5 "blah" do; end
-            }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+              another_no_name_thingy5("blah") {}
+            end.to raise_error(Chef::Exceptions::NoSuchResourceType)
           end
 
           it "and os = blarghle, another_no_name_thingy5 works" do
-            recipe = converge {
+            recipe = converge do
               # this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "blarghle"
-              another_no_name_thingy5 "blah" do; end
-            }
+              another_no_name_thingy5("blah") {}
+            end
             expect(recipe.logged_warnings).to eq ""
             expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy5)
           end
 
           it "the new resource name can be used in a recipe" do
-            recipe = converge {
-              another_thingy_name_for_another_no_name_thingy5 "blah" do; end
-            }
+            recipe = converge do
+              another_thingy_name_for_another_no_name_thingy5("blah") {}
+            end
             expect(recipe.logged_warnings).to eq ""
             expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy5)
           end
         end
 
         context "with a resource named AnotherNoNameThingy6, a provides with the original resource name, and a different resource_name" do
-          before(:context) {
+          before(:context) do
 
             class AnotherNoNameThingy6 < BaseThingy
               provides :another_no_name_thingy6, os: "blarghle"
               resource_name :another_thingy_name_for_another_no_name_thingy6
             end
 
-          }
+          end
 
           it "and os = linux, another_no_name_thingy6 does not work" do
-            expect_converge {
+            expect_converge do
               # this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "linux"
-              another_no_name_thingy6 "blah" do; end
-            }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+              another_no_name_thingy6("blah") {}
+            end.to raise_error(Chef::Exceptions::NoSuchResourceType)
           end
 
           it "and os = blarghle, another_no_name_thingy6 works" do
-            recipe = converge {
+            recipe = converge do
               # this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "blarghle"
-              another_no_name_thingy6 "blah" do; end
-            }
+              another_no_name_thingy6("blah") {}
+            end
             expect(recipe.logged_warnings).to eq ""
             expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy6)
           end
 
           it "the new resource name can be used in a recipe" do
-            recipe = converge {
-              another_thingy_name_for_another_no_name_thingy6 "blah" do; end
-            }
+            recipe = converge do
+              another_thingy_name_for_another_no_name_thingy6("blah") {}
+            end
             expect(recipe.logged_warnings).to eq ""
             expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy6)
           end
         end
 
         context "with a resource named AnotherNoNameThingy7, a new resource_name, and provides with that new resource name" do
-          before(:context) {
+          before(:context) do
 
             class AnotherNoNameThingy7 < BaseThingy
               resource_name :another_thingy_name_for_another_no_name_thingy7
               provides :another_thingy_name_for_another_no_name_thingy7, os: "blarghle"
             end
 
-          }
+          end
 
           it "and os = linux, another_thingy_name_for_another_no_name_thingy7 does not work" do
-            expect_converge {
+            expect_converge do
               # this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "linux"
-              another_thingy_name_for_another_no_name_thingy7 "blah" do; end
-            }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+              another_thingy_name_for_another_no_name_thingy7("blah") {}
+            end.to raise_error(Chef::Exceptions::NoSuchResourceType)
           end
 
           it "and os = blarghle, another_thingy_name_for_another_no_name_thingy7 works" do
-            recipe = converge {
+            recipe = converge do
               # this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "blarghle"
-              another_thingy_name_for_another_no_name_thingy7 "blah" do; end
-            }
+              another_thingy_name_for_another_no_name_thingy7("blah") {}
+            end
             expect(recipe.logged_warnings).to eq ""
             expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy7)
           end
 
           it "the old resource name does not work" do
-            expect_converge {
+            expect_converge do
               # this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "linux"
-              another_no_name_thingy_7 "blah" do; end
-            }.to raise_error(NoMethodError)
+              another_no_name_thingy_7("blah") {}
+            end.to raise_error(NoMethodError)
           end
         end
 
         # opposite order from the previous test (provides, then resource_name)
         context "with a resource named AnotherNoNameThingy8, a provides with a new resource name, and resource_name with that new resource name" do
-          before(:context) {
+          before(:context) do
 
             class AnotherNoNameThingy8 < BaseThingy
               provides :another_thingy_name_for_another_no_name_thingy8, os: "blarghle"
               resource_name :another_thingy_name_for_another_no_name_thingy8
             end
 
-          }
+          end
 
           it "and os = linux, another_thingy_name_for_another_no_name_thingy8 does not work" do
-            expect_converge {
+            expect_converge do
               # this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "linux"
-              another_thingy_name_for_another_no_name_thingy8 "blah" do; end
-            }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+              another_thingy_name_for_another_no_name_thingy8("blah") {}
+            end.to raise_error(Chef::Exceptions::NoSuchResourceType)
           end
 
           it "and os = blarghle, another_thingy_name_for_another_no_name_thingy8 works" do
-            recipe = converge {
+            recipe = converge do
               # this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "blarghle"
-              another_thingy_name_for_another_no_name_thingy8 "blah" do; end
-            }
+              another_thingy_name_for_another_no_name_thingy8("blah") {}
+            end
             expect(recipe.logged_warnings).to eq ""
             expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy8)
           end
 
           it "the old resource name does not work" do
-            expect_converge {
+            expect_converge do
               # this is an ugly way to test, make Cheffish expose node attrs
               run_context.node.automatic[:os] = "linux"
-              another_thingy_name8 "blah" do; end
-            }.to raise_error(NoMethodError)
+              another_thingy_name8("blah") {}
+            end.to raise_error(NoMethodError)
           end
         end
       end
@@ -436,64 +436,64 @@ describe "Recipe DSL methods" do
 
     context "provides" do
       context "when MySupplier provides :hemlock" do
-        before(:context) {
+        before(:context) do
 
           class RecipeDSLSpecNamespace::MySupplier < BaseThingy
             resource_name :hemlock
           end
 
-        }
+        end
 
         it "my_supplier does not work in a recipe" do
-          expect_converge {
-            my_supplier "blah" do; end
-          }.to raise_error(NoMethodError)
+          expect_converge do
+            my_supplier("blah") {}
+          end.to raise_error(NoMethodError)
         end
 
         it "hemlock works in a recipe" do
-          expect_recipe {
-            hemlock "blah" do; end
-          }.to emit_no_warnings_or_errors
+          expect_recipe do
+            hemlock("blah") {}
+          end.to emit_no_warnings_or_errors
           expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::MySupplier
         end
       end
 
       context "when Thingy3 has resource_name :thingy3" do
-        before(:context) {
+        before(:context) do
 
           class RecipeDSLSpecNamespace::Thingy3 < BaseThingy
             resource_name :thingy3
           end
 
-        }
+        end
 
         it "thingy3 works in a recipe" do
-          expect_recipe {
-            thingy3 "blah" do; end
-          }.to emit_no_warnings_or_errors
+          expect_recipe do
+            thingy3("blah") {}
+          end.to emit_no_warnings_or_errors
           expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
         end
 
         context "and Thingy4 has resource_name :thingy3" do
-          before(:context) {
+          before(:context) do
 
             class RecipeDSLSpecNamespace::Thingy4 < BaseThingy
               resource_name :thingy3
             end
 
-          }
+          end
 
           it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do
-            recipe = converge {
-              thingy3 "blah" do; end
-            }
+            recipe = converge do
+              thingy3("blah") {}
+            end
             expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
           end
 
           it "thingy4 does not work in a recipe" do
-            expect_converge {
-              thingy4 "blah" do; end
-            }.to raise_error(NoMethodError)
+            expect_converge do
+              thingy4("blah") {}
+            end.to raise_error(NoMethodError)
           end
 
           it "resource_matching_short_name returns Thingy4" do
@@ -503,7 +503,7 @@ describe "Recipe DSL methods" do
       end
 
       context "when Thingy5 has resource_name :thingy5 and provides :thingy5reverse, :thingy5_2 and :thingy5_2reverse" do
-        before(:context) {
+        before(:context) do
 
           class RecipeDSLSpecNamespace::Thingy5 < BaseThingy
             resource_name :thingy5
@@ -512,36 +512,36 @@ describe "Recipe DSL methods" do
             provides :thingy5_2reverse
           end
 
-        }
+        end
 
         it "thingy5 works in a recipe" do
-          expect_recipe {
-            thingy5 "blah" do; end
-          }.to emit_no_warnings_or_errors
+          expect_recipe do
+            thingy5("blah") {}
+          end.to emit_no_warnings_or_errors
           expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5
         end
 
         context "and Thingy6 provides :thingy5" do
-          before(:context) {
+          before(:context) do
 
             class RecipeDSLSpecNamespace::Thingy6 < BaseThingy
               resource_name :thingy6
               provides :thingy5
             end
 
-          }
+          end
 
           it "thingy6 works in a recipe and yields Thingy6" do
-            recipe = converge {
-              thingy6 "blah" do; end
-            }
+            recipe = converge do
+              thingy6("blah") {}
+            end
             expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy6
           end
 
           it "thingy5 works in a recipe and yields Foo::Thingy5 (the alphabetical one)" do
-            recipe = converge {
-              thingy5 "blah" do; end
-            }
+            recipe = converge do
+              thingy5("blah") {}
+            end
             expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5
           end
 
@@ -550,24 +550,24 @@ describe "Recipe DSL methods" do
           end
 
           context "and AThingy5 provides :thingy5reverse" do
-            before(:context) {
+            before(:context) do
 
               class RecipeDSLSpecNamespace::AThingy5 < BaseThingy
                 resource_name :thingy5reverse
               end
 
-            }
+            end
 
             it "thingy5reverse works in a recipe and yields AThingy5 (the alphabetical one)" do
-              recipe = converge {
-                thingy5reverse "blah" do; end
-              }
+              recipe = converge do
+                thingy5reverse("blah") {}
+              end
               expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::AThingy5
             end
           end
 
           context "and ZRecipeDSLSpecNamespace::Thingy5 provides :thingy5_2" do
-            before(:context) {
+            before(:context) do
 
               module ZRecipeDSLSpecNamespace
                 class Thingy5 < BaseThingy
@@ -575,18 +575,18 @@ describe "Recipe DSL methods" do
                 end
               end
 
-            }
+            end
 
             it "thingy5_2 works in a recipe and yields the RecipeDSLSpaceNamespace one (the alphabetical one)" do
-              recipe = converge {
-                thingy5_2 "blah" do; end
-              }
+              recipe = converge do
+                thingy5_2("blah") {}
+              end
               expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5
             end
           end
 
           context "and ARecipeDSLSpecNamespace::Thingy5 provides :thingy5_2" do
-            before(:context) {
+            before(:context) do
 
               module ARecipeDSLSpecNamespace
                 class Thingy5 < BaseThingy
@@ -594,53 +594,53 @@ describe "Recipe DSL methods" do
                 end
               end
 
-            }
+            end
 
             it "thingy5_2reverse works in a recipe and yields the ARecipeDSLSpaceNamespace one (the alphabetical one)" do
-              recipe = converge {
-                thingy5_2reverse "blah" do; end
-              }
+              recipe = converge do
+                thingy5_2reverse("blah") {}
+              end
               expect(BaseThingy.created_resource).to eq ARecipeDSLSpecNamespace::Thingy5
             end
           end
         end
 
         context "when Thingy3 has resource_name :thingy3" do
-          before(:context) {
+          before(:context) do
 
             class RecipeDSLSpecNamespace::Thingy3 < BaseThingy
               resource_name :thingy3
             end
 
-          }
+          end
 
           it "thingy3 works in a recipe" do
-            expect_recipe {
-              thingy3 "blah" do; end
-            }.to emit_no_warnings_or_errors
+            expect_recipe do
+              thingy3("blah") {}
+            end.to emit_no_warnings_or_errors
             expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
           end
 
           context "and Thingy4 has resource_name :thingy3" do
-            before(:context) {
+            before(:context) do
 
               class RecipeDSLSpecNamespace::Thingy4 < BaseThingy
                 resource_name :thingy3
               end
 
-            }
+            end
 
             it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do
-              recipe = converge {
-                thingy3 "blah" do; end
-              }
+              recipe = converge do
+                thingy3("blah") {}
+              end
               expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
             end
 
             it "thingy4 does not work in a recipe" do
-              expect_converge {
-                thingy4 "blah" do; end
-              }.to raise_error(NoMethodError)
+              expect_converge do
+                thingy4("blah") {}
+              end.to raise_error(NoMethodError)
             end
 
             it "resource_matching_short_name returns Thingy4" do
@@ -649,25 +649,25 @@ describe "Recipe DSL methods" do
           end
 
           context "and Thingy4 has resource_name :thingy3" do
-            before(:context) {
+            before(:context) do
 
               class RecipeDSLSpecNamespace::Thingy4 < BaseThingy
                 resource_name :thingy3
               end
 
-            }
+            end
 
             it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do
-              recipe = converge {
-                thingy3 "blah" do; end
-              }
+              recipe = converge do
+                thingy3("blah") {}
+              end
               expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
             end
 
             it "thingy4 does not work in a recipe" do
-              expect_converge {
-                thingy4 "blah" do; end
-              }.to raise_error(NoMethodError)
+              expect_converge do
+                thingy4("blah") {}
+              end.to raise_error(NoMethodError)
             end
 
             it "resource_matching_short_name returns Thingy4" do
@@ -679,35 +679,35 @@ describe "Recipe DSL methods" do
       end
 
       context "when Thingy7 provides :thingy8" do
-        before(:context) {
+        before(:context) do
 
           class RecipeDSLSpecNamespace::Thingy7 < BaseThingy
             resource_name :thingy7
             provides :thingy8
           end
 
-        }
+        end
 
         context "and Thingy8 has resource_name :thingy8" do
-          before(:context) {
+          before(:context) do
 
             class RecipeDSLSpecNamespace::Thingy8 < BaseThingy
               resource_name :thingy8
             end
 
-          }
+          end
 
           it "thingy7 works in a recipe and yields Thingy7" do
-            recipe = converge {
-              thingy7 "blah" do; end
-            }
+            recipe = converge do
+              thingy7("blah") {}
+            end
             expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy7
           end
 
           it "thingy8 works in a recipe and yields Thingy7 (alphabetical)" do
-            recipe = converge {
-              thingy8 "blah" do; end
-            }
+            recipe = converge do
+              thingy8("blah") {}
+            end
             expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy7
           end
 
@@ -718,7 +718,7 @@ describe "Recipe DSL methods" do
       end
 
       context "when Thingy12 provides :thingy12, :twizzle and :twizzle2" do
-        before(:context) {
+        before(:context) do
 
           class RecipeDSLSpecNamespace::Thingy12 < BaseThingy
             resource_name :thingy12
@@ -726,32 +726,32 @@ describe "Recipe DSL methods" do
             provides :twizzle2
           end
 
-        }
+        end
 
         it "thingy12 works in a recipe and yields Thingy12" do
-          expect_recipe {
-            thingy12 "blah" do; end
-          }.to emit_no_warnings_or_errors
+          expect_recipe do
+            thingy12("blah") {}
+          end.to emit_no_warnings_or_errors
           expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12
         end
 
         it "twizzle works in a recipe and yields Thingy12" do
-          expect_recipe {
-            twizzle "blah" do; end
-          }.to emit_no_warnings_or_errors
+          expect_recipe do
+            twizzle("blah") {}
+          end.to emit_no_warnings_or_errors
           expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12
         end
 
         it "twizzle2 works in a recipe and yields Thingy12" do
-          expect_recipe {
-            twizzle2 "blah" do; end
-          }.to emit_no_warnings_or_errors
+          expect_recipe do
+            twizzle2("blah") {}
+          end.to emit_no_warnings_or_errors
           expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12
         end
       end
 
       context "with platform-specific resources 'my_super_thingy_foo' and 'my_super_thingy_bar'" do
-        before(:context) {
+        before(:context) do
           class MySuperThingyFoo < BaseThingy
             resource_name :my_super_thingy_foo
             provides :my_super_thingy, platform: "foo"
@@ -761,14 +761,14 @@ describe "Recipe DSL methods" do
             resource_name :my_super_thingy_bar
             provides :my_super_thingy, platform: "bar"
           end
-        }
+        end
 
         it "A run with platform 'foo' uses MySuperThingyFoo" do
           r = Cheffish::ChefRun.new(chef_config)
           r.client.run_context.node.automatic["platform"] = "foo"
-          r.compile_recipe {
-            my_super_thingy "blah" do; end
-          }
+          r.compile_recipe do
+            my_super_thingy("blah") {}
+          end
           r.converge
           expect(r).to emit_no_warnings_or_errors
           expect(BaseThingy.created_resource).to eq MySuperThingyFoo
@@ -777,9 +777,9 @@ describe "Recipe DSL methods" do
         it "A run with platform 'bar' uses MySuperThingyBar" do
           r = Cheffish::ChefRun.new(chef_config)
           r.client.run_context.node.automatic["platform"] = "bar"
-          r.compile_recipe {
-            my_super_thingy "blah" do; end
-          }
+          r.compile_recipe do
+            my_super_thingy("blah") {}
+          end
           r.converge
           expect(r).to emit_no_warnings_or_errors
           expect(BaseThingy.created_resource).to eq MySuperThingyBar
@@ -788,20 +788,20 @@ describe "Recipe DSL methods" do
         it "A run with platform 'x' reports that my_super_thingy is not supported" do
           r = Cheffish::ChefRun.new(chef_config)
           r.client.run_context.node.automatic["platform"] = "x"
-          expect {
-            r.compile_recipe {
-              my_super_thingy "blah" do; end
-            }
-          }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+          expect do
+            r.compile_recipe do
+              my_super_thingy("blah") {}
+            end
+          end.to raise_error(Chef::Exceptions::NoSuchResourceType)
         end
       end
 
       context "when Thingy10 provides :thingy10" do
-        before(:context) {
+        before(:context) do
           class RecipeDSLSpecNamespace::Thingy10 < BaseThingy
             resource_name :thingy10
           end
-        }
+        end
 
         it "declaring a resource providing the same :thingy10 with override: true does not produce a warning" do
           expect(Chef::Log).not_to receive(:warn)
@@ -812,11 +812,11 @@ describe "Recipe DSL methods" do
       end
 
       context "when Thingy11 provides :thingy11" do
-        before(:context) {
+        before(:context) do
           class RecipeDSLSpecNamespace::Thingy11 < BaseThingy
             resource_name :thingy10
           end
-        }
+        end
 
         it "declaring a resource providing the same :thingy11 with os: 'linux' does not produce a warning" do
           expect(Chef::Log).not_to receive(:warn)
@@ -829,7 +829,7 @@ describe "Recipe DSL methods" do
 
     context "with a resource named 'B' with resource name :two_classes_one_dsl" do
       let(:two_classes_one_dsl) { :"two_classes_one_dsl#{Namer.current_index}" }
-      let(:resource_class) {
+      let(:resource_class) do
         result = Class.new(BaseThingy) do
           def self.name
             "B"
@@ -841,11 +841,11 @@ describe "Recipe DSL methods" do
         end
         result.resource_name two_classes_one_dsl
         result
-      }
+      end
       before { resource_class } # pull on it so it gets defined before the recipe runs
 
       context "and another resource named 'A' with resource_name :two_classes_one_dsl" do
-        let(:resource_class_a) {
+        let(:resource_class_a) do
           result = Class.new(BaseThingy) do
             def self.name
               "A"
@@ -857,14 +857,14 @@ describe "Recipe DSL methods" do
           end
           result.resource_name two_classes_one_dsl
           result
-        }
+        end
         before { resource_class_a } # pull on it so it gets defined before the recipe runs
 
         it "two_classes_one_dsl resolves to A (alphabetically earliest)" do
           two_classes_one_dsl = self.two_classes_one_dsl
-          recipe = converge {
+          recipe = converge do
             instance_eval("#{two_classes_one_dsl} 'blah'")
-          }
+          end
           expect(recipe.logged_warnings).to eq ""
           expect(BaseThingy.created_resource).to eq resource_class_a
         end
@@ -875,7 +875,7 @@ describe "Recipe DSL methods" do
       end
 
       context "and another resource named 'Z' with resource_name :two_classes_one_dsl" do
-        let(:resource_class_z) {
+        let(:resource_class_z) do
           result = Class.new(BaseThingy) do
             def self.name
               "Z"
@@ -887,14 +887,14 @@ describe "Recipe DSL methods" do
           end
           result.resource_name two_classes_one_dsl
           result
-        }
+        end
         before { resource_class_z } # pull on it so it gets defined before the recipe runs
 
         it "two_classes_one_dsl resolves to B (alphabetically earliest)" do
           two_classes_one_dsl = self.two_classes_one_dsl
-          recipe = converge {
+          recipe = converge do
             instance_eval("#{two_classes_one_dsl} 'blah'")
-          }
+          end
           expect(recipe.logged_warnings).to eq ""
           expect(BaseThingy.created_resource).to eq resource_class
         end
@@ -910,9 +910,9 @@ describe "Recipe DSL methods" do
 
           it "two_classes_one_dsl resolves to Z (respects the priority array)" do
             two_classes_one_dsl = self.two_classes_one_dsl
-            recipe = converge {
+            recipe = converge do
               instance_eval("#{two_classes_one_dsl} 'blah'")
-            }
+            end
             expect(recipe.logged_warnings).to eq ""
             expect(BaseThingy.created_resource).to eq resource_class_z
           end
@@ -928,9 +928,9 @@ describe "Recipe DSL methods" do
 
             it "two_classes_one_dsl resolves to B (picks the next thing in the priority array)" do
               two_classes_one_dsl = self.two_classes_one_dsl
-              recipe = converge {
+              recipe = converge do
                 instance_eval("#{two_classes_one_dsl} 'blah'")
-              }
+              end
               expect(recipe.logged_warnings).to eq ""
               expect(BaseThingy.created_resource).to eq resource_class
             end
@@ -949,9 +949,9 @@ describe "Recipe DSL methods" do
 
           it "two_classes_one_dsl resolves to Z (respects the most recent priority array)" do
             two_classes_one_dsl = self.two_classes_one_dsl
-            recipe = converge {
+            recipe = converge do
               instance_eval("#{two_classes_one_dsl} 'blah'")
-            }
+            end
             expect(recipe.logged_warnings).to eq ""
             expect(BaseThingy.created_resource).to eq resource_class_z
           end
@@ -967,9 +967,9 @@ describe "Recipe DSL methods" do
 
             it "two_classes_one_dsl resolves to B (picks the first match from the other priority array)" do
               two_classes_one_dsl = self.two_classes_one_dsl
-              recipe = converge {
+              recipe = converge do
                 instance_eval("#{two_classes_one_dsl} 'blah'")
-              }
+              end
               expect(recipe.logged_warnings).to eq ""
               expect(BaseThingy.created_resource).to eq resource_class
             end
@@ -992,9 +992,9 @@ describe "Recipe DSL methods" do
 
             it "two_classes_one_dsl resolves to B (picks the first match outside the priority array)" do
               two_classes_one_dsl = self.two_classes_one_dsl
-              recipe = converge {
+              recipe = converge do
                 instance_eval("#{two_classes_one_dsl} 'blah'")
-              }
+              end
               expect(recipe.logged_warnings).to eq ""
               expect(BaseThingy.created_resource).to eq resource_class
             end
@@ -1012,7 +1012,7 @@ describe "Recipe DSL methods" do
           resource_class.send(:define_method, :provider) { nil }
         end
 
-        let(:provider_class) {
+        let(:provider_class) do
           result = Class.new(BaseThingy::Provider) do
             def self.name
               "B"
@@ -1024,11 +1024,11 @@ describe "Recipe DSL methods" do
           end
           result.provides two_classes_one_dsl
           result
-        }
+        end
         before { provider_class } # pull on it so it gets defined before the recipe runs
 
         context "and another provider named 'A'" do
-          let(:provider_class_a) {
+          let(:provider_class_a) do
             result = Class.new(BaseThingy::Provider) do
               def self.name
                 "A"
@@ -1039,15 +1039,15 @@ describe "Recipe DSL methods" do
               def self.inspect; name.inspect; end
             end
             result
-          }
+          end
           context "which provides :two_classes_one_dsl" do
             before { provider_class_a.provides two_classes_one_dsl }
 
             it "two_classes_one_dsl resolves to A (alphabetically earliest)" do
               two_classes_one_dsl = self.two_classes_one_dsl
-              recipe = converge {
+              recipe = converge do
                 instance_eval("#{two_classes_one_dsl} 'blah'")
-              }
+              end
               expect(recipe.logged_warnings).to eq ""
               expect(BaseThingy.created_provider).to eq provider_class_a
             end
@@ -1057,9 +1057,9 @@ describe "Recipe DSL methods" do
 
             it "two_classes_one_dsl resolves to B (since A declined)" do
               two_classes_one_dsl = self.two_classes_one_dsl
-              recipe = converge {
+              recipe = converge do
                 instance_eval("#{two_classes_one_dsl} 'blah'")
-              }
+              end
               expect(recipe.logged_warnings).to eq ""
               expect(BaseThingy.created_provider).to eq provider_class
             end
@@ -1067,7 +1067,7 @@ describe "Recipe DSL methods" do
         end
 
         context "and another provider named 'Z'" do
-          let(:provider_class_z) {
+          let(:provider_class_z) do
             result = Class.new(BaseThingy::Provider) do
               def self.name
                 "Z"
@@ -1078,7 +1078,7 @@ describe "Recipe DSL methods" do
               def self.inspect; name.inspect; end
             end
             result
-          }
+          end
           before { provider_class_z } # pull on it so it gets defined before the recipe runs
 
           context "which provides :two_classes_one_dsl" do
@@ -1086,9 +1086,9 @@ describe "Recipe DSL methods" do
 
             it "two_classes_one_dsl resolves to B (alphabetically earliest)" do
               two_classes_one_dsl = self.two_classes_one_dsl
-              recipe = converge {
+              recipe = converge do
                 instance_eval("#{two_classes_one_dsl} 'blah'")
-              }
+              end
               expect(recipe.logged_warnings).to eq ""
               expect(BaseThingy.created_provider).to eq provider_class
             end
@@ -1098,9 +1098,9 @@ describe "Recipe DSL methods" do
 
               it "two_classes_one_dsl resolves to Z (respects the priority map)" do
                 two_classes_one_dsl = self.two_classes_one_dsl
-                recipe = converge {
+                recipe = converge do
                   instance_eval("#{two_classes_one_dsl} 'blah'")
-                }
+                end
                 expect(recipe.logged_warnings).to eq ""
                 expect(BaseThingy.created_provider).to eq provider_class_z
               end
@@ -1115,9 +1115,9 @@ describe "Recipe DSL methods" do
 
               it "two_classes_one_dsl resolves to B (the next one in the priority map)" do
                 two_classes_one_dsl = self.two_classes_one_dsl
-                recipe = converge {
+                recipe = converge do
                   instance_eval("#{two_classes_one_dsl} 'blah'")
-                }
+                end
                 expect(recipe.logged_warnings).to eq ""
                 expect(BaseThingy.created_provider).to eq provider_class
               end
@@ -1129,9 +1129,9 @@ describe "Recipe DSL methods" do
 
               it "two_classes_one_dsl resolves to B (the one in the next priority map)" do
                 two_classes_one_dsl = self.two_classes_one_dsl
-                recipe = converge {
+                recipe = converge do
                   instance_eval("#{two_classes_one_dsl} 'blah'")
-                }
+                end
                 expect(recipe.logged_warnings).to eq ""
                 expect(BaseThingy.created_provider).to eq provider_class
               end
@@ -1141,7 +1141,7 @@ describe "Recipe DSL methods" do
       end
 
       context "and another resource Blarghle with provides :two_classes_one_dsl, os: 'blarghle'" do
-        let(:resource_class_blarghle) {
+        let(:resource_class_blarghle) do
           result = Class.new(BaseThingy) do
             def self.name
               "Blarghle"
@@ -1154,27 +1154,27 @@ describe "Recipe DSL methods" do
           result.resource_name two_classes_one_dsl
           result.provides two_classes_one_dsl, os: "blarghle"
           result
-        }
+        end
         before { resource_class_blarghle } # pull on it so it gets defined before the recipe runs
 
         it "on os = blarghle, two_classes_one_dsl resolves to Blarghle" do
           two_classes_one_dsl = self.two_classes_one_dsl
-          recipe = converge {
+          recipe = converge do
             # this is an ugly way to test, make Cheffish expose node attrs
             run_context.node.automatic[:os] = "blarghle"
             instance_eval("#{two_classes_one_dsl} 'blah' do; end")
-          }
+          end
           expect(recipe.logged_warnings).to eq ""
           expect(BaseThingy.created_resource).to eq resource_class_blarghle
         end
 
         it "on os = linux, two_classes_one_dsl resolves to B" do
           two_classes_one_dsl = self.two_classes_one_dsl
-          recipe = converge {
+          recipe = converge do
             # this is an ugly way to test, make Cheffish expose node attrs
             run_context.node.automatic[:os] = "linux"
             instance_eval("#{two_classes_one_dsl} 'blah' do; end")
-          }
+          end
           expect(recipe.logged_warnings).to eq ""
           expect(BaseThingy.created_resource).to eq resource_class
         end
@@ -1182,37 +1182,38 @@ describe "Recipe DSL methods" do
     end
 
     context "with a resource MyResource" do
-      let(:resource_class) { Class.new(BaseThingy) do
-        def self.called_provides
-          @called_provides
-        end
+      let(:resource_class) do
+        Class.new(BaseThingy) do
+          def self.called_provides
+            @called_provides
+          end
 
-        def to_s
-          "MyResource"
-        end
-      end }
+          def to_s
+            "MyResource"
+          end
+        end end
       let(:my_resource) { :"my_resource#{Namer.current_index}" }
       let(:blarghle_blarghle_little_star) { :"blarghle_blarghle_little_star#{Namer.current_index}" }
 
       context "with resource_name :my_resource" do
-        before {
+        before do
           resource_class.resource_name my_resource
-        }
+        end
 
         context "with provides? returning true to my_resource" do
-          before {
+          before do
             my_resource = self.my_resource
             resource_class.define_singleton_method(:provides?) do |node, resource_name|
               @called_provides = true
               resource_name == my_resource
             end
-          }
+          end
 
           it "my_resource returns the resource and calls provides?, but does not emit a warning" do
             dsl_name = self.my_resource
-            recipe = converge {
+            recipe = converge do
               instance_eval("#{dsl_name} 'foo'")
-            }
+            end
             expect(recipe.logged_warnings).to eq ""
             expect(BaseThingy.created_resource).to eq resource_class
             expect(resource_class.called_provides).to be_truthy
@@ -1230,18 +1231,18 @@ describe "Recipe DSL methods" do
 
           it "my_resource does not return the resource" do
             dsl_name = self.my_resource
-            expect_converge {
+            expect_converge do
               instance_eval("#{dsl_name} 'foo'")
-            }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+            end.to raise_error(Chef::Exceptions::NoSuchResourceType)
             expect(resource_class.called_provides).to be_truthy
           end
 
           it "blarghle_blarghle_little_star 'foo' returns the resource and emits a warning" do
             Chef::Config[:treat_deprecation_warnings_as_errors] = false
             dsl_name = self.blarghle_blarghle_little_star
-            recipe = converge {
+            recipe = converge do
               instance_eval("#{dsl_name} 'foo'")
-            }
+            end
             expect(recipe.logged_warnings).to include "WARN: #{resource_class}.provides? returned true when asked if it provides DSL #{dsl_name}, but provides :#{dsl_name} was never called!"
             expect(BaseThingy.created_resource).to eq resource_class
             expect(resource_class.called_provides).to be_truthy
@@ -1281,9 +1282,9 @@ describe "Recipe DSL methods" do
 
               it "my_resource runs the provider and does not emit a warning" do
                 my_resource = self.my_resource
-                recipe = converge {
+                recipe = converge do
                   instance_eval("#{my_resource} 'foo'")
-                }
+                end
                 expect(recipe.logged_warnings).to eq ""
                 expect(BaseThingy.created_provider).to eq provider_class
               end
@@ -1311,9 +1312,9 @@ describe "Recipe DSL methods" do
 
                 it "my_resource runs the first provider" do
                   my_resource = self.my_resource
-                  recipe = converge {
+                  recipe = converge do
                     instance_eval("#{my_resource} 'foo'")
-                  }
+                  end
                   expect(recipe.logged_warnings).to eq ""
                   expect(BaseThingy.created_provider).to eq provider_class
                 end
@@ -1328,9 +1329,9 @@ describe "Recipe DSL methods" do
               # TODO no warning? ick
               it "my_resource runs the provider anyway" do
                 my_resource = self.my_resource
-                recipe = converge {
+                recipe = converge do
                   instance_eval("#{my_resource} 'foo'")
-                }
+                end
                 expect(recipe.logged_warnings).to eq ""
                 expect(BaseThingy.created_provider).to eq provider_class
               end
@@ -1360,9 +1361,9 @@ describe "Recipe DSL methods" do
 
                 it "my_resource runs the other provider" do
                   my_resource = self.my_resource
-                  recipe = converge {
+                  recipe = converge do
                     instance_eval("#{my_resource} 'foo'")
-                  }
+                  end
                   expect(recipe.logged_warnings).to eq ""
                   expect(BaseThingy.created_provider).to eq provider_class2
                 end
@@ -1371,24 +1372,24 @@ describe "Recipe DSL methods" do
           end
 
           context "with provides? returning true" do
-            before {
+            before do
               my_resource = self.my_resource
               provider_class.define_singleton_method(:provides?) do |node, resource|
                 @called_provides = true
                 resource.declared_type == my_resource
               end
-            }
+            end
 
             context "that provides :my_resource" do
-              before {
+              before do
                 provider_class.provides my_resource
-              }
+              end
 
               it "my_resource calls the provider (and calls provides?), but does not emit a warning" do
                 my_resource = self.my_resource
-                recipe = converge {
+                recipe = converge do
                   instance_eval("#{my_resource} 'foo'")
-                }
+                end
                 expect(recipe.logged_warnings).to eq ""
                 expect(BaseThingy.created_provider).to eq provider_class
                 expect(provider_class.called_provides).to be_truthy
@@ -1399,9 +1400,9 @@ describe "Recipe DSL methods" do
               it "my_resource calls the provider (and calls provides?), and emits a warning" do
                 Chef::Config[:treat_deprecation_warnings_as_errors] = false
                 my_resource = self.my_resource
-                recipe = converge {
+                recipe = converge do
                   instance_eval("#{my_resource} 'foo'")
-                }
+                end
                 expect(recipe.logged_warnings).to include("WARN: #{provider_class}.provides? returned true when asked if it provides DSL #{my_resource}, but provides :#{my_resource} was never called!")
                 expect(BaseThingy.created_provider).to eq provider_class
                 expect(provider_class.called_provides).to be_truthy
@@ -1410,24 +1411,24 @@ describe "Recipe DSL methods" do
           end
 
           context "with provides? returning false to my_resource" do
-            before {
+            before do
               my_resource = self.my_resource
               provider_class.define_singleton_method(:provides?) do |node, resource|
                 @called_provides = true
                 false
               end
-            }
+            end
 
             context "that provides :my_resource" do
-              before {
+              before do
                 provider_class.provides my_resource
-              }
+              end
 
               it "my_resource fails to find a provider (and calls provides)" do
                 my_resource = self.my_resource
-                expect_converge {
+                expect_converge do
                   instance_eval("#{my_resource} 'foo'")
-                }.to raise_error(Chef::Exceptions::ProviderNotFound)
+                end.to raise_error(Chef::Exceptions::ProviderNotFound)
                 expect(provider_class.called_provides).to be_truthy
               end
             end
@@ -1435,9 +1436,9 @@ describe "Recipe DSL methods" do
             context "that does not provide :my_resource" do
               it "my_resource fails to find a provider (and calls provides)" do
                 my_resource = self.my_resource
-                expect_converge {
+                expect_converge do
                   instance_eval("#{my_resource} 'foo'")
-                }.to raise_error(Chef::Exceptions::ProviderNotFound)
+                end.to raise_error(Chef::Exceptions::ProviderNotFound)
                 expect(provider_class.called_provides).to be_truthy
               end
             end
@@ -1451,15 +1452,15 @@ describe "Recipe DSL methods" do
   before { Namer.current_index += 1 }
 
   context "with an LWRP that declares actions" do
-    let(:resource_class) {
+    let(:resource_class) do
       Class.new(Chef::Resource::LWRPBase) do
         provides :"recipe_dsl_spec#{Namer.current_index}"
         actions :create
       end
-    }
-    let(:resource) {
+    end
+    let(:resource) do
       resource_class.new("blah", run_context)
-    }
+    end
     it "The actions are part of actions along with :nothing" do
       expect(resource_class.actions).to eq [ :nothing, :create ]
     end
@@ -1468,15 +1469,15 @@ describe "Recipe DSL methods" do
     end
 
     context "and a subclass that declares more actions" do
-      let(:subresource_class) {
+      let(:subresource_class) do
         Class.new(Chef::Resource::LWRPBase) do
           provides :"recipe_dsl_spec_sub#{Namer.current_index}"
           actions :delete
         end
-      }
-      let(:subresource) {
+      end
+      let(:subresource) do
         subresource_class.new("subblah", run_context)
-      }
+      end
 
       it "The parent class actions are not part of actions" do
         expect(subresource_class.actions).to eq [ :nothing, :delete ]
@@ -1510,9 +1511,9 @@ describe "Recipe DSL methods" do
 
     it "looks up the provider in Chef::Provider converting the resource name from snake case to camel case" do
       resource = nil
-      recipe = converge {
-        resource = lw_resource_with_hw_provider_test_case "blah" do; end
-      }
+      recipe = converge do
+        resource = lw_resource_with_hw_provider_test_case("blah") {}
+      end
       expect(resource.created_provider).to eq(Chef::Provider::LwResourceWithHwProviderTestCase)
     end
   end
diff --git a/spec/integration/recipes/resource_action_spec.rb b/spec/integration/recipes/resource_action_spec.rb
index 8f6f4b7..60d5831 100644
--- a/spec/integration/recipes/resource_action_spec.rb
+++ b/spec/integration/recipes/resource_action_spec.rb
@@ -9,94 +9,110 @@ module ResourceActionSpec
     shared_context "ActionJackson" do
       it "the default action is the first declared action" do
         converge <<-EOM, __FILE__, __LINE__ + 1
-        #{resource_dsl} "hi" do
-          foo "foo!"
-        end
-      EOM
+          #{resource_dsl} "hi" do
+            foo "foo!"
+          end
+        EOM
         expect(ActionJackson.ran_action).to eq :access_recipe_dsl
         expect(ActionJackson.succeeded).to eq true
       end
 
+      context "when running in whyrun mode" do
+        before do
+          Chef::Config[:why_run] = true
+        end
+
+        it "the default action runs" do
+          converge <<-EOM, __FILE__, __LINE__ + 1
+            #{resource_dsl} "hi" do
+              foo "foo!"
+            end
+          EOM
+          expect(ActionJackson.ran_action).to eq :access_recipe_dsl
+          expect(ActionJackson.succeeded).to eq true
+        end
+      end
+
       it "the action can access recipe DSL" do
         converge <<-EOM, __FILE__, __LINE__ + 1
-        #{resource_dsl} "hi" do
-          foo "foo!"
-          action :access_recipe_dsl
-        end
-      EOM
+          #{resource_dsl} "hi" do
+            foo "foo!"
+            action :access_recipe_dsl
+          end
+        EOM
         expect(ActionJackson.ran_action).to eq :access_recipe_dsl
         expect(ActionJackson.succeeded).to eq true
       end
 
       it "the action can access attributes" do
         converge <<-EOM, __FILE__, __LINE__ + 1
-        #{resource_dsl} "hi" do
-          foo "foo!"
-          action :access_attribute
-        end
-      EOM
+          #{resource_dsl} "hi" do
+            foo "foo!"
+            action :access_attribute
+          end
+        EOM
         expect(ActionJackson.ran_action).to eq :access_attribute
         expect(ActionJackson.succeeded).to eq "foo!"
       end
 
       it "the action can access public methods" do
         converge <<-EOM, __FILE__, __LINE__ + 1
-        #{resource_dsl} "hi" do
-          foo "foo!"
-          action :access_method
-        end
-      EOM
+          #{resource_dsl} "hi" do
+            foo "foo!"
+            action :access_method
+          end
+        EOM
         expect(ActionJackson.ran_action).to eq :access_method
         expect(ActionJackson.succeeded).to eq "foo_public!"
       end
 
       it "the action can access protected methods" do
         converge <<-EOM, __FILE__, __LINE__ + 1
-        #{resource_dsl} "hi" do
-          foo "foo!"
-          action :access_protected_method
-        end
-      EOM
+          #{resource_dsl} "hi" do
+            foo "foo!"
+            action :access_protected_method
+          end
+        EOM
         expect(ActionJackson.ran_action).to eq :access_protected_method
         expect(ActionJackson.succeeded).to eq "foo_protected!"
       end
 
       it "the action cannot access private methods" do
-        expect {
+        expect do
           converge(<<-EOM, __FILE__, __LINE__ + 1)
-          #{resource_dsl} "hi" do
-            foo "foo!"
-            action :access_private_method
-          end
-        EOM
-        }.to raise_error(NameError)
+            #{resource_dsl} "hi" do
+              foo "foo!"
+              action :access_private_method
+            end
+          EOM
+        end.to raise_error(NameError)
         expect(ActionJackson.ran_action).to eq :access_private_method
       end
 
       it "the action cannot access resource instance variables" do
         converge <<-EOM, __FILE__, __LINE__ + 1
-        #{resource_dsl} "hi" do
-          foo "foo!"
-          action :access_instance_variable
-        end
-      EOM
+          #{resource_dsl} "hi" do
+            foo "foo!"
+            action :access_instance_variable
+          end
+        EOM
         expect(ActionJackson.ran_action).to eq :access_instance_variable
         expect(ActionJackson.succeeded).to be_nil
       end
 
       it "the action does not compile until the prior resource has converged" do
         converge <<-EOM, __FILE__, __LINE__ + 1
-        ruby_block "wow" do
-          block do
-            ResourceActionSpec::ActionJackson.ruby_block_converged = "ruby_block_converged!"
+          ruby_block "wow" do
+            block do
+              ResourceActionSpec::ActionJackson.ruby_block_converged = "ruby_block_converged!"
+            end
           end
-        end
 
-        #{resource_dsl} "hi" do
-          foo "foo!"
-          action :access_class_method
-        end
-      EOM
+          #{resource_dsl} "hi" do
+            foo "foo!"
+            action :access_class_method
+          end
+        EOM
         expect(ActionJackson.ran_action).to eq :access_class_method
         expect(ActionJackson.succeeded).to eq "ruby_block_converged!"
       end
@@ -139,29 +155,9 @@ module ResourceActionSpec
           attr_accessor :ruby_block_converged
         end
 
-        public
-
-        def foo_public
-          "foo_public!"
-        end
-
-        protected
-
-        def foo_protected
-          "foo_protected!"
-        end
-
-        private
-
-        def foo_private
-          "foo_private!"
-        end
-
-        public
-
         action :access_recipe_dsl do
           ActionJackson.ran_action = :access_recipe_dsl
-          ruby_block "hi there" do
+          whyrun_safe_ruby_block "hi there" do
             block do
               ActionJackson.succeeded = true
             end
@@ -199,13 +195,29 @@ module ResourceActionSpec
           ActionJackson.ran_action = :access_class_method
           ActionJackson.succeeded = ActionJackson.ruby_block_converged
         end
+
+        def foo_public
+          "foo_public!"
+        end
+
+        protected
+
+        def foo_protected
+          "foo_protected!"
+        end
+
+        private
+
+        def foo_private
+          "foo_private!"
+        end
       end
 
-      before(:each) {
+      before(:each) do
         ActionJackson.ran_action = :error
         ActionJackson.succeeded = :error
         ActionJackson.ruby_block_converged = :error
-      }
+      end
 
       it_behaves_like "ActionJackson" do
         let(:resource_dsl) { :action_jackson }
@@ -217,11 +229,11 @@ module ResourceActionSpec
       end
 
       context "And 'action_jackgrandson' inheriting from ActionJackson and changing nothing" do
-        before(:context) {
+        before(:context) do
           class ActionJackgrandson < ActionJackson
             use_automatic_resource_name
           end
-        }
+        end
 
         it_behaves_like "ActionJackson" do
           let(:resource_dsl) { :action_jackgrandson }
@@ -267,38 +279,38 @@ module ResourceActionSpec
         end
 
         it "the default action remains the same even though new actions were specified first" do
-          converge {
+          converge do
             action_jackalope "hi" do
               foo "foo!"
               bar "bar!"
             end
-          }
+          end
           expect(ActionJackson.ran_action).to eq :access_recipe_dsl
           expect(ActionJackson.succeeded).to eq true
         end
 
         it "new actions run, and can access overridden, new, and overridden attributes" do
-          converge {
+          converge do
             action_jackalope "hi" do
               foo "foo!"
               bar "bar!"
               blarghle "blarghle!"
               action :access_jackalope
             end
-          }
+          end
           expect(ActionJackalope.jackalope_ran).to eq :access_jackalope
           expect(ActionJackalope.succeeded).to eq "foo!alope blarghle! bar!alope"
         end
 
         it "overridden actions run, call super, and can access overridden, new, and overridden attributes" do
-          converge {
+          converge do
             action_jackalope "hi" do
               foo "foo!"
               bar "bar!"
               blarghle "blarghle!"
               action :access_attribute
             end
-          }
+          end
           expect(ActionJackson.ran_action).to eq :access_attribute
           expect(ActionJackson.succeeded).to eq "foo!alope blarghle! bar!alope"
           expect(ActionJackalope.jackalope_ran).to eq :access_attribute
@@ -306,14 +318,14 @@ module ResourceActionSpec
         end
 
         it "non-overridden actions run and can access overridden and non-overridden variables (but not necessarily new ones)" do
-          converge {
+          converge do
             action_jackalope "hi" do
               foo "foo!"
               bar "bar!"
               blarghle "blarghle!"
               action :access_attribute2
             end
-          }
+          end
           expect(ActionJackson.ran_action).to eq :access_attribute2
           expect(ActionJackson.succeeded).to eq("foo!alope blarghle! bar!alope").or(eq("foo!alope blarghle!"))
         end
@@ -335,12 +347,12 @@ module ResourceActionSpec
       end
 
       it "the default action is :nothing" do
-        converge {
+        converge do
           no_action_jackson "hi" do
             foo "foo!"
             NoActionJackson.action_was = action
           end
-        }
+        end
         expect(NoActionJackson.action_was).to eq [:nothing]
       end
     end
@@ -359,9 +371,9 @@ module ResourceActionSpec
       end
 
       it "Running the action works" do
-        expect_recipe {
+        expect_recipe do
           weird_action_jackson "hi"
-        }.to be_up_to_date
+        end.to be_up_to_date
         expect(WeirdActionJackson.action_was).to eq :"a-b-c d"
       end
     end
@@ -404,41 +416,41 @@ module ResourceActionSpec
         attr_reader :x_warning_line
 
         it "Using the enclosing resource to set x to x emits a warning that you're using the wrong x" do
-          recipe = converge {
+          recipe = converge do
             resource_action_spec_also_with_x "hi" do
               x 1
               action :set_x_to_x
             end
-          }
+          end
           warnings = recipe.logs.lines.select { |l| l =~ /warn/i }
           expect(warnings.size).to eq 1
           expect(warnings[0]).to match(/property x is declared in both resource_action_spec_with_x\[hi\] and resource_action_spec_also_with_x\[hi\] action :set_x_to_x. Use new_resource.x instead. At #{__FILE__}:#{ResourceActionSpecAlsoWithX.x_warning_line}/)
         end
 
         it "Using the enclosing resource to set x to x outside the initializer emits no warning" do
-          expect_recipe {
+          expect_recipe do
             resource_action_spec_also_with_x "hi" do
               x 1
               action :set_x_to_x_in_non_initializer
             end
-          }.to emit_no_warnings_or_errors
+          end.to emit_no_warnings_or_errors
         end
 
         it "Using the enclosing resource to set x to 10 emits no warning" do
-          expect_recipe {
+          expect_recipe do
             resource_action_spec_also_with_x "hi" do
               x 1
               action :set_x_to_10
             end
-          }.to emit_no_warnings_or_errors
+          end.to emit_no_warnings_or_errors
         end
 
         it "Using the enclosing resource to set x to 10 emits no warning" do
-          expect_recipe {
+          expect_recipe do
             r = resource_action_spec_also_with_x "hi"
             r.x 1
             r.action :set_x_to_10
-          }.to emit_no_warnings_or_errors
+          end.to emit_no_warnings_or_errors
         end
       end
 
@@ -464,11 +476,11 @@ module ResourceActionSpec
       end
 
       it "Setting group to nil in an action does not emit a warning about it being defined in two places" do
-        expect_recipe {
+        expect_recipe do
           resource_action_spec_set_group_to_nil "hi" do
             action :set_group_to_nil
           end
-        }.to emit_no_warnings_or_errors
+        end.to emit_no_warnings_or_errors
       end
     end
 
@@ -484,9 +496,9 @@ module ResourceActionSpec
       end
 
       it "Raises an error when attempting to use a template in the action" do
-        expect_converge {
+        expect_converge do
           has_property_named_template "hi"
-        }.to raise_error(/Property template of has_property_named_template\[hi\] cannot be passed a block! If you meant to create a resource named template instead, you'll need to first rename the property./)
+        end.to raise_error(/Property template of has_property_named_template\[hi\] cannot be passed a block! If you meant to create a resource named template instead, you'll need to first rename the property./)
       end
     end
 
@@ -528,9 +540,9 @@ module ResourceActionSpec
 
       it "the methods are available to the action" do
         r = nil
-        expect_recipe {
+        expect_recipe do
           r = declares_action_class_methods "hi"
-        }.to emit_no_warnings_or_errors
+        end.to emit_no_warnings_or_errors
         expect(r.x).to eq(10)
       end
 
@@ -557,9 +569,9 @@ module ResourceActionSpec
 
         it "the methods are available to the action" do
           r = nil
-          expect_recipe {
+          expect_recipe do
             r = declares_action_class_methods_too "hi"
-          }.to emit_no_warnings_or_errors
+          end.to emit_no_warnings_or_errors
           expect(r.x).to eq(15)
         end
       end
diff --git a/spec/integration/recipes/resource_converge_if_changed_spec.rb b/spec/integration/recipes/resource_converge_if_changed_spec.rb
index 4e5fd52..89d831d 100644
--- a/spec/integration/recipes/resource_converge_if_changed_spec.rb
+++ b/spec/integration/recipes/resource_converge_if_changed_spec.rb
@@ -19,7 +19,7 @@ describe "Resource::ActionClass#converge_if_changed" do
 
   context "when the resource has identity, state and control properties" do
     let(:resource_name) { :"converge_if_changed_dsl#{Namer.current_index}" }
-    let(:resource_class) {
+    let(:resource_class) do
       result = Class.new(Chef::Resource) do
         def self.to_s; resource_name.to_s; end
 
@@ -36,7 +36,7 @@ describe "Resource::ActionClass#converge_if_changed" do
       end
       result.resource_name resource_name
       result
-    }
+    end
     let(:converged_recipe) { converge(converge_recipe) }
     let(:resource) { converged_recipe.resources.first }
 
@@ -70,13 +70,13 @@ describe "Resource::ActionClass#converge_if_changed" do
         end
 
         context "and state1 is set to a new value" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 state1 'new_state1'
               end
             EOM
-          }
+          end
 
           it "the resource updates state1" do
             expect(resource.converged).to eq 1
@@ -90,14 +90,14 @@ describe "Resource::ActionClass#converge_if_changed" do
         end
 
         context "and state1 and state2 are set to new values" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 state1 'new_state1'
                 state2 'new_state2'
               end
             EOM
-          }
+          end
 
           it "the resource updates state1 and state2" do
             expect(resource.converged).to eq 1
@@ -112,7 +112,7 @@ EOM
         end
 
         context "and state1 and state2 are set to new sensitive values" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 sensitive true
@@ -120,7 +120,7 @@ EOM
                 state2 'new_state2'
               end
             EOM
-          }
+          end
 
           it "the resource updates state1 and state2" do
             expect(resource.converged).to eq 1
@@ -135,14 +135,14 @@ EOM
         end
 
         context "and state1 is set to its current value but state2 is set to a new value" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 state1 'current_state1'
                 state2 'new_state2'
               end
             EOM
-          }
+          end
 
           it "the resource updates state2" do
             expect(resource.converged).to eq 1
@@ -156,14 +156,14 @@ EOM
         end
 
         context "and state1 and state2 are set to their current values" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 state1 'current_state1'
                 state2 'current_state2'
               end
             EOM
-          }
+          end
 
           it "the resource updates nothing" do
             expect(resource.converged).to eq 0
@@ -175,14 +175,14 @@ EOM
         end
 
         context "and identity1 and control1 are set to new values" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 identity1 'new_identity1'
                 control1 'new_control1'
               end
             EOM
-          }
+          end
 
           # Because the identity value is copied over to the new resource, by
           # default they do not register as "changed"
@@ -205,14 +205,14 @@ EOM
         end
 
         context "and identity1 and control1 are set to new values" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 identity1 'new_identity1'
                 control1 'new_control1'
               end
             EOM
-          }
+          end
 
           # Control values are not desired state and are therefore not considered
           # a reason for converging.
@@ -252,14 +252,14 @@ EOM
         end
 
         context "and state1 and state2 are set" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 state1 'new_state1'
                 state2 'new_state2'
               end
             EOM
-          }
+          end
 
           it "the resource is created" do
             expect(resource.converged).to eq 1
@@ -275,7 +275,7 @@ EOM
         end
 
         context "and state1 and state2 are set with sensitive property" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 sensitive true
@@ -283,7 +283,7 @@ EOM
                 state2 'new_state2'
               end
             EOM
-          }
+          end
 
           it "the resource is created" do
             expect(resource.converged).to eq 1
@@ -334,13 +334,13 @@ EOM
 
         context "and state1 is set to a new value" do
 
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 state1 'new_state1'
               end
             EOM
-          }
+          end
 
           it "the resource updates state1" do
             expect(resource.converged).to eq 1
@@ -354,14 +354,14 @@ EOM
         end
 
         context "and state1 and state2 are set to new values" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 state1 'new_state1'
                 state2 'new_state2'
               end
             EOM
-          }
+          end
 
           it "the resource updates state1 and state2" do
             expect(resource.converged).to eq 2
@@ -377,14 +377,14 @@ EOM
         end
 
         context "and state1 is set to its current value but state2 is set to a new value" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 state1 'current_state1'
                 state2 'new_state2'
               end
             EOM
-          }
+          end
 
           it "the resource updates state2" do
             expect(resource.converged).to eq 1
@@ -398,14 +398,14 @@ EOM
         end
 
         context "and state1 and state2 are set to their current values" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 state1 'current_state1'
                 state2 'current_state2'
               end
             EOM
-          }
+          end
 
           it "the resource updates nothing" do
             expect(resource.converged).to eq 0
@@ -425,9 +425,9 @@ EOM
         end
 
         context "and nothing is set" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             "#{resource_name} 'blah'"
-          }
+          end
 
           it "the resource is created" do
             expect(resource.converged).to eq 2
@@ -443,14 +443,14 @@ EOM
         end
 
         context "and state1 and state2 are set to new values" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 state1 'new_state1'
                 state2 'new_state2'
               end
             EOM
-          }
+          end
 
           it "the resource is created" do
             expect(resource.converged).to eq 2
@@ -466,7 +466,7 @@ EOM
         end
 
         context "and state1 and state2 are set to new sensitive values" do
-          let(:converge_recipe) {
+          let(:converge_recipe) do
             <<-EOM
               #{resource_name} 'blah' do
                 sensitive true
@@ -474,7 +474,7 @@ EOM
                 state2 'new_state2'
               end
             EOM
-          }
+          end
 
           it "the resource is created" do
             expect(resource.converged).to eq 2
diff --git a/spec/integration/recipes/resource_load_spec.rb b/spec/integration/recipes/resource_load_spec.rb
index 53ce1c9..954fbf5 100644
--- a/spec/integration/recipes/resource_load_spec.rb
+++ b/spec/integration/recipes/resource_load_spec.rb
@@ -18,7 +18,7 @@ describe "Resource.load_current_value" do
   before { Namer.incrementing_value = 0 }
 
   let(:resource_name) { :"load_current_value_dsl#{Namer.current_index}" }
-  let(:resource_class) {
+  let(:resource_class) do
     result = Class.new(Chef::Resource) do
       def self.to_s; resource_name.to_s; end
 
@@ -37,7 +37,7 @@ describe "Resource.load_current_value" do
     end
     result.resource_name resource_name
     result
-  }
+  end
 
   # Pull on resource_class to initialize it
   before { resource_class }
@@ -56,11 +56,11 @@ describe "Resource.load_current_value" do
       let(:resource) do
         e = self
         r = nil
-        converge {
+        converge do
           r = public_send(e.resource_name, "blah") do
             x "desired"
           end
-        }
+        end
         r
       end
 
@@ -83,17 +83,17 @@ describe "Resource.load_current_value" do
       end
 
       context "and identity: :i and :d with desired_state: false" do
-        before {
+        before do
           resource_class.class_eval do
             property :i, identity: true
             property :d, desired_state: false
           end
-        }
+        end
 
-        before {
+        before do
           resource.i "desired_i"
           resource.d "desired_d"
-        }
+        end
 
         it "i, name and d are passed to load_current_value, but not x" do
           expect(resource.current_value.x).to eq "loaded 2 (d=desired_d, i=desired_i, name=blah)"
@@ -101,17 +101,17 @@ describe "Resource.load_current_value" do
       end
 
       context "and name_property: :i and :d with desired_state: false" do
-        before {
+        before do
           resource_class.class_eval do
             property :i, name_property: true
             property :d, desired_state: false
           end
-        }
+        end
 
-        before {
+        before do
           resource.i "desired_i"
           resource.d "desired_d"
-        }
+        end
 
         it "i, name and d are passed to load_current_value, but not x" do
           expect(resource.current_value.x).to eq "loaded 2 (d=desired_d, i=desired_i, name=blah)"
@@ -123,10 +123,10 @@ describe "Resource.load_current_value" do
       let(:resource) do
         e = self
         r = nil
-        converge {
+        converge do
           r = public_send(e.resource_name, "blah") do
           end
-        }
+        end
         r
       end
 
@@ -135,16 +135,16 @@ describe "Resource.load_current_value" do
       end
     end
 
-    let (:subresource_name) {
+    let (:subresource_name) do
       :"load_current_value_subresource_dsl#{Namer.current_index}"
-    }
-    let (:subresource_class) {
+    end
+    let (:subresource_class) do
       r = Class.new(resource_class) do
         property :y, default: lazy { "default_y #{Namer.incrementing_value}" }
       end
       r.resource_name subresource_name
       r
-    }
+    end
 
     # Pull on subresource_class to initialize it
     before { subresource_class }
@@ -152,11 +152,11 @@ describe "Resource.load_current_value" do
     let(:subresource) do
       e = self
       r = nil
-      converge {
+      converge do
         r = public_send(e.subresource_name, "blah") do
           x "desired"
         end
-      }
+      end
       r
     end
 
@@ -170,14 +170,14 @@ describe "Resource.load_current_value" do
     end
 
     context "And a child resource class with load_current_value" do
-      before {
+      before do
         subresource_class.load_current_value do
           y "loaded_y #{Namer.incrementing_value} (#{self.class.properties.sort_by { |name, p| name }.
             select { |name, p| p.is_set?(self) }.
             map { |name, p| "#{name}=#{p.get(self)}" }.
             join(", ") })"
         end
-      }
+      end
 
       it "the overridden load_current_value is used" do
         current_resource = subresource.current_value
@@ -187,7 +187,7 @@ describe "Resource.load_current_value" do
     end
 
     context "and a child resource class with load_current_value calling super()" do
-      before {
+      before do
         subresource_class.load_current_value do
           super()
           y "loaded_y #{Namer.incrementing_value} (#{self.class.properties.sort_by { |name, p| name }.
@@ -195,7 +195,7 @@ describe "Resource.load_current_value" do
             map { |name, p| "#{name}=#{p.get(self)}" }.
             join(", ") })"
         end
-      }
+      end
 
       it "the original load_current_value is called as well as the child one" do
         current_resource = subresource.current_value
diff --git a/spec/integration/solo/solo_spec.rb b/spec/integration/solo/solo_spec.rb
index f142798..bfcb74b 100644
--- a/spec/integration/solo/solo_spec.rb
+++ b/spec/integration/solo/solo_spec.rb
@@ -112,7 +112,11 @@ EOM
       file "cookbooks/x/recipes/default.rb", <<EOM
 ruby_block "sleeping" do
   block do
-    sleep 10
+    retries = 200
+    while IO.read(Chef::Config[:log_location]) !~ /Chef client [0-9]+ is running, will wait for it to finish and then run./
+      sleep 0.1
+      raise "we ran out of retries" if ( retries -= 1 ) <= 0
+    end
   end
 end
 EOM
@@ -125,51 +129,38 @@ file_cache_path "#{path_to('config/cache')}"
 EOM
       # We have a timeout protection here so that if due to some bug
       # run_lock gets stuck we can discover it.
-      expect {
+      expect do
         Timeout.timeout(120) do
           chef_dir = File.join(File.dirname(__FILE__), "..", "..", "..")
 
-          # Instantiate the first chef-solo run
-          s1 = Process.spawn("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' \
--l debug -L #{path_to('logs/runs.log')}", :chdir => chef_dir)
+          threads = []
 
-          # Give it some time to progress
-          sleep 5
+          # Instantiate the first chef-solo run
+          threads << Thread.new do
+            s1 = Process.spawn("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default'  -l debug -L #{path_to('logs/runs.log')}", :chdir => chef_dir)
+            Process.waitpid(s1)
+          end
 
           # Instantiate the second chef-solo run
-          s2 = Process.spawn("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' \
--l debug -L #{path_to('logs/runs.log')}", :chdir => chef_dir)
+          threads << Thread.new do
+            s2 = Process.spawn("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default'  -l debug -L #{path_to('logs/runs.log')}", :chdir => chef_dir)
+            Process.waitpid(s2)
+          end
 
-          Process.waitpid(s1)
-          Process.waitpid(s2)
+          threads.each(&:join)
         end
-      }.not_to raise_error
+      end.not_to raise_error
 
       # Unfortunately file / directory helpers in integration tests
       # are implemented using before(:each) so we need to do all below
       # checks in one example.
       run_log = File.read(path_to("logs/runs.log"))
 
+      # second run should have a message which indicates it's waiting for the first run
+      expect(run_log).to match(/Chef client [0-9]+ is running, will wait for it to finish and then run./)
+
       # both of the runs should succeed
       expect(run_log.lines.reject { |l| !l.include? "INFO: Chef Run complete in" }.length).to eq(2)
-
-      # second run should have a message which indicates it's waiting for the first run
-      pid_lines = run_log.lines.reject { |l| !l.include? "Chef-client pid:" }
-      expect(pid_lines.length).to eq(2)
-      pids = pid_lines.map { |l| l.split(" ").last }
-      expect(run_log).to include("Chef client #{pids[0]} is running, will wait for it to finish and then run.")
-
-      # second run should start after first run ends
-      starts = [ ]
-      ends = [ ]
-      run_log.lines.each_with_index do |line, index|
-        if line.include? "Chef-client pid:"
-          starts << index
-        elsif line.include? "INFO: Chef Run complete in"
-          ends << index
-        end
-      end
-      expect(starts[1]).to be > ends[0]
     end
 
   end
diff --git a/spec/scripts/ssl-serve.rb b/spec/scripts/ssl-serve.rb
index c05aa2c..3f4e343 100644
--- a/spec/scripts/ssl-serve.rb
+++ b/spec/scripts/ssl-serve.rb
@@ -42,6 +42,6 @@ webrick_opts = DEFAULT_OPTIONS.merge(server_opts)
 pp :webrick_opts => webrick_opts
 
 server = WEBrick::HTTPServer.new(webrick_opts)
-trap "INT" do server.shutdown end
+trap("INT") { server.shutdown }
 
 server.start
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 2f6747c..47a5ec7 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -66,6 +66,8 @@ require "chef/util/file_edit"
 
 require "chef/config"
 
+require "chef/chef_fs/file_system_cache"
+
 if ENV["CHEF_FIPS"] == "1"
   Chef::Config.init_openssl
 end
@@ -150,6 +152,7 @@ RSpec.configure do |config|
   config.filter_run_excluding :solaris_only => true unless solaris?
   config.filter_run_excluding :system_windows_service_gem_only => true unless system_windows_service_gem?
   config.filter_run_excluding :unix_only => true unless unix?
+  config.filter_run_excluding :linux_only => true unless linux?
   config.filter_run_excluding :aix_only => true unless aix?
   config.filter_run_excluding :debian_family_only => true unless debian_family?
   config.filter_run_excluding :supports_cloexec => true unless supports_cloexec?
@@ -200,6 +203,8 @@ RSpec.configure do |config|
   config.before(:each) do
     Chef.reset!
 
+    Chef::ChefFS::FileSystemCache.instance.reset!
+
     Chef::Config.reset
 
     # By default, treat deprecation warnings as errors in tests.
@@ -220,6 +225,7 @@ RSpec.configure do |config|
 end
 
 require "webrick/utils"
+require "thread"
 
 #    Webrick uses a centralized/synchronized timeout manager. It works by
 #    starting a thread to check for timeouts on an interval. The timeout
@@ -238,7 +244,12 @@ module WEBrick
   module Utils
     class TimeoutHandler
       def initialize
-        @timeout_info = Hash.new
+      end
+
+      def register(*args)
+      end
+
+      def cancel(*args)
       end
     end
   end
diff --git a/spec/stress/win32/security_spec.rb b/spec/stress/win32/security_spec.rb
index 58cf7b3..3c03a65 100644
--- a/spec/stress/win32/security_spec.rb
+++ b/spec/stress/win32/security_spec.rb
@@ -49,21 +49,21 @@ describe "Chef::ReservedNames::Win32::Security", :windows_only do
   end
 
   it "should not leak when retrieving and reading the ACE from a file", :volatile do
-    expect {
+    expect do
       sids = Chef::ReservedNames::Win32::Security::SecurableObject.new(@monkeyfoo).security_descriptor.dacl.select { |ace| ace.sid }
       GC.start
-    }.not_to leak_memory(:warmup => 50, :iterations => 100)
+    end.not_to leak_memory(:warmup => 50, :iterations => 100)
   end
 
   it "should not leak when creating a new ACL and setting it on a file", :volatile do
     securable_object = Security::SecurableObject.new(@monkeyfoo)
-    expect {
+    expect do
       securable_object.dacl = Chef::ReservedNames::Win32::Security::ACL.create([
         Chef::ReservedNames::Win32::Security::ACE.access_allowed(Chef::ReservedNames::Win32::Security::SID.Everyone, Chef::ReservedNames::Win32::API::Security::GENERIC_READ),
         Chef::ReservedNames::Win32::Security::ACE.access_denied(Chef::ReservedNames::Win32::Security::SID.from_account("Users"), Chef::ReservedNames::Win32::API::Security::GENERIC_ALL),
       ])
       GC.start
-    }.not_to leak_memory(:warmup => 50, :iterations => 100)
+    end.not_to leak_memory(:warmup => 50, :iterations => 100)
   end
 
 end
diff --git a/spec/support/chef_helpers.rb b/spec/support/chef_helpers.rb
index f64c14d..d0b5ad0 100644
--- a/spec/support/chef_helpers.rb
+++ b/spec/support/chef_helpers.rb
@@ -84,6 +84,28 @@ def canonicalize_path(path)
   windows? ? path.tr("/", '\\') : path
 end
 
+# Makes a temp directory with a canonical path on any platform.
+# Only really needed to work around an issue on Windows where
+# Ruby's temp library generates paths with short names.
+def make_canonical_temp_directory
+  temp_directory = Dir.mktmpdir
+  if windows?
+    # On Windows, temporary file / directory path names may have shortened
+    # subdirectory names due to reliance on the TMP and TEMP environment variables
+    # in some Windows APIs and duplicated logic in Ruby's temp file implementation.
+    # To work around this in the unit test context, we obtain the long (canonical)
+    # path name via a Windows system call so that this path name can be used
+    # in expectations that assume the ability to canonically name paths in comparisons.
+    # Note that this was not an issue prior to Ruby 2.2 -- with Ruby 2.2,
+    # some Chef code started to use long file names, while Ruby's temp file implementation
+    # continued to return the shortened names -- this would cause these particular tests to
+    # fail if the username happened to be longer than 8 characters.
+    Chef::ReservedNames::Win32::File.get_long_path_name(temp_directory)
+  else
+    temp_directory
+  end
+end
+
 # Check if a cmd exists on the PATH
 def which(cmd)
   paths = ENV["PATH"].split(File::PATH_SEPARATOR) + [ "/bin", "/usr/bin", "/sbin", "/usr/sbin" ]
diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb
index 9ba56a1..62b262b 100644
--- a/spec/support/platform_helpers.rb
+++ b/spec/support/platform_helpers.rb
@@ -126,6 +126,10 @@ def unix?
   !windows?
 end
 
+def linux?
+  !!(RUBY_PLATFORM =~ /linux/)
+end
+
 def os_x?
   !!(RUBY_PLATFORM =~ /darwin/)
 end
@@ -169,7 +173,7 @@ def selinux_enabled?
     when 0
       return true
     else
-      raise RuntimeError, "Unknown exit code from command #{selinuxenabled_path}: #{cmd.exitstatus}"
+      raise "Unknown exit code from command #{selinuxenabled_path}: #{cmd.exitstatus}"
     end
   else
     # We assume selinux is not enabled if selinux utils are not
diff --git a/spec/support/shared/context/client.rb b/spec/support/shared/context/client.rb
index d8676ef..b0530ab 100644
--- a/spec/support/shared/context/client.rb
+++ b/spec/support/shared/context/client.rb
@@ -230,8 +230,9 @@ shared_context "audit phase failed with error" do
 end
 
 shared_context "audit phase completed with failed controls" do
-  let(:audit_runner) { instance_double("Chef::Audit::Runner", :failed? => true,
-                                                              :num_failed => 1, :num_total => 3) }
+  let(:audit_runner) do
+    instance_double("Chef::Audit::Runner", :failed? => true,
+                                           :num_failed => 1, :num_total => 3) end
 
   let(:audit_error) do
     err = Chef::Exceptions::AuditsFailed.new(audit_runner.num_failed, audit_runner.num_total)
diff --git a/spec/support/shared/functional/file_resource.rb b/spec/support/shared/functional/file_resource.rb
index bb8db77..eb7a378 100644
--- a/spec/support/shared/functional/file_resource.rb
+++ b/spec/support/shared/functional/file_resource.rb
@@ -400,9 +400,9 @@ shared_examples_for "a configured file resource" do
   end
 
   context "when the target file is a symlink", :not_supported_on_win2k3 do
-    let(:symlink_target) {
+    let(:symlink_target) do
       File.join(CHEF_SPEC_DATA, "file-test-target")
-    }
+    end
 
     describe "when configured not to manage symlink's target" do
       before(:each) do
@@ -820,9 +820,9 @@ shared_examples_for "a configured file resource" do
     end
 
     describe "when path is specified with windows separator", :windows_only do
-      let(:path) {
+      let(:path) do
         File.join(test_file_dir, make_tmpname(file_base)).gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR)
-      }
+      end
 
       before do
         @notified_resource = Chef::Resource.new("punk", resource.run_context)
@@ -1035,6 +1035,7 @@ shared_context Chef::Resource::File do
   end
 
   before do
+    FileUtils.rm_rf(test_file_dir)
     FileUtils.mkdir_p(test_file_dir)
   end
 
diff --git a/spec/support/shared/functional/http.rb b/spec/support/shared/functional/http.rb
index 7e9e3f3..76faaae 100644
--- a/spec/support/shared/functional/http.rb
+++ b/spec/support/shared/functional/http.rb
@@ -52,27 +52,27 @@ module ChefHTTPShared
 
     # just a normal file
     # (expected_content should be uncompressed)
-    @api.get("/nyan_cat.png", 200) {
+    @api.get("/nyan_cat.png", 200) do
       File.open(nyan_uncompressed_filename, "rb") do |f|
         f.read
       end
-    }
+    end
 
     # this ends in .gz, we do not uncompress it and drop it on the filesystem as a .gz file (the internet often lies)
     # (expected_content should be compressed)
-    @api.get("/nyan_cat.png.gz", 200, nil, { "Content-Type" => "application/gzip", "Content-Encoding" => "gzip" } ) {
+    @api.get("/nyan_cat.png.gz", 200, nil, { "Content-Type" => "application/gzip", "Content-Encoding" => "gzip" } ) do
       File.open(nyan_compressed_filename, "rb") do |f|
         f.read
       end
-    }
+    end
 
     # this is an uncompressed file that was compressed by some mod_gzip-ish webserver thingy, so we will expand it
     # (expected_content should be uncompressed)
-    @api.get("/nyan_cat_compressed.png", 200, nil, { "Content-Type" => "application/gzip", "Content-Encoding" => "gzip" } ) {
+    @api.get("/nyan_cat_compressed.png", 200, nil, { "Content-Type" => "application/gzip", "Content-Encoding" => "gzip" } ) do
       File.open(nyan_compressed_filename, "rb") do |f|
         f.read
       end
-    }
+    end
 
     #
     # endpoints that set Content-Length correctly
@@ -83,11 +83,11 @@ module ChefHTTPShared
       {
         "Content-Length" => nyan_uncompressed_size.to_s,
       }
-    ) {
+    ) do
       File.open(nyan_uncompressed_filename, "rb") do |f|
         f.read
       end
-    }
+    end
 
     # (expected_content should be uncompressed)
     @api.get("/nyan_cat_content_length_compressed.png", 200, nil,
@@ -96,11 +96,11 @@ module ChefHTTPShared
         "Content-Type"     => "application/gzip",
         "Content-Encoding" => "gzip",
       }
-    ) {
+    ) do
       File.open(nyan_compressed_filename, "rb") do |f|
         f.read
       end
-    }
+    end
 
     #
     # endpoints that simulate truncated downloads (bad content-length header)
@@ -111,11 +111,11 @@ module ChefHTTPShared
       {
         "Content-Length" => (nyan_uncompressed_size + 1).to_s,
       }
-    ) {
+    ) do
       File.open(nyan_uncompressed_filename, "rb") do |f|
         f.read
       end
-    }
+    end
 
     # (expected_content should be uncompressed)
     @api.get("/nyan_cat_truncated_compressed.png", 200, nil,
@@ -124,11 +124,11 @@ module ChefHTTPShared
         "Content-Type"     => "application/gzip",
         "Content-Encoding" => "gzip",
       }
-    ) {
+    ) do
       File.open(nyan_compressed_filename, "rb") do |f|
         f.read
       end
-    }
+    end
 
     #
     # in the presence of a transfer-encoding header, we must ignore the content-length (this bad content-length should work)
@@ -140,11 +140,11 @@ module ChefHTTPShared
         "Content-Length"    => (nyan_uncompressed_size + 1).to_s,
         "Transfer-Encoding" => "anything",
       }
-    ) {
+    ) do
       File.open(nyan_uncompressed_filename, "rb") do |f|
         f.read
       end
-    }
+    end
 
     #
     # 403 with a Content-Length
diff --git a/spec/support/shared/functional/securable_resource.rb b/spec/support/shared/functional/securable_resource.rb
index 506b967..95f4f4b 100644
--- a/spec/support/shared/functional/securable_resource.rb
+++ b/spec/support/shared/functional/securable_resource.rb
@@ -81,7 +81,7 @@ shared_context "use Windows permissions", :windows_only do
     SID ||= Chef::ReservedNames::Win32::Security::SID
     ACE ||= Chef::ReservedNames::Win32::Security::ACE
     ACL ||= Chef::ReservedNames::Win32::Security::ACL
-    SecurableObject ||= Chef::ReservedNames::Win32::Security::SecurableObject
+    SecurableObject ||= Chef::ReservedNames::Win32::Security::SecurableObject # rubocop:disable Style/ConstantName
   end
 
   def get_security_descriptor(path)
@@ -138,9 +138,9 @@ shared_context "use Windows permissions", :windows_only do
 
   RSpec::Matchers.define :have_expected_properties do |mask, type, flags|
     match do |ace|
-      ace.mask == mask
-      ace.type == type
-      ace.flags == flags
+      ace.mask == mask &&
+        ace.type == type &&
+        ace.flags == flags
     end
   end
 
@@ -439,7 +439,7 @@ shared_examples_for "a securable resource without existing target" do
 
     context "with a mode attribute" do
       if windows?
-        Security ||= Chef::ReservedNames::Win32::API::Security
+        Security ||= Chef::ReservedNames::Win32::API::Security # rubocop:disable Style/ConstantName
       end
 
       it "respects mode in string form as an octal number" do
diff --git a/spec/support/shared/functional/win32_service.rb b/spec/support/shared/functional/win32_service.rb
index 0f9072b..3199caa 100644
--- a/spec/support/shared/functional/win32_service.rb
+++ b/spec/support/shared/functional/win32_service.rb
@@ -23,9 +23,7 @@ shared_context "using Win32::Service" do
       # We can only uninstall when the service is stopped.
       if test_service_state != "stopped"
         ::Win32::Service.send("stop", "spec-service")
-        while test_service_state != "stopped"
-          sleep 1
-        end
+        sleep 1 while test_service_state != "stopped"
       end
 
       ::Win32::Service.delete("spec-service")
@@ -39,7 +37,7 @@ shared_context "using Win32::Service" do
 
   # Definition for the test-service
 
-  let(:test_service) {
+  let(:test_service) do
     {
       :service_name => "spec-service",
       :service_display_name => "Spec Test Service",
@@ -47,13 +45,13 @@ shared_context "using Win32::Service" do
       :service_file_path => File.expand_path(File.join(File.dirname(__FILE__), "../../platforms/win32/spec_service.rb")),
       :delayed_start => true,
     }
-  }
+  end
 
   # Test service creates a file for us to verify that it is running.
   # Since our test service is running as Local System we should look
   # for the file it creates under SYSTEM temp directory
 
-  let(:test_service_file) {
+  let(:test_service_file) do
     "#{ENV['SystemDrive']}\\windows\\temp\\spec_service_file"
-  }
+  end
 end
diff --git a/spec/support/shared/integration/app_server_support.rb b/spec/support/shared/integration/app_server_support.rb
index 9bc48db..4dfa3fa 100644
--- a/spec/support/shared/integration/app_server_support.rb
+++ b/spec/support/shared/integration/app_server_support.rb
@@ -33,9 +33,7 @@ module AppServerSupport
       end
     end
     Timeout.timeout(30) do
-      until server && server.status == :Running
-        sleep(0.01)
-      end
+      sleep(0.01) until server && server.status == :Running
     end
     [server, thread]
   end
diff --git a/spec/support/shared/integration/knife_support.rb b/spec/support/shared/integration/knife_support.rb
index 1e81cd1..1a374e1 100644
--- a/spec/support/shared/integration/knife_support.rb
+++ b/spec/support/shared/integration/knife_support.rb
@@ -20,10 +20,11 @@ require "chef/knife"
 require "chef/application/knife"
 require "logger"
 require "chef/log"
+require "chef/chef_fs/file_system_cache"
 
 module KnifeSupport
   DEBUG = ENV["DEBUG"]
-  def knife(*args)
+  def knife(*args, input: nil)
     # Allow knife('role from file roles/blah.json') rather than requiring the
     # arguments to be split like knife('role', 'from', 'file', 'roles/blah.json')
     # If any argument will have actual spaces in it, the long form is required.
@@ -37,7 +38,7 @@ module KnifeSupport
     Chef::Config[:concurrency] = 1
 
     # Work on machines where we can't access /var
-    checksums_cache_dir = Dir.mktmpdir("checksums") do |checksums_cache_dir|
+    Dir.mktmpdir("checksums") do |checksums_cache_dir|
       Chef::Config[:cache_options] = {
         :path => checksums_cache_dir,
         :skip_expires => true,
@@ -47,6 +48,13 @@ module KnifeSupport
       # ourselves, thank you very much
       stdout = StringIO.new
       stderr = StringIO.new
+
+      stdin = if input
+                StringIO.new(input)
+              else
+                STDIN
+              end
+
       old_loggers = Chef::Log.loggers
       old_log_level = Chef::Log.level
       begin
@@ -57,12 +65,15 @@ module KnifeSupport
         instance = subcommand_class.new(args)
 
         # Capture stdout/stderr
-        instance.ui = Chef::Knife::UI.new(stdout, stderr, STDIN, {})
+        instance.ui = Chef::Knife::UI.new(stdout, stderr, stdin, disable_editing: true)
 
         # Don't print stuff
         Chef::Config[:verbosity] = ( DEBUG ? 2 : 0 )
         instance.config[:config_file] = File.join(CHEF_SPEC_DATA, "null_config.rb")
 
+        # Ensure the ChefFS cache is empty
+        Chef::ChefFS::FileSystemCache.instance.reset!
+
         # Configure chef with a (mostly) blank knife.rb
         # We set a global and then mutate it in our stub knife.rb so we can be
         # extra sure that we're not loading someone's real knife.rb and then
@@ -100,8 +111,6 @@ module KnifeSupport
     end
   end
 
-  private
-
   class KnifeResult
 
     include ::RSpec::Matchers
diff --git a/spec/support/shared/shared_examples.rb b/spec/support/shared/shared_examples.rb
index 550fa2e..0c031bb 100644
--- a/spec/support/shared/shared_examples.rb
+++ b/spec/support/shared/shared_examples.rb
@@ -3,9 +3,9 @@
 # Any object which defines a .to_json should import this test
 shared_examples "to_json equivalent to Chef::JSONCompat.to_json" do
 
-  let(:jsonable) {
+  let(:jsonable) do
     raise "You must define the subject when including this test"
-  }
+  end
 
   it "should allow consumers to call #to_json or Chef::JSONCompat.to_json" do
     expect(jsonable.to_json).to eq(Chef::JSONCompat.to_json(jsonable))
diff --git a/spec/support/shared/unit/api_versioning.rb b/spec/support/shared/unit/api_versioning.rb
index b61469a..28141b7 100644
--- a/spec/support/shared/unit/api_versioning.rb
+++ b/spec/support/shared/unit/api_versioning.rb
@@ -43,13 +43,13 @@ shared_examples_for "user and client reregister" do
   let(:generic_exception) { Exception.new }
   let(:min_version) { "2" }
   let(:max_version) { "5" }
-  let(:return_hash_406) {
+  let(:return_hash_406) do
     {
       "min_version" => min_version,
       "max_version" => max_version,
       "request_version" => "30",
     }
-  }
+  end
 
   context "when V0 is not supported by the server" do
     context "when the exception is 406 and returns x-ops-server-api-version header" do
diff --git a/spec/support/shared/unit/application_dot_d.rb b/spec/support/shared/unit/application_dot_d.rb
index a8769d6..da4eb88 100644
--- a/spec/support/shared/unit/application_dot_d.rb
+++ b/spec/support/shared/unit/application_dot_d.rb
@@ -31,8 +31,9 @@ shared_examples_for "an application that loads a dot d" do
   context "when client_d_dir is set to a directory with configuration" do
     # We're not going to mock out globbing the directory. We want to
     # make sure that we are correctly globbing.
-    let(:client_d_dir) { Chef::Util::PathHelper.cleanpath(
-      File.join(File.dirname(__FILE__), "../../../data/client.d_00")) }
+    let(:client_d_dir) do
+      Chef::Util::PathHelper.cleanpath(
+      File.join(File.dirname(__FILE__), "../../../data/client.d_00")) end
 
     it "loads the configuration in order" do
       expect(IO).to receive(:read).with(Pathname.new("#{client_d_dir}/00-foo.rb").cleanpath.to_s).and_return("foo 0")
@@ -45,8 +46,9 @@ shared_examples_for "an application that loads a dot d" do
   end
 
   context "when client_d_dir is set to a directory without configuration" do
-    let(:client_d_dir) { Chef::Util::PathHelper.cleanpath(
-      File.join(File.dirname(__FILE__), "../../data/client.d_01")) }
+    let(:client_d_dir) do
+      Chef::Util::PathHelper.cleanpath(
+      File.join(File.dirname(__FILE__), "../../data/client.d_01")) end
 
     # client.d_01 has a nested folder with a rb file that if
     # executed, would raise an exception. If it is executed,
@@ -60,8 +62,9 @@ shared_examples_for "an application that loads a dot d" do
 
   context "when client_d_dir is set to a directory containing a directory named foo.rb" do
     # foo.rb as a directory should be ignored
-    let(:client_d_dir) { Chef::Util::PathHelper.cleanpath(
-      File.join(File.dirname(__FILE__), "../../data/client.d_02")) }
+    let(:client_d_dir) do
+      Chef::Util::PathHelper.cleanpath(
+      File.join(File.dirname(__FILE__), "../../data/client.d_02")) end
 
     it "does not raise an exception" do
       expect { app.reconfigure }.not_to raise_error
diff --git a/spec/support/shared/unit/provider/file.rb b/spec/support/shared/unit/provider/file.rb
index cb539ff..394fdaa 100644
--- a/spec/support/shared/unit/provider/file.rb
+++ b/spec/support/shared/unit/provider/file.rb
@@ -459,11 +459,11 @@ shared_examples_for Chef::Provider::File do
     context "do_validate_content" do
       before { setup_normal_file }
 
-      let(:tempfile) {
+      let(:tempfile) do
         t = double("Tempfile", :path => "/tmp/foo-bar-baz", :closed? => true)
         allow(content).to receive(:tempfile).and_return(t)
         t
-      }
+      end
 
       context "with user-supplied verifications" do
         it "calls #verify on each verification with tempfile path" do
diff --git a/spec/support/shared/unit/provider/useradd_based_user_provider.rb b/spec/support/shared/unit/provider/useradd_based_user_provider.rb
index 6677a06..8607612 100644
--- a/spec/support/shared/unit/provider/useradd_based_user_provider.rb
+++ b/spec/support/shared/unit/provider/useradd_based_user_provider.rb
@@ -18,13 +18,18 @@
 # limitations under the License.
 #
 
+# XXX: this used to be shared by solaris and linux classes, but at some
+# point became linux-specific.  it is now a misnomer to call these 'shared'
+# examples and they should either realy get turned into shared examples or
+# should be copypasta'd back directly into the linux tests.
+
 shared_examples_for "a useradd-based user provider" do |supported_useradd_options|
   before(:each) do
     @node = Chef::Node.new
     @events = Chef::EventDispatch::Dispatcher.new
     @run_context = Chef::RunContext.new(@node, {}, @events)
 
-    @new_resource = Chef::Resource::User.new("adam", @run_context)
+    @new_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
     @new_resource.comment "Adam Jacob"
     @new_resource.uid 1000
     @new_resource.gid 1000
@@ -35,7 +40,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
     @new_resource.manage_home false
     @new_resource.force false
     @new_resource.non_unique false
-    @current_resource = Chef::Resource::User.new("adam", @run_context)
+    @current_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
     @current_resource.comment "Adam Jacob"
     @current_resource.uid 1000
     @current_resource.gid 1000
@@ -46,7 +51,6 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
     @current_resource.manage_home false
     @current_resource.force false
     @current_resource.non_unique false
-    @current_resource.supports({ :manage_home => false, :non_unique => false })
   end
 
   describe "when setting option" do
@@ -96,62 +100,49 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
 
       it "should set useradd -r" do
         @new_resource.system(true)
-        expect(provider.useradd_options).to eq([ "-r" ])
+        expect(provider.useradd_options).to eq([ "-r", "-m" ])
       end
     end
 
     describe "when the resource has a different home directory and supports home directory management" do
       before do
-        allow(@new_resource).to receive(:home).and_return("/wowaweea")
-        allow(@new_resource).to receive(:supports).and_return({ :manage_home => true,
-                                                                :non_unique => false })
+        @new_resource.home "/wowaweea"
+        @new_resource.manage_home true
       end
 
       it "should set -m -d /homedir" do
-        expect(provider.universal_options).to eq(%w{-d /wowaweea -m})
-        expect(provider.useradd_options).to eq([])
+        expect(provider.universal_options).to eq(%w{-d /wowaweea})
+        expect(provider.usermod_options).to eq(%w{-m})
       end
     end
 
     describe "when the resource has a different home directory and supports home directory management (using real attributes)" do
       before do
-        allow(@new_resource).to receive(:home).and_return("/wowaweea")
-        allow(@new_resource).to receive(:manage_home).and_return(true)
-        allow(@new_resource).to receive(:non_unique).and_return(false)
+        @new_resource.home("/wowaweea")
+        @new_resource.manage_home true
+        @new_resource.non_unique false
       end
 
       it "should set -m -d /homedir" do
-        expect(provider.universal_options).to eql(%w{-d /wowaweea -m})
-        expect(provider.useradd_options).to eq([])
+        expect(provider.universal_options).to eq(%w{-d /wowaweea})
+        expect(provider.usermod_options).to eq(%w{-m})
       end
     end
 
-    describe "when the resource supports non_unique ids" do
-      before do
-        allow(@new_resource).to receive(:supports).and_return({ :manage_home => false,
-                                                                :non_unique => true })
-      end
-
-      it "should set -m -o" do
-        expect(provider.universal_options).to eql([ "-o" ])
-      end
+    it "when non_unique is false should not set -m" do
+      @new_resource.non_unique false
+      expect(provider.universal_options).to eql([ ])
     end
 
-    describe "when the resource supports non_unique ids (using real attributes)" do
-      before do
-        allow(@new_resource).to receive(:manage_home).and_return(false)
-        allow(@new_resource).to receive(:non_unique).and_return(true)
-      end
-
-      it "should set -m -o" do
-        expect(provider.universal_options).to eql([ "-o" ])
-      end
+    it "when non_unique is true should set -o" do
+      @new_resource.non_unique true
+      expect(provider.universal_options).to eql([ "-o" ])
     end
   end
 
   describe "when creating a user" do
     before(:each) do
-      @current_resource = Chef::Resource::User.new(@new_resource.name, @run_context)
+      @current_resource = Chef::Resource::User::LinuxUser.new(@new_resource.name, @run_context)
       @current_resource.username(@new_resource.username)
       provider.current_resource = @current_resource
       provider.new_resource.manage_home true
@@ -188,7 +179,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
         command.concat(["-p", "abracadabra"]) if supported_useradd_options.key?("password")
         command.concat([ "-s", "/usr/bin/zsh",
                          "-u", "1000",
-                         "-r",
+                         "-r", "-m",
                          "adam" ])
         expect(provider).to receive(:shell_out!).with(*command).and_return(true)
         provider.create_user
@@ -229,7 +220,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
     end
 
     it "CHEF-3429: does not set -m if we aren't changing the home directory" do
-      expect(provider).to receive(:updating_home?).and_return(false)
+      expect(provider).to receive(:updating_home?).at_least(:once).and_return(false)
       command = ["usermod",
                   "-g", "23",
                   "adam" ]
@@ -246,15 +237,12 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
     end
 
     it "should run userdel with the new resources user name and -r if manage_home is true" do
-      @new_resource.supports({ :manage_home => true,
-                               :non_unique => false })
+      @new_resource.manage_home true
       expect(provider).to receive(:shell_out!).with("userdel", "-r", @new_resource.username).and_return(true)
       provider.remove_user
     end
 
     it "should run userdel with the new resources user name if non_unique is true" do
-      @new_resource.supports({ :manage_home => false,
-                               :non_unique => true })
       expect(provider).to receive(:shell_out!).with("userdel", @new_resource.username).and_return(true)
       provider.remove_user
     end
@@ -269,7 +257,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
   describe "when checking the lock" do
     # lazy initialize so we can modify stdout and stderr strings
     let(:passwd_s_status) do
-      double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => @stdout, :stderr => @stderr)
+      double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => @stdout, :stderr => @stderr, :error! => nil)
     end
 
     before(:each) do
@@ -284,7 +272,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
     end
 
     it "should return false if status begins with P" do
-      expect(provider).to receive(:shell_out!).
+      expect(provider).to receive(:shell_out).
         with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
         and_return(passwd_s_status)
       expect(provider.check_lock).to eql(false)
@@ -292,7 +280,7 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
 
     it "should return false if status begins with N" do
       @stdout = "root N"
-      expect(provider).to receive(:shell_out!).
+      expect(provider).to receive(:shell_out).
         with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
         and_return(passwd_s_status)
       expect(provider.check_lock).to eql(false)
@@ -300,57 +288,38 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
 
     it "should return true if status begins with L" do
       @stdout = "root L"
-      expect(provider).to receive(:shell_out!).
+      expect(provider).to receive(:shell_out).
         with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
         and_return(passwd_s_status)
       expect(provider.check_lock).to eql(true)
     end
 
-    it "should raise a Chef::Exceptions::User if passwd -S fails on anything other than redhat/centos" do
-      @node.automatic_attrs[:platform] = "ubuntu"
-      expect(provider).to receive(:shell_out!).
+    it "should raise a ShellCommandFailed exception if passwd -S exits with something other than 0 or 1" do
+      expect(passwd_s_status).to receive(:error!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
+      expect(provider).to receive(:shell_out).
         with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
         and_return(passwd_s_status)
-      expect(passwd_s_status).to receive(:exitstatus).and_return(1)
-      expect { provider.check_lock }.to raise_error(Chef::Exceptions::User)
+      expect { provider.check_lock }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
     end
 
-    %w{redhat centos}.each do |os|
-      it "should not raise a Chef::Exceptions::User if passwd -S exits with 1 on #{os} and the passwd package is version 0.73-1" do
-        @node.automatic_attrs[:platform] = os
-        expect(passwd_s_status).to receive(:exitstatus).and_return(1)
-        expect(provider).to receive(:shell_out!).
-          with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
-          and_return(passwd_s_status)
-        rpm_status = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => "passwd-0.73-1\n", :stderr => "")
-        expect(provider).to receive(:shell_out!).with("rpm -q passwd").and_return(rpm_status)
-        expect { provider.check_lock }.not_to raise_error
-      end
-
-      it "should raise a Chef::Exceptions::User if passwd -S exits with 1 on #{os} and the passwd package is not version 0.73-1" do
-        @node.automatic_attrs[:platform] = os
-        expect(passwd_s_status).to receive(:exitstatus).and_return(1)
-        expect(provider).to receive(:shell_out!).
-          with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
-          and_return(passwd_s_status)
-        rpm_status = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => "passwd-0.73-2\n", :stderr => "")
-        expect(provider).to receive(:shell_out!).with("rpm -q passwd").and_return(rpm_status)
-        expect { provider.check_lock }.to raise_error(Chef::Exceptions::User)
-      end
-
-      it "should raise a ShellCommandFailed exception if passwd -S exits with something other than 0 or 1 on #{os}" do
-        @node.automatic_attrs[:platform] = os
-        expect(provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
-        expect { provider.check_lock }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
-      end
+    it "should raise an error if the output isn't parsable" do
+      expect(passwd_s_status).to receive(:stdout).and_return("")
+      expect(passwd_s_status).to receive(:stderr).and_return("")
+      expect(provider).to receive(:shell_out).
+        with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
+        and_return(passwd_s_status)
+      expect { provider.check_lock }.to raise_error(Chef::Exceptions::User)
     end
 
     context "when in why run mode" do
       before do
         passwd_status = double("Mixlib::ShellOut command", :exitstatus => 0, :stdout => "", :stderr => "passwd: user 'chef-test' does not exist\n")
-        expect(provider).to receive(:shell_out!).
+        expect(provider).to receive(:shell_out).
           with("passwd", "-S", @new_resource.username, { :returns => [0, 1] }).
           and_return(passwd_status)
+        # ubuntu returns 252 on user-does-not-exist so will raise if #error! is called or if
+        # shell_out! is used
+        allow(passwd_status).to receive(:error!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
         Chef::Config[:why_run] = true
       end
 
@@ -420,9 +389,9 @@ shared_examples_for "a useradd-based user provider" do |supported_useradd_option
       end
     end
     it "should return true if the current home does not exist but a home is specified by the new resource" do
-      @new_resource = Chef::Resource::User.new("adam", @run_context)
-      @current_resource = Chef::Resource::User.new("adam", @run_context)
-      provider = Chef::Provider::User::Useradd.new(@new_resource, @run_context)
+      @new_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
+      @current_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
+      provider = Chef::Provider::User::Linux.new(@new_resource, @run_context)
       provider.current_resource = @current_resource
       @current_resource.home nil
       @new_resource.home "/home/kitten"
diff --git a/spec/support/shared/unit/resource/static_provider_resolution.rb b/spec/support/shared/unit/resource/static_provider_resolution.rb
index 4ae5d45..e68b805 100644
--- a/spec/support/shared/unit/resource/static_provider_resolution.rb
+++ b/spec/support/shared/unit/resource/static_provider_resolution.rb
@@ -33,13 +33,13 @@ def static_provider_resolution(opts = {})
   platform_version = opts[:platform_version]
 
   describe resource_class, "static provider initialization" do
-    let(:node) {
+    let(:node) do
       node = Chef::Node.new
       node.automatic_attrs[:os] = os
       node.automatic_attrs[:platform_family] = platform_family
       node.automatic_attrs[:platform_version] = platform_version
       node
-    }
+    end
     let(:events) { Chef::EventDispatch::Dispatcher.new }
     let(:run_context) { Chef::RunContext.new(node, {}, events) }
     let(:resource) { resource_class.new("foo", run_context) }
diff --git a/spec/support/shared/unit/script_resource.rb b/spec/support/shared/unit/script_resource.rb
index 732b07d..27864e1 100644
--- a/spec/support/shared/unit/script_resource.rb
+++ b/spec/support/shared/unit/script_resource.rb
@@ -57,18 +57,18 @@ shared_examples_for "a script resource" do
   end
 
   describe "when executing guards" do
-    let(:resource) {
+    let(:resource) do
       resource = script_resource
       resource.run_context = run_context
       resource.code "echo hi"
       resource
-    }
-    let(:node) {
+    end
+    let(:node) do
       node = Chef::Node.new
       node.automatic[:platform] = "debian"
       node.automatic[:platform_version] = "6.0"
       node
-    }
+    end
     let(:events) { Chef::EventDispatch::Dispatcher.new }
     let(:run_context) { Chef::RunContext.new(node, {}, events) }
 
diff --git a/spec/support/shared/unit/user_and_client_shared.rb b/spec/support/shared/unit/user_and_client_shared.rb
index e3481df..6c31ca2 100644
--- a/spec/support/shared/unit/user_and_client_shared.rb
+++ b/spec/support/shared/unit/user_and_client_shared.rb
@@ -55,13 +55,13 @@ shared_examples_for "user or client create" do
     end
 
     context "when chef_key is returned by the server" do
-      let(:chef_key) {
+      let(:chef_key) do
         {
           "chef_key" => {
             "public_key" => "some_public_key",
           },
         }
-      }
+      end
 
       it "puts the public key into the objectr returned by create" do
         expect(rest_v1).to receive(:post).with(url, payload).and_return(payload.merge(chef_key))
@@ -70,14 +70,14 @@ shared_examples_for "user or client create" do
       end
 
       context "when private_key is returned in chef_key" do
-        let(:chef_key) {
+        let(:chef_key) do
           {
             "chef_key" => {
               "public_key" => "some_public_key",
               "private_key" => "some_private_key",
             },
           }
-        }
+        end
 
         it "puts the private key into the object returned by create" do
           expect(rest_v1).to receive(:post).with(url, payload).and_return(payload.merge(chef_key))
diff --git a/spec/tiny_server.rb b/spec/tiny_server.rb
index a3711e4..83c5bf4 100644
--- a/spec/tiny_server.rb
+++ b/spec/tiny_server.rb
@@ -20,97 +20,82 @@ require "rubygems"
 require "webrick"
 require "webrick/https"
 require "rack"
-#require 'thin'
+require "thread"
 require "singleton"
 require "open-uri"
 require "chef/config"
 
 module TinyServer
 
-  class Server < Rack::Server
-
-    attr_writer :app
-
-    def self.setup(options = nil, &block)
-      tiny_app = new(options)
-      app_code = Rack::Builder.new(&block).to_app
-      tiny_app.app = app_code
-      tiny_app
-    end
-
-    def shutdown
-      server.shutdown
-    end
-  end
-
   class Manager
 
     # 5 == debug, 3 == warning
     LOGGER = WEBrick::Log.new(STDOUT, 3)
     DEFAULT_OPTIONS = {
-      :server => "webrick",
-      :Port => 9000,
-      :Host => "localhost",
-      :environment => :none,
-      :Logger => LOGGER,
-      :AccessLog => [] # Remove this option to enable the access log when debugging.
+      Port: 9000,
+      Host: "localhost",
+      Logger: LOGGER,
+      # SSLEnable: options[:ssl],
+      # SSLCertName: [ [ 'CN', WEBrick::Utils::getservername ] ],
+      AccessLog: [], # Remove this option to enable the access log when debugging.
     }
 
-    def initialize(options = nil)
-      @options = options ? DEFAULT_OPTIONS.merge(options) : DEFAULT_OPTIONS
+    def initialize(**options)
+      @options = DEFAULT_OPTIONS.merge(options)
       @creator = caller.first
     end
 
-    def start
+    attr_reader :options
+    attr_reader :creator
+    attr_reader :server
+
+    def start(timeout = 5)
+      raise "Server already started!" if server
+
+      # Create the server (but don't start yet)
+      start_queue = Queue.new
+      @server = create_server(StartCallback: proc { start_queue << true })
+
       @server_thread = Thread.new do
-        @server = Server.setup(@options) do
-          run API.instance
-        end
-        @old_handler = trap(:INT, "EXIT")
-        @server.start
+        # Ensure any exceptions will cause the main rspec thread to fail too
+        Thread.current.abort_on_exception = true
+        server.start
       end
-      block_until_started
-      trap(:INT, @old_handler)
-    end
 
-    def url
-      "http://localhost:#{@options[:Port]}"
+      # Wait for the StartCallback to tell us we've started
+      Timeout.timeout(timeout) do
+        start_queue.pop
+      end
     end
 
-    def block_until_started
-      200.times do
-        if started? && !@server.nil?
-          return true
+    def stop(timeout = 5)
+      if server
+        server.shutdown
+        @server = nil
+      end
+
+      if server_thread
+        begin
+          # Wait for a normal shutdown
+          server_thread.join(timeout)
+        rescue
+          # If it wouldn't shut down normally, kill it.
+          server_thread.kill
+          server_thread.join(timeout)
         end
+        @server_thread = nil
       end
-      raise "ivar weirdness" if started? && @server.nil?
-      raise "TinyServer failed to boot :/"
-    end
-
-    def started?
-      open(url)
-      true
-    rescue OpenURI::HTTPError
-      true
-    rescue Errno::ECONNREFUSED, EOFError, Errno::ECONNRESET => e
-      sleep 0.1
-      true
-      # If the host has ":::1 localhost" in its hosts file and if IPv6
-      # is not enabled we can get NetworkUnreachable exception...
-    rescue Errno::ENETUNREACH, Net::ReadTimeout, IO::EAGAINWaitReadable,
-        Errno::EHOSTUNREACH => e
-      sleep 0.1
-      false
-    end
-
-    def stop
-      # yes, this is terrible.
-      @server.shutdown
-      @server_thread.kill
-      @server_thread.join
-      @server_thread = nil
     end
 
+    private
+
+    attr_reader :server_thread
+
+    def create_server(**extra_options)
+      server = WEBrick::HTTPServer.new(**options, **extra_options)
+      server.mount("/", Rack::Handler::WEBrick, API.instance)
+      server
+    end
   end
 
   class API
diff --git a/spec/unit/api_client_v1_spec.rb b/spec/unit/api_client_v1_spec.rb
index 8c90d5a..9c643fa 100644
--- a/spec/unit/api_client_v1_spec.rb
+++ b/spec/unit/api_client_v1_spec.rb
@@ -326,13 +326,13 @@ describe Chef::ApiClientV1 do
   describe "Versioned API Interactions" do
     let(:response_406) { OpenStruct.new(:code => "406") }
     let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
-    let(:payload) {
+    let(:payload) do
       {
         :name => "some_name",
         :validator => true,
         :admin => true,
       }
-    }
+    end
 
     before do
       @client = Chef::ApiClientV1.new
diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb
index 6765ca9..30fc58b 100644
--- a/spec/unit/application/client_spec.rb
+++ b/spec/unit/application/client_spec.rb
@@ -74,6 +74,8 @@ describe Chef::Application::Client, "reconfigure" do
   end
 
   before do
+    Chef::Config.reset
+
     allow(Kernel).to receive(:trap).and_return(:ok)
     allow(::File).to receive(:read).and_call_original
     allow(::File).to receive(:read).with(Chef::Config.platform_specific_path("/etc/chef/client.rb")).and_return("")
@@ -141,6 +143,39 @@ describe Chef::Application::Client, "reconfigure" do
                         :daemonize => true
       end
     end
+
+    describe "--config-option" do
+      context "with a single value" do
+        it_behaves_like "sets the configuration", "--config-option chef_server_url=http://example",
+                        :chef_server_url => "http://example"
+      end
+
+      context "with two values" do
+        it_behaves_like "sets the configuration", "--config-option chef_server_url=http://example --config-option policy_name=web",
+                        :chef_server_url => "http://example", :policy_name => "web"
+      end
+
+      context "with a boolean value" do
+        it_behaves_like "sets the configuration", "--config-option minimal_ohai=true",
+                        :minimal_ohai => true
+      end
+
+      context "with an empty value" do
+        it "should terminate with message" do
+          expect(Chef::Application).to receive(:fatal!).with('Unparsable config option ""').and_raise("so ded")
+          ARGV.replace(["--config-option", ""])
+          expect { app.reconfigure }.to raise_error "so ded"
+        end
+      end
+
+      context "with an invalid value" do
+        it "should terminate with message" do
+          expect(Chef::Application).to receive(:fatal!).with('Unparsable config option "asdf"').and_raise("so ded")
+          ARGV.replace(["--config-option", "asdf"])
+          expect { app.reconfigure }.to raise_error "so ded"
+        end
+      end
+    end
   end
 
   describe "when configured to not fork the client process" do
diff --git a/spec/unit/application/solo_spec.rb b/spec/unit/application/solo_spec.rb
index bb29261..686ae74 100644
--- a/spec/unit/application/solo_spec.rb
+++ b/spec/unit/application/solo_spec.rb
@@ -120,9 +120,10 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
         expect(app).to receive(:open).with("http://junglist.gen.nz/recipes.tgz").and_yield(tarfile)
         expect(File).to receive(:open).with("#{Dir.tmpdir}/chef-solo/recipes.tgz", "wb").and_yield(target_file)
 
-        shellout = instance_double("Mixlib::ShellOut", run_command: nil, error!: nil, stdout: "")
+        archive = double(Mixlib::Archive)
 
-        expect(app).to receive(:shell_out!).with("tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo").and_return(shellout)
+        expect(Mixlib::Archive).to receive(:new).with("#{Dir.tmpdir}/chef-solo/recipes.tgz").and_return(archive)
+        expect(archive).to receive(:extract).with("#{Dir.tmpdir}/chef-solo", { perms: false, ignore: /^\.$/ })
         app.reconfigure
         expect(target_file.string).to eq("remote_tarball_content")
       end
@@ -136,11 +137,10 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
         Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks"
         expect(FileUtils).to receive(:mkdir_p).with("#{Dir.tmpdir}/chef-solo").and_return(true)
 
-        allow(Chef::Mixin::Command).to receive(:run_command).and_return(true)
+        archive = double(Mixlib::Archive)
 
-        shellout = instance_double("Mixlib::ShellOut", run_command: nil, error!: nil, stdout: "")
-
-        expect(app).to receive(:shell_out!).with("tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo").and_return(shellout)
+        expect(Mixlib::Archive).to receive(:new).with("#{Dir.tmpdir}/chef-solo/recipes.tgz").and_return(archive)
+        expect(archive).to receive(:extract).with("#{Dir.tmpdir}/chef-solo", { perms: false, ignore: /^\.$/ })
         expect(app).to receive(:fetch_recipe_tarball).ordered
         expect(Chef::ConfigFetcher).to receive(:new).ordered.and_return(config_fetcher)
         app.reconfigure
@@ -212,6 +212,13 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
       end
     end
 
+    it "sets the repo path" do
+      expect(Chef::Config).to receive(:find_chef_repo_path).and_return("/var/chef")
+      app.reconfigure
+      expect(Chef::Config.has_key?(:chef_repo_path)).to be_truthy
+      expect(Chef::Config[:chef_repo_path]).to eq ("/var/chef")
+    end
+
     it "runs chef-client in local mode" do
       allow(app).to receive(:setup_application).and_return(true)
       allow(app).to receive(:run_application).and_return(true)
diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb
index 8ab6e13..867cd3f 100644
--- a/spec/unit/application_spec.rb
+++ b/spec/unit/application_spec.rb
@@ -361,7 +361,7 @@ describe Chef::Application do
         end
       end
 
-      context 'when called with an Array-like argument (#size)' do
+      context "when called with an Array-like argument (#size)" do
         before do
           allow(app).to receive(:fork_chef_client).and_return(true)
           allow(app).to receive(:run_with_graceful_exit_option).and_return(true)
diff --git a/spec/unit/audit/audit_event_proxy_spec.rb b/spec/unit/audit/audit_event_proxy_spec.rb
index 2496aef..820e670 100644
--- a/spec/unit/audit/audit_event_proxy_spec.rb
+++ b/spec/unit/audit/audit_event_proxy_spec.rb
@@ -34,8 +34,9 @@ describe Chef::Audit::AuditEventProxy do
   describe "#example_group_started" do
 
     let(:description) { "poots" }
-    let(:group) { double("ExampleGroup", :parent_groups => parents,
-                                         :description => description) }
+    let(:group) do
+      double("ExampleGroup", :parent_groups => parents,
+                             :description => description) end
     let(:notification) { double("Notification", :group => group) }
 
     context "when notified from a top-level example group" do
@@ -120,35 +121,36 @@ describe Chef::Audit::AuditEventProxy do
 
       let(:examples) { [example] }
 
-      let(:example) { double("Example", :metadata => metadata,
-                                        :description => example_description,
-                                        :full_description => full_description, :exception => nil) }
+      let(:example) do
+        double("Example", :metadata => metadata,
+                          :description => example_description,
+                          :full_description => full_description, :exception => nil) end
 
-      let(:metadata) {
+      let(:metadata) do
         {
           :described_class => described_class,
           :example_group => example_group,
           :line_number => line,
         }
-      }
+      end
 
-      let(:example_group) {
+      let(:example_group) do
         {
           :description => group_description,
           :parent_example_group => parent_group,
         }
-      }
+      end
 
-      let(:parent_group) {
+      let(:parent_group) do
         {
           :description => control_group_name,
           :parent_example_group => nil,
         }
-      }
+      end
 
       let(:line) { 27 }
 
-      let(:control_data) {
+      let(:control_data) do
         {
           :name => example_description,
           :desc => full_description,
@@ -157,7 +159,7 @@ describe Chef::Audit::AuditEventProxy do
           :context => context,
           :line_number => line,
         }
-      }
+      end
 
       shared_examples "built control" do
 
@@ -218,12 +220,14 @@ describe Chef::Audit::AuditEventProxy do
         let(:control_group_name) { "application ports" }
         let(:group_description) { "#{resource_type} #{resource_name}" }
         let(:example_description) { "should not be listening" }
-        let(:full_description) { [control_group_name, group_description,
-          example_description].join(" ") }
+        let(:full_description) do
+          [control_group_name, group_description,
+          example_description].join(" ") end
 
         # Metadata fields
-        let(:described_class) { double("Serverspec::Type::Port",
-          :class => "Serverspec::Type::Port", :name => resource_name) }
+        let(:described_class) do
+          double("Serverspec::Type::Port",
+          :class => "Serverspec::Type::Port", :name => resource_name) end
 
         # Control data fields
         let(:resource_type) { "Port" }
@@ -246,8 +250,9 @@ describe Chef::Audit::AuditEventProxy do
         let(:control_group_name) { "application ports" }
         let(:group_description) { "port 111" }
         let(:example_description) { "is not listening" }
-        let(:full_description) { [control_group_name, group_description,
-          example_description].join(" ") }
+        let(:full_description) do
+          [control_group_name, group_description,
+          example_description].join(" ") end
 
         # Metadata fields
         let(:described_class) { nil }
@@ -276,27 +281,29 @@ describe Chef::Audit::AuditEventProxy do
         let(:outer_group_description) { "File \"tmp/audit\"" }
         let(:group_description) { "#{resource_type} #{resource_name}" }
         let(:example_description) { "is a file" }
-        let(:full_description) { [control_group_name, outer_group_description,
-          group_description, example_description].join(" ") }
+        let(:full_description) do
+          [control_group_name, outer_group_description,
+          group_description, example_description].join(" ") end
 
         # Metadata parts
-        let(:described_class) { double("Serverspec::Type::File",
-          :class => "Serverspec::Type::File", :name => resource_name) }
+        let(:described_class) do
+          double("Serverspec::Type::File",
+          :class => "Serverspec::Type::File", :name => resource_name) end
 
         # Example group parts
-        let(:parent_group) {
+        let(:parent_group) do
           {
             :description => outer_group_description,
             :parent_example_group => control_group,
           }
-        }
+        end
 
-        let(:control_group) {
+        let(:control_group) do
           {
             :description => control_group_name,
             :parent_example_group => nil,
           }
-        }
+        end
 
         # Control data parts
         let(:resource_type) { "File" }
diff --git a/spec/unit/audit/audit_reporter_spec.rb b/spec/unit/audit/audit_reporter_spec.rb
index 1db56a5..cf91626 100644
--- a/spec/unit/audit/audit_reporter_spec.rb
+++ b/spec/unit/audit/audit_reporter_spec.rb
@@ -28,8 +28,9 @@ describe Chef::Audit::AuditReporter do
   let(:run_id) { 0 }
   let(:start_time) { Time.new(2014, 12, 3, 9, 31, 05, "-08:00") }
   let(:end_time) { Time.new(2014, 12, 3, 9, 36, 14, "-08:00") }
-  let(:run_status) { instance_double(Chef::RunStatus, :node => node, :run_id => run_id,
-                                                      :start_time => start_time, :end_time => end_time) }
+  let(:run_status) do
+    instance_double(Chef::RunStatus, :node => node, :run_id => run_id,
+                                     :start_time => start_time, :end_time => end_time) end
 
   describe "#audit_phase_start" do
 
@@ -85,9 +86,10 @@ describe Chef::Audit::AuditReporter do
 
       context "when audit phase failed" do
 
-        let(:audit_error) { double("AuditError", :class => "Chef::Exceptions::AuditError",
-                                                 :message => "Audit phase failed with error message: derpderpderp",
-                                                 :backtrace => ["/path/recipe.rb:57", "/path/library.rb:106"]) }
+        let(:audit_error) do
+          double("AuditError", :class => "Chef::Exceptions::AuditError",
+                               :message => "Audit phase failed with error message: derpderpderp",
+                               :backtrace => ["/path/recipe.rb:57", "/path/library.rb:106"]) end
 
         before do
           reporter.instance_variable_set(:@audit_phase_error, audit_error)
@@ -233,13 +235,15 @@ EOM
     let(:audit_data) { Chef::Audit::AuditData.new(node.name, run_id) }
     let(:run_data) { audit_data.to_hash }
 
-    let(:audit_error) { double("AuditError", :class => "Chef::Exceptions::AuditError",
-                                             :message => "Audit phase failed with error message: derpderpderp",
-                                             :backtrace => ["/path/recipe.rb:57", "/path/library.rb:106"]) }
+    let(:audit_error) do
+      double("AuditError", :class => "Chef::Exceptions::AuditError",
+                           :message => "Audit phase failed with error message: derpderpderp",
+                           :backtrace => ["/path/recipe.rb:57", "/path/library.rb:106"]) end
 
-    let(:run_error) { double("RunError", :class => "Chef::Exceptions::RunError",
-                                         :message => "This error shouldn't be reported.",
-                                         :backtrace => ["fix it", "fix it", "fix it"]) }
+    let(:run_error) do
+      double("RunError", :class => "Chef::Exceptions::RunError",
+                         :message => "This error shouldn't be reported.",
+                         :backtrace => ["fix it", "fix it", "fix it"]) end
 
     before do
       allow(reporter).to receive(:auditing_enabled?).and_return(true)
@@ -276,23 +280,27 @@ EOM
 
   shared_context "audit data" do
 
-    let(:control_group_foo) { instance_double(Chef::Audit::ControlGroupData,
-      :metadata => double("foo metadata")) }
-    let(:control_group_bar) { instance_double(Chef::Audit::ControlGroupData,
-      :metadata => double("bar metadata")) }
+    let(:control_group_foo) do
+      instance_double(Chef::Audit::ControlGroupData,
+      :metadata => double("foo metadata")) end
+    let(:control_group_bar) do
+      instance_double(Chef::Audit::ControlGroupData,
+      :metadata => double("bar metadata")) end
 
-    let(:ordered_control_groups) {
+    let(:ordered_control_groups) do
       {
         "foo" => control_group_foo,
         "bar" => control_group_bar,
       }
-    }
+    end
 
-    let(:audit_data) { instance_double(Chef::Audit::AuditData,
-      :add_control_group => true) }
+    let(:audit_data) do
+      instance_double(Chef::Audit::AuditData,
+      :add_control_group => true) end
 
-    let(:run_context) { instance_double(Chef::RunContext,
-      :audits => ordered_control_groups) }
+    let(:run_context) do
+      instance_double(Chef::RunContext,
+      :audits => ordered_control_groups) end
 
     before do
       allow(reporter).to receive(:ordered_control_groups).and_return(ordered_control_groups)
@@ -340,8 +348,9 @@ EOM
     include_context "audit data"
 
     let(:name) { "bat" }
-    let(:control_group) { instance_double(Chef::Audit::ControlGroupData,
-      :metadata => double("metadata")) }
+    let(:control_group) do
+      instance_double(Chef::Audit::ControlGroupData,
+      :metadata => double("metadata")) end
 
     before do
       allow(Chef::Audit::ControlGroupData).to receive(:new).
diff --git a/spec/unit/audit/control_group_data_spec.rb b/spec/unit/audit/control_group_data_spec.rb
index a7c8850..ea4ac26 100644
--- a/spec/unit/audit/control_group_data_spec.rb
+++ b/spec/unit/audit/control_group_data_spec.rb
@@ -122,9 +122,10 @@ describe Chef::Audit::ControlData do
   let(:context) { nil }
   let(:line_number) { 27 }
 
-  let(:control_data) { described_class.new(name: name,
-                                           resource_type: resource_type, resource_name: resource_name,
-                                           context: context, line_number: line_number) }
+  let(:control_data) do
+    described_class.new(name: name,
+                        resource_type: resource_type, resource_name: resource_name,
+                        context: context, line_number: line_number) end
 
   describe "#to_hash" do
 
@@ -171,7 +172,7 @@ describe Chef::Audit::ControlGroupData do
     let(:context) { nil }
     let(:line_number) { 0 }
 
-    let(:control_data) {
+    let(:control_data) do
       {
         :name => name,
         :resource_type => resource_type,
@@ -179,16 +180,17 @@ describe Chef::Audit::ControlGroupData do
         :context => context,
         :line_number => line_number,
       }
-    }
+    end
 
   end
 
   shared_context "control" do
     include_context "control data"
 
-    let(:control) { Chef::Audit::ControlData.new(name: name,
-                                                 resource_type: resource_type, resource_name: resource_name,
-                                                 context: context, line_number: line_number) }
+    let(:control) do
+      Chef::Audit::ControlData.new(name: name,
+                                   resource_type: resource_type, resource_name: resource_name,
+                                   context: context, line_number: line_number) end
 
     before do
       allow(Chef::Audit::ControlData).to receive(:new).
@@ -436,15 +438,18 @@ describe Chef::Audit::ControlGroupData do
         let(:control_hash_2) { { :line_number => 13 } }
         let(:control_hash_3) { { :line_number => 35 } }
 
-        let(:control_1) { double("control 1",
+        let(:control_1) do
+          double("control 1",
           :line_number => control_hash_1[:line_number],
-          :to_hash => control_hash_1) }
-        let(:control_2) { double("control 2",
+          :to_hash => control_hash_1) end
+        let(:control_2) do
+          double("control 2",
           :line_number => control_hash_2[:line_number],
-          :to_hash => control_hash_2) }
-        let(:control_3) { double("control 3",
+          :to_hash => control_hash_2) end
+        let(:control_3) do
+          double("control 3",
           :line_number => control_hash_3[:line_number],
-          :to_hash => control_hash_3) }
+          :to_hash => control_hash_3) end
 
         let(:control_list) { [control_1, control_2, control_3] }
         let(:ordered_control_hashes) { [control_hash_2, control_hash_1, control_hash_3] }
diff --git a/spec/unit/chef_class_spec.rb b/spec/unit/chef_class_spec.rb
index 3c8411e..21987c0 100644
--- a/spec/unit/chef_class_spec.rb
+++ b/spec/unit/chef_class_spec.rb
@@ -85,7 +85,7 @@ describe "Chef class" do
     end
   end
 
-  context '#event_handler' do
+  context "#event_handler" do
     it "adds a new handler" do
       x = 1
       Chef.event_handler do
diff --git a/spec/unit/chef_fs/data_handler/group_handler_spec.rb b/spec/unit/chef_fs/data_handler/group_handler_spec.rb
index 849bc84..ac02521 100644
--- a/spec/unit/chef_fs/data_handler/group_handler_spec.rb
+++ b/spec/unit/chef_fs/data_handler/group_handler_spec.rb
@@ -29,7 +29,7 @@ class TestEntry < Mash
 end
 
 describe Chef::ChefFS::DataHandler::GroupDataHandler do
-  describe '#normalize_for_post' do
+  describe "#normalize_for_post" do
     let(:entry) do
       TestEntry.new("workers.json", "hive")
     end
diff --git a/spec/unit/chef_fs/diff_spec.rb b/spec/unit/chef_fs/diff_spec.rb
index c1c9050..d946fcb 100644
--- a/spec/unit/chef_fs/diff_spec.rb
+++ b/spec/unit/chef_fs/diff_spec.rb
@@ -32,7 +32,7 @@ describe "diff", :uses_diff => true do
   include FileSystemSupport
 
   context "with two filesystems with all types of difference" do
-    let(:a) {
+    let(:a) do
       memory_fs("a", {
         :both_dirs => {
           :sub_both_dirs => { :subsub => nil },
@@ -58,8 +58,8 @@ describe "diff", :uses_diff => true do
         :dir_in_a_file_in_b => {},
         :file_in_a_dir_in_b => nil,
       }, /cannot_be_in_a/)
-    }
-    let(:b) {
+    end
+    let(:b) do
       memory_fs("b", {
         :both_dirs => {
           :sub_both_dirs => { :subsub => nil },
@@ -85,7 +85,7 @@ describe "diff", :uses_diff => true do
         :dir_in_a_file_in_b => nil,
         :file_in_a_dir_in_b => {},
       }, /cannot_be_in_b/)
-    }
+    end
     it "Chef::ChefFS::CommandLine.diff_print(/)" do
       results = []
       Chef::ChefFS::CommandLine.diff_print(pattern("/"), a, b, nil, nil) do |diff|
diff --git a/spec/unit/chef_fs/file_system/operation_failed_error_spec.rb b/spec/unit/chef_fs/file_system/operation_failed_error_spec.rb
index b0b5a54..7f3eb6e 100644
--- a/spec/unit/chef_fs/file_system/operation_failed_error_spec.rb
+++ b/spec/unit/chef_fs/file_system/operation_failed_error_spec.rb
@@ -30,17 +30,17 @@ describe Chef::ChefFS::FileSystem::OperationFailedError do
         allow(@response).to receive(:code).and_return("400")
         allow(@response).to receive(:body).and_return(response_body)
         exception = Net::HTTPServerException.new("(exception) unauthorized", @response)
-        expect {
+        expect do
           raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, exception), error_message
-        }.to raise_error(Chef::ChefFS::FileSystem::OperationFailedError, "#{error_message} cause: #{response_body}")
+        end.to raise_error(Chef::ChefFS::FileSystem::OperationFailedError, "#{error_message} cause: #{response_body}")
       end
     end
 
     context "does not have a cause attribute" do
       it "does not include error cause" do
-        expect {
+        expect do
           raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self), error_message
-        }.to raise_error(Chef::ChefFS::FileSystem::OperationFailedError, error_message)
+        end.to raise_error(Chef::ChefFS::FileSystem::OperationFailedError, error_message)
       end
     end
   end
diff --git a/spec/unit/chef_fs/file_system/repository/directory_spec.rb b/spec/unit/chef_fs/file_system/repository/directory_spec.rb
index e050181..6e53e52 100644
--- a/spec/unit/chef_fs/file_system/repository/directory_spec.rb
+++ b/spec/unit/chef_fs/file_system/repository/directory_spec.rb
@@ -76,6 +76,7 @@ describe Chef::ChefFS::FileSystem::Repository::Directory do
   context "#create_child" do
     it "creates a new TestFile" do
       expect(TestFile).to receive(:new).with("test_child", test_directory).and_return(file_double)
+      allow(file_double).to receive(:file_path).and_return("#{test_directory}/test_child")
       expect(file_double).to receive(:write).with("test")
       test_directory.create_child("test_child", "test")
     end
diff --git a/spec/unit/chef_fs/file_system_spec.rb b/spec/unit/chef_fs/file_system_spec.rb
index 429e039..5c74729 100644
--- a/spec/unit/chef_fs/file_system_spec.rb
+++ b/spec/unit/chef_fs/file_system_spec.rb
@@ -55,7 +55,7 @@ describe Chef::ChefFS::FileSystem do
   end
 
   context "with a populated filesystem" do
-    let(:fs) {
+    let(:fs) do
       memory_fs("", {
         :a => {
           :aa => {
@@ -69,7 +69,7 @@ describe Chef::ChefFS::FileSystem do
         :x => "",
         :y => {},
       })
-    }
+    end
     context "list" do
       it "/**" do
         list_should_yield_paths(fs, "/**", "/", "/a", "/x", "/y", "/a/aa", "/a/aa/c", "/a/aa/zz", "/a/ab", "/a/ab/c")
diff --git a/spec/unit/chef_fs/parallelizer.rb b/spec/unit/chef_fs/parallelizer.rb
index 9de8077..84637f7 100644
--- a/spec/unit/chef_fs/parallelizer.rb
+++ b/spec/unit/chef_fs/parallelizer.rb
@@ -35,9 +35,9 @@ describe Chef::ChefFS::Parallelizer do
 
     context "With :ordered => false (unordered output)" do
       it "An empty input produces an empty output" do
-        parallelize([], :ordered => false) do
+        expect(parallelize([], :ordered => false) do
           sleep 10
-        end.to_a == []
+        end.to_a).to eql([])
         expect(elapsed_time).to be < 0.1
       end
 
@@ -107,9 +107,9 @@ describe Chef::ChefFS::Parallelizer do
 
     context "With :ordered => true (ordered output)" do
       it "An empty input produces an empty output" do
-        parallelize([]) do
+        expect(parallelize([]) do
           sleep 10
-        end.to_a == []
+        end.to_a).to eql([])
         expect(elapsed_time).to be < 0.1
       end
 
@@ -211,9 +211,7 @@ describe Chef::ChefFS::Parallelizer do
             occupying_job_finished[0] = true
           end.wait
         end
-        until started
-          sleep(0.01)
-        end
+        sleep(0.01) until started
       end
 
       after :each do
diff --git a/spec/unit/cookbook/metadata_spec.rb b/spec/unit/cookbook/metadata_spec.rb
index c6d7e41..27666eb 100644
--- a/spec/unit/cookbook/metadata_spec.rb
+++ b/spec/unit/cookbook/metadata_spec.rb
@@ -461,21 +461,21 @@ describe Chef::Cookbook::Metadata do
       expect(metadata.grouping("/db/mysql/databases/tuning", group)).to eq(group)
     end
     it "should not accept anything but a string for display_name" do
-      expect {
+      expect do
         metadata.grouping("db/mysql/databases", :title => "foo")
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.grouping("db/mysql/databases", :title => Hash.new)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "should not accept anything but a string for the description" do
-      expect {
+      expect do
         metadata.grouping("db/mysql/databases", :description => "foo")
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.grouping("db/mysql/databases", :description => Hash.new)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
   end
 
@@ -498,63 +498,63 @@ describe Chef::Cookbook::Metadata do
     end
 
     it "should not accept anything but a string for display_name" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :display_name => "foo")
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :display_name => Hash.new)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "should not accept anything but a string for the description" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :description => "foo")
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :description => Hash.new)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "should not accept anything but a string for the source_url" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :source_url => "foo")
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :source_url => Hash.new)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "should not accept anything but a string for the issues_url" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :issues_url => "foo")
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :issues_url => Hash.new)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "should not accept anything but true or false for the privacy flag" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :privacy => true)
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :privacy => false)
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :privacy => "true")
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "should not accept anything but an array of strings for choice" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :choice => %w{dedicated shared})
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :choice => [10, "shared"])
-      }.to raise_error(ArgumentError)
-      expect {
+      end.to raise_error(ArgumentError)
+      expect do
         metadata.attribute("db/mysql/databases", :choice => Hash.new)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "should set choice to empty array by default" do
@@ -563,15 +563,15 @@ describe Chef::Cookbook::Metadata do
     end
 
     it "should let calculated be true or false" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :calculated => true)
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :calculated => false)
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :calculated => Hash.new)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "should set calculated to false by default" do
@@ -580,55 +580,55 @@ describe Chef::Cookbook::Metadata do
     end
 
     it "accepts String for the attribute type" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :type => "string")
-      }.not_to raise_error
+      end.not_to raise_error
     end
 
     it "accepts Array for the attribute type" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :type => "array")
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :type => Array.new)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "accepts symbol for the attribute type" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :type => "symbol")
-      }.not_to raise_error
+      end.not_to raise_error
     end
 
     it "should let type be hash (backwards compatibility only)" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :type => "hash")
-      }.not_to raise_error
+      end.not_to raise_error
     end
 
     it "should let required be required, recommended or optional" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :required => "required")
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :required => "recommended")
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :required => "optional")
-      }.not_to raise_error
+      end.not_to raise_error
     end
 
     it "should convert required true to required" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :required => true)
-      }.not_to raise_error
+      end.not_to raise_error
       #attrib = metadata.attributes["db/mysql/databases"][:required].should == "required"
     end
 
     it "should convert required false to optional" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :required => false)
-      }.not_to raise_error
+      end.not_to raise_error
       #attrib = metadata.attributes["db/mysql/databases"][:required].should == "optional"
     end
 
@@ -638,12 +638,12 @@ describe Chef::Cookbook::Metadata do
     end
 
     it "should make sure recipes is an array" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :recipes => [])
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :required => Hash.new)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "should set recipes to an empty array by default" do
@@ -652,24 +652,24 @@ describe Chef::Cookbook::Metadata do
     end
 
     it "should allow the default value to be a string, array, hash, boolean or numeric" do
-      expect {
+      expect do
         metadata.attribute("db/mysql/databases", :default => [])
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :default => {})
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :default => "alice in chains")
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :default => 1337)
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :default => true)
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         metadata.attribute("db/mysql/databases", :required => :not_gonna_do_it)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "should limit the types allowed in the choice array" do
@@ -678,87 +678,87 @@ describe Chef::Cookbook::Metadata do
         :choice => %w{test1 test2},
         :default => "test1",
       }
-      expect {
+      expect do
         metadata.attribute("test_cookbook/test", options)
-      }.not_to raise_error
+      end.not_to raise_error
 
       options = {
         :type => "boolean",
         :choice => [ true, false ],
         :default => true,
       }
-      expect {
+      expect do
         metadata.attribute("test_cookbook/test", options)
-      }.not_to raise_error
+      end.not_to raise_error
 
       options = {
         :type => "numeric",
         :choice => [ 1337, 420 ],
         :default => 1337,
       }
-      expect {
+      expect do
         metadata.attribute("test_cookbook/test", options)
-      }.not_to raise_error
+      end.not_to raise_error
 
       options = {
         :type => "numeric",
         :choice => [ true, "false" ],
         :default => false,
       }
-      expect {
+      expect do
         metadata.attribute("test_cookbook/test", options)
-      }.to raise_error(Chef::Exceptions::ValidationFailed)
+      end.to raise_error(Chef::Exceptions::ValidationFailed)
     end
 
     it "should error if default used with calculated" do
-      expect {
+      expect do
         attrs = {
           :calculated => true,
           :default => [ "I thought you said calculated" ],
         }
         metadata.attribute("db/mysql/databases", attrs)
-      }.to raise_error(ArgumentError)
-      expect {
+      end.to raise_error(ArgumentError)
+      expect do
         attrs = {
           :calculated => true,
           :default => "I thought you said calculated",
         }
         metadata.attribute("db/mysql/databases", attrs)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "should allow a default that is a choice" do
-      expect {
+      expect do
         attrs = {
           :choice => %w{a b c},
           :default => "b",
         }
         metadata.attribute("db/mysql/databases", attrs)
-      }.not_to raise_error
-      expect {
+      end.not_to raise_error
+      expect do
         attrs = {
           :choice => %w{a b c d e},
           :default => %w{b d},
         }
         metadata.attribute("db/mysql/databases", attrs)
-      }.not_to raise_error
+      end.not_to raise_error
     end
 
     it "should error if default is not a choice" do
-      expect {
+      expect do
         attrs = {
           :choice => %w{a b c},
           :default => "d",
         }
         metadata.attribute("db/mysql/databases", attrs)
-      }.to raise_error(ArgumentError)
-      expect {
+      end.to raise_error(ArgumentError)
+      expect do
         attrs = {
           :choice => %w{a b c d e},
           :default => %w{b z},
         }
         metadata.attribute("db/mysql/databases", attrs)
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
   end
 
diff --git a/spec/unit/cookbook/synchronizer_spec.rb b/spec/unit/cookbook/synchronizer_spec.rb
index 3f5624f..8287627 100644
--- a/spec/unit/cookbook/synchronizer_spec.rb
+++ b/spec/unit/cookbook/synchronizer_spec.rb
@@ -414,6 +414,13 @@ describe Chef::CookbookSynchronizer do
       and_return("/file-cache/cookbooks/cookbook_a/templates/default/apache2.conf.erb")
   end
 
+  describe "#server_api" do
+    it "sets keepalive to true" do
+      expect(Chef::ServerAPI).to receive(:new).with(Chef::Config[:chef_server_url], keepalives: true)
+      synchronizer.server_api
+    end
+  end
+
   describe "when syncing cookbooks with the server" do
     let(:server_api) { double("Chef::ServerAPI (mock)") }
 
diff --git a/spec/unit/cookbook_loader_spec.rb b/spec/unit/cookbook_loader_spec.rb
index db10a4e..eef5d2a 100644
--- a/spec/unit/cookbook_loader_spec.rb
+++ b/spec/unit/cookbook_loader_spec.rb
@@ -112,63 +112,63 @@ describe Chef::CookbookLoader do
       end
 
       it "should allow you to override an attribute file via cookbook_path" do
-        expect(cookbook_loader[:openldap].attribute_filenames.detect { |f|
+        expect(cookbook_loader[:openldap].attribute_filenames.detect do |f|
           f =~ /cookbooks\/openldap\/attributes\/default.rb/
-        }).not_to eql(nil)
-        expect(cookbook_loader[:openldap].attribute_filenames.detect { |f|
+        end).not_to eql(nil)
+        expect(cookbook_loader[:openldap].attribute_filenames.detect do |f|
           f =~ /kitchen\/openldap\/attributes\/default.rb/
-        }).to eql(nil)
+        end).to eql(nil)
       end
 
       it "should load different attribute files from deeper paths" do
-        expect(cookbook_loader[:openldap].attribute_filenames.detect { |f|
+        expect(cookbook_loader[:openldap].attribute_filenames.detect do |f|
           f =~ /kitchen\/openldap\/attributes\/robinson.rb/
-        }).not_to eql(nil)
+        end).not_to eql(nil)
       end
 
       it "should allow you to override a definition file via cookbook_path" do
-        expect(cookbook_loader[:openldap].definition_filenames.detect { |f|
+        expect(cookbook_loader[:openldap].definition_filenames.detect do |f|
           f =~ /cookbooks\/openldap\/definitions\/client.rb/
-        }).not_to eql(nil)
-        expect(cookbook_loader[:openldap].definition_filenames.detect { |f|
+        end).not_to eql(nil)
+        expect(cookbook_loader[:openldap].definition_filenames.detect do |f|
           f =~ /kitchen\/openldap\/definitions\/client.rb/
-        }).to eql(nil)
+        end).to eql(nil)
       end
 
       it "should load definition files from deeper paths" do
-        expect(cookbook_loader[:openldap].definition_filenames.detect { |f|
+        expect(cookbook_loader[:openldap].definition_filenames.detect do |f|
           f =~ /kitchen\/openldap\/definitions\/drewbarrymore.rb/
-        }).not_to eql(nil)
+        end).not_to eql(nil)
       end
 
       it "should allow you to override a recipe file via cookbook_path" do
-        expect(cookbook_loader[:openldap].recipe_filenames.detect { |f|
+        expect(cookbook_loader[:openldap].recipe_filenames.detect do |f|
           f =~ /cookbooks\/openldap\/recipes\/gigantor.rb/
-        }).not_to eql(nil)
-        expect(cookbook_loader[:openldap].recipe_filenames.detect { |f|
+        end).not_to eql(nil)
+        expect(cookbook_loader[:openldap].recipe_filenames.detect do |f|
           f =~ /kitchen\/openldap\/recipes\/gigantor.rb/
-        }).to eql(nil)
+        end).to eql(nil)
       end
 
       it "should load recipe files from deeper paths" do
-        expect(cookbook_loader[:openldap].recipe_filenames.detect { |f|
+        expect(cookbook_loader[:openldap].recipe_filenames.detect do |f|
           f =~ /kitchen\/openldap\/recipes\/woot.rb/
-        }).not_to eql(nil)
+        end).not_to eql(nil)
       end
 
       it "should allow you to have an 'ignore' file, which skips loading files in later cookbooks" do
-        expect(cookbook_loader[:openldap].recipe_filenames.detect { |f|
+        expect(cookbook_loader[:openldap].recipe_filenames.detect do |f|
           f =~ /kitchen\/openldap\/recipes\/ignoreme.rb/
-        }).to eql(nil)
+        end).to eql(nil)
       end
 
       it "should find files that start with a ." do
-        expect(cookbook_loader[:openldap].file_filenames.detect { |f|
+        expect(cookbook_loader[:openldap].file_filenames.detect do |f|
           f =~ /\.dotfile$/
-        }).to match(/\.dotfile$/)
-        expect(cookbook_loader[:openldap].file_filenames.detect { |f|
+        end).to match(/\.dotfile$/)
+        expect(cookbook_loader[:openldap].file_filenames.detect do |f|
           f =~ /\.ssh\/id_rsa$/
-        }).to match(/\.ssh\/id_rsa$/)
+        end).to match(/\.ssh\/id_rsa$/)
       end
 
       it "should load the metadata for the cookbook" do
diff --git a/spec/unit/cookbook_version_file_specificity_spec.rb b/spec/unit/cookbook_version_file_specificity_spec.rb
index df9e657..3b5450c 100644
--- a/spec/unit/cookbook_version_file_specificity_spec.rb
+++ b/spec/unit/cookbook_version_file_specificity_spec.rb
@@ -304,24 +304,24 @@ describe Chef::CookbookVersion, "file specificity" do
   it "should raise a FileNotFound exception without match" do
     node = Chef::Node.new
 
-    expect {
+    expect do
       @cookbook.preferred_manifest_record(node, :files, "doesn't_exist.rb")
-    }.to raise_error(Chef::Exceptions::FileNotFound)
+    end.to raise_error(Chef::Exceptions::FileNotFound)
   end
   it "should raise a FileNotFound exception consistently without match" do
     node = Chef::Node.new
 
-    expect {
+    expect do
       @cookbook.preferred_manifest_record(node, :files, "doesn't_exist.rb")
-    }.to raise_error(Chef::Exceptions::FileNotFound)
+    end.to raise_error(Chef::Exceptions::FileNotFound)
 
-    expect {
+    expect do
       @cookbook.preferred_manifest_record(node, :files, "doesn't_exist.rb")
-    }.to raise_error(Chef::Exceptions::FileNotFound)
+    end.to raise_error(Chef::Exceptions::FileNotFound)
 
-    expect {
+    expect do
       @cookbook.preferred_manifest_record(node, :files, "doesn't_exist.rb")
-    }.to raise_error(Chef::Exceptions::FileNotFound)
+    end.to raise_error(Chef::Exceptions::FileNotFound)
   end
 
   describe "when fetching the contents of a directory by file specificity" do
diff --git a/spec/unit/cookbook_version_spec.rb b/spec/unit/cookbook_version_spec.rb
index 7def2f5..81ea161 100644
--- a/spec/unit/cookbook_version_spec.rb
+++ b/spec/unit/cookbook_version_spec.rb
@@ -120,8 +120,8 @@ describe Chef::CookbookVersion do
       # Used to test file-specificity related file lookups
       let(:node) do
         Chef::Node.new.tap do |n|
-          n.set[:platform] = "ubuntu"
-          n.set[:platform_version] = "13.04"
+          n.normal[:platform] = "ubuntu"
+          n.normal[:platform_version] = "13.04"
           n.name("testing")
         end
       end
@@ -203,8 +203,8 @@ describe Chef::CookbookVersion do
     # Used to test file-specificity related file lookups
     let(:node) do
       Chef::Node.new.tap do |n|
-        n.set[:platform] = "ubuntu"
-        n.set[:platform_version] = "13.04"
+        n.normal[:platform] = "ubuntu"
+        n.normal[:platform_version] = "13.04"
         n.name("testing")
       end
     end
diff --git a/spec/unit/daemon_spec.rb b/spec/unit/daemon_spec.rb
index e31ce7d..ae3d626 100644
--- a/spec/unit/daemon_spec.rb
+++ b/spec/unit/daemon_spec.rb
@@ -22,7 +22,7 @@ describe Chef::Daemon do
   before do
     if windows?
       mock_struct = #Struct::Passwd.new(nil, nil, 111, 111)
-      mock_struct = OpenStruct.new(:uid => 2342, :gid => 2342)
+        mock_struct = OpenStruct.new(:uid => 2342, :gid => 2342)
       allow(Etc).to receive(:getpwnam).and_return mock_struct
       allow(Etc).to receive(:getgrnam).and_return mock_struct
       # mock unimplemented methods
diff --git a/spec/unit/data_bag_item_spec.rb b/spec/unit/data_bag_item_spec.rb
index 2711fca..e83f0ca 100644
--- a/spec/unit/data_bag_item_spec.rb
+++ b/spec/unit/data_bag_item_spec.rb
@@ -91,12 +91,12 @@ describe Chef::DataBagItem do
   end
 
   describe "object_name" do
-    let(:data_bag_item) {
+    let(:data_bag_item) do
       data_bag_item = Chef::DataBagItem.new
       data_bag_item.data_bag("dreams")
       data_bag_item.raw_data = { "id" => "the_beatdown" }
       data_bag_item
-    }
+    end
 
     it "should return an object name based on the bag name and the raw_data id" do
       expect(data_bag_item.object_name).to eq("data_bag_item_dreams_the_beatdown")
@@ -110,12 +110,12 @@ describe Chef::DataBagItem do
   end
 
   describe "class method name" do
-    let(:data_bag_item) {
+    let(:data_bag_item) do
       data_bag_item = Chef::DataBagItem.new
       data_bag_item.data_bag("dreams")
       data_bag_item.raw_data = { "id" => "the_beatdown", "name" => "Bruce" }
       data_bag_item
-    }
+    end
 
     it "should return the object name" do
       expect(data_bag_item.name).to eq(data_bag_item.object_name)
@@ -128,11 +128,11 @@ describe Chef::DataBagItem do
   end
 
   describe "when used like a Hash" do
-    let(:data_bag_item) {
+    let(:data_bag_item) do
       data_bag_item = Chef::DataBagItem.new
       data_bag_item.raw_data = { "id" => "journey", "trials" => "been through" }
       data_bag_item
-    }
+    end
 
     it "responds to keys" do
       expect(data_bag_item.keys).to include("id")
@@ -158,9 +158,9 @@ describe Chef::DataBagItem do
 
   describe "from_hash" do
     context "when hash contains raw_data" do
-      let(:data_bag_item) {
+      let(:data_bag_item) do
         Chef::DataBagItem.from_hash({ "raw_data" => { "id" => "whoa", "name" => "Bruce", "i_know" => "kung_fu" } })
-      }
+      end
 
       it "should have the id key set" do
         expect(data_bag_item["id"]).to eq("whoa")
@@ -172,9 +172,9 @@ describe Chef::DataBagItem do
     end
 
     context "when hash does not contain raw_data" do
-      let(:data_bag_item) {
+      let(:data_bag_item) do
         Chef::DataBagItem.from_hash({ "id" => "whoa", "name" => "Bruce", "i_know" => "kung_fu" })
-      }
+      end
 
       it "should have the id key set" do
         expect(data_bag_item["id"]).to eq("whoa")
@@ -187,12 +187,12 @@ describe Chef::DataBagItem do
   end
 
   describe "to_hash" do
-    let(:data_bag_item) {
+    let(:data_bag_item) do
       data_bag_item = Chef::DataBagItem.new
       data_bag_item.data_bag("still_lost")
       data_bag_item.raw_data = { "id" => "whoa", "name" => "Bruce", "i_know" => "kung_fu" }
       data_bag_item
-    }
+    end
 
     let!(:original_data_bag_keys) { data_bag_item.keys }
 
@@ -223,12 +223,12 @@ describe Chef::DataBagItem do
   end
 
   describe "when deserializing from JSON" do
-    let(:data_bag_item) {
+    let(:data_bag_item) do
       data_bag_item = Chef::DataBagItem.new
       data_bag_item.data_bag("mars_volta")
       data_bag_item.raw_data = { "id" => "octahedron", "name" => "Bruce", "snooze" => { "finally" => :world_will } }
       data_bag_item
-    }
+    end
 
     let(:deserial) { Chef::DataBagItem.from_hash(Chef::JSONCompat.parse(Chef::JSONCompat.to_json(data_bag_item))) }
 
@@ -275,13 +275,13 @@ describe Chef::DataBagItem do
   describe "save" do
     let(:server) { instance_double(Chef::ServerAPI) }
 
-    let(:data_bag_item) {
+    let(:data_bag_item) do
       data_bag_item = Chef::DataBagItem.new
       data_bag_item["id"] = "heart of darkness"
       data_bag_item.raw_data = { "id" => "heart_of_darkness", "author" => "Conrad" }
       data_bag_item.data_bag("books")
       data_bag_item
-    }
+    end
 
     before do
       expect(Chef::ServerAPI).to receive(:new).and_return(server)
@@ -320,12 +320,12 @@ describe Chef::DataBagItem do
   describe "destroy" do
     let(:server) { instance_double(Chef::ServerAPI) }
 
-    let(:data_bag_item) {
+    let(:data_bag_item) do
       data_bag_item = Chef::DataBagItem.new
       data_bag_item.data_bag("a_baggy_bag")
       data_bag_item.raw_data = { "id" => "some_id" }
       data_bag_item
-    }
+    end
 
     it "should set default parameters" do
       expect(Chef::ServerAPI).to receive(:new).and_return(server)
diff --git a/spec/unit/data_bag_spec.rb b/spec/unit/data_bag_spec.rb
index 84aa724..cadd609 100644
--- a/spec/unit/data_bag_spec.rb
+++ b/spec/unit/data_bag_spec.rb
@@ -241,9 +241,9 @@ describe Chef::DataBag do
       it "should raise an error if the configured data_bag_path is invalid" do
         file_dir_stub(@paths.first, false)
 
-        expect {
+        expect do
           Chef::DataBag.load("foo")
-        }.to raise_error Chef::Exceptions::InvalidDataBagPath, "Data bag path '/var/chef/data_bags' is invalid"
+        end.to raise_error Chef::Exceptions::InvalidDataBagPath, "Data bag path '/var/chef/data_bags' is invalid"
       end
 
     end
diff --git a/spec/unit/data_collector/messages/helpers_spec.rb b/spec/unit/data_collector/messages/helpers_spec.rb
index 0ed0f6c..b0d9f4d 100644
--- a/spec/unit/data_collector/messages/helpers_spec.rb
+++ b/spec/unit/data_collector/messages/helpers_spec.rb
@@ -25,7 +25,7 @@ class TestMessage
 end
 
 describe Chef::DataCollector::Messages::Helpers do
-  describe '#organization' do
+  describe "#organization" do
     context "when the run is a solo run" do
       it "returns the data collector organization" do
         allow(TestMessage).to receive(:solo_run?).and_return(true)
@@ -43,7 +43,7 @@ describe Chef::DataCollector::Messages::Helpers do
     end
   end
 
-  describe '#data_collector_organization' do
+  describe "#data_collector_organization" do
     context "when the org is specified in the config" do
       it "returns the org from the config" do
         Chef::Config[:data_collector][:organization] = "org1"
@@ -58,7 +58,7 @@ describe Chef::DataCollector::Messages::Helpers do
     end
   end
 
-  describe '#chef_server_organization' do
+  describe "#chef_server_organization" do
     context "when the URL is properly formatted" do
       it "returns the org from the parsed URL" do
         Chef::Config[:chef_server_url] = "http://mycompany.com/organizations/myorg"
@@ -74,7 +74,7 @@ describe Chef::DataCollector::Messages::Helpers do
     end
   end
 
-  describe '#collector_source' do
+  describe "#collector_source" do
     context "when the run is a solo run" do
       it "returns chef_solo" do
         allow(TestMessage).to receive(:solo_run?).and_return(true)
@@ -90,7 +90,7 @@ describe Chef::DataCollector::Messages::Helpers do
     end
   end
 
-  describe '#solo_run?' do
+  describe "#solo_run?" do
     context "when :solo is set in Chef::Config" do
       it "returns true" do
         Chef::Config[:solo] = true
@@ -116,7 +116,7 @@ describe Chef::DataCollector::Messages::Helpers do
     end
   end
 
-  describe '#node_uuid' do
+  describe "#node_uuid" do
     context "when the node UUID can be read" do
       it "returns the read-in node UUID" do
         allow(TestMessage).to receive(:read_node_uuid).and_return("read_uuid")
@@ -133,7 +133,7 @@ describe Chef::DataCollector::Messages::Helpers do
     end
   end
 
-  describe '#generate_node_uuid' do
+  describe "#generate_node_uuid" do
     it "generates a new UUID, stores it, and returns it" do
       expect(SecureRandom).to receive(:uuid).and_return("generated_uuid")
       expect(TestMessage).to receive(:update_metadata).with("node_uuid", "generated_uuid")
@@ -141,7 +141,7 @@ describe Chef::DataCollector::Messages::Helpers do
     end
   end
 
-  describe '#read_node_uuid' do
+  describe "#read_node_uuid" do
     it "reads the node UUID from metadata" do
       expect(TestMessage).to receive(:metadata).and_return({ "node_uuid" => "read_uuid" })
       expect(TestMessage.read_node_uuid).to eq("read_uuid")
@@ -170,21 +170,17 @@ describe Chef::DataCollector::Messages::Helpers do
     end
   end
 
-  describe '#update_metadata' do
-    let(:metadata) { double("metadata") }
-
+  describe "#update_metadata" do
     it "updates the file" do
       allow(TestMessage).to receive(:metadata_filename).and_return("fake_metadata_file.json")
-      allow(TestMessage).to receive(:metadata).and_return(metadata)
-      expect(metadata).to receive(:[]=).with("new_key", "new_value")
-      expect(metadata).to receive(:to_json).and_return("metadata_json")
+      allow(TestMessage).to receive(:metadata).and_return({ "key" => "current_value" })
       expect(Chef::FileCache).to receive(:store).with(
         "fake_metadata_file.json",
-        "metadata_json",
+        '{"key":"updated_value"}',
         0644
       )
 
-      TestMessage.update_metadata("new_key", "new_value")
+      TestMessage.update_metadata("key", "updated_value")
     end
   end
 end
diff --git a/spec/unit/data_collector/messages_spec.rb b/spec/unit/data_collector/messages_spec.rb
index aacca64..394f18d 100644
--- a/spec/unit/data_collector/messages_spec.rb
+++ b/spec/unit/data_collector/messages_spec.rb
@@ -18,10 +18,11 @@
 #
 
 require "spec_helper"
+require "ffi_yajl"
 require "chef/data_collector/messages/helpers"
 
 describe Chef::DataCollector::Messages do
-  describe '#run_start_message' do
+  describe "#run_start_message" do
     let(:run_status) { Chef::RunStatus.new(Chef::Node.new, Chef::EventDispatch::Dispatcher.new) }
     let(:required_fields) do
       %w{
@@ -60,13 +61,15 @@ describe Chef::DataCollector::Messages do
     end
   end
 
-  describe '#run_end_message' do
-    let(:run_status)    { Chef::RunStatus.new(Chef::Node.new, Chef::EventDispatch::Dispatcher.new) }
-    let(:resource)      { double("resource", for_json: "resource_data") }
+  describe "#run_end_message" do
+    let(:node)       { Chef::Node.new }
+    let(:run_status) { Chef::RunStatus.new(node, Chef::EventDispatch::Dispatcher.new) }
+    let(:report1)  { double("report1", report_data: { "status" => "updated" }) }
+    let(:report2)  { double("report2", report_data: { "status" => "skipped" }) }
     let(:reporter_data) do
       {
         run_status: run_status,
-        updated_resources: [resource],
+        resources: [report1, report2],
       }
     end
 
@@ -75,6 +78,20 @@ describe Chef::DataCollector::Messages do
       allow(run_status).to receive(:end_time).and_return(Time.now)
     end
 
+    it "includes a valid node object in the payload" do
+      message = Chef::DataCollector::Messages.run_end_message(reporter_data)
+      expect(message["node"]).to be_an_instance_of(Chef::Node)
+    end
+
+    it "returns a sane JSON representation of the node object" do
+      node.chef_environment = "my_test_environment"
+      node.run_list.add("recipe[my_test_cookbook::default]")
+      message = FFI_Yajl::Parser.parse(Chef::DataCollector::Messages.run_end_message(reporter_data).to_json)
+
+      expect(message["node"]["chef_environment"]).to eq("my_test_environment")
+      expect(message["node"]["run_list"]).to eq(["recipe[my_test_cookbook::default]"])
+    end
+
     context "when the run was successful" do
       let(:required_fields) do
         %w{
@@ -85,6 +102,7 @@ describe Chef::DataCollector::Messages do
           expanded_run_list
           message_type
           message_version
+          node
           node_name
           organization_name
           resources
@@ -116,6 +134,12 @@ describe Chef::DataCollector::Messages do
         end
         expect(extra_fields).to eq([])
       end
+
+      it "only includes updated resources in its count" do
+        message = Chef::DataCollector::Messages.run_end_message(reporter_data)
+        expect(message["total_resource_count"]).to eq(2)
+        expect(message["updated_resource_count"]).to eq(1)
+      end
     end
 
     context "when the run was not successful" do
@@ -129,6 +153,7 @@ describe Chef::DataCollector::Messages do
           expanded_run_list
           message_type
           message_version
+          node
           node_name
           organization_name
           resources
@@ -162,46 +187,4 @@ describe Chef::DataCollector::Messages do
       end
     end
   end
-
-  describe '#node_update_message' do
-    let(:run_status) { Chef::RunStatus.new(Chef::Node.new, Chef::EventDispatch::Dispatcher.new) }
-
-    let(:required_fields) do
-      %w{
-        entity_name
-        entity_type
-        entity_uuid
-        id
-        message_type
-        message_version
-        organization_name
-        recorded_at
-        remote_hostname
-        requestor_name
-        requestor_type
-        run_id
-        service_hostname
-        source
-        task
-        user_agent
-      }
-    end
-    let(:optional_fields) { %w{data} }
-
-    it "is not missing any required fields" do
-      missing_fields = required_fields.select do |key|
-        !Chef::DataCollector::Messages.node_update_message(run_status).key?(key)
-      end
-
-      expect(missing_fields).to eq([])
-    end
-
-    it "does not have any extra fields" do
-      extra_fields = Chef::DataCollector::Messages.node_update_message(run_status).keys.select do |key|
-        !required_fields.include?(key) && !optional_fields.include?(key)
-      end
-
-      expect(extra_fields).to eq([])
-    end
-  end
 end
diff --git a/spec/unit/data_collector_spec.rb b/spec/unit/data_collector_spec.rb
index 6065cbc..37df758 100644
--- a/spec/unit/data_collector_spec.rb
+++ b/spec/unit/data_collector_spec.rb
@@ -20,6 +20,7 @@
 
 require "spec_helper"
 require "chef/data_collector"
+require "chef/resource_builder"
 
 describe Chef::DataCollector do
   describe ".register_reporter?" do
@@ -155,7 +156,11 @@ describe Chef::DataCollector::Reporter do
   let(:reporter) { described_class.new }
   let(:run_status) { Chef::RunStatus.new(Chef::Node.new, Chef::EventDispatch::Dispatcher.new) }
 
-  describe '#run_started' do
+  before do
+    Chef::Config[:data_collector][:server_url] = "http://my-data-collector-server.mycompany.com"
+  end
+
+  describe "#run_started" do
     before do
       allow(reporter).to receive(:update_run_status)
       allow(reporter).to receive(:send_to_data_collector)
@@ -177,7 +182,7 @@ describe Chef::DataCollector::Reporter do
     end
   end
 
-  describe '#run_completed' do
+  describe "#run_completed" do
     it "sends the run completion" do
       node = Chef::Node.new
 
@@ -186,17 +191,39 @@ describe Chef::DataCollector::Reporter do
     end
   end
 
-  describe '#run_failed' do
+  describe "#run_failed" do
     it "updates the exception and sends the run completion" do
       expect(reporter).to receive(:send_run_completion).with(status: "failure")
       reporter.run_failed("test_exception")
     end
   end
 
-  describe '#resource_current_state_loaded' do
+  describe "#converge_start" do
+    it "stashes the run_context for later use" do
+      reporter.converge_start("test_context")
+      expect(reporter.run_context).to eq("test_context")
+    end
+  end
+
+  describe "#converge_complete" do
+    it "detects and processes any unprocessed resources" do
+      expect(reporter).to receive(:detect_unprocessed_resources)
+      reporter.converge_complete
+    end
+  end
+
+  describe "#converge_failed" do
+    it "detects and processes any unprocessed resources" do
+      expect(reporter).to receive(:detect_unprocessed_resources)
+      reporter.converge_failed("exception")
+    end
+  end
+
+  describe "#resource_current_state_loaded" do
     let(:new_resource)     { double("new_resource") }
     let(:action)           { double("action") }
     let(:current_resource) { double("current_resource") }
+    let(:resource_report)  { double("resource_report") }
 
     context "when resource is a nested resource" do
       it "does not update the resource report" do
@@ -207,36 +234,28 @@ describe Chef::DataCollector::Reporter do
     end
 
     context "when resource is not a nested resource" do
-      it "updates the resource report" do
+      it "creates the resource report and stores it as the current one" do
         allow(reporter).to receive(:nested_resource?).and_return(false)
-        expect(Chef::DataCollector::ResourceReport).to receive(:new).with(
-          new_resource,
-          action,
-          current_resource)
-        .and_return("resource_report")
-        expect(reporter).to receive(:update_current_resource_report).with("resource_report")
+        expect(reporter).to receive(:create_resource_report)
+          .with(new_resource, action, current_resource)
+          .and_return(resource_report)
+        expect(reporter).to receive(:update_current_resource_report).with(resource_report)
         reporter.resource_current_state_loaded(new_resource, action, current_resource)
       end
     end
   end
 
-  describe '#resource_up_to_date' do
+  describe "#resource_up_to_date" do
     let(:new_resource)    { double("new_resource") }
     let(:action)          { double("action") }
     let(:resource_report) { double("resource_report") }
 
     before do
-      allow(reporter).to receive(:increment_resource_count)
       allow(reporter).to receive(:nested_resource?)
       allow(reporter).to receive(:current_resource_report).and_return(resource_report)
       allow(resource_report).to receive(:up_to_date)
     end
 
-    it "increments the resource count" do
-      expect(reporter).to receive(:increment_resource_count)
-      reporter.resource_up_to_date(new_resource, action)
-    end
-
     context "when the resource is a nested resource" do
       it "does not mark the resource report as up-to-date" do
         allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true)
@@ -254,24 +273,18 @@ describe Chef::DataCollector::Reporter do
     end
   end
 
-  describe '#resource_skipped' do
+  describe "#resource_skipped" do
     let(:new_resource)    { double("new_resource") }
     let(:action)          { double("action") }
     let(:conditional)     { double("conditional") }
     let(:resource_report) { double("resource_report") }
 
     before do
-      allow(reporter).to receive(:increment_resource_count)
       allow(reporter).to receive(:nested_resource?)
-      allow(reporter).to receive(:current_resource_report).and_return(resource_report)
+      allow(reporter).to receive(:create_resource_report).and_return(resource_report)
       allow(resource_report).to receive(:skipped)
     end
 
-    it "increments the resource count" do
-      expect(reporter).to receive(:increment_resource_count)
-      reporter.resource_skipped(new_resource, action, conditional)
-    end
-
     context "when the resource is a nested resource" do
       it "does not mark the resource report as skipped" do
         allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true)
@@ -281,13 +294,12 @@ describe Chef::DataCollector::Reporter do
     end
 
     context "when the resource is not a nested resource" do
-      it "updates the resource report" do
+      it "creates the resource report and stores it as the current one" do
         allow(reporter).to receive(:nested_resource?).and_return(false)
-        expect(Chef::DataCollector::ResourceReport).to receive(:new).with(
-          new_resource,
-          action)
-        .and_return("resource_report")
-        expect(reporter).to receive(:update_current_resource_report).with("resource_report")
+        expect(reporter).to receive(:create_resource_report)
+          .with(new_resource, action)
+          .and_return(resource_report)
+        expect(reporter).to receive(:update_current_resource_report).with(resource_report)
         reporter.resource_skipped(new_resource, action, conditional)
       end
 
@@ -299,7 +311,7 @@ describe Chef::DataCollector::Reporter do
     end
   end
 
-  describe '#resource_updated' do
+  describe "#resource_updated" do
     let(:resource_report) { double("resource_report") }
 
     before do
@@ -307,18 +319,13 @@ describe Chef::DataCollector::Reporter do
       allow(resource_report).to receive(:updated)
     end
 
-    it "increments the resource count" do
-      expect(reporter).to receive(:increment_resource_count)
-      reporter.resource_updated("new_resource", "action")
-    end
-
     it "marks the resource report as updated" do
       expect(resource_report).to receive(:updated)
       reporter.resource_updated("new_resource", "action")
     end
   end
 
-  describe '#resource_failed' do
+  describe "#resource_failed" do
     let(:new_resource)    { double("new_resource") }
     let(:action)          { double("action") }
     let(:exception)       { double("exception") }
@@ -326,7 +333,6 @@ describe Chef::DataCollector::Reporter do
     let(:resource_report) { double("resource_report") }
 
     before do
-      allow(reporter).to receive(:increment_resource_count)
       allow(reporter).to receive(:update_error_description)
       allow(reporter).to receive(:current_resource_report).and_return(resource_report)
       allow(resource_report).to receive(:failed)
@@ -334,11 +340,6 @@ describe Chef::DataCollector::Reporter do
       allow(error_mapper).to receive(:for_json)
     end
 
-    it "increments the resource count" do
-      expect(reporter).to receive(:increment_resource_count)
-      reporter.resource_failed(new_resource, action, exception)
-    end
-
     it "updates the error description" do
       expect(Chef::Formatters::ErrorMapper).to receive(:resource_failed).with(
         new_resource,
@@ -367,20 +368,21 @@ describe Chef::DataCollector::Reporter do
     end
   end
 
-  describe '#resource_completed' do
+  describe "#resource_completed" do
     let(:new_resource)    { double("new_resource") }
     let(:resource_report) { double("resource_report") }
 
     before do
-      allow(reporter).to receive(:add_updated_resource)
       allow(reporter).to receive(:update_current_resource_report)
+      allow(reporter).to receive(:add_resource_report)
+      allow(reporter).to receive(:current_resource_report)
       allow(resource_report).to receive(:finish)
     end
 
     context "when there is no current resource report" do
-      it "does not add the updated resource" do
+      it "does not touch the current resource report" do
         allow(reporter).to receive(:current_resource_report).and_return(nil)
-        expect(reporter).not_to receive(:add_updated_resource)
+        expect(reporter).not_to receive(:update_current_resource_report)
         reporter.resource_completed(new_resource)
       end
     end
@@ -391,9 +393,9 @@ describe Chef::DataCollector::Reporter do
       end
 
       context "when the resource is a nested resource" do
-        it "does not add the updated resource" do
+        it "does not mark the resource as finished" do
           allow(reporter).to receive(:nested_resource?).with(new_resource).and_return(true)
-          expect(reporter).not_to receive(:add_updated_resource)
+          expect(resource_report).not_to receive(:finish)
           reporter.resource_completed(new_resource)
         end
       end
@@ -408,11 +410,6 @@ describe Chef::DataCollector::Reporter do
           reporter.resource_completed(new_resource)
         end
 
-        it "adds the resource to the updated resource list" do
-          expect(reporter).to receive(:add_updated_resource).with(resource_report)
-          reporter.resource_completed(new_resource)
-        end
-
         it "nils out the current resource report" do
           expect(reporter).to receive(:update_current_resource_report).with(nil)
           reporter.resource_completed(new_resource)
@@ -421,14 +418,14 @@ describe Chef::DataCollector::Reporter do
     end
   end
 
-  describe '#run_list_expanded' do
+  describe "#run_list_expanded" do
     it "sets the expanded run list" do
       reporter.run_list_expanded("test_run_list")
       expect(reporter.expanded_run_list).to eq("test_run_list")
     end
   end
 
-  describe '#run_list_expand_failed' do
+  describe "#run_list_expand_failed" do
     let(:node)         { double("node") }
     let(:error_mapper) { double("error_mapper") }
     let(:exception)    { double("exception") }
@@ -444,7 +441,7 @@ describe Chef::DataCollector::Reporter do
     end
   end
 
-  describe '#cookbook_resolution_failed' do
+  describe "#cookbook_resolution_failed" do
     let(:error_mapper)      { double("error_mapper") }
     let(:exception)         { double("exception") }
     let(:expanded_run_list) { double("expanded_run_list") }
@@ -461,7 +458,7 @@ describe Chef::DataCollector::Reporter do
 
   end
 
-  describe '#cookbook_sync_failed' do
+  describe "#cookbook_sync_failed" do
     let(:cookbooks)    { double("cookbooks") }
     let(:error_mapper) { double("error_mapper") }
     let(:exception)    { double("exception") }
@@ -477,7 +474,7 @@ describe Chef::DataCollector::Reporter do
     end
   end
 
-  describe '#disable_reporter_on_error' do
+  describe "#disable_reporter_on_error" do
     context "when no exception is raise by the block" do
       it "does not disable the reporter" do
         expect(reporter).not_to receive(:disable_data_collector_reporter)
@@ -491,13 +488,14 @@ describe Chef::DataCollector::Reporter do
 
     context "when an unexpected exception is raised by the block" do
       it "re-raises the exception" do
-        expect { reporter.send(:disable_reporter_on_error) { raise RuntimeError, "bummer" } }.to raise_error(RuntimeError)
+        expect { reporter.send(:disable_reporter_on_error) { raise "bummer" } }.to raise_error(RuntimeError)
       end
     end
 
     [ Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
       Errno::ECONNREFUSED, EOFError, Net::HTTPBadResponse,
-      Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError ].each do |exception_class|
+      Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError,
+      Errno::EHOSTDOWN ].each do |exception_class|
       context "when the block raises a #{exception_class} exception" do
         it "disables the reporter" do
           expect(reporter).to receive(:disable_data_collector_reporter)
@@ -522,4 +520,39 @@ describe Chef::DataCollector::Reporter do
       end
     end
   end
+
+  describe "#validate_data_collector_server_url!" do
+    context "when server_url is empty" do
+      it "raises an exception" do
+        Chef::Config[:data_collector][:server_url] = ""
+        expect { reporter.send(:validate_data_collector_server_url!) }.to raise_error(Chef::Exceptions::ConfigurationError)
+      end
+    end
+
+    context "when server_url is not empty" do
+      context "when server_url is an invalid URI" do
+        it "raises an exception" do
+          Chef::Config[:data_collector][:server_url] = "this is not a URI"
+          expect { reporter.send(:validate_data_collector_server_url!) }.to raise_error(Chef::Exceptions::ConfigurationError)
+        end
+      end
+
+      context "when server_url is a valid URI" do
+        context "when server_url is a URI with no host" do
+          it "raises an exception" do
+            Chef::Config[:data_collector][:server_url] = "/file/uri.txt"
+            expect { reporter.send(:validate_data_collector_server_url!) }.to raise_error(Chef::Exceptions::ConfigurationError)
+          end
+
+        end
+
+        context "when server_url is a URI with a valid host" do
+          it "does not an exception" do
+            Chef::Config[:data_collector][:server_url] = "http://www.google.com/data-collector"
+            expect { reporter.send(:validate_data_collector_server_url!) }.not_to raise_error
+          end
+        end
+      end
+    end
+  end
 end
diff --git a/spec/unit/dsl/audit_spec.rb b/spec/unit/dsl/audit_spec.rb
index a8227f6..42e543f 100644
--- a/spec/unit/dsl/audit_spec.rb
+++ b/spec/unit/dsl/audit_spec.rb
@@ -21,14 +21,14 @@ describe Chef::DSL::Audit do
   end
 
   it "raises an error when no audit name is given" do
-    expect { auditor.control_group do end }.to raise_error(Chef::Exceptions::AuditNameMissing)
+    expect { auditor.control_group {} }.to raise_error(Chef::Exceptions::AuditNameMissing)
   end
 
   context "audits already populated" do
     let(:audits) { { "unique" => {} } }
 
     it "raises an error if the audit name is a duplicate" do
-      expect { auditor.control_group "unique" do end }.to raise_error(Chef::Exceptions::AuditControlGroupDuplicate)
+      expect { auditor.control_group("unique") {} }.to raise_error(Chef::Exceptions::AuditControlGroupDuplicate)
     end
   end
 
@@ -36,7 +36,7 @@ describe Chef::DSL::Audit do
     let(:auditor) { BadAuditDSLTester.new }
 
     it "fails because it relies on the recipe DSL existing" do
-      expect { auditor.control_group "unique" do end }.to raise_error(NoMethodError, /undefined method `cookbook_name'/)
+      expect { auditor.control_group("unique") {} }.to raise_error(NoMethodError, /undefined method `cookbook_name'/)
     end
   end
 
diff --git a/spec/unit/dsl/data_query_spec.rb b/spec/unit/dsl/data_query_spec.rb
index 55c6e5f..f93f07b 100644
--- a/spec/unit/dsl/data_query_spec.rb
+++ b/spec/unit/dsl/data_query_spec.rb
@@ -60,14 +60,15 @@ describe Chef::DSL::DataQuery do
 
     let(:item_name) { "item_name" }
 
-    let(:raw_data) {{
+    let(:raw_data) do
+      {
       "id" => item_name,
       "greeting" => "hello",
       "nested" => {
         "a1" => [1, 2, 3],
         "a2" => { "b1" => true },
       },
-    }}
+    } end
 
     let(:item) do
       item = Chef::DataBagItem.new
diff --git a/spec/unit/dsl/declare_resource_spec.rb b/spec/unit/dsl/declare_resource_spec.rb
index 6dd9317..57a7fd7 100644
--- a/spec/unit/dsl/declare_resource_spec.rb
+++ b/spec/unit/dsl/declare_resource_spec.rb
@@ -50,17 +50,17 @@ describe Chef::ResourceCollection do
 
   describe "#edit_resource!" do
     it "raises if nothing is found" do
-      expect {
+      expect do
         recipe.edit_resource!(:zen_master, "monkey") do
           something true
         end
-      }.to raise_error(Chef::Exceptions::ResourceNotFound)
+      end.to raise_error(Chef::Exceptions::ResourceNotFound)
     end
 
     it "raises if nothing is found and no block is given" do
-      expect {
+      expect do
         recipe.edit_resource!(:zen_master, "monkey")
-      }.to raise_error(Chef::Exceptions::ResourceNotFound)
+      end.to raise_error(Chef::Exceptions::ResourceNotFound)
     end
 
     it "edits the resource if it finds one" do
@@ -131,20 +131,20 @@ describe Chef::ResourceCollection do
 
   describe "#find_resource!" do
     it "raises if nothing is found" do
-      expect {
+      expect do
         recipe.find_resource!(:zen_master, "monkey")
-      }.to raise_error(Chef::Exceptions::ResourceNotFound)
+      end.to raise_error(Chef::Exceptions::ResourceNotFound)
     end
 
     it "raises if given a block" do
       resource = recipe.declare_resource(:zen_master, "monkey") do
         something false
       end
-      expect {
+      expect do
         recipe.find_resource!(:zen_master, "monkey") do
           something false
         end
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "returns the resource if it finds one" do
@@ -221,9 +221,9 @@ describe Chef::ResourceCollection do
 
   describe "#delete_resource!" do
     it "raises if nothing is found" do
-      expect {
+      expect do
         recipe.delete_resource!(:zen_master, "monkey")
-      }.to raise_error(Chef::Exceptions::ResourceNotFound)
+      end.to raise_error(Chef::Exceptions::ResourceNotFound)
     end
 
     it "deletes and returns the resource if it finds one" do
@@ -235,6 +235,36 @@ describe Chef::ResourceCollection do
       ).to eql(resource)
       expect(run_context.resource_collection.all_resources.size).to eql(0)
     end
+
+    it "removes pending delayed notifications" do
+      recipe.declare_resource(:zen_master, "one")
+      recipe.declare_resource(:zen_master, "two") do
+        notifies :win, "zen_master[one]"
+      end
+      recipe.delete_resource(:zen_master, "two")
+      resource = recipe.declare_resource(:zen_master, "two")
+      expect(resource.delayed_notifications).to eql([])
+    end
+
+    it "removes pending immediate notifications" do
+      recipe.declare_resource(:zen_master, "one")
+      recipe.declare_resource(:zen_master, "two") do
+        notifies :win, "zen_master[one]", :immediate
+      end
+      recipe.delete_resource(:zen_master, "two")
+      resource = recipe.declare_resource(:zen_master, "two")
+      expect(resource.immediate_notifications).to eql([])
+    end
+
+    it "removes pending before notifications" do
+      recipe.declare_resource(:zen_master, "one")
+      recipe.declare_resource(:zen_master, "two") do
+        notifies :win, "zen_master[one]", :before
+      end
+      recipe.delete_resource(:zen_master, "two")
+      resource = recipe.declare_resource(:zen_master, "two")
+      expect(resource.before_notifications).to eql([])
+    end
   end
 
   describe "run_context helpers" do
diff --git a/spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb b/spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb
index c2cb275..7e885f8 100644
--- a/spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb
+++ b/spec/unit/encrypted_data_bag_item/check_encrypted_spec.rb
@@ -39,14 +39,15 @@ describe Chef::EncryptedDataBagItem::CheckEncrypted do
 
     let(:default_secret) { "abc123SECRET" }
     let(:item_name) { "item_name" }
-    let(:raw_data) {{
+    let(:raw_data) do
+      {
         "id" => item_name,
         "greeting" => "hello",
         "nested" => {
             "a1" => [1, 2, 3],
             "a2" => { "b1" => true },
         },
-    }}
+    } end
 
     let(:version) { 1 }
     let(:encoded_data) do
diff --git a/spec/unit/encrypted_data_bag_item_spec.rb b/spec/unit/encrypted_data_bag_item_spec.rb
index e17f7a2..a8fb144 100644
--- a/spec/unit/encrypted_data_bag_item_spec.rb
+++ b/spec/unit/encrypted_data_bag_item_spec.rb
@@ -320,11 +320,12 @@ end
 describe Chef::EncryptedDataBagItem do
   subject { described_class }
   let(:encrypted_data_bag_item) { subject.new(encoded_data, secret) }
-  let(:plaintext_data) {{
+  let(:plaintext_data) do
+    {
       "id" => "item_name",
       "greeting" => "hello",
       "nested" => { "a1" => [1, 2, 3], "a2" => { "b1" => true } },
-  }}
+  } end
   let(:secret) { "abc123SECRET" }
   let(:encoded_data) { subject.encrypt_data_bag_item(plaintext_data, secret) }
 
diff --git a/spec/unit/environment_spec.rb b/spec/unit/environment_spec.rb
index 5557b5d..63c96ad 100644
--- a/spec/unit/environment_spec.rb
+++ b/spec/unit/environment_spec.rb
@@ -296,9 +296,9 @@ describe Chef::Environment do
       end
 
       it "should raise and exception" do
-        expect {
+        expect do
           Chef::Environment.validate_cookbook_version("= 1.2.3.4")
-        }.to raise_error Chef::Exceptions::IllegalVersionConstraint,
+        end.to raise_error Chef::Exceptions::IllegalVersionConstraint,
                              "Environment cookbook version constraints not allowed in chef-solo"
       end
     end
@@ -450,9 +450,9 @@ describe Chef::Environment do
       it "should raise an error if the configured environment_path is invalid" do
         expect(File).to receive(:directory?).with(Chef::Config[:environment_path]).and_return(false)
 
-        expect {
+        expect do
           Chef::Environment.load("foo")
-        }.to raise_error Chef::Exceptions::InvalidEnvironmentPath, "Environment path '/var/chef/environments' is invalid"
+        end.to raise_error Chef::Exceptions::InvalidEnvironmentPath, "Environment path '/var/chef/environments' is invalid"
       end
 
       it "should raise an error if the file does not exist" do
@@ -460,9 +460,9 @@ describe Chef::Environment do
         expect(File).to receive(:exists?).with(File.join(Chef::Config[:environment_path], "foo.json")).and_return(false)
         expect(File).to receive(:exists?).with(File.join(Chef::Config[:environment_path], "foo.rb")).and_return(false)
 
-        expect {
+        expect do
           Chef::Environment.load("foo")
-        }.to raise_error Chef::Exceptions::EnvironmentNotFound, "Environment 'foo' could not be loaded from disk"
+        end.to raise_error Chef::Exceptions::EnvironmentNotFound, "Environment 'foo' could not be loaded from disk"
       end
     end
   end
diff --git a/spec/unit/event_dispatch/dispatcher_spec.rb b/spec/unit/event_dispatch/dispatcher_spec.rb
index 2360b26..5061a98 100644
--- a/spec/unit/event_dispatch/dispatcher_spec.rb
+++ b/spec/unit/event_dispatch/dispatcher_spec.rb
@@ -52,8 +52,8 @@ describe Chef::EventDispatch::Dispatcher do
       dispatcher.synchronized_cookbook("apache2", cookbook_version)
 
       exception = StandardError.new("foo")
-      expect(event_sink).to receive(:recipe_file_load_failed).with("/path/to/file.rb", exception)
-      dispatcher.recipe_file_load_failed("/path/to/file.rb", exception)
+      expect(event_sink).to receive(:recipe_file_load_failed).with("/path/to/file.rb", exception, "myrecipe")
+      dispatcher.recipe_file_load_failed("/path/to/file.rb", exception, "myrecipe")
     end
 
     context "when an event sink has fewer arguments for an event" do
diff --git a/spec/unit/file_access_control_spec.rb b/spec/unit/file_access_control_spec.rb
index 4357a91..ee806b5 100644
--- a/spec/unit/file_access_control_spec.rb
+++ b/spec/unit/file_access_control_spec.rb
@@ -43,7 +43,7 @@ describe Chef::FileAccessControl do
     end
 
     describe "class methods" do
-      it 'responds to #writable?' do
+      it "responds to #writable?" do
         expect(Chef::FileAccessControl).to respond_to(:writable?)
       end
     end
@@ -67,7 +67,7 @@ describe Chef::FileAccessControl do
 
     it "raises a Chef::Exceptions::UserIDNotFound error when Etc can't find the user's name" do
       expect(Etc).to receive(:getpwnam).with("toor").and_raise(ArgumentError)
-      expect { @fac.target_uid ; @provider_requirements.run(:create) }.to raise_error(Chef::Exceptions::UserIDNotFound, "cannot determine user id for 'toor', does the user exist on this system?")
+      expect { @fac.target_uid; @provider_requirements.run(:create) }.to raise_error(Chef::Exceptions::UserIDNotFound, "cannot determine user id for 'toor', does the user exist on this system?")
     end
 
     it "does not attempt to resolve the uid if the user is not specified" do
@@ -84,7 +84,7 @@ describe Chef::FileAccessControl do
 
     it "raises an ArgumentError if the resource's owner is set to something wack" do
       @resource.instance_variable_set(:@owner, :diaf)
-      expect { @fac.target_uid ; @provider_requirements.run(:create) }.to raise_error(ArgumentError)
+      expect { @fac.target_uid; @provider_requirements.run(:create) }.to raise_error(ArgumentError)
     end
 
     it "uses the resource's uid for the target uid when the resource's owner is specified by an integer" do
diff --git a/spec/unit/file_content_management/tempfile_spec.rb b/spec/unit/file_content_management/tempfile_spec.rb
index a833e21..a04ace5 100644
--- a/spec/unit/file_content_management/tempfile_spec.rb
+++ b/spec/unit/file_content_management/tempfile_spec.rb
@@ -19,38 +19,59 @@
 require "spec_helper"
 
 describe Chef::FileContentManagement::Tempfile do
-  let(:resource) {
-    r = Chef::Resource::File.new("new_file")
-    r.path "/foo/bar/new_file"
-    r
-  }
 
-  subject { described_class.new(resource) }
+  def tempfile_object_for_path(path)
+    r = Chef::Resource::File.new("decorative name that should not matter")
+    r.path path
+    Chef::FileContentManagement::Tempfile.new(r)
+  end
 
   describe "#tempfile_basename" do
     it "should return a dotfile", :unix_only do
+      subject = tempfile_object_for_path("/foo/bar/new_file")
       expect(subject.send(:tempfile_basename)).to eql(".chef-new_file")
     end
 
     it "should return a file", :windows_only do
+      subject = tempfile_object_for_path("/foo/bar/new_file")
+      expect(subject.send(:tempfile_basename)).to eql("chef-new_file")
+    end
+
+    it "should strip the extension", :unix_only do
+      subject = tempfile_object_for_path("/foo/bar/new_file.html.erb")
+      expect(subject.send(:tempfile_basename)).to eql(".chef-new_file")
+    end
+
+    it "should strip the extension", :windows_only do
+      subject = tempfile_object_for_path("/foo/bar/new_file.html.erb")
       expect(subject.send(:tempfile_basename)).to eql("chef-new_file")
     end
   end
 
+  describe "#tempfile_extension" do
+    it "should preserve the file extension" do
+      subject = tempfile_object_for_path("/foo/bar/new_file.html.erb")
+      expect(subject.send(:tempfile_extension)).to eql(".html.erb")
+    end
+  end
+
   describe "#tempfile_dirnames" do
 
     it "should select a temp dir" do
+      subject = tempfile_object_for_path("/foo/bar/new_file")
       Chef::Config[:file_staging_uses_destdir] = false
       expect(Dir).to receive(:tmpdir).and_return("/tmp/dir")
       expect(subject.send(:tempfile_dirnames)).to eql(%w{ /tmp/dir })
     end
 
     it "should select the destdir" do
+      subject = tempfile_object_for_path("/foo/bar/new_file")
       Chef::Config[:file_staging_uses_destdir] = true
       expect(subject.send(:tempfile_dirnames)).to eql(%w{ /foo/bar })
     end
 
     it "should select the destdir and a temp dir" do
+      subject = tempfile_object_for_path("/foo/bar/new_file")
       Chef::Config[:file_staging_uses_destdir] = :auto
       expect(Dir).to receive(:tmpdir).and_return("/tmp/dir")
       expect(subject.send(:tempfile_dirnames)).to eql(%w{ /foo/bar /tmp/dir })
@@ -67,18 +88,27 @@ describe Chef::FileContentManagement::Tempfile do
     end
 
     it "should create a temporary file" do
+      subject = tempfile_object_for_path("/foo/bar/new_file")
       expect(subject.send(:tempfile_open)).to be_a(Tempfile)
     end
 
+    it "should preserve the extension in the tempfile path" do
+      subject = tempfile_object_for_path("/foo/bar/new_file.html.erb")
+      tempfile = subject.send(:tempfile_open)
+      expect(tempfile.path).to match(/chef-new_file.*\.html\.erb$/)
+    end
+
     it "should pick the destdir preferrentially" do
-      expect(Tempfile).to receive(:open).with(tempname, "/foo/bar").and_return(tempfile)
+      subject = tempfile_object_for_path("/foo/bar/new_file")
+      expect(Tempfile).to receive(:open).with([tempname, ""], "/foo/bar").and_return(tempfile)
       subject.send(:tempfile_open)
     end
 
     it "should use ENV['TMP'] otherwise" do
+      subject = tempfile_object_for_path("/foo/bar/new_file")
       expect(Dir).to receive(:tmpdir).and_return("/tmp/dir")
-      expect(Tempfile).to receive(:open).with(tempname, "/foo/bar").and_raise(SystemCallError, "foo")
-      expect(Tempfile).to receive(:open).with(tempname, "/tmp/dir").and_return(tempfile)
+      expect(Tempfile).to receive(:open).with([tempname, ""], "/foo/bar").and_raise(SystemCallError, "foo")
+      expect(Tempfile).to receive(:open).with([tempname, ""], "/tmp/dir").and_return(tempfile)
       subject.send(:tempfile_open)
     end
   end
diff --git a/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb b/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb
index 97fa92c..0e5104a 100644
--- a/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb
+++ b/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb
@@ -32,13 +32,13 @@ describe Chef::Formatters::APIErrorFormatting do
       let(:min_version) { "2" }
       let(:max_version) { "5" }
       let(:request_version) { "30" }
-      let(:return_hash) {
+      let(:return_hash) do
         {
           "min_version" => min_version,
           "max_version" => max_version,
           "request_version" => request_version,
         }
-      }
+      end
 
       before do
         # mock out the header
diff --git a/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb b/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
index e76e69d..746b343 100644
--- a/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
+++ b/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
@@ -105,9 +105,9 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
             parent_resource
           end
 
-          let(:shell_out) {
+          let(:shell_out) do
             instance_double(Mixlib::ShellOut, :live_stream => true, :run_command => true, :error! => nil)
-          }
+          end
 
           before do
             # TODO for some reason Windows is failing on executing a ruby script
diff --git a/spec/unit/http/basic_client_spec.rb b/spec/unit/http/basic_client_spec.rb
index 8cf63d4..0def00a 100644
--- a/spec/unit/http/basic_client_spec.rb
+++ b/spec/unit/http/basic_client_spec.rb
@@ -29,6 +29,26 @@ describe "HTTP Connection" do
     end
   end
 
+  describe "#initialize" do
+    it "calls .start when doing keepalives" do
+      basic_client = Chef::HTTP::BasicClient.new(uri, keepalives: true)
+      expect(basic_client).to receive(:configure_ssl)
+      net_http_mock = instance_double(Net::HTTP, proxy_address: nil, "proxy_port=" => nil, "read_timeout=" => nil, "open_timeout=" => nil)
+      expect(net_http_mock).to receive(:start).and_return(net_http_mock)
+      expect(Net::HTTP).to receive(:new).and_return(net_http_mock)
+      expect(basic_client.http_client).to eql(net_http_mock)
+    end
+
+    it "does not call .start when not doing keepalives" do
+      basic_client = Chef::HTTP::BasicClient.new(uri)
+      expect(basic_client).to receive(:configure_ssl)
+      net_http_mock = instance_double(Net::HTTP, proxy_address: nil, "proxy_port=" => nil, "read_timeout=" => nil, "open_timeout=" => nil)
+      expect(net_http_mock).not_to receive(:start)
+      expect(Net::HTTP).to receive(:new).and_return(net_http_mock)
+      expect(basic_client.http_client).to eql(net_http_mock)
+    end
+  end
+
   describe "#build_http_client" do
     it "should build an http client" do
       subject.build_http_client
diff --git a/spec/unit/http/validate_content_length_spec.rb b/spec/unit/http/validate_content_length_spec.rb
index 85f8d92..5067d36 100644
--- a/spec/unit/http/validate_content_length_spec.rb
+++ b/spec/unit/http/validate_content_length_spec.rb
@@ -37,25 +37,25 @@ describe Chef::HTTP::ValidateContentLength do
   let(:content_length_value) { 23 }
   let(:streaming_length) { 23 }
   let(:response_body) { "Thanks for checking in." }
-  let(:response_headers) {
+  let(:response_headers) do
     {
       "content-length" => content_length_value,
     }
-  }
+  end
 
-  let(:response) {
+  let(:response) do
     m = double("HttpResponse", :body => response_body)
     allow(m).to receive(:[]) do |key|
       response_headers[key]
     end
 
     m
-  }
+  end
 
-  let(:middleware) {
+  let(:middleware) do
     client = TestClient.new(url)
     client.middlewares[0]
-  }
+  end
 
   def run_content_length_validation
     stream_handler = middleware.stream_response_handler(response)
@@ -169,12 +169,12 @@ describe Chef::HTTP::ValidateContentLength do
   end
 
   describe "when Transfer-Encoding & Content-Length is set" do
-    let(:response_headers) {
+    let(:response_headers) do
       {
         "content-length" => content_length_value,
         "transfer-encoding" => "chunked",
       }
-    }
+    end
 
     %w{direct streaming}.each do |req_type|
       describe "when running #{req_type} request" do
diff --git a/spec/unit/http_spec.rb b/spec/unit/http_spec.rb
index 69e59ec..d99f03a 100644
--- a/spec/unit/http_spec.rb
+++ b/spec/unit/http_spec.rb
@@ -43,6 +43,20 @@ describe Chef::HTTP do
 
   end
 
+  describe "#intialize" do
+    it "accepts a keepalive option and passes it to the http_client" do
+      http = Chef::HTTP.new(uri, keepalives: true)
+      expect(Chef::HTTP::BasicClient).to receive(:new).with(uri, ssl_policy: Chef::HTTP::APISSLPolicy, keepalives: true).and_call_original
+      expect(http.http_client).to be_a_kind_of(Chef::HTTP::BasicClient)
+    end
+
+    it "the default is not to use keepalives" do
+      http = Chef::HTTP.new(uri)
+      expect(Chef::HTTP::BasicClient).to receive(:new).with(uri, ssl_policy: Chef::HTTP::APISSLPolicy, keepalives: false).and_call_original
+      expect(http.http_client).to be_a_kind_of(Chef::HTTP::BasicClient)
+    end
+  end
+
   describe "create_url" do
 
     it "should return a correctly formatted url 1/3 CHEF-5261" do
diff --git a/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb b/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb
index 734b272..d822fe8 100644
--- a/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb
+++ b/spec/unit/knife/bootstrap/chef_vault_handler_spec.rb
@@ -29,10 +29,10 @@ describe Chef::Knife::Bootstrap::ChefVaultHandler do
 
   let(:client) { Chef::ApiClient.new }
 
-  let(:chef_vault_handler) {
+  let(:chef_vault_handler) do
     chef_vault_handler = Chef::Knife::Bootstrap::ChefVaultHandler.new(knife_config: knife_config, ui: ui)
     chef_vault_handler
-  }
+  end
 
   context "when there's no vault option" do
     it "should report its not doing anything" do
diff --git a/spec/unit/knife/bootstrap/client_builder_spec.rb b/spec/unit/knife/bootstrap/client_builder_spec.rb
index 491d0ca..97ba0fc 100644
--- a/spec/unit/knife/bootstrap/client_builder_spec.rb
+++ b/spec/unit/knife/bootstrap/client_builder_spec.rb
@@ -33,12 +33,12 @@ describe Chef::Knife::Bootstrap::ClientBuilder do
 
   let(:rest) { double("Chef::ServerAPI") }
 
-  let(:client_builder) {
+  let(:client_builder) do
     client_builder = Chef::Knife::Bootstrap::ClientBuilder.new(knife_config: knife_config, chef_config: chef_config, ui: ui)
     allow(client_builder).to receive(:rest).and_return(rest)
     allow(client_builder).to receive(:node_name).and_return(node_name)
     client_builder
-  }
+  end
 
   context "#sanity_check!" do
     let(:response_404) { OpenStruct.new(:code => "404") }
diff --git a/spec/unit/knife/bootstrap_spec.rb b/spec/unit/knife/bootstrap_spec.rb
index 5f1ba9f..9f944b8 100644
--- a/spec/unit/knife/bootstrap_spec.rb
+++ b/spec/unit/knife/bootstrap_spec.rb
@@ -236,11 +236,11 @@ describe Chef::Knife::Bootstrap do
     end
 
     context "with bootstrap_attribute options" do
-      let(:jsonfile) {
+      let(:jsonfile) do
         file = Tempfile.new (["node", ".json"])
         File.open(file.path, "w") { |f| f.puts '{"foo":{"bar":"baz"}}' }
         file
-      }
+      end
 
       it "should have foo => {bar => baz} in the first_boot from cli" do
         knife.parse_options(["-j", '{"foo":{"bar":"baz"}}'])
@@ -478,8 +478,9 @@ describe Chef::Knife::Bootstrap do
     end
 
     context "when client_d_dir is set" do
-      let(:client_d_dir) { Chef::Util::PathHelper.cleanpath(
-        File.join(File.dirname(__FILE__), "../../data/client.d_00")) }
+      let(:client_d_dir) do
+        Chef::Util::PathHelper.cleanpath(
+        File.join(File.dirname(__FILE__), "../../data/client.d_00")) end
 
       it "creates /etc/chef/client.d" do
         expect(rendered_template).to match("mkdir -p /etc/chef/client\.d")
@@ -497,8 +498,9 @@ describe Chef::Knife::Bootstrap do
       end
 
       context "a nested directory structure" do
-        let(:client_d_dir) { Chef::Util::PathHelper.cleanpath(
-          File.join(File.dirname(__FILE__), "../../data/client.d_01")) }
+        let(:client_d_dir) do
+          Chef::Util::PathHelper.cleanpath(
+          File.join(File.dirname(__FILE__), "../../data/client.d_01")) end
         it "creates a file foo/bar.rb" do
           expect(rendered_template).to match("cat > /etc/chef/client.d/foo/bar.rb <<'EOP'")
           expect(rendered_template).to match("1 / 0")
diff --git a/spec/unit/knife/client_bulk_delete_spec.rb b/spec/unit/knife/client_bulk_delete_spec.rb
index a9b18c9..994f4d3 100644
--- a/spec/unit/knife/client_bulk_delete_spec.rb
+++ b/spec/unit/knife/client_bulk_delete_spec.rb
@@ -24,7 +24,7 @@ describe Chef::Knife::ClientBulkDelete do
   let(:stderr_io) { StringIO.new }
   let(:stderr) { stderr_io.string }
 
-  let(:knife) {
+  let(:knife) do
     k = Chef::Knife::ClientBulkDelete.new
     k.name_args = name_args
     k.config = option_args
@@ -33,7 +33,7 @@ describe Chef::Knife::ClientBulkDelete do
     allow(k.ui).to receive(:confirm).and_return(knife_confirm)
     allow(k.ui).to receive(:confirm_without_exit).and_return(knife_confirm)
     k
-  }
+  end
 
   let(:name_args) { [ "." ] }
   let(:option_args) { {} }
@@ -41,7 +41,7 @@ describe Chef::Knife::ClientBulkDelete do
   let(:knife_confirm) { true }
 
   let(:nonvalidator_client_names) { %w{tim dan stephen} }
-  let(:nonvalidator_clients) {
+  let(:nonvalidator_clients) do
     clients = Hash.new
 
     nonvalidator_client_names.each do |client_name|
@@ -52,10 +52,10 @@ describe Chef::Knife::ClientBulkDelete do
     end
 
     clients
-  }
+  end
 
   let(:validator_client_names) { %w{myorg-validator} }
-  let(:validator_clients) {
+  let(:validator_clients) do
     clients = Hash.new
 
     validator_client_names.each do |validator_client_name|
@@ -67,12 +67,12 @@ describe Chef::Knife::ClientBulkDelete do
     end
 
     clients
-  }
+  end
 
   let(:client_names) { nonvalidator_client_names + validator_client_names }
-  let(:clients) {
+  let(:clients) do
     nonvalidator_clients.merge(validator_clients)
-  }
+  end
 
   before(:each) do
     allow(Chef::ApiClientV1).to receive(:list).and_return(clients)
diff --git a/spec/unit/knife/client_edit_spec.rb b/spec/unit/knife/client_edit_spec.rb
index 584a940..e7c9030 100644
--- a/spec/unit/knife/client_edit_spec.rb
+++ b/spec/unit/knife/client_edit_spec.rb
@@ -27,7 +27,7 @@ describe Chef::Knife::ClientEdit do
   end
 
   describe "run" do
-    let(:data) {
+    let(:data) do
       {
         "name" => "adam",
         "validator" => false,
@@ -35,7 +35,7 @@ describe Chef::Knife::ClientEdit do
         "chef_type" => "client",
         "create_key" => true,
       }
-    }
+    end
 
     it "should edit the client" do
       allow(Chef::ApiClientV1).to receive(:load).with("adam").and_return(data)
diff --git a/spec/unit/knife/configure_client_spec.rb b/spec/unit/knife/configure_client_spec.rb
index 192da86..3ecb89f 100644
--- a/spec/unit/knife/configure_client_spec.rb
+++ b/spec/unit/knife/configure_client_spec.rb
@@ -33,9 +33,9 @@ describe Chef::Knife::ConfigureClient do
     it "should print usage and exit when a directory is not provided" do
       expect(@knife).to receive(:show_usage)
       expect(@knife.ui).to receive(:fatal).with(/must provide the directory/)
-      expect {
+      expect do
         @knife.run
-      }.to raise_error SystemExit
+      end.to raise_error SystemExit
     end
 
     describe "when specifing a directory" do
diff --git a/spec/unit/knife/cookbook_create_spec.rb b/spec/unit/knife/cookbook_create_spec.rb
index 5e343f3..f860a8b 100644
--- a/spec/unit/knife/cookbook_create_spec.rb
+++ b/spec/unit/knife/cookbook_create_spec.rb
@@ -22,6 +22,7 @@ require "tmpdir"
 describe Chef::Knife::CookbookCreate do
   before(:each) do
     Chef::Config[:node_name] = "webmonkey.example.com"
+    Chef::Config[:treat_deprecation_warnings_as_errors] = false
     @knife = Chef::Knife::CookbookCreate.new
     @knife.config = {}
     @knife.name_args = ["foobar"]
diff --git a/spec/unit/knife/cookbook_metadata_spec.rb b/spec/unit/knife/cookbook_metadata_spec.rb
index 0e3fccf..4b405d0 100644
--- a/spec/unit/knife/cookbook_metadata_spec.rb
+++ b/spec/unit/knife/cookbook_metadata_spec.rb
@@ -128,9 +128,9 @@ describe Chef::Knife::CookbookMetadata do
       it "should print an error and exit when an #{description} syntax exception is encountered" do
         exception = klass.new("#{description} blah")
         allow(Chef::Cookbook::Metadata).to receive(:new).and_raise(exception)
-        expect {
+        expect do
           @knife.generate_metadata_from_file("foobar", "#{@cookbook_dir}/foobar/metadata.rb")
-        }.to raise_error(SystemExit)
+        end.to raise_error(SystemExit)
         expect(@stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
         expect(@stderr.string).to match /in #{@cookbook_dir}\/foobar\/metadata\.rb/im
         expect(@stderr.string).to match /#{description} blah/im
@@ -166,9 +166,9 @@ describe Chef::Knife::CookbookMetadata do
           and_return(@json_data)
         exception = klass.new("#{description} blah")
         allow(Chef::Cookbook::Metadata).to receive(:validate_json).and_raise(exception)
-        expect {
+        expect do
           @knife.validate_metadata_json(@cookbook_dir, "foobar")
-        }.to raise_error(SystemExit)
+        end.to raise_error(SystemExit)
         expect(@stderr.string).to match /error: the cookbook 'foobar' contains invalid or obsolete metadata syntax/im
         expect(@stderr.string).to match /in #{@cookbook_dir}\/foobar\/metadata\.json/im
         expect(@stderr.string).to match /#{description} blah/im
diff --git a/spec/unit/knife/cookbook_site_download_spec.rb b/spec/unit/knife/cookbook_site_download_spec.rb
index d283bd4..0ab6a8a 100644
--- a/spec/unit/knife/cookbook_site_download_spec.rb
+++ b/spec/unit/knife/cookbook_site_download_spec.rb
@@ -38,6 +38,7 @@ describe Chef::Knife::CookbookSiteDownload do
       expect(@noauth_rest).to receive(:get).
         with("#{@cookbook_api_url}/apache2").
         and_return(@current_data)
+      @knife.configure_chef
     end
 
     context "when the cookbook is deprecated and not forced" do
diff --git a/spec/unit/knife/cookbook_site_install_spec.rb b/spec/unit/knife/cookbook_site_install_spec.rb
index d60443d..d93af10 100644
--- a/spec/unit/knife/cookbook_site_install_spec.rb
+++ b/spec/unit/knife/cookbook_site_install_spec.rb
@@ -23,16 +23,18 @@ describe Chef::Knife::CookbookSiteInstall do
   let(:stdout) { StringIO.new }
   let(:stderr) { StringIO.new }
   let(:downloader) { Hash.new }
-  let(:repo) { double(:sanity_check => true, :reset_to_default_state => true,
-                      :prepare_to_import => true, :finalize_updates_to => true,
-                      :merge_updates_from => true) }
-  let(:install_path) {
+  let(:archive) { double(Mixlib::Archive, extract: true) }
+  let(:repo) do
+    double(:sanity_check => true, :reset_to_default_state => true,
+           :prepare_to_import => true, :finalize_updates_to => true,
+           :merge_updates_from => true) end
+  let(:install_path) do
     if Chef::Platform.windows?
       "C:/tmp/chef"
     else
       "/var/tmp/chef"
     end
-  }
+  end
 
   before(:each) do
     require "chef/knife/core/cookbook_scm_repo"
@@ -48,6 +50,7 @@ describe Chef::Knife::CookbookSiteInstall do
     allow(File).to receive(:unlink)
     allow(File).to receive(:rmtree)
     allow(knife).to receive(:shell_out!).and_return(true)
+    allow(Mixlib::Archive).to receive(:new).and_return(archive)
 
     # CookbookSiteDownload Stup
     allow(knife).to receive(:download_cookbook_to).and_return(downloader)
diff --git a/spec/unit/knife/cookbook_site_share_spec.rb b/spec/unit/knife/cookbook_site_share_spec.rb
index 9339114..823eff8 100644
--- a/spec/unit/knife/cookbook_site_share_spec.rb
+++ b/spec/unit/knife/cookbook_site_share_spec.rb
@@ -83,11 +83,11 @@ describe Chef::Knife::CookbookSiteShare do
       @knife.run
     end
 
-    it "should print error and exit when given only 1 argument and cannot determine category" do
+    it "should use a default category when given only 1 argument and cannot determine category" do
       @knife.name_args = ["cookbook_name"]
-      expect(@noauth_rest).to receive(:get).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name").and_return(@bad_category_response)
-      expect(@knife.ui).to receive(:fatal)
-      expect { @knife.run }.to raise_error(SystemExit)
+      expect(@noauth_rest).to receive(:get).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name") { raise Net::HTTPServerException.new("404 Not Found", OpenStruct.new(code: "404")) }
+      expect(@knife).to receive(:do_upload)
+      expect { @knife.run }.to_not raise_error
     end
 
     it "should print error and exit when given only 1 argument and Chef::ServerAPI throws an exception" do
@@ -199,8 +199,8 @@ describe Chef::Knife::CookbookSiteShare do
       response_text = Chef::JSONCompat.to_json({ :system_error => "Your call was dropped", :reason => "There's a map for that" })
       allow(@upload_response).to receive(:body).and_return(response_text)
       allow(@upload_response).to receive(:code).and_return(500)
-      expect(@knife.ui).to receive(:error).with(/#{Regexp.escape(response_text)}/)#.ordered
-      expect(@knife.ui).to receive(:error).with(/Unknown error/)#.ordered
+      expect(@knife.ui).to receive(:error).with(/#{Regexp.escape(response_text)}/) #.ordered
+      expect(@knife.ui).to receive(:error).with(/Unknown error/) #.ordered
       expect { @knife.run }.to raise_error(SystemExit)
     end
 
diff --git a/spec/unit/knife/core/hashed_command_loader_spec.rb b/spec/unit/knife/core/hashed_command_loader_spec.rb
index 081f9de..53bd81f 100644
--- a/spec/unit/knife/core/hashed_command_loader_spec.rb
+++ b/spec/unit/knife/core/hashed_command_loader_spec.rb
@@ -22,7 +22,7 @@ describe Chef::Knife::SubcommandLoader::HashedCommandLoader do
     allow(ChefConfig).to receive(:windows?) { false }
   end
 
-  let(:plugin_manifest) {
+  let(:plugin_manifest) do
     {
       "_autogenerated_command_paths" => {
         "plugins_paths" => {
@@ -39,11 +39,12 @@ describe Chef::Knife::SubcommandLoader::HashedCommandLoader do
         },
       },
     }
-  }
+  end
 
-  let(:loader) { Chef::Knife::SubcommandLoader::HashedCommandLoader.new(
+  let(:loader) do
+    Chef::Knife::SubcommandLoader::HashedCommandLoader.new(
     File.join(CHEF_SPEC_DATA, "knife-site-subcommands"),
-    plugin_manifest)}
+    plugin_manifest) end
 
   describe "#list_commands" do
     before do
diff --git a/spec/unit/knife/core/node_editor_spec.rb b/spec/unit/knife/core/node_editor_spec.rb
index 19af419..ce169a7 100644
--- a/spec/unit/knife/core/node_editor_spec.rb
+++ b/spec/unit/knife/core/node_editor_spec.rb
@@ -41,7 +41,7 @@ describe Chef::Knife::NodeEditor do
 
   subject { described_class.new(node, ui, config) }
 
-  describe '#view' do
+  describe "#view" do
     it "returns a Hash with only the name, chef_environment, normal, " +
       "policy_name, policy_group, and run_list properties" do
       expected = node_data.select do |key,|
@@ -61,7 +61,7 @@ describe Chef::Knife::NodeEditor do
     end
   end
 
-  describe '#apply_updates' do
+  describe "#apply_updates" do
     context "when the node name is changed" do
       before(:each) do
         allow(ui).to receive(:warn)
@@ -143,7 +143,7 @@ describe Chef::Knife::NodeEditor do
     end
   end
 
-  describe '#updated?' do
+  describe "#updated?" do
     context "before the node has been edited" do
       it "returns false" do
         expect(subject.updated?).to be false
diff --git a/spec/unit/knife/core/ui_spec.rb b/spec/unit/knife/core/ui_spec.rb
index 0b9547f..9f525f2 100644
--- a/spec/unit/knife/core/ui_spec.rb
+++ b/spec/unit/knife/core/ui_spec.rb
@@ -500,9 +500,9 @@ EOM
 
     shared_examples_for "confirm with negative answer" do
       it "confirm should exit 3" do
-        expect {
+        expect do
           run_confirm
-        }.to raise_error(SystemExit) { |e| expect(e.status).to eq(3) }
+        end.to raise_error(SystemExit) { |e| expect(e.status).to eq(3) }
       end
 
       it "confirm_without_exit should return false" do
diff --git a/spec/unit/knife/data_bag_from_file_spec.rb b/spec/unit/knife/data_bag_from_file_spec.rb
index 0b6f389..3bea392 100644
--- a/spec/unit/knife/data_bag_from_file_spec.rb
+++ b/spec/unit/knife/data_bag_from_file_spec.rb
@@ -52,7 +52,7 @@ describe Chef::Knife::DataBagFromFile do
     k
   end
 
-  let(:tmp_dir) { Dir.mktmpdir }
+  let(:tmp_dir) { make_canonical_temp_directory }
   let(:db_folder) { File.join(tmp_dir, data_bags_path, bag_name) }
   let(:db_file) { Tempfile.new(["data_bag_from_file_test", ".json"], db_folder) }
   let(:db_file2) { Tempfile.new(["data_bag_from_file_test2", ".json"], db_folder) }
@@ -72,11 +72,12 @@ describe Chef::Knife::DataBagFromFile do
   let(:loader) { double("Knife::Core::ObjectLoader") }
 
   let(:data_bags_path) { "data_bags" }
-  let(:plain_data) { {
+  let(:plain_data) do
+    {
       "id" => "item_name",
       "greeting" => "hello",
       "nested" => { "a1" => [1, 2, 3], "a2" => { "b1" => true } },
-  } }
+  } end
   let(:enc_data) { Chef::EncryptedDataBagItem.encrypt_data_bag_item(plain_data, secret) }
 
   let(:rest) { double("Chef::ServerAPI") }
diff --git a/spec/unit/knife/data_bag_show_spec.rb b/spec/unit/knife/data_bag_show_spec.rb
index 0672b8b..ece7f5b 100644
--- a/spec/unit/knife/data_bag_show_spec.rb
+++ b/spec/unit/knife/data_bag_show_spec.rb
@@ -45,8 +45,9 @@ describe Chef::Knife::DataBagShow do
   let(:bag_name) { "sudoing_admins" }
   let(:item_name) { "ME" }
 
-  let(:data_bag_contents) { { "id" => "id", "baz" => "http://localhost:4000/data/bag_o_data/baz",
-                              "qux" => "http://localhost:4000/data/bag_o_data/qux" } }
+  let(:data_bag_contents) do
+    { "id" => "id", "baz" => "http://localhost:4000/data/bag_o_data/baz",
+      "qux" => "http://localhost:4000/data/bag_o_data/qux" } end
   let(:enc_hash) { Chef::EncryptedDataBagItem.encrypt_data_bag_item(data_bag_contents, secret) }
   let(:data_bag) { Chef::DataBagItem.from_hash(data_bag_contents) }
   let(:data_bag_with_encoded_hash) { Chef::DataBagItem.from_hash(enc_hash) }
diff --git a/spec/unit/knife/key_create_spec.rb b/spec/unit/knife/key_create_spec.rb
index 146b6a9..5b00c6e 100644
--- a/spec/unit/knife/key_create_spec.rb
+++ b/spec/unit/knife/key_create_spec.rb
@@ -73,7 +73,7 @@ describe "key create commands that inherit knife" do
 end
 
 describe Chef::Knife::KeyCreate do
-  let(:public_key) {
+  let(:public_key) do
     "-----BEGIN PUBLIC KEY-----
 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvPo+oNPB7uuNkws0fC02
 KxSwdyqPLu0fhI1pOweNKAZeEIiEz2PkybathHWy8snSXGNxsITkf3eyvIIKa8OZ
@@ -83,15 +83,15 @@ IjSmiN/ihHtlhV/VSnBJ5PzT/lRknlrJ4kACoz7Pq9jv+aAx5ft/xE9yDa2DYs0q
 Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
 0wIDAQAB
 -----END PUBLIC KEY-----"
-  }
+  end
   let(:config) { Hash.new }
   let(:actor) { "charmander" }
   let(:ui) { instance_double("Chef::Knife::UI") }
 
   shared_examples_for "key create run command" do
-    let(:key_create_object) {
+    let(:key_create_object) do
       described_class.new(actor, actor_field_name, ui, config)
-    }
+    end
 
     context "when public_key and key_name weren't passed" do
       it "raises a Chef::Exceptions::KeyCommandInputError with the proper error message" do
@@ -100,11 +100,11 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
     end
 
     context "when the command is run" do
-      let(:expected_hash) {
+      let(:expected_hash) do
         {
           actor_field_name => "charmander",
         }
-      }
+      end
 
       before do
         allow(File).to receive(:read).and_return(public_key)
@@ -120,14 +120,14 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
       context "when a valid hash is passed" do
         let(:key_name) { "charmander-key" }
         let(:valid_expiration_date) { "2020-12-24T21:00:00Z" }
-        let(:expected_hash) {
+        let(:expected_hash) do
           {
             actor_field_name => "charmander",
             "public_key" => public_key,
             "expiration_date" => valid_expiration_date,
             "key_name" => key_name,
           }
-        }
+        end
         before do
           key_create_object.config[:public_key] = "public_key_path"
           key_create_object.config[:expiration_Date] = valid_expiration_date,
@@ -141,12 +141,12 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
       end
 
       context "when public_key is passed" do
-        let(:expected_hash) {
+        let(:expected_hash) do
           {
             actor_field_name => "charmander",
             "public_key" => public_key,
           }
-        }
+        end
         before do
           key_create_object.config[:public_key] = "public_key_path"
         end
@@ -158,13 +158,13 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
       end # when public_key is passed
 
       context "when public_key isn't passed and key_name is" do
-        let(:expected_hash) {
+        let(:expected_hash) do
           {
             actor_field_name => "charmander",
             "name" => "charmander-key",
             "create_key" => true,
           }
-        }
+        end
         before do
           key_create_object.config[:key_name] = "charmander-key"
         end
@@ -176,13 +176,13 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
       end
 
       context "when the server returns a private key" do
-        let(:expected_hash) {
+        let(:expected_hash) do
           {
             actor_field_name => "charmander",
             "public_key" => public_key,
             "private_key" => "super_private",
           }
-        }
+        end
 
         before do
           key_create_object.config[:public_key] = "public_key_path"
diff --git a/spec/unit/knife/key_delete_spec.rb b/spec/unit/knife/key_delete_spec.rb
index 3da5a97..0176f3c 100644
--- a/spec/unit/knife/key_delete_spec.rb
+++ b/spec/unit/knife/key_delete_spec.rb
@@ -80,9 +80,9 @@ describe Chef::Knife::KeyDelete do
   let(:ui) { instance_double("Chef::Knife::UI") }
 
   shared_examples_for "key delete run command" do
-    let(:key_delete_object) {
+    let(:key_delete_object) do
       described_class.new(keyname, actor, actor_field_name, ui)
-    }
+    end
 
     before do
       allow_any_instance_of(Chef::Key).to receive(:destroy)
diff --git a/spec/unit/knife/key_edit_spec.rb b/spec/unit/knife/key_edit_spec.rb
index 9195e97..244d8bd 100644
--- a/spec/unit/knife/key_edit_spec.rb
+++ b/spec/unit/knife/key_edit_spec.rb
@@ -75,7 +75,7 @@ describe "key edit commands that inherit knife" do
 end
 
 describe Chef::Knife::KeyEdit do
-  let(:public_key) {
+  let(:public_key) do
     "-----BEGIN PUBLIC KEY-----
 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvPo+oNPB7uuNkws0fC02
 KxSwdyqPLu0fhI1pOweNKAZeEIiEz2PkybathHWy8snSXGNxsITkf3eyvIIKa8OZ
@@ -85,23 +85,23 @@ IjSmiN/ihHtlhV/VSnBJ5PzT/lRknlrJ4kACoz7Pq9jv+aAx5ft/xE9yDa2DYs0q
 Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
 0wIDAQAB
 -----END PUBLIC KEY-----"
-  }
+  end
   let(:config) { Hash.new }
   let(:actor) { "charmander" }
   let(:keyname) { "charmander-key" }
   let(:ui) { instance_double("Chef::Knife::UI") }
 
   shared_examples_for "key edit run command" do
-    let(:key_edit_object) {
+    let(:key_edit_object) do
       described_class.new(keyname, actor, actor_field_name, ui, config)
-    }
+    end
 
     context "when the command is run" do
-      let(:expected_hash) {
+      let(:expected_hash) do
         {
           actor_field_name => "charmander",
         }
-      }
+      end
       let(:new_keyname) { "charizard-key" }
 
       before do
@@ -126,12 +126,12 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
       end
 
       context "when key_name is passed" do
-        let(:expected_hash) {
+        let(:expected_hash) do
           {
             actor_field_name => "charmander",
             "name" => new_keyname,
           }
-        }
+        end
         before do
           key_edit_object.config[:key_name] = new_keyname
           allow_any_instance_of(Chef::Key).to receive(:update)
@@ -155,14 +155,14 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
       end
 
       context "when public_key, key_name, and expiration_date are passed" do
-        let(:expected_hash) {
+        let(:expected_hash) do
           {
             actor_field_name => "charmander",
             "public_key" => public_key,
             "name" => new_keyname,
             "expiration_date" => "infinity",
           }
-        }
+        end
         before do
           key_edit_object.config[:public_key] = "this-public-key"
           key_edit_object.config[:key_name] = new_keyname
@@ -177,12 +177,12 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
       end
 
       context "when create_key is passed" do
-        let(:expected_hash) {
+        let(:expected_hash) do
           {
             actor_field_name => "charmander",
             "create_key" => true,
           }
-        }
+        end
 
         before do
           key_edit_object.config[:create_key] = true
@@ -196,12 +196,12 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
       end
 
       context "when public_key is passed" do
-        let(:expected_hash) {
+        let(:expected_hash) do
           {
             actor_field_name => "charmander",
             "public_key" => public_key,
           }
-        }
+        end
         before do
           allow(key_edit_object).to receive(:update_key_from_hash).and_return(Chef::Key.from_hash(expected_hash))
           key_edit_object.config[:public_key] = "public_key_path"
@@ -214,13 +214,13 @@ Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
       end # when public_key is passed
 
       context "when the server returns a private key" do
-        let(:expected_hash) {
+        let(:expected_hash) do
           {
             actor_field_name => "charmander",
             "public_key" => public_key,
             "private_key" => "super_private",
           }
-        }
+        end
 
         before do
           allow(key_edit_object).to receive(:update_key_from_hash).and_return(Chef::Key.from_hash(expected_hash))
diff --git a/spec/unit/knife/key_list_spec.rb b/spec/unit/knife/key_list_spec.rb
index 2d4f0a0..82fd1e4 100644
--- a/spec/unit/knife/key_list_spec.rb
+++ b/spec/unit/knife/key_list_spec.rb
@@ -78,9 +78,9 @@ describe Chef::Knife::KeyList do
   let(:ui) { instance_double("Chef::Knife::UI") }
 
   shared_examples_for "key list run command" do
-    let(:key_list_object) {
+    let(:key_list_object) do
       described_class.new(actor, list_method, ui, config)
-    }
+    end
 
     before do
       allow(Chef::Key).to receive(list_method).and_return(http_response)
@@ -191,26 +191,26 @@ describe Chef::Knife::KeyList do
   context "when list_method is :list_by_user" do
     it_should_behave_like "key list run command" do
       let(:list_method) { :list_by_user }
-      let(:http_response) {
+      let(:http_response) do
         [
           { "uri" => "https://api.opscode.piab/users/charmander/keys/non-expired1", "name" => "non-expired1", "expired" => false },
           { "uri" => "https://api.opscode.piab/users/charmander/keys/non-expired2", "name" => "non-expired2", "expired" => false },
           { "uri" => "https://api.opscode.piab/users/mary/keys/out-of-date",        "name" => "out-of-date", "expired" => true },
         ]
-      }
+      end
     end
   end
 
   context "when list_method is :list_by_client" do
     it_should_behave_like "key list run command" do
       let(:list_method) { :list_by_client }
-      let(:http_response) {
+      let(:http_response) do
         [
           { "uri" => "https://api.opscode.piab/organizations/pokemon/clients/charmander/keys/non-expired1", "name" => "non-expired1", "expired" => false },
           { "uri" => "https://api.opscode.piab/organizations/pokemon/clients/charmander/keys/non-expired2", "name" => "non-expired2", "expired" => false },
           { "uri" => "https://api.opscode.piab/organizations/pokemon/clients/mary/keys/out-of-date",        "name" => "out-of-date", "expired" => true },
         ]
-      }
+      end
     end
   end
 end
diff --git a/spec/unit/knife/key_show_spec.rb b/spec/unit/knife/key_show_spec.rb
index c161efb..139d4f9 100644
--- a/spec/unit/knife/key_show_spec.rb
+++ b/spec/unit/knife/key_show_spec.rb
@@ -78,19 +78,19 @@ describe Chef::Knife::KeyShow do
   let(:actor) { "charmander" }
   let(:keyname) { "charmander" }
   let(:ui) { instance_double("Chef::Knife::UI") }
-  let(:expected_hash) {
+  let(:expected_hash) do
     {
       actor_field_name => "charmander",
       "name" => "charmander-key",
       "public_key" => "some-public-key",
       "expiration_date" => "infinity",
     }
-  }
+  end
 
   shared_examples_for "key show run command" do
-    let(:key_show_object) {
+    let(:key_show_object) do
       described_class.new(keyname, actor, load_method, ui)
-    }
+    end
 
     before do
       allow(key_show_object).to receive(:display_output)
diff --git a/spec/unit/knife/node_environment_set_spec.rb b/spec/unit/knife/node_environment_set_spec.rb
index 03fc764..7ceafda 100644
--- a/spec/unit/knife/node_environment_set_spec.rb
+++ b/spec/unit/knife/node_environment_set_spec.rb
@@ -52,29 +52,5 @@ describe Chef::Knife::NodeEnvironmentSet do
       @knife.run
     end
 
-    describe "with no environment" do
-      # Set up outputs for inspection later
-      before(:each) do
-        @stdout = StringIO.new
-        @stderr = StringIO.new
-
-        allow(@knife.ui).to receive(:stdout).and_return(@stdout)
-        allow(@knife.ui).to receive(:stderr).and_return(@stderr)
-      end
-
-      it "should exit" do
-        @knife.name_args = [ "adam" ]
-        expect { @knife.run }.to raise_error SystemExit
-      end
-
-      it "should show the user the usage and an error" do
-        @knife.name_args = [ "adam" ]
-
-        begin ; @knife.run ; rescue SystemExit ; end
-
-        expect(@stdout.string).to eq "USAGE: knife node environment set NODE ENVIRONMENT\n"
-        expect(@stderr.string).to eq "FATAL: You must specify a node name and an environment.\n"
-      end
-    end
   end
 end
diff --git a/spec/unit/knife/node_run_list_set_spec.rb b/spec/unit/knife/node_run_list_set_spec.rb
index 1bbaa7d..bd55edb 100644
--- a/spec/unit/knife/node_run_list_set_spec.rb
+++ b/spec/unit/knife/node_run_list_set_spec.rb
@@ -111,30 +111,5 @@ describe Chef::Knife::NodeRunListSet do
       end
     end
 
-    describe "with no role or recipe" do
-      # Set up outputs for inspection later
-      before(:each) do
-        @stdout = StringIO.new
-        @stderr = StringIO.new
-
-        allow(@knife.ui).to receive(:stdout).and_return(@stdout)
-        allow(@knife.ui).to receive(:stderr).and_return(@stderr)
-      end
-
-      it "should exit" do
-        @knife.name_args = [ "adam" ]
-        expect { @knife.run }.to raise_error SystemExit
-      end
-
-      it "should show the user" do
-        @knife.name_args = [ "adam" ]
-
-        begin ; @knife.run ; rescue SystemExit ; end
-
-        expect(@stdout.string).to eq "USAGE: knife node run_list set NODE ENTRIES (options)\n"
-        expect(@stderr.string).to eq "FATAL: You must supply both a node name and a run list.\n"
-      end
-    end
-
   end
 end
diff --git a/spec/unit/knife/status_spec.rb b/spec/unit/knife/status_spec.rb
index 473598f..c87ea3a 100644
--- a/spec/unit/knife/status_spec.rb
+++ b/spec/unit/knife/status_spec.rb
@@ -34,10 +34,11 @@ describe Chef::Knife::Status do
   end
 
   describe "run" do
-    let(:opts) {{ filter_result:
+    let(:opts) do
+      { filter_result:
                  { name: ["name"], ipaddress: ["ipaddress"], ohai_time: ["ohai_time"],
                    ec2: ["ec2"], run_list: ["run_list"], platform: ["platform"],
-                   platform_version: ["platform_version"], chef_environment: ["chef_environment"] } }}
+                   platform_version: ["platform_version"], chef_environment: ["chef_environment"] } } end
 
     it "should default to searching for everything" do
       expect(@query).to receive(:search).with(:node, "*:*", opts)
diff --git a/spec/unit/knife/user_create_spec.rb b/spec/unit/knife/user_create_spec.rb
index e708d2d..07d72fd 100644
--- a/spec/unit/knife/user_create_spec.rb
+++ b/spec/unit/knife/user_create_spec.rb
@@ -24,13 +24,13 @@ Chef::Knife::UserCreate.load_deps
 describe Chef::Knife::UserCreate do
   let(:knife) { Chef::Knife::UserCreate.new }
 
-  let(:stderr) {
+  let(:stderr) do
     StringIO.new
-  }
+  end
 
-  let(:stdout) {
+  let(:stdout) do
     StringIO.new
-  }
+  end
 
   before(:each) do
     allow(knife.ui).to receive(:stdout).and_return(stdout)
diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb
index ec1e59d..f0ec45d 100644
--- a/spec/unit/knife_spec.rb
+++ b/spec/unit/knife_spec.rb
@@ -159,12 +159,13 @@ describe Chef::Knife do
 
   describe "the headers include X-Remote-Request-Id" do
 
-    let(:headers) {{ "Accept" => "application/json",
-                     "Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
-                     "X-Chef-Version" => Chef::VERSION,
-                     "Host" => "api.opscode.piab",
-                     "X-REMOTE-REQUEST-ID" => request_id,
-    }}
+    let(:headers) do
+      { "Accept" => "application/json",
+        "Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
+        "X-Chef-Version" => Chef::VERSION,
+        "Host" => "api.opscode.piab",
+        "X-REMOTE-REQUEST-ID" => request_id,
+    } end
 
     let(:request_id) { "1234" }
 
diff --git a/spec/unit/lwrp_spec.rb b/spec/unit/lwrp_spec.rb
index 6574a91..0689d99 100644
--- a/spec/unit/lwrp_spec.rb
+++ b/spec/unit/lwrp_spec.rb
@@ -28,13 +28,13 @@ describe "LWRP" do
   include Chef::Mixin::ConvertToClassName
 
   before do
-    @original_VERBOSE = $VERBOSE
+    @original_verbose = $VERBOSE
     $VERBOSE = nil
     Chef::Resource::LWRPBase.class_eval { @loaded_lwrps = {} }
   end
 
   after do
-    $VERBOSE = @original_VERBOSE
+    $VERBOSE = @original_verbose
   end
 
   def get_lwrp(name)
@@ -232,9 +232,9 @@ describe "LWRP" do
       end
 
       it "allows to user to user the resource_name" do
-        expect {
+        expect do
           klass.resource_name(:foo)
-        }.to_not raise_error
+        end.to_not raise_error
       end
 
       it "returns the set value for the resource" do
@@ -741,7 +741,7 @@ describe "LWRP" do
       end
     end
 
-    let(:recipe) {
+    let(:recipe) do
       cookbook_repo = File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "cookbooks"))
       cookbook_loader = Chef::CookbookLoader.new(cookbook_repo)
       cookbook_loader.load_cookbooks
@@ -750,7 +750,7 @@ describe "LWRP" do
       events = Chef::EventDispatch::Dispatcher.new
       run_context = Chef::RunContext.new(node, cookbook_collection, events)
       Chef::Recipe.new("hjk", "test", run_context)
-    }
+    end
 
     it "lets you extend the recipe DSL" do
       expect(Chef::Recipe).to receive(:include).with(MyAwesomeDSLExensionClass)
diff --git a/spec/unit/mixin/api_version_request_handling_spec.rb b/spec/unit/mixin/api_version_request_handling_spec.rb
index 13b7295..191dee6 100644
--- a/spec/unit/mixin/api_version_request_handling_spec.rb
+++ b/spec/unit/mixin/api_version_request_handling_spec.rb
@@ -50,12 +50,12 @@ describe Chef::Mixin::ApiVersionRequestHandling do
       context "when x-ops-server-api-version header exists" do
         let(:min_server_version) { 2 }
         let(:max_server_version) { 4 }
-        let(:return_hash) {
+        let(:return_hash) do
           {
             "min_version" => min_server_version,
             "max_version" => max_server_version,
           }
-        }
+        end
 
         before(:each) do
           allow(response).to receive(:[]).with("x-ops-server-api-version").and_return(Chef::JSONCompat.to_json(return_hash))
diff --git a/spec/unit/mixin/command_spec.rb b/spec/unit/mixin/command_spec.rb
index 0c2f6da..e9f0dac 100644
--- a/spec/unit/mixin/command_spec.rb
+++ b/spec/unit/mixin/command_spec.rb
@@ -49,22 +49,24 @@ describe Chef::Mixin::Command, :volatile do
       end
 
       it "should end when the child process reads from STDIN and a block is given" do
-        expect {Timeout.timeout(10) do
-          popen4("ruby -e 'while gets; end'", :waitlast => true) do |pid, stdin, stdout, stderr|
-            (1..5).each { |i| stdin.puts "#{i}" }
+        expect do
+          Timeout.timeout(10) do
+            popen4("ruby -e 'while gets; end'", :waitlast => true) do |pid, stdin, stdout, stderr|
+              (1..5).each { |i| stdin.puts "#{i}" }
+            end
           end
-        end
-        }.not_to raise_error
+        end.not_to raise_error
       end
 
       describe "when a process detaches but doesn't close STDOUT and STDERR [CHEF-584]" do
 
         it "returns immediately after the first child process exits" do
-          expect {Timeout.timeout(10) do
-            evil_forker = "exit if fork; 10.times { sleep 1}"
-            popen4("ruby -e '#{evil_forker}'") do |pid, stdin, stdout, stderr|
-            end
-          end}.not_to raise_error
+          expect do
+            Timeout.timeout(10) do
+              evil_forker = "exit if fork; 10.times { sleep 1}"
+              popen4("ruby -e '#{evil_forker}'") do |pid, stdin, stdout, stderr|
+              end
+            end end.not_to raise_error
         end
 
       end
@@ -92,10 +94,11 @@ describe Chef::Mixin::Command, :volatile do
           # btm
           # Serdar - During Solaris tests, we've seen that processes
           # are taking a long time to exit. Bumping timeout now to 10.
-          expect {Timeout.timeout(10) do
-            evil_forker = "exit if fork; 10.times { sleep 1}"
-            run_command(:command => "ruby -e '#{evil_forker}'")
-          end}.not_to raise_error
+          expect do
+            Timeout.timeout(10) do
+              evil_forker = "exit if fork; 10.times { sleep 1}"
+              run_command(:command => "ruby -e '#{evil_forker}'")
+            end end.not_to raise_error
         end
 
       end
diff --git a/spec/unit/mixin/deprecation_spec.rb b/spec/unit/mixin/deprecation_spec.rb
index 8f22b09..8707c64 100644
--- a/spec/unit/mixin/deprecation_spec.rb
+++ b/spec/unit/mixin/deprecation_spec.rb
@@ -36,7 +36,7 @@ describe Chef::Mixin do
     end
 
     it "warns when accessing the deprecated constant" do
-      Chef::Mixin::DeprecatedClass
+      Chef::Mixin::DeprecatedClass # rubocop:disable Lint/Void
       expect(@log_io.string).to include("This is a test deprecation")
     end
   end
diff --git a/spec/unit/mixin/homebrew_user_spec.rb b/spec/unit/mixin/homebrew_user_spec.rb
index de72f6b..c9a6e6e 100644
--- a/spec/unit/mixin/homebrew_user_spec.rb
+++ b/spec/unit/mixin/homebrew_user_spec.rb
@@ -51,11 +51,11 @@ describe Chef::Mixin::HomebrewUser do
     let(:user) { nil }
     let(:brew_owner) { 2001 }
     let(:default_brew_path) { "/usr/local/bin/brew" }
-    let(:stat_double) {
+    let(:stat_double) do
       d = double()
       expect(d).to receive(:uid).and_return(brew_owner)
       d
-    }
+    end
 
     context "debug statement prints owner name" do
 
diff --git a/spec/unit/mixin/params_validate_spec.rb b/spec/unit/mixin/params_validate_spec.rb
index dcee123..0cafb92 100644
--- a/spec/unit/mixin/params_validate_spec.rb
+++ b/spec/unit/mixin/params_validate_spec.rb
@@ -56,7 +56,7 @@ describe Chef::Mixin::ParamsValidate do
   end
 
   it "should allow you to check what kind_of? thing an argument is with kind_of" do
-    expect {
+    expect do
       @vo.validate(
         { :one => "string" },
         {
@@ -65,9 +65,9 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.not_to raise_error
+    end.not_to raise_error
 
-    expect {
+    expect do
       @vo.validate(
         { :one => "string" },
         {
@@ -76,11 +76,11 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should allow you to specify an argument is required with required" do
-    expect {
+    expect do
       @vo.validate(
         { :one => "string" },
         {
@@ -89,9 +89,9 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.not_to raise_error
+    end.not_to raise_error
 
-    expect {
+    expect do
       @vo.validate(
         { :two => "string" },
         {
@@ -100,9 +100,9 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
 
-    expect {
+    expect do
       @vo.validate(
         { :two => "string" },
         {
@@ -111,11 +111,11 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.not_to raise_error
+    end.not_to raise_error
   end
 
   it "should allow you to specify whether an object has a method with respond_to" do
-    expect {
+    expect do
       @vo.validate(
         { :one => @vo },
         {
@@ -124,9 +124,9 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.not_to raise_error
+    end.not_to raise_error
 
-    expect {
+    expect do
       @vo.validate(
         { :one => @vo },
         {
@@ -135,11 +135,11 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should allow you to specify whether an object has all the given methods with respond_to and an array" do
-    expect {
+    expect do
       @vo.validate(
         { :one => @vo },
         {
@@ -148,9 +148,9 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.not_to raise_error
+    end.not_to raise_error
 
-    expect {
+    expect do
       @vo.validate(
         { :one => @vo },
         {
@@ -159,7 +159,7 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should let you set a default value with default => value" do
@@ -173,7 +173,7 @@ describe Chef::Mixin::ParamsValidate do
   end
 
   it "should let you check regular expressions" do
-    expect {
+    expect do
       @vo.validate(
         { :one => "is good" },
         {
@@ -182,9 +182,9 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.not_to raise_error
+    end.not_to raise_error
 
-    expect {
+    expect do
       @vo.validate(
         { :one => "is good" },
         {
@@ -193,44 +193,44 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should let you specify your own callbacks" do
-    expect {
+    expect do
       @vo.validate(
         { :one => "is good" },
         {
           :one => {
             :callbacks => {
-              "should be equal to is good" => lambda { |a|
+              "should be equal to is good" => lambda do |a|
                 a == "is good"
-              },
+              end,
             },
           },
         }
       )
-    }.not_to raise_error
+    end.not_to raise_error
 
-    expect {
+    expect do
       @vo.validate(
         { :one => "is bad" },
         {
           :one => {
             :callbacks => {
-              "should be equal to 'is good'" => lambda { |a|
+              "should be equal to 'is good'" => lambda do |a|
                 a == "is good"
-              },
+              end,
             },
           },
         }
       )
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should let you combine checks" do
     args = { :one => "is good", :two => "is bad" }
-    expect {
+    expect do
       @vo.validate(
         args,
         {
@@ -239,9 +239,9 @@ describe Chef::Mixin::ParamsValidate do
             :respond_to => [ :to_s, :upcase ],
             :regex => /^is good/,
             :callbacks => {
-              "should be your friend" => lambda { |a|
+              "should be your friend" => lambda do |a|
                 a == "is good"
-              },
+              end,
             },
             :required => true,
           },
@@ -252,9 +252,9 @@ describe Chef::Mixin::ParamsValidate do
           :three => { :default => "neato mosquito" },
         }
       )
-    }.not_to raise_error
+    end.not_to raise_error
     expect(args[:three]).to eq("neato mosquito")
-    expect {
+    expect do
       @vo.validate(
         args,
         {
@@ -263,9 +263,9 @@ describe Chef::Mixin::ParamsValidate do
             :respond_to => [ :to_s, :upcase ],
             :regex => /^is good/,
             :callbacks => {
-              "should be your friend" => lambda { |a|
+              "should be your friend" => lambda do |a|
                 a == "is good"
-              },
+              end,
             },
             :required => true,
           },
@@ -276,11 +276,12 @@ describe Chef::Mixin::ParamsValidate do
           :three => { :default => "neato mosquito" },
         }
       )
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should raise an ArgumentError if the validation map has an unknown check" do
-    expect { @vo.validate(
+    expect do
+      @vo.validate(
         { :one => "two" },
         {
           :one => {
@@ -288,17 +289,17 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should accept keys that are strings in the options" do
-    expect {
+    expect do
       @vo.validate({ "one" => "two" }, { :one => { :regex => /^two$/ } })
-    }.not_to raise_error
+    end.not_to raise_error
   end
 
   it "should allow an array to kind_of" do
-    expect {
+    expect do
       @vo.validate(
         { :one => "string" },
         {
@@ -307,8 +308,8 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.not_to raise_error
-    expect {
+    end.not_to raise_error
+    expect do
       @vo.validate(
         { :one => ["string"] },
         {
@@ -317,8 +318,8 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.not_to raise_error
-    expect {
+    end.not_to raise_error
+    expect do
       @vo.validate(
         { :one => Hash.new },
         {
@@ -327,7 +328,7 @@ describe Chef::Mixin::ParamsValidate do
           },
         }
       )
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "asserts that a value returns false from a predicate method" do
@@ -354,15 +355,15 @@ describe Chef::Mixin::ParamsValidate do
   end
 
   it "should raise an ArgumentError when argument is nil and required is true" do
-    expect {
+    expect do
       @vo.set_or_return(:test, nil, { :required => true })
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should not raise an error when argument is nil and required is false" do
-    expect {
+    expect do
       @vo.set_or_return(:test, nil, { :required => false })
-    }.not_to raise_error
+    end.not_to raise_error
   end
 
   it "should set and return @name, then return @name for foo when argument is nil" do
diff --git a/spec/unit/mixin/powershell_out_spec.rb b/spec/unit/mixin/powershell_out_spec.rb
index eae5b03..8e5f358 100644
--- a/spec/unit/mixin/powershell_out_spec.rb
+++ b/spec/unit/mixin/powershell_out_spec.rb
@@ -22,9 +22,9 @@ describe Chef::Mixin::PowershellOut do
   let(:shell_out_class) { Class.new { include Chef::Mixin::PowershellOut } }
   subject(:object) { shell_out_class.new }
   let(:architecture) { "something" }
-  let(:flags) {
+  let(:flags) do
     "-NoLogo -NonInteractive -NoProfile -ExecutionPolicy Unrestricted -InputFormat None"
-  }
+  end
 
   describe "#powershell_out" do
     it "runs a command and returns the shell_out object" do
diff --git a/spec/unit/mixin/powershell_type_coercions_spec.rb b/spec/unit/mixin/powershell_type_coercions_spec.rb
index 7f2ecb9..6f52abc 100644
--- a/spec/unit/mixin/powershell_type_coercions_spec.rb
+++ b/spec/unit/mixin/powershell_type_coercions_spec.rb
@@ -27,12 +27,12 @@ end
 describe Chef::Mixin::PowershellTypeCoercions do
   let (:test_class) { Chef::PSTypeTester.new }
 
-  describe '#translate_type' do
+  describe "#translate_type" do
     it "single quotes a string" do
       expect(test_class.translate_type("foo")).to eq("'foo'")
     end
 
-    ["'", '"', '#', "`"].each do |c|
+    ["'", '"', "#", "`"].each do |c|
       it "base64 encodes a string that contains #{c}" do
         expect(test_class.translate_type("#{c}")).to match(Base64.strict_encode64(c))
       end
diff --git a/spec/unit/mixin/shell_out_spec.rb b/spec/unit/mixin/shell_out_spec.rb
index 191ea92..bf74ff4 100644
--- a/spec/unit/mixin/shell_out_spec.rb
+++ b/spec/unit/mixin/shell_out_spec.rb
@@ -25,7 +25,7 @@ require "spec_helper"
 describe Chef::Mixin::ShellOut do
   let(:shell_out_class) { Class.new { include Chef::Mixin::ShellOut } }
   subject(:shell_out_obj) { shell_out_class.new }
-  describe '#run_command_compatible_options' do
+  describe "#run_command_compatible_options" do
     subject { shell_out_obj.run_command_compatible_options(command_args) }
     let(:command_args) { [ cmd, options ] }
     let(:cmd) { "echo '#{rand(1000)}'" }
diff --git a/spec/unit/mixin/xml_escape_spec.rb b/spec/unit/mixin/xml_escape_spec.rb
index 903c911..495ad06 100644
--- a/spec/unit/mixin/xml_escape_spec.rb
+++ b/spec/unit/mixin/xml_escape_spec.rb
@@ -37,7 +37,7 @@ describe Chef::Mixin::XMLEscape do
   end
 
   it "does not modify ASCII strings" do
-    expect(@escaper.xml_escape('foobarbaz!@#$%^*()')).to eq('foobarbaz!@#$%^*()')
+    expect(@escaper.xml_escape("foobarbaz!@\#$%^*()")).to eq("foobarbaz!@\#$%^*()")
   end
 
   it "converts invalid bytes to asterisks" do
@@ -45,10 +45,10 @@ describe Chef::Mixin::XMLEscape do
   end
 
   it "converts UTF-8 correctly" do
-    expect(@escaper.xml_escape("\xC2\xA9")).to eq('©')
+    expect(@escaper.xml_escape("\xC2\xA9")).to eq("©")
   end
 
   it "converts win 1252 characters correctly" do
-    expect(@escaper.xml_escape("#{0x80.chr}")).to eq('€')
+    expect(@escaper.xml_escape("#{0x80.chr}")).to eq("€")
   end
 end
diff --git a/spec/unit/node/attribute_spec.rb b/spec/unit/node/attribute_spec.rb
index 57ad3c0..e40f454 100644
--- a/spec/unit/node/attribute_spec.rb
+++ b/spec/unit/node/attribute_spec.rb
@@ -218,7 +218,7 @@ describe Chef::Node::Attribute do
     end
 
     it "gives the value at each level of precedence for a path spec" do
-      expected = [["set_unless_enabled?", false],
+      expected = [
         %w{default default},
         %w{env_default env_default},
         %w{role_default role_default},
@@ -417,12 +417,6 @@ describe Chef::Node::Attribute do
       expect(@attributes.normal["foo"]["bar"]).to eq(:baz)
     end
 
-    it "should optionally skip setting the value if one already exists" do
-      @attributes.set_unless_value_present = true
-      @attributes.normal["hostname"] = "bar"
-      expect(@attributes["hostname"]).to eq("latte")
-    end
-
     it "does not support ||= when setting" do
       # This is a limitation of auto-vivification.
       # Users who need this behavior can use set_unless and friends
@@ -493,6 +487,7 @@ describe Chef::Node::Attribute do
     end
 
     it "should return true if an attribute exists but is set to nil using dot notation" do
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
       expect(@attributes.music.deeper.has_key?("gates_of_ishtar")).to eq(true)
     end
 
@@ -533,10 +528,12 @@ describe Chef::Node::Attribute do
 
   describe "method_missing" do
     it "should behave like a [] lookup" do
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
       expect(@attributes.music.mastodon).to eq("rocks")
     end
 
     it "should allow the last method to set a value if it has an = sign on the end" do
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
       @attributes.normal.music.mastodon = %w{dream still shining}
       expect(@attributes.normal.music.mastodon).to eq(%w{dream still shining})
     end
@@ -577,7 +574,7 @@ describe Chef::Node::Attribute do
 
     it "should yield lower if we go deeper" do
       collect = Array.new
-      @attributes.one.keys.each do |k|
+      @attributes["one"].keys.each do |k|
         collect << k
       end
       expect(collect.include?("two")).to eq(true)
@@ -587,7 +584,7 @@ describe Chef::Node::Attribute do
     end
 
     it "should not raise an exception if one of the hashes has a nil value on a deep lookup" do
-      expect { @attributes.place.keys { |k| } }.not_to raise_error
+      expect { @attributes["place"].keys { |k| } }.not_to raise_error
     end
   end
 
@@ -1171,6 +1168,7 @@ describe Chef::Node::Attribute do
     end
 
     it "raises an error when using `attr=value`" do
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
       expect { @attributes.new_key = "new value" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
     end
 
diff --git a/spec/unit/node/immutable_collections_spec.rb b/spec/unit/node/immutable_collections_spec.rb
index f57ed45..fe4e50d 100644
--- a/spec/unit/node/immutable_collections_spec.rb
+++ b/spec/unit/node/immutable_collections_spec.rb
@@ -95,6 +95,10 @@ describe Chef::Node::ImmutableMash do
     :replace,
     :select!,
     :shift,
+    :write,
+    :write!,
+    :unlink,
+    :unlink!,
   ].each do |mutator|
     it "doesn't allow mutation via `#{mutator}'" do
       expect { @immutable_mash.send(mutator) }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
diff --git a/spec/unit/node/vivid_mash_spec.rb b/spec/unit/node/vivid_mash_spec.rb
new file mode 100644
index 0000000..5319ba4
--- /dev/null
+++ b/spec/unit/node/vivid_mash_spec.rb
@@ -0,0 +1,377 @@
+#
+# Copyright:: Copyright 2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/node/attribute_collections"
+
+describe Chef::Node::VividMash do
+  class Root
+    attr_accessor :top_level_breadcrumb
+  end
+
+  let(:root) { Root.new }
+
+  let(:vivid) do
+    expect(root).to receive(:reset_cache).at_least(:once).with(nil)
+    Chef::Node::VividMash.new(root,
+      { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }
+    )
+  end
+
+  def with_breadcrumb(key)
+    expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
+    expect(root).to receive(:top_level_breadcrumb=).with(key).at_least(:once).and_call_original
+  end
+
+  context "#read" do
+    before do
+      # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
+      vivid
+      expect(root).not_to receive(:reset_cache)
+    end
+
+    it "reads hashes deeply" do
+      with_breadcrumb("one")
+      expect(vivid.read("one", "two", "three")).to eql("four")
+    end
+
+    it "does not trainwreck when hitting hash keys that do not exist" do
+      with_breadcrumb("one")
+      expect(vivid.read("one", "five", "six")).to eql(nil)
+    end
+
+    it "does not trainwreck when hitting an array with an out of bounds index" do
+      with_breadcrumb("array")
+      expect(vivid.read("array", 5, "one")).to eql(nil)
+    end
+
+    it "does not trainwreck when hitting an array with a string key" do
+      with_breadcrumb("array")
+      expect(vivid.read("array", "one", "two")).to eql(nil)
+    end
+
+    it "does not trainwreck when traversing a nil" do
+      with_breadcrumb("nil")
+      expect(vivid.read("nil", "one", "two")).to eql(nil)
+    end
+  end
+
+  context "#exist?" do
+    before do
+      # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
+      vivid
+      expect(root).not_to receive(:reset_cache)
+    end
+
+    it "true if there's a hash key there" do
+      with_breadcrumb("one")
+      expect(vivid.exist?("one", "two", "three")).to be true
+    end
+
+    it "true for intermediate hashes" do
+      with_breadcrumb("one")
+      expect(vivid.exist?("one")).to be true
+    end
+
+    it "true for arrays that exist" do
+      with_breadcrumb("array")
+      expect(vivid.exist?("array", 1)).to be true
+    end
+
+    it "true when the value of the key is nil" do
+      with_breadcrumb("nil")
+      expect(vivid.exist?("nil")).to be true
+    end
+
+    it "false when attributes don't exist" do
+      with_breadcrumb("one")
+      expect(vivid.exist?("one", "five", "six")).to be false
+    end
+
+    it "false when traversing a non-container" do
+      with_breadcrumb("one")
+      expect(vivid.exist?("one", "two", "three", "four")).to be false
+    end
+
+    it "false when an array index does not exist" do
+      with_breadcrumb("array")
+      expect(vivid.exist?("array", 3)).to be false
+    end
+
+    it "false when traversing a nil" do
+      with_breadcrumb("nil")
+      expect(vivid.exist?("nil", "foo", "bar")).to be false
+    end
+  end
+
+  context "#read!" do
+    before do
+      # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
+      vivid
+      expect(root).not_to receive(:reset_cache)
+    end
+
+    it "reads hashes deeply" do
+      with_breadcrumb("one")
+      expect(vivid.read!("one", "two", "three")).to eql("four")
+    end
+
+    it "reads arrays deeply" do
+      with_breadcrumb("array")
+      expect(vivid.read!("array", 1)).to eql(1)
+    end
+
+    it "throws an exception when attributes do not exist" do
+      with_breadcrumb("one")
+      expect { vivid.read!("one", "five", "six") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+    end
+
+    it "throws an exception when traversing a non-container" do
+      with_breadcrumb("one")
+      expect { vivid.read!("one", "two", "three", "four") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+    end
+
+    it "throws an exception when an array element does not exist" do
+      with_breadcrumb("array")
+      expect { vivid.read!("array", 3) }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+    end
+  end
+
+  context "#write" do
+    before do
+      vivid
+      expect(root).not_to receive(:reset_cache).with(nil)
+    end
+
+    it "should write into hashes" do
+      with_breadcrumb("one")
+      expect(root).to receive(:reset_cache).at_least(:once).with("one")
+      vivid.write("one", "five", "six")
+      expect(vivid["one"]["five"]).to eql("six")
+    end
+
+    it "should deeply autovivify" do
+      with_breadcrumb("one")
+      expect(root).to receive(:reset_cache).at_least(:once).with("one")
+      vivid.write("one", "five", "six", "seven", "eight", "nine", "ten")
+      expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
+    end
+
+    it "should raise an exception if you overwrite an array with a hash" do
+      with_breadcrumb("array")
+      expect(root).to receive(:reset_cache).at_least(:once).with("array")
+      vivid.write("array", "five", "six")
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => "six" }, "nil" => nil })
+    end
+
+    it "should raise an exception if you traverse through an array with a hash" do
+      with_breadcrumb("array")
+      expect(root).to receive(:reset_cache).at_least(:once).with("array")
+      vivid.write("array", "five", "six", "seven")
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => { "six" => "seven" } }, "nil" => nil })
+    end
+
+    it "should raise an exception if you overwrite a string with a hash" do
+      with_breadcrumb("one")
+      expect(root).to receive(:reset_cache).at_least(:once).with("one")
+      vivid.write("one", "two", "three", "four", "five")
+      expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => "five" } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+
+    it "should raise an exception if you traverse through a string with a hash" do
+      with_breadcrumb("one")
+      expect(root).to receive(:reset_cache).at_least(:once).with("one")
+      vivid.write("one", "two", "three", "four", "five", "six")
+      expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => { "five" => "six" } } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+
+    it "should raise an exception if you overwrite a nil with a hash" do
+      with_breadcrumb("nil")
+      expect(root).to receive(:reset_cache).at_least(:once).with("nil")
+      vivid.write("nil", "one", "two")
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => "two" } })
+    end
+
+    it "should raise an exception if you traverse through a nil with a hash" do
+      with_breadcrumb("nil")
+      expect(root).to receive(:reset_cache).at_least(:once).with("nil")
+      vivid.write("nil", "one", "two", "three")
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => { "two" => "three" } } })
+    end
+
+    it "writes with a block" do
+      with_breadcrumb("one")
+      expect(root).to receive(:reset_cache).at_least(:once).with("one")
+      vivid.write("one", "five") { "six" }
+      expect(vivid["one"]["five"]).to eql("six")
+    end
+  end
+
+  context "#write!" do
+    before do
+      vivid
+      expect(root).not_to receive(:reset_cache).with(nil)
+    end
+
+    it "should write into hashes" do
+      with_breadcrumb("one")
+      expect(root).to receive(:reset_cache).at_least(:once).with("one")
+      vivid.write!("one", "five", "six")
+      expect(vivid["one"]["five"]).to eql("six")
+    end
+
+    it "should deeply autovivify" do
+      with_breadcrumb("one")
+      expect(root).to receive(:reset_cache).at_least(:once).with("one")
+      vivid.write!("one", "five", "six", "seven", "eight", "nine", "ten")
+      expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
+    end
+
+    it "should raise an exception if you overwrite an array with a hash" do
+      with_breadcrumb("array")
+      expect(root).not_to receive(:reset_cache)
+      expect { vivid.write!("array", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+
+    it "should raise an exception if you traverse through an array with a hash" do
+      with_breadcrumb("array")
+      expect(root).not_to receive(:reset_cache)
+      expect { vivid.write!("array", "five", "six", "seven") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+
+    it "should raise an exception if you overwrite a string with a hash" do
+      with_breadcrumb("one")
+      expect(root).not_to receive(:reset_cache)
+      expect { vivid.write!("one", "two", "three", "four", "five") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+
+    it "should raise an exception if you traverse through a string with a hash" do
+      with_breadcrumb("one")
+      expect(root).not_to receive(:reset_cache)
+      expect { vivid.write!("one", "two", "three", "four", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+
+    it "should raise an exception if you overwrite a nil with a hash" do
+      with_breadcrumb("nil")
+      expect(root).not_to receive(:reset_cache)
+      expect { vivid.write!("nil", "one", "two") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+
+    it "should raise an exception if you traverse through a nil with a hash" do
+      with_breadcrumb("nil")
+      expect(root).not_to receive(:reset_cache)
+      expect { vivid.write!("nil", "one", "two", "three") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+
+    it "writes with a block" do
+      with_breadcrumb("one")
+      expect(root).to receive(:reset_cache).at_least(:once).with("one")
+      vivid.write!("one", "five") { "six" }
+      expect(vivid["one"]["five"]).to eql("six")
+    end
+  end
+
+  context "#unlink" do
+    before do
+      vivid
+      expect(root).not_to receive(:reset_cache).with(nil)
+    end
+
+    it "should return nil if the keys don't already exist" do
+      expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
+      expect(root).not_to receive(:reset_cache)
+      expect(vivid.unlink("five", "six", "seven", "eight")).to eql(nil)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+
+    it "should unlink hashes" do
+      with_breadcrumb("one")
+      expect(root).to receive(:reset_cache).at_least(:once).with("one")
+      expect( vivid.unlink("one") ).to eql({ "two" => { "three" => "four" } })
+      expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+
+    it "should unlink array elements" do
+      with_breadcrumb("array")
+      expect(root).to receive(:reset_cache).at_least(:once).with("array")
+      expect(vivid.unlink("array", 2)).to eql(2)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
+    end
+
+    it "should unlink nil" do
+      with_breadcrumb("nil")
+      expect(root).to receive(:reset_cache).at_least(:once).with("nil")
+      expect(vivid.unlink("nil")).to eql(nil)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
+    end
+
+    it "should traverse a nil and safely do nothing" do
+      with_breadcrumb("nil")
+      expect(root).not_to receive(:reset_cache)
+      expect(vivid.unlink("nil", "foo")).to eql(nil)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+  end
+
+  context "#unlink!" do
+    before do
+      vivid
+      expect(root).not_to receive(:reset_cache).with(nil)
+    end
+
+    it "should raise an exception if the keys don't already exist" do
+      expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
+      expect(root).not_to receive(:reset_cache)
+      expect { vivid.unlink!("five", "six", "seven", "eight") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+
+    it "should unlink! hashes" do
+      with_breadcrumb("one")
+      expect(root).to receive(:reset_cache).at_least(:once).with("one")
+      expect( vivid.unlink!("one") ).to eql({ "two" => { "three" => "four" } })
+      expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+
+    it "should unlink! array elements" do
+      with_breadcrumb("array")
+      expect(root).to receive(:reset_cache).at_least(:once).with("array")
+      expect(vivid.unlink!("array", 2)).to eql(2)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
+    end
+
+    it "should unlink! nil" do
+      with_breadcrumb("nil")
+      expect(root).to receive(:reset_cache).at_least(:once).with("nil")
+      expect(vivid.unlink!("nil")).to eql(nil)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
+    end
+
+    it "should raise an exception if it traverses a nil" do
+      with_breadcrumb("nil")
+      expect(root).not_to receive(:reset_cache)
+      expect { vivid.unlink!("nil", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
+    end
+  end
+end
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index 923b488..2c8fc44 100644
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -233,60 +233,69 @@ describe Chef::Node do
     end
 
     it "should let you go deep with attribute?" do
-      node.set["battles"]["people"]["wonkey"] = true
+      node.normal["battles"]["people"]["wonkey"] = true
       expect(node["battles"]["people"].attribute?("wonkey")).to eq(true)
       expect(node["battles"]["people"].attribute?("snozzberry")).to eq(false)
     end
 
     it "does not allow you to set an attribute via method_missing" do
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
       expect { node.sunshine = "is bright" }.to raise_error(Chef::Exceptions::ImmutableAttributeModification)
     end
 
     it "should allow you get get an attribute via method_missing" do
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
       node.default.sunshine = "is bright"
       expect(node.sunshine).to eql("is bright")
     end
 
     describe "normal attributes" do
       it "should allow you to set an attribute with set, without pre-declaring a hash" do
-        node.set[:snoopy][:is_a_puppy] = true
+        node.normal[:snoopy][:is_a_puppy] = true
         expect(node[:snoopy][:is_a_puppy]).to eq(true)
       end
 
+      it "should allow you to set an attribute with set_unless with method_missing but emit a deprecation warning" do
+        Chef::Config[:treat_deprecation_warnings_as_errors] = false
+        node.normal_unless.snoopy.is_a_puppy = false
+        expect(node[:snoopy][:is_a_puppy]).to eq(false)
+      end
+
       it "should allow you to set an attribute with set_unless" do
-        node.set_unless[:snoopy][:is_a_puppy] = false
+        node.normal_unless[:snoopy][:is_a_puppy] = false
         expect(node[:snoopy][:is_a_puppy]).to eq(false)
       end
 
       it "should not allow you to set an attribute with set_unless if it already exists" do
-        node.set[:snoopy][:is_a_puppy] = true
-        node.set_unless[:snoopy][:is_a_puppy] = false
+        node.normal[:snoopy][:is_a_puppy] = true
+        node.normal_unless[:snoopy][:is_a_puppy] = false
         expect(node[:snoopy][:is_a_puppy]).to eq(true)
       end
 
       it "should allow you to set an attribute with set_unless if is a nil value" do
         node.attributes.normal = { snoopy: { is_a_puppy: nil } }
-        node.set_unless[:snoopy][:is_a_puppy] = false
+        node.normal_unless[:snoopy][:is_a_puppy] = false
         expect(node[:snoopy][:is_a_puppy]).to eq(false)
       end
 
       it "should allow you to set a value after a set_unless" do
         # this tests for set_unless_present state bleeding between statements CHEF-3806
-        node.set_unless[:snoopy][:is_a_puppy] = false
-        node.set[:snoopy][:is_a_puppy] = true
+        node.normal_unless[:snoopy][:is_a_puppy] = false
+        node.normal[:snoopy][:is_a_puppy] = true
         expect(node[:snoopy][:is_a_puppy]).to eq(true)
       end
 
       it "should let you set a value after a 'dangling' set_unless" do
         # this tests for set_unless_present state bleeding between statements CHEF-3806
-        node.set[:snoopy][:is_a_puppy] = "what"
-        node.set_unless[:snoopy][:is_a_puppy]
-        node.set[:snoopy][:is_a_puppy] = true
+        node.normal[:snoopy][:is_a_puppy] = "what"
+        node.normal_unless[:snoopy][:is_a_puppy]
+        node.normal[:snoopy][:is_a_puppy] = true
         expect(node[:snoopy][:is_a_puppy]).to eq(true)
       end
 
       it "auto-vivifies attributes created via method syntax" do
-        node.set.fuu.bahrr.baz = "qux"
+        Chef::Config[:treat_deprecation_warnings_as_errors] = false
+        node.normal.fuu.bahrr.baz = "qux"
         expect(node.fuu.bahrr.baz).to eq("qux")
       end
 
@@ -295,6 +304,20 @@ describe Chef::Node do
         node.tag("three", "four")
         expect(node["tags"]).to eq(%w{one two three four})
       end
+
+      it "set is a deprecated alias for normal" do
+        Chef::Config[:treat_deprecation_warnings_as_errors] = false
+        expect(Chef).to receive(:log_deprecation).with(/set is deprecated/)
+        node.set[:snoopy][:is_a_puppy] = true
+        expect(node[:snoopy][:is_a_puppy]).to eq(true)
+      end
+
+      it "set_unless is a deprecated alias for normal_unless" do
+        Chef::Config[:treat_deprecation_warnings_as_errors] = false
+        expect(Chef).to receive(:log_deprecation).with(/set_unless is deprecated/)
+        node.set_unless[:snoopy][:is_a_puppy] = false
+        expect(node[:snoopy][:is_a_puppy]).to eq(false)
+      end
     end
 
     describe "default attributes" do
@@ -329,10 +352,41 @@ describe Chef::Node do
         expect(node[:snoopy][:is_a_puppy]).to eq(true)
       end
 
+      it "does not exhibit chef/chef/issues/5005 bug" do
+        node.env_default["a"]["r1"]["g"]["u"] = "u1"
+        node.default_unless["a"]["r1"]["g"]["r"] = "r"
+        expect(node["a"]["r1"]["g"]["u"]).to eql("u1")
+      end
+
       it "auto-vivifies attributes created via method syntax" do
+        Chef::Config[:treat_deprecation_warnings_as_errors] = false
         node.default.fuu.bahrr.baz = "qux"
         expect(node.fuu.bahrr.baz).to eq("qux")
       end
+
+      it "default_unless correctly resets the deep merge cache" do
+        node.normal["tags"] = []  # this sets our top-level breadcrumb
+        node.default_unless["foo"]["bar"] = "NK-19V"
+        expect(node["foo"]["bar"]).to eql("NK-19V")
+        node.default_unless["foo"]["baz"] = "NK-33"
+        expect(node["foo"]["baz"]).to eql("NK-33")
+      end
+
+      it "normal_unless correctly resets the deep merge cache" do
+        node.normal["tags"] = []  # this sets our top-level breadcrumb
+        node.normal_unless["foo"]["bar"] = "NK-19V"
+        expect(node["foo"]["bar"]).to eql("NK-19V")
+        node.normal_unless["foo"]["baz"] = "NK-33"
+        expect(node["foo"]["baz"]).to eql("NK-33")
+      end
+
+      it "override_unless correctly resets the deep merge cache" do
+        node.normal["tags"] = []  # this sets our top-level breadcrumb
+        node.override_unless["foo"]["bar"] = "NK-19V"
+        expect(node["foo"]["bar"]).to eql("NK-19V")
+        node.override_unless["foo"]["baz"] = "NK-33"
+        expect(node["foo"]["baz"]).to eql("NK-33")
+      end
     end
 
     describe "override attributes" do
@@ -368,6 +422,7 @@ describe Chef::Node do
       end
 
       it "auto-vivifies attributes created via method syntax" do
+        Chef::Config[:treat_deprecation_warnings_as_errors] = false
         node.override.fuu.bahrr.baz = "qux"
         expect(node.fuu.bahrr.baz).to eq("qux")
       end
@@ -453,8 +508,9 @@ describe Chef::Node do
           expect( node["mysql"]["server"][0]["port"] ).to be_nil
         end
 
-        it "does not have a horrible error message when mistaking arrays for hashes" do
-          expect { node.rm("mysql", "server", "port") }.to raise_error(TypeError, "Wrong type in index of attribute (did you use a Hash index on an Array?)")
+        it "when mistaking arrays for hashes, it considers the value removed and does nothing" do
+          node.rm("mysql", "server", "port")
+          expect(node["mysql"]["server"][0]["port"]).to eql(3456)
         end
       end
     end
@@ -706,6 +762,7 @@ describe Chef::Node do
     #
     describe "deep merge attribute cache edge conditions" do
       it "does not error with complicated attribute substitution" do
+        Chef::Config[:treat_deprecation_warnings_as_errors] = false
         node.default["chef_attribute_hell"]["attr1"] = "attribute1"
         node.default["chef_attribute_hell"]["attr2"] = "#{node.chef_attribute_hell.attr1}/attr2"
         expect { node.default["chef_attribute_hell"]["attr3"] = "#{node.chef_attribute_hell.attr2}/attr3" }.not_to raise_error
@@ -720,6 +777,7 @@ describe Chef::Node do
       end
 
       it "method interpolation syntax also works" do
+        Chef::Config[:treat_deprecation_warnings_as_errors] = false
         node.default["passenger"]["version"]     = "4.0.57"
         node.default["passenger"]["root_path"]   = "passenger-#{node['passenger']['version']}"
         node.default["passenger"]["root_path_2"] = "passenger-#{node.passenger['version']}"
@@ -729,6 +787,7 @@ describe Chef::Node do
     end
 
     it "should raise an ArgumentError if you ask for an attribute that doesn't exist via method_missing" do
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
       expect { node.sunshine }.to raise_error(NoMethodError)
     end
 
@@ -744,6 +803,51 @@ describe Chef::Node do
       expect(seen_attributes["sunshine"]).to eq("is bright")
       expect(seen_attributes["canada"]).to eq("is a nice place")
     end
+
+    describe "functional attribute API" do
+      # deeper functional testing of this API is in the VividMash spec tests
+      it "should have an exist? function" do
+        node.default["foo"]["bar"] = "baz"
+        expect(node.exist?("foo", "bar")).to be true
+        expect(node.exist?("bar", "foo")).to be false
+      end
+
+      it "should have a read function" do
+        node.override["foo"]["bar"] = "baz"
+        expect(node.read("foo", "bar")).to eql("baz")
+        expect(node.read("bar", "foo")).to eql(nil)
+      end
+
+      it "should have a read! function" do
+        node.override["foo"]["bar"] = "baz"
+        expect(node.read!("foo", "bar")).to eql("baz")
+        expect { node.read!("bar", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+      end
+
+      it "delegates write(:level) to node.level.write()" do
+        node.write(:default, "foo", "bar", "baz")
+        expect(node.default["foo"]["bar"]).to eql("baz")
+      end
+
+      it "delegates write!(:level) to node.level.write!()" do
+        node.write!(:default, "foo", "bar", "baz")
+        expect(node.default["foo"]["bar"]).to eql("baz")
+        node.default["bar"] = true
+        expect { node.write!(:default, "bar", "foo", "baz") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
+      end
+
+      it "delegates unlink(:level) to node.level.unlink()" do
+        node.default["foo"]["bar"] = "baz"
+        expect(node.unlink(:default, "foo", "bar")).to eql("baz")
+        expect(node.unlink(:default, "bar", "foo")).to eql(nil)
+      end
+
+      it "delegates unlink!(:level) to node.level.unlink!()" do
+        node.default["foo"]["bar"] = "baz"
+        expect(node.unlink!(:default, "foo", "bar")).to eql("baz")
+        expect { node.unlink!(:default, "bar", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
+      end
+    end
   end
 
   describe "consuming json" do
@@ -789,8 +893,8 @@ describe Chef::Node do
 
     it "should add json attributes to the node" do
       node.consume_external_attrs(@ohai_data, { "one" => "two", "three" => "four" })
-      expect(node.one).to eql("two")
-      expect(node.three).to eql("four")
+      expect(node["one"]).to eql("two")
+      expect(node["three"]).to eql("four")
     end
 
     it "should set the tags attribute to an empty array if it is not already defined" do
@@ -824,17 +928,17 @@ describe Chef::Node do
 
     it "deep merges attributes instead of overwriting them" do
       node.consume_external_attrs(@ohai_data, "one" => { "two" => { "three" => "four" } })
-      expect(node.one.to_hash).to eq({ "two" => { "three" => "four" } })
+      expect(node["one"].to_hash).to eq({ "two" => { "three" => "four" } })
       node.consume_external_attrs(@ohai_data, "one" => { "abc" => "123" })
       node.consume_external_attrs(@ohai_data, "one" => { "two" => { "foo" => "bar" } })
-      expect(node.one.to_hash).to eq({ "two" => { "three" => "four", "foo" => "bar" }, "abc" => "123" })
+      expect(node["one"].to_hash).to eq({ "two" => { "three" => "four", "foo" => "bar" }, "abc" => "123" })
     end
 
     it "gives attributes from JSON priority when deep merging" do
       node.consume_external_attrs(@ohai_data, "one" => { "two" => { "three" => "four" } })
-      expect(node.one.to_hash).to eq({ "two" => { "three" => "four" } })
+      expect(node["one"].to_hash).to eq({ "two" => { "three" => "four" } })
       node.consume_external_attrs(@ohai_data, "one" => { "two" => { "three" => "forty-two" } })
-      expect(node.one.to_hash).to eq({ "two" => { "three" => "forty-two" } })
+      expect(node["one"].to_hash).to eq({ "two" => { "three" => "forty-two" } })
     end
 
   end
@@ -1036,10 +1140,10 @@ describe Chef::Node do
     end
 
     it "sets attributes from the files" do
-      expect(node.ldap_server).to eql("ops1prod")
-      expect(node.ldap_basedn).to eql("dc=hjksolutions,dc=com")
-      expect(node.ldap_replication_password).to eql("forsure")
-      expect(node.smokey).to eql("robinson")
+      expect(node["ldap_server"]).to eql("ops1prod")
+      expect(node["ldap_basedn"]).to eql("dc=hjksolutions,dc=com")
+      expect(node["ldap_replication_password"]).to eql("forsure")
+      expect(node["smokey"]).to eql("robinson")
     end
 
     it "gives a sensible error when attempting to load a missing attributes file" do
@@ -1083,8 +1187,8 @@ describe Chef::Node do
     it "should load a node from a ruby file" do
       node.from_file(File.expand_path(File.join(CHEF_SPEC_DATA, "nodes", "test.rb")))
       expect(node.name).to eql("test.example.com-short")
-      expect(node.sunshine).to eql("in")
-      expect(node.something).to eql("else")
+      expect(node["sunshine"]).to eql("in")
+      expect(node["something"]).to eql("else")
       expect(node.run_list).to eq(["operations-master", "operations-monitoring"])
     end
 
@@ -1255,10 +1359,10 @@ describe Chef::Node do
     end
 
     include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
-      let(:jsonable) {
+      let(:jsonable) do
         node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA))
         node
-      }
+      end
     end
   end
 
@@ -1562,4 +1666,19 @@ describe Chef::Node do
     end
   end
 
+  describe "method_missing handling" do
+    it "should have an #empty? method via Chef::Node::Attribute" do
+      node.default["foo"] = "bar"
+      expect(node.empty?).to be false
+    end
+
+    it "it should correctly implement #respond_to?" do
+      expect(node.respond_to?(:empty?)).to be true
+    end
+
+    it "it should correctly retrieve the method with #method" do
+      expect(node.method(:empty?)).to be_kind_of(Method)
+    end
+  end
+
 end
diff --git a/spec/unit/platform/query_helpers_spec.rb b/spec/unit/platform/query_helpers_spec.rb
index 49da6a9..aa2b3c1 100644
--- a/spec/unit/platform/query_helpers_spec.rb
+++ b/spec/unit/platform/query_helpers_spec.rb
@@ -151,7 +151,7 @@ describe "Chef::Platform#supports_msi?" do
   end
 end
 
-describe 'Chef::Platform#supports_dsc?' do
+describe "Chef::Platform#supports_dsc?" do
   it "returns false if powershell is not present" do
     node = Chef::Node.new
     expect(Chef::Platform.supports_dsc?(node)).to be_falsey
@@ -174,7 +174,7 @@ describe 'Chef::Platform#supports_dsc?' do
   end
 end
 
-describe 'Chef::Platform#supports_dsc_invoke_resource?' do
+describe "Chef::Platform#supports_dsc_invoke_resource?" do
   it "returns false if powershell is not present" do
     node = Chef::Node.new
     expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_falsey
@@ -195,7 +195,7 @@ describe 'Chef::Platform#supports_dsc_invoke_resource?' do
   end
 end
 
-describe 'Chef::Platform#dsc_refresh_mode_disabled?' do
+describe "Chef::Platform#dsc_refresh_mode_disabled?" do
   let(:node) { instance_double("Chef::Node") }
   let(:cmdlet) { instance_double("Chef::Util::Powershell::Cmdlet") }
   let(:cmdlet_result) { instance_double("Chef::Util::Powershell::CmdletResult") }
diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb
index f5eb361..50ff343 100644
--- a/spec/unit/property_spec.rb
+++ b/spec/unit/property_spec.rb
@@ -1103,18 +1103,18 @@ describe "Chef::Resource.property" do
 
   context "property_type" do
     it "property_types validate their defaults" do
-      expect {
+      expect do
         module ::PropertySpecPropertyTypes
           include Chef::Mixin::Properties
           property_type(is: [:a, :b], default: :c)
         end
-      }.to raise_error(Chef::Exceptions::DeprecatedFeatureError, /Default value :c is invalid for property <property type>./)
-      expect {
+      end.to raise_error(Chef::Exceptions::DeprecatedFeatureError, /Default value :c is invalid for property <property type>./)
+      expect do
         module ::PropertySpecPropertyTypes
           include Chef::Mixin::Properties
           property_type(is: [:a, :b], default: :b)
         end
-      }.not_to raise_error
+      end.not_to raise_error
     end
 
     context "With property_type ABType (is: [:a, :b]) and CDType (is: [:c, :d])" do
diff --git a/spec/unit/provider/cookbook_file_spec.rb b/spec/unit/provider/cookbook_file_spec.rb
index b375784..f49cc7d 100644
--- a/spec/unit/provider/cookbook_file_spec.rb
+++ b/spec/unit/provider/cookbook_file_spec.rb
@@ -26,12 +26,12 @@ describe Chef::Provider::CookbookFile do
   let(:node) { double("Chef::Node") }
   let(:events) { double("Chef::Events").as_null_object } # mock all the methods
   let(:run_context) { double("Chef::RunContext", :node => node, :events => events) }
-  let(:enclosing_directory) {
+  let(:enclosing_directory) do
     canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
-  }
-  let(:resource_path) {
+  end
+  let(:resource_path) do
     canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
-  }
+  end
 
   # Subject
 
diff --git a/spec/unit/provider/cron/unix_spec.rb b/spec/unit/provider/cron/unix_spec.rb
index 83e0f43..5e1fcb3 100644
--- a/spec/unit/provider/cron/unix_spec.rb
+++ b/spec/unit/provider/cron/unix_spec.rb
@@ -91,9 +91,9 @@ describe Chef::Provider::Cron::Unix do
       let (:exitstatus) { 2 }
 
       it "should raise an exception if another error occurs" do
-        expect {
+        expect do
           provider.send(:read_crontab)
-        }.to raise_error(Chef::Exceptions::Cron, "Error determining state of #{new_resource.name}, exit: 2")
+        end.to raise_error(Chef::Exceptions::Cron, "Error determining state of #{new_resource.name}, exit: 2")
       end
 
       it "logs the crontab output to debug" do
@@ -130,9 +130,9 @@ describe Chef::Provider::Cron::Unix do
     context "when writing the crontab fails" do
       let(:exitstatus) { 1 }
       it "should raise an exception if the command returns non-zero" do
-        expect {
+        expect do
           provider.send(:write_crontab, "Foo")
-        }.to raise_error(Chef::Exceptions::Cron, /Error updating state of #{new_resource.name}, exit: 1/)
+        end.to raise_error(Chef::Exceptions::Cron, /Error updating state of #{new_resource.name}, exit: 1/)
       end
     end
   end
diff --git a/spec/unit/provider/cron_spec.rb b/spec/unit/provider/cron_spec.rb
index 010b1b0..64916ef 100644
--- a/spec/unit/provider/cron_spec.rb
+++ b/spec/unit/provider/cron_spec.rb
@@ -199,9 +199,9 @@ CRONTAB
 # Chef Name: foo[bar] (baz)
 21 */4 * * * some_prog 1234567
 CRONTAB
-        expect {
+        expect do
           @provider.load_current_resource
-        }.not_to raise_error
+        end.not_to raise_error
       end
     end
 
@@ -462,10 +462,10 @@ CRONTAB
         @new_resource.environment "TEST" => "LOL"
         expect(@provider).to receive(:write_crontab).with(<<-ENDCRON)
 # Chef Name: cronhole some stuff
-MAILTO=foo at example.com
-PATH=/usr/bin:/my/custom/path
-SHELL=/bin/foosh
-HOME=/home/foo
+MAILTO="foo at example.com"
+PATH="/usr/bin:/my/custom/path"
+SHELL="/bin/foosh"
+HOME="/home/foo"
 TEST=LOL
 30 * * * * /bin/true
         ENDCRON
@@ -524,10 +524,10 @@ TEST=LOL
 
 # Another comment
 # Chef Name: cronhole some stuff
-MAILTO=foo at example.com
-PATH=/usr/bin:/my/custom/path
-SHELL=/bin/foosh
-HOME=/home/foo
+MAILTO="foo at example.com"
+PATH="/usr/bin:/my/custom/path"
+SHELL="/bin/foosh"
+HOME="/home/foo"
 TEST=LOL
 30 * * * * /bin/true
         ENDCRON
@@ -585,10 +585,10 @@ TEST=LOL
 0 2 * * * /some/other/command
 
 # Chef Name: cronhole some stuff
-MAILTO=foo at example.com
-PATH=/usr/bin:/my/custom/path
-SHELL=/bin/foosh
-HOME=/home/foo
+MAILTO="foo at example.com"
+PATH="/usr/bin:/my/custom/path"
+SHELL="/bin/foosh"
+HOME="/home/foo"
 TEST=LOL
 30 * * * * /bin/true
 # Chef Name: something else
@@ -679,10 +679,10 @@ HOME=/home/foo
 0 2 * * * /some/other/command
 
 # Chef Name: cronhole some stuff
-MAILTO=foo at example.com
-PATH=/usr/bin:/my/custom/path
-SHELL=/bin/foosh
-HOME=/home/foo
+MAILTO="foo at example.com"
+PATH="/usr/bin:/my/custom/path"
+SHELL="/bin/foosh"
+HOME="/home/foo"
 30 * * * * /bin/true
 
 # Chef Name: something else
diff --git a/spec/unit/provider/deploy_spec.rb b/spec/unit/provider/deploy_spec.rb
index 62d9123..e697142 100644
--- a/spec/unit/provider/deploy_spec.rb
+++ b/spec/unit/provider/deploy_spec.rb
@@ -167,9 +167,9 @@ describe Chef::Provider::Deploy do
     allow(@provider).to receive(:deploy) { raise "Unexpected error" }
     allow(@provider).to receive(:previous_release_path).and_return("previous_release")
     expect(@provider).not_to receive(:rollback)
-    expect {
+    expect do
       @provider.run_action(:deploy)
-    }.to raise_exception(RuntimeError, "Unexpected error")
+    end.to raise_exception(RuntimeError, "Unexpected error")
   end
 
   it "rollbacks to previous release if error happens on deploy" do
@@ -178,9 +178,9 @@ describe Chef::Provider::Deploy do
     allow(@provider).to receive(:deploy) { raise "Unexpected error" }
     allow(@provider).to receive(:previous_release_path).and_return("previous_release")
     expect(@provider).to receive(:rollback)
-    expect {
+    expect do
       @provider.run_action(:deploy)
-    }.to raise_exception(RuntimeError, "Unexpected error")
+    end.to raise_exception(RuntimeError, "Unexpected error")
   end
 
   describe "on systems without broken Dir.glob results" do
@@ -232,17 +232,17 @@ describe Chef::Provider::Deploy do
         #FileUtils.should_receive(:rm_rf).with("/my/deploy/dir/releases/20040815162342")
         #@provider.run_action(:rollback)
         #@provider.release_path.should eql(NIL) -- no check needed since assertions will fail
-        expect {
+        expect do
           @provider.run_action(:rollback)
-        }.to raise_exception(RuntimeError, "There is no release to rollback to!")
+        end.to raise_exception(RuntimeError, "There is no release to rollback to!")
       end
 
       it "an exception is raised when there are no releases" do
         all_releases = []
         allow(Dir).to receive(:glob).with("/my/deploy/dir/releases/*").and_return(all_releases)
-        expect {
+        expect do
           @provider.run_action(:rollback)
-        }.to raise_exception(RuntimeError, "There is no release to rollback to!")
+        end.to raise_exception(RuntimeError, "There is no release to rollback to!")
       end
     end
   end
diff --git a/spec/unit/provider/directory_spec.rb b/spec/unit/provider/directory_spec.rb
index f8864af..aebbaa6 100644
--- a/spec/unit/provider/directory_spec.rb
+++ b/spec/unit/provider/directory_spec.rb
@@ -227,7 +227,7 @@ describe Chef::Provider::Directory do
     end
   end
 
-  describe "#run_action(:create)" do
+  describe "#run_action(:delete)" do
     describe "when the directory exists" do
       it "deletes the directory" do
         directory.run_action(:delete)
@@ -238,6 +238,16 @@ describe Chef::Provider::Directory do
         directory.run_action(:delete)
         expect(new_resource).to be_updated
       end
+
+      it "does not use rm_rf which silently consumes errors" do
+        expect(FileUtils).not_to receive(:rm_rf)
+        expect(FileUtils).to receive(:rm_r)
+        # set recursive or FileUtils isn't used at all.
+        new_resource.recursive(true)
+        directory.run_action(:delete)
+        # reset back...
+        new_resource.recursive(false)
+      end
     end
 
     describe "when the directory does not exist" do
diff --git a/spec/unit/provider/dsc_resource_spec.rb b/spec/unit/provider/dsc_resource_spec.rb
index 2a00c2e..34eb972 100644
--- a/spec/unit/provider/dsc_resource_spec.rb
+++ b/spec/unit/provider/dsc_resource_spec.rb
@@ -28,11 +28,11 @@ describe Chef::Provider::DscResource do
   end
 
   context "when Powershell does not support Invoke-DscResource" do
-    let (:node) {
+    let (:node) do
       node = Chef::Node.new
       node.automatic[:languages][:powershell][:version] = "4.0"
       node
-    }
+    end
     it "raises a ProviderNotFound exception" do
       expect(provider).not_to receive(:meta_configuration)
       expect { provider.run_action(:run) }.to raise_error(
@@ -44,11 +44,11 @@ describe Chef::Provider::DscResource do
 
     context "when RefreshMode is not set to Disabled" do
       context "and the WMF 5 is a preview release" do
-        let (:node) {
+        let (:node) do
           node = Chef::Node.new
           node.automatic[:languages][:powershell][:version] = "5.0.10018.0"
           node
-        }
+        end
         it "raises an exception" do
           expect(provider).to receive(:dsc_refresh_mode_disabled?).and_return(false)
           expect { provider.run_action(:run) }.to raise_error(
@@ -56,11 +56,11 @@ describe Chef::Provider::DscResource do
         end
       end
       context "and the WMF is 5 RTM or newer" do
-        let (:node) {
+        let (:node) do
           node = Chef::Node.new
           node.automatic[:languages][:powershell][:version] = "5.0.10586.0"
           node
-        }
+        end
         it "does not raises an exception" do
           expect(provider).to receive(:test_resource)
           expect(provider).to receive(:set_resource)
@@ -72,11 +72,11 @@ describe Chef::Provider::DscResource do
   end
 
   context "when the LCM supports Invoke-DscResource" do
-    let (:node) {
+    let (:node) do
       node = Chef::Node.new
       node.automatic[:languages][:powershell][:version] = "5.0.10018.0"
       node
-    }
+    end
     let (:resource_result) { double("CmdletResult", return_value: { "InDesiredState" => true }, stream: "description") }
     let (:invoke_dsc_resource) { double("cmdlet", run!: resource_result) }
     let (:store) { double("ResourceStore", find: resource_records) }
@@ -153,10 +153,11 @@ describe Chef::Provider::DscResource do
       end
 
       context "multiple resource are found" do
-        let (:resource_records) { [
+        let (:resource_records) do
+          [
           { "Module" => { "Name" => "ModuleName1" } },
           { "Module" => { "Name" => "ModuleName2" } },
-        ] }
+        ] end
 
         it "raises MultipleDscResourcesFound" do
           expect { provider.run_action(:run) }.to raise_error(Chef::Exceptions::MultipleDscResourcesFound)
diff --git a/spec/unit/provider/dsc_script_spec.rb b/spec/unit/provider/dsc_script_spec.rb
index 3877a37..5f091b8 100644
--- a/spec/unit/provider/dsc_script_spec.rb
+++ b/spec/unit/provider/dsc_script_spec.rb
@@ -23,11 +23,11 @@ require "spec_helper"
 
 describe Chef::Provider::DscScript do
   context "when DSC is available" do
-    let (:node) {
+    let (:node) do
       node = Chef::Node.new
       node.automatic[:languages][:powershell][:version] = "4.0"
       node
-    }
+    end
     let (:events) { Chef::EventDispatch::Dispatcher.new }
     let (:run_context) { Chef::RunContext.new(node, {}, events) }
     let (:resource) { Chef::Resource::DscScript.new("script", run_context) }
@@ -35,7 +35,7 @@ describe Chef::Provider::DscScript do
       Chef::Provider::DscScript.new(resource, run_context)
     end
 
-    describe '#load_current_resource' do
+    describe "#load_current_resource" do
       it "describes the resource as converged if there were 0 DSC resources" do
         allow(provider).to receive(:run_configuration).with(:test).and_return([])
         provider.load_current_resource
@@ -75,7 +75,7 @@ describe Chef::Provider::DscScript do
       end
     end
 
-    describe '#generate_configuration_document' do
+    describe "#generate_configuration_document" do
       # I think integration tests should cover these cases
 
       it "uses configuration_document_from_script_path when a dsc script file is given" do
@@ -123,7 +123,7 @@ describe Chef::Provider::DscScript do
       end
     end
 
-    describe '#generate_description' do
+    describe "#generate_description" do
       it "removes the resource name from the beginning of any log line from the LCM" do
         dsc_resource_info = Chef::Util::DSC::ResourceInfo.new("resourcename", true, ["resourcename doing something", "lastline"])
         provider.instance_variable_set("@dsc_resources_info", [dsc_resource_info])
@@ -156,16 +156,16 @@ describe Chef::Provider::DscScript do
         it "raises an exception for powershell version '#{version}'" do
           node.automatic[:languages][:powershell][:version] = version
 
-          expect {
+          expect do
             provider.run_action(:run)
-          }.to raise_error(Chef::Exceptions::ProviderNotFound)
+          end.to raise_error(Chef::Exceptions::ProviderNotFound)
         end
       end
 
       it "raises an exception if Powershell is not present" do
-        expect {
+        expect do
           provider.run_action(:run)
-        }.to raise_error(Chef::Exceptions::ProviderNotFound)
+        end.to raise_error(Chef::Exceptions::ProviderNotFound)
       end
 
     end
diff --git a/spec/unit/provider/env/windows_spec.rb b/spec/unit/provider/env/windows_spec.rb
index abe2344..5ddc1d6 100644
--- a/spec/unit/provider/env/windows_spec.rb
+++ b/spec/unit/provider/env/windows_spec.rb
@@ -24,16 +24,16 @@ describe Chef::Provider::Env::Windows, :windows_only do
   let(:run_context) { Chef::RunContext.new(node, {}, events) }
 
   context "when environment variable is not PATH" do
-    let(:new_resource) {
+    let(:new_resource) do
       new_resource = Chef::Resource::Env.new("CHEF_WINDOWS_ENV_TEST")
       new_resource.value("foo")
       new_resource
-    }
-    let(:provider) {
+    end
+    let(:provider) do
       provider = Chef::Provider::Env::Windows.new(new_resource, run_context)
       allow(provider).to receive(:env_obj).and_return(double("null object").as_null_object)
       provider
-    }
+    end
 
     describe "action_create" do
       before do
@@ -76,16 +76,16 @@ describe Chef::Provider::Env::Windows, :windows_only do
     describe "for PATH" do
       let(:system_root) { "%SystemRoot%" }
       let(:system_root_value) { 'D:\Windows' }
-      let(:new_resource) {
+      let(:new_resource) do
         new_resource = Chef::Resource::Env.new("PATH")
         new_resource.value(system_root)
         new_resource
-      }
-      let(:provider) {
+      end
+      let(:provider) do
         provider = Chef::Provider::Env::Windows.new(new_resource, run_context)
         allow(provider).to receive(:env_obj).and_return(double("null object").as_null_object)
         provider
-      }
+      end
 
       before do
         stub_const("ENV", { "PATH" => "" })
diff --git a/spec/unit/provider/file/content_spec.rb b/spec/unit/provider/file/content_spec.rb
index a31c75b..f840d92 100644
--- a/spec/unit/provider/file/content_spec.rb
+++ b/spec/unit/provider/file/content_spec.rb
@@ -28,12 +28,12 @@ describe Chef::Provider::File::Content do
     double("Chef::Provider::File::Resource (current)")
   end
 
-  let(:enclosing_directory) {
+  let(:enclosing_directory) do
     canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
-  }
-  let(:resource_path) {
+  end
+  let(:resource_path) do
     canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
-  }
+  end
 
   let(:new_resource) do
     double("Chef::Provider::File::Resource (new)", :name => "seattle.txt", :path => resource_path)
@@ -77,9 +77,9 @@ describe Chef::Provider::File::Content do
     end
 
     context "when creating a tempfiles in destdir fails" do
-      let(:enclosing_directory) {
+      let(:enclosing_directory) do
         canonicalize_path("/nonexisting/path")
-      }
+      end
 
       it "returns a tempfile in the tempdir when :file_deployment_uses_destdir is set to :auto" do
         Chef::Config[:file_staging_uses_destdir] = :auto
diff --git a/spec/unit/provider/file_spec.rb b/spec/unit/provider/file_spec.rb
index 11bef8a..d8d4ed2 100644
--- a/spec/unit/provider/file_spec.rb
+++ b/spec/unit/provider/file_spec.rb
@@ -35,12 +35,12 @@ describe Chef::Provider::File do
   let(:node) { double("Chef::Node") }
   let(:events) { double("Chef::Events").as_null_object } # mock all the methods
   let(:run_context) { double("Chef::RunContext", :node => node, :events => events) }
-  let(:enclosing_directory) {
+  let(:enclosing_directory) do
     canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
-  }
-  let(:resource_path) {
+  end
+  let(:resource_path) do
     canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
-  }
+  end
 
   # Subject
 
diff --git a/spec/unit/provider/launchd_spec.rb b/spec/unit/provider/launchd_spec.rb
index 4286405..2893044 100644
--- a/spec/unit/provider/launchd_spec.rb
+++ b/spec/unit/provider/launchd_spec.rb
@@ -51,7 +51,8 @@ describe Chef::Provider::Launchd do
 </plist>
 XML
 
-    let(:test_hash) do {
+    let(:test_hash) do
+      {
       "Label" => "call.mom.weekly",
       "Program" => "/Library/scripts/call_mom.sh",
       "StartCalendarInterval" => {
diff --git a/spec/unit/provider/link_spec.rb b/spec/unit/provider/link_spec.rb
index 6bb0855..9426cf4 100644
--- a/spec/unit/provider/link_spec.rb
+++ b/spec/unit/provider/link_spec.rb
@@ -249,4 +249,159 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
       end
     end
   end
+
+  describe "action_delete" do
+    before(:each) do
+      stat = double("stats", :ino => 5)
+      allow(stat).to receive(:uid).and_return(501)
+      allow(stat).to receive(:gid).and_return(501)
+      allow(stat).to receive(:mode).and_return(0755)
+      allow(provider.file_class).to receive(:stat).with(
+        "#{CHEF_SPEC_DATA}/fofile-link").and_return(stat)
+
+      provider.load_current_resource
+    end
+
+    shared_context "delete link to directories on Windows" do
+      before do
+        allow(::File).to receive(:directory?).with(
+          "#{CHEF_SPEC_DATA}/fofile-link").and_return(true)
+      end
+
+      it "invokes Dir.delete method to delete the link" do
+        expect(::Dir).to receive(:delete).with(provider.new_resource.target_file)
+        expect(Chef::Log).to receive(:info).with("#{provider.new_resource} deleted")
+        provider.run_action(:delete)
+      end
+    end
+
+    shared_context "delete link to directories on Linux" do
+      before do
+        allow(::File).to receive(:directory?).with(
+          "#{CHEF_SPEC_DATA}/fofile-link").and_return(true)
+      end
+
+      it "invokes File.delete method to delete the link" do
+        expect(::File).to receive(:delete).with(provider.new_resource.target_file)
+        expect(Chef::Log).to receive(:info).with("#{provider.new_resource} deleted")
+        provider.run_action(:delete)
+      end
+    end
+
+    shared_context "delete link to files" do
+      before do
+        allow(::File).to receive(:directory?).with(
+          "#{CHEF_SPEC_DATA}/fofile-link").and_return(false)
+      end
+
+      it "invokes File.delete method to delete the link" do
+        expect(::File).to receive(:delete).with(provider.new_resource.target_file)
+        expect(Chef::Log).to receive(:info).with("#{provider.new_resource} deleted")
+        provider.run_action(:delete)
+      end
+    end
+
+    shared_context "soft links prerequisites" do
+      before(:each) do
+        allow(provider.file_class).to receive(:symlink?).with(
+          "#{CHEF_SPEC_DATA}/fofile-link").and_return(true)
+        allow(provider.file_class).to receive(:readlink).with(
+          "#{CHEF_SPEC_DATA}/fofile-link").and_return("#{CHEF_SPEC_DATA}/fofile")
+      end
+    end
+
+    shared_context "hard links prerequisites" do
+      let(:new_resource) do
+        result = Chef::Resource::Link.new("#{CHEF_SPEC_DATA}/fofile-link")
+        result.to "#{CHEF_SPEC_DATA}/fofile"
+        result.link_type :hard
+        result
+      end
+
+      before(:each) do
+        stat = double("stats", :ino => 5)
+        allow(stat).to receive(:uid).and_return(502)
+        allow(stat).to receive(:gid).and_return(502)
+        allow(stat).to receive(:mode).and_return(0644)
+
+        allow(provider.file_class).to receive(:symlink?).with(
+          "#{CHEF_SPEC_DATA}/fofile-link").and_return(false)
+
+        allow(File).to receive(:exists?).with(
+          "#{CHEF_SPEC_DATA}/fofile-link").and_return(true)
+        allow(File).to receive(:exists?).with(
+          "#{CHEF_SPEC_DATA}/fofile").and_return(true)
+
+        allow(provider.file_class).to receive(:stat).with(
+          "#{CHEF_SPEC_DATA}/fofile").and_return(stat)
+      end
+    end
+
+    context "on Windows platform" do
+      let(:resource_link) do
+        Chef::Resource::Link.new(provider.new_resource.name)
+      end
+
+      before(:each) do
+        allow(Chef::Resource::Link).to receive(:new).with(
+          provider.new_resource.name).and_return(resource_link)
+        allow(resource_link).to receive(:verify_links_supported!)
+        allow(Chef::Platform).to receive(:windows?).and_return(true)
+      end
+
+      context "soft links" do
+        include_context "soft links prerequisites"
+
+        context "to directories" do
+          include_context "delete link to directories on Windows"
+        end
+
+        context "to files" do
+          include_context "delete link to files"
+        end
+      end
+
+      context "hard links" do
+        include_context "hard links prerequisites"
+
+        context "to directories" do
+          include_context "delete link to directories on Windows"
+        end
+
+        context "to files" do
+          include_context "delete link to files"
+        end
+      end
+    end
+
+    context "on Linux platform" do
+      before(:each) do
+        allow(Chef::Platform).to receive(:windows?).and_return(false)
+      end
+
+      context "soft links" do
+        include_context "soft links prerequisites"
+
+        context "to directories" do
+          include_context "delete link to directories on Linux"
+        end
+
+        context "to files" do
+          include_context "delete link to files"
+        end
+      end
+
+      context "hard links" do
+        include_context "hard links prerequisites"
+
+        context "to directories" do
+          include_context "delete link to directories on Linux"
+        end
+
+        context "to files" do
+          include_context "delete link to files"
+        end
+      end
+    end
+  end
 end
diff --git a/spec/unit/provider/log_spec.rb b/spec/unit/provider/log_spec.rb
index 2e4f9c4..ce7b1af 100644
--- a/spec/unit/provider/log_spec.rb
+++ b/spec/unit/provider/log_spec.rb
@@ -72,4 +72,18 @@ describe Chef::Provider::Log::ChefLog do
     expect(Chef::Log).to receive(:info).with(log_str).and_return(true)
     provider.run_action(:write)
   end
+
+  context "when count_log_resource_updates is passed in knife.rb" do
+    it "updates the resource count if count_log_resource_updates=true" do
+      Chef::Config[:count_log_resource_updates] = true
+      expect(new_resource).to receive(:updated_by_last_action)
+      provider.run_action(:write)
+    end
+
+    it "doesn't update the resource count if count_log_resource_updates=false" do
+      Chef::Config[:count_log_resource_updates] = false
+      expect(new_resource).not_to receive(:updated_by_last_action)
+      provider.run_action(:write)
+    end
+  end
 end
diff --git a/spec/unit/provider/mount/mount_spec.rb b/spec/unit/provider/mount/mount_spec.rb
index 42585d9..0b956d9 100644
--- a/spec/unit/provider/mount/mount_spec.rb
+++ b/spec/unit/provider/mount/mount_spec.rb
@@ -82,7 +82,7 @@ describe Chef::Provider::Mount::Mount do
 
     it "should raise an error if the mount device does not exist" do
       allow(::File).to receive(:exists?).with("/dev/sdz1").and_return false
-      expect { @provider.load_current_resource();@provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
+      expect { @provider.load_current_resource(); @provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
     end
 
     it "should not call mountable? with load_current_resource - CHEF-1565" do
@@ -99,25 +99,25 @@ describe Chef::Provider::Mount::Mount do
       @new_resource.device "d21afe51-a0fe-4dc6-9152-ac733763ae0a"
       expect(@provider).to receive(:shell_out).with("/sbin/findfs UUID=d21afe51-a0fe-4dc6-9152-ac733763ae0a").and_return(status)
       expect(::File).to receive(:exists?).with("").and_return(false)
-      expect { @provider.load_current_resource();@provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
+      expect { @provider.load_current_resource(); @provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
     end
 
     it "should raise an error if the mount point does not exist" do
       allow(::File).to receive(:exists?).with("/tmp/foo").and_return false
-      expect { @provider.load_current_resource();@provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
+      expect { @provider.load_current_resource(); @provider.mountable? }.to raise_error(Chef::Exceptions::Mount)
     end
 
     %w{tmpfs fuse cgroup}.each do |fstype|
       it "does not expect the device to exist for #{fstype}" do
         @new_resource.fstype(fstype)
         @new_resource.device("whatever")
-        expect { @provider.load_current_resource();@provider.mountable? }.not_to raise_error
+        expect { @provider.load_current_resource(); @provider.mountable? }.not_to raise_error
       end
     end
 
     it "does not expect the device to exist if it's none" do
       @new_resource.device("none")
-      expect { @provider.load_current_resource();@provider.mountable? }.not_to raise_error
+      expect { @provider.load_current_resource(); @provider.mountable? }.not_to raise_error
     end
 
     it "should set mounted true if the mount point is found in the mounts list" do
diff --git a/spec/unit/provider/mount/solaris_spec.rb b/spec/unit/provider/mount/solaris_spec.rb
index 51ace83..264c8b9 100644
--- a/spec/unit/provider/mount/solaris_spec.rb
+++ b/spec/unit/provider/mount/solaris_spec.rb
@@ -41,7 +41,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
 
   let(:options) { nil }
 
-  let(:new_resource) {
+  let(:new_resource) do
     new_resource = Chef::Resource::Mount.new(mountpoint)
     new_resource.device      device
     new_resource.device_type device_type
@@ -50,13 +50,13 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     new_resource.options     options
     new_resource.supports :remount => false
     new_resource
-  }
+  end
 
-  let(:provider) {
+  let(:provider) do
     Chef::Provider::Mount::Solaris.new(new_resource, run_context)
-  }
+  end
 
-  let(:vfstab_file_contents) {
+  let(:vfstab_file_contents) do
     <<-EOF.gsub /^\s*/, ""
     #device         device          mount           FS      fsck    mount   mount
     #to mount       to fsck         point           type    pass    at boot options
@@ -74,21 +74,21 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     # ufs
     /dev/dsk/c0t2d0s7       /dev/rdsk/c0t2d0s7      /mnt/foo            ufs     2       yes     -
     EOF
-  }
+  end
 
-  let(:vfstab_file) {
+  let(:vfstab_file) do
     t = Tempfile.new("rspec-vfstab")
     t.write(vfstab_file_contents)
     t.close
     t
-  }
+  end
 
-  let(:mount_output) {
+  let(:mount_output) do
     <<-EOF.gsub /^\s*/, ""
     /dev/dsk/c0t0d0s0 on / type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Tue Jul 31 22:34:46 2012
     /dev/dsk/c0t2d0s7 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
     EOF
-  }
+  end
 
   before do
     stub_const("Chef::Provider::Mount::Solaris::VFSTAB", vfstab_file.path )
@@ -214,7 +214,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
   describe "#load_current_resource" do
     context "when loading a normal UFS filesystem with noauto, don't mount at boot" do
 
-      let(:vfstab_file_contents) {
+      let(:vfstab_file_contents) do
         <<-EOF.gsub /^\s*/, ""
         #device         device          mount           FS      fsck    mount   mount
         #to mount       to fsck         point           type    pass    at boot options
@@ -232,7 +232,7 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
         # ufs
         /dev/dsk/c0t2d0s7       /dev/rdsk/c0t2d0s7      /mnt/foo            ufs     2       no     -
         EOF
-      }
+      end
 
       before do
         provider.load_current_resource
@@ -244,16 +244,16 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     end
 
     context "when the device is an smbfs mount" do
-      let(:mount_output) {
+      let(:mount_output) do
         <<-EOF.gsub /^\s*/, ""
         //solarsystem/tmp on /mnt type smbfs read/write/setuid/devices/dev=5080000 on Tue Mar 29 11:40:18 2011
         EOF
-      }
-      let(:vfstab_file_contents) {
+      end
+      let(:vfstab_file_contents) do
         <<-EOF.gsub /^\s*/, ""
         //WORKGROUP;username:password at host/share    -   /mountpoint smbfs   -   no  fileperms=0777,dirperms=0777
         EOF
-      }
+      end
 
       let(:fsck_device) { "-" }
 
@@ -263,17 +263,17 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     end
 
     context "when the device is an NFS mount" do
-      let(:mount_output) {
+      let(:mount_output) do
         <<-EOF.gsub /^\s*/, ""
         cartman:/share2 on /cartman type nfs rsize=32768,wsize=32768,NFSv4,dev=4000004 on Tue Mar 29 11:40:18 2011
         EOF
-      }
+      end
 
-      let(:vfstab_file_contents) {
+      let(:vfstab_file_contents) do
         <<-EOF.gsub /^\s*/, ""
         cartman:/share2         -                       /cartman        nfs     -       yes     rw,soft
         EOF
-      }
+      end
 
       let(:fsck_device) { "-" }
 
@@ -334,17 +334,17 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
 
       let(:target) { "/dev/mapper/target" }
 
-      let(:mount_output) {
+      let(:mount_output) do
         <<-EOF.gsub /^\s*/, ""
         #{target} on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
         EOF
-      }
+      end
 
-      let(:vfstab_file_contents) {
+      let(:vfstab_file_contents) do
         <<-EOF.gsub /^\s*/, ""
         #{target}       /dev/rdsk/c0t2d0s7      /mnt/foo            ufs     2       yes     -
         EOF
-      }
+      end
 
       before do
         expect(File).to receive(:symlink?).with(device).at_least(:once).and_return(true)
@@ -371,17 +371,17 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
 
       let(:absolute_target) { File.expand_path(target, File.dirname(device)) }
 
-      let(:mount_output) {
+      let(:mount_output) do
         <<-EOF.gsub /^\s*/, ""
         #{absolute_target} on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
         EOF
-      }
+      end
 
-      let(:vfstab_file_contents) {
+      let(:vfstab_file_contents) do
         <<-EOF.gsub /^\s*/, ""
         #{absolute_target}       /dev/rdsk/c0t2d0s7      /mnt/foo            ufs     2       yes     -
         EOF
-      }
+      end
 
       before do
         expect(File).to receive(:symlink?).with(device).at_least(:once).and_return(true)
@@ -404,12 +404,12 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     end
 
     context "when the matching mount point is last in the mounts list" do
-      let(:mount_output) {
+      let(:mount_output) do
         <<-EOF.gsub /^\s*/, ""
         /dev/dsk/c0t0d0s0 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Tue Jul 31 22:34:46 2012
         /dev/dsk/c0t2d0s7 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
         EOF
-      }
+      end
       it "should set mounted true" do
         provider.load_current_resource()
         expect(provider.current_resource.mounted).to be_truthy
@@ -417,12 +417,12 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     end
 
     context "when the matching mount point is not last in the mounts list" do
-      let(:mount_output) {
+      let(:mount_output) do
         <<-EOF.gsub /^\s*/, ""
         /dev/dsk/c0t2d0s7 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
         /dev/dsk/c0t0d0s0 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Tue Jul 31 22:34:46 2012
         EOF
-      }
+      end
       it "should set mounted false" do
         provider.load_current_resource()
         expect(provider.current_resource.mounted).to be_falsey
@@ -430,11 +430,11 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     end
 
     context "when the matching mount point is not in the mounts list (mountpoint wrong)" do
-      let(:mount_output) {
+      let(:mount_output) do
         <<-EOF.gsub /^\s*/, ""
         /dev/dsk/c0t2d0s7 on /mnt/foob type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
         EOF
-      }
+      end
       it "should set mounted false" do
         provider.load_current_resource()
         expect(provider.current_resource.mounted).to be_falsey
@@ -442,11 +442,11 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     end
 
     context "when the matching mount point is not in the mounts list (raw device wrong)" do
-      let(:mount_output) {
+      let(:mount_output) do
         <<-EOF.gsub /^\s*/, ""
         /dev/dsk/c0t2d0s72 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
         EOF
-      }
+      end
       it "should set mounted false" do
         provider.load_current_resource()
         expect(provider.current_resource.mounted).to be_falsey
@@ -454,12 +454,12 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     end
 
     context "when the mount point is last in fstab" do
-      let(:vfstab_file_contents) {
+      let(:vfstab_file_contents) do
         <<-EOF.gsub /^\s*/, ""
         /dev/dsk/c0t2d0s72       /dev/rdsk/c0t2d0s7      /mnt/foo            ufs     2       yes     -
         /dev/dsk/c0t2d0s7       /dev/rdsk/c0t2d0s7      /mnt/foo            ufs     2       yes     -
         EOF
-      }
+      end
 
       it "should set enabled to true" do
         provider.load_current_resource
@@ -468,12 +468,12 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     end
 
     context "when the mount point is not last in fstab and is a substring of another mount" do
-      let(:vfstab_file_contents) {
+      let(:vfstab_file_contents) do
         <<-EOF.gsub /^\s*/, ""
         /dev/dsk/c0t2d0s7       /dev/rdsk/c0t2d0s7      /mnt/foo            ufs     2       yes     -
         /dev/dsk/c0t2d0s72       /dev/rdsk/c0t2d0s7      /mnt/foo/bar            ufs     2       yes     -
         EOF
-      }
+      end
 
       it "should set enabled to true" do
         provider.load_current_resource
@@ -482,12 +482,12 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     end
 
     context "when the mount point is not last in fstab" do
-      let(:vfstab_file_contents) {
+      let(:vfstab_file_contents) do
         <<-EOF.gsub /^\s*/, ""
         /dev/dsk/c0t2d0s7       /dev/rdsk/c0t2d0s7      /mnt/foo            ufs     2       yes     -
         /dev/dsk/c0t2d0s72       /dev/rdsk/c0t2d0s72      /mnt/foo            ufs     2       yes     -
         EOF
-      }
+      end
 
       it "should set enabled to false" do
         provider.load_current_resource
@@ -496,11 +496,11 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     end
 
     context "when the mount point is not in fstab, but the mountpoint is a substring of one that is" do
-      let(:vfstab_file_contents) {
+      let(:vfstab_file_contents) do
         <<-EOF.gsub /^\s*/, ""
         /dev/dsk/c0t2d0s7       /dev/rdsk/c0t2d0s7      /mnt/foob            ufs     2       yes     -
         EOF
-      }
+      end
 
       it "should set enabled to false" do
         provider.load_current_resource
@@ -509,11 +509,11 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     end
 
     context "when the mount point is not in fstab, but the device is a substring of one that is" do
-      let(:vfstab_file_contents) {
+      let(:vfstab_file_contents) do
         <<-EOF.gsub /^\s*/, ""
         /dev/dsk/c0t2d0s72       /dev/rdsk/c0t2d0s7      /mnt/foo            ufs     2       yes     -
         EOF
-      }
+      end
 
       it "should set enabled to false" do
         provider.load_current_resource
@@ -522,11 +522,11 @@ describe Chef::Provider::Mount::Solaris, :unix_only do
     end
 
     context "when the mountpoint line is commented out" do
-      let(:vfstab_file_contents) {
+      let(:vfstab_file_contents) do
         <<-EOF.gsub /^\s*/, ""
         #/dev/dsk/c0t2d0s7       /dev/rdsk/c0t2d0s7      /mnt/foo            ufs     2       yes     -
         EOF
-      }
+      end
 
       it "should set enabled to false" do
         provider.load_current_resource
diff --git a/spec/unit/provider/osx_profile_spec.rb b/spec/unit/provider/osx_profile_spec.rb
index e307bc2..a1dcf3e 100644
--- a/spec/unit/provider/osx_profile_spec.rb
+++ b/spec/unit/provider/osx_profile_spec.rb
@@ -238,6 +238,12 @@ describe Chef::Provider::OsxProfile do
         ).to eql(new_resource.identifier)
     end
 
+    it "should work with spaces in the identifier" do
+      provider.action = :remove
+      provider.define_resource_requirements
+      expect { provider.process_resource_requirements }.not_to raise_error
+    end
+
     it "should build the shellout remove command correctly" do
       new_resource.identifier "com.testprofile.screensaver"
       new_resource.action(:remove)
diff --git a/spec/unit/provider/package/aix_spec.rb b/spec/unit/provider/package/aix_spec.rb
index 6940874..2b574bb 100644
--- a/spec/unit/provider/package/aix_spec.rb
+++ b/spec/unit/provider/package/aix_spec.rb
@@ -73,6 +73,19 @@ describe Chef::Provider::Package::Aix do
       expect(@new_resource.version).to eq("3.3.12.0")
     end
 
+    it "should warn if the package is not a fileset" do
+      info = "samba.base:samba.base.samples:3.3.12.0::COMMITTED:I:Samba for AIX:
+  /etc/objrepos:samba.base:3.3.12.0::COMMITTED:I:Samba for AIX:"
+      status = double("Status", :stdout => info, :exitstatus => 0)
+      expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
+      expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base", timeout: 900).and_return(@empty_status)
+      expect(Chef::Log).to receive(:warn).once.with(%r{bff package by product name})
+      @provider.load_current_resource
+
+      expect(@provider.current_resource.package_name).to eq("samba.base")
+      expect(@new_resource.version).to eq("3.3.12.0")
+    end
+
     it "should return the current version installed if found by lslpp" do
       status = double("Status", :stdout => @bffinfo, :exitstatus => 0)
       @stdout = StringIO.new(@bffinfo)
diff --git a/spec/unit/provider/package/chocolatey_spec.rb b/spec/unit/provider/package/chocolatey_spec.rb
index 8344c3d..704ef1a 100644
--- a/spec/unit/provider/package/chocolatey_spec.rb
+++ b/spec/unit/provider/package/chocolatey_spec.rb
@@ -462,18 +462,18 @@ describe "behavior when Chocolatey is not installed" do
     Chef::Provider::Package::Chocolatey.new(new_resource, run_context)
   end
 
-  before {
+  before do
     # the shellout sometimes returns "", but test nil to be safe.
     allow(provider).to receive(:choco_install_path).and_return(nil)
     provider.instance_variable_set("@choco_install_path", nil)
 
     # we don't care what this returns, but we have to let it be called.
     allow(provider).to receive(:shell_out!).and_return(double(:stdout => ""))
-  }
+  end
 
-  let(:error_regex) {
+  let(:error_regex) do
     /Could not locate.*install.*cookbook.*PowerShell.*GetEnvironmentVariable/m
-  }
+  end
 
   context "#choco_exe" do
     it "triggers a MissingLibrary exception when Chocolatey is not installed" do
diff --git a/spec/unit/provider/package/freebsd/port_spec.rb b/spec/unit/provider/package/freebsd/port_spec.rb
index b593da4..4ae8d96 100644
--- a/spec/unit/provider/package/freebsd/port_spec.rb
+++ b/spec/unit/provider/package/freebsd/port_spec.rb
@@ -74,8 +74,8 @@ describe Chef::Provider::Package::Freebsd::Port do
 
     it "should check 'pkg info' if make supports WITH_PKGNG if freebsd version is < 1000017" do
       pkg_enabled = OpenStruct.new(:stdout => "yes\n")
-      [1000016, 1000000, 901503, 902506, 802511].each do |__freebsd_version|
-        @node.automatic_attrs[:os_version] = __freebsd_version
+      [1000016, 1000000, 901503, 902506, 802511].each do |freebsd_version|
+        @node.automatic_attrs[:os_version] = freebsd_version
         expect(@new_resource).to receive(:shell_out!).with("make -V WITH_PKGNG", env: nil).and_return(pkg_enabled)
         expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', env: nil, returns: [0, 70], timeout: 900).and_return(@pkg_info)
         expect(@provider.current_installed_version).to eq("3.1.7")
@@ -83,8 +83,8 @@ describe Chef::Provider::Package::Freebsd::Port do
     end
 
     it "should check 'pkg info' if the freebsd version is greater than or equal to 1000017" do
-      __freebsd_version = 1000017
-      @node.automatic_attrs[:os_version] = __freebsd_version
+      freebsd_version = 1000017
+      @node.automatic_attrs[:os_version] = freebsd_version
       expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', env: nil, returns: [0, 70], timeout: 900).and_return(@pkg_info)
       expect(@provider.current_installed_version).to eq("3.1.7")
     end
diff --git a/spec/unit/provider/package/rubygems_spec.rb b/spec/unit/provider/package/rubygems_spec.rb
index f87c261..4934505 100644
--- a/spec/unit/provider/package/rubygems_spec.rb
+++ b/spec/unit/provider/package/rubygems_spec.rb
@@ -16,7 +16,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-require "pp"
 
 module GemspecBackcompatCreator
   def gemspec(name, version)
@@ -64,7 +63,7 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
     begin
       @gem_env.with_gem_sources("http://gems.example.org") do
         sources_in_block = Gem.sources
-        raise RuntimeError, "sources should be reset even in case of an error"
+        raise "sources should be reset even in case of an error"
       end
     rescue RuntimeError
     end
@@ -78,7 +77,7 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
     begin
       @gem_env.with_gem_sources(nil) do
         sources_in_block = Gem.sources
-        raise RuntimeError, "sources should be reset even in case of an error"
+        raise "sources should be reset even in case of an error"
       end
     rescue RuntimeError
     end
@@ -86,34 +85,55 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
     expect(Gem.sources).to eq(normal_sources)
   end
 
-  it "finds a matching gem candidate version" do
-    dep = Gem::Dependency.new("rspec", ">= 0")
-    dep_installer = Gem::DependencyInstaller.new
-    allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
-    latest = [[gemspec("rspec", Gem::Version.new("1.3.0")), "https://rubygems.org/"]]
-    expect(dep_installer).to receive(:find_gems_with_sources).with(dep).and_return(latest)
-    expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new("rspec", ">= 0"))).to eq(Gem::Version.new("1.3.0"))
-  end
+  context "new default rubygems behavior" do
+    before do
+      Chef::Config[:rubygems_cache_enabled] = false
+    end
 
-  it "finds a matching gem candidate version on rubygems 2.0.0+" do
-    dep = Gem::Dependency.new("rspec", ">= 0")
-    dep_installer = Gem::DependencyInstaller.new
-    allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
-    best_gem = double("best gem match", :spec => gemspec("rspec", Gem::Version.new("1.3.0")), :source => "https://rubygems.org")
-    available_set = double("Gem::AvailableSet test double")
-    expect(available_set).to receive(:pick_best!)
-    expect(available_set).to receive(:set).and_return([best_gem])
-    expect(dep_installer).to receive(:find_gems_with_sources).with(dep).and_return(available_set)
-    expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new("rspec", ">= 0"))).to eq(Gem::Version.new("1.3.0"))
+    it "finds a matching gem candidate version on rubygems 2.0.0+" do
+      dep = Gem::Dependency.new("rspec", ">= 0")
+      dep_installer = Gem::DependencyInstaller.new
+      allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
+      expect(dep_installer).not_to receive(:find_gems_with_sources).with(dep).and_call_original
+      expect(@gem_env.candidate_version_from_remote(dep)).to be_kind_of(Gem::Version)
+    end
+
+    it "gives the candidate version as nil if none is found" do
+      dep = Gem::Dependency.new("lksdjflksdjflsdkfj", ">= 0")
+      dep_installer = Gem::DependencyInstaller.new
+      allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
+      expect(dep_installer).not_to receive(:find_gems_with_sources).with(dep).and_call_original
+      expect(@gem_env.candidate_version_from_remote(dep)).to be_nil
+    end
+
+    it "finds a matching gem from a specific gemserver when explicit sources are given (to a server that doesn't respond to api requests)" do
+      dep = Gem::Dependency.new("rspec", ">= 0")
+      dep_installer = Gem::DependencyInstaller.new
+      allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
+      expect(dep_installer).not_to receive(:find_gems_with_sources).with(dep).and_call_original
+      expect(@gem_env.candidate_version_from_remote(dep, "http://production.cf.rubygems.org")).to be_kind_of(Gem::Version)
+    end
   end
 
-  it "gives the candidate version as nil if none is found" do
-    dep = Gem::Dependency.new("rspec", ">= 0")
-    latest = []
-    dep_installer = Gem::DependencyInstaller.new
-    allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
-    expect(dep_installer).to receive(:find_gems_with_sources).with(dep).and_return(latest)
-    expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new("rspec", ">= 0"))).to be_nil
+  context "old rubygems caching behavior" do
+    before do
+      Chef::Config[:rubygems_cache_enabled] = true
+    end
+
+    it "finds a matching gem candidate version on rubygems 2.0.0+" do
+      dep = Gem::Dependency.new("rspec", ">= 0")
+      expect(@gem_env.candidate_version_from_remote(dep)).to be_kind_of(Gem::Version)
+    end
+
+    it "gives the candidate version as nil if none is found" do
+      dep = Gem::Dependency.new("lksdjflksdjflsdkfj", ">= 0")
+      expect(@gem_env.candidate_version_from_remote(dep)).to be_nil
+    end
+
+    it "finds a matching gem from a specific gemserver when explicit sources are given" do
+      dep = Gem::Dependency.new("rspec", ">= 0")
+      expect(@gem_env.candidate_version_from_remote(dep, "http://production.cf.rubygems.org")).to be_kind_of(Gem::Version)
+    end
   end
 
   it "finds a matching candidate version from a .gem file when the path to the gem is supplied" do
@@ -122,17 +142,6 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
     expect(@gem_env.candidate_version_from_file(Gem::Dependency.new("chef-integration-test", ">= 0.2.0"), location)).to be_nil
   end
 
-  it "finds a matching gem from a specific gemserver when explicit sources are given" do
-    dep = Gem::Dependency.new("rspec", ">= 0")
-    latest = [[gemspec("rspec", Gem::Version.new("1.3.0")), "https://rubygems.org/"]]
-
-    expect(@gem_env).to receive(:with_gem_sources).with("http://gems.example.com").and_yield
-    dep_installer = Gem::DependencyInstaller.new
-    allow(@gem_env).to receive(:dependency_installer).and_return(dep_installer)
-    expect(dep_installer).to receive(:find_gems_with_sources).with(dep).and_return(latest)
-    expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new("rspec", ">=0"), "http://gems.example.com")).to eq(Gem::Version.new("1.3.0"))
-  end
-
   it "installs a gem with a hash of options for the dependency installer" do
     dep_installer = Gem::DependencyInstaller.new
     expect(@gem_env).to receive(:dependency_installer).with(:install_dir => "/foo/bar").and_return(dep_installer)
@@ -545,7 +554,6 @@ describe Chef::Provider::Package::Rubygems do
           expect(provider.candidate_version).to eq("0.1.0")
         end
       end
-
     end
 
     describe "when installing a gem" do
@@ -586,10 +594,9 @@ describe Chef::Provider::Package::Rubygems do
 
         context "when source is a path" do
           let(:source) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
-          let(:domain) { { domain: :local } }
 
           it "installs the gem from file via the gems api" do
-            expect(provider.gem_env).to receive(:install).with(source, domain)
+            expect(provider.gem_env).to receive(:install).with(source)
             provider.run_action(:install)
             expect(new_resource).to be_updated_by_last_action
           end
@@ -597,11 +604,10 @@ describe Chef::Provider::Package::Rubygems do
 
         context "when the gem name is a file path and source is nil" do
           let(:gem_name) { CHEF_SPEC_DATA + "/gems/chef-integration-test-0.1.0.gem" }
-          let(:domain) { { domain: :local } }
 
           it "installs the gem from file via the gems api" do
             expect(new_resource.source).to eq(gem_name)
-            expect(provider.gem_env).to receive(:install).with(gem_name, domain)
+            expect(provider.gem_env).to receive(:install).with(gem_name)
             provider.run_action(:install)
             expect(new_resource).to be_updated_by_last_action
           end
diff --git a/spec/unit/provider/package/windows/exe_spec.rb b/spec/unit/provider/package/windows/exe_spec.rb
index 23a5460..f18cbf3 100644
--- a/spec/unit/provider/package/windows/exe_spec.rb
+++ b/spec/unit/provider/package/windows/exe_spec.rb
@@ -110,23 +110,37 @@ describe Chef::Provider::Package::Windows::Exe do
   end
 
   describe "remove_package" do
-    context "no version given and one package installed" do
-      it "removes installed package" do
-        expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir\" uninst_file \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+    before do
+      allow(::File).to receive(:exist?).and_return(false)
+    end
+
+    context "no version given and one package installed with unquoted uninstall string" do
+      it "removes installed package and quotes uninstall string" do
+        allow(::File).to receive(:exist?).with("uninst_dir/uninst_file").and_return(true)
+        expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir\/uninst_file\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+        provider.remove_package
+      end
+    end
+
+    context "When timeout value is passed" do
+      it "removes installed package and quotes uninstall string" do
+        new_resource.timeout = 300
+        allow(::File).to receive(:exist?).with("uninst_dir/uninst_file").and_return(true)
+        expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir\/uninst_file\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, :timeout => 300, :returns => [0])
         provider.remove_package
       end
     end
 
-    context "several packages installed" do
+    context "several packages installed with quoted uninstall strings" do
       let(:uninstall_hash) do
         [
           {
           "DisplayVersion" => "v1",
-          "UninstallString" => File.join("uninst_dir1", "uninst_file1"),
+          "UninstallString" => "\"#{File.join("uninst_dir1", "uninst_file1")}\"",
           },
           {
           "DisplayVersion" => "v2",
-          "UninstallString" => File.join("uninst_dir2", "uninst_file2"),
+          "UninstallString" => "\"#{File.join("uninst_dir2", "uninst_file2")}\"",
           },
         ]
       end
@@ -134,15 +148,15 @@ describe Chef::Provider::Package::Windows::Exe do
       context "version given and installed" do
         it "removes given version" do
           new_resource.version("v2")
-          expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir2\" uninst_file2 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+          expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir2\/uninst_file2\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
           provider.remove_package
         end
       end
 
       context "no version given" do
         it "removes both versions" do
-          expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir1\" uninst_file1 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
-          expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \/d\"uninst_dir2\" uninst_file2 \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+          expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir1\/uninst_file1\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
+          expect(provider).to receive(:shell_out!).with(/start \"\" \/wait \"uninst_dir2\/uninst_file2\" \/S \/NCRC & exit %%%%ERRORLEVEL%%%%/, kind_of(Hash))
           provider.remove_package
         end
       end
diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb
index ab72613..e9aec93 100644
--- a/spec/unit/provider/package/yum_spec.rb
+++ b/spec/unit/provider/package/yum_spec.rb
@@ -312,7 +312,8 @@ describe Chef::Provider::Package::Yum do
     end
 
     context "when the package name isn't found" do
-      let(:yum_cache) { double(
+      let(:yum_cache) do
+        double(
           "Chef::Provider::Yum::YumCache",
           :reload_installed => true,
           :reset => true,
@@ -322,7 +323,7 @@ describe Chef::Provider::Package::Yum do
           :version_available? => true,
           :disable_extra_repo_control => true
       )
-      }
+      end
 
       before do
         allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(yum_cache)
@@ -1063,21 +1064,21 @@ describe Chef::Provider::Package::Yum::RPMVersion do
   end
 
   it "should raise an error unless passed 1 or 3 args" do
-    expect {
+    expect do
       Chef::Provider::Package::Yum::RPMVersion.new()
-    }.to raise_error(ArgumentError)
-    expect {
+    end.to raise_error(ArgumentError)
+    expect do
       Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5")
-    }.not_to raise_error
-    expect {
+    end.not_to raise_error
+    expect do
       Chef::Provider::Package::Yum::RPMVersion.new("1:1.6.5-9.36.el5", "extra")
-    }.to raise_error(ArgumentError)
-    expect {
+    end.to raise_error(ArgumentError)
+    expect do
       Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5")
-    }.not_to raise_error
-    expect {
+    end.not_to raise_error
+    expect do
       Chef::Provider::Package::Yum::RPMVersion.new("1", "1.6.5", "9.36.el5", "extra")
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   # thanks version_class_spec.rb!
@@ -1229,9 +1230,9 @@ describe Chef::Provider::Package::Yum::RPMPackage do
 
     it "should always have at least one provide, itself" do
       expect(@rpm.provides.size).to eq(1)
-      @rpm.provides[0].name == "testing"
-      @rpm.provides[0].version.evr == "1:1.6.5-9.36.el5"
-      @rpm.provides[0].flag == :==
+      expect(@rpm.provides[0].name).to eql("testing")
+      expect(@rpm.provides[0].version.evr).to eql("1:1.6.5-9.36.el5")
+      expect(@rpm.provides[0].flag).to eql(:==)
     end
   end
 
@@ -1253,37 +1254,37 @@ describe Chef::Provider::Package::Yum::RPMPackage do
 
     it "should always have at least one provide, itself" do
       expect(@rpm.provides.size).to eq(1)
-      @rpm.provides[0].name == "testing"
-      @rpm.provides[0].version.evr == "1:1.6.5-9.36.el5"
-      @rpm.provides[0].flag == :==
+      expect(@rpm.provides[0].name).to eql("testing")
+      expect(@rpm.provides[0].version.evr).to eql("1:1.6.5-9.36.el5")
+      expect(@rpm.provides[0].flag).to eql(:==)
     end
   end
 
   it "should raise an error unless passed 4 or 6 args" do
-    expect {
+    expect do
       Chef::Provider::Package::Yum::RPMPackage.new()
-    }.to raise_error(ArgumentError)
-    expect {
+    end.to raise_error(ArgumentError)
+    expect do
       Chef::Provider::Package::Yum::RPMPackage.new("testing")
-    }.to raise_error(ArgumentError)
-    expect {
+    end.to raise_error(ArgumentError)
+    expect do
       Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5")
-    }.to raise_error(ArgumentError)
-    expect {
+    end.to raise_error(ArgumentError)
+    expect do
       Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64")
-    }.to raise_error(ArgumentError)
-    expect {
+    end.to raise_error(ArgumentError)
+    expect do
       Chef::Provider::Package::Yum::RPMPackage.new("testing", "1:1.6.5-9.36.el5", "x86_64", [])
-    }.not_to raise_error
-    expect {
+    end.not_to raise_error
+    expect do
       Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64")
-    }.to raise_error(ArgumentError)
-    expect {
+    end.to raise_error(ArgumentError)
+    expect do
       Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", [])
-    }.not_to raise_error
-    expect {
+    end.not_to raise_error
+    expect do
       Chef::Provider::Package::Yum::RPMPackage.new("testing", "1", "1.6.5", "9.36.el5", "x86_64", [], "extra")
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   describe "<=>" do
@@ -1397,27 +1398,27 @@ describe Chef::Provider::Package::Yum::RPMDependency do
   end
 
   it "should raise an error unless passed 3 or 5 args" do
-    expect {
+    expect do
       Chef::Provider::Package::Yum::RPMDependency.new()
-    }.to raise_error(ArgumentError)
-    expect {
+    end.to raise_error(ArgumentError)
+    expect do
       Chef::Provider::Package::Yum::RPMDependency.new("testing")
-    }.to raise_error(ArgumentError)
-    expect {
+    end.to raise_error(ArgumentError)
+    expect do
       Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5")
-    }.to raise_error(ArgumentError)
-    expect {
+    end.to raise_error(ArgumentError)
+    expect do
       Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==)
-    }.not_to raise_error
-    expect {
+    end.not_to raise_error
+    expect do
       Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==, "extra")
-    }.to raise_error(ArgumentError)
-    expect {
+    end.to raise_error(ArgumentError)
+    expect do
       Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==)
-    }.not_to raise_error
-    expect {
+    end.not_to raise_error
+    expect do
       Chef::Provider::Package::Yum::RPMDependency.new("testing", "1", "1.6.5", "9.36.el5", :==, "extra")
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   describe "parse" do
@@ -1485,12 +1486,12 @@ describe Chef::Provider::Package::Yum::RPMDependency do
     it "should raise an error unless a RPMDependency is passed" do
       @rpmprovide = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==)
       @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :>=)
-      expect {
+      expect do
         @rpmprovide.satisfy?("hi")
-      }.to raise_error(ArgumentError)
-      expect {
+      end.to raise_error(ArgumentError)
+      expect do
         @rpmprovide.satisfy?(@rpmrequire)
-      }.not_to raise_error
+      end.not_to raise_error
     end
 
     it "should validate dependency satisfaction logic for standard examples" do
@@ -1712,12 +1713,12 @@ describe Chef::Provider::Package::Yum::RPMDb do
     it "should raise an error unless a RPMDependency is passed" do
       @rpmprovide = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :==)
       @rpmrequire = Chef::Provider::Package::Yum::RPMDependency.new("testing", "1:1.6.5-9.36.el5", :>=)
-      expect {
+      expect do
         @rpmdb.whatprovides("hi")
-      }.to raise_error(ArgumentError)
-      expect {
+      end.to raise_error(ArgumentError)
+      expect do
         @rpmdb.whatprovides(@rpmrequire)
-      }.not_to raise_error
+      end.not_to raise_error
     end
 
     it "should return an Array of packages statisfying a RPMDependency" do
@@ -1748,13 +1749,13 @@ describe Chef::Provider::Package::Yum::YumCache do
     end
   end
 
-  let(:yum_exe) {
+  let(:yum_exe) do
     StringIO.new("#!/usr/bin/python\n\naldsjfa\ldsajflkdsjf\lajsdfj")
-  }
+  end
 
-  let(:bin_exe) {
+  let(:bin_exe) do
     StringIO.new(SecureRandom.random_bytes)
-  }
+  end
 
   before(:each) do
     @stdin = double("STDIN", :nil_object => true)
diff --git a/spec/unit/provider/package/zypper_spec.rb b/spec/unit/provider/package/zypper_spec.rb
index df0a1da..8838c26 100644
--- a/spec/unit/provider/package/zypper_spec.rb
+++ b/spec/unit/provider/package/zypper_spec.rb
@@ -72,7 +72,7 @@ describe Chef::Provider::Package::Zypper do
       provider.load_current_resource
     end
 
-    it "should set the installed version if zypper info has one" do
+    it "should set the installed version if zypper info has one (zypper version < 1.13.0)" do
       status = double(:stdout => "Version: 1.0\nInstalled: Yes\n", :exitstatus => 0)
 
       allow(provider).to receive(:shell_out!).and_return(status)
@@ -80,7 +80,15 @@ describe Chef::Provider::Package::Zypper do
       provider.load_current_resource
     end
 
-    it "should set the candidate version if zypper info has one" do
+    it "should set the installed version if zypper info has one (zypper version >= 1.13.0)" do
+      status = double(:stdout => "Version        : 1.0                             \nInstalled      : Yes                             \n", :exitstatus => 0)
+
+      allow(provider).to receive(:shell_out!).and_return(status)
+      expect(current_resource).to receive(:version).with(["1.0"]).and_return(true)
+      provider.load_current_resource
+    end
+
+    it "should set the candidate version if zypper info has one (zypper version < 1.13.0)" do
       status = double(:stdout => "Version: 1.0\nInstalled: No\nStatus: out-of-date (version 0.9 installed)", :exitstatus => 0)
 
       allow(provider).to receive(:shell_out!).and_return(status)
@@ -88,6 +96,14 @@ describe Chef::Provider::Package::Zypper do
       expect(provider.candidate_version).to eql(["1.0"])
     end
 
+    it "should set the candidate version if zypper info has one (zypper version >= 1.13.0)" do
+      status = double(:stdout => "Version        : 1.0                             \nInstalled      : No                              \nStatus         : out-of-date (version 0.9 installed)", :exitstatus => 0)
+
+      allow(provider).to receive(:shell_out!).and_return(status)
+      provider.load_current_resource
+      expect(provider.candidate_version).to eql(["1.0"])
+    end
+
     it "should return the current resouce" do
       expect(provider.load_current_resource).to eql(current_resource)
     end
diff --git a/spec/unit/provider/package_spec.rb b/spec/unit/provider/package_spec.rb
index 2ef58db..40b7516 100644
--- a/spec/unit/provider/package_spec.rb
+++ b/spec/unit/provider/package_spec.rb
@@ -396,13 +396,13 @@ describe Chef::Provider::Package do
 
     describe "when installing the preseed file to the cache location" do
       let(:response_file_destination) { Dir.tmpdir + "/preseed--java--java-6.seed" }
-      let(:response_file_resource) {
+      let(:response_file_resource) do
         response_file_resource = Chef::Resource::CookbookFile.new(response_file_destination, run_context)
         response_file_resource.cookbook_name = "java"
         response_file_resource.backup(false)
         response_file_resource.source("java.response")
         response_file_resource
-      }
+      end
 
       before do
         expect(provider).to receive(:preseed_resource).with("java", "6").and_return(response_file_resource)
diff --git a/spec/unit/provider/powershell_script_spec.rb b/spec/unit/provider/powershell_script_spec.rb
index 5b5c783..4fd3f35 100644
--- a/spec/unit/provider/powershell_script_spec.rb
+++ b/spec/unit/provider/powershell_script_spec.rb
@@ -20,7 +20,7 @@ require "spec_helper"
 describe Chef::Provider::PowershellScript, "action_run" do
 
   let(:powershell_version) { nil }
-  let(:node) {
+  let(:node) do
     node = Chef::Node.new
     node.default["kernel"] = Hash.new
     node.default["kernel"][:machine] = :x86_64.to_s
@@ -28,14 +28,14 @@ describe Chef::Provider::PowershellScript, "action_run" do
       node.default[:languages] = { :powershell => { :version => powershell_version } }
     end
     node
-  }
+  end
 
-  let(:provider) {
+  let(:provider) do
     empty_events = Chef::EventDispatch::Dispatcher.new
     run_context = Chef::RunContext.new(node, {}, empty_events)
     new_resource = Chef::Resource::PowershellScript.new("run some powershell code", run_context)
     Chef::Provider::PowershellScript.new(new_resource, run_context)
-  }
+  end
 
   context "when setting interpreter flags" do
     context "on nano" do
@@ -43,8 +43,8 @@ describe Chef::Provider::PowershellScript, "action_run" do
         allow(Chef::Platform).to receive(:windows_nano_server?).and_return(true)
         allow(provider).to receive(:is_forced_32bit).and_return(false)
         os_info_double = double("os_info")
-        allow(provider.run_context.node.kernel).to receive(:os_info).and_return(os_info_double)
-        allow(os_info_double).to receive(:system_directory).and_return("C:\\Windows\\system32")
+        allow(provider.run_context.node["kernel"]).to receive(:[]).with("os_info").and_return(os_info_double)
+        allow(os_info_double).to receive(:[]).with("system_directory").and_return("C:\\Windows\\system32")
       end
 
       it "sets the -Command flag as the last flag" do
@@ -58,8 +58,8 @@ describe Chef::Provider::PowershellScript, "action_run" do
         allow(Chef::Platform).to receive(:windows_nano_server?).and_return(false)
         allow(provider).to receive(:is_forced_32bit).and_return(false)
         os_info_double = double("os_info")
-        allow(provider.run_context.node.kernel).to receive(:os_info).and_return(os_info_double)
-        allow(os_info_double).to receive(:system_directory).and_return("C:\\Windows\\system32")
+        allow(provider.run_context.node["kernel"]).to receive(:[]).with("os_info").and_return(os_info_double)
+        allow(os_info_double).to receive(:[]).with("system_directory").and_return("C:\\Windows\\system32")
       end
 
       it "sets the -File flag as the last flag" do
diff --git a/spec/unit/provider/registry_key_spec.rb b/spec/unit/provider/registry_key_spec.rb
index 41bc5b3..9e19d02 100644
--- a/spec/unit/provider/registry_key_spec.rb
+++ b/spec/unit/provider/registry_key_spec.rb
@@ -19,23 +19,6 @@
 require "spec_helper"
 
 shared_examples_for "a registry key" do
-  before(:each) do
-    @node = Chef::Node.new
-    @events = Chef::EventDispatch::Dispatcher.new
-    @run_context = Chef::RunContext.new(@node, {}, @events)
-
-    @new_resource = Chef::Resource::RegistryKey.new("windows is fun", @run_context)
-    @new_resource.key keyname
-    @new_resource.values( testval1 )
-    @new_resource.recursive false
-
-    @provider = Chef::Provider::RegistryKey.new(@new_resource, @run_context)
-
-    allow(@provider).to receive(:running_on_windows!).and_return(true)
-    @double_registry = double(Chef::Win32::Registry)
-    allow(@provider).to receive(:registry).and_return(@double_registry)
-  end
-
   describe "when first created" do
   end
 
@@ -273,6 +256,23 @@ shared_examples_for "a registry key" do
 end
 
 describe Chef::Provider::RegistryKey do
+  before(:each) do
+    @node = Chef::Node.new
+    @events = Chef::EventDispatch::Dispatcher.new
+    @run_context = Chef::RunContext.new(@node, {}, @events)
+
+    @new_resource = Chef::Resource::RegistryKey.new("windows is fun", @run_context)
+    @new_resource.key keyname
+    @new_resource.values( testval1 )
+    @new_resource.recursive false
+
+    @provider = Chef::Provider::RegistryKey.new(@new_resource, @run_context)
+
+    allow(@provider).to receive(:running_on_windows!).and_return(true)
+    @double_registry = double(Chef::Win32::Registry)
+    allow(@provider).to receive(:registry).and_return(@double_registry)
+  end
+
   context "when the key data is safe" do
     let(:keyname) { 'HKLM\Software\Opscode\Testing\Safe' }
     let(:testval1) { { :name => "one", :type => :string, :data => "1" } }
@@ -292,4 +292,22 @@ describe Chef::Provider::RegistryKey do
 
     it_should_behave_like "a registry key"
   end
+
+  describe "action_create" do
+    context "when key exists and type matches" do
+      let(:keyname) { 'hklm\\software\\opscode\\testing\\dword' }
+      let(:dword_passed_as_integer) { { :name => "one", :type => :dword, :data => 12345 } }
+      let(:testval1) { { :name => "one", :type => :dword, :data => "12345" } }
+      before do
+        expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(true)
+      end
+
+      it "does not make a change for datatype of data value differing" do
+        expect(@double_registry).to receive(:get_values).with(keyname).and_return( dword_passed_as_integer )
+        expect(@double_registry).not_to receive(:set_value)
+        @provider.load_current_resource
+        @provider.action_create
+      end
+    end
+  end
 end
diff --git a/spec/unit/provider/remote_directory_spec.rb b/spec/unit/provider/remote_directory_spec.rb
index 710d661..cb1b6e3 100644
--- a/spec/unit/provider/remote_directory_spec.rb
+++ b/spec/unit/provider/remote_directory_spec.rb
@@ -99,6 +99,21 @@ describe Chef::Provider::RemoteDirectory do
       expect(cookbook_file.owner).to          eq("toor")
       expect(cookbook_file.backup).to         eq(23)
     end
+
+    it "respects sensitive flag" do
+      @resource.cookbook "gondola_rides"
+      @resource.sensitive true
+      cookbook_file = @provider.send(:cookbook_file_resource,
+                                    "/target/destination/path.txt",
+                                    "relative/source/path.txt")
+      expect(cookbook_file.sensitive).to eq(true)
+
+      @resource.sensitive false
+      cookbook_file = @provider.send(:cookbook_file_resource,
+                                    "/target/destination/path.txt",
+                                    "relative/source/path.txt")
+      expect(cookbook_file.sensitive).to eq(false)
+    end
   end
 
   describe "when creating the remote directory" do
@@ -106,7 +121,7 @@ describe Chef::Provider::RemoteDirectory do
       @node.automatic_attrs[:platform] = :just_testing
       @node.automatic_attrs[:platform_version] = :just_testing
 
-      @destination_dir = Dir.mktmpdir << "/remote_directory_test"
+      @destination_dir = make_canonical_temp_directory << "/remote_directory_test"
       @resource.path(@destination_dir)
     end
 
diff --git a/spec/unit/provider/remote_file/ftp_spec.rb b/spec/unit/provider/remote_file/ftp_spec.rb
index 9963c40..b2fbb73 100644
--- a/spec/unit/provider/remote_file/ftp_spec.rb
+++ b/spec/unit/provider/remote_file/ftp_spec.rb
@@ -19,12 +19,12 @@
 require "spec_helper"
 
 describe Chef::Provider::RemoteFile::FTP do
-  let(:enclosing_directory) {
+  let(:enclosing_directory) do
     canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
-  }
-  let(:resource_path) {
+  end
+  let(:resource_path) do
     canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
-  }
+  end
 
   let(:new_resource) do
     r = Chef::Resource::RemoteFile.new("remote file ftp backend test (new resource)")
diff --git a/spec/unit/provider/remote_file/local_file_spec.rb b/spec/unit/provider/remote_file/local_file_spec.rb
index 31f14fb..6f345ca 100644
--- a/spec/unit/provider/remote_file/local_file_spec.rb
+++ b/spec/unit/provider/remote_file/local_file_spec.rb
@@ -17,6 +17,8 @@
 #
 
 require "spec_helper"
+require "uri"
+require "addressable/uri"
 
 describe Chef::Provider::RemoteFile::LocalFile do
 
@@ -47,7 +49,7 @@ describe Chef::Provider::RemoteFile::LocalFile do
     end
 
     describe "when given local windows path with spaces" do
-      let(:uri) { URI.parse(URI.escape("file:///z:/windows/path/foo & bar.txt")) }
+      let(:uri) { URI.parse(Addressable::URI.encode("file:///z:/windows/path/foo & bar.txt")) }
       it "returns a valid windows local path" do
         expect(fetcher.source_path).to eq("z:/windows/path/foo & bar.txt")
       end
@@ -61,7 +63,7 @@ describe Chef::Provider::RemoteFile::LocalFile do
     end
 
     describe "when given unc windows path with spaces" do
-      let(:uri) { URI.parse(URI.escape("file:////server/share/windows/path/foo & bar.txt")) }
+      let(:uri) { URI.parse(Addressable::URI.encode("file:////server/share/windows/path/foo & bar.txt")) }
       it "returns a valid windows unc path" do
         expect(fetcher.source_path).to eq("//server/share/windows/path/foo & bar.txt")
       end
diff --git a/spec/unit/provider/remote_file/sftp_spec.rb b/spec/unit/provider/remote_file/sftp_spec.rb
index 673ea01..7be507d 100644
--- a/spec/unit/provider/remote_file/sftp_spec.rb
+++ b/spec/unit/provider/remote_file/sftp_spec.rb
@@ -20,12 +20,12 @@ require "spec_helper"
 
 describe Chef::Provider::RemoteFile::SFTP do
   #built out dependencies
-  let(:enclosing_directory) {
+  let(:enclosing_directory) do
     canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
-  }
-  let(:resource_path) {
+  end
+  let(:resource_path) do
     canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
-  }
+  end
 
   let(:new_resource) do
     r = Chef::Resource::RemoteFile.new("remote file sftp backend test (new resource)")
diff --git a/spec/unit/provider/remote_file_spec.rb b/spec/unit/provider/remote_file_spec.rb
index 6107f93..6ceb1d4 100644
--- a/spec/unit/provider/remote_file_spec.rb
+++ b/spec/unit/provider/remote_file_spec.rb
@@ -37,12 +37,12 @@ describe Chef::Provider::RemoteFile do
   let(:node) { double("Chef::Node") }
   let(:events) { double("Chef::Events").as_null_object } # mock all the methods
   let(:run_context) { double("Chef::RunContext", :node => node, :events => events) }
-  let(:enclosing_directory) {
+  let(:enclosing_directory) do
     canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
-  }
-  let(:resource_path) {
+  end
+  let(:resource_path) do
     canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
-  }
+  end
 
   subject(:provider) do
     provider = described_class.new(resource, run_context)
diff --git a/spec/unit/provider/script_spec.rb b/spec/unit/provider/script_spec.rb
index 4e8d8bd..7e34a8f 100644
--- a/spec/unit/provider/script_spec.rb
+++ b/spec/unit/provider/script_spec.rb
@@ -25,12 +25,12 @@ describe Chef::Provider::Script, "action_run" do
 
   let(:run_context) { Chef::RunContext.new(node, {}, events) }
 
-  let(:new_resource) {
+  let(:new_resource) do
     new_resource = Chef::Resource::Script.new("run some perl code")
     new_resource.code "$| = 1; print 'i like beans'"
     new_resource.interpreter "perl"
     new_resource
-  }
+  end
 
   let(:provider) { Chef::Provider::Script.new(new_resource, run_context) }
 
@@ -87,9 +87,9 @@ describe Chef::Provider::Script, "action_run" do
     end
 
     describe "when running the script" do
-      let (:default_opts) {
+      let (:default_opts) do
         { timeout: 3600, returns: 0, log_level: :info, log_tag: "script[run some perl code]" }
-      }
+      end
 
       before do
         allow(STDOUT).to receive(:tty?).and_return(false)
diff --git a/spec/unit/provider/service/debian_service_spec.rb b/spec/unit/provider/service/debian_service_spec.rb
index 2192671..799ed99 100644
--- a/spec/unit/provider/service/debian_service_spec.rb
+++ b/spec/unit/provider/service/debian_service_spec.rb
@@ -39,9 +39,9 @@ describe Chef::Provider::Service::Debian do
       expect(File).to receive(:exists?).with("/usr/sbin/update-rc.d") .and_return(false)
 
       @provider.define_resource_requirements
-      expect {
+      expect do
         @provider.process_resource_requirements
-      }.to raise_error(Chef::Exceptions::Service)
+      end.to raise_error(Chef::Exceptions::Service)
     end
 
     context "when update-rc.d shows init linked to rc*.d/" do
@@ -108,9 +108,9 @@ describe Chef::Provider::Service::Debian do
 
       it "raises an error" do
         @provider.define_resource_requirements
-        expect {
+        expect do
           @provider.process_resource_requirements
-        }.to raise_error(Chef::Exceptions::Service)
+        end.to raise_error(Chef::Exceptions::Service)
       end
     end
 
diff --git a/spec/unit/provider/service/freebsd_service_spec.rb b/spec/unit/provider/service/freebsd_service_spec.rb
index 68d4d63..10eb3c1 100644
--- a/spec/unit/provider/service/freebsd_service_spec.rb
+++ b/spec/unit/provider/service/freebsd_service_spec.rb
@@ -257,10 +257,11 @@ PS_SAMPLE
       end
 
       context "when the enable variable partial matches (left) some other service and we are disabled" do
-        let(:lines) { [
+        let(:lines) do
+          [
           %Q{thing_#{new_resource.service_name}_enable="YES"},
           %Q{#{new_resource.service_name}_enable="NO"},
-        ] }
+        ] end
         it "sets enabled based on the exact match (false)" do
           provider.determine_enabled_status!
           expect(current_resource.enabled).to be false
@@ -268,10 +269,11 @@ PS_SAMPLE
       end
 
       context "when the enable variable partial matches (right) some other service and we are disabled" do
-        let(:lines) { [
+        let(:lines) do
+          [
           %Q{#{new_resource.service_name}_thing_enable="YES"},
           %Q{#{new_resource.service_name}_enable="NO"},
-        ] }
+        ] end
         it "sets enabled based on the exact match (false)" do
           provider.determine_enabled_status!
           expect(current_resource.enabled).to be false
@@ -279,10 +281,11 @@ PS_SAMPLE
       end
 
       context "when the enable variable partial matches (left) some other disabled service and we are enabled" do
-        let(:lines) { [
+        let(:lines) do
+          [
           %Q{thing_#{new_resource.service_name}_enable="NO"},
           %Q{#{new_resource.service_name}_enable="YES"},
-        ] }
+        ] end
         it "sets enabled based on the exact match (true)" do
           provider.determine_enabled_status!
           expect(current_resource.enabled).to be true
@@ -290,10 +293,11 @@ PS_SAMPLE
       end
 
       context "when the enable variable partial matches (right) some other disabled service and we are enabled" do
-        let(:lines) { [
+        let(:lines) do
+          [
           %Q{#{new_resource.service_name}_thing_enable="NO"},
           %Q{#{new_resource.service_name}_enable="YES"},
-        ] }
+        ] end
         it "sets enabled based on the exact match (true)" do
           provider.determine_enabled_status!
           expect(current_resource.enabled).to be true
diff --git a/spec/unit/provider/service/openbsd_service_spec.rb b/spec/unit/provider/service/openbsd_service_spec.rb
index b11015a..872a3bc 100644
--- a/spec/unit/provider/service/openbsd_service_spec.rb
+++ b/spec/unit/provider/service/openbsd_service_spec.rb
@@ -174,10 +174,11 @@ describe Chef::Provider::Service::Openbsd do
       end
 
       context "when the enable variable partial matches (left) some other service and we are disabled" do
-        let(:lines) { [
+        let(:lines) do
+          [
           %Q{thing_#{provider.builtin_service_enable_variable_name}="YES"},
           %Q{#{provider.builtin_service_enable_variable_name}="NO"},
-        ] }
+        ] end
         it "sets enabled based on the exact match (false)" do
           provider.determine_enabled_status!
           expect(current_resource.enabled).to be false
@@ -185,10 +186,11 @@ describe Chef::Provider::Service::Openbsd do
       end
 
       context "when the enable variable partial matches (right) some other service and we are disabled" do
-        let(:lines) { [
+        let(:lines) do
+          [
           %Q{#{provider.builtin_service_enable_variable_name}_thing="YES"},
           %Q{#{provider.builtin_service_enable_variable_name}},
-        ] }
+        ] end
         it "sets enabled based on the exact match (false)" do
           provider.determine_enabled_status!
           expect(current_resource.enabled).to be false
@@ -196,10 +198,11 @@ describe Chef::Provider::Service::Openbsd do
       end
 
       context "when the enable variable partial matches (left) some other disabled service and we are enabled" do
-        let(:lines) { [
+        let(:lines) do
+          [
           %Q{thing_#{provider.builtin_service_enable_variable_name}="NO"},
           %Q{#{provider.builtin_service_enable_variable_name}="YES"},
-        ] }
+        ] end
         it "sets enabled based on the exact match (true)" do
           provider.determine_enabled_status!
           expect(current_resource.enabled).to be true
@@ -207,10 +210,11 @@ describe Chef::Provider::Service::Openbsd do
       end
 
       context "when the enable variable partial matches (right) some other disabled service and we are enabled" do
-        let(:lines) { [
+        let(:lines) do
+          [
           %Q{#{provider.builtin_service_enable_variable_name}_thing="NO"},
           %Q{#{provider.builtin_service_enable_variable_name}="YES"},
-        ] }
+        ] end
         it "sets enabled based on the exact match (true)" do
           provider.determine_enabled_status!
           expect(current_resource.enabled).to be true
diff --git a/spec/unit/provider/service/systemd_service_spec.rb b/spec/unit/provider/service/systemd_service_spec.rb
index e0a9412..4e25f49 100644
--- a/spec/unit/provider/service/systemd_service_spec.rb
+++ b/spec/unit/provider/service/systemd_service_spec.rb
@@ -21,7 +21,7 @@ require "spec_helper"
 
 describe Chef::Provider::Service::Systemd do
 
-  let(:node) {
+  let(:node) do
     node = Chef::Node.new
     node.default["etc"] = Hash.new
     node.default["etc"]["passwd"] = {
@@ -30,7 +30,7 @@ describe Chef::Provider::Service::Systemd do
       },
     }
     node
-  }
+  end
 
   let(:events) { Chef::EventDispatch::Dispatcher.new }
 
@@ -196,14 +196,14 @@ describe Chef::Provider::Service::Systemd do
         context "when a user is specified" do
           it "should call '#{systemctl_path} --user start service_name' if no start command is specified" do
             current_resource.user("joe")
-            expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --user start #{service_name}", { "environment" => { "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/10000/bus" }, "user" => "joe" }).and_return(shell_out_success)
+            expect(provider).to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --user start #{service_name}", { :environment => { "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/10000/bus" }, :user => "joe" }).and_return(shell_out_success)
             provider.start_service
           end
 
           it "should not call '#{systemctl_path} --user start service_name' if it is already running" do
             current_resource.running(true)
             current_resource.user("joe")
-            expect(provider).not_to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --user start #{service_name}", { "environment" => { "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/10000/bus" }, "user" => "joe" })
+            expect(provider).not_to receive(:shell_out_with_systems_locale!).with("#{systemctl_path} --user start #{service_name}", { :environment => { "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/10000/bus" }, :user => "joe" })
             provider.start_service
           end
         end
diff --git a/spec/unit/provider/service/upstart_service_spec.rb b/spec/unit/provider/service/upstart_service_spec.rb
index fd9ea05..fb5a418 100644
--- a/spec/unit/provider/service/upstart_service_spec.rb
+++ b/spec/unit/provider/service/upstart_service_spec.rb
@@ -105,17 +105,21 @@ describe Chef::Provider::Service::Upstart do
     end
 
     describe "when the status command uses the new format" do
-      before do
+      it "should set running to true if the goal state is 'start'" do
+        @stdout = StringIO.new("rsyslog start/running")
+        allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+        @provider.load_current_resource
+        expect(@current_resource.running).to be_truthy
       end
 
-      it "should set running to true if the status command returns 0" do
-        @stdout = StringIO.new("rsyslog start/running")
+      it "should set running to true if the goal state is 'start' but current state is not 'running'" do
+        @stdout = StringIO.new("rsyslog start/starting")
         allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
         @provider.load_current_resource
         expect(@current_resource.running).to be_truthy
       end
 
-      it "should set running to false if the status command returns anything except 0" do
+      it "should set running to false if the goal state is 'stop'" do
         @stdout = StringIO.new("rsyslog stop/waiting")
         allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
         @provider.load_current_resource
@@ -124,17 +128,21 @@ describe Chef::Provider::Service::Upstart do
     end
 
     describe "when the status command uses the new format with an instance" do
-      before do
+      it "should set running to true if the goal state is 'start'" do
+        @stdout = StringIO.new("rsyslog (test) start/running, process 100")
+        allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+        @provider.load_current_resource
+        expect(@current_resource.running).to be_truthy
       end
 
-      it "should set running to true if the status command returns 0" do
-        @stdout = StringIO.new("rsyslog (test) start/running, process 100")
+      it "should set running to true if the goal state is 'start' but current state is not 'running'" do
+        @stdout = StringIO.new("rsyslog (test) start/starting, process 100")
         allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
         @provider.load_current_resource
         expect(@current_resource.running).to be_truthy
       end
 
-      it "should set running to false if the status command returns anything except 0" do
+      it "should set running to false if the goal state is 'stop'" do
         @stdout = StringIO.new("rsyslog (test) stop/waiting, process 100")
         allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
         @provider.load_current_resource
@@ -143,14 +151,21 @@ describe Chef::Provider::Service::Upstart do
     end
 
     describe "when the status command uses the old format" do
-      it "should set running to true if the status command returns 0" do
+      it "should set running to true if the goal state is 'start'" do
         @stdout = StringIO.new("rsyslog (start) running, process 32225")
         allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
         @provider.load_current_resource
         expect(@current_resource.running).to be_truthy
       end
 
-      it "should set running to false if the status command returns anything except 0" do
+      it "should set running to true if the goal state is 'start' but current state is not 'running'" do
+        @stdout = StringIO.new("rsyslog (start) starting, process 32225")
+        allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+        @provider.load_current_resource
+        expect(@current_resource.running).to be_truthy
+      end
+
+      it "should set running to false if the goal state is 'stop'" do
         @stdout = StringIO.new("rsyslog (stop) waiting")
         allow(@provider).to receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
         @provider.load_current_resource
@@ -214,8 +229,8 @@ describe Chef::Provider::Service::Upstart do
       end
     end
 
-    it "should track state when we fail to obtain service status via upstart_state" do
-      expect(@provider).to receive(:upstart_state).and_raise Chef::Exceptions::Exec
+    it "should track state when we fail to obtain service status via upstart_goal_state" do
+      expect(@provider).to receive(:upstart_goal_state).and_raise Chef::Exceptions::Exec
       @provider.load_current_resource
       expect(@provider.instance_variable_get("@command_success")).to eq(false)
     end
diff --git a/spec/unit/provider/service/windows_spec.rb b/spec/unit/provider/service/windows_spec.rb
index f944d8f..d4c4515 100644
--- a/spec/unit/provider/service/windows_spec.rb
+++ b/spec/unit/provider/service/windows_spec.rb
@@ -151,15 +151,15 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
       let(:old_run_as_user) { new_resource.run_as_user }
       let(:old_run_as_password) { new_resource.run_as_password }
 
-      before {
+      before do
         new_resource.run_as_user(".\\wallace")
         new_resource.run_as_password("Wensleydale")
-      }
+      end
 
-      after {
+      after do
         new_resource.run_as_user(old_run_as_user)
         new_resource.run_as_password(old_run_as_password)
-      }
+      end
 
       it "calls #grant_service_logon if the :run_as_user and :run_as_password attributes are present" do
         expect(Win32::Service).to receive(:start)
@@ -409,17 +409,17 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
 
   shared_context "testing private methods" do
 
-    let(:private_methods) {
+    let(:private_methods) do
       described_class.private_instance_methods
-    }
+    end
 
-    before {
+    before do
       described_class.send(:public, *private_methods)
-    }
+    end
 
-    after {
+    after do
       described_class.send(:private, *private_methods)
-    }
+    end
   end
 
   describe "grant_service_logon" do
diff --git a/spec/unit/provider/systemd_unit_spec.rb b/spec/unit/provider/systemd_unit_spec.rb
index 42604c2..7f2907c 100644
--- a/spec/unit/provider/systemd_unit_spec.rb
+++ b/spec/unit/provider/systemd_unit_spec.rb
@@ -58,8 +58,8 @@ describe Chef::Provider::SystemdUnit do
 
   let(:user_cmd_opts) do
     {
-      "user" => "joe",
-      "environment" => {
+      :user => "joe",
+      :environment => {
         "DBUS_SESSION_BUS_ADDRESS" => "unix:path=/run/user/1000/bus",
       },
     }
diff --git a/spec/unit/provider/template/content_spec.rb b/spec/unit/provider/template/content_spec.rb
index 0f936c8..8f30d8f 100644
--- a/spec/unit/provider/template/content_spec.rb
+++ b/spec/unit/provider/template/content_spec.rb
@@ -20,13 +20,13 @@ require "spec_helper"
 
 describe Chef::Provider::Template::Content do
 
-  let(:enclosing_directory) {
+  let(:enclosing_directory) do
     canonicalize_path(Dir.mktmpdir)
-  }
+  end
 
-  let(:resource_path) {
+  let(:resource_path) do
     canonicalize_path(File.expand_path(File.join(enclosing_directory, "openldap_stuff.conf")))
-  }
+  end
 
   let(:new_resource) do
     double("Chef::Resource::Template (new)",
@@ -46,10 +46,10 @@ describe Chef::Provider::Template::Content do
          :helper_modules => [])
   end
 
-  let(:rendered_file_locations) {
+  let(:rendered_file_locations) do
     [Dir.tmpdir + "/openldap_stuff.conf",
      enclosing_directory + "/openldap_stuff.conf"]
-  }
+  end
 
   let(:run_context) do
     cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks"))
@@ -101,9 +101,9 @@ describe Chef::Provider::Template::Content do
   end
 
   context "when creating a tempfile in destdir fails" do
-    let(:enclosing_directory) {
+    let(:enclosing_directory) do
       canonicalize_path("/nonexisting/path")
-    }
+    end
 
     it "returns a tempfile in the tempdir when :file_deployment_uses_destdir is set to :auto" do
       Chef::Config[:file_staging_uses_destdir] = :auto
diff --git a/spec/unit/provider/template_spec.rb b/spec/unit/provider/template_spec.rb
index 488039a..306fd6e 100644
--- a/spec/unit/provider/template_spec.rb
+++ b/spec/unit/provider/template_spec.rb
@@ -27,12 +27,12 @@ describe Chef::Provider::Template do
   let(:node) { double("Chef::Node") }
   let(:events) { double("Chef::Events").as_null_object } # mock all the methods
   let(:run_context) { double("Chef::RunContext", :node => node, :events => events) }
-  let(:enclosing_directory) {
+  let(:enclosing_directory) do
     canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
-  }
-  let(:resource_path) {
+  end
+  let(:resource_path) do
     canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
-  }
+  end
 
   # Subject
 
@@ -61,12 +61,12 @@ describe Chef::Provider::Template do
     let(:node) { double("Chef::Node") }
     let(:events) { double("Chef::Events").as_null_object } # mock all the methods
     let(:run_context) { double("Chef::RunContext", :node => node, :events => events) }
-    let(:enclosing_directory) {
+    let(:enclosing_directory) do
       canonicalize_path(File.expand_path(File.join(CHEF_SPEC_DATA, "templates")))
-    }
-    let(:resource_path) {
+    end
+    let(:resource_path) do
       canonicalize_path(File.expand_path(File.join(enclosing_directory, "seattle.txt")))
-    }
+    end
 
     # Subject
 
diff --git a/spec/unit/provider/user/dscl_spec.rb b/spec/unit/provider/user/dscl_spec.rb
index bf8b316..7b8be02 100644
--- a/spec/unit/provider/user/dscl_spec.rb
+++ b/spec/unit/provider/user/dscl_spec.rb
@@ -24,52 +24,52 @@ describe Chef::Provider::User::Dscl do
   before do
     allow(ChefConfig).to receive(:windows?) { false }
   end
-  let(:shellcmdresult) {
+  let(:shellcmdresult) do
     Struct.new(:stdout, :stderr, :exitstatus)
-  }
-  let(:node) {
+  end
+  let(:node) do
     node = Chef::Node.new
     allow(node).to receive(:[]).with(:platform_version).and_return(mac_version)
     allow(node).to receive(:[]).with(:platform).and_return("mac_os_x")
     node
-  }
+  end
 
-  let(:events) {
+  let(:events) do
     Chef::EventDispatch::Dispatcher.new
-  }
+  end
 
-  let(:run_context) {
+  let(:run_context) do
     Chef::RunContext.new(node, {}, events)
-  }
+  end
 
-  let(:new_resource) {
-    r = Chef::Resource::User.new("toor")
+  let(:new_resource) do
+    r = Chef::Resource::User::DsclUser.new("toor")
     r.password(password)
     r.salt(salt)
     r.iterations(iterations)
     r
-  }
+  end
 
-  let(:provider) {
+  let(:provider) do
     Chef::Provider::User::Dscl.new(new_resource, run_context)
-  }
+  end
 
-  let(:mac_version) {
+  let(:mac_version) do
     "10.9.1"
-  }
+  end
 
   let(:password) { nil }
   let(:salt) { nil }
   let(:iterations) { nil }
 
-  let(:salted_sha512_password) {
+  let(:salted_sha512_password) do
     "0f543f021c63255e64e121a3585601b8ecfedf6d2\
 705ddac69e682a33db5dbcdb9b56a2520bc8fff63a\
 2ba6b7984c0737ff0b7949455071581f7affcd536d\
 402b6cdb097"
-  }
+  end
 
-  let(:salted_sha512_pbkdf2_password) {
+  let(:salted_sha512_pbkdf2_password) do
     "c734b6e4787c3727bb35e29fdd92b97c\
 1de12df509577a045728255ec7c6c5f5\
 c18efa05ed02b682ffa7ebc05119900e\
@@ -78,24 +78,24 @@ b1d4880833aa7a190afc13e2bf0936b8\
 9464a8c234f3919082400b4f939bb77b\
 c5adbbac718b7eb99463a7b679571e0f\
 1c9fef2ef08d0b9e9c2bcf644eed2ffc"
-  }
+  end
 
-  let(:salted_sha512_pbkdf2_salt) {
+  let(:salted_sha512_pbkdf2_salt) do
     "2d942d8364a9ccf2b8e5cb7ed1ff58f78\
 e29dbfee7f9db58859144d061fd0058"
-  }
+  end
 
-  let(:salted_sha512_pbkdf2_iterations) {
+  let(:salted_sha512_pbkdf2_iterations) do
     25000
-  }
+  end
 
-  let(:vagrant_sha_512) {
+  let(:vagrant_sha_512) do
     "6f75d7190441facc34291ebbea1fc756b242d4f\
 e9bcff141bccb84f1979e27e539539aa31f9f7dcc92c0cea959\
 ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
-  }
+  end
 
-  let(:vagrant_sha_512_pbkdf2) {
+  let(:vagrant_sha_512_pbkdf2) do
     "12601a90db17cbf\
 8ba4808e6382fb0d3b9d8a6c1a190477bf680ab21afb\
 6065467136e55cc208a6f74156e3daf20fb13369ef4b\
@@ -103,15 +103,15 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
 47cca84341a7f93a27147343f89fb843fb46c0017d2\
 64afa4976baacf941b915bd1ec1ca24c30b3e759e02\
 403e02f59fe7ff5938a7636c"
-  }
+  end
 
-  let(:vagrant_sha_512_pbkdf2_salt) {
+  let(:vagrant_sha_512_pbkdf2_salt) do
     "ee954be472fdc60ddf89484781433993625f006af6ec810c08f49a7e413946a1"
-  }
+  end
 
-  let(:vagrant_sha_512_pbkdf2_iterations) {
+  let(:vagrant_sha_512_pbkdf2_iterations) do
     34482
-  }
+  end
 
   describe "when shelling out to dscl" do
     it "should run dscl with the supplied cmd /Path args" do
@@ -214,11 +214,13 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
   end
 
   describe "when modifying the home directory" do
-    let(:current_resource) {
+    let(:current_resource) do
       new_resource.dup
-    }
+    end
 
     before do
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
       new_resource.supports({ :manage_home => true })
       new_resource.home("/Users/toor")
 
@@ -237,6 +239,7 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
     end
 
     it "moves the users home to the new location if it exists and the target location is different" do
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
       new_resource.supports(:manage_home => true)
 
       current_home = CHEF_SPEC_DATA + "/old_home_dir"
@@ -307,9 +310,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
     end
 
     describe "when on Mac 10.6" do
-      let(:mac_version) {
+      let(:mac_version) do
         "10.6.5"
-      }
+      end
 
       it "should raise an error" do
         expect { run_requirements }.to raise_error(Chef::Exceptions::User)
@@ -317,9 +320,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
     end
 
     describe "when on Mac 10.7" do
-      let(:mac_version) {
+      let(:mac_version) do
         "10.7.5"
-      }
+      end
 
       describe "when password is SALTED-SHA512" do
         let(:password) { salted_sha512_password }
@@ -340,9 +343,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
 
     [ "10.9", "10.10"].each do |version|
       describe "when on Mac #{version}" do
-        let(:mac_version) {
+        let(:mac_version) do
           "#{version}.2"
-        }
+        end
 
         describe "when password is SALTED-SHA512" do
           let(:password) { salted_sha512_password }
@@ -413,9 +416,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
       let(:password) { "something" } # Load password during load_current_resource
 
       describe "on 10.7" do
-        let(:mac_version) {
+        let(:mac_version) do
           "10.7.5"
-        }
+        end
 
         let(:user_plist_file) { "10.7" }
 
@@ -478,9 +481,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
       end
 
       describe "on 10.8" do
-        let(:mac_version) {
+        let(:mac_version) do
           "10.8.3"
-        }
+        end
 
         let(:user_plist_file) { "10.8" }
 
@@ -504,9 +507,9 @@ e68d1f9821b26689312366")
 
       describe "on 10.7 upgraded to 10.8" do
         # In this scenario user password is still in 10.7 format
-        let(:mac_version) {
+        let(:mac_version) do
           "10.8.3"
-        }
+        end
 
         let(:user_plist_file) { "10.7-8" }
 
@@ -542,9 +545,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
       end
 
       describe "on 10.9" do
-        let(:mac_version) {
+        let(:mac_version) do
           "10.9.1"
-        }
+        end
 
         let(:user_plist_file) { "10.9" }
 
@@ -646,9 +649,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
 
   describe "prepare_password_shadow_info" do
     describe "when on Mac 10.7" do
-      let(:mac_version) {
+      let(:mac_version) do
         "10.7.1"
-      }
+      end
 
       describe "when the password is plain text" do
         let(:password) { "vagrant" }
@@ -676,9 +679,9 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
 
     ["10.8", "10.9", "10.10"].each do |version|
       describe "when on Mac #{version}" do
-        let(:mac_version) {
+        let(:mac_version) do
           "#{version}.1"
-        }
+        end
 
         describe "when the password is plain text" do
           let(:password) { "vagrant" }
@@ -856,6 +859,7 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
 
     describe "when Chef is removing the user" do
       it "removes the user from the groups and deletes home directory when the resource is configured to manage home" do
+        Chef::Config[:treat_deprecation_warnings_as_errors] = false
         new_resource.supports({ :manage_home => true })
         expect(provider).to receive(:run_dscl).with("list /Groups").and_return("my_group\nyour_group\nreal_group\n")
         expect(provider).to receive(:run_dscl).with("read /Groups/my_group").and_raise(Chef::Exceptions::DsclCommandFailed) # Empty group
diff --git a/spec/unit/provider/user/linux_spec.rb b/spec/unit/provider/user/linux_spec.rb
new file mode 100644
index 0000000..1c487c0
--- /dev/null
+++ b/spec/unit/provider/user/linux_spec.rb
@@ -0,0 +1,106 @@
+#
+# Author:: Adam Jacob (<adam at chef.io>)
+# Author:: Daniel DeLeo (<dan at chef.io>)
+# Copyright:: Copyright 2008-2016, Chef Software Inc.
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+require "chef/provider/user/useradd"
+
+describe Chef::Provider::User::Linux do
+
+  subject(:provider) do
+    p = described_class.new(@new_resource, @run_context)
+    p.current_resource = @current_resource
+    p
+  end
+
+  supported_useradd_options = {
+    "comment" => "-c",
+    "gid" => "-g",
+    "uid" => "-u",
+    "shell" => "-s",
+    "password" => "-p",
+  }
+
+  include_examples "a useradd-based user provider", supported_useradd_options
+
+  describe "manage_home behavior" do
+    before(:each) do
+      @new_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
+      @current_resource = Chef::Resource::User::LinuxUser.new("adam", @run_context)
+    end
+
+    it "supports manage_home does not exist", chef: ">= 13" do
+      expect( @new_resource.supports.key?(:manage_home) ).to be false
+    end
+
+    it "supports non_unique does not exist", chef: ">= 13" do
+      expect( @new_resource.supports.key?(:non_unique) ).to be false
+    end
+
+    # supports is a method on the superclass so can't totally be removed, but we should aggressively NOP it to decisively break it
+    it "disables the supports API", chef: ">= 13" do
+      @new_resource.supports( { manage_home: true } )
+      expect( @new_resource.supports.key?(:manage_home) ).to be false
+    end
+
+    it "sets supports manage_home to false" do
+      expect( @new_resource.supports[:manage_home] ).to be false
+    end
+
+    it "sets supports non-unique to false" do
+      expect( @new_resource.supports[:non_unique] ).to be false
+    end
+
+    it "throws a deprecation warning on setting supports[:manage_home]" do
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
+      expect(Chef).to receive(:log_deprecation).with("supports { manage_home: true } on the user resource is deprecated and will be removed in Chef 13, set manage_home: true instead")
+      @new_resource.supports( { :manage_home => true } )
+    end
+
+    it "defaults manage_home to false" do
+      expect( @new_resource.manage_home ).to be false
+    end
+
+    it "supports[:manage_home] (incorectly) acts like manage_home" do
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
+      @new_resource.supports({ manage_home: true })
+      expect( provider.useradd_options ).to eql(["-m"])
+    end
+
+    it "supports[:manage_home] does not change behavior of manage_home: false", chef: ">= 13" do
+      Chef::Config[:treat_deprecation_warnings_as_errors] = false
+      @new_resource.supports({ manage_home: true })
+      expect( provider.useradd_options ).to eql(["-M"])
+    end
+
+    it "by default manage_home is false and we use -M" do
+      expect( provider.useradd_options ).to eql(["-M"])
+    end
+
+    it "setting manage_home to false includes -M" do
+      @new_resource.manage_home false
+      expect( provider.useradd_options ).to eql(["-M"])
+    end
+
+    it "setting manage_home to true includes -m" do
+      @new_resource.manage_home true
+      expect( provider.useradd_options ).to eql(["-m"])
+    end
+  end
+end
diff --git a/spec/unit/provider/user/pw_spec.rb b/spec/unit/provider/user/pw_spec.rb
index 1e9fda9..fb7c921 100644
--- a/spec/unit/provider/user/pw_spec.rb
+++ b/spec/unit/provider/user/pw_spec.rb
@@ -24,7 +24,7 @@ describe Chef::Provider::User::Pw do
     @events = Chef::EventDispatch::Dispatcher.new
     @run_context = Chef::RunContext.new(@node, {}, @events)
 
-    @new_resource = Chef::Resource::User.new("adam")
+    @new_resource = Chef::Resource::User::PwUser.new("adam")
     @new_resource.comment   "Adam Jacob"
     @new_resource.uid       1000
     @new_resource.gid       1000
@@ -32,9 +32,11 @@ describe Chef::Provider::User::Pw do
     @new_resource.shell     "/usr/bin/zsh"
     @new_resource.password  "abracadabra"
 
+    # XXX: rip out in Chef-13
+    Chef::Config[:treat_deprecation_warnings_as_errors] = false
     @new_resource.supports :manage_home => true
 
-    @current_resource = Chef::Resource::User.new("adam")
+    @current_resource = Chef::Resource::User::PwUser.new("adam")
     @current_resource.comment  "Adam Jacob"
     @current_resource.uid      1000
     @current_resource.gid      1000
@@ -170,7 +172,6 @@ describe Chef::Provider::User::Pw do
       end
 
       it "logs an appropriate message" do
-        expect(Chef::Log).to receive(:debug).with("user[adam] no change needed to password")
         @provider.modify_password
       end
     end
@@ -194,7 +195,6 @@ describe Chef::Provider::User::Pw do
       end
 
       it "logs an appropriate message" do
-        expect(Chef::Log).to receive(:debug).with("user[adam] no change needed to password")
         @provider.modify_password
       end
     end
@@ -206,7 +206,6 @@ describe Chef::Provider::User::Pw do
       end
 
       it "should log an appropriate message" do
-        expect(Chef::Log).to receive(:debug).with("user[adam] updating password")
         @provider.modify_password
       end
 
@@ -236,7 +235,7 @@ describe Chef::Provider::User::Pw do
 
   describe "when loading the current state" do
     before do
-      @provider.new_resource = Chef::Resource::User.new("adam")
+      @provider.new_resource = Chef::Resource::User::PwUser.new("adam")
     end
 
     it "should raise an error if the required binary /usr/sbin/pw doesn't exist" do
diff --git a/spec/unit/provider/user/solaris_spec.rb b/spec/unit/provider/user/solaris_spec.rb
index 07a39a1..860c9e4 100644
--- a/spec/unit/provider/user/solaris_spec.rb
+++ b/spec/unit/provider/user/solaris_spec.rb
@@ -25,9 +25,9 @@ require "spec_helper"
 
 describe Chef::Provider::User::Solaris do
 
-  let(:shellcmdresult) {
+  let(:shellcmdresult) do
     Struct.new(:stdout, :stderr, :exitstatus)
-  }
+  end
 
   subject(:provider) do
     p = described_class.new(@new_resource, @run_context)
@@ -44,8 +44,8 @@ describe Chef::Provider::User::Solaris do
       @events = Chef::EventDispatch::Dispatcher.new
       @run_context = Chef::RunContext.new(@node, {}, @events)
 
-      @new_resource = Chef::Resource::User.new("adam", @run_context)
-      @current_resource = Chef::Resource::User.new("adam", @run_context)
+      @new_resource = Chef::Resource::User::SolarisUser.new("adam", @run_context)
+      @current_resource = Chef::Resource::User::SolarisUser.new("adam", @run_context)
 
       @new_resource.password "hocus-pocus"
 
@@ -81,7 +81,7 @@ describe Chef::Provider::User::Solaris do
       @events = Chef::EventDispatch::Dispatcher.new
       @run_context = Chef::RunContext.new(@node, {}, @events)
 
-      @new_resource = Chef::Resource::User.new("dave")
+      @new_resource = Chef::Resource::User::SolarisUser.new("dave")
       @current_resource = @new_resource.dup
 
       @provider = Chef::Provider::User::Solaris.new(@new_resource, @run_context)
diff --git a/spec/unit/provider/user/useradd_spec.rb b/spec/unit/provider/user/useradd_spec.rb
deleted file mode 100644
index 7c67449..0000000
--- a/spec/unit/provider/user/useradd_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Author:: Adam Jacob (<adam at chef.io>)
-# Author:: Daniel DeLeo (<dan at chef.io>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-#
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require "spec_helper"
-require "chef/provider/user/useradd"
-
-describe Chef::Provider::User::Useradd do
-
-  subject(:provider) do
-    p = described_class.new(@new_resource, @run_context)
-    p.current_resource = @current_resource
-    p
-  end
-
-  supported_useradd_options = {
-    "comment" => "-c",
-    "gid" => "-g",
-    "uid" => "-u",
-    "shell" => "-s",
-    "password" => "-p",
-  }
-
-  include_examples "a useradd-based user provider", supported_useradd_options
-
-  describe "manage_user" do
-    # CHEF-5247: Chef::Provider::User::Solaris subclasses Chef::Provider::User::Useradd, but does not use usermod to change passwords.
-    # Thus, a call to Solaris#manage_user calls Solaris#manage_password and Useradd#manage_user, but the latter should be a no-op.
-    it "should not run the command if universal_options is an empty array" do
-      allow(provider).to receive(:universal_options).and_return([])
-      expect(provider).not_to receive(:shell_out!)
-      provider.manage_user
-    end
-  end
-end
diff --git a/spec/unit/provider/user/windows_spec.rb b/spec/unit/provider/user/windows_spec.rb
index 4a62e6e..324b5f5 100644
--- a/spec/unit/provider/user/windows_spec.rb
+++ b/spec/unit/provider/user/windows_spec.rb
@@ -30,10 +30,10 @@ end
 describe Chef::Provider::User::Windows do
   before(:each) do
     @node = Chef::Node.new
-    @new_resource = Chef::Resource::User.new("monkey")
+    @new_resource = Chef::Resource::User::WindowsUser.new("monkey")
     @events = Chef::EventDispatch::Dispatcher.new
     @run_context = Chef::RunContext.new(@node, {}, @events)
-    @current_resource = Chef::Resource::User.new("monkey")
+    @current_resource = Chef::Resource::User::WindowsUser.new("monkey")
 
     @net_user = double("Chef::Util::Windows::NetUser")
     allow(Chef::Util::Windows::NetUser).to receive(:new).and_return(@net_user)
@@ -89,7 +89,7 @@ describe Chef::Provider::User::Windows do
 
     describe "and the attributes do not match" do
       before do
-        @current_resource = Chef::Resource::User.new("adam")
+        @current_resource = Chef::Resource::User::WindowsUser.new("adam")
         @current_resource.comment   "Adam Jacob-foo"
         @current_resource.uid       1111
         @current_resource.gid       1111
diff --git a/spec/unit/provider/user_spec.rb b/spec/unit/provider/user_spec.rb
index 1cdbe46..1a8ad6a 100644
--- a/spec/unit/provider/user_spec.rb
+++ b/spec/unit/provider/user_spec.rb
@@ -190,7 +190,7 @@ describe Chef::Provider::User do
   end
 
   describe "compare_user" do
-    let(:mapping) {
+    let(:mapping) do
       {
         "username" => %w{adam Adam},
         "comment" => ["Adam Jacob", "adam jacob"],
@@ -200,7 +200,7 @@ describe Chef::Provider::User do
         "shell" => ["/usr/bin/zsh", "/bin/bash"],
         "password" => %w{abcd 12345},
       }
-    }
+    end
 
     %w{uid gid comment home shell password}.each do |attribute|
       it "should return true if #{attribute} doesn't match" do
diff --git a/spec/unit/resource/apt_update_spec.rb b/spec/unit/provider/yum_repository_spec.rb
similarity index 59%
copy from spec/unit/resource/apt_update_spec.rb
copy to spec/unit/provider/yum_repository_spec.rb
index 8015cb0..5b019f7 100644
--- a/spec/unit/resource/apt_update_spec.rb
+++ b/spec/unit/provider/yum_repository_spec.rb
@@ -18,21 +18,18 @@
 
 require "spec_helper"
 
-describe Chef::Resource::AptUpdate do
+describe Chef::Provider::YumRepository do
+  let(:new_resource) { Chef::Resource::YumRepository.new("multiverse") }
 
-  let(:resource) { Chef::Resource::AptUpdate.new("update") }
-
-  it "should create a new Chef::Resource::AptUpdate" do
-    expect(resource).to be_a_kind_of(Chef::Resource)
-    expect(resource).to be_a_kind_of(Chef::Resource::AptUpdate)
+  let(:provider) do
+    node = Chef::Node.new
+    events = Chef::EventDispatch::Dispatcher.new
+    run_context = Chef::RunContext.new(node, {}, events)
+    Chef::Provider::YumRepository.new(new_resource, run_context)
   end
 
-  it "the default frequency should be 1 day" do
-    expect(resource.frequency).to eql(86_400)
+  it "responds to load_current_resource" do
+    expect(provider).to respond_to(:load_current_resource)
   end
 
-  it "the frequency should accept integers" do
-    resource.frequency(400)
-    expect(resource.frequency).to eql(400)
-  end
 end
diff --git a/spec/unit/provider_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb
index 5d4396e..5ba5dda 100644
--- a/spec/unit/provider_resolver_spec.rb
+++ b/spec/unit/provider_resolver_spec.rb
@@ -480,7 +480,7 @@ describe Chef::ProviderResolver do
         end
       end
 
-      on_platform %w{freebsd netbsd}, platform_version: "3.1.4" do
+      on_platform "freebsd", os: "freebsd", platform_version: "10.3" do
         it "returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
           stub_service_providers
           stub_service_configs(:usr_local_etc_rcd)
@@ -507,7 +507,41 @@ describe Chef::ProviderResolver do
           expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
         end
 
-        it "foo" do
+        it "always returns a freebsd provider by default?" do
+          stub_service_providers
+          stub_service_configs
+          expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+        end
+      end
+
+      on_platform "netbsd", os: "netbsd", platform_version: "7.0.1" do
+        it "returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
+          stub_service_providers
+          stub_service_configs(:usr_local_etc_rcd)
+          expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+        end
+
+        it "returns a Freebsd provider if it finds the /etc/rc.d initscript" do
+          stub_service_providers
+          stub_service_configs(:etc_rcd)
+          expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+        end
+
+        it "always returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
+          # should only care about :usr_local_etc_rcd stub in the service configs
+          stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
+          stub_service_configs(:usr_local_etc_rcd, :initd, :upstart, :xinetd, :systemd)
+          expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+        end
+
+        it "always returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
+          # should only care about :etc_rcd stub in the service configs
+          stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
+          stub_service_configs(:etc_rcd, :initd, :upstart, :xinetd, :systemd)
+          expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+        end
+
+        it "always returns a freebsd provider by default?" do
           stub_service_providers
           stub_service_configs
           expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
@@ -517,347 +551,355 @@ describe Chef::ProviderResolver do
     end
 
     PROVIDERS =
-    {
-      bash:                   [ Chef::Resource::Bash, Chef::Provider::Script ],
-      breakpoint:             [ Chef::Resource::Breakpoint, Chef::Provider::Breakpoint ],
-      chef_gem:               [ Chef::Resource::ChefGem, Chef::Provider::Package::Rubygems ],
-      cookbook_file:          [ Chef::Resource::CookbookFile, Chef::Provider::CookbookFile ],
-      csh:                    [ Chef::Resource::Csh, Chef::Provider::Script ],
-      deploy:                 [ Chef::Resource::Deploy, Chef::Provider::Deploy::Timestamped ],
-      deploy_revision:        [ Chef::Resource::DeployRevision, Chef::Provider::Deploy::Revision ],
-      directory:              [ Chef::Resource::Directory, Chef::Provider::Directory ],
-      easy_install_package:   [ Chef::Resource::EasyInstallPackage, Chef::Provider::Package::EasyInstall ],
-      erl_call:               [ Chef::Resource::ErlCall, Chef::Provider::ErlCall ],
-      execute:                [ Chef::Resource::Execute, Chef::Provider::Execute ],
-      file:                   [ Chef::Resource::File, Chef::Provider::File ],
-      gem_package:            [ Chef::Resource::GemPackage, Chef::Provider::Package::Rubygems ],
-      git:                    [ Chef::Resource::Git, Chef::Provider::Git ],
-      group:                  [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
-      homebrew_package:       [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
-      http_request:           [ Chef::Resource::HttpRequest, Chef::Provider::HttpRequest ],
-      ifconfig:               [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
-      link:                   [ Chef::Resource::Link, Chef::Provider::Link ],
-      log:                    [ Chef::Resource::Log, Chef::Provider::Log::ChefLog ],
-      macports_package:       [ Chef::Resource::MacportsPackage, Chef::Provider::Package::Macports ],
-      mdadm:                  [ Chef::Resource::Mdadm, Chef::Provider::Mdadm ],
-      mount:                  [ Chef::Resource::Mount, Chef::Provider::Mount::Mount ],
-      perl:                   [ Chef::Resource::Perl, Chef::Provider::Script ],
-      portage_package:        [ Chef::Resource::PortagePackage, Chef::Provider::Package::Portage ],
-      python:                 [ Chef::Resource::Python, Chef::Provider::Script ],
-      remote_directory:       [ Chef::Resource::RemoteDirectory, Chef::Provider::RemoteDirectory ],
-      route:                  [ Chef::Resource::Route, Chef::Provider::Route ],
-      ruby:                   [ Chef::Resource::Ruby, Chef::Provider::Script ],
-      ruby_block:             [ Chef::Resource::RubyBlock, Chef::Provider::RubyBlock ],
-      script:                 [ Chef::Resource::Script, Chef::Provider::Script ],
-      subversion:             [ Chef::Resource::Subversion, Chef::Provider::Subversion ],
-      template:               [ Chef::Resource::Template, Chef::Provider::Template ],
-      timestamped_deploy:     [ Chef::Resource::TimestampedDeploy, Chef::Provider::Deploy::Timestamped ],
-      user:                   [ Chef::Resource::User, Chef::Provider::User::Useradd ],
-      whyrun_safe_ruby_block: [ Chef::Resource::WhyrunSafeRubyBlock, Chef::Provider::WhyrunSafeRubyBlock ],
-
-      # We want to check that these are unsupported:
-      apt_package: nil,
-      bff_package: nil,
-      dpkg_package: nil,
-      dsc_script: nil,
-      ips_package: nil,
-      pacman_package: nil,
-      paludis_package: nil,
-      rpm_package: nil,
-      smartos_package: nil,
-      solaris_package: nil,
-      yum_package: nil,
-      windows_package: nil,
-      windows_service: nil,
-
-      "linux" => {
-        apt_package:     [ Chef::Resource::AptPackage, Chef::Provider::Package::Apt ],
-        dpkg_package:    [ Chef::Resource::DpkgPackage, Chef::Provider::Package::Dpkg ],
-        pacman_package:  [ Chef::Resource::PacmanPackage, Chef::Provider::Package::Pacman ],
-        paludis_package: [ Chef::Resource::PaludisPackage, Chef::Provider::Package::Paludis ],
-        rpm_package:     [ Chef::Resource::RpmPackage, Chef::Provider::Package::Rpm ],
-        yum_package:     [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
-
-        "debian" => {
-          ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Debian ],
-          package:  [ Chef::Resource::AptPackage, Chef::Provider::Package::Apt ],
-  #        service: [ Chef::Resource::DebianService, Chef::Provider::Service::Debian ],
+      {
+        bash:                   [ Chef::Resource::Bash, Chef::Provider::Script ],
+        breakpoint:             [ Chef::Resource::Breakpoint, Chef::Provider::Breakpoint ],
+        chef_gem:               [ Chef::Resource::ChefGem, Chef::Provider::Package::Rubygems ],
+        cookbook_file:          [ Chef::Resource::CookbookFile, Chef::Provider::CookbookFile ],
+        csh:                    [ Chef::Resource::Csh, Chef::Provider::Script ],
+        deploy:                 [ Chef::Resource::Deploy, Chef::Provider::Deploy::Timestamped ],
+        deploy_revision:        [ Chef::Resource::DeployRevision, Chef::Provider::Deploy::Revision ],
+        directory:              [ Chef::Resource::Directory, Chef::Provider::Directory ],
+        easy_install_package:   [ Chef::Resource::EasyInstallPackage, Chef::Provider::Package::EasyInstall ],
+        erl_call:               [ Chef::Resource::ErlCall, Chef::Provider::ErlCall ],
+        execute:                [ Chef::Resource::Execute, Chef::Provider::Execute ],
+        file:                   [ Chef::Resource::File, Chef::Provider::File ],
+        gem_package:            [ Chef::Resource::GemPackage, Chef::Provider::Package::Rubygems ],
+        git:                    [ Chef::Resource::Git, Chef::Provider::Git ],
+        group:                  [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
+        homebrew_package:       [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
+        http_request:           [ Chef::Resource::HttpRequest, Chef::Provider::HttpRequest ],
+        ifconfig:               [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+        link:                   [ Chef::Resource::Link, Chef::Provider::Link ],
+        log:                    [ Chef::Resource::Log, Chef::Provider::Log::ChefLog ],
+        macports_package:       [ Chef::Resource::MacportsPackage, Chef::Provider::Package::Macports ],
+        mdadm:                  [ Chef::Resource::Mdadm, Chef::Provider::Mdadm ],
+        mount:                  [ Chef::Resource::Mount, Chef::Provider::Mount::Mount ],
+        perl:                   [ Chef::Resource::Perl, Chef::Provider::Script ],
+        portage_package:        [ Chef::Resource::PortagePackage, Chef::Provider::Package::Portage ],
+        python:                 [ Chef::Resource::Python, Chef::Provider::Script ],
+        remote_directory:       [ Chef::Resource::RemoteDirectory, Chef::Provider::RemoteDirectory ],
+        route:                  [ Chef::Resource::Route, Chef::Provider::Route ],
+        ruby:                   [ Chef::Resource::Ruby, Chef::Provider::Script ],
+        ruby_block:             [ Chef::Resource::RubyBlock, Chef::Provider::RubyBlock ],
+        script:                 [ Chef::Resource::Script, Chef::Provider::Script ],
+        subversion:             [ Chef::Resource::Subversion, Chef::Provider::Subversion ],
+        template:               [ Chef::Resource::Template, Chef::Provider::Template ],
+        timestamped_deploy:     [ Chef::Resource::TimestampedDeploy, Chef::Provider::Deploy::Timestamped ],
+        aix_user:               [ Chef::Resource::User::AixUser, Chef::Provider::User::Aix ],
+        dscl_user:              [ Chef::Resource::User::DsclUser, Chef::Provider::User::Dscl ],
+        linux_user:             [ Chef::Resource::User::LinuxUser, Chef::Provider::User::Linux ],
+        pw_user:                [ Chef::Resource::User::PwUser, Chef::Provider::User::Pw ],
+        solaris_user:           [ Chef::Resource::User::SolarisUser, Chef::Provider::User::Solaris ],
+        windows_user:           [ Chef::Resource::User::WindowsUser, Chef::Provider::User::Windows ],
+        whyrun_safe_ruby_block: [ Chef::Resource::WhyrunSafeRubyBlock, Chef::Provider::WhyrunSafeRubyBlock ],
+
+        # We want to check that these are unsupported:
+        apt_package: nil,
+        bff_package: nil,
+        dpkg_package: nil,
+        dsc_script: nil,
+        ips_package: nil,
+        pacman_package: nil,
+        paludis_package: nil,
+        rpm_package: nil,
+        smartos_package: nil,
+        solaris_package: nil,
+        yum_package: nil,
+        windows_package: nil,
+        windows_service: nil,
+
+        "linux" => {
+          apt_package:     [ Chef::Resource::AptPackage, Chef::Provider::Package::Apt ],
+          dpkg_package:    [ Chef::Resource::DpkgPackage, Chef::Provider::Package::Dpkg ],
+          pacman_package:  [ Chef::Resource::PacmanPackage, Chef::Provider::Package::Pacman ],
+          paludis_package: [ Chef::Resource::PaludisPackage, Chef::Provider::Package::Paludis ],
+          rpm_package:     [ Chef::Resource::RpmPackage, Chef::Provider::Package::Rpm ],
+          yum_package:     [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
 
           "debian" => {
-            "7.0" => {
-            },
-            "6.0" => {
-              ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
-  #            service: [ Chef::Resource::InsservService, Chef::Provider::Service::Insserv ],
-            },
-            "5.0" => {
-              ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+            ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Debian ],
+            package:  [ Chef::Resource::AptPackage, Chef::Provider::Package::Apt ],
+    #        service: [ Chef::Resource::DebianService, Chef::Provider::Service::Debian ],
+
+            "debian" => {
+              "7.0" => {
+              },
+              "6.0" => {
+                ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+    #            service: [ Chef::Resource::InsservService, Chef::Provider::Service::Insserv ],
+              },
+              "5.0" => {
+                ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+              },
+            },
+            "gcel" => {
+              "3.1.4" => {
+                ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+              },
+            },
+            "linaro" => {
+              "3.1.4" => {
+                ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+              },
+            },
+            "linuxmint" => {
+              "3.1.4" => {
+                ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+    #            service: [ Chef::Resource::UpstartService, Chef::Provider::Service::Upstart ],
+              },
+            },
+            "raspbian" => {
+              "3.1.4" => {
+                ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+              },
+            },
+            "ubuntu" => {
+              "11.10" => {
+              },
+              "10.04" => {
+                ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+              },
             },
           },
-          "gcel" => {
-            "3.1.4" => {
-              ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
-            },
-          },
-          "linaro" => {
-            "3.1.4" => {
-              ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
-            },
-          },
-          "linuxmint" => {
-            "3.1.4" => {
-              ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
-  #            service: [ Chef::Resource::UpstartService, Chef::Provider::Service::Upstart ],
-            },
-          },
-          "raspbian" => {
-            "3.1.4" => {
-              ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
-            },
-          },
-          "ubuntu" => {
-            "11.10" => {
-            },
-            "10.04" => {
-              ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
-            },
-          },
-        },
-
-        "arch" => {
-          # TODO should be Chef::Resource::PacmanPackage
-          package: [ Chef::Resource::Package, Chef::Provider::Package::Pacman ],
 
           "arch" => {
-            "3.1.4" => {
-            },
-          },
-        },
-
-        "freebsd" => {
-          group: [ Chef::Resource::Group, Chef::Provider::Group::Pw ],
-          user:  [ Chef::Resource::User, Chef::Provider::User::Pw ],
+            # TODO should be Chef::Resource::PacmanPackage
+            package: [ Chef::Resource::Package, Chef::Provider::Package::Pacman ],
 
-          "freebsd" => {
-            "3.1.4" => {
+            "arch" => {
+              "3.1.4" => {
+              },
             },
           },
-        },
-        "suse" => {
-          group: [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
+
           "suse" => {
-            "12.0" => {
-            },
-            %w{11.1 11.2 11.3} => {
-              group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
+            group: [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
+            "suse" => {
+              "12.0" => {
+              },
+              %w{11.1 11.2 11.3} => {
+                group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
+              },
+            },
+            "opensuse" => {
+    #          service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+              package: [ Chef::Resource::ZypperPackage, Chef::Provider::Package::Zypper ],
+              group:   [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+              "12.3" => {
+              },
+              "12.2" => {
+                group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
+              },
             },
           },
-          "opensuse" => {
-  #          service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
-            package: [ Chef::Resource::ZypperPackage, Chef::Provider::Package::Zypper ],
-            group:   [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
-            "12.3" => {
-            },
-            "12.2" => {
-              group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
+
+          "gentoo" => {
+            # TODO should be Chef::Resource::PortagePackage
+            package:         [ Chef::Resource::Package, Chef::Provider::Package::Portage ],
+            portage_package: [ Chef::Resource::PortagePackage, Chef::Provider::Package::Portage ],
+    #        service: [ Chef::Resource::GentooService, Chef::Provider::Service::Gentoo ],
+
+            "gentoo" => {
+              "3.1.4" => {
+              },
             },
           },
-        },
 
-        "gentoo" => {
-          # TODO should be Chef::Resource::PortagePackage
-          package:         [ Chef::Resource::Package, Chef::Provider::Package::Portage ],
-          portage_package: [ Chef::Resource::PortagePackage, Chef::Provider::Package::Portage ],
-  #        service: [ Chef::Resource::GentooService, Chef::Provider::Service::Gentoo ],
-
-          "gentoo" => {
-            "3.1.4" => {
+          "rhel" => {
+    #        service: [ Chef::Resource::SystemdService, Chef::Provider::Service::Systemd ],
+            package:  [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
+            ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Redhat ],
+
+            %w{amazon xcp xenserver ibm_powerkvm cloudlinux parallels} => {
+              "3.1.4" => {
+    #            service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+              },
+            },
+            %w{redhat centos scientific oracle} => {
+              "7.0" => {
+              },
+              "6.0" => {
+    #            service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+              },
+            },
+            "fedora" => {
+              "15.0" => {
+              },
+              "14.0" => {
+    #            service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+              },
             },
           },
+
         },
 
-        "rhel" => {
-  #        service: [ Chef::Resource::SystemdService, Chef::Provider::Service::Systemd ],
-          package:  [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
-          ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Redhat ],
+        "freebsd" => {
+          "freebsd" => {
+            group: [ Chef::Resource::Group, Chef::Provider::Group::Pw ],
+            user:  [ Chef::Resource::User::PwUser, Chef::Provider::User::Pw ],
 
-          %w{amazon xcp xenserver ibm_powerkvm cloudlinux parallels} => {
-            "3.1.4" => {
-  #            service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
-            },
-          },
-          %w{redhat centos scientific oracle} => {
-            "7.0" => {
-            },
-            "6.0" => {
-  #            service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
-            },
-          },
-          "fedora" => {
-            "15.0" => {
-            },
-            "14.0" => {
-  #            service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+            "freebsd" => {
+              "10.3" => {
+              },
             },
           },
         },
 
-      },
+        "darwin" => {
+          %w{mac_os_x mac_os_x_server} => {
+            group:   [ Chef::Resource::Group, Chef::Provider::Group::Dscl ],
+            package: [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
+            osx_profile: [ Chef::Resource::OsxProfile, Chef::Provider::OsxProfile],
+            user:    [ Chef::Resource::User::DsclUser, Chef::Provider::User::Dscl ],
 
-      "darwin" => {
-        %w{mac_os_x mac_os_x_server} => {
-          group:   [ Chef::Resource::Group, Chef::Provider::Group::Dscl ],
-          package: [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
-          osx_profile: [ Chef::Resource::OsxProfile, Chef::Provider::OsxProfile],
-          user:    [ Chef::Resource::User, Chef::Provider::User::Dscl ],
-
-          "mac_os_x" => {
-            "10.9.2" => {
+            "mac_os_x" => {
+              "10.9.2" => {
+              },
             },
           },
         },
-      },
-
-      "windows" => {
-        batch:             [ Chef::Resource::Batch, Chef::Provider::Batch ],
-        dsc_script:        [ Chef::Resource::DscScript, Chef::Provider::DscScript ],
-        env:               [ Chef::Resource::Env, Chef::Provider::Env::Windows ],
-        group:             [ Chef::Resource::Group, Chef::Provider::Group::Windows ],
-        mount:             [ Chef::Resource::Mount, Chef::Provider::Mount::Windows ],
-        package:           [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
-        powershell_script: [ Chef::Resource::PowershellScript, Chef::Provider::PowershellScript ],
-        service:           [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
-        user:              [ Chef::Resource::User, Chef::Provider::User::Windows ],
-        windows_package:   [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
-        windows_service:   [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
 
         "windows" => {
-          %w{mswin mingw32 windows} => {
-            "10.9.2" => {
+          batch:             [ Chef::Resource::Batch, Chef::Provider::Batch ],
+          dsc_script:        [ Chef::Resource::DscScript, Chef::Provider::DscScript ],
+          env:               [ Chef::Resource::Env, Chef::Provider::Env::Windows ],
+          group:             [ Chef::Resource::Group, Chef::Provider::Group::Windows ],
+          mount:             [ Chef::Resource::Mount, Chef::Provider::Mount::Windows ],
+          package:           [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
+          powershell_script: [ Chef::Resource::PowershellScript, Chef::Provider::PowershellScript ],
+          service:           [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
+          user:              [ Chef::Resource::User::WindowsUser, Chef::Provider::User::Windows ],
+          windows_package:   [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
+          windows_service:   [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
+
+          "windows" => {
+            %w{mswin mingw32 windows} => {
+              "10.9.2" => {
+              },
             },
           },
         },
-      },
-
-      "aix" => {
-        bff_package: [ Chef::Resource::BffPackage, Chef::Provider::Package::Aix ],
-        cron: [ Chef::Resource::Cron, Chef::Provider::Cron::Aix ],
-        group: [ Chef::Resource::Group, Chef::Provider::Group::Aix ],
-        ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Aix ],
-        mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Aix ],
-        # TODO should be Chef::Resource::BffPackage
-        package: [ Chef::Resource::Package, Chef::Provider::Package::Aix ],
-        rpm_package: [ Chef::Resource::RpmPackage, Chef::Provider::Package::Rpm ],
-        user: [ Chef::Resource::User, Chef::Provider::User::Aix ],
-  #      service: [ Chef::Resource::AixService, Chef::Provider::Service::Aix ],
 
         "aix" => {
+          bff_package: [ Chef::Resource::BffPackage, Chef::Provider::Package::Aix ],
+          cron: [ Chef::Resource::Cron, Chef::Provider::Cron::Aix ],
+          group: [ Chef::Resource::Group, Chef::Provider::Group::Aix ],
+          ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Aix ],
+          mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Aix ],
+          # TODO should be Chef::Resource::BffPackage
+          package: [ Chef::Resource::Package, Chef::Provider::Package::Aix ],
+          rpm_package: [ Chef::Resource::RpmPackage, Chef::Provider::Package::Rpm ],
+          user: [ Chef::Resource::User::AixUser, Chef::Provider::User::Aix ],
+    #      service: [ Chef::Resource::AixService, Chef::Provider::Service::Aix ],
+
           "aix" => {
-            "5.6" => {
+            "aix" => {
+              "5.6" => {
+              },
             },
           },
         },
-      },
 
-      "hpux" => {
         "hpux" => {
           "hpux" => {
-            "3.1.4" => {
-              group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+            "hpux" => {
+              "3.1.4" => {
+                group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+              },
             },
           },
         },
-      },
 
-      "netbsd" => {
         "netbsd" => {
           "netbsd" => {
-            "3.1.4" => {
-              group: [ Chef::Resource::Group, Chef::Provider::Group::Groupmod ],
+            "netbsd" => {
+              "3.1.4" => {
+                group: [ Chef::Resource::Group, Chef::Provider::Group::Groupmod ],
+              },
             },
           },
         },
-      },
-
-      "openbsd" => {
-        group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
-        package: [ Chef::Resource::OpenbsdPackage, Chef::Provider::Package::Openbsd ],
 
         "openbsd" => {
+          group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+          package: [ Chef::Resource::OpenbsdPackage, Chef::Provider::Package::Openbsd ],
+
           "openbsd" => {
-            "3.1.4" => {
+            "openbsd" => {
+              "3.1.4" => {
+              },
             },
           },
         },
-      },
-
-      "solaris2" => {
-        group:           [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
-        ips_package:     [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
-        package:         [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
-        mount:           [ Chef::Resource::Mount, Chef::Provider::Mount::Solaris ],
-        solaris_package: [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
 
-        "smartos" => {
-          smartos_package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
-          package:         [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
+        "solaris2" => {
+          group:           [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+          ips_package:     [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
+          package:         [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
+          mount:           [ Chef::Resource::Mount, Chef::Provider::Mount::Solaris ],
+          solaris_package: [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
 
           "smartos" => {
-            "3.1.4" => {
-            },
-          },
-        },
+            smartos_package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
+            package:         [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
 
-        "solaris2" => {
-          "nexentacore" => {
-            "3.1.4" => {
-            },
-          },
-          "omnios" => {
-            "3.1.4" => {
-              user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
-            },
-          },
-          "openindiana" => {
-            "3.1.4" => {
-            },
-          },
-          "opensolaris" => {
-            "3.1.4" => {
+            "smartos" => {
+              "3.1.4" => {
+              },
             },
           },
+
           "solaris2" => {
-            user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
-            "5.11" => {
-              package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
-            },
-            "5.9" => {
+            "nexentacore" => {
+              "3.1.4" => {
+              },
+            },
+            "omnios" => {
+              "3.1.4" => {
+                user: [ Chef::Resource::User::SolarisUser, Chef::Provider::User::Solaris ],
+              },
+            },
+            "openindiana" => {
+              "3.1.4" => {
+              },
+            },
+            "opensolaris" => {
+              "3.1.4" => {
+              },
+            },
+            "solaris2" => {
+              user: [ Chef::Resource::User::SolarisUser, Chef::Provider::User::Solaris ],
+              "5.11" => {
+                package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
+              },
+              "5.9" => {
+              },
             },
           },
-        },
 
-      },
+        },
 
-      "solaris" => {
         "solaris" => {
           "solaris" => {
-            "3.1.4" => {
+            "solaris" => {
+              "3.1.4" => {
+              },
             },
           },
         },
-      },
 
-      "exherbo" => {
         "exherbo" => {
           "exherbo" => {
-            "3.1.4" => {
-              # TODO should be Chef::Resource::PaludisPackage
-              package: [ Chef::Resource::Package, Chef::Provider::Package::Paludis ],
+            "exherbo" => {
+              "3.1.4" => {
+                # TODO should be Chef::Resource::PaludisPackage
+                package: [ Chef::Resource::Package, Chef::Provider::Package::Paludis ],
+              },
             },
           },
         },
-      },
-    }
+      }
 
     def self.create_provider_tests(providers, test, expected, filter)
       expected = expected.merge(providers.select { |key, value| key.is_a?(Symbol) })
diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb
index e5dbd42..f42b756 100644
--- a/spec/unit/recipe_spec.rb
+++ b/spec/unit/recipe_spec.rb
@@ -75,15 +75,15 @@ describe Chef::Recipe do
       end
 
       it "should require a name argument" do
-        expect {
+        expect do
           recipe.cat
-        }.to raise_error(ArgumentError)
+        end.to raise_error(ArgumentError)
       end
 
       it "should allow regular errors (not NameErrors) to pass unchanged" do
-        expect {
+        expect do
           recipe.cat("felix") { raise ArgumentError, "You Suck" }
-        }.to raise_error(ArgumentError)
+        end.to raise_error(ArgumentError)
       end
 
       it "should add our zen_master to the collection" do
@@ -195,9 +195,7 @@ describe Chef::Recipe do
 
     describe "when cloning resources" do
       def expect_warning
-        expect(Chef::Log).to receive(:warn).with(/3694/)
-        expect(Chef::Log).to receive(:warn).with(/Previous/)
-        expect(Chef::Log).to receive(:warn).with(/Current/)
+        expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
       end
 
       it "should emit a 3694 warning when attributes change" do
@@ -244,7 +242,7 @@ describe Chef::Recipe do
 
       it "should not emit a 3694 warning for completely trivial resource cloning" do
         recipe.zen_master "klopp"
-        expect(Chef::Log).to_not receive(:warn)
+        expect(Chef).to_not receive(:log_deprecation)
         recipe.zen_master "klopp"
       end
 
@@ -252,7 +250,7 @@ describe Chef::Recipe do
         recipe.zen_master "klopp" do
           action :nothing
         end
-        expect(Chef::Log).to_not receive(:warn)
+        expect(Chef).to_not receive(:log_deprecation)
         recipe.zen_master "klopp" do
           action :score
         end
@@ -262,16 +260,38 @@ describe Chef::Recipe do
         recipe.zen_master "klopp" do
           action :score
         end
-        expect(Chef::Log).to_not receive(:warn)
+        expect(Chef).to_not receive(:log_deprecation)
         recipe.zen_master "klopp" do
           action :nothing
         end
       end
 
+      class Coerced < Chef::Resource
+        resource_name :coerced
+        provides :coerced
+        default_action :whatever
+        property :package_name, [String, Array], coerce: proc { |x| [x].flatten }, name_property: true
+        def after_created
+          Array(action).each do |action|
+            run_action(action)
+          end
+        end
+        action :whatever do
+          package_name # unlazy the package_name
+        end
+      end
+
+      it "does not emit 3694 when the name_property is unlazied by running it at compile_time" do
+        recipe.coerced "string"
+        expect(Chef).to_not receive(:log_deprecation)
+        recipe.coerced "string"
+      end
+
       it "validating resources via build_resource" do
-        expect {recipe.build_resource(:remote_file, "klopp") do
-          source Chef::DelayedEvaluator.new { "http://chef.io" }
-        end}.to_not raise_error
+        expect do
+          recipe.build_resource(:remote_file, "klopp") do
+            source Chef::DelayedEvaluator.new { "http://chef.io" }
+          end end.to_not raise_error
       end
 
     end
@@ -299,6 +319,7 @@ describe Chef::Recipe do
       end
 
       it "will insert another resource if create_if_missing is not set (cloned resource as of Chef-12)" do
+        expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
         zm_resource
         recipe.declare_resource(:zen_master, "klopp")
         expect(run_context.resource_collection.count).to eql(2)
@@ -421,15 +442,18 @@ describe Chef::Recipe do
       end
 
       it "copies attributes from the first resource" do
+        expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
         expect(duplicated_resource.something).to eq("bvb09")
       end
 
       it "does not copy the action from the first resource" do
+        expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
         expect(original_resource.action).to eq([:score])
         expect(duplicated_resource.action).to eq([:nothing])
       end
 
       it "does not copy the source location of the first resource" do
+        expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
         # sanity check source location:
         expect(original_resource.source_line).to include(__FILE__)
         expect(duplicated_resource.source_line).to include(__FILE__)
@@ -438,10 +462,12 @@ describe Chef::Recipe do
       end
 
       it "sets the cookbook name on the cloned resource to that resource's cookbook" do
+        expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
         expect(duplicated_resource.cookbook_name).to eq("second_cb")
       end
 
       it "sets the recipe name on the cloned resource to that resoure's recipe" do
+        expect(Chef).to receive(:log_deprecation).with(/^Cloning resource attributes for zen_master\[klopp\]/)
         expect(duplicated_resource.recipe_name).to eq("second_recipe")
       end
 
diff --git a/spec/unit/resource/apt_repository_spec.rb b/spec/unit/resource/apt_repository_spec.rb
index cd55ce6..0b0c0c5 100644
--- a/spec/unit/resource/apt_repository_spec.rb
+++ b/spec/unit/resource/apt_repository_spec.rb
@@ -19,8 +19,10 @@
 require "spec_helper"
 
 describe Chef::Resource::AptRepository do
-
-  let(:resource) { Chef::Resource::AptRepository.new("multiverse") }
+  let(:node) { Chef::Node.new }
+  let(:events) { Chef::EventDispatch::Dispatcher.new }
+  let(:run_context) { Chef::RunContext.new(node, {}, events) }
+  let(:resource) { Chef::Resource::AptRepository.new("multiverse", run_context) }
 
   it "should create a new Chef::Resource::AptUpdate" do
     expect(resource).to be_a_kind_of(Chef::Resource)
@@ -35,4 +37,14 @@ describe Chef::Resource::AptRepository do
     expect(resource.distribution(nil)).to eql(nil)
     expect(resource.distribution).to eql(nil)
   end
+
+  it "should resolve to a Noop class when apt-get is not found" do
+    expect(Chef::Provider::AptRepository).to receive(:which).with("apt-get").and_return(false)
+    expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+  end
+
+  it "should resolve to a AptRepository class when apt-get is found" do
+    expect(Chef::Provider::AptRepository).to receive(:which).with("apt-get").and_return(true)
+    expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::AptRepository)
+  end
 end
diff --git a/spec/unit/resource/apt_update_spec.rb b/spec/unit/resource/apt_update_spec.rb
index 8015cb0..dd72b18 100644
--- a/spec/unit/resource/apt_update_spec.rb
+++ b/spec/unit/resource/apt_update_spec.rb
@@ -19,8 +19,10 @@
 require "spec_helper"
 
 describe Chef::Resource::AptUpdate do
-
-  let(:resource) { Chef::Resource::AptUpdate.new("update") }
+  let(:node) { Chef::Node.new }
+  let(:events) { Chef::EventDispatch::Dispatcher.new }
+  let(:run_context) { Chef::RunContext.new(node, {}, events) }
+  let(:resource) { Chef::Resource::AptUpdate.new("update", run_context) }
 
   it "should create a new Chef::Resource::AptUpdate" do
     expect(resource).to be_a_kind_of(Chef::Resource)
@@ -35,4 +37,14 @@ describe Chef::Resource::AptUpdate do
     resource.frequency(400)
     expect(resource.frequency).to eql(400)
   end
+
+  it "should resolve to a Noop class when apt-get is not found" do
+    expect(Chef::Provider::AptUpdate).to receive(:which).with("apt-get").and_return(false)
+    expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+  end
+
+  it "should resolve to a AptUpdate class when apt-get is found" do
+    expect(Chef::Provider::AptUpdate).to receive(:which).with("apt-get").and_return(true)
+    expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::AptUpdate)
+  end
 end
diff --git a/spec/unit/resource/conditional_spec.rb b/spec/unit/resource/conditional_spec.rb
index b34b420..e84b098 100644
--- a/spec/unit/resource/conditional_spec.rb
+++ b/spec/unit/resource/conditional_spec.rb
@@ -124,6 +124,29 @@ describe Chef::Resource::Conditional do
         expect(@conditional.continue?).to be_falsey
       end
     end
+
+    describe "after running a block that returns a string value" do
+      before do
+        @conditional = Chef::Resource::Conditional.only_if(@parent_resource) { "some command" }
+      end
+
+      it "logs a warning" do
+        expect(Chef::Log).to receive(:warn).with("only_if block for [] returned \"some command\", did you mean to run a command? If so use 'only_if \"some command\"' in your code.")
+        @conditional.evaluate
+      end
+    end
+
+    describe "after running a block that returns a string value on a sensitive resource" do
+      before do
+        @parent_resource.sensitive(true)
+        @conditional = Chef::Resource::Conditional.only_if(@parent_resource) { "some command" }
+      end
+
+      it "logs a warning" do
+        expect(Chef::Log).to receive(:warn).with("only_if block for [] returned a string, did you mean to run a command?")
+        @conditional.evaluate
+      end
+    end
   end
 
   describe "when created as a `not_if`" do
@@ -204,5 +227,28 @@ describe Chef::Resource::Conditional do
         expect(@conditional.continue?).to be_truthy
       end
     end
+
+    describe "after running a block that returns a string value" do
+      before do
+        @conditional = Chef::Resource::Conditional.not_if(@parent_resource) { "some command" }
+      end
+
+      it "logs a warning" do
+        expect(Chef::Log).to receive(:warn).with("not_if block for [] returned \"some command\", did you mean to run a command? If so use 'not_if \"some command\"' in your code.")
+        @conditional.evaluate
+      end
+    end
+
+    describe "after running a block that returns a string value on a sensitive resource" do
+      before do
+        @parent_resource.sensitive(true)
+        @conditional = Chef::Resource::Conditional.not_if(@parent_resource) { "some command" }
+      end
+
+      it "logs a warning" do
+        expect(Chef::Log).to receive(:warn).with("not_if block for [] returned a string, did you mean to run a command?")
+        @conditional.evaluate
+      end
+    end
   end
 end
diff --git a/spec/unit/resource/dsc_resource_spec.rb b/spec/unit/resource/dsc_resource_spec.rb
index 00e667c..b610c26 100644
--- a/spec/unit/resource/dsc_resource_spec.rb
+++ b/spec/unit/resource/dsc_resource_spec.rb
@@ -24,16 +24,16 @@ describe Chef::Resource::DscResource do
   let(:dsc_test_timeout) { 101 }
 
   context "when Powershell supports Dsc" do
-    let(:dsc_test_run_context) {
+    let(:dsc_test_run_context) do
       node = Chef::Node.new
       node.automatic[:languages][:powershell][:version] = "5.0.10018.0"
       empty_events = Chef::EventDispatch::Dispatcher.new
       Chef::RunContext.new(node, {}, empty_events)
-    }
+    end
 
-    let(:dsc_test_resource) {
+    let(:dsc_test_resource) do
       Chef::Resource::DscResource.new(dsc_test_resource_name, dsc_test_run_context)
-    }
+    end
 
     it "has a default action of `:run`" do
       expect(dsc_test_resource.action).to eq([:run])
@@ -71,16 +71,16 @@ describe Chef::Resource::DscResource do
       end
 
       it "raises a TypeError if property_name is not a symbol" do
-        expect {
+        expect do
           dsc_test_resource.property("Foo", dsc_test_property_value)
-        }.to raise_error(TypeError)
+        end.to raise_error(TypeError)
       end
 
       context "when using DelayedEvaluators" do
         it "allows setting a dsc property with a property name of type Symbol" do
-          dsc_test_resource.property(dsc_test_property_name, Chef::DelayedEvaluator.new {
+          dsc_test_resource.property(dsc_test_property_name, Chef::DelayedEvaluator.new do
             dsc_test_property_value
-          })
+          end)
           expect(dsc_test_resource.property(dsc_test_property_name)).to eq(dsc_test_property_value)
           expect(dsc_test_resource.properties[dsc_test_property_name]).to eq(dsc_test_property_value)
         end
diff --git a/spec/unit/resource/dsc_script_spec.rb b/spec/unit/resource/dsc_script_spec.rb
index 4892049..f0c81e4 100644
--- a/spec/unit/resource/dsc_script_spec.rb
+++ b/spec/unit/resource/dsc_script_spec.rb
@@ -22,15 +22,15 @@ describe Chef::Resource::DscScript do
   let(:dsc_test_resource_name) { "DSCTest" }
 
   context "when Powershell supports Dsc" do
-    let(:dsc_test_run_context) {
+    let(:dsc_test_run_context) do
       node = Chef::Node.new
       node.automatic[:languages][:powershell][:version] = "4.0"
       empty_events = Chef::EventDispatch::Dispatcher.new
       Chef::RunContext.new(node, {}, empty_events)
-    }
-    let(:dsc_test_resource) {
+    end
+    let(:dsc_test_resource) do
       Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context)
-    }
+    end
     let(:configuration_code) { 'echo "This is supposed to create a configuration document."' }
     let(:configuration_path) { "c:/myconfigs/formatc.ps1" }
     let(:configuration_name) { "formatme" }
diff --git a/spec/unit/resource/freebsd_package_spec.rb b/spec/unit/resource/freebsd_package_spec.rb
index 0842114..4edc3db 100644
--- a/spec/unit/resource/freebsd_package_spec.rb
+++ b/spec/unit/resource/freebsd_package_spec.rb
@@ -52,10 +52,10 @@ describe Chef::Resource::FreebsdPackage do
       end
     end
 
-    describe "if __Freebsd_version is greater than or equal to 1000017" do
+    describe "if freebsd_version is greater than or equal to 1000017" do
       it "should be Freebsd::Pkgng" do
-        [1000017, 1000018, 1000500, 1001001, 1100000].each do |__freebsd_version|
-          @node.automatic_attrs[:os_version] = __freebsd_version
+        [1000017, 1000018, 1000500, 1001001, 1100000].each do |freebsd_version|
+          @node.automatic_attrs[:os_version] = freebsd_version
           @resource.after_created
           expect(@resource.provider).to eq(Chef::Provider::Package::Freebsd::Pkgng)
         end
@@ -71,13 +71,13 @@ describe Chef::Resource::FreebsdPackage do
       end
     end
 
-    describe "if __Freebsd_version is less than 1000017 and pkgng not enabled" do
+    describe "if freebsd_version is less than 1000017 and pkgng not enabled" do
       it "should be Freebsd::Pkg" do
         pkg_enabled = OpenStruct.new(:stdout => "\n")
         allow(@resource).to receive(:shell_out!).with("make -V WITH_PKGNG", :env => nil).and_return(pkg_enabled)
 
-        [1000016, 1000000, 901503, 902506, 802511].each do |__freebsd_version|
-          @node.automatic_attrs[:os_version] = __freebsd_version
+        [1000016, 1000000, 901503, 902506, 802511].each do |freebsd_version|
+          @node.automatic_attrs[:os_version] = freebsd_version
           @resource.after_created
           expect(@resource.provider).to eq(Chef::Provider::Package::Freebsd::Pkg)
         end
diff --git a/spec/unit/resource/launchd_spec.rb b/spec/unit/resource/launchd_spec.rb
index 95febc4..98d21a8 100644
--- a/spec/unit/resource/launchd_spec.rb
+++ b/spec/unit/resource/launchd_spec.rb
@@ -4,10 +4,11 @@ require "spec_helper"
 
 describe Chef::Resource::Launchd do
   @launchd = Chef::Resource::Launchd.new("io.chef.chef-client")
-  let(:resource) { Chef::Resource::Launchd.new(
+  let(:resource) do
+    Chef::Resource::Launchd.new(
     "io.chef.chef-client",
     run_context
-  )}
+  ) end
 
   it "should create a new Chef::Resource::Launchd" do
     expect(resource).to be_a_kind_of(Chef::Resource)
diff --git a/spec/unit/resource/osx_profile_spec.rb b/spec/unit/resource/osx_profile_spec.rb
index 7bd504d..513e570 100644
--- a/spec/unit/resource/osx_profile_spec.rb
+++ b/spec/unit/resource/osx_profile_spec.rb
@@ -19,10 +19,11 @@
 require "spec_helper"
 
 describe Chef::Resource::OsxProfile do
-  let(:resource) { Chef::Resource::OsxProfile.new(
+  let(:resource) do
+    Chef::Resource::OsxProfile.new(
     "Test Profile Resource",
     run_context)
-  }
+  end
 
   it "should create a new Chef::Resource::OsxProfile" do
     expect(resource).to be_a_kind_of(Chef::Resource)
diff --git a/spec/unit/resource/remote_file_spec.rb b/spec/unit/resource/remote_file_spec.rb
index 718129a..5fac457 100644
--- a/spec/unit/resource/remote_file_spec.rb
+++ b/spec/unit/resource/remote_file_spec.rb
@@ -86,15 +86,15 @@ describe Chef::Resource::RemoteFile do
     end
 
     it "should only accept a single argument if a delayed evalutor is used" do
-      expect {
+      expect do
         @resource.source("http://opscode.com/", Chef::DelayedEvaluator.new { "http://opscode.com/" })
-      }.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
+      end.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
     end
 
     it "should only accept a single array item if a delayed evalutor is used" do
-      expect {
+      expect do
         @resource.source(["http://opscode.com/", Chef::DelayedEvaluator.new { "http://opscode.com/" }])
-      }.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
+      end.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
     end
 
     it "does not accept a non-URI as the source" do
@@ -102,10 +102,10 @@ describe Chef::Resource::RemoteFile do
     end
 
     it "does not accept a non-URI as the source when read from a delayed evaluator" do
-      expect {
+      expect do
         @resource.source(Chef::DelayedEvaluator.new { "not-a-uri" })
         @resource.source
-      }.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
+      end.to raise_error(Chef::Exceptions::InvalidRemoteFileURI)
     end
 
     it "should raise an exception when source is an empty array" do
diff --git a/spec/unit/resource/service_spec.rb b/spec/unit/resource/service_spec.rb
index e06f5bc..7aadc55 100644
--- a/spec/unit/resource/service_spec.rb
+++ b/spec/unit/resource/service_spec.rb
@@ -53,9 +53,9 @@ describe Chef::Resource::Service do
   end
 
   it "should not accept a regexp for the service pattern" do
-    expect {
+    expect do
       @resource.pattern /.*/
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should accept a string for the service start command" do
@@ -64,9 +64,9 @@ describe Chef::Resource::Service do
   end
 
   it "should not accept a regexp for the service start command" do
-    expect {
+    expect do
       @resource.start_command /.*/
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should accept a string for the service stop command" do
@@ -75,9 +75,9 @@ describe Chef::Resource::Service do
   end
 
   it "should not accept a regexp for the service stop command" do
-    expect {
+    expect do
       @resource.stop_command /.*/
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should accept a string for the service status command" do
@@ -86,9 +86,9 @@ describe Chef::Resource::Service do
   end
 
   it "should not accept a regexp for the service status command" do
-    expect {
+    expect do
       @resource.status_command /.*/
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should accept a string for the service restart command" do
@@ -97,9 +97,9 @@ describe Chef::Resource::Service do
   end
 
   it "should not accept a regexp for the service restart command" do
-    expect {
+    expect do
       @resource.restart_command /.*/
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should accept a string for the service reload command" do
@@ -108,9 +108,9 @@ describe Chef::Resource::Service do
   end
 
   it "should not accept a regexp for the service reload command" do
-    expect {
+    expect do
       @resource.reload_command /.*/
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should accept a string for the service init command" do
@@ -119,9 +119,9 @@ describe Chef::Resource::Service do
   end
 
   it "should not accept a regexp for the service init command" do
-    expect {
+    expect do
       @resource.init_command /.*/
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   %w{enabled running}.each do |attrib|
diff --git a/spec/unit/resource/user_spec.rb b/spec/unit/resource/user_spec.rb
index 9648ee1..138ffb1 100644
--- a/spec/unit/resource/user_spec.rb
+++ b/spec/unit/resource/user_spec.rb
@@ -29,7 +29,7 @@ describe Chef::Resource::User, "initialize" do
   end
 
   it "should set the resource_name to :user" do
-    expect(@resource.resource_name).to eql(:user)
+    expect(@resource.resource_name).to eql(:user_resource_abstract_base_class)
   end
 
   it "should set the username equal to the argument to initialize" do
diff --git a/spec/unit/resource/yum_repository_spec.rb b/spec/unit/resource/yum_repository_spec.rb
new file mode 100644
index 0000000..afd6c67
--- /dev/null
+++ b/spec/unit/resource/yum_repository_spec.rb
@@ -0,0 +1,49 @@
+#
+# Author:: Thom May (<thom at chef.io>)
+# Copyright:: Copyright (c) 2016 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "spec_helper"
+
+describe Chef::Resource::YumRepository do
+  let(:node) { Chef::Node.new }
+  let(:events) { Chef::EventDispatch::Dispatcher.new }
+  let(:run_context) { Chef::RunContext.new(node, {}, events) }
+  let(:resource) { Chef::Resource::YumRepository.new("multiverse", run_context) }
+
+  context "on linux", :linux_only do
+    it "should create a new Chef::Resource::YumRepository" do
+      expect(resource).to be_a_kind_of(Chef::Resource)
+      expect(resource).to be_a_kind_of(Chef::Resource::YumRepository)
+    end
+
+    it "should resolve to a Noop class when yum is not found" do
+      expect(Chef::Provider::YumRepository).to receive(:which).with("yum").and_return(false)
+      expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+    end
+
+    it "should resolve to a YumRepository class when yum is found" do
+      expect(Chef::Provider::YumRepository).to receive(:which).with("yum").and_return(true)
+      expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::YumRepository)
+    end
+  end
+
+  context "on windows", :windows_only do
+    it "should resolve to a NoOp provider" do
+      expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop)
+    end
+  end
+end
diff --git a/spec/unit/resource_collection_spec.rb b/spec/unit/resource_collection_spec.rb
index 3fec2d9..5feb348 100644
--- a/spec/unit/resource_collection_spec.rb
+++ b/spec/unit/resource_collection_spec.rb
@@ -98,11 +98,11 @@ describe Chef::ResourceCollection do
     it "should allow you to iterate over every resource in the collection" do
       load_up_resources
       results = Array.new
-      expect {
+      expect do
         rc.each do |r|
           results << r.name
         end
-      }.not_to raise_error
+      end.not_to raise_error
       results.each_index do |i|
         case i
         when 0
@@ -120,11 +120,11 @@ describe Chef::ResourceCollection do
     it "should allow you to iterate over every resource by index" do
       load_up_resources
       results = Array.new
-      expect {
+      expect do
         rc.each_index do |i|
           results << rc[i].name
         end
-      }.not_to raise_error
+      end.not_to raise_error
       results.each_index do |i|
         case i
         when 0
diff --git a/spec/unit/resource_definition_spec.rb b/spec/unit/resource_definition_spec.rb
index 45dfaff..cc19cc7 100644
--- a/spec/unit/resource_definition_spec.rb
+++ b/spec/unit/resource_definition_spec.rb
@@ -53,26 +53,26 @@ describe Chef::ResourceDefinition do
   end
 
   it "should accept a new definition with a symbol for a name" do
-    expect {
+    expect do
       defn.define :smoke do
       end
-    }.not_to raise_error
-    expect {
+    end.not_to raise_error
+    expect do
       defn.define "george washington" do
       end
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
     expect(defn.name).to eql(:smoke)
   end
 
   it "should accept a new definition with a hash" do
-    expect {
+    expect do
       defn.define :smoke, :cigar => "cuban", :cigarette => "marlboro" do
       end
-    }.not_to raise_error
+    end.not_to raise_error
   end
 
   it "should expose the prototype hash params in the params hash" do
-    defn.define :smoke, :cigar => "cuban", :cigarette => "marlboro" do; end
+    defn.define(:smoke, :cigar => "cuban", :cigarette => "marlboro") {}
     expect(defn.params[:cigar]).to eql("cuban")
     expect(defn.params[:cigarette]).to eql("marlboro")
   end
@@ -91,16 +91,16 @@ describe Chef::ResourceDefinition do
   end
 
   it "should raise an exception if prototype_params is not a hash" do
-    expect {
+    expect do
       defn.define :monkey, Array.new do
       end
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should raise an exception if define is called without a block" do
-    expect {
+    expect do
       defn.define :monkey
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "should load a description from a file" do
diff --git a/spec/unit/resource_reporter_spec.rb b/spec/unit/resource_reporter_spec.rb
index 0df3a16..51075a7 100644
--- a/spec/unit/resource_reporter_spec.rb
+++ b/spec/unit/resource_reporter_spec.rb
@@ -664,9 +664,9 @@ describe Chef::ResourceReporter do
 
       it "fails the run and prints an message about the error" do
         expect(Chef::Log).to receive(:error).with(/500/)
-        expect {
+        expect do
           @resource_reporter.run_started(@run_status)
-        }.to raise_error(Net::HTTPServerException)
+        end.to raise_error(Net::HTTPServerException)
       end
     end
 
@@ -749,9 +749,9 @@ describe Chef::ResourceReporter do
       it "should raise if an unkwown error happens" do
         allow(@rest_client).to receive(:raw_request).and_raise(Exception.new)
 
-        expect {
+        expect do
           @resource_reporter.post_reporting_data
-        }.to raise_error(Exception)
+        end.to raise_error(Exception)
       end
     end
   end
diff --git a/spec/unit/resource_resolver_spec.rb b/spec/unit/resource_resolver_spec.rb
index f3a20ab..d707ade 100644
--- a/spec/unit/resource_resolver_spec.rb
+++ b/spec/unit/resource_resolver_spec.rb
@@ -20,11 +20,11 @@ require "spec_helper"
 require "chef/resource_resolver"
 
 describe Chef::ResourceResolver do
-  it '#resolve' do
+  it "#resolve" do
     expect(described_class.resolve(:execute)).to eq(Chef::Resource::Execute)
   end
 
-  it '#list' do
+  it "#list" do
     expect(described_class.list(:package)).to_not be_empty
   end
 
@@ -33,19 +33,19 @@ describe Chef::ResourceResolver do
       described_class.new(Chef::Node.new, "execute")
     end
 
-    it '#resolve' do
+    it "#resolve" do
       expect(resolver.resolve).to eq Chef::Resource::Execute
     end
 
-    it '#list' do
+    it "#list" do
       expect(resolver.list).to eq [ Chef::Resource::Execute ]
     end
 
-    it '#provided_by? returns true when resource class is in the list' do
+    it "#provided_by? returns true when resource class is in the list" do
       expect(resolver.provided_by?(Chef::Resource::Execute)).to be_truthy
     end
 
-    it '#provided_by? returns false when resource class is not in the list' do
+    it "#provided_by? returns false when resource class is not in the list" do
       expect(resolver.provided_by?(Chef::Resource::File)).to be_falsey
     end
   end
diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb
index e88931f..68fc675 100644
--- a/spec/unit/resource_spec.rb
+++ b/spec/unit/resource_spec.rb
@@ -95,7 +95,7 @@ describe Chef::Resource do
   end
 
   describe "when an identity attribute has been declared" do
-    let(:file_resource) {
+    let(:file_resource) do
       file_resource_class = Class.new(Chef::Resource) do
         identity_attr :path
         attr_accessor :path
@@ -104,7 +104,7 @@ describe Chef::Resource do
       file_resource = file_resource_class.new("identity-attr-test")
       file_resource.path = "/tmp/foo.txt"
       file_resource
-    }
+    end
 
     it "gives the value of its identity attribute" do
       expect(file_resource.identity).to eq("/tmp/foo.txt")
@@ -140,7 +140,7 @@ describe Chef::Resource do
   end
 
   describe "when a set of state attributes has been declared" do
-    let(:file_resource) {
+    let(:file_resource) do
       file_resource_class = Class.new(Chef::Resource) do
 
         state_attrs :checksum, :owner, :group, :mode
@@ -157,7 +157,7 @@ describe Chef::Resource do
       file_resource.group = "wheel"
       file_resource.mode = "0644"
       file_resource
-    }
+    end
 
     it "describes its state" do
       resource_state = file_resource.state
@@ -169,15 +169,35 @@ describe Chef::Resource do
     end
   end
 
+  describe "#state_for_resource_reporter" do
+    context "when a property is marked as sensitive" do
+      it "suppresses the sensitive property's value" do
+        resource_class = Class.new(Chef::Resource) { property :foo, String, sensitive: true }
+        resource = resource_class.new("sensitive_property_tests")
+        resource.foo = "some value"
+        expect(resource.state_for_resource_reporter[:foo]).to eq("*sensitive value suppressed*")
+      end
+    end
+
+    context "when a property is not marked as sensitive" do
+      it "does not suppress the property's value" do
+        resource_class = Class.new(Chef::Resource) { property :foo, String }
+        resource = resource_class.new("sensitive_property_tests")
+        resource.foo = "some value"
+        expect(resource.state_for_resource_reporter[:foo]).to eq("some value")
+      end
+    end
+  end
+
   describe "load_from" do
-    let(:prior_resource) {
+    let(:prior_resource) do
       prior_resource = Chef::Resource.new("funk")
       prior_resource.supports(:funky => true)
       prior_resource.source_line
       prior_resource.allowed_actions << :funkytown
       prior_resource.action(:funkytown)
       prior_resource
-    }
+    end
     before(:each) do
       resource.allowed_actions << :funkytown
       run_context.resource_collection << prior_resource
@@ -236,9 +256,9 @@ describe Chef::Resource do
 
     it "should raise an exception if told to act in other than :delay or :immediate(ly)" do
       run_context.resource_collection << Chef::Resource::ZenMaster.new("coffee")
-      expect {
+      expect do
         resource.notifies :reload, run_context.resource_collection.find(:zen_master => "coffee"), :someday
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "should allow multiple notified resources appear in the actions hash" do
@@ -503,12 +523,12 @@ describe Chef::Resource do
   end
 
   describe "retries" do
-    let(:retriable_resource) {
+    let(:retriable_resource) do
       retriable_resource = Chef::Resource::Cat.new("precious", run_context)
       retriable_resource.provider = Chef::Provider::SnakeOil
       retriable_resource.action = :purr
       retriable_resource
-    }
+    end
 
     before do
       node.automatic_attrs[:platform] = "fubuntu"
@@ -563,11 +583,11 @@ describe Chef::Resource do
     end
 
     it "warns when setting provider_base" do
-      expect {
+      expect do
         class OverrideProviderBaseTest2 < Chef::Resource
           provider_base Chef::Provider::Package
         end
-      }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
+      end.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
     end
 
   end
@@ -607,11 +627,11 @@ describe Chef::Resource do
   end
 
   describe "when invoking its action" do
-    let(:resource) {
+    let(:resource) do
       resource = Chef::Resource.new("provided", run_context)
       resource.provider = Chef::Provider::SnakeOil
       resource
-    }
+    end
     before do
       node.automatic_attrs[:platform] = "fubuntu"
       node.automatic_attrs[:platform_version] = "10.04"
@@ -797,11 +817,11 @@ describe Chef::Resource do
   end
 
   describe "when resource action is :nothing" do
-    let(:resource1) {
+    let(:resource1) do
       resource1 = Chef::Resource::Cat.new("sugar", run_context)
       resource1.action = :nothing
       resource1
-    }
+    end
     before do
       node.automatic_attrs[:platform] = "fubuntu"
       node.automatic_attrs[:platform_version] = "10.04"
diff --git a/spec/unit/run_context/child_run_context_spec.rb b/spec/unit/run_context/child_run_context_spec.rb
index 13a035c..47a6c84 100644
--- a/spec/unit/run_context/child_run_context_spec.rb
+++ b/spec/unit/run_context/child_run_context_spec.rb
@@ -24,16 +24,16 @@ require "support/lib/library_load_order"
 describe Chef::RunContext::ChildRunContext do
   context "with a run context with stuff in it" do
     let(:chef_repo_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "run_context", "cookbooks")) }
-    let(:cookbook_collection) {
+    let(:cookbook_collection) do
       cl = Chef::CookbookLoader.new(chef_repo_path)
       cl.load_cookbooks
       Chef::CookbookCollection.new(cl)
-    }
-    let(:node) {
+    end
+    let(:node) do
       node = Chef::Node.new
       node.run_list << "test" << "test::one" << "test::two"
       node
-    }
+    end
     let(:events) { Chef::EventDispatch::Dispatcher.new }
     let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) }
 
diff --git a/spec/unit/run_context/cookbook_compiler_spec.rb b/spec/unit/run_context/cookbook_compiler_spec.rb
index 868bed4..feb3961 100644
--- a/spec/unit/run_context/cookbook_compiler_spec.rb
+++ b/spec/unit/run_context/cookbook_compiler_spec.rb
@@ -158,7 +158,49 @@ describe Chef::RunContext::CookbookCompiler do
   end
 
   describe "loading recipes" do
-    # Tests for this behavior are in RunContext's tests
+    # Additional tests for this behavior are in RunContext's tests
+
+    describe "event dispatch" do
+      let(:recipe) { "dependency1::default" }
+      let(:recipe_path) do
+        File.expand_path("../../../data/run_context/cookbooks/dependency1/recipes/default.rb", __FILE__).tap do |path|
+          path.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
+        end
+      end
+      before do
+        node.run_list(recipe)
+      end
+      subject { compiler.compile_recipes }
+
+      it "dispatches normally" do
+        allow(run_context).to receive(:load_recipe)
+        expect(events).to receive(:recipe_load_start).with(1)
+        expect(events).to receive(:recipe_file_loaded).with(recipe_path, "dependency1::default")
+        expect(events).to receive(:recipe_load_complete).with(no_args)
+        subject
+      end
+
+      it "dispatches when a recipe is not found" do
+        exc = Chef::Exceptions::RecipeNotFound.new
+        allow(run_context).to receive(:load_recipe).and_raise(exc)
+        expect(events).to receive(:recipe_load_start).with(1)
+        expect(events).to_not receive(:recipe_file_loaded)
+        expect(events).to receive(:recipe_not_found).with(exc)
+        expect(events).to_not receive(:recipe_load_complete)
+        expect { subject }.to raise_error(exc)
+      end
+
+      it "dispatches when a recipe has an error" do
+        exc = ArgumentError.new
+        allow(run_context).to receive(:load_recipe).and_raise(exc)
+        expect(events).to receive(:recipe_load_start).with(1)
+        expect(events).to_not receive(:recipe_file_loaded)
+        expect(events).to receive(:recipe_file_load_failed).with(recipe_path, exc, "dependency1::default")
+        expect(events).to_not receive(:recipe_load_complete)
+        expect { subject }.to raise_error(exc)
+      end
+    end
+
   end
 
   describe "listing cookbook order" do
diff --git a/spec/unit/run_context_spec.rb b/spec/unit/run_context_spec.rb
index 7b2a27e..f1c3072 100644
--- a/spec/unit/run_context_spec.rb
+++ b/spec/unit/run_context_spec.rb
@@ -23,16 +23,16 @@ require "support/lib/library_load_order"
 
 describe Chef::RunContext do
   let(:chef_repo_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "run_context", "cookbooks")) }
-  let(:cookbook_collection) {
+  let(:cookbook_collection) do
     cl = Chef::CookbookLoader.new(chef_repo_path)
     cl.load_cookbooks
     Chef::CookbookCollection.new(cl)
-  }
-  let(:node) {
+  end
+  let(:node) do
     node = Chef::Node.new
     node.run_list << "test" << "test::one" << "test::two"
     node
-  }
+  end
   let(:events) { Chef::EventDispatch::Dispatcher.new }
   let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) }
 
@@ -151,13 +151,13 @@ describe Chef::RunContext do
 
   describe "querying the contents of cookbooks" do
     let(:chef_repo_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks")) }
-    let(:node) {
+    let(:node) do
       node = Chef::Node.new
-      node.set[:platform] = "ubuntu"
-      node.set[:platform_version] = "13.04"
+      node.normal[:platform] = "ubuntu"
+      node.normal[:platform_version] = "13.04"
       node.name("testing")
       node
-    }
+    end
 
     it "queries whether a given cookbook has a specific template" do
       expect(run_context).to have_template_in_cookbook("openldap", "test.erb")
@@ -212,11 +212,11 @@ describe Chef::RunContext do
 
     shared_context "notifying resource is a subclass of Chef::Resource" do
       let(:declared_type) { :alpaca }
-      let(:notifying_resource) {
+      let(:notifying_resource) do
         r = Class.new(Chef::Resource).new("guinea pig")
         r.declared_type = declared_type
         r
-      }
+      end
 
       it "should be keyed off the resource declared key" do
         run_context.send(setter, notification)
diff --git a/spec/unit/run_list/run_list_expansion_spec.rb b/spec/unit/run_list/run_list_expansion_spec.rb
index e1af112..3a39bc7 100644
--- a/spec/unit/run_list/run_list_expansion_spec.rb
+++ b/spec/unit/run_list/run_list_expansion_spec.rb
@@ -104,8 +104,8 @@ describe Chef::RunList::RunListExpansion do
     end
 
     it "produces json tree upon tracing expansion" do
-      jsonRunList = @expansion.to_json
-      expect(jsonRunList).to eq(@json)
+      json_run_list = @expansion.to_json
+      expect(json_run_list).to eq(@json)
     end
 
     it "has the ordered list of recipes" do
diff --git a/spec/unit/runner_spec.rb b/spec/unit/runner_spec.rb
index f15f395..c1e10a7 100644
--- a/spec/unit/runner_spec.rb
+++ b/spec/unit/runner_spec.rb
@@ -392,12 +392,12 @@ Multiple failures occurred:
     it "should resolve resource references in notifications when resources are defined lazily" do
       first_resource.action = :nothing
 
-      lazy_resources = lambda {
+      lazy_resources = lambda do
         last_resource = Chef::Resource::Cat.new("peanut", run_context)
         run_context.resource_collection << last_resource
         last_resource.notifies(:purr, first_resource.to_s, :delayed)
         last_resource.action = :purr
-      }
+      end
       second_resource = Chef::Resource::RubyBlock.new("myblock", run_context)
       run_context.resource_collection << second_resource
       second_resource.block { lazy_resources.call }
diff --git a/spec/unit/search/query_spec.rb b/spec/unit/search/query_spec.rb
index 7f815f1..5166778 100644
--- a/spec/unit/search/query_spec.rb
+++ b/spec/unit/search/query_spec.rb
@@ -27,13 +27,13 @@ describe Chef::Search::Query do
     let(:query_string) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0" }
     let(:server_url) { "https://api.opscode.com/organizations/opscode/nodes" }
     let(:args) { { filter_key => filter_hash } }
-    let(:filter_hash) {
+    let(:filter_hash) do
       {
         "env" => [ "chef_environment" ],
         "ruby_plat" => %w{languages ruby platform},
       }
-    }
-    let(:response) {
+    end
+    let(:response) do
       {
         "rows" => [
           { "url" => "#{server_url}/my-name-is-node",
@@ -64,15 +64,15 @@ describe Chef::Search::Query do
         "start" => 0,
         "total" => 4,
       }
-    }
-    let(:response_rows) {
+    end
+    let(:response_rows) do
       [
         { "env" => "elysium", "ruby_plat" => "nudibranch" },
         { "env" => "hades", "ruby_plat" => "i386-mingw32" },
         { "env" => "elysium", "ruby_plat" => "centos" },
         { "env" => "moon", "ruby_plat" => "solaris2" },
       ]
-    }
+    end
   end
 
   before(:each) do
@@ -86,7 +86,8 @@ describe Chef::Search::Query do
     let(:query_string_with_rows) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0&rows=4" }
     let(:query_string_continue_with_rows) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=4&rows=4" }
 
-    let(:response) { {
+    let(:response) do
+      {
       "rows" => [
         { "name" => "my-name-is-node",
           "chef_environment" => "elysium",
@@ -147,28 +148,28 @@ describe Chef::Search::Query do
       ],
       "start" => 0,
       "total" => 4,
-    } }
+    } end
 
-    let(:big_response) {
+    let(:big_response) do
       r = response.dup
       r["total"] = 8
       r
-    }
+    end
 
-    let(:big_response_empty) {
+    let(:big_response_empty) do
       {
         "start" => 0,
         "total" => 8,
         "rows" => [],
       }
-    }
+    end
 
-    let(:big_response_end) {
+    let(:big_response_end) do
       r = response.dup
       r["start"] = 4
       r["total"] = 8
       r
-    }
+    end
 
     it "accepts a type as the first argument" do
       expect { query.search("node") }.not_to raise_error
diff --git a/spec/unit/shell/shell_session_spec.rb b/spec/unit/shell/shell_session_spec.rb
index 22f3dc6..259e609 100644
--- a/spec/unit/shell/shell_session_spec.rb
+++ b/spec/unit/shell/shell_session_spec.rb
@@ -158,7 +158,7 @@ describe Shell::SoloSession do
 
   it "keeps json attribs and passes them to the node for consumption" do
     @session.node_attributes = { "besnard_lakes" => "are_the_dark_horse" }
-    expect(@session.node.besnard_lakes).to eq("are_the_dark_horse")
+    expect(@session.node["besnard_lakes"]).to eq("are_the_dark_horse")
     #pending "1) keep attribs in an ivar 2) pass them to the node 3) feed them to the node on reset"
   end
 
diff --git a/spec/unit/user_v1_spec.rb b/spec/unit/user_v1_spec.rb
index 51dc3c9..16f0d01 100644
--- a/spec/unit/user_v1_spec.rb
+++ b/spec/unit/user_v1_spec.rb
@@ -332,7 +332,7 @@ describe Chef::UserV1 do
         @user.password "some_password"
       end
 
-      let(:payload) {
+      let(:payload) do
         {
           :username => "some_username",
           :display_name => "some_display_name",
@@ -342,7 +342,7 @@ describe Chef::UserV1 do
           :email => "some_email",
           :password => "some_password",
         }
-      }
+      end
 
       context "when server API V1 is valid on the Chef Server receiving the request" do
         context "when the user submits valid data" do
@@ -354,7 +354,7 @@ describe Chef::UserV1 do
       end
 
       context "when server API V1 is not valid on the Chef Server receiving the request" do
-        let(:payload) {
+        let(:payload) do
           {
             :username => "some_username",
             :display_name => "some_display_name",
@@ -365,7 +365,7 @@ describe Chef::UserV1 do
             :password => "some_password",
             :public_key => "some_public_key",
           }
-        }
+        end
 
         before do
           @user.public_key "some_public_key"
@@ -442,7 +442,7 @@ describe Chef::UserV1 do
     end # update
 
     describe "create" do
-      let(:payload) {
+      let(:payload) do
         {
           :username => "some_username",
           :display_name => "some_display_name",
@@ -451,7 +451,7 @@ describe Chef::UserV1 do
           :email => "some_email",
           :password => "some_password",
         }
-      }
+      end
       before do
         @user.username "some_username"
         @user.display_name "some_display_name"
@@ -506,11 +506,11 @@ describe Chef::UserV1 do
     # DEPRECATION
     # This can be removed after API V0 support is gone
     describe "reregister" do
-      let(:payload) {
+      let(:payload) do
         {
           "username" => "some_username",
         }
-      }
+      end
 
       before do
         @user.username "some_username"
diff --git a/spec/unit/util/dsc/configuration_generator_spec.rb b/spec/unit/util/dsc/configuration_generator_spec.rb
index 12f62de..cfa7a4e 100644
--- a/spec/unit/util/dsc/configuration_generator_spec.rb
+++ b/spec/unit/util/dsc/configuration_generator_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Util::DSC::ConfigurationGenerator do
     Chef::Util::DSC::ConfigurationGenerator.new(node, "tmp")
   end
 
-  describe '#validate_configuration_name!' do
+  describe "#validate_configuration_name!" do
     it "should not raise an error if a name contains all upper case letters" do
       conf_man.send(:validate_configuration_name!, "HELLO")
     end
@@ -40,9 +40,9 @@ describe Chef::Util::DSC::ConfigurationGenerator do
 
     %w{! @ # $ % ^ & * & * ( ) - = + \{ \} . ? < > \\ /}.each do |sym|
       it "raises an Argument error if it configuration name contains #{sym}" do
-        expect {
+        expect do
           conf_man.send(:validate_configuration_name!, "Hello#{sym}")
-        }.to raise_error(ArgumentError)
+        end.to raise_error(ArgumentError)
       end
     end
   end
@@ -57,15 +57,15 @@ describe Chef::Util::DSC::ConfigurationGenerator do
       end
 
       it "should raise an ArgumentError if you try to override outputpath" do
-        expect {
+        expect do
           conf_man.send(:get_merged_configuration_flags!, { "outputpath" => "a" }, "hello")
-        }.to raise_error(ArgumentError)
+        end.to raise_error(ArgumentError)
       end
 
       it "should be case insensitive for switches that are not allowed" do
-        expect {
+        expect do
           conf_man.send(:get_merged_configuration_flags!, { "OutputPath" => "a" }, "hello")
-        }.to raise_error(ArgumentError)
+        end.to raise_error(ArgumentError)
       end
 
       it "should be case insensitive to switches that are allowed" do
@@ -83,15 +83,15 @@ describe Chef::Util::DSC::ConfigurationGenerator do
       end
 
       it "should raise an ArgumentError if you try to override outputpath" do
-        expect {
+        expect do
           conf_man.send(:get_merged_configuration_flags!, { :outputpath => "a" }, "hello")
-        }.to raise_error(ArgumentError)
+        end.to raise_error(ArgumentError)
       end
 
       it "should be case insensitive for switches that are not allowed" do
-        expect {
+        expect do
           conf_man.send(:get_merged_configuration_flags!, { :OutputPath => "a" }, "hello")
-        }.to raise_error(ArgumentError)
+        end.to raise_error(ArgumentError)
       end
 
       it "should be case insensitive to switches that are allowed" do
@@ -121,7 +121,7 @@ describe Chef::Util::DSC::ConfigurationGenerator do
     #
   end
 
-  describe '#write_document_generation_script' do
+  describe "#write_document_generation_script" do
     let(:file_like_object) { double("file like object") }
 
     it "should write the input to a file" do
diff --git a/spec/unit/util/dsc/local_configuration_manager_spec.rb b/spec/unit/util/dsc/local_configuration_manager_spec.rb
index 15cf383..45fe8df 100644
--- a/spec/unit/util/dsc/local_configuration_manager_spec.rb
+++ b/spec/unit/util/dsc/local_configuration_manager_spec.rb
@@ -23,15 +23,17 @@ describe Chef::Util::DSC::LocalConfigurationManager do
 
   let(:lcm) { Chef::Util::DSC::LocalConfigurationManager.new(nil, "tmp") }
 
-  let(:normal_lcm_output) { <<-EOH
+  let(:normal_lcm_output) do
+    <<-EOH
 logtype: [machinename]: LCM:  [ Start  Set      ]
 logtype: [machinename]: LCM:  [ Start  Resource ] [name]
 logtype: [machinename]: LCM:  [ End    Resource ] [name]
 logtype: [machinename]: LCM:  [ End    Set      ]
 EOH
-  }
+  end
 
-  let(:no_whatif_lcm_output) { <<-EOH
+  let(:no_whatif_lcm_output) do
+    <<-EOH
 Start-DscConfiguration : A parameter cannot be found\r\n that matches parameter name 'whatif'.
 At line:1 char:123
 + run-somecommand -whatif
@@ -39,16 +41,17 @@ At line:1 char:123
     + CategoryInfo          : InvalidArgument: (:) [Start-DscConfiguration], ParameterBindingException
     + FullyQualifiedErrorId : NamedParameterNotFound,SomeCompany.SomeAssembly.Commands.RunSomeCommand
 EOH
-  }
+  end
 
-  let(:dsc_resource_import_failure_output) { <<-EOH
+  let(:dsc_resource_import_failure_output) do
+    <<-EOH
 PowerShell provider MSFT_xWebsite failed to execute Test-TargetResource functionality with error message: Please ensure that WebAdministration module is installed. + CategoryInfo : InvalidOperation: (:) [], CimException + FullyQualifiedErrorId : ProviderOperationExecutionFailure + PSComputerName : . PowerShell provider MSFT_xWebsite failed to execute Test-TargetResource functionality with error message: Please ensure that WebAdministration module is installed. + CategoryInfo : InvalidOpe [...]
 EOH
-  }
+  end
 
-  let(:lcm_status) {
+  let(:lcm_status) do
     double("LCM cmdlet status", :stderr => lcm_standard_error, :return_value => lcm_standard_output, :succeeded? => lcm_cmdlet_success)
-  }
+  end
 
   describe "test_configuration method invocation" do
     context "when interacting with the LCM using a PowerShell cmdlet" do
@@ -77,7 +80,7 @@ EOH
         let(:lcm_standard_error) { no_whatif_lcm_output }
         let(:lcm_cmdlet_success) { false }
 
-        it 'returns true when passed to #whatif_not_supported?' do
+        it "returns true when passed to #whatif_not_supported?" do
           expect(lcm.send(:whatif_not_supported?, no_whatif_lcm_output)).to be_truthy
         end
 
diff --git a/spec/unit/util/dsc/resource_store.rb b/spec/unit/util/dsc/resource_store.rb
index 84b3919..a864a2c 100644
--- a/spec/unit/util/dsc/resource_store.rb
+++ b/spec/unit/util/dsc/resource_store.rb
@@ -21,28 +21,30 @@ require "chef/util/dsc/resource_store"
 
 describe Chef::Util::DSC::ResourceStore do
   let(:resource_store) { Chef::Util::DSC::ResourceStore.new }
-  let(:resource_a) { {
+  let(:resource_a) do
+    {
     "ResourceType" => "AFoo",
     "Name" => "Foo",
     "Module" => { "Name" => "ModuleA" },
     }
-  }
+  end
 
-  let(:resource_b) { {
+  let(:resource_b) do
+    {
     "ResourceType" => "BFoo",
     "Name" => "Foo",
     "Module" => { "Name" => "ModuleB" },
     }
-  }
+  end
 
   context "when resources are not cached" do
-    context 'when calling #resources' do
+    context "when calling #resources" do
       it "returns an empty array" do
         expect(resource_store.resources).to eql([])
       end
     end
 
-    context 'when calling #find' do
+    context "when calling #find" do
       it "returns an empty list if it cannot find any matching resources" do
         expect(resource_store).to receive(:query_resource).and_return([])
         expect(resource_store.find("foo")).to eql([])
diff --git a/spec/unit/util/editor_spec.rb b/spec/unit/util/editor_spec.rb
index 7a0ec91..e53bc96 100644
--- a/spec/unit/util/editor_spec.rb
+++ b/spec/unit/util/editor_spec.rb
@@ -2,7 +2,7 @@ require "spec_helper"
 require "chef/util/editor"
 
 describe Chef::Util::Editor do
-  describe '#initialize' do
+  describe "#initialize" do
     it "takes an Enumerable of lines" do
       editor = described_class.new(File.open(__FILE__))
       expect(editor.lines).to be == IO.readlines(__FILE__)
@@ -18,7 +18,7 @@ describe Chef::Util::Editor do
   subject(:editor) { described_class.new(input_lines) }
   let(:input_lines) { %w{one two two three} }
 
-  describe '#append_line_after' do
+  describe "#append_line_after" do
     context "when there is no match" do
       subject(:execute) { editor.append_line_after("missing", "new") }
 
@@ -44,7 +44,7 @@ describe Chef::Util::Editor do
     end
   end
 
-  describe '#append_line_if_missing' do
+  describe "#append_line_if_missing" do
     context "when there is no match" do
       subject(:execute) { editor.append_line_if_missing("missing", "new") }
 
@@ -70,7 +70,7 @@ describe Chef::Util::Editor do
     end
   end
 
-  describe '#remove_lines' do
+  describe "#remove_lines" do
     context "when there is no match" do
       subject(:execute) { editor.remove_lines("missing") }
 
@@ -96,7 +96,7 @@ describe Chef::Util::Editor do
     end
   end
 
-  describe '#replace' do
+  describe "#replace" do
     context "when there is no match" do
       subject(:execute) { editor.replace("missing", "new") }
 
@@ -123,7 +123,7 @@ describe Chef::Util::Editor do
     end
   end
 
-  describe '#replace_lines' do
+  describe "#replace_lines" do
     context "when there is no match" do
       subject(:execute) { editor.replace_lines("missing", "new") }
 
diff --git a/spec/unit/util/powershell/cmdlet_spec.rb b/spec/unit/util/powershell/cmdlet_spec.rb
index 5c0e66a..800e4cc 100644
--- a/spec/unit/util/powershell/cmdlet_spec.rb
+++ b/spec/unit/util/powershell/cmdlet_spec.rb
@@ -25,7 +25,7 @@ describe Chef::Util::Powershell::Cmdlet do
     @cmdlet = Chef::Util::Powershell::Cmdlet.new(@node, "Some-Commandlet")
   end
 
-  describe '#validate_switch_name!' do
+  describe "#validate_switch_name!" do
     it "should not raise an error if a name contains all upper case letters" do
       @cmdlet.send(:validate_switch_name!, "HELLO")
     end
@@ -40,14 +40,14 @@ describe Chef::Util::Powershell::Cmdlet do
 
     %w{! @ # $ % ^ & * & * ( ) - = + \{ \} . ? < > \\ /}.each do |sym|
       it "raises an Argument error if it configuration name contains #{sym}" do
-        expect {
+        expect do
           @cmdlet.send(:validate_switch_name!, "Hello#{sym}")
-        }.to raise_error(ArgumentError)
+        end.to raise_error(ArgumentError)
       end
     end
   end
 
-  describe '#escape_parameter_value' do
+  describe "#escape_parameter_value" do
     # Is this list really complete?
     %w{` " # '}.each do |c|
       it "escapse #{c}" do
@@ -60,23 +60,23 @@ describe Chef::Util::Powershell::Cmdlet do
     end
   end
 
-  describe '#escape_string_parameter_value' do
+  describe "#escape_string_parameter_value" do
     it "surrounds a string with ''" do
       expect(@cmdlet.send(:escape_string_parameter_value, "stuff")).to eql("'stuff'")
     end
   end
 
-  describe '#command_switches_string' do
+  describe "#command_switches_string" do
     it "raises an ArgumentError if the key is not a symbol" do
-      expect {
+      expect do
         @cmdlet.send(:command_switches_string, { "foo" => "bar" })
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "does not allow invalid switch names" do
-      expect {
+      expect do
         @cmdlet.send(:command_switches_string, { :foo! => "bar" })
-      }.to raise_error(ArgumentError)
+      end.to raise_error(ArgumentError)
     end
 
     it "ignores switches with a false value" do
diff --git a/tasks/bin/bundle-platform b/tasks/bin/bundle-platform
index 4bd659d..aa8443e 100755
--- a/tasks/bin/bundle-platform
+++ b/tasks/bin/bundle-platform
@@ -1,4 +1,5 @@
 #!/usr/bin/env ruby
+require_relative "bundler_patch"
 
 platforms = ARGV.shift
 platforms = platforms.split(" ").map { |p| Gem::Platform.new(p) }
diff --git a/tasks/bin/bundler_patch.rb b/tasks/bin/bundler_patch.rb
new file mode 100644
index 0000000..5665e44
--- /dev/null
+++ b/tasks/bin/bundler_patch.rb
@@ -0,0 +1,27 @@
+# This is a temporary monkey patch to address https://github.com/bundler/bundler/issues/4896
+# the heart of the fix is line #18 with the addition of:
+# && (possibility.activated - existing_node.payload.activated).empty?
+# This ensures we do not mis linux platform gems in some scenarios like ffi in kitchen-test.
+# There is a permanent fix to bundler (See https://github.com/bundler/bundler/pull/4836) which
+# is due to ship in v1.14. Once we adopt that version, we can remove this file
+
+require "bundler"
+require "bundler/vendor/molinillo/lib/molinillo/resolution"
+
+module Bundler::Molinillo
+  class Resolver
+    # A specific resolution from a given {Resolver}
+    class Resolution
+      def attempt_to_activate
+        debug(depth) { "Attempting to activate " + possibility.to_s }
+        existing_node = activated.vertex_named(name)
+        if existing_node.payload && (possibility.activated - existing_node.payload.activated).empty?
+          debug(depth) { "Found existing spec (#{existing_node.payload})" }
+          attempt_to_activate_existing_spec(existing_node)
+        else
+          attempt_to_activate_new_spec
+        end
+      end
+    end
+  end
+end
diff --git a/tasks/bin/gem-version-diff b/tasks/bin/gem-version-diff
new file mode 100755
index 0000000..617402d
--- /dev/null
+++ b/tasks/bin/gem-version-diff
@@ -0,0 +1,37 @@
+#!/usr/bin/env ruby
+
+require_relative "../../version_policy"
+
+old_version, new_version = ARGV[0..1]
+
+require "set"
+ENV["BUNDLE_WITHOUT"] = INSTALL_WITHOUT_GROUPS.join(":")
+relevant_gems = Set.new
+`bundle list`.each_line do |line|
+  next unless line =~ /^  \* (\S+)/
+  relevant_gems.add($1)
+end
+
+old_gems = {}
+old_file = `git show #{old_version}:Gemfile.lock`
+old_file.each_line do |line|
+  next unless line =~ /^    (\S+) \(([^\)]+)\)$/
+  next unless relevant_gems.include?($1)
+  old_gems[$1] = $2
+end
+
+new_gems = {}
+new_file = `git show #{new_version}:Gemfile.lock`
+new_file.each_line do |line|
+  next unless line =~ /^    (\S+) \(([^\)]+)\)$/
+  next unless relevant_gems.include?($1)
+  new_gems[$1] = $2
+end
+
+modified_gems = (old_gems.keys & new_gems.keys).sort.select { |name| new_gems[name] != old_gems[name] }.map { |name| "#{name} - #{new_gems[name]} (was #{old_gems[name]})" }
+removed_gems = (old_gems.keys - new_gems.keys).sort.map { |name| "#{name} - #{old_gems[name]}" }
+added_gems = (new_gems.keys - old_gems.keys).sort.map { |name| "#{name} - #{new_gems[name]}" }
+
+puts "MODIFIED:\n#{modified_gems.join("\n")}" if modified_gems.any?
+puts "\nADDED:\n#{added_gems.join("\n")}" if added_gems.any?
+puts "\nREMOVED:\n#{removed_gems.join("\n")}" if removed_gems.any?
diff --git a/tasks/bundle.rb b/tasks/bundle.rb
index 059b926..0176fe2 100644
--- a/tasks/bundle.rb
+++ b/tasks/bundle.rb
@@ -30,9 +30,9 @@ namespace :bundle do
       puts "-------------------------------------------------------------------"
       puts "Updating Gemfile.lock ..."
       puts "-------------------------------------------------------------------"
-      bundle "install #{args}", delete_gemfile_lock: true
+      bundle "update #{args}"
       platforms.each do |platform|
-        bundle "lock", platform: platform
+        bundle "update #{args}", platform: platform
       end
     end
   end
diff --git a/tasks/cbgb.rb b/tasks/cbgb.rb
index 9aa6700..70ca103 100644
--- a/tasks/cbgb.rb
+++ b/tasks/cbgb.rb
@@ -44,9 +44,9 @@ begin
       out << cbgb(cbgb["corporations"], cbgb["Org"]["Corporate-Contributors"]["governers"]) + "\n\n"
       out << "### " + cbgb["Org"]["Lieutenants"]["title"] + "\n\n"
       out << cbgb(cbgb["people"], cbgb["Org"]["Lieutenants"]["governers"]) + "\n\n"
-      File.open(CBGB_TARGET, "w") { |fn|
+      File.open(CBGB_TARGET, "w") do |fn|
         fn.write out
-      }
+      end
     end
   end
 
diff --git a/tasks/dependencies.rb b/tasks/dependencies.rb
index eb4bb1f..0b216f8 100644
--- a/tasks/dependencies.rb
+++ b/tasks/dependencies.rb
@@ -85,13 +85,11 @@ namespace :dependencies do
                                                      other_platforms: false, leave_frozen: false
   gemfile_lock_task :update_kitchen_tests_gemfile_lock, dirs: %w{
     kitchen-tests
-    kitchen-tests/test/integration/webapp/serverspec
   }
   berksfile_lock_task :update_kitchen_tests_berksfile_lock, dirs: %w{
     kitchen-tests
     kitchen-tests/cookbooks/audit_test
   }
-  # kitchen-tests/cookbooks/webapp isn't solving right now ....
 
   desc "Update omnibus overrides, including versions in version_policy.rb and latest version of gems: #{OMNIBUS_RUBYGEMS_AT_LATEST_VERSION.keys}."
   task :update_omnibus_overrides do |t, rake_args|
diff --git a/tasks/maintainers.rb b/tasks/maintainers.rb
index 9174285..e13d472 100644
--- a/tasks/maintainers.rb
+++ b/tasks/maintainers.rb
@@ -46,9 +46,9 @@ begin
       out << format_person(source["Org"]["Lead"]["person"]) + "\n\n"
 
       out << format_components(source["Org"]["Components"])
-      File.open(TARGET, "w") { |fn|
+      File.open(TARGET, "w") do |fn|
         fn.write out
-      }
+      end
     end
 
     desc "Synchronize GitHub teams"
diff --git a/version_policy.rb b/version_policy.rb
index 8d45001..bfe08e9 100644
--- a/version_policy.rb
+++ b/version_policy.rb
@@ -20,19 +20,20 @@ OMNIBUS_OVERRIDES = {
   # Lower level library pins
   ## according to comment in omnibus-sw, latest versions don't work on solaris
   # https://github.com/chef/omnibus-software/blob/aefb7e79d29ca746c3f843673ef5e317fa3cba54/config/software/libtool.rb#L23
+  :bundler => "1.12.5", # until we figure out how to work with 1.13.0
   "libffi" => "3.2.1",
   "libiconv" => "1.14",
   "liblzma" => "5.2.2",
   ## according to comment in omnibus-sw, the very latest versions don't work on solaris
   # https://github.com/chef/omnibus-software/blob/aefb7e79d29ca746c3f843673ef5e317fa3cba54/config/software/libtool.rb#L23
   "libtool" => "2.4.2",
-  "libxml2" => "2.9.3",
-  "libxslt" => "1.1.28",
+  "libxml2" => "2.9.4",
+  "libxslt" => "1.1.29",
   "libyaml" => "0.1.6",
   "makedepend" => "1.0.5",
   "ncurses" => "5.9",
   "pkg-config-lite" => "0.28-1",
-  "ruby" => "2.1.8",
+  "ruby" => "2.3.1",
   # Leave dev-kit pinned to 4.5 on 32-bit, because 4.7 is 20MB larger and we don't want
   # to unnecessarily make the client any fatter. (Since it's different between
   # 32 and 64, we have to do it in the project file still.)
@@ -44,7 +45,7 @@ OMNIBUS_OVERRIDES = {
 
   ## These can float as they are frequently updated in a way that works for us
   #override "cacerts" =>"???",
-  #override "openssl" =>"???",
+  "openssl" => "1.0.2h",
 }
 
 #
@@ -56,7 +57,7 @@ OMNIBUS_OVERRIDES = {
 #
 OMNIBUS_RUBYGEMS_AT_LATEST_VERSION = {
   rubygems: "rubygems-update",
-  bundler: "bundler",
+  # bundler: "bundler", # until we get working with 1.13.0
 }
 
 #
@@ -74,14 +75,13 @@ OMNIBUS_RUBYGEMS_AT_LATEST_VERSION = {
 # stove - halite pins to ~> 3.2 in 1.2.1
 # rubocop - chef-style pins to 0.39.0 in 0.3.1
 #
-ACCEPTABLE_OUTDATED_GEMS = %w{
-  gherkin
-  jwt
-  mini_portile2
-  slop
-  stove
-  rubocop
-}
+ACCEPTABLE_OUTDATED_GEMS = [
+  "json",       # aws-sdk-v1 pins this because Ruby 2.0; chef-provisioning fix to abandon v1 TBD
+  "rubocop",    # chefstyle pins this, will often be somewhat behind
+  "slop",       # expected to disappear with pry 0.11
+  "typhoeus",   # Until the travis gem updates to 1.0.
+  "cucumber-core", # Until cucumber 2.0
+]
 
 #
 # Some gems are part of our bundle (must be installed) but not important

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/chef.git



More information about the Pkg-ruby-extras-commits mailing list