summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--CHANGELOG.md33
-rw-r--r--README.md41
-rw-r--r--lib/chef/application/client.rb37
-rw-r--r--lib/chef/application/solo.rb5
-rw-r--r--lib/chef/chef_fs/chef_fs_data_store.rb10
-rw-r--r--lib/chef/chef_fs/config.rb29
-rw-r--r--lib/chef/chef_fs/data_handler/policy_data_handler.rb15
-rw-r--r--lib/chef/chef_fs/data_handler/user_data_handler.rb1
-rw-r--r--lib/chef/chef_fs/file_system.rb2
-rw-r--r--lib/chef/chef_fs/file_system/chef_repository_file_system_policies_dir.rb38
-rw-r--r--lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb4
-rw-r--r--lib/chef/client.rb1
-rw-r--r--lib/chef/config.rb4
-rw-r--r--lib/chef/data_bag_item.rb2
-rw-r--r--lib/chef/encrypted_data_bag_item/assertions.rb3
-rw-r--r--lib/chef/event_dispatch/dispatcher.rb8
-rw-r--r--lib/chef/exceptions.rb6
-rw-r--r--lib/chef/guard_interpreter/resource_guard_interpreter.rb1
-rw-r--r--lib/chef/knife.rb5
-rw-r--r--lib/chef/knife/cookbook_site_share.rb13
-rw-r--r--lib/chef/knife/cookbook_upload.rb21
-rw-r--r--lib/chef/knife/core/bootstrap_context.rb5
-rw-r--r--lib/chef/knife/raw.rb17
-rw-r--r--lib/chef/knife/serve.rb3
-rw-r--r--lib/chef/knife/ssl_fetch.rb2
-rw-r--r--lib/chef/mixin/command/windows.rb6
-rw-r--r--lib/chef/mixin/securable.rb8
-rw-r--r--lib/chef/mixin/template.rb16
-rw-r--r--lib/chef/mixin/why_run.rb2
-rw-r--r--lib/chef/node/attribute.rb8
-rw-r--r--lib/chef/node/attribute_collections.rb20
-rw-r--r--lib/chef/node/immutable_collections.rb14
-rw-r--r--lib/chef/org.rb148
-rw-r--r--lib/chef/platform/provider_mapping.rb3
-rw-r--r--lib/chef/platform/provider_priority_map.rb1
-rw-r--r--lib/chef/policy_builder/policyfile.rb23
-rw-r--r--lib/chef/provider/dsc_script.rb2
-rw-r--r--lib/chef/provider/env.rb12
-rw-r--r--lib/chef/provider/package/rpm.rb29
-rw-r--r--lib/chef/provider/powershell_script.rb4
-rw-r--r--lib/chef/provider/service/openbsd.rb216
-rw-r--r--lib/chef/provider/user.rb2
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/resource.rb1194
-rw-r--r--lib/chef/resource/conditional.rb2
-rw-r--r--lib/chef/resource/execute.rb7
-rw-r--r--lib/chef/resource/lwrp_base.rb11
-rw-r--r--lib/chef/resource/template.rb5
-rw-r--r--lib/chef/run_context.rb4
-rw-r--r--lib/chef/util/diff.rb5
-rw-r--r--lib/chef/util/path_helper.rb3
-rw-r--r--spec/data/recipes.tgzbin0 -> 293 bytes
-rw-r--r--spec/functional/file_content_management/deploy_strategies_spec.rb11
-rw-r--r--spec/functional/knife/exec_spec.rb4
-rw-r--r--spec/functional/resource/execute_spec.rb26
-rw-r--r--spec/functional/resource/powershell_spec.rb15
-rw-r--r--spec/functional/resource/user/useradd_spec.rb23
-rw-r--r--spec/integration/client/client_spec.rb53
-rw-r--r--spec/integration/knife/chef_fs_data_store_spec.rb12
-rw-r--r--spec/integration/knife/download_spec.rb2
-rw-r--r--spec/integration/solo/solo_spec.rb3
-rw-r--r--spec/spec_helper.rb7
-rw-r--r--spec/stress/win32/file_spec.rb4
-rw-r--r--spec/support/platform_helpers.rb12
-rw-r--r--spec/unit/application/client_spec.rb20
-rw-r--r--spec/unit/chef_fs/config_spec.rb52
-rw-r--r--spec/unit/chef_fs/file_pattern_spec.rb2
-rw-r--r--spec/unit/data_bag_item_spec.rb202
-rw-r--r--spec/unit/encrypted_data_bag_item_spec.rb24
-rw-r--r--spec/unit/knife/cookbook_site_share_spec.rb17
-rw-r--r--spec/unit/knife/cookbook_upload_spec.rb68
-rw-r--r--spec/unit/knife/raw_spec.rb43
-rw-r--r--spec/unit/org_spec.rb196
-rw-r--r--spec/unit/policy_builder/policyfile_spec.rb47
-rw-r--r--spec/unit/provider/env_spec.rb23
-rw-r--r--spec/unit/provider/package/rpm_spec.rb184
-rw-r--r--spec/unit/provider/service/openbsd_service_spec.rb543
-rw-r--r--spec/unit/provider/user_spec.rb2
-rw-r--r--spec/unit/resource/conditional_spec.rb50
-rw-r--r--spec/unit/resource/execute_spec.rb4
-rw-r--r--spec/unit/util/diff_spec.rb12
-rw-r--r--spec/unit/util/path_helper_spec.rb12
83 files changed, 2885 insertions, 843 deletions
diff --git a/.travis.yml b/.travis.yml
index c083a31ba9..699d8237ad 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,6 @@
+language: ruby
+
+sudo: false
# Early warning system to catch if Rubygems breaks something
before_install:
gem update --system
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2107c460b8..0a96ec8d28 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,34 @@
* [**Phil Dibowitz**](https://github.com/jaymzh):
Drop SSL warnings now that we have a safe default
* [Pull 2684](https://github.com/chef/chef/pull/2684) Remove ole_initialize/uninitialize which cause problems with Ruby >= 2
+* [**BinaryBabel**](https://github.com/binarybabel)
+ Make knife cookbook site share prefer gnutar when packaging
+* [**Dave Eddy**](https://github.com/bahamas10)
+ Support arrays for not_if and only_if
+* [**Scott Bonds**](https://github.com/bonds)
+ Add service provider for OpenBSD
+* [**Alex Slynko**](https://github.com/alex-slynko-wonga)
+ Change env provider to preserve ordering
+* [**Rob Redpath**](https://github.com/robredpath)
+ Add --lockfile opt for chef-client and chef-solo
+* [**Josh Murphy**](https://github.com/jdmurphy)
+ Check cookbooks exist in path(s) before attempting to upload them with --all
+* [**Vasiliy Tolstov**](https://github.com/vtolstov)
+ add ability to fetch recipes like in chef-solo when using local-mode
+* [**Jan**](https://github.com/habermann24)
+ FIX data_bag_item.rb:161: warning: circular argument reference - data_bag
+* [**David Radcliffe**](https://github.com/dwradcliffe)
+ add banner for knife serve command
+* [**Yukihiko Sawanobori**](https://github.com/sawanoboly)
+ use Chef::JSONCompat.parse for file_contents
+* [**Xabier de Zuazo**] (https://github.com/zuazo)
+ Remove some simple Ruby 1.8 and 1.9 code
+* [**Xabier de Zuazo**] (https://github.com/zuazo)
+ Remove all RSpec test filters related to Ruby 1.8 and 1.9
+* [**Xabier de Zuazo**] (https://github.com/zuazo)
+ Fix knife cookbook upload messages
+* [**David Crowder**] (https://github.com/david-crowder)
+ refactor to use shell_out in rpm provider
### Chef Contributions
* ruby 1.9.3 support is dropped
@@ -34,6 +62,11 @@
* suppress 3694 warnings on the most trivial resource cloning
* fixed bugs in the deep_merge_cache logic introduced in 12.0.0 around `node['foo']` vs `node[:foo]` vs. `node.foo`
* add `include_recipe "::recipe"` sugar to reference a recipe in the current cookbook
+* Add --proxy-auth option to `knife raw`
+* added Chef::Org model class for Chef Organizations in Chef 12 Server
+* `powershell_script` should now correctly get the exit code for scripts that it runs. See [Issue 2348](https://github.com/chef/chef/issues/2348)
+* Useradd functional tests fail randomly
+* Add comments to trusted_certs_content
## 12.0.3
* [**Phil Dibowitz**](https://github.com/jaymzh):
diff --git a/README.md b/README.md
index c1a818b333..3b1c7190fb 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
# Chef
[![Code Climate](https://codeclimate.com/github/opscode/chef.png)](https://codeclimate.com/github/opscode/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/opscode/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)
+[![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)
-Want to try Chef? Get started with [learnchef](https://learnchef.opscode.com)
+Want to try Chef? Get started with [learnchef](https://learn.chef.io)
-* Documentation: [http://docs.opscode.com](http://docs.opscode.com)
-* Source: [http://github.com/opscode/chef/tree/master](http://github.com/opscode/chef/tree/master)
-* Tickets/Issues: [https://github.com/opscode/chef/issues](https://github.com/opscode/chef/issues)
+* Documentation: [http://docs.chef.io](http://docs.chef.io)
+* Source: [http://github.com/chef/chef/tree/master](http://github.com/chef/chef/tree/master)
+* Tickets/Issues: [https://github.com/chef/chef/issues](https://github.com/chef/chef/issues)
* IRC: `#chef` and `#chef-hacking` on Freenode
* Mailing list: [http://lists.opscode.com](http://lists.opscode.com)
@@ -17,15 +17,15 @@ entire infrastructure.
This README focuses on developers who want to modify Chef source code.
If you just want to use Chef, check out these resources:
-* [learnchef](https://learnchef.opscode.com): Getting started guide
-* [http://docs.opscode.com](http://docs.opscode.com): Comprehensive User Docs
-* [Installer Downloads](http://www.getchef.com/chef/install/): Install Chef as a complete package
+* [learnchef](https://learn.chef.io): Getting started guide
+* [http://docs.chef.io](http://docs.chef.io): Comprehensive User Docs
+* [Installer Downloads](https://www.chef.io/download-chef-client/): Install Chef as a complete package
## Installing From Git
**NOTE:** Unless you have a specific reason to install from source (to
try a new feature, contribute a patch, or run chef on an OS for which no
-package is available), you should head to the [installer page](http://www.getchef.com/chef/install/)
+package is available), you should head to the [installer page](https://www.chef.io/download-chef-client/)
to get a prebuilt package.
### Prerequisites
@@ -45,7 +45,7 @@ emerge, etc.):
Then get the source and install it:
# Clone this repo
- git clone https://github.com/opscode/chef.git
+ git clone https://github.com/chef/chef.git
# cd into the source tree
cd chef
@@ -64,12 +64,15 @@ Then get the source and install it:
Before working on the code, if you plan to contribute your changes, you need to
read the
-[Chef Contributions document](http://docs.opscode.com/community_contributions.html).
+[Chef Contributions document](http://docs.chef.io/community_contributions.html).
-You will also need to set up the repository with the appropriate branches. We
-document the process on the
-[Working with Git](http://wiki.opscode.com/display/chef/Working+with+git) page
-of the Chef wiki.
+The general development process is:
+
+1. Fork this repo and clone it to your workstation
+2. Create a feature branch for your change
+3. Write code and tests
+4. Push your feature branch to github and open a pull request against
+ master
Once your repository is set up, you can start working on the code. We do use
TDD with RSpec, so you'll need to get a development environment running.
@@ -78,9 +81,9 @@ copy of the source running.
## Reporting Issues
-Issues can be reported by using [GitHub issues](https://github.com/opscode/chef/issues).
+Issues can be reported by using [GitHub issues](https://github.com/chef/chef/issues).
-Full details on how to report issues can be found in the [CONTRIBUTING](https://github.com/opscode/chef/blob/master/CONTRIBUTING.md#-chef-issue-tracking) doc.
+Full details on how to report issues can be found in the [CONTRIBUTING](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#-chef-issue-tracking) doc.
Note that this repository is primarily for reporting chef-client issues.
For reporting issues against other Chef projects, please look up the appropriate repository
@@ -114,8 +117,8 @@ Chef - A configuration management system
| | |
|:---------------------|:-----------------------------------------|
-| **Author:** | Adam Jacob (<adam@opscode.com>)
-| **Copyright:** | Copyright (c) 2008-2014 Chef Software, Inc.
+| **Author:** | Adam Jacob (<adam@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");
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index 40772c0f8f..c0635e1cb5 100644
--- a/lib/chef/application/client.rb
+++ b/lib/chef/application/client.rb
@@ -27,6 +27,7 @@ require 'chef/handler/error_report'
require 'chef/workstation_config_loader'
class Chef::Application::Client < Chef::Application
+ include Chef::Mixin::ShellOut
# Mimic self_pipe sleep from Unicorn to capture signals safely
SELF_PIPE = []
@@ -63,7 +64,7 @@ class Chef::Application::Client < Chef::Application
option :log_level,
:short => "-l LEVEL",
:long => "--log_level LEVEL",
- :description => "Set the log level (debug, info, warn, error, fatal)",
+ :description => "Set the log level (auto, debug, info, warn, error, fatal)",
:proc => lambda { |l| l.to_sym }
option :log_location,
@@ -104,7 +105,12 @@ class Chef::Application::Client < Chef::Application
option :pid_file,
:short => "-P PID_FILE",
:long => "--pid PIDFILE",
- :description => "Set the PID file location, defaults to /tmp/chef-client.pid",
+ :description => "Set the PID file location, for the chef-client daemon process. Defaults to /tmp/chef-client.pid",
+ :proc => nil
+
+ option :lockfile,
+ :long => "--lockfile LOCKFILE",
+ :description => "Set the lockfile location. Prevents multiple client processes from converging at the same time",
:proc => nil
option :interval,
@@ -200,6 +206,10 @@ class Chef::Application::Client < Chef::Application
:description => "Fork client",
:boolean => true
+ option :recipe_url,
+ :long => "--recipe-url=RECIPE_URL",
+ :description => "Pull down a remote archive of recipes and unpack it to the cookbook cache. Only used in local mode."
+
option :enable_reporting,
:short => "-R",
:long => "--enable-reporting",
@@ -252,6 +262,8 @@ class Chef::Application::Client < Chef::Application
def reconfigure
super
+ raise Chef::Exceptions::PIDFileLockfileMatch if Chef::Util::PathHelper.paths_eql? (Chef::Config[:pid_file] || '' ), (Chef::Config[:lockfile] || '')
+
Chef::Config[:specific_recipes] = cli_arguments.map { |file| File.expand_path(file) }
Chef::Config[:chef_server_url] = config[:chef_server_url] if config.has_key? :chef_server_url
@@ -260,6 +272,18 @@ class Chef::Application::Client < Chef::Application
if Chef::Config.local_mode && !Chef::Config.has_key?(:cookbook_path) && !Chef::Config.has_key?(:chef_repo_path)
Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Dir.pwd)
end
+
+ if !Chef::Config.local_mode && Chef::Config.has_key?(:recipe_url)
+ Chef::Application.fatal!("chef-client recipe-url can be used only in local-mode", 1)
+ elsif Chef::Config.local_mode && Chef::Config.has_key?(:recipe_url)
+ Chef::Log.debug "Creating path #{Chef::Config.chef_repo_path} to extract recipes into"
+ FileUtils.mkdir_p(Chef::Config.chef_repo_path)
+ tarball_path = File.join(Chef::Config.chef_repo_path, 'recipes.tgz')
+ fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path)
+ result = shell_out!("tar zxvf #{tarball_path} -C #{Chef::Config.chef_repo_path}")
+ Chef::Log.debug "#{result.stdout}"
+ end
+
Chef::Config.chef_zero.host = config[:chef_zero_host] if config[:chef_zero_host]
Chef::Config.chef_zero.port = config[:chef_zero_port] if config[:chef_zero_port]
@@ -435,4 +459,13 @@ class Chef::Application::Client < Chef::Application
msg += audit_mode_settings_explaination
return msg
end
+
+ def fetch_recipe_tarball(url, path)
+ Chef::Log.debug("Download recipes tarball from #{url} to #{path}")
+ File.open(path, 'wb') do |f|
+ open(url) do |r|
+ f.write(r.read)
+ end
+ end
+ end
end
diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb
index c3f5444ef7..c919c2b48b 100644
--- a/lib/chef/application/solo.rb
+++ b/lib/chef/application/solo.rb
@@ -99,6 +99,11 @@ class Chef::Application::Solo < Chef::Application
:proc => lambda { |p| true }
end
+ option :lockfile,
+ :long => "--lockfile LOCKFILE",
+ :description => "Set the lockfile location. Prevents multiple processes from converging at the same time",
+ :proc => nil
+
option :interval,
:short => "-i SECONDS",
:long => "--interval SECONDS",
diff --git a/lib/chef/chef_fs/chef_fs_data_store.rb b/lib/chef/chef_fs/chef_fs_data_store.rb
index 3813d0edb4..9b4f7320b8 100644
--- a/lib/chef/chef_fs/chef_fs_data_store.rb
+++ b/lib/chef/chef_fs/chef_fs_data_store.rb
@@ -369,6 +369,11 @@ class Chef
if path.length >= 3
path[2] = "#{path[2]}.json"
end
+ elsif path[0] == 'policies'
+ path = path.dup
+ if path.length >= 3
+ path[2] = "#{path[2]}.json"
+ end
elsif path[0] == 'cookbooks'
if path.length == 2
raise ChefZero::DataStore::DataNotFoundError.new(path)
@@ -445,10 +450,13 @@ class Chef
def with_dir(path)
# Do not automatically create data bags
create = !(path[0] == 'data' && path.size >= 2)
+
begin
yield get_dir(_to_chef_fs_path(path), create)
rescue Chef::ChefFS::FileSystem::NotFoundError => e
- raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
+ err = ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
+ err.set_backtrace(e.backtrace)
+ raise err
end
end
diff --git a/lib/chef/chef_fs/config.rb b/lib/chef/chef_fs/config.rb
index fcad6c919f..6666a3deee 100644
--- a/lib/chef/chef_fs/config.rb
+++ b/lib/chef/chef_fs/config.rb
@@ -26,6 +26,25 @@ class Chef
# objects representing the server and local repository, respectively).
#
class Config
+
+ # Not all of our object types pluralize by adding an 's', so we map them
+ # out here:
+ INFLECTIONS = {
+ "acls" => "acl",
+ "clients" => "client",
+ "cookbooks" => "cookbook",
+ "containers" => "container",
+ "data_bags" => "data_bag",
+ "environments" => "environment",
+ "groups" => "group",
+ "nodes" => "node",
+ "roles" => "role",
+ "users" => "user",
+ "policies" => "policy"
+ }
+ INFLECTIONS.each { |k,v| k.freeze; v.freeze }
+ INFLECTIONS.freeze
+
#
# Create a new Config object which can produce a chef_fs and local_fs.
#
@@ -215,14 +234,16 @@ class Chef
result = {}
case @chef_config[:repo_mode]
when 'static'
- object_names = %w(cookbooks data_bags environments roles)
+ object_names = %w(cookbooks data_bags environments roles policies)
when 'hosted_everything'
- object_names = %w(acls clients cookbooks containers data_bags environments groups nodes roles)
+ object_names = %w(acls clients cookbooks containers data_bags environments groups nodes roles policies)
else
- object_names = %w(clients cookbooks data_bags environments nodes roles users)
+ object_names = %w(clients cookbooks data_bags environments nodes roles users policies)
end
object_names.each do |object_name|
- variable_name = "#{object_name[0..-2]}_path" # cookbooks -> cookbook_path
+ # cookbooks -> cookbook_path
+ singular_name = INFLECTIONS[object_name] or raise "Unknown object name #{object_name}"
+ variable_name = "#{singular_name}_path"
paths = Array(@chef_config[variable_name]).flatten
result[object_name] = paths.map { |path| File.expand_path(path) }
end
diff --git a/lib/chef/chef_fs/data_handler/policy_data_handler.rb b/lib/chef/chef_fs/data_handler/policy_data_handler.rb
new file mode 100644
index 0000000000..769c13c364
--- /dev/null
+++ b/lib/chef/chef_fs/data_handler/policy_data_handler.rb
@@ -0,0 +1,15 @@
+require 'chef/chef_fs/data_handler/data_handler_base'
+
+class Chef
+ module ChefFS
+ module DataHandler
+ class PolicyDataHandler < DataHandlerBase
+
+ def normalize(policy, entry)
+ policy
+ end
+ end
+ end
+ end
+end
+
diff --git a/lib/chef/chef_fs/data_handler/user_data_handler.rb b/lib/chef/chef_fs/data_handler/user_data_handler.rb
index 2b50ce38d8..f6859faccd 100644
--- a/lib/chef/chef_fs/data_handler/user_data_handler.rb
+++ b/lib/chef/chef_fs/data_handler/user_data_handler.rb
@@ -8,6 +8,7 @@ class Chef
normalize_hash(user, {
'name' => remove_dot_json(entry.name),
'username' => remove_dot_json(entry.name),
+ 'display_name' => remove_dot_json(entry.name),
'admin' => false,
'json_class' => 'Chef::WebUIUser',
'chef_type' => 'webui_user',
diff --git a/lib/chef/chef_fs/file_system.rb b/lib/chef/chef_fs/file_system.rb
index 730fa0e5cc..3ab59046cc 100644
--- a/lib/chef/chef_fs/file_system.rb
+++ b/lib/chef/chef_fs/file_system.rb
@@ -379,7 +379,7 @@ class Chef
should_copy = true
src_value = nil
else
- are_same, src_value, dest_value = compare(src_entry, dest_entry)
+ are_same, src_value, _dest_value = compare(src_entry, dest_entry)
should_copy = !are_same
end
if should_copy
diff --git a/lib/chef/chef_fs/file_system/chef_repository_file_system_policies_dir.rb b/lib/chef/chef_fs/file_system/chef_repository_file_system_policies_dir.rb
new file mode 100644
index 0000000000..42768f10b7
--- /dev/null
+++ b/lib/chef/chef_fs/file_system/chef_repository_file_system_policies_dir.rb
@@ -0,0 +1,38 @@
+#
+# Author:: John Keiser (<jkeiser@opscode.com>)
+# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/chef_fs/file_system/chef_repository_file_system_entry'
+require 'chef/chef_fs/data_handler/policy_data_handler'
+
+class Chef
+ module ChefFS
+ module FileSystem
+
+ class ChefRepositoryFileSystemPoliciesDir < ChefRepositoryFileSystemEntry
+ def initialize(name, parent, path = nil)
+ super(name, parent, path, Chef::ChefFS::DataHandler::PolicyDataHandler.new)
+ end
+
+ def can_have_child?(name, is_dir)
+ is_dir && !name.start_with?('.')
+ end
+ end
+ end
+ end
+end
+
diff --git a/lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb b/lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb
index ac272d4c1a..d03baf91fe 100644
--- a/lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb
@@ -21,6 +21,7 @@ require 'chef/chef_fs/file_system/chef_repository_file_system_entry'
require 'chef/chef_fs/file_system/chef_repository_file_system_acls_dir'
require 'chef/chef_fs/file_system/chef_repository_file_system_cookbooks_dir'
require 'chef/chef_fs/file_system/chef_repository_file_system_data_bags_dir'
+require 'chef/chef_fs/file_system/chef_repository_file_system_policies_dir'
require 'chef/chef_fs/file_system/multiplexed_dir'
require 'chef/chef_fs/data_handler/client_data_handler'
require 'chef/chef_fs/data_handler/environment_data_handler'
@@ -33,6 +34,7 @@ require 'chef/chef_fs/data_handler/container_data_handler'
class Chef
module ChefFS
module FileSystem
+
#
# Represents the root of a local Chef repository, with directories for
# nodes, cookbooks, roles, etc. under it.
@@ -157,6 +159,8 @@ class Chef
dirs = paths.map { |path| ChefRepositoryFileSystemCookbooksDir.new(name, self, path) }
elsif name == 'data_bags'
dirs = paths.map { |path| ChefRepositoryFileSystemDataBagsDir.new(name, self, path) }
+ elsif name == 'policies'
+ dirs = paths.map { |path| ChefRepositoryFileSystemPoliciesDir.new(name, self, path) }
elsif name == 'acls'
dirs = paths.map { |path| ChefRepositoryFileSystemAclsDir.new(name, self, path) }
else
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index 3d9678ea31..3b4f8d4683 100644
--- a/lib/chef/client.rb
+++ b/lib/chef/client.rb
@@ -39,6 +39,7 @@ require 'chef/cookbook/remote_file_vendor'
require 'chef/event_dispatch/dispatcher'
require 'chef/event_loggers/base'
require 'chef/event_loggers/windows_eventlog'
+require 'chef/exceptions'
require 'chef/formatters/base'
require 'chef/formatters/doc'
require 'chef/formatters/minimal'
diff --git a/lib/chef/config.rb b/lib/chef/config.rb
index 453a8f83da..bf0901e182 100644
--- a/lib/chef/config.rb
+++ b/lib/chef/config.rb
@@ -203,6 +203,10 @@ class Chef
# Does not apply to Enterprise Chef commands.
default(:user_path) { derive_path_from_chef_repo_path('users') }
+ # Location of policies on disk. String or array of strings.
+ # Defaults to <chef_repo_path>/policies.
+ default(:policy_path) { derive_path_from_chef_repo_path('policies') }
+
# Turn on "path sanity" by default. See also: http://wiki.opscode.com/display/chef/User+Environment+PATH+Sanity
default :enforce_path_sanity, true
diff --git a/lib/chef/data_bag_item.rb b/lib/chef/data_bag_item.rb
index 6f0ae65478..fc0ee74c0c 100644
--- a/lib/chef/data_bag_item.rb
+++ b/lib/chef/data_bag_item.rb
@@ -158,7 +158,7 @@ class Chef
end
end
- def destroy(data_bag=data_bag, databag_item=name)
+ def destroy(data_bag=data_bag(), databag_item=name)
chef_server_rest.delete_rest("data/#{data_bag}/#{databag_item}")
end
diff --git a/lib/chef/encrypted_data_bag_item/assertions.rb b/lib/chef/encrypted_data_bag_item/assertions.rb
index 0f9416e7b6..ab93f46c10 100644
--- a/lib/chef/encrypted_data_bag_item/assertions.rb
+++ b/lib/chef/encrypted_data_bag_item/assertions.rb
@@ -44,9 +44,6 @@ class Chef::EncryptedDataBagItem
end
def assert_aead_requirements_met!(algorithm)
- unless OpenSSL::Cipher.method_defined?(:auth_data=)
- raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires Ruby >= 2.0"
- end
unless OpenSSL::Cipher.ciphers.include?(algorithm)
raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires an OpenSSL version with \"#{algorithm}\" algorithm support"
end
diff --git a/lib/chef/event_dispatch/dispatcher.rb b/lib/chef/event_dispatch/dispatcher.rb
index c172a406d8..9f43f14311 100644
--- a/lib/chef/event_dispatch/dispatcher.rb
+++ b/lib/chef/event_dispatch/dispatcher.rb
@@ -25,11 +25,9 @@ class Chef
# Define a method that will be forwarded to all
def self.def_forwarding_method(method_name)
- class_eval(<<-END_OF_METHOD, __FILE__, __LINE__)
- def #{method_name}(*args)
- @subscribers.each {|s| s.#{method_name}(*args)}
- end
- END_OF_METHOD
+ define_method(method_name) do |*args|
+ @subscribers.each { |s| s.send(method_name, *args) }
+ end
end
(Base.instance_methods - Object.instance_methods).each do |method_name|
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index 18b8ee5d3f..0fa8696584 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -429,5 +429,11 @@ class Chef
set_backtrace(backtrace)
end
end
+
+ class PIDFileLockfileMatch < RuntimeError
+ def initialize
+ super "PID file and lockfile are not permitted to match. Specify a different location with --pid or --lockfile"
+ end
+ end
end
end
diff --git a/lib/chef/guard_interpreter/resource_guard_interpreter.rb b/lib/chef/guard_interpreter/resource_guard_interpreter.rb
index 346b585d8c..7d9bccb6ca 100644
--- a/lib/chef/guard_interpreter/resource_guard_interpreter.rb
+++ b/lib/chef/guard_interpreter/resource_guard_interpreter.rb
@@ -95,6 +95,7 @@ class Chef
empty_events = Chef::EventDispatch::Dispatcher.new
anonymous_run_context = Chef::RunContext.new(parent_resource.node, {}, empty_events)
interpreter_resource = resource_class.new('Guard resource', anonymous_run_context)
+ interpreter_resource.is_guard_interpreter = true
interpreter_resource
end
diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb
index 51ccb99955..10774acc46 100644
--- a/lib/chef/knife.rb
+++ b/lib/chef/knife.rb
@@ -269,7 +269,8 @@ class Chef
list_commands(category_commands)
elsif missing_plugin = ( OFFICIAL_PLUGINS.find {|plugin| plugin == args[0]} )
ui.info("The #{missing_plugin} commands were moved to plugins in Chef 0.10")
- ui.info("You can install the plugin with `(sudo) gem install knife-#{missing_plugin}")
+ ui.info("You can install the plugin with `(sudo) gem install knife-#{missing_plugin}`")
+ ui.info("Use `chef gem install knife-#{missing_plugin}` instead if using ChefDK")
else
list_commands
end
@@ -308,7 +309,7 @@ class Chef
exit 1
end
- # copy Mixlib::CLI over so that it cab be configured in knife.rb
+ # copy Mixlib::CLI over so that it can be configured in knife.rb
# config file
Chef::Config[:verbosity] = config[:verbosity]
end
diff --git a/lib/chef/knife/cookbook_site_share.rb b/lib/chef/knife/cookbook_site_share.rb
index b4a7873c71..3ea130ba9f 100644
--- a/lib/chef/knife/cookbook_site_share.rb
+++ b/lib/chef/knife/cookbook_site_share.rb
@@ -29,8 +29,11 @@ class Chef
require 'chef/cookbook_loader'
require 'chef/cookbook_uploader'
require 'chef/cookbook_site_streaming_uploader'
+ require 'mixlib/shellout'
end
+ include Chef::Mixin::ShellOut
+
banner "knife cookbook site share COOKBOOK [CATEGORY] (options)"
category "cookbook site"
@@ -70,7 +73,15 @@ class Chef
begin
Chef::Log.debug("Temp cookbook directory is #{tmp_cookbook_dir.inspect}")
ui.info("Making tarball #{cookbook_name}.tgz")
- shell_out!("tar -czf #{cookbook_name}.tgz #{cookbook_name}", :cwd => tmp_cookbook_dir)
+ tar_cmd = "tar"
+ begin
+ # Unix and Mac only - prefer gnutar
+ if shell_out("which gnutar").exitstatus.equal?(0)
+ tar_cmd = "gnutar"
+ end
+ rescue Errno::ENOENT
+ end
+ shell_out!("#{tar_cmd} -czf #{cookbook_name}.tgz #{cookbook_name}", :cwd => tmp_cookbook_dir)
rescue => e
ui.error("Error making tarball #{cookbook_name}.tgz: #{e.message}. Increase log verbosity (-VV) for more information.")
Chef::Log.debug("\n#{e.backtrace.join("\n")}")
diff --git a/lib/chef/knife/cookbook_upload.rb b/lib/chef/knife/cookbook_upload.rb
index f5002be3a7..b2acd74b4b 100644
--- a/lib/chef/knife/cookbook_upload.rb
+++ b/lib/chef/knife/cookbook_upload.rb
@@ -104,18 +104,23 @@ class Chef
justify_width = @server_side_cookbooks.map {|name| name.size}.max.to_i + 2
if config[:all]
cookbook_repo.load_cookbooks
- cbs = []
+ cookbooks_for_upload = []
cookbook_repo.each do |cookbook_name, cookbook|
- cbs << cookbook
+ cookbooks_for_upload << cookbook
cookbook.freeze_version if config[:freeze]
version_constraints_to_update[cookbook_name] = cookbook.version
end
- begin
- upload(cbs, justify_width)
- rescue Exceptions::CookbookFrozen
- ui.warn("Not updating version constraints for some cookbooks in the environment as the cookbook is frozen.")
+ if cookbooks_for_upload.any?
+ begin
+ upload(cookbooks_for_upload, justify_width)
+ rescue Exceptions::CookbookFrozen
+ ui.warn("Not updating version constraints for some cookbooks in the environment as the cookbook is frozen.")
+ end
+ ui.info("Uploaded all cookbooks.")
+ else
+ cookbook_path = config[:cookbook_path].respond_to?(:join) ? config[:cookbook_path].join(', ') : config[:cookbook_path]
+ ui.warn("Could not find any cookbooks in your cookbook path: #{cookbook_path}. Use --cookbook-path to specify the desired path.")
end
- ui.info("Uploaded all cookbooks.")
else
if @name_args.empty?
show_usage
@@ -204,7 +209,7 @@ class Chef
# because cookbooks are lazy-loaded, we have to force the loader
# to load the cookbooks the user intends to upload here:
cookbooks_to_upload
-
+
unless cookbook_repo.merged_cookbooks.empty?
ui.warn "* " * 40
ui.warn(<<-WARNING)
diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb
index f7fee023de..60db34c8d0 100644
--- a/lib/chef/knife/core/bootstrap_context.rb
+++ b/lib/chef/knife/core/bootstrap_context.rb
@@ -49,6 +49,8 @@ class Chef
@secret
end
+ # Contains commands and content, see trusted_certs_content
+ # TODO: Rename to trusted_certs_script
def trusted_certs
@trusted_certs ||= trusted_certs_content
end
@@ -159,6 +161,9 @@ CONFIG
end
private
+
+ # Returns a string for copying the trusted certificates on the workstation to the system being bootstrapped
+ # This string should contain both the commands necessary to both create the files, as well as their content
def trusted_certs_content
content = ""
if @chef_config[:trusted_certs_dir]
diff --git a/lib/chef/knife/raw.rb b/lib/chef/knife/raw.rb
index 954d46beee..601cfcef9b 100644
--- a/lib/chef/knife/raw.rb
+++ b/lib/chef/knife/raw.rb
@@ -32,6 +32,12 @@ class Chef
:short => '-i FILE',
:description => "Name of file to use for PUT or POST"
+ option :proxy_auth,
+ :long => '--proxy-auth',
+ :boolean => true,
+ :default => false,
+ :description => "Use webui proxy authentication. Client key must be the webui key."
+
class RawInputServerAPI < Chef::HTTP
def initialize(options = {})
options[:client_name] ||= Chef::Config[:node_name]
@@ -64,15 +70,21 @@ class Chef
begin
method = config[:method].to_sym
+ headers = {'Content-Type' => 'application/json'}
+
+ if config[:proxy_auth]
+ headers['x-ops-request-source'] = 'web'
+ end
+
if config[:pretty]
chef_rest = RawInputServerAPI.new
- result = chef_rest.request(method, name_args[0], {'Content-Type' => 'application/json'}, data)
+ result = chef_rest.request(method, name_args[0], headers, data)
unless result.is_a?(String)
result = Chef::JSONCompat.to_json_pretty(result)
end
else
chef_rest = RawInputServerAPI.new(:raw_output => true)
- result = chef_rest.request(method, name_args[0], {'Content-Type' => 'application/json'}, data)
+ result = chef_rest.request(method, name_args[0], headers, data)
end
output result
rescue Timeout::Error => e
@@ -88,4 +100,3 @@ class Chef
end # class Raw
end
end
-
diff --git a/lib/chef/knife/serve.rb b/lib/chef/knife/serve.rb
index 870177e0be..84918e2ef2 100644
--- a/lib/chef/knife/serve.rb
+++ b/lib/chef/knife/serve.rb
@@ -4,6 +4,9 @@ require 'chef/local_mode'
class Chef
class Knife
class Serve < Knife
+
+ banner 'knife serve (options)'
+
option :repo_mode,
:long => '--repo-mode MODE',
:description => "Specifies the local repository layout. Values: static (only environments/roles/data_bags/cookbooks), everything (includes nodes/clients/users), hosted_everything (includes acls/groups/etc. for Enterprise/Hosted Chef). Default: everything/hosted_everything"
diff --git a/lib/chef/knife/ssl_fetch.rb b/lib/chef/knife/ssl_fetch.rb
index 745aca5786..fd7d101fd8 100644
--- a/lib/chef/knife/ssl_fetch.rb
+++ b/lib/chef/knife/ssl_fetch.rb
@@ -16,7 +16,7 @@
# limitations under the License.
#
-require 'chef/knife/ssl_fetch'
+require 'chef/knife'
require 'chef/config'
class Chef
diff --git a/lib/chef/mixin/command/windows.rb b/lib/chef/mixin/command/windows.rb
index e3d0cfdb18..0147d58039 100644
--- a/lib/chef/mixin/command/windows.rb
+++ b/lib/chef/mixin/command/windows.rb
@@ -18,11 +18,7 @@
# limitations under the License.
#
-if RUBY_VERSION =~ /^1\.8/
- require 'win32/open3'
-else
- require 'open3'
-end
+require 'open3'
class Chef
module Mixin
diff --git a/lib/chef/mixin/securable.rb b/lib/chef/mixin/securable.rb
index f77703f21a..aaedf0b9ba 100644
--- a/lib/chef/mixin/securable.rb
+++ b/lib/chef/mixin/securable.rb
@@ -111,13 +111,7 @@ class Chef
# equivalent to something like:
# def rights(permissions=nil, principals=nil, args_hash=nil)
- define_method(name) do |*args|
- # Ruby 1.8 compat: default the arguments
- permissions = args.length >= 1 ? args[0] : nil
- principals = args.length >= 2 ? args[1] : nil
- args_hash = args.length >= 3 ? args[2] : nil
- raise ArgumentError.new("wrong number of arguments (#{args.length} for 3)") if args.length >= 4
-
+ define_method(name) do |permissions=nil, principals=nil, args_hash=nil|
rights = self.instance_variable_get("@#{name.to_s}".to_sym)
unless permissions.nil?
input = {
diff --git a/lib/chef/mixin/template.rb b/lib/chef/mixin/template.rb
index ae23336581..d705a9e7be 100644
--- a/lib/chef/mixin/template.rb
+++ b/lib/chef/mixin/template.rb
@@ -23,18 +23,6 @@ class Chef
module Mixin
module Template
- # A compatibility wrapper around IO.binread so it works on Ruby 1.8.7.
- # --
- # Used in the TemplateContext class, but that method namespace is shared
- # with user code, so we want to avoid adding methods there when possible.
- def self.binread(file)
- if IO.respond_to?(:binread)
- IO.binread(file)
- else
- File.open(file, "rb") {|f| f.read }
- end
- end
-
# == ChefContext
# ChefContext was previously used to mix behavior into Erubis::Context so
# that it would be available to templates. This behavior has now moved to
@@ -105,11 +93,11 @@ class Chef
partial_context._extend_modules(@_extension_modules)
template_location = @template_finder.find(partial_name, options)
- _render_template(Mixin::Template.binread(template_location), partial_context)
+ _render_template(IO.binread(template_location), partial_context)
end
def render_template(template_location)
- _render_template(Mixin::Template.binread(template_location), self)
+ _render_template(IO.binread(template_location), self)
end
def render_template_from_string(template)
diff --git a/lib/chef/mixin/why_run.rb b/lib/chef/mixin/why_run.rb
index d650e3332f..d3acea5490 100644
--- a/lib/chef/mixin/why_run.rb
+++ b/lib/chef/mixin/why_run.rb
@@ -48,7 +48,7 @@ class Chef
# block/proc that implements the action.
def add_action(descriptions, &block)
@actions << [descriptions, block]
- if !Chef::Config[:why_run]
+ if (@resource.respond_to?(:is_guard_interpreter) && @resource.is_guard_interpreter) || !Chef::Config[:why_run]
block.call
end
events.resource_update_applied(@resource, @action, descriptions)
diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb
index 80f5ac4f8d..9d8738f637 100644
--- a/lib/chef/node/attribute.rb
+++ b/lib/chef/node/attribute.rb
@@ -138,11 +138,9 @@ class Chef
:values,
:values_at,
:zip].each do |delegated_method|
- class_eval(<<-METHOD_DEFN)
- def #{delegated_method}(*args, &block)
- merged_attributes.send(:#{delegated_method}, *args, &block)
- end
- METHOD_DEFN
+ define_method(delegated_method) do |*args, &block|
+ merged_attributes.send(delegated_method, *args, &block)
+ end
end
# return the cookbook level default attribute component
diff --git a/lib/chef/node/attribute_collections.rb b/lib/chef/node/attribute_collections.rb
index 333f4864c6..b912904534 100644
--- a/lib/chef/node/attribute_collections.rb
+++ b/lib/chef/node/attribute_collections.rb
@@ -61,12 +61,10 @@ class Chef
# also invalidate the cached merged_attributes on the root
# Node::Attribute object.
MUTATOR_METHODS.each do |mutator|
- class_eval(<<-METHOD_DEFN, __FILE__, __LINE__)
- def #{mutator}(*args, &block)
- root.reset_cache(root.top_level_breadcrumb)
- super
- end
- METHOD_DEFN
+ define_method(mutator) do |*args, &block|
+ root.reset_cache(root.top_level_breadcrumb)
+ super(*args, &block)
+ end
end
attr_reader :root
@@ -126,12 +124,10 @@ class Chef
# also invalidate the cached `merged_attributes` on the root Attribute
# object.
MUTATOR_METHODS.each do |mutator|
- class_eval(<<-METHOD_DEFN, __FILE__, __LINE__)
- def #{mutator}(*args, &block)
- root.reset_cache(root.top_level_breadcrumb)
- super
- end
- METHOD_DEFN
+ define_method(mutator) do |*args, &block|
+ root.reset_cache(root.top_level_breadcrumb)
+ super(*args, &block)
+ end
end
def initialize(root, data={})
diff --git a/lib/chef/node/immutable_collections.rb b/lib/chef/node/immutable_collections.rb
index 56b8fed3b7..0e2800641a 100644
--- a/lib/chef/node/immutable_collections.rb
+++ b/lib/chef/node/immutable_collections.rb
@@ -75,12 +75,9 @@ class Chef
# Redefine all of the methods that mutate a Hash to raise an error when called.
# This is the magic that makes this object "Immutable"
DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name|
- # Ruby 1.8 blocks can't have block arguments, so we must use string eval:
- class_eval(<<-METHOD_DEFN, __FILE__, __LINE__)
- def #{mutator_method_name}(*args, &block)
- raise Exceptions::ImmutableAttributeModification
- end
- METHOD_DEFN
+ define_method(mutator_method_name) do |*args, &block|
+ raise Exceptions::ImmutableAttributeModification
+ end
end
# For elements like Fixnums, true, nil...
@@ -164,12 +161,9 @@ class Chef
# Redefine all of the methods that mutate a Hash to raise an error when called.
# This is the magic that makes this object "Immutable"
DISALLOWED_MUTATOR_METHODS.each do |mutator_method_name|
- # Ruby 1.8 blocks can't have block arguments, so we must use string eval:
- class_eval(<<-METHOD_DEFN, __FILE__, __LINE__)
- def #{mutator_method_name}(*args, &block)
+ define_method(mutator_method_name) do |*args, &block|
raise Exceptions::ImmutableAttributeModification
end
- METHOD_DEFN
end
def method_missing(symbol, *args)
diff --git a/lib/chef/org.rb b/lib/chef/org.rb
new file mode 100644
index 0000000000..41d74b6186
--- /dev/null
+++ b/lib/chef/org.rb
@@ -0,0 +1,148 @@
+#
+# Author:: Steven Danna (steve@opscode.com)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/json_compat'
+require 'chef/mixin/params_validate'
+require 'chef/rest'
+
+class Chef
+ class Org
+
+ include Chef::Mixin::ParamsValidate
+
+ def initialize(name)
+ @name = name
+ @full_name = ''
+ # The Chef API returns the private key of the validator
+ # client on create
+ @private_key = nil
+ @guid = nil
+ end
+
+ def chef_rest
+ @chef_rest ||= Chef::REST.new(Chef::Config[:chef_server_root])
+ end
+
+ def name(arg=nil)
+ set_or_return(:name, arg,
+ :regex => /^[a-z0-9\-_]+$/)
+ end
+
+ def full_name(arg=nil)
+ set_or_return(:full_name,
+ arg, :kind_of => String)
+ end
+
+ def private_key(arg=nil)
+ set_or_return(:private_key,
+ arg, :kind_of => String)
+ end
+
+ def guid(arg=nil)
+ set_or_return(:guid,
+ arg, :kind_of => String)
+ end
+
+ def to_hash
+ result = {
+ "name" => @name,
+ "full_name" => @full_name
+ }
+ result["private_key"] = @private_key if @private_key
+ result["guid"] = @guid if @guid
+ result
+ end
+
+ def to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
+ end
+
+ def create
+ payload = {:name => self.name, :full_name => self.full_name}
+ new_org = chef_rest.post_rest("organizations", payload)
+ Chef::Org.from_hash(self.to_hash.merge(new_org))
+ end
+
+ def update
+ payload = {:name => self.name, :full_name => self.full_name}
+ new_org = chef_rest.put_rest("organizations/#{name}", payload)
+ Chef::Org.from_hash(self.to_hash.merge(new_org))
+ end
+
+ def destroy
+ chef_rest.delete_rest("organizations/#{@name}")
+ end
+
+ def save
+ begin
+ create
+ rescue Net::HTTPServerException => e
+ if e.response.code == "409"
+ update
+ else
+ raise e
+ end
+ end
+ end
+
+ def associate_user(username)
+ request_body = {:user => username}
+ response = chef_rest.post_rest "organizations/#{@name}/association_requests", request_body
+ association_id = response["uri"].split("/").last
+ chef_rest.put_rest "users/#{username}/association_requests/#{association_id}", { :response => 'accept' }
+ end
+
+ def dissociate_user(username)
+ chef_rest.delete_rest "organizations/#{name}/users/#{username}"
+ end
+
+ # Class methods
+ def self.from_hash(org_hash)
+ org = Chef::Org.new(org_hash['name'])
+ org.full_name org_hash['full_name']
+ org.private_key org_hash['private_key'] if org_hash.key?('private_key')
+ org.guid org_hash['guid'] if org_hash.key?('guid')
+ org
+ end
+
+ def self.from_json(json)
+ Chef::Org.from_hash(Chef::JSONCompat.from_json(json))
+ end
+
+ class <<self
+ alias_method :json_create, :from_json
+ end
+
+ def self.load(org_name)
+ response = Chef::REST.new(Chef::Config[:chef_server_root]).get_rest("organizations/#{org_name}")
+ Chef::Org.from_hash(response)
+ end
+
+ def self.list(inflate=false)
+ orgs = Chef::REST.new(Chef::Config[:chef_server_root]).get_rest('organizations')
+ if inflate
+ orgs.inject({}) do |org_map, (name, _url)|
+ org_map[name] = Chef::Org.load(name)
+ org_map
+ end
+ else
+ orgs
+ end
+ end
+ end
+end
diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb
index 271e72f761..3c7ecf038c 100644
--- a/lib/chef/platform/provider_mapping.rb
+++ b/lib/chef/platform/provider_mapping.rb
@@ -366,7 +366,8 @@ class Chef
:openbsd => {
:default => {
:group => Chef::Provider::Group::Usermod,
- :package => Chef::Provider::Package::Openbsd
+ :package => Chef::Provider::Package::Openbsd,
+ :service => Chef::Provider::Service::Openbsd
}
},
:hpux => {
diff --git a/lib/chef/platform/provider_priority_map.rb b/lib/chef/platform/provider_priority_map.rb
index aa69761012..2517f46dfd 100644
--- a/lib/chef/platform/provider_priority_map.rb
+++ b/lib/chef/platform/provider_priority_map.rb
@@ -54,6 +54,7 @@ class Chef
#
priority :service, Chef::Provider::Service::Freebsd, os: [ "freebsd", "netbsd" ]
+ priority :service, Chef::Provider::Service::Openbsd, os: [ "openbsd" ]
#
# Solaris-en
diff --git a/lib/chef/policy_builder/policyfile.rb b/lib/chef/policy_builder/policyfile.rb
index 0df3dd5dd2..ff8e067bf6 100644
--- a/lib/chef/policy_builder/policyfile.rb
+++ b/lib/chef/policy_builder/policyfile.rb
@@ -237,7 +237,12 @@ class Chef
end
def policyfile_location
- "data/policyfiles/#{deployment_group}"
+ if Chef::Config[:policy_document_native_api]
+ validate_policy_config!
+ "policies/#{policy_group}/#{policy_name}"
+ else
+ "data/policyfiles/#{deployment_group}"
+ end
end
# Do some mimimal validation of the policyfile we fetched from the
@@ -281,6 +286,22 @@ class Chef
raise ConfigurationError, "Setting `deployment_group` is not configured."
end
+ def validate_policy_config!
+ policy_group or
+ raise ConfigurationError, "Setting `policy_group` is not configured."
+
+ policy_name or
+ raise ConfigurationError, "Setting `policy_name` is not configured."
+ end
+
+ def policy_group
+ Chef::Config[:policy_group]
+ end
+
+ def policy_name
+ Chef::Config[:policy_name]
+ end
+
# Builds a 'cookbook_hash' map of the form
# "COOKBOOK_NAME" => "IDENTIFIER"
#
diff --git a/lib/chef/provider/dsc_script.rb b/lib/chef/provider/dsc_script.rb
index 5db50e74b3..d1f62d7f0d 100644
--- a/lib/chef/provider/dsc_script.rb
+++ b/lib/chef/provider/dsc_script.rb
@@ -161,7 +161,7 @@ class Chef
cleaned_messages = resource.change_log[0..-2].map { |c| c.sub(/^#{Regexp.escape(resource.name)}/, '').strip }
"converge DSC resource #{resource.name} by #{cleaned_messages.find_all{ |c| c != ''}.join("\n")}"
else
- # This is needed because a dsc script can have resouces that are both converged and not
+ # This is needed because a dsc script can have resources that are both converged and not
"converge DSC resource #{resource.name} by doing nothing because it is already converged"
end
end
diff --git a/lib/chef/provider/env.rb b/lib/chef/provider/env.rb
index 600cac1e6b..970131c407 100644
--- a/lib/chef/provider/env.rb
+++ b/lib/chef/provider/env.rb
@@ -61,9 +61,12 @@ class Chef
def requires_modify_or_create?
if @new_resource.delim
#e.g. check for existing value within PATH
- not new_values.all? do |val|
- current_values.include? val
+ new_values.inject(0) do |index, val|
+ next_index = current_values.find_index val
+ return true if next_index.nil? || next_index < index
+ next_index
end
+ false
else
@new_resource.value != @current_resource.value
end
@@ -145,10 +148,7 @@ class Chef
def modify_env
if @new_resource.delim
- values = new_values.reject do |v|
- current_values.include?(v)
- end
- @new_resource.value((values + [@current_resource.value]).join(@new_resource.delim))
+ @new_resource.value((new_values + current_values).uniq.join(@new_resource.delim))
end
create_env
end
diff --git a/lib/chef/provider/package/rpm.rb b/lib/chef/provider/package/rpm.rb
index 131587e066..1201431fed 100644
--- a/lib/chef/provider/package/rpm.rb
+++ b/lib/chef/provider/package/rpm.rb
@@ -17,6 +17,7 @@
#
require 'chef/provider/package'
require 'chef/mixin/command'
+require 'chef/mixin/shell_out'
require 'chef/resource/package'
require 'chef/mixin/get_source_from_package'
@@ -59,14 +60,13 @@ class Chef
end
Chef::Log.debug("#{@new_resource} checking rpm status")
- status = popen4("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}") do |pid, stdin, stdout, stderr|
- stdout.each do |line|
- case line
- when /^([\w\d+_.-]+)\s([\w\d_.-]+)$/
- @current_resource.package_name($1)
- @new_resource.version($2)
- @candidate_version = $2
- end
+ status = shell_out!("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}")
+ status.stdout.each_line do |line|
+ case line
+ when /^([\w\d+_.-]+)\s([\w\d_.-]+)$/
+ @current_resource.package_name($1)
+ @new_resource.version($2)
+ @candidate_version = $2
end
end
else
@@ -77,13 +77,12 @@ class Chef
end
Chef::Log.debug("#{@new_resource} checking install state")
- @rpm_status = popen4("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@current_resource.package_name}") do |pid, stdin, stdout, stderr|
- stdout.each do |line|
- case line
- when /^([\w\d+_.-]+)\s([\w\d_.-]+)$/
- Chef::Log.debug("#{@new_resource} current version is #{$2}")
- @current_resource.version($2)
- end
+ @rpm_status = shell_out!("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@current_resource.package_name}")
+ @rpm_status.stdout.each_line do |line|
+ case line
+ when /^([\w\d+_.-]+)\s([\w\d_.-]+)$/
+ Chef::Log.debug("#{@new_resource} current version is #{$2}")
+ @current_resource.version($2)
end
end
diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb
index 96955f73b6..8c79b384e9 100644
--- a/lib/chef/provider/powershell_script.rb
+++ b/lib/chef/provider/powershell_script.rb
@@ -24,8 +24,8 @@ class Chef
protected
EXIT_STATUS_EXCEPTION_HANDLER = "\ntrap [Exception] {write-error -exception ($_.Exception.Message);exit 1}".freeze
- EXIT_STATUS_NORMALIZATION_SCRIPT = "\nif ($? -ne $true) { if ( $LASTEXITCODE -ne 0) {exit $LASTEXITCODE} else { exit 1 }}".freeze
- EXIT_STATUS_RESET_SCRIPT = "\n$LASTEXITCODE=0".freeze
+ EXIT_STATUS_NORMALIZATION_SCRIPT = "\nif ($? -ne $true) { if ( $LASTEXITCODE ) {exit $LASTEXITCODE} else { exit 1 }}".freeze
+ EXIT_STATUS_RESET_SCRIPT = "\n$global:LASTEXITCODE=$null".freeze
# Process exit codes are strange with PowerShell. Unless you
# explicitly call exit in Powershell, the powershell.exe
diff --git a/lib/chef/provider/service/openbsd.rb b/lib/chef/provider/service/openbsd.rb
new file mode 100644
index 0000000000..d509ee10ff
--- /dev/null
+++ b/lib/chef/provider/service/openbsd.rb
@@ -0,0 +1,216 @@
+#
+# Author:: Scott Bonds (<scott@ggr.com>)
+# Copyright:: Copyright (c) 2014 Scott Bonds
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/mixin/command'
+require 'chef/mixin/shell_out'
+require 'chef/provider/service/init'
+require 'chef/resource/service'
+
+class Chef
+ class Provider
+ class Service
+ class Openbsd < Chef::Provider::Service::Init
+
+ provides :service, os: [ "openbsd" ]
+
+ include Chef::Mixin::ShellOut
+
+ attr_reader :init_command, :rc_conf, :rc_conf_local, :enabled_state_found
+
+ RC_CONF_PATH = '/etc/rc.conf'
+ RC_CONF_LOCAL_PATH = '/etc/rc.conf.local'
+
+ def initialize(new_resource, run_context)
+ super
+ @rc_conf = ::File.read(RC_CONF_PATH) rescue ''
+ @rc_conf_local = ::File.read(RC_CONF_LOCAL_PATH) rescue ''
+ @init_command = ::File.exist?(rcd_script_path) ? rcd_script_path : nil
+ new_resource.supports[:status] = true
+ new_resource.status_command("#{default_init_command} check")
+ end
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Service.new(new_resource.name)
+ current_resource.service_name(new_resource.service_name)
+
+ Chef::Log.debug("#{current_resource} found at #{init_command}")
+
+ determine_current_status!
+ determine_enabled_status!
+ current_resource
+ end
+
+ def define_resource_requirements
+ shared_resource_requirements
+
+ requirements.assert(:start, :enable, :reload, :restart) do |a|
+ a.assertion { init_command }
+ a.failure_message Chef::Exceptions::Service, "#{new_resource}: unable to locate the rc.d script"
+ end
+
+ requirements.assert(:all_actions) do |a|
+ a.assertion { enabled_state_found }
+ # for consistency with original behavior, this will not fail in non-whyrun mode;
+ # rather it will silently set enabled state=>false
+ a.whyrun "Unable to determine enabled/disabled state, assuming this will be correct for an actual run. Assuming disabled."
+ end
+
+ requirements.assert(:start, :enable, :reload, :restart) do |a|
+ a.assertion { init_command && builtin_service_enable_variable_name != nil }
+ a.failure_message Chef::Exceptions::Service, "Could not find the service name in #{init_command} and rcvar"
+ # No recovery in whyrun mode - the init file is present but not correct.
+ end
+ end
+
+ def enable_service
+ if !is_enabled?
+ if is_builtin?
+ if is_enabled_by_default?
+ update_rcl rc_conf_local.sub(/^#{Regexp.escape(builtin_service_enable_variable_name)}=.*/, '')
+ else
+ # add line with blank string, which means enable
+ update_rcl rc_conf_local + "\n" + "#{builtin_service_enable_variable_name}=\"\"\n"
+ end
+ else
+ # add to pkg_scripts, most recent addition goes last
+ old_services_list = rc_conf_local.match(/^pkg_scripts="(.*)"/)
+ old_services_list = old_services_list ? old_services_list[1].split(' ') : []
+ new_services_list = old_services_list + [new_resource.service_name]
+ if rc_conf_local.match(/^pkg_scripts="(.*)"/)
+ new_rcl = rc_conf_local.sub(/^pkg_scripts="(.*)"/, "pkg_scripts=\"#{new_services_list.join(' ')}\"")
+ else
+ new_rcl = rc_conf_local + "\n" + "pkg_scripts=\"#{new_services_list.join(' ')}\"\n"
+ end
+ update_rcl new_rcl
+ end
+ end
+ end
+
+ def disable_service
+ if is_enabled?
+ if is_builtin?
+ if is_enabled_by_default?
+ # add line to disable
+ update_rcl rc_conf_local + "\n" + "#{builtin_service_enable_variable_name}=\"NO\"\n"
+ else
+ # remove line to disable
+ update_rcl rc_conf_local.sub(/^#{Regexp.escape(builtin_service_enable_variable_name)}=.*/, '')
+ end
+ else
+ # remove from pkg_scripts
+ old_list = rc_conf_local.match(/^pkg_scripts="(.*)"/)
+ old_list = old_list ? old_list[1].split(' ') : []
+ new_list = old_list - [new_resource.service_name]
+ update_rcl rc_conf_local.sub(/^pkg_scripts="(.*)"/, pkg_scripts="#{new_list.join(' ')}")
+ end
+ end
+ end
+
+ private
+
+ def rcd_script_found?
+ !init_command.nil?
+ end
+
+ def rcd_script_path
+ "/etc/rc.d/#{new_resource.service_name}"
+ end
+
+ def update_rcl(value)
+ FileUtils.touch RC_CONF_LOCAL_PATH if !::File.exists? RC_CONF_LOCAL_PATH
+ ::File.write(RC_CONF_LOCAL_PATH, value)
+ @rc_conf_local = value
+ end
+
+ # The variable name used in /etc/rc.conf.local for enabling this service
+ def builtin_service_enable_variable_name
+ @bsevn ||= begin
+ result = nil
+ if rcd_script_found?
+ ::File.open(init_command) do |rcscript|
+ if m = rcscript.read.match(/^# \$OpenBSD: (\w+)[(.rc),]?/)
+ result = m[1] + "_flags"
+ end
+ end
+ end
+ # Fallback allows us to keep running in whyrun mode when
+ # the script does not exist.
+ result || new_resource.service_name
+ end
+ end
+
+ def is_builtin?
+ result = false
+ var_name = builtin_service_enable_variable_name
+ if var_name
+ if rc_conf.match(/^#{Regexp.escape(var_name)}=(.*)/)
+ result = true
+ end
+ end
+ result
+ end
+
+ def is_enabled_by_default?
+ result = false
+ var_name = builtin_service_enable_variable_name
+ if var_name
+ if m = rc_conf.match(/^#{Regexp.escape(var_name)}=(.*)/)
+ if !(m[1] =~ /"?[Nn][Oo]"?/)
+ result = true
+ end
+ end
+ end
+ result
+ end
+
+ def determine_enabled_status!
+ result = false # Default to disabled if the service doesn't currently exist at all
+ @enabled_state_found = false
+ if is_builtin?
+ var_name = builtin_service_enable_variable_name
+ if var_name
+ if m = rc_conf_local.match(/^#{Regexp.escape(var_name)}=(.*)/)
+ @enabled_state_found = true
+ if !(m[1] =~ /"?[Nn][Oo]"?/) # e.g. looking for httpd_flags=NO
+ result = true
+ end
+ end
+ end
+ if !@enabled_state_found
+ result = is_enabled_by_default?
+ end
+ else
+ var_name = @new_resource.service_name
+ if var_name
+ if m = rc_conf_local.match(/^pkg_scripts="(.*)"/)
+ @enabled_state_found = true
+ if m[1].include?(var_name) # e.g. looking for 'gdm' in pkg_scripts="gdm unbound"
+ result = true
+ end
+ end
+ end
+ end
+
+ current_resource.enabled result
+ end
+ alias :is_enabled? :determine_enabled_status!
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/user.rb b/lib/chef/provider/user.rb
index e554dc7f2f..f6ac72448e 100644
--- a/lib/chef/provider/user.rb
+++ b/lib/chef/provider/user.rb
@@ -67,7 +67,7 @@ class Chef
@current_resource.shell(user_info.shell)
@current_resource.password(user_info.passwd)
- if @new_resource.comment && user_info.gecos.respond_to?(:force_encoding)
+ if @new_resource.comment
user_info.gecos.force_encoding(@new_resource.comment.encoding)
end
@current_resource.comment(user_info.gecos)
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index 38c0e6fc9a..796a0f8fa6 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -81,6 +81,7 @@ require 'chef/provider/service/gentoo'
require 'chef/provider/service/init'
require 'chef/provider/service/invokercd'
require 'chef/provider/service/debian'
+require 'chef/provider/service/openbsd'
require 'chef/provider/service/redhat'
require 'chef/provider/service/insserv'
require 'chef/provider/service/simple'
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index bf49cd9d26..3e9d119cee 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -38,110 +38,58 @@ require 'chef/mixin/descendants_tracker'
class Chef
class Resource
- FORBIDDEN_IVARS = [:@run_context, :@not_if, :@only_if, :@enclosing_provider]
- HIDDEN_IVARS = [:@allowed_actions, :@resource_name, :@source_line, :@run_context, :@name, :@not_if, :@only_if, :@elapsed_time, :@enclosing_provider]
+ #
+ # Generic User DSL (not resource-specific)
+ #
include Chef::DSL::DataQuery
- include Chef::Mixin::ParamsValidate
include Chef::DSL::PlatformIntrospection
include Chef::DSL::RegistryHelper
include Chef::DSL::RebootPending
- include Chef::Mixin::ConvertToClassName
- include Chef::Mixin::Deprecation
- extend Chef::Mixin::ConvertToClassName
- extend Chef::Mixin::DescendantsTracker
-
- if Module.method(:const_defined?).arity == 1
- def self.strict_const_defined?(const)
- const_defined?(const)
- end
- else
- def self.strict_const_defined?(const)
- const_defined?(const, false)
- end
- end
-
- class << self
- # back-compat
- # NOTE: that we do not support unregistering classes as descendents like
- # we used to for LWRP unloading because that was horrible and removed in
- # Chef-12.
- alias :resource_classes :descendants
- alias :find_subclass_by_name :find_descendants_by_name
- end
-
- # Set or return the list of "state attributes" implemented by the Resource
- # subclass. State attributes are attributes that describe the desired state
- # of the system, such as file permissions or ownership. In general, state
- # attributes are attributes 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, attributes that are not "state attributes"
- # 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.
- def self.state_attrs(*attr_names)
- @state_attrs ||= []
- @state_attrs = attr_names unless attr_names.empty?
-
- # Return *all* state_attrs that this class has, including inherited ones
- if superclass.respond_to?(:state_attrs)
- superclass.state_attrs + @state_attrs
- else
- @state_attrs
- end
- end
-
- # Set or return the "identity attribute" for this resource class. This is
- # generally going to be the "name attribute" for this resource. In other
- # words, the resource type plus this attribute uniquely identify a given
- # bit of state that chef manages. For a File resource, this would be the
- # path, for a package resource, it will be the package name. This will show
- # up in chef-client's audit records as a searchable field.
- def self.identity_attr(attr_name=nil)
- @identity_attr ||= nil
- @identity_attr = attr_name if attr_name
-
- # If this class doesn't have an identity attr, we'll defer to the superclass:
- if @identity_attr || !superclass.respond_to?(:identity_attr)
- @identity_attr
- else
- superclass.identity_attr
- end
+ # The node the current Chef run is using.
+ #
+ # Corresponds to `run_context.node`.
+ #
+ # @return [Chef::Node] The node the current Chef run is using.
+ #
+ def node
+ run_context && run_context.node
end
- def self.dsl_name
- convert_to_snake_case(name, 'Chef::Resource')
+ #
+ # Find existing resources by searching the list of existing resources. Possible
+ # forms are:
+ #
+ # find(:file => "foobar")
+ # find(:file => [ "foobar", "baz" ])
+ # find("file[foobar]", "file[baz]")
+ # find("file[foobar,baz]")
+ #
+ # Calls `run_context.resource_collection.find(*args)`
+ #
+ # @return the matching resource, or an Array of matching resources.
+ #
+ # @raise ArgumentError if you feed it bad lookup information
+ # @raise RuntimeError if it can't find the resources you are looking for.
+ #
+ def resources(*args)
+ run_context.resource_collection.find(*args)
end
- attr_accessor :params
- attr_accessor :provider
- attr_accessor :allowed_actions
- attr_accessor :run_context
- attr_accessor :cookbook_name
- attr_accessor :recipe_name
- attr_accessor :enclosing_provider
- attr_accessor :source_line
- attr_accessor :retries
- attr_accessor :retry_delay
- attr_accessor :declared_type
- attr_reader :updated
-
- attr_reader :resource_name
- attr_reader :not_if_args
- attr_reader :only_if_args
-
- attr_reader :elapsed_time
-
- attr_reader :default_guard_interpreter
-
- # Each notify entry is a resource/action pair, modeled as an
- # Struct with a #resource and #action member
+ #
+ # Resource User Interface (for users)
+ #
+ #
+ # Create a new Resource.
+ #
+ # @param name The name of this resource (corresponds to the #name attribute,
+ # used for notifications to this resource).
+ # @param run_context The context of the Chef run. Corresponds to #run_context.
+ #
def initialize(name, run_context=nil)
@name = name
@run_context = run_context
@@ -171,89 +119,45 @@ class Chef
@sensitive = false
end
- # Returns a Hash of attribute => value for the state attributes declared in
- # the resource's class definition.
- def state
- self.class.state_attrs.inject({}) do |state_attrs, attr_name|
- state_attrs[attr_name] = send(attr_name)
- state_attrs
- end
- end
-
- # Returns the value of the identity attribute, if declared. Falls back to
- # #name if no identity attribute is declared.
- def identity
- if identity_attr = self.class.identity_attr
- send(identity_attr)
- else
- name
- end
- end
-
- def updated=(true_or_false)
- Chef::Log.warn("Chef::Resource#updated=(true|false) is deprecated. Please call #updated_by_last_action(true|false) instead.")
- Chef::Log.warn("Called from:")
- caller[0..3].each {|line| Chef::Log.warn(line)}
- updated_by_last_action(true_or_false)
- @updated = true_or_false
- end
-
- def node
- run_context && run_context.node
- end
-
- # If an unknown method is invoked, determine whether the enclosing Provider's
- # lexical scope can fulfill the request. E.g. This happens when the Resource's
- # block invokes new_resource.
- def method_missing(method_symbol, *args, &block)
- if enclosing_provider && enclosing_provider.respond_to?(method_symbol)
- enclosing_provider.send(method_symbol, *args, &block)
- else
- raise NoMethodError, "undefined method `#{method_symbol.to_s}' for #{self.class.to_s}"
- end
- end
-
- def load_from(resource)
- resource.instance_variables.each do |iv|
- unless iv == :@source_line || iv == :@action || iv == :@not_if || iv == :@only_if
- self.instance_variable_set(iv, resource.instance_variable_get(iv))
- end
- end
- end
-
- def supports(args={})
- if args.any?
- @supports = args
- else
- @supports
+ #
+ # 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]`.
+ #
+ # @param name [String] The name to set.
+ # @return [String] The name of this Resource.
+ #
+ def name(name=nil)
+ if !name.nil?
+ raise ArgumentError, "name must be a string!" unless name.kind_of?(String)
+ @name = name
end
+ @name
end
- def provider(arg=nil)
- klass = if arg.kind_of?(String) || arg.kind_of?(Symbol)
- lookup_provider_constant(arg)
- else
- arg
- end
- set_or_return(
- :provider,
- klass,
- :kind_of => [ Class ]
- )
- 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`)
+ # @return [Array[Symbol]] the list of actions.
+ #
def action(arg=nil)
if arg
action_list = arg.kind_of?(Array) ? arg : [ arg ]
action_list = action_list.collect { |a| a.to_sym }
action_list.each do |action|
validate(
- {
- :action => action,
- },
- {
- :action => { :kind_of => Symbol, :equal_to => @allowed_actions },
- }
+ { action: action },
+ { action: { kind_of: Symbol, equal_to: @allowed_actions } }
)
end
@action = action_list
@@ -262,71 +166,60 @@ class Chef
end
end
- def name(name=nil)
- if !name.nil?
- raise ArgumentError, "name must be a string!" unless name.kind_of?(String)
- @name = name
- end
- @name
- end
-
- def noop(tf=nil)
- if !tf.nil?
- raise ArgumentError, "noop must be true or false!" unless tf == true || tf == false
- @noop = tf
- end
- @noop
- end
-
- def ignore_failure(arg=nil)
- set_or_return(
- :ignore_failure,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def retries(arg=nil)
- set_or_return(
- :retries,
- arg,
- :kind_of => Integer
- )
- end
-
- def retry_delay(arg=nil)
- set_or_return(
- :retry_delay,
- arg,
- :kind_of => Integer
- )
- end
-
- def sensitive(arg=nil)
- set_or_return(
- :sensitive,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
- )
- end
-
- def epic_fail(arg=nil)
- ignore_failure(arg)
- end
-
- def guard_interpreter(arg=nil)
- if arg.nil?
- @guard_interpreter || @default_guard_interpreter
- else
- set_or_return(
- :guard_interpreter,
- arg,
- :kind_of => Symbol
- )
- end
- end
-
- # Sets up a notification from this resource to the resource specified by +resource_spec+.
+ #
+ # Sets up a notification that will run a particular action on another resource
+ # if and when *this* resource is updated by an action.
+ #
+ # If the action does nothing--does not update this resource, the
+ # notification never triggers.)
+ #
+ # Only one resource may be specified per notification.
+ #
+ # `delayed` notifications will only *ever* happen once per resource, so if
+ # multiple resources all notify a single resource to perform the same action,
+ # the action will only happen once. However, if they ask for different
+ # actions, each action will happen once, in the order they were updated.
+ #
+ # `immediate` notifications will cause the action to be triggered once per
+ # notification, regardless of how many other resources have triggered the
+ # notification as well.
+ #
+ # @param action The action to run on the other resource.
+ # @param resource_spec [String, Hash, Chef::Resource] The resource to run.
+ # @param timing [String, Symbol] When to notify. Has these values:
+ # - `delayed`: Will run the action on the other resource after all other
+ # actions have been run. This is the default.
+ # - `immediate`, `immediately`: Will run the action on the other resource
+ # immediately (before any other action is run).
+ #
+ # @example Resource by string
+ # file '/foo.txt' do
+ # content 'hi'
+ # notifies :create, 'file[/bar.txt]'
+ # end
+ # file '/bar.txt' do
+ # action :nothing
+ # content 'hi'
+ # end
+ # @example Resource by hash
+ # file '/foo.txt' do
+ # content 'hi'
+ # notifies :create, file: '/bar.txt'
+ # end
+ # file '/bar.txt' do
+ # action :nothing
+ # content 'hi'
+ # end
+ # @example Direct Resource
+ # bar = file '/bar.txt' do
+ # action :nothing
+ # content 'hi'
+ # end
+ # file '/foo.txt' do
+ # content 'hi'
+ # notifies :create, bar
+ # end
+ #
def notifies(action, resource_spec, timing=:delayed)
# when using old-style resources(:template => "/foo.txt") style, you
# could end up with multiple resources.
@@ -342,79 +235,348 @@ class Chef
notifies_immediately(action, resource)
else
raise ArgumentError, "invalid timing: #{timing} for notifies(#{action}, #{resources.inspect}, #{timing}) resource #{self} "\
- "Valid timings are: :delayed, :immediate, :immediately"
+ "Valid timings are: :delayed, :immediate, :immediately"
end
end
true
end
- # Iterates over all immediate and delayed notifications, calling
- # resolve_resource_reference on each in turn, causing them to
- # resolve lazy/forward references.
- def resolve_notification_references
- run_context.immediate_notifications(self).each { |n|
- n.resolve_resource_reference(run_context.resource_collection)
- }
- run_context.delayed_notifications(self).each {|n|
- n.resolve_resource_reference(run_context.resource_collection)
- }
+ #
+ # Subscribes to updates from other resources, causing a particular action to
+ # run on *this* resource when the other resource is updated.
+ #
+ # If multiple resources are specified, this resource action will be run if
+ # *any* of them change.
+ #
+ # This notification will only trigger *once*, no matter how many other
+ # resources are updated (or how many actions are run by a particular resource).
+ #
+ # @param action The action to run on the other resource.
+ # @param resources [String, Resource, Array[String, Resource]] The resources to subscribe to.
+ # @param timing [String, Symbol] When to notify. Has these values:
+ # - `delayed`: An update will cause the action to run after all other
+ # actions have been run. This is the default.
+ # - `immediate`, `immediately`: The action will run immediately following
+ # the other resource being updated.
+ #
+ # @example Resources by string
+ # file '/foo.txt' do
+ # content 'hi'
+ # action :nothing
+ # subscribes :create, 'file[/bar.txt]'
+ # end
+ # file '/bar.txt' do
+ # content 'hi'
+ # end
+ # @example Direct resource
+ # bar = file '/bar.txt' do
+ # content 'hi'
+ # end
+ # file '/foo.txt' do
+ # content 'hi'
+ # action :nothing
+ # subscribes :create, '/bar.txt'
+ # end
+ # @example Multiple resources by string
+ # file '/foo.txt' do
+ # content 'hi'
+ # action :nothing
+ # subscribes :create, [ 'file[/bar.txt]', 'file[/baz.txt]' ]
+ # end
+ # file '/bar.txt' do
+ # content 'hi'
+ # end
+ # file '/baz.txt' do
+ # content 'hi'
+ # end
+ # @example Multiple resources
+ # bar = file '/bar.txt' do
+ # content 'hi'
+ # end
+ # baz = file '/bar.txt' do
+ # content 'hi'
+ # end
+ # file '/foo.txt' do
+ # content 'hi'
+ # action :nothing
+ # subscribes :create, [ bar, baz ]
+ # end
+ #
+ def subscribes(action, resources, timing=:delayed)
+ resources = [resources].flatten
+ resources.each do |resource|
+ if resource.is_a?(String)
+ resource = Chef::Resource.new(resource, run_context)
+ end
+ if resource.run_context.nil?
+ resource.run_context = run_context
+ end
+ resource.notifies(action, self, timing)
+ end
+ true
end
- def notifies_immediately(action, resource_spec)
- run_context.notifies_immediately(Notification.new(resource_spec, action, self))
+ #
+ # A command or block that indicates whether to actually run this resource.
+ # The command or block is run just before the action actually executes, and
+ # the action will be skipped if the block returns false.
+ #
+ # If a block is specified, it must return `true` in order for the Resource
+ # to be executed.
+ #
+ # If a command is specified, the resource's #guard_interpreter will run the
+ # command and interpret the results according to `opts`. For example, the
+ # default `execute` resource will be treated as `false` if the command
+ # returns a non-zero exit code, and `true` if it returns 0. Thus, in the
+ # default case:
+ #
+ # - `only_if "your command"` will perform the action only if `your command`
+ # returns 0.
+ # - `only_if "your command", valid_exit_codes: [ 1, 2, 3 ]` will perform the
+ # action only if `your command` returns 1, 2, or 3.
+ #
+ # @param command [String] A string to execute.
+ # @param opts [Hash] Options control the execution of the command
+ # @param block [Proc] A ruby block to run. Ignored if a command is given.
+ #
+ def only_if(command=nil, opts={}, &block)
+ if command || block_given?
+ @only_if << Conditional.only_if(self, command, opts, &block)
+ end
+ @only_if
end
- def notifies_delayed(action, resource_spec)
- run_context.notifies_delayed(Notification.new(resource_spec, action, self))
+ #
+ # A command or block that indicates whether to actually run this resource.
+ # The command or block is run just before the action actually executes, and
+ # the action will be skipped if the block returns true.
+ #
+ # If a block is specified, it must return `false` in order for the Resource
+ # to be executed.
+ #
+ # If a command is specified, the resource's #guard_interpreter will run the
+ # command and interpret the results according to `opts`. For example, the
+ # default `execute` resource will be treated as `false` if the command
+ # returns a non-zero exit code, and `true` if it returns 0. Thus, in the
+ # default case:
+ #
+ # - `not_if "your command"` will perform the action only if `your command`
+ # returns a non-zero code.
+ # - `only_if "your command", valid_exit_codes: [ 1, 2, 3 ]` will perform the
+ # action only if `your command` returns something other than 1, 2, or 3.
+ #
+ # @param command [String] A string to execute.
+ # @param opts [Hash] Options control the execution of the command
+ # @param block [Proc] A ruby block to run. Ignored if a command is given.
+ #
+ def not_if(command=nil, opts={}, &block)
+ if command || block_given?
+ @not_if << Conditional.not_if(self, command, opts, &block)
+ end
+ @not_if
end
- def immediate_notifications
- run_context.immediate_notifications(self)
+ #
+ # The number of times to retry this resource if it fails by throwing an
+ # exception while running an action. Default: 0
+ #
+ # When the retries have run out, the Resource will throw the last
+ # exception.
+ #
+ # @param arg [Integer] The number of retries.
+ # @return [Integer] The number of retries.
+ #
+ def retries(arg=nil)
+ set_or_return(:retries, arg, kind_of: Integer)
end
+ attr_writer :retries
- def delayed_notifications
- run_context.delayed_notifications(self)
+ #
+ # The number of seconds to wait between retries. Default: 2.
+ #
+ # @param arg [Integer] The number of seconds to wait between retries.
+ # @return [Integer] The number of seconds to wait between retries.
+ #
+ def retry_delay(arg=nil)
+ set_or_return(:retry_delay, arg, kind_of: Integer)
end
+ attr_writer :retry_delay
- def resources(*args)
- run_context.resource_collection.find(*args)
+ #
+ # Whether to treat this resource's data as sensitive. If set, no resource
+ # data will be displayed in log output.
+ #
+ # @param arg [Boolean] Whether this resource is sensitive or not.
+ # @return [Boolean] Whether this resource is sensitive or not.
+ #
+ def sensitive(arg=nil)
+ set_or_return(:sensitive, arg, :kind_of => [ TrueClass, FalseClass ])
end
+ attr_writer :sensitive
- def subscribes(action, resources, timing=:delayed)
- resources = [resources].flatten
- resources.each do |resource|
- if resource.is_a?(String)
- resource = Chef::Resource.new(resource, run_context)
- end
- if resource.run_context.nil?
- resource.run_context = run_context
- end
- resource.notifies(action, self, timing)
+ # ??? TODO unreferenced. Delete?
+ attr_reader :not_if_args
+ # ??? TODO unreferenced. Delete?
+ attr_reader :only_if_args
+
+ #
+ # The time it took (in seconds) to run the most recently-run action. Not
+ # cumulative across actions. This is set to 0 as soon as a new action starts
+ # running, and set to the elapsed time at the end of the action.
+ #
+ # @return [Integer] The time (in seconds) it took to process the most recent
+ # action. Not cumulative.
+ #
+ attr_reader :elapsed_time
+
+ #
+ # The guard interpreter that will be used to process `only_if` and `not_if`
+ # statements. If left unset, the #default_guard_interpreter will be used.
+ #
+ # Must be a resource class like `Chef::Resource::Execute`, or
+ # a corresponding to the name of a resource. The resource must descend from
+ # `Chef::Resource::Execute`.
+ #
+ # TODO this needs to be coerced on input so that retrieval is consistent.
+ #
+ # @param arg [Class, Symbol, String] The Guard interpreter resource class/
+ # symbol/name.
+ # @return [Class, Symbol, String] The Guard interpreter resource.
+ #
+ def guard_interpreter(arg=nil)
+ if arg.nil?
+ @guard_interpreter || @default_guard_interpreter
+ else
+ set_or_return(:guard_interpreter, arg, :kind_of => Symbol)
end
- true
end
- def validate_resource_spec!(resource_spec)
- run_context.resource_collection.validate_lookup_spec!(resource_spec)
+ #
+ # Get the value of the state attributes in this resource as a hash.
+ #
+ # @return [Hash{Symbol => Object}] A Hash of attribute => value for the
+ # Resource class's `state_attrs`.
+ def state
+ self.class.state_attrs.inject({}) do |state_attrs, attr_name|
+ state_attrs[attr_name] = send(attr_name)
+ state_attrs
+ end
end
- def is(*args)
- if args.size == 1
- args.first
+ #
+ # The value of the identity attribute, if declared. Falls back to #name if
+ # no identity attribute is declared.
+ #
+ # @return The value of the identity attribute.
+ #
+ def identity
+ if identity_attr = self.class.identity_attr
+ send(identity_attr)
else
- return *args
+ name
end
end
- # We usually want to store and reference resources by their declared type and not the actual type that
- # was looked up by the Resolver (IE, "package" becomes YumPackage class). If we have not been provided
- # the declared key we want to fall back on the old to_s key.
- def declared_key
- return to_s if declared_type.nil?
- "#{declared_type}[#{@name}]"
+ #
+ # Whether to ignore failures. If set to `true`, and this resource when an
+ # action is run, the resource will be marked as failed but no exception will
+ # be thrown (and no error will be output). Defaults to `false`.
+ #
+ # TODO ignore_failure and retries seem to be mutually exclusive; I doubt
+ # that was intended.
+ #
+ # @param arg [Boolean] Whether to ignore failures.
+ # @return Whether this resource will ignore failures.
+ #
+ def ignore_failure(arg=nil)
+ set_or_return(:ignore_failure, arg, kind_of: [ TrueClass, FalseClass ])
+ end
+ attr_writer :ignore_failure
+
+ #
+ # Equivalent to #ignore_failure.
+ #
+ def epic_fail(arg=nil)
+ ignore_failure(arg)
+ end
+
+ #
+ # Make this resource into an exact (shallow) copy of the other resource.
+ #
+ # @param resource [Chef::Resource] The resource to copy from.
+ #
+ def load_from(resource)
+ resource.instance_variables.each do |iv|
+ unless iv == :@source_line || iv == :@action || iv == :@not_if || iv == :@only_if
+ self.instance_variable_set(iv, resource.instance_variable_get(iv))
+ end
+ end
+ end
+
+ #
+ # Runs the given action on this resource, immediately.
+ #
+ # @param action The action to run (e.g. `:create`)
+ # @param notification_type The notification type that triggered this (if any)
+ # @param notifying_resource The resource that triggered this notification (if any)
+ #
+ # @raise Any error that occurs during the actual action.
+ #
+ def run_action(action, notification_type=nil, notifying_resource=nil)
+ # reset state in case of multiple actions on the same resource.
+ @elapsed_time = 0
+ start_time = Time.now
+ events.resource_action_start(self, action, notification_type, notifying_resource)
+ # Try to resolve lazy/forward references in notifications again to handle
+ # the case where the resource was defined lazily (ie. in a ruby_block)
+ resolve_notification_references
+ validate_action(action)
+
+ if Chef::Config[:verbose_logging] || Chef::Log.level == :debug
+ # This can be noisy
+ Chef::Log.info("Processing #{self} action #{action} (#{defined_at})")
+ end
+
+ # ensure that we don't leave @updated_by_last_action set to true
+ # on accident
+ updated_by_last_action(false)
+
+ # Don't modify @retries directly and keep it intact, so that the
+ # recipe_snippet from ResourceFailureInspector can print the value
+ # that was set in the resource block initially.
+ remaining_retries = retries
+
+ begin
+ return if should_skip?(action)
+ provider_for_action(action).run_action
+ rescue Exception => e
+ if ignore_failure
+ Chef::Log.error("#{custom_exception_message(e)}; ignore_failure is set, continuing")
+ events.resource_failed(self, action, e)
+ elsif remaining_retries > 0
+ events.resource_failed_retriable(self, action, remaining_retries, e)
+ remaining_retries -= 1
+ Chef::Log.info("Retrying execution of #{self}, #{remaining_retries} attempt(s) left")
+ sleep retry_delay
+ retry
+ else
+ events.resource_failed(self, action, e)
+ raise customize_exception(e)
+ end
+ ensure
+ @elapsed_time = Time.now - start_time
+ # Reporting endpoint doesn't accept a negative resource duration so set it to 0.
+ # A negative value can occur when a resource changes the system time backwards
+ @elapsed_time = 0 if @elapsed_time < 0
+ events.resource_completed(self)
+ end
end
+ #
+ # Generic Ruby and Data Structure Stuff (for user)
+ #
+
def to_s
"#{@resource_name}[#{@name}]"
end
@@ -474,46 +636,344 @@ class Chef
instance_vars
end
- # If command is a block, returns true if the block returns true, false if it returns false.
- # ("Only run this resource if the block is true")
+ def self.json_create(o)
+ resource = self.new(o["instance_vars"]["@name"])
+ o["instance_vars"].each do |k,v|
+ resource.instance_variable_set("@#{k}".to_sym, v)
+ end
+ resource
+ end
+
+ #
+ # Resource Definition Interface (for resource developers)
+ #
+
+ include Chef::Mixin::ParamsValidate
+ include Chef::Mixin::Deprecation
+
#
- # If the command is not a block, executes the command. If it returns any status other than
- # 0, it returns false (clearly, a 0 status code is true)
+ # The provider class for this resource.
#
- # === Parameters
- # command<String>:: A a string to execute.
- # opts<Hash>:: Options control the execution of the command
- # block<Proc>:: A ruby block to run. Ignored if a command is given.
+ # If this is not set, `provider_for_action` will dynamically determine the
+ # provider.
#
- # === Evaluation
- # * evaluates to true if the block is true, or if the command returns 0
- # * evaluates to false if the block is false, or if the command returns a non-zero exit code.
- def only_if(command=nil, opts={}, &block)
- if command || block_given?
- @only_if << Conditional.only_if(self, command, opts, &block)
+ # @param arg [String, Symbol, Class] Sets the provider class for this resource.
+ # If passed a String or Symbol, e.g. `:file` or `"file"`, looks up the
+ # provider based on the name.
+ # @return The provider class for this resource.
+ #
+ def provider(arg=nil)
+ klass = if arg.kind_of?(String) || arg.kind_of?(Symbol)
+ lookup_provider_constant(arg)
+ else
+ arg
+ end
+ set_or_return(:provider, klass, kind_of: [ Class ])
+ end
+ def provider=(arg)
+ provider(arg)
+ end
+
+ # Set or return the list of "state attributes" implemented by the Resource
+ # subclass. State attributes are attributes that describe the desired state
+ # of the system, such as file permissions or ownership. In general, state
+ # attributes are attributes 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, attributes that are not "state attributes"
+ # 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.
+ def self.state_attrs(*attr_names)
+ @state_attrs ||= []
+ @state_attrs = attr_names unless attr_names.empty?
+
+ # Return *all* state_attrs that this class has, including inherited ones
+ if superclass.respond_to?(:state_attrs)
+ superclass.state_attrs + @state_attrs
+ else
+ @state_attrs
+ end
+ end
+
+ # Set or return the "identity attribute" for this resource class. This is
+ # generally going to be the "name attribute" for this resource. In other
+ # words, the resource type plus this attribute uniquely identify a given
+ # bit of state that chef manages. For a File resource, this would be the
+ # path, for a package resource, it will be the package name. This will show
+ # up in chef-client's audit records as a searchable field.
+ def self.identity_attr(attr_name=nil)
+ @identity_attr ||= nil
+ @identity_attr = attr_name if attr_name
+
+ # If this class doesn't have an identity attr, we'll defer to the superclass:
+ if @identity_attr || !superclass.respond_to?(:identity_attr)
+ @identity_attr
+ else
+ superclass.identity_attr
end
- @only_if
end
- # If command is a block, returns false if the block returns true, true if it returns false.
- # ("Do not run this resource if the block is true")
#
- # If the command is not a block, executes the command. If it returns a 0 exitstatus, returns false.
- # ("Do not run this resource if the command returns 0")
+ # The guard interpreter that will be used to process `only_if` and `not_if`
+ # statements by default. If left unset, or set to `:default`, the guard
+ # interpreter used will be Chef::GuardInterpreter::DefaultGuardInterpreter.
#
- # === Parameters
- # command<String>:: A a string to execute.
- # opts<Hash>:: Options control the execution of the command
- # block<Proc>:: A ruby block to run. Ignored if a command is given.
+ # Must be a resource class like `Chef::Resource::Execute`, or
+ # a corresponding to the name of a resource. The resource must descend from
+ # `Chef::Resource::Execute`.
#
- # === Evaluation
- # * evaluates to true if the block is false, or if the command returns a non-zero exit status.
- # * evaluates to false if the block is true, or if the command returns a 0 exit status.
- def not_if(command=nil, opts={}, &block)
- if command || block_given?
- @not_if << Conditional.not_if(self, command, opts, &block)
+ # TODO this needs to be coerced on input so that retrieval is consistent.
+ #
+ # @return [Class, Symbol, String] the default Guard interpreter resource.
+ #
+ attr_reader :default_guard_interpreter
+
+ #
+ # The list of actions this Resource is allowed to have. Setting `action`
+ # will fail unless it is in this list. Default: [ :nothing ]
+ #
+ # @return [Array<Symbol>] The list of actions this Resource is allowed to
+ # have.
+ #
+ attr_accessor :allowed_actions
+
+ #
+ # Whether or not this resource was updated during an action. If multiple
+ # actions are set on the resource, this will be `true` if *any* action
+ # caused an update to happen.
+ #
+ # @return [Boolean] Whether the resource was updated during an action.
+ #
+ attr_reader :updated
+
+ #
+ # Whether or not this resource was updated during an action. If multiple
+ # actions are set on the resource, this will be `true` if *any* action
+ # caused an update to happen.
+ #
+ # @return [Boolean] Whether the resource was updated during an action.
+ #
+ def updated?
+ updated
+ end
+
+ #
+ # Whether or not this resource was updated during the most recent action.
+ # This is set to `false` at the beginning of each action.
+ #
+ # @param true_or_false [Boolean] Whether the resource was updated during the
+ # current / most recent action.
+ # @return [Boolean] Whether the resource was updated during the most recent action.
+ #
+ def updated_by_last_action(true_or_false)
+ @updated ||= true_or_false
+ @updated_by_last_action = true_or_false
+ end
+
+ #
+ # Whether or not this resource was updated during the most recent action.
+ # This is set to `false` at the beginning of each action.
+ #
+ # @return [Boolean] Whether the resource was updated during the most recent action.
+ #
+ def updated_by_last_action?
+ @updated_by_last_action
+ end
+
+ #
+ # Set whether this class was updated during an action.
+ #
+ # @deprecated Multiple actions are supported by resources. Please call {}#updated_by_last_action} instead.
+ #
+ def updated=(true_or_false)
+ Chef::Log.warn("Chef::Resource#updated=(true|false) is deprecated. Please call #updated_by_last_action(true|false) instead.")
+ Chef::Log.warn("Called from:")
+ caller[0..3].each {|line| Chef::Log.warn(line)}
+ updated_by_last_action(true_or_false)
+ @updated = true_or_false
+ end
+
+ #
+ # The DSL name of this resource (e.g. `package` or `yum_package`)
+ #
+ # @return [String] The DSL name of this resource.
+ def self.dsl_name
+ convert_to_snake_case(name, 'Chef::Resource')
+ end
+
+ #
+ # The name of this resource (e.g. `file`)
+ #
+ # @return [String] The name of this resource.
+ #
+ attr_reader :resource_name
+
+ #
+ # Sets a list of capabilities of the real resource. For example, `:remount`
+ # (for filesystems) and `:restart` (for services).
+ #
+ # TODO Calling resource.supports({}) will not set this to empty; it will do
+ # a get instead. That's wrong.
+ #
+ # @param args Hash{Symbol=>Boolean} If non-empty, sets the capabilities of
+ # this resource. Default: {}
+ # @return Hash{Symbol=>Boolean} An array of things this resource supports.
+ #
+ def supports(args={})
+ if args.any?
+ @supports = args
+ else
+ @supports
end
- @not_if
+ end
+ def supports=(args)
+ supports(args)
+ end
+
+ #
+ # A hook called after a resource is created. Meant to be overriden by
+ # subclasses.
+ #
+ def after_created
+ nil
+ end
+
+ #
+ # The module where Chef should look for providers for this resource.
+ # The provider for `MyResource` will be looked up using
+ # `provider_base::MyResource`. Defaults to `Chef::Provider`.
+ #
+ # @param arg [Module] The module containing providers for this resource
+ # @return [Module] The module containing providers for this resource
+ #
+ # @example
+ # class MyResource < Chef::Resource
+ # provider_base Chef::Provider::Deploy
+ # # ...other stuff
+ # end
+ #
+ def self.provider_base(arg=nil)
+ @provider_base ||= arg
+ @provider_base ||= Chef::Provider
+ end
+
+
+ #
+ # Internal Resource Interface (for Chef)
+ #
+
+ FORBIDDEN_IVARS = [:@run_context, :@not_if, :@only_if, :@enclosing_provider]
+ HIDDEN_IVARS = [:@allowed_actions, :@resource_name, :@source_line, :@run_context, :@name, :@not_if, :@only_if, :@elapsed_time, :@enclosing_provider]
+
+ include Chef::Mixin::ConvertToClassName
+ extend Chef::Mixin::ConvertToClassName
+ extend Chef::Mixin::DescendantsTracker
+
+ # XXX: this is required for definition params inside of the scope of a
+ # subresource to work correctly.
+ attr_accessor :params
+
+ # @return [Chef::RunContext] The run context for this Resource. This is
+ # where the context for the current Chef run is stored, including the node
+ # and the resource collection.
+ attr_accessor :run_context
+
+ # @return [String] The cookbook this resource was declared in.
+ attr_accessor :cookbook_name
+
+ # @return [String] The recipe this resource was declared in.
+ attr_accessor :recipe_name
+
+ # @return [Chef::Provider] The provider this resource was declared in (if
+ # it was declared in an LWRP). When you call methods that do not exist
+ # on this Resource, Chef will try to call the method on the provider
+ # as well before giving up.
+ attr_accessor :enclosing_provider
+
+ # @return [String] The source line where this resource was declared.
+ # Expected to come from caller() or a stack trace, it usually follows one
+ # of these formats:
+ # /some/path/to/file.rb:80:in `wombat_tears'
+ # C:/some/path/to/file.rb:80 in 1`wombat_tears'
+ attr_accessor :source_line
+
+ # @return [String] The actual name that was used to create this resource.
+ # Sometimes, when you say something like `package 'blah'`, the system will
+ # create a different resource (i.e. `YumPackage`). When this happens, the
+ # user will expect to see the thing they wrote, not the type that was
+ # returned. May be `nil`, in which case callers should read #resource_name.
+ # See #declared_key.
+ attr_accessor :declared_type
+
+ #
+ # Iterates over all immediate and delayed notifications, calling
+ # resolve_resource_reference on each in turn, causing them to
+ # resolve lazy/forward references.
+ def resolve_notification_references
+ run_context.immediate_notifications(self).each { |n|
+ n.resolve_resource_reference(run_context.resource_collection)
+ }
+ run_context.delayed_notifications(self).each {|n|
+ n.resolve_resource_reference(run_context.resource_collection)
+ }
+ end
+
+ # Helper for #notifies
+ def notifies_immediately(action, resource_spec)
+ run_context.notifies_immediately(Notification.new(resource_spec, action, self))
+ end
+
+ # Helper for #notifies
+ def notifies_delayed(action, resource_spec)
+ run_context.notifies_delayed(Notification.new(resource_spec, action, self))
+ end
+
+ def self.strict_const_defined?(const)
+ const_defined?(const, false)
+ end
+
+ class << self
+ # back-compat
+ # NOTE: that we do not support unregistering classes as descendents like
+ # we used to for LWRP unloading because that was horrible and removed in
+ # Chef-12.
+ alias :resource_classes :descendants
+ alias :find_subclass_by_name :find_descendants_by_name
+ end
+
+ # If an unknown method is invoked, determine whether the enclosing Provider's
+ # lexical scope can fulfill the request. E.g. This happens when the Resource's
+ # block invokes new_resource.
+ def method_missing(method_symbol, *args, &block)
+ if enclosing_provider && enclosing_provider.respond_to?(method_symbol)
+ enclosing_provider.send(method_symbol, *args, &block)
+ else
+ raise NoMethodError, "undefined method `#{method_symbol.to_s}' for #{self.class.to_s}"
+ end
+ end
+
+ # Helper for #notifies
+ def validate_resource_spec!(resource_spec)
+ run_context.resource_collection.validate_lookup_spec!(resource_spec)
+ end
+
+ # We usually want to store and reference resources by their declared type and not the actual type that
+ # was looked up by the Resolver (IE, "package" becomes YumPackage class). If we have not been provided
+ # the declared key we want to fall back on the old to_s key.
+ def declared_key
+ return to_s if declared_type.nil?
+ "#{declared_type}[#{@name}]"
+ end
+
+ def immediate_notifications
+ run_context.immediate_notifications(self)
+ end
+
+ def delayed_notifications
+ run_context.delayed_notifications(self)
end
def defined_at
@@ -531,6 +991,11 @@ class Chef
end
end
+ #
+ # The cookbook in which this Resource was defined (if any).
+ #
+ # @return Chef::CookbookVersion The cookbook in which this Resource was defined.
+ #
def cookbook_version
if cookbook_name
run_context.cookbook_collection[cookbook_name]
@@ -541,56 +1006,6 @@ class Chef
run_context.events
end
- def run_action(action, notification_type=nil, notifying_resource=nil)
- # reset state in case of multiple actions on the same resource.
- @elapsed_time = 0
- start_time = Time.now
- events.resource_action_start(self, action, notification_type, notifying_resource)
- # Try to resolve lazy/forward references in notifications again to handle
- # the case where the resource was defined lazily (ie. in a ruby_block)
- resolve_notification_references
- validate_action(action)
-
- if Chef::Config[:verbose_logging] || Chef::Log.level == :debug
- # This can be noisy
- Chef::Log.info("Processing #{self} action #{action} (#{defined_at})")
- end
-
- # ensure that we don't leave @updated_by_last_action set to true
- # on accident
- updated_by_last_action(false)
-
- # Don't modify @retries directly and keep it intact, so that the
- # recipe_snippet from ResourceFailureInspector can print the value
- # that was set in the resource block initially.
- remaining_retries = retries
-
- begin
- return if should_skip?(action)
- provider_for_action(action).run_action
- rescue Exception => e
- if ignore_failure
- Chef::Log.error("#{custom_exception_message(e)}; ignore_failure is set, continuing")
- events.resource_failed(self, action, e)
- elsif remaining_retries > 0
- events.resource_failed_retriable(self, action, remaining_retries, e)
- remaining_retries -= 1
- Chef::Log.info("Retrying execution of #{self}, #{remaining_retries} attempt(s) left")
- sleep retry_delay
- retry
- else
- events.resource_failed(self, action, e)
- raise customize_exception(e)
- end
- ensure
- @elapsed_time = Time.now - start_time
- # Reporting endpoint doesn't accept a negative resource duration so set it to 0.
- # A negative value can occur when a resource changes the system time backwards
- @elapsed_time = 0 if @elapsed_time < 0
- events.resource_completed(self)
- end
- end
-
def validate_action(action)
raise ArgumentError, "nil is not a valid action for resource #{self}" if action.nil?
end
@@ -601,6 +1016,30 @@ class Chef
provider
end
+ # ??? TODO Seems unused. Delete?
+ def noop(tf=nil)
+ if !tf.nil?
+ raise ArgumentError, "noop must be true or false!" unless tf == true || tf == false
+ @noop = tf
+ end
+ @noop
+ end
+
+ # TODO Seems unused. Delete?
+ def is(*args)
+ if args.size == 1
+ args.first
+ else
+ return *args
+ end
+ end
+
+ #
+ # Preface an exception message with generic Resource information.
+ #
+ # @param e [StandardError] An exception with `e.message`
+ # @return [String] An exception message customized with class name.
+ #
def custom_exception_message(e)
"#{self} (#{defined_at}) had an error: #{e.class.name}: #{e.message}"
end
@@ -610,6 +1049,7 @@ class Chef
new_exception.set_backtrace(e.backtrace)
new_exception
end
+
# Evaluates not_if and only_if conditionals. Returns a falsey value if any
# of the conditionals indicate that this resource should be skipped, i.e.,
# if an only_if evaluates to false or a not_if evaluates to true.
@@ -635,48 +1075,6 @@ class Chef
end
end
- def updated_by_last_action(true_or_false)
- @updated ||= true_or_false
- @updated_by_last_action = true_or_false
- end
-
- def updated_by_last_action?
- @updated_by_last_action
- end
-
- def updated?
- updated
- end
-
- def self.json_create(o)
- resource = self.new(o["instance_vars"]["@name"])
- o["instance_vars"].each do |k,v|
- resource.instance_variable_set("@#{k}".to_sym, v)
- end
- resource
- end
-
- # Hook to allow a resource to run specific code after creation
- def after_created
- nil
- end
-
- # Resources that want providers namespaced somewhere other than
- # Chef::Provider can set the namespace with +provider_base+
- # Ex:
- # class MyResource < Chef::Resource
- # provider_base Chef::Provider::Deploy
- # # ...other stuff
- # end
- def self.provider_base(arg=nil)
- @provider_base ||= arg
- @provider_base ||= Chef::Provider
- end
-
- def self.node_map
- @@node_map ||= NodeMap.new
- end
-
# Maps a short_name (and optionally a platform and version) to a
# Chef::Resource. This allows finer grained per platform resource
# attributes and the end of overloaded resource definitions
@@ -719,6 +1117,10 @@ class Chef
klass
end
+ def self.node_map
+ @@node_map ||= NodeMap.new
+ end
+
# Returns the class of a Chef::Resource based on the short name
# ==== Parameters
# short_name<Symbol>:: short_name of the resource (ie :directory)
diff --git a/lib/chef/resource/conditional.rb b/lib/chef/resource/conditional.rb
index 8960a4d57f..cdc2638ef0 100644
--- a/lib/chef/resource/conditional.rb
+++ b/lib/chef/resource/conditional.rb
@@ -55,7 +55,7 @@ class Chef
def configure
case @command
- when String
+ when String,Array
@guard_interpreter = new_guard_interpreter(@parent_resource, @command, @command_opts, &@block)
@block = nil
when nil
diff --git a/lib/chef/resource/execute.rb b/lib/chef/resource/execute.rb
index 6853b62887..9f8b629fb8 100644
--- a/lib/chef/resource/execute.rb
+++ b/lib/chef/resource/execute.rb
@@ -26,6 +26,12 @@ class Chef
identity_attr :command
+ # The ResourceGuardInterpreter wraps a resource's guards in another resource. That inner resource
+ # needs to behave differently during (for example) why_run mode, so we flag it here. For why_run mode
+ # we still want to execute the guard resource even if we are not executing the wrapping resource.
+ # Only execute resources (and subclasses) can be guard interpreters.
+ attr_accessor :is_guard_interpreter
+
def initialize(name, run_context=nil)
super
@resource_name = :execute
@@ -43,6 +49,7 @@ class Chef
@allowed_actions.push(:run)
@umask = nil
@default_guard_interpreter = :execute
+ @is_guard_interpreter = false
end
def umask(arg=nil)
diff --git a/lib/chef/resource/lwrp_base.rb b/lib/chef/resource/lwrp_base.rb
index 20d177f507..0a1253780c 100644
--- a/lib/chef/resource/lwrp_base.rb
+++ b/lib/chef/resource/lwrp_base.rb
@@ -73,16 +73,7 @@ class Chef
# Define an attribute on this resource, including optional validation
# parameters.
def self.attribute(attr_name, validation_opts={})
- # Ruby 1.8 doesn't support default arguments to blocks, but we have to
- # use define_method with a block to capture +validation_opts+.
- # Workaround this by defining two methods :(
- class_eval(<<-SHIM, __FILE__, __LINE__)
- def #{attr_name}(arg=nil)
- _set_or_return_#{attr_name}(arg)
- end
- SHIM
-
- define_method("_set_or_return_#{attr_name.to_s}".to_sym) do |arg|
+ define_method(attr_name) do |arg=nil|
set_or_return(attr_name.to_sym, arg, validation_opts)
end
end
diff --git a/lib/chef/resource/template.rb b/lib/chef/resource/template.rb
index 8c9607ee07..67a9e6a418 100644
--- a/lib/chef/resource/template.rb
+++ b/lib/chef/resource/template.rb
@@ -102,9 +102,8 @@ class Chef
#
# ==== Method Arguments:
# Helper methods can also take arguments. The syntax available for
- # argument specification will be dependent on ruby version. Ruby 1.8 only
- # supports a subset of the argument specification syntax available for
- # method definition, whereas 1.9 supports the full syntax.
+ # argument specification supports full syntax available for method
+ # definition.
#
# Continuing the above example of simplifying attribute access, we can
# define a helper to look up app-specific attributes like this:
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index bb1bf28ad7..fc54506407 100644
--- a/lib/chef/run_context.rb
+++ b/lib/chef/run_context.rb
@@ -198,7 +198,7 @@ ERROR_MESSAGE
end
# An Array of all recipes that have been loaded. This is stored internally
- # as a Hash, so ordering is not preserved when using ruby 1.8.
+ # as a Hash, so ordering is predictable.
#
# Recipe names are given in fully qualified form, e.g., the recipe "nginx"
# will be given as "nginx::default"
@@ -209,7 +209,7 @@ ERROR_MESSAGE
end
# An Array of all attributes files that have been loaded. Stored internally
- # using a Hash, so order is not preserved on ruby 1.8.
+ # using a Hash, so order is predictable.
#
# Attribute file names are given in fully qualified form, e.g.,
# "nginx::default" instead of "nginx".
diff --git a/lib/chef/util/diff.rb b/lib/chef/util/diff.rb
index 3117484a71..c2dc6e045c 100644
--- a/lib/chef/util/diff.rb
+++ b/lib/chef/util/diff.rb
@@ -176,10 +176,7 @@ class Chef
end
def encode_diff_for_json(diff_str)
- if Object.const_defined? :Encoding
- diff_str.encode!('UTF-8', :invalid => :replace, :undef => :replace, :replace => '?')
- end
- return diff_str
+ diff_str.encode!('UTF-8', :invalid => :replace, :undef => :replace, :replace => '?')
end
end
diff --git a/lib/chef/util/path_helper.rb b/lib/chef/util/path_helper.rb
index 26c9c76fe5..a5485a864b 100644
--- a/lib/chef/util/path_helper.rb
+++ b/lib/chef/util/path_helper.rb
@@ -101,9 +101,6 @@ class Chef
# Produces a comparable path.
def self.canonical_path(path, add_prefix=true)
- # Rather than find an equivalent for File.absolute_path on 1.8.7, just bail out
- raise NotImplementedError, "This feature is not supported on Ruby versions < 1.9" if RUBY_VERSION.to_f < 1.9
-
# First remove extra separators and resolve any relative paths
abs_path = File.absolute_path(path)
diff --git a/spec/data/recipes.tgz b/spec/data/recipes.tgz
new file mode 100644
index 0000000000..a6c172a001
--- /dev/null
+++ b/spec/data/recipes.tgz
Binary files differ
diff --git a/spec/functional/file_content_management/deploy_strategies_spec.rb b/spec/functional/file_content_management/deploy_strategies_spec.rb
index bcd171eb73..03a6c504c1 100644
--- a/spec/functional/file_content_management/deploy_strategies_spec.rb
+++ b/spec/functional/file_content_management/deploy_strategies_spec.rb
@@ -20,15 +20,6 @@ require 'spec_helper'
shared_examples_for "a content deploy strategy" do
- # Ruby 1.8 has no binread
- def binread(file)
- if IO.respond_to?(:binread)
- IO.binread(file)
- else
- IO.read(file)
- end
- end
-
def normalize_mode(mode_int)
( mode_int & 07777).to_s(8)
end
@@ -160,7 +151,7 @@ shared_examples_for "a content deploy strategy" do
it "updates the target with content from staged" do
content_deployer.deploy(staging_file_path, target_file_path)
- expect(binread(target_file_path)).to eq(staging_file_content)
+ expect(IO.binread(target_file_path)).to eq(staging_file_content)
end
context "when the owner of the target file is not the owner of the staging file", :requires_root do
diff --git a/spec/functional/knife/exec_spec.rb b/spec/functional/knife/exec_spec.rb
index 0a9177b5e8..6262094a9f 100644
--- a/spec/functional/knife/exec_spec.rb
+++ b/spec/functional/knife/exec_spec.rb
@@ -41,9 +41,7 @@ describe Chef::Knife::Exec do
@server.stop
end
- skip "executes a script in the context of the chef-shell main context", :ruby_18_only
-
- it "executes a script in the context of the chef-shell main context", :ruby_gte_19_only do
+ it "executes a script in the context of the chef-shell main context" do
@node = Chef::Node.new
@node.name("ohai-world")
response = {"rows" => [@node],"start" => 0,"total" => 1}
diff --git a/spec/functional/resource/execute_spec.rb b/spec/functional/resource/execute_spec.rb
index 020814fcd6..aaa1c772b7 100644
--- a/spec/functional/resource/execute_spec.rb
+++ b/spec/functional/resource/execute_spec.rb
@@ -35,6 +35,32 @@ describe Chef::Resource::Execute do
end
end
+ describe "when why_run is enabled" do
+ before do
+ Chef::Config[:why_run] = true
+ end
+
+ let(:guard) { "ruby -e 'exit 0'" }
+ let!(:guard_resource) {
+ interpreter = Chef::GuardInterpreter::ResourceGuardInterpreter.new(resource, guard, nil)
+ interpreter.send(:get_interpreter_resource, resource)
+ }
+
+ it "executes the guard and not the regular resource" do
+ expect_any_instance_of(Chef::GuardInterpreter::ResourceGuardInterpreter).to receive(:get_interpreter_resource).and_return(guard_resource)
+
+ # why_run mode doesn't disable the updated_by_last_action logic, so we really have to look at the provider action
+ # to see if why_run correctly disabled the resource. It should shell_out! for the guard but not the resource.
+ expect_any_instance_of(Chef::Provider::Execute).to receive(:shell_out!).once
+
+ resource.only_if guard
+ resource.run_action(:run)
+
+ expect(resource).to be_updated_by_last_action
+ expect(guard_resource).to be_updated_by_last_action
+ end
+ end
+
describe "when parent resource sets :cwd" do
let(:guard) { %{ruby -e 'exit 1 unless File.exists?("./big_json_plus_one.json")'} }
diff --git a/spec/functional/resource/powershell_spec.rb b/spec/functional/resource/powershell_spec.rb
index 033f34e256..1b3ac844e0 100644
--- a/spec/functional/resource/powershell_spec.rb
+++ b/spec/functional/resource/powershell_spec.rb
@@ -56,6 +56,21 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
resource.run_action(:run)
end
+ it "returns the -27 for a powershell script that exits with -27" do
+ file = Tempfile.new(['foo', '.ps1'])
+ begin
+ file.write "exit -27"
+ file.close
+ resource.code(". \"#{file.path}\"")
+ resource.returns(-27)
+ resource.run_action(:run)
+ ensure
+ file.close
+ file.unlink
+ end
+ end
+
+
it "returns the process exit code" do
resource.code(arbitrary_nonzero_process_exit_code_content)
resource.returns(arbitrary_nonzero_process_exit_code)
diff --git a/spec/functional/resource/user/useradd_spec.rb b/spec/functional/resource/user/useradd_spec.rb
index 6b962c19aa..9ac88d7b60 100644
--- a/spec/functional/resource/user/useradd_spec.rb
+++ b/spec/functional/resource/user/useradd_spec.rb
@@ -82,12 +82,25 @@ describe Chef::Provider::User::Useradd, metadata do
end
after do
- begin
- pw_entry # will raise if the user doesn't exist
- shell_out!("userdel", "-r", username, :returns => [0,12])
- rescue UserNotFound
- # nothing to remove
+ max_retries = 3
+ while max_retries > 0
+ begin
+ pw_entry # will raise if the user doesn't exist
+ status = shell_out!("userdel", "-r", username, :returns => [0,8,12])
+
+ # Error code 8 during userdel indicates that the user is logged in.
+ # This occurs randomly because the accounts daemon holds a lock due to which userdel fails.
+ # The work around is to retry userdel for 3 times.
+ break if status.exitstatus != 8
+
+ sleep 1
+ max_retries = max_retries -1
+ rescue UserNotFound
+ break
+ end
end
+
+ status.error! if max_retries == 0
end
let(:node) do
diff --git a/spec/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb
index 62660bb852..3475a569b6 100644
--- a/spec/integration/client/client_spec.rb
+++ b/spec/integration/client/client_spec.rb
@@ -1,5 +1,34 @@
require 'support/shared/integration/integration_helper'
require 'chef/mixin/shell_out'
+require 'tiny_server'
+require 'tmpdir'
+
+def recipes_filename
+ File.join(CHEF_SPEC_DATA, 'recipes.tgz')
+end
+
+def start_tiny_server(server_opts={})
+ recipes_size = File::Stat.new(recipes_filename).size
+ @server = TinyServer::Manager.new(server_opts)
+ @server.start
+ @api = TinyServer::API.instance
+ @api.clear
+ #
+ # trivial endpoints
+ #
+ # just a normal file
+ # (expected_content should be uncompressed)
+ @api.get("/recipes.tgz", 200) {
+ File.open(recipes_filename, "rb") do |f|
+ f.read
+ end
+ }
+end
+
+def stop_tiny_server
+ @server.stop
+ @server = @api = nil
+end
describe "chef-client" do
include IntegrationSupport
@@ -279,4 +308,28 @@ end
end
end
+ context "when using recipe-url" do
+ before(:all) do
+ start_tiny_server
+ end
+
+ after(:all) do
+ stop_tiny_server
+ end
+
+ let(:tmp_dir) { Dir.mktmpdir("recipe-url") }
+
+ it "should complete with success when passed -z and --recipe-url" do
+ file 'config/client.rb', <<EOM
+chef_repo_path "#{tmp_dir}"
+EOM
+ result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --recipe-url=http://localhost:9000/recipes.tgz -o 'x::default' -z", :cwd => tmp_dir)
+ result.error!
+ end
+
+ it 'should fail when passed --recipe-url and not passed -z' do
+ result = shell_out("#{chef_client} --recipe-url=http://localhost:9000/recipes.tgz", :cwd => tmp_dir)
+ expect(result.exitstatus).to eq(1)
+ end
+ end
end
diff --git a/spec/integration/knife/chef_fs_data_store_spec.rb b/spec/integration/knife/chef_fs_data_store_spec.rb
index eb02db5384..c1f2c7134f 100644
--- a/spec/integration/knife/chef_fs_data_store_spec.rb
+++ b/spec/integration/knife/chef_fs_data_store_spec.rb
@@ -138,6 +138,7 @@ EOM
context 'PUT /TYPE/NAME' do
before do
file 'empty.json', {}
+ file 'dummynode.json', { "name" => "x", "chef_environment" => "rspec" , "json_class" => "Chef::Node", "normal" => {"foo" => "bar"}}
file 'rolestuff.json', '{"description":"hi there","name":"x"}'
file 'cookbooks_to_upload/x/metadata.rb', cookbook_x_100_metadata_rb
end
@@ -165,9 +166,10 @@ EOM
knife('list --local /environments').should_succeed "/environments/x.json\n"
end
- it 'knife raw -z -i empty.json -m PUT /nodes/x' do
- knife("raw -z -i #{path_to('empty.json')} -m PUT /nodes/x").should_succeed( /"x"/ )
+ it 'knife raw -z -i dummynode.json -m PUT /nodes/x' do
+ knife("raw -z -i #{path_to('dummynode.json')} -m PUT /nodes/x").should_succeed( /"x"/ )
knife('list --local /nodes').should_succeed "/nodes/x.json\n"
+ knife('show -z /nodes/x.json --verbose').should_succeed /"bar"/
end
it 'knife raw -z -i empty.json -m PUT /roles/x' do
@@ -196,6 +198,7 @@ EOM
context 'POST /TYPE/NAME' do
before do
file 'empty.json', { 'name' => 'z' }
+ file 'dummynode.json', { "name" => "z", "chef_environment" => "rspec" , "json_class" => "Chef::Node", "normal" => {"foo" => "bar"}}
file 'empty_x.json', { 'name' => 'x' }
file 'empty_id.json', { 'id' => 'z' }
file 'rolestuff.json', '{"description":"hi there","name":"x"}'
@@ -231,9 +234,10 @@ EOM
knife('list --local /environments').should_succeed "/environments/z.json\n"
end
- it 'knife raw -z -i empty.json -m POST /nodes' do
- knife("raw -z -i #{path_to('empty.json')} -m POST /nodes").should_succeed( /uri/ )
+ it 'knife raw -z -i dummynode.json -m POST /nodes' do
+ knife("raw -z -i #{path_to('dummynode.json')} -m POST /nodes").should_succeed( /uri/ )
knife('list --local /nodes').should_succeed "/nodes/z.json\n"
+ knife('show -z /nodes/z.json').should_succeed /"bar"/
end
it 'knife raw -z -i empty.json -m POST /roles' do
diff --git a/spec/integration/knife/download_spec.rb b/spec/integration/knife/download_spec.rb
index cf1e4fcf0f..c87e6fe20a 100644
--- a/spec/integration/knife/download_spec.rb
+++ b/spec/integration/knife/download_spec.rb
@@ -1069,7 +1069,7 @@ EOM
end
when_the_repository 'is empty' do
- it 'knife download /cookbooks/x signs all requests', :ruby_gte_19_only do
+ it 'knife download /cookbooks/x signs all requests' do
# Check that BasicClient.request() always gets called with X-OPS-USERID
original_new = Chef::HTTP::BasicClient.method(:new)
diff --git a/spec/integration/solo/solo_spec.rb b/spec/integration/solo/solo_spec.rb
index cc9ba1abb2..41f5f5506f 100644
--- a/spec/integration/solo/solo_spec.rb
+++ b/spec/integration/solo/solo_spec.rb
@@ -83,8 +83,7 @@ end
EOM
end
- # Ruby 1.8.7 doesn't have Process.spawn :(
- it "while running solo concurrently", :ruby_gte_19_only => true do
+ it "while running solo concurrently" do
file 'config/solo.rb', <<EOM
cookbook_path "#{path_to('cookbooks')}"
file_cache_path "#{path_to('config/cache')}"
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 995be5060b..b87736efef 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -124,11 +124,7 @@ RSpec.configure do |config|
config.filter_run_excluding :aix_only => true unless aix?
config.filter_run_excluding :supports_cloexec => true unless supports_cloexec?
config.filter_run_excluding :selinux_only => true unless selinux_enabled?
- config.filter_run_excluding :ruby_18_only => true unless ruby_18?
- config.filter_run_excluding :ruby_19_only => true unless ruby_19?
- config.filter_run_excluding :ruby_gte_19_only => true unless ruby_gte_19?
config.filter_run_excluding :ruby_20_only => true unless ruby_20?
- config.filter_run_excluding :ruby_gte_20_only => true unless ruby_gte_20?
# chef_gte_XX_only and chef_lt_XX_only pair up correctly with the same XX
# number. please conserve this pattern & resist filling out all the operators
config.filter_run_excluding :chef_gte_13_only => true unless chef_gte_13?
@@ -137,9 +133,8 @@ RSpec.configure do |config|
config.filter_run_excluding :requires_root_or_running_windows => true unless (root? || windows?)
config.filter_run_excluding :requires_unprivileged_user => true if root?
config.filter_run_excluding :uses_diff => true unless has_diff?
- config.filter_run_excluding :ruby_gte_20_and_openssl_gte_101 => true unless (ruby_gte_20? && openssl_gte_101?)
+ config.filter_run_excluding :openssl_gte_101 => true unless openssl_gte_101?
config.filter_run_excluding :openssl_lt_101 => true unless openssl_lt_101?
- config.filter_run_excluding :ruby_lt_20 => true unless ruby_lt_20?
config.filter_run_excluding :aes_256_gcm_only => true unless aes_256_gcm?
config.filter_run_excluding :broken => true
diff --git a/spec/stress/win32/file_spec.rb b/spec/stress/win32/file_spec.rb
index dd1dcd305e..6c4b26b05c 100644
--- a/spec/stress/win32/file_spec.rb
+++ b/spec/stress/win32/file_spec.rb
@@ -24,12 +24,12 @@ describe 'Chef::ReservedNames::Win32::File', :windows_only do
@path = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "data", "old_home_dir", "my-dot-emacs"))
end
- it "should not leak significant memory" do
+ it "should not leak significant memory", :volatile do
test = lambda { Chef::ReservedNames::Win32::File.symlink?(@path) }
expect(test).not_to leak_memory(:warmup => 50000, :iterations => 50000)
end
- it "should not leak handles" do
+ it "should not leak handles", :volatile do
test = lambda { Chef::ReservedNames::Win32::File.symlink?(@path) }
expect(test).not_to leak_handles(:warmup => 50, :iterations => 100)
end
diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb
index 959580c953..a412fe38e1 100644
--- a/spec/support/platform_helpers.rb
+++ b/spec/support/platform_helpers.rb
@@ -6,10 +6,6 @@ class ShellHelpers
extend Chef::Mixin::ShellOut
end
-def ruby_gte_20?
- RUBY_VERSION.to_f >= 2.0
-end
-
def ruby_lt_20?
!ruby_gte_20?
end
@@ -30,14 +26,6 @@ def ruby_20?
!!(RUBY_VERSION =~ /^2.0/)
end
-def ruby_19?
- !!(RUBY_VERSION =~ /^1.9/)
-end
-
-def ruby_18?
- !!(RUBY_VERSION =~ /^1.8/)
-end
-
def windows?
!!(RUBY_PLATFORM =~ /mswin|mingw|windows/)
end
diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb
index 33af9bc5c1..cce3d11577 100644
--- a/spec/unit/application/client_spec.rb
+++ b/spec/unit/application/client_spec.rb
@@ -26,6 +26,7 @@ describe Chef::Application::Client, "reconfigure" do
before do
allow(Kernel).to receive(:trap).and_return(:ok)
+ allow(::File).to receive(:read).with("/etc/chef/client.rb").and_return("")
@original_argv = ARGV.dup
ARGV.clear
@@ -215,8 +216,21 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
end
end
end
+
+ describe "when both the pidfile and lockfile opts are set to the same value" do
+
+ before do
+ Chef::Config[:pid_file] = "/path/to/file"
+ Chef::Config[:lockfile] = "/path/to/file"
+ end
+
+ it "should throw an exception" do
+ expect { @app.reconfigure }.to raise_error
+ end
+ end
end
+
describe Chef::Application::Client, "setup_application" do
before do
@app = Chef::Application::Client.new
@@ -236,11 +250,13 @@ describe Chef::Application::Client, "setup_application" do
end
describe Chef::Application::Client, "configure_chef" do
+ let(:app) { Chef::Application::Client.new }
+
before do
@original_argv = ARGV.dup
ARGV.clear
- @app = Chef::Application::Client.new
- @app.configure_chef
+ allow(::File).to receive(:read).with("/etc/chef/client.rb").and_return("")
+ app.configure_chef
end
after do
diff --git a/spec/unit/chef_fs/config_spec.rb b/spec/unit/chef_fs/config_spec.rb
index 031da6c4b5..c7c47ad8ab 100644
--- a/spec/unit/chef_fs/config_spec.rb
+++ b/spec/unit/chef_fs/config_spec.rb
@@ -55,4 +55,56 @@ describe Chef::ChefFS::Config do
Chef::ChefFS::Config.new(base_config, Dir.pwd, {}, ui)
end
end
+
+ describe "local FS configuration" do
+
+ let(:chef_config) do
+ Mash.new({
+ client_path: "/base_path/clients",
+ cookbook_path: "/base_path/cookbooks",
+ data_bag_path: "/base_path/data_bags",
+ environment_path: "/base_path/environments",
+ node_path: "/base_path/nodes",
+ role_path: "/base_path/roles",
+ user_path: "/base_path/users",
+ policy_path: "/base_path/policies"
+ })
+ end
+
+ let(:chef_fs_config) { Chef::ChefFS::Config.new(chef_config, Dir.pwd) }
+
+ subject(:local_fs) { chef_fs_config.local_fs }
+
+ def platform_path(*args)
+ File.expand_path(*args)
+ end
+
+ it "sets the correct nodes path on the local FS object" do
+ expect(local_fs.child_paths["nodes"]).to eq([platform_path("/base_path/nodes")])
+ end
+
+ it "sets the correct cookbook path on the local FS object" do
+ expect(local_fs.child_paths["cookbooks"]).to eq([platform_path("/base_path/cookbooks")])
+ end
+
+ it "sets the correct data bag path on the local FS object" do
+ expect(local_fs.child_paths["data_bags"]).to eq([platform_path("/base_path/data_bags")])
+ end
+
+ it "sets the correct environment path on the local FS object" do
+ expect(local_fs.child_paths["environments"]).to eq([platform_path("/base_path/environments")])
+ end
+
+ it "sets the correct role path on the local FS object" do
+ expect(local_fs.child_paths["roles"]).to eq([platform_path("/base_path/roles")])
+ end
+
+ it "sets the correct user path on the local FS object" do
+ expect(local_fs.child_paths["users"]).to eq([platform_path("/base_path/users")])
+ end
+
+ it "sets the correct policy path on the local FS object" do
+ expect(local_fs.child_paths["policies"]).to eq([platform_path("/base_path/policies")])
+ end
+ end
end
diff --git a/spec/unit/chef_fs/file_pattern_spec.rb b/spec/unit/chef_fs/file_pattern_spec.rb
index cdf506225a..a9f06e8424 100644
--- a/spec/unit/chef_fs/file_pattern_spec.rb
+++ b/spec/unit/chef_fs/file_pattern_spec.rb
@@ -355,7 +355,7 @@ describe Chef::ChefFS::FilePattern do
it 'could_match_children? /abc** returns false for /xyz' do
pending 'Make could_match_children? more rigorous'
# At the moment, we return false for this, but in the end it would be nice to return true:
- pattern.could_match_children?('/xyz').should be_falsey
+ expect(pattern.could_match_children?('/xyz')).to be_falsey
end
it 'exact_child_name_under' do
expect(pattern.exact_child_name_under('/')).to eq(nil)
diff --git a/spec/unit/data_bag_item_spec.rb b/spec/unit/data_bag_item_spec.rb
index 4cf6e59242..4348252388 100644
--- a/spec/unit/data_bag_item_spec.rb
+++ b/spec/unit/data_bag_item_spec.rb
@@ -20,86 +20,86 @@ require 'spec_helper'
require 'chef/data_bag_item'
describe Chef::DataBagItem do
- before(:each) do
- @data_bag_item = Chef::DataBagItem.new
- end
+ let(:data_bag_item) { Chef::DataBagItem.new }
describe "initialize" do
it "should be a Chef::DataBagItem" do
- expect(@data_bag_item).to be_a_kind_of(Chef::DataBagItem)
+ expect(data_bag_item).to be_a_kind_of(Chef::DataBagItem)
end
end
describe "data_bag" do
it "should let you set the data_bag to a string" do
- expect(@data_bag_item.data_bag("clowns")).to eq("clowns")
+ expect(data_bag_item.data_bag("clowns")).to eq("clowns")
end
it "should return the current data_bag type" do
- @data_bag_item.data_bag "clowns"
- expect(@data_bag_item.data_bag).to eq("clowns")
+ data_bag_item.data_bag "clowns"
+ expect(data_bag_item.data_bag).to eq("clowns")
end
it "should not accept spaces" do
- expect { @data_bag_item.data_bag "clown masters" }.to raise_error(ArgumentError)
+ expect { data_bag_item.data_bag "clown masters" }.to raise_error(ArgumentError)
end
it "should throw an ArgumentError if you feed it anything but a string" do
- expect { @data_bag_item.data_bag Hash.new }.to raise_error(ArgumentError)
+ expect { data_bag_item.data_bag Hash.new }.to raise_error(ArgumentError)
end
end
describe "raw_data" do
it "should let you set the raw_data with a hash" do
- expect { @data_bag_item.raw_data = { "id" => "octahedron" } }.not_to raise_error
+ expect { data_bag_item.raw_data = { "id" => "octahedron" } }.not_to raise_error
end
it "should let you set the raw_data from a mash" do
- expect { @data_bag_item.raw_data = Mash.new({ "id" => "octahedron" }) }.not_to raise_error
+ expect { data_bag_item.raw_data = Mash.new({ "id" => "octahedron" }) }.not_to raise_error
end
it "should raise an exception if you set the raw data without a key" do
- expect { @data_bag_item.raw_data = { "monkey" => "pants" } }.to raise_error(ArgumentError)
+ expect { data_bag_item.raw_data = { "monkey" => "pants" } }.to raise_error(ArgumentError)
end
it "should raise an exception if you set the raw data to something other than a hash" do
- expect { @data_bag_item.raw_data = "katie rules" }.to raise_error(ArgumentError)
+ expect { data_bag_item.raw_data = "katie rules" }.to raise_error(ArgumentError)
end
it "should accept alphanum/-/_ for the id" do
- expect { @data_bag_item.raw_data = { "id" => "h1-_" } }.not_to raise_error
+ expect { data_bag_item.raw_data = { "id" => "h1-_" } }.not_to raise_error
end
it "should accept alphanum.alphanum for the id" do
- expect { @data_bag_item.raw_data = { "id" => "foo.bar" } }.not_to raise_error
+ expect { data_bag_item.raw_data = { "id" => "foo.bar" } }.not_to raise_error
end
it "should accept .alphanum for the id" do
- expect { @data_bag_item.raw_data = { "id" => ".bozo" } }.not_to raise_error
+ expect { data_bag_item.raw_data = { "id" => ".bozo" } }.not_to raise_error
end
it "should raise an exception if the id contains anything but alphanum/-/_" do
- expect { @data_bag_item.raw_data = { "id" => "!@#" } }.to raise_error(ArgumentError)
+ expect { data_bag_item.raw_data = { "id" => "!@#" } }.to raise_error(ArgumentError)
end
it "should return the raw data" do
- @data_bag_item.raw_data = { "id" => "highway_of_emptiness" }
- expect(@data_bag_item.raw_data).to eq({ "id" => "highway_of_emptiness" })
+ data_bag_item.raw_data = { "id" => "highway_of_emptiness" }
+ expect(data_bag_item.raw_data).to eq({ "id" => "highway_of_emptiness" })
end
it "should be a Mash by default" do
- expect(@data_bag_item.raw_data).to be_a_kind_of(Mash)
+ expect(data_bag_item.raw_data).to be_a_kind_of(Mash)
end
end
describe "object_name" do
- before(:each) do
- @data_bag_item.data_bag("dreams")
- @data_bag_item.raw_data = { "id" => "the_beatdown" }
- end
+ let(:data_bag_item) {
+ data_bag_item = Chef::DataBagItem.new
+ data_bag_item.data_bag("dreams")
+ data_bag_item.raw_data = { "id" => "the_beatdown" }
+ data_bag_item
+ }
it "should return an object name based on the bag name and the raw_data id" do
- expect(@data_bag_item.object_name).to eq("data_bag_item_dreams_the_beatdown")
+ expect(data_bag_item.object_name).to eq("data_bag_item_dreams_the_beatdown")
end
end
@@ -110,17 +110,19 @@ describe Chef::DataBagItem do
end
describe "when used like a Hash" do
- before(:each) do
- @data_bag_item.raw_data = { "id" => "journey", "trials" => "been through" }
- end
+ let(:data_bag_item) {
+ data_bag_item = Chef::DataBagItem.new
+ data_bag_item.raw_data = { "id" => "journey", "trials" => "been through" }
+ data_bag_item
+ }
it "responds to keys" do
- expect(@data_bag_item.keys).to include("id")
- expect(@data_bag_item.keys).to include("trials")
+ expect(data_bag_item.keys).to include("id")
+ expect(data_bag_item.keys).to include("trials")
end
it "supports element reference with []" do
- expect(@data_bag_item["id"]).to eq("journey")
+ expect(data_bag_item["id"]).to eq("journey")
end
it "implements all the methods of Hash" do
@@ -131,100 +133,113 @@ describe Chef::DataBagItem do
:invert, :update, :replace, :merge!, :merge, :has_key?, :has_value?,
:key?, :value?]
methods.each do |m|
- expect(@data_bag_item).to respond_to(m)
+ expect(data_bag_item).to respond_to(m)
end
end
-
end
describe "to_hash" do
- before(:each) do
- @data_bag_item.data_bag("still_lost")
- @data_bag_item.raw_data = { "id" => "whoa", "i_know" => "kung_fu" }
- @to_hash = @data_bag_item.to_hash
- end
+ let(:data_bag_item) {
+ data_bag_item = Chef::DataBagItem.new
+ data_bag_item.data_bag("still_lost")
+ data_bag_item.raw_data = { "id" => "whoa", "i_know" => "kung_fu" }
+ data_bag_item
+ }
+
+ let(:to_hash) { data_bag_item.to_hash }
it "should return a hash" do
- expect(@to_hash).to be_a_kind_of(Hash)
+ expect(to_hash).to be_a_kind_of(Hash)
end
it "should have the raw_data keys as top level keys" do
- expect(@to_hash["id"]).to eq("whoa")
- expect(@to_hash["i_know"]).to eq("kung_fu")
+ expect(to_hash["id"]).to eq("whoa")
+ expect(to_hash["i_know"]).to eq("kung_fu")
end
it "should have the chef_type of data_bag_item" do
- expect(@to_hash["chef_type"]).to eq("data_bag_item")
+ expect(to_hash["chef_type"]).to eq("data_bag_item")
end
it "should have the data_bag set" do
- expect(@to_hash["data_bag"]).to eq("still_lost")
+ expect(to_hash["data_bag"]).to eq("still_lost")
end
end
describe "when deserializing from JSON" do
- before(:each) do
- @data_bag_item.data_bag('mars_volta')
- @data_bag_item.raw_data = { "id" => "octahedron", "snooze" => { "finally" => :world_will }}
- @deserial = Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(@data_bag_item))
- end
+ let(:data_bag_item) {
+ data_bag_item = Chef::DataBagItem.new
+ data_bag_item.data_bag('mars_volta')
+ data_bag_item.raw_data = { "id" => "octahedron", "snooze" => { "finally" => :world_will } }
+ data_bag_item
+ }
+
+ let(:deserial) { Chef::JSONCompat.from_json(Chef::JSONCompat.to_json(data_bag_item)) }
+
it "should deserialize to a Chef::DataBagItem object" do
- expect(@deserial).to be_a_kind_of(Chef::DataBagItem)
+ expect(deserial).to be_a_kind_of(Chef::DataBagItem)
end
it "should have a matching 'data_bag' value" do
- expect(@deserial.data_bag).to eq(@data_bag_item.data_bag)
+ expect(deserial.data_bag).to eq(data_bag_item.data_bag)
end
it "should have a matching 'id' key" do
- expect(@deserial["id"]).to eq("octahedron")
+ expect(deserial["id"]).to eq("octahedron")
end
it "should have a matching 'snooze' key" do
- expect(@deserial["snooze"]).to eq({ "finally" => "world_will" })
+ expect(deserial["snooze"]).to eq({ "finally" => "world_will" })
end
include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
- let(:jsonable) { @data_bag_item }
+ let(:jsonable) { data_bag_item }
end
end
describe "when converting to a string" do
it "converts to a string in the form data_bag_item[ID]" do
- @data_bag_item['id'] = "heart of darkness"
- expect(@data_bag_item.to_s).to eq('data_bag_item[heart of darkness]')
+ data_bag_item['id'] = "heart of darkness"
+ expect(data_bag_item.to_s).to eq('data_bag_item[heart of darkness]')
end
it "inspects as data_bag_item[BAG, ID, RAW_DATA]" do
raw_data = {"id" => "heart_of_darkness", "author" => "Conrad"}
- @data_bag_item.raw_data = raw_data
- @data_bag_item.data_bag("books")
+ data_bag_item.raw_data = raw_data
+ data_bag_item.data_bag("books")
- expect(@data_bag_item.inspect).to eq("data_bag_item[\"books\", \"heart_of_darkness\", #{raw_data.inspect}]")
+ expect(data_bag_item.inspect).to eq("data_bag_item[\"books\", \"heart_of_darkness\", #{raw_data.inspect}]")
end
end
describe "save" do
+ let(:server) { instance_double(Chef::REST) }
+
+ let(:data_bag_item) {
+ data_bag_item = Chef::DataBagItem.new
+ data_bag_item['id'] = "heart of darkness"
+ data_bag_item.raw_data = {"id" => "heart_of_darkness", "author" => "Conrad"}
+ data_bag_item.data_bag("books")
+ data_bag_item
+ }
+
before do
- @rest = double("Chef::REST")
- allow(Chef::REST).to receive(:new).and_return(@rest)
- @data_bag_item['id'] = "heart of darkness"
- raw_data = {"id" => "heart_of_darkness", "author" => "Conrad"}
- @data_bag_item.raw_data = raw_data
- @data_bag_item.data_bag("books")
+ expect(Chef::REST).to receive(:new).and_return(server)
end
+
it "should update the item when it already exists" do
- expect(@rest).to receive(:put_rest).with("data/books/heart_of_darkness", @data_bag_item)
- @data_bag_item.save
+ expect(server).to receive(:put_rest).with("data/books/heart_of_darkness", data_bag_item)
+ data_bag_item.save
end
it "should create if the item is not found" do
exception = double("404 error", :code => "404")
- expect(@rest).to receive(:put_rest).and_raise(Net::HTTPServerException.new("foo", exception))
- expect(@rest).to receive(:post_rest).with("data/books", @data_bag_item)
- @data_bag_item.save
+ expect(server).to receive(:put_rest).and_raise(Net::HTTPServerException.new("foo", exception))
+ expect(server).to receive(:post_rest).with("data/books", data_bag_item)
+ data_bag_item.save
end
+
describe "when whyrun mode is enabled" do
before do
Chef::Config[:why_run] = true
@@ -232,41 +247,60 @@ describe Chef::DataBagItem do
after do
Chef::Config[:why_run] = false
end
+
it "should not save" do
- expect(@rest).not_to receive(:put_rest)
- expect(@rest).not_to receive(:post_rest)
- @data_bag_item.data_bag("books")
- @data_bag_item.save
+ expect(server).not_to receive(:put_rest)
+ expect(server).not_to receive(:post_rest)
+ data_bag_item.data_bag("books")
+ data_bag_item.save
end
end
+ end
+
+ describe "destroy" do
+ let(:server) { instance_double(Chef::REST) }
+
+ let(:data_bag_item) {
+ data_bag_item = Chef::DataBagItem.new
+ data_bag_item.data_bag('a_baggy_bag')
+ data_bag_item.raw_data = { "id" => "some_id" }
+ data_bag_item
+ }
+ it "should set default parameters" do
+ expect(Chef::REST).to receive(:new).and_return(server)
+ expect(server).to receive(:delete_rest).with("data/a_baggy_bag/data_bag_item_a_baggy_bag_some_id")
+
+ data_bag_item.destroy
+ end
end
describe "when loading" do
before do
- @data_bag_item.raw_data = {"id" => "charlie", "shell" => "zsh", "ssh_keys" => %w{key1 key2}}
- @data_bag_item.data_bag("users")
+ data_bag_item.raw_data = {"id" => "charlie", "shell" => "zsh", "ssh_keys" => %w{key1 key2}}
+ data_bag_item.data_bag("users")
end
describe "from an API call" do
+ let(:http_client) { double("Chef::REST") }
+
before do
- @http_client = double("Chef::REST")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
+ allow(Chef::REST).to receive(:new).and_return(http_client)
end
it "converts raw data to a data bag item" do
- expect(@http_client).to receive(:get_rest).with("data/users/charlie").and_return(@data_bag_item.to_hash)
+ expect(http_client).to receive(:get_rest).with("data/users/charlie").and_return(data_bag_item.to_hash)
item = Chef::DataBagItem.load(:users, "charlie")
expect(item).to be_a_kind_of(Chef::DataBagItem)
- expect(item).to eq(@data_bag_item)
+ expect(item).to eq(data_bag_item)
end
it "does not convert when a DataBagItem is returned from the API call" do
- expect(@http_client).to receive(:get_rest).with("data/users/charlie").and_return(@data_bag_item)
+ expect(http_client).to receive(:get_rest).with("data/users/charlie").and_return(data_bag_item)
item = Chef::DataBagItem.load(:users, "charlie")
expect(item).to be_a_kind_of(Chef::DataBagItem)
- expect(item).to equal(@data_bag_item)
+ expect(item).to equal(data_bag_item)
end
end
@@ -280,13 +314,11 @@ describe Chef::DataBagItem do
end
it "converts the raw data to a data bag item" do
- expect(Chef::DataBag).to receive(:load).with('users').and_return({'charlie' => @data_bag_item.to_hash})
+ expect(Chef::DataBag).to receive(:load).with('users').and_return({'charlie' => data_bag_item.to_hash})
item = Chef::DataBagItem.load('users', 'charlie')
expect(item).to be_a_kind_of(Chef::DataBagItem)
- expect(item).to eq(@data_bag_item)
+ expect(item).to eq(data_bag_item)
end
end
-
end
-
end
diff --git a/spec/unit/encrypted_data_bag_item_spec.rb b/spec/unit/encrypted_data_bag_item_spec.rb
index 14afea507c..0a4306727b 100644
--- a/spec/unit/encrypted_data_bag_item_spec.rb
+++ b/spec/unit/encrypted_data_bag_item_spec.rb
@@ -124,14 +124,6 @@ describe Chef::EncryptedDataBagItem::Encryptor do
context "on unsupported platforms" do
let(:aead_algorithm) { Chef::EncryptedDataBagItem::AEAD_ALGORITHM }
- it "throws an error warning about the Ruby version if it has no GCM support" do
- # Force OpenSSL with AEAD support
- allow(OpenSSL::Cipher).to receive(:ciphers).and_return([ aead_algorithm ])
- # Ruby without AEAD support
- expect(OpenSSL::Cipher).to receive(:method_defined?).with(:auth_data=).and_return(false)
- expect { encryptor }.to raise_error(Chef::EncryptedDataBagItem::EncryptedDataBagRequirementsFailure, /requires Ruby/)
- end
-
it "throws an error warning about the OpenSSL version if it has no GCM support" do
# Force Ruby with AEAD support
allow(OpenSSL::Cipher).to receive(:method_defined?).with(:auth_data=).and_return(true)
@@ -140,14 +132,6 @@ describe Chef::EncryptedDataBagItem::Encryptor do
expect { encryptor }.to raise_error(Chef::EncryptedDataBagItem::EncryptedDataBagRequirementsFailure, /requires an OpenSSL/)
end
- context "on platforms with old Ruby", :ruby_lt_20 do
-
- it "throws an error warning about the Ruby version" do
- expect { encryptor }.to raise_error(Chef::EncryptedDataBagItem::EncryptedDataBagRequirementsFailure, /requires Ruby/)
- end
-
- end # context on platforms with old Ruby
-
context "on platforms with old OpenSSL", :openssl_lt_101 do
it "throws an error warning about the OpenSSL version" do
@@ -214,14 +198,6 @@ describe Chef::EncryptedDataBagItem::Decryptor do
}
end
- context "on platforms with old Ruby", :ruby_lt_20 do
-
- it "throws an error warning about the Ruby version" do
- expect { decryptor }.to raise_error(Chef::EncryptedDataBagItem::EncryptedDataBagRequirementsFailure, /requires Ruby/)
- end
-
- end # context on platforms with old Ruby
-
context "on platforms with old OpenSSL", :openssl_lt_101 do
it "throws an error warning about the OpenSSL version" do
diff --git a/spec/unit/knife/cookbook_site_share_spec.rb b/spec/unit/knife/cookbook_site_share_spec.rb
index 0f97261ad4..515a1603ad 100644
--- a/spec/unit/knife/cookbook_site_share_spec.rb
+++ b/spec/unit/knife/cookbook_site_share_spec.rb
@@ -108,11 +108,20 @@ describe Chef::Knife::CookbookSiteShare do
expect { @knife.run }.to raise_error(SystemExit)
end
- it 'should make a tarball of the cookbook' do
- expect(@knife).to receive(:shell_out!) do |args|
- expect(args.to_s).to match(/tar -czf/)
+ if File.exists?('/usr/bin/gnutar') || File.exists?('/bin/gnutar')
+ it 'should use gnutar to make a tarball of the cookbook' do
+ expect(@knife).to receive(:shell_out!) do |args|
+ expect(args.to_s).to match(/gnutar -czf/)
+ end
+ @knife.run
+ end
+ else
+ it 'should make a tarball of the cookbook' do
+ expect(@knife).to receive(:shell_out!) do |args|
+ expect(args.to_s).to match(/tar -czf/)
+ end
+ @knife.run
end
- @knife.run
end
it 'should exit and log to error when the tarball creation fails' do
diff --git a/spec/unit/knife/cookbook_upload_spec.rb b/spec/unit/knife/cookbook_upload_spec.rb
index 5dbd456ad8..fb94886cad 100644
--- a/spec/unit/knife/cookbook_upload_spec.rb
+++ b/spec/unit/knife/cookbook_upload_spec.rb
@@ -246,28 +246,62 @@ E
describe 'with -a or --all' do
before(:each) do
knife.config[:all] = true
- @test_cookbook1 = Chef::CookbookVersion.new('test_cookbook1', '/tmp/blah')
- @test_cookbook2 = Chef::CookbookVersion.new('test_cookbook2', '/tmp/blah')
- allow(cookbook_loader).to receive(:each).and_yield("test_cookbook1", @test_cookbook1).and_yield("test_cookbook2", @test_cookbook2)
- allow(cookbook_loader).to receive(:cookbook_names).and_return(["test_cookbook1", "test_cookbook2"])
end
- it 'should upload all cookbooks' do
- expect(knife).to receive(:upload).once
- knife.run
- end
+ context 'when cookbooks exist in the cookbook path' do
+ before(:each) do
+ @test_cookbook1 = Chef::CookbookVersion.new('test_cookbook1', '/tmp/blah')
+ @test_cookbook2 = Chef::CookbookVersion.new('test_cookbook2', '/tmp/blah')
+ allow(cookbook_loader).to receive(:each).and_yield("test_cookbook1", @test_cookbook1).and_yield("test_cookbook2", @test_cookbook2)
+ allow(cookbook_loader).to receive(:cookbook_names).and_return(["test_cookbook1", "test_cookbook2"])
+ end
- it 'should report on success' do
- expect(knife).to receive(:upload).once
- expect(knife.ui).to receive(:info).with(/Uploaded all cookbooks/)
- knife.run
+ it 'should upload all cookbooks' do
+ expect(knife).to receive(:upload).once
+ knife.run
+ end
+
+ it 'should report on success' do
+ expect(knife).to receive(:upload).once
+ expect(knife.ui).to receive(:info).with(/Uploaded all cookbooks/)
+ knife.run
+ end
+
+ it 'should update the version constraints for an environment' do
+ allow(knife).to receive(:assert_environment_valid!).and_return(true)
+ knife.config[:environment] = "production"
+ expect(knife).to receive(:update_version_constraints).once
+ knife.run
+ end
end
- it 'should update the version constraints for an environment' do
- allow(knife).to receive(:assert_environment_valid!).and_return(true)
- knife.config[:environment] = "production"
- expect(knife).to receive(:update_version_constraints).once
- knife.run
+ context 'when no cookbooks exist in the cookbook path' do
+ before(:each) do
+ allow(cookbook_loader).to receive(:each)
+ end
+
+ it 'should not upload any cookbooks' do
+ expect(knife).to_not receive(:upload)
+ knife.run
+ end
+
+ context 'when cookbook path is an array' do
+ it 'should warn users that no cookbooks exist' do
+ knife.config[:cookbook_path] = ['/chef-repo/cookbooks', '/home/user/cookbooks']
+ expect(knife.ui).to receive(:warn).with(
+ /Could not find any cookbooks in your cookbook path: #{knife.config[:cookbook_path].join(', ')}\. Use --cookbook-path to specify the desired path\./)
+ knife.run
+ end
+ end
+
+ context 'when cookbook path is a string' do
+ it 'should warn users that no cookbooks exist' do
+ knife.config[:cookbook_path] = '/chef-repo/cookbooks'
+ expect(knife.ui).to receive(:warn).with(
+ /Could not find any cookbooks in your cookbook path: #{knife.config[:cookbook_path]}\. Use --cookbook-path to specify the desired path\./)
+ knife.run
+ end
+ end
end
end
diff --git a/spec/unit/knife/raw_spec.rb b/spec/unit/knife/raw_spec.rb
new file mode 100644
index 0000000000..ab929abd39
--- /dev/null
+++ b/spec/unit/knife/raw_spec.rb
@@ -0,0 +1,43 @@
+#
+# Author:: Steven Danna (<steve@getchef.com>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'spec_helper'
+
+describe Chef::Knife::Raw do
+ let(:rest) do
+ r = double('Chef::Knife::Raw::RawInputServerAPI')
+ allow(Chef::Knife::Raw::RawInputServerAPI).to receive(:new).and_return(r)
+ r
+ end
+
+ let(:knife) do
+ k = Chef::Knife::Raw.new
+ k.config[:method] = "GET"
+ k.name_args = [ "/nodes" ]
+ k
+ end
+
+ describe "run" do
+ it "should set the x-ops-request-source header when --proxy-auth is set" do
+ knife.config[:proxy_auth] = true
+ expect(rest).to receive(:request).with(:GET, "/nodes",
+ { 'Content-Type' => 'application/json',
+ 'x-ops-request-source' => 'web'}, false)
+ knife.run
+ end
+ end
+end
diff --git a/spec/unit/org_spec.rb b/spec/unit/org_spec.rb
new file mode 100644
index 0000000000..cd6cc94d91
--- /dev/null
+++ b/spec/unit/org_spec.rb
@@ -0,0 +1,196 @@
+#
+# Author:: Steven Danna (steve@opscode.com)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+require 'chef/org'
+require 'tempfile'
+
+describe Chef::Org do
+ let(:org) { Chef::Org.new("an_org") }
+
+ describe "initialize" do
+ it "is a Chef::Org" do
+ expect(org).to be_a_kind_of(Chef::Org)
+ end
+ end
+
+ describe "name" do
+ it "lets you set the name to a string" do
+ org.name "sg1"
+ expect(org.name).to eq("sg1")
+ end
+
+ # It is not feasible to check all invalid characters. Here are a few
+ # that we probably care about.
+ it "raises on invalid characters" do
+ # capital letters
+ expect { org.name "Bar" }.to raise_error(ArgumentError)
+ # slashes
+ expect { org.name "foo/bar" }.to raise_error(ArgumentError)
+ # ?
+ expect { org.name "foo?" }.to raise_error(ArgumentError)
+ # &
+ expect { org.name "foo&" }.to raise_error(ArgumentError)
+ # spaces
+ expect { org.name "foo " }.to raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if you feed it anything but a string" do
+ expect { org.name Hash.new }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe "full_name" do
+ it "lets you set the full name" do
+ org.full_name "foo"
+ expect(org.full_name).to eq("foo")
+ end
+
+ it "raises an ArgumentError if you feed it anything but a string" do
+ expect { org.name Hash.new }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe "private_key" do
+ it "returns the private key" do
+ org.private_key("super private")
+ expect(org.private_key).to eq("super private")
+ end
+
+ it "raises an ArgumentError if you feed it something lame" do
+ expect { org.private_key Hash.new }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe "when serializing to JSON" do
+ let(:json) do
+ org.name("black")
+ org.full_name("black crowes")
+ org.to_json
+ end
+
+ it "serializes as a JSON object" do
+ expect(json).to match(/^\{.+\}$/)
+ end
+
+ it "includes the name value" do
+ expect(json).to include(%q{"name":"black"})
+ end
+
+ it "includes the full name value" do
+ expect(json).to include(%q{"full_name":"black crowes"})
+ end
+
+ it "includes the private key when present" do
+ org.private_key("monkeypants")
+ expect(org.to_json).to include(%q{"private_key":"monkeypants"})
+ end
+
+ it "does not include the private key if not present" do
+ expect(json).to_not include("private_key")
+ end
+ end
+
+ describe "when deserializing from JSON" do
+ let(:org) do
+ o = { "name" => "turtle",
+ "full_name" => "turtle_club",
+ "private_key" => "pandas" }
+ Chef::Org.from_json(o.to_json)
+ end
+
+ it "deserializes to a Chef::Org object" do
+ expect(org).to be_a_kind_of(Chef::Org)
+ end
+
+ it "preserves the name" do
+ expect(org.name).to eq("turtle")
+ end
+
+ it "preserves the full_name" do
+ expect(org.full_name).to eq("turtle_club")
+ end
+
+ it "includes the private key if present" do
+ expect(org.private_key).to eq("pandas")
+ end
+ end
+
+ describe "API Interactions" do
+ let(:rest) do
+ Chef::Config[:chef_server_root] = "http://www.example.com"
+ r = double('rest')
+ allow(Chef::REST).to receive(:new).and_return(r)
+ r
+ end
+
+ let(:org) do
+ o = Chef::Org.new("foobar")
+ o.full_name "foo bar bat"
+ o
+ end
+
+ describe "list" do
+ let(:response) { {"foobar" => "http://www.example.com/organizations/foobar"} }
+ let(:inflated_response) { {"foobar" => org } }
+
+ it "lists all orgs" do
+ expect(rest).to receive(:get_rest).with("organizations").and_return(response)
+ expect(Chef::Org.list).to eq(response)
+ end
+
+ it "inflate all orgs" do
+ allow(Chef::Org).to receive(:load).with("foobar").and_return(org)
+ expect(rest).to receive(:get_rest).with("organizations").and_return(response)
+ expect(Chef::Org.list(true)).to eq(inflated_response)
+ end
+ end
+
+ describe "create" do
+ it "creates a new org via the API" do
+ expect(rest).to receive(:post_rest).with("organizations", {:name => "foobar", :full_name => "foo bar bat"}).and_return({})
+ org.create
+ end
+ end
+
+ describe "read" do
+ it "loads a named org from the API" do
+ expect(rest).to receive(:get_rest).with("organizations/foobar").and_return({"name" => "foobar", "full_name" => "foo bar bat", "private_key" => "private"})
+ org = Chef::Org.load("foobar")
+ expect(org.name).to eq("foobar")
+ expect(org.full_name).to eq("foo bar bat")
+ expect(org.private_key).to eq("private")
+ end
+ end
+
+ describe "update" do
+ it "updates an existing org on via the API" do
+ expect(rest).to receive(:put_rest).with("organizations/foobar", {:name => "foobar", :full_name => "foo bar bat"}).and_return({})
+ org.update
+ end
+ end
+
+ describe "destroy" do
+ it "deletes the specified org via the API" do
+ expect(rest).to receive(:delete_rest).with("organizations/foobar")
+ org.destroy
+ end
+ end
+ end
+end
diff --git a/spec/unit/policy_builder/policyfile_spec.rb b/spec/unit/policy_builder/policyfile_spec.rb
index 5e2844201d..92cdd7f57e 100644
--- a/spec/unit/policy_builder/policyfile_spec.rb
+++ b/spec/unit/policy_builder/policyfile_spec.rb
@@ -211,9 +211,54 @@ describe Chef::PolicyBuilder::Policyfile do
end
end
+ context "and policy_document_native_api is configured" do
+
+ before do
+ Chef::Config[:policy_document_native_api] = true
+ Chef::Config[:policy_group] = "policy-stage"
+ Chef::Config[:policy_name] = "example"
+ end
+
+ context "and policy_name or policy_group are not configured" do
+
+ it "raises a Configuration error for policy_group" do
+ Chef::Config[:policy_group] = nil
+ expect { policy_builder.policy }.to raise_error(err_namespace::ConfigurationError)
+ end
+
+ it "raises a Configuration error for policy_name" do
+ Chef::Config[:policy_name] = nil
+ expect { policy_builder.policy }.to raise_error(err_namespace::ConfigurationError)
+ end
+
+ end
+
+ context "and policy_name and policy_group are configured" do
+
+ let(:policy_relative_url) { "policies/policy-stage/example" }
+
+ before do
+ expect(http_api).to receive(:get).with(policy_relative_url).and_return(parsed_policyfile_json)
+ end
+
+ it "fetches the policy file from a data bag item" do
+ expect(policy_builder.policy).to eq(parsed_policyfile_json)
+ end
+
+ it "extracts the run_list from the policyfile" do
+ expect(policy_builder.run_list).to eq(policyfile_run_list)
+ end
+ end
+
+ end
+
+
context "and a deployment_group is configured" do
+
+ let(:policy_relative_url) { "data/policyfiles/example-policy-stage" }
+
before do
- expect(http_api).to receive(:get).with("data/policyfiles/example-policy-stage").and_return(parsed_policyfile_json)
+ expect(http_api).to receive(:get).with(policy_relative_url).and_return(parsed_policyfile_json)
end
it "fetches the policy file from a data bag item" do
diff --git a/spec/unit/provider/env_spec.rb b/spec/unit/provider/env_spec.rb
index 19233dfba9..230603dcb3 100644
--- a/spec/unit/provider/env_spec.rb
+++ b/spec/unit/provider/env_spec.rb
@@ -252,7 +252,7 @@ describe Chef::Provider::Env do
end
context "when new_resource's value contains the delimiter" do
- it "should return false if all the current values are contained" do
+ it "should return false if all the current values are contained in specified order" do
@new_resource.value("C:/biz;C:/baz")
@new_resource.delim(";")
@current_resource.value("C:/biz;C:/foo/bin;C:/baz")
@@ -265,6 +265,13 @@ describe Chef::Provider::Env do
@current_resource.value("C:/biz;C:/foo/bin;C:/baz")
expect(@provider.requires_modify_or_create?).to be_truthy
end
+
+ it "should return true if values are contained in different order" do
+ @new_resource.value("C:/biz;C:/baz")
+ @new_resource.delim(";")
+ @current_resource.value("C:/baz;C:/foo/bin;C:/biz")
+ expect(@provider.requires_modify_or_create?).to be_truthy
+ end
end
end
@@ -286,12 +293,18 @@ describe Chef::Provider::Env do
expect(passed_value).to eq(new_value)
end
- it "should only add values not already contained when a delimiter is provided" do
+ it "should only add values not already contained" do
@new_resource.value("C:/foo;C:/bar;C:/baz")
- @new_resource.delim(";")
- @current_resource.value("C:/foo/bar;C:/bar;C:/baz")
+ @current_resource.value("C:/bar;C:/baz;C:/foo/bar")
+ @provider.modify_env
+ expect(@new_resource.value).to eq("C:/foo;C:/bar;C:/baz;C:/foo/bar")
+ end
+
+ it "should reorder values to keep order which asked" do
+ @new_resource.value("C:/foo;C:/bar;C:/baz")
+ @current_resource.value("C:/foo/bar;C:/baz;C:/bar")
@provider.modify_env
- expect(@new_resource.value).to eq("C:/foo;C:/foo/bar;C:/bar;C:/baz")
+ expect(@new_resource.value).to eq("C:/foo;C:/bar;C:/baz;C:/foo/bar")
end
end
end
diff --git a/spec/unit/provider/package/rpm_spec.rb b/spec/unit/provider/package/rpm_spec.rb
index 2aceee59a5..07c5dea8c6 100644
--- a/spec/unit/provider/package/rpm_spec.rb
+++ b/spec/unit/provider/package/rpm_spec.rb
@@ -19,138 +19,148 @@
require 'spec_helper'
describe Chef::Provider::Package::Rpm do
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
-
- @new_resource = Chef::Resource::Package.new("ImageMagick-c++")
- @new_resource.source "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm"
-
- @provider = Chef::Provider::Package::Rpm.new(@new_resource, @run_context)
+ let(:provider) { Chef::Provider::Package::Rpm.new(new_resource, run_context) }
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_resource) do
+ Chef::Resource::Package.new("ImageMagick-c++").tap do |resource|
+ resource.source "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm"
+ end
+ end
+ let(:exitstatus) { 0 }
+ let(:stdout) { String.new('') }
+ let(:status) { double('Process::Status', exitstatus: exitstatus, stdout: stdout) }
- @status = double("Status", :exitstatus => 0)
+ before(:each) do
allow(::File).to receive(:exists?).and_return(true)
+ allow(provider).to receive(:shell_out!).and_return(status)
end
describe "when determining the current state of the package" do
-
it "should create a current resource with the name of new_resource" do
- allow(@provider).to receive(:popen4).and_return(@status)
- @provider.load_current_resource
- expect(@provider.current_resource.name).to eq("ImageMagick-c++")
+ provider.load_current_resource
+ expect(provider.current_resource.name).to eq("ImageMagick-c++")
end
it "should set the current reource package name to the new resource package name" do
- allow(@provider).to receive(:popen4).and_return(@status)
- @provider.load_current_resource
- expect(@provider.current_resource.package_name).to eq('ImageMagick-c++')
+ provider.load_current_resource
+ expect(provider.current_resource.package_name).to eq('ImageMagick-c++')
end
it "should raise an exception if a source is supplied but not found" do
allow(::File).to receive(:exists?).and_return(false)
- expect { @provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
+ expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
end
- it "should get the source package version from rpm if provided" do
- @stdout = StringIO.new("ImageMagick-c++ 6.5.4.7-7.el6_5")
- expect(@provider).to receive(:popen4).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm").and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
- expect(@provider).to receive(:popen4).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' ImageMagick-c++").and_return(@status)
- @provider.load_current_resource
- expect(@provider.current_resource.package_name).to eq("ImageMagick-c++")
- expect(@provider.new_resource.version).to eq("6.5.4.7-7.el6_5")
- end
+ context "installation exists" do
+ let(:stdout) { "ImageMagick-c++ 6.5.4.7-7.el6_5" }
- it "should return the current version installed if found by rpm" do
- @stdout = StringIO.new("ImageMagick-c++ 6.5.4.7-7.el6_5")
- expect(@provider).to receive(:popen4).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm").and_return(@status)
- expect(@provider).to receive(:popen4).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' ImageMagick-c++").and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
- @provider.load_current_resource
- expect(@provider.current_resource.version).to eq("6.5.4.7-7.el6_5")
- end
+ it "should get the source package version from rpm if provided" do
+ expect(provider).to receive(:shell_out!).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm").and_return(status)
+ expect(provider).to receive(:shell_out!).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' ImageMagick-c++").and_return(status)
+ provider.load_current_resource
+ expect(provider.current_resource.package_name).to eq("ImageMagick-c++")
+ expect(provider.new_resource.version).to eq("6.5.4.7-7.el6_5")
+ end
- it "should raise an exception if the source is not set but we are installing" do
- new_resource = Chef::Resource::Package.new("ImageMagick-c++")
- provider = Chef::Provider::Package::Rpm.new(new_resource, @run_context)
- expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
+ it "should return the current version installed if found by rpm" do
+ expect(provider).to receive(:shell_out!).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm").and_return(status)
+ expect(provider).to receive(:shell_out!).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' ImageMagick-c++").and_return(status)
+ provider.load_current_resource
+ expect(provider.current_resource.version).to eq("6.5.4.7-7.el6_5")
+ end
end
- it "should raise an exception if rpm fails to run" do
- status = double("Status", :exitstatus => -1)
- allow(@provider).to receive(:popen4).and_return(status)
- expect { @provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
+ context "source is not defiend" do
+ let(:new_resource) { Chef::Resource::Package.new("ImageMagick-c++") }
+
+ it "should raise an exception if the source is not set but we are installing" do
+ expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
+ end
end
- it "should not detect the package name as version when not installed" do
- @status = double("Status", :exitstatus => -1)
- @stdout = StringIO.new("package openssh-askpass is not installed")
- @new_resource = Chef::Resource::Package.new("openssh-askpass")
- @new_resource.source 'openssh-askpass'
- @provider = Chef::Provider::Package::Rpm.new(@new_resource, @run_context)
- expect(@provider).to receive(:popen4).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' openssh-askpass").and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
- expect(@provider).to receive(:popen4).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' openssh-askpass").and_return(@status)
- @provider.load_current_resource
- expect(@provider.current_resource.version).to be_nil
+ context "installation does not exist" do
+ let(:stdout) { String.new("package openssh-askpass is not installed") }
+ let(:exitstatus) { -1 }
+ let(:new_resource) do
+ Chef::Resource::Package.new("openssh-askpass").tap do |resource|
+ resource.source "openssh-askpass"
+ end
+ end
+
+ it "should raise an exception if rpm fails to run" do
+ allow(provider).to receive(:shell_out!).and_return(status)
+ expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ it "should not detect the package name as version when not installed" do
+ expect(provider).to receive(:shell_out!).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' openssh-askpass").and_return(status)
+ expect(provider).to receive(:shell_out!).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' openssh-askpass").and_return(status)
+ provider.load_current_resource
+ expect(provider.current_resource.version).to be_nil
+ end
end
end
describe "after the current resource is loaded" do
- before do
- @current_resource = Chef::Resource::Package.new("ImageMagick-c++")
- @provider.current_resource = @current_resource
+ let(:current_resource) { Chef::Resource::Package.new("ImageMagick-c++") }
+ let(:provider) do
+ Chef::Provider::Package::Rpm.new(new_resource, run_context).tap do |provider|
+ provider.current_resource = current_resource
+ end
end
describe "when installing or upgrading" do
it "should run rpm -i with the package source to install" do
- expect(@provider).to receive(:shell_out!).with("rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- @provider.install_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
+ expect(provider).to receive(:shell_out!).with("rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ provider.install_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
end
it "should run rpm -U with the package source to upgrade" do
- @current_resource.version("21.4-19.el5")
- expect(@provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- @provider.upgrade_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
+ current_resource.version("21.4-19.el5")
+ expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ provider.upgrade_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
end
it "should install package if missing and set to upgrade" do
- @current_resource.version("ImageMagick-c++")
- expect(@provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- @provider.upgrade_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
- end
-
- it "should install from a path when the package is a path and the source is nil" do
- @new_resource = Chef::Resource::Package.new("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- @provider = Chef::Provider::Package::Rpm.new(@new_resource, @run_context)
- expect(@new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- @current_resource = Chef::Resource::Package.new("ImageMagick-c++")
- @provider.current_resource = @current_resource
- expect(@provider).to receive(:shell_out!).with("rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- @provider.install_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5")
+ current_resource.version("ImageMagick-c++")
+ expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ provider.upgrade_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
end
- it "should uprgrade from a path when the package is a path and the source is nil" do
- @new_resource = Chef::Resource::Package.new("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- @provider = Chef::Provider::Package::Rpm.new(@new_resource, @run_context)
- expect(@new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- @current_resource = Chef::Resource::Package.new("ImageMagick-c++")
- @current_resource.version("21.4-19.el5")
- @provider.current_resource = @current_resource
- expect(@provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- @provider.upgrade_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5")
+ context "installing when the name is a path" do
+ let(:new_resource) { Chef::Resource::Package.new("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") }
+ let(:current_resource) { Chef::Resource::Package.new("ImageMagick-c++") }
+
+ it "should install from a path when the package is a path and the source is nil" do
+ expect(new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ provider.current_resource = current_resource
+ expect(provider).to receive(:shell_out!).with("rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ provider.install_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5")
+ end
+
+ it "should uprgrade from a path when the package is a path and the source is nil" do
+ expect(new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ current_resource.version("21.4-19.el5")
+ provider.current_resource = current_resource
+ expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ provider.upgrade_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5")
+ end
end
it "installs with custom options specified in the resource" do
- @provider.candidate_version = '11'
- @new_resource.options("--dbpath /var/lib/rpm")
- expect(@provider).to receive(:shell_out!).with("rpm --dbpath /var/lib/rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- @provider.install_package(@new_resource.name, @provider.candidate_version)
+ provider.candidate_version = '11'
+ new_resource.options("--dbpath /var/lib/rpm")
+ expect(provider).to receive(:shell_out!).with("rpm --dbpath /var/lib/rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ provider.install_package(new_resource.name, provider.candidate_version)
end
end
describe "when removing the package" do
it "should run rpm -e to remove the package" do
- expect(@provider).to receive(:shell_out!).with("rpm -e ImageMagick-c++-6.5.4.7-7.el6_5")
- @provider.remove_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
+ expect(provider).to receive(:shell_out!).with("rpm -e ImageMagick-c++-6.5.4.7-7.el6_5")
+ provider.remove_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
end
end
end
diff --git a/spec/unit/provider/service/openbsd_service_spec.rb b/spec/unit/provider/service/openbsd_service_spec.rb
new file mode 100644
index 0000000000..1b5206470e
--- /dev/null
+++ b/spec/unit/provider/service/openbsd_service_spec.rb
@@ -0,0 +1,543 @@
+#
+# Author:: Bryan McLellan (btm@loftninjas.org)
+# Author:: Scott Bonds (scott@ggr.com)
+# Copyright:: Copyright (c) 2009 Bryan McLellan
+# Copyright:: Copyright (c) 2014 Scott Bonds
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+class Chef::Provider::Service::Openbsd
+ public :builtin_service_enable_variable_name
+ public :determine_enabled_status!
+ public :determine_current_status!
+ public :is_enabled?
+ attr_accessor :rc_conf, :rc_conf_local
+end
+
+describe Chef::Provider::Service::Openbsd do
+ let(:node) do
+ node = Chef::Node.new
+ node.automatic_attrs[:command] = {:ps => "ps -ax"}
+ node
+ end
+
+ let(:new_resource) do
+ new_resource = Chef::Resource::Service.new("sndiod")
+ new_resource.pattern("sndiod")
+ new_resource.supports({:status => false})
+ new_resource
+ end
+
+ let(:current_resource) do
+ current_resource = Chef::Resource::Service.new("sndiod")
+ current_resource
+ end
+
+ let(:provider) do
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ allow(::File).to receive(:read).with('/etc/rc.conf').and_return('')
+ allow(::File).to receive(:read).with('/etc/rc.conf.local').and_return('')
+ provider = Chef::Provider::Service::Openbsd.new(new_resource,run_context)
+ provider.action = :start
+ provider
+ end
+
+ before do
+ allow(Chef::Resource::Service).to receive(:new).and_return(current_resource)
+ end
+
+ def stub_etc_rcd_script
+ allow(::File).to receive(:exist?).and_return(false)
+ expect(::File).to receive(:exist?).with("/etc/rc.d/#{new_resource.service_name}").and_return(true)
+ end
+
+ def run_load_current_resource
+ stub_etc_rcd_script
+ provider.load_current_resource
+ end
+
+ describe Chef::Provider::Service::Openbsd, "initialize" do
+ it "should find /etc/rc.d init scripts" do
+ stub_etc_rcd_script
+ expect(provider.init_command).to eql "/etc/rc.d/sndiod"
+ end
+
+ it "should set init_command to nil if it can't find anything" do
+ expect(::File).to receive(:exist?).with('/etc/rc.d/sndiod').and_return(false)
+ expect(provider.init_command).to be nil
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "determine_current_status!" do
+ before do
+ stub_etc_rcd_script
+ provider.current_resource = current_resource
+ current_resource.service_name(new_resource.service_name)
+ end
+
+ context "when a status command has been specified" do
+ let(:status) { double(:stdout => "", :exitstatus => 0) }
+
+ before do
+ new_resource.status_command("/bin/chefhasmonkeypants status")
+ end
+
+ it "should run the services status command if one has been specified" do
+ expect(provider).to receive(:shell_out).with("/bin/chefhasmonkeypants status").and_return(status)
+ provider.determine_current_status!
+ end
+ end
+
+ context "when the service supports status" do
+ let(:status) { double(:stdout => "", :exitstatus => 0) }
+
+ before do
+ new_resource.supports({:status => true})
+ end
+
+ it "should run '/etc/rc.d/service_name status'" do
+ expect(provider).to receive(:shell_out).with("/etc/rc.d/#{new_resource.service_name} check").and_return(status)
+ provider.determine_current_status!
+ end
+
+ it "should set running to true if the status command returns 0" do
+ expect(provider).to receive(:shell_out).with("/etc/rc.d/#{new_resource.service_name} check").and_return(status)
+ provider.determine_current_status!
+ expect(current_resource.running).to be true
+ end
+
+ it "should set running to false if the status command returns anything except 0" do
+ expect(provider).to receive(:shell_out).with("/etc/rc.d/#{new_resource.service_name} check").and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ provider.determine_current_status!
+ expect(current_resource.running).to be false
+ end
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "determine_enabled_status!" do
+ before do
+ stub_etc_rcd_script
+ provider.current_resource = current_resource
+ current_resource.service_name(new_resource.service_name)
+
+ allow(provider).to receive(:service_enable_variable_name).and_return("#{new_resource.service_name}_enable")
+ end
+
+ context "when the service is builtin" do
+ before do
+ expect(::File).to receive(:open).with("/etc/rc.d/#{new_resource.service_name}")
+ provider.rc_conf = "#{provider.builtin_service_enable_variable_name}=NO"
+ provider.rc_conf_local = lines.join("\n")
+ end
+
+ %w{YES Yes yes yEs YeS}.each do |setting|
+ context "when the enable variable is set to #{setting}" do
+ let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}="#{setting}"} ] }
+ it "sets enabled to true" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be true
+ end
+ end
+ end
+
+ %w{No NO no nO None NONE none nOnE}.each do |setting|
+ context "when the enable variable is set to #{setting}" do
+ let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}="#{setting}"} ] }
+ it "sets enabled to false" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+ end
+
+ context "when the enable variable is garbage" do
+ let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}_enable="alskdjflasdkjflakdfj"} ] }
+ it "sets enabled to false" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+
+ context "when the enable variable partial matches (left) some other service and we are disabled" do
+ let(:lines) { [
+ %Q{thing_#{provider.builtin_service_enable_variable_name}="YES"},
+ %Q{#{provider.builtin_service_enable_variable_name}="NO"},
+ ] }
+ it "sets enabled based on the exact match (false)" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+
+ context "when the enable variable partial matches (right) some other service and we are disabled" do
+ let(:lines) { [
+ %Q{#{provider.builtin_service_enable_variable_name}_thing="YES"},
+ %Q{#{provider.builtin_service_enable_variable_name}},
+ ] }
+ it "sets enabled based on the exact match (false)" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+
+ context "when the enable variable partial matches (left) some other disabled service and we are enabled" do
+ let(:lines) { [
+ %Q{thing_#{provider.builtin_service_enable_variable_name}="NO"},
+ %Q{#{provider.builtin_service_enable_variable_name}="YES"},
+ ] }
+ it "sets enabled based on the exact match (true)" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be true
+ end
+ end
+
+ context "when the enable variable partial matches (right) some other disabled service and we are enabled" do
+ let(:lines) { [
+ %Q{#{provider.builtin_service_enable_variable_name}_thing="NO"},
+ %Q{#{provider.builtin_service_enable_variable_name}="YES"},
+ ] }
+ it "sets enabled based on the exact match (true)" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be true
+ end
+ end
+
+ context "when the enable variable only partial matches (left) some other enabled service" do
+ let(:lines) { [ %Q{thing_#{provider.builtin_service_enable_variable_name}_enable="YES"} ] }
+ it "sets enabled to false" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+
+ context "when the enable variable only partial matches (right) some other enabled service" do
+ let(:lines) { [ %Q{#{provider.builtin_service_enable_variable_name}_thing_enable="YES"} ] }
+ it "sets enabled to false" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+
+ context "when nothing matches" do
+ let(:lines) { [] }
+ it "sets enabled to true" do
+ provider.determine_enabled_status!
+ expect(current_resource.enabled).to be false
+ end
+ end
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "load_current_resource" do
+ before(:each) do
+ stub_etc_rcd_script
+ expect(provider).to receive(:determine_current_status!)
+ current_resource.running(false)
+ allow(provider).to receive(:service_enable_variable_name).and_return "#{new_resource.service_name}_enable"
+ expect(::File).to receive(:open).with("/etc/rc.d/#{new_resource.service_name}")
+ end
+
+ it "should create a current resource with the name of the new resource" do
+ expect(Chef::Resource::Service).to receive(:new).and_return(current_resource)
+ provider.load_current_resource
+ end
+
+ it "should set the current resources service name to the new resources service name" do
+ provider.load_current_resource
+ expect(current_resource.service_name).to eq(new_resource.service_name)
+ end
+
+ it "should return the current resource" do
+ expect(provider.load_current_resource).to eql(current_resource)
+ end
+
+ end
+
+ context "when testing actions" do
+ before(:each) do
+ stub_etc_rcd_script
+ expect(provider).to receive(:determine_current_status!)
+ current_resource.running(false)
+ expect(provider).to receive(:determine_enabled_status!)
+ current_resource.enabled(false)
+ provider.load_current_resource
+ end
+
+ describe Chef::Provider::Service::Openbsd, "start_service" do
+ it "should call the start command if one is specified" do
+ new_resource.start_command("/etc/rc.d/chef startyousillysally")
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/chef startyousillysally")
+ provider.start_service()
+ end
+
+ it "should call '/usr/local/etc/rc.d/service_name start' if no start command is specified" do
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} start")
+ provider.start_service()
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "stop_service" do
+ it "should call the stop command if one is specified" do
+ new_resource.stop_command("/etc/init.d/chef itoldyoutostop")
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef itoldyoutostop")
+ provider.stop_service()
+ end
+
+ it "should call '/usr/local/etc/rc.d/service_name stop' if no stop command is specified" do
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} stop")
+ provider.stop_service()
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "restart_service" do
+ it "should call 'restart' on the service_name if the resource supports it" do
+ new_resource.supports({:restart => true})
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} restart")
+ provider.restart_service()
+ end
+
+ it "should call the restart_command if one has been specified" do
+ new_resource.restart_command("/etc/init.d/chef restartinafire")
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/init.d/chef restartinafire")
+ provider.restart_service()
+ end
+
+ it "otherwise it should call stop and start" do
+ expect(provider).to receive(:stop_service)
+ expect(provider).to receive(:start_service)
+ provider.restart_service()
+ end
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "define_resource_requirements" do
+ before do
+ provider.current_resource = current_resource
+ end
+
+ context "when the init script is not found" do
+ before do
+ provider.init_command = nil
+ allow(provider).to receive(:builtin_service_enable_variable_name).and_return("#{new_resource.service_name}_enable")
+ end
+
+ [ "start", "reload", "restart", "enable" ].each do |action|
+ it "should raise an exception when the action is #{action}" do
+ provider.define_resource_requirements
+ provider.action = action
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
+ end
+ end
+
+ [ "stop", "disable" ].each do |action|
+ it "should not raise an error when the action is #{action}" do
+ provider.define_resource_requirements
+ provider.action = action
+ expect { provider.process_resource_requirements }.not_to raise_error
+ end
+ end
+ end
+
+ context "when the init script is found, but the service_enable_variable_name is nil" do
+ before do
+ allow(provider).to receive(:builtin_service_enable_variable_name).and_return(nil)
+ end
+
+ [ "start", "reload", "restart", "enable" ].each do |action|
+ it "should raise an exception when the action is #{action}" do
+ provider.action = action
+ provider.define_resource_requirements
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
+ end
+ end
+
+ [ "stop", "disable" ].each do |action|
+ it "should not raise an error when the action is #{action}" do
+ provider.action = action
+ provider.define_resource_requirements
+ expect { provider.process_resource_requirements }.not_to raise_error
+ end
+ end
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "enable_service" do
+ before do
+ provider.current_resource = current_resource
+ allow(FileUtils).to receive(:touch).with('/etc/rc.conf.local')
+ end
+ context "is builtin and disabled by default" do
+ before do
+ provider.rc_conf = "#{provider.builtin_service_enable_variable_name}=NO"
+ end
+ context "is enabled" do
+ before do
+ provider.rc_conf_local = "#{provider.builtin_service_enable_variable_name}=\"\""
+ end
+ it "should not change rc.conf.local since it is already enabled" do
+ expect(::File).not_to receive(:write)
+ provider.enable_service
+ end
+ end
+ context "is disabled" do
+ before do
+ provider.rc_conf_local = ''
+ end
+ it "should enable the service by adding a line to rc.conf.local" do
+ expect(::File).to receive(:write).with('/etc/rc.conf.local', include("#{provider.builtin_service_enable_variable_name}=\"\""))
+ expect(provider.is_enabled?).to be false
+ provider.enable_service
+ expect(provider.is_enabled?).to be true
+ end
+ end
+ end
+ context "is builtin and enabled by default" do
+ before do
+ provider.rc_conf = "#{provider.builtin_service_enable_variable_name}=\"\""
+ end
+ context "is enabled" do
+ before do
+ provider.rc_conf_local = ''
+ end
+ it "should not change rc.conf.local since it is already enabled" do
+ expect(::File).not_to receive(:write)
+ provider.enable_service
+ end
+ end
+ context "is disabled" do
+ before do
+ provider.rc_conf_local = "#{provider.builtin_service_enable_variable_name}=NO"
+ end
+ it "should enable the service by removing a line from rc.conf.local" do
+ expect(::File).to receive(:write).with('/etc/rc.conf.local', /^(?!#{provider.builtin_service_enable_variable_name})$/)
+ expect(provider.is_enabled?).to be false
+ provider.enable_service
+ expect(provider.is_enabled?).to be true
+ end
+ end
+ end
+ context "is not builtin" do
+ before do
+ provider.rc_conf = ''
+ end
+ context "is enabled" do
+ before do
+ provider.rc_conf_local = "pkg_scripts=\"#{new_resource.service_name}\"\n"
+ end
+ it "should not change rc.conf.local since it is already enabled" do
+ expect(::File).not_to receive(:write)
+ provider.enable_service
+ end
+ end
+ context "is disabled" do
+ before do
+ provider.rc_conf_local = ''
+ end
+ it "should enable the service by adding it to the pkg_scripts list" do
+ expect(::File).to receive(:write).with('/etc/rc.conf.local', "\npkg_scripts=\"#{new_resource.service_name}\"\n")
+ expect(provider.is_enabled?).to be false
+ provider.enable_service
+ expect(provider.is_enabled?).to be true
+ end
+ end
+ end
+ end
+
+ describe Chef::Provider::Service::Openbsd, "disable_service" do
+ before do
+ provider.current_resource = current_resource
+ allow(FileUtils).to receive(:touch).with('/etc/rc.conf.local')
+ end
+ context "is builtin and disabled by default" do
+ before do
+ provider.rc_conf = "#{provider.builtin_service_enable_variable_name}=NO"
+ end
+ context "is enabled" do
+ before do
+ provider.rc_conf_local = "#{provider.builtin_service_enable_variable_name}=\"\""
+ end
+ it "should disable the service by removing its line from rc.conf.local" do
+ expect(::File).to receive(:write).with('/etc/rc.conf.local', /^(?!#{provider.builtin_service_enable_variable_name})$/)
+ expect(provider.is_enabled?).to be true
+ provider.disable_service
+ expect(provider.is_enabled?).to be false
+ end
+ end
+ context "is disabled" do
+ before do
+ provider.rc_conf_local = ''
+ end
+ it "should not change rc.conf.local since it is already disabled" do
+ expect(::File).not_to receive(:write)
+ provider.disable_service
+ end
+ end
+ end
+ context "is builtin and enabled by default" do
+ before do
+ provider.rc_conf = "#{provider.builtin_service_enable_variable_name}=\"\""
+ end
+ context "is enabled" do
+ before do
+ provider.rc_conf_local = ''
+ end
+ it "should disable the service by adding a line to rc.conf.local" do
+ expect(::File).to receive(:write).with('/etc/rc.conf.local', include("#{provider.builtin_service_enable_variable_name}=\"NO\""))
+ expect(provider.is_enabled?).to be true
+ provider.disable_service
+ expect(provider.is_enabled?).to be false
+ end
+ end
+ context "is disabled" do
+ before do
+ provider.rc_conf_local = "#{provider.builtin_service_enable_variable_name}=NO"
+ end
+ it "should not change rc.conf.local since it is already disabled" do
+ expect(::File).not_to receive(:write)
+ provider.disable_service
+ end
+ end
+ end
+ context "is not builtin" do
+ before do
+ provider.rc_conf = ''
+ end
+ context "is enabled" do
+ before do
+ provider.rc_conf_local = "pkg_scripts=\"#{new_resource.service_name}\"\n"
+ end
+ it "should disable the service by removing it from the pkg_scripts list" do
+ expect(::File).to receive(:write).with('/etc/rc.conf.local', /^(?!#{new_resource.service_name})$/)
+ expect(provider.is_enabled?).to be true
+ provider.disable_service
+ expect(provider.is_enabled?).to be false
+ end
+ end
+ context "is disabled" do
+ before do
+ provider.rc_conf_local = ''
+ end
+ it "should not change rc.conf.local since it is already disabled" do
+ expect(::File).not_to receive(:write)
+ provider.disable_service
+ end
+ end
+ end
+ end
+
+end
diff --git a/spec/unit/provider/user_spec.rb b/spec/unit/provider/user_spec.rb
index 44434794e7..381168647b 100644
--- a/spec/unit/provider/user_spec.rb
+++ b/spec/unit/provider/user_spec.rb
@@ -91,7 +91,7 @@ describe Chef::Provider::User do
expect(@current_resource.username).to eq(@new_resource.username)
end
- it "should change the encoding of gecos to the encoding of the new resource", :ruby_gte_19_only do
+ it "should change the encoding of gecos to the encoding of the new resource" do
@pw_user.gecos.force_encoding('ASCII-8BIT')
@provider.load_current_resource
expect(@provider.current_resource.comment.encoding).to eq(@new_resource.comment.encoding)
diff --git a/spec/unit/resource/conditional_spec.rb b/spec/unit/resource/conditional_spec.rb
index 779c69425a..49240edfdf 100644
--- a/spec/unit/resource/conditional_spec.rb
+++ b/spec/unit/resource/conditional_spec.rb
@@ -47,7 +47,7 @@ describe Chef::Resource::Conditional do
end
describe "when created as an `only_if`" do
- describe "after running a successful command" do
+ describe "after running a successful command given as a string" do
before do
@conditional = Chef::Resource::Conditional.only_if(@parent_resource, "true")
end
@@ -57,7 +57,7 @@ describe Chef::Resource::Conditional do
end
end
- describe "after running a negative/false command" do
+ describe "after running a negative/false command given as a string" do
before do
@status.send("success?=", false)
@conditional = Chef::Resource::Conditional.only_if(@parent_resource, "false")
@@ -68,6 +68,27 @@ describe Chef::Resource::Conditional do
end
end
+ describe "after running a successful command given as an array" do
+ before do
+ @conditional = Chef::Resource::Conditional.only_if(@parent_resource, ["true"])
+ end
+
+ it "indicates that resource convergence should continue" do
+ expect(@conditional.continue?).to be true
+ end
+ end
+
+ describe "after running a negative/false command given as an array" do
+ before do
+ @status.send("success?=", false)
+ @conditional = Chef::Resource::Conditional.only_if(@parent_resource, ["false"])
+ end
+
+ it "indicates that resource convergence should not continue" do
+ expect(@conditional.continue?).to be false
+ end
+ end
+
describe 'after running a command which timed out' do
before do
@conditional = Chef::Resource::Conditional.only_if(@parent_resource, "false")
@@ -106,7 +127,7 @@ describe Chef::Resource::Conditional do
end
describe "when created as a `not_if`" do
- describe "after running a successful/true command" do
+ describe "after running a successful/true command given as a string" do
before do
@conditional = Chef::Resource::Conditional.not_if(@parent_resource, "true")
end
@@ -116,7 +137,7 @@ describe Chef::Resource::Conditional do
end
end
- describe "after running a failed/false command" do
+ describe "after running a failed/false command given as a string" do
before do
@status.send("success?=", false)
@conditional = Chef::Resource::Conditional.not_if(@parent_resource, "false")
@@ -127,6 +148,27 @@ describe Chef::Resource::Conditional do
end
end
+ describe "after running a successful/true command given as an array" do
+ before do
+ @conditional = Chef::Resource::Conditional.not_if(@parent_resource, ["true"])
+ end
+
+ it "indicates that resource convergence should not continue" do
+ expect(@conditional.continue?).to be false
+ end
+ end
+
+ describe "after running a failed/false command given as an array" do
+ before do
+ @status.send("success?=", false)
+ @conditional = Chef::Resource::Conditional.not_if(@parent_resource, ["false"])
+ end
+
+ it "indicates that resource convergence should continue" do
+ expect(@conditional.continue?).to be true
+ end
+ end
+
describe 'after running a command which timed out' do
before do
@conditional = Chef::Resource::Conditional.not_if(@parent_resource, "false")
diff --git a/spec/unit/resource/execute_spec.rb b/spec/unit/resource/execute_spec.rb
index 70b9d87d4c..09160ddbd0 100644
--- a/spec/unit/resource/execute_spec.rb
+++ b/spec/unit/resource/execute_spec.rb
@@ -28,4 +28,8 @@ describe Chef::Resource::Execute do
expect(execute_resource.guard_interpreter).to be(:execute)
end
+ it "defaults to not being a guard interpreter" do
+ expect(execute_resource.is_guard_interpreter).to eq(false)
+ end
+
end
diff --git a/spec/unit/util/diff_spec.rb b/spec/unit/util/diff_spec.rb
index ea226f1c04..b0a57a32c0 100644
--- a/spec/unit/util/diff_spec.rb
+++ b/spec/unit/util/diff_spec.rb
@@ -105,7 +105,7 @@ shared_examples_for "a diff util" do
end
end
- describe "when the default external encoding is UTF-8", :ruby_gte_19_only do
+ describe "when the default external encoding is UTF-8" do
before do
@saved_default_external = Encoding.default_external
@@ -170,7 +170,7 @@ shared_examples_for "a diff util" do
end
- describe "when the default external encoding is Latin-1", :ruby_gte_19_only do
+ describe "when the default external encoding is Latin-1" do
before do
@saved_default_external = Encoding.default_external
@@ -234,7 +234,7 @@ shared_examples_for "a diff util" do
end
end
- describe "when the default external encoding is Shift_JIS", :ruby_gte_19_only do
+ describe "when the default external encoding is Shift_JIS" do
before do
@saved_default_external = Encoding.default_external
@@ -411,7 +411,7 @@ shared_examples_for "a diff util" do
end
end
- describe "when the default external encoding is UTF-8", :ruby_gte_19_only do
+ describe "when the default external encoding is UTF-8" do
before do
@saved_default_external = Encoding.default_external
@@ -456,7 +456,7 @@ shared_examples_for "a diff util" do
end
- describe "when the default external encoding is Latin-1", :ruby_gte_19_only do
+ describe "when the default external encoding is Latin-1" do
before do
@saved_default_external = Encoding.default_external
@@ -500,7 +500,7 @@ shared_examples_for "a diff util" do
end
end
- describe "when the default external encoding is Shift-JIS", :ruby_gte_19_only do
+ describe "when the default external encoding is Shift-JIS" do
before do
@saved_default_external = Encoding.default_external
diff --git a/spec/unit/util/path_helper_spec.rb b/spec/unit/util/path_helper_spec.rb
index 4df4b9b1ff..5756c29b90 100644
--- a/spec/unit/util/path_helper_spec.rb
+++ b/spec/unit/util/path_helper_spec.rb
@@ -189,16 +189,8 @@ describe Chef::Util::PathHelper do
end
context "not on windows", :unix_only do
- context "ruby is at least 1.9", :ruby_gte_19_only do
- it "returns a canonical path" do
- expect(PathHelper.canonical_path("/etc//apache.d/sites-enabled/../sites-available/default")).to eq("/etc/apache.d/sites-available/default")
- end
- end
-
- context "ruby is less than 1.9", :ruby_18_only do
- it "returns a canonical path" do
- expect { PathHelper.canonical_path("/etc//apache.d/sites-enabled/../sites-available/default") }.to raise_error(NotImplementedError)
- end
+ it "returns a canonical path" do
+ expect(PathHelper.canonical_path("/etc//apache.d/sites-enabled/../sites-available/default")).to eq("/etc/apache.d/sites-available/default")
end
end
end