summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Williams <nath.e.will@gmail.com>2015-10-23 22:19:25 -0700
committerNathan Williams <nath.e.will@gmail.com>2015-10-23 22:19:25 -0700
commita58c59460aa6b9cfe0824dabaf6cac0c445d0288 (patch)
treedaf8cfecdc01e45dd19bd888974b6a75913401c5
parente6b2a07634f8eef374e1edb84387d69bca3044c1 (diff)
parent8bf1304da739d4be94edb101ad9e46c96b1d4ccd (diff)
downloadchef-a58c59460aa6b9cfe0824dabaf6cac0c445d0288.tar.gz
re-sync with master
-rw-r--r--.travis.yml36
-rw-r--r--CHANGELOG.md53
-rw-r--r--CONTRIBUTING.md46
-rw-r--r--DOC_CHANGES.md63
-rw-r--r--Gemfile31
-rw-r--r--MAINTAINERS.md42
-rw-r--r--MAINTAINERS.toml38
-rw-r--r--RELEASE_NOTES.md7
-rw-r--r--VERSION2
-rw-r--r--appveyor.yml10
-rwxr-xr-xbin/chef-service-manager2
-rw-r--r--chef-config/VERSION2
-rw-r--r--chef-config/chef-config.gemspec2
-rw-r--r--chef-config/lib/chef-config/config.rb43
-rw-r--r--chef-config/lib/chef-config/version.rb2
-rw-r--r--chef-config/spec/unit/config_spec.rb36
-rw-r--r--chef-windows.gemspec3
-rw-r--r--chef.gemspec4
-rw-r--r--distro/common/html/knife_cookbook_site.html36
-rw-r--r--distro/common/man/man1/knife-cookbook-site.122
-rw-r--r--external_tests/chef-rewind.gemfile5
-rw-r--r--external_tests/chef-sugar.gemfile6
-rw-r--r--external_tests/chefspec.gemfile7
-rw-r--r--external_tests/foodcritic.gemfile9
-rw-r--r--external_tests/halite.gemfile8
-rw-r--r--external_tests/poise.gemfile7
-rw-r--r--lib/chef/application.rb2
-rw-r--r--lib/chef/application/apply.rb10
-rw-r--r--lib/chef/application/client.rb18
-rw-r--r--lib/chef/application/knife.rb4
-rw-r--r--lib/chef/application/solo.rb8
-rw-r--r--lib/chef/application/windows_service.rb6
-rw-r--r--lib/chef/chef_class.rb39
-rw-r--r--lib/chef/chef_fs/data_handler/client_data_handler.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_server_root_dir.rb3
-rw-r--r--lib/chef/chef_fs/file_system/file_system_entry.rb2
-rw-r--r--lib/chef/chef_fs/file_system/organization_members_entry.rb4
-rw-r--r--lib/chef/client.rb31
-rw-r--r--lib/chef/config.rb3
-rw-r--r--lib/chef/cookbook/cookbook_version_loader.rb2
-rw-r--r--lib/chef/cookbook/remote_file_vendor.rb2
-rw-r--r--lib/chef/cookbook_site_streaming_uploader.rb2
-rw-r--r--lib/chef/cookbook_version.rb6
-rw-r--r--lib/chef/data_bag.rb2
-rw-r--r--lib/chef/data_bag_item.rb2
-rw-r--r--lib/chef/deprecation/mixin/template.rb3
-rw-r--r--lib/chef/deprecation/provider/cookbook_file.rb2
-rw-r--r--lib/chef/deprecation/provider/file.rb2
-rw-r--r--lib/chef/deprecation/provider/remote_directory.rb52
-rw-r--r--lib/chef/deprecation/provider/remote_file.rb3
-rw-r--r--lib/chef/deprecation/provider/template.rb2
-rw-r--r--lib/chef/deprecation/warnings.rb7
-rw-r--r--lib/chef/dsl/reboot_pending.rb3
-rw-r--r--lib/chef/dsl/recipe.rb5
-rw-r--r--lib/chef/dsl/resources.rb4
-rw-r--r--lib/chef/event_dispatch/base.rb18
-rw-r--r--lib/chef/event_dispatch/dispatcher.rb35
-rw-r--r--lib/chef/exceptions.rb4
-rw-r--r--lib/chef/file_access_control/unix.rb24
-rw-r--r--lib/chef/file_content_management/deploy/cp.rb4
-rw-r--r--lib/chef/file_content_management/deploy/mv_unix.rb8
-rw-r--r--lib/chef/file_content_management/deploy/mv_windows.rb2
-rw-r--r--lib/chef/file_content_management/tempfile.rb2
-rw-r--r--lib/chef/formatters/base.rb3
-rw-r--r--lib/chef/formatters/doc.rb52
-rw-r--r--lib/chef/formatters/error_inspectors/compile_error_inspector.rb34
-rw-r--r--lib/chef/http/decompressor.rb4
-rw-r--r--lib/chef/json_compat.rb1
-rw-r--r--lib/chef/knife/bootstrap.rb41
-rw-r--r--lib/chef/knife/bootstrap/client_builder.rb15
-rw-r--r--lib/chef/knife/bootstrap/templates/README.md7
-rw-r--r--lib/chef/knife/cookbook_create.rb2
-rw-r--r--lib/chef/knife/cookbook_site_download.rb2
-rw-r--r--lib/chef/knife/cookbook_site_install.rb2
-rw-r--r--lib/chef/knife/cookbook_site_share.rb12
-rw-r--r--lib/chef/knife/cookbook_site_unshare.rb4
-rw-r--r--lib/chef/knife/core/bootstrap_context.rb16
-rw-r--r--lib/chef/knife/core/node_presenter.rb25
-rw-r--r--lib/chef/knife/core/subcommand_loader.rb6
-rw-r--r--lib/chef/knife/node_run_list_remove.rb13
-rw-r--r--lib/chef/knife/search.rb6
-rw-r--r--lib/chef/knife/ssh.rb84
-rw-r--r--lib/chef/local_mode.rb5
-rw-r--r--lib/chef/log.rb6
-rw-r--r--lib/chef/mixin/deprecation.rb16
-rw-r--r--lib/chef/mixin/properties.rb302
-rw-r--r--lib/chef/mixin/template.rb1
-rw-r--r--lib/chef/mixin/which.rb2
-rw-r--r--lib/chef/mixin/wide_string.rb72
-rw-r--r--lib/chef/mixin/windows_env_helper.rb5
-rw-r--r--lib/chef/monkey_patches/webrick-utils.rb51
-rw-r--r--lib/chef/monkey_patches/win32/registry.rb72
-rw-r--r--lib/chef/node.rb102
-rw-r--r--lib/chef/node_map.rb4
-rw-r--r--lib/chef/platform/query_helpers.rb45
-rw-r--r--lib/chef/platform/service_helpers.rb52
-rw-r--r--lib/chef/policy_builder.rb9
-rw-r--r--lib/chef/policy_builder/dynamic.rb186
-rw-r--r--lib/chef/policy_builder/expand_node_object.rb46
-rw-r--r--lib/chef/policy_builder/policyfile.rb173
-rw-r--r--lib/chef/property.rb47
-rw-r--r--lib/chef/provider.rb4
-rw-r--r--lib/chef/provider/deploy.rb4
-rw-r--r--lib/chef/provider/dsc_resource.rb14
-rw-r--r--lib/chef/provider/execute.rb2
-rw-r--r--lib/chef/provider/lwrp_base.rb1
-rw-r--r--lib/chef/provider/package/openbsd.rb2
-rw-r--r--lib/chef/provider/package/rpm.rb4
-rw-r--r--lib/chef/provider/package/windows/msi.rb4
-rw-r--r--lib/chef/provider/package/yum.rb2
-rw-r--r--lib/chef/provider/powershell_script.rb26
-rw-r--r--lib/chef/provider/remote_directory.rb292
-rw-r--r--lib/chef/provider/remote_file/http.rb2
-rw-r--r--lib/chef/provider/service/redhat.rb6
-rw-r--r--lib/chef/provider/service/solaris.rb60
-rw-r--r--lib/chef/provider/subversion.rb20
-rw-r--r--lib/chef/provider/template/content.rb24
-rw-r--r--lib/chef/provider/user.rb2
-rw-r--r--lib/chef/provider/user/dscl.rb7
-rw-r--r--lib/chef/provider/user/solaris.rb36
-rw-r--r--lib/chef/provider/user/windows.rb8
-rw-r--r--lib/chef/provider_resolver.rb4
-rw-r--r--lib/chef/resource.rb392
-rw-r--r--lib/chef/resource/action_class.rb (renamed from lib/chef/resource/action_provider.rb)16
-rw-r--r--lib/chef/resource/chef_gem.rb6
-rw-r--r--lib/chef/resource/execute.rb2
-rw-r--r--lib/chef/resource/file/verification.rb2
-rw-r--r--lib/chef/resource/lwrp_base.rb12
-rw-r--r--lib/chef/resource/script.rb2
-rw-r--r--lib/chef/resource/subversion.rb5
-rw-r--r--lib/chef/resource/windows_script.rb8
-rw-r--r--lib/chef/resource_reporter.rb6
-rw-r--r--lib/chef/resource_resolver.rb6
-rw-r--r--lib/chef/rest.rb2
-rw-r--r--lib/chef/run_context.rb18
-rw-r--r--lib/chef/run_list/run_list_expansion.rb47
-rw-r--r--lib/chef/run_list/versioned_recipe_list.rb15
-rw-r--r--lib/chef/run_lock.rb51
-rw-r--r--lib/chef/search/query.rb18
-rw-r--r--lib/chef/util/diff.rb4
-rw-r--r--lib/chef/util/windows.rb32
-rw-r--r--lib/chef/util/windows/net_use.rb106
-rw-r--r--lib/chef/util/windows/net_user.rb1
-rw-r--r--lib/chef/version.rb2
-rw-r--r--lib/chef/win32/api/file.rb1
-rw-r--r--lib/chef/win32/api/net.rb161
-rw-r--r--lib/chef/win32/api/registry.rb51
-rw-r--r--lib/chef/win32/api/unicode.rb43
-rw-r--r--lib/chef/win32/crypto.rb3
-rw-r--r--lib/chef/win32/file.rb9
-rw-r--r--lib/chef/win32/mutex.rb5
-rw-r--r--lib/chef/win32/net.rb57
-rw-r--r--lib/chef/win32/registry.rb53
-rw-r--r--lib/chef/win32/security.rb2
-rw-r--r--lib/chef/win32/security/token.rb2
-rw-r--r--lib/chef/win32/unicode.rb9
-rw-r--r--pedant.gemfile7
-rw-r--r--spec/data/cookbooks/openldap/templates/default/nested_openldap_partials.erb1
-rw-r--r--spec/data/cookbooks/openldap/templates/default/nested_partial.erb1
-rw-r--r--spec/functional/dsl/reboot_pending_spec.rb76
-rw-r--r--spec/functional/knife/cookbook_delete_spec.rb24
-rw-r--r--spec/functional/knife/ssh_spec.rb16
-rw-r--r--spec/functional/resource/deploy_revision_spec.rb2
-rw-r--r--spec/functional/resource/dsc_resource_spec.rb2
-rw-r--r--spec/functional/resource/powershell_script_spec.rb77
-rw-r--r--spec/functional/resource/user/windows_spec.rb8
-rw-r--r--spec/functional/resource/windows_service_spec.rb2
-rw-r--r--spec/functional/run_lock_spec.rb557
-rw-r--r--spec/functional/win32/registry_spec.rb (renamed from spec/functional/win32/registry_helper_spec.rb)27
-rw-r--r--spec/integration/client/client_spec.rb68
-rw-r--r--spec/integration/knife/download_spec.rb13
-rw-r--r--spec/integration/knife/list_spec.rb8
-rw-r--r--spec/integration/knife/upload_spec.rb2
-rw-r--r--spec/integration/recipes/remote_directory.rb74
-rw-r--r--spec/integration/recipes/resource_action_spec.rb31
-rw-r--r--spec/integration/recipes/resource_converge_if_changed_spec.rb2
-rw-r--r--spec/integration/recipes/resource_load_spec.rb24
-rw-r--r--spec/spec_helper.rb6
-rw-r--r--spec/support/platform_helpers.rb5
-rw-r--r--spec/support/shared/context/win32.rb (renamed from lib/chef/mixin/wstring.rb)27
-rw-r--r--spec/support/shared/functional/windows_script.rb89
-rw-r--r--spec/support/shared/unit/mock_shellout.rb46
-rw-r--r--spec/support/shared/unit/provider/file.rb14
-rw-r--r--spec/unit/application/client_spec.rb19
-rw-r--r--spec/unit/application/knife_spec.rb4
-rw-r--r--spec/unit/chef_class_spec.rb4
-rw-r--r--spec/unit/client_spec.rb7
-rw-r--r--spec/unit/cookbook/syntax_check_spec.rb2
-rw-r--r--spec/unit/deprecation_spec.rb9
-rw-r--r--spec/unit/dsl/reboot_pending_spec.rb16
-rw-r--r--spec/unit/event_dispatch/dispatcher_spec.rb47
-rw-r--r--spec/unit/event_dispatch/dsl_spec.rb4
-rw-r--r--spec/unit/formatters/doc_spec.rb26
-rw-r--r--spec/unit/http_spec.rb2
-rw-r--r--spec/unit/knife/bootstrap/client_builder_spec.rb27
-rw-r--r--spec/unit/knife/bootstrap_spec.rb58
-rw-r--r--spec/unit/knife/cookbook_site_share_spec.rb6
-rw-r--r--spec/unit/knife/core/bootstrap_context_spec.rb25
-rw-r--r--spec/unit/knife/node_run_list_remove_spec.rb17
-rw-r--r--spec/unit/lwrp_spec.rb2
-rw-r--r--spec/unit/mixin/enforce_ownership_and_permissions_spec.rb20
-rw-r--r--spec/unit/mixin/properties_spec.rb97
-rw-r--r--spec/unit/mixin/template_spec.rb6
-rw-r--r--spec/unit/node_spec.rb213
-rw-r--r--spec/unit/platform/query_helpers_spec.rb149
-rw-r--r--spec/unit/policy_builder/dynamic_spec.rb275
-rw-r--r--spec/unit/policy_builder/expand_node_object_spec.rb75
-rw-r--r--spec/unit/policy_builder/policyfile_spec.rb306
-rw-r--r--spec/unit/property_spec.rb186
-rw-r--r--spec/unit/provider/deploy_spec.rb10
-rw-r--r--spec/unit/provider/dsc_resource_spec.rb13
-rw-r--r--spec/unit/provider/package/rpm_spec.rb19
-rw-r--r--spec/unit/provider/powershell_script_spec.rb78
-rw-r--r--spec/unit/provider/remote_directory_spec.rb3
-rw-r--r--spec/unit/provider/service/solaris_smf_service_spec.rb149
-rw-r--r--spec/unit/provider/service/windows_spec.rb411
-rw-r--r--spec/unit/provider/subversion_spec.rb81
-rw-r--r--spec/unit/provider/template/content_spec.rb54
-rw-r--r--spec/unit/provider/user/dscl_spec.rb7
-rw-r--r--spec/unit/provider/user/solaris_spec.rb75
-rw-r--r--spec/unit/provider/user/windows_spec.rb4
-rw-r--r--spec/unit/provider/user_spec.rb9
-rw-r--r--spec/unit/provider_resolver_spec.rb1357
-rw-r--r--spec/unit/registry_helper_spec.rb390
-rw-r--r--spec/unit/resource/file/verification_spec.rb2
-rw-r--r--spec/unit/resource/powershell_script_spec.rb30
-rw-r--r--spec/unit/resource/subversion_spec.rb4
-rw-r--r--spec/unit/resource_reporter_spec.rb7
-rw-r--r--spec/unit/run_list/run_list_expansion_spec.rb21
-rw-r--r--spec/unit/run_list/versioned_recipe_list_spec.rb5
-rw-r--r--spec/unit/search/query_spec.rb20
-rw-r--r--spec/unit/win32/registry_spec.rb394
-rw-r--r--spec/unit/windows_service_spec.rb12
-rw-r--r--tasks/external_tests.rb70
-rw-r--r--tasks/maintainers.rb169
235 files changed, 7136 insertions, 3119 deletions
diff --git a/.travis.yml b/.travis.yml
index d7ad317e28..9f253df6c4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,11 +26,41 @@ matrix:
include:
- rvm: 2.1
- rvm: 2.2
- - rvm: 2.1
+ - rvm: 2.2
gemfile: pedant.gemfile
script: bundle exec rake pedant
- ### START TEST KITCHEN ONLY ###
- - rvm: 2.1
+ - rvm: 2.2
+ env: "GEMFILE_MOD=\"gem 'cheffish', github: 'chef/cheffish'\""
+ script: bundle exec rake cheffish_spec
+ - rvm: 2.2
+ env: "GEMFILE_MOD=\"gem 'chef-provisioning', github: 'chef/chef-provisioning'\""
+ script: bundle exec rake chef_provisioning_spec
+ - rvm: 2.2
+ env: "GEMFILE_MOD=\"gem 'chef-provisioning-aws', github: 'chef/chef-provisioning-aws'\""
+ script: bundle exec rake chef_provisioning_aws_spec
+ - rvm: 2.2
+ env: "GEMFILE_MOD=\"gem 'chefspec'\""
+ script: bundle exec rake chefspec_spec
+ - rvm: 2.2
+ env: "GEMFILE_MOD=\"gem 'chef-sugar'\""
+ script: bundle exec rake chef_sugar_spec
+ - rvm: 2.2
+ env: "GEMFILE_MOD=\"gem 'chef-rewind'\""
+ script: bundle exec rake chef_rewind_spec
+ - rvm: 2.2
+ env: "GEMFILE_MOD=\"gem 'foodcritic', github: 'acrmp/foodcritic', branch: 'v5.0.0'\""
+ script: bundle exec rake foodcritic_spec
+ - rvm: 2.2
+ env: "GEMFILE_MOD=\"gem 'halite', github: 'poise/halite'\""
+ script: bundle exec rake halite_spec
+ - rvm: 2.2
+ env: "GEMFILE_MOD=\"gem 'poise', github: 'poise/poise'\""
+ script: bundle exec rake poise_spec
+ # Not working yet: halite
+ # - rvm: 2.2
+ # script: bundle exec rake poise_spec
+ ### START TEST KITCHEN ONLY ###
+ - rvm: 2.2
gemfile: kitchen-tests/Gemfile
before_install:
- echo -n $DO_KEY_CHUNK_{0..30} >> ~/.ssh/id_aws.base64
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22f02eee2f..125c9d68c3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,32 @@
+
## Unreleased
+
+* [**Dave Eddy**](https://github.com/bahamas10)
+ [pr#3187](https://github.com/chef/chef/pull/3187) overhaul solaris SMF service provider
+* [**Mikhail Zholobov**](https://github.com/legal90)
+ [pr#3192](https://github.com/chef/chef/pull/3192) provider/user/dscl: Set default gid to 20
+
+* [pr#4034](https://github.com/chef/chef/pull/4034) add optional ruby-profiling with --profile-ruby
+* [pr#3119](https://github.com/chef/chef/pull/3119) allow removing user, even if their GID isn't resolvable
+* [pr#4068](https://github.com/chef/chef/pull/4068) update messaging from LWRP to Custom Resource in logging and spec
+* [pr#4021](https://github.com/chef/chef/pull/4021) add missing requires for Chef::DSL::Recipe to LWRPBase
+* [pr#3597](https://github.com/chef/chef/pull/3597) print STDOUT from the powershell_script
+* [pr#4091](https://github.com/chef/chef/pull/4091) Allow downloading of root_files in a chef repository
+
+## 12.5.1
+
+* [**Ranjib Dey**](https://github.com/ranjib):
+ [pr#3588](https://github.com/chef/chef/pull/3588) Count skipped resources among total resources in doc formatter
+
+## 12.5.1
+
* [**Ranjib Dey**](https://github.com/ranjib):
[pr#3588](https://github.com/chef/chef/pull/3588) Count skipped resources among total resources in doc formatter
* [**John Kerry**](https://github.com/jkerry):
[pr#3539](https://github.com/chef/chef/pull/3539) Fix issue: registry\_key resource is case sensitive in chef but not on windows
* [**David Eddy**](https://github.com/bahamas10):
- [pr#3443](https://github.com/chef/chef/pull/3443) remove extraneous space
+ - [pr#3443](https://github.com/chef/chef/pull/3443) remove extraneous space
+ - [pr#3091](https://github.com/chef/chef/pull/3091) fix locking/unlocking users on SmartOS
* [**margueritepd**](https://github.com/margueritepd):
[pr#3693](https://github.com/chef/chef/pull/3693) Interpolate `%{path}` in verify command
* [**Jeremy Fleischman**](https://github.com/jfly):
@@ -18,16 +40,36 @@
* [**James Belchamber**](https://github.com/JamesBelchamber):
[pr#1796](https://github.com/chef/chef/pull/1796): make mount options aware
* [**Nate Walck**](https://github.com/natewalck):
- [pr#3594](https://github.com/chef/chef/pull/3594): Update service provider for OSX 10.11
-* [**Nate Walck**](https://github.com/natewalck):
- [pr#3704](https://github.com/chef/chef/pull/3704): Add SIP (OS X 10.11) support
+ - [pr#3594](https://github.com/chef/chef/pull/3594): Update service provider for OSX 10.11
+ - [pr#3704](https://github.com/chef/chef/pull/3704): Add SIP (OS X 10.11) support
* [**Phil Dibowitz**](https://github.com/jaymzh):
[pr#3805](https://github.com/chef/chef/pull/3805) LWRP parameter validators should use truthiness
* [**Igor Shpakov**](https://github.com/Igorshp):
[pr#3743](https://github.com/chef/chef/pull/3743) speed improvement for `remote_directory` resource
-
+* [**James FitzGibbon**](https://github.com/jf647):
+ [pr#3027](https://github.com/chef/chef/pull/3027) Add warnings to 'knife node run list remove ...'
+* [**Backslasher**](https://github.com/backslasher):
+ [pr#3172](https://github.com/chef/chef/pull/3172) Migrated deploy resource to use shell\_out instead of run\_command
+* [**Sean Walberg**](https://github.com/swalberg):
+ [pr#3190](https://github.com/chef/chef/pull/3190) Allow tags to be set on a node during bootstrap
+* [**ckaushik**](https://github.com/ckaushik) and [**Sam Dunne**](https://github.com/samdunne):
+ [pr#3510](https://github.com/chef/chef/pull/3510) Fix broken rendering
+of partial templates.
+* [**Simon Detheridge**](https://github.com/gh2k):
+ [pr#3806](https://github.com/chef/chef/pull/3806) Replace output\_of\_command with shell\_out! in subversion provider
+* [**Joel Handwell**](https://github.com/joelhandwell):
+ [pr#3821](https://github.com/chef/chef/pull/3821) Human friendly elapsed time in log
+
+* [pr#3985](https://github.com/chef/chef/pull/3985) Simplify the regex which determines the rpm version to resolve issue #3671
+* [pr#3928](https://github.com/chef/chef/pull/3928) Add named run list support when using policyfiles
+* [pr#3913](https://github.com/chef/chef/pull/3913) Add `policy_name`and `policy_group` fields to the node object
+* [pr#3875](https://github.com/chef/chef/pull/3875) Patch Win32::Registry#delete_key, #delete_value to use wide (W) APIs
+* [pr#3850](https://github.com/chef/chef/pull/3850) Patch Win32::Registry#write to fix encoding errors
+* [pr#3837](https://github.com/chef/chef/pull/3837) refactor remote_directory provider for mem+perf improvement
* [pr#3799](https://github.com/chef/chef/pull/3799) fix supports hash issues in service providers
+* [pr#3797](https://github.com/chef/chef/pull/3797) Fix dsc_script spec failure on 64-bit Ruby
* [pr#3817](https://github.com/chef/chef/pull/3817) Remove now-useless forcing of ruby Garbage Collector run
+* [pr#3775](https://github.com/chef/chef/pull/3775) Enable 64-bit support for Powershell and Batch scripts
* [pr#3774](https://github.com/chef/chef/pull/3774) Add support for yum-deprecated in yum provider
* [pr#3793](https://github.com/chef/chef/pull/3793) CHEF-5372: Support specific `run_levels` for RedHat service
* [pr#2460](https://github.com/chef/chef/pull/2460) add privacy flag
@@ -52,6 +94,7 @@
* [pr#3768](https://github.com/chef/chef/pull/3768) Make reboot\_pending? look for CBS RebootPending
* [pr#3815](https://github.com/chef/chef/pull/3815) Fix `powershell_script` validation to use correct architecture
* [pr#3772](https://github.com/chef/chef/pull/3772) Add `ps_credential` dsl method to `dsc_script`
+* [pr#3462](https://github.com/chef/chef/pull/3462) Fix issue where `ps_credential` does not work over winrm
## 12.4.1
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index cc416537ab..b2f9ece975 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -11,7 +11,7 @@ We utilize **Github Issues** for issue tracking and contributions. You can contr
We have a 3 step process that utilizes **Github Issues**:
-1. Sign or be added to an existing [Contributor License Agreement (CLA)](https://supermarket.getchef.com/become-a-contributor).
+1. Sign or be added to an existing [Contributor License Agreement (CLA)](https://supermarket.chef.io/become-a-contributor).
2. Create a Github Pull Request.
3. Do [Code Review](#cr) with the **Chef Engineering Team** or **Chef Core Committers** on the pull request.
@@ -21,7 +21,7 @@ Chef is built to last. We strive to ensure high quality throughout the Chef expe
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/opscode/chef/tree/master/spec/unit)
+ 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
@@ -63,14 +63,14 @@ You can watch the recordings of the old Code Review hangouts on the [opscodebtm]
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/opscode/chef/blob/master/LICENSE)
+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,
- most often when the contributor is doing work for a company.
+ 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
@@ -81,10 +81,10 @@ To make a good faith effort to ensure these criteria are met, Chef requires an I
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.getchef.com/icla-signatures/new) online.
+ [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.getchef.com/ccla-signatures/new) instead.
+ [Corporate CLA](https://supermarket.chef.io/ccla-signatures/new) instead.
### Chef Obvious Fix Policy
@@ -109,7 +109,7 @@ As a rule of thumb, changes are obvious fixes if they do not introduce any new f
```
------------------------------------------------------------------------
commit 370adb3f82d55d912b0cf9c1d1e99b132a8ed3b5
-Author: danielsdeleo <dan@opscode.com>
+Author: danielsdeleo <dan@chef.io>
Date: Wed Sep 18 11:44:40 2013 -0700
Fix typo in config file docs.
@@ -126,16 +126,16 @@ 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/opscode/).
+ [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/opscode/chef/issues)
+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.
-In order to decrease the back and forth an issues and help us get to the bottom of them quickly
- we use below issue template. You can copy paste this code into the issue you are opening and
+In order to decrease the back and forth in issues, and to help us get to the bottom of them quickly
+ we use the below issue template. You can copy/paste this template into the issue you are opening and
edit it accordingly.
<a name="issuetemplate"></a>
@@ -148,33 +148,29 @@ In order to decrease the back and forth an issues and help us get to the bottom
### Scenario:
[What you are trying to achieve and you can't?]
-
-
### Steps to Reproduce:
[If you are filing an issue what are the things we need to do in order to repro your problem?]
-
### Expected Result:
[What are you expecting to happen as the consequence of above reproduction steps?]
-
### Actual Result:
[What actually happens after the reproduction steps?]
```
### 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/opscode/chef-rfc). These queries will help track contributions through this process:
+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/opscode/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/opscode/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/opscode/chef/labels/Pending%20Maintainer%20Review)
-* [Suitable for First Contribution](https://github.com/opscode/chef/labels/Easy)
+* [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
Our primary shipping vehicle is operating system specific packages that includes
- all the requirements of Chef. We call these [Omnibus packages](https://github.com/opscode/omnibus-ruby)
+ all the requirements of Chef. We call these [Omnibus packages](https://github.com/chef/omnibus)
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 &
@@ -194,7 +190,7 @@ We frequently make `alpha` and `beta` releases with version numbers that look li
We do a `Minor` release approximately every 3 months and `Patch` releases on a when-needed
basis for regressions, significant bugs, and security issues.
-Announcements of releases are available on [Chef Blog](http://www.getchef.com/blog) when they are
+Announcements of releases are available on [Chef Blog](http://www.chef.io/blog) when they are
available.
## Chef Community
@@ -207,6 +203,6 @@ Chef is made possible by a strong community of developers and system administrat
Also here are some additional pointers to some awesome Chef content:
-* [Chef Docs](http://docs.opscode.com/)
-* [Learn Chef](https://learnchef.opscode.com/)
-* [Chef Inc](http://www.getchef.com/)
+* [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 4c07dea872..0b467f2570 100644
--- a/DOC_CHANGES.md
+++ b/DOC_CHANGES.md
@@ -6,6 +6,69 @@ Example Doc Change:
Description of the required change.
-->
+### client.rb named run list setting
+
+Policyfiles allow for multiple named run lists to be specified. To use
+them in chef-client, one can either specify them on the command line
+with:
+
+```
+chef-client --named-run-list NAME
+```
+
+or use the short option:
+
+```
+chef-client -n NAME
+```
+
+or specify the named run list in client.rb:
+
+```ruby
+named_run_list "NAME"
+```
+
+NOTE: ChefDK has supported named run lists in policyfiles for a few
+releases, but is affected by a bug where named run lists can be deleted
+from a Policyfile.lock.json during the upload. The fix will likely be
+included in ChefDK 0.8.0. See: https://github.com/chef/chef-dk/pull/520
+
+### client.rb policyfile settings
+
+Chef client can be configured to run in policyfile mode by setting
+`policy_name` and `policy_group` in client.rb. In order to use
+policyfiles, _both_ settings should be set. Example:
+
+```ruby
+policy_name "appserver"
+policy_group "staging"
+```
+
+As of Chef Client 12.5, when used in conjunction with Chef Server 12.3,
+these settings can instead be set directly on the node object. Setting
+them via the node JSON as described below will result in Chef Client
+creating the node object with these settings.
+
+### `chef-client -j JSON`
+
+Chef client node JSON can now be used to specify the policyfile settings
+`policy_name` and `policy_group`, like so:
+
+```json
+{
+ "policy_name": "appserver",
+ "policy_group": "staging"
+}
+```
+
+Doing so will cause `chef-client` to switch to policyfile mode
+automatically (i.e., the `use_policy` flag in `client.rb` is not
+required).
+
+Users who wish to take advantage of this functionality should upgrade
+the Chef Server to at least 12.3, which is the first Chef Server release
+capable of storing `policy_name` and `policy_group` in the node data.
+
### PSCredential Support for `dsc_script`
`dsc_script` now supports the use of `ps_credential` to create a PSCredential
diff --git a/Gemfile b/Gemfile
index af0bef493c..1bc5c79675 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,21 +3,46 @@ gemspec :name => "chef"
gem "activesupport", "< 4.0.0", :group => :compat_testing, :platform => "ruby"
-gem 'chef-config', path: "chef-config"
+gem 'chef-config', path: "chef-config" if File.exists?(__FILE__ + '../chef-config')
group(:docgen) do
- gem "tomlrb"
gem "yard"
end
+group(:maintenance) do
+ gem "tomlrb"
+
+ # To sync maintainers with github
+ gem "octokit"
+ gem "netrc"
+end
+
group(:development, :test) do
+ # for profiling
+ gem "ruby-prof"
+
gem "simplecov"
gem 'rack', "~> 1.5.1"
- gem 'cheffish', "~> 1.3"
+
gem 'ruby-shadow', :platforms => :ruby unless RUBY_PLATFORM.downcase.match(/(aix|cygwin)/)
+
+ # For external tests
+# gem 'chef-zero', github: 'chef/chef-zero'
+# gem 'cheffish', github: 'chef/cheffish'
+# gem 'chef-provisioning'#, github: 'chef/chef-provisioning'
+# gem 'chef-provisioning-aws', github: 'chef/chef-provisioning-aws'
+# gem 'test-kitchen'
+# gem 'chefspec'
+# gem 'chef-sugar'
+# gem 'poise', github: 'poise/poise', branch: 'deeecb890a6a0bc2037dfb09ce0fd0a8931519aa'
+# gem 'halite', github: 'poise/halite'
+# gem 'foodcritic', github: 'acrmp/foodcritic', branch: 'v5.0.0'
+# gem 'chef-rewind'
end
+instance_eval(ENV['GEMFILE_MOD']) if ENV['GEMFILE_MOD']
+
# If you want to load debugging tools into the bundle exec sandbox,
# add these additional dependencies into chef/Gemfile.local
eval(IO.read(__FILE__ + '.local'), binding) if File.exists?(__FILE__ + '.local')
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index 3c777366f8..d270a7c7ac 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -22,6 +22,8 @@ Handles the core parts of the Chef DSL, base resource and provider
infrastructure, the Chef applications and [omnibus-chef](https://github.com/chef/omnibus-chef). Includes anything not covered by
another component.
+To mention the team, use @chef/client-core
+
### Lieutenant
* [Thom May](https://github.com/thommay)
@@ -33,17 +35,20 @@ another component.
* [Daniel DeLeo](https://github.com/danielsdeleo)
* [AJ Christensen](https://github.com/fujin)
* [Phil Dibowitz](https://github.com/jaymzh)
-* [Jay Mundrawala](https://github.com/jdmundrawala)
+* [Jay Mundrawala](https://github.com/jaym)
* [Jon Cowie](https://github.com/jonlives)
* [Lamont Granquist](https://github.com/lamont-granquist)
* [Claire McQuin](https://github.com/mcquin)
* [Steven Murawski](https://github.com/smurawski)
* [Tyler Ball](https://github.com/tyler-ball)
* [Ranjib Dey](https://github.com/ranjib)
+* [Matt Wrock](https://github.com/mwrock)
## Dev Tools
Chef Zero, Knife, Chef Apply and Chef Shell.
+To mention the team, use @chef/client-dev-tools
+
### Maintainers
* [Daniel DeLeo](https://github.com/danielsdeleo)
@@ -54,6 +59,8 @@ Chef Zero, Knife, Chef Apply and Chef Shell.
## Test Tools
ChefSpec
+To mention the team, use @chef/client-test-tools
+
### Lieutenant
* [Seth Vargo](https://github.com/sethvargo)
@@ -70,6 +77,8 @@ The specific components of Chef related to a given platform - including (but not
## Enterprise Linux
+To mention the team, use @chef/client-enterprise-linux
+
### Lieutenant
* [Jon Cowie](https://github.com/jonlives)
@@ -81,6 +90,8 @@ The specific components of Chef related to a given platform - including (but not
## Ubuntu
+To mention the team, use @chef/client-ubuntu
+
### Lieutenant
* [Ranjib Dey](https://github.com/ranjib)
@@ -92,19 +103,24 @@ The specific components of Chef related to a given platform - including (but not
## Windows
+To mention the team, use @chef/client-windows
+
### Lieutenant
* [Bryan McLellan](https://github.com/btm)
### Maintainers
-* [Jay Mundrawala](https://github.com/jdmundrawala)
+* [Jay Mundrawala](https://github.com/jaym)
* [Kartik Cating-Subramanian](https://github.com/ksubrama)
* [Steven Murawski](https://github.com/smurawski)
* [Salim Alam](https://github.com/chefsalim)
+* [Matt Wrock](https://github.com/mwrock)
## Solaris
+To mention the team, use @chef/client-solaris
+
### Lieutenant
* [Thom May](https://github.com/thommay)
@@ -115,12 +131,16 @@ The specific components of Chef related to a given platform - including (but not
## AIX
+To mention the team, use @chef/client-aix
+
### Lieutenant
* [Lamont Granquist](https://github.com/lamont-granquist)
## Mac OS X
+To mention the team, use @chef/client-os-x
+
### Lieutenant
* [Joshua Timberman](https://github.com/jtimberman)
@@ -131,6 +151,8 @@ The specific components of Chef related to a given platform - including (but not
## Debian
+To mention the team, use @chef/client-debian
+
### Lieutenant
* [Thom May](https://github.com/thommay)
@@ -141,24 +163,32 @@ The specific components of Chef related to a given platform - including (but not
## Fedora
+To mention the team, use @chef/client-fedora
+
### Maintainers
* [Lamont Granquist](https://github.com/lamont-granquist)
## openSUSE
+To mention the team, use @chef/client-opensuse
+
### Maintainers
* [Lamont Granquist](https://github.com/lamont-granquist)
## SUSE Enterprise Linux Server
+To mention the team, use @chef/client-suse
+
### Maintainers
* [Lamont Granquist](https://github.com/lamont-granquist)
## FreeBSD
+To mention the team, use @chef/client-freebsd
+
### Lieutenant
* [Aaron Kalin](https://github.com/martinisoft)
@@ -170,24 +200,32 @@ The specific components of Chef related to a given platform - including (but not
## OpenBSD
+To mention the team, use @chef/client-openbsd
+
### Lieutenant
* [Joe Miller](https://github.com/joemiller)
## Gentoo
+To mention the team, use @chef/client-gentoo
+
### Maintainers
* [Lamont Granquist](https://github.com/lamont-granquist)
## OmniOS
+To mention the team, use @chef/client-omnios
+
### Maintainers
* [Thom May](https://github.com/thommay)
## ArchLinux
+To mention the team, use @chef/client-archlinux
+
### Maintainers
* [Lamont Granquist](https://github.com/lamont-granquist)
diff --git a/MAINTAINERS.toml b/MAINTAINERS.toml
index 61c75c1a30..ca0a2e67bc 100644
--- a/MAINTAINERS.toml
+++ b/MAINTAINERS.toml
@@ -1,5 +1,7 @@
#
# This file is structured to be consumed by both humans and computers.
+# To update the generated Markdown, run `bundle exec rake maintainers:generate`
+# To synchronize the maintainers with the github teams, run `bundle exec rake maintainers:synchronize`
# It is a TOML document containing Markdown
#
[Preamble]
@@ -24,6 +26,7 @@ a maintainer, lieutenant, or the project lead.
[Org.Components.Core]
title = "Chef Core"
+ team = "client-core"
text = """
Handles the core parts of the Chef DSL, base resource and provider
infrastructure, the Chef applications and [omnibus-chef](https://github.com/chef/omnibus-chef). Includes anything not covered by
@@ -38,17 +41,19 @@ another component.
"danielsdeleo",
"fujin",
"jaymzh",
- "jdmundrawala",
+ "jaym",
"jonlives",
"lamont-granquist",
"mcquin",
"smurawski",
"tyler-ball",
- "ranjib"
+ "ranjib",
+ "mwrock"
]
[Org.Components.DevTools]
title = "Dev Tools"
+ team = "client-dev-tools"
text = "Chef Zero, Knife, Chef Apply and Chef Shell."
paths = [
@@ -68,6 +73,7 @@ another component.
[Org.Components.TestTools]
title = "Test Tools"
+ team = "client-test-tools"
text = "ChefSpec"
lieutenant = "sethvargo"
@@ -86,6 +92,7 @@ The specific components of Chef related to a given platform - including (but not
[Org.Components.Subsystems."Enterprise Linux"]
title = "Enterprise Linux"
+ team = "client-enterprise-linux"
lieutenant = "jonlives"
@@ -96,6 +103,7 @@ The specific components of Chef related to a given platform - including (but not
[Org.Components.Subsystems.Ubuntu]
title = "Ubuntu"
+ team = "client-ubuntu"
lieutenant = "ranjib"
@@ -106,17 +114,20 @@ The specific components of Chef related to a given platform - including (but not
[Org.Components.Subsystems.Windows]
title = "Windows"
+ team = "client-windows"
lieutenant = "btm"
maintainers = [
- "jdmundrawala",
+ "jaym",
"ksubrama",
"smurawski",
- "chefsalim"
+ "chefsalim",
+ "mwrock"
]
[Org.Components.Subsystems.Solaris]
title = "Solaris"
+ team = "client-solaris"
lieutenant = "thommay"
@@ -126,11 +137,13 @@ The specific components of Chef related to a given platform - including (but not
[Org.Components.Subsystems.AIX]
title = "AIX"
+ team = "client-aix"
lieutenant = "lamont-granquist"
[Org.Components.Subsystems."Mac OS X"]
title = "Mac OS X"
+ team = "client-os-x"
lieutenant = "jtimberman"
@@ -140,6 +153,7 @@ The specific components of Chef related to a given platform - including (but not
[Org.Components.Subsystems.Debian]
title = "Debian"
+ team = "client-debian"
lieutenant = "thommay"
@@ -149,6 +163,7 @@ The specific components of Chef related to a given platform - including (but not
[Org.Components.Subsystems.Fedora]
title = "Fedora"
+ team = "client-fedora"
maintainers = [
"lamont-granquist"
@@ -156,6 +171,7 @@ The specific components of Chef related to a given platform - including (but not
[Org.Components.Subsystems.openSUSE]
title = "openSUSE"
+ team = "client-opensuse"
maintainers = [
"lamont-granquist"
@@ -163,6 +179,7 @@ The specific components of Chef related to a given platform - including (but not
[Org.Components.Subsystems."SUSE Enterprise Linux"]
title = "SUSE Enterprise Linux Server"
+ team = "client-suse"
maintainers = [
"lamont-granquist"
@@ -170,6 +187,7 @@ The specific components of Chef related to a given platform - including (but not
[Org.Components.Subsystems.FreeBSD]
title = "FreeBSD"
+ team = "client-freebsd"
lieutenant = "martinisoft"
@@ -180,11 +198,13 @@ The specific components of Chef related to a given platform - including (but not
[Org.Components.Subsystems.OpenBSD]
title = "OpenBSD"
+ team = "client-openbsd"
lieutenant = "joemiller"
[Org.Components.Subsystems.Gentoo]
title = "Gentoo"
+ team = "client-gentoo"
maintainers = [
"lamont-granquist"
@@ -192,6 +212,7 @@ The specific components of Chef related to a given platform - including (but not
[Org.Components.Subsystems.OmniOS]
title = "OmniOS"
+ team = "client-omnios"
maintainers = [
"thommay"
@@ -199,6 +220,7 @@ The specific components of Chef related to a given platform - including (but not
[Org.Components.Subsystems.ArchLinux]
title = "ArchLinux"
+ team = "client-archlinux"
maintainers = [
"lamont-granquist",
@@ -230,9 +252,9 @@ The specific components of Chef related to a given platform - including (but not
Name = "Phil Dibowitz"
GitHub = "jaymzh"
- [people.jdmundrawala]
+ [people.jaym]
Name = "Jay Mundrawala"
- GitHub = "jdmundrawala"
+ GitHub = "jaym"
[people.jonlives]
Name = "Jon Cowie"
@@ -305,3 +327,7 @@ The specific components of Chef related to a given platform - including (but not
[people.chefsalim]
Name = "Salim Alam"
GitHub = "chefsalim"
+
+ [people.mwrock]
+ Name = "Matt Wrock"
+ GitHub = "mwrock"
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index cba5b9f415..d95c416a41 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,5 +1,9 @@
# Chef Client Release Notes 12.5.0:
* OSX 10.11 support (support for SIP and service changes)
+* Windows cookbook <= 1.38.1 contains a library file which does not compile with
+chef-client 12.5.0. This is resolved in version 1.38.2 of the Windows cookbook.
+Cookbooks depending on the Windows cookbook should update the dependency version
+to at least 1.38.2 to be compatible with chef-client 12.5.0.
## PSCredential support for the `dsc_script` resource
@@ -59,7 +63,7 @@ In Fedora 22 yum has been deprecated in favor of DNF. Unfortunately, while DNF
compatible with yum, the yum provider in Chef is not compatible with DNF. Until a proper `dnf_package`
resource and associated provider is written and merged into core, 12.5.0 has been patched so that the
`yum_package` resource takes a property named `yum_binary` which can be set to point at the yum binary
-to run for all its commands. The `yum_binary` will also default to `yum-deprecated` if the
+to run for all its commands. The `yum_binary` will also default to `yum-deprecated` if the
`/usr/bin/yum-deprecated` command is found on the system. This means that Fedora 22 users can run
something like this early in their chef-client run:
@@ -73,4 +77,3 @@ end
After which the yum-deprecated binary will exist, and the yum provider will find it and should operate
normally and successfully.
-
diff --git a/VERSION b/VERSION
index 9a03cd310d..2b4b4d7cb5 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-12.5.0.current.0
+12.5.1
diff --git a/appveyor.yml b/appveyor.yml
index 06448e2be2..7cc8fb631d 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,12 +1,14 @@
version: "master-{build}"
-os: Windows Server 2012
+os: Windows Server 2012 R2
platform:
- x64
environment:
matrix:
- ruby_version: "200"
+ - ruby_version: "200-x64"
+ - ruby_version: "21"
clone_folder: c:\projects\chef
clone_depth: 1
@@ -14,13 +16,9 @@ skip_tags: true
branches:
only:
- master
- - 12.4-stable
-
-cache:
- - C:\Ruby200\lib\ruby\gems\2.0.0
- - C:\Ruby200\bin
install:
+ - systeminfo
- winrm quickconfig -q
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
- echo %PATH%
diff --git a/bin/chef-service-manager b/bin/chef-service-manager
index 7c031f70d4..5808a0be46 100755
--- a/bin/chef-service-manager
+++ b/bin/chef-service-manager
@@ -28,7 +28,7 @@ if Chef::Platform.windows?
:service_name => "chef-client",
:service_display_name => "Chef Client Service",
:service_description => "Runs Chef Client on regular, configurable intervals.",
- :service_file_path => File.expand_path(File.join(File.dirname(__FILE__), '../../../../bin/chef-windows-service')),
+ :service_file_path => File.expand_path('../chef-windows-service', $PROGRAM_NAME),
:delayed_start => true,
:dependencies => ['Winmgmt']
}
diff --git a/chef-config/VERSION b/chef-config/VERSION
index 9a03cd310d..2b4b4d7cb5 100644
--- a/chef-config/VERSION
+++ b/chef-config/VERSION
@@ -1 +1 @@
-12.5.0.current.0
+12.5.1
diff --git a/chef-config/chef-config.gemspec b/chef-config/chef-config.gemspec
index 475bd0f2d2..6619f04169 100644
--- a/chef-config/chef-config.gemspec
+++ b/chef-config/chef-config.gemspec
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency(rspec, "~> 3.2")
end
- spec.files = %w(Rakefile LICENSE README.md) +
+ spec.files = %w(Rakefile LICENSE README.md) + Dir.glob("*.gemspec") +
Dir.glob("{lib,spec}/**/*", File::FNM_DOTMATCH).reject {|f| File.directory?(f) }
spec.bindir = "bin"
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb
index a3f06e9b23..069f0ed6c0 100644
--- a/chef-config/lib/chef-config/config.rb
+++ b/chef-config/lib/chef-config/config.rb
@@ -68,7 +68,7 @@ module ChefConfig
default(:config_dir) do
if config_file
- PathHelper.dirname(config_file)
+ PathHelper.dirname(PathHelper.canonical_path(config_file, false))
else
PathHelper.join(user_home, ".chef", "")
end
@@ -339,10 +339,33 @@ module ChefConfig
# most of our testing scenarios)
default :minimal_ohai, false
+ ###
+ # Policyfile Settings
+ #
# Policyfile is a feature where a node gets its run list and cookbook
# version set from a single document on the server instead of expanding the
# run list and having the server compute the cookbook version set based on
# environment constraints.
+ #
+ # Policyfiles are auto-versioned. The user groups nodes by `policy_name`,
+ # which generally describes a hosts's functional role, and `policy_group`,
+ # which generally groups nodes by deployment phase (a.k.a., "environment").
+ # The Chef Server maps a given set of `policy_name` plus `policy_group` to
+ # a particular revision of a policy.
+
+ default :policy_name, nil
+ default :policy_group, nil
+
+ # Policyfiles can have multiple run lists, via the named run list feature.
+ # Generally this will be set by a CLI option via Chef::Application::Client,
+ # but it could be set in client.rb if desired.
+
+ default :named_run_list, nil
+
+ # During initial development, users were required to set `use_policyfile true`
+ # in `client.rb` to opt-in to policyfile use. Chef Client now examines
+ # configuration, node json, and the stored node to determine if policyfile
+ # usage is desired. This flag is still honored if set, but is unnecessary.
default :use_policyfile, false
# Policyfiles can be used in a native mode (default) or compatibility mode.
@@ -356,6 +379,16 @@ module ChefConfig
# policyfiles with servers that don't yet support the native endpoints.
default :policy_document_native_api, true
+ # When policyfiles are used in compatibility mode, `policy_name` and
+ # `policy_group` are instead specified using a combined configuration
+ # setting, `deployment_group`. For example, if policy_name should be
+ # "webserver" and policy_group should be "staging", then `deployment_group`
+ # should be set to "webserver-staging", which is the name of the data bag
+ # item that the policy will be stored as. NOTE: this setting only has an
+ # effect if `policy_document_native_api` is set to `false`.
+ default :deployment_group, nil
+
+
# Set these to enable SSL authentication / mutual-authentication
# with the server
@@ -657,6 +690,14 @@ module ChefConfig
default :watchdog_timeout, 2 * (60 * 60) # 2 hours
end
+ # Add an empty and non-strict config_context for chefdk. This lets the user
+ # have code like `chefdk.generator_cookbook "/path/to/cookbook"` in their
+ # config.rb, and it will be ignored by tools like knife and ohai. ChefDK
+ # itself can define the config options it accepts and enable strict mode,
+ # and that will only apply when running `chef` commands.
+ config_context :chefdk do
+ end
+
# Chef requires an English-language UTF-8 locale to function properly. We attempt
# to use the 'locale -a' command and search through a list of preferences until we
# find one that we can use. On Ubuntu systems we should find 'C.UTF-8' and be
diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb
index 9579f0638d..654a18eac1 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.dirname(File.expand_path(File.dirname(__FILE__)))
- VERSION = '12.5.0.current.0'
+ VERSION = '12.5.1'
end
#
diff --git a/chef-config/spec/unit/config_spec.rb b/chef-config/spec/unit/config_spec.rb
index 395fa2618e..d99ff428fb 100644
--- a/chef-config/spec/unit/config_spec.rb
+++ b/chef-config/spec/unit/config_spec.rb
@@ -301,14 +301,36 @@ RSpec.describe ChefConfig::Config do
describe "setting the config dir" do
+ context "when the config file is given with a relative path" do
+
+ before do
+ ChefConfig::Config.config_file = "client.rb"
+ end
+
+ it "expands the path when determining config_dir" do
+ # config_dir goes through PathHelper.canonical_path, which
+ # downcases on windows because the FS is case insensitive, so we
+ # have to downcase expected and actual to make the tests work.
+ expect(ChefConfig::Config.config_dir.downcase).to eq(to_platform(Dir.pwd).downcase)
+ end
+
+ it "does not set derived paths at FS root" do
+ ChefConfig::Config.local_mode = true
+ expect(ChefConfig::Config.cache_path.downcase).to eq(to_platform(File.join(Dir.pwd, 'local-mode-cache')).downcase)
+ end
+
+ end
+
context "when the config file is /etc/chef/client.rb" do
before do
- ChefConfig::Config.config_file = to_platform("/etc/chef/client.rb")
+ config_location = to_platform("/etc/chef/client.rb").downcase
+ allow(File).to receive(:absolute_path).with(config_location).and_return(config_location)
+ ChefConfig::Config.config_file = config_location
end
it "config_dir is /etc/chef" do
- expect(ChefConfig::Config.config_dir).to eq(to_platform("/etc/chef"))
+ expect(ChefConfig::Config.config_dir).to eq(to_platform("/etc/chef").downcase)
end
context "and chef is running in local mode" do
@@ -317,7 +339,7 @@ RSpec.describe ChefConfig::Config do
end
it "config_dir is /etc/chef" do
- expect(ChefConfig::Config.config_dir).to eq(to_platform("/etc/chef"))
+ expect(ChefConfig::Config.config_dir).to eq(to_platform("/etc/chef").downcase)
end
end
@@ -539,6 +561,14 @@ RSpec.describe ChefConfig::Config do
end
end
+ describe "allowing chefdk configuration outside of chefdk" do
+
+ it "allows arbitrary settings in the chefdk config context" do
+ expect { ChefConfig::Config.chefdk.generator_cookbook("/path") }.to_not raise_error
+ end
+
+ end
+
describe "Treating deprecation warnings as errors" do
context "when using our default RSpec configuration" do
diff --git a/chef-windows.gemspec b/chef-windows.gemspec
index 428174889f..1c72d9102a 100644
--- a/chef-windows.gemspec
+++ b/chef-windows.gemspec
@@ -9,10 +9,9 @@ gemspec.add_dependency "win32-event", "~> 0.6.1"
gemspec.add_dependency "win32-eventlog", "~> 0.6.2"
gemspec.add_dependency "win32-mmap", "~> 0.4.1"
gemspec.add_dependency "win32-mutex", "~> 0.4.2"
-gemspec.add_dependency "win32-process", "~> 0.7.5"
+gemspec.add_dependency "win32-process", "~> 0.8.2"
gemspec.add_dependency "win32-service", "~> 0.8.7"
gemspec.add_dependency "windows-api", "~> 0.4.4"
-gemspec.add_dependency "windows-pr", "~> 1.2.4"
gemspec.add_dependency "wmi-lite", "~> 1.0"
gemspec.extensions << "ext/win32-eventlog/Rakefile"
gemspec.files += %w(ext/win32-eventlog/Rakefile ext/win32-eventlog/chef-log.man)
diff --git a/chef.gemspec b/chef.gemspec
index da17e8b586..96673f391a 100644
--- a/chef.gemspec
+++ b/chef.gemspec
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
s.add_dependency "mixlib-cli", "~> 1.4"
s.add_dependency "mixlib-log", "~> 1.3"
s.add_dependency "mixlib-authentication", "~> 1.3"
- s.add_dependency "mixlib-shellout", ">= 2.0.0.rc.0", "< 3.0"
+ s.add_dependency "mixlib-shellout", "~> 2.0"
s.add_dependency "ohai", ">= 8.6.0.alpha.1", "< 9"
s.add_dependency "ffi-yajl", "~> 2.2"
@@ -52,5 +52,5 @@ Gem::Specification.new do |s|
s.executables = %w( chef-client chef-solo knife chef-shell chef-apply )
s.require_path = 'lib'
- s.files = %w(Rakefile LICENSE README.md CONTRIBUTING.md) + Dir.glob("{distro,lib,tasks,spec}/**/*", File::FNM_DOTMATCH).reject {|f| File.directory?(f) }
+ s.files = %w(Gemfile Rakefile LICENSE README.md CONTRIBUTING.md) + Dir.glob("{distro,lib,tasks,spec}/**/*", File::FNM_DOTMATCH).reject {|f| File.directory?(f) } + Dir.glob("*.gemspec")
end
diff --git a/distro/common/html/knife_cookbook_site.html b/distro/common/html/knife_cookbook_site.html
index 8815961629..60efafa7e1 100644
--- a/distro/common/html/knife_cookbook_site.html
+++ b/distro/common/html/knife_cookbook_site.html
@@ -5,12 +5,12 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-
+
<title>knife cookbook site &mdash; chef-client Man Pages</title>
-
+
<link rel="stylesheet" href="_static/guide.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
-
+
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
@@ -32,13 +32,13 @@
</div>
-
+
<div class="document">
<div class="documentwrapper">
<div class="body">
-
+
<div class="section" id="knife-cookbook-site">
<h1>knife cookbook site<a class="headerlink" href="#knife-cookbook-site" title="Permalink to this headline">¶</a></h1>
<p>The Cookbooks Site API is used to provide access to the cookbooks community hosted at <a class="reference external" href="https://supermarket.getchef.com/cookbooks">https://supermarket.getchef.com/cookbooks</a>. All of the cookbooks in the community are accessible through a RESTful API located at <a class="reference external" href="https://supermarket.getchef.com/api/v1/cookbooks">https://supermarket.getchef.com/api/v1/cookbooks</a> by using any of the supported endpoints. In most cases, using knife and the <strong>knife cookbook site</strong> sub-command (and any of its arguments) is the recommended method of interacting with these cookbooks, but in some cases, using the Cookbooks Site API directly may make sense.</p>
@@ -228,17 +228,17 @@ ant iis redmine
</div>
<p>to return something like:</p>
<div class="highlight-bash"><div class="highlight"><pre>apache2:
- cookbook: http://cookbooks.opscode.com/api/v1/cookbooks/apache2
+ cookbook: https://supermarket.chef.io/api/v1/cookbooks/apache2
cookbook_description: Installs and configures apache2 using Debian symlinks with helper definitions
cookbook_maintainer: opscode
cookbook_name: apache2
instiki:
- cookbook: http://cookbooks.opscode.com/api/v1/cookbooks/instiki
+ cookbook: https://supermarket.chef.io/api/v1/cookbooks/instiki
cookbook_description: Installs instiki, a Ruby on Rails wiki server under passenger+Apache2.
cookbook_maintainer: jtimberman
cookbook_name: instiki
kickstart:
- cookbook: http://cookbooks.opscode.com/api/v1/cookbooks/kickstart
+ cookbook: https://supermarket.chef.io/api/v1/cookbooks/kickstart
cookbook_description: Creates apache2 vhost and serves a kickstart file.
cookbook_maintainer: opscode
cookbook_name: kickstart
@@ -311,18 +311,18 @@ category: Networking
created_at: 2009-10-25T23:51:07Z
description: Installs and configures haproxy
external_url:
-latest_version: http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_3
+latest_version: https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_3
maintainer: opscode
name: haproxy
updated_at: 2011-06-30T21:53:25Z
versions:
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_3
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_2
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_1
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_0
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/0_8_1
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/0_8_0
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/0_7_0
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_3
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_2
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_1
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_0
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/0_8_1
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/0_8_0
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/0_7_0
</pre></div>
</div>
<p><strong>Show cookbook data as JSON</strong></p>
@@ -364,7 +364,7 @@ versions:
</div>
-
+
<div class="clearer"></div>
</div>
@@ -372,4 +372,4 @@ versions:
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/distro/common/man/man1/knife-cookbook-site.1 b/distro/common/man/man1/knife-cookbook-site.1
index a90a5305f0..acfcf6b882 100644
--- a/distro/common/man/man1/knife-cookbook-site.1
+++ b/distro/common/man/man1/knife-cookbook-site.1
@@ -364,17 +364,17 @@ to return something like:
.nf
.ft C
apache2:
- cookbook: http://cookbooks.opscode.com/api/v1/cookbooks/apache2
+ cookbook: https://supermarket.chef.io/api/v1/cookbooks/apache2
cookbook_description: Installs and configures apache2 using Debian symlinks with helper definitions
cookbook_maintainer: opscode
cookbook_name: apache2
instiki:
- cookbook: http://cookbooks.opscode.com/api/v1/cookbooks/instiki
+ cookbook: https://supermarket.chef.io/api/v1/cookbooks/instiki
cookbook_description: Installs instiki, a Ruby on Rails wiki server under passenger+Apache2.
cookbook_maintainer: jtimberman
cookbook_name: instiki
kickstart:
- cookbook: http://cookbooks.opscode.com/api/v1/cookbooks/kickstart
+ cookbook: https://supermarket.chef.io/api/v1/cookbooks/kickstart
cookbook_description: Creates apache2 vhost and serves a kickstart file.
cookbook_maintainer: opscode
cookbook_name: kickstart
@@ -481,18 +481,18 @@ category: Networking
created_at: 2009\-10\-25T23:51:07Z
description: Installs and configures haproxy
external_url:
-latest_version: http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_3
+latest_version: https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_3
maintainer: opscode
name: haproxy
updated_at: 2011\-06\-30T21:53:25Z
versions:
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_3
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_2
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_1
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/1_0_0
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/0_8_1
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/0_8_0
- http://cookbooks.opscode.com/api/v1/cookbooks/haproxy/versions/0_7_0
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_3
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_2
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_1
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/1_0_0
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/0_8_1
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/0_8_0
+ https://supermarket.chef.io/api/v1/cookbooks/haproxy/versions/0_7_0
.ft P
.fi
.UNINDENT
diff --git a/external_tests/chef-rewind.gemfile b/external_tests/chef-rewind.gemfile
deleted file mode 100644
index 39f7d6e0e8..0000000000
--- a/external_tests/chef-rewind.gemfile
+++ /dev/null
@@ -1,5 +0,0 @@
-source 'https://rubygems.org'
-
-gemspec(name: 'chef', path: "../")
-
-gem 'chef-rewind', github: 'thommay/chef-rewind'
diff --git a/external_tests/chef-sugar.gemfile b/external_tests/chef-sugar.gemfile
deleted file mode 100644
index 31ef3bb5b8..0000000000
--- a/external_tests/chef-sugar.gemfile
+++ /dev/null
@@ -1,6 +0,0 @@
-source 'https://rubygems.org'
-
-gemspec(name: 'chef', path: "../")
-
-gem 'chef-sugar', github: 'sethvargo/chef-sugar'
-gem 'chefspec'
diff --git a/external_tests/chefspec.gemfile b/external_tests/chefspec.gemfile
deleted file mode 100644
index fb7878afbd..0000000000
--- a/external_tests/chefspec.gemfile
+++ /dev/null
@@ -1,7 +0,0 @@
-source 'https://rubygems.org'
-
-gemspec(name: 'chef', path: "../")
-
-gem 'chefspec', github: 'sethvargo/chefspec', group: :development
-gem 'aruba'
-gem 'yard'
diff --git a/external_tests/foodcritic.gemfile b/external_tests/foodcritic.gemfile
deleted file mode 100644
index a2b71a0d8c..0000000000
--- a/external_tests/foodcritic.gemfile
+++ /dev/null
@@ -1,9 +0,0 @@
-source 'https://rubygems.org'
-
-gemspec(name: 'chef', path: "../")
-
-gem 'foodcritic', github: 'acrmp/foodcritic'
-gem 'cucumber'
-gem 'rubocop'
-gem 'simplecov'
-gem 'minitest'
diff --git a/external_tests/halite.gemfile b/external_tests/halite.gemfile
deleted file mode 100644
index cd8cd05668..0000000000
--- a/external_tests/halite.gemfile
+++ /dev/null
@@ -1,8 +0,0 @@
-source 'https://rubygems.org'
-
-gemspec(name: 'chef', path: "../")
-
-gem 'poise', github: 'poise/poise'
-gem 'halite', github: 'poise/halite'
-gem 'poise-boiler', github: 'poise/poise-boiler'
-gem 'rspec-command', github: 'coderanger/rspec-command'
diff --git a/external_tests/poise.gemfile b/external_tests/poise.gemfile
deleted file mode 100644
index 7d274b7a29..0000000000
--- a/external_tests/poise.gemfile
+++ /dev/null
@@ -1,7 +0,0 @@
-source 'https://rubygems.org'
-
-gemspec(name: 'chef', path: "../")
-
-gem 'poise', github: 'poise/poise'
-gem 'halite', github: 'poise/halite'
-gem 'poise-boiler', github: 'poise/poise-boiler'
diff --git a/lib/chef/application.rb b/lib/chef/application.rb
index 0563822ede..970544c068 100644
--- a/lib/chef/application.rb
+++ b/lib/chef/application.rb
@@ -382,7 +382,7 @@ class Chef
def emit_warnings
if Chef::Config[:chef_gem_compile_time]
- Chef::Log.deprecation "setting chef_gem_compile_time to true is deprecated"
+ Chef.log_deprecation "setting chef_gem_compile_time to true is deprecated"
end
end
diff --git a/lib/chef/application/apply.rb b/lib/chef/application/apply.rb
index 243b441119..b21b838d72 100644
--- a/lib/chef/application/apply.rb
+++ b/lib/chef/application/apply.rb
@@ -29,7 +29,7 @@ require 'chef/resources'
class Chef::Application::Apply < Chef::Application
- banner "Usage: chef-apply [RECIPE_FILE] [-e RECIPE_TEXT] [-s]"
+ banner "Usage: chef-apply [RECIPE_FILE | -e RECIPE_TEXT | -s] [OPTIONS]"
option :execute,
:short => "-e RECIPE_TEXT",
@@ -97,10 +97,16 @@ class Chef::Application::Apply < Chef::Application
:description => 'Enable whyrun mode',
:boolean => true
+ option :profile_ruby,
+ :long => "--[no-]profile-ruby",
+ :description => "Dump complete Ruby call graph stack of entire Chef run (expert only)",
+ :boolean => true,
+ :default => false
+
option :color,
:long => '--[no-]color',
:boolean => true,
- :default => !Chef::Platform.windows?,
+ :default => true,
:description => "Use colored output, defaults to enabled"
option :minimal_ohai,
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index 73eda81343..5fac34196d 100644
--- a/lib/chef/application/client.rb
+++ b/lib/chef/application/client.rb
@@ -2,7 +2,7 @@
# Author:: AJ Christensen (<aj@opscode.com)
# Author:: Christopher Brown (<cb@opscode.com>)
# Author:: Mark Mzyk (mmzyk@opscode.com)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -55,11 +55,17 @@ class Chef::Application::Client < Chef::Application
:boolean => true,
:default => false
+ option :profile_ruby,
+ :long => "--[no-]profile-ruby",
+ :description => "Dump complete Ruby call graph stack of entire Chef run (expert only)",
+ :boolean => true,
+ :default => false
+
option :color,
:long => '--[no-]color',
:boolean => true,
- :default => !Chef::Platform.windows?,
- :description => "Use colored output, defaults to false on Windows, true otherwise"
+ :default => true,
+ :description => "Use colored output, defaults to enabled"
option :log_level,
:short => "-l LEVEL",
@@ -160,6 +166,12 @@ class Chef::Application::Client < Chef::Application
:description => "Set the client key file location",
:proc => nil
+ option :named_run_list,
+ :short => "-n NAMED_RUN_LIST",
+ :long => "--named-run-list NAMED_RUN_LIST",
+ :description => "Use a policyfile's named run list instead of the default run list",
+ :default => nil
+
option :environment,
:short => '-E ENVIRONMENT',
:long => '--environment ENVIRONMENT',
diff --git a/lib/chef/application/knife.rb b/lib/chef/application/knife.rb
index af5216ae00..d169a5dab5 100644
--- a/lib/chef/application/knife.rb
+++ b/lib/chef/application/knife.rb
@@ -44,8 +44,8 @@ class Chef::Application::Knife < Chef::Application
option :color,
:long => '--[no-]color',
:boolean => true,
- :default => !Chef::Platform.windows?,
- :description => "Use colored output, defaults to false on Windows, true otherwise"
+ :default => true,
+ :description => "Use colored output, defaults to enabled"
option :environment,
:short => "-E ENVIRONMENT",
diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb
index 5bb2a1ceb0..4b472e9662 100644
--- a/lib/chef/application/solo.rb
+++ b/lib/chef/application/solo.rb
@@ -1,7 +1,7 @@
#
# Author:: AJ Christensen (<aj@opscode.com>)
# Author:: Mark Mzyk (mmzyk@opscode.com)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -52,6 +52,12 @@ class Chef::Application::Solo < Chef::Application
:boolean => true,
:default => false
+ option :profile_ruby,
+ :long => "--[no-]profile-ruby",
+ :description => "Dump complete Ruby call graph stack of entire Chef run (expert only)",
+ :boolean => true,
+ :default => false
+
option :color,
:long => '--[no-]color',
:boolean => true,
diff --git a/lib/chef/application/windows_service.rb b/lib/chef/application/windows_service.rb
index b42a01cfdb..2551582c3a 100644
--- a/lib/chef/application/windows_service.rb
+++ b/lib/chef/application/windows_service.rb
@@ -189,7 +189,11 @@ class Chef
config_params += " -c #{Chef::Config[:config_file]}" unless Chef::Config[:config_file].nil?
config_params += " -L #{Chef::Config[:log_location]}" unless Chef::Config[:log_location] == STDOUT
# Starts a new process and waits till the process exits
- result = shell_out("chef-client #{config_params}", :timeout => Chef::Config[:windows_service][:watchdog_timeout])
+ result = shell_out(
+ "chef-client #{config_params}",
+ :timeout => Chef::Config[:windows_service][:watchdog_timeout],
+ :logger => Chef::Log
+ )
Chef::Log.debug "#{result.stdout}"
Chef::Log.debug "#{result.stderr}"
rescue Mixlib::ShellOut::CommandTimeout => e
diff --git a/lib/chef/chef_class.rb b/lib/chef/chef_class.rb
index 458ac82467..6a0d09ec96 100644
--- a/lib/chef/chef_class.rb
+++ b/lib/chef/chef_class.rb
@@ -190,6 +190,45 @@ class Chef
def resource_handler_map
@resource_handler_map ||= Chef::Platform::ResourceHandlerMap.instance
end
+
+ #
+ # Emit a deprecation message.
+ #
+ # @param message The message to send.
+ # @param location The location. Defaults to the caller who called you (since
+ # generally the person who triggered the check is the one that needs to be
+ # fixed).
+ #
+ # @example
+ # Chef.deprecation("Deprecated!")
+ #
+ # @api private this will likely be removed in favor of an as-yet unwritten
+ # `Chef.log`
+ def log_deprecation(message, location=nil)
+ if !location
+ # Pick the first caller that is *not* part of the Chef gem, that's the
+ # thing the user wrote.
+ chef_gem_path = File.expand_path("../..", __FILE__)
+ caller(0..10).each do |c|
+ if !c.start_with?(chef_gem_path)
+ location = c
+ break
+ end
+ end
+ end
+ # `run_context.events` is the primary deprecation target if we're in a
+ # run. If we are not yet in a run, print to `Chef::Log`.
+ if run_context && run_context.events
+ run_context.events.deprecation(message, location)
+ else
+ Chef::Log.deprecation(message, location)
+ end
+ end
+ end
+
+ # @api private Only for test dependency injection; not evenly implemented as yet.
+ def self.path_to(path)
+ path
end
reset!
diff --git a/lib/chef/chef_fs/data_handler/client_data_handler.rb b/lib/chef/chef_fs/data_handler/client_data_handler.rb
index d81f35e861..5bcbd4e373 100644
--- a/lib/chef/chef_fs/data_handler/client_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/client_data_handler.rb
@@ -13,11 +13,13 @@ class Chef
'validator' => false,
'chef_type' => 'client'
}
+ # Handle the fact that admin/validator have changed type from string -> boolean
+ client['admin'] = (client['admin'] == 'true') if client['admin'].is_a?(String)
+ client['validator'] = (client['validator'] == 'true') if client['validator'].is_a?(String)
if entry.respond_to?(:org) && entry.org
defaults['orgname'] = entry.org
end
result = normalize_hash(client, defaults)
- # You can NOT send json_class, or it will fail
result.delete('json_class')
result
end
diff --git a/lib/chef/chef_fs/file_system/chef_server_root_dir.rb b/lib/chef/chef_fs/file_system/chef_server_root_dir.rb
index e3ffd644ad..a243e0ae6b 100644
--- a/lib/chef/chef_fs/file_system/chef_server_root_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server_root_dir.rb
@@ -120,7 +120,8 @@ class Chef
if File.dirname(path) == '/organizations'
File.basename(path)
else
- nil
+ # In Chef 12, everything is in an org.
+ 'chef'
end
end
end
diff --git a/lib/chef/chef_fs/file_system/file_system_entry.rb b/lib/chef/chef_fs/file_system/file_system_entry.rb
index 8611aa2e0f..478631eac2 100644
--- a/lib/chef/chef_fs/file_system/file_system_entry.rb
+++ b/lib/chef/chef_fs/file_system/file_system_entry.rb
@@ -83,7 +83,7 @@ class Chef
end
def exists?
- File.exists?(file_path) && parent.can_have_child?(name, dir?)
+ File.exists?(file_path) && (parent.nil? || parent.can_have_child?(name, dir?))
end
def read
diff --git a/lib/chef/chef_fs/file_system/organization_members_entry.rb b/lib/chef/chef_fs/file_system/organization_members_entry.rb
index 94393b341f..40042a9cbc 100644
--- a/lib/chef/chef_fs/file_system/organization_members_entry.rb
+++ b/lib/chef/chef_fs/file_system/organization_members_entry.rb
@@ -39,9 +39,9 @@ class Chef
members = minimize_value(_read_json)
(desired_members - members).each do |member|
begin
- rest.post(File.join(api_path, member), {})
+ rest.post(api_path, 'username' => member)
rescue Net::HTTPServerException => e
- if e.response.code == '404'
+ if %w(404 405).include?(e.response.code)
raise "Chef server at #{api_path} does not allow you to directly add members. Please either upgrade your Chef server or move the users you want into invitations.json instead of members.json."
else
raise
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index 621ce3d489..b2a00a7d01 100644
--- a/lib/chef/client.rb
+++ b/lib/chef/client.rb
@@ -232,6 +232,8 @@ class Chef
# @return Always returns true.
#
def run
+ start_profiling
+
run_error = nil
runlock = RunLock.new(Chef::Config.lockfile)
@@ -271,7 +273,7 @@ class Chef
if Chef::Config[:why_run] == true
# why_run should probably be renamed to why_converge
- Chef::Log.debug("Not running controls in 'why_run' mode - this mode is used to see potential converge changes")
+ Chef::Log.debug("Not running controls in 'why-run' mode - this mode is used to see potential converge changes")
elsif Chef::Config[:audit_mode] != :disabled
audit_error = run_audits(run_context)
end
@@ -284,6 +286,9 @@ class Chef
run_completed_successfully
events.run_completed(node)
+ # keep this inside the main loop to get exception backtraces
+ end_profiling
+
# rebooting has to be the last thing we do, no exceptions.
Chef::Platform::Rebooter.reboot_if_needed!(node)
rescue Exception => run_error
@@ -496,7 +501,7 @@ class Chef
# @api private
#
def policy_builder
- @policy_builder ||= Chef::PolicyBuilder.strategy.new(node_name, ohai.data, json_attribs, override_runlist, events)
+ @policy_builder ||= Chef::PolicyBuilder::Dynamic.new(node_name, ohai.data, json_attribs, override_runlist, events)
end
#
@@ -891,6 +896,28 @@ class Chef
attr_reader :override_runlist
attr_reader :specific_recipes
+ def profiling_prereqs!
+ require 'ruby-prof'
+ rescue LoadError
+ raise "You must have the ruby-prof gem installed in order to use --profile-ruby"
+ end
+
+ def start_profiling
+ return unless Chef::Config[:profile_ruby]
+ profiling_prereqs!
+ RubyProf.start
+ end
+
+ def end_profiling
+ return unless Chef::Config[:profile_ruby]
+ profiling_prereqs!
+ path = Chef::FileCache.create_cache_path("graph_profile.out", false)
+ File.open(path, "w+") do |file|
+ RubyProf::GraphPrinter.new(RubyProf.stop).print(file, {})
+ end
+ Chef::Log.warn("Ruby execution profile dumped to #{path}")
+ end
+
def empty_directory?(path)
!File.exists?(path) || (Dir.entries(path).size <= 2)
end
diff --git a/lib/chef/config.rb b/lib/chef/config.rb
index 6382af14c2..a43985f691 100644
--- a/lib/chef/config.rb
+++ b/lib/chef/config.rb
@@ -55,7 +55,8 @@ class Chef
default :event_loggers do
evt_loggers = []
- if ChefConfig.windows? and not Chef::Platform.windows_server_2003?
+ if ChefConfig.windows? && !(Chef::Platform.windows_server_2003? ||
+ Chef::Platform.windows_nano_server?)
evt_loggers << :win_evt
end
evt_loggers
diff --git a/lib/chef/cookbook/cookbook_version_loader.rb b/lib/chef/cookbook/cookbook_version_loader.rb
index bcbfcbeec8..dbccdbc0a8 100644
--- a/lib/chef/cookbook/cookbook_version_loader.rb
+++ b/lib/chef/cookbook/cookbook_version_loader.rb
@@ -91,7 +91,7 @@ class Chef
remove_ignored_files
if empty?
- Chef::Log.warn "found a directory #{cookbook_name} in the cookbook path, but it contains no cookbook files. skipping."
+ Chef::Log.warn "Found a directory #{cookbook_name} in the cookbook path, but it contains no cookbook files. skipping."
end
@cookbook_settings
end
diff --git a/lib/chef/cookbook/remote_file_vendor.rb b/lib/chef/cookbook/remote_file_vendor.rb
index 9d895b168e..7868430227 100644
--- a/lib/chef/cookbook/remote_file_vendor.rb
+++ b/lib/chef/cookbook/remote_file_vendor.rb
@@ -69,7 +69,7 @@ class Chef
Chef::FileCache.move_to(raw_file.path, cache_filename)
else
Chef::Log.debug("Not fetching #{cache_filename}, as the cache is up to date.")
- Chef::Log.debug("current checksum: #{current_checksum}; manifest checksum: #{found_manifest_record['checksum']})")
+ Chef::Log.debug("Current checksum: #{current_checksum}; manifest checksum: #{found_manifest_record['checksum']})")
end
full_path_cache_filename = Chef::FileCache.load(cache_filename, false)
diff --git a/lib/chef/cookbook_site_streaming_uploader.rb b/lib/chef/cookbook_site_streaming_uploader.rb
index 0302a51165..2be189e942 100644
--- a/lib/chef/cookbook_site_streaming_uploader.rb
+++ b/lib/chef/cookbook_site_streaming_uploader.rb
@@ -26,7 +26,7 @@ require 'openssl'
class Chef
# == Chef::CookbookSiteStreamingUploader
# A streaming multipart HTTP upload implementation. Used to upload cookbooks
- # (in tarball form) to http://cookbooks.opscode.com
+ # (in tarball form) to https://supermarket.chef.io
#
# inspired by http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
class CookbookSiteStreamingUploader
diff --git a/lib/chef/cookbook_version.rb b/lib/chef/cookbook_version.rb
index 8d302eeec2..3cdfd8c10b 100644
--- a/lib/chef/cookbook_version.rb
+++ b/lib/chef/cookbook_version.rb
@@ -51,12 +51,12 @@ class Chef
attr_accessor :metadata_filenames
def status=(new_status)
- Chef::Log.deprecation("Deprecated method `status' called from #{caller(1).first}. This method will be removed")
+ Chef.log_deprecation("Deprecated method `status' called. This method will be removed.")
@status = new_status
end
def status
- Chef::Log.deprecation("Deprecated method `status' called from #{caller(1).first}. This method will be removed")
+ Chef.log_deprecation("Deprecated method `status' called. This method will be removed.")
@status
end
@@ -480,7 +480,7 @@ class Chef
# @deprecated This method was used by the Ruby Chef Server and is no longer
# needed. There is no replacement.
def generate_manifest_with_urls(&url_generator)
- Chef::Log.deprecation("Deprecated method #generate_manifest_with_urls called from #{caller(1).first}")
+ Chef.log_deprecation("Deprecated method #generate_manifest_with_urls.")
rendered_manifest = manifest.dup
COOKBOOK_SEGMENTS.each do |segment|
diff --git a/lib/chef/data_bag.rb b/lib/chef/data_bag.rb
index 8475774fa1..401ba6f63f 100644
--- a/lib/chef/data_bag.rb
+++ b/lib/chef/data_bag.rb
@@ -144,7 +144,7 @@ class Chef
def save
begin
if Chef::Config[:why_run]
- Chef::Log.warn("In whyrun mode, so NOT performing data bag save.")
+ Chef::Log.warn("In why-run mode, so NOT performing data bag save.")
else
create
end
diff --git a/lib/chef/data_bag_item.rb b/lib/chef/data_bag_item.rb
index 9f92e26c50..31c9b69330 100644
--- a/lib/chef/data_bag_item.rb
+++ b/lib/chef/data_bag_item.rb
@@ -170,7 +170,7 @@ class Chef
r = chef_server_rest
begin
if Chef::Config[:why_run]
- Chef::Log.warn("In whyrun mode, so NOT performing data bag item save.")
+ Chef::Log.warn("In why-run mode, so NOT performing data bag item save.")
else
r.put_rest("data/#{data_bag}/#{item_id}", self)
end
diff --git a/lib/chef/deprecation/mixin/template.rb b/lib/chef/deprecation/mixin/template.rb
index 36d18ad90d..58a661c4bd 100644
--- a/lib/chef/deprecation/mixin/template.rb
+++ b/lib/chef/deprecation/mixin/template.rb
@@ -25,7 +25,7 @@ class Chef
# == Deprecation::Provider::Mixin::Template
# This module contains the deprecated functions of
# Chef::Mixin::Template. These functions are refactored to different
- # components. They are frozen and will be removed in Chef 12.
+ # components. They are frozen and will be removed in Chef 13.
#
module Template
@@ -46,4 +46,3 @@ class Chef
end
end
end
-
diff --git a/lib/chef/deprecation/provider/cookbook_file.rb b/lib/chef/deprecation/provider/cookbook_file.rb
index dfbf4a39a4..92f5ce3623 100644
--- a/lib/chef/deprecation/provider/cookbook_file.rb
+++ b/lib/chef/deprecation/provider/cookbook_file.rb
@@ -24,7 +24,7 @@ class Chef
# == Deprecation::Provider::CookbookFile
# This module contains the deprecated functions of
# Chef::Provider::CookbookFile. These functions are refactored to
- # different components. They are frozen and will be removed in Chef 12.
+ # different components. They are frozen and will be removed in Chef 13.
#
module CookbookFile
diff --git a/lib/chef/deprecation/provider/file.rb b/lib/chef/deprecation/provider/file.rb
index 125f31fe10..31038ab3d8 100644
--- a/lib/chef/deprecation/provider/file.rb
+++ b/lib/chef/deprecation/provider/file.rb
@@ -25,7 +25,7 @@ class Chef
# == Deprecation::Provider::File
# This module contains the deprecated functions of
# Chef::Provider::File. These functions are refactored to different
- # components. They are frozen and will be removed in Chef 12.
+ # components. They are frozen and will be removed in Chef 13.
#
module File
diff --git a/lib/chef/deprecation/provider/remote_directory.rb b/lib/chef/deprecation/provider/remote_directory.rb
new file mode 100644
index 0000000000..cc8026ec55
--- /dev/null
+++ b/lib/chef/deprecation/provider/remote_directory.rb
@@ -0,0 +1,52 @@
+#
+# Author:: Serdar Sutay (<serdar@opscode.com>)
+# Copyright:: Copyright (c) 2013-2015 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
+ module Deprecation
+ module Provider
+ module RemoteDirectory
+
+ def directory_root_in_cookbook_cache
+ Chef.log_deprecation "the Chef::Provider::RemoteDirectory#directory_root_in_cookbook_cache method is deprecated"
+
+ @directory_root_in_cookbook_cache ||=
+ begin
+ cookbook = run_context.cookbook_collection[resource_cookbook]
+ cookbook.preferred_filename_on_disk_location(node, :files, source, path)
+ end
+ end
+
+ # List all excluding . and ..
+ def ls(path)
+ files = Dir.glob(::File.join(Chef::Util::PathHelper.escape_glob(path), '**', '*'),
+ ::File::FNM_DOTMATCH)
+
+ # Remove current directory and previous directory
+ files = files.reject do |name|
+ basename = Pathname.new(name).basename().to_s
+ ['.', '..'].include?(basename)
+ end
+
+ # Clean all the paths... this is required because of the join
+ files.map {|f| Chef::Util::PathHelper.cleanpath(f)}
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/deprecation/provider/remote_file.rb b/lib/chef/deprecation/provider/remote_file.rb
index 4452de67cd..c06a5cc695 100644
--- a/lib/chef/deprecation/provider/remote_file.rb
+++ b/lib/chef/deprecation/provider/remote_file.rb
@@ -23,7 +23,7 @@ class Chef
# == Deprecation::Provider::RemoteFile
# This module contains the deprecated functions of
# Chef::Provider::RemoteFile. These functions are refactored to different
- # components. They are frozen and will be removed in Chef 12.
+ # components. They are frozen and will be removed in Chef 13.
#
module RemoteFile
@@ -83,4 +83,3 @@ class Chef
end
end
end
-
diff --git a/lib/chef/deprecation/provider/template.rb b/lib/chef/deprecation/provider/template.rb
index d7a228e97a..34e5f54b7e 100644
--- a/lib/chef/deprecation/provider/template.rb
+++ b/lib/chef/deprecation/provider/template.rb
@@ -25,7 +25,7 @@ class Chef
# == Deprecation::Provider::Template
# This module contains the deprecated functions of
# Chef::Provider::Template. These functions are refactored to different
- # components. They are frozen and will be removed in Chef 12.
+ # components. They are frozen and will be removed in Chef 13.
#
module Template
diff --git a/lib/chef/deprecation/warnings.rb b/lib/chef/deprecation/warnings.rb
index 34f468ff53..0b1ec2d5ed 100644
--- a/lib/chef/deprecation/warnings.rb
+++ b/lib/chef/deprecation/warnings.rb
@@ -25,10 +25,9 @@ class Chef
m = instance_method(name)
define_method(name) do |*args|
message = []
- message << "Method '#{name}' of '#{self.class}' is deprecated. It will be removed in Chef 12."
- message << "Please update your cookbooks accordingly. Accessed from:"
- caller[0..3].each {|l| message << l}
- Chef::Log.deprecation message
+ message << "Method '#{name}' of '#{self.class}' is deprecated. It will be removed in Chef 13."
+ message << "Please update your cookbooks accordingly."
+ Chef.log_deprecation(message)
super(*args)
end
end
diff --git a/lib/chef/dsl/reboot_pending.rb b/lib/chef/dsl/reboot_pending.rb
index c577118dd4..3d84b29ec5 100644
--- a/lib/chef/dsl/reboot_pending.rb
+++ b/lib/chef/dsl/reboot_pending.rb
@@ -49,7 +49,8 @@ class Chef
# The mere existence of the UpdateExeVolatile key should indicate a pending restart for certain updates
# http://support.microsoft.com/kb/832475
- (registry_key_exists?('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile') &&
+ Chef::Platform.windows_server_2003? &&
+ (registry_key_exists?('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile') &&
!registry_get_values('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').select { |v| v[:name] == "Flags" }[0].nil? &&
[1,2,3].include?(registry_get_values('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').select { |v| v[:name] == "Flags" }[0][:data]))
elsif platform?("ubuntu")
diff --git a/lib/chef/dsl/recipe.rb b/lib/chef/dsl/recipe.rb
index 29cfcd478c..26c0ec6768 100644
--- a/lib/chef/dsl/recipe.rb
+++ b/lib/chef/dsl/recipe.rb
@@ -140,8 +140,7 @@ class Chef
# method_missing manually. Not a fan. Not. A. Fan.
#
if respond_to?(method_symbol)
- Chef::Log.deprecation("Calling method_missing(#{method_symbol.inspect}) directly is deprecated in Chef 12 and will be removed in Chef 13.")
- Chef::Log.deprecation("Use public_send() or send() instead.")
+ Chef.log_deprecation("Calling method_missing(#{method_symbol.inspect}) directly is deprecated in Chef 12 and will be removed in Chef 13. Use public_send() or send() instead.")
return send(method_symbol, *args, &block)
end
@@ -150,7 +149,7 @@ class Chef
# never called. DEPRECATED.
#
if run_context.definitions.has_key?(method_symbol.to_sym)
- Chef::Log.deprecation("Definition #{method_symbol} (#{run_context.definitions[method_symbol.to_sym]}) was added to the run_context without calling Chef::DSL::Definitions.add_definition(#{method_symbol.to_sym.inspect}). This will become required in Chef 13.")
+ Chef.log_deprecation("Definition #{method_symbol} (#{run_context.definitions[method_symbol.to_sym]}) was added to the run_context without calling Chef::DSL::Definitions.add_definition(#{method_symbol.to_sym.inspect}). This will become required in Chef 13.")
Chef::DSL::Definitions.add_definition(method_symbol)
return send(method_symbol, *args, &block)
end
diff --git a/lib/chef/dsl/resources.rb b/lib/chef/dsl/resources.rb
index f15beaeab0..49588ed516 100644
--- a/lib/chef/dsl/resources.rb
+++ b/lib/chef/dsl/resources.rb
@@ -11,14 +11,14 @@ class Chef
begin
module_eval(<<-EOM, __FILE__, __LINE__+1)
def #{dsl_name}(*args, &block)
- Chef::Log.deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (\#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: \#{args}") if args.size > 1
+ Chef.log_deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (\#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: \#{args}") if args.size > 1
declare_resource(#{dsl_name.inspect}, args[0], caller[0], &block)
end
EOM
rescue SyntaxError
# Handle the case where dsl_name has spaces, etc.
define_method(dsl_name.to_sym) do |*args, &block|
- Chef::Log.deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: #{args}") if args.size > 1
+ Chef.log_deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: #{args}") if args.size > 1
declare_resource(dsl_name, args[0], caller[0], &block)
end
end
diff --git a/lib/chef/event_dispatch/base.rb b/lib/chef/event_dispatch/base.rb
index 0ae5101029..585a3db174 100644
--- a/lib/chef/event_dispatch/base.rb
+++ b/lib/chef/event_dispatch/base.rb
@@ -47,14 +47,19 @@ class Chef
def ohai_completed(node)
end
- # Already have a client key, assuming this node has registered.
+ # Announce that we're not going to register the client. Generally because
+ # we already have the private key, or because we're deliberately not using
+ # a key.
def skipping_registration(node_name, config)
end
- # About to attempt to register as +node_name+
+ # About to attempt to create a private key registered to the server with
+ # client +node_name+.
def registration_start(node_name, config)
end
+ # Successfully created the private key and registered this client with the
+ # server.
def registration_completed
end
@@ -340,7 +345,6 @@ class Chef
def resource_completed(resource)
end
-
# A stream has opened.
def stream_opened(stream, options = {})
end
@@ -376,8 +380,12 @@ class Chef
def whyrun_assumption(action, resource, message)
end
- ## TODO: deprecation warning. this way we can queue them up and present
- # them all at once.
+ # Emit a message about something being deprecated.
+ def deprecation(message, location=caller(2..2)[0])
+ end
+
+ def run_list_expanded(run_list_expansion)
+ end
# An uncategorized message. This supports the case that a user needs to
# pass output that doesn't fit into one of the callbacks above. Note that
diff --git a/lib/chef/event_dispatch/dispatcher.rb b/lib/chef/event_dispatch/dispatcher.rb
index 9e17d78507..f3e55539a9 100644
--- a/lib/chef/event_dispatch/dispatcher.rb
+++ b/lib/chef/event_dispatch/dispatcher.rb
@@ -25,28 +25,33 @@ class Chef
# define the forwarding in one go:
#
- # Define a method that will be forwarded to all
- def self.def_forwarding_method(method_name)
- define_method(method_name) do |*args|
- @subscribers.each do |s|
- # Skip new/unsupported event names.
- if s.respond_to?(method_name)
- mth = s.method(method_name)
- # Anything with a *args is arity -1, so use all arguments.
- arity = mth.arity < 0 ? args.length : mth.arity
- # Trim arguments to match what the subscriber expects to allow
- # adding new arguments without breaking compat.
- mth.call(*args.take(arity))
- end
+ def call_subscribers(method_name, *args)
+ @subscribers.each do |s|
+ # Skip new/unsupported event names.
+ next if !s.respond_to?(method_name)
+ mth = s.method(method_name)
+ # Trim arguments to match what the subscriber expects to allow
+ # adding new arguments without breaking compat.
+ if mth.arity < args.size && mth.arity >= 0
+ mth.call(*args.take(mth.arity))
+ else
+ mth.call(*args)
end
end
end
(Base.instance_methods - Object.instance_methods).each do |method_name|
- def_forwarding_method(method_name)
+ class_eval <<-EOM
+ def #{method_name}(*args)
+ call_subscribers(#{method_name.inspect}, *args)
+ end
+ EOM
end
+ # Special case deprecation, since it needs to know its caller
+ def deprecation(message, location=caller(2..2)[0])
+ call_subscribers(:deprecation, message, location)
+ end
end
end
end
-
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index e3649c068b..6e7ff2e24a 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -105,7 +105,7 @@ class Chef
class VerificationNotFound < RuntimeError; end
class InvalidEventType < ArgumentError; end
class MultipleIdentityError < RuntimeError; end
- # Used in Resource::ActionProvider#load_current_resource to denote that
+ # Used in Resource::ActionClass#load_current_resource to denote that
# the resource doesn't actually exist (for example, the file does not exist)
class CurrentValueDoesNotExist < RuntimeError; end
@@ -116,6 +116,8 @@ class Chef
end
end
+ class InvalidPolicybuilderCall < ArgumentError; end
+
class InvalidResourceSpecification < ArgumentError; end
class SolrConnectionError < RuntimeError; end
class IllegalChecksumRevert < RuntimeError; end
diff --git a/lib/chef/file_access_control/unix.rb b/lib/chef/file_access_control/unix.rb
index c53d832414..8178d5fbf2 100644
--- a/lib/chef/file_access_control/unix.rb
+++ b/lib/chef/file_access_control/unix.rb
@@ -79,18 +79,18 @@ class Chef
def should_update_owner?
if target_uid.nil?
# the user has not specified a permission on the new resource, so we never manage it with FAC
- Chef::Log.debug("found target_uid == nil, so no owner was specified on resource, not managing owner")
+ Chef::Log.debug("Found target_uid == nil, so no owner was specified on resource, not managing owner")
return false
elsif current_uid.nil?
# the user has specified a permission, and we are creating a file, so always enforce permissions
- Chef::Log.debug("found current_uid == nil, so we are creating a new file, updating owner")
+ Chef::Log.debug("Found current_uid == nil, so we are creating a new file, updating owner")
return true
elsif target_uid != current_uid
# the user has specified a permission, and it does not match the file, so fix the permission
- Chef::Log.debug("found target_uid != current_uid, updating owner")
+ Chef::Log.debug("Found target_uid != current_uid, updating owner")
return true
else
- Chef::Log.debug("found target_uid == current_uid, not updating owner")
+ Chef::Log.debug("Found target_uid == current_uid, not updating owner")
# the user has specified a permission, but it matches the file, so behave idempotently
return false
end
@@ -138,18 +138,18 @@ class Chef
def should_update_group?
if target_gid.nil?
# the user has not specified a permission on the new resource, so we never manage it with FAC
- Chef::Log.debug("found target_gid == nil, so no group was specified on resource, not managing group")
+ Chef::Log.debug("Found target_gid == nil, so no group was specified on resource, not managing group")
return false
elsif current_gid.nil?
# the user has specified a permission, and we are creating a file, so always enforce permissions
- Chef::Log.debug("found current_gid == nil, so we are creating a new file, updating group")
+ Chef::Log.debug("Found current_gid == nil, so we are creating a new file, updating group")
return true
elsif target_gid != current_gid
# the user has specified a permission, and it does not match the file, so fix the permission
- Chef::Log.debug("found target_gid != current_gid, updating group")
+ Chef::Log.debug("Found target_gid != current_gid, updating group")
return true
else
- Chef::Log.debug("found target_gid == current_gid, not updating group")
+ Chef::Log.debug("Found target_gid == current_gid, not updating group")
# the user has specified a permission, but it matches the file, so behave idempotently
return false
end
@@ -187,20 +187,20 @@ class Chef
def should_update_mode?
if target_mode.nil?
# the user has not specified a permission on the new resource, so we never manage it with FAC
- Chef::Log.debug("found target_mode == nil, so no mode was specified on resource, not managing mode")
+ Chef::Log.debug("Found target_mode == nil, so no mode was specified on resource, not managing mode")
return false
elsif current_mode.nil?
# the user has specified a permission, and we are creating a file, so always enforce permissions
- Chef::Log.debug("found current_mode == nil, so we are creating a new file, updating mode")
+ Chef::Log.debug("Found current_mode == nil, so we are creating a new file, updating mode")
return true
elsif target_mode != current_mode
# the user has specified a permission, and it does not match the file, so fix the permission
- Chef::Log.debug("found target_mode != current_mode, updating mode")
+ Chef::Log.debug("Found target_mode != current_mode, updating mode")
return true
elsif suid_bit_set? and (should_update_group? or should_update_owner?)
return true
else
- Chef::Log.debug("found target_mode == current_mode, not updating mode")
+ Chef::Log.debug("Found target_mode == current_mode, not updating mode")
# the user has specified a permission, but it matches the file, so behave idempotently
return false
end
diff --git a/lib/chef/file_content_management/deploy/cp.rb b/lib/chef/file_content_management/deploy/cp.rb
index c6b1d6cd11..ea378c2e5d 100644
--- a/lib/chef/file_content_management/deploy/cp.rb
+++ b/lib/chef/file_content_management/deploy/cp.rb
@@ -34,12 +34,12 @@ class Chef
#
class Cp
def create(file)
- Chef::Log.debug("touching #{file} to create it")
+ Chef::Log.debug("Touching #{file} to create it")
FileUtils.touch(file)
end
def deploy(src, dst)
- Chef::Log.debug("copying temporary file #{src} into place at #{dst}")
+ Chef::Log.debug("Copying temporary file #{src} into place at #{dst}")
FileUtils.cp(src, dst)
end
end
diff --git a/lib/chef/file_content_management/deploy/mv_unix.rb b/lib/chef/file_content_management/deploy/mv_unix.rb
index 758c594e50..9712486424 100644
--- a/lib/chef/file_content_management/deploy/mv_unix.rb
+++ b/lib/chef/file_content_management/deploy/mv_unix.rb
@@ -30,19 +30,19 @@ class Chef
def create(file)
# this is very simple, but it ensures that ownership and file modes take
# good defaults, in particular mode needs to obey umask on create
- Chef::Log.debug("touching #{file} to create it")
+ Chef::Log.debug("Touching #{file} to create it")
FileUtils.touch(file)
end
def deploy(src, dst)
# we are only responsible for content so restore the dst files perms
- Chef::Log.debug("reading modes from #{dst} file")
+ Chef::Log.debug("Reading modes from #{dst} file")
stat = ::File.stat(dst)
mode = stat.mode & 07777
uid = stat.uid
gid = stat.gid
- Chef::Log.debug("applying mode = #{mode.to_s(8)}, uid = #{uid}, gid = #{gid} to #{src}")
+ Chef::Log.debug("Applying mode = #{mode.to_s(8)}, uid = #{uid}, gid = #{gid} to #{src}")
# i own the inode, so should be able to at least chmod it
::File.chmod(mode, src)
@@ -67,7 +67,7 @@ class Chef
Chef::Log.warn("Could not set gid = #{gid} on #{src}, file modes not preserved")
end
- Chef::Log.debug("moving temporary file #{src} into place at #{dst}")
+ Chef::Log.debug("Moving temporary file #{src} into place at #{dst}")
FileUtils.mv(src, dst)
end
end
diff --git a/lib/chef/file_content_management/deploy/mv_windows.rb b/lib/chef/file_content_management/deploy/mv_windows.rb
index 0d16da9717..e2951dba4c 100644
--- a/lib/chef/file_content_management/deploy/mv_windows.rb
+++ b/lib/chef/file_content_management/deploy/mv_windows.rb
@@ -35,7 +35,7 @@ class Chef
ACL = Security::ACL
def create(file)
- Chef::Log.debug("touching #{file} to create it")
+ Chef::Log.debug("Touching #{file} to create it")
FileUtils.touch(file)
end
diff --git a/lib/chef/file_content_management/tempfile.rb b/lib/chef/file_content_management/tempfile.rb
index 2dde0ce21b..6e1624f9a4 100644
--- a/lib/chef/file_content_management/tempfile.rb
+++ b/lib/chef/file_content_management/tempfile.rb
@@ -49,7 +49,7 @@ class Chef
end
end
- raise Chef::Exceptions::FileContentStagingError(errors) if tf.nil?
+ raise Chef::Exceptions::FileContentStagingError, errors if tf.nil?
# We always process the tempfile in binmode so that we
# preserve the line endings of the content.
diff --git a/lib/chef/formatters/base.rb b/lib/chef/formatters/base.rb
index c901068aa0..d3756ef00c 100644
--- a/lib/chef/formatters/base.rb
+++ b/lib/chef/formatters/base.rb
@@ -212,6 +212,9 @@ class Chef
file_load_failed(path, exception)
end
+ def deprecation(message, location=caller(2..2)[0])
+ Chef::Log.deprecation("#{message} at #{location}")
+ end
end
diff --git a/lib/chef/formatters/doc.rb b/lib/chef/formatters/doc.rb
index a5d7e210c5..70108f547b 100644
--- a/lib/chef/formatters/doc.rb
+++ b/lib/chef/formatters/doc.rb
@@ -29,6 +29,18 @@ class Chef
end_time - start_time
end
+ def pretty_elapsed_time
+ time = elapsed_time
+ if time < 60 then
+ message = Time.at(time).utc.strftime("%S seconds")
+ elsif time < 3600 then
+ message = Time.at(time).utc.strftime("%M minutes %S seconds")
+ else
+ message = Time.at(time).utc.strftime("%H hours %M minutes %S seconds")
+ end
+ message
+ end
+
def run_start(version)
puts_line "Starting Chef Client, version #{version}"
end
@@ -43,10 +55,30 @@ class Chef
def run_completed(node)
@end_time = Time.now
+ # Print out deprecations.
+ if !deprecations.empty?
+ puts_line ""
+ puts_line "Deprecated features used!"
+ deprecations.each do |message, locations|
+ if locations.size == 1
+ puts_line " #{message} at #{locations.size} location:"
+ else
+ puts_line " #{message} at #{locations.size} locations:"
+ end
+ locations.each do |location|
+ prefix = " - "
+ Array(location).each do |line|
+ puts_line "#{prefix}#{line}"
+ prefix = " "
+ end
+ end
+ end
+ puts_line ""
+ end
if Chef::Config[:why_run]
puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources would have been updated"
else
- puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources updated in #{elapsed_time} seconds"
+ puts_line "Chef Client finished, #{@updated_resources}/#{total_resources} resources updated in #{pretty_elapsed_time}"
if total_audits > 0
puts_line " #{successful_audits}/#{total_audits} controls succeeded"
end
@@ -58,7 +90,7 @@ class Chef
if Chef::Config[:why_run]
puts_line "Chef Client failed. #{@updated_resources} resources would have been updated"
else
- puts_line "Chef Client failed. #{@updated_resources} resources updated in #{elapsed_time} seconds"
+ puts_line "Chef Client failed. #{@updated_resources} resources updated in #{pretty_elapsed_time}"
if total_audits > 0
puts_line " #{successful_audits} controls succeeded"
end
@@ -336,6 +368,16 @@ class Chef
end
end
+ def deprecation(message, location=caller(2..2)[0])
+ if Chef::Config[:treat_deprecation_warnings_as_errors]
+ super
+ end
+
+ # Save deprecations to the screen until the end
+ deprecations[message] ||= Set.new
+ deprecations[message] << location
+ end
+
def indent
indent_by(2)
end
@@ -343,6 +385,12 @@ class Chef
def unindent
indent_by(-2)
end
+
+ protected
+
+ def deprecations
+ @deprecations ||= {}
+ end
end
end
end
diff --git a/lib/chef/formatters/error_inspectors/compile_error_inspector.rb b/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
index fe418ed485..621fadce40 100644
--- a/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
+++ b/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
@@ -47,11 +47,31 @@ class Chef
if exception_message_modifying_frozen?
msg = <<-MESSAGE
- Chef calls the freeze method on certain ruby objects to prevent
- pollution across multiple instances. Specifically, resource
- properties have frozen default values to avoid modifying the
- property for all instances of a resource. Try modifying the
- particular instance variable or using an instance accessor instead.
+ Ruby objects are often frozen to prevent further modifications
+ when they would negatively impact the process (e.g. values inside
+ Ruby's ENV class) or to prevent polluting other objects when default
+ values are passed by reference to many instances of an object (e.g.
+ the empty Array as a Chef resource default, passed by reference
+ to every instance of the resource).
+
+ Chef uses Object#freeze to ensure the default values of properties
+ inside Chef resources are not modified, so that when a new instance
+ of a Chef resource is created, and Object#dup copies values by
+ reference, the new resource is not receiving a default value that
+ has been by a previous instance of that resource.
+
+ Instead of modifying an object that contains a default value for all
+ instances of a Chef resource, create a new object and assign it to
+ the resource's parameter, e.g.:
+
+ fruit_basket = resource(:fruit_basket, 'default')
+
+ # BAD: modifies 'contents' object for all new fruit_basket instances
+ fruit_basket.contents << 'apple'
+
+ # GOOD: allocates new array only owned by this fruit_basket instance
+ fruit_basket.contents %w(apple)
+
MESSAGE
error_description.section("Additional information:", msg.gsub(/^ {6}/, ''))
@@ -88,7 +108,7 @@ class Chef
def culprit_backtrace_entry
@culprit_backtrace_entry ||= begin
bt_entry = filtered_bt.first
- Chef::Log.debug("backtrace entry for compile error: '#{bt_entry}'")
+ Chef::Log.debug("Backtrace entry for compile error: '#{bt_entry}'")
bt_entry
end
end
@@ -118,7 +138,7 @@ class Chef
begin
filters = Array(Chef::Config.cookbook_path).map {|p| /^#{Regexp.escape(p)}/i }
r = exception.backtrace.select {|line| filters.any? {|filter| line =~ filter }}
- Chef::Log.debug("filtered backtrace of compile error: #{r.join(",")}")
+ Chef::Log.debug("Filtered backtrace of compile error: #{r.join(",")}")
r
end
end
diff --git a/lib/chef/http/decompressor.rb b/lib/chef/http/decompressor.rb
index e1d776da60..a61f510e7d 100644
--- a/lib/chef/http/decompressor.rb
+++ b/lib/chef/http/decompressor.rb
@@ -79,10 +79,10 @@ class Chef
else
case response[CONTENT_ENCODING]
when GZIP
- Chef::Log.debug "decompressing gzip response"
+ Chef::Log.debug "Decompressing gzip response"
Zlib::Inflate.new(Zlib::MAX_WBITS + 16).inflate(response.body)
when DEFLATE
- Chef::Log.debug "decompressing deflate response"
+ Chef::Log.debug "Decompressing deflate response"
Zlib::Inflate.inflate(response.body)
else
response.body
diff --git a/lib/chef/json_compat.rb b/lib/chef/json_compat.rb
index d0b3b4c7f8..5e9f29a60b 100644
--- a/lib/chef/json_compat.rb
+++ b/lib/chef/json_compat.rb
@@ -41,6 +41,7 @@ class Chef
CHEF_RESOURCECOLLECTION = "Chef::ResourceCollection".freeze
CHEF_RESOURCESET = "Chef::ResourceCollection::ResourceSet".freeze
CHEF_RESOURCELIST = "Chef::ResourceCollection::ResourceList".freeze
+ CHEF_RUNLISTEXPANSION = "Chef::RunListExpansion".freeze
class <<self
diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb
index 5b29591fcc..93236225a2 100644
--- a/lib/chef/knife/bootstrap.rb
+++ b/lib/chef/knife/bootstrap.rb
@@ -143,6 +143,22 @@ class Chef
:proc => lambda { |o| o.split(/[\s,]+/) },
:default => []
+ option :policy_name,
+ :long => "--policy-name POLICY_NAME",
+ :description => "Policyfile name to use (--policy-group must also be given)",
+ :default => nil
+
+ option :policy_group,
+ :long => "--policy-group POLICY_GROUP",
+ :description => "Policy group name to use (--policy-name must also be given)",
+ :default => nil
+
+ option :tags,
+ :long => "--tags TAGS",
+ :description => "Comma separated list of tags to apply to the node",
+ :proc => lambda { |o| o.split(/[\s,]+/) },
+ :default => []
+
option :first_boot_attributes,
:short => "-j JSON_ATTRIBS",
:long => "--json-attributes",
@@ -309,6 +325,7 @@ class Chef
def run
validate_name_args!
+ validate_options!
$stdout.sync = true
@@ -357,6 +374,17 @@ class Chef
end
end
+ def validate_options!
+ if incomplete_policyfile_options?
+ ui.error("--policy-name and --policy-group must be specified together")
+ exit 1
+ elsif policyfile_and_run_list_given?
+ ui.error("Policyfile options and --run-list are exclusive")
+ exit 1
+ end
+ true
+ end
+
def knife_ssh
ssh = Chef::Knife::Ssh.new
ssh.ui = ui
@@ -389,6 +417,19 @@ class Chef
command
end
+
+ private
+
+ # True if policy_name and run_list are both given
+ def policyfile_and_run_list_given?
+ !config[:run_list].empty? && !!config[:policy_name]
+ end
+
+ # True if one of policy_name or policy_group was given, but not both
+ def incomplete_policyfile_options?
+ (!!config[:policy_name] ^ config[:policy_group])
+ end
+
end
end
end
diff --git a/lib/chef/knife/bootstrap/client_builder.rb b/lib/chef/knife/bootstrap/client_builder.rb
index 304b06b8b7..7eb1e22628 100644
--- a/lib/chef/knife/bootstrap/client_builder.rb
+++ b/lib/chef/knife/bootstrap/client_builder.rb
@@ -91,6 +91,16 @@ class Chef
knife_config[:run_list]
end
+ # @return [String] policy_name from the knife_config
+ def policy_name
+ knife_config[:policy_name]
+ end
+
+ # @return [String] policy_group from the knife_config
+ def policy_group
+ knife_config[:policy_group]
+ end
+
# @return [Hash,Array] Object representation of json first-boot attributes from the knife_config
def first_boot_attributes
knife_config[:first_boot_attributes]
@@ -141,6 +151,11 @@ class Chef
node.run_list(normalized_run_list)
node.normal_attrs = first_boot_attributes if first_boot_attributes
node.environment(environment) if environment
+ node.policy_name = policy_name if policy_name
+ node.policy_group = policy_group if policy_group
+ (knife_config[:tags] || []).each do |tag|
+ node.tags << tag
+ end
node
end
end
diff --git a/lib/chef/knife/bootstrap/templates/README.md b/lib/chef/knife/bootstrap/templates/README.md
index 13a0fe7ada..b5bca25d8e 100644
--- a/lib/chef/knife/bootstrap/templates/README.md
+++ b/lib/chef/knife/bootstrap/templates/README.md
@@ -1,12 +1,11 @@
This directory contains bootstrap templates which can be used with the -d flag
to 'knife bootstrap' to install Chef in different ways. To simplify installation,
and reduce the matrix of common installation patterns to support, we have
-standardized on the [Omnibus](https://github.com/opscode/omnibus-ruby) built installation
+standardized on the [Omnibus](https://github.com/chef/omnibus) built installation
packages.
The 'chef-full' template downloads a script which is used to determine the correct
-Omnibus package for this system from the [Omnitruck](https://github.com/opscode/opscode-omnitruck) API. All other templates in this directory are deprecated and will be removed
-in the future.
+Omnibus package for this system from the [Omnitruck](https://docs.chef.io/api_omnitruck.html) API.
You can still utilize custom bootstrap templates on your system if your installation
-needs are unique. Additional information can be found on the [docs site](http://docs.opscode.com/knife_bootstrap.html#custom-templates). \ No newline at end of file
+needs are unique. Additional information can be found on the [docs site](https://docs.chef.io/knife_bootstrap.html#custom-templates).
diff --git a/lib/chef/knife/cookbook_create.rb b/lib/chef/knife/cookbook_create.rb
index e17a54079f..97f6e65d3c 100644
--- a/lib/chef/knife/cookbook_create.rb
+++ b/lib/chef/knife/cookbook_create.rb
@@ -48,7 +48,7 @@ class Chef
option :cookbook_copyright,
:short => "-C COPYRIGHT",
:long => "--copyright COPYRIGHT",
- :description => "Name of Copyright holder"
+ :description => "Name of copyright holder"
option :cookbook_email,
:short => "-m EMAIL",
diff --git a/lib/chef/knife/cookbook_site_download.rb b/lib/chef/knife/cookbook_site_download.rb
index c2d72ef8da..3e586e6542 100644
--- a/lib/chef/knife/cookbook_site_download.rb
+++ b/lib/chef/knife/cookbook_site_download.rb
@@ -84,7 +84,7 @@ class Chef
end
def download_cookbook
- ui.info "Downloading #{@name_args[0]} from the cookbooks site at version #{version} to #{download_location}"
+ ui.info "Downloading #{@name_args[0]} from Supermarket at version #{version} to #{download_location}"
noauth_rest.sign_on_redirect = false
tf = noauth_rest.get_rest desired_cookbook_data["file"], true
diff --git a/lib/chef/knife/cookbook_site_install.rb b/lib/chef/knife/cookbook_site_install.rb
index d0ab6da3ef..aee8b7fa94 100644
--- a/lib/chef/knife/cookbook_site_install.rb
+++ b/lib/chef/knife/cookbook_site_install.rb
@@ -93,7 +93,7 @@ class Chef
# TODO: it'd be better to store these outside the cookbook repo and
# keep them around, e.g., in ~/Library/Caches on OS X.
- ui.info("removing downloaded tarball")
+ ui.info("Removing downloaded tarball")
File.unlink(upstream_file)
if @repo.finalize_updates_to(@cookbook_name, downloader.version)
diff --git a/lib/chef/knife/cookbook_site_share.rb b/lib/chef/knife/cookbook_site_share.rb
index efd2e7f129..beb98b71b8 100644
--- a/lib/chef/knife/cookbook_site_share.rb
+++ b/lib/chef/knife/cookbook_site_share.rb
@@ -48,7 +48,7 @@ class Chef
:short => '-n',
:boolean => true,
:default => false,
- :description => "Don't take action, only print what files will be upload to SuperMarket."
+ :description => "Don't take action, only print what files will be uploaded to Supermarket."
def run
config[:cookbook_path] ||= Chef::Config[:cookbook_path]
@@ -94,7 +94,7 @@ class Chef
Chef::Log.debug("Removing local staging directory at #{tmp_cookbook_dir}")
FileUtils.rm_rf tmp_cookbook_dir
rescue => e
- ui.error("Error uploading cookbook #{cookbook_name} to the Opscode Cookbook Site: #{e.message}. Increase log verbosity (-VV) for more information.")
+ ui.error("Error uploading cookbook #{cookbook_name} to Supermarket: #{e.message}. Increase log verbosity (-VV) for more information.")
Chef::Log.debug("\n#{e.backtrace.join("\n")}")
exit(1)
end
@@ -108,15 +108,15 @@ class Chef
def get_category(cookbook_name)
begin
- data = noauth_rest.get_rest("http://cookbooks.opscode.com/api/v1/cookbooks/#{@name_args[0]}")
+ data = noauth_rest.get_rest("https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}")
if !data["category"] && data["error_code"]
- ui.fatal("Received an error from the Opscode Cookbook site: #{data["error_code"]}. On the first time you upload it, you are required to specify the category you want to share this cookbook to.")
+ 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 Opscode Cookbook Site: #{e.message}. Increase log verbosity (-VV) for more information.")
+ 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
@@ -136,7 +136,7 @@ class Chef
if http_resp.code.to_i != 201
if res['error_messages']
if res['error_messages'][0] =~ /Version already exists/
- ui.error "The same version of this cookbook already exists on the Opscode Cookbook Site."
+ ui.error "The same version of this cookbook already exists on Supermarket."
exit(1)
else
ui.error "#{res['error_messages'][0]}"
diff --git a/lib/chef/knife/cookbook_site_unshare.rb b/lib/chef/knife/cookbook_site_unshare.rb
index b34282ec32..77bb18322c 100644
--- a/lib/chef/knife/cookbook_site_unshare.rb
+++ b/lib/chef/knife/cookbook_site_unshare.rb
@@ -38,7 +38,7 @@ class Chef
exit 1
end
- confirm "Do you really want to unshare the cookbook #{@cookbook_name}"
+ confirm "Do you really want to unshare all versions of the cookbook #{@cookbook_name}"
begin
rest.delete_rest "https://supermarket.chef.io/api/v1/cookbooks/#{@name_args[0]}"
@@ -48,7 +48,7 @@ class Chef
exit 1
end
- ui.info "Unshared cookbook #{@cookbook_name}"
+ ui.info "Unshared all versions of the cookbook #{@cookbook_name}"
end
end
diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb
index 7197653489..d210b9418f 100644
--- a/lib/chef/knife/core/bootstrap_context.rb
+++ b/lib/chef/knife/core/bootstrap_context.rb
@@ -40,7 +40,7 @@ class Chef
end
def bootstrap_environment
- @chef_config[:environment] || '_default'
+ @chef_config[:environment]
end
def validation_key
@@ -128,7 +128,7 @@ CONFIG
client_path = @chef_config[:chef_client_path] || 'chef-client'
s = "#{client_path} -j /etc/chef/first-boot.json"
s << ' -l debug' if @config[:verbosity] and @config[:verbosity] >= 2
- s << " -E #{bootstrap_environment}"
+ s << " -E #{bootstrap_environment}" unless bootstrap_environment.nil?
s
end
@@ -163,11 +163,19 @@ CONFIG
end
def first_boot
- (@config[:first_boot_attributes] || {}).merge(:run_list => @run_list)
+ (@config[:first_boot_attributes] || {}).tap do |attributes|
+ if @config[:policy_name] && @config[:policy_group]
+ attributes.merge!(:policy_name => @config[:policy_name], :policy_group => @config[:policy_group])
+ else
+ attributes.merge!(:run_list => @run_list)
+ end
+
+ attributes.merge!(:tags => @config[:tags]) if @config[:tags] && !@config[:tags].empty?
+ end
end
private
-
+
# Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
# This string should contain both the commands necessary to both create the files, as well as their content
def trusted_certs_content
diff --git a/lib/chef/knife/core/node_presenter.rb b/lib/chef/knife/core/node_presenter.rb
index d1aab592ef..d9ea8c7669 100644
--- a/lib/chef/knife/core/node_presenter.rb
+++ b/lib/chef/knife/core/node_presenter.rb
@@ -67,7 +67,12 @@ class Chef
result = {}
result["name"] = node.name
- result["chef_environment"] = node.chef_environment
+ if node.policy_name.nil? && node.policy_group.nil?
+ result["chef_environment"] = node.chef_environment
+ else
+ result["policy_name"] = node.policy_name
+ result["policy_group"] = node.policy_group
+ end
result["run_list"] = node.run_list
result["normal"] = node.normal_attrs
@@ -95,11 +100,29 @@ class Chef
summarized=<<-SUMMARY
#{ui.color('Node Name:', :bold)} #{ui.color(node.name, :bold)}
+SUMMARY
+ show_policy = !(node.policy_name.nil? && node.policy_group.nil?)
+ if show_policy
+ summarized << <<-POLICY
+#{key('Policy Name:')} #{node.policy_name}
+#{key('Policy Group:')} #{node.policy_group}
+POLICY
+ else
+ summarized << <<-ENV
#{key('Environment:')} #{node.chef_environment}
+ENV
+ end
+ summarized << <<-SUMMARY
#{key('FQDN:')} #{node[:fqdn]}
#{key('IP:')} #{ip}
#{key('Run List:')} #{node.run_list}
+SUMMARY
+ unless show_policy
+ summarized << <<-ROLES
#{key('Roles:')} #{Array(node[:roles]).join(', ')}
+ROLES
+ end
+ summarized << <<-SUMMARY
#{key('Recipes:')} #{Array(node[:recipes]).join(', ')}
#{key('Platform:')} #{node[:platform]} #{node[:platform_version]}
#{key('Tags:')} #{Array(node[:tags]).join(', ')}
diff --git a/lib/chef/knife/core/subcommand_loader.rb b/lib/chef/knife/core/subcommand_loader.rb
index 1d359ffd53..808e053c40 100644
--- a/lib/chef/knife/core/subcommand_loader.rb
+++ b/lib/chef/knife/core/subcommand_loader.rb
@@ -51,7 +51,7 @@ class Chef
Chef::Log.debug("Using autogenerated hashed command manifest #{plugin_manifest_path}")
Knife::SubcommandLoader::HashedCommandLoader.new(chef_config_dir, plugin_manifest)
elsif custom_manifest?
- Chef::Log.deprecation("Using custom manifest #{plugin_manifest_path} is deprecated. Please use a `knife rehash` autogenerated manifest instead.")
+ Chef.log_deprecation("Using custom manifest #{plugin_manifest_path} is deprecated. Please use a `knife rehash` autogenerated manifest instead.")
Knife::SubcommandLoader::CustomManifestLoader.new(chef_config_dir, plugin_manifest)
else
Knife::SubcommandLoader::GemGlobLoader.new(chef_config_dir)
@@ -84,7 +84,7 @@ class Chef
# Deprecated and un-used instance variable.
@env = env
unless env.nil?
- Chef::Log.deprecation("The env argument to Chef::Knife::SubcommandLoader is deprecated. If you are using env to inject/mock HOME, consider mocking Chef::Util::PathHelper.home instead.")
+ Chef.log_deprecation("The env argument to Chef::Knife::SubcommandLoader is deprecated. If you are using env to inject/mock HOME, consider mocking Chef::Util::PathHelper.home instead.")
end
end
@@ -149,7 +149,7 @@ class Chef
# to get in the past.
#
def subcommand_files
- Chef::Log.deprecation "Using Chef::Knife::SubcommandLoader directly is deprecated.
+ Chef.log_deprecation "Using Chef::Knife::SubcommandLoader directly is deprecated.
Please use Chef::Knife::SubcommandLoader.for_config(chef_config_dir, env)"
@subcommand_files ||= if Chef::Knife::SubcommandLoader.plugin_manifest?
Chef::Knife::SubcommandLoader::CustomManifestLoader.new(chef_config_dir, env).subcommand_files
diff --git a/lib/chef/knife/node_run_list_remove.rb b/lib/chef/knife/node_run_list_remove.rb
index 4b8953a264..ef03c176b8 100644
--- a/lib/chef/knife/node_run_list_remove.rb
+++ b/lib/chef/knife/node_run_list_remove.rb
@@ -42,7 +42,18 @@ class Chef
entries = @name_args[1].split(',').map { |e| e.strip }
end
- entries.each { |e| node.run_list.remove(e) }
+ # iterate over the list of things to remove,
+ # warning if one of them was not found
+ entries.each do |e|
+ if node.run_list.find { |rli| e == rli.to_s }
+ node.run_list.remove(e)
+ else
+ ui.warn "#{e} is not in the run list"
+ unless e =~ /^(recipe|role)\[/
+ ui.warn '(did you forget recipe[] or role[] around it?)'
+ end
+ end
+ end
node.save
diff --git a/lib/chef/knife/search.rb b/lib/chef/knife/search.rb
index 9319d30e7d..014fc8dd87 100644
--- a/lib/chef/knife/search.rb
+++ b/lib/chef/knife/search.rb
@@ -136,7 +136,7 @@ class Chef
def read_cli_args
if config[:query]
if @name_args[1]
- ui.error "please specify query as an argument or an option via -q, not both"
+ ui.error "Please specify query as an argument or an option via -q, not both"
ui.msg opt_parser
exit 1
end
@@ -145,7 +145,7 @@ class Chef
else
case name_args.size
when 0
- ui.error "no query specified"
+ ui.error "No query specified"
ui.msg opt_parser
exit 1
when 1
@@ -160,7 +160,7 @@ class Chef
def fuzzify_query
if @query !~ /:/
- @query = "tags:*#{@query}* OR roles:*#{@query}* OR fqdn:*#{@query}* OR addresses:*#{@query}*"
+ @query = "tags:*#{@query}* OR roles:*#{@query}* OR fqdn:*#{@query}* OR addresses:*#{@query}* OR policy_name:*#{@query}* OR policy_group:*#{@query}*"
end
end
diff --git a/lib/chef/knife/ssh.rb b/lib/chef/knife/ssh.rb
index 68e01cf94f..996f40c91d 100644
--- a/lib/chef/knife/ssh.rb
+++ b/lib/chef/knife/ssh.rb
@@ -132,15 +132,19 @@ class Chef
if config[:ssh_gateway]
gw_host, gw_user = config[:ssh_gateway].split('@').reverse
gw_host, gw_port = gw_host.split(':')
- gw_opts = gw_port ? { :port => gw_port } : {}
+ gw_opts = session_options(gw_host, gw_port, gw_user)
+ user = gw_opts.delete(:user)
- session.via(gw_host, gw_user || config[:ssh_user], gw_opts)
+ begin
+ # Try to connect with a key.
+ session.via(gw_host, user, gw_opts)
+ rescue Net::SSH::AuthenticationFailed
+ prompt = "Enter the password for #{user}@#{gw_host}: "
+ gw_opts[:password] = prompt_for_password(prompt)
+ # Try again with a password.
+ session.via(gw_host, user, gw_opts)
+ end
end
- rescue Net::SSH::AuthenticationFailed
- user = gw_user || config[:ssh_user]
- prompt = "Enter the password for #{user}@#{gw_host}: "
- gw_opts.merge!(:password => prompt_for_password(prompt))
- session.via(gw_host, user, gw_opts)
end
def configure_session
@@ -204,32 +208,50 @@ class Chef
list
end
- def session_from_list(list)
- list.each do |item|
- host, ssh_port = item
- Chef::Log.debug("Adding #{host}")
- session_opts = {}
-
- ssh_config = Net::SSH.configuration_for(host)
-
+ # Net::SSH session options hash for global options. These should be
+ # options that will apply to the gateway connection in addition to the
+ # main one.
+ #
+ # @since 12.5.0
+ # @param host [String] Hostname for this session.
+ # @param port [String] SSH port for this session.
+ # @param user [String] Optional username for this session.
+ # @return [Hash<Symbol, Object>]
+ def session_options(host, port, user=nil)
+ ssh_config = Net::SSH.configuration_for(host)
+ {}.tap do |opts|
# Chef::Config[:knife][:ssh_user] is parsed in #configure_user and written to config[:ssh_user]
- user = config[:ssh_user] || ssh_config[:user]
- hostspec = user ? "#{user}@#{host}" : host
- session_opts[:keys] = File.expand_path(config[:identity_file]) if config[:identity_file]
- session_opts[:keys_only] = true if config[:identity_file]
- session_opts[:password] = config[:ssh_password] if config[:ssh_password]
- session_opts[:forward_agent] = config[:forward_agent]
- session_opts[:port] = config[:ssh_port] ||
- ssh_port || # Use cloud port if available
- Chef::Config[:knife][:ssh_port] ||
- ssh_config[:port]
- session_opts[:logger] = Chef::Log.logger if Chef::Log.level == :debug
-
+ opts[:user] = user || config[:ssh_user] || ssh_config[:user]
+ if config[:identity_file]
+ opts[:keys] = File.expand_path(config[:identity_file])
+ opts[:keys_only] = true
+ elsif config[:ssh_password]
+ opts[:password] = config[:ssh_password]
+ end
+ # Don't set the keys to nil if we don't have them.
+ forward_agent = config[:forward_agent] || ssh_config[:forward_agent]
+ opts[:forward_agent] = forward_agent unless forward_agent.nil?
+ port ||= ssh_config[:port]
+ opts[:port] = port unless port.nil?
+ opts[:logger] = Chef::Log.logger if Chef::Log.level == :debug
if !config[:host_key_verify]
- session_opts[:paranoid] = false
- session_opts[:user_known_hosts_file] = "/dev/null"
+ opts[:paranoid] = false
+ opts[:user_known_hosts_file] = '/dev/null'
end
+ end
+ end
+ def session_from_list(list)
+ list.each do |item|
+ host, ssh_port = item
+ Chef::Log.debug("Adding #{host}")
+ session_opts = session_options(host, ssh_port)
+ # Handle port overrides for the main connection.
+ session_opts[:port] = Chef::Config[:knife][:ssh_port] if Chef::Config[:knife][:ssh_port]
+ session_opts[:port] = config[:ssh_port] if config[:ssh_port]
+ # Create the hostspec.
+ hostspec = session_opts[:user] ? "#{session_opts.delete(:user)}@#{host}" : host
+ # Connect a new session on the multi.
session.use(hostspec, session_opts)
@longest = host.length if host.length > @longest
@@ -405,7 +427,7 @@ class Chef
begin
require 'appscript'
rescue LoadError
- STDERR.puts "you need the rb-appscript gem to use knife ssh macterm. `(sudo) gem install rb-appscript` to install"
+ STDERR.puts "You need the rb-appscript gem to use knife ssh macterm. `(sudo) gem install rb-appscript` to install"
raise
end
@@ -445,7 +467,7 @@ class Chef
session.servers_for.each do |server|
cssh_cmd << " #{server.user ? "#{server.user}@#{server.host}" : server.host}"
end
- Chef::Log.debug("starting cssh session with command: #{cssh_cmd}")
+ Chef::Log.debug("Starting cssh session with command: #{cssh_cmd}")
exec(cssh_cmd)
end
diff --git a/lib/chef/local_mode.rb b/lib/chef/local_mode.rb
index 79fb750dd8..fbb72cd6cb 100644
--- a/lib/chef/local_mode.rb
+++ b/lib/chef/local_mode.rb
@@ -15,6 +15,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
require 'chef/config'
+if Chef::Platform.windows?
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.1')
+ require 'chef/monkey_patches/webrick-utils'
+ end
+end
class Chef
module LocalMode
diff --git a/lib/chef/log.rb b/lib/chef/log.rb
index 9b27778a40..bf846c2072 100644
--- a/lib/chef/log.rb
+++ b/lib/chef/log.rb
@@ -37,7 +37,11 @@ class Chef
end
end
- def self.deprecation(msg=nil, &block)
+ def self.deprecation(msg=nil, location=caller(2..2)[0], &block)
+ if msg
+ msg << " at #{Array(location).join("\n")}"
+ msg = msg.join("") if msg.respond_to?(:join)
+ end
if Chef::Config[:treat_deprecation_warnings_as_errors]
error(msg, &block)
raise Chef::Exceptions::DeprecatedFeatureError.new(msg)
diff --git a/lib/chef/mixin/deprecation.rb b/lib/chef/mixin/deprecation.rb
index a3eacf75cb..562af541bd 100644
--- a/lib/chef/mixin/deprecation.rb
+++ b/lib/chef/mixin/deprecation.rb
@@ -102,20 +102,20 @@ class Chef
def deprecated_attr_reader(name, alternative, level=:warn)
define_method(name) do
- Chef::Log.deprecation("#{self.class}.#{name} is deprecated. Support will be removed in a future release.")
- Chef::Log.deprecation(alternative)
- Chef::Log.deprecation("Called from:")
- caller[0..3].each {|c| Chef::Log.deprecation(c)}
+ Chef.log_deprecation("#{self.class}.#{name} is deprecated. Support will be removed in a future release.")
+ Chef.log_deprecation(alternative)
+ Chef.log_deprecation("Called from:")
+ caller[0..3].each {|c| Chef.log_deprecation(c)}
instance_variable_get("@#{name}")
end
end
def deprecated_attr_writer(name, alternative, level=:warn)
define_method("#{name}=") do |value|
- Chef::Log.deprecation("Writing to #{self.class}.#{name} with #{name}= is deprecated. Support will be removed in a future release.")
- Chef::Log.deprecation(alternative)
- Chef::Log.deprecation("Called from:")
- caller[0..3].each {|c| Chef::Log.deprecation(c)}
+ Chef.log_deprecation("Writing to #{self.class}.#{name} with #{name}= is deprecated. Support will be removed in a future release.")
+ Chef.log_deprecation(alternative)
+ Chef.log_deprecation("Called from:")
+ caller[0..3].each {|c| Chef.log_deprecation(c)}
instance_variable_set("@#{name}", value)
end
end
diff --git a/lib/chef/mixin/properties.rb b/lib/chef/mixin/properties.rb
new file mode 100644
index 0000000000..85abe4427e
--- /dev/null
+++ b/lib/chef/mixin/properties.rb
@@ -0,0 +1,302 @@
+require 'chef/delayed_evaluator'
+require 'chef/mixin/params_validate'
+require 'chef/property'
+
+class Chef
+ module Mixin
+ module Properties
+ module ClassMethods
+ #
+ # The list of properties defined on this resource.
+ #
+ # Everything defined with `property` is in this list.
+ #
+ # @param include_superclass [Boolean] `true` to include properties defined
+ # on superclasses; `false` or `nil` to return the list of properties
+ # directly on this class.
+ #
+ # @return [Hash<Symbol,Property>] The list of property names and types.
+ #
+ def properties(include_superclass=true)
+ if include_superclass
+ result = {}
+ ancestors.reverse_each { |c| result.merge!(c.properties(false)) if c.respond_to?(:properties) }
+ result
+ else
+ @properties ||= {}
+ end
+ end
+
+ #
+ # Create a property on this resource class.
+ #
+ # If a superclass has this property, or if this property has already been
+ # defined by this resource, this will *override* the previous value.
+ #
+ # @param name [Symbol] The name of the property.
+ # @param type [Object,Array<Object>] The type(s) of this property.
+ # If present, this is prepended to the `is` validation option.
+ # @param options [Hash<Symbol,Object>] Validation options.
+ # @option options [Object,Array] :is An object, or list of
+ # objects, that must match the value using Ruby's `===` operator
+ # (`options[:is].any? { |v| v === value }`).
+ # @option options [Object,Array] :equal_to An object, or list
+ # of objects, that must be equal to the value using Ruby's `==`
+ # operator (`options[:is].any? { |v| v == value }`)
+ # @option options [Regexp,Array<Regexp>] :regex An object, or
+ # list of objects, that must match the value with `regex.match(value)`.
+ # @option options [Class,Array<Class>] :kind_of A class, or
+ # list of classes, that the value must be an instance of.
+ # @option options [Hash<String,Proc>] :callbacks A hash of
+ # messages -> procs, all of which match the value. The proc must
+ # return a truthy or falsey value (true means it matches).
+ # @option options [Symbol,Array<Symbol>] :respond_to A method
+ # name, or list of method names, the value must respond to.
+ # @option options [Symbol,Array<Symbol>] :cannot_be A property,
+ # or a list of properties, that the value cannot have (such as `:nil` or
+ # `:empty`). The method with a questionmark at the end is called on the
+ # value (e.g. `value.empty?`). If the value does not have this method,
+ # it is considered valid (i.e. if you don't respond to `empty?` we
+ # assume you are not empty).
+ # @option options [Proc] :coerce A proc which will be called to
+ # transform the user input to canonical form. The value is passed in,
+ # and the transformed value returned as output. Lazy values will *not*
+ # be passed to this method until after they are evaluated. Called in the
+ # context of the resource (meaning you can access other properties).
+ # @option options [Boolean] :required `true` if this property
+ # must be present; `false` otherwise. This is checked after the resource
+ # is fully initialized.
+ # @option options [Boolean] :name_property `true` if this
+ # property defaults to the same value as `name`. Equivalent to
+ # `default: lazy { name }`, except that #property_is_set? will
+ # return `true` if the property is set *or* if `name` is set.
+ # @option options [Boolean] :name_attribute Same as `name_property`.
+ # @option options [Object] :default The value this property
+ # will return if the user does not set one. If this is `lazy`, it will
+ # be run in the context of the instance (and able to access other
+ # properties).
+ # @option options [Boolean] :desired_state `true` if this property is
+ # part of desired state. Defaults to `true`.
+ # @option options [Boolean] :identity `true` if this property
+ # is part of object identity. Defaults to `false`.
+ #
+ # @example Bare property
+ # property :x
+ #
+ # @example With just a type
+ # property :x, String
+ #
+ # @example With just options
+ # property :x, default: 'hi'
+ #
+ # @example With type and options
+ # property :x, String, default: 'hi'
+ #
+ def property(name, type=NOT_PASSED, **options)
+ name = name.to_sym
+
+ options.each { |k,v| options[k.to_sym] = v if k.is_a?(String) }
+
+ options[:instance_variable_name] = :"@#{name}" if !options.has_key?(:instance_variable_name)
+ options.merge!(name: name, declared_in: self)
+
+ if type == NOT_PASSED
+ # If a type is not passed, the property derives from the
+ # superclass property (if any)
+ if properties.has_key?(name)
+ property = properties[name].derive(**options)
+ else
+ property = property_type(**options)
+ end
+
+ # If a Property is specified, derive a new one from that.
+ elsif type.is_a?(Property) || (type.is_a?(Class) && type <= Property)
+ property = type.derive(**options)
+
+ # If a primitive type was passed, combine it with "is"
+ else
+ if options[:is]
+ options[:is] = ([ type ] + [ options[:is] ]).flatten(1)
+ else
+ options[:is] = type
+ end
+ property = property_type(**options)
+ end
+
+ local_properties = properties(false)
+ local_properties[name] = property
+
+ property.emit_dsl
+ end
+
+ #
+ # Create a reusable property type that can be used in multiple properties
+ # in different resources.
+ #
+ # @param options [Hash<Symbol,Object>] Validation options. see #property for
+ # the list of options.
+ #
+ # @example
+ # property_type(default: 'hi')
+ #
+ def property_type(**options)
+ Property.derive(**options)
+ end
+
+ #
+ # Create a lazy value for assignment to a default value.
+ #
+ # @param block The block to run when the value is retrieved.
+ #
+ # @return [Chef::DelayedEvaluator] The lazy value
+ #
+ def lazy(&block)
+ DelayedEvaluator.new(&block)
+ end
+
+ #
+ # Get or set the list of desired state properties for this resource.
+ #
+ # State properties are properties that describe the desired state
+ # of the system, such as file permissions or ownership.
+ # In general, state properties are properties that could be populated by
+ # examining the state of the system (e.g., File.stat can tell you the
+ # permissions on an existing file). Contrarily, properties that are not
+ # "state properties" usually modify the way Chef itself behaves, for example
+ # by providing additional options for a package manager to use when
+ # installing a package.
+ #
+ # This list is used by the Chef client auditing system to extract
+ # information from resources to describe changes made to the system.
+ #
+ # This method is unnecessary when declaring properties with `property`;
+ # properties are added to state_properties by default, and can be turned off
+ # with `desired_state: false`.
+ #
+ # ```ruby
+ # property :x # part of desired state
+ # property :y, desired_state: false # not part of desired state
+ # ```
+ #
+ # @param names [Array<Symbol>] A list of property names to set as desired
+ # state.
+ #
+ # @return [Array<Property>] All properties in desired state.
+ #
+ def state_properties(*names)
+ if !names.empty?
+ names = names.map { |name| name.to_sym }.uniq
+
+ local_properties = properties(false)
+ # Add new properties to the list.
+ names.each do |name|
+ property = properties[name]
+ if !property
+ self.property name, instance_variable_name: false, desired_state: true
+ elsif !property.desired_state?
+ self.property name, desired_state: true
+ end
+ end
+
+ # If state_attrs *excludes* something which is currently desired state,
+ # mark it as desired_state: false.
+ local_properties.each do |name,property|
+ if property.desired_state? && !names.include?(name)
+ self.property name, desired_state: false
+ end
+ end
+ end
+
+ properties.values.select { |property| property.desired_state? }
+ end
+
+ #
+ # Set the identity of this resource to a particular set of properties.
+ #
+ # This drives #identity, which returns data that uniquely refers to a given
+ # resource on the given node (in such a way that it can be correlated
+ # across Chef runs).
+ #
+ # This method is unnecessary when declaring properties with `property`;
+ # properties can be added to identity during declaration with
+ # `identity: true`.
+ #
+ # ```ruby
+ # property :x, identity: true # part of identity
+ # property :y # not part of identity
+ # ```
+ #
+ # If no properties are marked as identity, "name" is considered the identity.
+ #
+ # @param names [Array<Symbol>] A list of property names to set as the identity.
+ #
+ # @return [Array<Property>] All identity properties.
+ #
+ def identity_properties(*names)
+ if !names.empty?
+ names = names.map { |name| name.to_sym }
+
+ # Add or change properties that are not part of the identity.
+ names.each do |name|
+ property = properties[name]
+ if !property
+ self.property name, instance_variable_name: false, identity: true
+ elsif !property.identity?
+ self.property name, identity: true
+ end
+ end
+
+ # If identity_properties *excludes* something which is currently part of
+ # the identity, mark it as identity: false.
+ properties.each do |name,property|
+ if property.identity? && !names.include?(name)
+
+ self.property name, identity: false
+ end
+ end
+ end
+
+ result = properties.values.select { |property| property.identity? }
+ result = [ properties[:name] ] if result.empty?
+ result
+ end
+
+ def included(other)
+ other.extend ClassMethods
+ end
+ end
+
+ def self.included(other)
+ other.extend ClassMethods
+ end
+
+ include Chef::Mixin::ParamsValidate
+
+ #
+ # Whether this property has been set (or whether it has a default that has
+ # been retrieved).
+ #
+ # @param name [Symbol] The name of the property.
+ # @return [Boolean] `true` if the property has been set.
+ #
+ def property_is_set?(name)
+ property = self.class.properties[name.to_sym]
+ raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
+ property.is_set?(self)
+ end
+
+ #
+ # Clear this property as if it had never been set. It will thereafter return
+ # the default.
+ # been retrieved).
+ #
+ # @param name [Symbol] The name of the property.
+ #
+ def reset_property(name)
+ property = self.class.properties[name.to_sym]
+ raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
+ property.reset(self)
+ end
+ end
+ end
+end
diff --git a/lib/chef/mixin/template.rb b/lib/chef/mixin/template.rb
index be83edc697..db9a2f6d91 100644
--- a/lib/chef/mixin/template.rb
+++ b/lib/chef/mixin/template.rb
@@ -136,6 +136,7 @@ class Chef
raise "You cannot render partials in this context" unless @template_finder
partial_variables = options.delete(:variables) || _public_instance_variables
+ partial_variables[:template_finder] = @template_finder
partial_context = self.class.new(partial_variables)
partial_context._extend_modules(@_extension_modules)
diff --git a/lib/chef/mixin/which.rb b/lib/chef/mixin/which.rb
index 4179c97b62..4e1c516386 100644
--- a/lib/chef/mixin/which.rb
+++ b/lib/chef/mixin/which.rb
@@ -28,7 +28,7 @@ class Chef
paths = ENV['PATH'].split(File::PATH_SEPARATOR) + extra_path
paths.each do |path|
filename = File.join(path, cmd)
- return filename if File.executable?(filename)
+ return filename if File.executable?(Chef.path_to(filename))
end
false
end
diff --git a/lib/chef/mixin/wide_string.rb b/lib/chef/mixin/wide_string.rb
new file mode 100644
index 0000000000..0c32b76365
--- /dev/null
+++ b/lib/chef/mixin/wide_string.rb
@@ -0,0 +1,72 @@
+#
+# Author:: Jay Mundrawala(<jdm@chef.io>)
+# Copyright:: Copyright 2015 Chef Software
+# 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
+ module Mixin
+ module WideString
+
+ def wstring(str)
+ if str.nil? || str.encoding == Encoding::UTF_16LE
+ str
+ else
+ utf8_to_wide(str)
+ end
+ end
+
+ def utf8_to_wide(ustring)
+ # ensure it is actually UTF-8
+ # Ruby likes to mark binary data as ASCII-8BIT
+ ustring = (ustring + "").force_encoding('UTF-8') if ustring.respond_to?(:force_encoding) && ustring.encoding.name != "UTF-8"
+
+ # ensure we have the double-null termination Windows Wide likes
+ ustring = ustring + "\000\000" if ustring.length == 0 or ustring[-1].chr != "\000"
+
+ # encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode
+ ustring = begin
+ if ustring.respond_to?(:encode)
+ ustring.encode('UTF-16LE')
+ else
+ require 'iconv'
+ Iconv.conv("UTF-16LE", "UTF-8", ustring)
+ end
+ end
+ ustring
+ end
+
+ def wide_to_utf8(wstring)
+ # ensure it is actually UTF-16LE
+ # Ruby likes to mark binary data as ASCII-8BIT
+ wstring = wstring.force_encoding('UTF-16LE') if wstring.respond_to?(:force_encoding)
+
+ # encode it all as UTF-8
+ wstring = begin
+ if wstring.respond_to?(:encode)
+ wstring.encode('UTF-8')
+ else
+ require 'iconv'
+ Iconv.conv("UTF-8", "UTF-16LE", wstring)
+ end
+ end
+ # remove trailing CRLF and NULL characters
+ wstring.strip!
+ wstring
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/mixin/windows_env_helper.rb b/lib/chef/mixin/windows_env_helper.rb
index a126801a28..cd12b4254a 100644
--- a/lib/chef/mixin/windows_env_helper.rb
+++ b/lib/chef/mixin/windows_env_helper.rb
@@ -18,6 +18,7 @@
require 'chef/exceptions'
+require 'chef/mixin/wide_string'
require 'chef/platform/query_helpers'
require 'chef/win32/error' if Chef::Platform.windows?
require 'chef/win32/api/system' if Chef::Platform.windows?
@@ -26,6 +27,8 @@ require 'chef/win32/api/unicode' if Chef::Platform.windows?
class Chef
module Mixin
module WindowsEnvHelper
+ include Chef::Mixin::WideString
+
if Chef::Platform.windows?
include Chef::ReservedNames::Win32::API::System
end
@@ -45,7 +48,7 @@ class Chef
Chef::ReservedNames::Win32::Error.raise!
end
if ( SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, FFI::MemoryPointer.from_string(
- Chef::ReservedNames::Win32::Unicode.utf8_to_wide('Environment')
+ utf8_to_wide('Environment')
).address, flags, 5000, nil) == 0 )
Chef::ReservedNames::Win32::Error.raise!
end
diff --git a/lib/chef/monkey_patches/webrick-utils.rb b/lib/chef/monkey_patches/webrick-utils.rb
new file mode 100644
index 0000000000..57f6db54ed
--- /dev/null
+++ b/lib/chef/monkey_patches/webrick-utils.rb
@@ -0,0 +1,51 @@
+require 'webrick/utils'
+
+module WEBrick
+ module Utils
+ ##
+ # Creates TCP server sockets bound to +address+:+port+ and returns them.
+ #
+ # It will create IPV4 and IPV6 sockets on all interfaces.
+ #
+ # NOTE: We need to monkey patch this method because
+ # create_listeners on Windows with Ruby > 2.0.0 does not
+ # raise an error if we're already listening on a port.
+ #
+ def create_listeners(address, port, logger=nil)
+ #
+ # utils.rb -- Miscellaneous utilities
+ #
+ # Author: IPR -- Internet Programming with Ruby -- writers
+ # Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+ # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+ # reserved.
+ #
+ # $IPR: utils.rb,v 1.10 2003/02/16 22:22:54 gotoyuzo Exp $
+ unless port
+ raise ArgumentError, "must specify port"
+ end
+ res = Socket::getaddrinfo(address, port,
+ Socket::AF_UNSPEC, # address family
+ Socket::SOCK_STREAM, # socket type
+ 0, # protocol
+ Socket::AI_PASSIVE) # flag
+ last_error = nil
+ sockets = []
+ res.each{|ai|
+ begin
+ logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
+ sock = TCPServer.new(ai[3], port)
+ port = sock.addr[1] if port == 0
+ Utils::set_close_on_exec(sock)
+ sockets << sock
+ rescue => ex
+ logger.warn("TCPServer Error: #{ex}") if logger
+ last_error = ex
+ end
+ }
+ raise last_error if sockets.empty?
+ return sockets
+ end
+ module_function :create_listeners
+ end
+end
diff --git a/lib/chef/monkey_patches/win32/registry.rb b/lib/chef/monkey_patches/win32/registry.rb
new file mode 100644
index 0000000000..2ce2ac97f1
--- /dev/null
+++ b/lib/chef/monkey_patches/win32/registry.rb
@@ -0,0 +1,72 @@
+#
+# Copyright:: Copyright 2015 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/win32/api/registry'
+require 'chef/win32/unicode'
+require 'win32/registry'
+
+module Win32
+ class Registry
+
+ module API
+
+ extend Chef::ReservedNames::Win32::API::Registry
+
+ module_function
+
+ if RUBY_VERSION =~ /^2\.1/
+ # ::Win32::Registry#delete_value is broken in Ruby 2.1 (up to Ruby 2.1.6).
+ # This should be resolved in a later release (see note #9 in link below).
+ # https://bugs.ruby-lang.org/issues/10820
+ def DeleteValue(hkey, name)
+ check RegDeleteValueW(hkey, name.to_wstring)
+ end
+ end
+
+ # ::Win32::Registry#delete_key uses RegDeleteKeyW. We need to use
+ # RegDeleteKeyExW to properly support WOW64 systems.
+ def DeleteKey(hkey, name)
+ check RegDeleteKeyExW(hkey, name.to_wstring, 0, 0)
+ end
+
+ end
+
+ if RUBY_VERSION =~ /^2.1/
+ # ::Win32::Registry#write does not correctly handle data in Ruby 2.1 (up to Ruby 2.1.6).
+ # https://bugs.ruby-lang.org/issues/11439
+ def write(name, type, data)
+ case type
+ when REG_SZ, REG_EXPAND_SZ
+ data = data.to_s.encode(WCHAR) + WCHAR_NUL
+ when REG_MULTI_SZ
+ data = data.to_a.map {|s| s.encode(WCHAR)}.join(WCHAR_NUL) << WCHAR_NUL << WCHAR_NUL
+ when REG_BINARY
+ data = data.to_s
+ when REG_DWORD
+ data = API.packdw(data.to_i)
+ when REG_DWORD_BIG_ENDIAN
+ data = [data.to_i].pack('N')
+ when REG_QWORD
+ data = API.packqw(data.to_i)
+ else
+ raise TypeError, "Unsupported type #{type}"
+ end
+ API.SetValue(@hkey, name, type, data, data.bytesize)
+ end
+ end
+ end
+end
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index 22c7d5bd8e..ad065cc02b 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -63,6 +63,8 @@ class Chef
include Chef::Mixin::ParamsValidate
+ NULL_ARG = Object.new
+
# Create a new Chef::Node object.
def initialize(chef_server_rest: nil)
@chef_server_rest = chef_server_rest
@@ -72,6 +74,9 @@ class Chef
@primary_runlist = Chef::RunList.new
@override_runlist = Chef::RunList.new
+ @policy_name = nil
+ @policy_group = nil
+
@attributes = Chef::Node::Attribute.new({}, {}, {}, {})
@run_state = {}
@@ -132,6 +137,50 @@ class Chef
alias :environment :chef_environment
+ # The `policy_name` for this node. Setting this to a non-nil value will
+ # enable policyfile mode when `chef-client` is run. If set in the config
+ # file or in node json, running `chef-client` will update this value.
+ #
+ # @see Chef::PolicyBuilder::Dynamic
+ # @see Chef::PolicyBuilder::Policyfile
+ #
+ # @param arg [String] the new policy_name value
+ # @return [String] the current policy_name, or the one you just set
+ def policy_name(arg=NULL_ARG)
+ return @policy_name if arg.equal?(NULL_ARG)
+ validate({policy_name: arg}, { policy_name: { kind_of: [ String, NilClass ], regex: /^[\-:.[:alnum:]_]+$/ } })
+ @policy_name = arg
+ end
+
+ # A "non-DSL-style" setter for `policy_name`
+ #
+ # @see #policy_name
+ def policy_name=(policy_name)
+ policy_name(policy_name)
+ end
+
+ # The `policy_group` for this node. Setting this to a non-nil value will
+ # enable policyfile mode when `chef-client` is run. If set in the config
+ # file or in node json, running `chef-client` will update this value.
+ #
+ # @see Chef::PolicyBuilder::Dynamic
+ # @see Chef::PolicyBuilder::Policyfile
+ #
+ # @param arg [String] the new policy_group value
+ # @return [String] the current policy_group, or the one you just set
+ def policy_group(arg=NULL_ARG)
+ return @policy_group if arg.equal?(NULL_ARG)
+ validate({policy_group: arg}, { policy_group: { kind_of: [ String, NilClass ], regex: /^[\-:.[:alnum:]_]+$/ } })
+ @policy_group = arg
+ end
+
+ # A "non-DSL-style" setter for `policy_group`
+ #
+ # @see #policy_group
+ def policy_group=(policy_group)
+ policy_group(policy_group)
+ end
+
def attributes
@attributes
end
@@ -391,7 +440,7 @@ class Chef
self.tags # make sure they're defined
- automatic_attrs[:recipes] = expansion.recipes.with_fully_qualified_names_and_version_constraints
+ automatic_attrs[:recipes] = expansion.recipes.with_duplicate_names
automatic_attrs[:expanded_run_list] = expansion.recipes.with_fully_qualified_names_and_version_constraints
automatic_attrs[:roles] = expansion.roles
@@ -461,6 +510,14 @@ class Chef
#Render correctly for run_list items so malformed json does not result
"run_list" => @primary_runlist.run_list.map { |item| item.to_s }
}
+ # Chef Server rejects node JSON with extra keys; prior to 12.3,
+ # "policy_name" and "policy_group" are unknown; after 12.3 they are
+ # optional, therefore only including them in the JSON if present
+ # maximizes compatibility for most people.
+ unless policy_group.nil? && policy_name.nil?
+ result["policy_name"] = policy_name
+ result["policy_group"] = policy_group
+ end
result
end
@@ -492,6 +549,10 @@ class Chef
else
o["recipes"].each { |r| node.recipes << r }
end
+
+ node.policy_name = o["policy_name"] if o.has_key?("policy_name")
+ node.policy_group = o["policy_group"] if o.has_key?("policy_group")
+
node
end
@@ -548,13 +609,21 @@ class Chef
# so then POST to create.
begin
if Chef::Config[:why_run]
- Chef::Log.warn("In whyrun mode, so NOT performing node save.")
+ Chef::Log.warn("In why-run mode, so NOT performing node save.")
else
chef_server_rest.put_rest("nodes/#{name}", data_for_save)
end
rescue Net::HTTPServerException => e
- raise e unless e.response.code == "404"
- chef_server_rest.post_rest("nodes", data_for_save)
+ if e.response.code == "404"
+ chef_server_rest.post_rest("nodes", data_for_save)
+ # Chef Server before 12.3 rejects node JSON with 'policy_name' or
+ # 'policy_group' keys, but 'policy_name' will be detected first.
+ # Backcompat can be removed in 13.0
+ elsif e.response.code == "400" && e.response.body.include?("Invalid key policy_name")
+ save_without_policyfile_attrs
+ else
+ raise
+ end
end
self
end
@@ -563,6 +632,15 @@ class Chef
def create
chef_server_rest.post_rest("nodes", data_for_save)
self
+ rescue Net::HTTPServerException => e
+ # Chef Server before 12.3 rejects node JSON with 'policy_name' or
+ # 'policy_group' keys, but 'policy_name' will be detected first.
+ # Backcompat can be removed in 13.0
+ if e.response.code == "400" && e.response.body.include?("Invalid key policy_name")
+ chef_server_rest.post_rest("nodes", data_for_save_without_policyfile_attrs)
+ else
+ raise
+ end
end
def to_s
@@ -575,6 +653,22 @@ class Chef
private
+ def save_without_policyfile_attrs
+ trimmed_data = data_for_save_without_policyfile_attrs
+
+ chef_server_rest.put_rest("nodes/#{name}", trimmed_data)
+ rescue Net::HTTPServerException => e
+ raise e unless e.response.code == "404"
+ chef_server_rest.post_rest("nodes", trimmed_data)
+ end
+
+ def data_for_save_without_policyfile_attrs
+ data_for_save.tap do |trimmed_data|
+ trimmed_data.delete("policy_name")
+ trimmed_data.delete("policy_group")
+ end
+ end
+
def data_for_save
data = for_json
["automatic", "default", "normal", "override"].each do |level|
diff --git a/lib/chef/node_map.rb b/lib/chef/node_map.rb
index d905c8779e..751f9576f6 100644
--- a/lib/chef/node_map.rb
+++ b/lib/chef/node_map.rb
@@ -32,8 +32,8 @@ class Chef
# @return [NodeMap] Returns self for possible chaining
#
def set(key, value, platform: nil, platform_version: nil, platform_family: nil, os: nil, on_platform: nil, on_platforms: nil, canonical: nil, override: nil, &block)
- Chef::Log.deprecation "The on_platform option to node_map has been deprecated" if on_platform
- Chef::Log.deprecation "The on_platforms option to node_map has been deprecated" if on_platforms
+ Chef.log_deprecation("The on_platform option to node_map has been deprecated") if on_platform
+ Chef.log_deprecation("The on_platforms option to node_map has been deprecated") if on_platforms
platform ||= on_platform || on_platforms
filters = {}
filters[:platform] = platform if platform
diff --git a/lib/chef/platform/query_helpers.rb b/lib/chef/platform/query_helpers.rb
index e64189fbd6..dfb99ed750 100644
--- a/lib/chef/platform/query_helpers.rb
+++ b/lib/chef/platform/query_helpers.rb
@@ -36,6 +36,44 @@ class Chef
is_server_2003
end
+ def windows_nano_server?
+ return false unless windows?
+ require 'win32/registry'
+
+ # This method may be called before ohai runs (e.g., it may be used to
+ # determine settings in config.rb). Chef::Win32::Registry.new uses
+ # node attributes to verify the machine architecture which aren't
+ # accessible before ohai runs.
+ nano = nil
+ key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Server\\ServerLevels"
+ access = ::Win32::Registry::KEY_QUERY_VALUE | 0x0100 # nano is 64-bit only
+ begin
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open(key, access) do |reg|
+ nano = reg["NanoServer"]
+ end
+ rescue ::Win32::Registry::Error
+ # If accessing the registry key failed, then we're probably not on
+ # nano. Fail through.
+ end
+ return nano == 1
+ end
+
+ def supports_msi?
+ return false unless windows?
+ require 'win32/registry'
+
+ key = "System\\CurrentControlSet\\Services\\msiserver"
+ access = ::Win32::Registry::KEY_QUERY_VALUE
+
+ begin
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open(key, access) do |reg|
+ true
+ end
+ rescue ::Win32::Registry::Error
+ false
+ end
+ end
+
def supports_powershell_execution_bypass?(node)
node[:languages] && node[:languages][:powershell] &&
node[:languages][:powershell][:version].to_i >= 3
@@ -52,6 +90,13 @@ class Chef
Gem::Version.new(node[:languages][:powershell][:version]) >=
Gem::Version.new("5.0.10018.0")
end
+
+ def dsc_refresh_mode_disabled?(node)
+ require 'chef/util/powershell/cmdlet'
+ cmdlet = Chef::Util::Powershell::Cmdlet.new(node, "Get-DscLocalConfigurationManager", :object)
+ metadata = cmdlet.run!.return_value
+ metadata['RefreshMode'] == 'Disabled'
+ end
end
end
end
diff --git a/lib/chef/platform/service_helpers.rb b/lib/chef/platform/service_helpers.rb
index 92efe24da2..f97eed2581 100644
--- a/lib/chef/platform/service_helpers.rb
+++ b/lib/chef/platform/service_helpers.rb
@@ -16,6 +16,8 @@
# limitations under the License.
#
+require 'chef/chef_class'
+
class Chef
class Platform
class ServiceHelpers
@@ -34,55 +36,55 @@ class Chef
# different services is NOT a design concern of this module.
#
def service_resource_providers
- @service_resource_providers ||= [].tap do |service_resource_providers|
-
- if ::File.exist?("/usr/sbin/update-rc.d")
- service_resource_providers << :debian
- end
+ providers = []
- if ::File.exist?("/usr/sbin/invoke-rc.d")
- service_resource_providers << :invokercd
- end
+ if ::File.exist?(Chef.path_to("/usr/sbin/update-rc.d"))
+ providers << :debian
+ end
- if ::File.exist?("/sbin/insserv")
- service_resource_providers << :insserv
- end
+ if ::File.exist?(Chef.path_to("/usr/sbin/invoke-rc.d"))
+ providers << :invokercd
+ end
- if ::File.exist?("/sbin/initctl")
- service_resource_providers << :upstart
- end
+ if ::File.exist?(Chef.path_to("/sbin/initctl"))
+ service_resource_providers << :upstart
+ end
- if ::File.exist?("/sbin/chkconfig")
- service_resource_providers << :redhat
- end
+ if ::File.exist?(Chef.path_to("/sbin/insserv"))
+ providers << :insserv
+ end
- if systemd_is_init?
- service_resource_providers << :systemd
- end
+ if systemd_is_init?
+ providers << :systemd
+ end
+ if ::File.exist?(Chef.path_to("/sbin/chkconfig"))
+ providers << :redhat
end
+
+ providers
end
def config_for_service(service_name)
configs = []
- if ::File.exist?("/etc/init.d/#{service_name}")
+ if ::File.exist?(Chef.path_to("/etc/init.d/#{service_name}"))
configs << :initd
end
- if ::File.exist?("/etc/init/#{service_name}.conf")
+ if ::File.exist?(Chef.path_to("/etc/init/#{service_name}.conf"))
configs << :upstart
end
- if ::File.exist?("/etc/xinetd.d/#{service_name}")
+ if ::File.exist?(Chef.path_to("/etc/xinetd.d/#{service_name}"))
configs << :xinetd
end
- if ::File.exist?("/etc/rc.d/#{service_name}")
+ if ::File.exist?(Chef.path_to("/etc/rc.d/#{service_name}"))
configs << :etc_rcd
end
- if ::File.exist?("/usr/local/etc/rc.d/#{service_name}")
+ if ::File.exist?(Chef.path_to("/usr/local/etc/rc.d/#{service_name}"))
configs << :usr_local_etc_rcd
end
diff --git a/lib/chef/policy_builder.rb b/lib/chef/policy_builder.rb
index 136b2853b0..56415dbbd0 100644
--- a/lib/chef/policy_builder.rb
+++ b/lib/chef/policy_builder.rb
@@ -18,6 +18,7 @@
require 'chef/policy_builder/expand_node_object'
require 'chef/policy_builder/policyfile'
+require 'chef/policy_builder/dynamic'
class Chef
@@ -37,13 +38,5 @@ class Chef
# * cookbook_hash is stored in run_context
module PolicyBuilder
- def self.strategy
- if Chef::Config[:use_policyfile]
- Policyfile
- else
- ExpandNodeObject
- end
- end
-
end
end
diff --git a/lib/chef/policy_builder/dynamic.rb b/lib/chef/policy_builder/dynamic.rb
new file mode 100644
index 0000000000..c9842ba532
--- /dev/null
+++ b/lib/chef/policy_builder/dynamic.rb
@@ -0,0 +1,186 @@
+#
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright 2015 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 'forwardable'
+
+require 'chef/log'
+require 'chef/rest'
+require 'chef/run_context'
+require 'chef/config'
+require 'chef/node'
+require 'chef/exceptions'
+
+class Chef
+ module PolicyBuilder
+
+ # PolicyBuilder that selects either a Policyfile or non-Policyfile
+ # implementation based on the content of the node object.
+ class Dynamic
+
+ extend Forwardable
+
+ attr_reader :node
+ attr_reader :node_name
+ attr_reader :ohai_data
+ attr_reader :json_attribs
+ attr_reader :override_runlist
+ attr_reader :events
+
+ def initialize(node_name, ohai_data, json_attribs, override_runlist, events)
+ @implementation = nil
+
+ @node_name = node_name
+ @ohai_data = ohai_data
+ @json_attribs = json_attribs
+ @override_runlist = override_runlist
+ @events = events
+
+ @node = nil
+ end
+
+ ## PolicyBuilder API ##
+
+ # Loads the node state from the server, then picks the correct
+ # implementation class based on the node and json_attribs.
+ #
+ # Calls #finish_load_node on the implementation object to complete the
+ # loading process. All subsequent lifecycle calls are delegated.
+ #
+ # @return [Chef::Node] the loaded node.
+ def load_node
+ events.node_load_start(node_name, config)
+ Chef::Log.debug("Building node object for #{node_name}")
+
+ @node =
+ if Chef::Config[:solo]
+ Chef::Node.build(node_name)
+ else
+ Chef::Node.find_or_create(node_name)
+ end
+ select_implementation(node)
+ implementation.finish_load_node(node)
+ node
+ rescue Exception => e
+ events.node_load_failed(node_name, e, config)
+ raise
+ end
+
+ ## Delegated Public API Methods ##
+
+ ### Accessors ###
+
+ def_delegator :implementation, :original_runlist
+ def_delegator :implementation, :run_context
+ def_delegator :implementation, :run_list_expansion
+
+ ### Lifecycle Methods ###
+
+ # @!method build_node
+ #
+ # Applies external attributes (e.g., from JSON file, environment,
+ # policyfile, etc.) and determines the correct expanded run list for the
+ # run.
+ #
+ # @return [Chef::Node]
+ def_delegator :implementation, :build_node
+
+ # @!method setup_run_context
+ #
+ # Synchronizes cookbooks and initializes the run context object for the
+ # run.
+ #
+ # @return [Chef::RunContext]
+ def_delegator :implementation, :setup_run_context
+
+ # @!method expanded_run_list
+ #
+ # Resolves the run list to a form containing only recipes and sets the
+ # `roles` and `recipes` automatic attributes on the node.
+ #
+ # @return [#recipes, #roles] A RunListExpansion or duck-type.
+ def_delegator :implementation, :expand_run_list
+
+ # @!method sync_cookbooks
+ #
+ # Synchronizes cookbooks. In a normal chef-client run, this is handled by
+ # #setup_run_context, but may be called directly in some circumstances.
+ #
+ # @return [Hash{String => Chef::CookbookManifest}] A map of
+ # CookbookManifest objects by cookbook name.
+ def_delegator :implementation, :sync_cookbooks
+
+ # @!method temporary_policy?
+ #
+ # Indicates whether the policy is temporary, which means an
+ # override_runlist was provided. Chef::Client uses this to decide whether
+ # to do the final node save at the end of the run or not.
+ #
+ # @return [true,false]
+ def_delegator :implementation, :temporary_policy?
+
+ ## Internal Public API ##
+
+ # Returns the selected implementation, or raises if not set. The
+ # implementation is set when #load_node is called.
+ #
+ # @return [PolicyBuilder::Policyfile, PolicyBuilder::ExpandNodeObject]
+ def implementation
+ @implementation or raise Exceptions::InvalidPolicybuilderCall, "#load_node must be called before other policy builder methods"
+ end
+
+ # @api private
+ #
+ # Sets the implementation based on the content of the node, node JSON
+ # (i.e., the `-j JSON_FILE` data), and config. This is only public for
+ # testing purposes; production code should call #load_node instead.
+ def select_implementation(node)
+ if policyfile_set_in_config? ||
+ policyfile_attribs_in_node_json? ||
+ node_has_policyfile_attrs?(node) ||
+ policyfile_compat_mode_config?
+ @implementation = Policyfile.new(node_name, ohai_data, json_attribs, override_runlist, events)
+ else
+ @implementation = ExpandNodeObject.new(node_name, ohai_data, json_attribs, override_runlist, events)
+ end
+ end
+
+ def config
+ Chef::Config
+ end
+
+ private
+
+ def node_has_policyfile_attrs?(node)
+ node.policy_name || node.policy_group
+ end
+
+ def policyfile_attribs_in_node_json?
+ json_attribs.key?("policy_name") || json_attribs.key?("policy_group")
+ end
+
+ def policyfile_set_in_config?
+ config[:use_policyfile] || config[:policy_name] || config[:policy_group]
+ end
+
+ def policyfile_compat_mode_config?
+ config[:deployment_group] && !config[:policy_document_native_api]
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/policy_builder/expand_node_object.rb b/lib/chef/policy_builder/expand_node_object.rb
index 524bdd95b1..2c6d644e42 100644
--- a/lib/chef/policy_builder/expand_node_object.rb
+++ b/lib/chef/policy_builder/expand_node_object.rb
@@ -33,6 +33,9 @@ class Chef
# expands the run_list on a node object and then queries the chef-server
# to find the correct set of cookbooks, given version constraints of the
# node's environment.
+ #
+ # Note that this class should only be used via PolicyBuilder::Dynamic and
+ # not instantiated directly.
class ExpandNodeObject
attr_reader :events
@@ -55,9 +58,10 @@ class Chef
@run_list_expansion = nil
end
- # This method injects the run_context and provider and resource priority
- # maps into the Chef class. The run_context has to be injected here, the provider and
- # resource maps could be moved if a better place can be found to do this work.
+ # This method injects the run_context and into the Chef class.
+ #
+ # NOTE: This is duplicated with the Policyfile implementation. If
+ # it gets any more complicated, it needs to be moved elsewhere.
#
# @param run_context [Chef::RunContext] the run_context to inject
def setup_chef_class(run_context)
@@ -93,25 +97,36 @@ class Chef
run_context
end
-
- # In client-server operation, loads the node state from the server. In
- # chef-solo operation, builds a new node object.
+ # DEPRECATED: As of Chef 12.5, chef selects either policyfile mode or
+ # "expand node" mode dynamically, based on the content of the node
+ # object, first boot JSON, and config. This happens in
+ # PolicyBuilder::Dynamic, which selects the implementation during
+ # #load_node and then delegates to either ExpandNodeObject or Policyfile
+ # implementations as appropriate. Tools authors should update their code
+ # to create a PolicyBuilder::Dynamc policy builder and allow it to select
+ # the proper implementation.
def load_node
- events.node_load_start(node_name, Chef::Config)
+ Chef.log_deprecation("ExpandNodeObject#load_node is deprecated. Please use Chef::PolicyBuilder::Dynamic instead of using ExpandNodeObject directly")
+
+ events.node_load_start(node_name, config)
Chef::Log.debug("Building node object for #{node_name}")
- if Chef::Config[:solo]
- @node = Chef::Node.build(node_name)
- else
- @node = Chef::Node.find_or_create(node_name)
- end
+ @node =
+ if Chef::Config[:solo]
+ Chef::Node.build(node_name)
+ else
+ Chef::Node.find_or_create(node_name)
+ end
+ finish_load_node(node)
+ node
rescue Exception => e
- # TODO: wrap this exception so useful error info can be given to the
- # user.
- events.node_load_failed(node_name, e, Chef::Config)
+ events.node_load_failed(node_name, e, config)
raise
end
+ def finish_load_node(node)
+ @node = node
+ end
# Applies environment, external JSON attributes, and override run list to
# the node, Then expands the run_list.
@@ -139,6 +154,7 @@ class Chef
Chef::Log.info("Run List expands to [#{@expanded_run_list_with_versions.join(', ')}]")
events.node_load_completed(node, @expanded_run_list_with_versions, Chef::Config)
+ events.run_list_expanded(@run_list_expansion)
node
end
diff --git a/lib/chef/policy_builder/policyfile.rb b/lib/chef/policy_builder/policyfile.rb
index 5991e3ce10..d6dcdf67b2 100644
--- a/lib/chef/policy_builder/policyfile.rb
+++ b/lib/chef/policy_builder/policyfile.rb
@@ -68,22 +68,20 @@ class Chef
@node = nil
- Chef::Log.warn("Using experimental Policyfile feature")
-
if Chef::Config[:solo]
- raise UnsupportedFeature, "Policyfile does not support chef-solo at this time."
+ raise UnsupportedFeature, "Policyfile does not support chef-solo. Use chef-client local mode instead."
end
if override_runlist
- raise UnsupportedFeature, "Policyfile does not support override run lists at this time"
+ raise UnsupportedFeature, "Policyfile does not support override run lists. Use named run_lists instead."
end
if json_attribs && json_attribs.key?("run_list")
- raise UnsupportedFeature, "Policyfile does not support setting the run_list in json data at this time"
+ raise UnsupportedFeature, "Policyfile does not support setting the run_list in json data."
end
if Chef::Config[:environment] && !Chef::Config[:environment].chomp.empty?
- raise UnsupportedFeature, "Policyfile does not work with Chef Environments"
+ raise UnsupportedFeature, "Policyfile does not work with Chef Environments."
end
end
@@ -112,18 +110,11 @@ class Chef
## PolicyBuilder API ##
- # Loads the node state from the server.
- def load_node
- events.node_load_start(node_name, Chef::Config)
- Chef::Log.debug("Building node object for #{node_name}")
-
- @node = Chef::Node.find_or_create(node_name)
+ def finish_load_node(node)
+ @node = node
+ select_policy_name_and_group
validate_policyfile
events.policyfile_loaded(policy)
- node
- rescue Exception => e
- events.node_load_failed(node_name, e, Chef::Config)
- raise
end
# Applies environment, external JSON attributes, and override run list to
@@ -154,25 +145,42 @@ class Chef
raise
end
+ # Synchronizes cookbooks and initializes the run context object for the
+ # run.
+ #
+ # @return [Chef::RunContext]
def setup_run_context(specific_recipes=nil)
Chef::Cookbook::FileVendor.fetch_from_remote(http_api)
sync_cookbooks
cookbook_collection = Chef::CookbookCollection.new(cookbooks_to_sync)
run_context = Chef::RunContext.new(node, cookbook_collection, events)
+ setup_chef_class(run_context)
+
run_context.load(run_list_expansion_ish)
+ setup_chef_class(run_context)
run_context
end
+ # Sets `run_list` on the node from the policy, sets `roles` and `recipes`
+ # attributes on the node accordingly.
+ #
+ # @return [RunListExpansionIsh] A RunListExpansion duck-type.
def expand_run_list
+ CookbookCacheCleaner.instance.skip_removal = true if named_run_list_requested?
+
node.run_list(run_list)
node.automatic_attrs[:roles] = []
node.automatic_attrs[:recipes] = run_list_expansion_ish.recipes
run_list_expansion_ish
end
-
+ # Synchronizes cookbooks. In a normal chef-client run, this is handled by
+ # #setup_run_context, but may be called directly in some circumstances.
+ #
+ # @return [Hash{String => Chef::CookbookManifest}] A map of
+ # CookbookManifest objects by cookbook name.
def sync_cookbooks
Chef::Log.debug("Synchronizing cookbooks")
synchronizer = Chef::CookbookSynchronizer.new(cookbooks_to_sync, events)
@@ -186,12 +194,18 @@ class Chef
# Whether or not this is a temporary policy. Since PolicyBuilder doesn't
# support override_runlist, this is always false.
+ #
+ # @return [false]
def temporary_policy?
false
end
## Internal Public API ##
+ # @api private
+ #
+ # Generates an array of strings with recipe names including version and
+ # identifier info.
def run_list_with_versions_for_display
run_list.map do |recipe_spec|
cookbook, recipe = parse_recipe_spec(recipe_spec)
@@ -201,6 +215,11 @@ class Chef
end
end
+ # @api private
+ #
+ # Sets up a RunListExpansionIsh object so that it can be used in place of
+ # a RunListExpansion object, to satisfy the API contract of
+ # #expand_run_list
def run_list_expansion_ish
recipes = run_list.map do |recipe_spec|
cookbook, recipe = parse_recipe_spec(recipe_spec)
@@ -209,11 +228,15 @@ class Chef
RunListExpansionIsh.new(recipes, [])
end
+ # @api private
+ #
+ # Sets attributes from the policyfile on the node, using the role priority.
def apply_policyfile_attributes
node.attributes.role_default = policy["default_attributes"]
node.attributes.role_override = policy["override_attributes"]
end
+ # @api private
def parse_recipe_spec(recipe_spec)
rmatch = recipe_spec.match(/recipe\[([^:]+)::([^:]+)\]/)
if rmatch.nil?
@@ -223,20 +246,31 @@ class Chef
end
end
+ # @api private
def cookbook_lock_for(cookbook_name)
cookbook_locks[cookbook_name]
end
+ # @api private
def run_list
- policy["run_list"]
+ if named_run_list_requested?
+ named_run_list or
+ raise ConfigurationError,
+ "Policy '#{retrieved_policy_name}' revision '#{revision_id}' does not have named_run_list '#{named_run_list_name}'" +
+ "(available named_run_lists: [#{available_named_run_lists.join(', ')}])"
+ else
+ policy["run_list"]
+ end
end
+ # @api private
def policy
@policy ||= http_api.get(policyfile_location)
rescue Net::HTTPServerException => e
raise ConfigurationError, "Error loading policyfile from `#{policyfile_location}': #{e.class} - #{e.message}"
end
+ # @api private
def policyfile_location
if Chef::Config[:policy_document_native_api]
validate_policy_config!
@@ -273,6 +307,7 @@ class Chef
end
end
+ # @api private
def validate_recipe_spec(recipe_spec)
parse_recipe_spec(recipe_spec)
nil
@@ -282,11 +317,13 @@ class Chef
class ConfigurationError < StandardError; end
+ # @api private
def deployment_group
Chef::Config[:deployment_group] or
raise ConfigurationError, "Setting `deployment_group` is not configured."
end
+ # @api private
def validate_policy_config!
policy_group or
raise ConfigurationError, "Setting `policy_group` is not configured."
@@ -295,14 +332,75 @@ class Chef
raise ConfigurationError, "Setting `policy_name` is not configured."
end
+ # @api private
def policy_group
Chef::Config[:policy_group]
end
+ # @api private
def policy_name
Chef::Config[:policy_name]
end
+ # @api private
+ #
+ # Selects the `policy_name` and `policy_group` from the following sources
+ # in priority order:
+ #
+ # 1. JSON attribs (i.e., `-j JSON_FILE`)
+ # 2. `Chef::Config`
+ # 3. The node object
+ #
+ # The selected values are then copied to `Chef::Config` and the node.
+ def select_policy_name_and_group
+ policy_name_to_set =
+ policy_name_from_json_attribs ||
+ policy_name_from_config ||
+ policy_name_from_node
+
+ policy_group_to_set =
+ policy_group_from_json_attribs ||
+ policy_group_from_config ||
+ policy_group_from_node
+
+ node.policy_name = policy_name_to_set
+ node.policy_group = policy_group_to_set
+
+ Chef::Config[:policy_name] = policy_name_to_set
+ Chef::Config[:policy_group] = policy_group_to_set
+ end
+
+ # @api private
+ def policy_group_from_json_attribs
+ json_attribs["policy_group"]
+ end
+
+ # @api private
+ def policy_name_from_json_attribs
+ json_attribs["policy_name"]
+ end
+
+ # @api private
+ def policy_group_from_config
+ Chef::Config[:policy_group]
+ end
+
+ # @api private
+ def policy_name_from_config
+ Chef::Config[:policy_name]
+ end
+
+ # @api private
+ def policy_group_from_node
+ node.policy_group
+ end
+
+ # @api private
+ def policy_name_from_node
+ node.policy_name
+ end
+
+ # @api private
# Builds a 'cookbook_hash' map of the form
# "COOKBOOK_NAME" => "IDENTIFIER"
#
@@ -330,6 +428,7 @@ class Chef
raise
end
+ # @api private
# Fetches the CookbookVersion object for the given name and identifer
# specified in the lock_data.
# TODO: This only implements Chef 11 compatibility mode, which means that
@@ -343,20 +442,58 @@ class Chef
end
end
+ # @api private
def cookbook_locks
policy["cookbook_locks"]
end
+ # @api private
+ def revision_id
+ policy["revision_id"]
+ end
+
+ # @api private
def http_api
@api_service ||= Chef::REST.new(config[:chef_server_url])
end
+ # @api private
def config
Chef::Config
end
private
+ # This method injects the run_context and into the Chef class.
+ #
+ # NOTE: This is duplicated with the ExpandNodeObject implementation. If
+ # it gets any more complicated, it needs to be moved elsewhere.
+ #
+ # @param run_context [Chef::RunContext] the run_context to inject
+ def setup_chef_class(run_context)
+ Chef.set_run_context(run_context)
+ end
+
+ def retrieved_policy_name
+ policy["name"]
+ end
+
+ def named_run_list
+ policy["named_run_lists"] && policy["named_run_lists"][named_run_list_name]
+ end
+
+ def available_named_run_lists
+ (policy["named_run_lists"] || {}).keys
+ end
+
+ def named_run_list_requested?
+ !!Chef::Config[:named_run_list]
+ end
+
+ def named_run_list_name
+ Chef::Config[:named_run_list]
+ end
+
def compat_mode_manifest_for(cookbook_name, lock_data)
xyz_version = lock_data["dotted_decimal_identifier"]
rel_url = "cookbooks/#{cookbook_name}/#{xyz_version}"
diff --git a/lib/chef/property.rb b/lib/chef/property.rb
index 1a3b8ec72c..e97d8f9607 100644
--- a/lib/chef/property.rb
+++ b/lib/chef/property.rb
@@ -86,7 +86,30 @@ class Chef
#
def initialize(**options)
options.each { |k,v| options[k.to_sym] = v if k.is_a?(String) }
- options[:name_property] = options.delete(:name_attribute) if options.has_key?(:name_attribute) && !options.has_key?(:name_property)
+
+ # Replace name_attribute with name_property
+ if options.has_key?(:name_attribute)
+ # If we have both name_attribute and name_property and they differ, raise an error
+ if options.has_key?(:name_property)
+ raise ArgumentError, "Cannot specify both name_property and name_attribute together on property #{options[:name]}#{options[:declared_in] ? " of resource #{options[:declared_in].resource_name}" : ""}."
+ end
+ # replace name_property with name_attribute in place
+ options = Hash[options.map { |k,v| k == :name_attribute ? [ :name_property, v ] : [ k,v ] }]
+ end
+
+ # Only pick the first of :default, :name_property and :name_attribute if
+ # more than one is specified.
+ if options.has_key?(:default) && options[:name_property]
+ if options[:default].nil? || options.keys.index(:name_property) < options.keys.index(:default)
+ options.delete(:default)
+ preferred_default = :name_property
+ else
+ options.delete(:name_property)
+ preferred_default = :default
+ end
+ Chef.log_deprecation("Cannot specify both default and name_property together on property #{options[:name]}#{options[:declared_in] ? " of resource #{options[:declared_in].resource_name}" : ""}. Only one (#{preferred_default}) will be obeyed. In Chef 13, this will become an error.")
+ end
+
@options = options
options[:name] = options[:name].to_sym if options[:name]
@@ -228,7 +251,7 @@ class Chef
if value.nil? && !explicitly_accepts_nil?(resource)
# If you say "my_property nil" and the property explicitly accepts
# nil values, we consider this a get.
- Chef::Log.deprecation("#{name} nil currently does not overwrite the value of #{name}. This will change in Chef 13, and the value will be set to nil instead. Please change your code to explicitly accept nil using \"property :#{name}, [MyType, nil]\", or stop setting this value to nil.")
+ Chef.log_deprecation("#{name} nil currently does not overwrite the value of #{name}. This will change in Chef 13, and the value will be set to nil instead. Please change your code to explicitly accept nil using \"property :#{name}, [MyType, nil]\", or stop setting this value to nil.")
return get(resource)
end
@@ -399,7 +422,16 @@ class Chef
# @return [Property] The new property type.
#
def derive(**modified_options)
- Property.new(**options.merge(**modified_options))
+ # Since name_property, name_attribute and default override each other,
+ # if you specify one of them in modified_options it overrides anything in
+ # the original options.
+ options = self.options
+ if modified_options.has_key?(:name_property) ||
+ modified_options.has_key?(:name_attribute) ||
+ modified_options.has_key?(:default)
+ options = options.reject { |k,v| k == :name_attribute || k == :name_property || k == :default }
+ end
+ Property.new(options.merge(modified_options))
end
#
@@ -424,10 +456,10 @@ class Chef
EOM
rescue SyntaxError
# If the name is not a valid ruby name, we use define_method.
- resource_class.define_method(name) do |value=NOT_PASSED|
+ declared_in.define_method(name) do |value=NOT_PASSED|
self.class.properties[name].call(self, value)
end
- resource_class.define_method("#{name}=") do |value|
+ declared_in.define_method("#{name}=") do |value|
self.class.properties[name].set(self, value)
end
end
@@ -447,6 +479,8 @@ class Chef
# A type accepts nil explicitly if "is" allows nil, it validates as nil, *and* is not simply
# an empty type.
#
+ # A type is presumed to accept nil if it does coercion (which must handle nil).
+ #
# These examples accept nil explicitly:
# ```ruby
# property :a, [ String, nil ]
@@ -478,7 +512,8 @@ class Chef
#
# @api private
def explicitly_accepts_nil?(resource)
- options.has_key?(:is) && resource.send(:_pv_is, { name => nil }, name, options[:is], raise_error: false)
+ options.has_key?(:coerce) ||
+ (options.has_key?(:is) && resource.send(:_pv_is, { name => nil }, name, options[:is], raise_error: false))
end
def get_value(resource)
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
index f2a493c3e6..e22f11d9be 100644
--- a/lib/chef/provider.rb
+++ b/lib/chef/provider.rb
@@ -265,7 +265,7 @@ class Chef
provider_class = self
@included_resource_dsl_module = Module.new do
extend Forwardable
- define_singleton_method(:to_s) { "#{resource_class} forwarder module" }
+ define_singleton_method(:to_s) { "forwarder module for #{provider_class}" }
define_singleton_method(:inspect) { to_s }
# Add a delegator for each explicit property that will get the *current* value
# of the property by default instead of the *actual* value.
@@ -421,7 +421,7 @@ class Chef
module DeprecatedLWRPClass
def const_missing(class_name)
if 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.")
+ 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]
else
raise NameError, "uninitialized constant Chef::Provider::#{class_name}"
diff --git a/lib/chef/provider/deploy.rb b/lib/chef/provider/deploy.rb
index 6d9b7f4397..77a0410593 100644
--- a/lib/chef/provider/deploy.rb
+++ b/lib/chef/provider/deploy.rb
@@ -201,7 +201,7 @@ class Chef
converge_by("execute migration command #{@new_resource.migration_command}") do
Chef::Log.info "#{@new_resource} migrating #{@new_resource.user} with environment #{env_info}"
- run_command(run_options(:command => @new_resource.migration_command, :cwd=>release_path, :log_level => :info))
+ shell_out!(@new_resource.migration_command,run_options(:cwd=>release_path, :log_level => :info))
end
end
end
@@ -221,7 +221,7 @@ class Chef
else
converge_by("restart app using command #{@new_resource.restart_command}") do
Chef::Log.info("#{@new_resource} restarting app")
- run_command(run_options(:command => @new_resource.restart_command, :cwd => @new_resource.current_path))
+ shell_out!(@new_resource.restart_command,run_options(:cwd=>@new_resource.current_path))
end
end
end
diff --git a/lib/chef/provider/dsc_resource.rb b/lib/chef/provider/dsc_resource.rb
index 379369ba6e..65830131ab 100644
--- a/lib/chef/provider/dsc_resource.rb
+++ b/lib/chef/provider/dsc_resource.rb
@@ -59,9 +59,7 @@ class Chef
a.block_action!
end
requirements.assert(:run) do |a|
- a.assertion {
- meta_configuration['RefreshMode'] == 'Disabled'
- }
+ a.assertion { dsc_refresh_mode_disabled? }
err = ["The LCM must have its RefreshMode set to Disabled. "]
a.failure_message Chef::Exceptions::ProviderNotFound, err.join(' ')
a.whyrun err + ["Assuming a previous resource sets the RefreshMode."]
@@ -85,6 +83,10 @@ class Chef
def supports_dsc_invoke_resource?
run_context && Chef::Platform.supports_dsc_invoke_resource?(node)
end
+
+ def dsc_refresh_mode_disabled?
+ Chef::Platform.dsc_refresh_mode_disabled?(node)
+ end
def generate_description
@converge_description
@@ -153,12 +155,6 @@ class Chef
cmdlet.run!
end
- def meta_configuration
- cmdlet = Chef::Util::Powershell::Cmdlet.new(node, "Get-DscLocalConfigurationManager", :object)
- result = cmdlet.run!
- result.return_value
- end
-
end
end
end
diff --git a/lib/chef/provider/execute.rb b/lib/chef/provider/execute.rb
index b44112c19e..c3dd3b4ee1 100644
--- a/lib/chef/provider/execute.rb
+++ b/lib/chef/provider/execute.rb
@@ -41,7 +41,7 @@ class Chef
def define_resource_requirements
# @todo: this should change to raise in some appropriate major version bump.
if creates && creates_relative? && !cwd
- Chef::Log.warn "Providing a relative path for the creates attribute without the cwd is deprecated and will be changed to fail (CHEF-3819)"
+ Chef::Log.warn "Providing a relative path for the creates attribute without the cwd is deprecated and will be changed to fail in the future (CHEF-3819)"
end
end
diff --git a/lib/chef/provider/lwrp_base.rb b/lib/chef/provider/lwrp_base.rb
index a96c382a01..9c7cd15bbf 100644
--- a/lib/chef/provider/lwrp_base.rb
+++ b/lib/chef/provider/lwrp_base.rb
@@ -19,6 +19,7 @@
#
require 'chef/provider'
+require 'chef/dsl/recipe'
require 'chef/dsl/include_recipe'
class Chef
diff --git a/lib/chef/provider/package/openbsd.rb b/lib/chef/provider/package/openbsd.rb
index 83fc09c8ae..7a6582363e 100644
--- a/lib/chef/provider/package/openbsd.rb
+++ b/lib/chef/provider/package/openbsd.rb
@@ -111,7 +111,7 @@ class Chef
end
end
results = results.reject(&:nil?)
- Chef::Log.debug("candidate versions of '#{new_resource.package_name}' are '#{results}'")
+ Chef::Log.debug("Candidate versions of '#{new_resource.package_name}' are '#{results}'")
case results.length
when 0
[]
diff --git a/lib/chef/provider/package/rpm.rb b/lib/chef/provider/package/rpm.rb
index c5d52a8384..6ce0dd689f 100644
--- a/lib/chef/provider/package/rpm.rb
+++ b/lib/chef/provider/package/rpm.rb
@@ -61,7 +61,7 @@ class Chef
Chef::Log.debug("#{@new_resource} checking rpm status")
shell_out_with_timeout!("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}").stdout.each_line do |line|
case line
- when /^([\w\d+_.-]+)\s([\w\d~_.-]+)$/
+ when /^(\S+)\s(\S+)$/
@current_resource.package_name($1)
@new_resource.version($2)
@candidate_version = $2
@@ -78,7 +78,7 @@ class Chef
@rpm_status = shell_out_with_timeout("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@current_resource.package_name}")
@rpm_status.stdout.each_line do |line|
case line
- when /^([\w\d+_.-]+)\s([\w\d~_.-]+)$/
+ when /^(\S+)\s(\S+)$/
Chef::Log.debug("#{@new_resource} current version is #{$2}")
@current_resource.version($2)
end
diff --git a/lib/chef/provider/package/windows/msi.rb b/lib/chef/provider/package/windows/msi.rb
index 31faa78215..7fdbbcff35 100644
--- a/lib/chef/provider/package/windows/msi.rb
+++ b/lib/chef/provider/package/windows/msi.rb
@@ -18,7 +18,7 @@
# TODO: Allow @new_resource.source to be a Product Code as a GUID for uninstall / network install
-require 'chef/win32/api/installer' if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+require 'chef/win32/api/installer' if (RUBY_PLATFORM =~ /mswin|mingw32|windows/) && Chef::Platform.supports_msi?
require 'chef/mixin/shell_out'
class Chef
@@ -26,7 +26,7 @@ class Chef
class Package
class Windows
class MSI
- include Chef::ReservedNames::Win32::API::Installer if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ include Chef::ReservedNames::Win32::API::Installer if (RUBY_PLATFORM =~ /mswin|mingw32|windows/) && Chef::Platform.supports_msi?
include Chef::Mixin::ShellOut
def initialize(resource)
diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb
index 81454380a3..aff8dc9326 100644
--- a/lib/chef/provider/package/yum.rb
+++ b/lib/chef/provider/package/yum.rb
@@ -791,7 +791,7 @@ class Chef
"/usr/bin/python"
end
rescue StandardError => e
- Chef::Log.warn("An error occured attempting to determine correct python executable. Using default.")
+ Chef::Log.warn("An error occurred attempting to determine correct python executable. Using default.")
Chef::Log.debug(e)
"/usr/bin/python"
end
diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb
index b876b6d8ee..e04efb6b42 100644
--- a/lib/chef/provider/powershell_script.rb
+++ b/lib/chef/provider/powershell_script.rb
@@ -16,6 +16,7 @@
# limitations under the License.
#
+require 'chef/platform/query_helpers'
require 'chef/provider/windows_script'
class Chef
@@ -40,16 +41,24 @@ class Chef
# Powershell.exe is always in "v1.0" folder (for backwards compatibility)
interpreter_path = Chef::Util::PathHelper.join(basepath, "WindowsPowerShell", "v1.0", interpreter)
- "\"#{interpreter_path}\" #{flags} \"#{script_file.path}\""
- end
-
- def flags
# Must use -File rather than -Command to launch the script
# file created by the base class that contains the script
# code -- otherwise, powershell.exe does not propagate the
# error status of a failed Windows process that ran at the
# end of the script, it gets changed to '1'.
- interpreter_flags = [default_interpreter_flags, '-File'].join(' ')
+ #
+ # Nano only supports -Command
+ cmd = "\"#{interpreter_path}\" #{flags}"
+ if Chef::Platform.windows_nano_server?
+ cmd << " -Command \". '#{script_file.path}'\""
+ else
+ cmd << " -File \"#{script_file.path}\""
+ end
+ cmd
+ end
+
+ def flags
+ interpreter_flags = [*default_interpreter_flags].join(' ')
if ! (@new_resource.flags.nil?)
interpreter_flags = [@new_resource.flags, interpreter_flags].join(' ')
@@ -87,7 +96,7 @@ EOH
# written to the file system at this point, which is required since
# the intent is to execute the code just written to it.
user_script_file.close
- validation_command = "\"#{interpreter}\" #{interpreter_arguments} -Command #{user_script_file.path}"
+ validation_command = "\"#{interpreter}\" #{interpreter_arguments} -Command \". '#{user_script_file.path}'\""
# Note that other script providers like bash allow syntax errors
# to be suppressed by setting 'returns' to a value that the
@@ -107,6 +116,8 @@ EOH
end
def default_interpreter_flags
+ return [] if Chef::Platform.windows_nano_server?
+
# Execution policy 'Bypass' is preferable since it doesn't require
# user input confirmation for files such as PowerShell modules
# downloaded from the Internet. However, 'Bypass' is not supported
@@ -188,6 +199,9 @@ elseif ( $LASTEXITCODE -ne $null -and $LASTEXITCODE -ne 0 )
$exitstatus = $LASTEXITCODE
}
+# Print STDOUT for the script execution
+Write-Output $chefscriptresult
+
# If this script is launched with -File, the process exit
# status of PowerShell.exe will be $exitstatus. If it was
# launched with -Command, it will be 0 if $exitstatus was 0,
diff --git a/lib/chef/provider/remote_directory.rb b/lib/chef/provider/remote_directory.rb
index 85ceb5cdae..3c1c50b963 100644
--- a/lib/chef/provider/remote_directory.rb
+++ b/lib/chef/provider/remote_directory.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,178 +16,266 @@
# limitations under the License.
#
-require 'chef/provider/file'
require 'chef/provider/directory'
+require 'chef/resource/file'
require 'chef/resource/directory'
-require 'chef/resource/remote_file'
+require 'chef/resource/cookbook_file'
require 'chef/mixin/file_class'
-require 'chef/platform'
-require 'uri'
-require 'tempfile'
-require 'net/https'
-require 'set'
+require 'chef/platform/query_helpers'
require 'chef/util/path_helper'
+require 'chef/deprecation/warnings'
+require 'chef/deprecation/provider/remote_directory'
+
+require 'forwardable'
class Chef
class Provider
class RemoteDirectory < Chef::Provider::Directory
+ extend Forwardable
+ include Chef::Mixin::FileClass
provides :remote_directory
- include Chef::Mixin::FileClass
+ def_delegators :@new_resource, :purge, :path, :source, :cookbook, :cookbook_name
+ def_delegators :@new_resource, :files_rights, :files_mode, :files_group, :files_owner, :files_backup
+ def_delegators :@new_resource, :rights, :mode, :group, :owner
+
+ # The overwrite property on the resource. Delegates to new_resource but can be mutated.
+ #
+ # @return [Boolean] if we are overwriting
+ #
+ def overwrite?
+ @overwrite = new_resource.overwrite if @overwrite.nil?
+ !!@overwrite
+ end
+
+ attr_accessor :managed_files
+
+ # Hash containing keys of the paths for all the files that we sync, plus all their
+ # parent directories.
+ #
+ # @return [Set] Ruby Set of the files that we manage
+ #
+ def managed_files
+ @managed_files ||= Set.new
+ end
+ # Handle action :create.
+ #
def action_create
super
- # Mark all files as needing to be purged
- files_to_purge = Set.new(ls(@new_resource.path)) # Make sure each path is clean
# Transfer files
files_to_transfer.each do |cookbook_file_relative_path|
create_cookbook_file(cookbook_file_relative_path)
- # parent directories and file being transferred are removed from the purge list
- Pathname.new(Chef::Util::PathHelper.cleanpath(::File.join(@new_resource.path, cookbook_file_relative_path))).descend do |d|
- files_to_purge.delete(d.to_s)
- end
+ # parent directories and file being transferred need to not be removed in the purge
+ add_managed_file(cookbook_file_relative_path)
end
- purge_unmanaged_files(files_to_purge)
+ purge_unmanaged_files
end
+ # Handle action :create_if_missing.
+ #
def action_create_if_missing
# if this action is called, ignore the existing overwrite flag
- @new_resource.overwrite(false)
+ @overwrite = false
action_create
end
- protected
+ private
- # List all excluding . and ..
- def ls(path)
- files = Dir.glob(::File.join(Chef::Util::PathHelper.escape_glob(path), '**', '*'),
- ::File::FNM_DOTMATCH)
-
- # Remove current directory and previous directory
- files = files.reject do |name|
- basename = Pathname.new(name).basename().to_s
- ['.', '..'].include?(basename)
+ # Add a file and its parent directories to the managed_files Hash.
+ #
+ # @param [String] cookbook_file_relative_path relative path to the file
+ # @api private
+ #
+ def add_managed_file(cookbook_file_relative_path)
+ if purge
+ Pathname.new(Chef::Util::PathHelper.cleanpath(::File.join(path, cookbook_file_relative_path))).descend do |d|
+ managed_files.add(d.to_s)
+ end
end
-
- # Clean all the paths... this is required because of the join
- files.map {|f| Chef::Util::PathHelper.cleanpath(f)}
end
- def purge_unmanaged_files(unmanaged_files)
- if @new_resource.purge
- unmanaged_files.sort.reverse.each do |f|
- # file_class comes from Chef::Mixin::FileClass
- if ::File.directory?(f) && !Chef::Platform.windows? && !file_class.symlink?(f.dup)
- # Linux treats directory symlinks as files
- # Remove a directory as a directory when not on windows if it is not a symlink
- purge_directory(f)
- elsif ::File.directory?(f) && Chef::Platform.windows?
- # Windows treats directory symlinks as directories so we delete them here
- purge_directory(f)
- else
- converge_by("delete unmanaged file #{f}") do
- ::File.delete(f)
- Chef::Log.debug("#{@new_resource} deleted file #{f}")
+ # Remove all files not in the managed_files Set.
+ #
+ # @api private
+ #
+ def purge_unmanaged_files
+ if purge
+ Dir.glob(::File.join(Chef::Util::PathHelper.escape_glob(path), '**', '*'), ::File::FNM_DOTMATCH).sort!.reverse!.each do |file|
+ # skip '.' and '..'
+ next if ['.','..'].include?(Pathname.new(file).basename().to_s)
+
+ # Clean the path. This is required because of the ::File.join
+ file = Chef::Util::PathHelper.cleanpath(file)
+
+ # Skip files that we've sync'd and their parent dirs
+ next if managed_files.include?(file)
+
+ if ::File.directory?(file)
+ if !Chef::Platform.windows? && file_class.symlink?(file.dup)
+ # Unix treats dir symlinks as files
+ purge_file(file)
+ else
+ # Unix dirs are dirs, Windows dirs and dir symlinks are dirs
+ purge_directory(file)
end
+ else
+ purge_file(file)
end
end
end
end
+ # Use a Chef directory sub-resource to remove a directory.
+ #
+ # @param [String] dir The path of the directory to remove
+ # @api private
+ #
def purge_directory(dir)
- converge_by("delete unmanaged directory #{dir}") do
- Dir::rmdir(dir)
- Chef::Log.debug("#{@new_resource} removed directory #{dir}")
- end
+ res = Chef::Resource::Directory.new(dir, run_context)
+ res.run_action(:delete)
+ new_resource.updated_by_last_action(true) if res.updated?
end
+ # Use a Chef file sub-resource to remove a file.
+ #
+ # @param [String] file The path of the file to remove
+ # @api private
+ #
+ def purge_file(file)
+ res = Chef::Resource::File.new(file, run_context)
+ res.run_action(:delete)
+ new_resource.updated_by_last_action(true) if res.updated?
+ end
+
+ # Get the files to tranfer. This returns files in lexicographical sort order.
+ #
+ # FIXME: it should do breadth-first, see CHEF-5080 (please use a performant sort)
+ #
+ # @return Array<String> The list of files to transfer
+ # @api private
+ #
def files_to_transfer
cookbook = run_context.cookbook_collection[resource_cookbook]
- files = cookbook.relative_filenames_in_preferred_directory(node, :files, @new_resource.source)
- files.sort.reverse
+ files = cookbook.relative_filenames_in_preferred_directory(node, :files, source)
+ files.sort_by! { |x| x.count(::File::SEPARATOR) }
end
- def directory_root_in_cookbook_cache
- @directory_root_in_cookbook_cache ||= begin
- cookbook = run_context.cookbook_collection[resource_cookbook]
- cookbook.preferred_filename_on_disk_location(node, :files, @new_resource.source, @new_resource.path)
- end
+ # Either the explicit cookbook that the user sets on the resource, or the implicit
+ # cookbook_name that the resource was declared in.
+ #
+ # @return [String] Cookbook to get file from.
+ # @api private
+ #
+ def resource_cookbook
+ cookbook || cookbook_name
end
- # Determine the cookbook to get the file from. If new resource sets an
- # explicit cookbook, use it, otherwise fall back to the implicit cookbook
- # i.e., the cookbook the resource was declared in.
- def resource_cookbook
- @new_resource.cookbook || @new_resource.cookbook_name
+ # If we are overwriting, then cookbook_file sub-resources should all be action :create,
+ # otherwise they should be :create_if_missing
+ #
+ # @return [Symbol] Action to take on cookbook_file sub-resources
+ # @api private
+ #
+ def action_for_cookbook_file
+ overwrite? ? :create : :create_if_missing
end
+ # This creates and uses a cookbook_file resource to sync a single file from the cookbook.
+ #
+ # @param [String] cookbook_file_relative_path The relative path to the cookbook file
+ # @api private
+ #
def create_cookbook_file(cookbook_file_relative_path)
- full_path = ::File.join(@new_resource.path, cookbook_file_relative_path)
+ full_path = ::File.join(path, cookbook_file_relative_path)
ensure_directory_exists(::File.dirname(full_path))
- file_to_fetch = cookbook_file_resource(full_path, cookbook_file_relative_path)
- if @new_resource.overwrite
- file_to_fetch.run_action(:create)
- else
- file_to_fetch.run_action(:create_if_missing)
- end
- @new_resource.updated_by_last_action(true) if file_to_fetch.updated?
+ res = cookbook_file_resource(full_path, cookbook_file_relative_path)
+ res.run_action(action_for_cookbook_file)
+ new_resource.updated_by_last_action(true) if res.updated?
end
+ # This creates the cookbook_file resource for use by create_cookbook_file.
+ #
+ # @param [String] target_path Path on the system to create
+ # @param [String] relative_source_path Relative path in the cookbook to the base source
+ # @return [Chef::Resource::CookbookFile] The built cookbook_file resource
+ # @api private
+ #
def cookbook_file_resource(target_path, relative_source_path)
- cookbook_file = Chef::Resource::CookbookFile.new(target_path, run_context)
- cookbook_file.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
- cookbook_file.source(::File.join(@new_resource.source, relative_source_path))
- if Chef::Platform.windows? && @new_resource.files_rights
- @new_resource.files_rights.each_pair do |permission, *args|
- cookbook_file.rights(permission, *args)
+ res = Chef::Resource::CookbookFile.new(target_path, run_context)
+ res.cookbook_name = resource_cookbook
+ res.source(::File.join(source, relative_source_path))
+ if Chef::Platform.windows? && files_rights
+ files_rights.each_pair do |permission, *args|
+ res.rights(permission, *args)
end
end
- cookbook_file.mode(@new_resource.files_mode) if @new_resource.files_mode
- cookbook_file.group(@new_resource.files_group) if @new_resource.files_group
- cookbook_file.owner(@new_resource.files_owner) if @new_resource.files_owner
- cookbook_file.backup(@new_resource.files_backup) if @new_resource.files_backup
+ res.mode(files_mode) if files_mode
+ res.group(files_group) if files_group
+ res.owner(files_owner) if files_owner
+ res.backup(files_backup) if files_backup
- cookbook_file
+ res
end
- def ensure_directory_exists(path)
- unless ::File.directory?(path)
- directory_to_create = resource_for_directory(path)
- directory_to_create.run_action(:create)
- @new_resource.updated_by_last_action(true) if directory_to_create.updated?
+ # This creates and uses a directory resource to create a directory if it is needed.
+ #
+ # @param [String] dir The path to the directory to create.
+ # @api private
+ #
+ def ensure_directory_exists(dir)
+ # doing the check here and skipping the resource should be more performant
+ unless ::File.directory?(dir)
+ res = directory_resource(dir)
+ res.run_action(:create)
+ new_resource.updated_by_last_action(true) if res.updated?
end
end
- def resource_for_directory(path)
- dir = Chef::Resource::Directory.new(path, run_context)
- dir.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
- if Chef::Platform.windows? && @new_resource.rights
+ # This creates the directory resource for ensure_directory_exists.
+ #
+ # @param [String] dir Directory path on the system
+ # @return [Chef::Resource::Directory] The built directory resource
+ # @api private
+ #
+ def directory_resource(dir)
+ res = Chef::Resource::Directory.new(dir, run_context)
+ res.cookbook_name = resource_cookbook
+ if Chef::Platform.windows? && rights
# rights are only meant to be applied to the toppest-level directory;
# Windows will handle inheritance.
- if path == @new_resource.path
- @new_resource.rights.each do |rights| #rights is a hash
- permissions = rights.delete(:permissions) #delete will return the value or nil if not found
- principals = rights.delete(:principals)
- dir.rights(permissions, principals, rights)
+ if dir == path
+ rights.each do |r|
+ r = r.dup # do not update the new_resource
+ permissions = r.delete(:permissions)
+ principals = r.delete(:principals)
+ res.rights(permissions, principals, r)
end
end
end
- dir.mode(@new_resource.mode) if @new_resource.mode
- dir.group(@new_resource.group)
- dir.owner(@new_resource.owner)
- dir.recursive(true)
- dir
- end
+ res.mode(mode) if mode
+ res.group(group) if group
+ res.owner(owner) if owner
+ res.recursive(true)
- def whyrun_supported?
- true
+ res
end
+ #
+ # Add back deprecated methods and aliases that are internally unused and should be removed in Chef-13
+ #
+ extend Chef::Deprecation::Warnings
+ include Chef::Deprecation::Provider::RemoteDirectory
+ add_deprecation_warnings_for(Chef::Deprecation::Provider::RemoteDirectory.instance_methods)
+
+ alias_method :resource_for_directory, :directory_resource
+ add_deprecation_warnings_for([:resource_for_directory])
+
end
end
end
diff --git a/lib/chef/provider/remote_file/http.rb b/lib/chef/provider/remote_file/http.rb
index f17ab5a56d..e1f1cb2da7 100644
--- a/lib/chef/provider/remote_file/http.rb
+++ b/lib/chef/provider/remote_file/http.rb
@@ -105,7 +105,7 @@ class Chef
# case you'd end up with a tar archive (no gzip) named, e.g., foo.tgz,
# which is not what you wanted.
if uri.to_s =~ /gz$/
- Chef::Log.debug("turning gzip compression off due to filename ending in gz")
+ Chef::Log.debug("Turning gzip compression off due to filename ending in gz")
opts[:disable_gzip] = true
end
opts
diff --git a/lib/chef/provider/service/redhat.rb b/lib/chef/provider/service/redhat.rb
index 33a9778715..3ad11a7672 100644
--- a/lib/chef/provider/service/redhat.rb
+++ b/lib/chef/provider/service/redhat.rb
@@ -61,8 +61,10 @@ class Chef
end
requirements.assert(:start, :enable, :reload, :restart) do |a|
- a.assertion { !@service_missing }
- a.failure_message Chef::Exceptions::Service, "#{new_resource}: unable to locate the init.d script!"
+ a.assertion do
+ custom_command_for_action?(action) || !@service_missing
+ end
+ a.failure_message Chef::Exceptions::Service, "#{new_resource}: No custom command for #{action} specified and unable to locate the init.d script!"
a.whyrun "Assuming service would be disabled. The init script is not presently installed."
end
end
diff --git a/lib/chef/provider/service/solaris.rb b/lib/chef/provider/service/solaris.rb
index eaea6bb1ab..7040503c6b 100644
--- a/lib/chef/provider/service/solaris.rb
+++ b/lib/chef/provider/service/solaris.rb
@@ -30,35 +30,39 @@ class Chef
def initialize(new_resource, run_context=nil)
super
- @init_command = "/usr/sbin/svcadm"
- @status_command = "/bin/svcs -l"
+ @init_command = "/usr/sbin/svcadm"
+ @status_command = "/bin/svcs"
@maintenace = false
end
def load_current_resource
@current_resource = Chef::Resource::Service.new(@new_resource.name)
@current_resource.service_name(@new_resource.service_name)
- unless ::File.exists? "/bin/svcs"
- raise Chef::Exceptions::Service, "/bin/svcs does not exist!"
+
+ [@init_command, @status_command].each do |cmd|
+ unless ::File.executable? cmd then
+ raise Chef::Exceptions::Service, "#{cmd} not executable!"
+ end
end
@status = service_status.enabled
+
@current_resource
end
def enable_service
- shell_out!("#{default_init_command} clear #{@new_resource.service_name}") if @maintenance
- shell_out!("#{default_init_command} enable -s #{@new_resource.service_name}")
+ shell_out!(default_init_command, "clear", @new_resource.service_name) if @maintenance
+ shell_out!(default_init_command, "enable", "-s", @new_resource.service_name)
end
def disable_service
- shell_out!("#{default_init_command} disable -s #{@new_resource.service_name}")
+ shell_out!(default_init_command, "disable", "-s", @new_resource.service_name)
end
alias_method :stop_service, :disable_service
alias_method :start_service, :enable_service
def reload_service
- shell_out_with_systems_locale!("#{default_init_command} refresh #{@new_resource.service_name}")
+ shell_out!(default_init_command, "refresh", @new_resource.service_name)
end
def restart_service
@@ -68,16 +72,38 @@ class Chef
end
def service_status
- status = shell_out!("#{@status_command} #{@current_resource.service_name}", :returns => [0, 1])
- status.stdout.each_line do |line|
- case line
- when /state\s+online/
- @current_resource.enabled(true)
- @current_resource.running(true)
- when /state\s+maintenance/
- @maintenance = true
- end
+ cmd = shell_out!(@status_command, "-l", @current_resource.service_name, :returns => [0, 1])
+ # Example output
+ # $ svcs -l rsyslog
+ # fmri svc:/application/rsyslog:default
+ # name rsyslog logging utility
+ # enabled true
+ # state online
+ # next_state none
+ # state_time April 2, 2015 04:25:19 PM EDT
+ # logfile /var/svc/log/application-rsyslog:default.log
+ # restarter svc:/system/svc/restarter:default
+ # contract_id 1115271
+ # dependency require_all/error svc:/milestone/multi-user:default (online)
+ # $
+
+ # load output into hash
+ status = {}
+ cmd.stdout.each_line do |line|
+ key, value = line.strip.split(/\s+/, 2)
+ status[key] = value
+ end
+
+ # check service state
+ @maintenance = false
+ case status['state']
+ when 'online'
+ @current_resource.enabled(true)
+ @current_resource.running(true)
+ when 'maintenance'
+ @maintenance = true
end
+
unless @current_resource.enabled
@current_resource.enabled(false)
@current_resource.running(false)
diff --git a/lib/chef/provider/subversion.rb b/lib/chef/provider/subversion.rb
index 5f36483c32..e3e3d5158a 100644
--- a/lib/chef/provider/subversion.rb
+++ b/lib/chef/provider/subversion.rb
@@ -130,8 +130,8 @@ class Chef
@new_resource.revision
else
command = scm(:info, @new_resource.repository, @new_resource.svn_info_args, authentication, "-r#{@new_resource.revision}")
- status, svn_info, error_message = output_of_command(command, run_options)
- handle_command_failures(status, "STDOUT: #{svn_info}\nSTDERR: #{error_message}")
+ svn_info = shell_out!(command, run_options(:cwd => cwd, :returns => [0,1])).stdout
+
extract_revision_info(svn_info)
end
end
@@ -142,11 +142,8 @@ class Chef
def find_current_revision
return nil unless ::File.exist?(::File.join(@new_resource.destination, ".svn"))
command = scm(:info)
- status, svn_info, error_message = output_of_command(command, run_options(:cwd => cwd))
+ svn_info = shell_out!(command, run_options(:cwd => cwd, :returns => [0,1])).stdout
- unless [0,1].include?(status.exitstatus)
- handle_command_failures(status, "STDOUT: #{svn_info}\nSTDERR: #{error_message}")
- end
extract_revision_info(svn_info)
end
@@ -180,6 +177,7 @@ class Chef
attrs
end
rev = (repo_attrs['Last Changed Rev'] || repo_attrs['Revision'])
+ rev.strip! if rev
raise "Could not parse `svn info` data: #{svn_info}" if repo_attrs.empty?
Chef::Log.debug "#{@new_resource} resolved revision #{@new_resource.revision} to #{rev}"
rev
@@ -197,12 +195,20 @@ class Chef
end
def scm(*args)
- ['svn', *args].compact.join(" ")
+ binary = svn_binary
+ binary = "\"#{binary}\"" if binary =~ /\s/
+ [binary, *args].compact.join(" ")
end
def target_dir_non_existent_or_empty?
!::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == ['.','..']
end
+
+ def svn_binary
+ @new_resource.svn_binary ||
+ (Chef::Platform.windows? ? 'svn.exe' : 'svn')
+ end
+
def assert_target_directory_valid!
target_parent_directory = ::File.dirname(@new_resource.destination)
unless ::File.directory?(target_parent_directory)
diff --git a/lib/chef/provider/template/content.rb b/lib/chef/provider/template/content.rb
index a231bd509e..693b19a8c6 100644
--- a/lib/chef/provider/template/content.rb
+++ b/lib/chef/provider/template/content.rb
@@ -29,30 +29,30 @@ class Chef
def template_location
@template_file_cache_location ||= begin
- template_finder.find(@new_resource.source, :local => @new_resource.local, :cookbook => @new_resource.cookbook)
+ template_finder.find(new_resource.source, :local => new_resource.local, :cookbook => new_resource.cookbook)
end
end
private
def file_for_provider
- context = TemplateContext.new(@new_resource.variables)
- context[:node] = @run_context.node
+ context = TemplateContext.new(new_resource.variables)
+ context[:node] = run_context.node
context[:template_finder] = template_finder
# helper variables
- context[:cookbook_name] = @new_resource.cookbook_name unless context.keys.include?(:coookbook_name)
- context[:recipe_name] = @new_resource.recipe_name unless context.keys.include?(:recipe_name)
- context[:recipe_line_string] = @new_resource.source_line unless context.keys.include?(:recipe_line_string)
- context[:recipe_path] = @new_resource.source_line_file unless context.keys.include?(:recipe_path)
- context[:recipe_line] = @new_resource.source_line_number unless context.keys.include?(:recipe_line)
- context[:template_name] = @new_resource.source unless context.keys.include?(:template_name)
+ context[:cookbook_name] = new_resource.cookbook_name unless context.keys.include?(:coookbook_name)
+ context[:recipe_name] = new_resource.recipe_name unless context.keys.include?(:recipe_name)
+ context[:recipe_line_string] = new_resource.source_line unless context.keys.include?(:recipe_line_string)
+ context[:recipe_path] = new_resource.source_line_file unless context.keys.include?(:recipe_path)
+ context[:recipe_line] = new_resource.source_line_number unless context.keys.include?(:recipe_line)
+ context[:template_name] = new_resource.source unless context.keys.include?(:template_name)
context[:template_path] = template_location unless context.keys.include?(:template_path)
- context._extend_modules(@new_resource.helper_modules)
+ context._extend_modules(new_resource.helper_modules)
output = context.render_template(template_location)
- tempfile = Tempfile.open("chef-rendered-template")
+ tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile
tempfile.binmode
tempfile.write(output)
tempfile.close
@@ -61,7 +61,7 @@ class Chef
def template_finder
@template_finder ||= begin
- TemplateFinder.new(run_context, @new_resource.cookbook_name, @run_context.node)
+ TemplateFinder.new(run_context, new_resource.cookbook_name, run_context.node)
end
end
end
diff --git a/lib/chef/provider/user.rb b/lib/chef/provider/user.rb
index 244b11db98..76aefbf1c8 100644
--- a/lib/chef/provider/user.rb
+++ b/lib/chef/provider/user.rb
@@ -89,7 +89,7 @@ class Chef
end
def define_resource_requirements
- requirements.assert(:all_actions) do |a|
+ requirements.assert(:create, :modify, :manage, :lock, :unlock) do |a|
a.assertion { @group_name_resolved }
a.failure_message Chef::Exceptions::User, "Couldn't lookup integer GID for group name #{@new_resource.gid}"
a.whyrun "group name #{@new_resource.gid} does not exist. This will cause group assignment to fail. Assuming this group will have been created previously."
diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb
index 0c0c85e18b..d9e235d4b1 100644
--- a/lib/chef/provider/user/dscl.rb
+++ b/lib/chef/provider/user/dscl.rb
@@ -257,10 +257,13 @@ user password using shadow hash.")
#
# Sets the group id for the user using dscl. Fails if a group doesn't
- # exist on the system with given group id.
+ # exist on the system with given group id. If `gid` is not specified, it
+ # sets a default Mac user group "staff", with id 20.
#
def dscl_set_gid
- unless @new_resource.gid && @new_resource.gid.to_s.match(/^\d+$/)
+ if @new_resource.gid.nil?
+ @new_resource.gid(20)
+ elsif !@new_resource.gid.to_s.match(/^\d+$/)
begin
possible_gid = run_dscl("read /Groups/#{@new_resource.gid} PrimaryGroupID").split(" ").last
rescue Chef::Exceptions::DsclCommandFailed => e
diff --git a/lib/chef/provider/user/solaris.rb b/lib/chef/provider/user/solaris.rb
index b242095f0c..c16db22ad4 100644
--- a/lib/chef/provider/user/solaris.rb
+++ b/lib/chef/provider/user/solaris.rb
@@ -1,7 +1,9 @@
#
# Author:: Stephen Nelson-Smith (<sns@opscode.com>)
# Author:: Jon Ramsey (<jonathon.ramsey@gmail.com>)
+# Author:: Dave Eddy (<dave@daveeddy.com>)
# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# Copyright:: Copyright 2015, Dave Eddy
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,7 +25,6 @@ class Chef
class User
class Solaris < Chef::Provider::User::Useradd
provides :user, platform: %w(omnios solaris2)
-
UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]]
attr_writer :password_file
@@ -43,6 +44,32 @@ class Chef
super
end
+ def check_lock
+ shadow_line = shell_out!('getent', 'shadow', new_resource.username).stdout.strip rescue nil
+
+ # if the command fails we return nil, this can happen if the user
+ # in question doesn't exist
+ return nil if shadow_line.nil?
+
+ # convert "dave:NP:16507::::::\n" to "NP"
+ fields = shadow_line.split(':')
+
+ # '*LK*...' and 'LK' are both considered locked,
+ # so look for LK at the beginning of the shadow entry
+ # optionally surrounded by '*'
+ @locked = !!fields[1].match(/^\*?LK\*?/)
+
+ @locked
+ end
+
+ def lock_user
+ shell_out!('passwd', '-l', new_resource.username)
+ end
+
+ def unlock_user
+ shell_out!('passwd', '-u', new_resource.username)
+ end
+
private
def manage_password
@@ -67,9 +94,10 @@ class Chef
buffer.close
# FIXME: mostly duplicates code with file provider deploying a file
- mode = ::File.stat(@password_file).mode & 07777
- uid = ::File.stat(@password_file).uid
- gid = ::File.stat(@password_file).gid
+ s = ::File.stat(@password_file)
+ mode = s.mode & 07777
+ uid = s.uid
+ gid = s.gid
FileUtils.chown uid, gid, buffer.path
FileUtils.chmod mode, buffer.path
diff --git a/lib/chef/provider/user/windows.rb b/lib/chef/provider/user/windows.rb
index e282a11d45..76519bb498 100644
--- a/lib/chef/provider/user/windows.rb
+++ b/lib/chef/provider/user/windows.rb
@@ -35,6 +35,10 @@ class Chef
end
def load_current_resource
+ if @new_resource.gid
+ Chef::Log.warn("The 'gid' attribute is not implemented by the Windows platform. Please use the 'group' resource to assign a user to a group.")
+ end
+
@current_resource = Chef::Resource::User.new(@new_resource.name)
@current_resource.username(@new_resource.username)
user_info = nil
@@ -42,7 +46,6 @@ class Chef
user_info = @net_user.get_info
@current_resource.uid(user_info[:user_id])
- @current_resource.gid(user_info[:primary_group_id])
@current_resource.comment(user_info[:full_name])
@current_resource.home(user_info[:home_dir])
@current_resource.shell(user_info[:script_path])
@@ -65,7 +68,7 @@ class Chef
Chef::Log.debug("#{@new_resource} password has changed")
return true
end
- [ :uid, :gid, :comment, :home, :shell ].any? do |user_attrib|
+ [ :uid, :comment, :home, :shell ].any? do |user_attrib|
!@new_resource.send(user_attrib).nil? && @new_resource.send(user_attrib) != @current_resource.send(user_attrib)
end
end
@@ -100,7 +103,6 @@ class Chef
field_list = {
'comment' => 'full_name',
'home' => 'home_dir',
- 'gid' => 'primary_group_id',
'uid' => 'user_id',
'shell' => 'script_path',
'password' => 'password'
diff --git a/lib/chef/provider_resolver.rb b/lib/chef/provider_resolver.rb
index 8459bc1328..82a24fc078 100644
--- a/lib/chef/provider_resolver.rb
+++ b/lib/chef/provider_resolver.rb
@@ -157,8 +157,8 @@ class Chef
# perf concern otherwise.)
handlers = providers.select { |handler| overrode_provides?(handler) && handler.provides?(node, resource) }
handlers.each do |handler|
- Chef::Log.deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource.resource_name}, but provides #{resource.resource_name.inspect} was never called!")
- Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
+ Chef.log_deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource.resource_name}, but provides #{resource.resource_name.inspect} was never called!")
+ Chef.log_deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
end
end
handlers
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index 5bef40625f..90453bd00e 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -19,7 +19,6 @@
#
require 'chef/exceptions'
-require 'chef/mixin/params_validate'
require 'chef/dsl/platform_introspection'
require 'chef/dsl/data_query'
require 'chef/dsl/registry_helper'
@@ -29,7 +28,7 @@ require 'chef/mixin/convert_to_class_name'
require 'chef/guard_interpreter/resource_guard_interpreter'
require 'chef/resource/conditional'
require 'chef/resource/conditional_action_not_nothing'
-require 'chef/resource/action_provider'
+require 'chef/resource/action_class'
require 'chef/resource_collection'
require 'chef/node_map'
require 'chef/node'
@@ -40,6 +39,7 @@ require 'chef/resource_resolver'
require 'set'
require 'chef/mixin/deprecation'
+require 'chef/mixin/properties'
require 'chef/mixin/provides'
require 'chef/mixin/shell_out'
require 'chef/mixin/powershell_out'
@@ -61,6 +61,34 @@ class Chef
include Chef::Mixin::ShellOut
include Chef::Mixin::PowershellOut
+ # Bring in `property` and `property_type`
+ include Chef::Mixin::Properties
+
+ #
+ # The name of this particular resource.
+ #
+ # This special resource attribute is set automatically from the declaration
+ # of the resource, e.g.
+ #
+ # execute 'Vitruvius' do
+ # command 'ls'
+ # end
+ #
+ # Will set the name to "Vitruvius".
+ #
+ # This is also used in to_s to show the resource name, e.g. `execute[Vitruvius]`.
+ #
+ # This is also used for resource notifications and subscribes in the same manner.
+ #
+ # This will coerce any object into a string via #to_s. Arrays are a special case
+ # so that `package ["foo", "bar"]` becomes package[foo, bar] instead of the more
+ # awkward `package[["foo", "bar"]]` that #to_s would produce.
+ #
+ # @param name [Object] The name to set, typically a String or Array
+ # @return [String] The name of this Resource.
+ #
+ property :name, String, coerce: proc { |v| v.is_a?(Array) ? v.join(', ') : v.to_s }, desired_state: false
+
#
# The node the current Chef run is using.
#
@@ -133,30 +161,6 @@ class Chef
end
#
- # The list of properties defined on this resource.
- #
- # Everything defined with `property` is in this list.
- #
- # @param include_superclass [Boolean] `true` to include properties defined
- # on superclasses; `false` or `nil` to return the list of properties
- # directly on this class.
- #
- # @return [Hash<Symbol,Property>] The list of property names and types.
- #
- def self.properties(include_superclass=true)
- @properties ||= {}
- if include_superclass
- if superclass.respond_to?(:properties)
- superclass.properties.merge(@properties)
- else
- @properties.dup
- end
- else
- @properties
- end
- end
-
- #
# The action or actions that will be taken when this resource is run.
#
# @param arg [Array[Symbol], Symbol] A list of actions (e.g. `:create`)
@@ -681,14 +685,13 @@ class Chef
# Resource Definition Interface (for resource developers)
#
- include Chef::Mixin::ParamsValidate
include Chef::Mixin::Deprecation
#
# The provider class for this resource.
#
# If `action :x do ... end` has been declared on this resource or its
- # superclasses, this will return the `action_provider_class`.
+ # superclasses, this will return the `action_class`.
#
# If this is not set, `provider_for_action` will dynamically determine the
# provider.
@@ -699,7 +702,7 @@ class Chef
#
# @return The provider class for this resource.
#
- # @see Chef::Resource.action_provider_class
+ # @see Chef::Resource.action_class
#
def provider(arg=nil)
klass = if arg.kind_of?(String) || arg.kind_of?(Symbol)
@@ -708,249 +711,13 @@ class Chef
arg
end
set_or_return(:provider, klass, kind_of: [ Class ]) ||
- self.class.action_provider_class
+ self.class.action_class
end
def provider=(arg)
provider(arg)
end
#
- # Create a property on this resource class.
- #
- # If a superclass has this property, or if this property has already been
- # defined by this resource, this will *override* the previous value.
- #
- # @param name [Symbol] The name of the property.
- # @param type [Object,Array<Object>] The type(s) of this property.
- # If present, this is prepended to the `is` validation option.
- # @param options [Hash<Symbol,Object>] Validation options.
- # @option options [Object,Array] :is An object, or list of
- # objects, that must match the value using Ruby's `===` operator
- # (`options[:is].any? { |v| v === value }`).
- # @option options [Object,Array] :equal_to An object, or list
- # of objects, that must be equal to the value using Ruby's `==`
- # operator (`options[:is].any? { |v| v == value }`)
- # @option options [Regexp,Array<Regexp>] :regex An object, or
- # list of objects, that must match the value with `regex.match(value)`.
- # @option options [Class,Array<Class>] :kind_of A class, or
- # list of classes, that the value must be an instance of.
- # @option options [Hash<String,Proc>] :callbacks A hash of
- # messages -> procs, all of which match the value. The proc must
- # return a truthy or falsey value (true means it matches).
- # @option options [Symbol,Array<Symbol>] :respond_to A method
- # name, or list of method names, the value must respond to.
- # @option options [Symbol,Array<Symbol>] :cannot_be A property,
- # or a list of properties, that the value cannot have (such as `:nil` or
- # `:empty`). The method with a questionmark at the end is called on the
- # value (e.g. `value.empty?`). If the value does not have this method,
- # it is considered valid (i.e. if you don't respond to `empty?` we
- # assume you are not empty).
- # @option options [Proc] :coerce A proc which will be called to
- # transform the user input to canonical form. The value is passed in,
- # and the transformed value returned as output. Lazy values will *not*
- # be passed to this method until after they are evaluated. Called in the
- # context of the resource (meaning you can access other properties).
- # @option options [Boolean] :required `true` if this property
- # must be present; `false` otherwise. This is checked after the resource
- # is fully initialized.
- # @option options [Boolean] :name_property `true` if this
- # property defaults to the same value as `name`. Equivalent to
- # `default: lazy { name }`, except that #property_is_set? will
- # return `true` if the property is set *or* if `name` is set.
- # @option options [Boolean] :name_attribute Same as `name_property`.
- # @option options [Object] :default The value this property
- # will return if the user does not set one. If this is `lazy`, it will
- # be run in the context of the instance (and able to access other
- # properties).
- # @option options [Boolean] :desired_state `true` if this property is
- # part of desired state. Defaults to `true`.
- # @option options [Boolean] :identity `true` if this property
- # is part of object identity. Defaults to `false`.
- #
- # @example Bare property
- # property :x
- #
- # @example With just a type
- # property :x, String
- #
- # @example With just options
- # property :x, default: 'hi'
- #
- # @example With type and options
- # property :x, String, default: 'hi'
- #
- def self.property(name, type=NOT_PASSED, **options)
- name = name.to_sym
-
- options[:instance_variable_name] = :"@#{name}" if !options.has_key?(:instance_variable_name)
- options.merge!(name: name, declared_in: self)
-
- if type == NOT_PASSED
- # If a type is not passed, the property derives from the
- # superclass property (if any)
- if properties.has_key?(name)
- property = properties[name].derive(**options)
- else
- property = property_type(**options)
- end
-
- # If a Property is specified, derive a new one from that.
- elsif type.is_a?(Property) || (type.is_a?(Class) && type <= Property)
- property = type.derive(**options)
-
- # If a primitive type was passed, combine it with "is"
- else
- if options[:is]
- options[:is] = ([ type ] + [ options[:is] ]).flatten(1)
- else
- options[:is] = type
- end
- property = property_type(**options)
- end
-
- if !options[:default].frozen? && (options[:default].is_a?(Array) || options[:default].is_a?(Hash))
- Chef::Log.warn("Property #{self}.#{name} has an array or hash default (#{options[:default]}). This means that if one resource modifies or appends to it, all other resources of the same type will also see the changes. Either freeze the constant with `.freeze` to prevent appending, or use lazy { #{options[:default].inspect} }.")
- end
-
- local_properties = properties(false)
- local_properties[name] = property
-
- property.emit_dsl
- end
-
- #
- # Create a reusable property type that can be used in multiple properties
- # in different resources.
- #
- # @param options [Hash<Symbol,Object>] Validation options. see #property for
- # the list of options.
- #
- # @example
- # property_type(default: 'hi')
- #
- def self.property_type(**options)
- Property.derive(**options)
- end
-
- #
- # The name of this particular resource.
- #
- # This special resource attribute is set automatically from the declaration
- # of the resource, e.g.
- #
- # execute 'Vitruvius' do
- # command 'ls'
- # end
- #
- # Will set the name to "Vitruvius".
- #
- # This is also used in to_s to show the resource name, e.g. `execute[Vitruvius]`.
- #
- # This is also used for resource notifications and subscribes in the same manner.
- #
- # This will coerce any object into a string via #to_s. Arrays are a special case
- # so that `package ["foo", "bar"]` becomes package[foo, bar] instead of the more
- # awkward `package[["foo", "bar"]]` that #to_s would produce.
- #
- # @param name [Object] The name to set, typically a String or Array
- # @return [String] The name of this Resource.
- #
- property :name, String, coerce: proc { |v| v.is_a?(Array) ? v.join(', ') : v.to_s }, desired_state: false
-
- #
- # Whether this property has been set (or whether it has a default that has
- # been retrieved).
- #
- # @param name [Symbol] The name of the property.
- # @return [Boolean] `true` if the property has been set.
- #
- def property_is_set?(name)
- property = self.class.properties[name.to_sym]
- raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
- property.is_set?(self)
- end
-
- #
- # Clear this property as if it had never been set. It will thereafter return
- # the default.
- # been retrieved).
- #
- # @param name [Symbol] The name of the property.
- #
- def reset_property(name)
- property = self.class.properties[name.to_sym]
- raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
- property.reset(self)
- end
-
- #
- # Create a lazy value for assignment to a default value.
- #
- # @param block The block to run when the value is retrieved.
- #
- # @return [Chef::DelayedEvaluator] The lazy value
- #
- def self.lazy(&block)
- DelayedEvaluator.new(&block)
- end
-
- #
- # Get or set the list of desired state properties for this resource.
- #
- # State properties are properties that describe the desired state
- # of the system, such as file permissions or ownership.
- # In general, state properties are properties that could be populated by
- # examining the state of the system (e.g., File.stat can tell you the
- # permissions on an existing file). Contrarily, properties that are not
- # "state properties" usually modify the way Chef itself behaves, for example
- # by providing additional options for a package manager to use when
- # installing a package.
- #
- # This list is used by the Chef client auditing system to extract
- # information from resources to describe changes made to the system.
- #
- # This method is unnecessary when declaring properties with `property`;
- # properties are added to state_properties by default, and can be turned off
- # with `desired_state: false`.
- #
- # ```ruby
- # property :x # part of desired state
- # property :y, desired_state: false # not part of desired state
- # ```
- #
- # @param names [Array<Symbol>] A list of property names to set as desired
- # state.
- #
- # @return [Array<Property>] All properties in desired state.
- #
- def self.state_properties(*names)
- if !names.empty?
- names = names.map { |name| name.to_sym }.uniq
-
- local_properties = properties(false)
- # Add new properties to the list.
- names.each do |name|
- property = properties[name]
- if !property
- self.property name, instance_variable_name: false, desired_state: true
- elsif !property.desired_state?
- self.property name, desired_state: true
- end
- end
-
- # If state_attrs *excludes* something which is currently desired state,
- # mark it as desired_state: false.
- local_properties.each do |name,property|
- if property.desired_state? && !names.include?(name)
- self.property name, desired_state: false
- end
- end
- end
-
- properties.values.select { |property| property.desired_state? }
- end
-
- #
# Set or return the list of "state properties" implemented by the Resource
# subclass.
#
@@ -975,56 +742,6 @@ class Chef
end
#
- # Set the identity of this resource to a particular set of properties.
- #
- # This drives #identity, which returns data that uniquely refers to a given
- # resource on the given node (in such a way that it can be correlated
- # across Chef runs).
- #
- # This method is unnecessary when declaring properties with `property`;
- # properties can be added to identity during declaration with
- # `identity: true`.
- #
- # ```ruby
- # property :x, identity: true # part of identity
- # property :y # not part of identity
- # ```
- #
- # If no properties are marked as identity, "name" is considered the identity.
- #
- # @param names [Array<Symbol>] A list of property names to set as the identity.
- #
- # @return [Array<Property>] All identity properties.
- #
- def self.identity_properties(*names)
- if !names.empty?
- names = names.map { |name| name.to_sym }
-
- # Add or change properties that are not part of the identity.
- names.each do |name|
- property = properties[name]
- if !property
- self.property name, instance_variable_name: false, identity: true
- elsif !property.identity?
- self.property name, identity: true
- end
- end
-
- # If identity_properties *excludes* something which is currently part of
- # the identity, mark it as identity: false.
- properties.each do |name,property|
- if property.identity? && !names.include?(name)
- self.property name, identity: false
- end
- end
- end
-
- result = properties.values.select { |property| property.identity? }
- result = [ properties[:name] ] if result.empty?
- result
- end
-
- #
# Set the identity of this resource to a particular property.
#
# This drives #identity, which returns data that uniquely refers to a given
@@ -1211,7 +928,7 @@ class Chef
# @deprecated Use resource_name instead.
#
def self.dsl_name
- Chef::Log.deprecation "Resource.dsl_name is deprecated and will be removed in Chef 13. Use resource_name instead."
+ Chef.log_deprecation "Resource.dsl_name is deprecated and will be removed in Chef 13. Use resource_name instead."
if name
name = self.name.split('::')[-1]
convert_to_snake_case(name)
@@ -1288,7 +1005,7 @@ class Chef
#
def self.provider_base(arg=nil)
if arg
- Chef::Log.deprecation("Resource.provider_base is deprecated and will be removed in Chef 13. Use provides on the provider, or provider on the resource, instead.")
+ Chef.log_deprecation("Resource.provider_base is deprecated and will be removed in Chef 13. Use provides on the provider, or provider on the resource, instead.")
end
@provider_base ||= arg || Chef::Provider
end
@@ -1376,7 +1093,8 @@ class Chef
#
def self.action(action, &recipe_block)
action = action.to_sym
- new_action_provider_class.action(action, &recipe_block)
+ declare_action_class
+ action_class.action(action, &recipe_block)
self.allowed_actions += [ action ]
default_action action if Array(default_action) == [:nothing]
end
@@ -1410,7 +1128,7 @@ class Chef
# @return A new copy of the resource, with values filled in from the actual
# current value.
#
- def current_resource
+ def current_value
provider = provider_for_action(Array(action).first)
if provider.whyrun_mode? && !provider.whyrun_supported?
raise "Cannot retrieve #{self.class.current_resource} in why-run mode: #{provider} does not support why-run"
@@ -1420,7 +1138,7 @@ class Chef
end
#
- # The action provider class is an automatic `Provider` created to handle
+ # The action class is an automatic `Provider` created to handle
# actions declared by `action :x do ... end`.
#
# This class will be returned by `resource.provider` if `resource.provider`
@@ -1429,40 +1147,38 @@ class Chef
#
# If the user has not declared actions on this class or its superclasses
# using `action :x do ... end`, then there is no need for this class and
- # `action_provider_class` will be `nil`.
+ # `action_class` will be `nil`.
#
# @api private
#
- def self.action_provider_class
- @action_provider_class ||
+ def self.action_class
+ @action_class ||
# If the superclass needed one, then we need one as well.
- if superclass.respond_to?(:action_provider_class) && superclass.action_provider_class
- new_action_provider_class
+ if superclass.respond_to?(:action_class) && superclass.action_class
+ declare_action_class
end
end
#
- # Ensure the action provider class actually gets created. This is called
+ # Ensure the action class actually gets created. This is called
# when the user does `action :x do ... end`.
#
+ # If a block is passed, it is run inside the action_class.
+ #
# @api private
- def self.new_action_provider_class
- return @action_provider_class if @action_provider_class
+ def self.declare_action_class
+ return @action_class if @action_class
- if superclass.respond_to?(:action_provider_class)
- base_provider = superclass.action_provider_class
+ if superclass.respond_to?(:action_class)
+ base_provider = superclass.action_class
end
base_provider ||= Chef::Provider
resource_class = self
- @action_provider_class = Class.new(base_provider) do
- include ActionProvider
- define_singleton_method(:to_s) { "#{resource_class} action provider" }
- def self.inspect
- to_s
- end
+ @action_class = Class.new(base_provider) do
+ include ActionClass
+ self.resource_class = resource_class
end
- @action_provider_class
end
#
diff --git a/lib/chef/resource/action_provider.rb b/lib/chef/resource/action_class.rb
index d71b54ef4d..12211418e9 100644
--- a/lib/chef/resource/action_provider.rb
+++ b/lib/chef/resource/action_class.rb
@@ -20,7 +20,7 @@ require 'chef/exceptions'
class Chef
class Resource
- module ActionProvider
+ module ActionClass
#
# If load_current_value! is defined on the resource, use that.
#
@@ -63,6 +63,20 @@ class Chef
end
module ClassMethods
+ #
+ # The Chef::Resource class this ActionClass was declared against.
+ #
+ # @return [Class] The Chef::Resource class this ActionClass was declared against.
+ #
+ attr_accessor :resource_class
+
+ def to_s
+ "#{resource_class} action provider"
+ end
+
+ def inspect
+ to_s
+ end
end
end
end
diff --git a/lib/chef/resource/chef_gem.rb b/lib/chef/resource/chef_gem.rb
index 0c2fdfa819..7e9d21ebd2 100644
--- a/lib/chef/resource/chef_gem.rb
+++ b/lib/chef/resource/chef_gem.rb
@@ -50,9 +50,9 @@ class Chef
# Chef::Resource.run_action: Caveat: this skips Chef::Runner.run_action, where notifications are handled
# Action could be an array of symbols, but probably won't (think install + enable for a package)
if compile_time.nil?
- Chef::Log.deprecation "#{self} chef_gem compile_time installation is deprecated"
- Chef::Log.deprecation "#{self} Please set `compile_time false` on the resource to use the new behavior."
- Chef::Log.deprecation "#{self} or set `compile_time true` on the resource if compile_time behavior is required."
+ Chef.log_deprecation "#{self} chef_gem compile_time installation is deprecated"
+ Chef.log_deprecation "#{self} Please set `compile_time false` on the resource to use the new behavior."
+ Chef.log_deprecation "#{self} or set `compile_time true` on the resource if compile_time behavior is required."
end
if compile_time || compile_time.nil?
diff --git a/lib/chef/resource/execute.rb b/lib/chef/resource/execute.rb
index ec669a75d3..11c4ae045c 100644
--- a/lib/chef/resource/execute.rb
+++ b/lib/chef/resource/execute.rb
@@ -102,7 +102,7 @@ class Chef
end
def path(arg=nil)
- Chef::Log.warn "'path' attribute of 'execute' is not used by any provider in Chef 11 and Chef 12. Use 'environment' attribute to configure 'PATH'. This attribute will be removed in Chef 13."
+ Chef::Log.warn "The 'path' attribute of 'execute' is not used by any provider in Chef 11 or Chef 12. Use 'environment' attribute to configure 'PATH'. This attribute will be removed in Chef 13."
set_or_return(
:path,
diff --git a/lib/chef/resource/file/verification.rb b/lib/chef/resource/file/verification.rb
index faf4791884..ba0bb08201 100644
--- a/lib/chef/resource/file/verification.rb
+++ b/lib/chef/resource/file/verification.rb
@@ -108,7 +108,7 @@ class Chef
def verify_command(path, opts)
# First implementation interpolated `file`; docs & RFC claim `path`
# is interpolated. Until `file` can be deprecated, interpolate both.
- Chef::Log.deprecation(
+ Chef.log_deprecation(
'%{file} is deprecated in verify command and will not be '\
'supported in Chef 13. Please use %{path} instead.'
) if @command.include?('%{file}')
diff --git a/lib/chef/resource/lwrp_base.rb b/lib/chef/resource/lwrp_base.rb
index 443e0ed819..a9a669f18c 100644
--- a/lib/chef/resource/lwrp_base.rb
+++ b/lib/chef/resource/lwrp_base.rb
@@ -1,8 +1,8 @@
#
-# Author:: Adam Jacob (<adam@opscode.com>)
-# Author:: Christopher Walters (<cw@opscode.com>)
-# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Copyright:: Copyright (c) 2008-2012 Opscode, Inc.
+# Author:: Adam Jacob (<adam@chef.io>)
+# Author:: Christopher Walters (<cw@chef.io>)
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -45,7 +45,7 @@ class Chef
def build_from_file(cookbook_name, filename, run_context)
if LWRPBase.loaded_lwrps[filename]
- Chef::Log.info("LWRP resource #{filename} from cookbook #{cookbook_name} has already been loaded! Skipping the reload.")
+ Chef::Log.info("Custom resource #{filename} from cookbook #{cookbook_name} has already been loaded! Skipping the reload.")
return loaded_lwrps[filename]
end
@@ -60,7 +60,7 @@ class Chef
# Make a useful string for the class (rather than <Class:312894723894>)
resource_class.instance_eval do
define_singleton_method(:to_s) do
- "LWRP resource #{resource_name} from cookbook #{cookbook_name}"
+ "Custom resource #{resource_name} from cookbook #{cookbook_name}"
end
define_singleton_method(:inspect) { to_s }
end
diff --git a/lib/chef/resource/script.rb b/lib/chef/resource/script.rb
index 30bed367cb..5081adf918 100644
--- a/lib/chef/resource/script.rb
+++ b/lib/chef/resource/script.rb
@@ -40,7 +40,7 @@ class Chef
unless arg.nil?
# Chef-13: change this to raise if the user is trying to set a value here
Chef::Log.warn "Specifying command attribute on a script resource is a coding error, use the 'code' attribute, or the execute resource"
- Chef::Log.warn "This attribute is deprecated and must be fixed or this code will fail on Chef-13"
+ Chef::Log.warn "This attribute is deprecated and must be fixed or this code will fail on Chef 13"
end
super
end
diff --git a/lib/chef/resource/subversion.rb b/lib/chef/resource/subversion.rb
index ae6a37caa2..a6f4cb4897 100644
--- a/lib/chef/resource/subversion.rb
+++ b/lib/chef/resource/subversion.rb
@@ -28,12 +28,17 @@ class Chef
super
@svn_arguments = '--no-auth-cache'
@svn_info_args = '--no-auth-cache'
+ @svn_binary = nil
end
# Override exception to strip password if any, so it won't appear in logs and different Chef notifications
def custom_exception_message(e)
"#{self} (#{defined_at}) had an error: #{e.class.name}: #{svn_password ? e.message.gsub(svn_password, "[hidden_password]") : e.message}"
end
+
+ def svn_binary(arg=nil)
+ set_or_return(:svn_binary, arg, :kind_of => [String])
+ end
end
end
end
diff --git a/lib/chef/resource/windows_script.rb b/lib/chef/resource/windows_script.rb
index 48e2b535a8..2bbd01d5aa 100644
--- a/lib/chef/resource/windows_script.rb
+++ b/lib/chef/resource/windows_script.rb
@@ -16,6 +16,7 @@
# limitations under the License.
#
+require 'chef/platform/query_helpers'
require 'chef/resource/script'
require 'chef/mixin/windows_architecture_helper'
@@ -51,9 +52,12 @@ class Chef
protected
def assert_architecture_compatible!(desired_architecture)
- if ! node_supports_windows_architecture?(node, desired_architecture)
+ if desired_architecture == :i386 && Chef::Platform.windows_nano_server?
raise Chef::Exceptions::Win32ArchitectureIncorrect,
- "cannot execute script with requested architecture '#{desired_architecture.to_s}' on a system with architecture '#{node_windows_architecture(node)}'"
+ "cannot execute script with requested architecture 'i386' on Windows Nano Server"
+ elsif ! node_supports_windows_architecture?(node, desired_architecture)
+ raise Chef::Exceptions::Win32ArchitectureIncorrect,
+ "cannot execute script with requested architecture '#{desired_architecture.to_s}' on a system with architecture '#{node_windows_architecture(node)}'"
end
end
end
diff --git a/lib/chef/resource_reporter.rb b/lib/chef/resource_reporter.rb
index 7d13a5a5ce..1175b0afb3 100644
--- a/lib/chef/resource_reporter.rb
+++ b/lib/chef/resource_reporter.rb
@@ -112,6 +112,7 @@ class Chef
@exception = nil
@rest_client = rest_client
@error_descriptions = {}
+ @expanded_run_list = {}
end
def run_started(run_status)
@@ -217,6 +218,10 @@ class Chef
end
end
+ def run_list_expanded(run_list_expansion)
+ @expanded_run_list = run_list_expansion
+ end
+
def post_reporting_data
if reporting_enabled?
run_data = prepare_run_data
@@ -271,6 +276,7 @@ class Chef
run_data["data"] = {}
run_data["start_time"] = start_time.to_s
run_data["end_time"] = end_time.to_s
+ run_data["expanded_run_list"] = Chef::JSONCompat.to_json(@expanded_run_list)
if exception
exception_data = {}
diff --git a/lib/chef/resource_resolver.rb b/lib/chef/resource_resolver.rb
index 47b3df18af..67cf134c62 100644
--- a/lib/chef/resource_resolver.rb
+++ b/lib/chef/resource_resolver.rb
@@ -56,7 +56,7 @@ class Chef
attr_reader :resource_name
# @api private
def resource
- Chef::Log.deprecation("Chef::ResourceResolver.resource deprecated. Use resource_name instead.")
+ Chef.log_deprecation("Chef::ResourceResolver.resource deprecated. Use resource_name instead.")
resource_name
end
# @api private
@@ -174,8 +174,8 @@ class Chef
if handlers.empty?
handlers = resources.select { |handler| overrode_provides?(handler) && handler.provides?(node, resource_name) }
handlers.each do |handler|
- Chef::Log.deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource_name}, but provides #{resource_name.inspect} was never called!")
- Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
+ Chef.log_deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource_name}, but provides #{resource_name.inspect} was never called!")
+ Chef.log_deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
end
end
handlers
diff --git a/lib/chef/rest.rb b/lib/chef/rest.rb
index f87cec9b76..4106a01077 100644
--- a/lib/chef/rest.rb
+++ b/lib/chef/rest.rb
@@ -166,7 +166,7 @@ class Chef
def retriable_http_request(method, url, req_body, headers)
rest_request = Chef::HTTP::HTTPRequest.new(method, url, req_body, headers)
- Chef::Log.debug("Sending HTTP Request via #{method} to #{url.host}:#{url.port}#{rest_request.path}")
+ Chef::Log.debug("Sending HTTP request via #{method} to #{url.host}:#{url.port}#{rest_request.path}")
retrying_http_errors(url) do
yield rest_request
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index b1113f594e..f7ab88f7e0 100644
--- a/lib/chef/run_context.rb
+++ b/lib/chef/run_context.rb
@@ -268,7 +268,7 @@ class Chef
# @see DSL::IncludeRecipe#load_recipe
#
def load_recipe(recipe_name, current_cookbook: nil)
- Chef::Log.debug("Loading Recipe #{recipe_name} via include_recipe")
+ Chef::Log.debug("Loading recipe #{recipe_name} via include_recipe")
cookbook_name, recipe_short_name = Chef::Recipe.parse_recipe_name(recipe_name, current_cookbook: current_cookbook)
@@ -308,7 +308,7 @@ ERROR_MESSAGE
raise Chef::Exceptions::RecipeNotFound, "could not find recipe file #{recipe_file}"
end
- Chef::Log.debug("Loading Recipe File #{recipe_file}")
+ Chef::Log.debug("Loading recipe file #{recipe_file}")
recipe = Chef::Recipe.new('@recipe_files', recipe_file, self)
recipe.from_file(recipe_file)
recipe
@@ -522,6 +522,9 @@ ERROR_MESSAGE
ChildRunContext.new(self)
end
+ # @api private
+ attr_writer :resource_collection
+
protected
attr_reader :cookbook_compiler
@@ -532,23 +535,18 @@ ERROR_MESSAGE
###
# These need to be settable so deploy can run a resource_collection
# independent of any cookbooks via +recipe_eval+
- def resource_collection=(value)
- Chef::Log.deprecation("Setting run_context.resource_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
- @resource_collection = value
- end
-
def audits=(value)
- Chef::Log.deprecation("Setting run_context.audits will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
+ Chef.log_deprecation("Setting run_context.audits will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
@audits = value
end
def immediate_notification_collection=(value)
- Chef::Log.deprecation("Setting run_context.immediate_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
+ Chef.log_deprecation("Setting run_context.immediate_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
@immediate_notification_collection = value
end
def delayed_notification_collection=(value)
- Chef::Log.deprecation("Setting run_context.delayed_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
+ Chef.log_deprecation("Setting run_context.delayed_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
@delayed_notification_collection = value
end
end
diff --git a/lib/chef/run_list/run_list_expansion.rb b/lib/chef/run_list/run_list_expansion.rb
index 46b45f1d9e..64e4326fb8 100644
--- a/lib/chef/run_list/run_list_expansion.rb
+++ b/lib/chef/run_list/run_list_expansion.rb
@@ -22,6 +22,7 @@ require 'chef/mixin/deep_merge'
require 'chef/role'
require 'chef/rest'
+require 'chef/json_compat'
class Chef
class RunList
@@ -54,6 +55,13 @@ class Chef
# * Duplicate roles are not shown.
attr_reader :run_list_trace
+ # Like run list trace but instead of saving the entries as strings it saves their objects
+ # The to_json method uses this list to construct json.
+ attr_reader :better_run_list_trace
+
+ attr_reader :all_missing_roles
+ attr_reader :role_errors
+
def initialize(environment, run_list_items, source=nil)
@environment = environment
@missing_roles_with_including_role = Array.new
@@ -68,6 +76,9 @@ class Chef
@applied_roles = {}
@run_list_trace = Hash.new {|h, key| h[key] = [] }
+ @better_run_list_trace = Hash.new {|h, key| h[key] = [] }
+ @all_missing_roles = {}
+ @role_errors = {}
end
# Did we find any errors (expanding roles)?
@@ -124,6 +135,7 @@ class Chef
def role_not_found(name, included_by)
Chef::Log.error("Role #{name} (included by '#{included_by}') is in the runlist but does not exist. Skipping expand.")
@missing_roles_with_including_role << [name, included_by]
+ @all_missing_roles[name] = true
nil
end
@@ -131,6 +143,15 @@ class Chef
@missing_roles_with_including_role.map {|item| item.first }
end
+ def to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
+ end
+
+ def to_hash
+ seen_items = {:recipe => {}, :role => {}}
+ {:id => @environment, :run_list => convert_run_list_trace('top level', seen_items)}
+ end
+
private
# these methods modifies internal state based on arguments, so hide it.
@@ -140,8 +161,10 @@ class Chef
end
def expand_run_list_items(items, included_by="top level")
+
if entry = items.shift
@run_list_trace[included_by.to_s] << entry.to_s
+ @better_run_list_trace[included_by.to_s] << entry
case entry.type
when :recipe
@@ -156,8 +179,26 @@ class Chef
end
end
+ # Recursive helper to decode the non-nested hash form back into a tree
+ def convert_run_list_trace(base, seen_items)
+ @better_run_list_trace[base].map do |item|
+ skipped = seen_items[item.type][item.name]
+ seen_items[item.type][item.name] = true
+ case item.type
+ when :recipe
+ {:type => 'recipe', :name => item.name, :version => item.version, :skipped => !!skipped}
+ when :role
+ error = @role_errors[item.name]
+ missing = @all_missing_roles[item.name]
+ {:type => :role, :name => item.name, :children => (missing || error || skipped) ? [] : convert_run_list_trace(item.to_s, seen_items),
+ :missing => missing, :error => error, :skipped => skipped}
+ end
+ end
+ end
+
end
+
# Expand a run list from disk. Suitable for chef-solo
class RunListExpansionFromDisk < RunListExpansion
@@ -184,8 +225,14 @@ class Chef
else
raise
end
+ rescue Exception => e
+ @role_errors[name] = e.to_s
+ raise
end
+
end
end
end
+
+
diff --git a/lib/chef/run_list/versioned_recipe_list.rb b/lib/chef/run_list/versioned_recipe_list.rb
index 2824f08f31..803156aef9 100644
--- a/lib/chef/run_list/versioned_recipe_list.rb
+++ b/lib/chef/run_list/versioned_recipe_list.rb
@@ -82,6 +82,21 @@ class Chef
qualified_recipe
end
end
+
+ # Get an array of strings of both fully-qualified and unexpanded recipe names
+ # in response to chef/chef#3767
+ # Chef-13 will revert to the behaviour of just including the fully-qualified name
+ #
+ # @return [Array] Array of strings with fully-qualified and unexpanded recipe names
+ def with_duplicate_names
+ self.map do |recipe_name|
+ if recipe_name.include?('::')
+ recipe_name
+ else
+ [recipe_name, "#{recipe_name}::default"]
+ end
+ end.flatten
+ end
end
end
end
diff --git a/lib/chef/run_lock.rb b/lib/chef/run_lock.rb
index cefe637db6..9e0952bdcb 100644
--- a/lib/chef/run_lock.rb
+++ b/lib/chef/run_lock.rb
@@ -87,27 +87,8 @@ class Chef
# Either acquire() or test() methods should be called in order to
# get the ownership of run_lock.
def test
- # ensure the runlock_file path exists
- create_path(File.dirname(runlock_file))
- @runlock = File.open(runlock_file,'a+')
-
- if Chef::Platform.windows?
- acquire_win32_mutex
- else
- # If we support FD_CLOEXEC, then use it.
- # NB: ruby-2.0.0-p195 sets FD_CLOEXEC by default, but not
- # ruby-1.8.7/1.9.3
- if Fcntl.const_defined?('F_SETFD') && Fcntl.const_defined?('FD_CLOEXEC')
- runlock.fcntl(Fcntl::F_SETFD, runlock.fcntl(Fcntl::F_GETFD, 0) | Fcntl::FD_CLOEXEC)
- end
- # Flock will return 0 if it can acquire the lock otherwise it
- # will return false
- if runlock.flock(File::LOCK_NB|File::LOCK_EX) == 0
- true
- else
- false
- end
- end
+ create_lock
+ acquire_lock
end
#
@@ -147,6 +128,34 @@ class Chef
end
end
+ # @api private solely for race condition tests
+ def create_lock
+ # ensure the runlock_file path exists
+ create_path(File.dirname(runlock_file))
+ @runlock = File.open(runlock_file,'a+')
+ end
+
+ # @api private solely for race condition tests
+ def acquire_lock
+ if Chef::Platform.windows?
+ acquire_win32_mutex
+ else
+ # If we support FD_CLOEXEC, then use it.
+ # NB: ruby-2.0.0-p195 sets FD_CLOEXEC by default, but not
+ # ruby-1.8.7/1.9.3
+ if Fcntl.const_defined?('F_SETFD') && Fcntl.const_defined?('FD_CLOEXEC')
+ runlock.fcntl(Fcntl::F_SETFD, runlock.fcntl(Fcntl::F_GETFD, 0) | Fcntl::FD_CLOEXEC)
+ end
+ # Flock will return 0 if it can acquire the lock otherwise it
+ # will return false
+ if runlock.flock(File::LOCK_NB|File::LOCK_EX) == 0
+ true
+ else
+ false
+ end
+ end
+ end
+
private
def reset
diff --git a/lib/chef/search/query.rb b/lib/chef/search/query.rb
index 6469a18c49..658af8779c 100644
--- a/lib/chef/search/query.rb
+++ b/lib/chef/search/query.rb
@@ -88,8 +88,21 @@ WARNDEP
if block
response["rows"].each { |row| block.call(row) if row }
- unless (response["start"] + response["rows"].length) >= response["total"]
- args_h[:start] = response["start"] + response["rows"].length
+ #
+ # args_h[:rows] and args_h[:start] are the page size and
+ # start position requested of the search index backing the
+ # search API.
+ #
+ # The response may contain fewer rows than arg_h[:rows] if
+ # the page of index results included deleted nodes which
+ # have been filtered from the returned data. In this case,
+ # we still want to start the next page at start +
+ # args_h[:rows] to avoid asking the search backend for
+ # overlapping pages (which could result in duplicates).
+ #
+ next_start = response["start"] + (args_h[:rows] || response["rows"].length)
+ unless next_start >= response["total"]
+ args_h[:start] = next_start
search(type, query, args_h, &block)
end
true
@@ -99,6 +112,7 @@ WARNDEP
end
private
+
def validate_type(t)
unless t.kind_of?(String) || t.kind_of?(Symbol)
msg = "Invalid search object type #{t.inspect} (#{t.class}), must be a String or Symbol." +
diff --git a/lib/chef/util/diff.rb b/lib/chef/util/diff.rb
index c2dc6e045c..b8336b5135 100644
--- a/lib/chef/util/diff.rb
+++ b/lib/chef/util/diff.rb
@@ -64,7 +64,7 @@ class Chef
def use_tempfile_if_missing(file)
tempfile = nil
unless File.exists?(file)
- Chef::Log.debug("file #{file} does not exist to diff against, using empty tempfile")
+ Chef::Log.debug("File #{file} does not exist to diff against, using empty tempfile")
tempfile = Tempfile.new("chef-diff")
file = tempfile.path
end
@@ -139,7 +139,7 @@ class Chef
return "(new content is binary, diff output suppressed)" if is_binary?(new_file)
begin
- Chef::Log.debug("running: diff -u #{old_file} #{new_file}")
+ Chef::Log.debug("Running: diff -u #{old_file} #{new_file}")
diff_str = udiff(old_file, new_file)
rescue Exception => e
diff --git a/lib/chef/util/windows.rb b/lib/chef/util/windows.rb
index 777fe4adbb..7d29a67ac5 100644
--- a/lib/chef/util/windows.rb
+++ b/lib/chef/util/windows.rb
@@ -15,42 +15,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-#requires: gem install windows-pr
-require 'windows/api'
-require 'windows/error'
-require 'windows/handle'
-require 'windows/unicode'
-require 'windows/msvcrt/buffer'
-require 'windows/msvcrt/string'
-require 'windows/network/management'
class Chef
class Util
class Windows
- protected
-
- include ::Windows::Error
- include ::Windows::Unicode
- include ::Windows::MSVCRT::Buffer
- include ::Windows::MSVCRT::String
- include ::Windows::Network::Management
-
- PTR_SIZE = 4 #XXX 64-bit
-
- def lpwstr_to_s(buffer, offset)
- str = 0.chr * (256 * 2) #XXX unhardcode this length (*2 for WCHAR)
- wcscpy str, buffer[offset*PTR_SIZE,PTR_SIZE].unpack('L')[0]
- wide_to_multi str
- end
-
- def dword_to_i(buffer, offset)
- buffer[offset*PTR_SIZE,PTR_SIZE].unpack('i')[0] || 0
- end
-
- #return pointer for use with pack('L')
- def str_to_ptr(v)
- [v].pack('p*').unpack('L')[0]
- end
end
end
end
diff --git a/lib/chef/util/windows/net_use.rb b/lib/chef/util/windows/net_use.rb
index 62d7e169dc..b94576e702 100644
--- a/lib/chef/util/windows/net_use.rb
+++ b/lib/chef/util/windows/net_use.rb
@@ -21,61 +21,18 @@
#see also cmd.exe: net use /?
require 'chef/util/windows'
+require 'chef/win32/net'
class Chef::Util::Windows::NetUse < Chef::Util::Windows
-
- private
-
- USE_NOFORCE = 0
- USE_FORCE = 1
- USE_LOTS_OF_FORCE = 2 #every windows API should support this flag
-
- USE_INFO_2 = [
- [:local, nil],
- [:remote, nil],
- [:password, nil],
- [:status, 0],
- [:asg_type, 0],
- [:refcount, 0],
- [:usecount, 0],
- [:username, nil],
- [:domainname, nil]
- ]
-
- USE_INFO_2_TEMPLATE =
- USE_INFO_2.collect { |field| field[1].class == Fixnum ? 'i' : 'L' }.join
-
- SIZEOF_USE_INFO_2 = #sizeof(USE_INFO_2)
- USE_INFO_2.inject(0) do |sum, item|
- sum + (item[1].class == Fixnum ? 4 : PTR_SIZE)
- end
-
- def use_info_2(args)
- USE_INFO_2.collect { |field|
- args.include?(field[0]) ? args[field[0]] : field[1]
- }
- end
-
- def use_info_2_pack(use)
- use.collect { |v|
- v.class == Fixnum ? v : str_to_ptr(multi_to_wide(v))
- }.pack(USE_INFO_2_TEMPLATE)
+ def initialize(localname)
+ @use_name = localname
end
- def use_info_2_unpack(buffer)
- use = Hash.new
- USE_INFO_2.each_with_index do |field,offset|
- use[field[0]] = field[1].class == Fixnum ?
- dword_to_i(buffer, offset) : lpwstr_to_s(buffer, offset)
+ def to_ui2_struct(use_info)
+ use_info.inject({}) do |memo, (k,v)|
+ memo["ui2_#{k}".to_sym] = v
+ memo
end
- use
- end
-
- public
-
- def initialize(localname)
- @localname = localname
- @name = multi_to_wide(localname)
end
def add(args)
@@ -84,38 +41,45 @@ class Chef::Util::Windows::NetUse < Chef::Util::Windows
args = Hash.new
args[:remote] = remote
end
- args[:local] ||= @localname
- use = use_info_2(args)
- buffer = use_info_2_pack(use)
- rc = NetUseAdd.call(nil, 2, buffer, nil)
- if rc != NERR_Success
- raise ArgumentError, get_last_error(rc)
+ args[:local] ||= use_name
+ ui2_hash = to_ui2_struct(args)
+
+ begin
+ Chef::ReservedNames::Win32::Net.net_use_add_l2(nil, ui2_hash)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
end
- def get_info
- ptr = 0.chr * PTR_SIZE
- rc = NetUseGetInfo.call(nil, @name, 2, ptr)
-
- if rc != NERR_Success
- raise ArgumentError, get_last_error(rc)
+ def from_use_info_struct(ui2_hash)
+ ui2_hash.inject({}) do |memo, (k,v)|
+ memo[k.to_s.sub('ui2_', '').to_sym] = v
+ memo
end
+ end
- ptr = ptr.unpack('L')[0]
- buffer = 0.chr * SIZEOF_USE_INFO_2
- memcpy(buffer, ptr, buffer.size)
- NetApiBufferFree(ptr)
- use_info_2_unpack(buffer)
+ def get_info
+ begin
+ ui2 = Chef::ReservedNames::Win32::Net.net_use_get_info_l2(nil, use_name)
+ from_use_info_struct(ui2)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
+ end
end
def device
get_info()[:remote]
end
- #XXX should we use some FORCE here?
+
def delete
- rc = NetUseDel.call(nil, @name, USE_NOFORCE)
- if rc != NERR_Success
- raise ArgumentError, get_last_error(rc)
+ begin
+ Chef::ReservedNames::Win32::Net.net_use_del(nil, use_name, :use_noforce)
+ rescue Chef::Exceptions::Win32APIError => e
+ raise ArgumentError, e
end
end
+
+ def use_name
+ @use_name
+ end
end
diff --git a/lib/chef/util/windows/net_user.rb b/lib/chef/util/windows/net_user.rb
index 26fbe53db6..4ce051228a 100644
--- a/lib/chef/util/windows/net_user.rb
+++ b/lib/chef/util/windows/net_user.rb
@@ -88,7 +88,6 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows
def initialize(username)
@username = username
- @name = multi_to_wide(username)
end
LOGON32_PROVIDER_DEFAULT = Security::LOGON32_PROVIDER_DEFAULT
diff --git a/lib/chef/version.rb b/lib/chef/version.rb
index faa61aee54..c769533aa6 100644
--- a/lib/chef/version.rb
+++ b/lib/chef/version.rb
@@ -21,7 +21,7 @@
class Chef
CHEF_ROOT = File.dirname(File.expand_path(File.dirname(__FILE__)))
- VERSION = '12.5.0.current.0'
+ VERSION = '12.5.1'
end
#
diff --git a/lib/chef/win32/api/file.rb b/lib/chef/win32/api/file.rb
index 728a6c14df..9ff1ad40d6 100644
--- a/lib/chef/win32/api/file.rb
+++ b/lib/chef/win32/api/file.rb
@@ -20,6 +20,7 @@
require 'chef/win32/api'
require 'chef/win32/api/security'
require 'chef/win32/api/system'
+require 'chef/win32/unicode'
class Chef
module ReservedNames::Win32
diff --git a/lib/chef/win32/api/net.rb b/lib/chef/win32/api/net.rb
index 082cf4bb9a..b173987a05 100644
--- a/lib/chef/win32/api/net.rb
+++ b/lib/chef/win32/api/net.rb
@@ -17,6 +17,7 @@
#
require 'chef/win32/api'
+require 'chef/win32/unicode'
class Chef
module ReservedNames::Win32
@@ -40,6 +41,10 @@ class Chef
UF_NORMAL_ACCOUNT = 0x000200
UF_DONT_EXPIRE_PASSWD = 0x010000
+ USE_NOFORCE = 0
+ USE_FORCE = 1
+ USE_LOTS_OF_FORCE = 2 #every windows API should support this flag
+
NERR_Success = 0
NERR_InvalidComputer = 2351
NERR_NotPrimary = 2226
@@ -55,37 +60,7 @@ class Chef
ffi_lib "netapi32"
- class USER_INFO_3 < FFI::Struct
- layout :usri3_name, :LPWSTR,
- :usri3_password, :LPWSTR,
- :usri3_password_age, :DWORD,
- :usri3_priv, :DWORD,
- :usri3_home_dir, :LPWSTR,
- :usri3_comment, :LPWSTR,
- :usri3_flags, :DWORD,
- :usri3_script_path, :LPWSTR,
- :usri3_auth_flags, :DWORD,
- :usri3_full_name, :LPWSTR,
- :usri3_usr_comment, :LPWSTR,
- :usri3_parms, :LPWSTR,
- :usri3_workstations, :LPWSTR,
- :usri3_last_logon, :DWORD,
- :usri3_last_logoff, :DWORD,
- :usri3_acct_expires, :DWORD,
- :usri3_max_storage, :DWORD,
- :usri3_units_per_week, :DWORD,
- :usri3_logon_hours, :PBYTE,
- :usri3_bad_pw_count, :DWORD,
- :usri3_num_logons, :DWORD,
- :usri3_logon_server, :LPWSTR,
- :usri3_country_code, :DWORD,
- :usri3_code_page, :DWORD,
- :usri3_user_id, :DWORD,
- :usri3_primary_group_id, :DWORD,
- :usri3_profile, :LPWSTR,
- :usri3_home_dir_drive, :LPWSTR,
- :usri3_password_expired, :DWORD
-
+ module StructHelpers
def set(key, val)
val = if val.is_a? String
encoded = if val.encoding == Encoding::UTF_16LE
@@ -117,6 +92,47 @@ class Chef
end
end
+ def as_ruby
+ members.inject({}) do |memo, key|
+ memo[key] = get(key)
+ memo
+ end
+ end
+ end
+
+
+ class USER_INFO_3 < FFI::Struct
+ include StructHelpers
+ layout :usri3_name, :LPWSTR,
+ :usri3_password, :LPWSTR,
+ :usri3_password_age, :DWORD,
+ :usri3_priv, :DWORD,
+ :usri3_home_dir, :LPWSTR,
+ :usri3_comment, :LPWSTR,
+ :usri3_flags, :DWORD,
+ :usri3_script_path, :LPWSTR,
+ :usri3_auth_flags, :DWORD,
+ :usri3_full_name, :LPWSTR,
+ :usri3_usr_comment, :LPWSTR,
+ :usri3_parms, :LPWSTR,
+ :usri3_workstations, :LPWSTR,
+ :usri3_last_logon, :DWORD,
+ :usri3_last_logoff, :DWORD,
+ :usri3_acct_expires, :DWORD,
+ :usri3_max_storage, :DWORD,
+ :usri3_units_per_week, :DWORD,
+ :usri3_logon_hours, :PBYTE,
+ :usri3_bad_pw_count, :DWORD,
+ :usri3_num_logons, :DWORD,
+ :usri3_logon_server, :LPWSTR,
+ :usri3_country_code, :DWORD,
+ :usri3_code_page, :DWORD,
+ :usri3_user_id, :DWORD,
+ :usri3_primary_group_id, :DWORD,
+ :usri3_profile, :LPWSTR,
+ :usri3_home_dir_drive, :LPWSTR,
+ :usri3_password_expired, :DWORD
+
def usri3_logon_hours
val = self[:usri3_logon_hours]
if !val.nil? && !val.null?
@@ -125,13 +141,6 @@ class Chef
nil
end
end
-
- def as_ruby
- members.inject({}) do |memo, key|
- memo[key] = get(key)
- memo
- end
- end
end
class LOCALGROUP_MEMBERS_INFO_0 < FFI::Struct
@@ -146,19 +155,36 @@ class Chef
layout :lgrpi0_name, :LPWSTR
end
+ class USE_INFO_2 < FFI::Struct
+ include StructHelpers
+
+ layout :ui2_local, :LMSTR,
+ :ui2_remote, :LMSTR,
+ :ui2_password, :LMSTR,
+ :ui2_status, :DWORD,
+ :ui2_asg_type, :DWORD,
+ :ui2_refcount, :DWORD,
+ :ui2_usecount, :DWORD,
+ :ui2_username, :LPWSTR,
+ :ui2_domainname, :LMSTR
+ end
+
+
#NET_API_STATUS NetLocalGroupAdd(
#_In_ LPCWSTR servername,
#_In_ DWORD level,
#_In_ LPBYTE buf,
#_Out_ LPDWORD parm_err
#);
- safe_attach_function :NetLocalGroupAdd, [ :LPCWSTR, :DWORD, :LPBYTE, :LPDWORD], :DWORD
+ safe_attach_function :NetLocalGroupAdd, [
+ :LPCWSTR, :DWORD, :LPBYTE, :LPDWORD
+ ], :DWORD
#NET_API_STATUS NetLocalGroupDel(
#_In_ LPCWSTR servername,
#_In_ LPCWSTR groupname
#);
- safe_attach_function :NetLocalGroupDel, [ :LPCWSTR, :LPCWSTR], :DWORD
+ safe_attach_function :NetLocalGroupDel, [:LPCWSTR, :LPCWSTR], :DWORD
#NET_API_STATUS NetLocalGroupGetMembers(
#_In_ LPCWSTR servername,
@@ -170,7 +196,7 @@ class Chef
#_Out_ LPDWORD totalentries,
#_Inout_ PDWORD_PTR resumehandle
#);
- safe_attach_function :NetLocalGroupGetMembers, [
+ safe_attach_function :NetLocalGroupGetMembers, [
:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD,
:LPDWORD, :LPDWORD, :PDWORD_PTR
], :DWORD
@@ -185,12 +211,15 @@ class Chef
# _Out_ LPDWORD totalentries,
# _Inout_ LPDWORD resume_handle
# );
- safe_attach_function :NetUserEnum, [ :LPCWSTR, :DWORD, :DWORD, :LPBYTE, :DWORD, :LPDWORD, :LPDWORD, :LPDWORD ], :DWORD
+ safe_attach_function :NetUserEnum, [
+ :LPCWSTR, :DWORD, :DWORD, :LPBYTE,
+ :DWORD, :LPDWORD, :LPDWORD, :LPDWORD
+ ], :DWORD
# NET_API_STATUS NetApiBufferFree(
# _In_ LPVOID Buffer
# );
- safe_attach_function :NetApiBufferFree, [ :LPVOID ], :DWORD
+ safe_attach_function :NetApiBufferFree, [:LPVOID], :DWORD
#NET_API_STATUS NetUserAdd(
#_In_ LMSTR servername,
@@ -198,7 +227,9 @@ class Chef
#_In_ LPBYTE buf,
#_Out_ LPDWORD parm_err
#);
- safe_attach_function :NetUserAdd, [:LMSTR, :DWORD, :LPBYTE, :LPDWORD ], :DWORD
+ safe_attach_function :NetUserAdd, [
+ :LMSTR, :DWORD, :LPBYTE, :LPDWORD
+ ], :DWORD
#NET_API_STATUS NetLocalGroupAddMembers(
# _In_ LPCWSTR servername,
@@ -207,7 +238,9 @@ class Chef
# _In_ LPBYTE buf,
# _In_ DWORD totalentries
#);
- safe_attach_function :NetLocalGroupAddMembers, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD ], :DWORD
+ safe_attach_function :NetLocalGroupAddMembers, [
+ :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD
+ ], :DWORD
#NET_API_STATUS NetLocalGroupSetMembers(
# _In_ LPCWSTR servername,
@@ -216,7 +249,9 @@ class Chef
# _In_ LPBYTE buf,
# _In_ DWORD totalentries
#);
- safe_attach_function :NetLocalGroupSetMembers, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD ], :DWORD
+ safe_attach_function :NetLocalGroupSetMembers, [
+ :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD
+ ], :DWORD
#NET_API_STATUS NetLocalGroupDelMembers(
# _In_ LPCWSTR servername,
@@ -225,7 +260,9 @@ class Chef
# _In_ LPBYTE buf,
# _In_ DWORD totalentries
#);
- safe_attach_function :NetLocalGroupDelMembers, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD ], :DWORD
+ safe_attach_function :NetLocalGroupDelMembers, [
+ :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD
+ ], :DWORD
#NET_API_STATUS NetUserGetInfo(
# _In_ LPCWSTR servername,
@@ -233,7 +270,9 @@ class Chef
# _In_ DWORD level,
# _Out_ LPBYTE *bufptr
#);
- safe_attach_function :NetUserGetInfo, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE], :DWORD
+ safe_attach_function :NetUserGetInfo, [
+ :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE
+ ], :DWORD
#NET_API_STATUS NetApiBufferFree(
# _In_ LPVOID Buffer
@@ -247,7 +286,9 @@ class Chef
# _In_ LPBYTE buf,
# _Out_ LPDWORD parm_err
#);
- safe_attach_function :NetUserSetInfo, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :LPDWORD], :DWORD
+ safe_attach_function :NetUserSetInfo, [
+ :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :LPDWORD
+ ], :DWORD
#NET_API_STATUS NetUserDel(
# _In_ LPCWSTR servername,
@@ -255,6 +296,28 @@ class Chef
#);
safe_attach_function :NetUserDel, [:LPCWSTR, :LPCWSTR], :DWORD
+#NET_API_STATUS NetUseDel(
+ #_In_ LMSTR UncServerName,
+ #_In_ LMSTR UseName,
+ #_In_ DWORD ForceCond
+#);
+ safe_attach_function :NetUseDel, [:LMSTR, :LMSTR, :DWORD], :DWORD
+
+#NET_API_STATUS NetUseGetInfo(
+ #_In_ LMSTR UncServerName,
+ #_In_ LMSTR UseName,
+ #_In_ DWORD Level,
+ #_Out_ LPBYTE *BufPtr
+#);
+ safe_attach_function :NetUseGetInfo, [:LMSTR, :LMSTR, :DWORD, :pointer], :DWORD
+
+#NET_API_STATUS NetUseAdd(
+ #_In_ LMSTR UncServerName,
+ #_In_ DWORD Level,
+ #_In_ LPBYTE Buf,
+ #_Out_ LPDWORD ParmError
+#);
+ safe_attach_function :NetUseAdd, [:LMSTR, :DWORD, :LPBYTE, :LPDWORD], :DWORD
end
end
end
diff --git a/lib/chef/win32/api/registry.rb b/lib/chef/win32/api/registry.rb
new file mode 100644
index 0000000000..cbbf6b66bb
--- /dev/null
+++ b/lib/chef/win32/api/registry.rb
@@ -0,0 +1,51 @@
+#
+# Author:: Salim Alam (<salam@chef.io>)
+# Copyright:: Copyright 2015 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/win32/api'
+
+class Chef
+ module ReservedNames::Win32
+ module API
+ module Registry
+ extend Chef::ReservedNames::Win32::API
+
+ ###############################################
+ # Win32 API Bindings
+ ###############################################
+
+ ffi_lib 'advapi32'
+
+ # LONG WINAPI RegDeleteKeyEx(
+ # _In_ HKEY hKey,
+ # _In_ LPCTSTR lpSubKey,
+ # _In_ REGSAM samDesired,
+ # _Reserved_ DWORD Reserved
+ # );
+ safe_attach_function :RegDeleteKeyExW, [ :HKEY, :LPCTSTR, :LONG, :DWORD ], :LONG
+ safe_attach_function :RegDeleteKeyExA, [ :HKEY, :LPCTSTR, :LONG, :DWORD ], :LONG
+
+ # LONG WINAPI RegDeleteValue(
+ # _In_ HKEY hKey,
+ # _In_opt_ LPCTSTR lpValueName
+ # );
+ safe_attach_function :RegDeleteValueW, [ :HKEY, :LPCTSTR ], :LONG
+
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef/win32/api/unicode.rb b/lib/chef/win32/api/unicode.rb
index 2e3a599f0a..2a9166aa99 100644
--- a/lib/chef/win32/api/unicode.rb
+++ b/lib/chef/win32/api/unicode.rb
@@ -129,49 +129,6 @@ int WideCharToMultiByte(
=end
safe_attach_function :WideCharToMultiByte, [:UINT, :DWORD, :LPCWSTR, :int, :LPSTR, :int, :LPCSTR, :LPBOOL], :int
- ###############################################
- # Helpers
- ###############################################
-
- def utf8_to_wide(ustring)
- # ensure it is actually UTF-8
- # Ruby likes to mark binary data as ASCII-8BIT
- ustring = (ustring + "").force_encoding('UTF-8') if ustring.respond_to?(:force_encoding) && ustring.encoding.name != "UTF-8"
-
- # ensure we have the double-null termination Windows Wide likes
- ustring = ustring + "\000\000" if ustring.length == 0 or ustring[-1].chr != "\000"
-
- # encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode
- ustring = begin
- if ustring.respond_to?(:encode)
- ustring.encode('UTF-16LE')
- else
- require 'iconv'
- Iconv.conv("UTF-16LE", "UTF-8", ustring)
- end
- end
- ustring
- end
-
- def wide_to_utf8(wstring)
- # ensure it is actually UTF-16LE
- # Ruby likes to mark binary data as ASCII-8BIT
- wstring = wstring.force_encoding('UTF-16LE') if wstring.respond_to?(:force_encoding)
-
- # encode it all as UTF-8
- wstring = begin
- if wstring.respond_to?(:encode)
- wstring.encode('UTF-8')
- else
- require 'iconv'
- Iconv.conv("UTF-8", "UTF-16LE", wstring)
- end
- end
- # remove trailing CRLF and NULL characters
- wstring.strip!
- wstring
- end
-
end
end
end
diff --git a/lib/chef/win32/crypto.rb b/lib/chef/win32/crypto.rb
index 79cf51b002..e9c1da954b 100644
--- a/lib/chef/win32/crypto.rb
+++ b/lib/chef/win32/crypto.rb
@@ -19,6 +19,7 @@
require 'chef/win32/error'
require 'chef/win32/api/memory'
require 'chef/win32/api/crypto'
+require 'chef/win32/unicode'
require 'digest'
class Chef
@@ -29,7 +30,7 @@ class Chef
def self.encrypt(str, &block)
data_blob = CRYPT_INTEGER_BLOB.new
- unless CryptProtectData(CRYPT_INTEGER_BLOB.new(str.to_wstring), nil, nil, nil, nil, 0, data_blob)
+ unless CryptProtectData(CRYPT_INTEGER_BLOB.new(str.to_wstring), nil, nil, nil, nil, CRYPTPROTECT_LOCAL_MACHINE, data_blob)
Chef::ReservedNames::Win32::Error.raise!
end
bytes = data_blob[:pbData].get_bytes(0, data_blob[:cbData])
diff --git a/lib/chef/win32/file.rb b/lib/chef/win32/file.rb
index 57347643fc..700ddb24d3 100644
--- a/lib/chef/win32/file.rb
+++ b/lib/chef/win32/file.rb
@@ -17,10 +17,11 @@
# limitations under the License.
#
+require 'chef/mixin/wide_string'
require 'chef/win32/api/file'
require 'chef/win32/api/security'
require 'chef/win32/error'
-require 'chef/mixin/wstring'
+require 'chef/win32/unicode'
class Chef
module ReservedNames::Win32
@@ -161,9 +162,9 @@ class Chef
def self.file_access_check(path, desired_access)
security_descriptor = Chef::ReservedNames::Win32::Security.get_file_security(path)
- token_rights = Chef::ReservedNames::Win32::Security::TOKEN_IMPERSONATE |
+ token_rights = Chef::ReservedNames::Win32::Security::TOKEN_IMPERSONATE |
Chef::ReservedNames::Win32::Security::TOKEN_QUERY |
- Chef::ReservedNames::Win32::Security::TOKEN_DUPLICATE |
+ Chef::ReservedNames::Win32::Security::TOKEN_DUPLICATE |
Chef::ReservedNames::Win32::Security::STANDARD_RIGHTS_READ
token = Chef::ReservedNames::Win32::Security.open_process_token(
Chef::ReservedNames::Win32::Process.get_current_process,
@@ -176,7 +177,7 @@ class Chef
mapping[:GenericExecute] = Chef::ReservedNames::Win32::Security::FILE_GENERIC_EXECUTE
mapping[:GenericAll] = Chef::ReservedNames::Win32::Security::FILE_ALL_ACCESS
- Chef::ReservedNames::Win32::Security.access_check(security_descriptor, duplicate_token,
+ Chef::ReservedNames::Win32::Security.access_check(security_descriptor, duplicate_token,
desired_access, mapping)
end
diff --git a/lib/chef/win32/mutex.rb b/lib/chef/win32/mutex.rb
index 0b7d99f111..0d8eba1b3c 100644
--- a/lib/chef/win32/mutex.rb
+++ b/lib/chef/win32/mutex.rb
@@ -17,6 +17,7 @@
#
require 'chef/win32/api/synchronization'
+require 'chef/win32/unicode'
class Chef
module ReservedNames::Win32
@@ -78,7 +79,7 @@ class Chef
# of the process goes away and this class is only being used
# to synchronize chef-clients runs on a node.
Chef::Log.error("Can not release mutex '#{name}'. This might cause issues \
-if the mutex is attempted to be acquired by other threads.")
+if other threads attempt to acquire the mutex.")
Chef::ReservedNames::Win32::Error.raise!
end
end
@@ -113,5 +114,3 @@ if the mutex is attempted to be acquired by other threads.")
end
end
end
-
-
diff --git a/lib/chef/win32/net.rb b/lib/chef/win32/net.rb
index 0de310daf1..59f29c4d1b 100644
--- a/lib/chef/win32/net.rb
+++ b/lib/chef/win32/net.rb
@@ -18,11 +18,11 @@
require 'chef/win32/api/net'
require 'chef/win32/error'
-require 'chef/mixin/wstring'
+require 'chef/mixin/wide_string'
class Chef
module ReservedNames::Win32
- class NetUser
+ class Net
include Chef::ReservedNames::Win32::API::Error
extend Chef::ReservedNames::Win32::API::Error
@@ -286,6 +286,59 @@ END
net_api_error!(rc)
end
end
+
+ def self.net_use_del(server_name, use_name, force=:use_noforce)
+ server_name = wstring(server_name)
+ use_name = wstring(use_name)
+ force_const = case force
+ when :use_noforce
+ USE_NOFORCE
+ when :use_force
+ USE_FORCE
+ when :use_lots_of_force
+ USE_LOTS_OF_FORCE
+ else
+ raise ArgumentError, "force must be one of [:use_noforce, :use_force, or :use_lots_of_force]"
+ end
+
+ rc = NetUseDel(server_name, use_name, force_const)
+ if rc != NERR_Success
+ net_api_error!(rc)
+ end
+ end
+
+ def self.net_use_get_info_l2(server_name, use_name)
+ server_name = wstring(server_name)
+ use_name = wstring(use_name)
+ ui2_p = FFI::MemoryPointer.new(:pointer)
+
+ rc = NetUseGetInfo(server_name, use_name, 2, ui2_p)
+ if rc != NERR_Success
+ net_api_error!(rc)
+ end
+
+ ui2 = USE_INFO_2.new(ui2_p.read_pointer).as_ruby
+ NetApiBufferFree(ui2_p.read_pointer)
+
+ ui2
+ end
+
+ def self.net_use_add_l2(server_name, ui2_hash)
+ server_name = wstring(server_name)
+ group_name = wstring(group_name)
+
+ buf = USE_INFO_2.new
+
+ ui2_hash.each do |(k,v)|
+ buf.set(k,v)
+ end
+
+ rc = NetUseAdd(server_name, 2, buf, nil)
+ if rc != NERR_Success
+ net_api_error!(rc)
+ end
+ end
end
+ NetUser = Net # For backwards compatibility
end
end
diff --git a/lib/chef/win32/registry.rb b/lib/chef/win32/registry.rb
index 1a1aa12fad..12ad08a965 100644
--- a/lib/chef/win32/registry.rb
+++ b/lib/chef/win32/registry.rb
@@ -17,8 +17,12 @@
# limitations under the License.
#
require 'chef/reserved_names'
+require 'chef/win32/api'
+require 'chef/mixin/wide_string'
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ require 'chef/monkey_patches/win32/registry'
+ require 'chef/win32/api/registry'
require 'win32/registry'
require 'win32/api'
end
@@ -27,6 +31,14 @@ class Chef
class Win32
class Registry
+ if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ include Chef::ReservedNames::Win32::API::Registry
+ extend Chef::ReservedNames::Win32::API::Registry
+ end
+
+ include Chef::Mixin::WideString
+ extend Chef::Mixin::WideString
+
attr_accessor :run_context
attr_accessor :architecture
@@ -115,38 +127,23 @@ class Chef
Chef::Log.debug("Registry key #{key_path}, does not exist, not deleting")
return true
end
- #key_path is in the form "HKLM\Software\Opscode" for example, extracting
- #hive = HKLM,
- #hive_namespace = ::Win32::Registry::HKEY_LOCAL_MACHINE
- hive = key_path.split("\\").shift
- hive_namespace, key_including_parent = get_hive_and_key(key_path)
- if has_subkeys?(key_path)
- if recursive == true
- subkeys = get_subkeys(key_path)
- subkeys.each do |key|
- keypath_to_check = hive+"\\"+key_including_parent+"\\"+key
- Chef::Log.debug("Deleting registry key #{key_path} recursively")
- delete_key(keypath_to_check, true)
- end
- delete_key_ex(hive_namespace, key_including_parent)
- else
- raise Chef::Exceptions::Win32RegNoRecursive, "Registry key #{key_path} has subkeys, and recursive not specified"
- end
- else
- delete_key_ex(hive_namespace, key_including_parent)
- return true
+ if has_subkeys?(key_path) && !recursive
+ raise Chef::Exceptions::Win32RegNoRecursive, "Registry key #{key_path} has subkeys, and recursive not specified"
end
+ hive, key_including_parent = get_hive_and_key(key_path)
+ # key_including_parent: Software\\Root\\Branch\\Fruit
+ # key => Fruit
+ # key_parent => Software\\Root\\Branch
+ key_parts = key_including_parent.split("\\")
+ key = key_parts.pop
+ key_parent = key_parts.join("\\")
+ hive.open(key_parent, ::Win32::Registry::KEY_WRITE | registry_system_architecture) do |reg|
+ reg.delete_key(key, recursive)
+ end
+ Chef::Log.debug("Registry key #{key_path} deleted")
true
end
- #Using the 'RegDeleteKeyEx' Windows API that correctly supports WOW64 systems (Win2003)
- #instead of the 'RegDeleteKey'
- def delete_key_ex(hive, key)
- regDeleteKeyEx = ::Win32::API.new('RegDeleteKeyEx', 'LPLL', 'L', 'advapi32')
- hive_num = hive.hkey - (1 << 32)
- regDeleteKeyEx.call(hive_num, key, ::Win32::Registry::KEY_WRITE | registry_system_architecture, 0)
- end
-
def key_exists?(key_path)
hive, key = get_hive_and_key(key_path)
begin
diff --git a/lib/chef/win32/security.rb b/lib/chef/win32/security.rb
index 5c83180bc0..bc80517d80 100644
--- a/lib/chef/win32/security.rb
+++ b/lib/chef/win32/security.rb
@@ -22,7 +22,7 @@ require 'chef/win32/memory'
require 'chef/win32/process'
require 'chef/win32/unicode'
require 'chef/win32/security/token'
-require 'chef/mixin/wstring'
+require 'chef/mixin/wide_string'
class Chef
module ReservedNames::Win32
diff --git a/lib/chef/win32/security/token.rb b/lib/chef/win32/security/token.rb
index 9e494a73b9..8d4e54ad8c 100644
--- a/lib/chef/win32/security/token.rb
+++ b/lib/chef/win32/security/token.rb
@@ -18,7 +18,7 @@
require 'chef/win32/security'
require 'chef/win32/api/security'
-
+require 'chef/win32/unicode'
require 'ffi'
class Chef
diff --git a/lib/chef/win32/unicode.rb b/lib/chef/win32/unicode.rb
index e7399d5255..d63b9790b9 100644
--- a/lib/chef/win32/unicode.rb
+++ b/lib/chef/win32/unicode.rb
@@ -17,6 +17,7 @@
# limitations under the License.
#
+require 'chef/mixin/wide_string'
require 'chef/win32/api/unicode'
class Chef
@@ -30,6 +31,8 @@ end
module FFI
class Pointer
+ include Chef::Mixin::WideString
+
def read_wstring(num_wchars = nil)
if num_wchars.nil?
# Find the length of the string
@@ -43,13 +46,15 @@ module FFI
num_wchars = length
end
- Chef::ReservedNames::Win32::Unicode.wide_to_utf8(self.get_bytes(0, num_wchars*2))
+ wide_to_utf8(self.get_bytes(0, num_wchars*2))
end
end
end
class String
+ include Chef::Mixin::WideString
+
def to_wstring
- Chef::ReservedNames::Win32::Unicode.utf8_to_wide(self)
+ utf8_to_wide(self)
end
end
diff --git a/pedant.gemfile b/pedant.gemfile
index 8e64fc039c..3302bccfe1 100644
--- a/pedant.gemfile
+++ b/pedant.gemfile
@@ -1,13 +1,12 @@
source "https://rubygems.org"
gemspec :name => "chef"
-gem 'rest-client', :github => 'opscode/rest-client', :branch => 'lcg/1.6.7-version-lying'
-
# TODO figure out how to grab this stuff from the main Gemfile
gem "activesupport", "< 4.0.0", :group => :compat_testing, :platform => "ruby"
-gem "mixlib-shellout", github: "opscode/mixlib-shellout", branch: "master"
-gem "ohai", github: "opscode/ohai", branch: "master"
+# We are pinning chef-zero to 4.2.x until ChefFS can deal
+# with V1 api calls or chef-zero supports both v0 and v1
+gem "chef-zero", "~> 4.2.3"
group(:docgen) do
gem "tomlrb"
diff --git a/spec/data/cookbooks/openldap/templates/default/nested_openldap_partials.erb b/spec/data/cookbooks/openldap/templates/default/nested_openldap_partials.erb
new file mode 100644
index 0000000000..2d356ec21d
--- /dev/null
+++ b/spec/data/cookbooks/openldap/templates/default/nested_openldap_partials.erb
@@ -0,0 +1 @@
+before <%= render 'nested_partial.erb', :variables => { :hello => @test } %> after
diff --git a/spec/data/cookbooks/openldap/templates/default/nested_partial.erb b/spec/data/cookbooks/openldap/templates/default/nested_partial.erb
new file mode 100644
index 0000000000..415646ca31
--- /dev/null
+++ b/spec/data/cookbooks/openldap/templates/default/nested_partial.erb
@@ -0,0 +1 @@
+{<%= @hello %>}
diff --git a/spec/functional/dsl/reboot_pending_spec.rb b/spec/functional/dsl/reboot_pending_spec.rb
index 14dd9412d5..1d11f38dbc 100644
--- a/spec/functional/dsl/reboot_pending_spec.rb
+++ b/spec/functional/dsl/reboot_pending_spec.rb
@@ -30,13 +30,6 @@ describe Chef::DSL::RebootPending, :windows_only do
ohai
end
- def registry_unsafe?
- registry.value_exists?('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { :name => 'PendingFileRenameOperations' }) ||
- registry.key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired')
- registry.key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired') ||
- registry.key_exists?('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile')
- end
-
let(:node) { Chef::Node.new }
let(:events) { Chef::EventDispatch::Dispatcher.new }
let!(:ohai) { run_ohai } # Ensure we have necessary node data
@@ -45,76 +38,73 @@ describe Chef::DSL::RebootPending, :windows_only do
let(:registry) { Chef::Win32::Registry.new(run_context) }
describe "reboot_pending?" do
+ let(:reg_key) { nil }
+ let(:original_set) { false }
- describe "when there is nothing to indicate a reboot is pending" do
- it "should return false" do
- skip "Found existing registry keys" if registry_unsafe?
- expect(recipe.reboot_pending?).to be_falsey
- end
- end
+ before(:all) { @any_flag = Hash.new }
+
+ after { @any_flag[reg_key] = original_set }
describe 'HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations' do
+ let(:reg_key) { 'HKLM\SYSTEM\CurrentControlSet\Control\Session Manager' }
+ let(:original_set) { registry.value_exists?(reg_key, { :name => 'PendingFileRenameOperations' }) }
+
it "returns true if the registry value exists" do
- skip "Found existing registry keys" if registry_unsafe?
- registry.set_value('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager',
+ skip 'found existing registry key' if original_set
+ registry.set_value(reg_key,
{ :name => 'PendingFileRenameOperations', :type => :multi_string, :data => ['\??\C:\foo.txt|\??\C:\bar.txt'] })
expect(recipe.reboot_pending?).to be_truthy
end
after do
- unless registry_unsafe?
- registry.delete_value('HKLM\SYSTEM\CurrentControlSet\Control\Session Manager', { :name => 'PendingFileRenameOperations' })
+ unless original_set
+ registry.delete_value(reg_key, { :name => 'PendingFileRenameOperations' })
end
end
end
- describe 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired' do
+ describe 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired' do
+ let(:reg_key) { 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired' }
+ let(:original_set) { registry.key_exists?(reg_key) }
+
it "returns true if the registry key exists" do
- skip "Found existing registry keys" if registry_unsafe?
- registry.create_key('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired', false)
+ skip 'found existing registry key' if original_set
+ pending "Permissions are limited to 'TrustedInstaller' by default"
+ registry.create_key(reg_key, false)
expect(recipe.reboot_pending?).to be_truthy
end
after do
- unless registry_unsafe?
- registry.delete_key('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired', false)
+ unless original_set
+ registry.delete_key(reg_key, false)
end
end
end
- describe 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired' do
+ describe 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired' do
+ let(:reg_key) { 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired' }
+ let(:original_set) { registry.key_exists?(reg_key) }
+
it "returns true if the registry key exists" do
- pending "Permissions are limited to 'TrustedInstaller' by default"
- skip "Found existing registry keys" if registry_unsafe?
- registry.create_key('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired', false)
+ skip 'found existing registry key' if original_set
+ registry.create_key(reg_key, false)
expect(recipe.reboot_pending?).to be_truthy
end
after do
- unless registry_unsafe?
- registry.delete_key('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired', false)
+ unless original_set
+ registry.delete_key(reg_key, false)
end
end
end
- describe 'HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile\Flags' do
- it "returns true if the registry key exists" do
- skip "Found existing registry keys" if registry_unsafe?
- registry.create_key('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile', true)
- registry.set_value('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile',
- { :name => 'Flags', :type => :dword, :data => 3 })
-
- expect(recipe.reboot_pending?).to be_truthy
- end
-
- after do
- unless registry_unsafe?
- registry.delete_value('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile', { :name => 'Flags' })
- registry.delete_key('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile', false)
- end
+ describe "when there is nothing to indicate a reboot is pending" do
+ it "should return false" do
+ skip 'reboot pending' if @any_flag.any? { |_,v| v == true }
+ expect(recipe.reboot_pending?).to be_falsey
end
end
end
diff --git a/spec/functional/knife/cookbook_delete_spec.rb b/spec/functional/knife/cookbook_delete_spec.rb
index 15ac8f55ab..bffad8cbed 100644
--- a/spec/functional/knife/cookbook_delete_spec.rb
+++ b/spec/functional/knife/cookbook_delete_spec.rb
@@ -40,20 +40,30 @@ describe Chef::Knife::CookbookDelete do
end
context "when the cookbook doesn't exist" do
- before do
- @log_output = StringIO.new
-
- Chef::Log.logger = Logger.new(@log_output)
- Chef::Log.level = :debug
+ let(:log_output) { StringIO.new }
+ before do
@knife.name_args = %w{no-such-cookbook}
@api.get("/cookbooks/no-such-cookbook", 404, Chef::JSONCompat.to_json({'error'=>'dear Tim, no. -Sent from my iPad'}))
end
+ around do |ex|
+ old_logger = Chef::Log.logger
+ old_level = Chef::Log.level
+ begin
+ Chef::Log.logger = Logger.new(log_output)
+ Chef::Log.level = :debug
+ ex.run
+ ensure
+ Chef::Log.logger = old_logger
+ Chef::Log.level = old_level
+ end
+ end
+
it "logs an error and exits" do
- allow(@knife.ui).to receive(:stderr).and_return(@log_output)
+ allow(@knife.ui).to receive(:stderr).and_return(log_output)
expect {@knife.run}.to raise_error(SystemExit)
- expect(@log_output.string).to match(/Cannot find a cookbook named no-such-cookbook to delete/)
+ expect(log_output.string).to match(/Cannot find a cookbook named no-such-cookbook to delete/)
end
end
diff --git a/spec/functional/knife/ssh_spec.rb b/spec/functional/knife/ssh_spec.rb
index 6608d05771..51524b7009 100644
--- a/spec/functional/knife/ssh_spec.rb
+++ b/spec/functional/knife/ssh_spec.rb
@@ -31,6 +31,22 @@ describe Chef::Knife::Ssh do
@server.stop
end
+ let(:ssh_config) { Hash.new }
+ before do
+ allow(Net::SSH).to receive(:configuration_for).and_return(ssh_config)
+ end
+
+ # Force log level to info.
+ around do |ex|
+ old_level = Chef::Log.level
+ begin
+ Chef::Log.level = :info
+ ex.run
+ ensure
+ Chef::Log.level = old_level
+ end
+ end
+
describe "identity file" do
context "when knife[:ssh_identity_file] is set" do
before do
diff --git a/spec/functional/resource/deploy_revision_spec.rb b/spec/functional/resource/deploy_revision_spec.rb
index e5f5341fcd..4bce309a51 100644
--- a/spec/functional/resource/deploy_revision_spec.rb
+++ b/spec/functional/resource/deploy_revision_spec.rb
@@ -819,7 +819,7 @@ describe Chef::Resource::DeployRevision, :unix_only => true do
end
before do
- expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Chef::Exceptions::Exec)
+ expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
deploy_to_latest_with_callback_tracking.run_action(:deploy)
end
diff --git a/spec/functional/resource/dsc_resource_spec.rb b/spec/functional/resource/dsc_resource_spec.rb
index 6f453eeb9f..24503f1ec7 100644
--- a/spec/functional/resource/dsc_resource_spec.rb
+++ b/spec/functional/resource/dsc_resource_spec.rb
@@ -43,6 +43,8 @@ describe Chef::Resource::DscResource, :windows_powershell_dsc_only do
before do
if !Chef::Platform.supports_dsc_invoke_resource?(node)
skip 'Requires Powershell >= 5.0.10018.0'
+ elsif !Chef::Platform.dsc_refresh_mode_disabled?(node)
+ skip 'Requires LCM RefreshMode is Disabled'
end
end
context 'with an invalid dsc resource' do
diff --git a/spec/functional/resource/powershell_script_spec.rb b/spec/functional/resource/powershell_script_spec.rb
index be744e748b..91b74fd752 100644
--- a/spec/functional/resource/powershell_script_spec.rb
+++ b/spec/functional/resource/powershell_script_spec.rb
@@ -16,6 +16,7 @@
# limitations under the License.
#
+require 'chef/platform/query_helpers'
require 'spec_helper'
describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
@@ -27,7 +28,6 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
it_behaves_like "a Windows script running on Windows"
-
let(:successful_executable_script_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe $env:systemroot" }
let(:failed_executable_script_content) { "#{ENV['SystemRoot']}\\system32\\attrib.exe /badargument" }
let(:processor_architecture_script_content) { "echo $env:PROCESSOR_ARCHITECTURE" }
@@ -57,6 +57,8 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns the exit status 27 for a powershell script that exits with 27" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
file = Tempfile.new(['foo', '.ps1'])
begin
file.write "exit 27"
@@ -73,6 +75,8 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
let (:negative_exit_status) { -27 }
let (:unsigned_exit_status) { (-negative_exit_status ^ 65535) + 1 }
it "returns the exit status -27 as a signed integer or an unsigned 16-bit 2's complement value of 65509 for a powershell script that exits with -27" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
# Versions of PowerShell prior to 4.0 return a 16-bit unsigned value --
# PowerShell 4.0 and later versions return a 32-bit signed value.
file = Tempfile.new(['foo', '.ps1'])
@@ -96,6 +100,8 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns the process exit code" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.code(arbitrary_nonzero_process_exit_code_content)
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
@@ -114,24 +120,34 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns 1 if the last command was a cmdlet that failed" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.code(cmdlet_exit_code_not_found_content)
resource.returns(1)
resource.run_action(:run)
end
it "returns 1 if the last command was a cmdlet that failed and was preceded by a successfully executed non-cmdlet Windows binary" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.code([windows_process_exit_code_success_content, cmdlet_exit_code_not_found_content].join(';'))
resource.returns(1)
expect { resource.run_action(:run) }.not_to raise_error
end
it "raises a Mixlib::ShellOut::ShellCommandFailed error if the script is not syntactically correct" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.code('if({)')
resource.returns(0)
expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
end
it "raises an error if the script is not syntactically correct even if returns is set to 1 which is what powershell.exe returns for syntactically invalid scripts" do
+ # This test fails because shell_out expects the exit status to be 1, but it is actually 0
+ # The error is a false-positive.
+ skip "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.code('if({)')
resource.returns(1)
expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
@@ -146,24 +162,32 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
# errors than 0 or 1, we return that instead, which is acceptable
# since callers can test for nonzero rather than testing for 1.
it "returns 1 if the last command was a cmdlet that failed and was preceded by an unsuccessfully executed non-cmdlet Windows binary" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.code([arbitrary_nonzero_process_exit_code_content,cmdlet_exit_code_not_found_content].join(';'))
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
end
it "returns 0 if the last command was a non-cmdlet Windows binary that succeeded and was preceded by a failed cmdlet" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.code([cmdlet_exit_code_success_content, arbitrary_nonzero_process_exit_code_content].join(';'))
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
end
it "returns a specific error code if the last command was a non-cmdlet Windows binary that failed and was preceded by cmdlet that succeeded" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.code([cmdlet_exit_code_success_content, arbitrary_nonzero_process_exit_code_content].join(';'))
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
end
it "returns a specific error code if the last command was a non-cmdlet Windows binary that failed and was preceded by cmdlet that failed" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.code([cmdlet_exit_code_not_found_content, arbitrary_nonzero_process_exit_code_content].join(';'))
resource.returns(arbitrary_nonzero_process_exit_code)
resource.run_action(:run)
@@ -182,6 +206,8 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns 1 for $false as the last line of the script when convert_boolean_return is true" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.convert_boolean_return true
resource.code "$false"
resource.returns(1)
@@ -208,6 +234,8 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
it "returns 1 if an invalid flag is passed to the interpreter" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.code(cmdlet_exit_code_success_content)
resource.flags(invalid_powershell_interpreter_flag)
resource.returns(1)
@@ -286,7 +314,7 @@ configuration LCM
expect(source_contains_case_insensitive_content?( get_script_output, 'AMD64' )).to eq(true)
end
- it "executes a script with a 32-bit process if :i386 arch is specified" do
+ it "executes a script with a 32-bit process if :i386 arch is specified", :not_supported_on_nano do
resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}")
resource.architecture(:i386)
resource.returns(0)
@@ -294,6 +322,12 @@ configuration LCM
expect(source_contains_case_insensitive_content?( get_script_output, 'x86' )).to eq(true)
end
+
+ it "raises an error when executing a script with a 32-bit process on Windows Nano Server", :windows_nano_only do
+ resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}")
+ expect{ resource.architecture(:i386) }.to raise_error(Chef::Exceptions::Win32ArchitectureIncorrect,
+ "cannot execute script with requested architecture 'i386' on Windows Nano Server")
+ end
end
describe "when executing guards" do
@@ -347,6 +381,8 @@ configuration LCM
end
it "evaluates a powershell $false for a not_if block as true" do
+ pending "powershell.exe always exits with $true on nano" if Chef::Platform.windows_nano_server?
+
resource.not_if "$false"
expect(resource.should_skip?(:run)).to be_falsey
end
@@ -357,6 +393,8 @@ configuration LCM
end
it "evaluates a powershell $false for an only_if block as false" do
+ pending "powershell.exe always exits with $true on nano" if Chef::Platform.windows_nano_server?
+
resource.only_if "$false"
expect(resource.should_skip?(:run)).to be_truthy
end
@@ -377,6 +415,8 @@ configuration LCM
end
it "evaluates a non-zero powershell exit status for not_if as true" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.not_if "exit 37"
expect(resource.should_skip?(:run)).to be_falsey
end
@@ -387,6 +427,8 @@ configuration LCM
end
it "evaluates a failed executable exit status for not_if as false" do
+ pending "powershell.exe always exits with success on nano" if Chef::Platform.windows_nano_server?
+
resource.not_if windows_process_exit_code_not_found_content
expect(resource.should_skip?(:run)).to be_falsey
end
@@ -397,6 +439,8 @@ configuration LCM
end
it "evaluates a failed executable exit status for only_if as false" do
+ pending "powershell.exe always exits with success on nano" if Chef::Platform.windows_nano_server?
+
resource.only_if windows_process_exit_code_not_found_content
expect(resource.should_skip?(:run)).to be_truthy
end
@@ -407,6 +451,8 @@ configuration LCM
end
it "evaluates a failed cmdlet exit status for not_if as true" do
+ pending "powershell.exe always exits with success on nano" if Chef::Platform.windows_nano_server?
+
resource.not_if "throw 'up'"
expect(resource.should_skip?(:run)).to be_falsey
end
@@ -417,6 +463,8 @@ configuration LCM
end
it "evaluates a failed cmdlet exit status for only_if as false" do
+ pending "powershell.exe always exits with success on nano" if Chef::Platform.windows_nano_server?
+
resource.only_if "throw 'up'"
expect(resource.should_skip?(:run)).to be_truthy
end
@@ -459,30 +507,36 @@ configuration LCM
end
it "evaluates a 64-bit resource with a 64-bit guard and interprets boolean true as nonzero status code", :windows64_only do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.architecture :x86_64
resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -eq 'AMD64')"
expect(resource.should_skip?(:run)).to be_truthy
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code" do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code", :not_supported_on_nano do
resource.architecture :i386
resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -ne 'X86')"
expect(resource.should_skip?(:run)).to be_falsey
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code" do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code", :not_supported_on_nano do
resource.architecture :i386
resource.only_if "exit [int32]($env:PROCESSOR_ARCHITECTURE -eq 'X86')"
expect(resource.should_skip?(:run)).to be_truthy
end
it "evaluates a simple boolean false as nonzero status code when convert_boolean_return is true for only_if" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.convert_boolean_return true
resource.only_if "$false"
expect(resource.should_skip?(:run)).to be_truthy
end
it "evaluates a simple boolean false as nonzero status code when convert_boolean_return is true for not_if" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
resource.convert_boolean_return true
resource.not_if "$false"
expect(resource.should_skip?(:run)).to be_falsey
@@ -500,33 +554,40 @@ configuration LCM
expect(resource.should_skip?(:run)).to be_truthy
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for only_if" do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for only_if", :not_supported_on_nano do
resource.convert_boolean_return true
resource.architecture :i386
resource.only_if "$env:PROCESSOR_ARCHITECTURE -eq 'X86'"
expect(resource.should_skip?(:run)).to be_falsey
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for not_if" do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean false as zero status code using convert_boolean_return for not_if", :not_supported_on_nano do
resource.convert_boolean_return true
resource.architecture :i386
resource.not_if "$env:PROCESSOR_ARCHITECTURE -ne 'X86'"
expect(resource.should_skip?(:run)).to be_falsey
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for only_if" do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for only_if", :not_supported_on_nano do
resource.convert_boolean_return true
resource.architecture :i386
resource.only_if "$env:PROCESSOR_ARCHITECTURE -ne 'X86'"
expect(resource.should_skip?(:run)).to be_truthy
end
- it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for not_if" do
+ it "evaluates a 32-bit resource with a 32-bit guard and interprets boolean true as nonzero status code using convert_boolean_return for not_if", :not_supported_on_nano do
resource.convert_boolean_return true
resource.architecture :i386
resource.not_if "$env:PROCESSOR_ARCHITECTURE -eq 'X86'"
expect(resource.should_skip?(:run)).to be_truthy
end
+
+ it "raises an error when a 32-bit guard is used on Windows Nano Server", :windows_nano_only do
+ resource.only_if "$true", :architecture => :i386
+ expect{resource.run_action(:run)}.to raise_error(
+ Chef::Exceptions::Win32ArchitectureIncorrect,
+ /cannot execute script with requested architecture 'i386' on Windows Nano Server/)
+ end
end
end
diff --git a/spec/functional/resource/user/windows_spec.rb b/spec/functional/resource/user/windows_spec.rb
index 5e68478b34..5e3a9090d4 100644
--- a/spec/functional/resource/user/windows_spec.rb
+++ b/spec/functional/resource/user/windows_spec.rb
@@ -66,6 +66,14 @@ describe Chef::Provider::User::Windows, :windows_only do
new_resource.run_action(:create)
expect(new_resource).to be_updated_by_last_action
end
+
+ context 'with a gid specified' do
+ it 'warns unsupported' do
+ expect(Chef::Log).to receive(:warn).with(/not implemented/)
+ new_resource.gid('agroup')
+ new_resource.run_action(:create)
+ end
+ end
end
describe 'action :remove' do
diff --git a/spec/functional/resource/windows_service_spec.rb b/spec/functional/resource/windows_service_spec.rb
index 29d1fc42c3..90545429e5 100644
--- a/spec/functional/resource/windows_service_spec.rb
+++ b/spec/functional/resource/windows_service_spec.rb
@@ -18,7 +18,7 @@
require 'spec_helper'
-describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_gem_only do
+describe Chef::Resource::WindowsService, :windows_only, :system_windows_service_gem_only, :appveyor_only do
include_context "using Win32::Service"
diff --git a/spec/functional/run_lock_spec.rb b/spec/functional/run_lock_spec.rb
index 0cb8635256..78b3847d19 100644
--- a/spec/functional/run_lock_spec.rb
+++ b/spec/functional/run_lock_spec.rb
@@ -34,211 +34,232 @@ describe Chef::RunLock do
let(:lockfile){ "#{random_temp_root}/this/long/path/does/not/exist/chef-client-running.pid" }
# make sure to start with a clean slate.
- before(:each){ FileUtils.rm_r(random_temp_root) if File.exist?(random_temp_root) }
- after(:each){ FileUtils.rm_r(random_temp_root) }
+ before(:each){ log_event("rm -rf before"); FileUtils.rm_r(random_temp_root) if File.exist?(random_temp_root) }
+ after(:each){ log_event("rm -rf after"); FileUtils.rm_r(random_temp_root) if File.exist?(random_temp_root) }
- def wait_on_lock
- tries = 0
- until File.exist?(lockfile)
- raise "Lockfile never created, abandoning test" if tries > 10
- tries += 1
- sleep 0.1
- end
+ def log_event(message, time=Time.now.strftime("%H:%M:%S.%L"))
+ events << [ message, time ]
end
-
- ##
- # Side channel via a pipe allows child processes to send errors to the parent
-
- # Don't lazy create the pipe or else we might not share it with subprocesses
- let!(:error_pipe) do
- r,w = IO.pipe
- w.sync = true
- [r,w]
+ def events
+ @events ||= []
end
- let(:error_read) { error_pipe[0] }
- let(:error_write) { error_pipe[1] }
-
- after do
- error_read.close unless error_read.closed?
- error_write.close unless error_write.closed?
+ WAIT_ON_LOCK_TIME = 1.0
+ def wait_on_lock
+ Timeout::timeout(WAIT_ON_LOCK_TIME) do
+ until File.exist?(lockfile)
+ sleep 0.1
+ end
+ end
+ rescue Timeout::Error
+ raise "Lockfile never created, abandoning test"
end
- # Send a RuntimeError from the child process to the parent process. Also
- # prints error to $stdout, just in case something goes wrong with the error
- # marshaling stuff.
- def send_side_channel_error(message)
- $stderr.puts(message)
- $stderr.puts(caller)
- e = RuntimeError.new(message)
- error_write.print(Marshal.dump(e))
- end
+ CLIENT_PROCESS_TIMEOUT = 10
+ BREATHING_ROOM = 1
- # Read the error (if any) from the error channel. If a marhaled error is
- # present, it is unmarshaled and raised (which will fail the test)
- def raise_side_channel_error!
- error_write.close
- err = error_read.read
- error_read.close
+ # ClientProcess is defined below
+ let!(:p1) { ClientProcess.new(self, 'p1') }
+ let!(:p2) { ClientProcess.new(self, 'p2') }
+ after(:each) do |example|
begin
- # ArgumentError from Marshal.load indicates no data, which we assume
- # means no error in child process.
- raise Marshal.load(err)
- rescue ArgumentError
- nil
+ p1.stop
+ p2.stop
+ rescue
+ example.exception = $!
+ raise
+ ensure
+ if example.exception
+ print_events
+ end
end
end
- ##
- # Interprocess synchronization via a pipe. This allows us to control the
- # state of the processes competing over the lock without relying on sleep.
-
- let!(:sync_pipe) do
- r,w = IO.pipe
- w.sync = true
- [r,w]
- end
- let(:sync_read) { sync_pipe[0] }
- let(:sync_write) { sync_pipe[1] }
-
- after do
- sync_read.close unless sync_read.closed?
- sync_write.close unless sync_write.closed?
- end
-
- # Wait on synchronization signal. If not received within the timeout, an
- # error is sent via the error channel, and the process exits.
- def sync_wait
- if IO.select([sync_read], nil, nil, 20).nil?
- # timeout reading from the sync pipe.
- send_side_channel_error("Error syncing processes in run lock test (timeout)")
- exit!(1)
- else
- sync_read.getc
+ def print_events
+ # Consume any remaining events that went on the channel and print them all
+ p1.last_event
+ p2.last_event
+ events.each_with_index.sort_by { |(message, time), index| [ time, index ] }.each do |(message, time), index|
+ print "#{time} #{message}\n"
end
end
- # Sends a character in the sync pipe, which wakes ("unlocks") another
- # process that is waiting on the sync signal
- def sync_send
- sync_write.putc("!")
- sync_write.flush
- end
-
- ##
- # IPC to record test results in a pipe. Tests can read pipe contents to
- # check that operations occur in the expected order.
-
- let!(:results_pipe) do
- r,w = IO.pipe
- w.sync = true
- [r,w]
- end
- let(:results_read) { results_pipe[0] }
- let(:results_write) { results_pipe[1] }
-
- after do
- results_read.close unless results_read.closed?
- results_write.close unless results_write.closed?
- end
-
- # writes the message to the results pipe for later checking.
- # note that nothing accounts for the pipe filling and waiting forever on a
- # read or write call, so don't put too much data in.
- def record(message)
- results_write.puts(message)
- results_write.flush
- end
-
- def results
- results_write.flush
- results_write.close
- message = results_read.read
- results_read.close
- message
- end
-
- ##
- # Run lock is the system under test
- let!(:run_lock) { Chef::RunLock.new(lockfile) }
-
- it "creates the full path to the lockfile" do
- expect { run_lock.acquire }.not_to raise_error
- expect(File).to exist(lockfile)
- end
-
- it "sets FD_CLOEXEC on the lockfile", :supports_cloexec => true do
- run_lock.acquire
- expect(run_lock.runlock.fcntl(Fcntl::F_GETFD, 0) & Fcntl::FD_CLOEXEC).to eq(Fcntl::FD_CLOEXEC)
- end
-
- it "allows only one chef client run per lockfile" do
- # First process, gets the lock and keeps it.
- p1 = fork do
- run_lock.acquire
- record "p1 has lock"
- # Wait until the other process is trying to get the lock:
- sync_wait
- # sleep a little bit to make process p2 wait on the lock
- sleep 2
- record "p1 releasing lock"
- run_lock.release
- exit!(0)
+ context "when the lockfile does not already exist" do
+ context "when a client creates the lockfile but has not yet acquired the lock" do
+ before { p1.run_to("created lock") }
+ shared_context "second client gets the lock" do
+ it "the lockfile is created" do
+ log_event("lockfile exists? #{File.exist?(lockfile)}")
+ expect(File.exist?(lockfile)).to be_truthy
+ end
+
+ it "the lockfile is not locked" do
+ run_lock = Chef::RunLock.new(lockfile)
+ begin
+ expect(run_lock.test).to be_truthy
+ ensure
+ run_lock.release
+ end
+ end
+
+ it "the lockfile is empty" do
+ expect(IO.read(lockfile)).to eq('')
+ end
+
+ context "and a second client gets the lock" do
+ before { p2.run_to("acquired lock") }
+ it "the first client does not get the lock until the second finishes" do
+ p1.run_to("acquired lock") do
+ p2.run_to_completion
+ end
+ end
+ it "and the first client tries to get the lock and the second is killed, the first client gets the lock immediately" do
+ p1.run_to("acquired lock") do
+ sleep BREATHING_ROOM
+ expect(p1.last_event).to match(/after (started|created lock)/)
+ p2.stop
+ end
+ p1.run_to_completion
+ end
+ end
+ end
+
+ context "and the second client has done nothing" do
+ include_context "second client gets the lock"
+ end
+
+ context "and the second client has created the lockfile but not yet acquired the lock" do
+ before { p2.run_to("created lock") }
+ include_context "second client gets the lock"
+ end
end
- # Wait until p1 creates the lockfile
- wait_on_lock
-
- p2 = fork do
- # inform process p1 that we're trying to get the lock
- sync_send
- run_lock.acquire
- record "p2 has lock"
- run_lock.release
- exit!(0)
+ context "when a client acquires the lock but has not yet saved the pid" do
+ before { p1.run_to("acquired lock") }
+
+ it "the lockfile is created" do
+ log_event("lockfile exists? #{File.exist?(lockfile)}")
+ expect(File.exist?(lockfile)).to be_truthy
+ end
+
+ it "the lockfile is locked" do
+ run_lock = Chef::RunLock.new(lockfile)
+ begin
+ expect(run_lock.test).to be_falsey
+ ensure
+ run_lock.release
+ end
+ end
+
+ it "sets FD_CLOEXEC on the lockfile", :supports_cloexec => true do
+ run_lock = File.open(lockfile)
+ expect(run_lock.fcntl(Fcntl::F_GETFD, 0) & Fcntl::FD_CLOEXEC).to eq(Fcntl::FD_CLOEXEC)
+ end
+
+ it "the lockfile is empty" do
+ expect(IO.read(lockfile)).to eq('')
+ end
+
+ it "and a second client tries to acquire the lock, it doesn't get the lock until *after* the first client exits" do
+ # Start p2 and tell it to move forward in the background
+ p2.run_to("acquired lock") do
+ # While p2 is trying to acquire, wait a bit and then let p1 complete
+ sleep(BREATHING_ROOM)
+ expect(p2.last_event).to match(/after (started|created lock)/)
+ p1.run_to_completion
+ end
+
+ p2.run_to_completion
+ end
+
+ it "and a second client tries to get the lock and the first is killed, the second client gets the lock immediately" do
+ p2.run_to("acquired lock") do
+ sleep BREATHING_ROOM
+ expect(p2.last_event).to match(/after (started|created lock)/)
+ p1.stop
+ end
+ p2.run_to_completion
+ end
end
- Process.waitpid2(p1)
- Process.waitpid2(p2)
-
- raise_side_channel_error!
-
- expected=<<-E
-p1 has lock
-p1 releasing lock
-p2 has lock
-E
- expect(results).to eq(expected)
- end
-
- it "clears the lock if the process dies unexpectedly" do
- p1 = fork do
- run_lock.acquire
- record "p1 has lock"
- sleep 60
- record "p1 still has lock"
- exit! 1
+ context "when a client acquires the lock and saves the pid" do
+ before { p1.run_to("saved pid") }
+
+ it "the lockfile is created" do
+ expect(File.exist?(lockfile)).to be_truthy
+ end
+
+ it "the lockfile is locked" do
+ run_lock = Chef::RunLock.new(lockfile)
+ begin
+ expect(run_lock.test).to be_falsey
+ ensure
+ run_lock.release
+ end
+ end
+
+ it "sets FD_CLOEXEC on the lockfile", :supports_cloexec => true do
+ run_lock = File.open(lockfile)
+ expect(run_lock.fcntl(Fcntl::F_GETFD, 0) & Fcntl::FD_CLOEXEC).to eq(Fcntl::FD_CLOEXEC)
+ end
+
+ it "the PID is in the lockfile" do
+ expect(IO.read(lockfile)).to eq p1.pid.to_s
+ end
+
+ it "and a second client tries to acquire the lock, it doesn't get the lock until *after* the first client exits" do
+ # Start p2 and tell it to move forward in the background
+ p2.run_to("acquired lock") do
+ # While p2 is trying to acquire, wait a bit and then let p1 complete
+ sleep(BREATHING_ROOM)
+ expect(p2.last_event).to match(/after (started|created lock)/)
+ p1.run_to_completion
+ end
+
+ p2.run_to_completion
+ end
+
+ it "when a second client tries to get the lock and the first is killed, the second client gets the lock immediately" do
+ p2.run_to("acquired lock") do
+ sleep BREATHING_ROOM
+ expect(p2.last_event).to match(/after (started|created lock)/)
+ p1.stop
+ end
+ p2.run_to_completion
+ end
end
- wait_on_lock
- Process.kill(:KILL, p1)
- Process.waitpid2(p1)
-
- p2 = fork do
- run_lock.acquire
- record "p2 has lock"
- run_lock.release
- exit! 0
+ context "when a client acquires a lock and exits normally" do
+ before { p1.run_to_completion }
+
+ it "the lockfile remains" do
+ expect(File.exist?(lockfile)).to be_truthy
+ end
+
+ it "the lockfile is not locked" do
+ run_lock = Chef::RunLock.new(lockfile)
+ begin
+ expect(run_lock.test).to be_truthy
+ ensure
+ run_lock.release
+ end
+ end
+
+ it "the PID is in the lockfile" do
+ expect(IO.read(lockfile)).to eq p1.pid.to_s
+ end
+
+ it "and a second client tries to acquire the lock, it gets the lock immediately" do
+ p2.run_to_completion
+ end
end
-
- Process.waitpid2(p2)
-
- expect(results).to match(/p2 has lock\Z/)
end
it "test returns true and acquires the lock" do
+ run_lock = Chef::RunLock.new(lockfile)
p1 = fork do
expect(run_lock.test).to eq(true)
+ run_lock.save_pid
sleep 2
exit! 1
end
@@ -255,8 +276,10 @@ E
end
it "test returns without waiting when the lock is acquired" do
+ run_lock = Chef::RunLock.new(lockfile)
p1 = fork do
run_lock.acquire
+ run_lock.save_pid
sleep 2
exit! 1
end
@@ -267,20 +290,176 @@ E
Process.waitpid2(p1)
end
- it "doesn't truncate the lock file so that contents can be read" do
- p1 = fork do
- run_lock.acquire
- run_lock.save_pid
- sleep 2
- exit! 1
+ end
+
+ #
+ # Runs a process in the background that will:
+ #
+ # 1. start up (`started` event)
+ # 2. acquire the runlock file (`acquired lock` event)
+ # 3. save the pid to the lockfile (`saved pid` event)
+ # 4. exit
+ #
+ # You control exactly how far the client process goes with the `run_to`
+ # method: it will stop at any given spot so you can test for race conditions.
+ #
+ # It uses a pair of pipes to communicate with the process. The tests will
+ # send an event name over to the process, which gives the process permission
+ # to run until it reaches that event (at which point it waits for another event
+ # name). The process sends the name of each event it reaches back to the tests.
+ #
+ class ClientProcess
+ def initialize(example, name)
+ @example = example
+ @name = name
+ @read_from_process, @write_to_tests = IO.pipe
+ @read_from_tests, @write_to_process = IO.pipe
+ end
+
+ attr_reader :example
+ attr_reader :name
+ attr_reader :pid
+
+ def last_event
+ while true
+ line = readline_nonblock(read_from_process)
+ break if line.nil?
+ event, time = line.split("@")
+ example.log_event("#{name}.last_event got #{event}")
+ example.log_event("[#{name}] #{event}", time.strip)
+ @last_event = event
end
+ @last_event
+ end
- wait_on_lock
- sleep 0.5 # Possible race condition on Solaris which pid is observed as 0
- expect(File.read(lockfile)).to eq(p1.to_s)
+ def run_to(to_event, &background_block)
+ example.log_event("#{name}.run_to(#{to_event.inspect})")
- Process.waitpid2(p1)
+ # Start the process if it's not started
+ start if !pid
+
+ # Tell the process what to stop at (also means it can go)
+ write_to_process.print "#{to_event}\n"
+
+ # Run the background block
+ background_block.call if background_block
+
+ # Wait until it gets there
+ Timeout::timeout(CLIENT_PROCESS_TIMEOUT) do
+ until @last_event == "after #{to_event}"
+ got_event, time = read_from_process.gets.split("@")
+ example.log_event("#{name}.last_event got #{got_event}")
+ example.log_event("[#{name}] #{got_event}", time.strip)
+ @last_event = got_event
+ end
+ end
+
+ example.log_event("#{name}.run_to(#{to_event.inspect}) finished")
end
+ def run_to_completion
+ example.log_event("#{name}.run_to_completion")
+ # Start the process if it's not started
+ start if !pid
+
+ # Tell the process to stop at nothing (no blocking)
+ @write_to_process.print "nothing\n"
+
+ # Wait for the process to exit
+ wait_for_exit
+ example.log_event("#{name}.run_to_completion finished")
+ end
+
+ def wait_for_exit
+ example.log_event("#{name}.wait_for_exit (pid #{pid})")
+ Timeout::timeout(CLIENT_PROCESS_TIMEOUT) do
+ Process.wait(pid) if pid
+ end
+ example.log_event("#{name}.wait_for_exit finished (pid #{pid})")
+ end
+
+ def stop
+ if pid
+ example.log_event("#{name}.stop (pid #{pid})")
+ begin
+ # Send it the kill signal over and over until it dies
+ Timeout::timeout(CLIENT_PROCESS_TIMEOUT) do
+ Process.kill(:KILL, pid)
+ while !Process.waitpid2(pid, Process::WNOHANG)
+ sleep(0.05)
+ end
+ end
+ example.log_event("#{name}.stop finished (stopped pid #{pid})")
+ # Process not found is perfectly fine when we're trying to kill a process :)
+ rescue Errno::ESRCH
+ example.log_event("#{name}.stop finished (pid #{pid} wasn't running)")
+ end
+ end
+ end
+
+ def fire_event(event)
+ # Let the caller know what event we've reached
+ write_to_tests.print("after #{event}@#{Time.now.strftime("%H:%M:%S.%L")}\n")
+
+ # Block until the client tells us where to stop
+ if !@run_to_event || event == @run_to_event
+ write_to_tests.print("waiting for instructions after #{event}@#{Time.now.strftime("%H:%M:%S.%L")}\n")
+ @run_to_event = read_from_tests.gets.strip
+ write_to_tests.print("told to run to #{@run_to_event} after #{event}@#{Time.now.strftime("%H:%M:%S.%L")}\n")
+ elsif @run_to_event
+ write_to_tests.print("continuing until #{@run_to_event} after #{event}@#{Time.now.strftime("%H:%M:%S.%L")}\n")
+ end
+ end
+
+ private
+
+ attr_reader :read_from_process
+ attr_reader :write_to_tests
+ attr_reader :read_from_tests
+ attr_reader :write_to_process
+
+ class TestRunLock < Chef::RunLock
+ attr_accessor :client_process
+ def create_lock
+ super
+ client_process.fire_event("created lock")
+ end
+ end
+
+ def start
+ example.log_event("#{name}.start")
+ @pid = fork do
+ begin
+ Timeout::timeout(CLIENT_PROCESS_TIMEOUT) do
+ run_lock = TestRunLock.new(example.lockfile)
+ run_lock.client_process = self
+ fire_event("started")
+ run_lock.acquire
+ fire_event("acquired lock")
+ run_lock.save_pid
+ fire_event("saved pid")
+ exit!(0)
+ end
+ rescue
+ fire_event($!.message.lines.join(" // "))
+ raise
+ end
+ end
+ example.log_event("#{name}.start forked (pid #{pid})")
+ end
+
+ def readline_nonblock(fd)
+ buffer = ""
+ buffer << fd.read_nonblock(1) while buffer[-1] != "\n"
+
+ buffer
+ #rescue IO::EAGAINUnreadable
+ rescue IO::WaitReadable
+ unless buffer == ""
+ sleep 0.1
+ retry
+ end
+ nil
+ end
end
end
diff --git a/spec/functional/win32/registry_helper_spec.rb b/spec/functional/win32/registry_spec.rb
index 9ef6fd006f..dcfc49e2b3 100644
--- a/spec/functional/win32/registry_helper_spec.rb
+++ b/spec/functional/win32/registry_spec.rb
@@ -20,25 +20,6 @@
require 'spec_helper'
require 'chef/win32/registry'
-describe Chef::Resource::RegistryKey, :unix_only do
- before(:all) do
- events = Chef::EventDispatch::Dispatcher.new
- node = Chef::Node.new
- ohai = Ohai::System.new
- ohai.all_plugins
- node.consume_external_attrs(ohai.data,{})
- run_context = Chef::RunContext.new(node, {}, events)
- @resource = Chef::Resource::RegistryKey.new("HKCU\\Software", run_context)
- end
- context "when load_current_resource is run on a non-windows node" do
- it "throws an exception because you don't have a windows registry (derp)" do
- @resource.key("HKCU\\Software\\Opscode")
- @resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}])
- expect{@resource.run_action(:create)}.to raise_error(Chef::Exceptions::Win32NotWindows)
- end
- end
-end
-
describe 'Chef::Win32::Registry', :windows_only do
before(:all) do
@@ -556,11 +537,11 @@ describe 'Chef::Win32::Registry', :windows_only do
end
after(:all) do
- ::Win32::Registry::HKEY_LOCAL_MACHINE.open("Software\\Root", ::Win32::Registry::KEY_ALL_ACCESS | 0x0100) do |reg|
- reg.delete_key("Trunk", true)
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open("Software", ::Win32::Registry::KEY_ALL_ACCESS | 0x0100) do |reg|
+ reg.delete_key("Root", true)
end
- ::Win32::Registry::HKEY_LOCAL_MACHINE.open("Software\\Root", ::Win32::Registry::KEY_ALL_ACCESS | 0x0200) do |reg|
- reg.delete_key("Trunk", true)
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open("Software", ::Win32::Registry::KEY_ALL_ACCESS | 0x0200) do |reg|
+ reg.delete_key("Root", true)
end
end
diff --git a/spec/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb
index 8c72048965..314a9310be 100644
--- a/spec/integration/client/client_spec.rb
+++ b/spec/integration/client/client_spec.rb
@@ -301,6 +301,74 @@ EOM
result.error!
end
+ it "should complete with success when using --profile-ruby and output a profile file" do
+ file 'config/client.rb', <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+EOM
+ result = shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' -z --profile-ruby", :cwd => chef_dir)
+ expect(File.exist?(path_to("config/local-mode-cache/cache/graph_profile.out"))).to be true
+ end
+
+ it "doesn't produce a profile when --profile-ruby is not present" do
+ file 'config/client.rb', <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+EOM
+ result = shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default' -z", :cwd => chef_dir)
+ expect(File.exist?(path_to("config/local-mode-cache/cache/graph_profile.out"))).to be false
+ end
+ end
+
+ when_the_repository "has a cookbook that generates deprecation warnings" do
+ before do
+ file 'cookbooks/x/recipes/default.rb', <<-EOM
+ class ::MyResource < Chef::Resource
+ use_automatic_resource_name
+ property :x, default: []
+ property :y, default: {}
+ end
+
+ my_resource 'blah' do
+ 1.upto(10) do
+ x nil
+ end
+ x nil
+ end
+ EOM
+ end
+
+ def match_indices(regex, str)
+ result = []
+ pos = 0
+ while match = regex.match(str, pos)
+ result << match.begin(0)
+ pos = match.end(0) + 1
+ end
+ result
+ end
+
+ it "should output each deprecation warning only once, at the end of the run" do
+ file 'config/client.rb', <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+# Mimick what happens when you are on the console
+formatters << :doc
+log_level :warn
+EOM
+
+ ENV.delete('CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS')
+
+ result = shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ expect(result.error?).to be_falsey
+
+ # Search to the end of the client run in the output
+ run_complete = result.stdout.index("Running handlers complete")
+ expect(run_complete).to be >= 0
+
+ # Make sure there is exactly one result for each, and that it occurs *after* the complete message.
+ expect(match_indices(/nil currently does not overwrite the value of/, result.stdout)).to match([ be > run_complete ])
+ end
end
when_the_repository "has a cookbook with only an audit recipe" do
diff --git a/spec/integration/knife/download_spec.rb b/spec/integration/knife/download_spec.rb
index c87e6fe20a..b8a19061b7 100644
--- a/spec/integration/knife/download_spec.rb
+++ b/spec/integration/knife/download_spec.rb
@@ -1103,6 +1103,15 @@ EOM
before :each do
Chef::Config.chef_server_url = URI.join(Chef::Config.chef_server_url, '/organizations/foo')
end
+ when_the_repository 'has existing top level files' do
+ before do
+ file 'invitations.json', {}
+ end
+
+ it "can still download top level files" do
+ knife('download /invitations.json').should_succeed
+ end
+ end
when_the_repository 'is empty' do
it 'knife download / downloads everything' do
@@ -1113,11 +1122,13 @@ Created /acls/clients/foo-validator.json
Created /acls/containers
Created /acls/containers/clients.json
Created /acls/containers/containers.json
+Created /acls/containers/cookbook_artifacts.json
Created /acls/containers/cookbooks.json
Created /acls/containers/data.json
Created /acls/containers/environments.json
Created /acls/containers/groups.json
Created /acls/containers/nodes.json
+Created /acls/containers/policies.json
Created /acls/containers/roles.json
Created /acls/containers/sandboxes.json
Created /acls/containers/x.json
@@ -1139,11 +1150,13 @@ Created /clients/foo-validator.json
Created /containers
Created /containers/clients.json
Created /containers/containers.json
+Created /containers/cookbook_artifacts.json
Created /containers/cookbooks.json
Created /containers/data.json
Created /containers/environments.json
Created /containers/groups.json
Created /containers/nodes.json
+Created /containers/policies.json
Created /containers/roles.json
Created /containers/sandboxes.json
Created /containers/x.json
diff --git a/spec/integration/knife/list_spec.rb b/spec/integration/knife/list_spec.rb
index 911b56ef18..b289642c7d 100644
--- a/spec/integration/knife/list_spec.rb
+++ b/spec/integration/knife/list_spec.rb
@@ -702,11 +702,13 @@ foo-validator.json
/acls/containers:
clients.json
containers.json
+cookbook_artifacts.json
cookbooks.json
data.json
environments.json
groups.json
nodes.json
+policies.json
roles.json
sandboxes.json
@@ -733,11 +735,13 @@ foo-validator.json
/containers:
clients.json
containers.json
+cookbook_artifacts.json
cookbooks.json
data.json
environments.json
groups.json
nodes.json
+policies.json
roles.json
sandboxes.json
@@ -804,11 +808,13 @@ foo-validator.json
/acls/containers:
clients.json
containers.json
+cookbook_artifacts.json
cookbooks.json
data.json
environments.json
groups.json
nodes.json
+policies.json
roles.json
sandboxes.json
@@ -835,11 +841,13 @@ foo-validator.json
/containers:
clients.json
containers.json
+cookbook_artifacts.json
cookbooks.json
data.json
environments.json
groups.json
nodes.json
+policies.json
roles.json
sandboxes.json
diff --git a/spec/integration/knife/upload_spec.rb b/spec/integration/knife/upload_spec.rb
index 826ecec364..6ca8c3d8ce 100644
--- a/spec/integration/knife/upload_spec.rb
+++ b/spec/integration/knife/upload_spec.rb
@@ -198,7 +198,7 @@ Created /nodes/y.json
Created /roles/y.json
Created /users/y.json
EOM
- knife('diff --name-status /').should_succeed ''
+ knife('diff /').should_succeed ''
end
it 'knife upload --no-diff adds the new files' do
diff --git a/spec/integration/recipes/remote_directory.rb b/spec/integration/recipes/remote_directory.rb
new file mode 100644
index 0000000000..a1988ccd52
--- /dev/null
+++ b/spec/integration/recipes/remote_directory.rb
@@ -0,0 +1,74 @@
+require 'support/shared/integration/integration_helper'
+
+describe Chef::Resource::RemoteDirectory do
+ include IntegrationSupport
+ include Chef::Mixin::ShellOut
+
+ # Until Cheffish::RSpec has cookbook support, we have to run the whole client
+ let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") }
+
+ # Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
+ # following constraints are satisfied:
+ # * Windows: windows can only run batch scripts as bare executables. Rubygems
+ # creates batch wrappers for installed gems, but we don't have batch wrappers
+ # in the source tree.
+ # * Other `chef-client` in PATH: A common case is running the tests on a
+ # machine that has omnibus chef installed. In that case we need to ensure
+ # we're running `chef-client` from the source tree and not the external one.
+ # cf. CHEF-4914
+ let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" }
+
+ when_the_repository "has a cookbook with a source_dir with two subdirectories, each with one file and subdir in a different alphabetical order" do
+ before do
+ file 'config/client.rb', <<-EOM
+ local_mode true
+ cookbook_path "#{path_to('cookbooks')}"
+ EOM
+ directory "cookbooks/test" do
+ directory "files/default/source_dir" do
+ directory "sub1" do
+ file "aaa", ""
+ file "zzz/file", ""
+ end
+ directory "sub2" do
+ file "aaa/file", ""
+ file "zzz", ""
+ end
+ end
+ end
+ end
+
+ context "and a recipe is run with a remote_directory that syncs source_dir with different mode and file_mode" do
+ let!(:dest_dir) { path_to("dest_dir") }
+ before do
+ directory "cookbooks/test" do
+ file "recipes/default.rb", <<-EOM
+ remote_directory #{dest_dir.inspect} do
+ source "source_dir"
+ mode "0754"
+ files_mode 0777
+ end
+ EOM
+ end
+ shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'test::default'", :cwd => chef_dir)
+ end
+
+ def mode_of(path)
+ path = path_to(path)
+ stat = File.stat(path)
+ (stat.mode & 0777).to_s(8)
+ end
+
+ it "creates all directories and files with the correct permissions" do
+ expect(mode_of("dest_dir/sub1")).to eq "754"
+ expect(mode_of("dest_dir/sub1/aaa")).to eq "777"
+ expect(mode_of("dest_dir/sub1/zzz")).to eq "754"
+ expect(mode_of("dest_dir/sub1/zzz/file")).to eq "777"
+ expect(mode_of("dest_dir/sub2")).to eq "754"
+ expect(mode_of("dest_dir/sub2/aaa")).to eq "754"
+ expect(mode_of("dest_dir/sub2/aaa/file")).to eq "777"
+ expect(mode_of("dest_dir/sub2/zzz")).to eq "777"
+ end
+ end
+ end
+end
diff --git a/spec/integration/recipes/resource_action_spec.rb b/spec/integration/recipes/resource_action_spec.rb
index 53611c144f..6f3f5ab47e 100644
--- a/spec/integration/recipes/resource_action_spec.rb
+++ b/spec/integration/recipes/resource_action_spec.rb
@@ -4,7 +4,7 @@ describe "Resource.action" do
include IntegrationSupport
shared_context "ActionJackson" do
- it "The default action is the first declared action" do
+ it "the default action is the first declared action" do
converge <<-EOM, __FILE__, __LINE__+1
#{resource_dsl} 'hi' do
foo 'foo!'
@@ -14,7 +14,7 @@ describe "Resource.action" do
expect(ActionJackson.succeeded).to eq true
end
- it "The action can access recipe DSL" do
+ it "the action can access recipe DSL" do
converge <<-EOM, __FILE__, __LINE__+1
#{resource_dsl} 'hi' do
foo 'foo!'
@@ -25,7 +25,7 @@ describe "Resource.action" do
expect(ActionJackson.succeeded).to eq true
end
- it "The action can access attributes" do
+ it "the action can access attributes" do
converge <<-EOM, __FILE__, __LINE__+1
#{resource_dsl} 'hi' do
foo 'foo!'
@@ -36,7 +36,7 @@ describe "Resource.action" do
expect(ActionJackson.succeeded).to eq 'foo!'
end
- it "The action can access public methods" do
+ it "the action can access public methods" do
converge <<-EOM, __FILE__, __LINE__+1
#{resource_dsl} 'hi' do
foo 'foo!'
@@ -47,7 +47,7 @@ describe "Resource.action" do
expect(ActionJackson.succeeded).to eq 'foo_public!'
end
- it "The action can access protected methods" do
+ it "the action can access protected methods" do
converge <<-EOM, __FILE__, __LINE__+1
#{resource_dsl} 'hi' do
foo 'foo!'
@@ -58,7 +58,7 @@ describe "Resource.action" do
expect(ActionJackson.succeeded).to eq 'foo_protected!'
end
- it "The action cannot access private methods" do
+ it "the action cannot access private methods" do
expect {
converge(<<-EOM, __FILE__, __LINE__+1)
#{resource_dsl} 'hi' do
@@ -70,7 +70,7 @@ describe "Resource.action" do
expect(ActionJackson.ran_action).to eq :access_private_method
end
- it "The action cannot access resource instance variables" do
+ it "the action cannot access resource instance variables" do
converge <<-EOM, __FILE__, __LINE__+1
#{resource_dsl} 'hi' do
foo 'foo!'
@@ -81,7 +81,7 @@ describe "Resource.action" do
expect(ActionJackson.succeeded).to be_nil
end
- it "The action does not compile until the prior resource has converged" do
+ it "the action does not compile until the prior resource has converged" do
converge <<-EOM, __FILE__, __LINE__+1
ruby_block 'wow' do
block do
@@ -98,7 +98,7 @@ describe "Resource.action" do
expect(ActionJackson.succeeded).to eq 'ruby_block_converged!'
end
- it "The action's resources converge before the next resource converges" do
+ it "the action's resources converge before the next resource converges" do
converge <<-EOM, __FILE__, __LINE__+1
#{resource_dsl} 'hi' do
foo 'foo!'
@@ -202,6 +202,11 @@ describe "Resource.action" do
let(:resource_dsl) { :action_jackson }
end
+ it "Can retrieve ancestors of action class without crashing" do
+ converge { action_jackson 'hi' }
+ expect { ActionJackson.action_class.ancestors.join(",") }.not_to raise_error
+ end
+
context "And 'action_jackgrandson' inheriting from ActionJackson and changing nothing" do
before(:context) {
class ActionJackgrandson < ActionJackson
@@ -214,7 +219,7 @@ describe "Resource.action" do
end
end
- context "And 'action_jackalope' inheriting from ActionJackson with an extra attribute and action" do
+ context "And 'action_jackalope' inheriting from ActionJackson with an extra attribute, action and custom method" do
before(:context) {
class ActionJackalope < ActionJackson
use_automatic_resource_name
@@ -228,6 +233,7 @@ describe "Resource.action" do
@bar
end
class <<self
+ attr_accessor :load_current_resource_ran
attr_accessor :jackalope_ran
end
action :access_jackalope do
@@ -243,6 +249,7 @@ describe "Resource.action" do
}
before do
ActionJackalope.jackalope_ran = nil
+ ActionJackalope.load_current_resource_ran = nil
end
context "action_jackson still behaves the same" do
@@ -251,7 +258,7 @@ describe "Resource.action" do
end
end
- it "The default action remains the same even though new actions were specified first" do
+ it "the default action remains the same even though new actions were specified first" do
converge {
action_jackalope 'hi' do
foo 'foo!'
@@ -320,7 +327,7 @@ describe "Resource.action" do
end
end
}
- it "The default action is :nothing" do
+ it "the default action is :nothing" do
converge {
no_action_jackson 'hi' do
foo 'foo!'
diff --git a/spec/integration/recipes/resource_converge_if_changed_spec.rb b/spec/integration/recipes/resource_converge_if_changed_spec.rb
index d00252a717..82d38a8faf 100644
--- a/spec/integration/recipes/resource_converge_if_changed_spec.rb
+++ b/spec/integration/recipes/resource_converge_if_changed_spec.rb
@@ -1,6 +1,6 @@
require 'support/shared/integration/integration_helper'
-describe "Resource::ActionProvider#converge_if_changed" do
+describe "Resource::ActionClass#converge_if_changed" do
include IntegrationSupport
module Namer
diff --git a/spec/integration/recipes/resource_load_spec.rb b/spec/integration/recipes/resource_load_spec.rb
index c29b877b59..556201efd8 100644
--- a/spec/integration/recipes/resource_load_spec.rb
+++ b/spec/integration/recipes/resource_load_spec.rb
@@ -63,17 +63,17 @@ describe "Resource.load_current_value" do
end
it "current_resource is passed name but not x" do
- expect(resource.current_resource.x).to eq 'loaded 2 (name=blah)'
+ expect(resource.current_value.x).to eq 'loaded 2 (name=blah)'
end
- it "resource.current_resource returns a different resource" do
- expect(resource.current_resource.x).to eq 'loaded 2 (name=blah)'
+ it "resource.current_value returns a different resource" do
+ expect(resource.current_value.x).to eq 'loaded 2 (name=blah)'
expect(resource.x).to eq 'desired'
end
- it "resource.current_resource constructs the resource anew each time" do
- expect(resource.current_resource.x).to eq 'loaded 2 (name=blah)'
- expect(resource.current_resource.x).to eq 'loaded 3 (name=blah)'
+ it "resource.current_value constructs the resource anew each time" do
+ expect(resource.current_value.x).to eq 'loaded 2 (name=blah)'
+ expect(resource.current_value.x).to eq 'loaded 3 (name=blah)'
end
it "the provider accesses the current value of x" do
@@ -94,7 +94,7 @@ describe "Resource.load_current_value" do
}
it "i, name and d are passed to load_current_value, but not x" do
- expect(resource.current_resource.x).to eq 'loaded 2 (d=desired_d, i=desired_i, name=blah)'
+ expect(resource.current_value.x).to eq 'loaded 2 (d=desired_d, i=desired_i, name=blah)'
end
end
@@ -112,7 +112,7 @@ describe "Resource.load_current_value" do
}
it "i, name and d are passed to load_current_value, but not x" do
- expect(resource.current_resource.x).to eq 'loaded 2 (d=desired_d, i=desired_i, name=blah)'
+ expect(resource.current_value.x).to eq 'loaded 2 (d=desired_d, i=desired_i, name=blah)'
end
end
end
@@ -160,10 +160,10 @@ describe "Resource.load_current_value" do
context "and a child resource class with no load_current_value" do
it "the parent load_current_value is used" do
- expect(subresource.current_resource.x).to eq 'loaded 2 (name=blah)'
+ expect(subresource.current_value.x).to eq 'loaded 2 (name=blah)'
end
it "load_current_value yields a copy of the child class" do
- expect(subresource.current_resource).to be_kind_of(subresource_class)
+ expect(subresource.current_value).to be_kind_of(subresource_class)
end
end
@@ -178,7 +178,7 @@ describe "Resource.load_current_value" do
}
it "the overridden load_current_value is used" do
- current_resource = subresource.current_resource
+ current_resource = subresource.current_value
expect(current_resource.x).to eq 'default 3'
expect(current_resource.y).to eq 'loaded_y 2 (name=blah)'
end
@@ -196,7 +196,7 @@ describe "Resource.load_current_value" do
}
it "the original load_current_value is called as well as the child one" do
- current_resource = subresource.current_resource
+ current_resource = subresource.current_value
expect(current_resource.x).to eq 'loaded 3 (name=blah)'
expect(current_resource.y).to eq 'loaded_y 4 (name=blah, x=loaded 3 (name=blah))'
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index d0d02f1a2a..4f7fde8eaf 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -73,6 +73,7 @@ require 'spec/support/local_gems.rb' if File.exists?(File.join(File.dirname(__FI
# Explicitly require spec helpers that need to load first
require 'spec/support/platform_helpers'
+require 'spec/support/shared/unit/mock_shellout'
# Autoloads support files
# Excludes support/platforms by default
@@ -98,6 +99,7 @@ TEST_PLATFORM_VERSION = TEST_NODE['platform_version']
RSpec.configure do |config|
config.include(Matchers)
+ config.include(MockShellout::RSpec)
config.filter_run :focus => true
config.filter_run_excluding :external => true
@@ -126,10 +128,12 @@ RSpec.configure do |config|
config.filter_run_excluding :mac_osx_only=> true if !mac_osx?
config.filter_run_excluding :not_supported_on_win2k3 => true if windows_win2k3?
config.filter_run_excluding :not_supported_on_solaris => true if solaris?
+ config.filter_run_excluding :not_supported_on_nano => true if windows_nano_server?
config.filter_run_excluding :win2k3_only => true unless windows_win2k3?
config.filter_run_excluding :windows_2008r2_or_later => true unless windows_2008r2_or_later?
config.filter_run_excluding :windows64_only => true unless windows64?
config.filter_run_excluding :windows32_only => true unless windows32?
+ config.filter_run_excluding :windows_nano_only => true unless windows_nano_server?
config.filter_run_excluding :ruby64_only => true unless ruby_64bit?
config.filter_run_excluding :ruby32_only => true unless ruby_32bit?
config.filter_run_excluding :windows_powershell_dsc_only => true unless windows_powershell_dsc?
@@ -185,6 +189,8 @@ RSpec.configure do |config|
config.run_all_when_everything_filtered = true
config.before(:each) do
+ Chef.reset!
+
Chef::Config.reset
# By default, treat deprecation warnings as errors in tests.
diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb
index 1cfad05172..9c6c3fdf72 100644
--- a/spec/support/platform_helpers.rb
+++ b/spec/support/platform_helpers.rb
@@ -83,6 +83,11 @@ def windows_powershell_dsc?
supports_dsc
end
+def windows_nano_server?
+ require 'chef/platform/query_helpers'
+ Chef::Platform.windows_nano_server?
+end
+
def mac_osx_106?
if File.exists? "/usr/bin/sw_vers"
result = ShellHelpers.shell_out("/usr/bin/sw_vers")
diff --git a/lib/chef/mixin/wstring.rb b/spec/support/shared/context/win32.rb
index bb6fdf4884..3dbe876114 100644
--- a/lib/chef/mixin/wstring.rb
+++ b/spec/support/shared/context/win32.rb
@@ -1,6 +1,5 @@
#
-# Author:: Jay Mundrawala(<jdm@chef.io>)
-# Copyright:: Copyright 2015 Chef Software
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,16 +15,20 @@
# limitations under the License.
#
-class Chef
- module Mixin
- module WideString
- def wstring(str)
- if str.nil? || str.encoding == Encoding::UTF_16LE
- str
- else
- str.to_wstring
- end
- end
+RSpec.shared_context "Win32" do
+ before(:all) do
+ @original_win32 = if defined?(Win32)
+ win32 = Object.send(:const_get, 'Win32')
+ Object.send(:remove_const, 'Win32')
+ win32
+ else
+ nil
end
+ Win32 = Module.new
+ end
+
+ after(:all) do
+ Object.send(:remove_const, 'Win32') if defined?(Win32)
+ Object.send(:const_set, 'Win32', @original_win32) if @original_win32
end
end
diff --git a/spec/support/shared/functional/windows_script.rb b/spec/support/shared/functional/windows_script.rb
index 3499cc98ec..d84c06c86b 100644
--- a/spec/support/shared/functional/windows_script.rb
+++ b/spec/support/shared/functional/windows_script.rb
@@ -19,14 +19,15 @@
# Shared context used by both Powershell and Batch script provider
# tests.
+require 'chef/platform/query_helpers'
+
shared_context Chef::Resource::WindowsScript do
before(:all) do
-
- ohai_reader = Ohai::System.new
- ohai_reader.all_plugins("platform")
+ @ohai_reader = Ohai::System.new
+ @ohai_reader.all_plugins(["platform", "kernel"])
new_node = Chef::Node.new
- new_node.consume_external_attrs(ohai_reader.data,{})
+ new_node.consume_external_attrs(@ohai_reader.data,{})
events = Chef::EventDispatch::Dispatcher.new
@@ -51,12 +52,11 @@ shared_context Chef::Resource::WindowsScript do
shared_examples_for "a script resource with architecture attribute" do
context "with the given architecture attribute value" do
- let(:resource_architecture) { architecture }
let(:expected_architecture) do
- if architecture
- expected_architecture = architecture
+ if resource_architecture
+ expected_architecture = resource_architecture
else
- expected_architecture = :i386
+ expected_architecture = @ohai_reader.data['kernel']['machine'].to_sym
end
end
let(:expected_architecture_output) do
@@ -77,16 +77,16 @@ shared_context Chef::Resource::WindowsScript do
before(:each) do
resource.code resource_command
- (resource.architecture architecture) if architecture
+ (resource.architecture resource_architecture) if resource_architecture
resource.returns(0)
end
- it "should create a process with the expected architecture" do
+ it "creates a process with the expected architecture" do
resource.run_action(:run)
expect(get_process_architecture).to eq(expected_architecture_output.downcase)
end
- it "should execute guards with the same architecture as the resource" do
+ it "executes guards with the same architecture as the resource" do
resource.only_if resource_guard_command
resource.run_action(:run)
expect(get_process_architecture).to eq(expected_architecture_output.downcase)
@@ -94,18 +94,32 @@ shared_context Chef::Resource::WindowsScript do
expect(get_guard_process_architecture).to eq(get_process_architecture)
end
- let (:architecture) { :x86_64 }
- it "should execute a 64-bit guard if the guard's architecture is specified as 64-bit", :windows64_only do
- resource.only_if resource_guard_command, :architecture => :x86_64
- resource.run_action(:run)
- expect(get_guard_process_architecture).to eq('amd64')
+ context "when the guard's architecture is specified as 64-bit" do
+ let (:guard_architecture) { :x86_64 }
+ it "executes a 64-bit guard", :windows64_only do
+ resource.only_if resource_guard_command, :architecture => guard_architecture
+ resource.run_action(:run)
+ expect(get_guard_process_architecture).to eq('amd64')
+ end
end
- let (:architecture) { :i386 }
- it "should execute a 32-bit guard if the guard's architecture is specified as 32-bit" do
- resource.only_if resource_guard_command, :architecture => :i386
- resource.run_action(:run)
- expect(get_guard_process_architecture).to eq('x86')
+ context "when the guard's architecture is specified as 32-bit", :not_supported_on_nano do
+ let (:guard_architecture) { :i386 }
+ it "executes a 32-bit guard" do
+ resource.only_if resource_guard_command, :architecture => guard_architecture
+ resource.run_action(:run)
+ expect(get_guard_process_architecture).to eq('x86')
+ end
+ end
+
+ context "when the guard's architecture is specified as 32-bit", :windows_nano_only do
+ let (:guard_architecture) { :i386 }
+ it "raises an error" do
+ resource.only_if resource_guard_command, :architecture => guard_architecture
+ expect{ resource.run_action(:run) }.to raise_error(
+ Chef::Exceptions::Win32ArchitectureIncorrect,
+ /cannot execute script with requested architecture 'i386' on Windows Nano Server/)
+ end
end
end
end
@@ -114,7 +128,28 @@ shared_context Chef::Resource::WindowsScript do
describe "when the run action is invoked on Windows" do
it "executes the script code" do
- resource.code("whoami > #{script_output_path}")
+ resource.code("whoami > \"#{script_output_path}\"")
+ resource.returns(0)
+ resource.run_action(:run)
+ end
+ end
+
+ context "when $env:TMP has a space" do
+ before(:each) do
+ @dir = Dir.mktmpdir("Jerry Smith")
+ @original_env = ENV.to_hash.dup
+ ENV.delete('TMP')
+ ENV['TMP'] = @dir
+ end
+
+ after(:each) do
+ FileUtils.remove_entry_secure(@dir)
+ ENV.clear
+ ENV.update(@original_env)
+ end
+
+ it "executes the script code" do
+ resource.code("whoami > \"#{script_output_path}\"")
resource.returns(0)
resource.run_action(:run)
end
@@ -122,6 +157,8 @@ shared_context Chef::Resource::WindowsScript do
context "when evaluating guards" do
it "has a guard_interpreter attribute set to the short name of the resource" do
+ pending "powershell.exe always exits with 0 on nano" if Chef::Platform.windows_nano_server?
+
expect(resource.guard_interpreter).to eq(resource.resource_name)
resource.not_if "findstr.exe /thiscommandhasnonzeroexitstatus"
expect(Chef::Resource).to receive(:resource_for_node).and_call_original
@@ -131,17 +168,17 @@ shared_context Chef::Resource::WindowsScript do
end
context "when the architecture attribute is not set" do
- let(:architecture) { nil }
+ let(:resource_architecture) { nil }
it_behaves_like "a script resource with architecture attribute"
end
- context "when the architecture attribute is :i386" do
- let(:architecture) { :i386 }
+ context "when the architecture attribute is :i386", :not_supported_on_nano do
+ let(:resource_architecture) { :i386 }
it_behaves_like "a script resource with architecture attribute"
end
context "when the architecture attribute is :x86_64" do
- let(:architecture) { :x86_64 }
+ let(:resource_architecture) { :x86_64 }
it_behaves_like "a script resource with architecture attribute"
end
end
diff --git a/spec/support/shared/unit/mock_shellout.rb b/spec/support/shared/unit/mock_shellout.rb
new file mode 100644
index 0000000000..7c3e49ec82
--- /dev/null
+++ b/spec/support/shared/unit/mock_shellout.rb
@@ -0,0 +1,46 @@
+#
+# Author:: John Keiser <jkeiser@chef.io>
+# Copyright:: Copyright (c) 2015 John Keiser.
+# 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.
+#
+
+#
+# Mocks shellout results. Examples:
+# mock_shellout_command("systemctl --all", exitstatus: 1)
+#
+class MockShellout
+ module RSpec
+ def mock_shellout_command(command, **result)
+ allow(::Mixlib::ShellOut).to receive(:new).with(command, anything).and_return MockShellout.new(result)
+ end
+ end
+
+ def initialize(**properties)
+ @properties = {
+ stdout: "",
+ stderr: "",
+ exitstatus: 0
+ }.merge(properties)
+ end
+ def method_missing(name, *args)
+ @properties[name.to_sym]
+ end
+ def error?
+ exitstatus != 0
+ end
+ def error!
+ raise Mixlib::ShellOut::ShellCommandFailed, "Expected process to exit with 0, but received #{exitstatus}" if error?
+ end
+end
diff --git a/spec/support/shared/unit/provider/file.rb b/spec/support/shared/unit/provider/file.rb
index 7de9698451..ff9e271a0a 100644
--- a/spec/support/shared/unit/provider/file.rb
+++ b/spec/support/shared/unit/provider/file.rb
@@ -465,11 +465,13 @@ shared_examples_for Chef::Provider::File do
t
}
- let(:verification) { double("Verification") }
+ let(:verification) { instance_double(Chef::Resource::File::Verification) }
+ let(:verification_fail) { instance_double(Chef::Resource::File::Verification) }
context "with user-supplied verifications" do
it "calls #verify on each verification with tempfile path" do
- allow(Chef::Resource::File::Verification).to receive(:new).and_return(verification)
+ allow(Chef::Resource::File::Verification).to(
+ receive(:new).with(anything(), "true", anything()).and_return(verification))
provider.new_resource.verify "true"
provider.new_resource.verify "true"
expect(verification).to receive(:verify).with(tempfile.path).twice.and_return(true)
@@ -477,10 +479,14 @@ shared_examples_for Chef::Provider::File do
end
it "raises an exception if any verification fails" do
+ allow(Chef::Resource::File::Verification).to(
+ receive(:new).with(anything(), "true", anything()).and_return(verification))
+ allow(Chef::Resource::File::Verification).to(
+ receive(:new).with(anything(), "false", anything()).and_return(verification_fail))
provider.new_resource.verify "true"
provider.new_resource.verify "false"
- allow(verification).to receive(:verify).with("true").and_return(true)
- allow(verification).to receive(:verify).with("false").and_return(false)
+ expect(verification).to receive(:verify).with(tempfile.path).and_return(true)
+ expect(verification_fail).to receive(:verify).with(tempfile.path).and_return(false)
expect{provider.send(:do_validate_content)}.to raise_error(Chef::Exceptions::ValidationFailed)
end
end
diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb
index 64a6bcc9d2..727536f1f8 100644
--- a/spec/unit/application/client_spec.rb
+++ b/spec/unit/application/client_spec.rb
@@ -47,6 +47,19 @@ describe Chef::Application::Client, "reconfigure" do
expect(app).to receive(:set_specific_recipes).and_return(true)
app.reconfigure
end
+
+ context "when given a named_run_list" do
+
+ before do
+ ARGV.replace( %w[ --named-run-list arglebargle-example ] )
+ app.reconfigure
+ end
+
+ it "sets named_run_list in Chef::Config" do
+ expect(Chef::Config[:named_run_list]).to eq("arglebargle-example")
+ end
+
+ end
end
describe "when configured to not fork the client process" do
@@ -237,7 +250,7 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
end
it "should throw an exception" do
- expect { @app.reconfigure }.to raise_error
+ expect { app.reconfigure }.to raise_error(Chef::Exceptions::PIDFileLockfileMatch)
end
end
end
@@ -275,9 +288,9 @@ describe Chef::Application::Client, "configure_chef" do
ARGV.replace(@original_argv)
end
- it "should set the colored output to false by default on windows and true otherwise" do
+ it "should set the colored output to true by default on windows and true on all other platforms as well" do
if windows?
- expect(Chef::Config[:color]).to be_falsey
+ expect(Chef::Config[:color]).to be_truthy
else
expect(Chef::Config[:color]).to be_truthy
end
diff --git a/spec/unit/application/knife_spec.rb b/spec/unit/application/knife_spec.rb
index 3c215eac7f..8894e86240 100644
--- a/spec/unit/application/knife_spec.rb
+++ b/spec/unit/application/knife_spec.rb
@@ -70,13 +70,13 @@ describe Chef::Application::Knife do
end
end
- it "should set the colored output to false by default on windows and true otherwise" do
+ it "should set the colored output to true by default on windows and true on all other platforms as well" do
with_argv(*%w{noop knife command}) do
expect(@knife).to receive(:exit).with(0)
@knife.run
end
if windows?
- expect(Chef::Config[:color]).to be_falsey
+ expect(Chef::Config[:color]).to be_truthy
else
expect(Chef::Config[:color]).to be_truthy
end
diff --git a/spec/unit/chef_class_spec.rb b/spec/unit/chef_class_spec.rb
index d6bf74572e..f1b877520c 100644
--- a/spec/unit/chef_class_spec.rb
+++ b/spec/unit/chef_class_spec.rb
@@ -46,10 +46,6 @@ describe "Chef class" do
Chef.set_provider_priority_map(provider_priority_map)
end
- after do
- Chef.reset!
- end
-
context "priority maps" do
context "#get_provider_priority_array" do
it "should use the current node to get the right priority_map" do
diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb
index 8146774764..8fbf56844e 100644
--- a/spec/unit/client_spec.rb
+++ b/spec/unit/client_spec.rb
@@ -364,6 +364,8 @@ describe Chef::Client do
expect(node[:expanded_run_list]).to be_nil
allow(client.policy_builder).to receive(:node).and_return(node)
+ client.policy_builder.select_implementation(node)
+ allow(client.policy_builder.implementation).to receive(:node).and_return(node)
# chefspec and possibly others use the return value of this method
expect(client.build_node).to eq(node)
@@ -373,7 +375,8 @@ describe Chef::Client do
expect(node[:roles].length).to eq(1)
expect(node[:roles]).to include("role_containing_cookbook1")
expect(node[:recipes]).not_to be_nil
- expect(node[:recipes].length).to eq(1)
+ expect(node[:recipes].length).to eq(2)
+ expect(node[:recipes]).to include("cookbook1")
expect(node[:recipes]).to include("cookbook1::default")
expect(node[:expanded_run_list]).not_to be_nil
expect(node[:expanded_run_list].length).to eq(1)
@@ -391,6 +394,8 @@ describe Chef::Client do
expect(mock_chef_rest).to receive(:get_rest).with("environments/A").and_return(test_env)
expect(Chef::REST).to receive(:new).and_return(mock_chef_rest)
allow(client.policy_builder).to receive(:node).and_return(node)
+ client.policy_builder.select_implementation(node)
+ allow(client.policy_builder.implementation).to receive(:node).and_return(node)
expect(client.build_node).to eq(node)
expect(node.chef_environment).to eq("A")
diff --git a/spec/unit/cookbook/syntax_check_spec.rb b/spec/unit/cookbook/syntax_check_spec.rb
index 764829c387..efdb5b7926 100644
--- a/spec/unit/cookbook/syntax_check_spec.rb
+++ b/spec/unit/cookbook/syntax_check_spec.rb
@@ -55,6 +55,8 @@ describe Chef::Cookbook::SyntaxCheck do
helper_test.erb
helpers.erb
openldap_stuff.conf.erb
+ nested_openldap_partials.erb
+ nested_partial.erb
openldap_variable_stuff.conf.erb
test.erb
some_windows_line_endings.erb
diff --git a/spec/unit/deprecation_spec.rb b/spec/unit/deprecation_spec.rb
index 2e1f3c39f3..674de5ec1d 100644
--- a/spec/unit/deprecation_spec.rb
+++ b/spec/unit/deprecation_spec.rb
@@ -65,19 +65,16 @@ describe Chef::Deprecation do
end
context 'deprecation warning messages' do
- before(:each) do
- @warning_output = [ ]
- allow(Chef::Log).to receive(:warn) { |msg| @warning_output << msg }
- end
+ RSpec::Matchers.define_negated_matcher :a_non_empty_array, :be_empty
it 'should be enabled for deprecated methods' do
+ expect(Chef::Log).to receive(:warn).with(a_non_empty_array)
TestClass.new.deprecated_method(10)
- expect(@warning_output).not_to be_empty
end
it 'should contain stack trace' do
+ expect(Chef::Log).to receive(:warn).with(a_string_including(".rb"))
TestClass.new.deprecated_method(10)
- expect(@warning_output.join("").include?(".rb")).to be_truthy
end
end
diff --git a/spec/unit/dsl/reboot_pending_spec.rb b/spec/unit/dsl/reboot_pending_spec.rb
index a55f91d5e6..6705820e17 100644
--- a/spec/unit/dsl/reboot_pending_spec.rb
+++ b/spec/unit/dsl/reboot_pending_spec.rb
@@ -50,11 +50,17 @@ describe Chef::DSL::RebootPending do
expect(recipe.reboot_pending?).to be_truthy
end
- it 'should return true if value "HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile" contains specific data' do
- allow(recipe).to receive(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').and_return(true)
- allow(recipe).to receive(:registry_get_values).with('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').and_return(
- [{:name => "Flags", :type => :dword, :data => 3}])
- expect(recipe.reboot_pending?).to be_truthy
+ context "version is server 2003" do
+ before do
+ allow(Chef::Platform).to receive(:windows_server_2003?).and_return(true)
+ end
+
+ it 'should return true if value "HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile" contains specific data on 2k3' do
+ allow(recipe).to receive(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').and_return(true)
+ allow(recipe).to receive(:registry_get_values).with('HKLM\SOFTWARE\Microsoft\Updates\UpdateExeVolatile').and_return(
+ [{:name => "Flags", :type => :dword, :data => 3}])
+ expect(recipe.reboot_pending?).to be_truthy
+ end
end
end
diff --git a/spec/unit/event_dispatch/dispatcher_spec.rb b/spec/unit/event_dispatch/dispatcher_spec.rb
index 1014feea89..5a06e1d6d1 100644
--- a/spec/unit/event_dispatch/dispatcher_spec.rb
+++ b/spec/unit/event_dispatch/dispatcher_spec.rb
@@ -73,8 +73,51 @@ describe Chef::EventDispatch::Dispatcher do
expect(event_sink.synchronized_cookbook_args).to eq ["apache2"]
end
end
-
end
-end
+ context "when two event sinks have different arguments for an event" do
+ let(:event_sink_1) do
+ Class.new(Chef::EventDispatch::Base) do
+ attr_reader :synchronized_cookbook_args
+ def synchronized_cookbook(cookbook_name)
+ @synchronized_cookbook_args = [cookbook_name]
+ end
+ end.new
+ end
+ let(:event_sink_2) do
+ Class.new(Chef::EventDispatch::Base) do
+ attr_reader :synchronized_cookbook_args
+ def synchronized_cookbook(cookbook_name, cookbook)
+ @synchronized_cookbook_args = [cookbook_name, cookbook]
+ end
+ end.new
+ end
+
+ context "and the one with fewer arguments comes first" do
+ before do
+ dispatcher.register(event_sink_1)
+ dispatcher.register(event_sink_2)
+ end
+ it "trims the arugment list" do
+ cookbook_version = double("cookbook_version")
+ dispatcher.synchronized_cookbook("apache2", cookbook_version)
+ expect(event_sink_1.synchronized_cookbook_args).to eq ["apache2"]
+ expect(event_sink_2.synchronized_cookbook_args).to eq ["apache2", cookbook_version]
+ end
+ end
+
+ context "and the one with fewer arguments comes last" do
+ before do
+ dispatcher.register(event_sink_2)
+ dispatcher.register(event_sink_1)
+ end
+ it "trims the arugment list" do
+ cookbook_version = double("cookbook_version")
+ dispatcher.synchronized_cookbook("apache2", cookbook_version)
+ expect(event_sink_1.synchronized_cookbook_args).to eq ["apache2"]
+ expect(event_sink_2.synchronized_cookbook_args).to eq ["apache2", cookbook_version]
+ end
+ end
+ end
+end
diff --git a/spec/unit/event_dispatch/dsl_spec.rb b/spec/unit/event_dispatch/dsl_spec.rb
index f467ea81ea..0f7adce7a8 100644
--- a/spec/unit/event_dispatch/dsl_spec.rb
+++ b/spec/unit/event_dispatch/dsl_spec.rb
@@ -32,10 +32,6 @@ describe Chef::EventDispatch::DSL do
Chef.set_run_context(run_context)
end
- after do
- Chef.reset!
- end
-
subject{ described_class.new('test') }
it 'set handler name' do
diff --git a/spec/unit/formatters/doc_spec.rb b/spec/unit/formatters/doc_spec.rb
index eb98f5abd3..7266afc320 100644
--- a/spec/unit/formatters/doc_spec.rb
+++ b/spec/unit/formatters/doc_spec.rb
@@ -49,4 +49,30 @@ describe Chef::Formatters::Base do
expect(out.string).to include("- apache2 (1.2.3")
end
+ it "prints only seconds when elapsed time is less than 60 seconds" do
+ @now = Time.now
+ allow(Time).to receive(:now).and_return(@now, @now + 10.0)
+ formatter.run_completed(nil)
+ expect(formatter.elapsed_time).to eql(10.0)
+ expect(formatter.pretty_elapsed_time).to include("10 seconds")
+ expect(formatter.pretty_elapsed_time).not_to include("minutes")
+ expect(formatter.pretty_elapsed_time).not_to include("hours")
+ end
+
+ it "prints minutes and seconds when elapsed time is more than 60 seconds" do
+ @now = Time.now
+ allow(Time).to receive(:now).and_return(@now, @now + 610.0)
+ formatter.run_completed(nil)
+ expect(formatter.elapsed_time).to eql(610.0)
+ expect(formatter.pretty_elapsed_time).to include("10 minutes 10 seconds")
+ expect(formatter.pretty_elapsed_time).not_to include("hours")
+ end
+
+ it "prints hours, minutes and seconds when elapsed time is more than 3600 seconds" do
+ @now = Time.now
+ allow(Time).to receive(:now).and_return(@now, @now + 36610.0)
+ formatter.run_completed(nil)
+ expect(formatter.elapsed_time).to eql(36610.0)
+ expect(formatter.pretty_elapsed_time).to include("10 hours 10 minutes 10 seconds")
+ end
end
diff --git a/spec/unit/http_spec.rb b/spec/unit/http_spec.rb
index 4d851df951..a654d14aa2 100644
--- a/spec/unit/http_spec.rb
+++ b/spec/unit/http_spec.rb
@@ -89,4 +89,4 @@ describe Chef::HTTP do
end # head
-end
+end \ No newline at end of file
diff --git a/spec/unit/knife/bootstrap/client_builder_spec.rb b/spec/unit/knife/bootstrap/client_builder_spec.rb
index e6aa307c7e..e7232fe8d6 100644
--- a/spec/unit/knife/bootstrap/client_builder_spec.rb
+++ b/spec/unit/knife/bootstrap/client_builder_spec.rb
@@ -149,6 +149,22 @@ describe Chef::Knife::Bootstrap::ClientBuilder do
client_builder.run
end
+ it "does not add tags by default" do
+ allow(node).to receive(:run_list).with([])
+ expect(node).to_not receive(:tags)
+ client_builder.run
+ end
+
+ it "adds tags to the node when given" do
+ tag_receiver = []
+
+ knife_config[:tags] = %w[foo bar]
+ allow(node).to receive(:run_list).with([])
+ allow(node).to receive(:tags).and_return(tag_receiver)
+ client_builder.run
+ expect(tag_receiver).to eq %w[foo bar]
+ end
+
it "builds a node when the run_list is a string" do
knife_config[:run_list] = "role[base],role[app]"
expect(node).to receive(:run_list).with(["role[base]", "role[app]"])
@@ -174,5 +190,16 @@ describe Chef::Knife::Bootstrap::ClientBuilder do
expect(node).to receive(:run_list).with([])
client_builder.run
end
+
+ it "builds a node with policy_name and policy_group when given" do
+ knife_config[:policy_name] = "my-app"
+ knife_config[:policy_group] = "staging"
+
+ expect(node).to receive(:run_list).with([])
+ expect(node).to receive(:policy_name=).with("my-app")
+ expect(node).to receive(:policy_group=).with("staging")
+
+ client_builder.run
+ end
end
end
diff --git a/spec/unit/knife/bootstrap_spec.rb b/spec/unit/knife/bootstrap_spec.rb
index 0195e6d406..48aae3e61b 100644
--- a/spec/unit/knife/bootstrap_spec.rb
+++ b/spec/unit/knife/bootstrap_spec.rb
@@ -250,14 +250,14 @@ describe Chef::Knife::Bootstrap do
it "should create a hint file when told to" do
knife.parse_options(["--hint", "openstack"])
knife.merge_configs
- expect(knife.render_template).to match /\/etc\/chef\/ohai\/hints\/openstack.json/
+ expect(knife.render_template).to match(/\/etc\/chef\/ohai\/hints\/openstack.json/)
end
it "should populate a hint file with JSON when given a file to read" do
allow(::File).to receive(:read).and_return('{ "foo" : "bar" }')
knife.parse_options(["--hint", "openstack=hints/openstack.json"])
knife.merge_configs
- expect(knife.render_template).to match /\{\"foo\":\"bar\"\}/
+ expect(knife.render_template).to match(/\{\"foo\":\"bar\"\}/)
end
end
@@ -395,6 +395,58 @@ describe Chef::Knife::Bootstrap do
end
end
+ describe "handling policyfile options" do
+
+ context "when only policy_name is given" do
+
+ let(:bootstrap_cli_options) { %w[ --policy-name my-app-server ] }
+
+ it "returns an error stating that policy_name and policy_group must be given together" do
+ expect { knife.validate_options! }.to raise_error(SystemExit)
+ expect(stderr.string).to include("ERROR: --policy-name and --policy-group must be specified together")
+ end
+
+ end
+
+ context "when only policy_group is given" do
+
+ let(:bootstrap_cli_options) { %w[ --policy-group staging ] }
+
+ it "returns an error stating that policy_name and policy_group must be given together" do
+ expect { knife.validate_options! }.to raise_error(SystemExit)
+ expect(stderr.string).to include("ERROR: --policy-name and --policy-group must be specified together")
+ end
+
+ end
+
+ context "when both policy_name and policy_group are given, but run list is also given" do
+
+ let(:bootstrap_cli_options) { %w[ --policy-name my-app --policy-group staging --run-list cookbook ] }
+
+ it "returns an error stating that policyfile and run_list are exclusive" do
+ expect { knife.validate_options! }.to raise_error(SystemExit)
+ expect(stderr.string).to include("ERROR: Policyfile options and --run-list are exclusive")
+ end
+
+ end
+
+ context "when policy_name and policy_group are given with no conflicting options" do
+
+ let(:bootstrap_cli_options) { %w[ --policy-name my-app --policy-group staging ] }
+
+ it "passes options validation" do
+ expect { knife.validate_options! }.to_not raise_error
+ end
+
+ it "passes them into the bootstrap context" do
+ expect(knife.bootstrap_context.first_boot).to have_key(:policy_name)
+ expect(knife.bootstrap_context.first_boot).to have_key(:policy_group)
+ end
+
+ end
+
+ end
+
describe "when configuring the underlying knife ssh command" do
context "from the command line" do
let(:knife_ssh) do
@@ -525,7 +577,7 @@ describe Chef::Knife::Bootstrap do
it "verifies that a server to bootstrap was given as a command line arg" do
knife.name_args = nil
expect { knife.run }.to raise_error(SystemExit)
- expect(stderr.string).to match /ERROR:.+FQDN or ip/
+ expect(stderr.string).to match(/ERROR:.+FQDN or ip/)
end
describe "when running the bootstrap" do
diff --git a/spec/unit/knife/cookbook_site_share_spec.rb b/spec/unit/knife/cookbook_site_share_spec.rb
index 76e4ec730e..a7caca9744 100644
--- a/spec/unit/knife/cookbook_site_share_spec.rb
+++ b/spec/unit/knife/cookbook_site_share_spec.rb
@@ -78,21 +78,21 @@ describe Chef::Knife::CookbookSiteShare do
it 'should not fail when given only 1 argument and can determine category' do
@knife.name_args = ['cookbook_name']
- expect(@noauth_rest).to receive(:get_rest).with("http://cookbooks.opscode.com/api/v1/cookbooks/cookbook_name").and_return(@category_response)
+ expect(@noauth_rest).to receive(:get_rest).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name").and_return(@category_response)
expect(@knife).to receive(:do_upload)
@knife.run
end
it 'should print error and exit when given only 1 argument and cannot determine category' do
@knife.name_args = ['cookbook_name']
- expect(@noauth_rest).to receive(:get_rest).with("http://cookbooks.opscode.com/api/v1/cookbooks/cookbook_name").and_return(@bad_category_response)
+ expect(@noauth_rest).to receive(:get_rest).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)
end
it 'should print error and exit when given only 1 argument and Chef::REST throws an exception' do
@knife.name_args = ['cookbook_name']
- expect(@noauth_rest).to receive(:get_rest).with("http://cookbooks.opscode.com/api/v1/cookbooks/cookbook_name") { raise Errno::ECONNREFUSED, "Connection refused" }
+ expect(@noauth_rest).to receive(:get_rest).with("https://supermarket.chef.io/api/v1/cookbooks/cookbook_name") { raise Errno::ECONNREFUSED, "Connection refused" }
expect(@knife.ui).to receive(:fatal)
expect { @knife.run }.to raise_error(SystemExit)
end
diff --git a/spec/unit/knife/core/bootstrap_context_spec.rb b/spec/unit/knife/core/bootstrap_context_spec.rb
index 3718cb228c..0433ef9983 100644
--- a/spec/unit/knife/core/bootstrap_context_spec.rb
+++ b/spec/unit/knife/core/bootstrap_context_spec.rb
@@ -38,14 +38,14 @@ describe Chef::Knife::Core::BootstrapContext do
expect{described_class.new(config, run_list, chef_config)}.not_to raise_error
end
- it "runs chef with the first-boot.json in the _default environment" do
- expect(bootstrap_context.start_chef).to eq "chef-client -j /etc/chef/first-boot.json -E _default"
+ it "runs chef with the first-boot.json with no environment specified" do
+ expect(bootstrap_context.start_chef).to eq "chef-client -j /etc/chef/first-boot.json"
end
describe "when in verbosity mode" do
let(:config) { {:verbosity => 2} }
it "adds '-l debug' when verbosity is >= 2" do
- expect(bootstrap_context.start_chef).to eq "chef-client -j /etc/chef/first-boot.json -l debug -E _default"
+ expect(bootstrap_context.start_chef).to eq "chef-client -j /etc/chef/first-boot.json -l debug"
end
end
@@ -70,7 +70,7 @@ EXPECTED
describe "alternate chef-client path" do
let(:chef_config){ {:chef_client_path => '/usr/local/bin/chef-client'} }
it "runs chef-client from another path when specified" do
- expect(bootstrap_context.start_chef).to eq "/usr/local/bin/chef-client -j /etc/chef/first-boot.json -E _default"
+ expect(bootstrap_context.start_chef).to eq "/usr/local/bin/chef-client -j /etc/chef/first-boot.json"
end
end
@@ -97,6 +97,13 @@ EXPECTED
end
end
+ describe "when tags are given" do
+ let(:config) { {:tags => [ "unicorn" ] } }
+ it "adds the attributes to first_boot" do
+ expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json({:run_list => run_list, :tags => ["unicorn"]}))
+ end
+ end
+
describe "when JSON attributes are given" do
let(:config) { {:first_boot_attributes => {:baz => :quux}} }
it "adds the attributes to first_boot" do
@@ -110,6 +117,16 @@ EXPECTED
end
end
+ describe "when policy_name and policy_group are present in config" do
+
+ let(:config) { { policy_name: "my_app_server", policy_group: "staging" } }
+
+ it "includes them in the first_boot data and excludes run_list" do
+ expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json(config))
+ end
+
+ end
+
describe "when an encrypted_data_bag_secret is provided" do
let(:secret) { "supersekret" }
it "reads the encrypted_data_bag_secret" do
diff --git a/spec/unit/knife/node_run_list_remove_spec.rb b/spec/unit/knife/node_run_list_remove_spec.rb
index ceceef7178..a279a59635 100644
--- a/spec/unit/knife/node_run_list_remove_spec.rb
+++ b/spec/unit/knife/node_run_list_remove_spec.rb
@@ -84,6 +84,23 @@ describe Chef::Knife::NodeRunListRemove do
expect(@node.run_list).not_to include('role[monkey]')
expect(@node.run_list).not_to include('recipe[duck::type]')
end
+
+ it "should warn when the thing to remove is not in the runlist" do
+ @node.run_list << 'role[blah]'
+ @node.run_list << 'recipe[duck::type]'
+ @knife.name_args = [ 'adam', 'role[blork]' ]
+ expect(@knife.ui).to receive(:warn).with("role[blork] is not in the run list")
+ @knife.run
+ end
+
+ it "should warn even more when the thing to remove is not in the runlist and unqualified" do
+ @node.run_list << 'role[blah]'
+ @node.run_list << 'recipe[duck::type]'
+ @knife.name_args = [ 'adam', 'blork' ]
+ expect(@knife.ui).to receive(:warn).with("blork is not in the run list")
+ expect(@knife.ui).to receive(:warn).with(/did you forget recipe\[\] or role\[\]/)
+ @knife.run
+ end
end
end
end
diff --git a/spec/unit/lwrp_spec.rb b/spec/unit/lwrp_spec.rb
index bcb64cb21e..7f6d315bbb 100644
--- a/spec/unit/lwrp_spec.rb
+++ b/spec/unit/lwrp_spec.rb
@@ -190,7 +190,7 @@ describe "LWRP" do
end
it "should have a class that outputs a reasonable string" do
- expect(get_lwrp(:lwrp_foo).to_s).to eq "LWRP resource lwrp_foo from cookbook lwrp"
+ expect(get_lwrp(:lwrp_foo).to_s).to eq "Custom resource lwrp_foo from cookbook lwrp"
end
it "should add the specified actions to the allowed_actions array" do
diff --git a/spec/unit/mixin/enforce_ownership_and_permissions_spec.rb b/spec/unit/mixin/enforce_ownership_and_permissions_spec.rb
index aeef175ff9..408926293e 100644
--- a/spec/unit/mixin/enforce_ownership_and_permissions_spec.rb
+++ b/spec/unit/mixin/enforce_ownership_and_permissions_spec.rb
@@ -49,12 +49,12 @@ describe Chef::Mixin::EnforceOwnershipAndPermissions do
allow_any_instance_of(Chef::FileAccessControl).to receive(:uid_from_resource).and_return(0)
allow_any_instance_of(Chef::FileAccessControl).to receive(:requires_changes?).and_return(false)
allow_any_instance_of(Chef::FileAccessControl).to receive(:define_resource_requirements)
+ allow_any_instance_of(Chef::FileAccessControl).to receive(:describe_changes)
+
+ passwd_struct = OpenStruct.new(:name => "root", :passwd => "x",
+ :uid => 0, :gid => 0, :dir => '/root',
+ :shell => '/bin/bash')
- passwd_struct = if windows?
- Struct::Passwd.new("root", "x", 0, 0, "/root", "/bin/bash")
- else
- Struct::Passwd.new("root", "x", 0, 0, "root", "/root", "/bin/bash")
- end
group_struct = OpenStruct.new(:name => "root", :passwd => "x", :gid => 0)
allow(Etc).to receive(:getpwuid).and_return(passwd_struct)
allow(Etc).to receive(:getgrgid).and_return(group_struct)
@@ -73,12 +73,12 @@ describe Chef::Mixin::EnforceOwnershipAndPermissions do
before do
allow_any_instance_of(Chef::FileAccessControl).to receive(:requires_changes?).and_return(true)
allow_any_instance_of(Chef::FileAccessControl).to receive(:uid_from_resource).and_return(0)
+ allow_any_instance_of(Chef::FileAccessControl).to receive(:describe_changes)
+
+ passwd_struct = OpenStruct.new(:name => "root", :passwd => "x",
+ :uid => 0, :gid => 0, :dir => '/root',
+ :shell => '/bin/bash')
- passwd_struct = if windows?
- Struct::Passwd.new("root", "x", 0, 0, "/root", "/bin/bash")
- else
- Struct::Passwd.new("root", "x", 0, 0, "root", "/root", "/bin/bash")
- end
group_struct = OpenStruct.new(:name => "root", :passwd => "x", :gid => 0)
allow(Etc).to receive(:getpwuid).and_return(passwd_struct)
allow(Etc).to receive(:getgrgid).and_return(group_struct)
diff --git a/spec/unit/mixin/properties_spec.rb b/spec/unit/mixin/properties_spec.rb
new file mode 100644
index 0000000000..18178619e4
--- /dev/null
+++ b/spec/unit/mixin/properties_spec.rb
@@ -0,0 +1,97 @@
+require 'support/shared/integration/integration_helper'
+require 'chef/mixin/properties'
+
+module ChefMixinPropertiesSpec
+ describe "Chef::Resource.property" do
+ include IntegrationSupport
+
+ context "with a base class A with properties a, ab, and ac" do
+ class A
+ include Chef::Mixin::Properties
+ property :a, 'a', default: 'a'
+ property :ab, ['a', 'b'], default: 'a'
+ property :ac, ['a', 'c'], default: 'a'
+ end
+
+ context "and a module B with properties b, ab and bc" do
+ module B
+ include Chef::Mixin::Properties
+ property :b, 'b', default: 'b'
+ property :ab, default: 'b'
+ property :bc, ['b', 'c'], default: 'c'
+ end
+
+ context "and a derived class C < A with properties c, ac and bc" do
+ class C < A
+ include B
+ property :c, 'c', default: 'c'
+ property :ac, default: 'c'
+ property :bc, default: 'c'
+ end
+
+ it "A.properties has a, ab, and ac with types 'a', ['a', 'b'], and ['b', 'c']" do
+ expect(A.properties.keys).to eq [ :a, :ab, :ac ]
+ expect(A.properties[:a].validation_options[:is]).to eq 'a'
+ expect(A.properties[:ab].validation_options[:is]).to eq [ 'a', 'b' ]
+ expect(A.properties[:ac].validation_options[:is]).to eq [ 'a', 'c' ]
+ end
+ it "B.properties has b, ab, and bc with types 'b', nil and ['b', 'c']" do
+ expect(B.properties.keys).to eq [ :b, :ab, :bc ]
+ expect(B.properties[:b].validation_options[:is]).to eq 'b'
+ expect(B.properties[:ab].validation_options[:is]).to be_nil
+ expect(B.properties[:bc].validation_options[:is]).to eq [ 'b', 'c' ]
+ end
+ it "C.properties has a, b, c, ac and bc with merged types" do
+ expect(C.properties.keys).to eq [ :a, :ab, :ac, :b, :bc, :c ]
+ expect(C.properties[:a].validation_options[:is]).to eq 'a'
+ expect(C.properties[:b].validation_options[:is]).to eq 'b'
+ expect(C.properties[:c].validation_options[:is]).to eq 'c'
+ expect(C.properties[:ac].validation_options[:is]).to eq [ 'a', 'c' ]
+ expect(C.properties[:bc].validation_options[:is]).to eq [ 'b', 'c' ]
+ end
+ it "C.properties has ab with a non-merged type (from B)" do
+ expect(C.properties[:ab].validation_options[:is]).to be_nil
+ end
+
+ context "and an instance of C" do
+ let(:c) { C.new }
+
+ it "all properties can be retrieved and merged properties default to ab->b, ac->c, bc->c" do
+ expect(c.a).to eq('a')
+ expect(c.b).to eq('b')
+ expect(c.c).to eq('c')
+ expect(c.ab).to eq('b')
+ expect(c.ac).to eq('c')
+ expect(c.bc).to eq('c')
+ end
+ end
+ end
+ end
+ end
+ end
+
+ context "with an Inner module" do
+ module Inner
+ include Chef::Mixin::Properties
+ property :inner
+ end
+
+ context "and an Outer module including it" do
+ module Outer
+ include Inner
+ property :outer
+ end
+
+ context "and an Outerest class including that" do
+ class Outerest
+ include Outer
+ property :outerest
+ end
+
+ it "Outerest.properties.validation_options[:is] inner, outer, outerest" do
+ expect(Outerest.properties.keys).to eq [:inner, :outer, :outerest]
+ end
+ end
+ end
+ end
+end
diff --git a/spec/unit/mixin/template_spec.rb b/spec/unit/mixin/template_spec.rb
index 6a867b5f9a..95d0eb6711 100644
--- a/spec/unit/mixin/template_spec.rb
+++ b/spec/unit/mixin/template_spec.rb
@@ -150,6 +150,11 @@ describe Chef::Mixin::Template, "render_template" do
output == "before {super secret is candy} after"
end
+ it "should pass the template finder to the partials" do
+ output = @template_context.render_template_from_string("before {<%= render 'nested_openldap_partials.erb', :variables => {:hello => 'Hello World!' } %>} after")
+ output == "before {Hello World!} after"
+ end
+
it "should pass variables to partials" do
output = @template_context.render_template_from_string("before {<%= render 'openldap_variable_stuff.conf.erb', :variables => {:secret => 'whatever' } %>} after")
expect(output).to eq("before {super secret is whatever} after")
@@ -266,4 +271,3 @@ describe Chef::Mixin::Template, "render_template" do
end
end
end
-
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index b7752eb734..5f3bed2833 100644
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -127,6 +127,78 @@ describe Chef::Node do
end
end
+ describe "policy_name" do
+
+ it "defaults to nil" do
+ expect(node.policy_name).to be_nil
+ end
+
+ it "sets policy_name with a regular setter" do
+ node.policy_name = "example-policy"
+ expect(node.policy_name).to eq("example-policy")
+ end
+
+ it "allows policy_name with every valid character" do
+ expect { node.policy_name = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqurstuvwxyz0123456789-_:.' }.to_not raise_error
+ end
+
+ it "sets policy_name when given an argument" do
+ node.policy_name("example-policy")
+ expect(node.policy_name).to eq("example-policy")
+ end
+
+ it "sets policy_name to nil when given nil" do
+ node.policy_name = "example-policy"
+ node.policy_name = nil
+ expect(node.policy_name).to be_nil
+ end
+
+ it "disallows non-strings" do
+ expect { node.policy_name(Hash.new) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { node.policy_name(42) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "cannot be blank" do
+ expect { node.policy_name("")}.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+ end
+
+ describe "policy_group" do
+
+ it "defaults to nil" do
+ expect(node.policy_group).to be_nil
+ end
+
+ it "sets policy_group with a regular setter" do
+ node.policy_group = "staging"
+ expect(node.policy_group).to eq("staging")
+ end
+
+ it "allows policy_group with every valid character" do
+ expect { node.policy_group = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqurstuvwxyz0123456789-_:.' }.to_not raise_error
+ end
+
+ it "sets an environment with chef_environment(something)" do
+ node.policy_group("staging")
+ expect(node.policy_group).to eq("staging")
+ end
+
+ it "sets policy_group to nil when given nil" do
+ node.policy_group = "staging"
+ node.policy_group = nil
+ expect(node.policy_group).to be_nil
+ end
+
+ it "disallows non-strings" do
+ expect { node.policy_group(Hash.new) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ expect { node.policy_group(42) }.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "cannot be blank" do
+ expect { node.policy_group("")}.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+ end
+
describe "attributes" do
it "should have attributes" do
expect(node.attribute).to be_a_kind_of(Hash)
@@ -1113,6 +1185,43 @@ describe Chef::Node do
expect(serialized_node.run_list).to eq(node.run_list)
end
+ context "when policyfile attributes are not present" do
+
+ it "does not have a policy_name key in the json" do
+ expect(node.for_json.keys).to_not include("policy_name")
+ end
+
+ it "does not have a policy_group key in the json" do
+ expect(node.for_json.keys).to_not include("policy_name")
+ end
+ end
+
+ context "when policyfile attributes are present" do
+
+ before do
+ node.policy_name = "my-application"
+ node.policy_group = "staging"
+ end
+
+ it "includes policy_name key in the json" do
+ expect(node.for_json).to have_key("policy_name")
+ expect(node.for_json["policy_name"]).to eq("my-application")
+ end
+
+ it "includes a policy_group key in the json" do
+ expect(node.for_json).to have_key("policy_group")
+ expect(node.for_json["policy_group"]).to eq("staging")
+ end
+
+ it "parses policyfile attributes from JSON" do
+ round_tripped_node = Chef::Node.json_create(node.for_json)
+
+ expect(round_tripped_node.policy_name).to eq("my-application")
+ expect(round_tripped_node.policy_group).to eq("staging")
+ end
+
+ end
+
include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) {
node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA))
@@ -1308,6 +1417,110 @@ describe Chef::Node do
node.save
end
end
+
+ context "when policyfile attributes are present" do
+
+ before do
+ node.name("example-node")
+ node.policy_name = "my-application"
+ node.policy_group = "staging"
+ end
+
+ context "and the server supports policyfile attributes in node JSON" do
+
+ it "creates the object normally" do
+ expect(@rest).to receive(:post_rest).with("nodes", node.for_json)
+ node.create
+ end
+
+ it "saves the node object normally" do
+ expect(@rest).to receive(:put_rest).with("nodes/example-node", node.for_json)
+ node.save
+ end
+ end
+
+ # Chef Server before 12.3
+ context "and the Chef Server does not support policyfile attributes in node JSON" do
+
+ let(:response_body) { %q[{"error":["Invalid key policy_name in request body"]}] }
+
+ let(:response) do
+ Net::HTTPResponse.send(:response_class, "400").new("1.0", "400", "Bad Request").tap do |r|
+ allow(r).to receive(:body).and_return(response_body)
+ end
+ end
+
+ let(:http_exception) do
+ begin
+ response.error!
+ rescue => e
+ e
+ end
+ end
+
+ let(:trimmed_node) do
+ node.for_json.tap do |j|
+ j.delete("policy_name")
+ j.delete("policy_group")
+ end
+
+ end
+
+ context "on Chef Client 13 and later" do
+
+ # Though we normally attempt to provide compatibility with chef
+ # server one major version back, policyfiles were beta when we
+ # added the policyfile attributes to the node JSON, therefore
+ # policyfile users need to be on 12.3 minimum when upgrading Chef
+ # Client to 13+
+ it "lets the 400 pass through", :chef_gte_13_only do
+ expect { node.save }.to raise_error(http_exception)
+ end
+
+ end
+
+ context "when the node exists" do
+
+ it "falls back to saving without policyfile attributes" do
+ expect(@rest).to receive(:put_rest).with("nodes/example-node", node.for_json).and_raise(http_exception)
+ expect(@rest).to receive(:put_rest).with("nodes/example-node", trimmed_node).and_return(@node)
+ expect { node.save }.to_not raise_error
+ end
+
+ end
+
+ context "when the node doesn't exist" do
+
+ let(:response_404) do
+ Net::HTTPResponse.send(:response_class, "404").new("1.0", "404", "Not Found")
+ end
+
+ let(:http_exception_404) do
+ begin
+ response_404.error!
+ rescue => e
+ e
+ end
+ end
+
+ it "falls back to saving without policyfile attributes" do
+ expect(@rest).to receive(:put_rest).with("nodes/example-node", node.for_json).and_raise(http_exception)
+ expect(@rest).to receive(:put_rest).with("nodes/example-node", trimmed_node).and_raise(http_exception_404)
+ expect(@rest).to receive(:post_rest).with("nodes", trimmed_node).and_return(@node)
+ node.save
+ end
+
+ it "creates the node without policyfile attributes" do
+ expect(@rest).to receive(:post_rest).with("nodes", node.for_json).and_raise(http_exception)
+ expect(@rest).to receive(:post_rest).with("nodes", trimmed_node).and_return(@node)
+ node.create
+ end
+ end
+
+ end
+
+ end
+
end
end
diff --git a/spec/unit/platform/query_helpers_spec.rb b/spec/unit/platform/query_helpers_spec.rb
index 33d4c2c3b7..d18b6f7902 100644
--- a/spec/unit/platform/query_helpers_spec.rb
+++ b/spec/unit/platform/query_helpers_spec.rb
@@ -21,7 +21,7 @@ require 'spec_helper'
describe "Chef::Platform#windows_server_2003?" do
it "returns false early when not on windows" do
allow(ChefConfig).to receive(:windows?).and_return(false)
- expect(Chef::Platform).not_to receive(:require)
+ expect(Chef::Platform).not_to receive(:require)
expect(Chef::Platform.windows_server_2003?).to be_falsey
end
@@ -31,7 +31,127 @@ describe "Chef::Platform#windows_server_2003?" do
end
end
-describe 'Chef::Platform#supports_dsc?' do
+describe "Chef::Platform#windows_nano_server?" do
+ include_context "Win32"
+
+ let(:key) { "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Server\\ServerLevels" }
+ let(:key_query_value) { 0x0001 }
+ let(:access) { key_query_value | 0x0100 }
+ let(:hive) { double("Win32::Registry::HKEY_LOCAL_MACHINE") }
+ let(:registry) { double("Win32::Registry") }
+
+ before(:all) do
+ Win32::Registry = Class.new
+ Win32::Registry::Error = Class.new(RuntimeError)
+ end
+
+ before do
+ Win32::Registry::HKEY_LOCAL_MACHINE = hive
+ Win32::Registry::KEY_QUERY_VALUE = key_query_value
+ end
+
+ after do
+ Win32::Registry.send(:remove_const, 'HKEY_LOCAL_MACHINE') if defined?(Win32::Registry::HKEY_LOCAL_MACHINE)
+ Win32::Registry.send(:remove_const, 'KEY_QUERY_VALUE') if defined?(Win32::Registry::KEY_QUERY_VALUE)
+ end
+
+ it "returns false early when not on windows" do
+ allow(ChefConfig).to receive(:windows?).and_return(false)
+ expect(Chef::Platform).to_not receive(:require)
+ expect(Chef::Platform.windows_nano_server?).to be false
+ end
+
+ it "returns true when the registry value is 1" do
+ allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(Chef::Platform).to receive(:require).with('win32/registry')
+ expect(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).
+ with(key, access).
+ and_yield(registry)
+ expect(registry).to receive(:[]).with("NanoServer").and_return(1)
+ expect(Chef::Platform.windows_nano_server?).to be true
+ end
+
+ it "returns false when the registry value is not 1" do
+ allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(Chef::Platform).to receive(:require).with('win32/registry')
+ expect(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).
+ with(key, access).
+ and_yield(registry)
+ expect(registry).to receive(:[]).with("NanoServer").and_return(0)
+ expect(Chef::Platform.windows_nano_server?).to be false
+ end
+
+ it "returns false when the registry value does not exist" do
+ allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(Chef::Platform).to receive(:require).with('win32/registry')
+ expect(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).
+ with(key, access).
+ and_yield(registry)
+ expect(registry).to receive(:[]).with("NanoServer").
+ and_raise(Win32::Registry::Error, "The system cannot find the file specified.")
+ expect(Chef::Platform.windows_nano_server?).to be false
+ end
+
+ it "returns false when the registry key does not exist" do
+ allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(Chef::Platform).to receive(:require).with('win32/registry')
+ expect(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).
+ with(key, access).
+ and_raise(Win32::Registry::Error, "The system cannot find the file specified.")
+ expect(Chef::Platform.windows_nano_server?).to be false
+ end
+end
+
+describe "Chef::Platform#supports_msi?" do
+ include_context "Win32" # clear and restore Win32:: namespace
+
+ let(:key) { "System\\CurrentControlSet\\Services\\msiserver" }
+ let(:key_query_value) { 0x0001 }
+ let(:access) { key_query_value }
+ let(:hive) { double("Win32::Registry::HKEY_LOCAL_MACHINE") }
+ let(:registry) { double("Win32::Registry") }
+
+ before(:all) do
+ Win32::Registry = Class.new
+ Win32::Registry::Error = Class.new(RuntimeError)
+ end
+
+ before do
+ Win32::Registry::HKEY_LOCAL_MACHINE = hive
+ Win32::Registry::KEY_QUERY_VALUE = key_query_value
+ end
+
+ after do
+ Win32::Registry.send(:remove_const, 'HKEY_LOCAL_MACHINE') if defined?(Win32::Registry::HKEY_LOCAL_MACHINE)
+ Win32::Registry.send(:remove_const, 'KEY_QUERY_VALUE') if defined?(Win32::Registry::KEY_QUERY_VALUE)
+ end
+
+ it "returns false early when not on windows" do
+ allow(ChefConfig).to receive(:windows?).and_return(false)
+ expect(Chef::Platform).to_not receive(:require)
+ expect(Chef::Platform.supports_msi?).to be false
+ end
+
+ it "returns true when the registry key exists" do
+ allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(Chef::Platform).to receive(:require).with('win32/registry')
+ expect(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).
+ with(key, access).
+ and_yield(registry)
+ expect(Chef::Platform.supports_msi?).to be true
+ end
+
+ it "returns false when the registry key does not exist" do
+ allow(ChefConfig).to receive(:windows?).and_return(true)
+ allow(Chef::Platform).to receive(:require).with('win32/registry')
+ expect(Win32::Registry::HKEY_LOCAL_MACHINE).to receive(:open).
+ with(key, access).
+ and_raise(Win32::Registry::Error, "The system cannot find the file specified.")
+ expect(Chef::Platform.supports_msi?).to be false
+ end
+end
+
+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
@@ -54,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
@@ -75,3 +195,26 @@ describe 'Chef::Platform#supports_dsc_invoke_resource?' do
end
end
+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')}
+
+ it "returns true when RefreshMode is Disabled" do
+ expect(Chef::Util::Powershell::Cmdlet).to receive(:new).
+ with(node, "Get-DscLocalConfigurationManager", :object).
+ and_return(cmdlet)
+ expect(cmdlet).to receive(:run!).and_return(cmdlet_result)
+ expect(cmdlet_result).to receive(:return_value).and_return({ 'RefreshMode' => 'Disabled' })
+ expect(Chef::Platform.dsc_refresh_mode_disabled?(node)).to be true
+ end
+
+ it "returns false when RefreshMode is not Disabled" do
+ expect(Chef::Util::Powershell::Cmdlet).to receive(:new).
+ with(node, "Get-DscLocalConfigurationManager", :object).
+ and_return(cmdlet)
+ expect(cmdlet).to receive(:run!).and_return(cmdlet_result)
+ expect(cmdlet_result).to receive(:return_value).and_return({ 'RefreshMode' => 'LaLaLa' })
+ expect(Chef::Platform.dsc_refresh_mode_disabled?(node)).to be false
+ end
+end
diff --git a/spec/unit/policy_builder/dynamic_spec.rb b/spec/unit/policy_builder/dynamic_spec.rb
new file mode 100644
index 0000000000..aff19f4d11
--- /dev/null
+++ b/spec/unit/policy_builder/dynamic_spec.rb
@@ -0,0 +1,275 @@
+#
+# Author:: Daniel DeLeo (<dan@getchef.com>)
+# Copyright:: Copyright 2014 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/policy_builder'
+
+describe Chef::PolicyBuilder::Dynamic do
+
+ let(:node_name) { "joe_node" }
+ let(:ohai_data) { {"platform" => "ubuntu", "platform_version" => "13.04", "fqdn" => "joenode.example.com"} }
+ let(:json_attribs) { {"custom_attr" => "custom_attr_value"} }
+ let(:override_runlist) { nil }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+
+ let(:err_namespace) { Chef::PolicyBuilder::Policyfile }
+
+ let(:base_node) do
+ node = Chef::Node.new
+ node.name(node_name)
+ node
+ end
+
+ let(:node) { base_node }
+
+ subject(:policy_builder) { Chef::PolicyBuilder::Dynamic.new(node_name, ohai_data, json_attribs, override_runlist, events) }
+
+ describe "loading policy data" do
+
+ describe "delegating PolicyBuilder API to the correct implementation" do
+
+ let(:implementation) { instance_double("Chef::PolicyBuilder::Policyfile") }
+
+ before do
+ allow(policy_builder).to receive(:implementation).and_return(implementation)
+ end
+
+ # Dynamic should load_node, figure out the correct backend, then forward
+ # messages to it after. That behavior is tested below.
+ it "responds to #load_node" do
+ expect(policy_builder).to respond_to(:load_node)
+ end
+
+ it "forwards #original_runlist" do
+ expect(implementation).to receive(:original_runlist)
+ policy_builder.original_runlist
+ end
+
+ it "forwards #run_context" do
+ expect(implementation).to receive(:run_context)
+ policy_builder.run_context
+ end
+
+ it "forwards #run_list_expansion" do
+ expect(implementation).to receive(:run_list_expansion)
+ policy_builder.run_list_expansion
+ end
+
+ it "forwards #build_node to the implementation object" do
+ expect(implementation).to receive(:build_node)
+ policy_builder.build_node
+ end
+
+ it "forwards #setup_run_context to the implementation object" do
+ expect(implementation).to receive(:setup_run_context)
+ policy_builder.setup_run_context
+
+ arg = Object.new
+
+ expect(implementation).to receive(:setup_run_context).with(arg)
+ policy_builder.setup_run_context(arg)
+ end
+
+ it "forwards #expand_run_list to the implementation object" do
+ expect(implementation).to receive(:expand_run_list)
+ policy_builder.expand_run_list
+ end
+
+ it "forwards #sync_cookbooks to the implementation object" do
+ expect(implementation).to receive(:sync_cookbooks)
+ policy_builder.sync_cookbooks
+ end
+
+ it "forwards #temporary_policy? to the implementation object" do
+ expect(implementation).to receive(:temporary_policy?)
+ policy_builder.temporary_policy?
+ end
+
+ end
+
+ describe "selecting a backend implementation" do
+
+ let(:implementation) do
+ policy_builder.select_implementation(node)
+ policy_builder.implementation
+ end
+
+ context "when no policyfile attributes are present on the node" do
+
+ context "and json_attribs are not given" do
+
+ let(:json_attribs) { {} }
+
+ it "uses the ExpandNodeObject implementation" do
+ expect(implementation).to be_a(Chef::PolicyBuilder::ExpandNodeObject)
+ end
+
+ end
+
+ context "and no policyfile attributes are present in json_attribs" do
+
+ let(:json_attribs) { {"foo" => "bar"} }
+
+ it "uses the ExpandNodeObject implementation" do
+ expect(implementation).to be_a(Chef::PolicyBuilder::ExpandNodeObject)
+ end
+
+ end
+
+ context "and :use_policyfile is set in Chef::Config" do
+
+ before do
+ Chef::Config[:use_policyfile] = true
+ end
+
+ it "uses the Policyfile implementation" do
+ expect(implementation).to be_a(Chef::PolicyBuilder::Policyfile)
+ end
+
+ end
+
+ context "and policy_name and policy_group are set on Chef::Config" do
+
+ before do
+ Chef::Config[:policy_name] = "example-policy"
+ Chef::Config[:policy_group] = "testing"
+ end
+
+ it "uses the Policyfile implementation" do
+ expect(implementation).to be_a(Chef::PolicyBuilder::Policyfile)
+ end
+
+ end
+
+ context "and deployment_group and policy_document_native_api are set on Chef::Config" do
+
+ before do
+ Chef::Config[:deployment_group] = "example-policy-staging"
+ Chef::Config[:policy_document_native_api] = false
+ end
+
+ it "uses the Policyfile implementation" do
+ expect(implementation).to be_a(Chef::PolicyBuilder::Policyfile)
+ end
+
+ end
+
+ context "and policyfile attributes are present in json_attribs" do
+
+ let(:json_attribs) { {"policy_name" => "example-policy", "policy_group" => "testing"} }
+
+ it "uses the Policyfile implementation" do
+ expect(implementation).to be_a(Chef::PolicyBuilder::Policyfile)
+ end
+
+ end
+
+ end
+
+ context "when policyfile attributes are present on the node" do
+
+ let(:node) do
+ base_node.policy_name = "example-policy"
+ base_node.policy_group = "staging"
+ base_node
+ end
+
+ it "uses the Policyfile implementation" do
+ expect(implementation).to be_a(Chef::PolicyBuilder::Policyfile)
+ end
+
+ end
+
+ end
+
+ describe "loading a node" do
+
+ let(:implementation) { instance_double("Chef::PolicyBuilder::Policyfile") }
+
+ before do
+ allow(policy_builder).to receive(:implementation).and_return(implementation)
+ end
+
+ context "when not running chef solo" do
+
+
+ context "when successful" do
+
+ before do
+ expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
+ expect(policy_builder).to receive(:select_implementation).with(node)
+ expect(implementation).to receive(:finish_load_node).with(node)
+ end
+
+ it "selects the backend implementation and continues node loading" do
+ policy_builder.load_node
+ end
+
+ end
+
+ context "when an error occurs finding the node" do
+
+ before do
+ expect(Chef::Node).to receive(:find_or_create).with(node_name).and_raise("oops")
+ end
+
+ it "sends a node_load_failed event and re-raises" do
+ expect(events).to receive(:node_load_failed)
+ expect { policy_builder.load_node }.to raise_error("oops")
+ end
+
+ end
+
+ context "when an error occurs in the implementation's finish_load_node call" do
+
+ before do
+ expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
+ expect(policy_builder).to receive(:select_implementation).with(node)
+ expect(implementation).to receive(:finish_load_node).and_raise("oops")
+ end
+
+
+ it "sends a node_load_failed event and re-raises" do
+ expect(events).to receive(:node_load_failed)
+ expect { policy_builder.load_node }.to raise_error("oops")
+ end
+
+ end
+
+ end
+
+ context "when running chef solo" do
+
+ before do
+ Chef::Config[:solo] = true
+ expect(Chef::Node).to receive(:build).with(node_name).and_return(node)
+ expect(policy_builder).to receive(:select_implementation).with(node)
+ expect(implementation).to receive(:finish_load_node).with(node)
+ end
+
+ it "selects the backend implementation and continues node loading" do
+ policy_builder.load_node
+ end
+
+ end
+
+ end
+
+ end
+
+end
diff --git a/spec/unit/policy_builder/expand_node_object_spec.rb b/spec/unit/policy_builder/expand_node_object_spec.rb
index 8e9fdc305e..306d677108 100644
--- a/spec/unit/policy_builder/expand_node_object_spec.rb
+++ b/spec/unit/policy_builder/expand_node_object_spec.rb
@@ -34,10 +34,18 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
expect(policy_builder).to respond_to(:node)
end
- it "implements a load_node method" do
+ it "implements a load_node method for backwards compatibility until Chef 13" do
expect(policy_builder).to respond_to(:load_node)
end
+ it "has removed the deprecated #load_node method", :chef_gte_13_only do
+ expect(policy_builder).to_not respond_to(:load_node)
+ end
+
+ it "implements a finish_load_node method" do
+ expect(policy_builder).to respond_to(:finish_load_node)
+ end
+
it "implements a build_node method" do
expect(policy_builder).to respond_to(:build_node)
end
@@ -63,39 +71,13 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
expect(policy_builder).to respond_to(:temporary_policy?)
end
- describe "loading the node" do
-
- context "on chef-solo" do
-
- before do
- Chef::Config[:solo] = true
- end
-
- it "creates a new in-memory node object with the given name" do
- policy_builder.load_node
- expect(policy_builder.node.name).to eq(node_name)
- end
-
- end
-
- context "on chef-client" do
-
- let(:node) { Chef::Node.new.tap { |n| n.name(node_name) } }
-
- it "loads or creates a node on the server" do
- expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
- policy_builder.load_node
- expect(policy_builder.node).to eq(node)
- end
+ describe "finishing loading the node" do
- end
- end
-
- describe "building the node" do
+ let(:node) { Chef::Node.new.tap { |n| n.name(node_name) } }
- # XXX: Chef::Client just needs to be able to call this, it doesn't depend on the return value.
- it "builds the node and returns the updated node object" do
- skip
+ it "stores the node" do
+ policy_builder.finish_load_node(node)
+ expect(policy_builder.node).to eq(node)
end
end
@@ -124,7 +106,8 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
end
- context "once the node has been loaded" do
+ context "deprecated #load_node method" do
+
let(:node) do
node = Chef::Node.new
node.name(node_name)
@@ -133,10 +116,29 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
end
before do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
policy_builder.load_node
end
+ it "loads the node" do
+ expect(policy_builder.node).to eq(node)
+ end
+
+ end
+
+ context "once the node has been loaded" do
+ let(:node) do
+ node = Chef::Node.new
+ node.name(node_name)
+ node.run_list(["recipe[a::default]", "recipe[b::server]"])
+ node
+ end
+
+ before do
+ policy_builder.finish_load_node(node)
+ end
+
it "expands the run_list" do
expect(policy_builder.expand_run_list).to be_a(Chef::RunList::RunListExpansion)
expect(policy_builder.run_list_expansion).to be_a(Chef::RunList::RunListExpansion)
@@ -167,8 +169,7 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
before do
Chef::Config[:environment] = configured_environment
- expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
- policy_builder.load_node
+ policy_builder.finish_load_node(node)
policy_builder.build_node
end
@@ -302,11 +303,9 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
let(:cookbook_synchronizer) { double("CookbookSynchronizer") }
before do
- expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
-
allow(policy_builder).to receive(:api_service).and_return(chef_http)
- policy_builder.load_node
+ policy_builder.finish_load_node(node)
policy_builder.build_node
run_list_expansion = policy_builder.run_list_expansion
diff --git a/spec/unit/policy_builder/policyfile_spec.rb b/spec/unit/policy_builder/policyfile_spec.rb
index 5fa00d8f2b..b656a66ec3 100644
--- a/spec/unit/policy_builder/policyfile_spec.rb
+++ b/spec/unit/policy_builder/policyfile_spec.rb
@@ -76,8 +76,11 @@ describe Chef::PolicyBuilder::Policyfile do
let(:policyfile_run_list) { ["recipe[example1::default]", "recipe[example2::server]"] }
- let(:parsed_policyfile_json) do
+ let(:basic_valid_policy_data) do
{
+ "name" => "example-policy",
+ "revision_id" => "123abc",
+
"run_list" => policyfile_run_list,
"cookbook_locks" => {
@@ -90,6 +93,8 @@ describe Chef::PolicyBuilder::Policyfile do
}
end
+ let(:parsed_policyfile_json) { basic_valid_policy_data }
+
let(:err_namespace) { Chef::PolicyBuilder::Policyfile }
it "configures a Chef HTTP API client" do
@@ -181,19 +186,13 @@ describe Chef::PolicyBuilder::Policyfile do
let(:error404) { Net::HTTPServerException.new("404 message", :body) }
before do
- expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
expect(http_api).to receive(:get).
with("data/policyfiles/example-policy-stage").
and_raise(error404)
end
it "raises an error" do
- expect { policy_builder.load_node }.to raise_error(err_namespace::ConfigurationError)
- end
-
- it "sends error message to the event system" do
- expect(events).to receive(:node_load_failed).with(node_name, an_instance_of(err_namespace::ConfigurationError), Chef::Config)
- expect { policy_builder.load_node }.to raise_error(err_namespace::ConfigurationError)
+ expect { policy_builder.finish_load_node(node) }.to raise_error(err_namespace::ConfigurationError)
end
end
@@ -201,20 +200,12 @@ describe Chef::PolicyBuilder::Policyfile do
context "when the deployment_group is not configured" do
before do
Chef::Config[:deployment_group] = nil
- expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
end
it "errors while loading the node" do
- expect { policy_builder.load_node }.to raise_error(err_namespace::ConfigurationError)
+ expect { policy_builder.finish_load_node(node) }.to raise_error(err_namespace::ConfigurationError)
end
-
- it "passes error information to the event system" do
- # TODO: also make sure something acceptable happens with the error formatters
- err_class = err_namespace::ConfigurationError
- expect(events).to receive(:node_load_failed).with(node_name, an_instance_of(err_class), Chef::Config)
- expect { policy_builder.load_node }.to raise_error(err_class)
- end
end
context "when deployment_group is correctly configured" do
@@ -307,8 +298,7 @@ describe Chef::PolicyBuilder::Policyfile do
end
it "implements #expand_run_list in a manner compatible with ExpandNodeObject" do
- expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
- policy_builder.load_node
+ policy_builder.finish_load_node(node)
expect(policy_builder.expand_run_list).to respond_to(:recipes)
expect(policy_builder.expand_run_list.recipes).to eq(["example1::default", "example2::server"])
expect(policy_builder.expand_run_list.roles).to eq([])
@@ -345,46 +335,261 @@ describe Chef::PolicyBuilder::Policyfile do
describe "building the node object" do
+ let(:extra_chef_config) { {} }
+
before do
- expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
+ # must be set before #build_node is called to have the proper effect
+ extra_chef_config.each do |key, value|
+ Chef::Config[key] = value
+ end
- policy_builder.load_node
+ policy_builder.finish_load_node(node)
policy_builder.build_node
end
+ # it sets policy_name and policy_group in the following priority order:
+ # -j JSON > config file > node object
+
+ describe "selecting policy_name and policy_group from the various sources" do
+
+ context "when only set in node JSON" do
+
+ let(:json_attribs) do
+ {
+ "policy_name" => "policy_name_from_node_json",
+ "policy_group" => "policy_group_from_node_json"
+ }
+ end
+
+ it "sets policy_name and policy_group on Chef::Config" do
+ expect(Chef::Config[:policy_name]).to eq("policy_name_from_node_json")
+ expect(Chef::Config[:policy_group]).to eq("policy_group_from_node_json")
+ end
+
+ it "sets policy_name and policy_group on the node object" do
+ expect(node.policy_name).to eq("policy_name_from_node_json")
+ expect(node.policy_group).to eq("policy_group_from_node_json")
+ end
+
+ end
+
+ context "when only set in Chef::Config" do
+
+ let(:extra_chef_config) do
+ {
+ policy_name: "policy_name_from_config",
+ policy_group: "policy_group_from_config"
+ }
+ end
+
+ it "sets policy_name and policy_group on the node object" do
+ expect(node.policy_name).to eq("policy_name_from_config")
+ expect(node.policy_group).to eq("policy_group_from_config")
+ end
+
+ end
+
+ context "when only set on the node" do
+
+ let(:node) do
+ node = Chef::Node.new
+ node.name(node_name)
+ node.policy_name = "policy_name_from_node"
+ node.policy_group = "policy_group_from_node"
+ node
+ end
+
+ it "sets policy_name and policy_group on Chef::Config" do
+ expect(Chef::Config[:policy_name]).to eq("policy_name_from_node")
+ expect(Chef::Config[:policy_group]).to eq("policy_group_from_node")
+ end
+
+ end
+
+ context "when set in Chef::Config and the fetched node" do
+
+ let(:node) do
+ node = Chef::Node.new
+ node.name(node_name)
+ node.policy_name = "policy_name_from_node"
+ node.policy_group = "policy_group_from_node"
+ node
+ end
+
+ let(:extra_chef_config) do
+ {
+ policy_name: "policy_name_from_config",
+ policy_group: "policy_group_from_config"
+ }
+ end
+
+ it "prefers the policy_name and policy_group from Chef::Config" do
+ expect(node.policy_name).to eq("policy_name_from_config")
+ expect(node.policy_group).to eq("policy_group_from_config")
+ end
+
+ end
+
+ context "when set in node json and the fetched node" do
+
+ let(:json_attribs) do
+ {
+ "policy_name" => "policy_name_from_node_json",
+ "policy_group" => "policy_group_from_node_json"
+ }
+ end
+
+ let(:node) do
+ node = Chef::Node.new
+ node.name(node_name)
+ node.policy_name = "policy_name_from_node"
+ node.policy_group = "policy_group_from_node"
+ node
+ end
+
+
+ it "prefers the policy_name and policy_group from the node json" do
+ expect(policy_builder.policy_name).to eq("policy_name_from_node_json")
+ expect(policy_builder.policy_group).to eq("policy_group_from_node_json")
+
+ expect(Chef::Config[:policy_name]).to eq("policy_name_from_node_json")
+ expect(Chef::Config[:policy_group]).to eq("policy_group_from_node_json")
+ expect(node.policy_name).to eq("policy_name_from_node_json")
+ expect(node.policy_group).to eq("policy_group_from_node_json")
+ end
+
+ end
+
+ context "when set in all sources" do
+
+ let(:json_attribs) do
+ {
+ "policy_name" => "policy_name_from_node_json",
+ "policy_group" => "policy_group_from_node_json"
+ }
+ end
+
+ let(:node) do
+ node = Chef::Node.new
+ node.name(node_name)
+ node.policy_name = "policy_name_from_node"
+ node.policy_group = "policy_group_from_node"
+ node
+ end
+
+ let(:extra_chef_config) do
+ {
+ policy_name: "policy_name_from_config",
+ policy_group: "policy_group_from_config"
+ }
+ end
+
+ it "prefers the policy_name and group from node json" do
+ expect(policy_builder.policy_name).to eq("policy_name_from_node_json")
+ expect(policy_builder.policy_group).to eq("policy_group_from_node_json")
+
+ expect(Chef::Config[:policy_name]).to eq("policy_name_from_node_json")
+ expect(Chef::Config[:policy_group]).to eq("policy_group_from_node_json")
+ expect(node.policy_name).to eq("policy_name_from_node_json")
+ expect(node.policy_group).to eq("policy_group_from_node_json")
+ end
+
+ end
+
+ end
+
it "resets default and override data" do
expect(node["default_key"]).to be_nil
expect(node["override_key"]).to be_nil
end
- it "applies ohai data" do
- expect(ohai_data).to_not be_empty # ensure test is testing something
- ohai_data.each do |key, value|
- expect(node.automatic_attrs[key]).to eq(value)
+ describe "setting attribute values" do
+
+ before do
+ policy_builder.build_node
end
- end
- it "applies attributes from json file" do
- expect(node["custom_attr"]).to eq("custom_attr_value")
- end
+ it "resets default and override data" do
+ expect(node["default_key"]).to be_nil
+ expect(node["override_key"]).to be_nil
+ end
- it "applies attributes from the policyfile" do
- expect(node["policyfile_default_attr"]).to eq("policyfile_default_value")
- expect(node["policyfile_override_attr"]).to eq("policyfile_override_value")
- end
+ it "applies ohai data" do
+ expect(ohai_data).to_not be_empty # ensure test is testing something
+ ohai_data.each do |key, value|
+ expect(node.automatic_attrs[key]).to eq(value)
+ end
+ end
- it "sets the policyfile's run_list on the node object" do
- expect(node.run_list).to eq(policyfile_run_list)
- end
+ it "applies attributes from json file" do
+ expect(node["custom_attr"]).to eq("custom_attr_value")
+ end
- it "creates node.automatic_attrs[:roles]" do
- expect(node.automatic_attrs[:roles]).to eq([])
- end
+ it "applies attributes from the policyfile" do
+ expect(node["policyfile_default_attr"]).to eq("policyfile_default_value")
+ expect(node["policyfile_override_attr"]).to eq("policyfile_override_value")
+ end
+
+ it "sets the policyfile's run_list on the node object" do
+ expect(node.run_list).to eq(policyfile_run_list)
+ end
+
+ it "creates node.automatic_attrs[:roles]" do
+ expect(node.automatic_attrs[:roles]).to eq([])
+ end
- it "create node.automatic_attrs[:recipes]" do
- expect(node.automatic_attrs[:recipes]).to eq(["example1::default", "example2::server"])
+ it "create node.automatic_attrs[:recipes]" do
+ expect(node.automatic_attrs[:recipes]).to eq(["example1::default", "example2::server"])
+ end
end
+ context "when a named run_list is given" do
+
+ before do
+ Chef::Config[:named_run_list] = "deploy-app"
+ end
+
+ context "and the named run_list is not present in the policy" do
+
+ it "raises a ConfigurationError" do
+ err_class = Chef::PolicyBuilder::Policyfile::ConfigurationError
+ err_text = "Policy 'example-policy' revision '123abc' does not have named_run_list 'deploy-app'(available named_run_lists: [])"
+ expect { policy_builder.build_node }.to raise_error(err_class, err_text)
+ end
+
+ end
+
+ context "and the named run_list is present in the policy" do
+
+ let(:parsed_policyfile_json) do
+ basic_valid_policy_data.dup.tap do |p|
+ p["named_run_lists"] = {
+ "deploy-app" => [ "recipe[example1::default]" ]
+ }
+ end
+ end
+
+ before do
+ policy_builder.build_node
+ end
+
+ it "sets the run list to the desired named run list" do
+ expect(policy_builder.run_list).to eq([ "recipe[example1::default]" ])
+ expected_expansion = Chef::PolicyBuilder::Policyfile::RunListExpansionIsh.new([ "example1::default" ], [])
+ expect(policy_builder.run_list_expansion).to eq(expected_expansion)
+ expect(policy_builder.run_list_with_versions_for_display).to eq(["example1::default@2.3.5 (168d210)"])
+ expect(node.run_list).to eq([ Chef::RunList::RunListItem.new("recipe[example1::default]") ])
+ expect(node[:roles]).to eq( [] )
+ expect(node[:recipes]).to eq( ["example1::default"] )
+ end
+
+ it "disables the cookbook cache cleaner" do
+ expect(Chef::CookbookCacheCleaner.instance.skip_removal).to be(true)
+ end
+
+ end
+
+ end
end
@@ -414,9 +619,7 @@ describe Chef::PolicyBuilder::Policyfile do
let(:error404) { Net::HTTPServerException.new("404 message", :body) }
before do
- expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
-
- policy_builder.load_node
+ policy_builder.finish_load_node(node)
policy_builder.build_node
expect(http_api).to receive(:get).with(cookbook1_url).
@@ -433,9 +636,9 @@ describe Chef::PolicyBuilder::Policyfile do
shared_examples_for "fetching cookbooks when they exist" do
context "and the cookbooks can be fetched" do
before do
- expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
+ Chef.reset!
- policy_builder.load_node
+ policy_builder.finish_load_node(node)
policy_builder.build_node
allow(Chef::CookbookSynchronizer).to receive(:new).
@@ -443,6 +646,10 @@ describe Chef::PolicyBuilder::Policyfile do
and_return(cookbook_synchronizer)
end
+ after do
+ Chef.reset!
+ end
+
it "builds a Hash of the form 'cookbook_name' => Chef::CookbookVersion" do
expect(policy_builder.cookbooks_to_sync).to eq(expected_cookbook_hash)
end
@@ -460,6 +667,13 @@ describe Chef::PolicyBuilder::Policyfile do
expect(run_context.cookbook_collection.keys).to match_array(["example1", "example2"])
end
+ it "makes the run context available via static method on Chef" do
+ expect(cookbook_synchronizer).to receive(:sync_cookbooks)
+ expect_any_instance_of(Chef::RunContext).to receive(:load).with(policy_builder.run_list_expansion_ish)
+ run_context = policy_builder.setup_run_context
+ expect(Chef.run_context).to eq(run_context)
+ end
+
end
end # shared_examples_for "fetching cookbooks"
diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb
index 55eaead9ba..dc06cb3326 100644
--- a/spec/unit/property_spec.rb
+++ b/spec/unit/property_spec.rb
@@ -107,6 +107,56 @@ describe "Chef::Resource.property" do
end
end
+ with_property ":x, name_property: true" do
+ context "and subclass" do
+ let(:subresource_class) do
+ new_resource_name = self.class.new_resource_name
+ Class.new(resource_class) do
+ resource_name new_resource_name
+ end
+ end
+ let(:subresource) do
+ subresource_class.new('blah')
+ end
+
+ context "with property :x on the subclass" do
+ before do
+ subresource_class.class_eval do
+ property :x
+ end
+ end
+
+ it "x is still name_property" do
+ expect(subresource.x).to eq 'blah'
+ end
+ end
+
+ context "with property :x, name_attribute: false on the subclass" do
+ before do
+ subresource_class.class_eval do
+ property :x, name_attribute: false
+ end
+ end
+
+ it "x is no longer name_property" do
+ expect(subresource.x).to be_nil
+ end
+ end
+
+ context "with property :x, default: 10 on the subclass" do
+ before do
+ subresource_class.class_eval do
+ property :x, default: 10
+ end
+ end
+
+ it "x is no longer name_property" do
+ expect(subresource.x).to eq(10)
+ end
+ end
+ end
+ end
+
with_property ":x, Integer" do
context "and subclass" do
let(:subresource_class) do
@@ -462,24 +512,22 @@ describe "Chef::Resource.property" do
end
context "hash default" do
- with_property ':x, default: {}' do
- it "when x is not set, it returns {}" do
- expect(resource.x).to eq({})
- end
- it "The same exact value is returned multiple times in a row" do
- value = resource.x
- expect(value).to eq({})
- expect(resource.x.object_id).to eq(value.object_id)
- end
- it "Multiple instances of x receive the exact same value" do
- expect(resource.x.object_id).to eq(resource_class.new('blah2').x.object_id)
- end
- end
+ context "(deprecations allowed)" do
+ before { Chef::Config[:treat_deprecation_warnings_as_errors] = false }
- it "when a property is declared with default: {}, a warning is issued" do
- expect(Chef::Log).to receive(:warn).with(match(/^Property .+\.x has an array or hash default \(\{\}\)\. This means that if one resource modifies or appends to it, all other resources of the same type will also see the changes\. Either freeze the constant with \`\.freeze\` to prevent appending, or use lazy \{ \{\} \}\.$/))
- resource_class.class_eval("property :x, default: {}", __FILE__, __LINE__)
- expect(resource.x).to eq({})
+ with_property ':x, default: {}' do
+ it "when x is not set, it returns {}" do
+ expect(resource.x).to eq({})
+ end
+ it "The same exact value is returned multiple times in a row" do
+ value = resource.x
+ expect(value).to eq({})
+ expect(resource.x.object_id).to eq(value.object_id)
+ end
+ it "Multiple instances of x receive the exact same value" do
+ expect(resource.x.object_id).to eq(resource_class.new('blah2').x.object_id)
+ end
+ end
end
with_property ':x, default: lazy { {} }' do
@@ -873,6 +921,9 @@ describe "Chef::Resource.property" do
expect(resource.x 10).to eq "101"
expect(Namer.current_index).to eq 1
end
+ it "does not emit a deprecation warning if set to nil" do
+ expect(resource.x nil).to eq "1"
+ end
it "coercion sets the value (and coercion does not run on get)" do
expect(resource.x 10).to eq "101"
expect(resource.x).to eq "101"
@@ -885,6 +936,11 @@ describe "Chef::Resource.property" do
expect(Namer.current_index).to eq 2
end
end
+ with_property ':x, coerce: proc { |x| x }' do
+ it "does not emit a deprecation warning if set to nil" do
+ expect(resource.x nil).to be_nil
+ end
+ end
with_property ':x, coerce: proc { |x| Namer.next_index; raise "hi" if x == 10; x }, is: proc { |x| Namer.next_index; x != 10 }' do
it "failed coercion fails to set the value" do
resource.x 20
@@ -948,21 +1004,99 @@ describe "Chef::Resource.property" do
expect(resource.x).to eq 'blah'
end
end
- with_property ":x, default: 10, #{name}: true" do
- it "chooses default over #{name}" do
- expect(resource.x).to eq 10
+
+ with_property ":x, #{name}: false" do
+ it "defaults to nil" do
+ expect(resource.x).to be_nil
end
end
- with_property ":x, #{name}: true, default: 10" do
- it "chooses default over #{name}" do
- expect(resource.x).to eq 10
+
+ with_property ":x, #{name}: nil" do
+ it "defaults to nil" do
+ expect(resource.x).to be_nil
end
end
- with_property ":x, #{name}: true, required: true" do
- it "defaults x to resource.name" do
- expect(resource.x).to eq 'blah'
+
+ context "default ordering deprecation warnings" do
+ it "emits a deprecation warning for property :x, default: 10, #{name}: true" do
+ expect { resource_class.property :x, :default => 10, name.to_sym => true }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
+ /Cannot specify both default and name_property together on property x of resource chef_resource_property_spec_(\d+). Only one \(default\) will be obeyed./
+ end
+ it "emits a deprecation warning for property :x, default: nil, #{name}: true" do
+ expect { resource_class.property :x, :default => nil, name.to_sym => true }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
+ /Cannot specify both default and name_property together on property x of resource chef_resource_property_spec_(\d+). Only one \(name_property\) will be obeyed./
+ end
+ it "emits a deprecation warning for property :x, #{name}: true, default: 10" do
+ expect { resource_class.property :x, name.to_sym => true, :default => 10 }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
+ /Cannot specify both default and name_property together on property x of resource chef_resource_property_spec_(\d+). Only one \(name_property\) will be obeyed./
+ end
+ it "emits a deprecation warning for property :x, #{name}: true, default: nil" do
+ expect { resource_class.property :x, name.to_sym => true, :default => nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError,
+ /Cannot specify both default and name_property together on property x of resource chef_resource_property_spec_(\d+). Only one \(name_property\) will be obeyed./
end
end
+
+ context "default ordering" do
+ before { Chef::Config[:treat_deprecation_warnings_as_errors] = false }
+ with_property ":x, default: 10, #{name}: true" do
+ it "chooses default over #{name}" do
+ expect(resource.x).to eq 10
+ end
+ end
+ with_property ":x, default: nil, #{name}: true" do
+ it "chooses #{name} over default" do
+ expect(resource.x).to eq 'blah'
+ end
+ end
+ with_property ":x, #{name}: true, default: 10" do
+ it "chooses #{name} over default" do
+ expect(resource.x).to eq 'blah'
+ end
+ end
+ with_property ":x, #{name}: true, default: nil" do
+ it "chooses #{name} over default" do
+ expect(resource.x).to eq 'blah'
+ end
+ end
+ end
+
+ context "default ordering when #{name} is nil" do
+ with_property ":x, #{name}: nil, default: 10" do
+ it "chooses default" do
+ expect(resource.x).to eq 10
+ end
+ end
+ with_property ":x, default: 10, #{name}: nil" do
+ it "chooses default" do
+ expect(resource.x).to eq 10
+ end
+ end
+ end
+
+ context "default ordering when #{name} is false" do
+ with_property ":x, #{name}: false, default: 10" do
+ it "chooses default" do
+ expect(resource.x).to eq 10
+ end
+ end
+ with_property ":x, default: 10, #{name}: nil" do
+ it "chooses default" do
+ expect(resource.x).to eq 10
+ end
+ end
+ end
+
end
end
+
+ it "raises an error if both name_property and name_attribute are specified" do
+ expect { resource_class.property :x, :name_property => false, :name_attribute => 1 }.to raise_error ArgumentError,
+ /Cannot specify both name_property and name_attribute together on property x of resource chef_resource_property_spec_(\d+)./
+ expect { resource_class.property :x, :name_property => false, :name_attribute => nil }.to raise_error ArgumentError,
+ /Cannot specify both name_property and name_attribute together on property x of resource chef_resource_property_spec_(\d+)./
+ expect { resource_class.property :x, :name_property => false, :name_attribute => false }.to raise_error ArgumentError,
+ /Cannot specify both name_property and name_attribute together on property x of resource chef_resource_property_spec_(\d+)./
+ expect { resource_class.property :x, :name_property => true, :name_attribute => true }.to raise_error ArgumentError,
+ /Cannot specify both name_property and name_attribute together on property x of resource chef_resource_property_spec_(\d+)./
+ end
end
diff --git a/spec/unit/provider/deploy_spec.rb b/spec/unit/provider/deploy_spec.rb
index f6bb78823f..e6a7125e32 100644
--- a/spec/unit/provider/deploy_spec.rb
+++ b/spec/unit/provider/deploy_spec.rb
@@ -362,7 +362,7 @@ describe Chef::Provider::Deploy do
it "skips the migration when resource.migrate => false but runs symlinks before migration" do
@resource.migrate false
- expect(@provider).not_to receive :run_command
+ expect(@provider).not_to receive :shell_out!
expect(@provider).to receive :run_symlinks_before_migrate
@provider.migrate
end
@@ -378,7 +378,7 @@ describe Chef::Provider::Deploy do
allow(STDOUT).to receive(:tty?).and_return(true)
allow(Chef::Log).to receive(:info?).and_return(true)
- expect(@provider).to receive(:run_command).with(:command => "migration_foo", :cwd => @expected_release_dir,
+ expect(@provider).to receive(:shell_out!).with("migration_foo",:cwd => @expected_release_dir,
:user => "deployNinja", :group => "deployNinjas",
:log_level => :info, :live_stream => STDOUT,
:log_tag => "deploy[/my/deploy/dir]",
@@ -445,13 +445,13 @@ describe Chef::Provider::Deploy do
end
it "does nothing for restart if restart_command is empty" do
- expect(@provider).not_to receive(:run_command)
+ expect(@provider).not_to receive(:shell_out!)
@provider.restart
end
it "runs the restart command in the current application dir when the resource has a restart_command" do
@resource.restart_command "restartcmd"
- expect(@provider).to receive(:run_command).with(:command => "restartcmd", :cwd => "/my/deploy/dir/current", :log_tag => "deploy[/my/deploy/dir]", :log_level => :debug)
+ expect(@provider).to receive(:shell_out!).with("restartcmd", :cwd => "/my/deploy/dir/current", :log_tag => "deploy[/my/deploy/dir]", :log_level => :debug)
@provider.restart
end
@@ -509,7 +509,7 @@ describe Chef::Provider::Deploy do
it "shouldn't give a no method error on migrate if the environment is nil" do
allow(@provider).to receive(:enforce_ownership)
allow(@provider).to receive(:run_symlinks_before_migrate)
- allow(@provider).to receive(:run_command)
+ allow(@provider).to receive(:shell_out!)
@provider.migrate
end
diff --git a/spec/unit/provider/dsc_resource_spec.rb b/spec/unit/provider/dsc_resource_spec.rb
index 65c1c019f0..9946ab8410 100644
--- a/spec/unit/provider/dsc_resource_spec.rb
+++ b/spec/unit/provider/dsc_resource_spec.rb
@@ -50,30 +50,23 @@ describe Chef::Provider::DscResource do
}
context 'when RefreshMode is not set to Disabled' do
- let (:meta_configuration) { {'RefreshMode' => 'AnythingElse'}}
-
it 'raises an exception' do
- expect(provider).to receive(:meta_configuration).and_return(
- meta_configuration)
+ expect(provider).to receive(:dsc_refresh_mode_disabled?).and_return(false)
expect { provider.run_action(:run) }.to raise_error(
Chef::Exceptions::ProviderNotFound, /Disabled/)
end
end
context 'when RefreshMode is set to Disabled' do
- let (:meta_configuration) { {'RefreshMode' => 'Disabled'}}
-
it 'does not update the resource if it is up to date' do
- expect(provider).to receive(:meta_configuration).and_return(
- meta_configuration)
+ expect(provider).to receive(:dsc_refresh_mode_disabled?).and_return(true)
expect(provider).to receive(:test_resource).and_return(true)
provider.run_action(:run)
expect(resource).not_to be_updated
end
it 'converges the resource if it is not up to date' do
- expect(provider).to receive(:meta_configuration).and_return(
- meta_configuration)
+ expect(provider).to receive(:dsc_refresh_mode_disabled?).and_return(true)
expect(provider).to receive(:test_resource).and_return(false)
expect(provider).to receive(:set_resource)
provider.run_action(:run)
diff --git a/spec/unit/provider/package/rpm_spec.rb b/spec/unit/provider/package/rpm_spec.rb
index e0e45d0b4f..ad9d694e34 100644
--- a/spec/unit/provider/package/rpm_spec.rb
+++ b/spec/unit/provider/package/rpm_spec.rb
@@ -256,6 +256,24 @@ describe Chef::Provider::Package::Rpm do
end
end
+ context "when the package name contains a plus symbol (chef#3671)" do
+
+ let(:package_name) { "chef-server-core" }
+
+ let(:package_source) { "/tmp/chef-server-core-12.2.0+20150713220422-1.el6.x86_64.rpm" }
+
+ let(:rpm_qp_stdout) { "chef-server-core 12.2.0+20150713220422-1.el6" }
+ let(:rpm_q_stdout) { "chef-server-core 12.2.0+20150713220422-1.el6" }
+
+ let(:rpm_qp_exitstatus) { 0 }
+ let(:rpm_q_exitstatus) { 0 }
+
+ it "should correctly determine the candidate version and installed version" do
+ expect(provider.current_resource.package_name).to eq("chef-server-core")
+ expect(provider.new_resource.version).to eq("12.2.0+20150713220422-1.el6")
+ end
+ end
+
end
context "when the source is given as an URI" do
@@ -413,4 +431,3 @@ describe Chef::Provider::Package::Rpm do
end
-
diff --git a/spec/unit/provider/powershell_script_spec.rb b/spec/unit/provider/powershell_script_spec.rb
index 855c18af9b..121973763d 100644
--- a/spec/unit/provider/powershell_script_spec.rb
+++ b/spec/unit/provider/powershell_script_spec.rb
@@ -38,41 +38,67 @@ describe Chef::Provider::PowershellScript, "action_run" do
}
context 'when setting interpreter flags' do
- it "should set the -File flag as the last flag" do
- expect(provider.flags.split(' ').pop).to eq("-File")
+ context 'on nano' do
+ before(:each) 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")
+ end
+
+ it "sets the -Command flag as the last flag" do
+ flags = provider.command.split(' ').keep_if { |flag| flag =~ /^-/ }
+ expect(flags.pop).to eq("-Command")
+ end
end
- let(:execution_policy_flag) do
- execution_policy_index = 0
- provider_flags = provider.flags.split(' ')
- execution_policy_specified = false
+ context 'not on nano' do
+ before(:each) 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")
+ end
- provider_flags.find do | value |
- execution_policy_index += 1
- execution_policy_specified = value.downcase == '-ExecutionPolicy'.downcase
+ it "sets the -File flag as the last flag" do
+ flags = provider.command.split(' ').keep_if { |flag| flag =~ /^-/ }
+ expect(flags.pop).to eq("-File")
end
- execution_policy = execution_policy_specified ? provider_flags[execution_policy_index] : nil
- end
+ let(:execution_policy_flag) do
+ execution_policy_index = 0
+ provider_flags = provider.flags.split(' ')
+ execution_policy_specified = false
- context 'when running with an unspecified PowerShell version' do
- let(:powershell_version) { nil }
- it "should set the -ExecutionPolicy flag to 'Unrestricted' by default" do
- expect(execution_policy_flag.downcase).to eq('unrestricted'.downcase)
+ provider_flags.find do | value |
+ execution_policy_index += 1
+ execution_policy_specified = value.downcase == '-ExecutionPolicy'.downcase
+ end
+
+ execution_policy = execution_policy_specified ? provider_flags[execution_policy_index] : nil
+ end
+
+ context 'when running with an unspecified PowerShell version' do
+ let(:powershell_version) { nil }
+ it "sets the -ExecutionPolicy flag to 'Unrestricted' by default" do
+ expect(execution_policy_flag.downcase).to eq('unrestricted'.downcase)
+ end
end
- end
- { '2.0' => 'Unrestricted',
- '2.5' => 'Unrestricted',
- '3.0' => 'Bypass',
- '3.6' => 'Bypass',
- '4.0' => 'Bypass',
- '5.0' => 'Bypass' }.each do | version_policy |
- let(:powershell_version) { version_policy[0].to_f }
- context "when running PowerShell version #{version_policy[0]}" do
+ { '2.0' => 'Unrestricted',
+ '2.5' => 'Unrestricted',
+ '3.0' => 'Bypass',
+ '3.6' => 'Bypass',
+ '4.0' => 'Bypass',
+ '5.0' => 'Bypass' }.each do | version_policy |
let(:powershell_version) { version_policy[0].to_f }
- it "should set the -ExecutionPolicy flag to '#{version_policy[1]}'" do
- expect(execution_policy_flag.downcase).to eq(version_policy[1].downcase)
+ context "when running PowerShell version #{version_policy[0]}" do
+ let(:powershell_version) { version_policy[0].to_f }
+ it "sets the -ExecutionPolicy flag to '#{version_policy[1]}'" do
+ expect(execution_policy_flag.downcase).to eq(version_policy[1].downcase)
+ end
end
end
end
diff --git a/spec/unit/provider/remote_directory_spec.rb b/spec/unit/provider/remote_directory_spec.rb
index 99e2fe285c..6426dafd79 100644
--- a/spec/unit/provider/remote_directory_spec.rb
+++ b/spec/unit/provider/remote_directory_spec.rb
@@ -79,7 +79,7 @@ describe Chef::Provider::RemoteDirectory do
end
it "configures access control on intermediate directorys" do
- directory_resource = @provider.send(:resource_for_directory, File.join(Dir.tmpdir, "intermediate_dir"))
+ directory_resource = @provider.send(:directory_resource, File.join(Dir.tmpdir, "intermediate_dir"))
expect(directory_resource.path).to eq(File.join(Dir.tmpdir, "intermediate_dir"))
expect(directory_resource.mode).to eq("0750")
expect(directory_resource.group).to eq("wheel")
@@ -219,4 +219,3 @@ describe Chef::Provider::RemoteDirectory do
end
end
-
diff --git a/spec/unit/provider/service/solaris_smf_service_spec.rb b/spec/unit/provider/service/solaris_smf_service_spec.rb
index 2039408914..62c3ac6c6e 100644
--- a/spec/unit/provider/service/solaris_smf_service_spec.rb
+++ b/spec/unit/provider/service/solaris_smf_service_spec.rb
@@ -31,66 +31,126 @@ describe Chef::Provider::Service::Solaris do
@provider = Chef::Provider::Service::Solaris.new(@new_resource, @run_context)
allow(Chef::Resource::Service).to receive(:new).and_return(@current_resource)
- @stdin = StringIO.new
- @stdout = StringIO.new
- @stderr = StringIO.new
- @pid = 2342
- @stdout_string = "state disabled"
- allow(@stdout).to receive(:gets).and_return(@stdout_string)
- @status = double("Status", :exitstatus => 0, :stdout => @stdout)
- allow(@provider).to receive(:shell_out!).and_return(@status)
+ # enabled / started service (svcs -l chef)
+ enabled_svc_stdout = [
+ 'fmri svc:/application/chef:default',
+ 'name chef service',
+ 'enabled true',
+ 'state online',
+ 'next_state none',
+ 'state_time April 2, 2015 04:25:19 PM EDT',
+ 'logfile /var/svc/log/application-chef:default.log',
+ 'restarter svc:/system/svc/restarter:default',
+ 'contract_id 1115271',
+ 'dependency require_all/error svc:/milestone/multi-user:default (online)'
+ ].join("\n")
+
+ # disabled / stopped service (svcs -l chef)
+ disabled_svc_stdout = [
+ 'fmri svc:/application/chef:default',
+ 'name chef service',
+ 'enabled false',
+ 'state disabled',
+ 'next_state none',
+ 'state_time April 2, 2015 04:25:19 PM EDT',
+ 'logfile /var/svc/log/application-chef:default.log',
+ 'restarter svc:/system/svc/restarter:default',
+ 'contract_id 1115271',
+ 'dependency require_all/error svc:/milestone/multi-user:default (online)'
+ ].join("\n")
+
+ # disabled / stopped service (svcs -l chef)
+ maintenance_svc_stdout = [
+ 'fmri svc:/application/chef:default',
+ 'name chef service',
+ 'enabled true',
+ 'state maintenance',
+ 'next_state none',
+ 'state_time April 2, 2015 04:25:19 PM EDT',
+ 'logfile /var/svc/log/application-chef:default.log',
+ 'restarter svc:/system/svc/restarter:default',
+ 'contract_id 1115271',
+ 'dependency require_all/error svc:/milestone/multi-user:default (online)'
+ ].join("\n")
+
+ # shell_out! return value for a service that is running
+ @enabled_svc_status = double("Status", :exitstatus => 0, :stdout => enabled_svc_stdout, :stdin => '', :stderr => '')
+
+ # shell_out! return value for a service that is disabled
+ @disabled_svc_status = double("Status", :exitstatus => 0, :stdout => disabled_svc_stdout, :stdin => '', :stderr => '')
+
+ # shell_out! return value for a service that is in maintenance mode
+ @maintenance_svc_status = double("Status", :exitstatus => 0, :stdout => maintenance_svc_stdout, :stdin => '', :stderr => '')
+
+ # shell_out! return value for a service that does not exist
+ @no_svc_status = double("Status", :exitstatus => 1, :stdout => '', :stdin => '', :stderr => "svcs: Pattern 'chef' doesn't match any instances\n")
+
+ # shell_out! return value for a successful execution
+ @success = double("clear", :exitstatus => 0, :stdout => '', :stdin => '', :stderr => '')
end
- it "should raise an error if /bin/svcs does not exist" do
- expect(File).to receive(:exists?).with("/bin/svcs").and_return(false)
+ it "should raise an error if /bin/svcs and /usr/sbin/svcadm are not executable" do
+ allow(File).to receive(:executable?).with("/bin/svcs").and_return(false)
+ allow(File).to receive(:executable?).with("/usr/sbin/svcadm").and_return(false)
expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Service)
end
- describe "on a host with /bin/svcs" do
+ it "should raise an error if /bin/svcs is not executable" do
+ allow(File).to receive(:executable?).with("/bin/svcs").and_return(false)
+ allow(File).to receive(:executable?).with("/usr/sbin/svcadm").and_return(true)
+ expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Service)
+ end
+
+ it "should raise an error if /usr/sbin/svcadm is not executable" do
+ allow(File).to receive(:executable?).with("/bin/svcs").and_return(true)
+ allow(File).to receive(:executable?).with("/usr/sbin/svcadm").and_return(false)
+ expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Service)
+ end
+
+ describe "on a host with /bin/svcs and /usr/sbin/svcadm" do
before do
- allow(File).to receive(:exists?).with('/bin/svcs').and_return(true)
+ allow(File).to receive(:executable?).with("/bin/svcs").and_return(true)
+ allow(File).to receive(:executable?).with("/usr/sbin/svcadm").and_return(true)
end
describe "when discovering the current service state" do
it "should create a current resource with the name of the new resource" do
- allow(@provider).to receive(:shell_out!).with("/bin/svcs -l chef").and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", {:returns=>[0, 1]}).and_return(@enabled_svc_status)
expect(Chef::Resource::Service).to receive(:new).and_return(@current_resource)
@provider.load_current_resource
end
it "should return the current resource" do
- allow(@provider).to receive(:shell_out!).with("/bin/svcs -l chef").and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", {:returns=>[0, 1]}).and_return(@enabled_svc_status)
expect(@provider.load_current_resource).to eql(@current_resource)
end
it "should call '/bin/svcs -l service_name'" do
- expect(@provider).to receive(:shell_out!).with("/bin/svcs -l chef", {:returns=>[0, 1]}).and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", {:returns=>[0, 1]}).and_return(@enabled_svc_status)
@provider.load_current_resource
end
it "should mark service as not running" do
- allow(@provider).to receive(:shell_out!).and_return(@status)
+ expect(@provider).to receive(:shell_out!).and_return(@disabled_svc_status)
expect(@current_resource).to receive(:running).with(false)
@provider.load_current_resource
end
it "should mark service as running" do
- @status = double("Status", :exitstatus => 0, :stdout => 'state online')
- allow(@provider).to receive(:shell_out!).and_return(@status)
+ expect(@provider).to receive(:shell_out!).and_return(@enabled_svc_status)
expect(@current_resource).to receive(:running).with(true)
@provider.load_current_resource
end
it "should not mark service as maintenance" do
- allow(@provider).to receive(:shell_out!).and_return(@status)
+ expect(@provider).to receive(:shell_out!).and_return(@enabled_svc_status)
@provider.load_current_resource
expect(@provider.maintenance).to be_falsey
end
it "should mark service as maintenance" do
- @status = double("Status", :exitstatus => 0, :stdout => 'state maintenance')
- allow(@provider).to receive(:shell_out!).and_return(@status)
+ expect(@provider).to receive(:shell_out!).and_return(@maintenance_svc_status)
@provider.load_current_resource
expect(@provider.maintenance).to be_truthy
end
@@ -99,30 +159,41 @@ describe Chef::Provider::Service::Solaris do
describe "when enabling the service" do
before(:each) do
@provider.current_resource = @current_resource
- @current_resource.enabled(true)
end
it "should call svcadm enable -s chef" do
- expect(@provider).not_to receive(:shell_out!).with("/usr/sbin/svcadm clear #{@current_resource.service_name}")
- expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm enable -s #{@current_resource.service_name}").and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", {:returns=>[0, 1]}).and_return(@enabled_svc_status)
+ expect(@provider).not_to receive(:shell_out!).with("/usr/sbin/svcadm", "clear", @current_resource.service_name)
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "enable", "-s", @current_resource.service_name).and_return(@success)
+ @provider.load_current_resource
+
expect(@provider.enable_service).to be_truthy
expect(@current_resource.enabled).to be_truthy
end
it "should call svcadm enable -s chef for start_service" do
- expect(@provider).not_to receive(:shell_out!).with("/usr/sbin/svcadm clear #{@current_resource.service_name}")
- expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm enable -s #{@current_resource.service_name}").and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", {:returns=>[0, 1]}).and_return(@enabled_svc_status)
+ expect(@provider).not_to receive(:shell_out!).with("/usr/sbin/svcadm", "clear", @current_resource.service_name)
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "enable", "-s", @current_resource.service_name).and_return(@success)
+ @provider.load_current_resource
expect(@provider.start_service).to be_truthy
expect(@current_resource.enabled).to be_truthy
end
it "should call svcadm clear chef for start_service when state maintenance" do
- @status = double("Status", :exitstatus => 0, :stdout => 'state maintenance')
- allow(@provider).to receive(:shell_out!).and_return(@status)
+ # we are in maint mode
+ expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", {:returns=>[0, 1]}).and_return(@maintenance_svc_status)
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "clear", @current_resource.service_name).and_return(@success)
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "enable", "-s", @current_resource.service_name).and_return(@success)
+
+ # load the resource, then enable it
@provider.load_current_resource
- expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm clear #{@current_resource.service_name}").and_return(@status)
- expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm enable -s #{@current_resource.service_name}").and_return(@status)
expect(@provider.enable_service).to be_truthy
+
+ # now we are enabled
+ expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", {:returns=>[0, 1]}).and_return(@enabled_svc_status)
+ @provider.load_current_resource
+
expect(@current_resource.enabled).to be_truthy
end
end
@@ -130,17 +201,20 @@ describe Chef::Provider::Service::Solaris do
describe "when disabling the service" do
before(:each) do
@provider.current_resource = @current_resource
- @current_resource.enabled(false)
end
it "should call svcadm disable -s chef" do
- expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm disable -s chef").and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", {:returns=>[0, 1]}).and_return(@disabled_svc_status)
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "disable", "-s", "chef").and_return(@success)
+ @provider.load_current_resource
expect(@provider.disable_service).to be_truthy
expect(@current_resource.enabled).to be_falsey
end
it "should call svcadm disable -s chef for stop_service" do
- expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm disable -s chef").and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", {:returns=>[0, 1]}).and_return(@disabled_svc_status)
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "disable", "-s", "chef").and_return(@success)
+ @provider.load_current_resource
expect(@provider.stop_service).to be_truthy
expect(@current_resource.enabled).to be_falsey
end
@@ -149,12 +223,12 @@ describe Chef::Provider::Service::Solaris do
describe "when reloading the service" do
before(:each) do
- @status = double("Process::Status", :exitstatus => 0)
@provider.current_resource = @current_resource
+ allow(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", {:returns=>[0, 1]}).and_return(@enabled_svc_status)
end
it "should call svcadm refresh chef" do
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/usr/sbin/svcadm refresh chef").and_return(@status)
+ expect(@provider).to receive(:shell_out!).with("/usr/sbin/svcadm", "refresh", "chef")
@provider.reload_service
end
@@ -162,19 +236,16 @@ describe Chef::Provider::Service::Solaris do
describe "when the service doesn't exist" do
before(:each) do
- @stdout_string = ""
- @status = double("Status", :exitstatus => 1, :stdout => @stdout)
@provider.current_resource = @current_resource
+ expect(@provider).to receive(:shell_out!).with("/bin/svcs", "-l", "chef", {:returns=>[0, 1]}).and_return(@no_svc_status)
end
it "should be marked not running" do
- expect(@provider).to receive(:shell_out!).with("/bin/svcs -l chef", {:returns=>[0, 1]}).and_return(@status)
@provider.service_status
expect(@current_resource.running).to be_falsey
end
it "should be marked not enabled" do
- expect(@provider).to receive(:shell_out!).with("/bin/svcs -l chef", {:returns=>[0, 1]}).and_return(@status)
@provider.service_status
expect(@current_resource.enabled).to be_falsey
end
diff --git a/spec/unit/provider/service/windows_spec.rb b/spec/unit/provider/service/windows_spec.rb
index 784a2232b2..4c9f5b3377 100644
--- a/spec/unit/provider/service/windows_spec.rb
+++ b/spec/unit/provider/service/windows_spec.rb
@@ -21,369 +21,380 @@ require 'spec_helper'
require 'mixlib/shellout'
describe Chef::Provider::Service::Windows, "load_current_resource" do
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::WindowsService.new("chef")
- @provider = Chef::Provider::Service::Windows.new(@new_resource, @run_context)
- @provider.current_resource = Chef::Resource::WindowsService.new("current-chef")
- Object.send(:remove_const, 'Win32') if defined?(Win32)
- Win32 = Module.new
+ include_context "Win32"
+
+ let(:new_resource) { Chef::Resource::WindowsService.new("chef") }
+ let(:provider) do
+ prvdr = Chef::Provider::Service::Windows.new(new_resource,
+ Chef::RunContext.new(Chef::Node.new, {}, Chef::EventDispatch::Dispatcher.new))
+ prvdr.current_resource = Chef::Resource::WindowsService.new("current-chef")
+ prvdr
+ end
+
+ before(:all) do
Win32::Service = Class.new
+ end
+
+ before(:each) do
Win32::Service::AUTO_START = 0x00000002
Win32::Service::DEMAND_START = 0x00000003
Win32::Service::DISABLED = 0x00000004
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "running"))
- allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return(
+ allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
double("ConfigStruct", :start_type => "auto start"))
allow(Win32::Service).to receive(:exists?).and_return(true)
allow(Win32::Service).to receive(:configure).and_return(Win32::Service)
end
- it "should set the current resources service name to the new resources service name" do
- @provider.load_current_resource
- expect(@provider.current_resource.service_name).to eq('chef')
+ after(:each) do
+ Win32::Service.send(:remove_const, 'AUTO_START') if defined?(Win32::Service::AUTO_START)
+ Win32::Service.send(:remove_const, 'DEMAND_START') if defined?(Win32::Service::DEMAND_START)
+ Win32::Service.send(:remove_const, 'DISABLED') if defined?(Win32::Service::DISABLED)
end
- it "should return the current resource" do
- expect(@provider.load_current_resource).to equal(@provider.current_resource)
+ it "sets the current resources service name to the new resources service name" do
+ provider.load_current_resource
+ expect(provider.current_resource.service_name).to eq('chef')
end
- it "should set the current resources status" do
- @provider.load_current_resource
- expect(@provider.current_resource.running).to be_truthy
+ it "returns the current resource" do
+ expect(provider.load_current_resource).to equal(provider.current_resource)
end
- it "should set the current resources start type" do
- @provider.load_current_resource
- expect(@provider.current_resource.enabled).to be_truthy
+ it "sets the current resources status" do
+ provider.load_current_resource
+ expect(provider.current_resource.running).to be_truthy
+ end
+
+ it "sets the current resources start type" do
+ provider.load_current_resource
+ expect(provider.current_resource.enabled).to be_truthy
end
it "does not set the current resources start type if it is neither AUTO START or DISABLED" do
- allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return(
+ allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
double("ConfigStruct", :start_type => "manual"))
- @provider.load_current_resource
- expect(@provider.current_resource.enabled).to be_nil
+ provider.load_current_resource
+ expect(provider.current_resource.enabled).to be_nil
end
describe Chef::Provider::Service::Windows, "start_service" do
before(:each) do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "stopped"),
double("StatusStruct", :current_state => "running"))
end
- it "should call the start command if one is specified" do
- @new_resource.start_command "sc start chef"
- expect(@provider).to receive(:shell_out!).with("#{@new_resource.start_command}").and_return("Starting custom service")
- @provider.start_service
- expect(@new_resource.updated_by_last_action?).to be_truthy
+ it "calls the start command if one is specified" do
+ new_resource.start_command "sc start chef"
+ expect(provider).to receive(:shell_out!).with("#{new_resource.start_command}").and_return("Starting custom service")
+ provider.start_service
+ expect(new_resource.updated_by_last_action?).to be_truthy
end
- it "should use the built-in command if no start command is specified" do
- expect(Win32::Service).to receive(:start).with(@new_resource.service_name)
- @provider.start_service
- expect(@new_resource.updated_by_last_action?).to be_truthy
+ it "uses the built-in command if no start command is specified" do
+ expect(Win32::Service).to receive(:start).with(new_resource.service_name)
+ provider.start_service
+ expect(new_resource.updated_by_last_action?).to be_truthy
end
- it "should do nothing if the service does not exist" do
- allow(Win32::Service).to receive(:exists?).with(@new_resource.service_name).and_return(false)
- expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name)
- @provider.start_service
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ it "does nothing if the service does not exist" do
+ allow(Win32::Service).to receive(:exists?).with(new_resource.service_name).and_return(false)
+ expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
+ provider.start_service
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
- it "should do nothing if the service is running" do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ it "does nothing if the service is running" do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "running"))
- @provider.load_current_resource
- expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name)
- @provider.start_service
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ provider.load_current_resource
+ expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
+ provider.start_service
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
- it "should raise an error if the service is paused" do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ it "raises an error if the service is paused" do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "paused"))
- @provider.load_current_resource
- expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name)
- expect { @provider.start_service }.to raise_error( Chef::Exceptions::Service )
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ provider.load_current_resource
+ expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
+ expect { provider.start_service }.to raise_error( Chef::Exceptions::Service )
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
- it "should wait and continue if the service is in start_pending" do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ it "waits and continues if the service is in start_pending" do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "start pending"),
double("StatusStruct", :current_state => "start pending"),
double("StatusStruct", :current_state => "running"))
- @provider.load_current_resource
- expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name)
- @provider.start_service
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ provider.load_current_resource
+ expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
+ provider.start_service
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
- it "should fail if the service is in stop_pending" do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ it "fails if the service is in stop_pending" do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "stop pending"))
- @provider.load_current_resource
- expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name)
- expect { @provider.start_service }.to raise_error( Chef::Exceptions::Service )
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ provider.load_current_resource
+ expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
+ expect { provider.start_service }.to raise_error( Chef::Exceptions::Service )
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
describe "running as a different account" do
- let(:old_run_as_user) { @new_resource.run_as_user }
- let(:old_run_as_password) { @new_resource.run_as_password }
+ let(:old_run_as_user) { new_resource.run_as_user }
+ let(:old_run_as_password) { new_resource.run_as_password }
before {
- @new_resource.run_as_user(".\\wallace")
- @new_resource.run_as_password("Wensleydale")
+ new_resource.run_as_user(".\\wallace")
+ new_resource.run_as_password("Wensleydale")
}
after {
- @new_resource.run_as_user(old_run_as_user)
- @new_resource.run_as_password(old_run_as_password)
+ new_resource.run_as_user(old_run_as_user)
+ new_resource.run_as_password(old_run_as_password)
}
- it "should call #grant_service_logon if the :run_as_user and :run_as_password attributes are present" do
+ it "calls #grant_service_logon if the :run_as_user and :run_as_password attributes are present" do
expect(Win32::Service).to receive(:start)
- expect(@provider).to receive(:grant_service_logon).and_return(true)
- @provider.start_service
+ expect(provider).to receive(:grant_service_logon).and_return(true)
+ provider.start_service
end
end
end
-
describe Chef::Provider::Service::Windows, "stop_service" do
before(:each) do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "running"),
double("StatusStruct", :current_state => "stopped"))
end
- it "should call the stop command if one is specified" do
- @new_resource.stop_command "sc stop chef"
- expect(@provider).to receive(:shell_out!).with("#{@new_resource.stop_command}").and_return("Stopping custom service")
- @provider.stop_service
- expect(@new_resource.updated_by_last_action?).to be_truthy
+ it "calls the stop command if one is specified" do
+ new_resource.stop_command "sc stop chef"
+ expect(provider).to receive(:shell_out!).with("#{new_resource.stop_command}").and_return("Stopping custom service")
+ provider.stop_service
+ expect(new_resource.updated_by_last_action?).to be_truthy
end
- it "should use the built-in command if no stop command is specified" do
- expect(Win32::Service).to receive(:stop).with(@new_resource.service_name)
- @provider.stop_service
- expect(@new_resource.updated_by_last_action?).to be_truthy
+ it "uses the built-in command if no stop command is specified" do
+ expect(Win32::Service).to receive(:stop).with(new_resource.service_name)
+ provider.stop_service
+ expect(new_resource.updated_by_last_action?).to be_truthy
end
- it "should do nothing if the service does not exist" do
- allow(Win32::Service).to receive(:exists?).with(@new_resource.service_name).and_return(false)
- expect(Win32::Service).not_to receive(:stop).with(@new_resource.service_name)
- @provider.stop_service
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ it "does nothing if the service does not exist" do
+ allow(Win32::Service).to receive(:exists?).with(new_resource.service_name).and_return(false)
+ expect(Win32::Service).not_to receive(:stop).with(new_resource.service_name)
+ provider.stop_service
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
- it "should do nothing if the service is stopped" do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ it "does nothing if the service is stopped" do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "stopped"))
- @provider.load_current_resource
- expect(Win32::Service).not_to receive(:stop).with(@new_resource.service_name)
- @provider.stop_service
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ provider.load_current_resource
+ expect(Win32::Service).not_to receive(:stop).with(new_resource.service_name)
+ provider.stop_service
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
- it "should raise an error if the service is paused" do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ it "raises an error if the service is paused" do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "paused"))
- @provider.load_current_resource
- expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name)
- expect { @provider.stop_service }.to raise_error( Chef::Exceptions::Service )
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ provider.load_current_resource
+ expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
+ expect { provider.stop_service }.to raise_error( Chef::Exceptions::Service )
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
- it "should wait and continue if the service is in stop_pending" do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ it "waits and continue if the service is in stop_pending" do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "stop pending"),
double("StatusStruct", :current_state => "stop pending"),
double("StatusStruct", :current_state => "stopped"))
- @provider.load_current_resource
- expect(Win32::Service).not_to receive(:stop).with(@new_resource.service_name)
- @provider.stop_service
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ provider.load_current_resource
+ expect(Win32::Service).not_to receive(:stop).with(new_resource.service_name)
+ provider.stop_service
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
- it "should fail if the service is in start_pending" do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ it "fails if the service is in start_pending" do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "start pending"))
- @provider.load_current_resource
- expect(Win32::Service).not_to receive(:stop).with(@new_resource.service_name)
- expect { @provider.stop_service }.to raise_error( Chef::Exceptions::Service )
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ provider.load_current_resource
+ expect(Win32::Service).not_to receive(:stop).with(new_resource.service_name)
+ expect { provider.stop_service }.to raise_error( Chef::Exceptions::Service )
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
- it "should pass custom timeout to the stop command if provided" do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ it "passes custom timeout to the stop command if provided" do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "running"))
- @new_resource.timeout 1
- expect(Win32::Service).to receive(:stop).with(@new_resource.service_name)
+ new_resource.timeout 1
+ expect(Win32::Service).to receive(:stop).with(new_resource.service_name)
Timeout.timeout(2) do
- expect { @provider.stop_service }.to raise_error(Timeout::Error)
+ expect { provider.stop_service }.to raise_error(Timeout::Error)
end
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
end
describe Chef::Provider::Service::Windows, "restart_service" do
- it "should call the restart command if one is specified" do
- @new_resource.restart_command "sc restart"
- expect(@provider).to receive(:shell_out!).with("#{@new_resource.restart_command}")
- @provider.restart_service
- expect(@new_resource.updated_by_last_action?).to be_truthy
+ it "calls the restart command if one is specified" do
+ new_resource.restart_command "sc restart"
+ expect(provider).to receive(:shell_out!).with("#{new_resource.restart_command}")
+ provider.restart_service
+ expect(new_resource.updated_by_last_action?).to be_truthy
end
- it "should stop then start the service if it is running" do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ it "stops then starts the service if it is running" do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "running"),
double("StatusStruct", :current_state => "stopped"),
double("StatusStruct", :current_state => "stopped"),
double("StatusStruct", :current_state => "running"))
- expect(Win32::Service).to receive(:stop).with(@new_resource.service_name)
- expect(Win32::Service).to receive(:start).with(@new_resource.service_name)
- @provider.restart_service
- expect(@new_resource.updated_by_last_action?).to be_truthy
+ expect(Win32::Service).to receive(:stop).with(new_resource.service_name)
+ expect(Win32::Service).to receive(:start).with(new_resource.service_name)
+ provider.restart_service
+ expect(new_resource.updated_by_last_action?).to be_truthy
end
- it "should just start the service if it is stopped" do
- allow(Win32::Service).to receive(:status).with(@new_resource.service_name).and_return(
+ it "just starts the service if it is stopped" do
+ allow(Win32::Service).to receive(:status).with(new_resource.service_name).and_return(
double("StatusStruct", :current_state => "stopped"),
double("StatusStruct", :current_state => "stopped"),
double("StatusStruct", :current_state => "running"))
- expect(Win32::Service).to receive(:start).with(@new_resource.service_name)
- @provider.restart_service
- expect(@new_resource.updated_by_last_action?).to be_truthy
+ expect(Win32::Service).to receive(:start).with(new_resource.service_name)
+ provider.restart_service
+ expect(new_resource.updated_by_last_action?).to be_truthy
end
- it "should do nothing if the service does not exist" do
- allow(Win32::Service).to receive(:exists?).with(@new_resource.service_name).and_return(false)
- expect(Win32::Service).not_to receive(:stop).with(@new_resource.service_name)
- expect(Win32::Service).not_to receive(:start).with(@new_resource.service_name)
- @provider.restart_service
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ it "does nothing if the service does not exist" do
+ allow(Win32::Service).to receive(:exists?).with(new_resource.service_name).and_return(false)
+ expect(Win32::Service).not_to receive(:stop).with(new_resource.service_name)
+ expect(Win32::Service).not_to receive(:start).with(new_resource.service_name)
+ provider.restart_service
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
end
describe Chef::Provider::Service::Windows, "enable_service" do
before(:each) do
- allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return(
+ allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
double("ConfigStruct", :start_type => "disabled"))
end
- it "should enable service" do
- expect(Win32::Service).to receive(:configure).with(:service_name => @new_resource.service_name, :start_type => Win32::Service::AUTO_START)
- @provider.enable_service
- expect(@new_resource.updated_by_last_action?).to be_truthy
+ it "enables service" do
+ expect(Win32::Service).to receive(:configure).with(:service_name => new_resource.service_name, :start_type => Win32::Service::AUTO_START)
+ provider.enable_service
+ expect(new_resource.updated_by_last_action?).to be_truthy
end
- it "should do nothing if the service does not exist" do
- allow(Win32::Service).to receive(:exists?).with(@new_resource.service_name).and_return(false)
+ it "does nothing if the service does not exist" do
+ allow(Win32::Service).to receive(:exists?).with(new_resource.service_name).and_return(false)
expect(Win32::Service).not_to receive(:configure)
- @provider.enable_service
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ provider.enable_service
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
end
describe Chef::Provider::Service::Windows, "action_enable" do
it "does nothing if the service is enabled" do
- allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return(
+ allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
double("ConfigStruct", :start_type => "auto start"))
- expect(@provider).not_to receive(:enable_service)
- @provider.action_enable
+ expect(provider).not_to receive(:enable_service)
+ provider.action_enable
end
it "enables the service if it is not set to automatic start" do
- allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return(
+ allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
double("ConfigStruct", :start_type => "disabled"))
- expect(@provider).to receive(:enable_service)
- @provider.action_enable
+ expect(provider).to receive(:enable_service)
+ provider.action_enable
end
end
describe Chef::Provider::Service::Windows, "action_disable" do
it "does nothing if the service is disabled" do
- allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return(
+ allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
double("ConfigStruct", :start_type => "disabled"))
- expect(@provider).not_to receive(:disable_service)
- @provider.action_disable
+ expect(provider).not_to receive(:disable_service)
+ provider.action_disable
end
it "disables the service if it is not set to disabled" do
- allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return(
+ allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
double("ConfigStruct", :start_type => "auto start"))
- expect(@provider).to receive(:disable_service)
- @provider.action_disable
+ expect(provider).to receive(:disable_service)
+ provider.action_disable
end
end
describe Chef::Provider::Service::Windows, "disable_service" do
before(:each) do
- allow(Win32::Service).to receive(:config_info).with(@new_resource.service_name).and_return(
+ allow(Win32::Service).to receive(:config_info).with(new_resource.service_name).and_return(
double("ConfigStruct", :start_type => "auto start"))
end
- it "should disable service" do
+ it "disables service" do
expect(Win32::Service).to receive(:configure)
- @provider.disable_service
- expect(@new_resource.updated_by_last_action?).to be_truthy
+ provider.disable_service
+ expect(new_resource.updated_by_last_action?).to be_truthy
end
- it "should do nothing if the service does not exist" do
- allow(Win32::Service).to receive(:exists?).with(@new_resource.service_name).and_return(false)
+ it "does nothing if the service does not exist" do
+ allow(Win32::Service).to receive(:exists?).with(new_resource.service_name).and_return(false)
expect(Win32::Service).not_to receive(:configure)
- @provider.disable_service
- expect(@new_resource.updated_by_last_action?).to be_falsey
+ provider.disable_service
+ expect(new_resource.updated_by_last_action?).to be_falsey
end
end
describe Chef::Provider::Service::Windows, "action_configure_startup" do
{ :automatic => "auto start", :manual => "demand start", :disabled => "disabled" }.each do |type,win32|
it "sets the startup type to #{type} if it is something else" do
- @new_resource.startup_type(type)
- allow(@provider).to receive(:current_start_type).and_return("fire")
- expect(@provider).to receive(:set_startup_type).with(type)
- @provider.action_configure_startup
+ new_resource.startup_type(type)
+ allow(provider).to receive(:current_start_type).and_return("fire")
+ expect(provider).to receive(:set_startup_type).with(type)
+ provider.action_configure_startup
end
it "leaves the startup type as #{type} if it is already set" do
- @new_resource.startup_type(type)
- allow(@provider).to receive(:current_start_type).and_return(win32)
- expect(@provider).not_to receive(:set_startup_type).with(type)
- @provider.action_configure_startup
+ new_resource.startup_type(type)
+ allow(provider).to receive(:current_start_type).and_return(win32)
+ expect(provider).not_to receive(:set_startup_type).with(type)
+ provider.action_configure_startup
end
end
end
describe Chef::Provider::Service::Windows, "set_start_type" do
it "when called with :automatic it calls Win32::Service#configure with Win32::Service::AUTO_START" do
- expect(Win32::Service).to receive(:configure).with(:service_name => @new_resource.service_name, :start_type => Win32::Service::AUTO_START)
- @provider.send(:set_startup_type, :automatic)
+ expect(Win32::Service).to receive(:configure).with(:service_name => new_resource.service_name, :start_type => Win32::Service::AUTO_START)
+ provider.send(:set_startup_type, :automatic)
end
it "when called with :manual it calls Win32::Service#configure with Win32::Service::DEMAND_START" do
- expect(Win32::Service).to receive(:configure).with(:service_name => @new_resource.service_name, :start_type => Win32::Service::DEMAND_START)
- @provider.send(:set_startup_type, :manual)
+ expect(Win32::Service).to receive(:configure).with(:service_name => new_resource.service_name, :start_type => Win32::Service::DEMAND_START)
+ provider.send(:set_startup_type, :manual)
end
it "when called with :disabled it calls Win32::Service#configure with Win32::Service::DISABLED" do
- expect(Win32::Service).to receive(:configure).with(:service_name => @new_resource.service_name, :start_type => Win32::Service::DISABLED)
- @provider.send(:set_startup_type, :disabled)
+ expect(Win32::Service).to receive(:configure).with(:service_name => new_resource.service_name, :start_type => Win32::Service::DISABLED)
+ provider.send(:set_startup_type, :disabled)
end
it "raises an exception when given an unknown start type" do
- expect { @provider.send(:set_startup_type, :fire_truck) }.to raise_error(Chef::Exceptions::ConfigurationError)
+ expect { provider.send(:set_startup_type, :fire_truck) }.to raise_error(Chef::Exceptions::ConfigurationError)
end
end
@@ -409,9 +420,9 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
let(:success_string) { "The task has completed successfully.\r\nSee logfile etc." }
let(:failure_string) { "Look on my works, ye Mighty, and despair!" }
let(:command) {
- dbfile = @provider.grant_dbfile_name(username)
- policyfile = @provider.grant_policyfile_name(username)
- logfile = @provider.grant_logfile_name(username)
+ dbfile = provider.grant_dbfile_name(username)
+ policyfile = provider.grant_policyfile_name(username)
+ logfile = provider.grant_logfile_name(username)
%Q{secedit.exe /configure /db "#{dbfile}" /cfg "#{policyfile}" /areas USER_RIGHTS SECURITYPOLICY SERVICES /log "#{logfile}"}
}
@@ -424,20 +435,20 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
after {
# only needed for the second test.
- ::File.delete(@provider.grant_policyfile_name(username)) rescue nil
- ::File.delete(@provider.grant_logfile_name(username)) rescue nil
- ::File.delete(@provider.grant_dbfile_name(username)) rescue nil
+ ::File.delete(provider.grant_policyfile_name(username)) rescue nil
+ ::File.delete(provider.grant_logfile_name(username)) rescue nil
+ ::File.delete(provider.grant_dbfile_name(username)) rescue nil
}
it "calls Mixlib::Shellout with the correct command string" do
expect_any_instance_of(Mixlib::ShellOut).to receive(:exitstatus).and_return(0)
- expect(@provider.grant_service_logon(username)).to equal true
+ expect(provider.grant_service_logon(username)).to equal true
end
it "raises an exception when the grant command fails" do
expect_any_instance_of(Mixlib::ShellOut).to receive(:exitstatus).and_return(1)
expect_any_instance_of(Mixlib::ShellOut).to receive(:stdout).and_return(failure_string)
- expect { @provider.grant_service_logon(username) }.to raise_error(Chef::Exceptions::Service)
+ expect { provider.grant_service_logon(username) }.to raise_error(Chef::Exceptions::Service)
end
end
@@ -445,17 +456,17 @@ describe Chef::Provider::Service::Windows, "load_current_resource" do
include_context "testing private methods"
it "correctly reformats usernames to create valid filenames" do
- expect(@provider.clean_username_for_path("\\\\problem username/oink.txt")).to eq("_problem_username_oink_txt")
- expect(@provider.clean_username_for_path("boring_username")).to eq("boring_username")
+ expect(provider.clean_username_for_path("\\\\problem username/oink.txt")).to eq("_problem_username_oink_txt")
+ expect(provider.clean_username_for_path("boring_username")).to eq("boring_username")
end
it "correctly reformats usernames for the policy file" do
- expect(@provider.canonicalize_username(".\\maryann")).to eq("maryann")
- expect(@provider.canonicalize_username("maryann")).to eq("maryann")
+ expect(provider.canonicalize_username(".\\maryann")).to eq("maryann")
+ expect(provider.canonicalize_username("maryann")).to eq("maryann")
- expect(@provider.canonicalize_username("\\\\maryann")).to eq("maryann")
- expect(@provider.canonicalize_username("mydomain\\\\maryann")).to eq("mydomain\\\\maryann")
- expect(@provider.canonicalize_username("\\\\mydomain\\\\maryann")).to eq("mydomain\\\\maryann")
+ expect(provider.canonicalize_username("\\\\maryann")).to eq("maryann")
+ expect(provider.canonicalize_username("mydomain\\\\maryann")).to eq("mydomain\\\\maryann")
+ expect(provider.canonicalize_username("\\\\mydomain\\\\maryann")).to eq("mydomain\\\\maryann")
end
end
end
diff --git a/spec/unit/provider/subversion_spec.rb b/spec/unit/provider/subversion_spec.rb
index 9ca11b8d82..9d4a8bd218 100644
--- a/spec/unit/provider/subversion_spec.rb
+++ b/spec/unit/provider/subversion_spec.rb
@@ -27,6 +27,7 @@ describe Chef::Provider::Subversion do
@resource.revision "12345"
@resource.svn_arguments(false)
@resource.svn_info_args(false)
+ @resource.svn_binary "svn"
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
@@ -63,28 +64,18 @@ describe Chef::Provider::Subversion do
"Last Changed Rev: 11410\n" + # Last Changed Rev is preferred to Revision
"Last Changed Date: 2009-03-25 06:09:56 -0600 (Wed, 25 Mar 2009)\n\n"
expect(::File).to receive(:exist?).at_least(1).times.with("/my/deploy/dir/.svn").and_return(true)
- expect(::File).to receive(:directory?).with("/my/deploy/dir").and_return(true)
- expect(::Dir).to receive(:chdir).with("/my/deploy/dir").and_yield
- allow(@stdout).to receive(:string).and_return(example_svn_info)
- allow(@stderr).to receive(:string).and_return("")
- allow(@exitstatus).to receive(:exitstatus).and_return(0)
- expected_command = ["svn info", {:cwd=>"/my/deploy/dir"}]
- expect(@provider).to receive(:popen4).with(*expected_command).
- and_yield("no-pid", "no-stdin", @stdout,@stderr).
- and_return(@exitstatus)
+ expected_command = ["svn info", {:cwd => '/my/deploy/dir', :returns => [0,1]}]
+ expect(@provider).to receive(:shell_out!).with(*expected_command).
+ and_return(double("ShellOut result", :stdout => example_svn_info, :stderr => ""))
expect(@provider.find_current_revision).to eql("11410")
end
it "gives nil as the current revision if the deploy dir isn't a SVN working copy" do
example_svn_info = "svn: '/tmp/deploydir' is not a working copy\n"
expect(::File).to receive(:exist?).with("/my/deploy/dir/.svn").and_return(true)
- expect(::File).to receive(:directory?).with("/my/deploy/dir").and_return(true)
- expect(::Dir).to receive(:chdir).with("/my/deploy/dir").and_yield
- allow(@stdout).to receive(:string).and_return(example_svn_info)
- allow(@stderr).to receive(:string).and_return("")
- allow(@exitstatus).to receive(:exitstatus).and_return(1)
- expect(@provider).to receive(:popen4).and_yield("no-pid", "no-stdin", @stdout,@stderr).
- and_return(@exitstatus)
+ expected_command = ["svn info", {:cwd => '/my/deploy/dir', :returns => [0,1]}]
+ expect(@provider).to receive(:shell_out!).with(*expected_command).
+ and_return(double("ShellOut result", :stdout => example_svn_info, :stderr => ""))
expect(@provider.find_current_revision).to be_nil
end
@@ -127,28 +118,20 @@ describe Chef::Provider::Subversion do
"Last Changed Author: codeninja\n" +
"Last Changed Rev: 11410\n" + # Last Changed Rev is preferred to Revision
"Last Changed Date: 2009-03-25 06:09:56 -0600 (Wed, 25 Mar 2009)\n\n"
- exitstatus = double("exitstatus")
- allow(exitstatus).to receive(:exitstatus).and_return(0)
@resource.revision "HEAD"
- allow(@stdout).to receive(:string).and_return(example_svn_info)
- allow(@stderr).to receive(:string).and_return("")
- expected_command = ["svn info http://svn.example.org/trunk/ --no-auth-cache -rHEAD", {:cwd=>Dir.tmpdir}]
- expect(@provider).to receive(:popen4).with(*expected_command).
- and_yield("no-pid","no-stdin",@stdout,@stderr).
- and_return(exitstatus)
+ expected_command = ["svn info http://svn.example.org/trunk/ --no-auth-cache -rHEAD", {:cwd => '/my/deploy/dir', :returns => [0,1]}]
+ expect(@provider).to receive(:shell_out!).with(*expected_command).
+ and_return(double("ShellOut result", :stdout => example_svn_info, :stderr => ""))
expect(@provider.revision_int).to eql("11410")
end
it "returns a helpful message if data from `svn info` can't be parsed" do
example_svn_info = "some random text from an error message\n"
- exitstatus = double("exitstatus")
- allow(exitstatus).to receive(:exitstatus).and_return(0)
@resource.revision "HEAD"
- allow(@stdout).to receive(:string).and_return(example_svn_info)
- allow(@stderr).to receive(:string).and_return("")
- expect(@provider).to receive(:popen4).and_yield("no-pid","no-stdin",@stdout,@stderr).
- and_return(exitstatus)
- expect {@provider.revision_int}.to raise_error(RuntimeError, "Could not parse `svn info` data: some random text from an error message")
+ expected_command = ["svn info http://svn.example.org/trunk/ --no-auth-cache -rHEAD", {:cwd => '/my/deploy/dir', :returns => [0,1]}]
+ expect(@provider).to receive(:shell_out!).with(*expected_command).
+ and_return(double("ShellOut result", :stdout => example_svn_info, :stderr => ""))
+ expect {@provider.revision_int}.to raise_error(RuntimeError, "Could not parse `svn info` data: some random text from an error message\n")
end
@@ -277,4 +260,40 @@ describe Chef::Provider::Subversion do
expect(@resource).to be_updated
end
+ context "selects the correct svn binary" do
+ before do
+ end
+
+ it "selects 'svn' as the binary by default" do
+ @resource.svn_binary nil
+ allow(ChefConfig).to receive(:windows?) { false }
+ expect(@provider).to receive(:svn_binary).and_return('svn')
+ expect(@provider.export_command).to eql(
+ 'svn export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir')
+ end
+
+ it "selects an svn binary with an exe extension on windows" do
+ @resource.svn_binary nil
+ allow(ChefConfig).to receive(:windows?) { true }
+ expect(@provider).to receive(:svn_binary).and_return('svn.exe')
+ expect(@provider.export_command).to eql(
+ 'svn.exe export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir')
+ end
+
+ it "uses a custom svn binary as part of the svn command" do
+ @resource.svn_binary 'teapot'
+ expect(@provider).to receive(:svn_binary).and_return('teapot')
+ expect(@provider.export_command).to eql(
+ 'teapot export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir')
+ end
+
+ it "wraps custom svn binary with quotes if it contains whitespace" do
+ @resource.svn_binary 'c:/program files (x86)/subversion/svn.exe'
+ expect(@provider).to receive(:svn_binary).and_return('c:/program files (x86)/subversion/svn.exe')
+ expect(@provider.export_command).to eql(
+ '"c:/program files (x86)/subversion/svn.exe" export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir')
+ end
+
+ end
+
end
diff --git a/spec/unit/provider/template/content_spec.rb b/spec/unit/provider/template/content_spec.rb
index 3d6e822c00..509c8cf33b 100644
--- a/spec/unit/provider/template/content_spec.rb
+++ b/spec/unit/provider/template/content_spec.rb
@@ -20,6 +20,14 @@ require 'spec_helper'
describe Chef::Provider::Template::Content do
+ let(:enclosing_directory) {
+ canonicalize_path(Dir.mktmpdir)
+ }
+
+ let(:resource_path) {
+ canonicalize_path(File.expand_path(File.join(enclosing_directory, "openldap_stuff.conf")))
+ }
+
let(:new_resource) do
double("Chef::Resource::Template (new)",
:cookbook_name => 'openldap',
@@ -28,6 +36,8 @@ describe Chef::Provider::Template::Content do
:source_line_file => "/Users/lamont/solo/cookbooks/openldap/recipes/default.rb",
:source_line_number => "2",
:source => 'openldap_stuff.conf.erb',
+ :name => 'openldap_stuff.conf',
+ :path => resource_path,
:local => false,
:cookbook => nil,
:variables => {},
@@ -36,7 +46,10 @@ describe Chef::Provider::Template::Content do
:helper_modules => [])
end
- let(:rendered_file_location) { Dir.tmpdir + '/openldap_stuff.conf' }
+ let(:rendered_file_locations) {
+ [Dir.tmpdir + '/openldap_stuff.conf',
+ enclosing_directory + '/openldap_stuff.conf']
+ }
let(:run_context) do
cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks"))
@@ -54,7 +67,9 @@ describe Chef::Provider::Template::Content do
end
after do
- FileUtils.rm(rendered_file_location) if ::File.exist?(rendered_file_location)
+ rendered_file_locations.each do |file|
+ FileUtils.rm(file) if ::File.exist?(file)
+ end
end
it "finds the template file in the cookbook cache if it isn't local" do
@@ -74,6 +89,39 @@ describe Chef::Provider::Template::Content do
expect(content.template_location).to eq(CHEF_SPEC_DATA + '/cookbooks/openldap/templates/default/test.erb')
end
+ it "returns a tempfile in the tempdir when :file_staging_uses_destdir is not set" do
+ Chef::Config[:file_staging_uses_destdir] = false
+ expect(content.tempfile.path.start_with?(Dir::tmpdir)).to be true
+ expect(canonicalize_path(content.tempfile.path).start_with?(enclosing_directory)).to be false
+ end
+
+ it "returns a tempfile in the destdir when :file_staging_uses_destdir is set" do
+ Chef::Config[:file_staging_uses_destdir] = true
+ expect(canonicalize_path(content.tempfile.path).start_with?(enclosing_directory)).to be true
+ end
+
+ context "when creating a tempfile in destdir fails" do
+ let(:enclosing_directory) {
+ canonicalize_path("/nonexisting/path")
+ }
+
+ it "returns a tempfile in the tempdir when :file_deployment_uses_destdir is set to :auto" do
+ Chef::Config[:file_staging_uses_destdir] = :auto
+ expect(content.tempfile.path.start_with?(Dir::tmpdir)).to be true
+ expect(canonicalize_path(content.tempfile.path).start_with?(enclosing_directory)).to be false
+ end
+
+ it "fails when :file_desployment_uses_destdir is set" do
+ Chef::Config[:file_staging_uses_destdir] = true
+ expect{content.tempfile}.to raise_error(Chef::Exceptions::FileContentStagingError)
+ end
+
+ it "returns a tempfile in the tempdir when :file_desployment_uses_destdir is not set" do
+ expect(content.tempfile.path.start_with?(Dir::tmpdir)).to be true
+ expect(canonicalize_path(content.tempfile.path).start_with?(enclosing_directory)).to be false
+ end
+ end
+
it "creates the template with the rendered content" do
run_context.node.normal[:slappiness] = "a warm gun"
expect(IO.read(content.tempfile.path)).to eq("slappiness is a warm gun")
@@ -88,6 +136,8 @@ describe Chef::Provider::Template::Content do
:source_line_file => CHEF_SPEC_DATA + "/cookbooks/openldap/recipes/default.rb",
:source_line_number => "2",
:source => 'helpers.erb',
+ :name => 'helpers.erb',
+ :path => CHEF_SPEC_DATA + '/cookbooks/openldap/templates/default/helpers.erb',
:local => false,
:cookbook => nil,
:variables => {},
diff --git a/spec/unit/provider/user/dscl_spec.rb b/spec/unit/provider/user/dscl_spec.rb
index 32d0812d8c..e8cf6445be 100644
--- a/spec/unit/provider/user/dscl_spec.rb
+++ b/spec/unit/provider/user/dscl_spec.rb
@@ -789,6 +789,13 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
expect { provider.dscl_set_gid }.to raise_error(Chef::Exceptions::GroupIDNotFound)
end
end
+
+ it "should set group ID to 20 if it's not specified" do
+ new_resource.gid nil
+ expect(provider).to receive(:run_dscl).with("create /Users/toor PrimaryGroupID '20'").ordered.and_return(true)
+ provider.dscl_set_gid
+ expect(new_resource.gid).to eq(20)
+ end
end
describe "when the user exists and chef is managing it" do
diff --git a/spec/unit/provider/user/solaris_spec.rb b/spec/unit/provider/user/solaris_spec.rb
index ef62fd1d5a..a3c17a9a56 100644
--- a/spec/unit/provider/user/solaris_spec.rb
+++ b/spec/unit/provider/user/solaris_spec.rb
@@ -1,7 +1,9 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Author:: Dave Eddy (<dave@daveeddy.com>)
# Copyright:: Copyright (c) 2008, 2010 Opscode, Inc.
+# Copyright:: Copyright (c) 2015, Dave Eddy
#
# License:: Apache License, Version 2.0
#
@@ -18,6 +20,9 @@
# limitations under the License.
#
+ShellCmdResult = Struct.new(:stdout, :stderr, :exitstatus)
+
+require 'mixlib/shellout'
require 'spec_helper'
describe Chef::Provider::User::Solaris do
@@ -31,15 +36,6 @@ describe Chef::Provider::User::Solaris do
p
end
- supported_useradd_options = {
- 'comment' => "-c",
- 'gid' => "-g",
- 'uid' => "-u",
- 'shell' => "-s"
- }
-
- include_examples "a useradd-based user provider", supported_useradd_options
-
describe "when we want to set a password" do
before(:each) do
@node = Chef::Node.new
@@ -77,4 +73,65 @@ describe Chef::Provider::User::Solaris do
end
end
+ describe 'when managing user locked status' do
+ 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('dave')
+ @current_resource = @new_resource.dup
+
+ @provider = Chef::Provider::User::Solaris.new(@new_resource, @run_context)
+ @provider.current_resource = @current_resource
+ end
+ describe 'when determining if the user is locked' do
+
+ # locked shadow lines
+ [
+ 'dave:LK:::::::',
+ 'dave:*LK*:::::::',
+ 'dave:*LK*foobar:::::::',
+ 'dave:*LK*bahamas10:::::::',
+ 'dave:*LK*L....:::::::',
+ ].each do |shadow|
+ it "should return true if user is locked with #{shadow}" do
+ shell_return = ShellCmdResult.new(shadow + "\n", '', 0)
+ expect(provider).to receive(:shell_out!).with('getent', 'shadow', @new_resource.username).and_return(shell_return)
+ expect(provider.check_lock).to eql(true)
+ end
+ end
+
+ # unlocked shadow lines
+ [
+ 'dave:NP:::::::',
+ 'dave:*NP*:::::::',
+ 'dave:foobar:::::::',
+ 'dave:bahamas10:::::::',
+ 'dave:L...:::::::',
+ ].each do |shadow|
+ it "should return false if user is unlocked with #{shadow}" do
+ shell_return = ShellCmdResult.new(shadow + "\n", '', 0)
+ expect(provider).to receive(:shell_out!).with('getent', 'shadow', @new_resource.username).and_return(shell_return)
+ expect(provider.check_lock).to eql(false)
+ end
+ end
+ end
+
+ describe 'when locking the user' do
+ it 'should run passwd -l with the new resources username' do
+ shell_return = ShellCmdResult.new('', '', 0)
+ expect(provider).to receive(:shell_out!).with('passwd', '-l', @new_resource.username).and_return(shell_return)
+ provider.lock_user
+ end
+ end
+
+ describe 'when unlocking the user' do
+ it 'should run passwd -u with the new resources username' do
+ shell_return = ShellCmdResult.new('', '', 0)
+ expect(provider).to receive(:shell_out!).with('passwd', '-u', @new_resource.username).and_return(shell_return)
+ provider.unlock_user
+ end
+ end
+ end
end
diff --git a/spec/unit/provider/user/windows_spec.rb b/spec/unit/provider/user/windows_spec.rb
index e51e20a68f..7e08f971a9 100644
--- a/spec/unit/provider/user/windows_spec.rb
+++ b/spec/unit/provider/user/windows_spec.rb
@@ -107,8 +107,8 @@ describe Chef::Provider::User::Windows do
expect(@provider.set_options[:home_dir]).to eq('/home/adam')
end
- it "marks the primary_group_id attribute to be updated" do
- expect(@provider.set_options[:primary_group_id]).to eq(1000)
+ it "ignores the primary_group_id attribute" do
+ expect(@provider.set_options[:primary_group_id]).to eq(nil)
end
it "marks the user_id attribute to be updated" do
diff --git a/spec/unit/provider/user_spec.rb b/spec/unit/provider/user_spec.rb
index 2345ce18fb..bd24a6a01e 100644
--- a/spec/unit/provider/user_spec.rb
+++ b/spec/unit/provider/user_spec.rb
@@ -452,11 +452,20 @@ describe Chef::Provider::User do
it "should raise an error if we can't translate the group name during resource assertions" do
expect(Etc).to receive(:getgrnam).and_raise(ArgumentError)
+ @provider.action = :create
@provider.define_resource_requirements
@provider.convert_group_name
expect { @provider.process_resource_requirements }.to raise_error(Chef::Exceptions::User)
end
+ it "does not raise an error if we can't translate the group name during resource assertions if we are removing the user" do
+ expect(Etc).to receive(:getgrnam).and_raise(ArgumentError)
+ @provider.action = :remove
+ @provider.define_resource_requirements
+ @provider.convert_group_name
+ expect { @provider.process_resource_requirements }.not_to raise_error
+ end
+
it "should set the new resources gid to the integerized version if available" do
expect(Etc).to receive(:getgrnam).with("999").and_return(@group)
@provider.convert_group_name
diff --git a/spec/unit/provider_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb
index 88df4a20cc..2fb99f610c 100644
--- a/spec/unit/provider_resolver_spec.rb
+++ b/spec/unit/provider_resolver_spec.rb
@@ -20,6 +20,9 @@ require 'spec_helper'
require 'chef/mixin/convert_to_class_name'
require 'chef/provider_resolver'
require 'chef/platform/service_helpers'
+require 'support/shared/integration/integration_helper'
+require 'tmpdir'
+require 'fileutils'
include Chef::Mixin::ConvertToClassName
@@ -27,833 +30,887 @@ include Chef::Mixin::ConvertToClassName
#module Chef::Provider
describe Chef::ProviderResolver do
+ include IntegrationSupport
- let(:resource_name) { :service }
- let(:provider) { nil }
- let(:action) { :start }
-
- let(:node) do
- node = Chef::Node.new
- node.automatic[:os] = os
- node.automatic[:platform_family] = platform_family
- node.automatic[:platform] = platform
- node.automatic[:platform_version] = platform_version
- node.automatic[:kernel] = { machine: 'i386' }
- node
- end
- let(:run_context) { Chef::RunContext.new(node, nil, nil) }
-
- let(:provider_resolver) { Chef::ProviderResolver.new(node, resource, action) }
- let(:resolved_provider) do
- begin
- resource ? resource.provider_for_action(action).class : nil
- rescue Chef::Exceptions::ProviderNotFound
- nil
+ # Root the filesystem under a temp directory so Chef.path_to will point at it
+ when_the_repository "is empty" do
+ before do
+ allow(Chef).to receive(:path_to) { |path| File.join(path_to(""), path) }
end
- end
- let(:resource) do
- resource_class = Chef::ResourceResolver.resolve(resource_name, node: node)
- if resource_class
- resource = resource_class.new('test', run_context)
- resource.provider = provider if provider
+ let(:resource_name) { :service }
+ let(:provider) { nil }
+ let(:action) { :start }
+
+ let(:node) do
+ node = Chef::Node.new
+ node.automatic[:os] = os
+ node.automatic[:platform_family] = platform_family
+ node.automatic[:platform] = platform
+ node.automatic[:platform_version] = platform_version
+ node.automatic[:kernel] = { machine: 'i386' }
+ node
end
- resource
- end
-
- def self.on_platform(platform, *tags,
- platform_version: '11.0.1',
- platform_family: nil,
- os: nil,
- &block)
- Array(platform).each do |platform|
- Array(platform_version).each do |platform_version|
- on_one_platform(platform, platform_version, platform_family || platform, os || platform_family || platform, *tags, &block)
+ let(:run_context) { Chef::RunContext.new(node, nil, nil) }
+
+ let(:provider_resolver) { Chef::ProviderResolver.new(node, resource, action) }
+ let(:resolved_provider) do
+ begin
+ resource ? resource.provider_for_action(action).class : nil
+ rescue Chef::Exceptions::ProviderNotFound
+ nil
end
end
- end
-
- def self.on_one_platform(platform, platform_version, platform_family, os, *tags, &block)
- describe "on #{platform} #{platform_version}, platform_family: #{platform_family}, os: #{os}", *tags do
- let(:os) { os }
- let(:platform) { platform }
- let(:platform_family) { platform_family }
- let(:platform_version) { platform_version }
- define_singleton_method(:os) { os }
- define_singleton_method(:platform) { platform }
- define_singleton_method(:platform_family) { platform_family }
- define_singleton_method(:platform_version) { platform_version }
-
- instance_eval(&block)
+ let(:service_name) { "test" }
+ let(:resource) do
+ resource_class = Chef::ResourceResolver.resolve(resource_name, node: node)
+ if resource_class
+ resource = resource_class.new(service_name, run_context)
+ resource.provider = provider if provider
+ end
+ resource
end
- end
- def self.expect_providers(**providers)
- providers.each do |name, expected|
- describe name.to_s do
- let(:resource_name) { name }
-
- tags = []
- expected_provider = nil
- expected_resource = nil
- Array(expected).each do |p|
- if p.is_a?(Class) && p <= Chef::Provider
- expected_provider = p
- elsif p.is_a?(Class) && p <= Chef::Resource
- expected_resource = p
- else
- tags << p
- end
- end
-
- if expected_resource && expected_provider
- it "'#{name}' resolves to resource #{expected_resource} and provider #{expected_provider}", *tags do
- expect(resource.class).to eql(expected_resource)
- provider = double(expected_provider, class: expected_provider)
- expect(provider).to receive(:action=).with(action)
- expect(expected_provider).to receive(:new).with(resource, run_context).and_return(provider)
- expect(resolved_provider).to eql(expected_provider)
- end
- elsif expected_provider
- it "'#{name}' resolves to provider #{expected_provider}", *tags do
- provider = double(expected_provider)
- expect(provider).to receive(:action=).with(action)
- expect(expected_provider).to receive(:new).with(resource, run_context).and_return(provider)
- expect(resolved_provider).to eql(expected_provider)
- end
- else
- it "'#{name}' fails to resolve (since #{name.inspect} is unsupported on #{platform} #{platform_version})", *tags do
- expect(resolved_provider).to be_nil
- end
+ def self.on_platform(platform, *tags,
+ platform_version: '11.0.1',
+ platform_family: nil,
+ os: nil,
+ &block)
+ Array(platform).each do |platform|
+ Array(platform_version).each do |platform_version|
+ on_one_platform(platform, platform_version, platform_family || platform, os || platform_family || platform, *tags, &block)
end
end
end
- end
-
- describe "resolving service resource" do
- def stub_service_providers(*services)
- services ||= []
- allow(Chef::Platform::ServiceHelpers).to receive(:service_resource_providers)
- .and_return(services)
- end
-
- def stub_service_configs(*configs)
- configs ||= []
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return(configs)
- end
-
- before do
- allow(resource).to receive(:service_name).and_return("ntp")
- end
-
- shared_examples_for "an ubuntu platform with upstart, update-rc.d and systemd" do
- before do
- stub_service_providers(:debian, :invokercd, :upstart, :systemd)
- end
- it "when only the SysV init script exists, it returns a Service::Debian provider" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :initd, :systemd ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
- end
-
- it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :initd, :upstart, :systemd ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
- end
+ def self.on_one_platform(platform, platform_version, platform_family, os, *tags, &block)
+ describe "on #{platform} #{platform_version}, platform_family: #{platform_family}, os: #{os}", *tags do
+ let(:os) { os }
+ let(:platform) { platform }
+ let(:platform_family) { platform_family }
+ let(:platform_version) { platform_version }
- it "when only the Upstart script exists, it returns a Service::Upstart provider" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :upstart, :systemd ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
- end
+ define_singleton_method(:os) { os }
+ define_singleton_method(:platform) { platform }
+ define_singleton_method(:platform_family) { platform_family }
+ define_singleton_method(:platform_version) { platform_version }
- it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :systemd ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
- end
- it "when only the SysV init script exists, it returns a Service::Debian provider" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :initd ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Debian)
+ instance_eval(&block)
end
+ end
- it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :initd, :upstart ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
- end
+ def self.expect_providers(**providers)
+ providers.each do |name, expected|
+ describe name.to_s do
+ let(:resource_name) { name }
+
+ tags = []
+ expected_provider = nil
+ expected_resource = nil
+ Array(expected).each do |p|
+ if p.is_a?(Class) && p <= Chef::Provider
+ expected_provider = p
+ elsif p.is_a?(Class) && p <= Chef::Resource
+ expected_resource = p
+ else
+ tags << p
+ end
+ end
- it "when only the Upstart script exists, it returns a Service::Upstart provider" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :upstart ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
+ if expected_resource && expected_provider
+ it "'#{name}' resolves to resource #{expected_resource} and provider #{expected_provider}", *tags do
+ expect(resource.class).to eql(expected_resource)
+ provider = double(expected_provider, class: expected_provider)
+ expect(provider).to receive(:action=).with(action)
+ expect(expected_provider).to receive(:new).with(resource, run_context).and_return(provider)
+ expect(resolved_provider).to eql(expected_provider)
+ end
+ elsif expected_provider
+ it "'#{name}' resolves to provider #{expected_provider}", *tags do
+ provider = double(expected_provider)
+ expect(provider).to receive(:action=).with(action)
+ expect(expected_provider).to receive(:new).with(resource, run_context).and_return(provider)
+ expect(resolved_provider).to eql(expected_provider)
+ end
+ else
+ it "'#{name}' fails to resolve (since #{name.inspect} is unsupported on #{platform} #{platform_version})", *tags do
+ expect(resolved_provider).to be_nil
+ end
+ end
+ end
end
+ end
- it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
+ describe "resolving service resource" do
+ def stub_service_providers(*services)
+ services.each do |service|
+ case service
+ when :debian
+ directory 'usr/sbin/update-rc.d'
+ when :invokercd
+ directory 'usr/sbin/invoke-rc.d'
+ when :insserv
+ directory 'sbin/insserv'
+ when :upstart
+ directory 'etc/init'
+ directory 'sbin/start'
+ when :redhat
+ directory 'sbin/chkconfig'
+ when :systemd
+ file 'bin/systemctl', ''
+ # Make systemctl executable
+ File.chmod(0755, path_to('bin/systemctl'))
+ # Windows doesn't respect executable bit, do this to let Windows users see if they've broken the resolver
+ allow(::File).to receive(:executable?) { |p| p == path_to('bin/systemctl') } if windows?
+ file 'proc/1/comm', "systemd\n"
+ mock_shellout_command("/bin/systemctl --all", stdout: "")
+ mock_shellout_command("/bin/systemctl list-unit-files", stdout: "")
+ else
+ raise ArgumentError, service
+ end
+ end
end
- end
- shared_examples_for "an ubuntu platform with upstart and update-rc.d" do
- before do
- stub_service_providers(:debian, :invokercd, :upstart)
+ def stub_service_configs(*configs)
+ configs.each do |config|
+ case config
+ when :initd
+ file "etc/init.d/#{service_name}", ""
+ when :upstart
+ file "etc/init/#{service_name}.conf", ""
+ when :xinetd
+ file "etc/xinetd.d/#{service_name}", ""
+ when :etc_rcd
+ file "etc/rc.d/#{service_name}", ""
+ when :usr_local_etc_rcd
+ file "usr/local/etc/rc.d/#{service_name}", ""
+ when :systemd
+ file 'bin/systemctl', ''
+ # Make systemctl executable
+ File.chmod(0755, path_to("bin/systemctl"))
+ # Windows doesn't respect executable bit, do this to let Windows users see if they've broken the resolver
+ allow(::File).to receive(:executable?) { |p| p == path_to('bin/systemctl') } if windows?
+ file 'proc/1/comm', "systemd\n"
+ mock_shellout_command("/bin/systemctl --all", stdout: <<-EOM)
+ superv loaded
+ stinky something-else
+ #{service_name} loaded
+ blargh not-found
+ EOM
+ mock_shellout_command("/bin/systemctl list-unit-files", stdout: <<-EOM)
+ usuperv loaded
+ ustinky something-else
+ u#{service_name} loaded
+ ublargh not-found
+ EOM
+ else
+ raise ArgumentError, config
+ end
+ end
end
- # needs to be handled by the highest priority init.d handler
- context "when only the SysV init script exists" do
+ shared_examples_for "an ubuntu platform with upstart, update-rc.d and systemd" do
before do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :initd ] )
+ stub_service_providers(:debian, :invokercd, :upstart, :systemd)
end
- it "enables init, invokercd, debian and upstart providers" do
- expect(provider_resolver.enabled_handlers).to include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- Chef::Provider::Service::Upstart,
- )
+ it "when only the SysV init script exists, it returns a Service::Debian provider" do
+ stub_service_configs(:initd, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
end
- it "supports all the enabled handlers except for upstart" do
- expect(provider_resolver.supported_handlers).to include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- )
- expect(provider_resolver.supported_handlers).to_not include(
- Chef::Provider::Service::Upstart,
- )
+ it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do
+ stub_service_configs(:initd, :upstart, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
end
- it "returns a Service::Debian provider" do
- expect(resolved_provider).to eql(Chef::Provider::Service::Debian)
+ it "when only the Upstart script exists, it returns a Service::Upstart provider" do
+ stub_service_configs(:upstart, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
end
- end
- # on ubuntu this must be handled by upstart, the init script will exit 1 and fail
- context "when both SysV and Upstart scripts exist" do
- before do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :initd, :upstart ] )
+ it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do
+ stub_service_configs(:systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
end
- it "enables init, invokercd, debian and upstart providers" do
- expect(provider_resolver.enabled_handlers).to include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- Chef::Provider::Service::Upstart,
- )
+ it "when only the SysV init script exists, it returns a Service::Debian provider" do
+ stub_service_configs(:initd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Debian)
end
- it "supports all the enabled handlers" do
- expect(provider_resolver.supported_handlers).to include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- Chef::Provider::Service::Upstart,
- )
+ it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do
+ stub_service_configs(:initd, :upstart)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
end
- it "returns a Service::Upstart provider" do
+ it "when only the Upstart script exists, it returns a Service::Upstart provider" do
+ stub_service_configs(:upstart)
expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
end
+
+ it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do
+ stub_service_configs
+ expect(resolved_provider).to eql(Chef::Provider::Service::Systemd)
+ end
end
- # this case is a pure-upstart script which is easy
- context "when only the Upstart script exists" do
+ shared_examples_for "an ubuntu platform with upstart and update-rc.d" do
before do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :upstart ] )
+ stub_service_providers(:debian, :invokercd, :upstart)
end
- it "enables init, invokercd, debian and upstart providers" do
- expect(provider_resolver.enabled_handlers).to include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- Chef::Provider::Service::Upstart,
- )
- end
+ # needs to be handled by the highest priority init.d handler
+ context "when only the SysV init script exists" do
+ before do
+ stub_service_configs(:initd)
+ end
- it "supports only the upstart handler" do
- expect(provider_resolver.supported_handlers).to include(
- Chef::Provider::Service::Upstart,
- )
- expect(provider_resolver.supported_handlers).to_not include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- )
- end
+ it "enables init, invokercd, debian and upstart providers" do
+ expect(provider_resolver.enabled_handlers).to include(
+ Chef::Provider::Service::Debian,
+ Chef::Provider::Service::Init,
+ Chef::Provider::Service::Invokercd,
+ Chef::Provider::Service::Upstart,
+ )
+ end
- it "returns a Service::Upstart provider" do
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
- end
- end
+ it "supports all the enabled handlers except for upstart" do
+ expect(provider_resolver.supported_handlers).to include(
+ Chef::Provider::Service::Debian,
+ Chef::Provider::Service::Init,
+ Chef::Provider::Service::Invokercd,
+ )
+ expect(provider_resolver.supported_handlers).to_not include(
+ Chef::Provider::Service::Upstart,
+ )
+ end
- # this case is important to get correct for why-run when no config is setup
- context "when both do not exist" do
- before do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ ] )
+ it "returns a Service::Debian provider" do
+ expect(resolved_provider).to eql(Chef::Provider::Service::Debian)
+ end
end
- it "enables init, invokercd, debian and upstart providers" do
- expect(provider_resolver.enabled_handlers).to include(
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- Chef::Provider::Service::Upstart,
- )
- end
+ # on ubuntu this must be handled by upstart, the init script will exit 1 and fail
+ context "when both SysV and Upstart scripts exist" do
+ before do
+ stub_service_configs(:initd, :upstart)
+ end
- it "no providers claim to support the resource" do
- expect(provider_resolver.supported_handlers).to_not include(
- Chef::Provider::Service::Upstart,
- Chef::Provider::Service::Debian,
- Chef::Provider::Service::Init,
- Chef::Provider::Service::Invokercd,
- )
- end
+ it "enables init, invokercd, debian and upstart providers" do
+ expect(provider_resolver.enabled_handlers).to include(
+ Chef::Provider::Service::Debian,
+ Chef::Provider::Service::Init,
+ Chef::Provider::Service::Invokercd,
+ Chef::Provider::Service::Upstart,
+ )
+ end
- it "returns a Debian Provider" do
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
- end
- end
- end
+ it "supports all the enabled handlers" do
+ expect(provider_resolver.supported_handlers).to include(
+ Chef::Provider::Service::Debian,
+ Chef::Provider::Service::Init,
+ Chef::Provider::Service::Invokercd,
+ Chef::Provider::Service::Upstart,
+ )
+ end
- shared_examples_for "a debian platform using the insserv provider" do
- context "with a default install" do
- before do
- stub_service_providers(:debian, :invokercd, :insserv)
+ it "returns a Service::Upstart provider" do
+ expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
+ end
end
- it "uses the Service::Insserv Provider to manage sysv init scripts" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :initd ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Insserv)
- end
+ # this case is a pure-upstart script which is easy
+ context "when only the Upstart script exists" do
+ before do
+ stub_service_configs(:upstart)
+ end
- it "uses the Service::Insserv Provider when there is no config" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Insserv)
- end
- end
+ it "enables init, invokercd, debian and upstart providers" do
+ expect(provider_resolver.enabled_handlers).to include(
+ Chef::Provider::Service::Debian,
+ Chef::Provider::Service::Init,
+ Chef::Provider::Service::Invokercd,
+ Chef::Provider::Service::Upstart,
+ )
+ end
- context "when the user has installed upstart" do
- before do
- stub_service_providers(:debian, :invokercd, :insserv, :upstart)
- end
+ it "supports only the upstart handler" do
+ expect(provider_resolver.supported_handlers).to include(
+ Chef::Provider::Service::Upstart,
+ )
+ expect(provider_resolver.supported_handlers).to_not include(
+ Chef::Provider::Service::Debian,
+ Chef::Provider::Service::Init,
+ Chef::Provider::Service::Invokercd,
+ )
+ end
- it "when only the SysV init script exists, it returns an Insserv provider" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :initd ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Insserv)
+ it "returns a Service::Upstart provider" do
+ expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
+ end
end
- it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :initd, :upstart ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
- end
+ # this case is important to get correct for why-run when no config is setup
+ context "when both do not exist" do
+ before do
+ stub_service_configs
+ end
- it "when only the Upstart script exists, it returns a Service::Upstart provider" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ :upstart ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
- end
+ it "enables init, invokercd, debian and upstart providers" do
+ expect(provider_resolver.enabled_handlers).to include(
+ Chef::Provider::Service::Debian,
+ Chef::Provider::Service::Init,
+ Chef::Provider::Service::Invokercd,
+ Chef::Provider::Service::Upstart,
+ )
+ end
- it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do
- allow(Chef::Platform::ServiceHelpers).to receive(:config_for_service).with("ntp")
- .and_return( [ ] )
- expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
+ it "no providers claim to support the resource" do
+ expect(provider_resolver.supported_handlers).to_not include(
+ Chef::Provider::Service::Upstart,
+ Chef::Provider::Service::Debian,
+ Chef::Provider::Service::Init,
+ Chef::Provider::Service::Invokercd,
+ )
+ end
+
+ it "returns a Debian Provider" do
+ expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
+ end
end
end
- end
- on_platform "ubuntu", platform_version: "14.10", platform_family: "debian", os: "linux" do
- it_behaves_like "an ubuntu platform with upstart, update-rc.d and systemd"
- end
+ shared_examples_for "a debian platform using the insserv provider" do
+ context "with a default install" do
+ before do
+ stub_service_providers(:debian, :invokercd, :insserv)
+ end
- on_platform "ubuntu", platform_version: "14.04", platform_family: "debian", os: "linux" do
- it_behaves_like "an ubuntu platform with upstart and update-rc.d"
- end
+ it "uses the Service::Insserv Provider to manage sysv init scripts" do
+ stub_service_configs(:initd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Insserv)
+ end
- on_platform "ubuntu", platform_version: "10.04", platform_family: "debian", os: "linux" do
- it_behaves_like "an ubuntu platform with upstart and update-rc.d"
- end
+ it "uses the Service::Insserv Provider when there is no config" do
+ stub_service_configs
+ expect(resolved_provider).to eql(Chef::Provider::Service::Insserv)
+ end
+ end
- # old debian uses the Debian provider (does not have insserv or upstart, or update-rc.d???)
- on_platform "debian", platform_version: "4.0", os: "linux" do
- #it_behaves_like "a debian platform using the debian provider"
- end
+ context "when the user has installed upstart" do
+ before do
+ stub_service_providers(:debian, :invokercd, :insserv, :upstart)
+ end
- # Debian replaced the debian provider with insserv in the FIXME:VERSION distro
- on_platform "debian", platform_version: "7.0", os: "linux" do
- it_behaves_like "a debian platform using the insserv provider"
- end
+ it "when only the SysV init script exists, it returns an Insserv provider" do
+ stub_service_configs(:initd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Insserv)
+ end
+
+ it "when both SysV and Upstart scripts exist, it returns a Service::Upstart provider" do
+ stub_service_configs(:initd, :upstart)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
+ end
+
+ it "when only the Upstart script exists, it returns a Service::Upstart provider" do
+ stub_service_configs(:upstart)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
+ end
- on_platform %w{solaris2 openindiana opensolaris nexentacore omnios smartos}, os: "solaris2", platform_version: "5.11" do
- it "returns a Solaris provider" do
- stub_service_providers
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Solaris)
+ it "when both do not exist, it calls the old style provider resolver and returns a Debian Provider" do
+ stub_service_configs
+ expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
+ end
+ end
end
- it "always returns a Solaris provider" do
- # no matter what we stub on the next two lines we should get a Solaris provider
- stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
- stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Solaris)
+ on_platform "ubuntu", platform_version: "15.10", platform_family: "debian", os: "linux" do
+ it_behaves_like "an ubuntu platform with upstart, update-rc.d and systemd"
+
+ it "when the unit-files are missing and system-ctl list-unit-files returns an error" do
+ stub_service_providers(:debian, :invokercd, :upstart, :systemd)
+ stub_service_configs(:initd, :upstart)
+ mock_shellout_command("/bin/systemctl list-unit-files", exitstatus: 1)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Upstart)
+ end
end
- end
- on_platform %w{mswin mingw32 windows}, platform_family: "windows", platform_version: "5.11" do
- it "returns a Windows provider" do
- stub_service_providers
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Windows)
+ on_platform "ubuntu", platform_version: "14.10", platform_family: "debian", os: "linux" do
+ it_behaves_like "an ubuntu platform with upstart, update-rc.d and systemd"
end
- it "always returns a Windows provider" do
- # no matter what we stub on the next two lines we should get a Windows provider
- stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
- stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Windows)
+ on_platform "ubuntu", platform_version: "14.04", platform_family: "debian", os: "linux" do
+ it_behaves_like "an ubuntu platform with upstart and update-rc.d"
end
- end
- on_platform %w{mac_os_x mac_os_x_server}, os: "darwin", platform_family: "mac_os_x", platform_version: "10.9.2" do
- it "returns a Macosx provider" do
- stub_service_providers
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Macosx)
+ on_platform "ubuntu", platform_version: "10.04", platform_family: "debian", os: "linux" do
+ it_behaves_like "an ubuntu platform with upstart and update-rc.d"
end
- it "always returns a Macosx provider" do
- # no matter what we stub on the next two lines we should get a Macosx provider
- stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
- stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Macosx)
+ # old debian uses the Debian provider (does not have insserv or upstart, or update-rc.d???)
+ on_platform "debian", platform_version: "4.0", os: "linux" do
+ #it_behaves_like "a debian platform using the debian provider"
end
- end
- on_platform %w(freebsd netbsd), platform_version: '3.1.4' 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)
+ # Debian replaced the debian provider with insserv in the FIXME:VERSION distro
+ on_platform "debian", platform_version: "7.0", os: "linux" do
+ it_behaves_like "a debian platform using the insserv provider"
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)
+ on_platform %w{solaris2 openindiana opensolaris nexentacore omnios smartos}, os: "solaris2", platform_version: "5.11" do
+ it "returns a Solaris provider" do
+ stub_service_providers
+ stub_service_configs
+ expect(resolved_provider).to eql(Chef::Provider::Service::Solaris)
+ end
+
+ it "always returns a Solaris provider" do
+ # no matter what we stub on the next two lines we should get a Solaris provider
+ stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
+ stub_service_configs(:initd, :upstart, :xinetd, :usr_local_etc_rcd, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Solaris)
+ end
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)
+ on_platform %w{mswin mingw32 windows}, platform_family: "windows", platform_version: "5.11" do
+ it "returns a Windows provider" do
+ stub_service_providers
+ stub_service_configs
+ expect(resolved_provider).to eql(Chef::Provider::Service::Windows)
+ end
+
+ it "always returns a Windows provider" do
+ # no matter what we stub on the next two lines we should get a Windows provider
+ stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
+ stub_service_configs(:initd, :upstart, :xinetd, :usr_local_etc_rcd, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Windows)
+ end
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)
+ on_platform %w{mac_os_x mac_os_x_server}, os: "darwin", platform_family: "mac_os_x", platform_version: "10.9.2" do
+ it "returns a Macosx provider" do
+ stub_service_providers
+ stub_service_configs
+ expect(resolved_provider).to eql(Chef::Provider::Service::Macosx)
+ end
+
+ it "always returns a Macosx provider" do
+ # no matter what we stub on the next two lines we should get a Macosx provider
+ stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
+ stub_service_configs(:initd, :upstart, :xinetd, :usr_local_etc_rcd, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Macosx)
+ end
end
- it "foo" do
- stub_service_providers
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+ on_platform %w(freebsd netbsd), platform_version: '3.1.4' 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 "foo" do
+ stub_service_providers
+ stub_service_configs
+ expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+ end
end
- end
- end
+ 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 ],
+ 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" => {
- "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" => {
- }
- },
- },
+ # TODO should be Chef::Resource::PacmanPackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Pacman ],
- "freebsd" => {
- group: [ Chef::Resource::Group, Chef::Provider::Group::Pw ],
- user: [ Chef::Resource::User, Chef::Provider::User::Pw ],
+ "arch" => {
+ "3.1.4" => {
+ }
+ },
+ },
"freebsd" => {
- "3.1.4" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Pw ],
+ user: [ Chef::Resource::User, Chef::Provider::User::Pw ],
+
+ "freebsd" => {
+ "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 ],
- },
- },
- "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 ],
+ 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 ],
+ },
},
},
- },
-
- "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" => {
- },
- },
- },
+ # 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 ],
- "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 ],
+ "gentoo" => {
+ "3.1.4" => {
+ },
},
},
- %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 ],
+
+ %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 ],
+ },
},
},
- },
- },
+ },
- "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 ],
- user: [ Chef::Resource::User, 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 ],
+ 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, 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, 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 ],
+
+ "smartos" => {
+ "3.1.4" => {
+ },
},
},
- },
- "solaris2" => {
- "nexentacore" => {
- "3.1.4" => {
+ "solaris2" => {
+ "nexentacore" => {
+ "3.1.4" => {
+ },
},
- },
- "omnios" => {
- "3.1.4" => {
- user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
- }
- },
- "openindiana" => {
- "3.1.4" => {
+ "omnios" => {
+ "3.1.4" => {
+ user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
+ }
},
- },
- "opensolaris" => {
- "3.1.4" => {
+ "openindiana" => {
+ "3.1.4" => {
+ },
},
- },
- "solaris2" => {
- user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
- "5.11" => {
- package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
+ "opensolaris" => {
+ "3.1.4" => {
+ },
},
- "5.9" => {
+ "solaris2" => {
+ user: [ Chef::Resource::User, 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) })
- providers.each do |key, value|
- if !key.is_a?(Symbol)
- next_test = test.merge({ filter => key })
- next_filter =
- case filter
- when :os
- :platform_family
- when :platform_family
- :platform
- when :platform
- :platform_version
- when :platform_version
- nil
- else
- raise "Hash too deep; only os, platform_family, platform and platform_version supported"
- end
- create_provider_tests(value, next_test, expected, next_filter)
+
+ def self.create_provider_tests(providers, test, expected, filter)
+ expected = expected.merge(providers.select { |key, value| key.is_a?(Symbol) })
+ providers.each do |key, value|
+ if !key.is_a?(Symbol)
+ next_test = test.merge({ filter => key })
+ next_filter =
+ case filter
+ when :os
+ :platform_family
+ when :platform_family
+ :platform
+ when :platform
+ :platform_version
+ when :platform_version
+ nil
+ else
+ raise "Hash too deep; only os, platform_family, platform and platform_version supported"
+ end
+ create_provider_tests(value, next_test, expected, next_filter)
+ end
end
- end
- # If there is no filter, we're as deep as we need to go
- if !filter
- on_platform test.delete(:platform), test do
- expect_providers(expected)
+ # If there is no filter, we're as deep as we need to go
+ if !filter
+ on_platform test.delete(:platform), test do
+ expect_providers(expected)
+ end
end
end
- end
- create_provider_tests(PROVIDERS, {}, {}, :os)
+ create_provider_tests(PROVIDERS, {}, {}, :os)
+ end
end
diff --git a/spec/unit/registry_helper_spec.rb b/spec/unit/registry_helper_spec.rb
deleted file mode 100644
index b2d0b7b125..0000000000
--- a/spec/unit/registry_helper_spec.rb
+++ /dev/null
@@ -1,390 +0,0 @@
-#
-# Author:: Prajakta Purohit (prajakta@opscode.com)
-# Copyright:: Copyright (c) 2012 Opscode, 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::Provider::RegistryKey do
-
- let(:value1) { { :name => "one", :type => :string, :data => "1" } }
- let(:value1_upcase_name) { {:name => "ONE", :type => :string, :data => "1"} }
- let(:key_path) { 'HKCU\Software\OpscodeNumbers' }
- let(:key) { 'Software\OpscodeNumbers' }
- let(:key_parent) { 'Software' }
- let(:key_to_delete) { 'OpscodeNumbers' }
- let(:sub_key) {'OpscodePrimes'}
- let(:missing_key_path) {'HKCU\Software'}
-
- before(:each) do
- allow_any_instance_of(Chef::Win32::Registry).to receive(:machine_architecture).and_return(:x86_64)
- @registry = Chef::Win32::Registry.new()
-
- #Making the values for registry constants available on unix
- Object.send(:remove_const, 'Win32') if defined?(Win32)
- Win32 = Module.new
- Win32::Registry = Class.new
- Win32::Registry::KEY_SET_VALUE = 0x0002
- Win32::Registry::KEY_QUERY_VALUE = 0x0001
- Win32::Registry::KEY_WRITE = 0x00020000 | 0x0002 | 0x0004
- Win32::Registry::KEY_READ = 0x00020000 | 0x0001 | 0x0008 | 0x0010
-
- Win32::Registry::Error = Class.new(RuntimeError)
-
- @hive_mock = double("::Win32::Registry::HKEY_CURRENT_USER")
- @reg_mock = double("reg")
- end
-
- describe "get_values" do
- it "gets all values for a key if the key exists" do
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@reg_mock).to receive(:map)
- @registry.get_values(key_path)
- end
-
- it "throws an exception if key does not exist" do
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
- expect{@registry.get_values(key_path)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
- end
- end
-
- describe "set_value" do
- it "does nothing if key and hive and value exist" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(true)
- expect(@registry).to receive(:data_exists?).with(key_path, value1).and_return(true)
- @registry.set_value(key_path, value1)
- end
- it "does nothing if case insensitive key and hive and value exist" do
- expect(@registry).to receive(:key_exists!).with(key_path.downcase).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path.downcase).and_return([@hive_mock, key])
- expect(@registry).to receive(:value_exists?).with(key_path.downcase, value1).and_return(true)
- expect(@registry).to receive(:data_exists?).with(key_path.downcase, value1).and_return(true)
- @registry.set_value(key_path.downcase, value1)
- end
- it "does nothing if key and hive and value with a case insensitive name exist" do
- expect(@registry).to receive(:key_exists!).with(key_path.downcase).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path.downcase).and_return([@hive_mock, key])
- expect(@registry).to receive(:value_exists?).with(key_path.downcase, value1_upcase_name).and_return(true)
- expect(@registry).to receive(:data_exists?).with(key_path.downcase, value1_upcase_name).and_return(true)
- @registry.set_value(key_path.downcase, value1_upcase_name)
- end
- it "updates value if key and hive and value exist, but data is different" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(true)
- expect(@registry).to receive(:data_exists?).with(key_path, value1).and_return(false)
- expect(@hive_mock).to receive(:open).with(key, Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@registry).to receive(:get_type_from_name).with(:string).and_return(1)
- expect(@reg_mock).to receive(:write).with("one", 1, "1")
- @registry.set_value(key_path, value1)
- end
-
- it "creates value if the key exists and the value does not exist" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(false)
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@registry).to receive(:get_type_from_name).with(:string).and_return(1)
- expect(@reg_mock).to receive(:write).with("one", 1, "1")
- @registry.set_value(key_path, value1)
- end
-
- it "should raise an exception if the key does not exist" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
- expect {@registry.set_value(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
- end
- end
-
- describe "delete_value" do
- it "deletes value if value exists" do
- expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_SET_VALUE | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@reg_mock).to receive(:delete_value).with("one").and_return(true)
- @registry.delete_value(key_path, value1)
- end
-
- it "raises an exception if the key does not exist" do
- expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
- @registry.delete_value(key_path, value1)
- end
-
- it "does nothing if the value does not exist" do
- expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(false)
- @registry.delete_value(key_path, value1)
- end
- end
-
- describe "create_key" do
- it "creates key if intermediate keys are missing and recursive is set to true" do
- expect(@registry).to receive(:keys_missing?).with(key_path).and_return(true)
- expect(@registry).to receive(:create_missing).with(key_path)
- expect(@registry).to receive(:key_exists?).with(key_path).and_return(false)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:create).with(key, ::Win32::Registry::KEY_WRITE | @registry.registry_system_architecture)
- @registry.create_key(key_path, true)
- end
-
- it "raises an exception if intermediate keys are missing and recursive is set to false" do
- expect(@registry).to receive(:keys_missing?).with(key_path).and_return(true)
- expect{@registry.create_key(key_path, false)}.to raise_error(Chef::Exceptions::Win32RegNoRecursive)
- end
-
- it "does nothing if the key exists" do
- expect(@registry).to receive(:keys_missing?).with(key_path).and_return(true)
- expect(@registry).to receive(:create_missing).with(key_path)
- expect(@registry).to receive(:key_exists?).with(key_path).and_return(true)
- @registry.create_key(key_path, true)
- end
-
- it "create key if intermediate keys not missing and recursive is set to false" do
- expect(@registry).to receive(:keys_missing?).with(key_path).and_return(false)
- expect(@registry).to receive(:key_exists?).with(key_path).and_return(false)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:create).with(key, ::Win32::Registry::KEY_WRITE | @registry.registry_system_architecture)
- @registry.create_key(key_path, false)
- end
-
- it "create key if intermediate keys not missing and recursive is set to true" do
- expect(@registry).to receive(:keys_missing?).with(key_path).and_return(false)
- expect(@registry).to receive(:key_exists?).with(key_path).and_return(false)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:create).with(key, ::Win32::Registry::KEY_WRITE | @registry.registry_system_architecture)
- @registry.create_key(key_path, true)
- end
- end
-
- describe "delete_key", :windows_only do
- it "deletes key if it has subkeys and recursive is set to true" do
- expect(@registry).to receive(:key_exists?).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@registry).to receive(:has_subkeys?).with(key_path).and_return(true)
- expect(@registry).to receive(:get_subkeys).with(key_path).and_return([sub_key])
- expect(@registry).to receive(:key_exists?).with(key_path+"\\"+sub_key).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path+"\\"+sub_key).and_return([@hive_mock, key+"\\"+sub_key])
- expect(@registry).to receive(:has_subkeys?).with(key_path+"\\"+sub_key).and_return(false)
- expect(@registry).to receive(:delete_key_ex).twice
- @registry.delete_key(key_path, true)
- end
-
- it "raises an exception if it has subkeys but recursive is set to false" do
- expect(@registry).to receive(:key_exists?).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@registry).to receive(:has_subkeys?).with(key_path).and_return(true)
- expect{@registry.delete_key(key_path, false)}.to raise_error(Chef::Exceptions::Win32RegNoRecursive)
- end
-
- it "deletes key if the key exists and has no subkeys" do
- expect(@registry).to receive(:key_exists?).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@registry).to receive(:has_subkeys?).with(key_path).and_return(false)
- expect(@registry).to receive(:delete_key_ex)
- @registry.delete_key(key_path, true)
- end
- end
-
- describe "key_exists?" do
- it "returns true if key_exists" do
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@registry.key_exists?(key_path)).to eq(true)
- end
-
- it "returns false if key does not exist" do
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_raise(::Win32::Registry::Error)
- expect(@registry.key_exists?(key_path)).to eq(false)
- end
- end
-
- describe "key_exists!" do
- it "throws an exception if the key_parent does not exist" do
- expect(@registry).to receive(:key_exists?).with(key_path).and_return(false)
- expect{@registry.key_exists!(key_path)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
- end
- end
-
- describe "hive_exists?" do
- it "returns true if the hive exists" do
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- @registry.hive_exists?(key_path) == true
- end
-
- it "returns false if the hive does not exist" do
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_raise(Chef::Exceptions::Win32RegHiveMissing)
- @registry.hive_exists?(key_path) == false
- end
- end
-
- describe "has_subkeys?" do
- it "returns true if the key has subkeys" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@reg_mock).to receive(:each_key).and_yield(key)
- @registry.has_subkeys?(key_path) == true
- end
-
- it "returns false if the key does not have subkeys" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@reg_mock).to receive(:each_key).and_return(no_args())
- expect(@registry.has_subkeys?(key_path)).to eq(false)
- end
-
- it "throws an exception if the key does not exist" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
- expect {@registry.set_value(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
- end
- end
-
- describe "get_subkeys" do
- it "returns the subkeys if they exist" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@reg_mock).to receive(:each_key).and_yield(sub_key)
- @registry.get_subkeys(key_path)
- end
- end
-
- describe "value_exists?" do
- it "throws an exception if the key does not exist" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
- expect {@registry.value_exists?(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
- end
-
- it "returns true if the value exists" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@reg_mock).to receive(:any?).and_yield("one")
- @registry.value_exists?(key_path, value1) == true
- end
-
- it "returns false if the value does not exist" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@reg_mock).to receive(:any?).and_yield(no_args())
- @registry.value_exists?(key_path, value1) == false
- end
- end
-
- describe "data_exists?" do
- it "throws an exception if the key does not exist" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
- expect {@registry.data_exists?(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
- end
-
- it "returns true if the data exists" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@registry).to receive(:get_type_from_name).with(:string).and_return(1)
- expect(@reg_mock).to receive(:each).with(no_args()).and_yield("one", 1, "1")
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@registry.data_exists?(key_path, value1)).to eq(true)
- end
-
- it "returns false if the data does not exist" do
- expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@registry).to receive(:get_type_from_name).with(:string).and_return(1)
- expect(@reg_mock).to receive(:each).with(no_args()).and_yield("one", 1, "2")
- expect(@registry.data_exists?(key_path, value1)).to eq(false)
- end
- end
-
- describe "value_exists!" do
- it "does nothing if the value exists" do
- expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(true)
- @registry.value_exists!(key_path, value1)
- end
-
- it "throws an exception if the value does not exist" do
- expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(false)
- expect{@registry.value_exists!(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegValueMissing)
- end
- end
-
- describe "data_exists!" do
- it "does nothing if the data exists" do
- expect(@registry).to receive(:data_exists?).with(key_path, value1).and_return(true)
- @registry.data_exists!(key_path, value1)
- end
-
- it "throws an exception if the data does not exist" do
- expect(@registry).to receive(:data_exists?).with(key_path, value1).and_return(false)
- expect{@registry.data_exists!(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegDataMissing)
- end
- end
-
- describe "type_matches?" do
- it "returns true if type matches" do
- expect(@registry).to receive(:value_exists!).with(key_path, value1).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@registry).to receive(:get_type_from_name).with(:string).and_return(1)
- expect(@reg_mock).to receive(:each).and_yield("one", 1)
- expect(@registry.type_matches?(key_path, value1)).to eq(true)
- end
-
- it "returns false if type does not match" do
- expect(@registry).to receive(:value_exists!).with(key_path, value1).and_return(true)
- expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
- expect(@hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
- expect(@reg_mock).to receive(:each).and_yield("two", 2)
- expect(@registry.type_matches?(key_path, value1)).to eq(false)
- end
-
- it "throws an exception if value does not exist" do
- expect(@registry).to receive(:value_exists?).with(key_path, value1).and_return(false)
- expect{@registry.type_matches?(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegValueMissing)
- end
- end
-
- describe "type_matches!" do
- it "does nothing if the type_matches" do
- expect(@registry).to receive(:type_matches?).with(key_path, value1).and_return(true)
- @registry.type_matches!(key_path, value1)
- end
-
- it "throws an exception if the type does not match" do
- expect(@registry).to receive(:type_matches?).with(key_path, value1).and_return(false)
- expect{@registry.type_matches!(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegTypesMismatch)
- end
- end
-
- describe "keys_missing?" do
- it "returns true if the keys are missing" do
- expect(@registry).to receive(:key_exists?).with(missing_key_path).and_return(false)
- expect(@registry.keys_missing?(key_path)).to eq(true)
- end
-
- it "returns false if no keys in the path are missing" do
- expect(@registry).to receive(:key_exists?).with(missing_key_path).and_return(true)
- expect(@registry.keys_missing?(key_path)).to eq(false)
- end
- end
-end
diff --git a/spec/unit/resource/file/verification_spec.rb b/spec/unit/resource/file/verification_spec.rb
index 04ae9ad629..6b929789c8 100644
--- a/spec/unit/resource/file/verification_spec.rb
+++ b/spec/unit/resource/file/verification_spec.rb
@@ -88,7 +88,7 @@ describe Chef::Resource::File::Verification do
end
it "warns about deprecation when \%{file} is used" do
- expect(Chef::Log).to receive(:deprecation).with(/%{file} is deprecated/)
+ expect(Chef::Log).to receive(:deprecation).with(/%{file} is deprecated/, /verification_spec\.rb/)
test_command = platform_specific_verify_command('file')
Chef::Resource::File::Verification.new(parent_resource, test_command, {})
.verify(temp_path)
diff --git a/spec/unit/resource/powershell_script_spec.rb b/spec/unit/resource/powershell_script_spec.rb
index 2505c4a3d7..42fcd61a58 100644
--- a/spec/unit/resource/powershell_script_spec.rb
+++ b/spec/unit/resource/powershell_script_spec.rb
@@ -30,24 +30,28 @@ describe Chef::Resource::PowershellScript do
run_context = Chef::RunContext.new(node, nil, nil)
@resource = Chef::Resource::PowershellScript.new("powershell_unit_test", run_context)
-
end
- it "should create a new Chef::Resource::PowershellScript" do
+ it "creates a new Chef::Resource::PowershellScript" do
expect(@resource).to be_a_kind_of(Chef::Resource::PowershellScript)
end
- it "should set convert_boolean_return to false by default" do
+ it "sets convert_boolean_return to false by default" do
expect(@resource.convert_boolean_return).to eq(false)
end
- it "should return the value for convert_boolean_return that was set" do
+ it "returns the value for convert_boolean_return that was set" do
@resource.convert_boolean_return true
expect(@resource.convert_boolean_return).to eq(true)
@resource.convert_boolean_return false
expect(@resource.convert_boolean_return).to eq(false)
end
+ it "raises an error when architecture is i386 on Windows Nano Server" do
+ allow(Chef::Platform).to receive(:windows_nano_server?).and_return(true)
+ expect{@resource.architecture(:i386)}.to raise_error(Chef::Exceptions::Win32ArchitectureIncorrect, "cannot execute script with requested architecture 'i386' on Windows Nano Server")
+ end
+
context "when using guards" do
let(:resource) { @resource }
before(:each) do
@@ -62,32 +66,32 @@ describe Chef::Resource::PowershellScript do
expect(inherited_difference).to eq([])
end
- it "should allow guard interpreter to be set to Chef::Resource::Script" do
+ it "allows guard interpreter to be set to Chef::Resource::Script" do
resource.guard_interpreter(:script)
allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false)
resource.only_if("echo hi")
end
- it "should allow guard interpreter to be set to Chef::Resource::Bash derived from Chef::Resource::Script" do
+ it "allows guard interpreter to be set to Chef::Resource::Bash derived from Chef::Resource::Script" do
resource.guard_interpreter(:bash)
allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false)
resource.only_if("echo hi")
end
- it "should allow guard interpreter to be set to Chef::Resource::PowershellScript derived indirectly from Chef::Resource::Script" do
+ it "allows guard interpreter to be set to Chef::Resource::PowershellScript derived indirectly from Chef::Resource::Script" do
resource.guard_interpreter(:powershell_script)
allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(false)
resource.only_if("echo hi")
end
- it "should enable convert_boolean_return by default for guards in the context of powershell_script when no guard params are specified" do
+ it "enables convert_boolean_return by default for guards in the context of powershell_script when no guard params are specified" do
allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:evaluate_action).and_return(true)
allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
{:convert_boolean_return => true, :code => "$true"}).and_return(Proc.new {})
resource.only_if("$true")
end
- it "should enable convert_boolean_return by default for guards in non-Chef::Resource::Script derived resources when no guard params are specified" do
+ it "enables convert_boolean_return by default for guards in non-Chef::Resource::Script derived resources when no guard params are specified" do
node = Chef::Node.new
run_context = Chef::RunContext.new(node, nil, nil)
file_resource = Chef::Resource::File.new('idontexist', run_context)
@@ -98,21 +102,21 @@ describe Chef::Resource::PowershellScript do
resource.only_if("$true")
end
- it "should enable convert_boolean_return by default for guards in the context of powershell_script when guard params are specified" do
+ it "enables convert_boolean_return by default for guards in the context of powershell_script when guard params are specified" do
guard_parameters = {:cwd => '/etc/chef', :architecture => :x86_64}
allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
{:convert_boolean_return => true, :code => "$true"}.merge(guard_parameters)).and_return(Proc.new {})
resource.only_if("$true", guard_parameters)
end
- it "should pass convert_boolean_return as true if it was specified as true in a guard parameter" do
+ it "passes convert_boolean_return as true if it was specified as true in a guard parameter" do
guard_parameters = {:cwd => '/etc/chef', :convert_boolean_return => true, :architecture => :x86_64}
allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
{:convert_boolean_return => true, :code => "$true"}.merge(guard_parameters)).and_return(Proc.new {})
resource.only_if("$true", guard_parameters)
end
- it "should pass convert_boolean_return as false if it was specified as true in a guard parameter" do
+ it "passes convert_boolean_return as false if it was specified as true in a guard parameter" do
other_guard_parameters = {:cwd => '/etc/chef', :architecture => :x86_64}
parameters_with_boolean_disabled = other_guard_parameters.merge({:convert_boolean_return => false, :code => "$true"})
allow_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:block_from_attributes).with(
@@ -127,6 +131,6 @@ describe Chef::Resource::PowershellScript do
let(:resource_name) { :powershell_script }
let(:interpreter_file_name) { 'powershell.exe' }
- it_should_behave_like "a Windows script resource"
+ it_behaves_like "a Windows script resource"
end
end
diff --git a/spec/unit/resource/subversion_spec.rb b/spec/unit/resource/subversion_spec.rb
index 5cd5d0de80..aa4d1ed708 100644
--- a/spec/unit/resource/subversion_spec.rb
+++ b/spec/unit/resource/subversion_spec.rb
@@ -54,6 +54,10 @@ describe Chef::Resource::Subversion do
expect(@svn.svn_arguments).to eq('--no-auth-cache')
end
+ it "sets svn binary to nil by default" do
+ expect(@svn.svn_binary).to be_nil
+ end
+
it "resets svn arguments to nil when given false in the setter" do
@svn.svn_arguments(false)
expect(@svn.svn_arguments).to be_nil
diff --git a/spec/unit/resource_reporter_spec.rb b/spec/unit/resource_reporter_spec.rb
index 4f3a085584..f2c0b8fd8b 100644
--- a/spec/unit/resource_reporter_spec.rb
+++ b/spec/unit/resource_reporter_spec.rb
@@ -50,6 +50,9 @@ describe Chef::ResourceReporter do
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
@run_status = Chef::RunStatus.new(@node, @events)
+ @run_list = Chef::RunList.new
+ @run_list << 'recipe[lobster]' << 'role[rage]' << 'recipe[fist]'
+ @expansion = Chef::RunList::RunListExpansion.new("_default", @run_list.run_list_items)
@run_id = @run_status.run_id
allow(Time).to receive(:now).and_return(@start_time, @end_time)
end
@@ -424,6 +427,10 @@ describe Chef::ResourceReporter do
expect(@report["run_list"]).to eq(Chef::JSONCompat.to_json(@run_status.node.run_list))
end
+ it "includes the expanded_run_list" do
+ expect(@report).to have_key("expanded_run_list")
+ end
+
it "includes the end_time" do
expect(@report).to have_key("end_time")
expect(@report["end_time"]).to eq(@run_status.end_time.to_s)
diff --git a/spec/unit/run_list/run_list_expansion_spec.rb b/spec/unit/run_list/run_list_expansion_spec.rb
index 859219d346..a7df9e749b 100644
--- a/spec/unit/run_list/run_list_expansion_spec.rb
+++ b/spec/unit/run_list/run_list_expansion_spec.rb
@@ -21,7 +21,7 @@ require 'spec_helper'
describe Chef::RunList::RunListExpansion do
before do
@run_list = Chef::RunList.new
- @run_list << 'recipe[lobster]' << 'role[rage]' << 'recipe[fist]'
+ @run_list << 'recipe[lobster::mastercookbook@0.1.0]' << 'role[rage]' << 'recipe[fist@0.1]'
@expansion = Chef::RunList::RunListExpansion.new("_default", @run_list.run_list_items)
end
@@ -59,7 +59,7 @@ describe Chef::RunList::RunListExpansion do
end
it "has the correct list of recipes for the given environment" do
- expect(@expansion.recipes).to eq(["lobster", "prod-only", "fist"])
+ expect(@expansion.recipes).to eq(["lobster::mastercookbook", "prod-only", "fist"])
end
end
@@ -82,19 +82,34 @@ describe Chef::RunList::RunListExpansion do
describe "after expanding a run list" do
before do
@first_role = Chef::Role.new
+ @first_role.name('rage')
@first_role.run_list('role[mollusk]')
@first_role.default_attributes({'foo' => 'bar'})
@first_role.override_attributes({'baz' => 'qux'})
@second_role = Chef::Role.new
+ @second_role.name('rage')
@second_role.run_list('recipe[crabrevenge]')
@second_role.default_attributes({'foo' => 'boo'})
@second_role.override_attributes({'baz' => 'bux'})
allow(@expansion).to receive(:fetch_role).and_return(@first_role, @second_role)
@expansion.expand
+ @json = '{"id":"_default","run_list":[{"type":"recipe","name":"lobster::mastercookbook","version":"0.1.0",'
+ .concat(
+'"skipped":false},{"type":"role","name":"rage","children":[{"type":"role","name":"mollusk","children":[],"missing":null,'
+ .concat(
+'"error":null,"skipped":null},{"type":"recipe","name":"crabrevenge","version":null,"skipped":false}],"missing":null,'
+ .concat(
+'"error":null,"skipped":null},{"type":"recipe","name":"fist","version":"0.1","skipped":false}]}')))
+
+ end
+
+ it "produces json tree upon tracing expansion" do
+ jsonRunList = @expansion.to_json
+ expect(jsonRunList).to eq(@json)
end
it "has the ordered list of recipes" do
- expect(@expansion.recipes).to eq(['lobster', 'crabrevenge', 'fist'])
+ expect(@expansion.recipes).to eq(['lobster::mastercookbook', 'crabrevenge', 'fist'])
end
it "has the merged attributes from the roles with outer roles overriding inner" do
diff --git a/spec/unit/run_list/versioned_recipe_list_spec.rb b/spec/unit/run_list/versioned_recipe_list_spec.rb
index 9c3ecaa0dd..be57d6c944 100644
--- a/spec/unit/run_list/versioned_recipe_list_spec.rb
+++ b/spec/unit/run_list/versioned_recipe_list_spec.rb
@@ -187,4 +187,9 @@ describe Chef::RunList::VersionedRecipeList do
end
end
+ context "with duplicated names", :chef_gte_13_only do
+ it "should fail in Chef 13" do
+ expect(list).to_not respond_to(:with_duplicate_names)
+ end
+ end
end
diff --git a/spec/unit/search/query_spec.rb b/spec/unit/search/query_spec.rb
index 59ac80f228..f85b1760d4 100644
--- a/spec/unit/search/query_spec.rb
+++ b/spec/unit/search/query_spec.rb
@@ -83,6 +83,8 @@ describe Chef::Search::Query do
describe "search" do
let(:query_string) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0" }
let(:query_string_continue) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=4" }
+ 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) { {
"rows" => [
@@ -149,6 +151,14 @@ describe Chef::Search::Query do
r
}
+ let(:big_response_empty) {
+ {
+ "start" => 0,
+ "total" => 8,
+ "rows" => []
+ }
+ }
+
let(:big_response_end) {
r = response.dup
r["start"] = 4
@@ -208,7 +218,7 @@ describe Chef::Search::Query do
it "pages through the responses" do
@call_me = double("blocky")
response["rows"].each { |r| expect(@call_me).to receive(:do).with(r) }
- query.search(:node, "*:*", sort: nil, start: 0, rows: 1) { |r| @call_me.do(r) }
+ query.search(:node, "*:*", sort: nil, start: 0, rows: 4) { |r| @call_me.do(r) }
end
it "sends multiple API requests when the server indicates there is more data" do
@@ -219,6 +229,14 @@ describe Chef::Search::Query do
end
end
+ it "paginates correctly in the face of filtered nodes" do
+ expect(rest).to receive(:get_rest).with(query_string_with_rows).and_return(big_response_empty)
+ expect(rest).to receive(:get_rest).with(query_string_continue_with_rows).and_return(big_response_end)
+ query.search(:node, "platform:rhel", rows: 4) do |r|
+ nil
+ end
+ end
+
context "when :filter_result is provided as a result" do
include_context "filtered search" do
let(:filter_key) { :filter_result }
diff --git a/spec/unit/win32/registry_spec.rb b/spec/unit/win32/registry_spec.rb
new file mode 100644
index 0000000000..56def30638
--- /dev/null
+++ b/spec/unit/win32/registry_spec.rb
@@ -0,0 +1,394 @@
+#
+# Author:: Prajakta Purohit (prajakta@opscode.com)
+# Copyright:: Copyright (c) 2012 Opscode, 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::Win32::Registry do
+ include_context "Win32"
+
+ let(:value1) { { :name => "one", :type => :string, :data => "1" } }
+ let(:value1_upcase_name) { {:name => "ONE", :type => :string, :data => "1"} }
+ let(:key_path) { 'HKCU\Software\OpscodeNumbers' }
+ let(:key) { 'Software\OpscodeNumbers' }
+ let(:key_parent) { 'Software' }
+ let(:key_to_delete) { 'OpscodeNumbers' }
+ let(:sub_key) {'OpscodePrimes'}
+ let(:missing_key_path) {'HKCU\Software'}
+ let(:registry) { Chef::Win32::Registry.new() }
+ let(:hive_mock) { double("::Win32::Registry::KHKEY_CURRENT_USER") }
+ let(:reg_mock) { double("reg") }
+
+ before(:all) do
+ Win32::Registry = Class.new
+ Win32::Registry::Error = Class.new(RuntimeError)
+ end
+
+ before(:each) do
+ allow_any_instance_of(Chef::Win32::Registry).to receive(:machine_architecture).and_return(:x86_64)
+
+ #Making the values for registry constants available on unix
+ Win32::Registry::KEY_SET_VALUE = 0x0002
+ Win32::Registry::KEY_QUERY_VALUE = 0x0001
+ Win32::Registry::KEY_WRITE = 0x00020000 | 0x0002 | 0x0004
+ Win32::Registry::KEY_READ = 0x00020000 | 0x0001 | 0x0008 | 0x0010
+ end
+
+ after(:each) do
+ Win32::Registry.send(:remove_const, 'KEY_SET_VALUE') if defined?(Win32::Registry::KEY_SET_VALUE)
+ Win32::Registry.send(:remove_const, 'KEY_QUERY_VALUE') if defined?(Win32::Registry::KEY_QUERY_VALUE)
+ Win32::Registry.send(:remove_const, 'KEY_READ') if defined?(Win32::Registry::KEY_READ)
+ Win32::Registry.send(:remove_const, 'KEY_WRITE') if defined?(Win32::Registry::KEY_WRITE)
+ end
+
+ describe "get_values" do
+ it "gets all values for a key if the key exists" do
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(reg_mock).to receive(:map)
+ registry.get_values(key_path)
+ end
+
+ it "throws an exception if key does not exist" do
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
+ expect{registry.get_values(key_path)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ end
+
+ describe "set_value" do
+ it "does nothing if key and hive and value exist" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(registry).to receive(:value_exists?).with(key_path, value1).and_return(true)
+ expect(registry).to receive(:data_exists?).with(key_path, value1).and_return(true)
+ registry.set_value(key_path, value1)
+ end
+ it "does nothing if case insensitive key and hive and value exist" do
+ expect(registry).to receive(:key_exists!).with(key_path.downcase).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path.downcase).and_return([hive_mock, key])
+ expect(registry).to receive(:value_exists?).with(key_path.downcase, value1).and_return(true)
+ expect(registry).to receive(:data_exists?).with(key_path.downcase, value1).and_return(true)
+ registry.set_value(key_path.downcase, value1)
+ end
+ it "does nothing if key and hive and value with a case insensitive name exist" do
+ expect(registry).to receive(:key_exists!).with(key_path.downcase).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path.downcase).and_return([hive_mock, key])
+ expect(registry).to receive(:value_exists?).with(key_path.downcase, value1_upcase_name).and_return(true)
+ expect(registry).to receive(:data_exists?).with(key_path.downcase, value1_upcase_name).and_return(true)
+ registry.set_value(key_path.downcase, value1_upcase_name)
+ end
+ it "updates value if key and hive and value exist, but data is different" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(registry).to receive(:value_exists?).with(key_path, value1).and_return(true)
+ expect(registry).to receive(:data_exists?).with(key_path, value1).and_return(false)
+ expect(hive_mock).to receive(:open).with(key, Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(registry).to receive(:get_type_from_name).with(:string).and_return(1)
+ expect(reg_mock).to receive(:write).with("one", 1, "1")
+ registry.set_value(key_path, value1)
+ end
+
+ it "creates value if the key exists and the value does not exist" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(registry).to receive(:value_exists?).with(key_path, value1).and_return(false)
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(registry).to receive(:get_type_from_name).with(:string).and_return(1)
+ expect(reg_mock).to receive(:write).with("one", 1, "1")
+ registry.set_value(key_path, value1)
+ end
+
+ it "should raise an exception if the key does not exist" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
+ expect {registry.set_value(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ end
+
+ describe "delete_value" do
+ it "deletes value if value exists" do
+ expect(registry).to receive(:value_exists?).with(key_path, value1).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_SET_VALUE | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(reg_mock).to receive(:delete_value).with("one").and_return(true)
+ registry.delete_value(key_path, value1)
+ end
+
+ it "raises an exception if the key does not exist" do
+ expect(registry).to receive(:value_exists?).with(key_path, value1).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
+ registry.delete_value(key_path, value1)
+ end
+
+ it "does nothing if the value does not exist" do
+ expect(registry).to receive(:value_exists?).with(key_path, value1).and_return(false)
+ registry.delete_value(key_path, value1)
+ end
+ end
+
+ describe "create_key" do
+ it "creates key if intermediate keys are missing and recursive is set to true" do
+ expect(registry).to receive(:keys_missing?).with(key_path).and_return(true)
+ expect(registry).to receive(:create_missing).with(key_path)
+ expect(registry).to receive(:key_exists?).with(key_path).and_return(false)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:create).with(key, ::Win32::Registry::KEY_WRITE | registry.registry_system_architecture)
+ registry.create_key(key_path, true)
+ end
+
+ it "raises an exception if intermediate keys are missing and recursive is set to false" do
+ expect(registry).to receive(:keys_missing?).with(key_path).and_return(true)
+ expect{registry.create_key(key_path, false)}.to raise_error(Chef::Exceptions::Win32RegNoRecursive)
+ end
+
+ it "does nothing if the key exists" do
+ expect(registry).to receive(:keys_missing?).with(key_path).and_return(true)
+ expect(registry).to receive(:create_missing).with(key_path)
+ expect(registry).to receive(:key_exists?).with(key_path).and_return(true)
+ registry.create_key(key_path, true)
+ end
+
+ it "create key if intermediate keys not missing and recursive is set to false" do
+ expect(registry).to receive(:keys_missing?).with(key_path).and_return(false)
+ expect(registry).to receive(:key_exists?).with(key_path).and_return(false)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:create).with(key, ::Win32::Registry::KEY_WRITE | registry.registry_system_architecture)
+ registry.create_key(key_path, false)
+ end
+
+ it "create key if intermediate keys not missing and recursive is set to true" do
+ expect(registry).to receive(:keys_missing?).with(key_path).and_return(false)
+ expect(registry).to receive(:key_exists?).with(key_path).and_return(false)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:create).with(key, ::Win32::Registry::KEY_WRITE | registry.registry_system_architecture)
+ registry.create_key(key_path, true)
+ end
+ end
+
+ describe "delete_key", :windows_only do
+ it "deletes key if it has subkeys and recursive is set to true" do
+ expect(registry).to receive(:key_exists?).with(key_path).and_return(true)
+ expect(registry).to receive(:has_subkeys?).with(key_path).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key_parent, ::Win32::Registry::KEY_WRITE | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(reg_mock).to receive(:delete_key).with(key_to_delete, true).and_return(true)
+ registry.delete_key(key_path, true)
+ end
+
+ it "raises an exception if it has subkeys but recursive is set to false" do
+ expect(registry).to receive(:key_exists?).with(key_path).and_return(true)
+ expect(registry).to receive(:has_subkeys?).with(key_path).and_return(true)
+ expect{registry.delete_key(key_path, false)}.to raise_error(Chef::Exceptions::Win32RegNoRecursive)
+ end
+
+ it "deletes key if the key exists and has no subkeys" do
+ expect(registry).to receive(:key_exists?).with(key_path).and_return(true)
+ expect(registry).to receive(:has_subkeys?).with(key_path).and_return(false)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key_parent, ::Win32::Registry::KEY_WRITE | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(reg_mock).to receive(:delete_key).with(key_to_delete, true).and_return(true)
+ registry.delete_key(key_path, true)
+ end
+ end
+
+ describe "key_exists?" do
+ it "returns true if key_exists" do
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(registry.key_exists?(key_path)).to eq(true)
+ end
+
+ it "returns false if key does not exist" do
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_raise(::Win32::Registry::Error)
+ expect(registry.key_exists?(key_path)).to eq(false)
+ end
+ end
+
+ describe "key_exists!" do
+ it "throws an exception if the key_parent does not exist" do
+ expect(registry).to receive(:key_exists?).with(key_path).and_return(false)
+ expect{registry.key_exists!(key_path)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ end
+
+ describe "hive_exists?" do
+ it "returns true if the hive exists" do
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ registry.hive_exists?(key_path) == true
+ end
+
+ it "returns false if the hive does not exist" do
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_raise(Chef::Exceptions::Win32RegHiveMissing)
+ registry.hive_exists?(key_path) == false
+ end
+ end
+
+ describe "has_subkeys?" do
+ it "returns true if the key has subkeys" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(reg_mock).to receive(:each_key).and_yield(key)
+ registry.has_subkeys?(key_path) == true
+ end
+
+ it "returns false if the key does not have subkeys" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(reg_mock).to receive(:each_key).and_return(no_args())
+ expect(registry.has_subkeys?(key_path)).to eq(false)
+ end
+
+ it "throws an exception if the key does not exist" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
+ expect {registry.set_value(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ end
+
+ describe "get_subkeys" do
+ it "returns the subkeys if they exist" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(reg_mock).to receive(:each_key).and_yield(sub_key)
+ registry.get_subkeys(key_path)
+ end
+ end
+
+ describe "value_exists?" do
+ it "throws an exception if the key does not exist" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
+ expect {registry.value_exists?(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+
+ it "returns true if the value exists" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(reg_mock).to receive(:any?).and_yield("one")
+ registry.value_exists?(key_path, value1) == true
+ end
+
+ it "returns false if the value does not exist" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(reg_mock).to receive(:any?).and_yield(no_args())
+ registry.value_exists?(key_path, value1) == false
+ end
+ end
+
+ describe "data_exists?" do
+ it "throws an exception if the key does not exist" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
+ expect {registry.data_exists?(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+
+ it "returns true if the data exists" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(registry).to receive(:get_type_from_name).with(:string).and_return(1)
+ expect(reg_mock).to receive(:each).with(no_args()).and_yield("one", 1, "1")
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(registry.data_exists?(key_path, value1)).to eq(true)
+ end
+
+ it "returns false if the data does not exist" do
+ expect(registry).to receive(:key_exists!).with(key_path).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(registry).to receive(:get_type_from_name).with(:string).and_return(1)
+ expect(reg_mock).to receive(:each).with(no_args()).and_yield("one", 1, "2")
+ expect(registry.data_exists?(key_path, value1)).to eq(false)
+ end
+ end
+
+ describe "value_exists!" do
+ it "does nothing if the value exists" do
+ expect(registry).to receive(:value_exists?).with(key_path, value1).and_return(true)
+ registry.value_exists!(key_path, value1)
+ end
+
+ it "throws an exception if the value does not exist" do
+ expect(registry).to receive(:value_exists?).with(key_path, value1).and_return(false)
+ expect{registry.value_exists!(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegValueMissing)
+ end
+ end
+
+ describe "data_exists!" do
+ it "does nothing if the data exists" do
+ expect(registry).to receive(:data_exists?).with(key_path, value1).and_return(true)
+ registry.data_exists!(key_path, value1)
+ end
+
+ it "throws an exception if the data does not exist" do
+ expect(registry).to receive(:data_exists?).with(key_path, value1).and_return(false)
+ expect{registry.data_exists!(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegDataMissing)
+ end
+ end
+
+ describe "type_matches?" do
+ it "returns true if type matches" do
+ expect(registry).to receive(:value_exists!).with(key_path, value1).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(registry).to receive(:get_type_from_name).with(:string).and_return(1)
+ expect(reg_mock).to receive(:each).and_yield("one", 1)
+ expect(registry.type_matches?(key_path, value1)).to eq(true)
+ end
+
+ it "returns false if type does not match" do
+ expect(registry).to receive(:value_exists!).with(key_path, value1).and_return(true)
+ expect(registry).to receive(:get_hive_and_key).with(key_path).and_return([hive_mock, key])
+ expect(hive_mock).to receive(:open).with(key, ::Win32::Registry::KEY_READ | registry.registry_system_architecture).and_yield(reg_mock)
+ expect(reg_mock).to receive(:each).and_yield("two", 2)
+ expect(registry.type_matches?(key_path, value1)).to eq(false)
+ end
+
+ it "throws an exception if value does not exist" do
+ expect(registry).to receive(:value_exists?).with(key_path, value1).and_return(false)
+ expect{registry.type_matches?(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegValueMissing)
+ end
+ end
+
+ describe "type_matches!" do
+ it "does nothing if the type_matches" do
+ expect(registry).to receive(:type_matches?).with(key_path, value1).and_return(true)
+ registry.type_matches!(key_path, value1)
+ end
+
+ it "throws an exception if the type does not match" do
+ expect(registry).to receive(:type_matches?).with(key_path, value1).and_return(false)
+ expect{registry.type_matches!(key_path, value1)}.to raise_error(Chef::Exceptions::Win32RegTypesMismatch)
+ end
+ end
+
+ describe "keys_missing?" do
+ it "returns true if the keys are missing" do
+ expect(registry).to receive(:key_exists?).with(missing_key_path).and_return(false)
+ expect(registry.keys_missing?(key_path)).to eq(true)
+ end
+
+ it "returns false if no keys in the path are missing" do
+ expect(registry).to receive(:key_exists?).with(missing_key_path).and_return(true)
+ expect(registry.keys_missing?(key_path)).to eq(false)
+ end
+ end
+end
diff --git a/spec/unit/windows_service_spec.rb b/spec/unit/windows_service_spec.rb
index bc5e781c03..396584716d 100644
--- a/spec/unit/windows_service_spec.rb
+++ b/spec/unit/windows_service_spec.rb
@@ -49,7 +49,11 @@ describe "Chef::Application::WindowsService", :windows_only do
allow(instance.instance_variable_get(:@service_signal)).to receive(:wait)
allow(instance).to receive(:state).and_return(4)
expect(instance).to receive(:run_chef_client).and_call_original
- expect(instance).to receive(:shell_out).with("chef-client --no-fork -c test_config_file -L #{tempfile.path}", {:timeout => 7200}).and_return(shell_out_result)
+ expect(instance).to receive(:shell_out).with("chef-client --no-fork -c test_config_file -L #{tempfile.path}",
+ {
+ :timeout => 7200,
+ :logger => Chef::Log
+ }).and_return(shell_out_result)
instance.service_main
tempfile.unlink
end
@@ -63,7 +67,11 @@ describe "Chef::Application::WindowsService", :windows_only do
allow(instance.instance_variable_get(:@service_signal)).to receive(:wait)
allow(instance).to receive(:state).and_return(4)
expect(instance).to receive(:run_chef_client).and_call_original
- expect(instance).to receive(:shell_out).with("chef-client --no-fork -c test_config_file -L #{tempfile.path}", {:timeout => 10}).and_return(shell_out_result)
+ expect(instance).to receive(:shell_out).with("chef-client --no-fork -c test_config_file -L #{tempfile.path}",
+ {
+ :timeout => 10,
+ :logger => Chef::Log
+ }).and_return(shell_out_result)
instance.service_main
tempfile.unlink
end
diff --git a/tasks/external_tests.rb b/tasks/external_tests.rb
index 2ff991ddf7..9304244424 100644
--- a/tasks/external_tests.rb
+++ b/tasks/external_tests.rb
@@ -1,29 +1,53 @@
-task :chef_sugar_spec do
- gem_path = Bundler.environment.specs['chef-sugar'].first.full_gem_path
- system("cd #{gem_path} && rake")
-end
+require 'tempfile'
-task :foodcritic_spec do
- gem_path = Bundler.environment.specs['foodcritic'].first.full_gem_path
- system("cd #{gem_path} && rake test")
+def bundle_exec_with_chef(test_gem, commands)
+ gem_path = Bundler.environment.specs[test_gem].first.full_gem_path
+ gemfile_path = File.join(gem_path, 'Gemfile.chef-external-test')
+ gemfile = File.open(gemfile_path, "w")
+ begin
+ IO.read(File.join(gem_path, 'Gemfile')).each_line do |line|
+ if line =~ /^\s*gemspec/
+ next
+ elsif line =~ /^\s*gem 'chef'|\s*gem "chef"/
+ next
+ elsif line =~ /^\s*dev_gem\s*['"](.+)['"]\s*$/
+ line = "gem '#{$1}', github: 'poise/#{$1}'"
+ elsif line =~ /\s*gem\s*['"]#{test_gem}['"]/ # foodcritic
+ next
+ end
+ gemfile.puts(line)
+ end
+ gemfile.puts("gem 'chef', path: #{File.expand_path('../..', __FILE__).inspect}")
+ gemfile.puts("gemspec path: #{gem_path.inspect}")
+ gemfile.close
+ Dir.chdir(gem_path) do
+ system({ 'BUNDLE_GEMFILE' => gemfile.path, 'RUBYOPT' => nil }, "bundle install")
+ Array(commands).each do |command|
+ system({ 'BUNDLE_GEMFILE' => gemfile.path, 'RUBYOPT' => nil }, "bundle exec #{command}")
+ end
+ end
+ ensure
+ File.delete(gemfile_path)
+ end
end
-task :chefspec_spec do
- gem_path = Bundler.environment.specs['chefspec'].first.full_gem_path
- system("cd #{gem_path} && rake")
-end
+EXTERNAL_PROJECTS = {
+ "chef-zero" => [ "rake spec", "rake pedant" ],
+ "cheffish" => "rake spec",
+ "chef-provisioning" => "rake spec",
+ "chef-provisioning-aws" => "rake spec",
+ "chef-sugar" => "rake",
+ "foodcritic" => "rake test",
+ "chefspec" => "rake",
+ "chef-rewind" => "rake spec",
+ "poise" => "rake spec",
+ "halite" => "rake spec"
+}
-task :chef_rewind_spec do
- gem_path = Bundler.environment.specs['chef-rewind'].first.full_gem_path
- system("cd #{gem_path} && rake spec")
-end
-
-task :poise_spec do
- gem_path = Bundler.environment.specs['poise'].first.full_gem_path
- system("cd #{gem_path} && rake spec")
-end
+task :external_specs => EXTERNAL_PROJECTS.keys.map { |g| :"#{g.sub("-","_")}_spec" }
-task :halite_spec do
- gem_path = Bundler.environment.specs['halite'].first.full_gem_path
- system("cd #{gem_path} && rake spec")
+EXTERNAL_PROJECTS.each do |test_gem, commands|
+ task :"#{test_gem.gsub('-','_')}_spec" do
+ bundle_exec_with_chef(test_gem, commands)
+ end
end
diff --git a/tasks/maintainers.rb b/tasks/maintainers.rb
index 5a2c8d9c2d..73a422fc61 100644
--- a/tasks/maintainers.rb
+++ b/tasks/maintainers.rb
@@ -20,50 +20,191 @@ require 'rake'
SOURCE = File.join(File.dirname(__FILE__), "..", "MAINTAINERS.toml")
TARGET = File.join(File.dirname(__FILE__), "..", "MAINTAINERS.md")
+# The list of repositories that teams should own
+REPOSITORIES = ["chef/chef", "chef/chef-census", "chef/chef-repo",
+ "chef/client-docs", "chef/ffi-yajl", "chef/libyajl2-gem",
+ "chef/mixlib-authentication", "chef/mixlib-cli",
+ "chef/mixlib-config", "chef/mixlib-install", "chef/mixlib-log",
+ "chef/mixlib-shellout", "chef/ohai", "chef/omnibus-chef"]
+
begin
require 'tomlrb'
+ require 'octokit'
+ require 'pp'
task :default => :generate
namespace :maintainers do
desc "Generate MarkDown version of MAINTAINERS file"
task :generate do
- maintainers = Tomlrb.load_file SOURCE
out = "<!-- This is a generated file. Please do not edit directly -->\n\n"
- out << "# " + maintainers["Preamble"]["title"] + "\n\n"
- out << maintainers["Preamble"]["text"] + "\n"
- out << "# " + maintainers["Org"]["Lead"]["title"] + "\n\n"
- out << person(maintainers["people"], maintainers["Org"]["Lead"]["person"]) + "\n\n"
- out << components(maintainers["people"], maintainers["Org"]["Components"])
+ out << "# " + source["Preamble"]["title"] + "\n\n"
+ out << source["Preamble"]["text"] + "\n"
+
+ # The project lead is a special case
+ out << "# " + source["Org"]["Lead"]["title"] + "\n\n"
+ out << format_person(source["Org"]["Lead"]["person"]) + "\n\n"
+
+ out << format_components(source["Org"]["Components"])
File.open(TARGET, "w") { |fn|
fn.write out
}
end
+
+ desc "Synchronize GitHub teams"
+ # there's a special @chef/client-maintainers team that's everyone
+ # and then there's a team per component
+ task :synchronize do
+ Octokit.auto_paginate = true
+ get_github_teams
+ prepare_teams(source["Org"]["Components"].dup)
+ sync_teams!
+ end
+ end
+
+ def github
+ @github ||= Octokit::Client.new(:netrc => true)
+ end
+
+ def source
+ @source ||= Tomlrb.load_file SOURCE
end
- def components(list, cmp)
+ def teams
+ @teams ||= {"client-maintainers" => {"title" => "Client Maintainers"}}
+ end
+
+ def add_members(team, name)
+ teams["client-maintainers"]["members"] ||= []
+ teams["client-maintainers"]["members"] << name
+ teams[team] ||= {}
+ teams[team]["members"] ||= []
+ teams[team]["members"] << name
+ end
+
+ def set_team_title(team, title)
+ teams[team] ||= {}
+ teams[team]["title"] = title
+ end
+
+ def gh_teams
+ @gh_teams ||= {}
+ end
+
+ # we have to resolve team names to ids. While we're at it, we can get the privacy
+ # setting, so we know whether we need to update it
+ def get_github_teams
+ github.org_teams("chef").each do |team|
+ gh_teams[team[:slug]] = {"id" => team[:id], "privacy" => team[:privacy]}
+ end
+ end
+
+ def get_github_team(team)
+ github.team_members(gh_teams[team]["id"]).map do |member|
+ member[:login]
+ end.sort.uniq.map(&:downcase)
+ rescue
+ []
+ end
+
+ def create_team(team)
+ puts "creating new github team: #{team} with title: #{teams[team]["title"]} "
+ t = github.create_team("chef", name: team, description: teams[team]["title"],
+ privacy: "closed", repo_names: REPOSITORIES,
+ accept: "application/vnd.github.ironman-preview+json")
+ gh_teams[team] = { "id" => t[:id], "privacy" => t[:privacy] }
+ end
+
+ def compare_teams(current, desired)
+ # additions are the subtraction of the current state from the desired state
+ # deletions are the subtraction of the desired state from the current state
+ [desired - current, current - desired]
+ end
+
+ def prepare_teams(cmp)
+ %w(text paths).each { |k| cmp.delete(k) }
+ if cmp.key?("team")
+ team = cmp.delete("team")
+ add_members(team, cmp.delete("lieutenant")) if cmp.key?("lieutenant")
+ add_members(team, cmp.delete("maintainers")) if cmp.key?("maintainers")
+ set_team_title(team, cmp.delete("title"))
+ else
+ %w(maintainers lieutenant title).each { |k| cmp.delete(k) }
+ end
+ cmp.each { |_k, v| prepare_teams(v) }
+ end
+
+ def update_team(team, additions, deletions)
+ create_team(team) unless gh_teams.key?(team)
+ update_team_privacy(team)
+ add_team_members(team, additions)
+ remove_team_members(team, deletions)
+ rescue
+ puts "failed for #{team}"
+ end
+
+ def update_team_privacy(team)
+ return if gh_teams[team]["privacy"] == "closed"
+ puts "Setting #{team} privacy to closed from #{gh_teams[team]["privacy"]}"
+ github.update_team(gh_teams[team]["id"], privacy: "closed",
+ accept: "application/vnd.github.ironman-preview+json")
+ end
+
+ def add_team_members(team, additions)
+ additions.each do |member|
+ puts "Adding #{member} to #{team}"
+ github.add_team_membership(gh_teams[team]["id"], member, role: "member",
+ accept: "application/vnd.github.ironman-preview+json")
+ end
+ end
+
+ def remove_team_members(team, deletions)
+ deletions.each do |member|
+ puts "Removing #{member} from #{team}"
+ github.remove_team_membership(gh_teams[team]["id"], member,
+ accept: "application/vnd.github.ironman-preview+json")
+ end
+ end
+
+ def sync_teams!
+ teams.each do |name, details|
+ current = get_github_team(name)
+ desired = details["members"].flatten.sort.uniq.map(&:downcase)
+ additions, deletions = compare_teams(current, desired)
+ update_team(name, additions, deletions)
+ end
+ end
+
+ def get_person(person)
+ source["people"][person]
+ end
+
+ def format_components(cmp)
out = "## " + cmp.delete("title") + "\n\n"
out << cmp.delete("text") + "\n" if cmp.has_key?("text")
+ out << "To mention the team, use @chef/#{cmp.delete("team")}\n\n" if cmp.has_key?("team")
if cmp.has_key?("lieutenant")
out << "### Lieutenant\n\n"
- out << person(list, cmp.delete("lieutenant")) + "\n\n"
+ out << format_person(cmp.delete("lieutenant")) + "\n\n"
end
- out << maintainers(list, cmp.delete("maintainers")) + "\n" if cmp.has_key?("maintainers")
+ out << format_maintainers(cmp.delete("maintainers")) + "\n" if cmp.has_key?("maintainers")
cmp.delete("paths")
- cmp.each {|k,v| out << components(list, v) }
+ cmp.each {|k,v| out << format_components(v) }
out
end
- def maintainers(list, people)
+ def format_maintainers(people)
o = "### Maintainers\n\n"
people.each do |p|
- o << person(list, p) + "\n"
+ o << format_person(p) + "\n"
end
o
end
- def person(list, person)
- "* [#{list[person]["Name"]}](https://github.com/#{list[person]["GitHub"]})"
+ def format_person(person)
+ mnt = get_person(person)
+ "* [#{mnt["Name"]}](https://github.com/#{mnt["GitHub"]})"
end
+
rescue LoadError
STDERR.puts "\n*** TomlRb not available.\n\n"
end