summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Dodge <mikedodge04@fb.com>2015-10-27 11:15:21 -0700
committerMike Dodge <mikedodge04@fb.com>2015-10-27 11:15:21 -0700
commit1130d5472a2da541a9c03c4aeb0eac52c71db8fc (patch)
tree0be91e774d58712ff7ae163b144de88d95442aad
parent8c042622fbf94acca813bd6d84d967d2ea08ec43 (diff)
parent30ff430b33dc15cf5d2b17bc0a2115b5bf68f52d (diff)
downloadchef-1130d5472a2da541a9c03c4aeb0eac52c71db8fc.tar.gz
Merge remote-tracking branch 'chef/master'
-rw-r--r--.gitignore3
-rw-r--r--CHANGELOG.md55
-rw-r--r--CONTRIBUTING.md36
-rw-r--r--Gemfile4
-rw-r--r--MAINTAINERS.md1
-rw-r--r--MAINTAINERS.toml7
-rw-r--r--README.md4
-rw-r--r--VERSION2
-rw-r--r--chef-config/VERSION2
-rw-r--r--chef-config/chef-config.gemspec2
-rw-r--r--chef-config/lib/chef-config/version.rb2
-rw-r--r--chef-windows.gemspec2
-rw-r--r--chef.gemspec2
-rw-r--r--lib/chef/api_client/registration.rb3
-rw-r--r--lib/chef/application/apply.rb11
-rw-r--r--lib/chef/application/client.rb12
-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_fs/file_system/file_system_entry.rb2
-rw-r--r--lib/chef/client.rb29
-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_version.rb8
-rw-r--r--lib/chef/data_bag.rb2
-rw-r--r--lib/chef/data_bag_item.rb2
-rw-r--r--lib/chef/digester.rb6
-rw-r--r--lib/chef/dsl/platform_introspection.rb6
-rw-r--r--lib/chef/event_dispatch/base.rb3
-rw-r--r--lib/chef/event_dispatch/dispatcher.rb7
-rw-r--r--lib/chef/exceptions.rb5
-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/error_inspectors/compile_error_inspector.rb4
-rw-r--r--lib/chef/http/decompressor.rb4
-rw-r--r--lib/chef/json_compat.rb1
-rw-r--r--lib/chef/knife/bootstrap.rb45
-rw-r--r--lib/chef/knife/cookbook_site_install.rb6
-rw-r--r--lib/chef/knife/cookbook_site_unshare.rb4
-rw-r--r--lib/chef/knife/core/bootstrap_context.rb2
-rw-r--r--lib/chef/knife/ssh.rb16
-rw-r--r--lib/chef/knife/status.rb15
-rw-r--r--lib/chef/mixin/properties.rb302
-rw-r--r--lib/chef/node.rb12
-rw-r--r--lib/chef/policy_builder/expand_node_object.rb31
-rw-r--r--lib/chef/property.rb5
-rw-r--r--lib/chef/provider.rb2
-rw-r--r--lib/chef/provider/deploy.rb2
-rw-r--r--lib/chef/provider/execute.rb13
-rw-r--r--lib/chef/provider/group.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/yum.rb2
-rw-r--r--lib/chef/provider/powershell_script.rb26
-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/template/content.rb24
-rw-r--r--lib/chef/provider/user.rb2
-rw-r--r--lib/chef/provider/user/dscl.rb211
-rw-r--r--lib/chef/provider/user/windows.rb8
-rw-r--r--lib/chef/resource.rb339
-rw-r--r--lib/chef/resource/execute.rb2
-rw-r--r--lib/chef/resource/lwrp_base.rb12
-rw-r--r--lib/chef/resource/registry_key.rb2
-rw-r--r--lib/chef/resource/resource_notification.rb15
-rw-r--r--lib/chef/resource/script.rb2
-rw-r--r--lib/chef/resource/windows_script.rb8
-rw-r--r--lib/chef/resource_reporter.rb6
-rw-r--r--lib/chef/rest.rb2
-rw-r--r--lib/chef/run_context.rb4
-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/search/query.rb18
-rw-r--r--lib/chef/util/diff.rb4
-rw-r--r--lib/chef/version.rb2
-rw-r--r--lib/chef/win32/mutex.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/integration/client/client_spec.rb17
-rw-r--r--spec/integration/knife/download_spec.rb9
-rw-r--r--spec/integration/recipes/resource_action_spec.rb5
-rw-r--r--spec/spec_helper.rb2
-rw-r--r--spec/support/platform_helpers.rb5
-rw-r--r--spec/support/shared/functional/windows_script.rb89
-rw-r--r--spec/support/shared/unit/platform_introspector.rb7
-rw-r--r--spec/unit/application/client_spec.rb4
-rw-r--r--spec/unit/application/knife_spec.rb4
-rw-r--r--spec/unit/client_spec.rb3
-rw-r--r--spec/unit/event_dispatch/dispatcher_spec.rb47
-rw-r--r--spec/unit/http_spec.rb2
-rw-r--r--spec/unit/knife/bootstrap_spec.rb39
-rw-r--r--spec/unit/knife/core/bootstrap_context_spec.rb2
-rw-r--r--spec/unit/knife/status_spec.rb26
-rw-r--r--spec/unit/lwrp_spec.rb2
-rw-r--r--spec/unit/mixin/properties_spec.rb97
-rw-r--r--spec/unit/node_spec.rb8
-rw-r--r--spec/unit/policy_builder/expand_node_object_spec.rb29
-rw-r--r--spec/unit/property_spec.rb8
-rw-r--r--spec/unit/provider/deploy_spec.rb2
-rw-r--r--spec/unit/provider/execute_spec.rb9
-rw-r--r--spec/unit/provider/powershell_script_spec.rb78
-rw-r--r--spec/unit/provider/service/solaris_smf_service_spec.rb149
-rw-r--r--spec/unit/provider/template/content_spec.rb54
-rw-r--r--spec/unit/provider/user/dscl_spec.rb14
-rw-r--r--spec/unit/provider/user/windows_spec.rb4
-rw-r--r--spec/unit/provider/user_spec.rb9
-rw-r--r--spec/unit/resource/powershell_script_spec.rb30
-rw-r--r--spec/unit/resource/registry_key_spec.rb4
-rw-r--r--spec/unit/resource/resource_notification_spec.rb89
-rw-r--r--spec/unit/resource_reporter_spec.rb7
-rw-r--r--spec/unit/rest_spec.rb4
-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/windows_service_spec.rb12
119 files changed, 1798 insertions, 773 deletions
diff --git a/.gitignore b/.gitignore
index 39962d2f5f..953f58919f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,6 @@ Vagrantfile
# Kitchen Tests Local Mode Data
kitchen-tests/nodes/*
+
+# Temporary files present during spec runs
+spec/data/test-dir
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 251b68e50e..00041ca584 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,59 @@
+
## 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
+* [**Mikhail Zholobov**](https://github.com/legal90)
+ [pr#3193](https://github.com/chef/chef/pull/3193) provider/user/dscl: Set "comment" default value
+* [**Jordan Evans**](https://github.com/jordane)
+ [pr#3263](https://github.com/chef/chef/pull/3263) `value_for_platform` should use `Chef::VersionConstraint::Platform`
+* [**Scott McGillivray**](https://github.com/thechile)
+ [pr#3450](https://github.com/chef/chef/pull/3450) Fix 'knife cookbook show' to work on root files
+* [**Aubrey Holland**](https://github.com/aub)
+ [pr#3986](https://github.com/chef/chef/pull/3986) fix errors when files go away during chown
+* [**James Michael DuPont**](https://github.com/h4ck3rm1k3)
+ [pr#3973](https://github.com/chef/chef/pull/3973) better error reporting
+* [**Michael Pereira**](https://github.com/MichaelPereira)
+ [pr#3968](https://github.com/chef/chef/pull/3968) Fix cookbook installation from supermarket on windows
+* [**Yukihiko SAWANOBORI**](https://github.com/sawanoboly)
+ [pr#3941](https://github.com/chef/chef/pull/3941) allow reboot by reboot resource with chef-apply
+* [**permyakovsv**](https://github.com/permyakovsv)
+ [pr#3901](https://github.com/chef/chef/pull/3901) Add tmux-split parameter to knife ssh
+* [**Yukihiko SAWANOBORI**](https://github.com/sawanoboly)
+ [pr#3900](https://github.com/chef/chef/pull/3900) Add new option json attributes file to bootstraping
+* [**Evan Gilman**](https://github.com/evan2645)
+ [pr#3864](https://github.com/chef/chef/pull/3864) Knife `bootstrap_environment` should use Explicit config before Implicit
+* [**Ranjib Dey**](https://github.com/ranjib)
+ [pr#3834](https://github.com/chef/chef/pull/3834) Dont spit out stdout and stderr for execute resource failure, if its declared sensitive
+* [**Jeff Blaine**](https://github.com/jblaine)
+ [pr#3776](https://github.com/chef/chef/pull/3776) Changes --hide-healthy to --hide-by-mins MINS
+* [**dbresson**](https://github.com/dbresson)
+ [pr#3650](https://github.com/chef/chef/pull/3650) Define == for node objects
+* [**Jordan Evans**](https://github.com/jordane)
+ [pr#3633](https://github.com/chef/chef/pull/3633) add the word group to `converge_by` call for group provider
+* [**Patrick Connolly**](https://github.com/patcon)
+ [pr#3529](https://github.com/chef/chef/pull/3529) Allow user@hostname format for knife-bootstrap
+
+* [pr#3530](https://github.com/chef/chef/pull/3530) Allow using --sudo option with user's home folder in knife bootstrap
+* [pr#3858](https://github.com/chef/chef/pull/3858) Remove duplicate 'Accept' header in spec
+* [pr#3911](https://github.com/chef/chef/pull/3911) Avoid subclassing Struct.new
+* [pr#3990](https://github.com/chef/chef/pull/3990) Use SHA256 instead of MD5 for `registry_key` when data is not displayable
+* [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):
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e7f2e05f06..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,7 +63,7 @@ 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.
@@ -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,12 +126,12 @@ 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 in issues, and to help us get to the bottom of them quickly
@@ -160,17 +160,17 @@ In order to decrease the back and forth in issues, and to help us get to the bot
### 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 &
@@ -190,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
@@ -203,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/Gemfile b/Gemfile
index 9d5cf0fea1..1bc5c79675 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,7 +3,7 @@ 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 "yard"
@@ -18,6 +18,8 @@ group(:maintenance) do
end
group(:development, :test) do
+ # for profiling
+ gem "ruby-prof"
gem "simplecov"
gem 'rack', "~> 1.5.1"
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index d270a7c7ac..0023cb91bf 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -197,6 +197,7 @@ To mention the team, use @chef/client-freebsd
* [Cory Stephenson](https://github.com/Aevin1387)
* [David Aronsohn](https://github.com/tbunnyman)
+* [Bryant Lippert](https://github.com/agentmeerkat)
## OpenBSD
diff --git a/MAINTAINERS.toml b/MAINTAINERS.toml
index ca0a2e67bc..12eb536fa3 100644
--- a/MAINTAINERS.toml
+++ b/MAINTAINERS.toml
@@ -193,7 +193,8 @@ The specific components of Chef related to a given platform - including (but not
maintainers = [
"Aevin1387",
- "tBunnyMan"
+ "tBunnyMan",
+ "agentmeerkat"
]
[Org.Components.Subsystems.OpenBSD]
@@ -236,6 +237,10 @@ The specific components of Chef related to a given platform - including (but not
Name = "Cory Stephenson"
GitHub = "Aevin1387"
+ [people.agentmeerkat]
+ Name = "Bryant Lippert"
+ GitHub = "agentmeerkat"
+
[people.btm]
Name = "Bryan McLellan"
GitHub = "btm"
diff --git a/README.md b/README.md
index d84d857473..49409efe05 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
# Chef
-[![Code Climate](https://codeclimate.com/github/opscode/chef.png)](https://codeclimate.com/github/opscode/chef)
+[![Code Climate](https://codeclimate.com/github/chef/chef.png)](https://codeclimate.com/github/chef/chef)
[![Build Status Master](https://travis-ci.org/chef/chef.svg?branch=master)](https://travis-ci.org/chef/chef)
[![Build Status Master](https://ci.appveyor.com/api/projects/status/github/chef/chef?branch=master&svg=true&passingText=master%20-%20Ok&pendingText=master%20-%20Pending&failingText=master%20-%20Failing)](https://ci.appveyor.com/project/Chef/chef/branch/master)
@@ -9,7 +9,7 @@ Want to try Chef? Get started with [learnchef](https://learn.chef.io)
* Source: [http://github.com/chef/chef/tree/master](http://github.com/chef/chef/tree/master)
* Tickets/Issues: [https://github.com/chef/chef/issues](https://github.com/chef/chef/issues)
* IRC: `#chef` and `#chef-hacking` on Freenode
-* Mailing list: [http://lists.opscode.com](http://lists.opscode.com)
+* Mailing list: [https://discourse.chef.io](https://discourse.chef.io)
Chef is a configuration management tool designed to bring automation to your
entire infrastructure.
diff --git a/VERSION b/VERSION
index b7d7205d7e..2b4b4d7cb5 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-12.5.0
+12.5.1
diff --git a/chef-config/VERSION b/chef-config/VERSION
index b7d7205d7e..2b4b4d7cb5 100644
--- a/chef-config/VERSION
+++ b/chef-config/VERSION
@@ -1 +1 @@
-12.5.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/version.rb b/chef-config/lib/chef-config/version.rb
index ca9e132f7c..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'
+ VERSION = '12.5.1'
end
#
diff --git a/chef-windows.gemspec b/chef-windows.gemspec
index 2a7ad86f92..1c72d9102a 100644
--- a/chef-windows.gemspec
+++ b/chef-windows.gemspec
@@ -9,7 +9,7 @@ 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 "wmi-lite", "~> 1.0"
diff --git a/chef.gemspec b/chef.gemspec
index b1abe7670a..96673f391a 100644
--- a/chef.gemspec
+++ b/chef.gemspec
@@ -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(Gemfile 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/lib/chef/api_client/registration.rb b/lib/chef/api_client/registration.rb
index de5fc7ac3d..4882323293 100644
--- a/lib/chef/api_client/registration.rb
+++ b/lib/chef/api_client/registration.rb
@@ -68,7 +68,8 @@ class Chef
def assert_destination_writable!
if (File.exists?(destination) && !File.writable?(destination)) or !File.writable?(File.dirname(destination))
- raise Chef::Exceptions::CannotWritePrivateKey, "I cannot write your private key to #{destination} - check permissions?"
+ abs_path = File.expand_path(destination)
+ raise Chef::Exceptions::CannotWritePrivateKey, "I can't write your private key to #{abs_path} - check permissions?"
end
end
diff --git a/lib/chef/application/apply.rb b/lib/chef/application/apply.rb
index 243b441119..4c559542f1 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,
@@ -190,6 +196,7 @@ class Chef::Application::Apply < Chef::Application
ensure
@recipe_fh.close
end
+ Chef::Platform::Rebooter.reboot_if_needed!(runner)
end
def run_application
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index 148257ab89..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",
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_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/client.rb b/lib/chef/client.rb
index 7d5d463242..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
@@ -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/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_version.rb b/lib/chef/cookbook_version.rb
index 3cdfd8c10b..0e9617f98c 100644
--- a/lib/chef/cookbook_version.rb
+++ b/lib/chef/cookbook_version.rb
@@ -4,7 +4,7 @@
# Author:: Tim Hinderliter (<tim@opscode.com>)
# Author:: Seth Falcon (<seth@opscode.com>)
# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Copyright:: Copyright 2008-2011 Opscode, Inc.
+# Copyright:: Copyright 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -450,7 +450,11 @@ class Chef
end
relative_search_path.map {|relative_path| File.join(segment.to_s, relative_path)}
else
- [File.join(segment, path)]
+ if segment.to_sym == :root_files
+ [path]
+ else
+ [File.join(segment, path)]
+ end
end
end
private :preferences_for_path
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/digester.rb b/lib/chef/digester.rb
index 75c4e76859..f2b496b785 100644
--- a/lib/chef/digester.rb
+++ b/lib/chef/digester.rb
@@ -38,7 +38,11 @@ class Chef
end
def generate_checksum(file)
- checksum_file(file, OpenSSL::Digest::SHA256.new)
+ if file.is_a?(StringIO)
+ checksum_io(file, OpenSSL::Digest::SHA256.new)
+ else
+ checksum_file(file, OpenSSL::Digest::SHA256.new)
+ end
end
def self.generate_md5_checksum_for_file(*args)
diff --git a/lib/chef/dsl/platform_introspection.rb b/lib/chef/dsl/platform_introspection.rb
index 2a52010a70..a6bd12d2ef 100644
--- a/lib/chef/dsl/platform_introspection.rb
+++ b/lib/chef/dsl/platform_introspection.rb
@@ -50,7 +50,7 @@ class Chef
def value_for_node(node)
platform, version = node[:platform].to_s, node[:platform_version].to_s
- # Check if we match a version constraint via Chef::VersionConstraint and Chef::Version::Platform
+ # Check if we match a version constraint via Chef::VersionConstraint::Platform and Chef::Version::Platform
matched_value = match_versions(node)
if @values.key?(platform) && @values[platform].key?(version)
@values[platform][version]
@@ -76,11 +76,11 @@ class Chef
keys = @values[platform].keys
keys.each do |k|
begin
- if Chef::VersionConstraint.new(k).include?(node_version)
+ if Chef::VersionConstraint::Platform.new(k).include?(node_version)
key_matches << k
end
rescue Chef::Exceptions::InvalidVersionConstraint => e
- Chef::Log.debug "Caught InvalidVersionConstraint. This means that a key in value_for_platform cannot be interpreted as a Chef::VersionConstraint."
+ Chef::Log.debug "Caught InvalidVersionConstraint. This means that a key in value_for_platform cannot be interpreted as a Chef::VersionConstraint::Platform."
Chef::Log.debug(e)
end
end
diff --git a/lib/chef/event_dispatch/base.rb b/lib/chef/event_dispatch/base.rb
index 1c9a58be23..585a3db174 100644
--- a/lib/chef/event_dispatch/base.rb
+++ b/lib/chef/event_dispatch/base.rb
@@ -384,6 +384,9 @@ class Chef
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
# there's no semantic information about the content or importance of the
diff --git a/lib/chef/event_dispatch/dispatcher.rb b/lib/chef/event_dispatch/dispatcher.rb
index 966a3f32ec..f3e55539a9 100644
--- a/lib/chef/event_dispatch/dispatcher.rb
+++ b/lib/chef/event_dispatch/dispatcher.rb
@@ -32,8 +32,11 @@ class Chef
mth = s.method(method_name)
# Trim arguments to match what the subscriber expects to allow
# adding new arguments without breaking compat.
- args = args.take(mth.arity) if mth.arity < args.size && mth.arity >= 0
- mth.call(*args)
+ if mth.arity < args.size && mth.arity >= 0
+ mth.call(*args.take(mth.arity))
+ else
+ mth.call(*args)
+ end
end
end
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index 6e7ff2e24a..855c86d9cc 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -74,6 +74,11 @@ class Chef
class InvalidPrivateKey < ArgumentError; end
class MissingKeyAttribute < ArgumentError; end
class KeyCommandInputError < ArgumentError; end
+ class BootstrapCommandInputError < ArgumentError
+ def initialize
+ super "You cannot pass both --json-attributes and --json-attribute-file. Please pass one or none."
+ end
+ end
class InvalidKeyArgument < ArgumentError; end
class InvalidKeyAttribute < ArgumentError; end
class InvalidUserAttribute < ArgumentError; 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/error_inspectors/compile_error_inspector.rb b/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
index 3c22d2e763..621fadce40 100644
--- a/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
+++ b/lib/chef/formatters/error_inspectors/compile_error_inspector.rb
@@ -108,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
@@ -138,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 93236225a2..8502ccb8eb 100644
--- a/lib/chef/knife/bootstrap.rb
+++ b/lib/chef/knife/bootstrap.rb
@@ -42,7 +42,7 @@ class Chef
Chef::Knife::Ssh.load_deps
end
- banner "knife bootstrap FQDN (options)"
+ banner "knife bootstrap [SSH_USER@]FQDN (options)"
option :ssh_user,
:short => "-x USERNAME",
@@ -122,6 +122,11 @@ class Chef
:description => "Execute the bootstrap via sudo",
:boolean => true
+ option :preserve_home,
+ :long => "--sudo-preserve-home",
+ :description => "Preserve non-root user HOME environment variable with sudo",
+ :boolean => true
+
option :use_sudo_password,
:long => "--use-sudo-password",
:description => "Execute the bootstrap via sudo with password",
@@ -164,7 +169,13 @@ class Chef
:long => "--json-attributes",
:description => "A JSON string to be added to the first run of chef-client",
:proc => lambda { |o| Chef::JSONCompat.parse(o) },
- :default => {}
+ :default => nil
+
+ option :first_boot_attributes_from_file,
+ :long => "--json-attribute-file FILE",
+ :description => "A JSON file to be used to the first run of chef-client",
+ :proc => lambda { |o| Chef::JSONCompat.parse(File.read(o)) },
+ :default => nil
option :host_key_verify,
:long => "--[no-]host-key-verify",
@@ -256,13 +267,25 @@ class Chef
"chef-full"
end
+ def host_descriptor
+ Array(@name_args).first
+ end
+
# The server_name is the DNS or IP we are going to connect to, it is not necessarily
# the node name, the fqdn, or the hostname of the server. This is a public API hook
# which knife plugins use or inherit and override.
#
# @return [String] The DNS or IP that bootstrap will connect to
def server_name
- Array(@name_args).first
+ if host_descriptor
+ @server_name ||= host_descriptor.split('@').reverse[0]
+ end
+ end
+
+ def user_name
+ if host_descriptor
+ @user_name ||= host_descriptor.split('@').reverse[1]
+ end
end
def bootstrap_template
@@ -317,13 +340,22 @@ class Chef
)
end
+ def first_boot_attributes
+ @config[:first_boot_attributes] || @config[:first_boot_attributes_from_file] || {}
+ end
+
def render_template
+ @config[:first_boot_attributes] = first_boot_attributes
template_file = find_template
template = IO.read(template_file).chomp
Erubis::Eruby.new(template).evaluate(bootstrap_context)
end
def run
+ if @config[:first_boot_attributes] && @config[:first_boot_attributes_from_file]
+ raise Chef::Exceptions::BootstrapCommandInputError
+ end
+
validate_name_args!
validate_options!
@@ -358,7 +390,7 @@ class Chef
if config[:ssh_password]
raise
else
- ui.info("Failed to authenticate #{config[:ssh_user]} - trying password auth")
+ ui.info("Failed to authenticate #{knife_ssh.config[:ssh_user]} - trying password auth")
knife_ssh_with_password_auth.run
end
end
@@ -389,7 +421,7 @@ class Chef
ssh = Chef::Knife::Ssh.new
ssh.ui = ui
ssh.name_args = [ server_name, ssh_command ]
- ssh.config[:ssh_user] = config[:ssh_user]
+ ssh.config[:ssh_user] = user_name || config[:ssh_user]
ssh.config[:ssh_password] = config[:ssh_password]
ssh.config[:ssh_port] = config[:ssh_port]
ssh.config[:ssh_gateway] = config[:ssh_gateway]
@@ -412,7 +444,8 @@ class Chef
command = render_template
if config[:use_sudo]
- command = config[:use_sudo_password] ? "echo '#{config[:ssh_password]}' | sudo -SH #{command}" : "sudo -H #{command}"
+ sudo_prefix = config[:use_sudo_password] ? "echo '#{config[:ssh_password]}' | sudo -S " : "sudo "
+ command = config[:preserve_home] ? "#{sudo_prefix} #{command}" : "#{sudo_prefix} -H #{command}"
end
command
diff --git a/lib/chef/knife/cookbook_site_install.rb b/lib/chef/knife/cookbook_site_install.rb
index aee8b7fa94..cc68fe7897 100644
--- a/lib/chef/knife/cookbook_site_install.rb
+++ b/lib/chef/knife/cookbook_site_install.rb
@@ -142,7 +142,11 @@ class Chef
def extract_cookbook(upstream_file, version)
ui.info("Uncompressing #{@cookbook_name} version #{version}.")
# FIXME: Detect if we have the bad tar from git on Windows: https://github.com/opscode/chef/issues/1753
- shell_out!("tar zxvf #{convert_path upstream_file}", :cwd => @install_path)
+ extract_command="tar zxvf \"#{convert_path upstream_file}\""
+ if Chef::Platform.windows?
+ extract_command << " --force-local"
+ end
+ shell_out!(extract_command, :cwd => @install_path)
end
def clear_existing_files(cookbook_path)
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 d210b9418f..8613a50cb4 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]
+ @config[:environment]
end
def validation_key
diff --git a/lib/chef/knife/ssh.rb b/lib/chef/knife/ssh.rb
index 62af853e88..89a9608c60 100644
--- a/lib/chef/knife/ssh.rb
+++ b/lib/chef/knife/ssh.rb
@@ -111,6 +111,12 @@ class Chef
:boolean => true,
:proc => Proc.new { :raise }
+ option :tmux_split,
+ :long => "--tmux-split",
+ :description => "Split tmux window.",
+ :boolean => true,
+ :default => false
+
def session
config[:on_error] ||= :skip
ssh_error_handler = Proc.new do |server|
@@ -225,6 +231,8 @@ class Chef
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]
@@ -402,7 +410,11 @@ class Chef
new_window_cmds = lambda do
if session.servers_for.size > 1
[""] + session.servers_for[1..-1].map do |server|
- "new-window -a -n '#{server.host}' #{ssh_dest.call(server)}"
+ if config[:tmux_split]
+ "split-window #{ssh_dest.call(server)}; tmux select-layout tiled"
+ else
+ "new-window -a -n '#{server.host}' #{ssh_dest.call(server)}"
+ end
end
else
[]
@@ -465,7 +477,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/knife/status.rb b/lib/chef/knife/status.rb
index 95f2c724ff..1a61b035cb 100644
--- a/lib/chef/knife/status.rb
+++ b/lib/chef/knife/status.rb
@@ -44,7 +44,11 @@ class Chef
option :hide_healthy,
:short => "-H",
:long => "--hide-healthy",
- :description => "Hide nodes that have run chef in the last hour"
+ :description => "Hide nodes that have run chef in the last hour. [DEPRECATED] Use --hide-by-mins MINS instead"
+
+ option :hide_by_mins,
+ :long => "--hide-by-mins MINS",
+ :description => "Hide nodes that have run chef in the last MINS minutes"
def append_to_query(term)
@query << " AND " unless @query.empty?
@@ -68,12 +72,21 @@ class Chef
append_to_query("chef_environment:#{config[:environment]}") if config[:environment]
if config[:hide_healthy]
+ ui.warn("-H / --hide-healthy is deprecated. Use --hide-by-mins MINS instead")
time = Time.now.to_i
# AND NOT is not valid lucene syntax, so don't use append_to_query
@query << " " unless @query.empty?
@query << "NOT ohai_time:[#{(time - 60*60).to_s} TO #{time.to_s}]"
end
+ if config[:hide_by_mins]
+ hidemins = config[:hide_by_mins].to_i
+ time = Time.now.to_i
+ # AND NOT is not valid lucene syntax, so don't use append_to_query
+ @query << " " unless @query.empty?
+ @query << "NOT ohai_time:[#{(time - hidemins*60).to_s} TO #{time.to_s}]"
+ end
+
@query = @query.empty? ? "*:*" : @query
all_nodes = []
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/node.rb b/lib/chef/node.rb
index 668ddbdc35..83ec7e2550 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -440,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
@@ -609,7 +609,7 @@ 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
@@ -647,6 +647,14 @@ class Chef
"node[#{name}]"
end
+ def ==(other)
+ if other.kind_of?(self.class)
+ self.name == other.name
+ else
+ false
+ end
+ end
+
def <=>(other_node)
self.name <=> other_node.name
end
diff --git a/lib/chef/policy_builder/expand_node_object.rb b/lib/chef/policy_builder/expand_node_object.rb
index 26b98afa52..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
@@ -94,6 +97,33 @@ class Chef
run_context
end
+ # 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
+ 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}")
+
+ @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
+ events.node_load_failed(node_name, e, config)
+ raise
+ end
+
def finish_load_node(node)
@node = node
end
@@ -124,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/property.rb b/lib/chef/property.rb
index c1207d9132..e97d8f9607 100644
--- a/lib/chef/property.rb
+++ b/lib/chef/property.rb
@@ -479,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 ]
@@ -510,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 3138704a55..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.
diff --git a/lib/chef/provider/deploy.rb b/lib/chef/provider/deploy.rb
index 77a0410593..c59200e717 100644
--- a/lib/chef/provider/deploy.rb
+++ b/lib/chef/provider/deploy.rb
@@ -276,7 +276,7 @@ class Chef
def enforce_ownership
converge_by("force ownership of #{@new_resource.deploy_to} to #{@new_resource.group}:#{@new_resource.user}") do
- FileUtils.chown_R(@new_resource.user, @new_resource.group, @new_resource.deploy_to)
+ FileUtils.chown_R(@new_resource.user, @new_resource.group, @new_resource.deploy_to, :force => true)
Chef::Log.info("#{@new_resource} set user to #{@new_resource.user}") if @new_resource.user
Chef::Log.info("#{@new_resource} set group to #{@new_resource.group}") if @new_resource.group
end
diff --git a/lib/chef/provider/execute.rb b/lib/chef/provider/execute.rb
index b44112c19e..30de0d3b9e 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
@@ -58,7 +58,16 @@ class Chef
end
converge_by("execute #{description}") do
- result = shell_out!(command, opts)
+ begin
+ shell_out!(command, opts)
+ rescue Mixlib::ShellOut::ShellCommandFailed
+ if sensitive?
+ raise Mixlib::ShellOut::ShellCommandFailed,
+ "Command execution failed. STDOUT/STDERR suppressed for sensitive resource"
+ else
+ raise
+ end
+ end
Chef::Log.info("#{new_resource} ran successfully")
end
end
diff --git a/lib/chef/provider/group.rb b/lib/chef/provider/group.rb
index a802758dce..a1cf92058d 100644
--- a/lib/chef/provider/group.rb
+++ b/lib/chef/provider/group.rb
@@ -125,7 +125,7 @@ class Chef
def action_create
case @group_exists
when false
- converge_by("create #{@new_resource.group_name}") do
+ converge_by("create group #{@new_resource.group_name}") do
create_group
Chef::Log.info("#{@new_resource} created")
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/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_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/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..1faf2438ac 100644
--- a/lib/chef/provider/user/dscl.rb
+++ b/lib/chef/provider/user/dscl.rb
@@ -44,6 +44,10 @@ class Chef
# This provider only supports Mac OSX versions 10.7 and above
class Dscl < Chef::Provider::User
+ attr_accessor :user_info
+ attr_accessor :authentication_authority
+ attr_accessor :password_shadow_conversion_algorithm
+
provides :user, os: "darwin"
def define_resource_requirements
@@ -56,19 +60,19 @@ class Chef
requirements.assert(:all_actions) do |a|
a.assertion { ::File.exists?("/usr/bin/dscl") }
- a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/dscl' on the system for #{@new_resource}!")
+ a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/dscl' on the system for #{new_resource}!")
end
requirements.assert(:all_actions) do |a|
a.assertion { ::File.exists?("/usr/bin/plutil") }
- a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/plutil' on the system for #{@new_resource}!")
+ a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/plutil' on the system for #{new_resource}!")
end
requirements.assert(:create, :modify, :manage) do |a|
a.assertion do
- if @new_resource.password && mac_osx_version_greater_than_10_7?
+ if new_resource.password && mac_osx_version_greater_than_10_7?
# SALTED-SHA512 password shadow hashes are not supported on 10.8 and above.
- !salted_sha512?(@new_resource.password)
+ !salted_sha512?(new_resource.password)
else
true
end
@@ -80,10 +84,10 @@ in 'password', with the associated 'salt' and 'iterations'.")
requirements.assert(:create, :modify, :manage) do |a|
a.assertion do
- if @new_resource.password && mac_osx_version_greater_than_10_7? && salted_sha512_pbkdf2?(@new_resource.password)
+ if new_resource.password && mac_osx_version_greater_than_10_7? && salted_sha512_pbkdf2?(new_resource.password)
# salt and iterations should be specified when
# SALTED-SHA512-PBKDF2 password shadow hash is given
- !@new_resource.salt.nil? && !@new_resource.iterations.nil?
+ !new_resource.salt.nil? && !new_resource.iterations.nil?
else
true
end
@@ -94,9 +98,9 @@ in 'password', with the associated 'salt' and 'iterations'.")
requirements.assert(:create, :modify, :manage) do |a|
a.assertion do
- if @new_resource.password && !mac_osx_version_greater_than_10_7?
+ if new_resource.password && !mac_osx_version_greater_than_10_7?
# On 10.7 SALTED-SHA512-PBKDF2 is not supported
- !salted_sha512_pbkdf2?(@new_resource.password)
+ !salted_sha512_pbkdf2?(new_resource.password)
else
true
end
@@ -109,21 +113,21 @@ user password using shadow hash.")
end
def load_current_resource
- @current_resource = Chef::Resource::User.new(@new_resource.username)
- @current_resource.username(@new_resource.username)
+ @current_resource = Chef::Resource::User.new(new_resource.username)
+ current_resource.username(new_resource.username)
@user_info = read_user_info
- if @user_info
- @current_resource.uid(dscl_get(@user_info, :uid))
- @current_resource.gid(dscl_get(@user_info, :gid))
- @current_resource.home(dscl_get(@user_info, :home))
- @current_resource.shell(dscl_get(@user_info, :shell))
- @current_resource.comment(dscl_get(@user_info, :comment))
- @authentication_authority = dscl_get(@user_info, :auth_authority)
-
- if @new_resource.password && dscl_get(@user_info, :password) == "********"
+ if user_info
+ current_resource.uid(dscl_get(user_info, :uid))
+ current_resource.gid(dscl_get(user_info, :gid))
+ current_resource.home(dscl_get(user_info, :home))
+ current_resource.shell(dscl_get(user_info, :shell))
+ current_resource.comment(dscl_get(user_info, :comment))
+ @authentication_authority = dscl_get(user_info, :auth_authority)
+
+ if new_resource.password && dscl_get(user_info, :password) == "********"
# A password is set. Let's get the password information from shadow file
- shadow_hash_binary = dscl_get(@user_info, :shadow_hash)
+ shadow_hash_binary = dscl_get(user_info, :shadow_hash)
# Calling shell_out directly since we want to give an input stream
shadow_hash_xml = convert_binary_plist_to_xml(shadow_hash_binary.string)
@@ -132,26 +136,26 @@ user password using shadow hash.")
if shadow_hash["SALTED-SHA512"]
# Convert the shadow value from Base64 encoding to hex before consuming them
@password_shadow_conversion_algorithm = "SALTED-SHA512"
- @current_resource.password(shadow_hash["SALTED-SHA512"].string.unpack('H*').first)
+ current_resource.password(shadow_hash["SALTED-SHA512"].string.unpack('H*').first)
elsif shadow_hash["SALTED-SHA512-PBKDF2"]
@password_shadow_conversion_algorithm = "SALTED-SHA512-PBKDF2"
# Convert the entropy from Base64 encoding to hex before consuming them
- @current_resource.password(shadow_hash["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack('H*').first)
- @current_resource.iterations(shadow_hash["SALTED-SHA512-PBKDF2"]["iterations"])
+ current_resource.password(shadow_hash["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack('H*').first)
+ current_resource.iterations(shadow_hash["SALTED-SHA512-PBKDF2"]["iterations"])
# Convert the salt from Base64 encoding to hex before consuming them
- @current_resource.salt(shadow_hash["SALTED-SHA512-PBKDF2"]["salt"].string.unpack('H*').first)
+ current_resource.salt(shadow_hash["SALTED-SHA512-PBKDF2"]["salt"].string.unpack('H*').first)
else
raise(Chef::Exceptions::User,"Unknown shadow_hash format: #{shadow_hash.keys.join(' ')}")
end
end
- convert_group_name if @new_resource.gid
+ convert_group_name if new_resource.gid
else
@user_exists = false
- Chef::Log.debug("#{@new_resource} user does not exist")
+ Chef::Log.debug("#{new_resource} user does not exist")
end
- @current_resource
+ current_resource
end
#
@@ -190,15 +194,16 @@ user password using shadow hash.")
# Create a user using dscl
#
def dscl_create_user
- run_dscl("create /Users/#{@new_resource.username}")
+ run_dscl("create /Users/#{new_resource.username}")
end
#
# Saves the specified Chef user `comment` into RealName attribute
- # of Mac user.
+ # of Mac user. If `comment` is not specified, it takes `username` value.
#
def dscl_create_comment
- run_dscl("create /Users/#{@new_resource.username} RealName '#{@new_resource.comment}'")
+ comment = new_resource.comment || new_resource.username
+ run_dscl("create /Users/#{new_resource.username} RealName '#{comment}'")
end
#
@@ -207,13 +212,14 @@ user password using shadow hash.")
# from 200 if `system` is set, 500 otherwise.
#
def dscl_set_uid
- @new_resource.uid(get_free_uid) if (@new_resource.uid.nil? || @new_resource.uid == '')
+ # XXX: mutates the new resource
+ new_resource.uid(get_free_uid) if (new_resource.uid.nil? || new_resource.uid == '')
- if uid_used?(@new_resource.uid)
- raise(Chef::Exceptions::RequestedUIDUnavailable, "uid #{@new_resource.uid} is already in use")
+ if uid_used?(new_resource.uid)
+ raise(Chef::Exceptions::RequestedUIDUnavailable, "uid #{new_resource.uid} is already in use")
end
- run_dscl("create /Users/#{@new_resource.username} UniqueID #{@new_resource.uid}")
+ run_dscl("create /Users/#{new_resource.username} UniqueID #{new_resource.uid}")
end
#
@@ -222,7 +228,7 @@ user password using shadow hash.")
#
def get_free_uid(search_limit=1000)
uid = nil
- base_uid = @new_resource.system ? 200 : 500
+ base_uid = new_resource.system ? 200 : 500
next_uid_guess = base_uid
users_uids = run_dscl("list /Users uid")
while(next_uid_guess < search_limit + base_uid)
@@ -248,7 +254,7 @@ user password using shadow hash.")
tmap
end
if uid_map[uid.to_s]
- unless uid_map[uid.to_s] == @new_resource.username.to_s
+ unless uid_map[uid.to_s] == new_resource.username.to_s
return true
end
end
@@ -257,18 +263,23 @@ 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?
+ # XXX: mutates the new resource
+ 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
+ possible_gid = run_dscl("read /Groups/#{new_resource.gid} PrimaryGroupID").split(" ").last
rescue Chef::Exceptions::DsclCommandFailed => e
- raise Chef::Exceptions::GroupIDNotFound.new("Group not found for #{@new_resource.gid} when creating user #{@new_resource.username}")
+ raise Chef::Exceptions::GroupIDNotFound.new("Group not found for #{new_resource.gid} when creating user #{new_resource.username}")
end
- @new_resource.gid(possible_gid) if possible_gid && possible_gid.match(/^\d+$/)
+ # XXX: mutates the new resource
+ new_resource.gid(possible_gid) if possible_gid && possible_gid.match(/^\d+$/)
end
- run_dscl("create /Users/#{@new_resource.username} PrimaryGroupID '#{@new_resource.gid}'")
+ run_dscl("create /Users/#{new_resource.username} PrimaryGroupID '#{new_resource.gid}'")
end
#
@@ -276,15 +287,15 @@ user password using shadow hash.")
# directory is managed (moved / created) for the user.
#
def dscl_set_home
- if @new_resource.home.nil? || @new_resource.home.empty?
- run_dscl("delete /Users/#{@new_resource.username} NFSHomeDirectory")
+ if new_resource.home.nil? || new_resource.home.empty?
+ run_dscl("delete /Users/#{new_resource.username} NFSHomeDirectory")
return
end
- if @new_resource.supports[:manage_home]
+ if new_resource.supports[:manage_home]
validate_home_dir_specification!
- if (@current_resource.home == @new_resource.home) && !new_home_exists?
+ if (current_resource.home == new_resource.home) && !new_home_exists?
ditto_home
elsif !current_home_exists? && !new_home_exists?
ditto_home
@@ -292,49 +303,49 @@ user password using shadow hash.")
move_home
end
end
- run_dscl("create /Users/#{@new_resource.username} NFSHomeDirectory '#{@new_resource.home}'")
+ run_dscl("create /Users/#{new_resource.username} NFSHomeDirectory '#{new_resource.home}'")
end
def validate_home_dir_specification!
- unless @new_resource.home =~ /^\//
- raise(Chef::Exceptions::InvalidHomeDirectory,"invalid path spec for User: '#{@new_resource.username}', home directory: '#{@new_resource.home}'")
+ unless new_resource.home =~ /^\//
+ raise(Chef::Exceptions::InvalidHomeDirectory,"invalid path spec for User: '#{new_resource.username}', home directory: '#{new_resource.home}'")
end
end
def current_home_exists?
- ::File.exist?("#{@current_resource.home}")
+ ::File.exist?("#{current_resource.home}")
end
def new_home_exists?
- ::File.exist?("#{@new_resource.home}")
+ ::File.exist?("#{new_resource.home}")
end
def ditto_home
skel = "/System/Library/User Template/English.lproj"
raise(Chef::Exceptions::User,"can't find skel at: #{skel}") unless ::File.exists?(skel)
- shell_out! "ditto '#{skel}' '#{@new_resource.home}'"
- ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
+ shell_out! "ditto '#{skel}' '#{new_resource.home}'"
+ ::FileUtils.chown_R(new_resource.username,new_resource.gid.to_s,new_resource.home)
end
def move_home
- Chef::Log.debug("#{@new_resource} moving #{self} home from #{@current_resource.home} to #{@new_resource.home}")
+ Chef::Log.debug("#{new_resource} moving #{self} home from #{current_resource.home} to #{new_resource.home}")
- src = @current_resource.home
- FileUtils.mkdir_p(@new_resource.home)
+ src = current_resource.home
+ FileUtils.mkdir_p(new_resource.home)
files = ::Dir.glob("#{Chef::Util::PathHelper.escape_glob(src)}/*", ::File::FNM_DOTMATCH) - ["#{src}/.","#{src}/.."]
- ::FileUtils.mv(files,@new_resource.home, :force => true)
+ ::FileUtils.mv(files,new_resource.home, :force => true)
::FileUtils.rmdir(src)
- ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
+ ::FileUtils.chown_R(new_resource.username,new_resource.gid.to_s,new_resource.home)
end
#
# Sets the shell for the user using dscl.
#
def dscl_set_shell
- if @new_resource.shell || ::File.exists?("#{@new_resource.shell}")
- run_dscl("create /Users/#{@new_resource.username} UserShell '#{@new_resource.shell}'")
+ if new_resource.shell || ::File.exists?("#{new_resource.shell}")
+ run_dscl("create /Users/#{new_resource.username} UserShell '#{new_resource.shell}'")
else
- run_dscl("create /Users/#{@new_resource.username} UserShell '/usr/bin/false'")
+ run_dscl("create /Users/#{new_resource.username} UserShell '/usr/bin/false'")
end
end
@@ -345,7 +356,7 @@ user password using shadow hash.")
#
def set_password
# Return if there is no password to set
- return if @new_resource.password.nil?
+ return if new_resource.password.nil?
shadow_info = prepare_password_shadow_info
@@ -355,7 +366,7 @@ user password using shadow hash.")
:input => shadow_info.to_plist, :live_stream => shadow_info_binary)
command.run_command
- if @user_info.nil?
+ if user_info.nil?
# User is just created. read_user_info() will read the fresh information
# for the user with a cache flush. However with experimentation we've seen
# that dscl cache is not immediately updated after the creation of the user
@@ -365,8 +376,8 @@ user password using shadow hash.")
end
# Replace the shadow info in user's plist
- dscl_set(@user_info, :shadow_hash, shadow_info_binary)
- save_user_info(@user_info)
+ dscl_set(user_info, :shadow_hash, shadow_info_binary)
+ save_user_info(user_info)
end
#
@@ -379,12 +390,12 @@ user password using shadow hash.")
iterations = nil
if mac_osx_version_10_7?
- hash_value = if salted_sha512?(@new_resource.password)
- @new_resource.password
+ hash_value = if salted_sha512?(new_resource.password)
+ new_resource.password
else
# Create a random 4 byte salt
salt = OpenSSL::Random.random_bytes(4)
- encoded_password = OpenSSL::Digest::SHA512.hexdigest(salt + @new_resource.password)
+ encoded_password = OpenSSL::Digest::SHA512.hexdigest(salt + new_resource.password)
hash_value = salt.unpack('H*').first + encoded_password
end
@@ -392,16 +403,16 @@ user password using shadow hash.")
shadow_info["SALTED-SHA512"].string = convert_to_binary(hash_value)
shadow_info
else
- if salted_sha512_pbkdf2?(@new_resource.password)
- entropy = convert_to_binary(@new_resource.password)
- salt = convert_to_binary(@new_resource.salt)
- iterations = @new_resource.iterations
+ if salted_sha512_pbkdf2?(new_resource.password)
+ entropy = convert_to_binary(new_resource.password)
+ salt = convert_to_binary(new_resource.salt)
+ iterations = new_resource.iterations
else
salt = OpenSSL::Random.random_bytes(32)
- iterations = @new_resource.iterations # Use the default if not specified by the user
+ iterations = new_resource.iterations # Use the default if not specified by the user
entropy = OpenSSL::PKCS5::pbkdf2_hmac(
- @new_resource.password,
+ new_resource.password,
salt,
iterations,
128,
@@ -427,43 +438,43 @@ user password using shadow hash.")
# and deleting home directory if needed.
#
def remove_user
- if @new_resource.supports[:manage_home]
+ if new_resource.supports[:manage_home]
# Remove home directory
- FileUtils.rm_rf(@current_resource.home)
+ FileUtils.rm_rf(current_resource.home)
end
# Remove the user from its groups
run_dscl("list /Groups").each_line do |group|
if member_of_group?(group.chomp)
- run_dscl("delete /Groups/#{group.chomp} GroupMembership '#{@new_resource.username}'")
+ run_dscl("delete /Groups/#{group.chomp} GroupMembership '#{new_resource.username}'")
end
end
# Remove user account
- run_dscl("delete /Users/#{@new_resource.username}")
+ run_dscl("delete /Users/#{new_resource.username}")
end
#
# Locks the user.
#
def lock_user
- run_dscl("append /Users/#{@new_resource.username} AuthenticationAuthority ';DisabledUser;'")
+ run_dscl("append /Users/#{new_resource.username} AuthenticationAuthority ';DisabledUser;'")
end
#
# Unlocks the user
#
def unlock_user
- auth_string = @authentication_authority.gsub(/AuthenticationAuthority: /,"").gsub(/;DisabledUser;/,"").strip
- run_dscl("create /Users/#{@new_resource.username} AuthenticationAuthority '#{auth_string}'")
+ auth_string = authentication_authority.gsub(/AuthenticationAuthority: /,"").gsub(/;DisabledUser;/,"").strip
+ run_dscl("create /Users/#{new_resource.username} AuthenticationAuthority '#{auth_string}'")
end
#
# Returns true if the user is locked, false otherwise.
#
def locked?
- if @authentication_authority
- !!(@authentication_authority =~ /DisabledUser/ )
+ if authentication_authority
+ !!(authentication_authority =~ /DisabledUser/ )
else
false
end
@@ -485,11 +496,11 @@ user password using shadow hash.")
# given attribute.
#
def diverged?(parameter)
- parameter_updated?(parameter) && (not @new_resource.send(parameter).nil?)
+ parameter_updated?(parameter) && (not new_resource.send(parameter).nil?)
end
def parameter_updated?(parameter)
- not (@new_resource.send(parameter) == @current_resource.send(parameter))
+ not (new_resource.send(parameter) == current_resource.send(parameter))
end
#
@@ -500,11 +511,11 @@ user password using shadow hash.")
# type of the password specified.
#
def diverged_password?
- return false if @new_resource.password.nil?
+ return false if new_resource.password.nil?
# Dscl provider supports both plain text passwords and shadow hashes.
if mac_osx_version_10_7?
- if salted_sha512?(@new_resource.password)
+ if salted_sha512?(new_resource.password)
diverged?(:password)
else
!salted_sha512_password_match?
@@ -514,14 +525,14 @@ user password using shadow hash.")
# will be updated when the user logs in. So it's possible that we will have
# SALTED-SHA512 password in the current_resource. In that case we will force
# password to be updated.
- return true if salted_sha512?(@current_resource.password)
+ return true if salted_sha512?(current_resource.password)
# Some system users don't have salts; this can happen if the system is
# upgraded and the user hasn't logged in yet. In this case, we will force
# the password to be updated.
- return true if @current_resource.salt.nil?
+ return true if current_resource.salt.nil?
- if salted_sha512_pbkdf2?(@new_resource.password)
+ if salted_sha512_pbkdf2?(new_resource.password)
diverged?(:password) || diverged?(:salt) || diverged?(:iterations)
else
!salted_sha512_pbkdf2_password_match?
@@ -543,7 +554,7 @@ user password using shadow hash.")
# GroupMembership: root admin etc
members = membership_info.split(" ")
members.shift # Get rid of GroupMembership: string
- members.include?(@new_resource.username)
+ members.include?(new_resource.username)
end
#
@@ -577,7 +588,7 @@ user password using shadow hash.")
shell_out("dscacheutil '-flushcache'")
begin
- user_plist_file = "#{USER_PLIST_DIRECTORY}/#{@new_resource.username}.plist"
+ user_plist_file = "#{USER_PLIST_DIRECTORY}/#{new_resource.username}.plist"
user_plist_info = run_plutil("convert xml1 -o - #{user_plist_file}")
user_info = Plist::parse_xml(user_plist_info)
rescue Chef::Exceptions::PlistUtilCommandFailed
@@ -591,7 +602,7 @@ user password using shadow hash.")
# in DSCL_PROPERTY_MAP to the disk.
#
def save_user_info(user_info)
- user_plist_file = "#{USER_PLIST_DIRECTORY}/#{@new_resource.username}.plist"
+ user_plist_file = "#{USER_PLIST_DIRECTORY}/#{new_resource.username}.plist"
Plist::Emit.save_plist(user_info, user_plist_file)
run_plutil("convert binary1 #{user_plist_file}")
end
@@ -673,9 +684,9 @@ user password using shadow hash.")
def salted_sha512_password_match?
# Salt is included in the first 4 bytes of shadow data
- salt = @current_resource.password.slice(0,8)
- shadow = OpenSSL::Digest::SHA512.hexdigest(convert_to_binary(salt) + @new_resource.password)
- @current_resource.password == salt + shadow
+ salt = current_resource.password.slice(0,8)
+ shadow = OpenSSL::Digest::SHA512.hexdigest(convert_to_binary(salt) + new_resource.password)
+ current_resource.password == salt + shadow
end
def salted_sha512_pbkdf2?(string)
@@ -683,15 +694,15 @@ user password using shadow hash.")
end
def salted_sha512_pbkdf2_password_match?
- salt = convert_to_binary(@current_resource.salt)
+ salt = convert_to_binary(current_resource.salt)
OpenSSL::PKCS5::pbkdf2_hmac(
- @new_resource.password,
+ new_resource.password,
salt,
- @current_resource.iterations,
+ current_resource.iterations,
128,
OpenSSL::Digest::SHA512.new
- ).unpack('H*').first == @current_resource.password
+ ).unpack('H*').first == current_resource.password
end
end
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/resource.rb b/lib/chef/resource.rb
index 9e40fdccfd..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'
@@ -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,7 +685,6 @@ class Chef
# Resource Definition Interface (for resource developers)
#
- include Chef::Mixin::ParamsValidate
include Chef::Mixin::Deprecation
#
@@ -715,240 +718,6 @@ class Chef
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.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 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.
#
@@ -973,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
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/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/registry_key.rb b/lib/chef/resource/registry_key.rb
index d2e5c4b94c..f1bf7954ce 100644
--- a/lib/chef/resource/registry_key.rb
+++ b/lib/chef/resource/registry_key.rb
@@ -125,7 +125,7 @@ class Chef
scrubbed_value = value.dup
if needs_checksum?(scrubbed_value)
data_io = StringIO.new(scrubbed_value[:data].to_s)
- scrubbed_value[:data] = Chef::Digester.instance.generate_md5_checksum(data_io)
+ scrubbed_value[:data] = Chef::Digester.instance.generate_checksum(data_io)
end
scrubbed << scrubbed_value
end
diff --git a/lib/chef/resource/resource_notification.rb b/lib/chef/resource/resource_notification.rb
index a27ed961c7..4fd61ad1f8 100644
--- a/lib/chef/resource/resource_notification.rb
+++ b/lib/chef/resource/resource_notification.rb
@@ -20,7 +20,15 @@ require 'chef/resource'
class Chef
class Resource
- class Notification < Struct.new(:resource, :action, :notifying_resource)
+ class Notification
+
+ attr_accessor :resource, :action, :notifying_resource
+
+ def initialize(resource, action, notifying_resource)
+ @resource = resource
+ @action = action
+ @notifying_resource = notifying_resource
+ end
def duplicates?(other_notification)
unless other_notification.respond_to?(:resource) && other_notification.respond_to?(:action)
@@ -104,6 +112,11 @@ is defined near #{resource.source_line}
raise err
end
+ def ==(other)
+ return false unless other.is_a?(self.class)
+ other.resource == resource && other.action == action && other.notifying_resource == notifying_resource
+ end
+
end
end
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/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/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 b2a4b13ea4..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
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/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/version.rb b/lib/chef/version.rb
index c0ff754873..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'
+ VERSION = '12.5.1'
end
#
diff --git a/lib/chef/win32/mutex.rb b/lib/chef/win32/mutex.rb
index f4755e9019..0d8eba1b3c 100644
--- a/lib/chef/win32/mutex.rb
+++ b/lib/chef/win32/mutex.rb
@@ -79,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
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/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb
index 5b235e2720..314a9310be 100644
--- a/spec/integration/client/client_spec.rb
+++ b/spec/integration/client/client_spec.rb
@@ -301,6 +301,23 @@ 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
diff --git a/spec/integration/knife/download_spec.rb b/spec/integration/knife/download_spec.rb
index 8842ed5ac4..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
diff --git a/spec/integration/recipes/resource_action_spec.rb b/spec/integration/recipes/resource_action_spec.rb
index 1c84b986cc..6f3f5ab47e 100644
--- a/spec/integration/recipes/resource_action_spec.rb
+++ b/spec/integration/recipes/resource_action_spec.rb
@@ -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
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 92a4daf6d5..4f7fde8eaf 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -128,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?
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/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/platform_introspector.rb b/spec/support/shared/unit/platform_introspector.rb
index 9f42c985f8..df24370fde 100644
--- a/spec/support/shared/unit/platform_introspector.rb
+++ b/spec/support/shared/unit/platform_introspector.rb
@@ -32,6 +32,7 @@ shared_examples_for "a platform introspector" do
# The following @platform_hash keys are used for testing version constraints
@platform_hash['exact_match'] = { '1.2.3' => 'exact', '>= 1.0' => 'not exact'}
@platform_hash['multiple_matches'] = { '~> 2.3.4' => 'matched ~> 2.3.4', '>= 2.3' => 'matched >=2.3' }
+ @platform_hash['invalid_cookbook_version'] = {'>= 21' => 'Matches a single number'}
@platform_hash['successful_matches'] = { '< 3.0' => 'matched < 3.0', '>= 3.0' => 'matched >= 3.0' }
@platform_family_hash = {
@@ -95,6 +96,12 @@ shared_examples_for "a platform introspector" do
expect {platform_introspector.value_for_platform(@platform_hash)}.to raise_error(RuntimeError)
end
+ it 'should not require .0 to match >= 21.0' do
+ node.automatic_attrs[:platform] = 'invalid_cookbook_version'
+ node.automatic_attrs[:platform_version] = '21'
+ expect(platform_introspector.value_for_platform(@platform_hash)).to eq('Matches a single number')
+ end
+
it 'should return the value for that match' do
node.automatic_attrs[:platform] = 'successful_matches'
node.automatic_attrs[:platform_version] = '2.9'
diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb
index 831a3fc19e..727536f1f8 100644
--- a/spec/unit/application/client_spec.rb
+++ b/spec/unit/application/client_spec.rb
@@ -288,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/client_spec.rb b/spec/unit/client_spec.rb
index f736c38859..8fbf56844e 100644
--- a/spec/unit/client_spec.rb
+++ b/spec/unit/client_spec.rb
@@ -375,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)
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/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_spec.rb b/spec/unit/knife/bootstrap_spec.rb
index 48aae3e61b..342c1878f1 100644
--- a/spec/unit/knife/bootstrap_spec.rb
+++ b/spec/unit/knife/bootstrap_spec.rb
@@ -235,12 +235,39 @@ describe Chef::Knife::Bootstrap do
expect(knife.render_template).to eq('{"run_list":["role[base]","recipe[cupcakes]"]}')
end
- it "should have foo => {bar => baz} in the first_boot" do
- knife.parse_options(["-j", '{"foo":{"bar":"baz"}}'])
- knife.merge_configs
- expected_hash = FFI_Yajl::Parser.new.parse('{"foo":{"bar":"baz"},"run_list":[]}')
- actual_hash = FFI_Yajl::Parser.new.parse(knife.render_template)
- expect(actual_hash).to eq(expected_hash)
+ context "with bootstrap_attribute options" do
+ let(:jsonfile) {
+ file = Tempfile.new (['node', '.json'])
+ File.open(file.path, "w") {|f| f.puts '{"foo":{"bar":"baz"}}' }
+ file
+ }
+
+ it "should have foo => {bar => baz} in the first_boot from cli" do
+ knife.parse_options(["-j", '{"foo":{"bar":"baz"}}'])
+ knife.merge_configs
+ expected_hash = FFI_Yajl::Parser.new.parse('{"foo":{"bar":"baz"},"run_list":[]}')
+ actual_hash = FFI_Yajl::Parser.new.parse(knife.render_template)
+ expect(actual_hash).to eq(expected_hash)
+ end
+
+ it "should have foo => {bar => baz} in the first_boot from file" do
+ knife.parse_options(["--json-attribute-file", jsonfile.path])
+ knife.merge_configs
+ expected_hash = FFI_Yajl::Parser.new.parse('{"foo":{"bar":"baz"},"run_list":[]}')
+ actual_hash = FFI_Yajl::Parser.new.parse(knife.render_template)
+ expect(actual_hash).to eq(expected_hash)
+ jsonfile.close
+ end
+
+ context "when --json-attributes and --json-attribute-file were both passed" do
+ it "raises a Chef::Exceptions::BootstrapCommandInputError with the proper error message" do
+ knife.parse_options(["-j", '{"foo":{"bar":"baz"}}'])
+ knife.parse_options(["--json-attribute-file", jsonfile.path])
+ knife.merge_configs
+ expect{ knife.run }.to raise_error(Chef::Exceptions::BootstrapCommandInputError)
+ jsonfile.close
+ end
+ end
end
end
diff --git a/spec/unit/knife/core/bootstrap_context_spec.rb b/spec/unit/knife/core/bootstrap_context_spec.rb
index 0433ef9983..4d69d6300a 100644
--- a/spec/unit/knife/core/bootstrap_context_spec.rb
+++ b/spec/unit/knife/core/bootstrap_context_spec.rb
@@ -91,7 +91,7 @@ EXPECTED
end
describe "when bootstrapping into a specific environment" do
- let(:chef_config){ {:environment => "prodtastic"} }
+ let(:config){ {:environment => "prodtastic"} }
it "starts chef in the configured environment" do
expect(bootstrap_context.start_chef).to eq('chef-client -j /etc/chef/first-boot.json -E prodtastic')
end
diff --git a/spec/unit/knife/status_spec.rb b/spec/unit/knife/status_spec.rb
index ee44f3b3fd..11728a6f9b 100644
--- a/spec/unit/knife/status_spec.rb
+++ b/spec/unit/knife/status_spec.rb
@@ -44,9 +44,9 @@ describe Chef::Knife::Status do
@knife.run
end
- it "should filter healthy nodes" do
- @knife.config[:hide_healthy] = true
- expect(@query).to receive(:search).with(:node, "NOT ohai_time:[1428569820 TO 1428573420]", opts)
+ it "should filter by nodes older than some mins" do
+ @knife.config[:hide_by_mins] = 59
+ expect(@query).to receive(:search).with(:node, "NOT ohai_time:[1428569880 TO 1428573420]", opts)
@knife.run
end
@@ -56,10 +56,10 @@ describe Chef::Knife::Status do
@knife.run
end
- it "should filter by environment and health" do
+ it "should filter by environment and nodes older than some mins" do
@knife.config[:environment] = "production"
- @knife.config[:hide_healthy] = true
- expect(@query).to receive(:search).with(:node, "chef_environment:production NOT ohai_time:[1428569820 TO 1428573420]", opts)
+ @knife.config[:hide_by_mins] = 59
+ expect(@query).to receive(:search).with(:node, "chef_environment:production NOT ohai_time:[1428569880 TO 1428573420]", opts)
@knife.run
end
@@ -79,22 +79,22 @@ describe Chef::Knife::Status do
@knife.run
end
- it "should filter healthy nodes" do
- @knife.config[:hide_healthy] = true
- expect(@query).to receive(:search).with(:node, "name:my_custom_name NOT ohai_time:[1428569820 TO 1428573420]", opts)
+ it "should filter by nodes older than some mins with nodename specified" do
+ @knife.config[:hide_by_mins] = 59
+ expect(@query).to receive(:search).with(:node, "name:my_custom_name NOT ohai_time:[1428569880 TO 1428573420]", opts)
@knife.run
end
- it "should filter by environment" do
+ it "should filter by environment with nodename specified" do
@knife.config[:environment] = "production"
expect(@query).to receive(:search).with(:node, "name:my_custom_name AND chef_environment:production", opts)
@knife.run
end
- it "should filter by environment and health" do
+ it "should filter by environment and nodes older than some mins with nodename specified" do
@knife.config[:environment] = "production"
- @knife.config[:hide_healthy] = true
- expect(@query).to receive(:search).with(:node, "name:my_custom_name AND chef_environment:production NOT ohai_time:[1428569820 TO 1428573420]", opts)
+ @knife.config[:hide_by_mins] = 59
+ expect(@query).to receive(:search).with(:node, "name:my_custom_name AND chef_environment:production NOT ohai_time:[1428569880 TO 1428573420]", opts)
@knife.run
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/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/node_spec.rb b/spec/unit/node_spec.rb
index 5f3bed2833..17e085a465 100644
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -42,6 +42,14 @@ describe Chef::Node do
expect([n3, n1, n2].sort).to eq([n1, n2, n3])
end
+ it "should share identity only with others of the same name" do
+ n1 = Chef::Node.build('foo')
+ n2 = Chef::Node.build('foo')
+ n3 = Chef::Node.build('bar')
+ expect(n1).to eq(n2)
+ expect(n1).not_to eq(n3)
+ end
+
describe "when the node does not exist on the server" do
before do
response = OpenStruct.new(:code => '404')
diff --git a/spec/unit/policy_builder/expand_node_object_spec.rb b/spec/unit/policy_builder/expand_node_object_spec.rb
index 815926ffb7..306d677108 100644
--- a/spec/unit/policy_builder/expand_node_object_spec.rb
+++ b/spec/unit/policy_builder/expand_node_object_spec.rb
@@ -34,6 +34,14 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
expect(policy_builder).to respond_to(:node)
end
+ 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
@@ -98,6 +106,27 @@ describe Chef::PolicyBuilder::ExpandNodeObject do
end
+ context "deprecated #load_node method" 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
+ 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
diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb
index 9ecf7a4cde..dc06cb3326 100644
--- a/spec/unit/property_spec.rb
+++ b/spec/unit/property_spec.rb
@@ -921,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"
@@ -933,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
diff --git a/spec/unit/provider/deploy_spec.rb b/spec/unit/provider/deploy_spec.rb
index e6a7125e32..adcb9431eb 100644
--- a/spec/unit/provider/deploy_spec.rb
+++ b/spec/unit/provider/deploy_spec.rb
@@ -356,7 +356,7 @@ describe Chef::Provider::Deploy do
it "chowns the whole release dir to user and group specified in the resource" do
@resource.user "foo"
@resource.group "bar"
- expect(FileUtils).to receive(:chown_R).with("foo", "bar", "/my/deploy/dir")
+ expect(FileUtils).to receive(:chown_R).with("foo", "bar", "/my/deploy/dir", { :force => true })
@provider.enforce_ownership
end
diff --git a/spec/unit/provider/execute_spec.rb b/spec/unit/provider/execute_spec.rb
index 1274203ce3..e7607d9417 100644
--- a/spec/unit/provider/execute_spec.rb
+++ b/spec/unit/provider/execute_spec.rb
@@ -172,5 +172,14 @@ describe Chef::Provider::Execute do
provider.run_action(:run)
expect(new_resource).to be_updated
end
+
+ it "should not include stdout/stderr in failure exception for sensitive resource" do
+ opts.delete(:live_stream)
+ new_resource.sensitive true
+ expect(provider).to receive(:shell_out!).and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ expect do
+ provider.run_action(:run)
+ end.to raise_error(Mixlib::ShellOut::ShellCommandFailed, /suppressed for sensitive resource/)
+ end
end
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/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/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..a9407a4d7e 100644
--- a/spec/unit/provider/user/dscl_spec.rb
+++ b/spec/unit/provider/user/dscl_spec.rb
@@ -760,6 +760,13 @@ ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
provider.dscl_create_comment
end
+ it "sets the comment field to username" do
+ new_resource.comment nil
+ expect(provider).to receive(:run_dscl).with("create /Users/toor RealName '#mockssuck'").and_return(true)
+ provider.dscl_create_comment
+ expect(new_resource.comment).to eq("#mockssuck")
+ end
+
it "should run run_dscl with create /Users/user PrimaryGroupID to set the users primary group" do
expect(provider).to receive(:run_dscl).with("create /Users/toor PrimaryGroupID '1001'").and_return(true)
provider.dscl_set_gid
@@ -789,6 +796,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/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/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/registry_key_spec.rb b/spec/unit/resource/registry_key_spec.rb
index 2e2811d026..2d82f1a51c 100644
--- a/spec/unit/resource/registry_key_spec.rb
+++ b/spec/unit/resource/registry_key_spec.rb
@@ -91,7 +91,7 @@ describe Chef::Resource::RegistryKey, "values" do
it "should return checksummed data if the type is unsafe" do
@resource.values( { :name => 'poosh', :type => :binary, :data => 255.chr * 1 })
- expect(@resource.values).to eql([ { :name => 'poosh', :type => :binary, :data => "00594fd4f42ba43fc1ca0427a0576295" } ])
+ expect(@resource.values).to eql([ { :name => 'poosh', :type => :binary, :data => 'a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89' } ])
end
it "should throw an exception if the name field is missing" do
@@ -194,6 +194,6 @@ describe Chef::Resource::RegistryKey, "state" do
it "should return scrubbed values" do
@resource.values([ { :name => 'poosh', :type => :binary, :data => 255.chr * 1 } ])
- expect(@resource.state).to eql( { :values => [{ :name => 'poosh', :type => :binary, :data => "00594fd4f42ba43fc1ca0427a0576295" }] } )
+ expect(@resource.state).to eql( { :values => [{ :name => 'poosh', :type => :binary, :data => 'a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89'}] } )
end
end
diff --git a/spec/unit/resource/resource_notification_spec.rb b/spec/unit/resource/resource_notification_spec.rb
index 7f6b124d4d..024b6f93f7 100644
--- a/spec/unit/resource/resource_notification_spec.rb
+++ b/spec/unit/resource/resource_notification_spec.rb
@@ -19,149 +19,148 @@ require 'spec_helper'
require 'chef/resource/resource_notification'
describe Chef::Resource::Notification do
- before do
- @notification = Chef::Resource::Notification.new(:service_apache, :restart, :template_httpd_conf)
- end
+
+ let(:notification) { Chef::Resource::Notification.new(:service_apache, :restart, :template_httpd_conf) }
it "has a resource to be notified" do
- expect(@notification.resource).to eq(:service_apache)
+ expect(notification.resource).to eq(:service_apache)
end
it "has an action to take on the service" do
- expect(@notification.action).to eq(:restart)
+ expect(notification.action).to eq(:restart)
end
it "has a notifying resource" do
- expect(@notification.notifying_resource).to eq(:template_httpd_conf)
+ expect(notification.notifying_resource).to eq(:template_httpd_conf)
end
it "is a duplicate of another notification with the same target resource and action" do
other = Chef::Resource::Notification.new(:service_apache, :restart, :sync_web_app_code)
- expect(@notification.duplicates?(other)).to be_truthy
+ expect(notification.duplicates?(other)).to be_truthy
end
it "is not a duplicate of another notification if the actions differ" do
other = Chef::Resource::Notification.new(:service_apache, :enable, :install_apache)
- expect(@notification.duplicates?(other)).to be_falsey
+ expect(notification.duplicates?(other)).to be_falsey
end
it "is not a duplicate of another notification if the target resources differ" do
other = Chef::Resource::Notification.new(:service_sshd, :restart, :template_httpd_conf)
- expect(@notification.duplicates?(other)).to be_falsey
+ expect(notification.duplicates?(other)).to be_falsey
end
it "raises an ArgumentError if you try to check a non-ducktype object for duplication" do
- expect {@notification.duplicates?(:not_a_notification)}.to raise_error(ArgumentError)
+ expect {notification.duplicates?(:not_a_notification)}.to raise_error(ArgumentError)
end
it "takes no action to resolve a resource reference that doesn't need to be resolved" do
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
- @notification.resource = @keyboard_cat
+ notification.resource = @keyboard_cat
@long_cat = Chef::Resource::Cat.new("long_cat")
- @notification.notifying_resource = @long_cat
+ notification.notifying_resource = @long_cat
@resource_collection = Chef::ResourceCollection.new
# would raise an error since the resource is not in the collection
- @notification.resolve_resource_reference(@resource_collection)
- expect(@notification.resource).to eq(@keyboard_cat)
+ notification.resolve_resource_reference(@resource_collection)
+ expect(notification.resource).to eq(@keyboard_cat)
end
it "resolves a lazy reference to a resource" do
- @notification.resource = {:cat => "keyboard_cat"}
+ notification.resource = {:cat => "keyboard_cat"}
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @keyboard_cat
@long_cat = Chef::Resource::Cat.new("long_cat")
- @notification.notifying_resource = @long_cat
- @notification.resolve_resource_reference(@resource_collection)
- expect(@notification.resource).to eq(@keyboard_cat)
+ notification.notifying_resource = @long_cat
+ notification.resolve_resource_reference(@resource_collection)
+ expect(notification.resource).to eq(@keyboard_cat)
end
it "resolves a lazy reference to its notifying resource" do
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
- @notification.resource = @keyboard_cat
- @notification.notifying_resource = {:cat => "long_cat"}
+ notification.resource = @keyboard_cat
+ notification.notifying_resource = {:cat => "long_cat"}
@long_cat = Chef::Resource::Cat.new("long_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @long_cat
- @notification.resolve_resource_reference(@resource_collection)
- expect(@notification.notifying_resource).to eq(@long_cat)
+ notification.resolve_resource_reference(@resource_collection)
+ expect(notification.notifying_resource).to eq(@long_cat)
end
it "resolves lazy references to both its resource and its notifying resource" do
- @notification.resource = {:cat => "keyboard_cat"}
+ notification.resource = {:cat => "keyboard_cat"}
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @keyboard_cat
- @notification.notifying_resource = {:cat => "long_cat"}
+ notification.notifying_resource = {:cat => "long_cat"}
@long_cat = Chef::Resource::Cat.new("long_cat")
@resource_collection << @long_cat
- @notification.resolve_resource_reference(@resource_collection)
- expect(@notification.resource).to eq(@keyboard_cat)
- expect(@notification.notifying_resource).to eq(@long_cat)
+ notification.resolve_resource_reference(@resource_collection)
+ expect(notification.resource).to eq(@keyboard_cat)
+ expect(notification.notifying_resource).to eq(@long_cat)
end
it "raises a RuntimeError if you try to reference multiple resources" do
- @notification.resource = {:cat => ["keyboard_cat", "cheez_cat"]}
+ notification.resource = {:cat => ["keyboard_cat", "cheez_cat"]}
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
@cheez_cat = Chef::Resource::Cat.new("cheez_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @keyboard_cat
@resource_collection << @cheez_cat
@long_cat = Chef::Resource::Cat.new("long_cat")
- @notification.notifying_resource = @long_cat
- expect {@notification.resolve_resource_reference(@resource_collection)}.to raise_error(RuntimeError)
+ notification.notifying_resource = @long_cat
+ expect {notification.resolve_resource_reference(@resource_collection)}.to raise_error(RuntimeError)
end
it "raises a RuntimeError if you try to reference multiple notifying resources" do
- @notification.notifying_resource = {:cat => ["long_cat", "cheez_cat"]}
+ notification.notifying_resource = {:cat => ["long_cat", "cheez_cat"]}
@long_cat = Chef::Resource::Cat.new("long_cat")
@cheez_cat = Chef::Resource::Cat.new("cheez_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @long_cat
@resource_collection << @cheez_cat
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
- @notification.resource = @keyboard_cat
- expect {@notification.resolve_resource_reference(@resource_collection)}.to raise_error(RuntimeError)
+ notification.resource = @keyboard_cat
+ expect {notification.resolve_resource_reference(@resource_collection)}.to raise_error(RuntimeError)
end
it "raises a RuntimeError if it can't find a resource in the resource collection when resolving a lazy reference" do
- @notification.resource = {:cat => "keyboard_cat"}
+ notification.resource = {:cat => "keyboard_cat"}
@cheez_cat = Chef::Resource::Cat.new("cheez_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @cheez_cat
@long_cat = Chef::Resource::Cat.new("long_cat")
- @notification.notifying_resource = @long_cat
- expect {@notification.resolve_resource_reference(@resource_collection)}.to raise_error(RuntimeError)
+ notification.notifying_resource = @long_cat
+ expect {notification.resolve_resource_reference(@resource_collection)}.to raise_error(RuntimeError)
end
it "raises a RuntimeError if it can't find a notifying resource in the resource collection when resolving a lazy reference" do
- @notification.notifying_resource = {:cat => "long_cat"}
+ notification.notifying_resource = {:cat => "long_cat"}
@cheez_cat = Chef::Resource::Cat.new("cheez_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @cheez_cat
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
- @notification.resource = @keyboard_cat
- expect {@notification.resolve_resource_reference(@resource_collection)}.to raise_error(RuntimeError)
+ notification.resource = @keyboard_cat
+ expect {notification.resolve_resource_reference(@resource_collection)}.to raise_error(RuntimeError)
end
it "raises an ArgumentError if improper syntax is used in the lazy reference to its resource" do
- @notification.resource = "cat => keyboard_cat"
+ notification.resource = "cat => keyboard_cat"
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @keyboard_cat
@long_cat = Chef::Resource::Cat.new("long_cat")
- @notification.notifying_resource = @long_cat
- expect {@notification.resolve_resource_reference(@resource_collection)}.to raise_error(ArgumentError)
+ notification.notifying_resource = @long_cat
+ expect {notification.resolve_resource_reference(@resource_collection)}.to raise_error(ArgumentError)
end
it "raises an ArgumentError if improper syntax is used in the lazy reference to its notifying resource" do
- @notification.notifying_resource = "cat => long_cat"
+ notification.notifying_resource = "cat => long_cat"
@long_cat = Chef::Resource::Cat.new("long_cat")
@resource_collection = Chef::ResourceCollection.new
@resource_collection << @long_cat
@keyboard_cat = Chef::Resource::Cat.new("keyboard_cat")
- @notification.resource = @keyboard_cat
- expect {@notification.resolve_resource_reference(@resource_collection)}.to raise_error(ArgumentError)
+ notification.resource = @keyboard_cat
+ expect {notification.resolve_resource_reference(@resource_collection)}.to raise_error(ArgumentError)
end
# Create test to resolve lazy references to both notifying resource and dest. resource
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/rest_spec.rb b/spec/unit/rest_spec.rb
index 3b04981610..3eee997c50 100644
--- a/spec/unit/rest_spec.rb
+++ b/spec/unit/rest_spec.rb
@@ -69,8 +69,8 @@ describe Chef::REST do
rest
end
- let(:standard_read_headers) {{"Accept"=>"application/json", "Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID"=>request_id, 'X-Ops-Server-API-Version' => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION}}
- let(:standard_write_headers) {{"Accept"=>"application/json", "Content-Type"=>"application/json", "Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID"=>request_id, 'X-Ops-Server-API-Version' => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION}}
+ let(:standard_read_headers) {{"Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID"=>request_id, 'X-Ops-Server-API-Version' => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION}}
+ let(:standard_write_headers) {{"Accept"=>"application/json", "Content-Type"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID"=>request_id, 'X-Ops-Server-API-Version' => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION}}
before(:each) do
Chef::Log.init(log_stringio)
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/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