summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNate Walck <nwalck@fb.com>2015-07-20 14:44:27 -0700
committerNate Walck <nwalck@fb.com>2015-07-20 14:44:27 -0700
commit6f6a21f17a3f42256a805afa90d45e54c754641d (patch)
treef6a4593921a7c87cf9ed2b889b37f6aaf4d61b20
parent27dc8b7bc9cc5f7ba553c83072215132c840c3ef (diff)
parentc91b3371036de811c9b2ba0e26f287e676dfd96f (diff)
downloadchef-6f6a21f17a3f42256a805afa90d45e54c754641d.tar.gz
Merge remote-tracking branch 'upstream/master'
-rw-r--r--.travis.yml1
-rw-r--r--CHANGELOG.md31
-rw-r--r--MAINTAINERS.md2
-rw-r--r--MAINTAINERS.toml12
-rw-r--r--appveyor.yml2
-rwxr-xr-xbin/chef-service-manager3
-rw-r--r--chef-config/lib/chef-config/config.rb3
-rw-r--r--chef-config/lib/chef-config/exceptions.rb4
-rw-r--r--chef-config/lib/chef-config/workstation_config_loader.rb179
-rw-r--r--chef-config/spec/unit/workstation_config_loader_spec.rb (renamed from spec/unit/workstation_config_loader_spec.rb)40
-rw-r--r--chef-windows.gemspec2
-rw-r--r--lib/chef.rb2
-rw-r--r--lib/chef/api_client.rb160
-rw-r--r--lib/chef/api_client_v1.rb325
-rw-r--r--lib/chef/application/solo.rb2
-rw-r--r--lib/chef/application/windows_service_manager.rb29
-rw-r--r--lib/chef/chef_class.rb29
-rw-r--r--lib/chef/chef_fs/config.rb46
-rw-r--r--lib/chef/chef_fs/file_pattern.rb19
-rw-r--r--lib/chef/chef_fs/file_system/chef_server_root_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/cookbook_dir.rb1
-rw-r--r--lib/chef/chef_fs/knife.rb42
-rw-r--r--lib/chef/chef_fs/path_utils.rb99
-rw-r--r--lib/chef/constants.rb27
-rw-r--r--lib/chef/delayed_evaluator.rb21
-rw-r--r--lib/chef/dsl/resources.rb10
-rw-r--r--lib/chef/event_dispatch/dsl.rb64
-rw-r--r--lib/chef/exceptions.rb11
-rw-r--r--lib/chef/guard_interpreter/resource_guard_interpreter.rb5
-rw-r--r--lib/chef/http/http_request.rb2
-rw-r--r--lib/chef/knife/bootstrap/templates/chef-full.erb10
-rw-r--r--lib/chef/knife/client_bulk_delete.rb4
-rw-r--r--lib/chef/knife/client_create.rb8
-rw-r--r--lib/chef/knife/client_delete.rb6
-rw-r--r--lib/chef/knife/client_edit.rb12
-rw-r--r--lib/chef/knife/client_list.rb4
-rw-r--r--lib/chef/knife/client_reregister.rb4
-rw-r--r--lib/chef/knife/client_show.rb4
-rw-r--r--lib/chef/knife/osc_user_create.rb6
-rw-r--r--lib/chef/knife/osc_user_delete.rb4
-rw-r--r--lib/chef/knife/osc_user_edit.rb6
-rw-r--r--lib/chef/knife/osc_user_list.rb4
-rw-r--r--lib/chef/knife/osc_user_reregister.rb4
-rw-r--r--lib/chef/knife/osc_user_show.rb4
-rw-r--r--lib/chef/knife/ssl_check.rb5
-rw-r--r--lib/chef/knife/user_create.rb6
-rw-r--r--lib/chef/knife/user_delete.rb8
-rw-r--r--lib/chef/knife/user_edit.rb9
-rw-r--r--lib/chef/knife/user_list.rb4
-rw-r--r--lib/chef/knife/user_reregister.rb4
-rw-r--r--lib/chef/knife/user_show.rb4
-rw-r--r--lib/chef/mixin/params_validate.rb194
-rw-r--r--lib/chef/node_map.rb32
-rw-r--r--lib/chef/osc_user.rb194
-rw-r--r--lib/chef/platform/handler_map.rb45
-rw-r--r--lib/chef/platform/priority_map.rb51
-rw-r--r--lib/chef/platform/provider_handler_map.rb29
-rw-r--r--lib/chef/platform/provider_mapping.rb4
-rw-r--r--lib/chef/platform/rebooter.rb2
-rw-r--r--lib/chef/platform/resource_handler_map.rb29
-rw-r--r--lib/chef/platform/resource_priority_map.rb6
-rw-r--r--lib/chef/property.rb539
-rw-r--r--lib/chef/provider.rb2
-rw-r--r--lib/chef/provider/dsc_resource.rb4
-rw-r--r--lib/chef/provider/dsc_script.rb2
-rw-r--r--lib/chef/provider/mount/aix.rb2
-rw-r--r--lib/chef/provider/package.rb33
-rw-r--r--lib/chef/provider/package/aix.rb1
-rw-r--r--lib/chef/provider/package/apt.rb1
-rw-r--r--lib/chef/provider/package/homebrew.rb1
-rw-r--r--lib/chef/provider/package/ips.rb1
-rw-r--r--lib/chef/provider/package/macports.rb1
-rw-r--r--lib/chef/provider/package/openbsd.rb1
-rw-r--r--lib/chef/provider/package/pacman.rb1
-rw-r--r--lib/chef/provider/package/paludis.rb1
-rw-r--r--lib/chef/provider/package/portage.rb2
-rw-r--r--lib/chef/provider/package/smartos.rb1
-rw-r--r--lib/chef/provider/package/solaris.rb2
-rw-r--r--lib/chef/provider/package/yum.rb1
-rw-r--r--lib/chef/provider/package/zypper.rb1
-rw-r--r--lib/chef/provider/powershell_script.rb51
-rw-r--r--lib/chef/provider/registry_key.rb10
-rw-r--r--lib/chef/provider/service.rb26
-rw-r--r--lib/chef/provider/service/debian.rb2
-rw-r--r--lib/chef/provider/service/insserv.rb2
-rw-r--r--lib/chef/provider/service/invokercd.rb2
-rw-r--r--lib/chef/provider/service/openbsd.rb2
-rw-r--r--lib/chef/provider/service/redhat.rb2
-rw-r--r--lib/chef/provider/service/upstart.rb3
-rw-r--r--lib/chef/provider_resolver.rb112
-rw-r--r--lib/chef/resource.rb468
-rw-r--r--lib/chef/resource/dsc_script.rb2
-rw-r--r--lib/chef/resource/ips_package.rb1
-rw-r--r--lib/chef/resource/mount.rb8
-rw-r--r--lib/chef/resource/openbsd_package.rb11
-rw-r--r--lib/chef/resource/solaris_package.rb5
-rw-r--r--lib/chef/resource_resolver.rb80
-rw-r--r--lib/chef/run_context.rb7
-rw-r--r--lib/chef/run_list/versioned_recipe_list.rb11
-rw-r--r--lib/chef/user.rb240
-rw-r--r--lib/chef/user_v1.rb335
-rw-r--r--lib/chef/win32/registry.rb12
-rw-r--r--lib/chef/workstation_config_loader.rb161
-rw-r--r--spec/data/trusted_certs/opscode.pem109
-rw-r--r--spec/functional/provider/whyrun_safe_ruby_block_spec.rb2
-rw-r--r--spec/functional/rebooter_spec.rb2
-rw-r--r--spec/functional/resource/package_spec.rb2
-rw-r--r--spec/functional/resource/powershell_script_spec.rb (renamed from spec/functional/resource/powershell_spec.rb)6
-rw-r--r--spec/functional/win32/registry_helper_spec.rb12
-rw-r--r--spec/functional/win32/service_manager_spec.rb4
-rw-r--r--spec/integration/knife/chef_repo_path_spec.rb24
-rw-r--r--spec/integration/recipes/recipe_dsl_spec.rb762
-rw-r--r--spec/integration/recipes/resource_action_spec.rb2
-rw-r--r--spec/spec_helper.rb33
-rw-r--r--spec/support/shared/functional/win32_service.rb3
-rw-r--r--spec/support/shared/unit/api_versioning.rb4
-rw-r--r--spec/unit/api_client_spec.rb223
-rw-r--r--spec/unit/api_client_v1_spec.rb457
-rw-r--r--spec/unit/application/solo_spec.rb7
-rw-r--r--spec/unit/chef_class_spec.rb23
-rw-r--r--spec/unit/chef_fs/path_util_spec.rb108
-rw-r--r--spec/unit/event_dispatch/dsl_spec.rb87
-rw-r--r--spec/unit/json_compat_spec.rb7
-rw-r--r--spec/unit/knife/client_bulk_delete_spec.rb8
-rw-r--r--spec/unit/knife/client_create_spec.rb2
-rw-r--r--spec/unit/knife/client_delete_spec.rb6
-rw-r--r--spec/unit/knife/client_edit_spec.rb15
-rw-r--r--spec/unit/knife/client_list_spec.rb2
-rw-r--r--spec/unit/knife/client_reregister_spec.rb4
-rw-r--r--spec/unit/knife/client_show_spec.rb4
-rw-r--r--spec/unit/knife/osc_user_create_spec.rb10
-rw-r--r--spec/unit/knife/osc_user_delete_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_edit_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_list_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_reregister_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_show_spec.rb2
-rw-r--r--spec/unit/knife/ssl_check_spec.rb4
-rw-r--r--spec/unit/knife/user_create_spec.rb2
-rw-r--r--spec/unit/knife/user_delete_spec.rb4
-rw-r--r--spec/unit/knife/user_edit_spec.rb4
-rw-r--r--spec/unit/knife/user_list_spec.rb2
-rw-r--r--spec/unit/knife/user_reregister_spec.rb2
-rw-r--r--spec/unit/knife/user_show_spec.rb4
-rw-r--r--spec/unit/lwrp_spec.rb280
-rw-r--r--spec/unit/mixin/params_validate_spec.rb6
-rw-r--r--spec/unit/node_map_spec.rb12
-rw-r--r--spec/unit/osc_user_spec.rb276
-rw-r--r--spec/unit/platform_spec.rb2
-rw-r--r--spec/unit/property/state_spec.rb669
-rw-r--r--spec/unit/property/validation_spec.rb60
-rw-r--r--spec/unit/property_spec.rb258
-rw-r--r--spec/unit/provider/deploy_spec.rb2
-rw-r--r--spec/unit/provider/dsc_resource_spec.rb6
-rw-r--r--spec/unit/provider/dsc_script_spec.rb4
-rw-r--r--spec/unit/provider/powershell_script_spec.rb (renamed from spec/unit/provider/powershell_spec.rb)0
-rw-r--r--spec/unit/provider/registry_key_spec.rb12
-rw-r--r--spec/unit/provider_resolver_spec.rb305
-rw-r--r--spec/unit/recipe_spec.rb6
-rw-r--r--spec/unit/registry_helper_spec.rb16
-rw-r--r--spec/unit/resource/breakpoint_spec.rb2
-rw-r--r--spec/unit/resource/cron_spec.rb2
-rw-r--r--spec/unit/resource/directory_spec.rb2
-rw-r--r--spec/unit/resource/dsc_resource_spec.rb2
-rw-r--r--spec/unit/resource/dsc_script_spec.rb4
-rw-r--r--spec/unit/resource/env_spec.rb2
-rw-r--r--spec/unit/resource/erl_call_spec.rb2
-rw-r--r--spec/unit/resource/file_spec.rb2
-rw-r--r--spec/unit/resource/group_spec.rb2
-rw-r--r--spec/unit/resource/link_spec.rb2
-rw-r--r--spec/unit/resource/mdadm_spec.rb2
-rw-r--r--spec/unit/resource/mount_spec.rb2
-rw-r--r--spec/unit/resource/ohai_spec.rb2
-rw-r--r--spec/unit/resource/powershell_script_spec.rb (renamed from spec/unit/resource/powershell_spec.rb)0
-rw-r--r--spec/unit/resource/registry_key_spec.rb2
-rw-r--r--spec/unit/resource/route_spec.rb2
-rw-r--r--spec/unit/resource/ruby_block_spec.rb6
-rw-r--r--spec/unit/resource/user_spec.rb2
-rw-r--r--spec/unit/resource/windows_service_spec.rb2
-rw-r--r--spec/unit/resource_resolver_spec.rb12
-rw-r--r--spec/unit/resource_spec.rb96
-rw-r--r--spec/unit/run_list/versioned_recipe_list_spec.rb163
-rw-r--r--spec/unit/user_spec.rb502
-rw-r--r--spec/unit/user_v1_spec.rb584
183 files changed, 6038 insertions, 3268 deletions
diff --git a/.travis.yml b/.travis.yml
index 2910725d7b..d7ad317e28 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,7 +11,6 @@ branches:
- master
- 10-stable
- 11-stable
- - 12-stable
# do not run expensive spec tests on PRs, only on branches
script: "
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df15462289..28886da594 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,35 @@
-## 12.5.0
+## Unreleased
* [**Ranjib Dey**](https://github.com/ranjib):
[pr#3588](https://github.com/chef/chef/pull/3588) Count skipped resources among total resources in doc formatter
+* [**John Kerry**](https://github.com/jkerry):
+ [pr#3539](https://github.com/chef/chef/pull/3539) Fix issue: registry_key resource is case sensitive in chef but not on windows
+* [**David Eddy**](https://github.com/bahamas10):
+ [pr#3443](https://github.com/chef/chef/pull/3443) remove extraneous space
+
+
+* [pr#3455](https://github.com/chef/chef/pull/3455) powershell_script: do not allow suppression of syntax errors
+* [pr#3519](https://github.com/chef/chef/pull/3519) The wording seemed odd.
+* [pr#3208](https://github.com/chef/chef/pull/3208) Missing require (require what you use).
+* [pr#3449](https://github.com/chef/chef/pull/3449) correcting minor typo in user_edit knife action
+* [pr#3572](https://github.com/chef/chef/pull/3572) Use windows paths without case-sensitivity.
+* [pr#3666](https://github.com/chef/chef/pull/3666) Support SNI in `knife ssl check`.
+* [pr#3667](https://github.com/chef/chef/pull/3667) Change chef service to start as 'Automatic delayed start'.
+* [pr#3683](https://github.com/chef/chef/pull/3683) Correct Windows reboot command to delay in minutes, per the property.
+
+## 12.4.1
+
+* [**Noah Kantrowitz**](https://github.com/coderanger):
+ [pr#3605](https://github.com/chef/chef/pull/3605) Rework `Resource#action` to match 12.3 API
+
+* [pr#3586](https://github.com/chef/chef/issues/3586) Fix bug preventing light weight resources from being used with heavy weight providers
+* [Issue #3593](https://github.com/chef/chef/issues/3593) Fix bug where provider priority map did not take into consideration a provided block
+* [pr#3630](https://github.com/chef/chef/pull/3630) Restore Chef::User and Chef::ApiClient namespace to API V0 functionality and move new functionality into Chef::UserV1 and Chef::ApiClientV1 until Chef 13.
+* [pr#3611](https://github.com/chef/chef/pull/3611) Call `provides?` even if `provides` is not called
+* [pr#3589](https://github.com/chef/chef/pull/3589) Fix errant bashisms
+* [pr#3620](https://github.com/chef/chef/pull/3620) Fix issue where recipe names in run list mutate when version constaints are present
+* [pr#3623](https://github.com/chef/chef/pull/3623) Allow LWRPs to access the real class when accessed through `Chef::Resource` and `Chef::Provider`
+* [pr#3627](https://github.com/chef/chef/pull/3627) Separate priority map and DSL handler map so that `provides` has veto power over priority
+* [pr#3638](https://github.com/chef/chef/pull/3638) Deprecate passing more than 1 argument to create a resource
## 12.4.0
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index 43009d1548..b1ae0713f1 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -29,6 +29,7 @@ another component.
### Maintainers
* [Bryan McLellan](https://github.com/btm)
+* [Noah Kantrowitz](https://github.com/coderanger)
* [Daniel DeLeo](https://github.com/danielsdeleo)
* [AJ Christensen](https://github.com/fujin)
* [Phil Dibowitz](https://github.com/jaymzh)
@@ -189,4 +190,5 @@ The specific components of Chef related to a given platform - including (but not
### Maintainers
* [Lamont Granquist](https://github.com/lamont-granquist)
+* [Ryan Cragun](https://github.com/ryancragun)
diff --git a/MAINTAINERS.toml b/MAINTAINERS.toml
index 8f36763e54..257058b4d2 100644
--- a/MAINTAINERS.toml
+++ b/MAINTAINERS.toml
@@ -34,6 +34,7 @@ another component.
maintainers = [
"btm",
+ "coderanger",
"danielsdeleo",
"fujin",
"jaymzh",
@@ -199,7 +200,8 @@ The specific components of Chef related to a given platform - including (but not
title = "ArchLinux"
maintainers = [
- "lamont-granquist"
+ "lamont-granquist",
+ "ryancragun"
]
[people]
@@ -290,3 +292,11 @@ The specific components of Chef related to a given platform - including (but not
[people.joemiller]
Name = "Joe Miller"
GitHub = "joemiller"
+
+ [people.coderanger]
+ Name = "Noah Kantrowitz"
+ GitHub = "coderanger"
+
+ [people.ryancragun]
+ Name = "Ryan Cragun"
+ GitHub = "ryancragun"
diff --git a/appveyor.yml b/appveyor.yml
index 5609648cb1..06448e2be2 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -14,7 +14,7 @@ skip_tags: true
branches:
only:
- master
- - 12-stable
+ - 12.4-stable
cache:
- C:\Ruby200\lib\ruby\gems\2.0.0
diff --git a/bin/chef-service-manager b/bin/chef-service-manager
index 7cef10f506..bbcab5ca92 100755
--- a/bin/chef-service-manager
+++ b/bin/chef-service-manager
@@ -28,7 +28,8 @@ if Chef::Platform.windows?
:service_name => "chef-client",
:service_display_name => "Chef Client Service",
:service_description => "Runs Chef Client on regular, configurable intervals.",
- :service_file_path => File.expand_path(File.join(File.dirname(__FILE__), '../../../../bin/chef-windows-service'))
+ :service_file_path => File.expand_path(File.join(File.dirname(__FILE__), '../../../../bin/chef-windows-service')),
+ :delayed_start => true
}
Chef::Application::WindowsServiceManager.new(chef_client_service).run
else
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb
index 63de8a451f..301a3ba0b6 100644
--- a/chef-config/lib/chef-config/config.rb
+++ b/chef-config/lib/chef-config/config.rb
@@ -739,6 +739,3 @@ module ChefConfig
end
end
end
-
-
-
diff --git a/chef-config/lib/chef-config/exceptions.rb b/chef-config/lib/chef-config/exceptions.rb
index f5d76d856b..1f80e505df 100644
--- a/chef-config/lib/chef-config/exceptions.rb
+++ b/chef-config/lib/chef-config/exceptions.rb
@@ -20,7 +20,7 @@ require 'chef-config/logger'
module ChefConfig
- class InvalidPath < StandardError
- end
+ class ConfigurationError < ArgumentError; end
+ class InvalidPath < StandardError; end
end
diff --git a/chef-config/lib/chef-config/workstation_config_loader.rb b/chef-config/lib/chef-config/workstation_config_loader.rb
new file mode 100644
index 0000000000..177cd776d4
--- /dev/null
+++ b/chef-config/lib/chef-config/workstation_config_loader.rb
@@ -0,0 +1,179 @@
+#
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# 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-config/config'
+require 'chef-config/exceptions'
+require 'chef-config/logger'
+require 'chef-config/path_helper'
+require 'chef-config/windows'
+
+module ChefConfig
+ class WorkstationConfigLoader
+
+ # Path to a config file requested by user, (e.g., via command line option). Can be nil
+ attr_accessor :explicit_config_file
+
+ # TODO: initialize this with a logger for Chef and Knife
+ def initialize(explicit_config_file, logger=nil)
+ @explicit_config_file = explicit_config_file
+ @chef_config_dir = nil
+ @config_location = nil
+ @logger = logger || NullLogger.new
+ end
+
+ def no_config_found?
+ config_location.nil?
+ end
+
+ def config_location
+ @config_location ||= (explicit_config_file || locate_local_config)
+ end
+
+ def chef_config_dir
+ if @chef_config_dir.nil?
+ @chef_config_dir = false
+ full_path = working_directory.split(File::SEPARATOR)
+ (full_path.length - 1).downto(0) do |i|
+ candidate_directory = File.join(full_path[0..i] + [".chef"])
+ if File.exist?(candidate_directory) && File.directory?(candidate_directory)
+ @chef_config_dir = candidate_directory
+ break
+ end
+ end
+ end
+ @chef_config_dir
+ end
+
+ def load
+ # Ignore it if there's no explicit_config_file and can't find one at a
+ # default path.
+ return false if config_location.nil?
+
+ if explicit_config_file && !path_exists?(config_location)
+ raise ChefConfig::ConfigurationError, "Specified config file #{config_location} does not exist"
+ end
+
+ # Have to set Config.config_file b/c other config is derived from it.
+ Config.config_file = config_location
+ read_config(IO.read(config_location), config_location)
+ end
+
+ # (Private API, public for test purposes)
+ def env
+ ENV
+ end
+
+ # (Private API, public for test purposes)
+ def path_exists?(path)
+ Pathname.new(path).expand_path.exist?
+ end
+
+ private
+
+ def have_config?(path)
+ if path_exists?(path)
+ logger.info("Using config at #{path}")
+ true
+ else
+ logger.debug("Config not found at #{path}, trying next option")
+ false
+ end
+ end
+
+ def locate_local_config
+ candidate_configs = []
+
+ # Look for $KNIFE_HOME/knife.rb (allow multiple knives config on same machine)
+ if env['KNIFE_HOME']
+ candidate_configs << File.join(env['KNIFE_HOME'], 'config.rb')
+ candidate_configs << File.join(env['KNIFE_HOME'], 'knife.rb')
+ end
+ # Look for $PWD/knife.rb
+ if Dir.pwd
+ candidate_configs << File.join(Dir.pwd, 'config.rb')
+ candidate_configs << File.join(Dir.pwd, 'knife.rb')
+ end
+ # Look for $UPWARD/.chef/knife.rb
+ if chef_config_dir
+ candidate_configs << File.join(chef_config_dir, 'config.rb')
+ candidate_configs << File.join(chef_config_dir, 'knife.rb')
+ end
+ # Look for $HOME/.chef/knife.rb
+ PathHelper.home('.chef') do |dot_chef_dir|
+ candidate_configs << File.join(dot_chef_dir, 'config.rb')
+ candidate_configs << File.join(dot_chef_dir, 'knife.rb')
+ end
+
+ candidate_configs.find do | candidate_config |
+ have_config?(candidate_config)
+ end
+ end
+
+ def working_directory
+ a = if ChefConfig.windows?
+ env['CD']
+ else
+ env['PWD']
+ end || Dir.pwd
+
+ a
+ end
+
+ def read_config(config_content, config_file_path)
+ Config.from_string(config_content, config_file_path)
+ rescue SignalException
+ raise
+ rescue SyntaxError => e
+ message = ""
+ message << "You have invalid ruby syntax in your config file #{config_file_path}\n\n"
+ message << "#{e.class.name}: #{e.message}\n"
+ if file_line = e.message[/#{Regexp.escape(config_file_path)}:[\d]+/]
+ line = file_line[/:([\d]+)$/, 1].to_i
+ message << highlight_config_error(config_file_path, line)
+ end
+ raise ChefConfig::ConfigurationError, message
+ rescue Exception => e
+ message = "You have an error in your config file #{config_file_path}\n\n"
+ message << "#{e.class.name}: #{e.message}\n"
+ filtered_trace = e.backtrace.grep(/#{Regexp.escape(config_file_path)}/)
+ filtered_trace.each {|bt_line| message << " " << bt_line << "\n" }
+ if !filtered_trace.empty?
+ line_nr = filtered_trace.first[/#{Regexp.escape(config_file_path)}:([\d]+)/, 1]
+ message << highlight_config_error(config_file_path, line_nr.to_i)
+ end
+ raise ChefConfig::ConfigurationError, message
+ end
+
+
+ def highlight_config_error(file, line)
+ config_file_lines = []
+ IO.readlines(file).each_with_index {|l, i| config_file_lines << "#{(i + 1).to_s.rjust(3)}: #{l.chomp}"}
+ if line == 1
+ lines = config_file_lines[0..3]
+ else
+ lines = config_file_lines[Range.new(line - 2, line)]
+ end
+ "Relevant file content:\n" + lines.join("\n") + "\n"
+ end
+
+ def logger
+ @logger
+ end
+
+ end
+end
diff --git a/spec/unit/workstation_config_loader_spec.rb b/chef-config/spec/unit/workstation_config_loader_spec.rb
index 72631f3dfa..9f24a4f11b 100644
--- a/spec/unit/workstation_config_loader_spec.rb
+++ b/chef-config/spec/unit/workstation_config_loader_spec.rb
@@ -18,9 +18,12 @@
require 'spec_helper'
require 'tempfile'
-require 'chef/workstation_config_loader'
-describe Chef::WorkstationConfigLoader do
+require 'chef-config/exceptions'
+require 'chef-config/windows'
+require 'chef-config/workstation_config_loader'
+
+RSpec.describe ChefConfig::WorkstationConfigLoader do
let(:explicit_config_location) { nil }
@@ -65,7 +68,7 @@ describe Chef::WorkstationConfigLoader do
let(:home) { "/Users/example.user" }
before do
- allow(Chef::Util::PathHelper).to receive(:home).with('.chef').and_yield(File.join(home, '.chef'))
+ allow(ChefConfig::PathHelper).to receive(:home).with('.chef').and_yield(File.join(home, '.chef'))
allow(config_loader).to receive(:path_exists?).with("#{home}/.chef/knife.rb").and_return(true)
end
@@ -88,7 +91,7 @@ describe Chef::WorkstationConfigLoader do
let(:env_pwd) { "/path/to/cwd" }
before do
- if Chef::Platform.windows?
+ if ChefConfig.windows?
env["CD"] = env_pwd
else
env["PWD"] = env_pwd
@@ -224,7 +227,7 @@ describe Chef::WorkstationConfigLoader do
let(:explicit_config_location) { "/nope/nope/nope/frab/jab/nab" }
it "raises a configuration error" do
- expect { config_loader.load }.to raise_error(Chef::Exceptions::ConfigurationError)
+ expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
end
end
@@ -233,15 +236,20 @@ describe Chef::WorkstationConfigLoader do
let(:config_content) { "" }
+ # We need to keep a reference to the tempfile because while #close does
+ # not unlink the file, the object being GC'd will.
+ let(:tempfile) do
+ Tempfile.new("Chef-WorkstationConfigLoader-rspec-test").tap do |t|
+ t.print(config_content)
+ t.close
+ end
+ end
+
let(:explicit_config_location) do
- # could use described_class, but remove all ':' from the path if so.
- t = Tempfile.new("Chef-WorkstationConfigLoader-rspec-test")
- t.print(config_content)
- t.close
- t.path
+ tempfile.path
end
- after { File.unlink(explicit_config_location) if File.exists?(explicit_config_location) }
+ after { File.unlink(explicit_config_location) if File.exist?(explicit_config_location) }
context "and is valid" do
@@ -249,12 +257,12 @@ describe Chef::WorkstationConfigLoader do
it "loads the config" do
expect(config_loader.load).to be(true)
- expect(Chef::Config.config_file_evaluated).to be(true)
+ expect(ChefConfig::Config.config_file_evaluated).to be(true)
end
- it "sets Chef::Config.config_file" do
+ it "sets ChefConfig::Config.config_file" do
config_loader.load
- expect(Chef::Config.config_file).to eq(explicit_config_location)
+ expect(ChefConfig::Config.config_file).to eq(explicit_config_location)
end
end
@@ -263,7 +271,7 @@ describe Chef::WorkstationConfigLoader do
let(:config_content) { "{{{{{:{{" }
it "raises a ConfigurationError" do
- expect { config_loader.load }.to raise_error(Chef::Exceptions::ConfigurationError)
+ expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
end
end
@@ -272,7 +280,7 @@ describe Chef::WorkstationConfigLoader do
let(:config_content) { ":foo\n:bar\nraise 'oops'\n:baz\n" }
it "raises a ConfigurationError" do
- expect { config_loader.load }.to raise_error(Chef::Exceptions::ConfigurationError)
+ expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError)
end
end
diff --git a/chef-windows.gemspec b/chef-windows.gemspec
index 167358c7d7..428174889f 100644
--- a/chef-windows.gemspec
+++ b/chef-windows.gemspec
@@ -10,7 +10,7 @@ gemspec.add_dependency "win32-eventlog", "~> 0.6.2"
gemspec.add_dependency "win32-mmap", "~> 0.4.1"
gemspec.add_dependency "win32-mutex", "~> 0.4.2"
gemspec.add_dependency "win32-process", "~> 0.7.5"
-gemspec.add_dependency "win32-service", "0.8.6"
+gemspec.add_dependency "win32-service", "~> 0.8.7"
gemspec.add_dependency "windows-api", "~> 0.4.4"
gemspec.add_dependency "windows-pr", "~> 1.2.4"
gemspec.add_dependency "wmi-lite", "~> 1.0"
diff --git a/lib/chef.rb b/lib/chef.rb
index 6bce976439..1a0b802adb 100644
--- a/lib/chef.rb
+++ b/lib/chef.rb
@@ -31,5 +31,5 @@ require 'chef/daemon'
require 'chef/run_status'
require 'chef/handler'
require 'chef/handler/json_file'
-
+require 'chef/event_dispatch/dsl'
require 'chef/chef_class'
diff --git a/lib/chef/api_client.rb b/lib/chef/api_client.rb
index ad31fb7d7b..b7b9f7dc43 100644
--- a/lib/chef/api_client.rb
+++ b/lib/chef/api_client.rb
@@ -1,7 +1,7 @@
#
-# Author:: Adam Jacob (<adam@chef.io>)
-# Author:: Nuo Yan (<nuo@chef.io>)
-# Copyright:: Copyright (c) 2008, 2015 Chef Software, Inc.
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,18 +23,18 @@ require 'chef/mixin/from_file'
require 'chef/mash'
require 'chef/json_compat'
require 'chef/search/query'
-require 'chef/exceptions'
-require 'chef/mixin/api_version_request_handling'
require 'chef/server_api'
+# DEPRECATION NOTE
+#
+# This code will be removed in Chef 13 in favor of the code in Chef::ApiClientV1,
+# which will be moved to this namespace. New development should occur in
+# Chef::ApiClientV1 until the time before Chef 13.
class Chef
class ApiClient
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
- include Chef::Mixin::ApiVersionRequestHandling
-
- SUPPORTED_API_VERSIONS = [0,1]
# Create a new Chef::ApiClient object.
def initialize
@@ -43,25 +43,6 @@ class Chef
@private_key = nil
@admin = false
@validator = false
- @create_key = nil
- end
-
- def chef_rest_v0
- @chef_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0"})
- end
-
- def chef_rest_v1
- @chef_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "1"})
- end
-
- # will default to the current version (Chef::Authenticator::DEFAULT_SERVER_API_VERSION)
- def http_api
- @http_api ||= Chef::REST.new(Chef::Config[:chef_server_url])
- end
-
- # will default to the current version (Chef::Authenticator::DEFAULT_SERVER_API_VERSION)
- def self.http_api
- Chef::REST.new(Chef::Config[:chef_server_url])
end
# Gets or sets the client name.
@@ -113,8 +94,7 @@ class Chef
)
end
- # Private key. The server will return it as a string.
- # Set to true under API V0 to have the server regenerate the default key.
+ # Gets or sets the private key.
#
# @params [Optional String] The string representation of the private key.
# @return [String] The current value.
@@ -122,19 +102,7 @@ class Chef
set_or_return(
:private_key,
arg,
- :kind_of => [String, TrueClass, FalseClass]
- )
- end
-
- # Used to ask server to generate key pair under api V1
- #
- # @params [Optional True/False] Should be true or false - default is false.
- # @return [True/False] The current value
- def create_key(arg=nil)
- set_or_return(
- :create_key,
- arg,
- :kind_of => [ TrueClass, FalseClass ]
+ :kind_of => [String, FalseClass]
)
end
@@ -145,14 +113,13 @@ class Chef
def to_hash
result = {
"name" => @name,
+ "public_key" => @public_key,
"validator" => @validator,
"admin" => @admin,
'json_class' => self.class.name,
"chef_type" => "client"
}
- result["private_key"] = @private_key unless @private_key.nil?
- result["public_key"] = @public_key unless @public_key.nil?
- result["create_key"] = @create_key unless @create_key.nil?
+ result["private_key"] = @private_key if @private_key
result
end
@@ -166,11 +133,10 @@ class Chef
def self.from_hash(o)
client = Chef::ApiClient.new
client.name(o["name"] || o["clientname"])
+ client.private_key(o["private_key"]) if o.key?("private_key")
+ client.public_key(o["public_key"])
client.admin(o["admin"])
client.validator(o["validator"])
- client.private_key(o["private_key"]) if o.key?("private_key")
- client.public_key(o["public_key"]) if o.key?("public_key")
- client.create_key(o["create_key"]) if o.key?("create_key")
client
end
@@ -182,6 +148,10 @@ class Chef
from_hash(Chef::JSONCompat.parse(j))
end
+ def self.http_api
+ Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0"})
+ end
+
def self.reregister(name)
api_client = load(name)
api_client.reregister
@@ -218,11 +188,11 @@ class Chef
# Save this client via the REST API, returns a hash including the private key
def save
begin
- update
+ http_api.put("clients/#{name}", { :name => self.name, :admin => self.admin, :validator => self.validator})
rescue Net::HTTPServerException => e
# If that fails, go ahead and try and update it
if e.response.code == "404"
- create
+ http_api.post("clients", {:name => self.name, :admin => self.admin, :validator => self.validator })
else
raise e
end
@@ -230,95 +200,18 @@ class Chef
end
def reregister
- # Try API V0 and if it fails due to V0 not being supported, raise the proper error message.
- # reregister only supported in API V0 or lesser.
- reregistered_self = chef_rest_v0.put("clients/#{name}", { :name => name, :admin => admin, :validator => validator, :private_key => true })
+ reregistered_self = http_api.put("clients/#{name}", { :name => name, :admin => admin, :validator => validator, :private_key => true })
if reregistered_self.respond_to?(:[])
private_key(reregistered_self["private_key"])
else
private_key(reregistered_self.private_key)
end
self
- rescue Net::HTTPServerException => e
- # if there was a 406 related to versioning, give error explaining that
- # only API version 0 is supported for reregister command
- if e.response.code == "406" && e.response["x-ops-server-api-version"]
- version_header = Chef::JSONCompat.from_json(e.response["x-ops-server-api-version"])
- min_version = version_header["min_version"]
- max_version = version_header["max_version"]
- error_msg = reregister_only_v0_supported_error_msg(max_version, min_version)
- raise Chef::Exceptions::OnlyApiVersion0SupportedForAction.new(error_msg)
- else
- raise e
- end
- end
-
- # Updates the client via the REST API
- def update
- # NOTE: API V1 dropped support for updating client keys via update (aka PUT),
- # but this code never supported key updating in the first place. Since
- # it was never implemented, we will simply ignore that functionality
- # as it is being deprecated.
- # Delete this comment after V0 support is dropped.
- payload = { :name => name }
- payload[:validator] = validator unless validator.nil?
-
- # DEPRECATION
- # This field is ignored in API V1, but left for backwards-compat,
- # can remove after API V0 is no longer supported.
- payload[:admin] = admin unless admin.nil?
-
- begin
- new_client = chef_rest_v1.put("clients/#{name}", payload)
- rescue Net::HTTPServerException => e
- # rescue API V0 if 406 and the server supports V0
- supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
- raise e unless supported_versions && supported_versions.include?(0)
- new_client = chef_rest_v0.put("clients/#{name}", payload)
- end
-
- new_client
end
# Create the client via the REST API
def create
- payload = {
- :name => name,
- :validator => validator,
- # this field is ignored in API V1, but left for backwards-compat,
- # can remove after OSC 11 support is finished?
- :admin => admin
- }
- begin
- # try API V1
- raise Chef::Exceptions::InvalidClientAttribute, "You cannot set both public_key and create_key for create." if !create_key.nil? && !public_key.nil?
-
- payload[:public_key] = public_key unless public_key.nil?
- payload[:create_key] = create_key unless create_key.nil?
-
- new_client = chef_rest_v1.post("clients", payload)
-
- # get the private_key out of the chef_key hash if it exists
- if new_client['chef_key']
- if new_client['chef_key']['private_key']
- new_client['private_key'] = new_client['chef_key']['private_key']
- end
- new_client['public_key'] = new_client['chef_key']['public_key']
- new_client.delete('chef_key')
- end
-
- rescue Net::HTTPServerException => e
- # rescue API V0 if 406 and the server supports V0
- supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
- raise e unless supported_versions && supported_versions.include?(0)
-
- # under API V0, a key pair will always be created unless public_key is
- # passed on initial POST
- payload[:public_key] = public_key unless public_key.nil?
-
- new_client = chef_rest_v0.post("clients", payload)
- end
- Chef::ApiClient.from_hash(self.to_hash.merge(new_client))
+ http_api.post("clients", self)
end
# As a string
@@ -326,5 +219,14 @@ class Chef
"client[#{@name}]"
end
+ def inspect
+ "Chef::ApiClient name:'#{name}' admin:'#{admin.inspect}' validator:'#{validator}' " +
+ "public_key:'#{public_key}' private_key:'#{private_key}'"
+ end
+
+ def http_api
+ @http_api ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0"})
+ end
+
end
end
diff --git a/lib/chef/api_client_v1.rb b/lib/chef/api_client_v1.rb
new file mode 100644
index 0000000000..80f0d2517c
--- /dev/null
+++ b/lib/chef/api_client_v1.rb
@@ -0,0 +1,325 @@
+#
+# Author:: Adam Jacob (<adam@chef.io>)
+# Author:: Nuo Yan (<nuo@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");
+# 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/config'
+require 'chef/mixin/params_validate'
+require 'chef/mixin/from_file'
+require 'chef/mash'
+require 'chef/json_compat'
+require 'chef/search/query'
+require 'chef/exceptions'
+require 'chef/mixin/api_version_request_handling'
+require 'chef/server_api'
+require 'chef/api_client'
+
+# COMPATIBILITY NOTE
+#
+# This ApiClientV1 code attempts to make API V1 requests and falls back to
+# API V0 requests when it fails. New development should occur here instead
+# of Chef::ApiClient as this will replace that namespace when Chef 13 is released.
+#
+# If you need to default to API V0 behavior (i.e. you need GET client to return
+# a public key, etc), please use Chef::ApiClient and update your code to support
+# API V1 before you pull in Chef 13.
+class Chef
+ class ApiClientV1
+
+ include Chef::Mixin::FromFile
+ include Chef::Mixin::ParamsValidate
+ include Chef::Mixin::ApiVersionRequestHandling
+
+ SUPPORTED_API_VERSIONS = [0,1]
+
+ # Create a new Chef::ApiClientV1 object.
+ def initialize
+ @name = ''
+ @public_key = nil
+ @private_key = nil
+ @admin = false
+ @validator = false
+ @create_key = nil
+ end
+
+ def chef_rest_v0
+ @chef_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0", :inflate_json_class => false})
+ end
+
+ def chef_rest_v1
+ @chef_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "1", :inflate_json_class => false})
+ end
+
+ def self.http_api
+ Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "1", :inflate_json_class => false})
+ end
+
+ # Gets or sets the client name.
+ #
+ # @params [Optional String] The name must be alpha-numeric plus - and _.
+ # @return [String] The current value of the name.
+ def name(arg=nil)
+ set_or_return(
+ :name,
+ arg,
+ :regex => /^[\-[:alnum:]_\.]+$/
+ )
+ end
+
+ # Gets or sets whether this client is an admin.
+ #
+ # @params [Optional True/False] Should be true or false - default is false.
+ # @return [True/False] The current value
+ def admin(arg=nil)
+ set_or_return(
+ :admin,
+ arg,
+ :kind_of => [ TrueClass, FalseClass ]
+ )
+ end
+
+ # Gets or sets the public key.
+ #
+ # @params [Optional String] The string representation of the public key.
+ # @return [String] The current value.
+ def public_key(arg=nil)
+ set_or_return(
+ :public_key,
+ arg,
+ :kind_of => String
+ )
+ end
+
+ # Gets or sets whether this client is a validator.
+ #
+ # @params [Boolean] whether or not the client is a validator. If
+ # `nil`, retrieves the already-set value.
+ # @return [Boolean] The current value
+ def validator(arg=nil)
+ set_or_return(
+ :validator,
+ arg,
+ :kind_of => [TrueClass, FalseClass]
+ )
+ end
+
+ # Private key. The server will return it as a string.
+ # Set to true under API V0 to have the server regenerate the default key.
+ #
+ # @params [Optional String] The string representation of the private key.
+ # @return [String] The current value.
+ def private_key(arg=nil)
+ set_or_return(
+ :private_key,
+ arg,
+ :kind_of => [String, TrueClass, FalseClass]
+ )
+ end
+
+ # Used to ask server to generate key pair under api V1
+ #
+ # @params [Optional True/False] Should be true or false - default is false.
+ # @return [True/False] The current value
+ def create_key(arg=nil)
+ set_or_return(
+ :create_key,
+ arg,
+ :kind_of => [ TrueClass, FalseClass ]
+ )
+ end
+
+ # The hash representation of the object. Includes the name and public_key.
+ # Private key is included if available.
+ #
+ # @return [Hash]
+ def to_hash
+ result = {
+ "name" => @name,
+ "validator" => @validator,
+ "admin" => @admin,
+ "chef_type" => "client"
+ }
+ result["private_key"] = @private_key unless @private_key.nil?
+ result["public_key"] = @public_key unless @public_key.nil?
+ result["create_key"] = @create_key unless @create_key.nil?
+ result
+ end
+
+ # The JSON representation of the object.
+ #
+ # @return [String] the JSON string.
+ def to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
+ end
+
+ def self.from_hash(o)
+ client = Chef::ApiClientV1.new
+ client.name(o["name"] || o["clientname"])
+ client.admin(o["admin"])
+ client.validator(o["validator"])
+ client.private_key(o["private_key"]) if o.key?("private_key")
+ client.public_key(o["public_key"]) if o.key?("public_key")
+ client.create_key(o["create_key"]) if o.key?("create_key")
+ client
+ end
+
+ def self.from_json(j)
+ Chef::ApiClientV1.from_hash(Chef::JSONCompat.from_json(j))
+ end
+
+ def self.reregister(name)
+ api_client = Chef::ApiClientV1.load(name)
+ api_client.reregister
+ end
+
+ def self.list(inflate=false)
+ if inflate
+ response = Hash.new
+ Chef::Search::Query.new.search(:client) do |n|
+ n = self.from_hash(n) if n.instance_of?(Hash)
+ response[n.name] = n
+ end
+ response
+ else
+ http_api.get("clients")
+ end
+ end
+
+ # Load a client by name via the API
+ def self.load(name)
+ response = http_api.get("clients/#{name}")
+ Chef::ApiClientV1.from_hash(response)
+ end
+
+ # Remove this client via the REST API
+ def destroy
+ chef_rest_v1.delete("clients/#{@name}")
+ end
+
+ # Save this client via the REST API, returns a hash including the private key
+ def save
+ begin
+ update
+ rescue Net::HTTPServerException => e
+ # If that fails, go ahead and try and update it
+ if e.response.code == "404"
+ create
+ else
+ raise e
+ end
+ end
+ end
+
+ def reregister
+ # Try API V0 and if it fails due to V0 not being supported, raise the proper error message.
+ # reregister only supported in API V0 or lesser.
+ reregistered_self = chef_rest_v0.put("clients/#{name}", { :name => name, :admin => admin, :validator => validator, :private_key => true })
+ if reregistered_self.respond_to?(:[])
+ private_key(reregistered_self["private_key"])
+ else
+ private_key(reregistered_self.private_key)
+ end
+ self
+ rescue Net::HTTPServerException => e
+ # if there was a 406 related to versioning, give error explaining that
+ # only API version 0 is supported for reregister command
+ if e.response.code == "406" && e.response["x-ops-server-api-version"]
+ version_header = Chef::JSONCompat.from_json(e.response["x-ops-server-api-version"])
+ min_version = version_header["min_version"]
+ max_version = version_header["max_version"]
+ error_msg = reregister_only_v0_supported_error_msg(max_version, min_version)
+ raise Chef::Exceptions::OnlyApiVersion0SupportedForAction.new(error_msg)
+ else
+ raise e
+ end
+ end
+
+ # Updates the client via the REST API
+ def update
+ # NOTE: API V1 dropped support for updating client keys via update (aka PUT),
+ # but this code never supported key updating in the first place. Since
+ # it was never implemented, we will simply ignore that functionality
+ # as it is being deprecated.
+ # Delete this comment after V0 support is dropped.
+ payload = { :name => name }
+ payload[:validator] = validator unless validator.nil?
+
+ # DEPRECATION
+ # This field is ignored in API V1, but left for backwards-compat,
+ # can remove after API V0 is no longer supported.
+ payload[:admin] = admin unless admin.nil?
+
+ begin
+ new_client = chef_rest_v1.put("clients/#{name}", payload)
+ rescue Net::HTTPServerException => e
+ # rescue API V0 if 406 and the server supports V0
+ supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
+ raise e unless supported_versions && supported_versions.include?(0)
+ new_client = chef_rest_v0.put("clients/#{name}", payload)
+ end
+
+ Chef::ApiClientV1.from_hash(new_client)
+ end
+
+ # Create the client via the REST API
+ def create
+ payload = {
+ :name => name,
+ :validator => validator,
+ # this field is ignored in API V1, but left for backwards-compat,
+ # can remove after OSC 11 support is finished?
+ :admin => admin
+ }
+ begin
+ # try API V1
+ raise Chef::Exceptions::InvalidClientAttribute, "You cannot set both public_key and create_key for create." if !create_key.nil? && !public_key.nil?
+
+ payload[:public_key] = public_key unless public_key.nil?
+ payload[:create_key] = create_key unless create_key.nil?
+
+ new_client = chef_rest_v1.post("clients", payload)
+
+ # get the private_key out of the chef_key hash if it exists
+ if new_client['chef_key']
+ if new_client['chef_key']['private_key']
+ new_client['private_key'] = new_client['chef_key']['private_key']
+ end
+ new_client['public_key'] = new_client['chef_key']['public_key']
+ new_client.delete('chef_key')
+ end
+
+ rescue Net::HTTPServerException => e
+ # rescue API V0 if 406 and the server supports V0
+ supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
+ raise e unless supported_versions && supported_versions.include?(0)
+
+ # under API V0, a key pair will always be created unless public_key is
+ # passed on initial POST
+ payload[:public_key] = public_key unless public_key.nil?
+
+ new_client = chef_rest_v0.post("clients", payload)
+ end
+ Chef::ApiClientV1.from_hash(self.to_hash.merge(new_client))
+ end
+
+ # As a string
+ def to_s
+ "client[#{@name}]"
+ end
+
+ end
+end
diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb
index dd09d65b42..5bb2a1ceb0 100644
--- a/lib/chef/application/solo.rb
+++ b/lib/chef/application/solo.rb
@@ -214,7 +214,7 @@ class Chef::Application::Solo < Chef::Application
FileUtils.mkdir_p(recipes_path)
tarball_path = File.join(recipes_path, 'recipes.tgz')
fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path)
- Chef::Mixin::Command.run_command(:command => "tar zxvf #{tarball_path} -C #{recipes_path}")
+ Mixlib::ShellOut.new("tar zxvf #{tarball_path} -C #{recipes_path}").run_command
end
# json_attribs shuld be fetched after recipe_url tarball is unpacked.
diff --git a/lib/chef/application/windows_service_manager.rb b/lib/chef/application/windows_service_manager.rb
index de8ed657c2..9bd6095260 100644
--- a/lib/chef/application/windows_service_manager.rb
+++ b/lib/chef/application/windows_service_manager.rb
@@ -78,7 +78,7 @@ class Chef
raise ArgumentError, "Service definition is not provided" if service_options.nil?
- required_options = [:service_name, :service_display_name, :service_name, :service_description, :service_file_path]
+ required_options = [:service_name, :service_display_name, :service_description, :service_file_path]
required_options.each do |req_option|
if !service_options.has_key?(req_option)
@@ -92,6 +92,7 @@ class Chef
@service_file_path = service_options[:service_file_path]
@service_start_name = service_options[:run_as_user]
@password = service_options[:run_as_password]
+ @delayed_start = service_options[:delayed_start]
end
def run(params = ARGV)
@@ -113,17 +114,21 @@ class Chef
cmd = "\"#{ruby}\" \"#{@service_file_path}\" #{opts}".gsub(File::SEPARATOR, File::ALT_SEPARATOR)
::Win32::Service.new(
- :service_name => @service_name,
- :display_name => @service_display_name,
- :description => @service_description,
- # Prior to 0.8.5, win32-service creates interactive services by default,
- # and we don't want that, so we need to override the service type.
- :service_type => ::Win32::Service::SERVICE_WIN32_OWN_PROCESS,
- :start_type => ::Win32::Service::SERVICE_AUTO_START,
- :binary_path_name => cmd,
- :service_start_name => @service_start_name,
- :password => @password,
- )
+ :service_name => @service_name,
+ :display_name => @service_display_name,
+ :description => @service_description,
+ # Prior to 0.8.5, win32-service creates interactive services by default,
+ # and we don't want that, so we need to override the service type.
+ :service_type => ::Win32::Service::SERVICE_WIN32_OWN_PROCESS,
+ :start_type => ::Win32::Service::SERVICE_AUTO_START,
+ :binary_path_name => cmd,
+ :service_start_name => @service_start_name,
+ :password => @password,
+ )
+ ::Win32::Service.configure(
+ :service_name => @service_name,
+ :delayed_start => @delayed_start
+ ) unless @delayed_start.nil?
puts "Service '#{@service_name}' has successfully been installed."
end
when 'status'
diff --git a/lib/chef/chef_class.rb b/lib/chef/chef_class.rb
index a0c74f7aec..458ac82467 100644
--- a/lib/chef/chef_class.rb
+++ b/lib/chef/chef_class.rb
@@ -28,6 +28,8 @@
require 'chef/platform/provider_priority_map'
require 'chef/platform/resource_priority_map'
+require 'chef/platform/provider_handler_map'
+require 'chef/platform/resource_handler_map'
class Chef
class << self
@@ -50,7 +52,14 @@ class Chef
#
attr_reader :run_context
+ # Register an event handler with user specified block
#
+ # @return[Chef::EventDispatch::Base] handler object
+ def event_handler(&block)
+ dsl = Chef::EventDispatch::DSL.new('Chef client DSL')
+ dsl.instance_eval(&block)
+ end
+
# Get the array of providers associated with a resource_name for the current node
#
# @param resource_name [Symbol] name of the resource as a symbol
@@ -160,20 +169,26 @@ class Chef
@node = nil
@provider_priority_map = nil
@resource_priority_map = nil
+ @provider_handler_map = nil
+ @resource_handler_map = nil
end
# @api private
def provider_priority_map
- @provider_priority_map ||= begin
- # these slurp in the resource+provider world, so be exceedingly lazy about requiring them
- Chef::Platform::ProviderPriorityMap.instance
- end
+ # these slurp in the resource+provider world, so be exceedingly lazy about requiring them
+ @provider_priority_map ||= Chef::Platform::ProviderPriorityMap.instance
end
# @api private
def resource_priority_map
- @resource_priority_map ||= begin
- Chef::Platform::ResourcePriorityMap.instance
- end
+ @resource_priority_map ||= Chef::Platform::ResourcePriorityMap.instance
+ end
+ # @api private
+ def provider_handler_map
+ @provider_handler_map ||= Chef::Platform::ProviderHandlerMap.instance
+ end
+ # @api private
+ def resource_handler_map
+ @resource_handler_map ||= Chef::Platform::ResourceHandlerMap.instance
end
end
diff --git a/lib/chef/chef_fs/config.rb b/lib/chef/chef_fs/config.rb
index 6666a3deee..40cbb36530 100644
--- a/lib/chef/chef_fs/config.rb
+++ b/lib/chef/chef_fs/config.rb
@@ -111,7 +111,7 @@ class Chef
#
def initialize(chef_config = Chef::Config, cwd = Dir.pwd, options = {}, ui = nil)
@chef_config = chef_config
- @cwd = cwd
+ @cwd = File.expand_path(cwd)
@cookbook_version = options[:cookbook_version]
if @chef_config[:repo_mode] == 'everything' && is_hosted? && !ui.nil?
@@ -166,34 +166,37 @@ class Chef
# server_path('/home/jkeiser/chef_repo/cookbooks/blah') == '/cookbooks/blah'
# server_path('/home/*/chef_repo/cookbooks/blah') == nil
#
- # If there are multiple paths (cookbooks, roles, data bags, etc. can all
- # have separate paths), and cwd+the path reaches into one of them, we will
- # return a path relative to that. Otherwise we will return a path to
- # chef_repo.
+ # If there are multiple different, manually specified paths to object locations
+ # (cookbooks, roles, data bags, etc. can all have separate paths), and cwd+the
+ # path reaches into one of them, we will return a path relative to the first
+ # one to match it. Otherwise we expect the path provided to be to the chef
+ # repo path itself. Paths that are not available on the server are not supported.
#
# Globs are allowed as well, but globs outside server paths are NOT
# (presently) supported. See above examples. TODO support that.
#
# If the path does not reach into ANY specified directory, nil is returned.
def server_path(file_path)
- pwd = File.expand_path(Dir.pwd)
- absolute_pwd = Chef::ChefFS::PathUtils.realest_path(File.expand_path(file_path, pwd))
+ target_path = Chef::ChefFS::PathUtils.realest_path(file_path, @cwd)
# Check all object paths (cookbooks_dir, data_bags_dir, etc.)
+ # These are either manually specified by the user or autogenerated relative
+ # to chef_repo_path.
object_paths.each_pair do |name, paths|
paths.each do |path|
- realest_path = Chef::ChefFS::PathUtils.realest_path(path)
- if PathUtils.descendant_of?(absolute_pwd, realest_path)
- relative_path = Chef::ChefFS::PathUtils::relative_to(absolute_pwd, realest_path)
- return relative_path == '.' ? "/#{name}" : "/#{name}/#{relative_path}"
+ object_abs_path = Chef::ChefFS::PathUtils.realest_path(path, @cwd)
+ if relative_path = PathUtils.descendant_path(target_path, object_abs_path)
+ return Chef::ChefFS::PathUtils.join("/#{name}", relative_path)
end
end
end
# Check chef_repo_path
Array(@chef_config[:chef_repo_path]).flatten.each do |chef_repo_path|
- realest_chef_repo_path = Chef::ChefFS::PathUtils.realest_path(chef_repo_path)
- if absolute_pwd == realest_chef_repo_path
+ # We're using realest_path here but we really don't need to - we can just expand the
+ # path and use realpath because a repo_path if provided *must* exist.
+ realest_chef_repo_path = Chef::ChefFS::PathUtils.realest_path(chef_repo_path, @cwd)
+ if Chef::ChefFS::PathUtils.os_path_eq?(target_path, realest_chef_repo_path)
return '/'
end
end
@@ -201,15 +204,10 @@ class Chef
nil
end
- # The current directory, relative to server root
+ # The current directory, relative to server root. This is a case-sensitive server path.
+ # It only exists if the current directory is a child of one of the recognized object_paths below.
def base_path
- @base_path ||= begin
- if @chef_config[:chef_repo_path]
- server_path(File.expand_path(@cwd))
- else
- nil
- end
- end
+ @base_path ||= server_path(@cwd)
end
# Print the given server path, relative to the current directory
@@ -217,10 +215,10 @@ class Chef
server_path = entry.path
if base_path && server_path[0,base_path.length] == base_path
if server_path == base_path
- return "."
- elsif server_path[base_path.length,1] == "/"
+ return '.'
+ elsif server_path[base_path.length,1] == '/'
return server_path[base_path.length + 1, server_path.length - base_path.length - 1]
- elsif base_path == "/" && server_path[0,1] == "/"
+ elsif base_path == '/' && server_path[0,1] == '/'
return server_path[1, server_path.length - 1]
end
end
diff --git a/lib/chef/chef_fs/file_pattern.rb b/lib/chef/chef_fs/file_pattern.rb
index 134d22cbd5..b2351dac68 100644
--- a/lib/chef/chef_fs/file_pattern.rb
+++ b/lib/chef/chef_fs/file_pattern.rb
@@ -72,7 +72,7 @@ class Chef
def could_match_children?(path)
return false if path == '' # Empty string is not a path
- argument_is_absolute = !!(path =~ /^#{Chef::ChefFS::PathUtils::regexp_path_separator}/)
+ argument_is_absolute = Chef::ChefFS::PathUtils::is_absolute?(path)
return false if is_absolute != argument_is_absolute
path = path[1,path.length-1] if argument_is_absolute
@@ -111,7 +111,7 @@ class Chef
#
# This method assumes +could_match_children?(path)+ is +true+.
def exact_child_name_under(path)
- path = path[1,path.length-1] if !!(path =~ /^#{Chef::ChefFS::PathUtils::regexp_path_separator}/)
+ path = path[1,path.length-1] if Chef::ChefFS::PathUtils::is_absolute?(path)
dirs_in_path = Chef::ChefFS::PathUtils::split(path).length
return nil if exact_parts.length <= dirs_in_path
return exact_parts[dirs_in_path]
@@ -149,7 +149,7 @@ class Chef
# abc/*/def.match?('abc/foo/def') == true
# abc/*/def.match?('abc/foo') == false
def match?(path)
- argument_is_absolute = !!(path =~ /^#{Chef::ChefFS::PathUtils::regexp_path_separator}/)
+ argument_is_absolute = Chef::ChefFS::PathUtils::is_absolute?(path)
return false if is_absolute != argument_is_absolute
path = path[1,path.length-1] if argument_is_absolute
!!regexp.match(path)
@@ -160,17 +160,6 @@ class Chef
pattern
end
- # Given a relative file pattern and a directory, makes a new file pattern
- # starting with the directory.
- #
- # FilePattern.relative_to('/usr/local', 'bin/*grok') == FilePattern.new('/usr/local/bin/*grok')
- #
- # BUG: this does not support patterns starting with <tt>..</tt>
- def self.relative_to(dir, pattern)
- return FilePattern.new(pattern) if pattern =~ /^#{Chef::ChefFS::PathUtils::regexp_path_separator}/
- FilePattern.new(Chef::ChefFS::PathUtils::join(dir, pattern))
- end
-
private
def regexp
@@ -195,7 +184,7 @@ class Chef
def calculate
if !@regexp
- @is_absolute = !!(@pattern =~ /^#{Chef::ChefFS::PathUtils::regexp_path_separator}/)
+ @is_absolute = Chef::ChefFS::PathUtils::is_absolute?(@pattern)
full_regexp_parts = []
normalized_parts = []
diff --git a/lib/chef/chef_fs/file_system/chef_server_root_dir.rb b/lib/chef/chef_fs/file_system/chef_server_root_dir.rb
index 370308ee0a..824325f31b 100644
--- a/lib/chef/chef_fs/file_system/chef_server_root_dir.rb
+++ b/lib/chef/chef_fs/file_system/chef_server_root_dir.rb
@@ -90,11 +90,11 @@ class Chef
end
def rest
- Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key, :raw_output => true)
+ Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key, :raw_output => true, :api_version => "0")
end
def get_json(path)
- Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key).get(path)
+ Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key, :api_version => "0").get(path)
end
def chef_rest
diff --git a/lib/chef/chef_fs/file_system/cookbook_dir.rb b/lib/chef/chef_fs/file_system/cookbook_dir.rb
index 03652dc376..555f9aef0a 100644
--- a/lib/chef/chef_fs/file_system/cookbook_dir.rb
+++ b/lib/chef/chef_fs/file_system/cookbook_dir.rb
@@ -16,6 +16,7 @@
# limitations under the License.
#
+require 'chef/chef_fs/command_line'
require 'chef/chef_fs/file_system/rest_list_dir'
require 'chef/chef_fs/file_system/cookbook_subdir'
require 'chef/chef_fs/file_system/cookbook_file'
diff --git a/lib/chef/chef_fs/knife.rb b/lib/chef/chef_fs/knife.rb
index 86872dab71..9101e455f8 100644
--- a/lib/chef/chef_fs/knife.rb
+++ b/lib/chef/chef_fs/knife.rb
@@ -17,6 +17,7 @@
#
require 'chef/knife'
+require 'pathname'
class Chef
module ChefFS
@@ -63,7 +64,7 @@ class Chef
# --chef-repo-path forcibly overrides all other paths
if config[:chef_repo_path]
Chef::Config[:chef_repo_path] = config[:chef_repo_path]
- %w(acl client cookbook container data_bag environment group node role user).each do |variable_name|
+ Chef::ChefFS::Config::INFLECTIONS.each_value do |variable_name|
Chef::Config.delete("#{variable_name}_path".to_sym)
end
end
@@ -98,14 +99,41 @@ class Chef
end
def pattern_arg_from(arg)
- # TODO support absolute file paths and not just patterns? Too much?
- # Could be super useful in a world with multiple repo paths
- if !@chef_fs_config.base_path && !Chef::ChefFS::PathUtils.is_absolute?(arg)
- # Check if chef repo path is specified to give a better error message
- ui.error("Attempt to use relative path '#{arg}' when current directory is outside the repository path")
+ inferred_path = nil
+ if Chef::ChefFS::PathUtils.is_absolute?(arg)
+ # We should be able to use this as-is - but the user might have incorrectly provided
+ # us with a path that is based off of the OS root path instead of the Chef-FS root.
+ # Do a quick and dirty sanity check.
+ if possible_server_path = @chef_fs_config.server_path(arg)
+ ui.warn("The absolute path provided is suspicious: #{arg}")
+ ui.warn("If you wish to refer to a file location, please provide a path that is rooted at the chef-repo.")
+ ui.warn("Consider writing '#{possible_server_path}' instead of '#{arg}'")
+ end
+ # Use the original path because we can't be sure.
+ inferred_path = arg
+ elsif arg[0,1] == '~'
+ # Let's be nice and fix it if possible - but warn the user.
+ ui.warn("A path relative to a user home directory has been provided: #{arg}")
+ ui.warn("Paths provided need to be rooted at the chef-repo being considered or be relative paths.")
+ inferred_path = @chef_fs_config.server_path(arg)
+ ui.warn("Using '#{inferred_path}' as the path instead of '#{arg}'.")
+ elsif Pathname.new(arg).absolute?
+ # It is definitely a system absolute path (such as C:\ or \\foo\bar) but it cannot be
+ # interpreted as a Chef-FS absolute path. Again attempt to be nice but warn the user.
+ ui.warn("An absolute file system path that isn't a server path was provided: #{arg}")
+ ui.warn("Paths provided need to be rooted at the chef-repo being considered or be relative paths.")
+ inferred_path = @chef_fs_config.server_path(arg)
+ ui.warn("Using '#{inferred_path}' as the path instead of '#{arg}'.")
+ elsif @chef_fs_config.base_path.nil?
+ # These are all relative paths. We can't resolve and root paths unless we are in the
+ # chef repo.
+ ui.error("Attempt to use relative path '#{arg}' when current directory is outside the repository path.")
+ ui.error("Current working directory is '#{@chef_fs_config.cwd}'.")
exit(1)
+ else
+ inferred_path = Chef::ChefFS::PathUtils::join(@chef_fs_config.base_path, arg)
end
- Chef::ChefFS::FilePattern.relative_to(@chef_fs_config.base_path, arg)
+ Chef::ChefFS::FilePattern.new(inferred_path)
end
def format_path(entry)
diff --git a/lib/chef/chef_fs/path_utils.rb b/lib/chef/chef_fs/path_utils.rb
index 9ef75ce2e5..595f966378 100644
--- a/lib/chef/chef_fs/path_utils.rb
+++ b/lib/chef/chef_fs/path_utils.rb
@@ -23,31 +23,31 @@ class Chef
module ChefFS
class PathUtils
- # If you are in 'source', this is what you would have to type to reach 'dest'
- # relative_to('/a/b/c/d/e', '/a/b/x/y') == '../../c/d/e'
- # relative_to('/a/b', '/a/b') == '.'
- def self.relative_to(dest, source)
- # Skip past the common parts
- source_parts = Chef::ChefFS::PathUtils.split(source)
- dest_parts = Chef::ChefFS::PathUtils.split(dest)
- i = 0
- until i >= source_parts.length || i >= dest_parts.length || source_parts[i] != dest_parts[i]
- i+=1
- end
- # dot-dot up from 'source' to the common ancestor, then
- # descend to 'dest' from the common ancestor
- result = Chef::ChefFS::PathUtils.join(*(['..']*(source_parts.length-i) + dest_parts[i,dest.length-i]))
- result == '' ? '.' : result
- end
+ # A Chef-FS path is a path in a chef-repository that can be used to address
+ # both files on a local file-system as well as objects on a chef server.
+ # These paths are stricter than file-system paths allowed on various OSes.
+ # Absolute Chef-FS paths begin with "/" (on windows, "\" is acceptable as well).
+ # "/" is used as the path element separator (on windows, "\" is acceptable as well).
+ # No directory/path element may contain a literal "\" character. Any such characters
+ # encountered are either dealt with as separators (on windows) or as escape
+ # characters (on POSIX systems). Relative Chef-FS paths may use ".." or "." but
+ # may never use these to back-out of the root of a Chef-FS path. Any such extraneous
+ # ".."s are ignored.
+ # Chef-FS paths are case sensitive (since the paths on the server are).
+ # On OSes with case insensitive paths, you may be unable to locally deal with two
+ # objects whose server paths only differ by case. OTOH, the case of path segments
+ # that are outside the Chef-FS root (such as when looking at a file-system absolute
+ # path to discover the Chef-FS root path) are handled in accordance to the rules
+ # of the local file-system and OS.
def self.join(*parts)
return "" if parts.length == 0
# Determine if it started with a slash
absolute = parts[0].length == 0 || parts[0].length > 0 && parts[0] =~ /^#{regexp_path_separator}/
# Remove leading and trailing slashes from each part so that the join will work (and the slash at the end will go away)
- parts = parts.map { |part| part.gsub(/^\/|\/$/, "") }
+ parts = parts.map { |part| part.gsub(/^#{regexp_path_separator}+|#{regexp_path_separator}+$/, '') }
# Don't join empty bits
- result = parts.select { |part| part != "" }.join("/")
+ result = parts.select { |part| part != '' }.join('/')
# Put the / back on
absolute ? "/#{result}" : result
end
@@ -60,36 +60,67 @@ class Chef
Chef::ChefFS::windows? ? '[\/\\\\]' : '/'
end
+ # Given a server path, determines if it is absolute.
+ def self.is_absolute?(path)
+ !!(path =~ /^#{regexp_path_separator}/)
+ end
# Given a path which may only be partly real (i.e. /x/y/z when only /x exists,
# or /x/y/*/blah when /x/y/z/blah exists), call File.realpath on the biggest
- # part that actually exists.
+ # part that actually exists. The paths operated on here are not Chef-FS paths.
+ # These are OS paths that may contain symlinks but may not also fully exist.
#
# If /x is a symlink to /blarghle, and has no subdirectories, then:
# PathUtils.realest_path('/x/y/z') == '/blarghle/y/z'
# PathUtils.realest_path('/x/*/z') == '/blarghle/*/z'
# PathUtils.realest_path('/*/y/z') == '/*/y/z'
- def self.realest_path(path)
- path = Pathname.new(path)
- begin
- path.realpath.to_s
- rescue Errno::ENOENT
- dirname = path.dirname
- if dirname
- PathUtils.join(realest_path(dirname), path.basename.to_s)
- else
- path.to_s
+ #
+ # TODO: Move this to wherever util/path_helper is these days.
+ def self.realest_path(path, cwd = Dir.pwd)
+ path = File.expand_path(path, cwd)
+ parent_path = File.dirname(path)
+ suffix = []
+
+ # File.dirname happens to return the path as its own dirname if you're
+ # at the root (such as at \\foo\bar, C:\ or /)
+ until parent_path == path do
+ # This can occur if a path such as "C:" is given. Ruby gives the parent as "C:."
+ # for reasons only it knows.
+ raise ArgumentError "Invalid path segment #{path}" if parent_path.length > path.length
+ begin
+ path = File.realpath(path)
+ break
+ rescue Errno::ENOENT
+ suffix << File.basename(path)
+ path = parent_path
+ parent_path = File.dirname(path)
end
end
+ File.join(path, *suffix.reverse)
end
- def self.descendant_of?(path, ancestor)
- path[0,ancestor.length] == ancestor &&
- (ancestor.length == path.length || path[ancestor.length,1] =~ /#{PathUtils.regexp_path_separator}/)
+ # Compares two path fragments according to the case-sentitivity of the host platform.
+ def self.os_path_eq?(left, right)
+ Chef::ChefFS::windows? ? left.casecmp(right) == 0 : left == right
end
- def self.is_absolute?(path)
- path =~ /^#{regexp_path_separator}/
+ # Given two general OS-dependent file paths, determines the relative path of the
+ # child with respect to the ancestor. Both child and ancestor must exist and be
+ # fully resolved - this is strictly a lexical comparison. No trailing slashes
+ # and other shenanigans are allowed.
+ #
+ # TODO: Move this to util/path_helper.
+ def self.descendant_path(path, ancestor)
+ candidate_fragment = path[0, ancestor.length]
+ return nil unless PathUtils.os_path_eq?(candidate_fragment, ancestor)
+ if ancestor.length == path.length
+ ''
+ elsif path[ancestor.length,1] =~ /#{PathUtils.regexp_path_separator}/
+ path[ancestor.length+1..-1]
+ else
+ nil
+ end
end
+
end
end
end
diff --git a/lib/chef/constants.rb b/lib/chef/constants.rb
new file mode 100644
index 0000000000..d39ce4c68d
--- /dev/null
+++ b/lib/chef/constants.rb
@@ -0,0 +1,27 @@
+#
+# Author:: John Keiser <jkeiser@chef.io>
+# Copyright:: Copyright (c) 2015 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.
+
+class Chef
+ NOT_PASSED = Object.new
+ def NOT_PASSED.to_s
+ "NOT_PASSED"
+ end
+ def NOT_PASSED.inspect
+ to_s
+ end
+ NOT_PASSED.freeze
+end
diff --git a/lib/chef/delayed_evaluator.rb b/lib/chef/delayed_evaluator.rb
new file mode 100644
index 0000000000..9f18a53445
--- /dev/null
+++ b/lib/chef/delayed_evaluator.rb
@@ -0,0 +1,21 @@
+#
+# Author:: John Keiser <jkeiser@chef.io>
+# Copyright:: Copyright (c) 2015 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.
+
+class Chef
+ class DelayedEvaluator < Proc
+ end
+end
diff --git a/lib/chef/dsl/resources.rb b/lib/chef/dsl/resources.rb
index 1ce12ed0a0..f15beaeab0 100644
--- a/lib/chef/dsl/resources.rb
+++ b/lib/chef/dsl/resources.rb
@@ -10,14 +10,16 @@ class Chef
def self.add_resource_dsl(dsl_name)
begin
module_eval(<<-EOM, __FILE__, __LINE__+1)
- def #{dsl_name}(name=nil, created_at=nil, &block)
- declare_resource(#{dsl_name.inspect}, name, created_at || caller[0], &block)
+ def #{dsl_name}(*args, &block)
+ Chef::Log.deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (\#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: \#{args}") if args.size > 1
+ declare_resource(#{dsl_name.inspect}, args[0], caller[0], &block)
end
EOM
rescue SyntaxError
# Handle the case where dsl_name has spaces, etc.
- define_method(dsl_name.to_sym) do |name=nil, created_at=nil, &block|
- declare_resource(dsl_name, name, created_at || caller[0], &block)
+ define_method(dsl_name.to_sym) do |*args, &block|
+ Chef::Log.deprecation("Cannot create resource #{dsl_name} with more than one argument. All arguments except the name (#{args[0].inspect}) will be ignored. This will cause an error in Chef 13. Arguments: #{args}") if args.size > 1
+ declare_resource(dsl_name, args[0], caller[0], &block)
end
end
end
diff --git a/lib/chef/event_dispatch/dsl.rb b/lib/chef/event_dispatch/dsl.rb
new file mode 100644
index 0000000000..c6f21c9b45
--- /dev/null
+++ b/lib/chef/event_dispatch/dsl.rb
@@ -0,0 +1,64 @@
+#
+# Author:: Ranjib Dey (<ranjib@linux.com>)
+# Copyright:: Copyright (c) 2015 Ranjib Dey
+# 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/event_dispatch/base'
+require 'chef/exceptions'
+require 'chef/config'
+
+class Chef
+ module EventDispatch
+ class DSL
+ attr_reader :handler
+
+ def initialize(name)
+ klass = Class.new(Chef::EventDispatch::Base) do
+ attr_reader :name
+ end
+ @handler = klass.new
+ @handler.instance_variable_set(:@name, name)
+
+ # Use event.register API to add anonymous handler if Chef.run_context
+ # and associated event dispatcher is set, else fallback to
+ # Chef::Config[:hanlder]
+ if Chef.run_context && Chef.run_context.events
+ Chef::Log.debug("Registering handler '#{name}' using events api")
+ Chef.run_context.events.register(handler)
+ else
+ Chef::Log.debug("Registering handler '#{name}' using global config")
+ Chef::Config[:event_handlers] << handler
+ end
+ end
+
+ # Adds a new event handler derived from base handler
+ # with user defined block against a chef event
+ #
+ # @return [Chef::EventDispatch::Base] a base handler object
+ def on(event_type, &block)
+ validate!(event_type)
+ handler.define_singleton_method(event_type) do |*args|
+ instance_exec(*args, &block)
+ end
+ end
+
+ private
+ def validate!(event_type)
+ all_event_types = (Chef::EventDispatch::Base.instance_methods - Object.instance_methods)
+ raise Chef::Exceptions::InvalidEventType, "Invalid event type: #{event_type}" unless all_event_types.include?(event_type)
+ end
+ end
+ end
+end
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index dd0bac3cf9..e2e36e8162 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -17,12 +17,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+require 'chef-config/exceptions'
+
class Chef
# == Chef::Exceptions
# Chef's custom exceptions are all contained within the Chef::Exceptions
# namespace.
class Exceptions
+ ConfigurationError = ChefConfig::ConfigurationError
+
# Backcompat with Chef::ShellOut code:
require 'mixlib/shellout/exceptions'
@@ -68,7 +72,6 @@ class Chef
class DuplicateRole < RuntimeError; end
class ValidationFailed < ArgumentError; end
class InvalidPrivateKey < ArgumentError; end
- class ConfigurationError < ArgumentError; end
class MissingKeyAttribute < ArgumentError; end
class KeyCommandInputError < ArgumentError; end
class InvalidKeyArgument < ArgumentError; end
@@ -97,7 +100,11 @@ class Chef
class ConflictingMembersInGroup < ArgumentError; end
class InvalidResourceReference < RuntimeError; end
class ResourceNotFound < RuntimeError; end
+ class ProviderNotFound < RuntimeError; end
+ NoProviderAvailable = ProviderNotFound
class VerificationNotFound < RuntimeError; end
+ class InvalidEventType < ArgumentError; end
+ class MultipleIdentityError < RuntimeError; end
# Can't find a Resource of this type that is valid on this platform.
class NoSuchResourceType < NameError
@@ -218,8 +225,6 @@ class Chef
class ChildConvergeError < RuntimeError; end
- class NoProviderAvailable < RuntimeError; end
-
class DeprecatedFeatureError < RuntimeError;
def initalize(message)
super("#{message} (raising error due to treat_deprecation_warnings_as_errors being set)")
diff --git a/lib/chef/guard_interpreter/resource_guard_interpreter.rb b/lib/chef/guard_interpreter/resource_guard_interpreter.rb
index d4b386a15a..8cff3bc032 100644
--- a/lib/chef/guard_interpreter/resource_guard_interpreter.rb
+++ b/lib/chef/guard_interpreter/resource_guard_interpreter.rb
@@ -68,7 +68,10 @@ class Chef
run_action = action || @resource.action
begin
- @resource.run_action(run_action)
+ # Coerce to an array to be safe. This could happen with a legacy
+ # resource or something overriding the default_action code in a
+ # subclass.
+ Array(run_action).each {|action_to_run| @resource.run_action(action_to_run) }
resource_updated = @resource.updated
rescue Mixlib::ShellOut::ShellCommandFailed
resource_updated = nil
diff --git a/lib/chef/http/http_request.rb b/lib/chef/http/http_request.rb
index 7582f4458f..1baf5724ae 100644
--- a/lib/chef/http/http_request.rb
+++ b/lib/chef/http/http_request.rb
@@ -40,7 +40,7 @@ class Chef
engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby"
- UA_COMMON = "/#{::Chef::VERSION} (#{engine}-#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}; ohai-#{Ohai::VERSION}; #{RUBY_PLATFORM}; +http://opscode.com)"
+ UA_COMMON = "/#{::Chef::VERSION} (#{engine}-#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}; ohai-#{Ohai::VERSION}; #{RUBY_PLATFORM}; +https://chef.io)"
DEFAULT_UA = "Chef Client" << UA_COMMON
USER_AGENT = "User-Agent".freeze
diff --git a/lib/chef/knife/bootstrap/templates/chef-full.erb b/lib/chef/knife/bootstrap/templates/chef-full.erb
index 335b1f181c..575aec0f50 100644
--- a/lib/chef/knife/bootstrap/templates/chef-full.erb
+++ b/lib/chef/knife/bootstrap/templates/chef-full.erb
@@ -12,7 +12,7 @@ tmp_dir="$tmp/install.sh.$$"
(umask 077 && mkdir $tmp_dir) || exit 1
exists() {
- if command -v $1 &>/dev/null
+ if command -v $1 >/dev/null 2>&1
then
return 0
else
@@ -166,12 +166,12 @@ do_download() {
<%= knife_config[:bootstrap_install_command] %>
<% else %>
install_sh="<%= knife_config[:bootstrap_url] ? knife_config[:bootstrap_url] : "https://www.opscode.com/chef/install.sh" %>"
- if ! exists /usr/bin/chef-client; then
+ if test -f /usr/bin/chef-client; then
+ echo "-----> Existing Chef installation detected"
+ else
echo "-----> Installing Chef Omnibus (<%= latest_current_chef_version_string %>)"
do_download ${install_sh} $tmp_dir/install.sh
sh $tmp_dir/install.sh -P chef <%= latest_current_chef_version_string %>
- else
- echo "-----> Existing Chef installation detected"
fi
<% end %>
@@ -226,6 +226,6 @@ cat > /etc/chef/first-boot.json <<EOP
<%= Chef::JSONCompat.to_json(first_boot) %>
EOP
-echo "Starting first Chef Client run..."
+echo "Starting the first Chef Client run..."
<%= start_chef %>'
diff --git a/lib/chef/knife/client_bulk_delete.rb b/lib/chef/knife/client_bulk_delete.rb
index f2be772759..b439e6f995 100644
--- a/lib/chef/knife/client_bulk_delete.rb
+++ b/lib/chef/knife/client_bulk_delete.rb
@@ -23,7 +23,7 @@ class Chef
class ClientBulkDelete < Knife
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -39,7 +39,7 @@ class Chef
ui.fatal("You must supply a regular expression to match the results against")
exit 42
end
- all_clients = Chef::ApiClient.list(true)
+ all_clients = Chef::ApiClientV1.list(true)
matcher = /#{name_args[0]}/
clients_to_delete = {}
diff --git a/lib/chef/knife/client_create.rb b/lib/chef/knife/client_create.rb
index 570c1ee950..fa9a1a7e32 100644
--- a/lib/chef/knife/client_create.rb
+++ b/lib/chef/knife/client_create.rb
@@ -23,7 +23,7 @@ class Chef
class ClientCreate < Knife
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -57,12 +57,12 @@ class Chef
banner "knife client create CLIENTNAME (options)"
def client
- @client_field ||= Chef::ApiClient.new
+ @client_field ||= Chef::ApiClientV1.new
end
def create_client(client)
# should not be using save :( bad behavior
- client.save
+ Chef::ApiClientV1.from_hash(client).save
end
def run
@@ -93,7 +93,7 @@ class Chef
output = edit_data(client)
final_client = create_client(output)
- ui.info("Created #{output}")
+ ui.info("Created #{final_client}")
# output private_key if one
if final_client.private_key
diff --git a/lib/chef/knife/client_delete.rb b/lib/chef/knife/client_delete.rb
index d7d302ee1d..a49c0867a8 100644
--- a/lib/chef/knife/client_delete.rb
+++ b/lib/chef/knife/client_delete.rb
@@ -23,7 +23,7 @@ class Chef
class ClientDelete < Knife
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -43,8 +43,8 @@ class Chef
exit 1
end
- delete_object(Chef::ApiClient, @client_name, 'client') {
- object = Chef::ApiClient.load(@client_name)
+ delete_object(Chef::ApiClientV1, @client_name, 'client') {
+ object = Chef::ApiClientV1.load(@client_name)
if object.validator
unless config[:delete_validators]
ui.fatal("You must specify --delete-validators to delete the validator client #{@client_name}")
diff --git a/lib/chef/knife/client_edit.rb b/lib/chef/knife/client_edit.rb
index c81bce902a..5dcd8f212b 100644
--- a/lib/chef/knife/client_edit.rb
+++ b/lib/chef/knife/client_edit.rb
@@ -23,7 +23,7 @@ class Chef
class ClientEdit < Knife
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -38,7 +38,15 @@ class Chef
exit 1
end
- edit_object(Chef::ApiClient, @client_name)
+ original_data = Chef::ApiClientV1.load(@client_name).to_hash
+ edited_client = edit_data(original_data)
+ if original_data != edited_client
+ client = Chef::ApiClientV1.from_hash(edited_client)
+ client.save
+ ui.msg("Saved #{client}.")
+ else
+ ui.msg("Client unchanged, not saving.")
+ end
end
end
end
diff --git a/lib/chef/knife/client_list.rb b/lib/chef/knife/client_list.rb
index da0bf12dc3..d8a3698b6a 100644
--- a/lib/chef/knife/client_list.rb
+++ b/lib/chef/knife/client_list.rb
@@ -23,7 +23,7 @@ class Chef
class ClientList < Knife
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -35,7 +35,7 @@ class Chef
:description => "Show corresponding URIs"
def run
- output(format_list_for_display(Chef::ApiClient.list))
+ output(format_list_for_display(Chef::ApiClientV1.list))
end
end
end
diff --git a/lib/chef/knife/client_reregister.rb b/lib/chef/knife/client_reregister.rb
index 666fd09fd2..b94761e718 100644
--- a/lib/chef/knife/client_reregister.rb
+++ b/lib/chef/knife/client_reregister.rb
@@ -23,7 +23,7 @@ class Chef
class ClientReregister < Knife
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -43,7 +43,7 @@ class Chef
exit 1
end
- client = Chef::ApiClient.reregister(@client_name)
+ client = Chef::ApiClientV1.reregister(@client_name)
Chef::Log.debug("Updated client data: #{client.inspect}")
key = client.private_key
if config[:file]
diff --git a/lib/chef/knife/client_show.rb b/lib/chef/knife/client_show.rb
index 822848fdc2..bdac3f9758 100644
--- a/lib/chef/knife/client_show.rb
+++ b/lib/chef/knife/client_show.rb
@@ -25,7 +25,7 @@ class Chef
include Knife::Core::MultiAttributeReturnOption
deps do
- require 'chef/api_client'
+ require 'chef/api_client_v1'
require 'chef/json_compat'
end
@@ -40,7 +40,7 @@ class Chef
exit 1
end
- client = Chef::ApiClient.load(@client_name)
+ client = Chef::ApiClientV1.load(@client_name)
output(format_for_display(client))
end
diff --git a/lib/chef/knife/osc_user_create.rb b/lib/chef/knife/osc_user_create.rb
index c368296040..6c3415473f 100644
--- a/lib/chef/knife/osc_user_create.rb
+++ b/lib/chef/knife/osc_user_create.rb
@@ -27,7 +27,7 @@ class Chef
class OscUserCreate < Knife
deps do
- require 'chef/osc_user'
+ require 'chef/user'
require 'chef/json_compat'
end
@@ -69,7 +69,7 @@ class Chef
exit 1
end
- user = Chef::OscUser.new
+ user = Chef::User.new
user.name(@user_name)
user.admin(config[:admin])
user.password config[:user_password]
@@ -79,7 +79,7 @@ class Chef
end
output = edit_data(user)
- user = Chef::OscUser.from_hash(output).create
+ user = Chef::User.from_hash(output).create
ui.info("Created #{user}")
if user.private_key
diff --git a/lib/chef/knife/osc_user_delete.rb b/lib/chef/knife/osc_user_delete.rb
index d6fbd4a6a9..5cd4f10413 100644
--- a/lib/chef/knife/osc_user_delete.rb
+++ b/lib/chef/knife/osc_user_delete.rb
@@ -28,7 +28,7 @@ class Chef
class OscUserDelete < Knife
deps do
- require 'chef/osc_user'
+ require 'chef/user'
require 'chef/json_compat'
end
@@ -43,7 +43,7 @@ class Chef
exit 1
end
- delete_object(Chef::OscUser, @user_name)
+ delete_object(Chef::User, @user_name)
end
end
diff --git a/lib/chef/knife/osc_user_edit.rb b/lib/chef/knife/osc_user_edit.rb
index 4c38674d08..526475db05 100644
--- a/lib/chef/knife/osc_user_edit.rb
+++ b/lib/chef/knife/osc_user_edit.rb
@@ -28,7 +28,7 @@ class Chef
class OscUserEdit < Knife
deps do
- require 'chef/osc_user'
+ require 'chef/user'
require 'chef/json_compat'
end
@@ -43,10 +43,10 @@ class Chef
exit 1
end
- original_user = Chef::OscUser.load(@user_name).to_hash
+ original_user = Chef::User.load(@user_name).to_hash
edited_user = edit_data(original_user)
if original_user != edited_user
- user = Chef::OscUser.from_hash(edited_user)
+ user = Chef::User.from_hash(edited_user)
user.update
ui.msg("Saved #{user}.")
else
diff --git a/lib/chef/knife/osc_user_list.rb b/lib/chef/knife/osc_user_list.rb
index 92f049cd19..84fca31899 100644
--- a/lib/chef/knife/osc_user_list.rb
+++ b/lib/chef/knife/osc_user_list.rb
@@ -28,7 +28,7 @@ class Chef
class OscUserList < Knife
deps do
- require 'chef/osc_user'
+ require 'chef/user'
require 'chef/json_compat'
end
@@ -40,7 +40,7 @@ class Chef
:description => "Show corresponding URIs"
def run
- output(format_list_for_display(Chef::OscUser.list))
+ output(format_list_for_display(Chef::User.list))
end
end
end
diff --git a/lib/chef/knife/osc_user_reregister.rb b/lib/chef/knife/osc_user_reregister.rb
index a71e0aa677..163b286fe0 100644
--- a/lib/chef/knife/osc_user_reregister.rb
+++ b/lib/chef/knife/osc_user_reregister.rb
@@ -28,7 +28,7 @@ class Chef
class OscUserReregister < Knife
deps do
- require 'chef/osc_user'
+ require 'chef/user'
require 'chef/json_compat'
end
@@ -48,7 +48,7 @@ class Chef
exit 1
end
- user = Chef::OscUser.load(@user_name).reregister
+ user = Chef::User.load(@user_name).reregister
Chef::Log.debug("Updated user data: #{user.inspect}")
key = user.private_key
if config[:file]
diff --git a/lib/chef/knife/osc_user_show.rb b/lib/chef/knife/osc_user_show.rb
index 6a41ddae88..cb3a77585a 100644
--- a/lib/chef/knife/osc_user_show.rb
+++ b/lib/chef/knife/osc_user_show.rb
@@ -30,7 +30,7 @@ class Chef
include Knife::Core::MultiAttributeReturnOption
deps do
- require 'chef/osc_user'
+ require 'chef/user'
require 'chef/json_compat'
end
@@ -45,7 +45,7 @@ class Chef
exit 1
end
- user = Chef::OscUser.load(@user_name)
+ user = Chef::User.load(@user_name)
output(format_for_display(user))
end
diff --git a/lib/chef/knife/ssl_check.rb b/lib/chef/knife/ssl_check.rb
index c5fe4fc1aa..d71eacfc7e 100644
--- a/lib/chef/knife/ssl_check.rb
+++ b/lib/chef/knife/ssl_check.rb
@@ -73,11 +73,12 @@ class Chef
exit 1
end
-
def verify_peer_socket
@verify_peer_socket ||= begin
tcp_connection = TCPSocket.new(host, port)
- OpenSSL::SSL::SSLSocket.new(tcp_connection, verify_peer_ssl_context)
+ ssl_client = OpenSSL::SSL::SSLSocket.new(tcp_connection, verify_peer_ssl_context)
+ ssl_client.hostname = host
+ ssl_client
end
end
diff --git a/lib/chef/knife/user_create.rb b/lib/chef/knife/user_create.rb
index e73f6be8b6..995573cd03 100644
--- a/lib/chef/knife/user_create.rb
+++ b/lib/chef/knife/user_create.rb
@@ -27,7 +27,7 @@ class Chef
attr_accessor :user_field
deps do
- require 'chef/user'
+ require 'chef/user_v1'
require 'chef/json_compat'
end
@@ -61,11 +61,11 @@ class Chef
banner "knife user create USERNAME DISPLAY_NAME FIRST_NAME LAST_NAME EMAIL PASSWORD (options)"
def user
- @user_field ||= Chef::User.new
+ @user_field ||= Chef::UserV1.new
end
def create_user_from_hash(hash)
- Chef::User.from_hash(hash).create
+ Chef::UserV1.from_hash(hash).create
end
def osc_11_warning
diff --git a/lib/chef/knife/user_delete.rb b/lib/chef/knife/user_delete.rb
index 803be6b90c..828cd51588 100644
--- a/lib/chef/knife/user_delete.rb
+++ b/lib/chef/knife/user_delete.rb
@@ -23,7 +23,7 @@ class Chef
class UserDelete < Knife
deps do
- require 'chef/user'
+ require 'chef/user_v1'
require 'chef/json_compat'
end
@@ -55,7 +55,7 @@ EOF
if Kernel.block_given?
object = block.call
else
- object = Chef::User.load(user_name)
+ object = Chef::UserV1.load(user_name)
object.destroy
end
@@ -77,10 +77,10 @@ EOF
# Below is modification of Chef::Knife.delete_object to detect OSC 11 server.
# When OSC 11 is deprecated, simply delete all this and go back to:
#
- # delete_object(Chef::User, @user_name)
+ # delete_object(Chef::UserV1, @user_name)
#
# Also delete our override of delete_object above
- object = Chef::User.load(@user_name)
+ object = Chef::UserV1.load(@user_name)
# OSC 11 case
if object.username.nil?
diff --git a/lib/chef/knife/user_edit.rb b/lib/chef/knife/user_edit.rb
index dd2fc02743..c3a4326ee8 100644
--- a/lib/chef/knife/user_edit.rb
+++ b/lib/chef/knife/user_edit.rb
@@ -23,7 +23,7 @@ class Chef
class UserEdit < Knife
deps do
- require 'chef/user'
+ require 'chef/user_v1'
require 'chef/json_compat'
end
@@ -56,8 +56,7 @@ EOF
exit 1
end
- original_user = Chef::User.load(@user_name).to_hash
-
+ original_user = Chef::UserV1.load(@user_name).to_hash
# DEPRECATION NOTE
# Remove this if statement and corrosponding code post OSC 11 support.
#
@@ -69,11 +68,11 @@ EOF
else # EC / CS 12 user create
edited_user = edit_data(original_user)
if original_user != edited_user
- user = Chef::User.from_hash(edited_user)
+ user = Chef::UserV1.from_hash(edited_user)
user.update
ui.msg("Saved #{user}.")
else
- ui.msg("User unchaged, not saving.")
+ ui.msg("User unchanged, not saving.")
end
end
diff --git a/lib/chef/knife/user_list.rb b/lib/chef/knife/user_list.rb
index 7ae43dadc9..6a130392b9 100644
--- a/lib/chef/knife/user_list.rb
+++ b/lib/chef/knife/user_list.rb
@@ -25,7 +25,7 @@ class Chef
class UserList < Knife
deps do
- require 'chef/user'
+ require 'chef/user_v1'
require 'chef/json_compat'
end
@@ -37,7 +37,7 @@ class Chef
:description => "Show corresponding URIs"
def run
- output(format_list_for_display(Chef::User.list))
+ output(format_list_for_display(Chef::UserV1.list))
end
end
diff --git a/lib/chef/knife/user_reregister.rb b/lib/chef/knife/user_reregister.rb
index eab2245025..09fd1cd2d6 100644
--- a/lib/chef/knife/user_reregister.rb
+++ b/lib/chef/knife/user_reregister.rb
@@ -23,7 +23,7 @@ class Chef
class UserReregister < Knife
deps do
- require 'chef/user'
+ require 'chef/user_v1'
require 'chef/json_compat'
end
@@ -61,7 +61,7 @@ EOF
exit 1
end
- user = Chef::User.load(@user_name)
+ user = Chef::UserV1.load(@user_name)
# DEPRECATION NOTE
# Remove this if statement and corrosponding code post OSC 11 support.
diff --git a/lib/chef/knife/user_show.rb b/lib/chef/knife/user_show.rb
index f5e81e9972..3a2443471a 100644
--- a/lib/chef/knife/user_show.rb
+++ b/lib/chef/knife/user_show.rb
@@ -25,7 +25,7 @@ class Chef
include Knife::Core::MultiAttributeReturnOption
deps do
- require 'chef/user'
+ require 'chef/user_v1'
require 'chef/json_compat'
end
@@ -58,7 +58,7 @@ EOF
exit 1
end
- user = Chef::User.load(@user_name)
+ user = Chef::UserV1.load(@user_name)
# DEPRECATION NOTE
# Remove this if statement and corrosponding code post OSC 11 support.
diff --git a/lib/chef/mixin/params_validate.rb b/lib/chef/mixin/params_validate.rb
index f7d52a19cf..322caea474 100644
--- a/lib/chef/mixin/params_validate.rb
+++ b/lib/chef/mixin/params_validate.rb
@@ -15,11 +15,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-class Chef
- NOT_PASSED = Object.new
+require 'chef/constants'
+require 'chef/property'
+require 'chef/delayed_evaluator'
- class DelayedEvaluator < Proc
- end
+class Chef
module Mixin
module ParamsValidate
@@ -34,20 +34,55 @@ class Chef
# Would raise an exception if the value of :one above is not a kind_of? string. Valid
# map options are:
#
- # :default:: Sets the default value for this parameter.
- # :callbacks:: Takes a hash of Procs, which should return true if the argument is valid.
- # The key will be inserted into the error message if the Proc does not return true:
- # "Option #{key}'s value #{value} #{message}!"
- # :kind_of:: Ensure that the value is a kind_of?(Whatever). If passed an array, it will ensure
- # that the value is one of those types.
- # :respond_to:: Ensure that the value has a given method. Takes one method name or an array of
- # method names.
- # :required:: Raise an exception if this parameter is missing. Valid values are true or false,
- # by default, options are not required.
- # :regex:: Match the value of the parameter against a regular expression.
- # :equal_to:: Match the value of the parameter with ==. An array means it can be equal to any
- # of the values.
+ # @param opts [Hash<Symbol,Object>] Validation opts.
+ # @option opts [Object,Array] :is An object, or list of
+ # objects, that must match the value using Ruby's `===` operator
+ # (`opts[:is].any? { |v| v === value }`). (See #_pv_is.)
+ # @option opts [Object,Array] :equal_to An object, or list
+ # of objects, that must be equal to the value using Ruby's `==`
+ # operator (`opts[:is].any? { |v| v == value }`) (See #_pv_equal_to.)
+ # @option opts [Regexp,Array<Regexp>] :regex An object, or
+ # list of objects, that must match the value with `regex.match(value)`.
+ # (See #_pv_regex)
+ # @option opts [Class,Array<Class>] :kind_of A class, or
+ # list of classes, that the value must be an instance of. (See
+ # #_pv_kind_of.)
+ # @option opts [Hash<String,Proc>] :callbacks A hash of
+ # messages -> procs, all of which match the value. The proc must
+ # return a truthy or falsey value (true means it matches). (See
+ # #_pv_callbacks.)
+ # @option opts [Symbol,Array<Symbol>] :respond_to A method
+ # name, or list of method names, the value must respond to. (See
+ # #_pv_respond_to.)
+ # @option opts [Symbol,Array<Symbol>] :cannot_be A property,
+ # or a list of properties, that the value cannot have (such as `:nil` or
+ # `:empty`). The method with a questionmark at the end is called on the
+ # value (e.g. `value.empty?`). If the value does not have this method,
+ # it is considered valid (i.e. if you don't respond to `empty?` we
+ # assume you are not empty). (See #_pv_cannot_be.)
+ # @option opts [Proc] :coerce A proc which will be called to
+ # transform the user input to canonical form. The value is passed in,
+ # and the transformed value returned as output. Lazy values will *not*
+ # be passed to this method until after they are evaluated. Called in the
+ # context of the resource (meaning you can access other properties).
+ # (See #_pv_coerce.) (See #_pv_coerce.)
+ # @option opts [Boolean] :required `true` if this property
+ # must be present and not `nil`; `false` otherwise. This is checked
+ # after the resource is fully initialized. (See #_pv_required.)
+ # @option opts [Boolean] :name_property `true` if this
+ # property defaults to the same value as `name`. Equivalent to
+ # `default: lazy { name }`, except that #property_is_set? will
+ # return `true` if the property is set *or* if `name` is set. (See
+ # #_pv_name_property.)
+ # @option opts [Boolean] :name_attribute Same as `name_property`.
+ # @option opts [Object] :default The value this property
+ # will return if the user does not set one. If this is `lazy`, it will
+ # be run in the context of the instance (and able to access other
+ # properties). (See #_pv_default.)
+ #
def validate(opts, map)
+ map = map.validation_options if map.is_a?(Property)
+
#--
# validate works by taking the keys in the validation map, assuming it's a hash, and
# looking for _pv_:symbol as methods. Assuming it find them, it calls the right
@@ -84,91 +119,8 @@ class Chef
end
def set_or_return(symbol, value, validation)
- symbol = symbol.to_sym
- iv_symbol = :"@#{symbol}"
-
- # Steal default, coerce, name_property and required from validation
- # so that we can handle the order in which they are applied
- validation = validation.dup
- if validation.has_key?(:default)
- default = validation.delete(:default)
- elsif validation.has_key?('default')
- default = validation.delete('default')
- else
- default = NOT_PASSED
- end
- coerce = validation.delete(:coerce)
- coerce ||= validation.delete('coerce')
- name_property = validation.delete(:name_property)
- name_property ||= validation.delete('name_property')
- name_property ||= validation.delete(:name_attribute)
- name_property ||= validation.delete('name_attribute')
- required = validation.delete(:required)
- required ||= validation.delete('required')
-
- opts = {}
- # If the user passed NOT_PASSED, or passed nil, then this is a get.
- if value == NOT_PASSED || (value.nil? && !explicitly_allows_nil?(symbol, validation))
-
- # Get the value if there is one
- if self.instance_variable_defined?(iv_symbol)
- opts[symbol] = self.instance_variable_get(iv_symbol)
-
- # Handle lazy values
- if opts[symbol].is_a?(DelayedEvaluator)
- if opts[symbol].arity >= 1
- opts[symbol] = opts[symbol].call(self)
- else
- opts[symbol] = opts[symbol].call
- end
-
- # Coerce and validate the default value
- _pv_required(opts, symbol, required, explicitly_allows_nil?(symbol, validation)) if required
- _pv_coerce(opts, symbol, coerce) if coerce
- validate(opts, { symbol => validation })
- end
-
- # Get the default value
- else
- _pv_required(opts, symbol, required, explicitly_allows_nil?(symbol, validation)) if required
- _pv_default(opts, symbol, default) unless default == NOT_PASSED
- _pv_name_property(opts, symbol, name_property)
-
- if opts.has_key?(symbol)
- # Handle lazy defaults.
- if opts[symbol].is_a?(DelayedEvaluator)
- if opts[symbol].arity >= 1
- opts[symbol] = opts[symbol].call(self)
- else
- opts[symbol] = instance_eval(&opts[symbol])
- end
- end
-
- # Coerce and validate the default value
- _pv_required(opts, symbol, required, explicitly_allows_nil?(symbol, validation)) if required
- _pv_coerce(opts, symbol, coerce) if coerce
- # We presently do not validate defaults, for backwards compatibility.
-# validate(opts, { symbol => validation })
-
- # Defaults are presently "stickily" set on the instance
- self.instance_variable_set(iv_symbol, opts[symbol])
- end
- end
-
- # Set the value
- else
- opts[symbol] = value
- unless opts[symbol].is_a?(DelayedEvaluator)
- # Coerce and validate the value
- _pv_required(opts, symbol, required, explicitly_allows_nil?(symbol, validation)) if required
- _pv_coerce(opts, symbol, coerce) if coerce
- validate(opts, { symbol => validation })
- end
-
- self.instance_variable_set(iv_symbol, opts[symbol])
- end
-
- opts[symbol]
+ property = SetOrReturnProperty.new(name: symbol, **validation)
+ property.call(self, value)
end
private
@@ -193,8 +145,9 @@ class Chef
if is_required
return true if opts.has_key?(key.to_s) && (explicitly_allows_nil || !opts[key.to_s].nil?)
return true if opts.has_key?(key.to_sym) && (explicitly_allows_nil || !opts[key.to_sym].nil?)
- raise Exceptions::ValidationFailed, "Required argument #{key} is missing!"
+ raise Exceptions::ValidationFailed, "Required argument #{key.inspect} is missing!"
end
+ true
end
#
@@ -380,7 +333,7 @@ class Chef
def _pv_name_property(opts, key, is_name_property=true)
if is_name_property
if opts[key].nil?
- opts[key] = self.instance_variable_get("@name")
+ opts[key] = self.instance_variable_get(:"@name")
end
end
end
@@ -425,9 +378,9 @@ class Chef
# x 1 #=> invalid
# ```
#
- # @example PropertyType
+ # @example Property
# ```ruby
- # type = PropertyType.new(is: String)
+ # type = Property.new(is: String)
# property :x, type
# x 'foo' #=> valid
# x 1 #=> invalid
@@ -448,8 +401,12 @@ class Chef
value = _pv_opts_lookup(opts, key)
to_be = [ to_be ].flatten(1)
to_be.each do |tb|
- if tb.is_a?(Proc)
+ case tb
+ when Proc
return true if instance_exec(value, &tb)
+ when Property
+ validate(opts, { key => tb.validation_options })
+ return true
else
return true if tb === value
end
@@ -484,6 +441,29 @@ class Chef
opts[key.to_sym] = instance_exec(opts[key], &coercer)
end
end
+
+ # Used by #set_or_return to avoid emitting a deprecation warning for
+ # "value nil" and to keep default stickiness working exactly the same
+ # @api private
+ class SetOrReturnProperty < Chef::Property
+ def get(resource)
+ value = super
+ # All values are sticky, frozen or not
+ if !is_set?(resource)
+ set_value(resource, value)
+ end
+ value
+ end
+
+ def call(resource, value=NOT_PASSED)
+ # setting to nil does a get
+ if value.nil? && !explicitly_accepts_nil?(resource)
+ get(resource)
+ else
+ super
+ end
+ end
+ end
end
end
end
diff --git a/lib/chef/node_map.rb b/lib/chef/node_map.rb
index d5eed7c215..d905c8779e 100644
--- a/lib/chef/node_map.rb
+++ b/lib/chef/node_map.rb
@@ -20,13 +20,6 @@ class Chef
class NodeMap
#
- # Create a new NodeMap
- #
- def initialize
- @map = {}
- end
-
- #
# Set a key/value pair on the map with a filter. The filter must be true
# when applied to the node in order to retrieve the value.
#
@@ -55,18 +48,17 @@ class Chef
# The map is sorted in order of preference already; we just need to find
# our place in it (just before the first value with the same preference level).
insert_at = nil
- @map[key] ||= []
- @map[key].each_with_index do |matcher,index|
+ map[key] ||= []
+ map[key].each_with_index do |matcher,index|
cmp = compare_matchers(key, new_matcher, matcher)
insert_at ||= index if cmp && cmp <= 0
end
if insert_at
- @map[key].insert(insert_at, new_matcher)
+ map[key].insert(insert_at, new_matcher)
else
- @map[key] << new_matcher
+ map[key] << new_matcher
end
- insert_at ||= @map[key].size - 1
- @map
+ map
end
#
@@ -100,8 +92,8 @@ class Chef
#
def list(node, key, canonical: nil)
raise ArgumentError, "first argument must be a Chef::Node" unless node.is_a?(Chef::Node) || node.nil?
- return [] unless @map.has_key?(key)
- @map[key].select do |matcher|
+ return [] unless map.has_key?(key)
+ map[key].select do |matcher|
node_matches?(node, matcher) && canonical_matches?(canonical, matcher)
end.map { |matcher| matcher[:value] }
end
@@ -110,11 +102,11 @@ class Chef
# @return remaining
# @api private
def delete_canonical(key, value)
- remaining = @map[key]
+ remaining = map[key]
if remaining
remaining.delete_if { |matcher| matcher[:canonical] && Array(matcher[:value]) == Array(value) }
if remaining.empty?
- @map.delete(key)
+ map.delete(key)
remaining = nil
end
end
@@ -181,7 +173,7 @@ class Chef
end
def compare_matchers(key, new_matcher, matcher)
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:block] }
+ cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:block] }
return cmp if cmp != 0
cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:filters][:platform_version] }
return cmp if cmp != 0
@@ -222,5 +214,9 @@ class Chef
end
cmp
end
+
+ def map
+ @map ||= {}
+ end
end
end
diff --git a/lib/chef/osc_user.rb b/lib/chef/osc_user.rb
deleted file mode 100644
index 52bfd11108..0000000000
--- a/lib/chef/osc_user.rb
+++ /dev/null
@@ -1,194 +0,0 @@
-#
-# Author:: Steven Danna (steve@opscode.com)
-# Copyright:: Copyright 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/config'
-require 'chef/mixin/params_validate'
-require 'chef/mixin/from_file'
-require 'chef/mash'
-require 'chef/json_compat'
-require 'chef/search/query'
-
-# TODO
-# DEPRECATION NOTE
-# This class was previously Chef::User. It is the code to support the User object
-# corrosponding to the Open Source Chef Server 11 and only still exists to support
-# users still on OSC 11.
-#
-# Chef::User now supports Chef Server 12.
-#
-# New development should occur in Chef::User.
-# This file and corrosponding osc_user knife files
-# should be removed once client support for Open Source Chef Server 11 expires.
-class Chef
- class OscUser
-
- include Chef::Mixin::FromFile
- include Chef::Mixin::ParamsValidate
-
- def initialize
- @name = ''
- @public_key = nil
- @private_key = nil
- @password = nil
- @admin = false
- end
-
- def name(arg=nil)
- set_or_return(:name, arg,
- :regex => /^[a-z0-9\-_]+$/)
- end
-
- def admin(arg=nil)
- set_or_return(:admin,
- arg, :kind_of => [TrueClass, FalseClass])
- end
-
- def public_key(arg=nil)
- set_or_return(:public_key,
- arg, :kind_of => String)
- end
-
- def private_key(arg=nil)
- set_or_return(:private_key,
- arg, :kind_of => String)
- end
-
- def password(arg=nil)
- set_or_return(:password,
- arg, :kind_of => String)
- end
-
- def to_hash
- result = {
- "name" => @name,
- "public_key" => @public_key,
- "admin" => @admin
- }
- result["private_key"] = @private_key if @private_key
- result["password"] = @password if @password
- result
- end
-
- def to_json(*a)
- Chef::JSONCompat.to_json(to_hash, *a)
- end
-
- def destroy
- Chef::REST.new(Chef::Config[:chef_server_url]).delete_rest("users/#{@name}")
- end
-
- def create
- payload = {:name => self.name, :admin => self.admin, :password => self.password }
- payload[:public_key] = public_key if public_key
- new_user =Chef::REST.new(Chef::Config[:chef_server_url]).post_rest("users", payload)
- Chef::OscUser.from_hash(self.to_hash.merge(new_user))
- end
-
- def update(new_key=false)
- payload = {:name => name, :admin => admin}
- payload[:private_key] = new_key if new_key
- payload[:password] = password if password
- updated_user = Chef::REST.new(Chef::Config[:chef_server_url]).put_rest("users/#{name}", payload)
- Chef::OscUser.from_hash(self.to_hash.merge(updated_user))
- end
-
- def save(new_key=false)
- begin
- create
- rescue Net::HTTPServerException => e
- if e.response.code == "409"
- update(new_key)
- else
- raise e
- end
- end
- end
-
- def reregister
- r = Chef::REST.new(Chef::Config[:chef_server_url])
- reregistered_self = r.put_rest("users/#{name}", { :name => name, :admin => admin, :private_key => true })
- private_key(reregistered_self["private_key"])
- self
- end
-
- def to_s
- "user[#{@name}]"
- end
-
- def inspect
- "Chef::OscUser name:'#{name}' admin:'#{admin.inspect}'" +
- "public_key:'#{public_key}' private_key:#{private_key}"
- end
-
- # Class Methods
-
- def self.from_hash(user_hash)
- user = Chef::OscUser.new
- user.name user_hash['name']
- user.private_key user_hash['private_key'] if user_hash.key?('private_key')
- user.password user_hash['password'] if user_hash.key?('password')
- user.public_key user_hash['public_key']
- user.admin user_hash['admin']
- user
- end
-
- def self.from_json(json)
- Chef::OscUser.from_hash(Chef::JSONCompat.from_json(json))
- end
-
- class << self
- alias_method :json_create, :from_json
- end
-
- def self.list(inflate=false)
- response = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest('users')
- users = if response.is_a?(Array)
- transform_ohc_list_response(response) # OHC/OPC
- else
- response # OSC
- end
- if inflate
- users.inject({}) do |user_map, (name, _url)|
- user_map[name] = Chef::OscUser.load(name)
- user_map
- end
- else
- users
- end
- end
-
- def self.load(name)
- response = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("users/#{name}")
- Chef::OscUser.from_hash(response)
- end
-
- # Gross. Transforms an API response in the form of:
- # [ { "user" => { "username" => USERNAME }}, ...]
- # into the form
- # { "USERNAME" => "URI" }
- def self.transform_ohc_list_response(response)
- new_response = Hash.new
- response.each do |u|
- name = u['user']['username']
- new_response[name] = Chef::Config[:chef_server_url] + "/users/#{name}"
- end
- new_response
- end
-
- private_class_method :transform_ohc_list_response
- end
-end
diff --git a/lib/chef/platform/handler_map.rb b/lib/chef/platform/handler_map.rb
new file mode 100644
index 0000000000..001eb3dc8f
--- /dev/null
+++ b/lib/chef/platform/handler_map.rb
@@ -0,0 +1,45 @@
+#
+# Author:: John Keiser (<jkeiser@chef.io>)
+# Copyright:: Copyright (c) 2015 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/node_map'
+
+class Chef
+ class Platform
+ class HandlerMap < Chef::NodeMap
+ #
+ # "provides" lines with identical filters sort by class name (ascending).
+ #
+ def compare_matchers(key, new_matcher, matcher)
+ cmp = super
+ if cmp == 0
+ # Sort by class name (ascending) as well, if all other properties
+ # are exactly equal
+ if new_matcher[:value].is_a?(Class) && !new_matcher[:override]
+ cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:value].name }
+ if cmp < 0
+ Chef::Log.warn "You are overriding #{key} on #{new_matcher[:filters].inspect} with #{new_matcher[:value].inspect}: used to be #{matcher[:value].inspect}. Use override: true if this is what you intended."
+ elsif cmp > 0
+ Chef::Log.warn "You declared a new resource #{new_matcher[:value].inspect} for resource #{key}, but it comes alphabetically after #{matcher[:value].inspect} and has the same filters (#{new_matcher[:filters].inspect}), so it will not be used. Use override: true if you want to use it for #{key}."
+ end
+ end
+ end
+ cmp
+ end
+ end
+ end
+end
diff --git a/lib/chef/platform/priority_map.rb b/lib/chef/platform/priority_map.rb
index d559eece78..0b050deb59 100644
--- a/lib/chef/platform/priority_map.rb
+++ b/lib/chef/platform/priority_map.rb
@@ -1,3 +1,21 @@
+#
+# Author:: John Keiser (<jkeiser@chef.io>)
+# Copyright:: Copyright (c) 2015 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/node_map'
class Chef
@@ -6,7 +24,7 @@ class Chef
def priority(resource_name, priority_array, *filter)
set_priority_array(resource_name.to_sym, priority_array, *filter)
end
-
+
# @api private
def get_priority_array(node, key)
get(node, key)
@@ -18,37 +36,6 @@ class Chef
set(key, priority_array, *filter, &block)
priority_array
end
-
- # @api private
- def list_handlers(node, key, **filters)
- list(node, key, **filters).flatten(1).uniq
- end
-
- #
- # Priority maps have one extra precedence: priority arrays override "provides,"
- # and "provides" lines with identical filters sort by class name (ascending).
- #
- def compare_matchers(key, new_matcher, matcher)
- # Priority arrays come before "provides"
- if new_matcher[:value].is_a?(Array) != matcher[:value].is_a?(Array)
- return new_matcher[:value].is_a?(Array) ? -1 : 1
- end
-
- cmp = super
- if cmp == 0
- # Sort by class name (ascending) as well, if all other properties
- # are exactly equal
- if new_matcher[:value].is_a?(Class) && !new_matcher[:override]
- cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:value].name }
- if cmp < 0
- Chef::Log.warn "You are overriding #{key} on #{new_matcher[:filters].inspect} with #{new_matcher[:value].inspect}: used to be #{matcher[:value].inspect}. Use override: true if this is what you intended."
- elsif cmp > 0
- Chef::Log.warn "You declared a new resource #{new_matcher[:value].inspect} for resource #{key}, but it comes alphabetically after #{matcher[:value].inspect} and has the same filters (#{new_matcher[:filters].inspect}), so it will not be used. Use override: true if you want to use it for #{key}."
- end
- end
- end
- cmp
- end
end
end
end
diff --git a/lib/chef/platform/provider_handler_map.rb b/lib/chef/platform/provider_handler_map.rb
new file mode 100644
index 0000000000..4549d7994e
--- /dev/null
+++ b/lib/chef/platform/provider_handler_map.rb
@@ -0,0 +1,29 @@
+#
+# Author:: John Keiser (<jkeiser@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'singleton'
+require 'chef/platform/handler_map'
+
+class Chef
+ class Platform
+ # @api private
+ class ProviderHandlerMap < Chef::Platform::HandlerMap
+ include Singleton
+ end
+ end
+end
diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb
index 4278b8d24f..38dd0e38af 100644
--- a/lib/chef/platform/provider_mapping.rb
+++ b/lib/chef/platform/provider_mapping.rb
@@ -176,7 +176,7 @@ class Chef
platform_provider(platform, version, resource_type) ||
resource_matching_provider(platform, version, resource_type)
- raise ArgumentError, "Cannot find a provider for #{resource_type} on #{platform} version #{version}" if provider_klass.nil?
+ raise Chef::Exceptions::ProviderNotFound, "Cannot find a provider for #{resource_type} on #{platform} version #{version}" if provider_klass.nil?
provider_klass
end
@@ -197,7 +197,7 @@ class Chef
def resource_matching_provider(platform, version, resource_type)
if resource_type.kind_of?(Chef::Resource)
- class_name = resource_type.class.name ? resource_type.class.name.split('::').last :
+ class_name = resource_type.class.name ? resource_type.class.name.split('::').last :
convert_to_class_name(resource_type.resource_name.to_s)
begin
diff --git a/lib/chef/platform/rebooter.rb b/lib/chef/platform/rebooter.rb
index b46f0e394c..b78ac38f0c 100644
--- a/lib/chef/platform/rebooter.rb
+++ b/lib/chef/platform/rebooter.rb
@@ -32,7 +32,7 @@ class Chef
cmd = if Chef::Platform.windows?
# should this do /f as well? do we then need a minimum delay to let apps quit?
- "shutdown /r /t #{reboot_info[:delay_mins]} /c \"#{reboot_info[:reason]}\""
+ "shutdown /r /t #{reboot_info[:delay_mins]*60} /c \"#{reboot_info[:reason]}\""
else
# probably Linux-only.
"shutdown -r +#{reboot_info[:delay_mins]} \"#{reboot_info[:reason]}\""
diff --git a/lib/chef/platform/resource_handler_map.rb b/lib/chef/platform/resource_handler_map.rb
new file mode 100644
index 0000000000..27a7bb1342
--- /dev/null
+++ b/lib/chef/platform/resource_handler_map.rb
@@ -0,0 +1,29 @@
+#
+# Author:: John Keiser (<jkeiser@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'singleton'
+require 'chef/platform/handler_map'
+
+class Chef
+ class Platform
+ # @api private
+ class ResourceHandlerMap < Chef::Platform::HandlerMap
+ include Singleton
+ end
+ end
+end
diff --git a/lib/chef/platform/resource_priority_map.rb b/lib/chef/platform/resource_priority_map.rb
index aa57e3ddf0..5cc86fd2e7 100644
--- a/lib/chef/platform/resource_priority_map.rb
+++ b/lib/chef/platform/resource_priority_map.rb
@@ -6,12 +6,6 @@ class Chef
# @api private
class ResourcePriorityMap < Chef::Platform::PriorityMap
include Singleton
-
- # @api private
- def get_priority_array(node, resource_name, canonical: nil)
- super(node, resource_name.to_sym, canonical: canonical)
- end
-
end
end
end
diff --git a/lib/chef/property.rb b/lib/chef/property.rb
new file mode 100644
index 0000000000..408090d37b
--- /dev/null
+++ b/lib/chef/property.rb
@@ -0,0 +1,539 @@
+#
+# Author:: John Keiser <jkeiser@chef.io>
+# Copyright:: Copyright (c) 2015 John Keiser.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/exceptions'
+require 'chef/delayed_evaluator'
+
+class Chef
+ #
+ # Type and validation information for a property on a resource.
+ #
+ # A property named "x" manipulates the "@x" instance variable on a
+ # resource. The *presence* of the variable (`instance_variable_defined?(@x)`)
+ # tells whether the variable is defined; it may have any actual value,
+ # constrained only by validation.
+ #
+ # Properties may have validation, defaults, and coercion, and have full
+ # support for lazy values.
+ #
+ # @see Chef::Resource.property
+ # @see Chef::DelayedEvaluator
+ #
+ class Property
+ #
+ # Create a reusable property type that can be used in multiple properties
+ # in different resources.
+ #
+ # @param options [Hash<Symbol,Object>] Validation options. See Chef::Resource.property for
+ # the list of options.
+ #
+ # @example
+ # Property.derive(default: 'hi')
+ #
+ def self.derive(**options)
+ new(**options)
+ end
+
+ #
+ # Create a new property.
+ #
+ # @param options [Hash<Symbol,Object>] Property options, including
+ # control options here, as well as validation options (see
+ # Chef::Mixin::ParamsValidate#validate for a description of validation
+ # options).
+ # @option options [Symbol] :name The name of this property.
+ # @option options [Class] :declared_in The class this property comes from.
+ # @option options [Symbol] :instance_variable_name The instance variable
+ # tied to this property. Must include a leading `@`. Defaults to `@<name>`.
+ # `nil` means the property is opaque and not tied to a specific instance
+ # variable.
+ # @option options [Boolean] :desired_state `true` if this property is part of desired
+ # state. Defaults to `true`.
+ # @option options [Boolean] :identity `true` if this property is part of object
+ # identity. Defaults to `false`.
+ # @option options [Boolean] :name_property `true` if this
+ # property defaults to the same value as `name`. Equivalent to
+ # `default: lazy { name }`, except that #property_is_set? will
+ # return `true` if the property is set *or* if `name` is set.
+ # @option options [Object] :default The value this property
+ # will return if the user does not set one. If this is `lazy`, it will
+ # be run in the context of the instance (and able to access other
+ # properties) and cached. If not, the value will be frozen with Object#freeze
+ # to prevent users from modifying it in an instance.
+ # @option options [Proc] :coerce A proc which will be called to
+ # transform the user input to canonical form. The value is passed in,
+ # and the transformed value returned as output. Lazy values will *not*
+ # be passed to this method until after they are evaluated. Called in the
+ # context of the resource (meaning you can access other properties).
+ # @option options [Boolean] :required `true` if this property
+ # must be present; `false` otherwise. This is checked after the resource
+ # is fully initialized.
+ #
+ def initialize(**options)
+ options.each { |k,v| options[k.to_sym] = v if k.is_a?(String) }
+ options[:name_property] = options.delete(:name_attribute) unless options.has_key?(:name_property)
+ @options = options
+
+ if options.has_key?(:default)
+ options[:default] = options[:default].freeze
+ end
+ options[:name] = options[:name].to_sym if options[:name]
+ options[:instance_variable_name] = options[:instance_variable_name].to_sym if options[:instance_variable_name]
+ end
+
+ #
+ # The name of this property.
+ #
+ # @return [String]
+ #
+ def name
+ options[:name]
+ end
+
+ #
+ # The class this property was defined in.
+ #
+ # @return [Class]
+ #
+ def declared_in
+ options[:declared_in]
+ end
+
+ #
+ # The instance variable associated with this property.
+ #
+ # Defaults to `@<name>`
+ #
+ # @return [Symbol]
+ #
+ def instance_variable_name
+ if options.has_key?(:instance_variable_name)
+ options[:instance_variable_name]
+ elsif name
+ :"@#{name}"
+ end
+ end
+
+ #
+ # The raw default value for this resource.
+ #
+ # Does not coerce or validate the default. Does not evaluate lazy values.
+ #
+ # Defaults to `lazy { name }` if name_property is true; otherwise defaults to
+ # `nil`
+ #
+ def default
+ return options[:default] if options.has_key?(:default)
+ return Chef::DelayedEvaluator.new { name } if name_property?
+ nil
+ end
+
+ #
+ # Whether this is part of the resource's natural identity or not.
+ #
+ # @return [Boolean]
+ #
+ def identity?
+ options[:identity]
+ end
+
+ #
+ # Whether this is part of desired state or not.
+ #
+ # Defaults to true.
+ #
+ # @return [Boolean]
+ #
+ def desired_state?
+ return true if !options.has_key?(:desired_state)
+ options[:desired_state]
+ end
+
+ #
+ # Whether this is name_property or not.
+ #
+ # @return [Boolean]
+ #
+ def name_property?
+ options[:name_property]
+ end
+
+ #
+ # Whether this property has a default value.
+ #
+ # @return [Boolean]
+ #
+ def has_default?
+ options.has_key?(:default) || name_property?
+ end
+
+ #
+ # Whether this property is required or not.
+ #
+ # @return [Boolean]
+ #
+ def required?
+ options[:required]
+ end
+
+ #
+ # Validation options. (See Chef::Mixin::ParamsValidate#validate.)
+ #
+ # @return [Hash<Symbol,Object>]
+ #
+ def validation_options
+ @validation_options ||= options.reject { |k,v|
+ [:declared_in,:name,:instance_variable_name,:desired_state,:identity,:default,:name_property,:coerce,:required].include?(k)
+ }
+ end
+
+ #
+ # Handle the property being called.
+ #
+ # The base implementation does the property get-or-set:
+ #
+ # ```ruby
+ # resource.myprop # get
+ # resource.myprop value # set
+ # ```
+ #
+ # Subclasses may implement this with any arguments they want, as long as
+ # the corresponding DSL calls it correctly.
+ #
+ # @param resource [Chef::Resource] The resource to get the property from.
+ # @param value The value to set (or NOT_PASSED if it is a get).
+ #
+ # @return The current value of the property. If it is a `set`, lazy values
+ # will be returned without running, validating or coercing. If it is a
+ # `get`, the non-lazy, coerced, validated value will always be returned.
+ #
+ def call(resource, value=NOT_PASSED)
+ if value == NOT_PASSED
+ return get(resource)
+ end
+
+ # myprop nil is sometimes a get (backcompat)
+ if value.nil? && !explicitly_accepts_nil?(resource)
+ # If you say "my_property nil" and the property explicitly accepts
+ # nil values, we consider this a get.
+ Chef::Log.deprecation("#{name} nil currently does not overwrite the value of #{name}. This will change in Chef 13, and the value will be set to nil instead. Please change your code to explicitly accept nil using \"property :#{name}, [MyType, nil]\", or stop setting this value to nil.")
+ return get(resource)
+ end
+
+ # Anything else (myprop value) is a set
+ set(resource, value)
+ end
+
+ #
+ # Get the property value from the resource, handling lazy values,
+ # defaults, and validation.
+ #
+ # - If the property's value is lazy, it is evaluated, coerced and validated.
+ # - If the property has no value, and is required, raises ValidationFailed.
+ # - If the property has no value, but has a lazy default, it is evaluated,
+ # coerced and validated. If the evaluated value is frozen, the resulting
+ # - If the property has no value, but has a default, the default value
+ # will be returned and frozen. If the default value is lazy, it will be
+ # evaluated, coerced and validated, and the result stored in the property.
+ # - If the property has no value, but is name_property, `resource.name`
+ # is retrieved, coerced, validated and stored in the property.
+ # - Otherwise, `nil` is returned.
+ #
+ # @param resource [Chef::Resource] The resource to get the property from.
+ #
+ # @return The value of the property.
+ #
+ # @raise Chef::Exceptions::ValidationFailed If the value is invalid for
+ # this property, or if the value is required and not set.
+ #
+ def get(resource)
+ if is_set?(resource)
+ value = get_value(resource)
+ if value.is_a?(DelayedEvaluator)
+ value = exec_in_resource(resource, value)
+ value = coerce(resource, value)
+ validate(resource, value)
+ end
+ value
+
+ else
+ if has_default?
+ value = default
+ if value.is_a?(DelayedEvaluator)
+ value = exec_in_resource(resource, value)
+ end
+
+ value = coerce(resource, value)
+
+ # We don't validate defaults
+
+ # If the value is mutable (non-frozen), we set it on the instance
+ # so that people can mutate it. (All constant default values are
+ # frozen.)
+ if !value.frozen?
+ set_value(resource, value)
+ end
+
+ value
+
+ elsif required?
+ raise Chef::Exceptions::ValidationFailed, "#{name} is required"
+ end
+ end
+ end
+
+ #
+ # Set the value of this property in the given resource.
+ #
+ # Non-lazy values are coerced and validated before being set. Coercion
+ # and validation of lazy values is delayed until they are first retrieved.
+ #
+ # @param resource [Chef::Resource] The resource to set this property in.
+ # @param value The value to set.
+ #
+ # @return The value that was set, after coercion (if lazy, still returns
+ # the lazy value)
+ #
+ # @raise Chef::Exceptions::ValidationFailed If the value is invalid for
+ # this property.
+ #
+ def set(resource, value)
+ unless value.is_a?(DelayedEvaluator)
+ value = coerce(resource, value)
+ validate(resource, value)
+ end
+ set_value(resource, value)
+ end
+
+ #
+ # Find out whether this property has been set.
+ #
+ # This will be true if:
+ # - The user explicitly set the value
+ # - The property has a default, and the value was retrieved.
+ #
+ # From this point of view, it is worth looking at this as "what does the
+ # user think this value should be." In order words, if the user grabbed
+ # the value, even if it was a default, they probably based calculations on
+ # it. If they based calculations on it and the value changes, the rest of
+ # the world gets inconsistent.
+ #
+ # @param resource [Chef::Resource] The resource to get the property from.
+ #
+ # @return [Boolean]
+ #
+ def is_set?(resource)
+ value_is_set?(resource)
+ end
+
+ #
+ # Reset the value of this property so that is_set? will return false and the
+ # default will be returned in the future.
+ #
+ # @param resource [Chef::Resource] The resource to get the property from.
+ #
+ def reset(resource)
+ reset_value(resource)
+ end
+
+ #
+ # Coerce an input value into canonical form for the property.
+ #
+ # After coercion, the value is suitable for storage in the resource.
+ # You must validate values after coercion, however.
+ #
+ # Does no special handling for lazy values.
+ #
+ # @param resource [Chef::Resource] The resource we're coercing against
+ # (to provide context for the coerce).
+ # @param value The value to coerce.
+ #
+ # @return The coerced value.
+ #
+ # @raise Chef::Exceptions::ValidationFailed If the value is invalid for
+ # this property.
+ #
+ def coerce(resource, value)
+ if options.has_key?(:coerce)
+ value = exec_in_resource(resource, options[:coerce], value)
+ end
+ value
+ end
+
+ #
+ # Validate a value.
+ #
+ # Calls Chef::Mixin::ParamsValidate#validate with #validation_options as
+ # options.
+ #
+ # @param resource [Chef::Resource] The resource we're validating against
+ # (to provide context for the validate).
+ # @param value The value to validate.
+ #
+ # @raise Chef::Exceptions::ValidationFailed If the value is invalid for
+ # this property.
+ #
+ def validate(resource, value)
+ resource.validate({ name => value }, { name => validation_options })
+ end
+
+ #
+ # Derive a new Property that is just like this one, except with some added or
+ # changed options.
+ #
+ # @param options [Hash<Symbol,Object>] List of options that would be passed
+ # to #initialize.
+ #
+ # @return [Property] The new property type.
+ #
+ def derive(**modified_options)
+ Property.new(**options.merge(**modified_options))
+ end
+
+ #
+ # Emit the DSL for this property into the resource class (`declared_in`).
+ #
+ # Creates a getter and setter for the property.
+ #
+ def emit_dsl
+ # We don't create the getter/setter if it's a custom property; we will
+ # be using the existing getter/setter to manipulate it instead.
+ return if !instance_variable_name
+
+ # We prefer this form because the property name won't show up in the
+ # stack trace if you use `define_method`.
+ declared_in.class_eval <<-EOM, __FILE__, __LINE__+1
+ def #{name}(value=NOT_PASSED)
+ self.class.properties[#{name.inspect}].call(self, value)
+ end
+ def #{name}=(value)
+ self.class.properties[#{name.inspect}].set(self, value)
+ end
+ EOM
+ rescue SyntaxError
+ # If the name is not a valid ruby name, we use define_method.
+ resource_class.define_method(name) do |value=NOT_PASSED|
+ self.class.properties[name].call(self, value)
+ end
+ resource_class.define_method("#{name}=") do |value|
+ self.class.properties[name].set(self, value)
+ end
+ end
+
+ protected
+
+ #
+ # The options this Property will use for get/set behavior and validation.
+ #
+ # @see #initialize for a list of valid options.
+ #
+ attr_reader :options
+
+ #
+ # Find out whether this type accepts nil explicitly.
+ #
+ # A type accepts nil explicitly if "is" allows nil, it validates as nil, *and* is not simply
+ # an empty type.
+ #
+ # These examples accept nil explicitly:
+ # ```ruby
+ # property :a, [ String, nil ]
+ # property :a, [ String, NilClass ]
+ # property :a, [ String, proc { |v| v.nil? } ]
+ # ```
+ #
+ # This does not (because the "is" doesn't exist or doesn't have nil):
+ #
+ # ```ruby
+ # property :x, String
+ # ```
+ #
+ # These do not, even though nil would validate fine (because they do not
+ # have "is"):
+ #
+ # ```ruby
+ # property :a
+ # property :a, equal_to: [ 1, 2, 3, nil ]
+ # property :a, kind_of: [ String, NilClass ]
+ # property :a, respond_to: [ ]
+ # property :a, callbacks: { "a" => proc { |v| v.nil? } }
+ # ```
+ #
+ # @param resource [Chef::Resource] The resource we're coercing against
+ # (to provide context for the coerce).
+ #
+ # @return [Boolean] Whether this value explicitly accepts nil.
+ #
+ # @api private
+ def explicitly_accepts_nil?(resource)
+ options.has_key?(:is) && resource.send(:_pv_is, { name => nil }, name, options[:is], raise_error: false)
+ end
+
+ def get_value(resource)
+ if instance_variable_name
+ resource.instance_variable_get(instance_variable_name)
+ else
+ resource.send(name)
+ end
+ end
+
+ def set_value(resource, value)
+ if instance_variable_name
+ resource.instance_variable_set(instance_variable_name, value)
+ else
+ resource.send(name, value)
+ end
+ end
+
+ def value_is_set?(resource)
+ if instance_variable_name
+ resource.instance_variable_defined?(instance_variable_name)
+ else
+ true
+ end
+ end
+
+ def reset_value(resource)
+ if instance_variable_name
+ if value_is_set?(resource)
+ resource.remove_instance_variable(instance_variable_name)
+ end
+ else
+ raise ArgumentError, "Property #{name} has no instance variable defined and cannot be reset"
+ end
+ end
+
+ def exec_in_resource(resource, proc, *args)
+ if resource
+ if proc.arity > args.size
+ value = proc.call(resource, *args)
+ else
+ value = resource.instance_exec(*args, &proc)
+ end
+ else
+ value = proc.call
+ end
+
+ if value.is_a?(DelayedEvaluator)
+ value = coerce(resource, value)
+ validate(resource, value)
+ end
+ value
+ end
+ end
+end
diff --git a/lib/chef/provider.rb b/lib/chef/provider.rb
index 280277d947..dcfc92645b 100644
--- a/lib/chef/provider.rb
+++ b/lib/chef/provider.rb
@@ -176,7 +176,7 @@ class Chef
end
def self.provides(short_name, opts={}, &block)
- Chef.provider_priority_map.set(short_name, self, opts, &block)
+ Chef.provider_handler_map.set(short_name, self, opts, &block)
end
def self.provides?(node, resource)
diff --git a/lib/chef/provider/dsc_resource.rb b/lib/chef/provider/dsc_resource.rb
index 5fa84a21e9..379369ba6e 100644
--- a/lib/chef/provider/dsc_resource.rb
+++ b/lib/chef/provider/dsc_resource.rb
@@ -53,7 +53,7 @@ class Chef
requirements.assert(:run) do |a|
a.assertion { supports_dsc_invoke_resource? }
err = ["You must have Powershell version >= 5.0.10018.0 to use dsc_resource."]
- a.failure_message Chef::Exceptions::NoProviderAvailable,
+ a.failure_message Chef::Exceptions::ProviderNotFound,
err
a.whyrun err + ["Assuming a previous resource installs Powershell 5.0.10018.0 or higher."]
a.block_action!
@@ -63,7 +63,7 @@ class Chef
meta_configuration['RefreshMode'] == 'Disabled'
}
err = ["The LCM must have its RefreshMode set to Disabled. "]
- a.failure_message Chef::Exceptions::NoProviderAvailable, err.join(' ')
+ a.failure_message Chef::Exceptions::ProviderNotFound, err.join(' ')
a.whyrun err + ["Assuming a previous resource sets the RefreshMode."]
a.block_action!
end
diff --git a/lib/chef/provider/dsc_script.rb b/lib/chef/provider/dsc_script.rb
index a75e68a475..b2432132b7 100644
--- a/lib/chef/provider/dsc_script.rb
+++ b/lib/chef/provider/dsc_script.rb
@@ -70,7 +70,7 @@ class Chef
"Powershell 4.0 or higher was not detected on your system and is required to use the dsc_script resource.",
]
a.assertion { supports_dsc? }
- a.failure_message Chef::Exceptions::NoProviderAvailable, err.join(' ')
+ a.failure_message Chef::Exceptions::ProviderNotFound, err.join(' ')
a.whyrun err + ["Assuming a previous resource installs Powershell 4.0 or higher."]
a.block_action!
end
diff --git a/lib/chef/provider/mount/aix.rb b/lib/chef/provider/mount/aix.rb
index 4ad7b24c15..510dfde46d 100644
--- a/lib/chef/provider/mount/aix.rb
+++ b/lib/chef/provider/mount/aix.rb
@@ -32,7 +32,7 @@ class Chef
@new_resource.options.clear
end
if @new_resource.fstype == "auto"
- @new_resource.fstype = nil
+ @new_resource.send(:clear_fstype)
end
end
diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb
index 9d534ec414..880104bff7 100644
--- a/lib/chef/provider/package.rb
+++ b/lib/chef/provider/package.rb
@@ -142,7 +142,7 @@ class Chef
def action_remove
if removing_package?
description = @new_resource.version ? "version #{@new_resource.version} of " : ""
- converge_by("remove #{description} package #{@current_resource.package_name}") do
+ converge_by("remove #{description}package #{@current_resource.package_name}") do
remove_package(@current_resource.package_name, @new_resource.version)
Chef::Log.info("#{@new_resource} removed")
end
@@ -491,37 +491,6 @@ class Chef
end
end
- # Set provider priority
- require 'chef/chef_class'
- require 'chef/provider/package/dpkg'
- require 'chef/provider/package/homebrew'
- require 'chef/provider/package/macports'
- require 'chef/provider/package/apt'
- require 'chef/provider/package/yum'
- require 'chef/provider/package/zypper'
- require 'chef/provider/package/portage'
- require 'chef/provider/package/pacman'
- require 'chef/provider/package/ips'
- require 'chef/provider/package/solaris'
- require 'chef/provider/package/smartos'
- require 'chef/provider/package/aix'
- require 'chef/provider/package/paludis'
-
- Chef.set_provider_priority_array :package, [ Homebrew, Macports ], os: "darwin"
-
- Chef.set_provider_priority_array :package, Apt, platform_family: "debian"
- Chef.set_provider_priority_array :package, Yum, platform_family: %w(rhel fedora)
- Chef.set_provider_priority_array :package, Zypper, platform_family: "suse"
- Chef.set_provider_priority_array :package, Portage, platform: "gentoo"
- Chef.set_provider_priority_array :package, Pacman, platform: "arch"
- Chef.set_provider_priority_array :package, Ips, platform: %w(openindiana opensolaris omnios solaris2)
- Chef.set_provider_priority_array :package, Solaris, platform: "nexentacore"
- Chef.set_provider_priority_array :package, Solaris, platform: "solaris2", platform_version: '< 5.11'
-
- Chef.set_provider_priority_array :package, SmartOS, platform: "smartos"
- Chef.set_provider_priority_array :package, Aix, platform: "aix"
- Chef.set_provider_priority_array :package, Paludis, platform: "exherbo"
-
private
def shell_out_with_timeout(*command_args)
diff --git a/lib/chef/provider/package/aix.rb b/lib/chef/provider/package/aix.rb
index b97db9d061..5165f4b4ea 100644
--- a/lib/chef/provider/package/aix.rb
+++ b/lib/chef/provider/package/aix.rb
@@ -26,6 +26,7 @@ class Chef
class Package
class Aix < Chef::Provider::Package
+ provides :package, os: "aix"
provides :bff_package, os: "aix"
include Chef::Mixin::GetSourceFromPackage
diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb
index bd6ed283bf..e109c9966a 100644
--- a/lib/chef/provider/package/apt.rb
+++ b/lib/chef/provider/package/apt.rb
@@ -25,6 +25,7 @@ class Chef
class Package
class Apt < Chef::Provider::Package
+ provides :package, platform_family: "debian"
provides :apt_package, os: "linux"
# return [Hash] mapping of package name to Boolean value
diff --git a/lib/chef/provider/package/homebrew.rb b/lib/chef/provider/package/homebrew.rb
index beede1c916..e5c45f0a62 100644
--- a/lib/chef/provider/package/homebrew.rb
+++ b/lib/chef/provider/package/homebrew.rb
@@ -26,6 +26,7 @@ class Chef
class Package
class Homebrew < Chef::Provider::Package
+ provides :package, os: "darwin", override: true
provides :homebrew_package
include Chef::Mixin::HomebrewUser
diff --git a/lib/chef/provider/package/ips.rb b/lib/chef/provider/package/ips.rb
index 4d7f4a3583..96c2e711d4 100644
--- a/lib/chef/provider/package/ips.rb
+++ b/lib/chef/provider/package/ips.rb
@@ -27,6 +27,7 @@ class Chef
class Package
class Ips < Chef::Provider::Package
+ provides :package, platform: %w(openindiana opensolaris omnios solaris2)
provides :ips_package, os: "solaris2"
attr_accessor :virtual
diff --git a/lib/chef/provider/package/macports.rb b/lib/chef/provider/package/macports.rb
index e945211540..c7ea71ac8c 100644
--- a/lib/chef/provider/package/macports.rb
+++ b/lib/chef/provider/package/macports.rb
@@ -3,6 +3,7 @@ class Chef
class Package
class Macports < Chef::Provider::Package
+ provides :package, os: "darwin"
provides :macports_package
def load_current_resource
diff --git a/lib/chef/provider/package/openbsd.rb b/lib/chef/provider/package/openbsd.rb
index f231101390..83fc09c8ae 100644
--- a/lib/chef/provider/package/openbsd.rb
+++ b/lib/chef/provider/package/openbsd.rb
@@ -31,6 +31,7 @@ class Chef
class Openbsd < Chef::Provider::Package
provides :package, os: "openbsd"
+ provides :openbsd_package
include Chef::Mixin::ShellOut
include Chef::Mixin::GetSourceFromPackage
diff --git a/lib/chef/provider/package/pacman.rb b/lib/chef/provider/package/pacman.rb
index bf03e54656..01e3a9cc01 100644
--- a/lib/chef/provider/package/pacman.rb
+++ b/lib/chef/provider/package/pacman.rb
@@ -25,6 +25,7 @@ class Chef
class Package
class Pacman < Chef::Provider::Package
+ provides :package, platform: "arch"
provides :pacman_package, os: "linux"
def load_current_resource
diff --git a/lib/chef/provider/package/paludis.rb b/lib/chef/provider/package/paludis.rb
index 407e0d0110..2d6302515b 100644
--- a/lib/chef/provider/package/paludis.rb
+++ b/lib/chef/provider/package/paludis.rb
@@ -24,6 +24,7 @@ class Chef
class Package
class Paludis < Chef::Provider::Package
+ provides :package, platform: "exherbo"
provides :paludis_package, os: "linux"
def load_current_resource
diff --git a/lib/chef/provider/package/portage.rb b/lib/chef/provider/package/portage.rb
index 4ba0160bb0..95782a6774 100644
--- a/lib/chef/provider/package/portage.rb
+++ b/lib/chef/provider/package/portage.rb
@@ -25,6 +25,8 @@ class Chef
class Provider
class Package
class Portage < Chef::Provider::Package
+
+ provides :package, platform: "gentoo"
provides :portage_package
PACKAGE_NAME_PATTERN = %r{(?:([^/]+)/)?([^/]+)}
diff --git a/lib/chef/provider/package/smartos.rb b/lib/chef/provider/package/smartos.rb
index 0d5b801c96..71b8a9b9e1 100644
--- a/lib/chef/provider/package/smartos.rb
+++ b/lib/chef/provider/package/smartos.rb
@@ -29,6 +29,7 @@ class Chef
class SmartOS < Chef::Provider::Package
attr_accessor :is_virtual_package
+ provides :package, platform: "smartos"
provides :smartos_package, os: "solaris2", platform_family: "smartos"
def load_current_resource
diff --git a/lib/chef/provider/package/solaris.rb b/lib/chef/provider/package/solaris.rb
index 9b10403344..e62f37d27b 100644
--- a/lib/chef/provider/package/solaris.rb
+++ b/lib/chef/provider/package/solaris.rb
@@ -27,6 +27,8 @@ class Chef
include Chef::Mixin::GetSourceFromPackage
+ provides :package, platform: "nexentacore"
+ provides :package, platform: "solaris2", platform_version: '< 5.11'
provides :solaris_package, os: "solaris2"
# def initialize(*args)
diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb
index 85c2ba683c..e8c0483741 100644
--- a/lib/chef/provider/package/yum.rb
+++ b/lib/chef/provider/package/yum.rb
@@ -28,6 +28,7 @@ class Chef
class Package
class Yum < Chef::Provider::Package
+ provides :package, platform_family: %w(rhel fedora)
provides :yum_package, os: "linux"
class RPMUtils
diff --git a/lib/chef/provider/package/zypper.rb b/lib/chef/provider/package/zypper.rb
index c2a3ac4ba8..ac42304ffb 100644
--- a/lib/chef/provider/package/zypper.rb
+++ b/lib/chef/provider/package/zypper.rb
@@ -29,6 +29,7 @@ class Chef
class Package
class Zypper < Chef::Provider::Package
+ provides :package, platform_family: "suse"
provides :zypper_package, os: "linux"
def load_current_resource
diff --git a/lib/chef/provider/powershell_script.rb b/lib/chef/provider/powershell_script.rb
index ed44dee6ae..3c52db1f33 100644
--- a/lib/chef/provider/powershell_script.rb
+++ b/lib/chef/provider/powershell_script.rb
@@ -30,8 +30,8 @@ class Chef
end
def action_run
- valid_syntax = validate_script_syntax!
- super if valid_syntax
+ validate_script_syntax!
+ super
end
def flags
@@ -62,30 +62,43 @@ class Chef
def validate_script_syntax!
interpreter_arguments = default_interpreter_flags.join(' ')
Tempfile.open(['chef_powershell_script-user-code', '.ps1']) do | user_script_file |
- user_script_file.puts("{#{@new_resource.code}}")
- user_script_file.close
+ # Wrap the user's code in a PowerShell script block so that
+ # it isn't executed. However, syntactically invalid script
+ # in that block will still trigger a syntax error which is
+ # exactly what we want here -- verify the syntax without
+ # actually running the script.
+ user_code_wrapped_in_powershell_script_block = <<-EOH
+{
+ #{@new_resource.code}
+}
+EOH
+ user_script_file.puts user_code_wrapped_in_powershell_script_block
+ # A .close or explicit .flush required to ensure the file is
+ # written to the file system at this point, which is required since
+ # the intent is to execute the code just written to it.
+ user_script_file.close
validation_command = "\"#{interpreter}\" #{interpreter_arguments} -Command #{user_script_file.path}"
- # For consistency with other script resources, allow even syntax errors
- # to be suppressed if the returns attribute would have suppressed it
- # at converge.
- valid_returns = [0]
- specified_returns = @new_resource.returns.is_a?(Integer) ?
- [@new_resource.returns] :
- @new_resource.returns
- valid_returns.concat([1]) if specified_returns.include?(1)
-
- result = shell_out!(validation_command, {returns: valid_returns})
- result.exitstatus == 0
+ # Note that other script providers like bash allow syntax errors
+ # to be suppressed by setting 'returns' to a value that the
+ # interpreter would return as a status code in the syntax
+ # error case. We explicitly don't do this here -- syntax
+ # errors will not be suppressed, since doing so could make
+ # it harder for users to detect / debug invalid scripts.
+
+ # Therefore, the only return value for a syntactically valid
+ # script is 0. If an exception is raised by shellout, this
+ # means a non-zero return and thus a syntactically invalid script.
+ shell_out!(validation_command, {returns: [0]})
end
end
def default_interpreter_flags
- # 'Bypass' is preferable since it doesn't require user input confirmation
- # for files such as PowerShell modules downloaded from the
- # Internet. However, 'Bypass' is not supported prior to
- # PowerShell 3.0, so the fallback is 'Unrestricted'
+ # Execution policy 'Bypass' is preferable since it doesn't require
+ # user input confirmation for files such as PowerShell modules
+ # downloaded from the Internet. However, 'Bypass' is not supported
+ # prior to PowerShell 3.0, so the fallback is 'Unrestricted'
execution_policy = Chef::Platform.supports_powershell_execution_bypass?(run_context.node) ? 'Bypass' : 'Unrestricted'
[
diff --git a/lib/chef/provider/registry_key.rb b/lib/chef/provider/registry_key.rb
index cd62f7c56f..948fa6c63f 100644
--- a/lib/chef/provider/registry_key.rb
+++ b/lib/chef/provider/registry_key.rb
@@ -64,7 +64,7 @@ class Chef
def values_to_hash(values)
if values
- @name_hash = Hash[values.map { |val| [val[:name], val] }]
+ @name_hash = Hash[values.map { |val| [val[:name].downcase, val] }]
else
@name_hash = {}
end
@@ -100,8 +100,8 @@ class Chef
end
end
@new_resource.unscrubbed_values.each do |value|
- if @name_hash.has_key?(value[:name])
- current_value = @name_hash[value[:name]]
+ if @name_hash.has_key?(value[:name].downcase)
+ current_value = @name_hash[value[:name].downcase]
unless current_value[:type] == value[:type] && current_value[:data] == value[:data]
converge_by("set value #{value}") do
registry.set_value(@new_resource.key, value)
@@ -122,7 +122,7 @@ class Chef
end
end
@new_resource.unscrubbed_values.each do |value|
- unless @name_hash.has_key?(value[:name])
+ unless @name_hash.has_key?(value[:name].downcase)
converge_by("create value #{value}") do
registry.set_value(@new_resource.key, value)
end
@@ -133,7 +133,7 @@ class Chef
def action_delete
if registry.key_exists?(@new_resource.key)
@new_resource.unscrubbed_values.each do |value|
- if @name_hash.has_key?(value[:name])
+ if @name_hash.has_key?(value[:name].downcase)
converge_by("delete value #{value}") do
registry.delete_value(@new_resource.key, value)
end
diff --git a/lib/chef/provider/service.rb b/lib/chef/provider/service.rb
index 9c523b5e66..15dd72cb04 100644
--- a/lib/chef/provider/service.rb
+++ b/lib/chef/provider/service.rb
@@ -188,29 +188,11 @@ class Chef
require 'chef/provider/service/upstart'
require 'chef/provider/service/debian'
require 'chef/provider/service/invokercd'
- require 'chef/provider/service/freebsd'
- require 'chef/provider/service/openbsd'
- require 'chef/provider/service/solaris'
- require 'chef/provider/service/macosx'
-
- def self.os(os, *providers)
- Chef.set_provider_priority_array(:service, providers, os: os)
- end
- def self.platform_family(platform_family, *providers)
- Chef.set_provider_priority_array(:service, providers, platform_family: platform_family)
- end
-
- os %w(freebsd netbsd), Freebsd
- os %w(openbsd), Openbsd
- os %w(solaris2), Solaris
- os %w(darwin), Macosx
- os %w(linux), Systemd, Insserv, Redhat
-
- platform_family %w(arch), Systemd, Arch
- platform_family %w(gentoo), Systemd, Gentoo
- platform_family %w(debian), Systemd, Upstart, Insserv, Debian, Invokercd
- platform_family %w(rhel fedora suse), Systemd, Insserv, Redhat
+ Chef.set_provider_priority_array :service, [ Systemd, Arch ], platform_family: 'arch'
+ Chef.set_provider_priority_array :service, [ Systemd, Gentoo ], platform_family: 'gentoo'
+ Chef.set_provider_priority_array :service, [ Systemd, Upstart, Insserv, Debian, Invokercd ], platform_family: 'debian'
+ Chef.set_provider_priority_array :service, [ Systemd, Insserv, Redhat ], platform_family: %w(rhel fedora suse)
end
end
end
diff --git a/lib/chef/provider/service/debian.rb b/lib/chef/provider/service/debian.rb
index c67f3f05da..46c23fdd34 100644
--- a/lib/chef/provider/service/debian.rb
+++ b/lib/chef/provider/service/debian.rb
@@ -22,6 +22,8 @@ class Chef
class Provider
class Service
class Debian < Chef::Provider::Service::Init
+ provides :service, platform_family: 'debian'
+
UPDATE_RC_D_ENABLED_MATCHES = /\/rc[\dS].d\/S|not installed/i
UPDATE_RC_D_PRIORITIES = /\/rc([\dS]).d\/([SK])(\d\d)/i
diff --git a/lib/chef/provider/service/insserv.rb b/lib/chef/provider/service/insserv.rb
index 4534e33f32..2fd2eac38e 100644
--- a/lib/chef/provider/service/insserv.rb
+++ b/lib/chef/provider/service/insserv.rb
@@ -24,6 +24,8 @@ class Chef
class Service
class Insserv < Chef::Provider::Service::Init
+ provides :service, platform_family: %w(debian rhel fedora suse)
+
def self.provides?(node, resource)
super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:insserv)
end
diff --git a/lib/chef/provider/service/invokercd.rb b/lib/chef/provider/service/invokercd.rb
index fdf4cbc256..39022546b0 100644
--- a/lib/chef/provider/service/invokercd.rb
+++ b/lib/chef/provider/service/invokercd.rb
@@ -23,6 +23,8 @@ class Chef
class Service
class Invokercd < Chef::Provider::Service::Init
+ provides :service, platform_family: 'debian', override: true
+
def self.provides?(node, resource)
super && Chef::Platform::ServiceHelpers.service_resource_providers.include?(:invokercd)
end
diff --git a/lib/chef/provider/service/openbsd.rb b/lib/chef/provider/service/openbsd.rb
index d509ee10ff..173191e01c 100644
--- a/lib/chef/provider/service/openbsd.rb
+++ b/lib/chef/provider/service/openbsd.rb
@@ -26,7 +26,7 @@ class Chef
class Service
class Openbsd < Chef::Provider::Service::Init
- provides :service, os: [ "openbsd" ]
+ provides :service, os: "openbsd"
include Chef::Mixin::ShellOut
diff --git a/lib/chef/provider/service/redhat.rb b/lib/chef/provider/service/redhat.rb
index a8deb13aec..2330d88eb7 100644
--- a/lib/chef/provider/service/redhat.rb
+++ b/lib/chef/provider/service/redhat.rb
@@ -23,6 +23,8 @@ class Chef
class Service
class Redhat < Chef::Provider::Service::Init
+ provides :service, platform_family: %w(rhel fedora suse)
+
CHKCONFIG_ON = /\d:on/
CHKCONFIG_MISSING = /No such/
diff --git a/lib/chef/provider/service/upstart.rb b/lib/chef/provider/service/upstart.rb
index d8ce6af649..8809d1c708 100644
--- a/lib/chef/provider/service/upstart.rb
+++ b/lib/chef/provider/service/upstart.rb
@@ -25,6 +25,9 @@ class Chef
class Provider
class Service
class Upstart < Chef::Provider::Service::Simple
+
+ provides :service, platform_family: 'debian', override: true
+
UPSTART_STATE_FORMAT = /\w+ \(?(\w+)\)?[\/ ](\w+)/
def self.provides?(node, resource)
diff --git a/lib/chef/provider_resolver.rb b/lib/chef/provider_resolver.rb
index 5bfee343d1..8459bc1328 100644
--- a/lib/chef/provider_resolver.rb
+++ b/lib/chef/provider_resolver.rb
@@ -17,7 +17,7 @@
#
require 'chef/exceptions'
-require 'chef/platform/provider_priority_map'
+require 'chef/platform/priority_map'
class Chef
#
@@ -62,12 +62,47 @@ class Chef
maybe_chef_platform_lookup(resource)
end
+ # Does NOT call provides? on the resource (it is assumed this is being
+ # called *from* provides?).
def provided_by?(provider_class)
- prioritized_handlers.include?(provider_class)
+ potential_handlers.include?(provider_class)
+ end
+
+ def enabled_handlers
+ @enabled_handlers ||= potential_handlers.select { |handler| !overrode_provides?(handler) || handler.provides?(node, resource) }
+ end
+
+ # TODO deprecate this and allow actions to be passed as a filter to
+ # `provides` so we don't have to have two separate things.
+ # @api private
+ def supported_handlers
+ enabled_handlers.select { |handler| handler.supports?(resource, action) }
end
private
+ def potential_handlers
+ handler_map.list(node, resource.resource_name).uniq
+ end
+
+ # The list of handlers, with any in the priority_map moved to the front
+ def prioritized_handlers
+ @prioritized_handlers ||= begin
+ supported_handlers = self.supported_handlers
+ if supported_handlers.empty?
+ # if none of the providers specifically support the resource, we still need to pick one of the providers that are
+ # enabled on the node to handle the why-run use case. FIXME we should only do this in why-run mode then.
+ Chef::Log.debug "No providers responded true to `supports?` for action #{action} on resource #{resource}, falling back to enabled handlers so we can return something anyway."
+ supported_handlers = enabled_handlers
+ end
+
+ prioritized = priority_map.list(node, resource.resource_name).flatten(1)
+ prioritized &= supported_handlers # Filter the priority map by the actual enabled handlers
+ prioritized |= supported_handlers # Bring back any handlers that aren't in the priority map, at the *end* (ordered set)
+ prioritized
+ end
+ end
+
# if resource.provider is set, just return one of those objects
def maybe_explicit_provider(resource)
return nil unless resource.provider
@@ -78,27 +113,7 @@ class Chef
def maybe_dynamic_provider_resolution(resource, action)
Chef::Log.debug "Providers for generic #{resource.resource_name} resource enabled on node include: #{enabled_handlers}"
- # Get all the handlers in the priority bucket
- handlers = prioritized_handlers
-
- # Narrow it down to handlers that return `true` to `provides?`
- # TODO deprecate this and don't bother calling--the fact that they said
- # `provides` should be enough. But we need to do it right now because
- # some classes implement additional handling.
- enabled_handlers = prioritized_handlers.select { |handler| handler.provides?(node, resource) }
-
- # Narrow it down to handlers that return `true` to `supports?`
- # TODO deprecate this and allow actions to be passed as a filter to
- # `provides` so we don't have to have two separate things.
- supported_handlers = enabled_handlers.select { |handler| handler.supports?(resource, action) }
- if supported_handlers.empty?
- # if none of the providers specifically support the resource, we still need to pick one of the providers that are
- # enabled on the node to handle the why-run use case. FIXME we should only do this in why-run mode then.
- Chef::Log.debug "No providers responded true to `supports?` for action #{action} on resource #{resource}, falling back to enabled handlers so we can return something anyway."
- handler = enabled_handlers.first
- else
- handler = supported_handlers.first
- end
+ handler = prioritized_handlers.first
if handler
Chef::Log.debug "Provider for action #{action} on resource #{resource} is #{handler}"
@@ -114,13 +129,16 @@ class Chef
Chef::Platform.find_provider_for_node(node, resource)
end
- def provider_priority_map
- Chef::Platform::ProviderPriorityMap.instance
+ def priority_map
+ Chef.provider_priority_map
end
- def prioritized_handlers
- @prioritized_handlers ||=
- provider_priority_map.list_handlers(node, resource.resource_name).flatten(1).uniq
+ def handler_map
+ Chef.provider_handler_map
+ end
+
+ def overrode_provides?(handler)
+ handler.method(:provides?).owner != Chef::Provider.method(:provides?).owner
end
module Deprecated
@@ -129,33 +147,21 @@ class Chef
@providers ||= Chef::Provider.descendants
end
- # this cut looks at if the provider can handle the resource type on the node
def enabled_handlers
- @enabled_handlers ||=
- providers.select do |klass|
- # NB: this is different from resource_resolver which must pass a resource_name
- # FIXME: deprecate this and normalize on passing resource_name here
- klass.provides?(node, resource)
- end.sort {|a,b| a.to_s <=> b.to_s }
- end
-
- # this cut looks at if the provider can handle the specific resource and action
- def supported_handlers
- @supported_handlers ||=
- enabled_handlers.select do |klass|
- klass.supports?(resource, action)
- end
- end
-
- # If there are no providers for a DSL, we search through the
- def prioritized_handlers
- @prioritized_handlers ||= super || begin
- result = providers.select { |handler| handler.provides?(node, resource) }.sort_by(:name)
- if !result.empty?
- Chef::Log.deprecation("#{resource.resource_name.to_sym} is marked as providing DSL #{method_symbol}, but provides #{resource.resource_name.to_sym.inspect} was never called!")
- Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
+ @enabled_handlers ||= begin
+ handlers = super
+ if handlers.empty?
+ # Look through all providers, and find ones that return true to provides.
+ # Don't bother with ones that don't override provides?, since they
+ # would have been in enabled_handlers already if that were so. (It's a
+ # perf concern otherwise.)
+ handlers = providers.select { |handler| overrode_provides?(handler) && handler.provides?(node, resource) }
+ handlers.each do |handler|
+ Chef::Log.deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource.resource_name}, but provides #{resource.resource_name.inspect} was never called!")
+ Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
+ end
end
- result
+ handlers
end
end
end
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index 8d2532dac4..dca0033409 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -101,7 +101,7 @@ class Chef
# @param run_context The context of the Chef run. Corresponds to #run_context.
#
def initialize(name, run_context=nil)
- name(name)
+ name(name) unless name.nil?
@run_context = run_context
@noop = nil
@before = nil
@@ -130,37 +130,27 @@ class Chef
end
#
- # The name of this particular resource.
+ # The list of properties defined on this resource.
#
- # This special resource attribute is set automatically from the declaration
- # of the resource, e.g.
- #
- # execute 'Vitruvius' do
- # command 'ls'
- # end
+ # Everything defined with `property` is in this list.
#
- # Will set the name to "Vitruvius".
+ # @param include_superclass [Boolean] `true` to include properties defined
+ # on superclasses; `false` or `nil` to return the list of properties
+ # directly on this class.
#
- # This is also used in to_s to show the resource name, e.g. `execute[Vitruvius]`.
+ # @return [Hash<Symbol,Property>] The list of property names and types.
#
- # This is also used for resource notifications and subscribes in the same manner.
- #
- # This will coerce any object into a string via #to_s. Arrays are a special case
- # so that `package ["foo", "bar"]` becomes package[foo, bar] instead of the more
- # awkward `package[["foo", "bar"]]` that #to_s would produce.
- #
- # @param name [Object] The name to set, typically a String or Array
- # @return [String] The name of this Resource.
- #
- def name(name=nil)
- if !name.nil?
- if name.is_a?(Array)
- @name = name.join(', ')
+ def self.properties(include_superclass=true)
+ @properties ||= {}
+ if include_superclass
+ if superclass.respond_to?(:properties)
+ superclass.properties.merge(@properties)
else
- @name = name.to_s
+ @properties.dup
end
+ else
+ @properties
end
- @name
end
#
@@ -169,26 +159,24 @@ class Chef
# @param arg [Array[Symbol], Symbol] A list of actions (e.g. `:create`)
# @return [Array[Symbol]] the list of actions.
#
- attr_accessor :action
def action(arg=nil)
if arg
- if arg.is_a?(Array)
- arg = arg.map { |a| a.to_sym }
- else
- arg = arg.to_sym
- end
- Array(arg).each do |action|
+ arg = Array(arg).map(&:to_sym)
+ arg.each do |action|
validate(
{ action: action },
{ action: { kind_of: Symbol, equal_to: allowed_actions } }
)
end
- self.action = arg
+ @action = arg
else
@action
end
end
+ # Alias for normal assigment syntax.
+ alias_method :action=, :action
+
#
# Sets up a notification that will run a particular action on another resource
# if and when *this* resource is updated by an action.
@@ -477,13 +465,21 @@ class Chef
#
# Get the value of the state attributes in this resource as a hash.
#
+ # Does not include properties that are not set (unless they are identity
+ # properties).
+ #
# @return [Hash{Symbol => Object}] A Hash of attribute => value for the
# Resource class's `state_attrs`.
+ #
def state_for_resource_reporter
- self.class.state_attrs.inject({}) do |state_attrs, attr_name|
- state_attrs[attr_name] = send(attr_name)
- state_attrs
+ state = {}
+ state_properties = self.class.state_properties
+ state_properties.each do |property|
+ if property.identity? || property.is_set?(self)
+ state[property.name] = send(property.name)
+ end
end
+ state
end
#
@@ -496,17 +492,22 @@ class Chef
alias_method :state, :state_for_resource_reporter
#
- # The value of the identity attribute, if declared. Falls back to #name if
- # no identity attribute is declared.
+ # The value of the identity of this resource.
+ #
+ # - If there are no identity properties on the resource, `name` is returned.
+ # - If there is exactly one identity property on the resource, it is returned.
+ # - If there are more than one, they are returned in a hash.
#
- # @return The value of the identity attribute.
+ # @return [Object,Hash<Symbol,Object>] The identity of this resource.
#
def identity
- if identity_attr = self.class.identity_attr
- send(identity_attr)
- else
- name
+ result = {}
+ identity_properties = self.class.identity_properties
+ identity_properties.each do |property|
+ result[property.name] = send(property.name)
end
+ return result.values.first if identity_properties.size == 1
+ result
end
#
@@ -758,6 +759,10 @@ class Chef
# will return if the user does not set one. If this is `lazy`, it will
# be run in the context of the instance (and able to access other
# properties).
+ # @option options [Boolean] :desired_state `true` if this property is
+ # part of desired state. Defaults to `true`.
+ # @option options [Boolean] :identity `true` if this property
+ # is part of object identity. Defaults to `false`.
#
# @example Bare property
# property :x
@@ -774,29 +779,101 @@ class Chef
def self.property(name, type=NOT_PASSED, **options)
name = name.to_sym
- if type != NOT_PASSED
+ options[:instance_variable_name] = :"@#{name}" if !options.has_key?(:instance_variable_name)
+ options.merge!(name: name, declared_in: self)
+
+ if type == NOT_PASSED
+ # If a type is not passed, the property derives from the
+ # superclass property (if any)
+ if properties.has_key?(name)
+ property = properties[name].derive(**options)
+ else
+ property = property_type(**options)
+ end
+
+ # If a Property is specified, derive a new one from that.
+ elsif type.is_a?(Property) || (type.is_a?(Class) && type <= Property)
+ property = type.derive(**options)
+
+ # If a primitive type was passed, combine it with "is"
+ else
if options[:is]
options[:is] = ([ type ] + [ options[:is] ]).flatten(1)
else
options[:is] = type
end
+ property = property_type(**options)
end
- define_method(name) do |value=NOT_PASSED|
- set_or_return(name, value, options)
- end
- define_method("#{name}=") do |value|
- set_or_return(name, value, options)
- end
+ local_properties = properties(false)
+ local_properties[name] = property
+
+ property.emit_dsl
+ end
+
+ #
+ # Create a reusable property type that can be used in multiple properties
+ # in different resources.
+ #
+ # @param options [Hash<Symbol,Object>] Validation options. see #property for
+ # the list of options.
+ #
+ # @example
+ # property_type(default: 'hi')
+ #
+ def self.property_type(**options)
+ Property.derive(**options)
end
#
+ # The name of this particular resource.
+ #
+ # This special resource attribute is set automatically from the declaration
+ # of the resource, e.g.
+ #
+ # execute 'Vitruvius' do
+ # command 'ls'
+ # end
+ #
+ # Will set the name to "Vitruvius".
+ #
+ # This is also used in to_s to show the resource name, e.g. `execute[Vitruvius]`.
+ #
+ # This is also used for resource notifications and subscribes in the same manner.
+ #
+ # This will coerce any object into a string via #to_s. Arrays are a special case
+ # so that `package ["foo", "bar"]` becomes package[foo, bar] instead of the more
+ # awkward `package[["foo", "bar"]]` that #to_s would produce.
+ #
+ # @param name [Object] The name to set, typically a String or Array
+ # @return [String] The name of this Resource.
+ #
+ property :name, String, coerce: proc { |v| v.is_a?(Array) ? v.join(', ') : v.to_s }, desired_state: false
+
+ #
# Whether this property has been set (or whether it has a default that has
# been retrieved).
#
+ # @param name [Symbol] The name of the property.
+ # @return [Boolean] `true` if the property has been set.
+ #
def property_is_set?(name)
- name = name.to_sym
- instance_variable_defined?("@#{name}")
+ property = self.class.properties[name.to_sym]
+ raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
+ property.is_set?(self)
+ end
+
+ #
+ # Clear this property as if it had never been set. It will thereafter return
+ # the default.
+ # been retrieved).
+ #
+ # @param name [Symbol] The name of the property.
+ #
+ def reset_property(name)
+ property = self.class.properties[name.to_sym]
+ raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
+ property.reset(self)
end
#
@@ -810,46 +887,186 @@ class Chef
DelayedEvaluator.new(&block)
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.
+ #
+ # Get or set the list of desired state properties for this resource.
+ #
+ # State properties are properties that describe the desired state
+ # of the system, such as file permissions or ownership.
+ # In general, state properties are properties that could be populated by
+ # examining the state of the system (e.g., File.stat can tell you the
+ # permissions on an existing file). Contrarily, properties that are not
+ # "state properties" usually modify the way Chef itself behaves, for example
+ # by providing additional options for a package manager to use when
+ # installing a package.
#
# This list is used by the Chef client auditing system to extract
# information from resources to describe changes made to the system.
- def self.state_attrs(*attr_names)
- @state_attrs ||= []
- @state_attrs = attr_names unless attr_names.empty?
+ #
+ # This method is unnecessary when declaring properties with `property`;
+ # properties are added to state_properties by default, and can be turned off
+ # with `desired_state: false`.
+ #
+ # ```ruby
+ # property :x # part of desired state
+ # property :y, desired_state: false # not part of desired state
+ # ```
+ #
+ # @param names [Array<Symbol>] A list of property names to set as desired
+ # state.
+ #
+ # @return [Array<Property>] All properties in desired state.
+ #
+ def self.state_properties(*names)
+ if !names.empty?
+ names = names.map { |name| name.to_sym }.uniq
- # 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
+ local_properties = properties(false)
+ # Add new properties to the list.
+ names.each do |name|
+ property = properties[name]
+ if !property
+ self.property name, instance_variable_name: false, desired_state: true
+ elsif !property.desired_state?
+ self.property name, desired_state: true
+ end
+ end
+
+ # If state_attrs *excludes* something which is currently desired state,
+ # mark it as desired_state: false.
+ local_properties.each do |name,property|
+ if property.desired_state? && !names.include?(name)
+ self.property name, desired_state: false
+ end
+ end
end
+
+ properties.values.select { |property| property.desired_state? }
+ end
+
+ #
+ # Set or return the list of "state properties" implemented by the Resource
+ # subclass.
+ #
+ # Equivalent to calling #state_properties and getting `state_properties.keys`.
+ #
+ # @deprecated Use state_properties.keys instead. Note that when you declare
+ # properties with `property`: properties are added to state_properties by
+ # default, and can be turned off with `desired_state: false`
+ #
+ # ```ruby
+ # property :x # part of desired state
+ # property :y, desired_state: false # not part of desired state
+ # ```
+ #
+ # @param names [Array<Symbol>] A list of property names to set as desired
+ # state.
+ #
+ # @return [Array<Symbol>] All property names with desired state.
+ #
+ def self.state_attrs(*names)
+ state_properties(*names).map { |property| property.name }
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
+ #
+ # Set the identity of this resource to a particular set of properties.
+ #
+ # This drives #identity, which returns data that uniquely refers to a given
+ # resource on the given node (in such a way that it can be correlated
+ # across Chef runs).
+ #
+ # This method is unnecessary when declaring properties with `property`;
+ # properties can be added to identity during declaration with
+ # `identity: true`.
+ #
+ # ```ruby
+ # property :x, identity: true # part of identity
+ # property :y # not part of identity
+ # ```
+ #
+ # If no properties are marked as identity, "name" is considered the identity.
+ #
+ # @param names [Array<Symbol>] A list of property names to set as the identity.
+ #
+ # @return [Array<Property>] All identity properties.
+ #
+ def self.identity_properties(*names)
+ if !names.empty?
+ names = names.map { |name| name.to_sym }
- # 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
+ # Add or change properties that are not part of the identity.
+ names.each do |name|
+ property = properties[name]
+ if !property
+ self.property name, instance_variable_name: false, identity: true
+ elsif !property.identity?
+ self.property name, identity: true
+ end
+ end
+
+ # If identity_properties *excludes* something which is currently part of
+ # the identity, mark it as identity: false.
+ properties.each do |name,property|
+ if property.identity? && !names.include?(name)
+ self.property name, identity: false
+ end
+ end
end
+
+ result = properties.values.select { |property| property.identity? }
+ result = [ properties[:name] ] if result.empty?
+ result
+ end
+
+ #
+ # Set the identity of this resource to a particular property.
+ #
+ # This drives #identity, which returns data that uniquely refers to a given
+ # resource on the given node (in such a way that it can be correlated
+ # across Chef runs).
+ #
+ # This method is unnecessary when declaring properties with `property`;
+ # properties can be added to identity during declaration with
+ # `identity: true`.
+ #
+ # ```ruby
+ # property :x, identity: true # part of identity
+ # property :y # not part of identity
+ # ```
+ #
+ # @param name [Symbol] A list of property names to set as the identity.
+ #
+ # @return [Symbol] The identity property if there is only one; or `nil` if
+ # there are more than one.
+ #
+ # @raise [ArgumentError] If no arguments are passed and the resource has
+ # more than one identity property.
+ #
+ def self.identity_property(name=nil)
+ result = identity_properties(*Array(name))
+ if result.size > 1
+ raise Chef::Exceptions::MultipleIdentityError, "identity_property cannot be called on an object with more than one identity property (#{result.map { |r| r.name }.join(", ")})."
+ end
+ result.first
+ end
+
+ #
+ # Set a property as the "identity attribute" for this resource.
+ #
+ # Identical to calling #identity_property.first.key.
+ #
+ # @param name [Symbol] The name of the property to set.
+ #
+ # @return [Symbol]
+ #
+ # @deprecated `identity_property` should be used instead.
+ #
+ # @raise [ArgumentError] If no arguments are passed and the resource has
+ # more than one identity property.
+ #
+ def self.identity_attr(name=nil)
+ property = identity_property(name)
+ return nil if !property
+ property.name
end
#
@@ -1019,7 +1236,7 @@ class Chef
if name
name = name.to_sym
# If our class is not already providing this name, provide it.
- if !Chef::ResourceResolver.list(name).include?(self)
+ if !Chef::ResourceResolver.includes_handler?(name, self)
provides name, canonical: true
end
@resource_name = name
@@ -1095,22 +1312,17 @@ class Chef
# Setting default_action will automatially add the action to
# allowed_actions, if it isn't already there.
#
- # Defaults to :nothing.
+ # Defaults to [:nothing].
#
# @param action_name [Symbol,Array<Symbol>] The default action (or series
# of actions) to use.
#
- # @return [Symbol,Array<Symbol>] The default actions for the resource.
+ # @return [Array<Symbol>] The default actions for the resource.
#
def self.default_action(action_name=NOT_PASSED)
unless action_name.equal?(NOT_PASSED)
- if action_name.is_a?(Array)
- @default_action = action_name.map { |arg| arg.to_sym }
- else
- @default_action = action_name.to_sym
- end
-
- self.allowed_actions |= Array(@default_action)
+ @default_action = Array(action_name).map(&:to_sym)
+ self.allowed_actions |= @default_action
end
if @default_action
@@ -1118,7 +1330,7 @@ class Chef
elsif superclass.respond_to?(:default_action)
superclass.default_action
else
- :nothing
+ [:nothing]
end
end
def self.default_action=(action_name)
@@ -1159,7 +1371,7 @@ class Chef
action = action.to_sym
new_action_provider_class.action(action, &recipe_block)
self.allowed_actions += [ action ]
- default_action action if default_action == :nothing
+ default_action action if Array(default_action) == [:nothing]
end
#
@@ -1206,9 +1418,6 @@ class Chef
define_method(:load_current_resource) {}
end
end
- def self.default_action=(action_name)
- default_action(action_name)
- end
#
# Internal Resource Interface (for Chef)
@@ -1304,7 +1513,7 @@ class Chef
end
def self.inherited(child)
super
- @sorted_descendants = nil
+ @@sorted_descendants = nil
# set resource_name automatically if it's not set
if child.name && !child.resource_name
if child.name =~ /^Chef::Resource::(\w+)$/
@@ -1342,13 +1551,13 @@ class Chef
remove_canonical_dsl
end
- result = Chef.resource_priority_map.set(name, self, options, &block)
+ result = Chef.resource_handler_map.set(name, self, options, &block)
Chef::DSL::Resources.add_resource_dsl(name)
result
end
- def self.provides?(node, resource)
- Chef::ResourceResolver.resolve(resource, node: node).provided_by?(self)
+ def self.provides?(node, resource_name)
+ Chef::ResourceResolver.new(node, resource_name).provided_by?(self)
end
# Helper for #notifies
@@ -1506,56 +1715,11 @@ class Chef
Chef::Resource.send(:remove_const, class_name)
end
- # In order to generate deprecation warnings when you use Chef::Resource::MyLwrp,
- # we make a special subclass (identical in nearly all respects) of the
- # actual LWRP. When you say any of these, a deprecation warning will be
- # generated:
- #
- # - Chef::Resource::MyLwrp.new(...)
- # - resource.is_a?(Chef::Resource::MyLwrp)
- # - resource.kind_of?(Chef::Resource::MyLwrp)
- # - case resource
- # when Chef::Resource::MyLwrp
- # end
- #
- resource_subclass = Class.new(resource_class) do
- resource_name nil # we do not actually provide anything
- def initialize(*args, &block)
- Chef::Log.deprecation("Using an LWRP by its name (#{self.class.name}) directly is no longer supported in Chef 13 and will be removed. Use Chef::Resource.resource_for_node(node, name) instead.")
- super
- end
- def self.resource_name(*args)
- if args.empty?
- @resource_name ||= superclass.resource_name
- else
- super
- end
- end
- self
- end
- eval("Chef::Resource::#{class_name} = resource_subclass")
- # Make case, is_a and kind_of work with the new subclass, for backcompat.
- # Any subclass of Chef::Resource::ResourceClass is already a subclass of resource_class
- # Any subclass of resource_class is considered a subclass of Chef::Resource::ResourceClass
- resource_class.class_eval do
- define_method(:is_a?) do |other|
- other.is_a?(Module) && other === self
- end
- define_method(:kind_of?) do |other|
- other.is_a?(Module) && other === self
- end
+ if !Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Resource.const_set(class_name, resource_class)
+ deprecated_constants[class_name.to_sym] = resource_class
end
- resource_subclass.class_eval do
- define_singleton_method(:===) do |other|
- Chef::Log.deprecation("Using an LWRP by its name (#{class_name}) directly is no longer supported in Chef 13 and will be removed. Use Chef::Resource.resource_for_node(node, name) instead.")
- # resource_subclass is a superclass of all resource_class descendants.
- if self == resource_subclass && other.class <= resource_class
- return true
- end
- super(other)
- end
- end
- deprecated_constants[class_name.to_sym] = resource_subclass
+
end
def self.deprecated_constants
@@ -1579,7 +1743,7 @@ class Chef
def self.remove_canonical_dsl
if @resource_name
- remaining = Chef.resource_priority_map.delete_canonical(@resource_name, self)
+ remaining = Chef.resource_handler_map.delete_canonical(@resource_name, self)
if !remaining
Chef::DSL::Resources.remove_resource_dsl(@resource_name)
end
diff --git a/lib/chef/resource/dsc_script.rb b/lib/chef/resource/dsc_script.rb
index 2fcf183375..2877f61eb4 100644
--- a/lib/chef/resource/dsc_script.rb
+++ b/lib/chef/resource/dsc_script.rb
@@ -22,7 +22,7 @@ class Chef
class Resource
class DscScript < Chef::Resource
- provides :dsc_script, platform: "windows"
+ provides :dsc_script, os: "windows"
default_action :run
diff --git a/lib/chef/resource/ips_package.rb b/lib/chef/resource/ips_package.rb
index 8d720dd411..2bf8e1dba8 100644
--- a/lib/chef/resource/ips_package.rb
+++ b/lib/chef/resource/ips_package.rb
@@ -23,6 +23,7 @@ class Chef
class Resource
class IpsPackage < ::Chef::Resource::Package
+ provides :package, os: "solaris2"
provides :ips_package, os: "solaris2"
allowed_actions :install, :remove, :upgrade
diff --git a/lib/chef/resource/mount.rb b/lib/chef/resource/mount.rb
index 79986d127f..a5da0ba329 100644
--- a/lib/chef/resource/mount.rb
+++ b/lib/chef/resource/mount.rb
@@ -174,6 +174,14 @@ class Chef
)
end
+ private
+
+ # Used by the AIX provider to set fstype to nil.
+ # TODO use property to make nil a valid value for fstype
+ def clear_fstype
+ @fstype = nil
+ end
+
end
end
end
diff --git a/lib/chef/resource/openbsd_package.rb b/lib/chef/resource/openbsd_package.rb
index f91fdb37e0..9ae8813d69 100644
--- a/lib/chef/resource/openbsd_package.rb
+++ b/lib/chef/resource/openbsd_package.rb
@@ -29,17 +29,6 @@ class Chef
include Chef::Mixin::ShellOut
provides :package, os: "openbsd"
-
- def after_created
- assign_provider
- end
-
- private
-
- def assign_provider
- @provider = Chef::Provider::Package::Openbsd
- end
-
end
end
end
diff --git a/lib/chef/resource/solaris_package.rb b/lib/chef/resource/solaris_package.rb
index 2dc72d5c47..a98fb8b4fa 100644
--- a/lib/chef/resource/solaris_package.rb
+++ b/lib/chef/resource/solaris_package.rb
@@ -24,10 +24,7 @@ class Chef
class Resource
class SolarisPackage < Chef::Resource::Package
provides :package, os: "solaris2", platform_family: "nexentacore"
- provides :package, os: "solaris2", platform_family: "solaris2" do |node|
- # on >= Solaris 11 we default to IPS packages instead
- node[:platform_version].to_f <= 5.10
- end
+ provides :package, os: "solaris2", platform_family: "solaris2", platform_version: "<= 5.10"
end
end
end
diff --git a/lib/chef/resource_resolver.rb b/lib/chef/resource_resolver.rb
index 9df627beb2..47b3df18af 100644
--- a/lib/chef/resource_resolver.rb
+++ b/lib/chef/resource_resolver.rb
@@ -105,52 +105,80 @@ class Chef
#
# Whether this DSL is provided by the given resource_class.
#
+ # Does NOT call provides? on the resource (it is assumed this is being
+ # called *from* provides?).
+ #
# @api private
def provided_by?(resource_class)
- !prioritized_handlers.include?(resource_class)
+ potential_handlers.include?(resource_class)
+ end
+
+ #
+ # Whether the given handler attempts to provide the resource class at all.
+ #
+ # @api private
+ def self.includes_handler?(resource_name, resource_class)
+ handler_map.list(nil, resource_name).include?(resource_class)
end
protected
+ def self.priority_map
+ Chef.resource_priority_map
+ end
+
+ def self.handler_map
+ Chef.resource_handler_map
+ end
+
def priority_map
- Chef::Platform::ResourcePriorityMap.instance
+ Chef.resource_priority_map
+ end
+
+ def handler_map
+ Chef.resource_handler_map
+ end
+
+ # @api private
+ def potential_handlers
+ handler_map.list(node, resource_name, canonical: canonical).uniq
+ end
+
+ def enabled_handlers
+ potential_handlers.select { |handler| !overrode_provides?(handler) || handler.provides?(node, resource_name) }
end
def prioritized_handlers
- @prioritized_handlers ||=
- priority_map.list_handlers(node, resource_name, canonical: canonical)
+ @prioritized_handlers ||= begin
+ enabled_handlers = self.enabled_handlers
+
+ prioritized = priority_map.list(node, resource_name, canonical: canonical).flatten(1)
+ prioritized &= enabled_handlers # Filter the priority map by the actual enabled handlers
+ prioritized |= enabled_handlers # Bring back any handlers that aren't in the priority map, at the *end* (ordered set)
+ prioritized
+ end
+ end
+
+ def overrode_provides?(handler)
+ handler.method(:provides?).owner != Chef::Resource.method(:provides?).owner
end
module Deprecated
# return a deterministically sorted list of Chef::Resource subclasses
- # @deprecated Now prioritized_handlers does its own work (more efficiently)
def resources
Chef::Resource.sorted_descendants
end
- # A list of all handlers
- # @deprecated Now prioritized_handlers does its own work
def enabled_handlers
- Chef::Log.deprecation("enabled_handlers is deprecated. If you are implementing a ResourceResolver, use provided_handlers. If you are not, use Chef::ResourceResolver.list(#{resource_name.inspect}, node: <node>)")
- resources.select { |klass| klass.provides?(node, resource_name) }
- end
-
- protected
-
- # A list of all handlers for the given DSL. If there are no handlers in
- # the map, we still check all descendants of Chef::Resource for backwards
- # compatibility purposes.
- def prioritized_handlers
- @prioritized_handlers ||= super ||
- resources.select do |klass|
- # Don't bother calling provides? unless it's overridden. We already
- # know prioritized_handlers
- if klass.method(:provides?).owner != Chef::Resource && klass.provides?(node, resource_name)
- Chef::Log.deprecation("Resources #{provided.join(", ")} are marked as providing DSL #{resource_name}, but provides #{resource_name.inspect} was never called!")
- Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
- true
- end
+ handlers = super
+ if handlers.empty?
+ handlers = resources.select { |handler| overrode_provides?(handler) && handler.provides?(node, resource_name) }
+ handlers.each do |handler|
+ Chef::Log.deprecation("#{handler}.provides? returned true when asked if it provides DSL #{resource_name}, but provides #{resource_name.inspect} was never called!")
+ Chef::Log.deprecation("In Chef 13, this will break: you must call provides to mark the names you provide, even if you also override provides? yourself.")
end
+ end
+ handlers
end
end
prepend Deprecated
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index f55d1740b1..b1113f594e 100644
--- a/lib/chef/run_context.rb
+++ b/lib/chef/run_context.rb
@@ -532,17 +532,24 @@ ERROR_MESSAGE
###
# These need to be settable so deploy can run a resource_collection
# independent of any cookbooks via +recipe_eval+
+ def resource_collection=(value)
+ Chef::Log.deprecation("Setting run_context.resource_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
+ @resource_collection = value
+ end
def audits=(value)
Chef::Log.deprecation("Setting run_context.audits will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
+ @audits = value
end
def immediate_notification_collection=(value)
Chef::Log.deprecation("Setting run_context.immediate_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
+ @immediate_notification_collection = value
end
def delayed_notification_collection=(value)
Chef::Log.deprecation("Setting run_context.delayed_notification_collection will be removed in a future Chef. Use run_context.create_child to create a new RunContext instead.")
+ @delayed_notification_collection = value
end
end
prepend Deprecated
diff --git a/lib/chef/run_list/versioned_recipe_list.rb b/lib/chef/run_list/versioned_recipe_list.rb
index 7cce6fa48c..2824f08f31 100644
--- a/lib/chef/run_list/versioned_recipe_list.rb
+++ b/lib/chef/run_list/versioned_recipe_list.rb
@@ -70,15 +70,16 @@ class Chef
# @return [Array] Array of strings with fully-qualified recipe names
def with_fully_qualified_names_and_version_constraints
self.map do |recipe_name|
- ret = if recipe_name.include?('::')
+ qualified_recipe = if recipe_name.include?('::')
recipe_name
else
"#{recipe_name}::default"
end
- if @versions[recipe_name]
- ret << "@#{@versions[recipe_name]}"
- end
- ret
+
+ version = @versions[recipe_name]
+ qualified_recipe = "#{qualified_recipe}@#{version}" if version
+
+ qualified_recipe
end
end
end
diff --git a/lib/chef/user.rb b/lib/chef/user.rb
index 717deb63c3..31ebeda86f 100644
--- a/lib/chef/user.rb
+++ b/lib/chef/user.rb
@@ -21,85 +21,45 @@ require 'chef/mixin/from_file'
require 'chef/mash'
require 'chef/json_compat'
require 'chef/search/query'
-require 'chef/mixin/api_version_request_handling'
-require 'chef/exceptions'
require 'chef/server_api'
-# OSC 11 BACKWARDS COMPATIBILITY NOTE (remove after OSC 11 support ends)
+# TODO
+# DEPRECATION NOTE
+# This class will be replaced by Chef::UserV1 in Chef 13. It is the code to support the User object
+# corrosponding to the Open Source Chef Server 11 and only still exists to support
+# users still on OSC 11.
#
-# In general, Chef::User is no longer expected to support Open Source Chef 11 Server requests.
-# The object that handles those requests has been moved to the Chef::OscUser namespace.
+# Chef::UserV1 now supports Chef Server 12 and will be moved to this namespace in Chef 13.
#
-# Exception: self.list is backwards compatible with OSC 11
+# New development should occur in Chef::UserV1.
+# This file and corrosponding osc_user knife files
+# should be removed once client support for Open Source Chef Server 11 expires.
class Chef
class User
include Chef::Mixin::FromFile
include Chef::Mixin::ParamsValidate
- include Chef::Mixin::ApiVersionRequestHandling
-
- SUPPORTED_API_VERSIONS = [0,1]
def initialize
- @username = nil
- @display_name = nil
- @first_name = nil
- @middle_name = nil
- @last_name = nil
- @email = nil
- @password = nil
+ @name = ''
@public_key = nil
@private_key = nil
- @create_key = nil
@password = nil
+ @admin = false
end
- def chef_root_rest_v0
- @chef_root_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], {:api_version => "0"})
- end
-
- def chef_root_rest_v1
- @chef_root_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], {:api_version => "1"})
+ def chef_rest_v0
+ @chef_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0"})
end
- def username(arg=nil)
- set_or_return(:username, arg,
+ def name(arg=nil)
+ set_or_return(:name, arg,
:regex => /^[a-z0-9\-_]+$/)
end
- def display_name(arg=nil)
- set_or_return(:display_name,
- arg, :kind_of => String)
- end
-
- def first_name(arg=nil)
- set_or_return(:first_name,
- arg, :kind_of => String)
- end
-
- def middle_name(arg=nil)
- set_or_return(:middle_name,
- arg, :kind_of => String)
- end
-
- def last_name(arg=nil)
- set_or_return(:last_name,
- arg, :kind_of => String)
- end
-
- def email(arg=nil)
- set_or_return(:email,
- arg, :kind_of => String)
- end
-
- def password(arg=nil)
- set_or_return(:password,
- arg, :kind_of => String)
- end
-
- def create_key(arg=nil)
- set_or_return(:create_key, arg,
- :kind_of => [TrueClass, FalseClass])
+ def admin(arg=nil)
+ set_or_return(:admin,
+ arg, :kind_of => [TrueClass, FalseClass])
end
def public_key(arg=nil)
@@ -119,17 +79,12 @@ class Chef
def to_hash
result = {
- "username" => @username
+ "name" => @name,
+ "public_key" => @public_key,
+ "admin" => @admin
}
- result["display_name"] = @display_name unless @display_name.nil?
- result["first_name"] = @first_name unless @first_name.nil?
- result["middle_name"] = @middle_name unless @middle_name.nil?
- result["last_name"] = @last_name unless @last_name.nil?
- result["email"] = @email unless @email.nil?
- result["password"] = @password unless @password.nil?
- result["public_key"] = @public_key unless @public_key.nil?
- result["private_key"] = @private_key unless @private_key.nil?
- result["create_key"] = @create_key unless @create_key.nil?
+ result["private_key"] = @private_key if @private_key
+ result["password"] = @password if @password
result
end
@@ -138,86 +93,21 @@ class Chef
end
def destroy
- # will default to the current API version (Chef::Authenticator::DEFAULT_SERVER_API_VERSION)
- Chef::REST.new(Chef::Config[:chef_server_url]).delete("users/#{@username}")
+ chef_rest_v0.delete("users/#{@name}")
end
def create
- # try v1, fail back to v0 if v1 not supported
- begin
- payload = {
- :username => @username,
- :display_name => @display_name,
- :first_name => @first_name,
- :last_name => @last_name,
- :email => @email,
- :password => @password
- }
- payload[:public_key] = @public_key unless @public_key.nil?
- payload[:create_key] = @create_key unless @create_key.nil?
- payload[:middle_name] = @middle_name unless @middle_name.nil?
- raise Chef::Exceptions::InvalidUserAttribute, "You cannot set both public_key and create_key for create." if !@create_key.nil? && !@public_key.nil?
- new_user = chef_root_rest_v1.post("users", payload)
-
- # get the private_key out of the chef_key hash if it exists
- if new_user['chef_key']
- if new_user['chef_key']['private_key']
- new_user['private_key'] = new_user['chef_key']['private_key']
- end
- new_user['public_key'] = new_user['chef_key']['public_key']
- new_user.delete('chef_key')
- end
- rescue Net::HTTPServerException => e
- # rescue API V0 if 406 and the server supports V0
- supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
- raise e unless supported_versions && supported_versions.include?(0)
- payload = {
- :username => @username,
- :display_name => @display_name,
- :first_name => @first_name,
- :last_name => @last_name,
- :email => @email,
- :password => @password
- }
- payload[:middle_name] = @middle_name unless @middle_name.nil?
- payload[:public_key] = @public_key unless @public_key.nil?
- # under API V0, the server will create a key pair if public_key isn't passed
- new_user = chef_root_rest_v0.post("users", payload)
- end
-
+ payload = {:name => self.name, :admin => self.admin, :password => self.password }
+ payload[:public_key] = public_key if public_key
+ new_user = chef_rest_v0.post("users", payload)
Chef::User.from_hash(self.to_hash.merge(new_user))
end
def update(new_key=false)
- begin
- payload = {:username => username}
- payload[:display_name] = display_name unless display_name.nil?
- payload[:first_name] = first_name unless first_name.nil?
- payload[:middle_name] = middle_name unless middle_name.nil?
- payload[:last_name] = last_name unless last_name.nil?
- payload[:email] = email unless email.nil?
- payload[:password] = password unless password.nil?
-
- # API V1 will fail if these key fields are defined, and try V0 below if relevant 400 is returned
- payload[:public_key] = public_key unless public_key.nil?
- payload[:private_key] = new_key if new_key
-
- updated_user = chef_root_rest_v1.put("users/#{username}", payload)
- rescue Net::HTTPServerException => e
- if e.response.code == "400"
- # if a 400 is returned but the error message matches the error related to private / public key fields, try V0
- # else, raise the 400
- error = Chef::JSONCompat.from_json(e.response.body)["error"].first
- error_match = /Since Server API v1, all keys must be updated via the keys endpoint/.match(error)
- if error_match.nil?
- raise e
- end
- else # for other types of errors, test for API versioning errors right away
- supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
- raise e unless supported_versions && supported_versions.include?(0)
- end
- updated_user = chef_root_rest_v0.put("users/#{username}", payload)
- end
+ payload = {:name => name, :admin => admin}
+ payload[:private_key] = new_key if new_key
+ payload[:password] = password if password
+ updated_user = chef_rest_v0.put("users/#{name}", payload)
Chef::User.from_hash(self.to_hash.merge(updated_user))
end
@@ -233,47 +123,30 @@ class Chef
end
end
- # Note: remove after API v0 no longer supported by client (and knife command).
def reregister
- begin
- payload = self.to_hash.merge({"private_key" => true})
- reregistered_self = chef_root_rest_v0.put("users/#{username}", payload)
- private_key(reregistered_self["private_key"])
- # only V0 supported for reregister
- rescue Net::HTTPServerException => e
- # if there was a 406 related to versioning, give error explaining that
- # only API version 0 is supported for reregister command
- if e.response.code == "406" && e.response["x-ops-server-api-version"]
- version_header = Chef::JSONCompat.from_json(e.response["x-ops-server-api-version"])
- min_version = version_header["min_version"]
- max_version = version_header["max_version"]
- error_msg = reregister_only_v0_supported_error_msg(max_version, min_version)
- raise Chef::Exceptions::OnlyApiVersion0SupportedForAction.new(error_msg)
- else
- raise e
- end
- end
+ reregistered_self = chef_rest_v0.put("users/#{name}", { :name => name, :admin => admin, :private_key => true })
+ private_key(reregistered_self["private_key"])
self
end
def to_s
- "user[#{@username}]"
+ "user[#{@name}]"
+ end
+
+ def inspect
+ "Chef::User name:'#{name}' admin:'#{admin.inspect}'" +
+ "public_key:'#{public_key}' private_key:#{private_key}"
end
# Class Methods
def self.from_hash(user_hash)
user = Chef::User.new
- user.username user_hash['username']
- user.display_name user_hash['display_name'] if user_hash.key?('display_name')
- user.first_name user_hash['first_name'] if user_hash.key?('first_name')
- user.middle_name user_hash['middle_name'] if user_hash.key?('middle_name')
- user.last_name user_hash['last_name'] if user_hash.key?('last_name')
- user.email user_hash['email'] if user_hash.key?('email')
- user.password user_hash['password'] if user_hash.key?('password')
- user.public_key user_hash['public_key'] if user_hash.key?('public_key')
+ user.name user_hash['name']
user.private_key user_hash['private_key'] if user_hash.key?('private_key')
- user.create_key user_hash['create_key'] if user_hash.key?('create_key')
+ user.password user_hash['password'] if user_hash.key?('password')
+ user.public_key user_hash['public_key']
+ user.admin user_hash['admin']
user
end
@@ -286,19 +159,12 @@ class Chef
end
def self.list(inflate=false)
- response = Chef::REST.new(Chef::Config[:chef_server_url]).get('users')
+ response = Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0"}).get('users')
users = if response.is_a?(Array)
- # EC 11 / CS 12 V0, V1
- # GET /organizations/<org>/users
- transform_list_response(response)
- else
- # OSC 11
- # GET /users
- # EC 11 / CS 12 V0, V1
- # GET /users
- response # OSC
- end
-
+ transform_ohc_list_response(response) # OHC/OPC
+ else
+ response # OSC
+ end
if inflate
users.inject({}) do |user_map, (name, _url)|
user_map[name] = Chef::User.load(name)
@@ -309,9 +175,8 @@ class Chef
end
end
- def self.load(username)
- # will default to the current API version (Chef::Authenticator::DEFAULT_SERVER_API_VERSION)
- response = Chef::REST.new(Chef::Config[:chef_server_url]).get("users/#{username}")
+ def self.load(name)
+ response = Chef::ServerAPI.new(Chef::Config[:chef_server_url], {:api_version => "0"}).get("users/#{name}")
Chef::User.from_hash(response)
end
@@ -319,7 +184,7 @@ class Chef
# [ { "user" => { "username" => USERNAME }}, ...]
# into the form
# { "USERNAME" => "URI" }
- def self.transform_list_response(response)
+ def self.transform_ohc_list_response(response)
new_response = Hash.new
response.each do |u|
name = u['user']['username']
@@ -328,7 +193,6 @@ class Chef
new_response
end
- private_class_method :transform_list_response
-
+ private_class_method :transform_ohc_list_response
end
end
diff --git a/lib/chef/user_v1.rb b/lib/chef/user_v1.rb
new file mode 100644
index 0000000000..31cb0576a2
--- /dev/null
+++ b/lib/chef/user_v1.rb
@@ -0,0 +1,335 @@
+#
+# Author:: Steven Danna (steve@opscode.com)
+# Copyright:: Copyright 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/config'
+require 'chef/mixin/params_validate'
+require 'chef/mixin/from_file'
+require 'chef/mash'
+require 'chef/json_compat'
+require 'chef/search/query'
+require 'chef/mixin/api_version_request_handling'
+require 'chef/exceptions'
+require 'chef/server_api'
+
+# OSC 11 BACKWARDS COMPATIBILITY NOTE (remove after OSC 11 support ends)
+#
+# In general, Chef::UserV1 is no longer expected to support Open Source Chef 11 Server requests.
+# The object that handles those requests remain in the Chef::User namespace.
+# This code will be moved to the Chef::User namespace as of Chef 13.
+#
+# Exception: self.list is backwards compatible with OSC 11
+class Chef
+ class UserV1
+
+ include Chef::Mixin::FromFile
+ include Chef::Mixin::ParamsValidate
+ include Chef::Mixin::ApiVersionRequestHandling
+
+ SUPPORTED_API_VERSIONS = [0,1]
+
+ def initialize
+ @username = nil
+ @display_name = nil
+ @first_name = nil
+ @middle_name = nil
+ @last_name = nil
+ @email = nil
+ @password = nil
+ @public_key = nil
+ @private_key = nil
+ @create_key = nil
+ @password = nil
+ end
+
+ def chef_root_rest_v0
+ @chef_root_rest_v0 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], {:api_version => "0"})
+ end
+
+ def chef_root_rest_v1
+ @chef_root_rest_v1 ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], {:api_version => "1"})
+ end
+
+ def username(arg=nil)
+ set_or_return(:username, arg,
+ :regex => /^[a-z0-9\-_]+$/)
+ end
+
+ def display_name(arg=nil)
+ set_or_return(:display_name,
+ arg, :kind_of => String)
+ end
+
+ def first_name(arg=nil)
+ set_or_return(:first_name,
+ arg, :kind_of => String)
+ end
+
+ def middle_name(arg=nil)
+ set_or_return(:middle_name,
+ arg, :kind_of => String)
+ end
+
+ def last_name(arg=nil)
+ set_or_return(:last_name,
+ arg, :kind_of => String)
+ end
+
+ def email(arg=nil)
+ set_or_return(:email,
+ arg, :kind_of => String)
+ end
+
+ def password(arg=nil)
+ set_or_return(:password,
+ arg, :kind_of => String)
+ end
+
+ def create_key(arg=nil)
+ set_or_return(:create_key, arg,
+ :kind_of => [TrueClass, FalseClass])
+ end
+
+ def public_key(arg=nil)
+ set_or_return(:public_key,
+ arg, :kind_of => String)
+ end
+
+ def private_key(arg=nil)
+ set_or_return(:private_key,
+ arg, :kind_of => String)
+ end
+
+ def password(arg=nil)
+ set_or_return(:password,
+ arg, :kind_of => String)
+ end
+
+ def to_hash
+ result = {
+ "username" => @username
+ }
+ result["display_name"] = @display_name unless @display_name.nil?
+ result["first_name"] = @first_name unless @first_name.nil?
+ result["middle_name"] = @middle_name unless @middle_name.nil?
+ result["last_name"] = @last_name unless @last_name.nil?
+ result["email"] = @email unless @email.nil?
+ result["password"] = @password unless @password.nil?
+ result["public_key"] = @public_key unless @public_key.nil?
+ result["private_key"] = @private_key unless @private_key.nil?
+ result["create_key"] = @create_key unless @create_key.nil?
+ result
+ end
+
+ def to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
+ end
+
+ def destroy
+ # will default to the current API version (Chef::Authenticator::DEFAULT_SERVER_API_VERSION)
+ Chef::REST.new(Chef::Config[:chef_server_url]).delete("users/#{@username}")
+ end
+
+ def create
+ # try v1, fail back to v0 if v1 not supported
+ begin
+ payload = {
+ :username => @username,
+ :display_name => @display_name,
+ :first_name => @first_name,
+ :last_name => @last_name,
+ :email => @email,
+ :password => @password
+ }
+ payload[:public_key] = @public_key unless @public_key.nil?
+ payload[:create_key] = @create_key unless @create_key.nil?
+ payload[:middle_name] = @middle_name unless @middle_name.nil?
+ raise Chef::Exceptions::InvalidUserAttribute, "You cannot set both public_key and create_key for create." if !@create_key.nil? && !@public_key.nil?
+ new_user = chef_root_rest_v1.post("users", payload)
+
+ # get the private_key out of the chef_key hash if it exists
+ if new_user['chef_key']
+ if new_user['chef_key']['private_key']
+ new_user['private_key'] = new_user['chef_key']['private_key']
+ end
+ new_user['public_key'] = new_user['chef_key']['public_key']
+ new_user.delete('chef_key')
+ end
+ rescue Net::HTTPServerException => e
+ # rescue API V0 if 406 and the server supports V0
+ supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
+ raise e unless supported_versions && supported_versions.include?(0)
+ payload = {
+ :username => @username,
+ :display_name => @display_name,
+ :first_name => @first_name,
+ :last_name => @last_name,
+ :email => @email,
+ :password => @password
+ }
+ payload[:middle_name] = @middle_name unless @middle_name.nil?
+ payload[:public_key] = @public_key unless @public_key.nil?
+ # under API V0, the server will create a key pair if public_key isn't passed
+ new_user = chef_root_rest_v0.post("users", payload)
+ end
+
+ Chef::UserV1.from_hash(self.to_hash.merge(new_user))
+ end
+
+ def update(new_key=false)
+ begin
+ payload = {:username => username}
+ payload[:display_name] = display_name unless display_name.nil?
+ payload[:first_name] = first_name unless first_name.nil?
+ payload[:middle_name] = middle_name unless middle_name.nil?
+ payload[:last_name] = last_name unless last_name.nil?
+ payload[:email] = email unless email.nil?
+ payload[:password] = password unless password.nil?
+
+ # API V1 will fail if these key fields are defined, and try V0 below if relevant 400 is returned
+ payload[:public_key] = public_key unless public_key.nil?
+ payload[:private_key] = new_key if new_key
+
+ updated_user = chef_root_rest_v1.put("users/#{username}", payload)
+ rescue Net::HTTPServerException => e
+ if e.response.code == "400"
+ # if a 400 is returned but the error message matches the error related to private / public key fields, try V0
+ # else, raise the 400
+ error = Chef::JSONCompat.from_json(e.response.body)["error"].first
+ error_match = /Since Server API v1, all keys must be updated via the keys endpoint/.match(error)
+ if error_match.nil?
+ raise e
+ end
+ else # for other types of errors, test for API versioning errors right away
+ supported_versions = server_client_api_version_intersection(e, SUPPORTED_API_VERSIONS)
+ raise e unless supported_versions && supported_versions.include?(0)
+ end
+ updated_user = chef_root_rest_v0.put("users/#{username}", payload)
+ end
+ Chef::UserV1.from_hash(self.to_hash.merge(updated_user))
+ end
+
+ def save(new_key=false)
+ begin
+ create
+ rescue Net::HTTPServerException => e
+ if e.response.code == "409"
+ update(new_key)
+ else
+ raise e
+ end
+ end
+ end
+
+ # Note: remove after API v0 no longer supported by client (and knife command).
+ def reregister
+ begin
+ payload = self.to_hash.merge({"private_key" => true})
+ reregistered_self = chef_root_rest_v0.put("users/#{username}", payload)
+ private_key(reregistered_self["private_key"])
+ # only V0 supported for reregister
+ rescue Net::HTTPServerException => e
+ # if there was a 406 related to versioning, give error explaining that
+ # only API version 0 is supported for reregister command
+ if e.response.code == "406" && e.response["x-ops-server-api-version"]
+ version_header = Chef::JSONCompat.from_json(e.response["x-ops-server-api-version"])
+ min_version = version_header["min_version"]
+ max_version = version_header["max_version"]
+ error_msg = reregister_only_v0_supported_error_msg(max_version, min_version)
+ raise Chef::Exceptions::OnlyApiVersion0SupportedForAction.new(error_msg)
+ else
+ raise e
+ end
+ end
+ self
+ end
+
+ def to_s
+ "user[#{@username}]"
+ end
+
+ # Class Methods
+
+ def self.from_hash(user_hash)
+ user = Chef::UserV1.new
+ user.username user_hash['username']
+ user.display_name user_hash['display_name'] if user_hash.key?('display_name')
+ user.first_name user_hash['first_name'] if user_hash.key?('first_name')
+ user.middle_name user_hash['middle_name'] if user_hash.key?('middle_name')
+ user.last_name user_hash['last_name'] if user_hash.key?('last_name')
+ user.email user_hash['email'] if user_hash.key?('email')
+ user.password user_hash['password'] if user_hash.key?('password')
+ user.public_key user_hash['public_key'] if user_hash.key?('public_key')
+ user.private_key user_hash['private_key'] if user_hash.key?('private_key')
+ user.create_key user_hash['create_key'] if user_hash.key?('create_key')
+ user
+ end
+
+ def self.from_json(json)
+ Chef::UserV1.from_hash(Chef::JSONCompat.from_json(json))
+ end
+
+ class << self
+ alias_method :json_create, :from_json
+ end
+
+ def self.list(inflate=false)
+ response = Chef::REST.new(Chef::Config[:chef_server_url]).get('users')
+ users = if response.is_a?(Array)
+ # EC 11 / CS 12 V0, V1
+ # GET /organizations/<org>/users
+ transform_list_response(response)
+ else
+ # OSC 11
+ # GET /users
+ # EC 11 / CS 12 V0, V1
+ # GET /users
+ response # OSC
+ end
+
+ if inflate
+ users.inject({}) do |user_map, (name, _url)|
+ user_map[name] = Chef::UserV1.load(name)
+ user_map
+ end
+ else
+ users
+ end
+ end
+
+ def self.load(username)
+ # will default to the current API version (Chef::Authenticator::DEFAULT_SERVER_API_VERSION)
+ response = Chef::REST.new(Chef::Config[:chef_server_url]).get("users/#{username}")
+ Chef::UserV1.from_hash(response)
+ end
+
+ # Gross. Transforms an API response in the form of:
+ # [ { "user" => { "username" => USERNAME }}, ...]
+ # into the form
+ # { "USERNAME" => "URI" }
+ def self.transform_list_response(response)
+ new_response = Hash.new
+ response.each do |u|
+ name = u['user']['username']
+ new_response[name] = Chef::Config[:chef_server_url] + "/users/#{name}"
+ end
+ new_response
+ end
+
+ private_class_method :transform_list_response
+
+ end
+end
diff --git a/lib/chef/win32/registry.rb b/lib/chef/win32/registry.rb
index 18f12d26b8..1a1aa12fad 100644
--- a/lib/chef/win32/registry.rb
+++ b/lib/chef/win32/registry.rb
@@ -203,7 +203,7 @@ class Chef
key_exists!(key_path)
hive, key = get_hive_and_key(key_path)
hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg|
- return true if reg.any? {|val| val == value[:name] }
+ return true if reg.any? {|val| safely_downcase(val) == safely_downcase(value[:name]) }
end
return false
end
@@ -213,7 +213,7 @@ class Chef
hive, key = get_hive_and_key(key_path)
hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg|
reg.each do |val_name, val_type, val_data|
- if val_name == value[:name] &&
+ if safely_downcase(val_name) == safely_downcase(value[:name]) &&
val_type == get_type_from_name(value[:type]) &&
val_data == value[:data]
return true
@@ -289,6 +289,14 @@ class Chef
private
+
+ def safely_downcase(val)
+ if val.is_a? String
+ return val.downcase
+ end
+ return val
+ end
+
def node
run_context && run_context.node
end
diff --git a/lib/chef/workstation_config_loader.rb b/lib/chef/workstation_config_loader.rb
index 2454c9cccf..8398c5d616 100644
--- a/lib/chef/workstation_config_loader.rb
+++ b/lib/chef/workstation_config_loader.rb
@@ -1,5 +1,5 @@
#
-# Author:: Daniel DeLeo (<dan@getchef.com>)
+# Author:: Claire McQuin (<claire@chef.io>)
# Copyright:: Copyright (c) 2014 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
@@ -16,163 +16,8 @@
# limitations under the License.
#
-require 'chef/config_fetcher'
-require 'chef/config'
-require 'chef/null_logger'
-require 'chef/util/path_helper'
+require 'chef-config/workstation_config_loader'
class Chef
-
- class WorkstationConfigLoader
-
- # Path to a config file requested by user, (e.g., via command line option). Can be nil
- attr_accessor :explicit_config_file
-
- # TODO: initialize this with a logger for Chef and Knife
- def initialize(explicit_config_file, logger=nil)
- @explicit_config_file = explicit_config_file
- @config_location = nil
- @logger = logger || NullLogger.new
- end
-
- def no_config_found?
- config_location.nil?
- end
-
- def config_location
- @config_location ||= (explicit_config_file || locate_local_config)
- end
-
- def chef_config_dir
- if @chef_config_dir.nil?
- @chef_config_dir = false
- full_path = working_directory.split(File::SEPARATOR)
- (full_path.length - 1).downto(0) do |i|
- candidate_directory = File.join(full_path[0..i] + [".chef" ])
- if File.exist?(candidate_directory) && File.directory?(candidate_directory)
- @chef_config_dir = candidate_directory
- break
- end
- end
- end
- @chef_config_dir
- end
-
- def load
- # Ignore it if there's no explicit_config_file and can't find one at a
- # default path.
- return false if config_location.nil?
-
- if explicit_config_file && !path_exists?(config_location)
- raise Exceptions::ConfigurationError, "Specified config file #{config_location} does not exist"
- end
-
- # Have to set Chef::Config.config_file b/c other config is derived from it.
- Chef::Config.config_file = config_location
- read_config(IO.read(config_location), config_location)
- end
-
- # (Private API, public for test purposes)
- def env
- ENV
- end
-
- # (Private API, public for test purposes)
- def path_exists?(path)
- Pathname.new(path).expand_path.exist?
- end
-
- private
-
- def have_config?(path)
- if path_exists?(path)
- logger.info("Using config at #{path}")
- true
- else
- logger.debug("Config not found at #{path}, trying next option")
- false
- end
- end
-
- def locate_local_config
- candidate_configs = []
-
- # Look for $KNIFE_HOME/knife.rb (allow multiple knives config on same machine)
- if env['KNIFE_HOME']
- candidate_configs << File.join(env['KNIFE_HOME'], 'config.rb')
- candidate_configs << File.join(env['KNIFE_HOME'], 'knife.rb')
- end
- # Look for $PWD/knife.rb
- if Dir.pwd
- candidate_configs << File.join(Dir.pwd, 'config.rb')
- candidate_configs << File.join(Dir.pwd, 'knife.rb')
- end
- # Look for $UPWARD/.chef/knife.rb
- if chef_config_dir
- candidate_configs << File.join(chef_config_dir, 'config.rb')
- candidate_configs << File.join(chef_config_dir, 'knife.rb')
- end
- # Look for $HOME/.chef/knife.rb
- Chef::Util::PathHelper.home('.chef') do |dot_chef_dir|
- candidate_configs << File.join(dot_chef_dir, 'config.rb')
- candidate_configs << File.join(dot_chef_dir, 'knife.rb')
- end
-
- candidate_configs.find do | candidate_config |
- have_config?(candidate_config)
- end
- end
-
- def working_directory
- a = if Chef::Platform.windows?
- env['CD']
- else
- env['PWD']
- end || Dir.pwd
-
- a
- end
-
- def read_config(config_content, config_file_path)
- Chef::Config.from_string(config_content, config_file_path)
- rescue SignalException
- raise
- rescue SyntaxError => e
- message = ""
- message << "You have invalid ruby syntax in your config file #{config_file_path}\n\n"
- message << "#{e.class.name}: #{e.message}\n"
- if file_line = e.message[/#{Regexp.escape(config_file_path)}:[\d]+/]
- line = file_line[/:([\d]+)$/, 1].to_i
- message << highlight_config_error(config_file_path, line)
- end
- raise Exceptions::ConfigurationError, message
- rescue Exception => e
- message = "You have an error in your config file #{config_file_path}\n\n"
- message << "#{e.class.name}: #{e.message}\n"
- filtered_trace = e.backtrace.grep(/#{Regexp.escape(config_file_path)}/)
- filtered_trace.each {|bt_line| message << " " << bt_line << "\n" }
- if !filtered_trace.empty?
- line_nr = filtered_trace.first[/#{Regexp.escape(config_file_path)}:([\d]+)/, 1]
- message << highlight_config_error(config_file_path, line_nr.to_i)
- end
- raise Exceptions::ConfigurationError, message
- end
-
-
- def highlight_config_error(file, line)
- config_file_lines = []
- IO.readlines(file).each_with_index {|l, i| config_file_lines << "#{(i + 1).to_s.rjust(3)}: #{l.chomp}"}
- if line == 1
- lines = config_file_lines[0..3]
- else
- lines = config_file_lines[Range.new(line - 2, line)]
- end
- "Relevant file content:\n" + lines.join("\n") + "\n"
- end
-
- def logger
- @logger
- end
-
- end
+ WorkstationConfigLoader = ChefConfig::WorkstationConfigLoader
end
diff --git a/spec/data/trusted_certs/opscode.pem b/spec/data/trusted_certs/opscode.pem
index 37a3dd1ef2..e421a4e6e9 100644
--- a/spec/data/trusted_certs/opscode.pem
+++ b/spec/data/trusted_certs/opscode.pem
@@ -1,60 +1,57 @@
-----BEGIN CERTIFICATE-----
-MIIFrDCCBJSgAwIBAgIQB1O/fCb6cEytJ4BP3HTbCTANBgkqhkiG9w0BAQUFADBI
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSIwIAYDVQQDExlE
-aWdpQ2VydCBTZWN1cmUgU2VydmVyIENBMB4XDTE0MDYxMDAwMDAwMFoXDTE1MDcw
-MTEyMDAwMFowaTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO
-BgNVBAcTB1NlYXR0bGUxGzAZBgNVBAoTEkNoZWYgU29mdHdhcmUsIEluYzEWMBQG
-A1UEAwwNKi5vcHNjb2RlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
-ggEBAMm+rf2RcPGBlZoM+hI4BxlaHbdRg1GZJ/T46UWFOBnZFVP++TX/pyjDsvns
-xymcQywtoN/26+UIys6oWX1um9ikEokvf67LdsUeemQGFHFky8X1Ka2hVtKnxBhi
-XZfvyHDR4IyFWU9AwmhnqySzxqCtynUu8Gktx7JVfqbRFMZ186pDcSw8LoaqjTVG
-SzO7eNH2sM3doMueAHj7ITc2wUzmfa0Pdh+K8UoCn/HopU5LzycziJVPYvUkLT2m
-YCV7VWRc+kObZseHhZAbyaDk3RgPQ/eRMhytAgbruBHWDqNesNw+ZA70w856Oj2Y
-geO7JF+5V6WvkywrF8vydaoM2l8CAwEAAaOCAm8wggJrMB8GA1UdIwQYMBaAFJBx
-2zfrc8jv3NUeErY0uitaoKaSMB0GA1UdDgQWBBQK5zjZwbcmcMNLnI2h1ioAldEV
-ujCBygYDVR0RBIHCMIG/gg0qLm9wc2NvZGUuY29tghBjb3JwLm9wc2NvZGUuY29t
-ghIqLmNvcnAub3BzY29kZS5jb22CDyoubGVhcm5jaGVmLmNvbYISKi5jb3JwLmdl
-dGNoZWYuY29tgg0qLmdldGNoZWYuY29tggwqLm9wc2NvZGUudXOCC2dldGNoZWYu
-Y29tggtvcHNjb2RlLmNvbYIRYXBpLmJlcmtzaGVsZi5jb22CDWxlYXJuY2hlZi5j
-b22CCm9wc2NvZGUudXMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
-BwMBBggrBgEFBQcDAjBhBgNVHR8EWjBYMCqgKKAmhiRodHRwOi8vY3JsMy5kaWdp
-Y2VydC5jb20vc3NjYS1nNi5jcmwwKqAooCaGJGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0
-LmNvbS9zc2NhLWc2LmNybDBCBgNVHSAEOzA5MDcGCWCGSAGG/WwBATAqMCgGCCsG
-AQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMHgGCCsGAQUFBwEB
-BGwwajAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEIGCCsG
-AQUFBzAChjZodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTZWN1
-cmVTZXJ2ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQUFAAOCAQEA
-kgBpJ2t+St7SmWfeNU9EWAhy0NuUnRIi1jnqXdapfPmS6V/M0i2wP/p+crMty78e
-+3ieuF5s0GJBLs85Hikcl3SlrrbIBJxozov1TY6zeOi6+TCsdXer6t6iQKz36zno
-+k+T6lnMCyo9+pk1PhcAWyfo1Fz4xVOBVec/71VovFkkGD2//KB+sbDs+yh21N9M
-ReO7duj16rQSctfO9R2h65djBNlgz6hXY2nlw8/x3uFfZobXOxDrTcH6Z8HIslkE
-MiTXGix6zdqJaFRCWi+prnAztWs+jEy+v95VSEHPj3xpwZ9WjsxQN0kFA2EX61v/
-kGunmyhehGjblQRt7bpyiA==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEjzCCA3egAwIBAgIQBp4dt3/PHfupevXlyaJANzANBgkqhkiG9w0BAQUFADBh
+MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
-QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEgxCzAJBgNVBAYTAlVT
-MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxIjAgBgNVBAMTGURpZ2lDZXJ0IFNlY3Vy
-ZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7V+Qh
-qdWbYDd+jqFhf4HiGsJ1ZNmRUAvkNkQkbjDSm3on+sJqrmpwCTi5IArIZRBKiKwx
-8tyS8mOhXYBjWYCSIxzm73ZKUDXJ2HE4ue3w5kKu0zgmeTD5IpTG26Y/QXiQ2N5c
-fml9+JAVOtChoL76srIZodgr0c6/a91Jq6OS/rWryME+7gEA2KlEuEJziMNh9atK
-gygK0tRJ+mqxzd9XLJTl4sqDX7e6YlwvaKXwwLn9K9HpH9gaYhW9/z2m98vv5ttl
-LyU47PvmIGZYljQZ0hXOIdMkzNkUb9j+Vcfnb7YPGoxJvinyulqagSY3JG/XSBJs
-Lln1nBi72fZo4t9FAgMBAAGjggFaMIIBVjASBgNVHRMBAf8ECDAGAQH/AgEAMA4G
-A1UdDwEB/wQEAwIBhjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6
-Ly9vY3NwLmRpZ2ljZXJ0LmNvbTB7BgNVHR8EdDByMDegNaAzhjFodHRwOi8vY3Js
-My5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3JsMDegNaAzhjFo
-dHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3Js
-MD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k
-aWdpY2VydC5jb20vQ1BTMB0GA1UdDgQWBBSQcds363PI79zVHhK2NLorWqCmkjAf
-BgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3RVTANBgkqhkiG9w0BAQUFAAOC
-AQEAMM7RlVEArgYLoQ4CwBestn+PIPZAdXQczHixpE/q9NDEnaLegQcmH0CIUfAf
-z7dMQJnQ9DxxmHOIlywZ126Ej6QfnFog41FcsMWemWpPyGn3EP9OrRnZyVizM64M
-2ZYpnnGycGOjtpkWQh1l8/egHn3F1GUUsmKE1GxcCAzYbJMrtHZZitF//wPYwl24
-LyLWOPD2nGt9RuuZdPfrSg6ppgTre87wXGuYMVqYQOtpxAX0IKjKCDplbDgV9Vws
-slXkLGtB8L5cRspKKaBIXiDSRf8F3jSvcEuBOeLKB1d8tjHcISnivpcOd5AUUUDh
-v+PMGxmcJcqnBrJT3yOyzxIZow==
+QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaME0xCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRpZ2lDZXJ0IFNIQTIg
+U2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANyuWJBNwcQwFZA1W248ghX1LFy949v/cUP6ZCWA1O4Yok3wZtAKc24RmDYXZK83
+nf36QYSvx6+M/hpzTc8zl5CilodTgyu5pnVILR1WN3vaMTIa16yrBvSqXUu3R0bd
+KpPDkC55gIDvEwRqFDu1m5K+wgdlTvza/P96rtxcflUxDOg5B6TXvi/TC2rSsd9f
+/ld0Uzs1gN2ujkSYs58O09rg1/RrKatEp0tYhG2SS4HD2nOLEpdIkARFdRrdNzGX
+kujNVA075ME/OV4uuPNcfhCOhkEAjUVmR7ChZc6gqikJTvOX6+guqw9ypzAO+sf0
+/RR3w6RbKFfCs/mC/bdFWJsCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8C
+AQAwDgYDVR0PAQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYY
+aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6
+Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwN6A1
+oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RD
+QS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v
+d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFA+AYRyCMWHVLyjnjUY4tCzh
+xtniMB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA0GCSqGSIb3DQEB
+CwUAA4IBAQAjPt9L0jFCpbZ+QlwaRMxp0Wi0XUvgBCFsS+JtzLHgl4+mUwnNqipl
+5TlPHoOlblyYoiQm5vuh7ZPHLgLGTUq/sELfeNqzqPlt/yGFUzZgTHbO7Djc1lGA
+8MXW5dRNJ2Srm8c+cftIl7gzbckTB+6WohsYFfZcTEDts8Ls/3HB40f/1LkAtDdC
+2iDJ6m6K7hQGrn2iWZiIqBtvLfTyyRRfJs8sjX7tN8Cp1Tm5gr8ZDOo0rwAhaPit
+c+LJMto4JQtV05od8GiG7S5BNO98pVAdvzr508EIDObtHopYJeS4d60tbvVS3bR0
+j6tJLp07kzQoH3jOlOrHvdPJbRzeXDLz
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFDTCCA/WgAwIBAgIQBZ8R1sZP2Lbc8x554UUQ2DANBgkqhkiG9w0BAQsFADBN
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E
+aWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTQxMTEwMDAwMDAwWhcN
+MTcxMTE0MTIwMDAwWjBlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
+bjEQMA4GA1UEBxMHU2VhdHRsZTEbMBkGA1UEChMSQ2hlZiBTb2Z0d2FyZSwgSW5j
+MRIwEAYDVQQDDAkqLmNoZWYuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC3xCIczkV10O5jTDpbd4YlPLC6kfnVoOkno2N/OOlcLQu3ulj/Lj1j4r6e
+2XthJLcFgTO+y+1/IKnnpLKDfkx1YngWEBXEBP+MrrpDUKKs053s45/bI9QBPISA
+tXgnYxMH9Glo6FWWd13TUq++OKGw1p1wazH64XK4MAf5y/lkmWXIWumNuO35ZqtB
+ME3wJISwVHzHB2CQjlDklt+Mb0APEiIFIZflgu9JNBYzLdvUtxiz15FUZQI7SsYL
+TfXOD1KBNMWqN8snG2e5gRAzB2D161DFvAZt8OiYUe+3QurNlTYVzeHv1ok6UqgM
+ZcLzg8m801rRip0D7FCGvMCU/ktdAgMBAAGjggHPMIIByzAfBgNVHSMEGDAWgBQP
+gGEcgjFh1S8o541GOLQs4cbZ4jAdBgNVHQ4EFgQUwldjw4Pb4HV+wxGZ7MSSRh+d
+pm4wHQYDVR0RBBYwFIIJKi5jaGVmLmlvggdjaGVmLmlvMA4GA1UdDwEB/wQEAwIF
+oDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwawYDVR0fBGQwYjAvoC2g
+K4YpaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NzY2Etc2hhMi1nMy5jcmwwL6At
+oCuGKWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zc2NhLXNoYTItZzMuY3JsMEIG
+A1UdIAQ7MDkwNwYJYIZIAYb9bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
+LmRpZ2ljZXJ0LmNvbS9DUFMwfAYIKwYBBQUHAQEEcDBuMCQGCCsGAQUFBzABhhho
+dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRgYIKwYBBQUHMAKGOmh0dHA6Ly9jYWNl
+cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJTZWN1cmVTZXJ2ZXJDQS5jcnQw
+DAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAvcTWenNuvvrhX2omm8LQ
+zWOuu8jqpoflACwD4lOSZ4TgOe4pQGCjXq8aRBD5k+goqQrPVf9lHnelUHFQac0Q
+5WT4YUmisUbF0S4uY5OGQymM52MvUWG4ODL4gaWhFvN+HAXrDPP/9iitsjV0QOnl
+CDq7Q4/XYRYW3opu5nLLbfW6v4QvF5yzZagEACGs7Vt32p6l391UcU8f6wiB3uMD
+eioCvjpv/+2YOUNlDPCM3uBubjUhHOwO817wBxXkzdk1OSRe4jzcw/uX6wL7birt
+fbaSkpilvVX529pSzB2Lvi9xWOoGMM578dpQ0h3PwhmmvKhhCWP+pI05k3oSkYCP
+ng==
-----END CERTIFICATE-----
diff --git a/spec/functional/provider/whyrun_safe_ruby_block_spec.rb b/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
index b3c2333e9a..2b582feb05 100644
--- a/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
+++ b/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
@@ -43,7 +43,7 @@ describe Chef::Resource::WhyrunSafeRubyBlock do
end
it "updates the evil laugh, even in why-run mode" do
- new_resource.run_action(new_resource.action)
+ Array(new_resource.action).each {|action| new_resource.run_action(action) }
expect($evil_global_evil_laugh).to eq(:mwahahaha)
expect(new_resource).to be_updated
end
diff --git a/spec/functional/rebooter_spec.rb b/spec/functional/rebooter_spec.rb
index 485e98f247..a0e2665de5 100644
--- a/spec/functional/rebooter_spec.rb
+++ b/spec/functional/rebooter_spec.rb
@@ -43,7 +43,7 @@ describe Chef::Platform::Rebooter do
let(:expected) do
{
- :windows => 'shutdown /r /t 5 /c "rebooter spec test"',
+ :windows => 'shutdown /r /t 300 /c "rebooter spec test"',
:linux => 'shutdown -r +5 "rebooter spec test"'
}
end
diff --git a/spec/functional/resource/package_spec.rb b/spec/functional/resource/package_spec.rb
index 5c17ca0107..8d37b072e8 100644
--- a/spec/functional/resource/package_spec.rb
+++ b/spec/functional/resource/package_spec.rb
@@ -386,5 +386,3 @@ describe Chef::Resource::Package, metadata do
end
end
-
-
diff --git a/spec/functional/resource/powershell_spec.rb b/spec/functional/resource/powershell_script_spec.rb
index 17ae8cbd2a..a8e51f4d9d 100644
--- a/spec/functional/resource/powershell_spec.rb
+++ b/spec/functional/resource/powershell_script_spec.rb
@@ -125,16 +125,16 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
expect { resource.run_action(:run) }.not_to raise_error
end
- it "raises an error if the script is not syntactically correct and returns is not set to 1" do
+ it "raises a Mixlib::ShellOut::ShellCommandFailed error if the script is not syntactically correct" do
resource.code('if({)')
resource.returns(0)
expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
end
- it "returns 1 if the script provided to the code attribute is not syntactically correct" do
+ it "raises an error if the script is not syntactically correct even if returns is set to 1 which is what powershell.exe returns for syntactically invalid scripts" do
resource.code('if({)')
resource.returns(1)
- expect { resource.run_action(:run) }.not_to raise_error
+ expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
end
# This somewhat ambiguous case, two failures of different types,
diff --git a/spec/functional/win32/registry_helper_spec.rb b/spec/functional/win32/registry_helper_spec.rb
index 7b070e6fe1..9ef6fd006f 100644
--- a/spec/functional/win32/registry_helper_spec.rb
+++ b/spec/functional/win32/registry_helper_spec.rb
@@ -130,6 +130,9 @@ describe 'Chef::Win32::Registry', :windows_only do
it "returns true if the value exists" do
expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals"})).to eq(true)
end
+ it "returns true if the value exists with a case mismatch on the value name" do
+ expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"petals"})).to eq(true)
+ end
it "returns false if the value does not exist" do
expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"FOOBAR"})).to eq(false)
end
@@ -145,6 +148,9 @@ describe 'Chef::Win32::Registry', :windows_only do
it "returns true if the value exists" do
expect(@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals"})).to eq(true)
end
+ it "returns true if the value exists with a case mismatch on the value name" do
+ expect(@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"petals"})).to eq(true)
+ end
it "throws an exception if the value does not exist" do
expect {@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"FOOBAR"})}.to raise_error(Chef::Exceptions::Win32RegValueMissing)
end
@@ -160,6 +166,9 @@ describe 'Chef::Win32::Registry', :windows_only do
it "returns true if all the data matches" do
expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(true)
end
+ it "returns true if all the data matches with a case mismatch on the data name" do
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(true)
+ end
it "returns false if the name does not exist" do
expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"slateP", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(false)
end
@@ -181,6 +190,9 @@ describe 'Chef::Win32::Registry', :windows_only do
it "returns true if all the data matches" do
expect(@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(true)
end
+ it "returns true if all the data matches with a case mismatch on the data name" do
+ expect(@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(true)
+ end
it "throws an exception if the name does not exist" do
expect {@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"slateP", :type=>:multi_string, :data=>["Pink", "Delicate"]})}.to raise_error(Chef::Exceptions::Win32RegDataMissing)
end
diff --git a/spec/functional/win32/service_manager_spec.rb b/spec/functional/win32/service_manager_spec.rb
index d2474deace..a1ce36146f 100644
--- a/spec/functional/win32/service_manager_spec.rb
+++ b/spec/functional/win32/service_manager_spec.rb
@@ -33,7 +33,7 @@ end
# directories.
#
-describe "Chef::Application::WindowsServiceManager", :windows_only, :system_windows_service_gem_only do
+describe "Chef::Application::WindowsServiceManager", :windows_only, :system_windows_service_gem_only, :appveyor_only do
include_context "using Win32::Service"
@@ -43,7 +43,7 @@ describe "Chef::Application::WindowsServiceManager", :windows_only, :system_wind
end
it "throws an error with required missing options" do
- test_service.each do |key,value|
+ [:service_name, :service_display_name, :service_description, :service_file_path].each do |key|
service_def = test_service.dup
service_def.delete(key)
diff --git a/spec/integration/knife/chef_repo_path_spec.rb b/spec/integration/knife/chef_repo_path_spec.rb
index 874b33901f..908657e5f7 100644
--- a/spec/integration/knife/chef_repo_path_spec.rb
+++ b/spec/integration/knife/chef_repo_path_spec.rb
@@ -24,6 +24,8 @@ describe 'chef_repo_path tests', :workstation do
include IntegrationSupport
include KnifeSupport
+ let(:error_rel_path_outside_repo) { /^ERROR: Attempt to use relative path '' when current directory is outside the repository path/ }
+
# TODO alternate repo_path / *_path
context 'alternate *_path' do
when_the_repository 'has clients and clients2, cookbooks and cookbooks2, etc.' do
@@ -109,14 +111,14 @@ EOM
context 'when cwd is at the top level' do
before { cwd '.' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
context 'when cwd is inside the data_bags directory' do
before { cwd 'data_bags' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
@@ -192,14 +194,14 @@ EOM
context 'when cwd is inside the data_bags directory' do
before { cwd 'data_bags' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
context 'when cwd is inside chef_repo2' do
before { cwd 'chef_repo2' }
it 'knife list -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
@@ -225,14 +227,14 @@ EOM
context 'when cwd is at the top level' do
before { cwd '.' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
context 'when cwd is inside the data_bags directory' do
before { cwd 'data_bags' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
@@ -445,7 +447,7 @@ EOM
context 'when cwd is at the top level' do
before { cwd '.' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
@@ -621,14 +623,14 @@ EOM
context 'when cwd is at the top level' do
before { cwd '.' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
context 'when cwd is inside the data_bags directory' do
before { cwd 'data_bags' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
@@ -782,7 +784,7 @@ EOM
context 'when cwd is at the top level' do
before { cwd '.' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
@@ -823,7 +825,7 @@ EOM
context 'when cwd is inside chef_repo2/data_bags' do
before { cwd 'chef_repo2/data_bags' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
end
diff --git a/spec/integration/recipes/recipe_dsl_spec.rb b/spec/integration/recipes/recipe_dsl_spec.rb
index 5426dce080..a4de6ae542 100644
--- a/spec/integration/recipes/recipe_dsl_spec.rb
+++ b/spec/integration/recipes/recipe_dsl_spec.rb
@@ -11,7 +11,7 @@ describe "Recipe DSL methods" do
before(:all) { Namer.current_index = 1 }
before { Namer.current_index += 1 }
- context "With resource 'base_thingy' declared as BaseThingy" do
+ context "with resource 'base_thingy' declared as BaseThingy" do
before(:context) {
class BaseThingy < Chef::Resource
@@ -19,6 +19,7 @@ describe "Recipe DSL methods" do
default_action :create
class<<self
+ attr_accessor :created_name
attr_accessor :created_resource
attr_accessor :created_provider
end
@@ -30,6 +31,7 @@ describe "Recipe DSL methods" do
def load_current_resource
end
def action_create
+ BaseThingy.created_name = new_resource.name
BaseThingy.created_resource = new_resource.class
BaseThingy.created_provider = self.class
end
@@ -47,12 +49,38 @@ describe "Recipe DSL methods" do
BaseThingy.created_provider = nil
end
+ it "creates base_thingy when you call base_thingy in a recipe" do
+ recipe = converge {
+ base_thingy 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_name).to eq 'blah'
+ expect(BaseThingy.created_resource).to eq BaseThingy
+ end
+
+ it "errors out when you call base_thingy do ... end in a recipe" do
+ expect_converge {
+ base_thingy do; end
+ }.to raise_error(ArgumentError, 'You must supply a name when declaring a base_thingy resource')
+ end
+
+ it "emits a warning when you call base_thingy 'foo', 'bar' do ... end in a recipe" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ recipe = converge {
+ base_thingy 'foo', 'bar' do
+ end
+ }
+ expect(recipe.logged_warnings).to match(/Cannot create resource base_thingy with more than one argument. All arguments except the name \("foo"\) will be ignored. This will cause an error in Chef 13. Arguments: \["foo", "bar"\]/)
+ expect(BaseThingy.created_name).to eq 'foo'
+ expect(BaseThingy.created_resource).to eq BaseThingy
+ end
+
context "Deprecated automatic resource DSL" do
before do
Chef::Config[:treat_deprecation_warnings_as_errors] = false
end
- context "With a resource 'backcompat_thingy' declared in Chef::Resource and Chef::Provider" do
+ context "with a resource 'backcompat_thingy' declared in Chef::Resource and Chef::Provider" do
before(:context) {
class Chef::Resource::BackcompatThingy < Chef::Resource
@@ -97,7 +125,7 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named RecipeDSLSpecNamespace::Bar::BarThingy" do
+ context "with a resource named RecipeDSLSpecNamespace::Bar::BarThingy" do
before(:context) {
class RecipeDSLSpecNamespace::Bar::BarThingy < BaseThingy
@@ -112,7 +140,7 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named Chef::Resource::NoNameThingy with resource_name nil" do
+ context "with a resource named Chef::Resource::NoNameThingy with resource_name nil" do
before(:context) {
class Chef::Resource::NoNameThingy < BaseThingy
@@ -128,7 +156,7 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named AnotherNoNameThingy with resource_name :another_thingy_name" do
+ context "with a resource named AnotherNoNameThingy with resource_name :another_thingy_name" do
before(:context) {
class AnotherNoNameThingy < BaseThingy
@@ -152,7 +180,7 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named AnotherNoNameThingy2 with resource_name :another_thingy_name2; resource_name :another_thingy_name3" do
+ context "with a resource named AnotherNoNameThingy2 with resource_name :another_thingy_name2; resource_name :another_thingy_name3" do
before(:context) {
class AnotherNoNameThingy2 < BaseThingy
@@ -184,7 +212,7 @@ describe "Recipe DSL methods" do
end
context "provides overriding resource_name" do
- context "With a resource named AnotherNoNameThingy3 with provides :another_no_name_thingy3, os: 'blarghle'" do
+ context "with a resource named AnotherNoNameThingy3 with provides :another_no_name_thingy3, os: 'blarghle'" do
before(:context) {
class AnotherNoNameThingy3 < BaseThingy
@@ -213,7 +241,7 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named AnotherNoNameThingy4 with two provides" do
+ context "with a resource named AnotherNoNameThingy4 with two provides" do
before(:context) {
class AnotherNoNameThingy4 < BaseThingy
@@ -253,7 +281,7 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named AnotherNoNameThingy5, a different resource_name, and a provides with the original resource_name" do
+ context "with a resource named AnotherNoNameThingy5, a different resource_name, and a provides with the original resource_name" do
before(:context) {
class AnotherNoNameThingy5 < BaseThingy
@@ -290,7 +318,7 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named AnotherNoNameThingy6, a provides with the original resource name, and a different resource_name" do
+ context "with a resource named AnotherNoNameThingy6, a provides with the original resource name, and a different resource_name" do
before(:context) {
class AnotherNoNameThingy6 < BaseThingy
@@ -327,7 +355,7 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named AnotherNoNameThingy7, a new resource_name, and provides with that new resource name" do
+ context "with a resource named AnotherNoNameThingy7, a new resource_name, and provides with that new resource name" do
before(:context) {
class AnotherNoNameThingy7 < BaseThingy
@@ -365,7 +393,7 @@ describe "Recipe DSL methods" do
end
# opposite order from the previous test (provides, then resource_name)
- context "With a resource named AnotherNoNameThingy8, a provides with a new resource name, and resource_name with that new resource name" do
+ context "with a resource named AnotherNoNameThingy8, a provides with a new resource name, and resource_name with that new resource name" do
before(:context) {
class AnotherNoNameThingy8 < BaseThingy
@@ -401,119 +429,6 @@ describe "Recipe DSL methods" do
}.to raise_error(NoMethodError)
end
end
-
- context "With a resource TwoClassesOneDsl" do
- let(:class_name) { "TwoClassesOneDsl#{Namer.current_index}" }
- let(:dsl_method) { :"two_classes_one_dsl#{Namer.current_index}" }
-
- before {
- eval <<-EOM, nil, __FILE__, __LINE__+1
- class #{class_name} < BaseThingy
- resource_name #{dsl_method.inspect}
- end
- EOM
- }
- context "and resource BlahModule::TwoClassesOneDsl" do
- before {
- eval <<-EOM, nil, __FILE__, __LINE__+1
- module BlahModule
- class #{class_name} < BaseThingy
- resource_name #{dsl_method.inspect}
- end
- end
- EOM
- }
- it "two_classes_one_dsl resolves to BlahModule::TwoClassesOneDsl (alphabetical)" do
- dsl_method = self.dsl_method
- recipe = converge {
- instance_eval("#{dsl_method} 'blah' do; end")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq eval("BlahModule::#{class_name}")
- end
- it "resource_matching_short_name returns BlahModule::TwoClassesOneDsl" do
- expect(Chef::Resource.resource_matching_short_name(dsl_method)).to eq eval("BlahModule::#{class_name}")
- end
- end
- context "and resource BlahModule::TwoClassesOneDsl with resource_name nil" do
- before {
- eval <<-EOM, nil, __FILE__, __LINE__+1
- module BlahModule
- class BlahModule::#{class_name} < BaseThingy
- resource_name nil
- end
- end
- EOM
- }
- it "two_classes_one_dsl resolves to ::TwoClassesOneDsl" do
- dsl_method = self.dsl_method
- recipe = converge {
- instance_eval("#{dsl_method} 'blah' do; end")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq eval("::#{class_name}")
- end
- it "resource_matching_short_name returns ::TwoClassesOneDsl" do
- expect(Chef::Resource.resource_matching_short_name(dsl_method)).to eq eval("::#{class_name}")
- end
- end
- context "and resource BlahModule::TwoClassesOneDsl with resource_name :argh" do
- before {
- eval <<-EOM, nil, __FILE__, __LINE__+1
- module BlahModule
- class BlahModule::#{class_name} < BaseThingy
- resource_name :argh
- end
- end
- EOM
- }
- it "two_classes_one_dsl resolves to ::TwoClassesOneDsl" do
- dsl_method = self.dsl_method
- recipe = converge {
- instance_eval("#{dsl_method} 'blah' do; end")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq eval("::#{class_name}")
- end
- it "resource_matching_short_name returns ::TwoClassesOneDsl" do
- expect(Chef::Resource.resource_matching_short_name(dsl_method)).to eq eval("::#{class_name}")
- end
- end
- context "and resource BlahModule::TwoClassesOneDsl with provides :two_classes_one_dsl, os: 'blarghle'" do
- before {
- eval <<-EOM, nil, __FILE__, __LINE__+1
- module BlahModule
- class BlahModule::#{class_name} < BaseThingy
- resource_name #{dsl_method.inspect}
- provides #{dsl_method.inspect}, os: 'blarghle'
- end
- end
- EOM
- }
-
- it "and os = blarghle, two_classes_one_dsl resolves to BlahModule::TwoClassesOneDsl" do
- dsl_method = self.dsl_method
- recipe = converge {
- # this is an ugly way to test, make Cheffish expose node attrs
- run_context.node.automatic[:os] = 'blarghle'
- instance_eval("#{dsl_method} 'blah' do; end")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq eval("BlahModule::#{class_name}")
- end
-
- it "and os = linux, two_classes_one_dsl resolves to ::TwoClassesOneDsl" do
- dsl_method = self.dsl_method
- recipe = converge {
- # this is an ugly way to test, make Cheffish expose node attrs
- run_context.node.automatic[:os] = 'linux'
- instance_eval("#{dsl_method} 'blah' do; end")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq eval("::#{class_name}")
- end
- end
- end
end
end
@@ -833,7 +748,7 @@ describe "Recipe DSL methods" do
end
end
- context "With platform-specific resources 'my_super_thingy_foo' and 'my_super_thingy_bar'" do
+ context "with platform-specific resources 'my_super_thingy_foo' and 'my_super_thingy_bar'" do
before(:context) {
class MySuperThingyFoo < BaseThingy
resource_name :my_super_thingy_foo
@@ -924,6 +839,601 @@ describe "Recipe DSL methods" do
end
end
end
+
+ context "with a resource named 'B' with resource name :two_classes_one_dsl" do
+ let(:two_classes_one_dsl) { :"two_classes_one_dsl#{Namer.current_index}" }
+ let(:resource_class) {
+ result = Class.new(BaseThingy) do
+ def self.name
+ "B"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.resource_name two_classes_one_dsl
+ result
+ }
+ before { resource_class } # pull on it so it gets defined before the recipe runs
+
+ context "and another resource named 'A' with resource_name :two_classes_one_dsl" do
+ let(:resource_class_a) {
+ result = Class.new(BaseThingy) do
+ def self.name
+ "A"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.resource_name two_classes_one_dsl
+ result
+ }
+ before { resource_class_a } # pull on it so it gets defined before the recipe runs
+
+ it "two_classes_one_dsl resolves to A (alphabetically earliest)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class_a
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class_a
+ end
+ end
+
+ context "and another resource named 'Z' with resource_name :two_classes_one_dsl" do
+ let(:resource_class_z) {
+ result = Class.new(BaseThingy) do
+ def self.name
+ "Z"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.resource_name two_classes_one_dsl
+ result
+ }
+ before { resource_class_z } # pull on it so it gets defined before the recipe runs
+
+ it "two_classes_one_dsl resolves to B (alphabetically earliest)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+
+ context "and a priority array [ Z, B ]" do
+ before do
+ Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class_z, resource_class ])
+ end
+
+ it "two_classes_one_dsl resolves to Z (respects the priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class_z
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+
+ context "when Z provides(:two_classes_one_dsl) { false }" do
+ before do
+ resource_class_z.provides(two_classes_one_dsl) { false }
+ end
+
+ it "two_classes_one_dsl resolves to B (picks the next thing in the priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+ end
+ end
+
+ context "and priority arrays [ B ] and [ Z ]" do
+ before do
+ Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class ])
+ Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class_z ])
+ end
+
+ it "two_classes_one_dsl resolves to Z (respects the most recent priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class_z
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+
+ context "when Z provides(:two_classes_one_dsl) { false }" do
+ before do
+ resource_class_z.provides(two_classes_one_dsl) { false }
+ end
+
+ it "two_classes_one_dsl resolves to B (picks the first match from the other priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+ end
+ end
+
+ context "and a priority array [ Z ]" do
+ before do
+ Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class_z ])
+ end
+
+ context "when Z provides(:two_classes_one_dsl) { false }" do
+ before do
+ resource_class_z.provides(two_classes_one_dsl) { false }
+ end
+
+ it "two_classes_one_dsl resolves to B (picks the first match outside the priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+ end
+ end
+
+ end
+
+ context "and a provider named 'B' which provides :two_classes_one_dsl" do
+ before do
+ resource_class.send(:define_method, :provider) { nil }
+ end
+
+ let(:provider_class) {
+ result = Class.new(BaseThingy::Provider) do
+ def self.name
+ "B"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.provides two_classes_one_dsl
+ result
+ }
+ before { provider_class } # pull on it so it gets defined before the recipe runs
+
+ context "and another provider named 'A'" do
+ let(:provider_class_a) {
+ result = Class.new(BaseThingy::Provider) do
+ def self.name
+ "A"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result
+ }
+ context "which provides :two_classes_one_dsl" do
+ before { provider_class_a.provides two_classes_one_dsl }
+
+ it "two_classes_one_dsl resolves to A (alphabetically earliest)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class_a
+ end
+ end
+ context "which provides(:two_classes_one_dsl) { false }" do
+ before { provider_class_a.provides(two_classes_one_dsl) { false } }
+
+ it "two_classes_one_dsl resolves to B (since A declined)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+ end
+ end
+
+ context "and another provider named 'Z'" do
+ let(:provider_class_z) {
+ result = Class.new(BaseThingy::Provider) do
+ def self.name
+ "Z"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result
+ }
+ before { provider_class_z } # pull on it so it gets defined before the recipe runs
+
+ context "which provides :two_classes_one_dsl" do
+ before { provider_class_z.provides two_classes_one_dsl }
+
+ it "two_classes_one_dsl resolves to B (alphabetically earliest)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+
+ context "with a priority array [ Z, B ]" do
+ before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class_z, provider_class ] }
+
+ it "two_classes_one_dsl resolves to Z (respects the priority map)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class_z
+ end
+ end
+ end
+
+ context "which provides(:two_classes_one_dsl) { false }" do
+ before { provider_class_z.provides(two_classes_one_dsl) { false } }
+
+ context "with a priority array [ Z, B ]" do
+ before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class_z, provider_class ] }
+
+ it "two_classes_one_dsl resolves to B (the next one in the priority map)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+ end
+
+ context "with priority arrays [ B ] and [ Z ]" do
+ before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class_z ] }
+ before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class ] }
+
+ it "two_classes_one_dsl resolves to B (the one in the next priority map)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+ end
+ end
+ end
+ end
+
+ context "and another resource Blarghle with provides :two_classes_one_dsl, os: 'blarghle'" do
+ let(:resource_class_blarghle) {
+ result = Class.new(BaseThingy) do
+ def self.name
+ "Blarghle"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.resource_name two_classes_one_dsl
+ result.provides two_classes_one_dsl, os: 'blarghle'
+ result
+ }
+ before { resource_class_blarghle } # pull on it so it gets defined before the recipe runs
+
+ it "on os = blarghle, two_classes_one_dsl resolves to Blarghle" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'blarghle'
+ instance_eval("#{two_classes_one_dsl} 'blah' do; end")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class_blarghle
+ end
+
+ it "on os = linux, two_classes_one_dsl resolves to B" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'linux'
+ instance_eval("#{two_classes_one_dsl} 'blah' do; end")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+ end
+ end
+
+ context "with a resource MyResource" do
+ let(:resource_class) { Class.new(BaseThingy) do
+ def self.called_provides
+ @called_provides
+ end
+ def to_s
+ "MyResource"
+ end
+ end }
+ let(:my_resource) { :"my_resource#{Namer.current_index}" }
+ let(:blarghle_blarghle_little_star) { :"blarghle_blarghle_little_star#{Namer.current_index}" }
+
+ context "with resource_name :my_resource" do
+ before {
+ resource_class.resource_name my_resource
+ }
+
+ context "with provides? returning true to my_resource" do
+ before {
+ my_resource = self.my_resource
+ resource_class.define_singleton_method(:provides?) do |node, resource_name|
+ @called_provides = true
+ resource_name == my_resource
+ end
+ }
+
+ it "my_resource returns the resource and calls provides?, but does not emit a warning" do
+ dsl_name = self.my_resource
+ recipe = converge {
+ instance_eval("#{dsl_name} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ expect(resource_class.called_provides).to be_truthy
+ end
+ end
+
+ context "with provides? returning true to blarghle_blarghle_little_star and not resource_name" do
+ before do
+ blarghle_blarghle_little_star = self.blarghle_blarghle_little_star
+ resource_class.define_singleton_method(:provides?) do |node, resource_name|
+ @called_provides = true
+ resource_name == blarghle_blarghle_little_star
+ end
+ end
+
+ it "my_resource does not return the resource" do
+ dsl_name = self.my_resource
+ expect_converge {
+ instance_eval("#{dsl_name} 'foo'")
+ }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ expect(resource_class.called_provides).to be_truthy
+ end
+
+ it "blarghle_blarghle_little_star 'foo' returns the resource and emits a warning" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ dsl_name = self.blarghle_blarghle_little_star
+ recipe = converge {
+ instance_eval("#{dsl_name} 'foo'")
+ }
+ expect(recipe.logged_warnings).to include "WARN: #{resource_class}.provides? returned true when asked if it provides DSL #{dsl_name}, but provides :#{dsl_name} was never called!"
+ expect(BaseThingy.created_resource).to eq resource_class
+ expect(resource_class.called_provides).to be_truthy
+ end
+ end
+
+ context "and a provider" do
+ let(:provider_class) do
+ Class.new(BaseThingy::Provider) do
+ def self.name
+ "MyProvider"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ def self.called_provides
+ @called_provides
+ end
+ end
+ end
+
+ before do
+ resource_class.send(:define_method, :provider) { nil }
+ end
+
+ context "that provides :my_resource" do
+ before do
+ provider_class.provides my_resource
+ end
+
+ context "with supports? returning true" do
+ before do
+ provider_class.define_singleton_method(:supports?) { |resource,action| true }
+ end
+
+ it "my_resource runs the provider and does not emit a warning" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+
+ context "and another provider supporting :my_resource with supports? false" do
+ let(:provider_class2) do
+ Class.new(BaseThingy::Provider) do
+ def self.name
+ "MyProvider2"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ def self.called_provides
+ @called_provides
+ end
+ provides my_resource
+ def self.supports?(resource, action)
+ false
+ end
+ end
+ end
+
+ it "my_resource runs the first provider" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+ end
+ end
+
+ context "with supports? returning false" do
+ before do
+ provider_class.define_singleton_method(:supports?) { |resource,action| false }
+ end
+
+ # TODO no warning? ick
+ it "my_resource runs the provider anyway" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+
+ context "and another provider supporting :my_resource with supports? true" do
+ let(:provider_class2) do
+ my_resource = self.my_resource
+ Class.new(BaseThingy::Provider) do
+ def self.name
+ "MyProvider2"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ def self.called_provides
+ @called_provides
+ end
+ provides my_resource
+ def self.supports?(resource, action)
+ true
+ end
+ end
+ end
+ before { provider_class2 } # make sure the provider class shows up
+
+ it "my_resource runs the other provider" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class2
+ end
+ end
+ end
+ end
+
+ context "with provides? returning true" do
+ before {
+ my_resource = self.my_resource
+ provider_class.define_singleton_method(:provides?) do |node, resource|
+ @called_provides = true
+ resource.declared_type == my_resource
+ end
+ }
+
+ context "that provides :my_resource" do
+ before {
+ provider_class.provides my_resource
+ }
+
+ it "my_resource calls the provider (and calls provides?), but does not emit a warning" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ expect(provider_class.called_provides).to be_truthy
+ end
+ end
+
+ context "that does not call provides :my_resource" do
+ it "my_resource calls the provider (and calls provides?), and emits a warning" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to include("WARN: #{provider_class}.provides? returned true when asked if it provides DSL #{my_resource}, but provides :#{my_resource} was never called!")
+ expect(BaseThingy.created_provider).to eq provider_class
+ expect(provider_class.called_provides).to be_truthy
+ end
+ end
+ end
+
+ context "with provides? returning false to my_resource" do
+ before {
+ my_resource = self.my_resource
+ provider_class.define_singleton_method(:provides?) do |node, resource|
+ @called_provides = true
+ false
+ end
+ }
+
+ context "that provides :my_resource" do
+ before {
+ provider_class.provides my_resource
+ }
+
+ it "my_resource fails to find a provider (and calls provides)" do
+ my_resource = self.my_resource
+ expect_converge {
+ instance_eval("#{my_resource} 'foo'")
+ }.to raise_error(Chef::Exceptions::ProviderNotFound)
+ expect(provider_class.called_provides).to be_truthy
+ end
+ end
+
+ context "that does not provide :my_resource" do
+ it "my_resource fails to find a provider (and calls provides)" do
+ my_resource = self.my_resource
+ expect_converge {
+ instance_eval("#{my_resource} 'foo'")
+ }.to raise_error(Chef::Exceptions::ProviderNotFound)
+ expect(provider_class.called_provides).to be_truthy
+ end
+ end
+ end
+ end
+ end
+ end
end
before(:all) { Namer.current_index = 0 }
diff --git a/spec/integration/recipes/resource_action_spec.rb b/spec/integration/recipes/resource_action_spec.rb
index 4786294803..cee79133a9 100644
--- a/spec/integration/recipes/resource_action_spec.rb
+++ b/spec/integration/recipes/resource_action_spec.rb
@@ -337,7 +337,7 @@ describe "Resource.action" do
NoActionJackson.action_was = action
end
}
- expect(NoActionJackson.action_was).to eq :nothing
+ expect(NoActionJackson.action_was).to eq [:nothing]
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index dcf244c3cc..3b55ba9de0 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -87,12 +87,14 @@ Dir["spec/support/**/*.rb"].
OHAI_SYSTEM = Ohai::System.new
OHAI_SYSTEM.all_plugins("platform")
-TEST_PLATFORM =
- (OHAI_SYSTEM['platform'] ||
- 'unknown_test_platform').dup.freeze
-TEST_PLATFORM_VERSION =
- (OHAI_SYSTEM['platform_version'] ||
- 'unknown_platform_version').dup.freeze
+test_node = Chef::Node.new
+test_node.automatic['os'] = (OHAI_SYSTEM['os'] || 'unknown_os').dup.freeze
+test_node.automatic['platform_family'] = (OHAI_SYSTEM['platform_family'] || 'unknown_platform_family').dup.freeze
+test_node.automatic['platform'] = (OHAI_SYSTEM['platform'] || 'unknown_platform').dup.freeze
+test_node.automatic['platform_version'] = (OHAI_SYSTEM['platform_version'] || 'unknown_platform_version').dup.freeze
+TEST_NODE = test_node.freeze
+TEST_PLATFORM = TEST_NODE['platform']
+TEST_PLATFORM_VERSION = TEST_NODE['platform_version']
RSpec.configure do |config|
config.include(Matchers)
@@ -116,6 +118,7 @@ RSpec.configure do |config|
config.filter_run_excluding :volatile_from_verify => false
config.filter_run_excluding :skip_appveyor => true if ENV["APPVEYOR"]
+ config.filter_run_excluding :appveyor_only => true unless ENV["APPVEYOR"]
config.filter_run_excluding :windows_only => true unless windows?
config.filter_run_excluding :not_supported_on_mac_osx_106 => true if mac_osx_106?
@@ -162,13 +165,17 @@ RSpec.configure do |config|
config.filter_run_excluding :provider => lambda {|criteria|
type, target_provider = criteria.first
- platform = TEST_PLATFORM.dup
- platform_version = TEST_PLATFORM_VERSION.dup
-
- begin
- provider_for_running_platform = Chef::Platform.find_provider(platform, platform_version, type)
- provider_for_running_platform != target_provider
- rescue ArgumentError # no provider for platform
+ node = TEST_NODE.dup
+ resource_class = Chef::ResourceResolver.resolve(type, node: node)
+ if resource_class
+ resource = resource_class.new('test', Chef::RunContext.new(node, nil, nil))
+ begin
+ provider = resource.provider_for_action(Array(resource_class.default_action).first)
+ provider.class != target_provider
+ rescue Chef::Exceptions::ProviderNotFound # no provider for platform
+ true
+ end
+ else
true
end
}
diff --git a/spec/support/shared/functional/win32_service.rb b/spec/support/shared/functional/win32_service.rb
index 7dd1920418..2ee1a8ad88 100644
--- a/spec/support/shared/functional/win32_service.rb
+++ b/spec/support/shared/functional/win32_service.rb
@@ -46,7 +46,8 @@ shared_context "using Win32::Service" do
:service_name => "spec-service",
:service_display_name => "Spec Test Service",
:service_description => "Service for testing Chef::Application::WindowsServiceManager.",
- :service_file_path => File.expand_path(File.join(File.dirname(__FILE__), '../../platforms/win32/spec_service.rb'))
+ :service_file_path => File.expand_path(File.join(File.dirname(__FILE__), '../../platforms/win32/spec_service.rb')),
+ :delayed_start => true
}
}
diff --git a/spec/support/shared/unit/api_versioning.rb b/spec/support/shared/unit/api_versioning.rb
index a4f353de60..05a4117f8e 100644
--- a/spec/support/shared/unit/api_versioning.rb
+++ b/spec/support/shared/unit/api_versioning.rb
@@ -26,7 +26,7 @@ shared_examples_for "version handling" do
allow(rest_v1).to receive(http_verb).and_raise(exception_406)
end
- context "when the server does not support the min or max server API version that Chef::User supports" do
+ context "when the server does not support the min or max server API version that Chef::UserV1 supports" do
before do
allow(object).to receive(:server_client_api_version_intersection).and_return([])
end
@@ -34,7 +34,7 @@ shared_examples_for "version handling" do
it "raises the original exception" do
expect{ object.send(method) }.to raise_error(exception_406)
end
- end # when the server does not support the min or max server API version that Chef::User supports
+ end # when the server does not support the min or max server API version that Chef::UserV1 supports
end # version handling
shared_examples_for "user and client reregister" do
diff --git a/spec/unit/api_client_spec.rb b/spec/unit/api_client_spec.rb
index bc4b38848b..a0e399b470 100644
--- a/spec/unit/api_client_spec.rb
+++ b/spec/unit/api_client_spec.rb
@@ -21,6 +21,11 @@ require 'spec_helper'
require 'chef/api_client'
require 'tempfile'
+# DEPRECATION NOTE
+#
+# This code will be removed in Chef 13 in favor of the code in Chef::ApiClientV1,
+# which will be moved to this namespace. New development should occur in
+# Chef::ApiClientV1 until the time before Chef 13.
describe Chef::ApiClient do
before(:each) do
@client = Chef::ApiClient.new
@@ -53,20 +58,6 @@ describe Chef::ApiClient do
expect { @client.admin(Hash.new) }.to raise_error(ArgumentError)
end
- it "has an create_key flag attribute" do
- @client.create_key(true)
- expect(@client.create_key).to be_truthy
- end
-
- it "create_key defaults to false" do
- expect(@client.create_key).to be_falsey
- end
-
- it "allows only boolean values for the create_key flag" do
- expect { @client.create_key(false) }.not_to raise_error
- expect { @client.create_key(Hash.new) }.to raise_error(ArgumentError)
- end
-
it "has a 'validator' flag attribute" do
@client.validator(true)
expect(@client.validator).to be_truthy
@@ -129,12 +120,6 @@ describe Chef::ApiClient do
expect(@json).to include(%q{"validator":false})
end
- it "includes the 'create_key' flag when present" do
- @client.create_key(true)
- @json = @client.to_json
- expect(@json).to include(%q{"create_key":true})
- end
-
it "includes the private key when present" do
@client.private_key("monkeypants")
expect(@client.to_json).to include(%q{"private_key":"monkeypants"})
@@ -143,15 +128,11 @@ describe Chef::ApiClient do
it "does not include the private key if not present" do
expect(@json).not_to include("private_key")
end
-
- include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
- let(:jsonable) { @client }
- end
end
describe "when deserializing from JSON (string) using ApiClient#from_json" do
let(:client_string) do
- "{\"name\":\"black\",\"public_key\":\"crowes\",\"private_key\":\"monkeypants\",\"admin\":true,\"validator\":true,\"create_key\":true}"
+ "{\"name\":\"black\",\"public_key\":\"crowes\",\"private_key\":\"monkeypants\",\"admin\":true,\"validator\":true}"
end
let(:client) do
@@ -178,10 +159,6 @@ describe Chef::ApiClient do
expect(client.admin).to be_truthy
end
- it "preserves the create_key status" do
- expect(client.create_key).to be_truthy
- end
-
it "preserves the 'validator' status" do
expect(client.validator).to be_truthy
end
@@ -199,7 +176,6 @@ describe Chef::ApiClient do
"private_key" => "monkeypants",
"admin" => true,
"validator" => true,
- "create_key" => true,
"json_class" => "Chef::ApiClient"
}
end
@@ -224,10 +200,6 @@ describe Chef::ApiClient do
expect(client.admin).to be_truthy
end
- it "preserves the create_key status" do
- expect(client.create_key).to be_truthy
- end
-
it "preserves the 'validator' status" do
expect(client.validator).to be_truthy
end
@@ -243,18 +215,16 @@ describe Chef::ApiClient do
before(:each) do
client = {
- "name" => "black",
- "clientname" => "black",
- "public_key" => "crowes",
- "private_key" => "monkeypants",
- "admin" => true,
- "create_key" => true,
- "validator" => true,
- "json_class" => "Chef::ApiClient"
+ "name" => "black",
+ "clientname" => "black",
+ "public_key" => "crowes",
+ "private_key" => "monkeypants",
+ "admin" => true,
+ "validator" => true,
+ "json_class" => "Chef::ApiClient"
}
-
- @http_client = double("Chef::REST mock")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
expect(@http_client).to receive(:get).with("clients/black").and_return(client)
@client = Chef::ApiClient.load(client['name'])
end
@@ -275,10 +245,6 @@ describe Chef::ApiClient do
expect(@client.admin).to be_a_kind_of(TrueClass)
end
- it "preserves the create_key status" do
- expect(@client.create_key).to be_a_kind_of(TrueClass)
- end
-
it "preserves the 'validator' status" do
expect(@client.validator).to be_a_kind_of(TrueClass)
end
@@ -304,18 +270,13 @@ describe Chef::ApiClient do
File.open(Chef::Config[:client_key], "r") {|f| f.read.chomp }
end
- it "has an HTTP client configured with default credentials" do
- expect(@client.http_api).to be_a_kind_of(Chef::REST)
- expect(@client.http_api.client_name).to eq("silent-bob")
- expect(@client.http_api.signing_key.to_s).to eq(private_key_data)
- end
end
describe "when requesting a new key" do
before do
@http_client = double("Chef::REST mock")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
end
context "and the client does not exist on the server" do
@@ -332,34 +293,24 @@ describe Chef::ApiClient do
end
context "and the client exists" do
- let(:chef_rest_v0_mock) { double('chef rest root v0 object') }
- let(:payload) {
- {:name => "lost-my-key", :admin => false, :validator => false, :private_key => true}
- }
-
before do
@api_client_without_key = Chef::ApiClient.new
@api_client_without_key.name("lost-my-key")
- allow(@api_client_without_key).to receive(:chef_rest_v0).and_return(chef_rest_v0_mock)
- #allow(@api_client_with_key).to receive(:http_api).and_return(_api_mock)
-
- allow(chef_rest_v0_mock).to receive(:put).with("clients/lost-my-key", payload).and_return(@api_client_with_key)
- allow(chef_rest_v0_mock).to receive(:get).with("clients/lost-my-key").and_return(@api_client_without_key)
- allow(@http_client).to receive(:get).with("clients/lost-my-key").and_return(@api_client_without_key)
+ expect(@http_client).to receive(:get).with("clients/lost-my-key").and_return(@api_client_without_key)
end
+
context "and the client exists on a Chef 11-like server" do
before do
@api_client_with_key = Chef::ApiClient.new
@api_client_with_key.name("lost-my-key")
@api_client_with_key.private_key("the new private key")
- allow(@api_client_with_key).to receive(:chef_rest_v0).and_return(chef_rest_v0_mock)
+ expect(@http_client).to receive(:put).
+ with("clients/lost-my-key", :name => "lost-my-key", :admin => false, :validator => false, :private_key => true).
+ and_return(@api_client_with_key)
end
it "returns an ApiClient with a private key" do
- expect(chef_rest_v0_mock).to receive(:put).with("clients/lost-my-key", payload).
- and_return(@api_client_with_key)
-
response = Chef::ApiClient.reregister("lost-my-key")
# no sane == method for ApiClient :'(
expect(response).to eq(@api_client_without_key)
@@ -372,7 +323,7 @@ describe Chef::ApiClient do
context "and the client exists on a Chef 10-like server" do
before do
@api_client_with_key = {"name" => "lost-my-key", "private_key" => "the new private key"}
- expect(chef_rest_v0_mock).to receive(:put).
+ expect(@http_client).to receive(:put).
with("clients/lost-my-key", :name => "lost-my-key", :admin => false, :validator => false, :private_key => true).
and_return(@api_client_with_key)
end
@@ -390,134 +341,4 @@ describe Chef::ApiClient do
end
end
-
- describe "Versioned API Interactions" do
- let(:response_406) { OpenStruct.new(:code => '406') }
- let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
- let(:payload) {
- {
- :name => "some_name",
- :validator => true,
- :admin => true
- }
- }
-
- before do
- @client = Chef::ApiClient.new
- allow(@client).to receive(:chef_rest_v0).and_return(double('chef rest root v0 object'))
- allow(@client).to receive(:chef_rest_v1).and_return(double('chef rest root v1 object'))
- @client.name "some_name"
- @client.validator true
- @client.admin true
- end
-
- describe "create" do
-
- # from spec/support/shared/unit/user_and_client_shared.rb
- it_should_behave_like "user or client create" do
- let(:object) { @client }
- let(:error) { Chef::Exceptions::InvalidClientAttribute }
- let(:rest_v0) { @client.chef_rest_v0 }
- let(:rest_v1) { @client.chef_rest_v1 }
- let(:url) { "clients" }
- end
-
- context "when API V1 is not supported by the server" do
- # from spec/support/shared/unit/api_versioning.rb
- it_should_behave_like "version handling" do
- let(:object) { @client }
- let(:method) { :create }
- let(:http_verb) { :post }
- let(:rest_v1) { @client.chef_rest_v1 }
- end
- end
-
- end # create
-
- describe "update" do
- context "when a valid client is defined" do
-
- shared_examples_for "client updating" do
- it "updates the client" do
- expect(rest). to receive(:put).with("clients/some_name", payload)
- @client.update
- end
-
- context "when only the name field exists" do
-
- before do
- # needed since there is no way to set to nil via code
- @client.instance_variable_set(:@validator, nil)
- @client.instance_variable_set(:@admin, nil)
- end
-
- after do
- @client.validator true
- @client.admin true
- end
-
- it "updates the client with only the name" do
- expect(rest). to receive(:put).with("clients/some_name", {:name => "some_name"})
- @client.update
- end
- end
-
- end
-
- context "when API V1 is supported by the server" do
-
- it_should_behave_like "client updating" do
- let(:rest) { @client.chef_rest_v1 }
- end
-
- end # when API V1 is supported by the server
-
- context "when API V1 is not supported by the server" do
- context "when no version is supported" do
- # from spec/support/shared/unit/api_versioning.rb
- it_should_behave_like "version handling" do
- let(:object) { @client }
- let(:method) { :create }
- let(:http_verb) { :post }
- let(:rest_v1) { @client.chef_rest_v1 }
- end
- end # when no version is supported
-
- context "when API V0 is supported" do
-
- before do
- allow(@client.chef_rest_v1).to receive(:put).and_raise(exception_406)
- allow(@client).to receive(:server_client_api_version_intersection).and_return([0])
- end
-
- it_should_behave_like "client updating" do
- let(:rest) { @client.chef_rest_v0 }
- end
-
- end
-
- end # when API V1 is not supported by the server
- end # when a valid client is defined
- end # update
-
- # DEPRECATION
- # This can be removed after API V0 support is gone
- describe "reregister" do
- context "when server API V0 is valid on the Chef Server receiving the request" do
- it "creates a new object via the API" do
- expect(@client.chef_rest_v0).to receive(:put).with("clients/#{@client.name}", payload.merge({:private_key => true})).and_return({})
- @client.reregister
- end
- end # when server API V0 is valid on the Chef Server receiving the request
-
- context "when server API V0 is not supported by the Chef Server" do
- # from spec/support/shared/unit/api_versioning.rb
- it_should_behave_like "user and client reregister" do
- let(:object) { @client }
- let(:rest_v0) { @client.chef_rest_v0 }
- end
- end # when server API V0 is not supported by the Chef Server
- end # reregister
-
- end
end
diff --git a/spec/unit/api_client_v1_spec.rb b/spec/unit/api_client_v1_spec.rb
new file mode 100644
index 0000000000..17aba8c3af
--- /dev/null
+++ b/spec/unit/api_client_v1_spec.rb
@@ -0,0 +1,457 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+require 'chef/api_client_v1'
+require 'tempfile'
+
+describe Chef::ApiClientV1 do
+ before(:each) do
+ @client = Chef::ApiClientV1.new
+ end
+
+ it "has a name attribute" do
+ @client.name("ops_master")
+ expect(@client.name).to eq("ops_master")
+ end
+
+ it "does not allow spaces in the name" do
+ expect { @client.name "ops master" }.to raise_error(ArgumentError)
+ end
+
+ it "only allows string values for the name" do
+ expect { @client.name Hash.new }.to raise_error(ArgumentError)
+ end
+
+ it "has an admin flag attribute" do
+ @client.admin(true)
+ expect(@client.admin).to be_truthy
+ end
+
+ it "defaults to non-admin" do
+ expect(@client.admin).to be_falsey
+ end
+
+ it "allows only boolean values for the admin flag" do
+ expect { @client.admin(false) }.not_to raise_error
+ expect { @client.admin(Hash.new) }.to raise_error(ArgumentError)
+ end
+
+ it "has an create_key flag attribute" do
+ @client.create_key(true)
+ expect(@client.create_key).to be_truthy
+ end
+
+ it "create_key defaults to false" do
+ expect(@client.create_key).to be_falsey
+ end
+
+ it "allows only boolean values for the create_key flag" do
+ expect { @client.create_key(false) }.not_to raise_error
+ expect { @client.create_key(Hash.new) }.to raise_error(ArgumentError)
+ end
+
+ it "has a 'validator' flag attribute" do
+ @client.validator(true)
+ expect(@client.validator).to be_truthy
+ end
+
+ it "defaults to non-validator" do
+ expect(@client.validator).to be_falsey
+ end
+
+ it "allows only boolean values for the 'validator' flag" do
+ expect { @client.validator(false) }.not_to raise_error
+ expect { @client.validator(Hash.new) }.to raise_error(ArgumentError)
+ end
+
+ it "has a public key attribute" do
+ @client.public_key("super public")
+ expect(@client.public_key).to eq("super public")
+ end
+
+ it "accepts only String values for the public key" do
+ expect { @client.public_key "" }.not_to raise_error
+ expect { @client.public_key Hash.new }.to raise_error(ArgumentError)
+ end
+
+
+ it "has a private key attribute" do
+ @client.private_key("super private")
+ expect(@client.private_key).to eq("super private")
+ end
+
+ it "accepts only String values for the private key" do
+ expect { @client.private_key "" }.not_to raise_error
+ expect { @client.private_key Hash.new }.to raise_error(ArgumentError)
+ end
+
+ describe "when serializing to JSON" do
+ before(:each) do
+ @client.name("black")
+ @client.public_key("crowes")
+ @json = @client.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 public key value" do
+ expect(@json).to include(%{"public_key":"crowes"})
+ end
+
+ it "includes the 'admin' flag" do
+ expect(@json).to include(%q{"admin":false})
+ end
+
+ it "includes the 'validator' flag" do
+ expect(@json).to include(%q{"validator":false})
+ end
+
+ it "includes the 'create_key' flag when present" do
+ @client.create_key(true)
+ @json = @client.to_json
+ expect(@json).to include(%q{"create_key":true})
+ end
+
+ it "includes the private key when present" do
+ @client.private_key("monkeypants")
+ expect(@client.to_json).to include(%q{"private_key":"monkeypants"})
+ end
+
+ it "does not include the private key if not present" do
+ expect(@json).not_to include("private_key")
+ end
+
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
+ let(:jsonable) { @client }
+ end
+ end
+
+ describe "when deserializing from JSON (string) using ApiClient#from_json" do
+ let(:client_string) do
+ "{\"name\":\"black\",\"public_key\":\"crowes\",\"private_key\":\"monkeypants\",\"admin\":true,\"validator\":true,\"create_key\":true}"
+ end
+
+ let(:client) do
+ Chef::ApiClientV1.from_json(client_string)
+ end
+
+ it "does not require a 'json_class' string" do
+ expect(Chef::JSONCompat.parse(client_string)["json_class"]).to eq(nil)
+ end
+
+ it "should deserialize to a Chef::ApiClientV1 object" do
+ expect(client).to be_a_kind_of(Chef::ApiClientV1)
+ end
+
+ it "preserves the name" do
+ expect(client.name).to eq("black")
+ end
+
+ it "preserves the public key" do
+ expect(client.public_key).to eq("crowes")
+ end
+
+ it "preserves the admin status" do
+ expect(client.admin).to be_truthy
+ end
+
+ it "preserves the create_key status" do
+ expect(client.create_key).to be_truthy
+ end
+
+ it "preserves the 'validator' status" do
+ expect(client.validator).to be_truthy
+ end
+
+ it "includes the private key if present" do
+ expect(client.private_key).to eq("monkeypants")
+ end
+ end
+
+ describe "when deserializing from JSON (hash) using ApiClientV1#from_json" do
+ let(:client_hash) do
+ {
+ "name" => "black",
+ "public_key" => "crowes",
+ "private_key" => "monkeypants",
+ "admin" => true,
+ "validator" => true,
+ "create_key" => true
+ }
+ end
+
+ let(:client) do
+ Chef::ApiClientV1.from_json(Chef::JSONCompat.to_json(client_hash))
+ end
+
+ it "should deserialize to a Chef::ApiClientV1 object" do
+ expect(client).to be_a_kind_of(Chef::ApiClientV1)
+ end
+
+ it "preserves the name" do
+ expect(client.name).to eq("black")
+ end
+
+ it "preserves the public key" do
+ expect(client.public_key).to eq("crowes")
+ end
+
+ it "preserves the admin status" do
+ expect(client.admin).to be_truthy
+ end
+
+ it "preserves the create_key status" do
+ expect(client.create_key).to be_truthy
+ end
+
+ it "preserves the 'validator' status" do
+ expect(client.validator).to be_truthy
+ end
+
+ it "includes the private key if present" do
+ expect(client.private_key).to eq("monkeypants")
+ end
+ end
+
+ describe "when loading from JSON" do
+ before do
+ end
+
+ before(:each) do
+ client = {
+ "name" => "black",
+ "clientname" => "black",
+ "public_key" => "crowes",
+ "private_key" => "monkeypants",
+ "admin" => true,
+ "create_key" => true,
+ "validator" => true
+ }
+
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
+ expect(@http_client).to receive(:get).with("clients/black").and_return(client)
+ @client = Chef::ApiClientV1.load(client['name'])
+ end
+
+ it "should deserialize to a Chef::ApiClientV1 object" do
+ expect(@client).to be_a_kind_of(Chef::ApiClientV1)
+ end
+
+ it "preserves the name" do
+ expect(@client.name).to eq("black")
+ end
+
+ it "preserves the public key" do
+ expect(@client.public_key).to eq("crowes")
+ end
+
+ it "preserves the admin status" do
+ expect(@client.admin).to be_a_kind_of(TrueClass)
+ end
+
+ it "preserves the create_key status" do
+ expect(@client.create_key).to be_a_kind_of(TrueClass)
+ end
+
+ it "preserves the 'validator' status" do
+ expect(@client.validator).to be_a_kind_of(TrueClass)
+ end
+
+ it "includes the private key if present" do
+ expect(@client.private_key).to eq("monkeypants")
+ end
+
+ end
+
+ describe "with correctly configured API credentials" do
+ before do
+ Chef::Config[:node_name] = "silent-bob"
+ Chef::Config[:client_key] = File.expand_path('ssl/private_key.pem', CHEF_SPEC_DATA)
+ end
+
+ after do
+ Chef::Config[:node_name] = nil
+ Chef::Config[:client_key] = nil
+ end
+
+ let :private_key_data do
+ File.open(Chef::Config[:client_key], "r") {|f| f.read.chomp }
+ end
+
+ end
+
+
+ describe "when requesting a new key" do
+ before do
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
+ end
+
+ context "and the client does not exist on the server" do
+ before do
+ @a_404_response = Net::HTTPNotFound.new("404 not found and such", nil, nil)
+ @a_404_exception = Net::HTTPServerException.new("404 not found exception", @a_404_response)
+
+ expect(@http_client).to receive(:get).with("clients/lost-my-key").and_raise(@a_404_exception)
+ end
+
+ it "raises a 404 error" do
+ expect { Chef::ApiClientV1.reregister("lost-my-key") }.to raise_error(Net::HTTPServerException)
+ end
+ end
+ end
+
+ describe "Versioned API Interactions" do
+ let(:response_406) { OpenStruct.new(:code => '406') }
+ let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+ let(:payload) {
+ {
+ :name => "some_name",
+ :validator => true,
+ :admin => true
+ }
+ }
+
+ before do
+ @client = Chef::ApiClientV1.new
+ allow(@client).to receive(:chef_rest_v0).and_return(double('chef rest root v0 object'))
+ allow(@client).to receive(:chef_rest_v1).and_return(double('chef rest root v1 object'))
+ @client.name "some_name"
+ @client.validator true
+ @client.admin true
+ end
+
+ describe "create" do
+
+ # from spec/support/shared/unit/user_and_client_shared.rb
+ it_should_behave_like "user or client create" do
+ let(:object) { @client }
+ let(:error) { Chef::Exceptions::InvalidClientAttribute }
+ let(:rest_v0) { @client.chef_rest_v0 }
+ let(:rest_v1) { @client.chef_rest_v1 }
+ let(:url) { "clients" }
+ end
+
+ context "when API V1 is not supported by the server" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "version handling" do
+ let(:object) { @client }
+ let(:method) { :create }
+ let(:http_verb) { :post }
+ let(:rest_v1) { @client.chef_rest_v1 }
+ end
+ end
+
+ end # create
+
+ describe "update" do
+ context "when a valid client is defined" do
+
+ shared_examples_for "client updating" do
+ it "updates the client" do
+ expect(rest). to receive(:put).with("clients/some_name", payload).and_return(payload)
+ @client.update
+ end
+
+ context "when only the name field exists" do
+
+ before do
+ # needed since there is no way to set to nil via code
+ @client.instance_variable_set(:@validator, nil)
+ @client.instance_variable_set(:@admin, nil)
+ end
+
+ after do
+ @client.validator true
+ @client.admin true
+ end
+
+ it "updates the client with only the name" do
+ expect(rest). to receive(:put).with("clients/some_name", {:name => "some_name"}).and_return({:name => "some_name"})
+ @client.update
+ end
+ end
+
+ end
+
+ context "when API V1 is supported by the server" do
+
+ it_should_behave_like "client updating" do
+ let(:rest) { @client.chef_rest_v1 }
+ end
+
+ end # when API V1 is supported by the server
+
+ context "when API V1 is not supported by the server" do
+ context "when no version is supported" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "version handling" do
+ let(:object) { @client }
+ let(:method) { :create }
+ let(:http_verb) { :post }
+ let(:rest_v1) { @client.chef_rest_v1 }
+ end
+ end # when no version is supported
+
+ context "when API V0 is supported" do
+
+ before do
+ allow(@client.chef_rest_v1).to receive(:put).and_raise(exception_406)
+ allow(@client).to receive(:server_client_api_version_intersection).and_return([0])
+ end
+
+ it_should_behave_like "client updating" do
+ let(:rest) { @client.chef_rest_v0 }
+ end
+
+ end
+
+ end # when API V1 is not supported by the server
+ end # when a valid client is defined
+ end # update
+
+ # DEPRECATION
+ # This can be removed after API V0 support is gone
+ describe "reregister" do
+ context "when server API V0 is valid on the Chef Server receiving the request" do
+ it "creates a new object via the API" do
+ expect(@client.chef_rest_v0).to receive(:put).with("clients/#{@client.name}", payload.merge({:private_key => true})).and_return({})
+ @client.reregister
+ end
+ end # when server API V0 is valid on the Chef Server receiving the request
+
+ context "when server API V0 is not supported by the Chef Server" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "user and client reregister" do
+ let(:object) { @client }
+ let(:rest_v0) { @client.chef_rest_v0 }
+ end
+ end # when server API V0 is not supported by the Chef Server
+ end # reregister
+
+ end
+end
diff --git a/spec/unit/application/solo_spec.rb b/spec/unit/application/solo_spec.rb
index 1785ecfc86..7013bfa0bc 100644
--- a/spec/unit/application/solo_spec.rb
+++ b/spec/unit/application/solo_spec.rb
@@ -106,7 +106,8 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
describe "when the recipe_url configuration option is specified" do
let(:tarfile) { StringIO.new("remote_tarball_content") }
let(:target_file) { StringIO.new }
-
+ let(:shellout) { double(run_command: nil, error!: nil, stdout: '') }
+
before do
Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks"
Chef::Config[:recipe_url] = "http://junglist.gen.nz/recipes.tgz"
@@ -117,7 +118,7 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
allow(app).to receive(:open).with("http://junglist.gen.nz/recipes.tgz").and_yield(tarfile)
allow(File).to receive(:open).with("#{Dir.tmpdir}/chef-solo/recipes.tgz", "wb").and_yield(target_file)
- allow(Chef::Mixin::Command).to receive(:run_command).and_return(true)
+ allow(Mixlib::ShellOut).to receive(:new).and_return(shellout)
end
it "should create the recipes path based on the parent of the cookbook path" do
@@ -136,7 +137,7 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
end
it "should untar the target file to the parent of the cookbook path" do
- expect(Chef::Mixin::Command).to receive(:run_command).with({:command => "tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo"}).and_return(true)
+ expect(Mixlib::ShellOut).to receive(:new).with("tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo")
app.reconfigure
end
end
diff --git a/spec/unit/chef_class_spec.rb b/spec/unit/chef_class_spec.rb
index 2528246be6..d6bf74572e 100644
--- a/spec/unit/chef_class_spec.rb
+++ b/spec/unit/chef_class_spec.rb
@@ -88,4 +88,27 @@ describe "Chef class" do
expect(Chef.node).to eql(node)
end
end
+
+ context '#event_handler' do
+ it 'adds a new handler' do
+ x = 1
+ Chef.event_handler do
+ on :converge_start do
+ x = 2
+ end
+ end
+ expect(Chef::Config[:event_handlers]).to_not be_empty
+ Chef::Config[:event_handlers].first.send(:converge_start)
+ expect(x).to eq(2)
+ end
+
+ it 'raise error if unknown event type is passed' do
+ expect do
+ Chef.event_handler do
+ on :yolo do
+ end
+ end
+ end.to raise_error(Chef::Exceptions::InvalidEventType)
+ end
+ end
end
diff --git a/spec/unit/chef_fs/path_util_spec.rb b/spec/unit/chef_fs/path_util_spec.rb
new file mode 100644
index 0000000000..42eb126dfb
--- /dev/null
+++ b/spec/unit/chef_fs/path_util_spec.rb
@@ -0,0 +1,108 @@
+#
+# Author:: Kartik Null Cating-Subramanian (<ksubramanian@chef.io>)
+# Copyright:: Copyright (c) 2015 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/chef_fs/path_utils'
+
+describe Chef::ChefFS::PathUtils do
+ context 'invoking join' do
+ it 'joins well-behaved distinct path elements' do
+ expect(Chef::ChefFS::PathUtils.join('a', 'b', 'c')).to eq('a/b/c')
+ end
+
+ it 'strips extraneous slashes in the middle of paths' do
+ expect(Chef::ChefFS::PathUtils.join('a/', '/b', '/c/')).to eq('a/b/c')
+ expect(Chef::ChefFS::PathUtils.join('a/', '/b', '///c/')).to eq('a/b/c')
+ end
+
+ it 'preserves the whether the first element was absolute or not' do
+ expect(Chef::ChefFS::PathUtils.join('/a/', '/b', 'c/')).to eq('/a/b/c')
+ expect(Chef::ChefFS::PathUtils.join('///a/', '/b', 'c/')).to eq('/a/b/c')
+ end
+ end
+
+ context 'invoking is_absolute?' do
+ it 'confirms that paths starting with / are absolute' do
+ expect(Chef::ChefFS::PathUtils.is_absolute?('/foo/bar/baz')).to be true
+ expect(Chef::ChefFS::PathUtils.is_absolute?('/foo')).to be true
+ end
+
+ it 'confirms that paths starting with // are absolute even though that looks like some windows network path' do
+ expect(Chef::ChefFS::PathUtils.is_absolute?('//foo/bar/baz')).to be true
+ end
+
+ it 'confirms that root is indeed absolute' do
+ expect(Chef::ChefFS::PathUtils.is_absolute?('/')).to be true
+ end
+
+ it 'confirms that paths starting without / are relative' do
+ expect(Chef::ChefFS::PathUtils.is_absolute?('foo/bar/baz')).to be false
+ expect(Chef::ChefFS::PathUtils.is_absolute?('a')).to be false
+ end
+
+ it 'returns false for an empty path.' do
+ expect(Chef::ChefFS::PathUtils.is_absolute?('')).to be false
+ end
+ end
+
+ context 'invoking realest_path' do
+ let(:good_path) { File.dirname(__FILE__) }
+ let(:parent_path) { File.dirname(good_path) }
+
+ it 'handles paths with no wildcards or globs' do
+ expect(Chef::ChefFS::PathUtils.realest_path(good_path)).to eq(File.expand_path(good_path))
+ end
+
+ it 'handles paths with .. and .' do
+ expect(Chef::ChefFS::PathUtils.realest_path(good_path+'/../.')).to eq(File.expand_path(parent_path))
+ end
+
+ it 'handles paths with *' do
+ expect(Chef::ChefFS::PathUtils.realest_path(good_path + '/*/foo')).to eq(File.expand_path(good_path + '/*/foo'))
+ end
+
+ it 'handles directories that do not exist' do
+ expect(Chef::ChefFS::PathUtils.realest_path(good_path + '/something/or/other')).to eq(File.expand_path(good_path + '/something/or/other'))
+ end
+
+ it 'handles root correctly' do
+ if Chef::Platform.windows?
+ expect(Chef::ChefFS::PathUtils.realest_path('C:/')).to eq('C:/')
+ else
+ expect(Chef::ChefFS::PathUtils.realest_path('/')).to eq('/')
+ end
+ end
+ end
+
+ context 'invoking descendant_path' do
+ it 'handles paths with various casing on windows' do
+ allow(Chef::ChefFS).to receive(:windows?) { true }
+ expect(Chef::ChefFS::PathUtils.descendant_path('C:/ab/b/c', 'C:/AB/B')).to eq('c')
+ expect(Chef::ChefFS::PathUtils.descendant_path('C:/ab/b/c', 'c:/ab/B')).to eq('c')
+ end
+
+ it 'returns nil if the path does not have the given ancestor' do
+ expect(Chef::ChefFS::PathUtils.descendant_path('/D/E/F', '/A/B/C')).to be_nil
+ expect(Chef::ChefFS::PathUtils.descendant_path('/A/B/D', '/A/B/C')).to be_nil
+ end
+
+ it 'returns blank if the ancestor equals the path' do
+ expect(Chef::ChefFS::PathUtils.descendant_path('/A/B/D', '/A/B/D')).to eq('')
+ end
+ end
+end
diff --git a/spec/unit/event_dispatch/dsl_spec.rb b/spec/unit/event_dispatch/dsl_spec.rb
new file mode 100644
index 0000000000..f467ea81ea
--- /dev/null
+++ b/spec/unit/event_dispatch/dsl_spec.rb
@@ -0,0 +1,87 @@
+#
+# Author:: Ranjib Dey (<ranjib@linux.com>)
+#
+# Copyright:: Copyright (c) 2015 Ranjib Dey
+# 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/event_dispatch/dsl'
+
+describe Chef::EventDispatch::DSL do
+ let(:events) do
+ Chef::EventDispatch::Dispatcher.new
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, nil, events)
+ end
+
+ before do
+ Chef.set_run_context(run_context)
+ end
+
+ after do
+ Chef.reset!
+ end
+
+ subject{ described_class.new('test') }
+
+ it 'set handler name' do
+ subject.on(:run_started) {}
+ expect(events.subscribers.first.name).to eq('test')
+ end
+
+ it 'raise error when invalid event type is supplied' do
+ expect do
+ subject.on(:foo_bar) {}
+ end.to raise_error(Chef::Exceptions::InvalidEventType)
+ end
+
+ it 'register user hooks against valid event type' do
+ subject.on(:run_failed) {'testhook'}
+ expect(events.subscribers.first.run_failed).to eq('testhook')
+ end
+
+ it 'preserve state across event hooks' do
+ calls = []
+ Chef.event_handler do
+ on :resource_updated do
+ calls << :updated
+ end
+ on :resource_action_start do
+ calls << :started
+ end
+ end
+ resource = Chef::Resource::RubyBlock.new('foo', run_context)
+ resource.block { }
+ resource.run_action(:run)
+ expect(calls).to eq([:started, :updated])
+ end
+
+ it 'preserve instance variables across handler callbacks' do
+ Chef.event_handler do
+ on :resource_action_start do
+ @ivar = [1]
+ end
+ on :resource_updated do
+ @ivar << 2
+ end
+ end
+ resource = Chef::Resource::RubyBlock.new('foo', run_context)
+ resource.block { }
+ resource.run_action(:run)
+ expect(events.subscribers.first.instance_variable_get(:@ivar)).to eq([1, 2])
+ end
+end
diff --git a/spec/unit/json_compat_spec.rb b/spec/unit/json_compat_spec.rb
index aaf0c48806..fd6469c146 100644
--- a/spec/unit/json_compat_spec.rb
+++ b/spec/unit/json_compat_spec.rb
@@ -72,8 +72,6 @@ describe Chef::JSONCompat do
end
end
- # On FreeBSD 10.1 i386 rspec fails with a SystemStackError loading the expect line with more that 252 entries
- # https://github.com/chef/chef/issues/3101
describe "with the file with 252 or less nested entries" do
let(:json) { IO.read(File.join(CHEF_SPEC_DATA, 'nested.json')) }
let(:hash) { Chef::JSONCompat.from_json(json) }
@@ -84,7 +82,10 @@ describe Chef::JSONCompat do
end
it "should has 'test' as a 252 nested value" do
- expect(hash['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']).to eq('test')
+ v = 252.times.inject(hash) do |memo, _|
+ memo['key']
+ end
+ expect(v).to eq('test')
end
end
end
diff --git a/spec/unit/knife/client_bulk_delete_spec.rb b/spec/unit/knife/client_bulk_delete_spec.rb
index 45bb4dd16c..1a6317ac00 100644
--- a/spec/unit/knife/client_bulk_delete_spec.rb
+++ b/spec/unit/knife/client_bulk_delete_spec.rb
@@ -45,7 +45,7 @@ describe Chef::Knife::ClientBulkDelete do
clients = Hash.new
nonvalidator_client_names.each do |client_name|
- client = Chef::ApiClient.new()
+ client = Chef::ApiClientV1.new()
client.name(client_name)
allow(client).to receive(:destroy).and_return(true)
clients[client_name] = client
@@ -59,7 +59,7 @@ describe Chef::Knife::ClientBulkDelete do
clients = Hash.new
validator_client_names.each do |validator_client_name|
- validator_client = Chef::ApiClient.new()
+ validator_client = Chef::ApiClientV1.new()
validator_client.name(validator_client_name)
allow(validator_client).to receive(:validator).and_return(true)
allow(validator_client).to receive(:destroy).and_return(true)
@@ -75,7 +75,7 @@ describe Chef::Knife::ClientBulkDelete do
}
before(:each) do
- allow(Chef::ApiClient).to receive(:list).and_return(clients)
+ allow(Chef::ApiClientV1).to receive(:list).and_return(clients)
end
describe "run" do
@@ -89,7 +89,7 @@ describe Chef::Knife::ClientBulkDelete do
describe "with any clients" do
it "should get the list of the clients" do
- expect(Chef::ApiClient).to receive(:list)
+ expect(Chef::ApiClientV1).to receive(:list)
knife.run
end
diff --git a/spec/unit/knife/client_create_spec.rb b/spec/unit/knife/client_create_spec.rb
index 8fecfc885f..a1dcc564e2 100644
--- a/spec/unit/knife/client_create_spec.rb
+++ b/spec/unit/knife/client_create_spec.rb
@@ -34,7 +34,7 @@ describe Chef::Knife::ClientCreate do
end
let(:client) do
- Chef::ApiClient.new
+ Chef::ApiClientV1.new
end
let(:knife) do
diff --git a/spec/unit/knife/client_delete_spec.rb b/spec/unit/knife/client_delete_spec.rb
index 0fb5e0bab7..619009979b 100644
--- a/spec/unit/knife/client_delete_spec.rb
+++ b/spec/unit/knife/client_delete_spec.rb
@@ -30,7 +30,7 @@ describe Chef::Knife::ClientDelete do
describe 'run' do
it 'should delete the client' do
- expect(@knife).to receive(:delete_object).with(Chef::ApiClient, 'adam', 'client')
+ expect(@knife).to receive(:delete_object).with(Chef::ApiClientV1, 'adam', 'client')
@knife.run
end
@@ -46,8 +46,8 @@ describe Chef::Knife::ClientDelete do
before(:each) do
allow(Chef::Knife::UI).to receive(:confirm).and_return(true)
allow(@knife).to receive(:confirm).and_return(true)
- @client = Chef::ApiClient.new
- expect(Chef::ApiClient).to receive(:load).and_return(@client)
+ @client = Chef::ApiClientV1.new
+ expect(Chef::ApiClientV1).to receive(:load).and_return(@client)
end
it 'should delete non-validator client if --delete-validators is not set' do
diff --git a/spec/unit/knife/client_edit_spec.rb b/spec/unit/knife/client_edit_spec.rb
index c040c5e2f2..ad56d9212d 100644
--- a/spec/unit/knife/client_edit_spec.rb
+++ b/spec/unit/knife/client_edit_spec.rb
@@ -17,16 +17,29 @@
#
require 'spec_helper'
+require 'chef/api_client_v1'
describe Chef::Knife::ClientEdit do
before(:each) do
@knife = Chef::Knife::ClientEdit.new
@knife.name_args = [ 'adam' ]
+ @knife.config[:disable_editing] = true
end
describe 'run' do
+ let(:data) {
+ {
+ "name" => "adam",
+ "validator" => false,
+ "admin" => false,
+ "chef_type" => "client",
+ "create_key" => true
+ }
+ }
+
it 'should edit the client' do
- expect(@knife).to receive(:edit_object).with(Chef::ApiClient, 'adam')
+ allow(Chef::ApiClientV1).to receive(:load).with('adam').and_return(data)
+ expect(@knife).to receive(:edit_data).with(data).and_return(data)
@knife.run
end
diff --git a/spec/unit/knife/client_list_spec.rb b/spec/unit/knife/client_list_spec.rb
index eff01da4e9..ce0fa4f5e8 100644
--- a/spec/unit/knife/client_list_spec.rb
+++ b/spec/unit/knife/client_list_spec.rb
@@ -26,7 +26,7 @@ describe Chef::Knife::ClientList do
describe 'run' do
it 'should list the clients' do
- expect(Chef::ApiClient).to receive(:list)
+ expect(Chef::ApiClientV1).to receive(:list)
expect(@knife).to receive(:format_list_for_display)
@knife.run
end
diff --git a/spec/unit/knife/client_reregister_spec.rb b/spec/unit/knife/client_reregister_spec.rb
index f1be4ed570..7e763242e4 100644
--- a/spec/unit/knife/client_reregister_spec.rb
+++ b/spec/unit/knife/client_reregister_spec.rb
@@ -41,7 +41,7 @@ describe Chef::Knife::ClientReregister do
context 'when not configured for file output' do
it 'reregisters the client and prints the key' do
- expect(Chef::ApiClient).to receive(:reregister).with('adam').and_return(@client_mock)
+ expect(Chef::ApiClientV1).to receive(:reregister).with('adam').and_return(@client_mock)
@knife.run
expect(@stdout.string).to match( /foo_key/ )
end
@@ -49,7 +49,7 @@ describe Chef::Knife::ClientReregister do
context 'when configured for file output' do
it 'should write the private key to a file' do
- expect(Chef::ApiClient).to receive(:reregister).with('adam').and_return(@client_mock)
+ expect(Chef::ApiClientV1).to receive(:reregister).with('adam').and_return(@client_mock)
@knife.config[:file] = '/tmp/monkeypants'
filehandle = StringIO.new
diff --git a/spec/unit/knife/client_show_spec.rb b/spec/unit/knife/client_show_spec.rb
index 8404e8d019..73a876cee0 100644
--- a/spec/unit/knife/client_show_spec.rb
+++ b/spec/unit/knife/client_show_spec.rb
@@ -27,7 +27,7 @@ describe Chef::Knife::ClientShow do
describe 'run' do
it 'should list the client' do
- expect(Chef::ApiClient).to receive(:load).with('adam').and_return(@client_mock)
+ expect(Chef::ApiClientV1).to receive(:load).with('adam').and_return(@client_mock)
expect(@knife).to receive(:format_for_display).with(@client_mock)
@knife.run
end
@@ -37,7 +37,7 @@ describe Chef::Knife::ClientShow do
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
fake_client_contents = {"foo"=>"bar", "baz"=>"qux"}
- expect(Chef::ApiClient).to receive(:load).with('adam').and_return(fake_client_contents)
+ expect(Chef::ApiClientV1).to receive(:load).with('adam').and_return(fake_client_contents)
@knife.run
expect(@stdout.string).to eql("{\n \"foo\": \"bar\",\n \"baz\": \"qux\"\n}\n")
end
diff --git a/spec/unit/knife/osc_user_create_spec.rb b/spec/unit/knife/osc_user_create_spec.rb
index 1b17d0d22f..e4ed78fe2b 100644
--- a/spec/unit/knife/osc_user_create_spec.rb
+++ b/spec/unit/knife/osc_user_create_spec.rb
@@ -36,19 +36,19 @@ describe Chef::Knife::OscUserCreate do
@knife.name_args = [ 'a_user' ]
@knife.config[:user_password] = "foobar"
- @user = Chef::OscUser.new
+ @user = Chef::User.new
@user.name "a_user"
- @user_with_private_key = Chef::OscUser.new
+ @user_with_private_key = Chef::User.new
@user_with_private_key.name "a_user"
@user_with_private_key.private_key 'private_key'
allow(@user).to receive(:create).and_return(@user_with_private_key)
- allow(Chef::OscUser).to receive(:new).and_return(@user)
- allow(Chef::OscUser).to receive(:from_hash).and_return(@user)
+ allow(Chef::User).to receive(:new).and_return(@user)
+ allow(Chef::User).to receive(:from_hash).and_return(@user)
allow(@knife).to receive(:edit_data).and_return(@user.to_hash)
end
it "creates a new user" do
- expect(Chef::OscUser).to receive(:new).and_return(@user)
+ expect(Chef::User).to receive(:new).and_return(@user)
expect(@user).to receive(:create)
@knife.run
expect(@stderr.string).to match /created user.+a_user/i
diff --git a/spec/unit/knife/osc_user_delete_spec.rb b/spec/unit/knife/osc_user_delete_spec.rb
index 0e16393ffe..4a3ec4228f 100644
--- a/spec/unit/knife/osc_user_delete_spec.rb
+++ b/spec/unit/knife/osc_user_delete_spec.rb
@@ -31,7 +31,7 @@ describe Chef::Knife::OscUserDelete do
end
it 'deletes the user' do
- expect(@knife).to receive(:delete_object).with(Chef::OscUser, 'my_user')
+ expect(@knife).to receive(:delete_object).with(Chef::User, 'my_user')
@knife.run
end
diff --git a/spec/unit/knife/osc_user_edit_spec.rb b/spec/unit/knife/osc_user_edit_spec.rb
index 71a9192389..279f2e30ef 100644
--- a/spec/unit/knife/osc_user_edit_spec.rb
+++ b/spec/unit/knife/osc_user_edit_spec.rb
@@ -38,7 +38,7 @@ describe Chef::Knife::OscUserEdit do
it 'loads and edits the user' do
data = { :name => "my_user" }
- allow(Chef::OscUser).to receive(:load).with("my_user").and_return(data)
+ allow(Chef::User).to receive(:load).with("my_user").and_return(data)
expect(@knife).to receive(:edit_data).with(data).and_return(data)
@knife.run
end
diff --git a/spec/unit/knife/osc_user_list_spec.rb b/spec/unit/knife/osc_user_list_spec.rb
index 59a15be058..f496a414b8 100644
--- a/spec/unit/knife/osc_user_list_spec.rb
+++ b/spec/unit/knife/osc_user_list_spec.rb
@@ -30,7 +30,7 @@ describe Chef::Knife::OscUserList do
end
it 'lists the users' do
- expect(Chef::OscUser).to receive(:list)
+ expect(Chef::User).to receive(:list)
expect(@knife).to receive(:format_list_for_display)
@knife.run
end
diff --git a/spec/unit/knife/osc_user_reregister_spec.rb b/spec/unit/knife/osc_user_reregister_spec.rb
index 406bbf1f3e..989eb180f1 100644
--- a/spec/unit/knife/osc_user_reregister_spec.rb
+++ b/spec/unit/knife/osc_user_reregister_spec.rb
@@ -29,7 +29,7 @@ describe Chef::Knife::OscUserReregister do
@knife = Chef::Knife::OscUserReregister.new
@knife.name_args = [ 'a_user' ]
@user_mock = double('user_mock', :private_key => "private_key")
- allow(Chef::OscUser).to receive(:load).and_return(@user_mock)
+ allow(Chef::User).to receive(:load).and_return(@user_mock)
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
end
diff --git a/spec/unit/knife/osc_user_show_spec.rb b/spec/unit/knife/osc_user_show_spec.rb
index 67b9b45809..18d2086099 100644
--- a/spec/unit/knife/osc_user_show_spec.rb
+++ b/spec/unit/knife/osc_user_show_spec.rb
@@ -32,7 +32,7 @@ describe Chef::Knife::OscUserShow do
end
it 'loads and displays the user' do
- expect(Chef::OscUser).to receive(:load).with('my_user').and_return(@user_mock)
+ expect(Chef::User).to receive(:load).with('my_user').and_return(@user_mock)
expect(@knife).to receive(:format_for_display).with(@user_mock)
@knife.run
end
diff --git a/spec/unit/knife/ssl_check_spec.rb b/spec/unit/knife/ssl_check_spec.rb
index 8eda555108..fd46c47d99 100644
--- a/spec/unit/knife/ssl_check_spec.rb
+++ b/spec/unit/knife/ssl_check_spec.rb
@@ -163,6 +163,7 @@ E
expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs (no warn)
expect(ssl_socket).to receive(:connect) # no error
expect(ssl_socket).to receive(:post_connection_check).with("foo.example.com") # no error
+ expect(ssl_socket).to receive(:hostname=).with("foo.example.com") # no error
end
it "prints a success message" do
@@ -197,6 +198,7 @@ E
expect(ssl_socket).to receive(:post_connection_check).
with("foo.example.com").
and_raise(OpenSSL::SSL::SSLError)
+ expect(ssl_socket).to receive(:hostname=).with("foo.example.com") # no error
expect(ssl_socket_for_debug).to receive(:connect)
expect(ssl_socket_for_debug).to receive(:peer_cert).and_return(self_signed_crt)
end
@@ -215,6 +217,8 @@ E
expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs
expect(ssl_socket).to receive(:connect).
and_raise(OpenSSL::SSL::SSLError)
+ expect(ssl_socket).to receive(:hostname=).
+ with("foo.example.com") # no error
expect(ssl_socket_for_debug).to receive(:connect)
expect(ssl_socket_for_debug).to receive(:peer_cert).and_return(self_signed_crt)
end
diff --git a/spec/unit/knife/user_create_spec.rb b/spec/unit/knife/user_create_spec.rb
index 49d62cc2d7..fa5c8324b4 100644
--- a/spec/unit/knife/user_create_spec.rb
+++ b/spec/unit/knife/user_create_spec.rb
@@ -186,7 +186,7 @@ describe Chef::Knife::UserCreate do
context "when a private_key is returned" do
before do
- allow(knife).to receive(:create_user_from_hash).and_return(Chef::User.from_hash(knife.user.to_hash.merge({"private_key" => "some_private_key"})))
+ allow(knife).to receive(:create_user_from_hash).and_return(Chef::UserV1.from_hash(knife.user.to_hash.merge({"private_key" => "some_private_key"})))
end
context "when --file is passed" do
diff --git a/spec/unit/knife/user_delete_spec.rb b/spec/unit/knife/user_delete_spec.rb
index e49c781358..a24160624a 100644
--- a/spec/unit/knife/user_delete_spec.rb
+++ b/spec/unit/knife/user_delete_spec.rb
@@ -26,7 +26,7 @@ describe Chef::Knife::UserDelete do
before(:each) do
Chef::Knife::UserDelete.load_deps
knife.name_args = [ 'my_user' ]
- allow(Chef::User).to receive(:load).and_return(user)
+ allow(Chef::UserV1).to receive(:load).and_return(user)
allow(user).to receive(:username).and_return('my_user')
allow(knife.ui).to receive(:stderr).and_return(stdout)
allow(knife.ui).to receive(:stdout).and_return(stdout)
@@ -51,7 +51,7 @@ describe Chef::Knife::UserDelete do
end
it 'deletes the user' do
- #expect(knife).to receive(:delete_object).with(Chef::User, 'my_user')
+ #expect(knife).to receive(:delete_object).with(Chef::UserV1, 'my_user')
expect(knife).to receive(:delete_object).with('my_user')
knife.run
end
diff --git a/spec/unit/knife/user_edit_spec.rb b/spec/unit/knife/user_edit_spec.rb
index 15a7726b20..a21d982d29 100644
--- a/spec/unit/knife/user_edit_spec.rb
+++ b/spec/unit/knife/user_edit_spec.rb
@@ -36,7 +36,7 @@ describe Chef::Knife::UserEdit do
context "when the username field is not supported by the server" do
before do
allow(knife).to receive(:run_osc_11_user_edit).and_raise(SystemExit)
- allow(Chef::User).to receive(:load).and_return({"username" => nil})
+ allow(Chef::UserV1).to receive(:load).and_return({"username" => nil})
end
it "displays the osc warning" do
@@ -52,7 +52,7 @@ describe Chef::Knife::UserEdit do
it 'loads and edits the user' do
data = { "username" => "my_user" }
- allow(Chef::User).to receive(:load).with("my_user").and_return(data)
+ allow(Chef::UserV1).to receive(:load).with("my_user").and_return(data)
expect(knife).to receive(:edit_data).with(data).and_return(data)
knife.run
end
diff --git a/spec/unit/knife/user_list_spec.rb b/spec/unit/knife/user_list_spec.rb
index 9990cc802d..fa2bac426e 100644
--- a/spec/unit/knife/user_list_spec.rb
+++ b/spec/unit/knife/user_list_spec.rb
@@ -29,7 +29,7 @@ describe Chef::Knife::UserList do
end
it 'lists the users' do
- expect(Chef::User).to receive(:list)
+ expect(Chef::UserV1).to receive(:list)
expect(knife).to receive(:format_list_for_display)
knife.run
end
diff --git a/spec/unit/knife/user_reregister_spec.rb b/spec/unit/knife/user_reregister_spec.rb
index 412a6ec374..89aa6726cd 100644
--- a/spec/unit/knife/user_reregister_spec.rb
+++ b/spec/unit/knife/user_reregister_spec.rb
@@ -26,7 +26,7 @@ describe Chef::Knife::UserReregister do
before do
Chef::Knife::UserReregister.load_deps
knife.name_args = [ 'a_user' ]
- allow(Chef::User).to receive(:load).and_return(user_mock)
+ allow(Chef::UserV1).to receive(:load).and_return(user_mock)
allow(knife.ui).to receive(:stdout).and_return(stdout)
allow(knife.ui).to receive(:stderr).and_return(stdout)
allow(user_mock).to receive(:username).and_return('a_user')
diff --git a/spec/unit/knife/user_show_spec.rb b/spec/unit/knife/user_show_spec.rb
index 43392a3a5c..7c39e428c0 100644
--- a/spec/unit/knife/user_show_spec.rb
+++ b/spec/unit/knife/user_show_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Knife::UserShow do
context "when the username field is not supported by the server" do
before do
allow(knife).to receive(:run_osc_11_user_show).and_raise(SystemExit)
- allow(Chef::User).to receive(:load).with('my_user').and_return(user_mock)
+ allow(Chef::UserV1).to receive(:load).with('my_user').and_return(user_mock)
allow(user_mock).to receive(:username).and_return(nil)
end
@@ -51,7 +51,7 @@ describe Chef::Knife::UserShow do
end
it 'loads and displays the user' do
- expect(Chef::User).to receive(:load).with('my_user').and_return(user_mock)
+ expect(Chef::UserV1).to receive(:load).with('my_user').and_return(user_mock)
expect(knife).to receive(:format_for_display).with(user_mock)
knife.run
end
diff --git a/spec/unit/lwrp_spec.rb b/spec/unit/lwrp_spec.rb
index 784ff966cd..bcb64cb21e 100644
--- a/spec/unit/lwrp_spec.rb
+++ b/spec/unit/lwrp_spec.rb
@@ -163,7 +163,7 @@ describe "LWRP" do
it "Should load the old content, and not the new" do
resource = Chef::ResourceResolver.resolve(:lwrp_foo)
expect(resource).to eq @original_resource
- expect(resource.default_action).to eq(:pass_buck)
+ expect(resource.default_action).to eq([:pass_buck])
expect(Chef.method_defined?(:method_created_by_override_lwrp_foo)).to be_falsey
end
end
@@ -177,10 +177,6 @@ describe "LWRP" do
end
end
- it "should load the resource into a properly-named class and emit a warning when it is initialized" do
- expect { Chef::Resource::LwrpFoo.new('hi') }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
- end
-
it "should be resolvable with Chef::ResourceResolver.resolve(:lwrp_foo)" do
expect(Chef::ResourceResolver.resolve(:lwrp_foo, node: Chef::Node.new)).to eq(get_lwrp(:lwrp_foo))
end
@@ -202,7 +198,7 @@ describe "LWRP" do
end
it "should set the specified action as the default action" do
- expect(get_lwrp(:lwrp_foo).new("blah").action).to eq(:pass_buck)
+ expect(get_lwrp(:lwrp_foo).new("blah").action).to eq([:pass_buck])
end
it "should create a method for each attribute" do
@@ -228,127 +224,6 @@ describe "LWRP" do
expect(cls.node[:penguin_name]).to eql("jackass")
end
- context "resource class created" do
- before do
- @old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
- Chef::Config[:treat_deprecation_warnings_as_errors] = false
- end
- after do
- Chef::Config[:treat_deprecation_warnings_as_errors] = @old_treat_deprecation_warnings_as_errors
- end
-
- it "should load the resource into a properly-named class" do
- expect(Chef::Resource::LwrpFoo).to be_kind_of(Class)
- expect(Chef::Resource::LwrpFoo <= Chef::Resource::LWRPBase).to be_truthy
- end
-
- it "get_lwrp(:lwrp_foo).new is a Chef::Resource::LwrpFoo" do
- lwrp = get_lwrp(:lwrp_foo).new('hi')
- expect(lwrp.kind_of?(Chef::Resource::LwrpFoo)).to be_truthy
- expect(lwrp.is_a?(Chef::Resource::LwrpFoo)).to be_truthy
- expect(get_lwrp(:lwrp_foo) === lwrp).to be_truthy
- expect(Chef::Resource::LwrpFoo === lwrp).to be_truthy
- end
-
- it "Chef::Resource::LwrpFoo.new is a get_lwrp(:lwrp_foo)" do
- lwrp = Chef::Resource::LwrpFoo.new('hi')
- expect(lwrp.kind_of?(get_lwrp(:lwrp_foo))).to be_truthy
- expect(lwrp.is_a?(get_lwrp(:lwrp_foo))).to be_truthy
- expect(get_lwrp(:lwrp_foo) === lwrp).to be_truthy
- expect(Chef::Resource::LwrpFoo === lwrp).to be_truthy
- end
-
- it "works even if LwrpFoo exists in the top level" do
- module ::LwrpFoo
- end
- expect(Chef::Resource::LwrpFoo).not_to eq(::LwrpFoo)
- end
-
- context "with a subclass of get_lwrp(:lwrp_foo)" do
- let(:subclass) do
- Class.new(get_lwrp(:lwrp_foo))
- end
-
- it "subclass.new is a subclass" do
- lwrp = subclass.new('hi')
- expect(lwrp.kind_of?(subclass)).to be_truthy
- expect(lwrp.is_a?(subclass)).to be_truthy
- expect(subclass === lwrp).to be_truthy
- expect(lwrp.class === subclass)
- end
- it "subclass.new is a Chef::Resource::LwrpFoo" do
- lwrp = subclass.new('hi')
- expect(lwrp.kind_of?(Chef::Resource::LwrpFoo)).to be_truthy
- expect(lwrp.is_a?(Chef::Resource::LwrpFoo)).to be_truthy
- expect(Chef::Resource::LwrpFoo === lwrp).to be_truthy
- expect(lwrp.class === Chef::Resource::LwrpFoo)
- end
- it "subclass.new is a get_lwrp(:lwrp_foo)" do
- lwrp = subclass.new('hi')
- expect(lwrp.kind_of?(get_lwrp(:lwrp_foo))).to be_truthy
- expect(lwrp.is_a?(get_lwrp(:lwrp_foo))).to be_truthy
- expect(get_lwrp(:lwrp_foo) === lwrp).to be_truthy
- expect(lwrp.class === get_lwrp(:lwrp_foo))
- end
- it "Chef::Resource::LwrpFoo.new is *not* a subclass" do
- lwrp = Chef::Resource::LwrpFoo.new('hi')
- expect(lwrp.kind_of?(subclass)).to be_falsey
- expect(lwrp.is_a?(subclass)).to be_falsey
- expect(subclass === lwrp.class).to be_falsey
- expect(subclass === Chef::Resource::LwrpFoo).to be_falsey
- end
- it "get_lwrp(:lwrp_foo).new is *not* a subclass" do
- lwrp = get_lwrp(:lwrp_foo).new('hi')
- expect(lwrp.kind_of?(subclass)).to be_falsey
- expect(lwrp.is_a?(subclass)).to be_falsey
- expect(subclass === lwrp.class).to be_falsey
- expect(subclass === get_lwrp(:lwrp_foo)).to be_falsey
- end
- end
-
- context "with a subclass of Chef::Resource::LwrpFoo" do
- let(:subclass) do
- Class.new(Chef::Resource::LwrpFoo)
- end
-
- it "subclass.new is a subclass" do
- lwrp = subclass.new('hi')
- expect(lwrp.kind_of?(subclass)).to be_truthy
- expect(lwrp.is_a?(subclass)).to be_truthy
- expect(subclass === lwrp).to be_truthy
- expect(lwrp.class === subclass)
- end
- it "subclass.new is a Chef::Resource::LwrpFoo" do
- lwrp = subclass.new('hi')
- expect(lwrp.kind_of?(Chef::Resource::LwrpFoo)).to be_truthy
- expect(lwrp.is_a?(Chef::Resource::LwrpFoo)).to be_truthy
- expect(Chef::Resource::LwrpFoo === lwrp).to be_truthy
- expect(lwrp.class === Chef::Resource::LwrpFoo)
- end
- it "subclass.new is a get_lwrp(:lwrp_foo)" do
- lwrp = subclass.new('hi')
- expect(lwrp.kind_of?(get_lwrp(:lwrp_foo))).to be_truthy
- expect(lwrp.is_a?(get_lwrp(:lwrp_foo))).to be_truthy
- expect(get_lwrp(:lwrp_foo) === lwrp).to be_truthy
- expect(lwrp.class === get_lwrp(:lwrp_foo))
- end
- it "Chef::Resource::LwrpFoo.new is *not* a subclass" do
- lwrp = Chef::Resource::LwrpFoo.new('hi')
- expect(lwrp.kind_of?(subclass)).to be_falsey
- expect(lwrp.is_a?(subclass)).to be_falsey
- expect(subclass === lwrp.class).to be_falsey
- expect(subclass === Chef::Resource::LwrpFoo).to be_falsey
- end
- it "get_lwrp(:lwrp_foo).new is *not* a subclass" do
- lwrp = get_lwrp(:lwrp_foo).new('hi')
- expect(lwrp.kind_of?(subclass)).to be_falsey
- expect(lwrp.is_a?(subclass)).to be_falsey
- expect(subclass === lwrp.class).to be_falsey
- expect(subclass === get_lwrp(:lwrp_foo)).to be_falsey
- end
- end
- end
-
context "resource_name" do
let(:klass) { Class.new(Chef::Resource::LWRPBase) }
@@ -419,7 +294,7 @@ describe "LWRP" do
end
it "delegates #default_action to the parent" do
- expect(child.default_action).to eq(:eat)
+ expect(child.default_action).to eq([:eat])
end
end
@@ -436,7 +311,7 @@ describe "LWRP" do
end
it "does not delegate #default_action to the parent" do
- expect(child.default_action).to eq(:dont_eat)
+ expect(child.default_action).to eq([:dont_eat])
end
end
@@ -564,7 +439,7 @@ describe "LWRP" do
end
it "sets itself as a provider for a resource of the same name" do
- found_providers = Chef::Platform::ProviderPriorityMap.instance.list_handlers(node, :lwrp_buck_passer)
+ found_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :lwrp_buck_passer)
# we bypass the per-file loading to get the file to load each time,
# which creates the LWRP class repeatedly. New things get prepended to
# the list of providers.
@@ -576,7 +451,7 @@ describe "LWRP" do
let(:lwrp_cookbok_name) { "l_w_r_p" }
it "sets itself as a provider for a resource of the same name" do
- found_providers = Chef::Platform::ProviderPriorityMap.instance.list_handlers(node, :l_w_r_p_buck_passer)
+ found_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :l_w_r_p_buck_passer)
expect(found_providers.size).to eq(1)
expect(found_providers.last).to eq(get_lwrp_provider(:l_w_r_p_buck_passer))
end
@@ -587,10 +462,10 @@ describe "LWRP" do
let(:lwrp_cookbok_name) { "l-w-r-p" }
it "sets itself as a provider for a resource of the same name" do
- incorrect_providers = Chef::Platform::ProviderPriorityMap.instance.list_handlers(node, :'l-w-r-p_buck_passer')
+ incorrect_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :'l-w-r-p_buck_passer')
expect(incorrect_providers).to eq([])
- found_providers = Chef::Platform::ProviderPriorityMap.instance.list_handlers(node, :l_w_r_p_buck_passer)
+ found_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :l_w_r_p_buck_passer)
expect(found_providers.first).to eq(get_lwrp_provider(:l_w_r_p_buck_passer))
end
end
@@ -704,7 +579,144 @@ describe "LWRP" do
end
end
-
end
+ context "resource class created" do
+ before(:context) do
+ @tmpdir = Dir.mktmpdir("lwrp_test")
+ resource_path = File.join(@tmpdir, "once.rb")
+ IO.write(resource_path, "default_action :create")
+
+ @old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ Chef::Resource::LWRPBase.build_from_file("lwrp", resource_path, nil)
+ end
+
+ after(:context) do
+ FileUtils.remove_entry @tmpdir
+ Chef::Config[:treat_deprecation_warnings_as_errors] = @old_treat_deprecation_warnings_as_errors
+ end
+
+ it "should load the resource into a properly-named class" do
+ expect(Chef::Resource::LwrpOnce).to be_kind_of(Class)
+ expect(Chef::Resource::LwrpOnce <= Chef::Resource::LWRPBase).to be_truthy
+ end
+
+ it "get_lwrp(:lwrp_once).new is a Chef::Resource::LwrpOnce" do
+ lwrp = get_lwrp(:lwrp_once).new('hi')
+ expect(lwrp.kind_of?(Chef::Resource::LwrpOnce)).to be_truthy
+ expect(lwrp.is_a?(Chef::Resource::LwrpOnce)).to be_truthy
+ expect(get_lwrp(:lwrp_once) === lwrp).to be_truthy
+ expect(Chef::Resource::LwrpOnce === lwrp).to be_truthy
+ end
+
+ it "Chef::Resource::LwrpOnce.new is a get_lwrp(:lwrp_once)" do
+ lwrp = Chef::Resource::LwrpOnce.new('hi')
+ expect(lwrp.kind_of?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(lwrp.is_a?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(get_lwrp(:lwrp_once) === lwrp).to be_truthy
+ expect(Chef::Resource::LwrpOnce === lwrp).to be_truthy
+ end
+
+ it "works even if LwrpOnce exists in the top level" do
+ module ::LwrpOnce
+ end
+ expect(Chef::Resource::LwrpOnce).not_to eq(::LwrpOnce)
+ end
+
+ it "allows monkey patching of the lwrp through Chef::Resource" do
+ monkey = Module.new do
+ def issue_3607
+ end
+ end
+ Chef::Resource::LwrpOnce.send(:include, monkey)
+ expect { get_lwrp(:lwrp_once).new("blah").issue_3607 }.not_to raise_error
+ end
+
+ context "with a subclass of get_lwrp(:lwrp_once)" do
+ let(:subclass) do
+ Class.new(get_lwrp(:lwrp_once))
+ end
+
+ it "subclass.new is a subclass" do
+ lwrp = subclass.new('hi')
+ expect(lwrp.kind_of?(subclass)).to be_truthy
+ expect(lwrp.is_a?(subclass)).to be_truthy
+ expect(subclass === lwrp).to be_truthy
+ expect(lwrp.class === subclass)
+ end
+ it "subclass.new is a Chef::Resource::LwrpOnce" do
+ lwrp = subclass.new('hi')
+ expect(lwrp.kind_of?(Chef::Resource::LwrpOnce)).to be_truthy
+ expect(lwrp.is_a?(Chef::Resource::LwrpOnce)).to be_truthy
+ expect(Chef::Resource::LwrpOnce === lwrp).to be_truthy
+ expect(lwrp.class === Chef::Resource::LwrpOnce)
+ end
+ it "subclass.new is a get_lwrp(:lwrp_once)" do
+ lwrp = subclass.new('hi')
+ expect(lwrp.kind_of?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(lwrp.is_a?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(get_lwrp(:lwrp_once) === lwrp).to be_truthy
+ expect(lwrp.class === get_lwrp(:lwrp_once))
+ end
+ it "Chef::Resource::LwrpOnce.new is *not* a subclass" do
+ lwrp = Chef::Resource::LwrpOnce.new('hi')
+ expect(lwrp.kind_of?(subclass)).to be_falsey
+ expect(lwrp.is_a?(subclass)).to be_falsey
+ expect(subclass === lwrp.class).to be_falsey
+ expect(subclass === Chef::Resource::LwrpOnce).to be_falsey
+ end
+ it "get_lwrp(:lwrp_once).new is *not* a subclass" do
+ lwrp = get_lwrp(:lwrp_once).new('hi')
+ expect(lwrp.kind_of?(subclass)).to be_falsey
+ expect(lwrp.is_a?(subclass)).to be_falsey
+ expect(subclass === lwrp.class).to be_falsey
+ expect(subclass === get_lwrp(:lwrp_once)).to be_falsey
+ end
+ end
+
+ context "with a subclass of Chef::Resource::LwrpOnce" do
+ let(:subclass) do
+ Class.new(Chef::Resource::LwrpOnce)
+ end
+
+ it "subclass.new is a subclass" do
+ lwrp = subclass.new('hi')
+ expect(lwrp.kind_of?(subclass)).to be_truthy
+ expect(lwrp.is_a?(subclass)).to be_truthy
+ expect(subclass === lwrp).to be_truthy
+ expect(lwrp.class === subclass)
+ end
+ it "subclass.new is a Chef::Resource::LwrpOnce" do
+ lwrp = subclass.new('hi')
+ expect(lwrp.kind_of?(Chef::Resource::LwrpOnce)).to be_truthy
+ expect(lwrp.is_a?(Chef::Resource::LwrpOnce)).to be_truthy
+ expect(Chef::Resource::LwrpOnce === lwrp).to be_truthy
+ expect(lwrp.class === Chef::Resource::LwrpOnce)
+ end
+ it "subclass.new is a get_lwrp(:lwrp_once)" do
+ lwrp = subclass.new('hi')
+ expect(lwrp.kind_of?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(lwrp.is_a?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(get_lwrp(:lwrp_once) === lwrp).to be_truthy
+ expect(lwrp.class === get_lwrp(:lwrp_once))
+ end
+ it "Chef::Resource::LwrpOnce.new is *not* a subclass" do
+ lwrp = Chef::Resource::LwrpOnce.new('hi')
+ expect(lwrp.kind_of?(subclass)).to be_falsey
+ expect(lwrp.is_a?(subclass)).to be_falsey
+ expect(subclass === lwrp.class).to be_falsey
+ expect(subclass === Chef::Resource::LwrpOnce).to be_falsey
+ end
+ it "get_lwrp(:lwrp_once).new is *not* a subclass" do
+ lwrp = get_lwrp(:lwrp_once).new('hi')
+ expect(lwrp.kind_of?(subclass)).to be_falsey
+ expect(lwrp.is_a?(subclass)).to be_falsey
+ expect(subclass === lwrp.class).to be_falsey
+ expect(subclass === get_lwrp(:lwrp_once)).to be_falsey
+ end
+ end
+ end
end
+
+
diff --git a/spec/unit/mixin/params_validate_spec.rb b/spec/unit/mixin/params_validate_spec.rb
index 85e1c1abab..3724bbf583 100644
--- a/spec/unit/mixin/params_validate_spec.rb
+++ b/spec/unit/mixin/params_validate_spec.rb
@@ -21,6 +21,8 @@ require 'spec_helper'
class TinyClass
include Chef::Mixin::ParamsValidate
+ attr_reader :name
+
def music(is_good=true)
is_good
end
@@ -331,11 +333,11 @@ describe Chef::Mixin::ParamsValidate do
it "asserts that a value returns false from a predicate method" do
expect do
@vo.validate({:not_blank => "should pass"},
- {:not_blank => {:cannot_be => :nil, :cannot_be => :empty}})
+ {:not_blank => {:cannot_be => [ :nil, :empty ]}})
end.not_to raise_error
expect do
@vo.validate({:not_blank => ""},
- {:not_blank => {:cannot_be => :nil, :cannot_be => :empty}})
+ {:not_blank => {:cannot_be => [ :nil, :empty ]}})
end.to raise_error(Chef::Exceptions::ValidationFailed)
end
diff --git a/spec/unit/node_map_spec.rb b/spec/unit/node_map_spec.rb
index 9b5ff5e8c6..7b37ea59f4 100644
--- a/spec/unit/node_map_spec.rb
+++ b/spec/unit/node_map_spec.rb
@@ -131,6 +131,18 @@ describe Chef::NodeMap do
allow(node).to receive(:[]).with(:platform_version).and_return("6.0")
expect(node_map.get(node, :thing)).to eql(nil)
end
+
+ context "when there is a less specific definition" do
+ before do
+ node_map.set(:thing, :bar, platform_family: "rhel")
+ end
+
+ it "returns the value when the node matches" do
+ allow(node).to receive(:[]).with(:platform_family).and_return("rhel")
+ allow(node).to receive(:[]).with(:platform_version).and_return("7.0")
+ expect(node_map.get(node, :thing)).to eql(:foo)
+ end
+ end
end
describe "resource back-compat testing" do
diff --git a/spec/unit/osc_user_spec.rb b/spec/unit/osc_user_spec.rb
deleted file mode 100644
index 16731b47bd..0000000000
--- a/spec/unit/osc_user_spec.rb
+++ /dev/null
@@ -1,276 +0,0 @@
-#
-# Author:: Steven Danna (steve@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.
-#
-
-# DEPRECATION NOTE
-# This code only remains to support users still operating with
-# Open Source Chef Server 11 and should be removed once support
-# for OSC 11 ends. New development should occur in user_spec.rb.
-
-require 'spec_helper'
-
-require 'chef/osc_user'
-require 'tempfile'
-
-describe Chef::OscUser do
- before(:each) do
- @user = Chef::OscUser.new
- end
-
- describe "initialize" do
- it "should be a Chef::OscUser" do
- expect(@user).to be_a_kind_of(Chef::OscUser)
- end
- end
-
- describe "name" do
- it "should let you set the name to a string" do
- expect(@user.name("ops_master")).to eq("ops_master")
- end
-
- it "should return the current name" do
- @user.name "ops_master"
- expect(@user.name).to eq("ops_master")
- end
-
- # It is not feasible to check all invalid characters. Here are a few
- # that we probably care about.
- it "should not accept invalid characters" do
- # capital letters
- expect { @user.name "Bar" }.to raise_error(ArgumentError)
- # slashes
- expect { @user.name "foo/bar" }.to raise_error(ArgumentError)
- # ?
- expect { @user.name "foo?" }.to raise_error(ArgumentError)
- # &
- expect { @user.name "foo&" }.to raise_error(ArgumentError)
- end
-
-
- it "should not accept spaces" do
- expect { @user.name "ops master" }.to raise_error(ArgumentError)
- end
-
- it "should throw an ArgumentError if you feed it anything but a string" do
- expect { @user.name Hash.new }.to raise_error(ArgumentError)
- end
- end
-
- describe "admin" do
- it "should let you set the admin bit" do
- expect(@user.admin(true)).to eq(true)
- end
-
- it "should return the current admin value" do
- @user.admin true
- expect(@user.admin).to eq(true)
- end
-
- it "should default to false" do
- expect(@user.admin).to eq(false)
- end
-
- it "should throw an ArgumentError if you feed it anything but true or false" do
- expect { @user.name Hash.new }.to raise_error(ArgumentError)
- end
- end
-
- describe "public_key" do
- it "should let you set the public key" do
- expect(@user.public_key("super public")).to eq("super public")
- end
-
- it "should return the current public key" do
- @user.public_key("super public")
- expect(@user.public_key).to eq("super public")
- end
-
- it "should throw an ArgumentError if you feed it something lame" do
- expect { @user.public_key Hash.new }.to raise_error(ArgumentError)
- end
- end
-
- describe "private_key" do
- it "should let you set the private key" do
- expect(@user.private_key("super private")).to eq("super private")
- end
-
- it "should return the private key" do
- @user.private_key("super private")
- expect(@user.private_key).to eq("super private")
- end
-
- it "should throw an ArgumentError if you feed it something lame" do
- expect { @user.private_key Hash.new }.to raise_error(ArgumentError)
- end
- end
-
- describe "when serializing to JSON" do
- before(:each) do
- @user.name("black")
- @user.public_key("crowes")
- @json = @user.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 public key value" do
- expect(@json).to include(%{"public_key":"crowes"})
- end
-
- it "includes the 'admin' flag" do
- expect(@json).to include(%q{"admin":false})
- end
-
- it "includes the private key when present" do
- @user.private_key("monkeypants")
- expect(@user.to_json).to include(%q{"private_key":"monkeypants"})
- end
-
- it "does not include the private key if not present" do
- expect(@json).not_to include("private_key")
- end
-
- it "includes the password if present" do
- @user.password "password"
- expect(@user.to_json).to include(%q{"password":"password"})
- end
-
- it "does not include the password if not present" do
- expect(@json).not_to include("password")
- end
-
- include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
- let(:jsonable) { @user }
- end
- end
-
- describe "when deserializing from JSON" do
- before(:each) do
- user = { "name" => "mr_spinks",
- "public_key" => "turtles",
- "private_key" => "pandas",
- "password" => "password",
- "admin" => true }
- @user = Chef::OscUser.from_json(Chef::JSONCompat.to_json(user))
- end
-
- it "should deserialize to a Chef::OscUser object" do
- expect(@user).to be_a_kind_of(Chef::OscUser)
- end
-
- it "preserves the name" do
- expect(@user.name).to eq("mr_spinks")
- end
-
- it "preserves the public key" do
- expect(@user.public_key).to eq("turtles")
- end
-
- it "preserves the admin status" do
- expect(@user.admin).to be_truthy
- end
-
- it "includes the private key if present" do
- expect(@user.private_key).to eq("pandas")
- end
-
- it "includes the password if present" do
- expect(@user.password).to eq("password")
- end
-
- end
-
- describe "API Interactions" do
- before (:each) do
- @user = Chef::OscUser.new
- @user.name "foobar"
- @http_client = double("Chef::REST mock")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
- end
-
- describe "list" do
- before(:each) do
- Chef::Config[:chef_server_url] = "http://www.example.com"
- @osc_response = { "admin" => "http://www.example.com/users/admin"}
- @ohc_response = [ { "user" => { "username" => "admin" }} ]
- allow(Chef::OscUser).to receive(:load).with("admin").and_return(@user)
- @osc_inflated_response = { "admin" => @user }
- end
-
- it "lists all clients on an OSC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@osc_response)
- expect(Chef::OscUser.list).to eq(@osc_response)
- end
-
- it "inflate all clients on an OSC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@osc_response)
- expect(Chef::OscUser.list(true)).to eq(@osc_inflated_response)
- end
-
- it "lists all clients on an OHC/OPC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@ohc_response)
- # We expect that Chef::OscUser.list will give a consistent response
- # so OHC API responses should be transformed to OSC-style output.
- expect(Chef::OscUser.list).to eq(@osc_response)
- end
-
- it "inflate all clients on an OHC/OPC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@ohc_response)
- expect(Chef::OscUser.list(true)).to eq(@osc_inflated_response)
- end
- end
-
- describe "create" do
- it "creates a new user via the API" do
- @user.password "password"
- expect(@http_client).to receive(:post_rest).with("users", {:name => "foobar", :admin => false, :password => "password"}).and_return({})
- @user.create
- end
- end
-
- describe "read" do
- it "loads a named user from the API" do
- expect(@http_client).to receive(:get_rest).with("users/foobar").and_return({"name" => "foobar", "admin" => true, "public_key" => "pubkey"})
- user = Chef::OscUser.load("foobar")
- expect(user.name).to eq("foobar")
- expect(user.admin).to eq(true)
- expect(user.public_key).to eq("pubkey")
- end
- end
-
- describe "update" do
- it "updates an existing user on via the API" do
- expect(@http_client).to receive(:put_rest).with("users/foobar", {:name => "foobar", :admin => false}).and_return({})
- @user.update
- end
- end
-
- describe "destroy" do
- it "deletes the specified user via the API" do
- expect(@http_client).to receive(:delete_rest).with("users/foobar")
- @user.destroy
- end
- end
- end
-end
diff --git a/spec/unit/platform_spec.rb b/spec/unit/platform_spec.rb
index 36325d5411..34b46f657f 100644
--- a/spec/unit/platform_spec.rb
+++ b/spec/unit/platform_spec.rb
@@ -103,7 +103,7 @@ describe Chef::Platform do
end
it "should raise an exception if a provider cannot be found for a resource type" do
- expect { Chef::Platform.find_provider("Darwin", "9.2.2", :coffee) }.to raise_error(ArgumentError)
+ expect { Chef::Platform.find_provider("Darwin", "9.2.2", :coffee) }.to raise_error(Chef::Exceptions::ProviderNotFound)
end
it "should look up a provider for a resource with a Chef::Resource object" do
diff --git a/spec/unit/property/state_spec.rb b/spec/unit/property/state_spec.rb
index 80ebe01a41..e7fee0387f 100644
--- a/spec/unit/property/state_spec.rb
+++ b/spec/unit/property/state_spec.rb
@@ -50,20 +50,20 @@ describe "Chef::Resource#identity and #state" do
end
# identity
- context "Chef::Resource#identity_attr" do
+ context "Chef::Resource#identity_properties" do
with_property ":x" do
- # it "name is the default identity" do
- # expect(resource_class.identity_attr).to eq :name
- # expect(resource_class.properties[:name].identity?).to be_falsey
- # expect(resource.name).to eq 'blah'
- # expect(resource.identity).to eq 'blah'
- # end
+ it "name is the default identity" do
+ expect(resource_class.identity_properties).to eq [ Chef::Resource.properties[:name] ]
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
+ expect(resource.name).to eq 'blah'
+ expect(resource.identity).to eq 'blah'
+ end
- it "identity_attr :x changes the identity" do
- expect(resource_class.identity_attr :x).to eq :x
- expect(resource_class.identity_attr).to eq :x
- # expect(resource_class.properties[:name].identity?).to be_falsey
- # expect(resource_class.properties[:x].identity?).to be_truthy
+ it "identity_properties :x changes the identity" do
+ expect(resource_class.identity_properties :x).to eq [ resource_class.properties[:x] ]
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
+ expect(resource_class.properties[:x].identity?).to be_truthy
expect(resource.x 'woo').to eq 'woo'
expect(resource.x).to eq 'woo'
@@ -72,28 +72,31 @@ describe "Chef::Resource#identity and #state" do
expect(resource.identity).to eq 'woo'
end
- # with_property ":y, identity: true" do
- # context "and identity_attr :x" do
- # before do
- # resource_class.class_eval do
- # identity_attr :x
- # end
- # end
- #
- # it "only returns :x as identity" do
- # resource.x 'foo'
- # resource.y 'bar'
- # expect(resource_class.identity_attr).to eq :x
- # expect(resource.identity).to eq 'foo'
- # end
- # it "does not flip y.desired_state off" do
- # resource.x 'foo'
- # resource.y 'bar'
- # expect(resource_class.state_attrs).to eq [ :x, :y ]
- # expect(resource.state).to eq({ x: 'foo', y: 'bar' })
- # end
- # end
- # end
+ with_property ":y, identity: true" do
+ context "and identity_properties :x" do
+ before do
+ resource_class.class_eval do
+ identity_properties :x
+ end
+ end
+
+ it "only returns :x as identity" do
+ resource.x 'foo'
+ resource.y 'bar'
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
+ expect(resource.identity).to eq 'foo'
+ end
+ it "does not flip y.desired_state off" do
+ resource.x 'foo'
+ resource.y 'bar'
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:x],
+ resource_class.properties[:y]
+ ]
+ expect(resource.state_for_resource_reporter).to eq(x: 'foo', y: 'bar')
+ end
+ end
+ end
context "With a subclass" do
let(:subresource_class) do
@@ -107,57 +110,63 @@ describe "Chef::Resource#identity and #state" do
end
it "name is the default identity on the subclass" do
- # expect(subresource_class.identity_attr).to eq :name
- # expect(subresource_class.properties[:name].identity?).to be_falsey
+ expect(subresource_class.identity_properties).to eq [ Chef::Resource.properties[:name] ]
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
expect(subresource.name).to eq 'sub'
expect(subresource.identity).to eq 'sub'
end
- context "With identity_attr :x on the superclass" do
+ context "With identity_properties :x on the superclass" do
before do
resource_class.class_eval do
- identity_attr :x
+ identity_properties :x
end
end
it "The subclass inherits :x as identity" do
- expect(subresource_class.identity_attr).to eq :x
- # expect(subresource_class.properties[:name].identity?).to be_falsey
- # expect(subresource_class.properties[:x].identity?).to be_truthy
+ expect(subresource_class.identity_properties).to eq [ subresource_class.properties[:x] ]
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
+ expect(subresource_class.properties[:x].identity?).to be_truthy
subresource.x 'foo'
expect(subresource.identity).to eq 'foo'
end
- # context "With property :y, identity: true on the subclass" do
- # before do
- # subresource_class.class_eval do
- # property :y, identity: true
- # end
- # end
- # it "The subclass's identity includes both x and y" do
- # expect(subresource_class.identity_attr).to eq :x
- # subresource.x 'foo'
- # subresource.y 'bar'
- # expect(subresource.identity).to eq({ x: 'foo', y: 'bar' })
- # end
- # end
+ context "With property :y, identity: true on the subclass" do
+ before do
+ subresource_class.class_eval do
+ property :y, identity: true
+ end
+ end
+ it "The subclass's identity includes both x and y" do
+ expect(subresource_class.identity_properties).to eq [
+ subresource_class.properties[:x],
+ subresource_class.properties[:y]
+ ]
+ subresource.x 'foo'
+ subresource.y 'bar'
+ expect(subresource.identity).to eq(x: 'foo', y: 'bar')
+ end
+ end
with_property ":y, String" do
- context "With identity_attr :y on the subclass" do
+ context "With identity_properties :y on the subclass" do
before do
subresource_class.class_eval do
- identity_attr :y
+ identity_properties :y
end
end
- # it "y is part of state" do
- # subresource.x 'foo'
- # subresource.y 'bar'
- # expect(subresource.state).to eq({ x: 'foo', y: 'bar' })
- # expect(subresource_class.state_attrs).to eq [ :x, :y ]
- # end
+ it "y is part of state" do
+ subresource.x 'foo'
+ subresource.y 'bar'
+ expect(subresource.state_for_resource_reporter).to eq(x: 'foo', y: 'bar')
+ expect(subresource_class.state_properties).to eq [
+ subresource_class.properties[:x],
+ subresource_class.properties[:y]
+ ]
+ end
it "y is the identity" do
- expect(subresource_class.identity_attr).to eq :y
+ expect(subresource_class.identity_properties).to eq [ subresource_class.properties[:y] ]
subresource.x 'foo'
subresource.y 'bar'
expect(subresource.identity).to eq 'bar'
@@ -171,24 +180,24 @@ describe "Chef::Resource#identity and #state" do
end
end
- # with_property ":string_only, String, identity: true", ":string_only2, String" do
- # it "identity_attr does not change validation" do
- # resource_class.identity_attr :string_only
- # expect { resource.string_only 12 }.to raise_error Chef::Exceptions::ValidationFailed
- # expect { resource.string_only2 12 }.to raise_error Chef::Exceptions::ValidationFailed
- # end
- # end
- #
- # with_property ":x, desired_state: false" do
- # it "identity_attr does not flip on desired_state" do
- # resource_class.identity_attr :x
- # resource.x 'hi'
- # expect(resource.identity).to eq 'hi'
- # # expect(resource_class.properties[:x].desired_state?).to be_falsey
- # expect(resource_class.state_attrs).to eq []
- # expect(resource.state).to eq({})
- # end
- # end
+ with_property ":string_only, String, identity: true", ":string_only2, String" do
+ it "identity_properties does not change validation" do
+ resource_class.identity_properties :string_only
+ expect { resource.string_only 12 }.to raise_error Chef::Exceptions::ValidationFailed
+ expect { resource.string_only2 12 }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ end
+
+ with_property ":x, desired_state: false" do
+ it "identity_properties does not change desired_state" do
+ resource_class.identity_properties :x
+ resource.x 'hi'
+ expect(resource.identity).to eq 'hi'
+ expect(resource_class.properties[:x].desired_state?).to be_falsey
+ expect(resource_class.state_properties).to eq []
+ expect(resource.state_for_resource_reporter).to eq({})
+ end
+ end
context "With custom property custom_property defined only as methods, using different variables for storage" do
before do
@@ -202,24 +211,26 @@ describe "Chef::Resource#identity and #state" do
end
end
- context "And identity_attr :custom_property" do
+ context "And identity_properties :custom_property" do
before do
resource_class.class_eval do
- identity_attr :custom_property
+ identity_properties :custom_property
end
end
- it "identity_attr comes back as :custom_property" do
- # expect(resource_class.properties[:custom_property].identity?).to be_truthy
- expect(resource_class.identity_attr).to eq :custom_property
+ it "identity_properties comes back as :custom_property" do
+ expect(resource_class.properties[:custom_property].identity?).to be_truthy
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:custom_property] ]
end
- # it "custom_property becomes part of desired_state" do
- # resource.custom_property = 1
- # expect(resource.state).to eq({ custom_property: 6 })
- # expect(resource_class.properties[:custom_property].desired_state?).to be_truthy
- # expect(resource_class.state_attrs).to eq [ :custom_property ]
- # end
- it "identity_attr does not change custom_property's getter or setter" do
+ it "custom_property becomes part of desired_state" do
+ resource.custom_property = 1
+ expect(resource.state_for_resource_reporter).to eq(custom_property: 6)
+ expect(resource_class.properties[:custom_property].desired_state?).to be_truthy
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:custom_property]
+ ]
+ end
+ it "identity_properties does not change custom_property's getter or setter" do
resource.custom_property = 1
expect(resource.custom_property).to eq 6
end
@@ -232,111 +243,111 @@ describe "Chef::Resource#identity and #state" do
end
end
- # context "PropertyType#identity" do
- # with_property ":x, identity: true" do
- # it "name is only part of the identity if an identity attribute is defined" do
- # expect(resource_class.identity_attr).to eq :x
- # resource.x 'woo'
- # expect(resource.identity).to eq 'woo'
- # end
- # end
- #
- # with_property ":x, identity: true, default: 'xxx'",
- # ":y, identity: true, default: 'yyy'",
- # ":z, identity: true, default: 'zzz'" do
- # it "identity_attr returns the first identity attribute if multiple are defined" do
- # expect(resource_class.identity_attr).to eq :x
- # end
- # it "identity returns all identity values in a hash if multiple are defined" do
- # resource.x 'foo'
- # resource.y 'bar'
- # resource.z 'baz'
- # expect(resource.identity).to eq({ x: 'foo', y: 'bar', z: 'baz' })
- # end
- # it "identity returns only identity values that are set, and does not include defaults" do
- # resource.x 'foo'
- # resource.z 'baz'
- # expect(resource.identity).to eq({ x: 'foo', z: 'baz' })
- # end
- # it "identity returns only set identity values in a hash, if there is only one set identity value" do
- # resource.x 'foo'
- # expect(resource.identity).to eq({ x: 'foo' })
- # end
- # it "identity returns an empty hash if no identity values are set" do
- # expect(resource.identity).to eq({})
- # end
- # it "identity_attr wipes out any other identity attributes if multiple are defined" do
- # resource_class.identity_attr :y
- # resource.x 'foo'
- # resource.y 'bar'
- # resource.z 'baz'
- # expect(resource.identity).to eq 'bar'
- # end
- # end
- #
- # with_property ":x, identity: true, name_property: true" do
- # it "identity when x is not defined returns the value of x" do
- # expect(resource.identity).to eq 'blah'
- # end
- # it "state when x is not defined returns the value of x" do
- # expect(resource.state).to eq({ x: 'blah' })
- # end
- # end
- # end
-
- # state_attrs
- context "Chef::Resource#state_attrs" do
- it "name is not part of state_attrs" do
- expect(Chef::Resource.state_attrs).to eq []
- expect(resource_class.state_attrs).to eq []
- expect(resource.state).to eq({})
+ context "Property#identity" do
+ with_property ":x, identity: true" do
+ it "name is only part of the identity if an identity attribute is defined" do
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
+ resource.x 'woo'
+ expect(resource.identity).to eq 'woo'
+ end
+ end
+
+ with_property ":x, identity: true, default: 'xxx'",
+ ":y, identity: true, default: 'yyy'",
+ ":z, identity: true, default: 'zzz'" do
+ it "identity_property raises an error if multiple identity values are defined" do
+ expect { resource_class.identity_property }.to raise_error Chef::Exceptions::MultipleIdentityError
+ end
+ it "identity_attr raises an error if multiple identity values are defined" do
+ expect { resource_class.identity_attr }.to raise_error Chef::Exceptions::MultipleIdentityError
+ end
+ it "identity returns all identity values in a hash if multiple are defined" do
+ resource.x 'foo'
+ resource.y 'bar'
+ resource.z 'baz'
+ expect(resource.identity).to eq(x: 'foo', y: 'bar', z: 'baz')
+ end
+ it "identity returns all values whether any value is set or not" do
+ expect(resource.identity).to eq(x: 'xxx', y: 'yyy', z: 'zzz')
+ end
+ it "identity_properties wipes out any other identity attributes if multiple are defined" do
+ resource_class.identity_properties :y
+ resource.x 'foo'
+ resource.y 'bar'
+ resource.z 'baz'
+ expect(resource.identity).to eq 'bar'
+ end
+ end
+
+ with_property ":x, identity: true, name_property: true" do
+ it "identity when x is not defined returns the value of x" do
+ expect(resource.identity).to eq 'blah'
+ end
+ it "state when x is not defined returns the value of x" do
+ expect(resource.state_for_resource_reporter).to eq(x: 'blah')
+ end
+ end
+ end
+
+ # state_properties
+ context "Chef::Resource#state_properties" do
+ it "state_properties is empty by default" do
+ expect(Chef::Resource.state_properties).to eq []
+ expect(resource.state_for_resource_reporter).to eq({})
end
- # with_property ":x", ":y", ":z" do
- # it "x, y and z are state attributes" do
- # resource.x 1
- # resource.y 2
- # resource.z 3
- # expect(resource_class.state_attrs).to eq [ :x, :y, :z ]
- # expect(resource.state).to eq(x: 1, y: 2, z: 3)
- # end
- # it "values that are not set are not included in state" do
- # resource.x 1
- # expect(resource.state).to eq(x: 1)
- # end
- # it "when no values are set, nothing is included in state" do
- # end
- # end
- #
- # with_property ":x", ":y, desired_state: false", ":z, desired_state: true" do
- # it "x and z are state attributes, and y is not" do
- # resource.x 1
- # resource.y 2
- # resource.z 3
- # expect(resource_class.state_attrs).to eq [ :x, :z ]
- # expect(resource.state).to eq(x: 1, z: 3)
- # end
- # end
-
- # with_property ":x, name_property: true" do
- # it "Unset values with name_property are included in state" do
- # expect(resource.state).to eq(x: 'blah')
- # end
- # it "Set values with name_property are included in state" do
- # resource.x 1
- # expect(resource.state).to eq(x: 1)
- # end
- # end
-
- # with_property ":x, default: 1" do
- # it "Unset values with defaults are not included in state" do
- # expect(resource.state).to eq({})
- # end
- # it "Set values with defaults are included in state" do
- # resource.x 1
- # expect(resource.state).to eq(x: 1)
- # end
- # end
+ with_property ":x", ":y", ":z" do
+ it "x, y and z are state attributes" do
+ resource.x 1
+ resource.y 2
+ resource.z 3
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:x],
+ resource_class.properties[:y],
+ resource_class.properties[:z]
+ ]
+ expect(resource.state_for_resource_reporter).to eq(x: 1, y: 2, z: 3)
+ end
+ it "values that are not set are not included in state" do
+ resource.x 1
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
+ end
+ it "when no values are set, nothing is included in state" do
+ end
+ end
+
+ with_property ":x", ":y, desired_state: false", ":z, desired_state: true" do
+ it "x and z are state attributes, and y is not" do
+ resource.x 1
+ resource.y 2
+ resource.z 3
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:x],
+ resource_class.properties[:z]
+ ]
+ expect(resource.state_for_resource_reporter).to eq(x: 1, z: 3)
+ end
+ end
+
+ with_property ":x, name_property: true" do
+ # it "Unset values with name_property are included in state" do
+ # expect(resource.state_for_resource_reporter).to eq({ x: 'blah' })
+ # end
+ it "Set values with name_property are included in state" do
+ resource.x 1
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
+ end
+ end
+
+ with_property ":x, default: 1" do
+ it "Unset values with defaults are not included in state" do
+ expect(resource.state_for_resource_reporter).to eq({})
+ end
+ it "Set values with defaults are included in state" do
+ resource.x 1
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
+ end
+ end
context "With a class with a normal getter and setter" do
before do
@@ -349,143 +360,147 @@ describe "Chef::Resource#identity and #state" do
end
end
end
- it "state_attrs(:x) causes the value to be included in properties" do
- resource_class.state_attrs(:x)
+ it "state_properties(:x) causes the value to be included in properties" do
+ resource_class.state_properties(:x)
resource.x = 1
expect(resource.x).to eq 6
- expect(resource.state).to eq(x: 6)
+ expect(resource.state_for_resource_reporter).to eq(x: 6)
+ end
+ end
+
+ context "When state_properties happens before properties are declared" do
+ before do
+ resource_class.class_eval do
+ state_properties :x
+ property :x
+ end
+ end
+ it "the property works and is in state_properties" do
+ expect(resource_class.state_properties).to include(resource_class.properties[:x])
+ resource.x = 1
+ expect(resource.x).to eq 1
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
+ end
+ end
+
+ with_property ":x, Integer, identity: true" do
+ it "state_properties(:x) leaves the property in desired_state" do
+ resource_class.state_properties(:x)
+ resource.x 10
+
+ expect(resource_class.properties[:x].desired_state?).to be_truthy
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:x]
+ ]
+ expect(resource.state_for_resource_reporter).to eq(x: 10)
+ end
+ it "state_properties(:x) does not turn off validation" do
+ resource_class.state_properties(:x)
+ expect { resource.x 'ouch' }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ it "state_properties(:x) does not turn off identity" do
+ resource_class.state_properties(:x)
+ resource.x 10
+
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
+ expect(resource_class.properties[:x].identity?).to be_truthy
+ expect(resource.identity).to eq 10
end
end
- # with_property ":x, Integer, identity: true" do
- # it "state_attrs(:x) leaves the property in desired_state" do
- # resource_class.state_attrs(:x)
- # resource.x 10
- #
- # # expect(resource_class.properties[:x].desired_state?).to be_truthy
- # expect(resource_class.state_attrs).to eq [ :x ]
- # expect(resource.state).to eq(x: 10)
- # end
- # it "state_attrs(:x) does not turn off validation" do
- # resource_class.state_attrs(:x)
- # expect { resource.x 'ouch' }.to raise_error Chef::Exceptions::ValidationFailed
- # end
- # it "state_attrs(:x) does not turn off identity" do
- # resource_class.state_attrs(:x)
- # resource.x 10
- #
- # expect(resource_class.identity_attr).to eq :x
- # # expect(resource_class.properties[:x].identity?).to be_truthy
- # expect(resource.identity).to eq 10
- # end
- # end
-
- # with_property ":x, Integer, identity: true, desired_state: false" do
- # before do
- # resource_class.class_eval do
- # def y
- # 20
- # end
- # end
- # end
- # it "state_attrs(:x) sets the property in desired_state" do
- # resource_class.state_attrs(:x)
- # resource.x 10
- #
- # # expect(resource_class.properties[:x].desired_state?).to be_truthy
- # expect(resource_class.state_attrs).to eq [ :x ]
- # expect(resource.state).to eq(x: 10)
- # end
- # it "state_attrs(:x) does not turn off validation" do
- # resource_class.state_attrs(:x)
- # expect { resource.x 'ouch' }.to raise_error Chef::Exceptions::ValidationFailed
- # end
- # it "state_attrs(:x) does not turn off identity" do
- # resource_class.state_attrs(:x)
- # resource.x 10
- #
- # expect(resource_class.identity_attr).to eq :x
- # # expect(resource_class.properties[:x].identity?).to be_truthy
- # expect(resource.identity).to eq 10
- # end
- # it "state_attrs(:y) adds y and removes x from desired state" do
- # resource_class.state_attrs(:y)
- # resource.x 10
- #
- # # expect(resource_class.properties[:x].desired_state?).to be_falsey
- # # expect(resource_class.properties[:y].desired_state?).to be_truthy
- # expect(resource_class.state_attrs).to eq [ :y ]
- # expect(resource.state).to eq(y: 20)
- # end
- # it "state_attrs(:y) does not turn off validation" do
- # resource_class.state_attrs(:y)
- #
- # expect { resource.x 'ouch' }.to raise_error Chef::Exceptions::ValidationFailed
- # end
- # it "state_attrs(:y) does not turn off identity" do
- # resource_class.state_attrs(:y)
- # resource.x 10
- #
- # expect(resource_class.identity_attr).to eq :x
- # # expect(resource_class.properties[:x].identity?).to be_truthy
- # expect(resource.identity).to eq 10
- # end
- #
- # context "With a subclassed resource" do
- # let(:resource_subclass) do
- # new_resource_name = self.class.new_resource_name
- # Class.new(resource_class) do
- # resource_name new_resource_name
- # end
- # end
- # let(:subresource) do
- # resource_subclass.new('blah')
- # end
- # it "state_attrs(:x) sets the property in desired_state" do
- # resource_subclass.state_attrs(:x)
- # subresource.x 10
- #
- # # expect(resource_subclass.properties[:x].desired_state?).to be_truthy
- # expect(resource_subclass.state_attrs).to eq [ :x ]
- # expect(subresource.state).to eq(x: 10)
- # end
- # it "state_attrs(:x) does not turn off validation" do
- # resource_subclass.state_attrs(:x)
- # expect { subresource.x 'ouch' }.to raise_error Chef::Exceptions::ValidationFailed
- # end
- # it "state_attrs(:x) does not turn off identity" do
- # resource_subclass.state_attrs(:x)
- # subresource.x 10
- #
- # expect(resource_subclass.identity_attr).to eq :x
- # # expect(resource_subclass.properties[:x].identity?).to be_truthy
- # expect(subresource.identity).to eq 10
- # end
- # it "state_attrs(:y) adds y and removes x from desired state" do
- # resource_subclass.state_attrs(:y)
- # subresource.x 10
- #
- # # expect(resource_subclass.properties[:x].desired_state?).to be_falsey
- # # expect(resource_subclass.properties[:y].desired_state?).to be_truthy
- # expect(resource_subclass.state_attrs).to eq [ :y ]
- # expect(subresource.state).to eq(y: 20)
- # end
- # it "state_attrs(:y) does not turn off validation" do
- # resource_subclass.state_attrs(:y)
- #
- # expect { subresource.x 'ouch' }.to raise_error Chef::Exceptions::ValidationFailed
- # end
- # it "state_attrs(:y) does not turn off identity" do
- # resource_subclass.state_attrs(:y)
- # subresource.x 10
- #
- # expect(resource_subclass.identity_attr).to eq :x
- # # expect(resource_subclass.properties[:x].identity?).to be_truthy
- # expect(subresource.identity).to eq 10
- # end
- # end
- # end
+ with_property ":x, Integer, identity: true, desired_state: false" do
+ before do
+ resource_class.class_eval do
+ def y
+ 20
+ end
+ end
+ end
+
+ it "state_properties(:x) leaves x identical" do
+ old_value = resource_class.properties[:y]
+ resource_class.state_properties(:x)
+ resource.x 10
+
+ expect(resource_class.properties[:y].object_id).to eq old_value.object_id
+
+ expect(resource_class.properties[:x].desired_state?).to be_truthy
+ expect(resource_class.properties[:x].identity?).to be_truthy
+ expect(resource_class.identity_properties).to eq [
+ resource_class.properties[:x]
+ ]
+ expect(resource.identity).to eq(10)
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:x]
+ ]
+ expect(resource.state_for_resource_reporter).to eq(x: 10)
+ end
+
+ it "state_properties(:y) adds y to desired state" do
+ old_value = resource_class.properties[:x]
+ resource_class.state_properties(:y)
+ resource.x 10
+
+ expect(resource_class.properties[:x].object_id).to eq old_value.object_id
+ expect(resource_class.properties[:x].desired_state?).to be_falsey
+ expect(resource_class.properties[:y].desired_state?).to be_truthy
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:y]
+ ]
+ expect(resource.state_for_resource_reporter).to eq(y: 20)
+ end
+
+ context "With a subclassed resource" do
+ let(:subresource_class) do
+ new_resource_name = self.class.new_resource_name
+ Class.new(resource_class) do
+ resource_name new_resource_name
+ end
+ end
+ let(:subresource) do
+ subresource_class.new('blah')
+ end
+
+ it "state_properties(:x) adds x to desired state" do
+ old_value = resource_class.properties[:y]
+ subresource_class.state_properties(:x)
+ subresource.x 10
+
+ expect(subresource_class.properties[:y].object_id).to eq old_value.object_id
+
+ expect(subresource_class.properties[:x].desired_state?).to be_truthy
+ expect(subresource_class.properties[:x].identity?).to be_truthy
+ expect(subresource_class.identity_properties).to eq [
+ subresource_class.properties[:x]
+ ]
+ expect(subresource.identity).to eq(10)
+ expect(subresource_class.state_properties).to eq [
+ subresource_class.properties[:x]
+ ]
+ expect(subresource.state_for_resource_reporter).to eq(x: 10)
+ end
+
+ it "state_properties(:y) adds y to desired state" do
+ old_value = resource_class.properties[:x]
+ subresource_class.state_properties(:y)
+ subresource.x 10
+
+ expect(subresource_class.properties[:x].object_id).to eq old_value.object_id
+ expect(subresource_class.properties[:y].desired_state?).to be_truthy
+ expect(subresource_class.state_properties).to eq [
+ subresource_class.properties[:y]
+ ]
+ expect(subresource.state_for_resource_reporter).to eq(y: 20)
+
+ expect(subresource_class.properties[:x].identity?).to be_truthy
+ expect(subresource_class.identity_properties).to eq [
+ subresource_class.properties[:x]
+ ]
+ expect(subresource.identity).to eq(10)
+ end
+ end
+ end
end
end
diff --git a/spec/unit/property/validation_spec.rb b/spec/unit/property/validation_spec.rb
index e32147bd38..a3649d228c 100644
--- a/spec/unit/property/validation_spec.rb
+++ b/spec/unit/property/validation_spec.rb
@@ -111,10 +111,6 @@ describe "Chef::Resource.property validation" do
it "get succeeds" do
expect(resource.x).to eq 'default'
end
- it "set(nil) = get" do
- expect(resource.x nil).to eq 'default'
- expect(resource.x).to eq 'default'
- end
it "set to valid value succeeds" do
expect(resource.x 'str').to eq 'str'
expect(resource.x).to eq 'str'
@@ -122,15 +118,18 @@ describe "Chef::Resource.property validation" do
it "set to invalid value raises ValidationFailed" do
expect { resource.x 10 }.to raise_error Chef::Exceptions::ValidationFailed
end
+ it "set to nil emits a deprecation warning and does a get" do
+ expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ resource.x 'str'
+ expect(resource.x nil).to eq 'str'
+ expect(resource.x).to eq 'str'
+ end
end
context "when the variable does not have an initial value" do
it "get succeeds" do
expect(resource.x).to be_nil
end
- it "set(nil) = get" do
- expect(resource.x nil).to be_nil
- expect(resource.x).to be_nil
- end
it "set to valid value succeeds" do
expect(resource.x 'str').to eq 'str'
expect(resource.x).to eq 'str'
@@ -138,6 +137,13 @@ describe "Chef::Resource.property validation" do
it "set to invalid value raises ValidationFailed" do
expect { resource.x 10 }.to raise_error Chef::Exceptions::ValidationFailed
end
+ it "set to nil emits a deprecation warning and does a get" do
+ expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ resource.x 'str'
+ expect(resource.x nil).to eq 'str'
+ expect(resource.x).to eq 'str'
+ end
end
end
with_property ":x, [ String, nil ]" do
@@ -255,10 +261,10 @@ describe "Chef::Resource.property validation" do
[ '', 'abac' ],
[ nil ]
- # PropertyType
- # validation_test 'is: PropertyType.new(is: :a)',
- # [ :a ],
- # [ :b, nil ]
+ # Property
+ validation_test 'is: Chef::Property.new(is: :a)',
+ [ :a ],
+ [ :b, nil ]
# RSpec Matcher
class Globalses
@@ -523,10 +529,11 @@ describe "Chef::Resource.property validation" do
expect(resource.x 1).to eq 1
expect(resource.x).to eq 1
end
- it "value nil does a get" do
+ it "value nil emits a deprecation warning and does a get" do
+ expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
Chef::Config[:treat_deprecation_warnings_as_errors] = false
resource.x 1
- resource.x nil
+ expect(resource.x nil).to eq 1
expect(resource.x).to eq 1
end
end
@@ -549,31 +556,35 @@ describe "Chef::Resource.property validation" do
end
with_property ':x, name_property: true, required: true' do
- it "if x is not specified, retrieval fails" do
- expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
+ it "if x is not specified, the name property is returned" do
+ expect(resource.x).to eq 'blah'
end
it "value 1 is valid" do
expect(resource.x 1).to eq 1
expect(resource.x).to eq 1
end
- it "value nil does a get" do
+ it "value nil emits a deprecation warning and does a get" do
+ expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
resource.x 1
- resource.x nil
+ expect(resource.x nil).to eq 1
expect(resource.x).to eq 1
end
end
with_property ':x, default: 10, required: true' do
- it "if x is not specified, retrieval fails" do
- expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
+ it "if x is not specified, the default is returned" do
+ expect(resource.x).to eq 10
end
it "value 1 is valid" do
expect(resource.x 1).to eq 1
expect(resource.x).to eq 1
end
- it "value nil does a get" do
+ it "value nil is invalid" do
+ expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
resource.x 1
- resource.x nil
+ expect(resource.x nil).to eq 1
expect(resource.x).to eq 1
end
end
@@ -596,11 +607,6 @@ describe "Chef::Resource.property validation" do
end
end
- # it "getting the value causes a deprecation warning" do
- # Chef::Config[:treat_deprecation_warnings_as_errors] = true
- # expect { resource.x }.to raise_error Chef::Exceptions::DeprecatedFeatureError
- # end
-
it "value 1 is valid" do
expect(resource.x 1).to eq 1
expect(resource.x).to eq 1
diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb
index ce0552c564..09f7e52329 100644
--- a/spec/unit/property_spec.rb
+++ b/spec/unit/property_spec.rb
@@ -58,11 +58,15 @@ describe "Chef::Resource.property" do
else
tags = []
end
- properties = properties.map { |property| "property #{property}" }
- context "With properties #{english_join(properties)}", *tags do
+ if properties.size == 1
+ description = "With property #{properties.first}"
+ else
+ description = "With properties #{english_join(properties.map { |property| "#{property.inspect}" })}"
+ end
+ context description, *tags do
before do
properties.each do |property_str|
- resource_class.class_eval(property_str, __FILE__, __LINE__)
+ resource_class.class_eval("property #{property_str}", __FILE__, __LINE__)
end
end
instance_eval(&block)
@@ -75,11 +79,10 @@ describe "Chef::Resource.property" do
expect(resource.bare_property 10).to eq 10
expect(resource.bare_property).to eq 10
end
- # it "emits a deprecation warning and does a get, if set to nil" do
it "emits a deprecation warning and does a get, if set to nil" do
expect(resource.bare_property 10).to eq 10
- # expect { resource.bare_property nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
- # Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ expect { resource.bare_property nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
expect(resource.bare_property nil).to eq 10
expect(resource.bare_property).to eq 10
end
@@ -92,11 +95,11 @@ describe "Chef::Resource.property" do
expect(resource.bare_property 10).to eq 10
expect(resource.bare_property).to eq 10
end
- # it "can be set to nil with =" do
- # expect(resource.bare_property 10).to eq 10
- # expect(resource.bare_property = nil).to be_nil
- # expect(resource.bare_property).to be_nil
- # end
+ it "can be set to nil with =" do
+ expect(resource.bare_property 10).to eq 10
+ expect(resource.bare_property = nil).to be_nil
+ expect(resource.bare_property).to be_nil
+ end
it "can be updated with =" do
expect(resource.bare_property 10).to eq 10
expect(resource.bare_property = 20).to eq 20
@@ -121,7 +124,7 @@ describe "Chef::Resource.property" do
expect(subresource.x).to eq 10
expect(subresource.x = 20).to eq 20
expect(subresource.x).to eq 20
- # expect(subresource_class.properties[:x]).not_to be_nil
+ expect(subresource_class.properties[:x]).not_to be_nil
end
it "x's validation is inherited" do
@@ -140,18 +143,18 @@ describe "Chef::Resource.property" do
expect(subresource.x).to eq 10
expect(subresource.x = 20).to eq 20
expect(subresource.x).to eq 20
- # expect(subresource_class.properties[:x]).not_to be_nil
+ expect(subresource_class.properties[:x]).not_to be_nil
end
it "y is there" do
expect(subresource.y 10).to eq 10
expect(subresource.y).to eq 10
expect(subresource.y = 20).to eq 20
expect(subresource.y).to eq 20
- # expect(subresource_class.properties[:y]).not_to be_nil
+ expect(subresource_class.properties[:y]).not_to be_nil
end
it "y is not on the superclass" do
expect { resource_class.y 10 }.to raise_error
- # expect(resource_class.properties[:y]).to be_nil
+ expect(resource_class.properties[:y]).to be_nil
end
end
@@ -167,17 +170,37 @@ describe "Chef::Resource.property" do
expect(subresource.x).to eq 10
expect(subresource.x = 20).to eq 20
expect(subresource.x).to eq 20
- # expect(subresource_class.properties[:x]).not_to be_nil
- # expect(subresource_class.properties[:x]).not_to eq resource_class.properties[:x]
+ expect(subresource_class.properties[:x]).not_to be_nil
+ expect(subresource_class.properties[:x]).not_to eq resource_class.properties[:x]
end
- it "x's validation is overwritten" do
- expect(subresource.x 'ohno').to eq 'ohno'
- expect(subresource.x).to eq 'ohno'
+ it "x's validation is inherited" do
+ expect { subresource.x 'ohno' }.to raise_error Chef::Exceptions::ValidationFailed
end
+ end
- it "the superclass's validation for x is still there" do
- expect { resource.x 'ohno' }.to raise_error Chef::Exceptions::ValidationFailed
+ context "with property :x, default: 80 on the subclass" do
+ before do
+ subresource_class.class_eval do
+ property :x, default: 80
+ end
+ end
+
+ it "x is still there" do
+ expect(subresource.x 10).to eq 10
+ expect(subresource.x).to eq 10
+ expect(subresource.x = 20).to eq 20
+ expect(subresource.x).to eq 20
+ expect(subresource_class.properties[:x]).not_to be_nil
+ expect(subresource_class.properties[:x]).not_to eq resource_class.properties[:x]
+ end
+
+ it "x defaults to 80" do
+ expect(subresource.x).to eq 80
+ end
+
+ it "x's validation is inherited" do
+ expect { subresource.x 'ohno' }.to raise_error Chef::Exceptions::ValidationFailed
end
end
@@ -193,8 +216,8 @@ describe "Chef::Resource.property" do
expect(subresource.x).to eq "10"
expect(subresource.x = "20").to eq "20"
expect(subresource.x).to eq "20"
- # expect(subresource_class.properties[:x]).not_to be_nil
- # expect(subresource_class.properties[:x]).not_to eq resource_class.properties[:x]
+ expect(subresource_class.properties[:x]).not_to be_nil
+ expect(subresource_class.properties[:x]).not_to eq resource_class.properties[:x]
end
it "x's validation is overwritten" do
@@ -212,14 +235,89 @@ describe "Chef::Resource.property" do
end
end
- context "Chef::Resource::PropertyType#property_is_set?" do
+ context "Chef::Resource::Property#reset_property" do
+ it "when a resource is newly created, reset_property(:name) sets property to nil" do
+ expect(resource.property_is_set?(:name)).to be_truthy
+ resource.reset_property(:name)
+ expect(resource.property_is_set?(:name)).to be_falsey
+ expect(resource.name).to be_nil
+ end
+
+ it "when referencing an undefined property, reset_property(:x) raises an error" do
+ expect { resource.reset_property(:x) }.to raise_error(ArgumentError)
+ end
+
+ with_property ':x' do
+ it "when the resource is newly created, reset_property(:x) does nothing" do
+ expect(resource.property_is_set?(:x)).to be_falsey
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to be_nil
+ end
+ it "when x is set, reset_property resets it" do
+ resource.x 10
+ expect(resource.property_is_set?(:x)).to be_truthy
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to be_nil
+ end
+ end
+
+ with_property ':x, Integer' do
+ it "when the resource is newly created, reset_property(:x) does nothing" do
+ expect(resource.property_is_set?(:x)).to be_falsey
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to be_nil
+ end
+ it "when x is set, reset_property resets it even though `nil` is technically invalid" do
+ resource.x 10
+ expect(resource.property_is_set?(:x)).to be_truthy
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to be_nil
+ end
+ end
+
+ with_property ':x, default: 10' do
+ it "when the resource is newly created, reset_property(:x) does nothing" do
+ expect(resource.property_is_set?(:x)).to be_falsey
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to eq 10
+ end
+ it "when x is set, reset_property resets it and it returns the default" do
+ resource.x 20
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to eq 10
+ end
+ end
+
+ with_property ':x, default: lazy { 10 }' do
+ it "when the resource is newly created, reset_property(:x) does nothing" do
+ expect(resource.property_is_set?(:x)).to be_falsey
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to eq 10
+ end
+ it "when x is set, reset_property resets it and it returns the default" do
+ resource.x 20
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to eq 10
+ end
+ end
+ end
+
+ context "Chef::Resource::Property#property_is_set?" do
it "when a resource is newly created, property_is_set?(:name) is true" do
expect(resource.property_is_set?(:name)).to be_truthy
end
- # it "when referencing an undefined property, property_is_set?(:x) raises an error" do
- # expect { resource.property_is_set?(:x) }.to raise_error(ArgumentError)
- # end
+ it "when referencing an undefined property, property_is_set?(:x) raises an error" do
+ expect { resource.property_is_set?(:x) }.to raise_error(ArgumentError)
+ end
with_property ':x' do
it "when the resource is newly created, property_is_set?(:x) is false" do
@@ -259,9 +357,9 @@ describe "Chef::Resource.property" do
resource.x lazy { 10 }
expect(resource.property_is_set?(:x)).to be_truthy
end
- it "when x is retrieved, property_is_set?(:x) is true" do
+ it "when x is retrieved, property_is_set?(:x) is false" do
resource.x
- expect(resource.property_is_set?(:x)).to be_truthy
+ expect(resource.property_is_set?(:x)).to be_falsey
end
end
@@ -281,9 +379,9 @@ describe "Chef::Resource.property" do
resource.x lazy { 10 }
expect(resource.property_is_set?(:x)).to be_truthy
end
- it "when x is retrieved, property_is_set?(:x) is true" do
+ it "when x is retrieved, property_is_set?(:x) is false" do
resource.x
- expect(resource.property_is_set?(:x)).to be_truthy
+ expect(resource.property_is_set?(:x)).to be_falsey
end
end
@@ -299,14 +397,14 @@ describe "Chef::Resource.property" do
resource.x = 10
expect(resource.property_is_set?(:x)).to be_truthy
end
- it "when x is retrieved, property_is_set?(:x) is true" do
+ it "when x is retrieved, property_is_set?(:x) is false" do
resource.x
- expect(resource.property_is_set?(:x)).to be_truthy
+ expect(resource.property_is_set?(:x)).to be_falsey
end
end
end
- context "Chef::Resource::PropertyType#default" do
+ context "Chef::Resource::Property#default" do
with_property ':x, default: 10' do
it "when x is set, it returns its value" do
expect(resource.x 20).to eq 20
@@ -317,7 +415,7 @@ describe "Chef::Resource.property" do
expect(resource.x).to eq 10
end
it "when x is not set, it is not included in state" do
- expect(resource.state).to eq({})
+ expect(resource.state_for_resource_reporter).to eq({})
end
it "when x is set to nil, it returns nil" do
resource.instance_eval { @x = nil }
@@ -339,8 +437,15 @@ describe "Chef::Resource.property" do
end
with_property ':x, default: 10, identity: true' do
- it "when x is not set, it is not included in identity" do
- expect(resource.state).to eq({})
+ it "when x is not set, it is included in identity" do
+ expect(resource.identity).to eq(10)
+ end
+ end
+
+ with_property ':x, default: 1, identity: true', ':y, default: 2, identity: true' do
+ it "when x is not set, it is still included in identity" do
+ resource.y 20
+ expect(resource.identity).to eq(x: 1, y: 20)
end
end
@@ -462,21 +567,35 @@ describe "Chef::Resource.property" do
# end
end
- with_property ":x, default: lazy { Namer.next_index }, is: proc { |v| Namer.next_index; true }" do
+ with_property ":x, default: lazy { Namer.next_index.to_s }, is: proc { |v| Namer.next_index; true }" do
it "validation is not run at all on the default value" do
- expect(resource.x).to eq 1
+ expect(resource.x).to eq '1'
+ expect(Namer.current_index).to eq 1
+ end
+ # it "validation is run each time" do
+ # expect(resource.x).to eq '1'
+ # expect(Namer.current_index).to eq 2
+ # expect(resource.x).to eq '1'
+ # expect(Namer.current_index).to eq 2
+ # end
+ end
+
+ with_property ":x, default: lazy { Namer.next_index.to_s.freeze }, is: proc { |v| Namer.next_index; true }" do
+ it "validation is not run at all on the default value" do
+ expect(resource.x).to eq '1'
expect(Namer.current_index).to eq 1
end
# it "validation is only run the first time" do
- # expect(resource.x).to eq 1
+ # expect(resource.x).to eq '1'
# expect(Namer.current_index).to eq 2
- # expect(resource.x).to eq 1
+ # expect(resource.x).to eq '1'
# expect(Namer.current_index).to eq 2
# end
end
end
context "coercion of defaults" do
+ # Frozen default, non-frozen coerce
with_property ':x, coerce: proc { |v| "#{v}#{next_index}" }, default: 10' do
it "when the resource is created, the proc is not yet run" do
resource
@@ -487,13 +606,32 @@ describe "Chef::Resource.property" do
expect(resource.x).to eq 'hi1'
expect(Namer.current_index).to eq 1
end
- it "when x is retrieved, coercion is run, no more than once" do
+ it "when x is retrieved, coercion is run exactly once" do
expect(resource.x).to eq '101'
expect(resource.x).to eq '101'
expect(Namer.current_index).to eq 1
end
end
+ # Frozen default, frozen coerce
+ with_property ':x, coerce: proc { |v| "#{v}#{next_index}".freeze }, default: 10' do
+ it "when the resource is created, the proc is not yet run" do
+ resource
+ expect(Namer.current_index).to eq 0
+ end
+ it "when x is set, coercion is run" do
+ expect(resource.x 'hi').to eq 'hi1'
+ expect(resource.x).to eq 'hi1'
+ expect(Namer.current_index).to eq 1
+ end
+ it "when x is retrieved, coercion is run each time" do
+ expect(resource.x).to eq '101'
+ expect(resource.x).to eq '102'
+ expect(Namer.current_index).to eq 2
+ end
+ end
+
+ # Frozen lazy default, non-frozen coerce
with_property ':x, coerce: proc { |v| "#{v}#{next_index}" }, default: lazy { 10 }' do
it "when the resource is created, the proc is not yet run" do
resource
@@ -504,6 +642,29 @@ describe "Chef::Resource.property" do
expect(resource.x).to eq 'hi1'
expect(Namer.current_index).to eq 1
end
+ it "when x is retrieved, coercion is run exactly once" do
+ expect(resource.x).to eq '101'
+ expect(resource.x).to eq '101'
+ expect(Namer.current_index).to eq 1
+ end
+ end
+
+ # Non-frozen lazy default, frozen coerce
+ with_property ':x, coerce: proc { |v| "#{v}#{next_index}".freeze }, default: lazy { "10" }' do
+ it "when the resource is created, the proc is not yet run" do
+ resource
+ expect(Namer.current_index).to eq 0
+ end
+ it "when x is set, coercion is run" do
+ expect(resource.x 'hi').to eq 'hi1'
+ expect(resource.x).to eq 'hi1'
+ expect(Namer.current_index).to eq 1
+ end
+ it "when x is retrieved, coercion is run each time" do
+ expect(resource.x).to eq '101'
+ expect(resource.x).to eq '102'
+ expect(Namer.current_index).to eq 2
+ end
end
with_property ':x, proc { |v| Namer.next_index; true }, coerce: proc { |v| "#{v}#{next_index}" }, default: lazy { 10 }' do
@@ -706,7 +867,7 @@ describe "Chef::Resource.property" do
end
end
- context "Chef::Resource::PropertyType#coerce" do
+ context "Chef::Resource::Property#coerce" do
with_property ':x, coerce: proc { |v| "#{v}#{Namer.next_index}" }' do
it "coercion runs on set" do
expect(resource.x 10).to eq "101"
@@ -740,7 +901,7 @@ describe "Chef::Resource.property" do
end
end
- context "Chef::Resource::PropertyType validation" do
+ context "Chef::Resource::Property validation" do
with_property ':x, is: proc { |v| Namer.next_index; v.is_a?(Integer) }' do
it "validation runs on set" do
expect(resource.x 10).to eq 10
@@ -768,7 +929,7 @@ describe "Chef::Resource.property" do
end
[ 'name_attribute', 'name_property' ].each do |name|
- context "Chef::Resource::PropertyType##{name}" do
+ context "Chef::Resource::Property##{name}" do
with_property ":x, #{name}: true" do
it "defaults x to resource.name" do
expect(resource.x).to eq 'blah'
@@ -797,6 +958,11 @@ describe "Chef::Resource.property" do
expect(resource.x).to eq 10
end
end
+ with_property ":x, #{name}: true, required: true" do
+ it "defaults x to resource.name" do
+ expect(resource.x).to eq 'blah'
+ end
+ end
end
end
end
diff --git a/spec/unit/provider/deploy_spec.rb b/spec/unit/provider/deploy_spec.rb
index 63658ac601..f6bb78823f 100644
--- a/spec/unit/provider/deploy_spec.rb
+++ b/spec/unit/provider/deploy_spec.rb
@@ -622,7 +622,7 @@ describe Chef::Provider::Deploy do
gems = @provider.send(:gem_packages)
- expect(gems.map { |g| g.action }).to eq([:install])
+ expect(gems.map { |g| g.action }).to eq([%i{install}])
expect(gems.map { |g| g.name }).to eq(%w{eventmachine})
expect(gems.map { |g| g.version }).to eq(%w{0.12.9})
end
diff --git a/spec/unit/provider/dsc_resource_spec.rb b/spec/unit/provider/dsc_resource_spec.rb
index 0a6c22bdcf..65c1c019f0 100644
--- a/spec/unit/provider/dsc_resource_spec.rb
+++ b/spec/unit/provider/dsc_resource_spec.rb
@@ -35,10 +35,10 @@ describe Chef::Provider::DscResource do
node
}
- it 'raises a NoProviderAvailable exception' do
+ it 'raises a ProviderNotFound exception' do
expect(provider).not_to receive(:meta_configuration)
expect{provider.run_action(:run)}.to raise_error(
- Chef::Exceptions::NoProviderAvailable, /5\.0\.10018\.0/)
+ Chef::Exceptions::ProviderNotFound, /5\.0\.10018\.0/)
end
end
@@ -56,7 +56,7 @@ describe Chef::Provider::DscResource do
expect(provider).to receive(:meta_configuration).and_return(
meta_configuration)
expect { provider.run_action(:run) }.to raise_error(
- Chef::Exceptions::NoProviderAvailable, /Disabled/)
+ Chef::Exceptions::ProviderNotFound, /Disabled/)
end
end
diff --git a/spec/unit/provider/dsc_script_spec.rb b/spec/unit/provider/dsc_script_spec.rb
index d4b2eb3b22..76589e71c1 100644
--- a/spec/unit/provider/dsc_script_spec.rb
+++ b/spec/unit/provider/dsc_script_spec.rb
@@ -158,14 +158,14 @@ describe Chef::Provider::DscScript do
expect {
provider.run_action(:run)
- }.to raise_error(Chef::Exceptions::NoProviderAvailable)
+ }.to raise_error(Chef::Exceptions::ProviderNotFound)
end
end
it 'raises an exception if Powershell is not present' do
expect {
provider.run_action(:run)
- }.to raise_error(Chef::Exceptions::NoProviderAvailable)
+ }.to raise_error(Chef::Exceptions::ProviderNotFound)
end
end
diff --git a/spec/unit/provider/powershell_spec.rb b/spec/unit/provider/powershell_script_spec.rb
index 855c18af9b..855c18af9b 100644
--- a/spec/unit/provider/powershell_spec.rb
+++ b/spec/unit/provider/powershell_script_spec.rb
diff --git a/spec/unit/provider/registry_key_spec.rb b/spec/unit/provider/registry_key_spec.rb
index 79811fdab8..47543ffe39 100644
--- a/spec/unit/provider/registry_key_spec.rb
+++ b/spec/unit/provider/registry_key_spec.rb
@@ -77,6 +77,18 @@ shared_examples_for "a registry key" do
end
describe "action_create" do
+ context "when a case insensitive match for the key exists" do
+ before(:each) do
+ expect(@double_registry).to receive(:key_exists?).twice.with(keyname.downcase).and_return(true)
+ end
+ it "should do nothing if the if a case insensitive key and the value both exist" do
+ @provider.new_resource.key(keyname.downcase)
+ expect(@double_registry).to receive(:get_values).with(keyname.downcase).and_return( testval1 )
+ expect(@double_registry).not_to receive(:set_value)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ end
context "when the key exists" do
before(:each) do
expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(true)
diff --git a/spec/unit/provider_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb
index cd3d7713a7..88df4a20cc 100644
--- a/spec/unit/provider_resolver_spec.rb
+++ b/spec/unit/provider_resolver_spec.rb
@@ -28,30 +28,37 @@ include Chef::Mixin::ConvertToClassName
describe Chef::ProviderResolver do
+ let(:resource_name) { :service }
+ let(:provider) { nil }
+ let(:action) { :start }
+
let(:node) do
node = Chef::Node.new
- allow(node).to receive(:[]).with(:os).and_return(os)
- allow(node).to receive(:[]).with(:platform_family).and_return(platform_family)
- allow(node).to receive(:[]).with(:platform).and_return(platform)
- allow(node).to receive(:[]).with(:platform_version).and_return(platform_version)
- allow(node).to receive(:is_a?).and_return(Chef::Node)
+ node.automatic[:os] = os
+ node.automatic[:platform_family] = platform_family
+ node.automatic[:platform] = platform
+ node.automatic[:platform_version] = platform_version
+ node.automatic[:kernel] = { machine: 'i386' }
node
end
+ let(:run_context) { Chef::RunContext.new(node, nil, nil) }
let(:provider_resolver) { Chef::ProviderResolver.new(node, resource, action) }
+ let(:resolved_provider) do
+ begin
+ resource ? resource.provider_for_action(action).class : nil
+ rescue Chef::Exceptions::ProviderNotFound
+ nil
+ end
+ end
- let(:action) { :start }
-
- let(:resolved_provider) { provider_resolver.resolve }
-
- let(:provider) { nil }
-
- let(:resource_name) { :service }
-
- let(:resource) { double(Chef::Resource, provider: provider, resource_name: resource_name) }
-
- before do
- allow(resource).to receive(:is_a?).with(Chef::Resource).and_return(true)
+ let(:resource) do
+ resource_class = Chef::ResourceResolver.resolve(resource_name, node: node)
+ if resource_class
+ resource = resource_class.new('test', run_context)
+ resource.provider = provider if provider
+ end
+ resource
end
def self.on_platform(platform, *tags,
@@ -83,16 +90,41 @@ describe Chef::ProviderResolver do
end
def self.expect_providers(**providers)
- providers.each do |name, provider|
+ providers.each do |name, expected|
describe name.to_s do
let(:resource_name) { name }
- if provider
- it "resolves to a #{provider}" do
- expect(resolved_provider).to eql(provider)
+
+ tags = []
+ expected_provider = nil
+ expected_resource = nil
+ Array(expected).each do |p|
+ if p.is_a?(Class) && p <= Chef::Provider
+ expected_provider = p
+ elsif p.is_a?(Class) && p <= Chef::Resource
+ expected_resource = p
+ else
+ tags << p
+ end
+ end
+
+ if expected_resource && expected_provider
+ it "'#{name}' resolves to resource #{expected_resource} and provider #{expected_provider}", *tags do
+ expect(resource.class).to eql(expected_resource)
+ provider = double(expected_provider, class: expected_provider)
+ expect(provider).to receive(:action=).with(action)
+ expect(expected_provider).to receive(:new).with(resource, run_context).and_return(provider)
+ expect(resolved_provider).to eql(expected_provider)
+ end
+ elsif expected_provider
+ it "'#{name}' resolves to provider #{expected_provider}", *tags do
+ provider = double(expected_provider)
+ expect(provider).to receive(:action=).with(action)
+ expect(expected_provider).to receive(:new).with(resource, run_context).and_return(provider)
+ expect(resolved_provider).to eql(expected_provider)
end
else
- it "Fails to resolve (since #{name.inspect} is unsupported on #{platform} #{platform_version})" do
- expect { resolved_provider }.to raise_error /Cannot find a provider/
+ it "'#{name}' fails to resolve (since #{name.inspect} is unsupported on #{platform} #{platform_version})", *tags do
+ expect(resolved_provider).to be_nil
end
end
end
@@ -454,48 +486,48 @@ describe Chef::ProviderResolver do
PROVIDERS =
{
- bash: Chef::Provider::Script,
- breakpoint: Chef::Provider::Breakpoint,
- chef_gem: Chef::Provider::Package::Rubygems,
- cookbook_file: Chef::Provider::CookbookFile,
- csh: Chef::Provider::Script,
- deploy: Chef::Provider::Deploy::Timestamped,
- deploy_revision: Chef::Provider::Deploy::Revision,
- directory: Chef::Provider::Directory,
- easy_install_package: Chef::Provider::Package::EasyInstall,
- erl_call: Chef::Provider::ErlCall,
- execute: Chef::Provider::Execute,
- file: Chef::Provider::File,
- gem_package: Chef::Provider::Package::Rubygems,
- git: Chef::Provider::Git,
- group: Chef::Provider::Group::Gpasswd,
- homebrew_package: Chef::Provider::Package::Homebrew,
- http_request: Chef::Provider::HttpRequest,
- ifconfig: Chef::Provider::Ifconfig,
- link: Chef::Provider::Link,
- log: Chef::Provider::Log::ChefLog,
- macports_package: Chef::Provider::Package::Macports,
- mdadm: Chef::Provider::Mdadm,
- mount: Chef::Provider::Mount::Mount,
- perl: Chef::Provider::Script,
- portage_package: Chef::Provider::Package::Portage,
- python: Chef::Provider::Script,
- remote_directory: Chef::Provider::RemoteDirectory,
- route: Chef::Provider::Route,
- ruby: Chef::Provider::Script,
- ruby_block: Chef::Provider::RubyBlock,
- script: Chef::Provider::Script,
- subversion: Chef::Provider::Subversion,
- template: Chef::Provider::Template,
- timestamped_deploy: Chef::Provider::Deploy::Timestamped,
- user: Chef::Provider::User::Useradd,
- whyrun_safe_ruby_block: Chef::Provider::WhyrunSafeRubyBlock,
+ bash: [ Chef::Resource::Bash, Chef::Provider::Script ],
+ breakpoint: [ Chef::Resource::Breakpoint, Chef::Provider::Breakpoint ],
+ chef_gem: [ Chef::Resource::ChefGem, Chef::Provider::Package::Rubygems ],
+ cookbook_file: [ Chef::Resource::CookbookFile, Chef::Provider::CookbookFile ],
+ csh: [ Chef::Resource::Csh, Chef::Provider::Script ],
+ deploy: [ Chef::Resource::Deploy, Chef::Provider::Deploy::Timestamped ],
+ deploy_revision: [ Chef::Resource::DeployRevision, Chef::Provider::Deploy::Revision ],
+ directory: [ Chef::Resource::Directory, Chef::Provider::Directory ],
+ easy_install_package: [ Chef::Resource::EasyInstallPackage, Chef::Provider::Package::EasyInstall ],
+ erl_call: [ Chef::Resource::ErlCall, Chef::Provider::ErlCall ],
+ execute: [ Chef::Resource::Execute, Chef::Provider::Execute ],
+ file: [ Chef::Resource::File, Chef::Provider::File ],
+ gem_package: [ Chef::Resource::GemPackage, Chef::Provider::Package::Rubygems ],
+ git: [ Chef::Resource::Git, Chef::Provider::Git ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
+ homebrew_package: [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
+ http_request: [ Chef::Resource::HttpRequest, Chef::Provider::HttpRequest ],
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ link: [ Chef::Resource::Link, Chef::Provider::Link ],
+ log: [ Chef::Resource::Log, Chef::Provider::Log::ChefLog ],
+ macports_package: [ Chef::Resource::MacportsPackage, Chef::Provider::Package::Macports ],
+ mdadm: [ Chef::Resource::Mdadm, Chef::Provider::Mdadm ],
+ mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Mount ],
+ perl: [ Chef::Resource::Perl, Chef::Provider::Script ],
+ portage_package: [ Chef::Resource::PortagePackage, Chef::Provider::Package::Portage ],
+ python: [ Chef::Resource::Python, Chef::Provider::Script ],
+ remote_directory: [ Chef::Resource::RemoteDirectory, Chef::Provider::RemoteDirectory ],
+ route: [ Chef::Resource::Route, Chef::Provider::Route ],
+ ruby: [ Chef::Resource::Ruby, Chef::Provider::Script ],
+ ruby_block: [ Chef::Resource::RubyBlock, Chef::Provider::RubyBlock ],
+ script: [ Chef::Resource::Script, Chef::Provider::Script ],
+ subversion: [ Chef::Resource::Subversion, Chef::Provider::Subversion ],
+ template: [ Chef::Resource::Template, Chef::Provider::Template ],
+ timestamped_deploy: [ Chef::Resource::TimestampedDeploy, Chef::Provider::Deploy::Timestamped ],
+ user: [ Chef::Resource::User, Chef::Provider::User::Useradd ],
+ whyrun_safe_ruby_block: [ Chef::Resource::WhyrunSafeRubyBlock, Chef::Provider::WhyrunSafeRubyBlock ],
# We want to check that these are unsupported:
apt_package: nil,
bff_package: nil,
- dsc_script: nil,
dpkg_package: nil,
+ dsc_script: nil,
ips_package: nil,
pacman_package: nil,
paludis_package: nil,
@@ -507,61 +539,62 @@ describe Chef::ProviderResolver do
windows_service: nil,
"linux" => {
- apt_package: Chef::Provider::Package::Apt,
- dpkg_package: Chef::Provider::Package::Dpkg,
- pacman_package: Chef::Provider::Package::Pacman,
- paludis_package: Chef::Provider::Package::Paludis,
- rpm_package: Chef::Provider::Package::Rpm,
- yum_package: Chef::Provider::Package::Yum,
+ apt_package: [ Chef::Resource::AptPackage, Chef::Provider::Package::Apt ],
+ dpkg_package: [ Chef::Resource::DpkgPackage, Chef::Provider::Package::Dpkg ],
+ pacman_package: [ Chef::Resource::PacmanPackage, Chef::Provider::Package::Pacman ],
+ paludis_package: [ Chef::Resource::PaludisPackage, Chef::Provider::Package::Paludis ],
+ rpm_package: [ Chef::Resource::RpmPackage, Chef::Provider::Package::Rpm ],
+ yum_package: [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
"debian" => {
- ifconfig: Chef::Provider::Ifconfig::Debian,
- package: Chef::Provider::Package::Apt,
-# service: Chef::Provider::Service::Debian,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Debian ],
+ package: [ Chef::Resource::AptPackage, Chef::Provider::Package::Apt ],
+# service: [ Chef::Resource::DebianService, Chef::Provider::Service::Debian ],
"debian" => {
"7.0" => {
},
"6.0" => {
- ifconfig: Chef::Provider::Ifconfig,
-# service: Chef::Provider::Service::Insserv,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+# service: [ Chef::Resource::InsservService, Chef::Provider::Service::Insserv ],
},
"5.0" => {
- ifconfig: Chef::Provider::Ifconfig,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
},
},
"gcel" => {
"3.1.4" => {
- ifconfig: Chef::Provider::Ifconfig,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
},
},
"linaro" => {
"3.1.4" => {
- ifconfig: Chef::Provider::Ifconfig,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
},
},
"linuxmint" => {
"3.1.4" => {
- ifconfig: Chef::Provider::Ifconfig,
-# service: Chef::Provider::Service::Upstart,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+# service: [ Chef::Resource::UpstartService, Chef::Provider::Service::Upstart ],
},
},
"raspbian" => {
"3.1.4" => {
- ifconfig: Chef::Provider::Ifconfig,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
},
},
"ubuntu" => {
"11.10" => {
},
"10.04" => {
- ifconfig: Chef::Provider::Ifconfig,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
},
},
},
"arch" => {
- package: Chef::Provider::Package::Pacman,
+ # TODO should be Chef::Resource::PacmanPackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Pacman ],
"arch" => {
"3.1.4" => {
@@ -570,8 +603,8 @@ describe Chef::ProviderResolver do
},
"freebsd" => {
- group: Chef::Provider::Group::Pw,
- user: Chef::Provider::User::Pw,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Pw ],
+ user: [ Chef::Resource::User, Chef::Provider::User::Pw ],
"freebsd" => {
"3.1.4" => {
@@ -579,30 +612,31 @@ describe Chef::ProviderResolver do
},
},
"suse" => {
- group: Chef::Provider::Group::Gpasswd,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
"suse" => {
"12.0" => {
},
%w(11.1 11.2 11.3) => {
- group: Chef::Provider::Group::Suse,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
},
},
"opensuse" => {
-# service: Chef::Provider::Service::Redhat,
- package: Chef::Provider::Package::Zypper,
- group: Chef::Provider::Group::Usermod,
+# service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+ package: [ Chef::Resource::ZypperPackage, Chef::Provider::Package::Zypper ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
"12.3" => {
},
"12.2" => {
- group: Chef::Provider::Group::Suse,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
},
},
},
"gentoo" => {
- package: Chef::Provider::Package::Portage,
- portage_package: Chef::Provider::Package::Portage,
-# service: Chef::Provider::Service::Gentoo,
+ # TODO should be Chef::Resource::PortagePackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Portage ],
+ portage_package: [ Chef::Resource::PortagePackage, Chef::Provider::Package::Portage ],
+# service: [ Chef::Resource::GentooService, Chef::Provider::Service::Gentoo ],
"gentoo" => {
"3.1.4" => {
@@ -611,27 +645,27 @@ describe Chef::ProviderResolver do
},
"rhel" => {
-# service: Chef::Provider::Service::Systemd,
- package: Chef::Provider::Package::Yum,
- ifconfig: Chef::Provider::Ifconfig::Redhat,
+# service: [ Chef::Resource::SystemdService, Chef::Provider::Service::Systemd ],
+ package: [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Redhat ],
%w(amazon xcp xenserver ibm_powerkvm cloudlinux parallels) => {
"3.1.4" => {
-# service: Chef::Provider::Service::Redhat,
+# service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
},
},
%w(redhat centos scientific oracle) => {
"7.0" => {
},
"6.0" => {
-# service: Chef::Provider::Service::Redhat,
+# service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
},
},
"fedora" => {
"15.0" => {
},
"14.0" => {
-# service: Chef::Provider::Service::Redhat,
+# service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
},
},
},
@@ -640,9 +674,9 @@ describe Chef::ProviderResolver do
"darwin" => {
%w(mac_os_x mac_os_x_server) => {
- group: Chef::Provider::Group::Dscl,
- package: Chef::Provider::Package::Homebrew,
- user: Chef::Provider::User::Dscl,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Dscl ],
+ package: [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
+ user: [ Chef::Resource::User, Chef::Provider::User::Dscl ],
"mac_os_x" => {
"10.9.2" => {
@@ -652,17 +686,17 @@ describe Chef::ProviderResolver do
},
"windows" => {
- batch: Chef::Provider::Batch,
- dsc_script: Chef::Provider::DscScript,
- env: Chef::Provider::Env::Windows,
- group: Chef::Provider::Group::Windows,
- mount: Chef::Provider::Mount::Windows,
- package: Chef::Provider::Package::Windows,
- powershell_script: Chef::Provider::PowershellScript,
- service: Chef::Provider::Service::Windows,
- user: Chef::Provider::User::Windows,
- windows_package: Chef::Provider::Package::Windows,
- windows_service: Chef::Provider::Service::Windows,
+ batch: [ Chef::Resource::Batch, Chef::Provider::Batch ],
+ dsc_script: [ Chef::Resource::DscScript, Chef::Provider::DscScript ],
+ env: [ Chef::Resource::Env, Chef::Provider::Env::Windows ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Windows ],
+ mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Windows ],
+ package: [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
+ powershell_script: [ Chef::Resource::PowershellScript, Chef::Provider::PowershellScript ],
+ service: [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
+ user: [ Chef::Resource::User, Chef::Provider::User::Windows ],
+ windows_package: [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
+ windows_service: [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
"windows" => {
%w(mswin mingw32 windows) => {
@@ -673,15 +707,16 @@ describe Chef::ProviderResolver do
},
"aix" => {
- bff_package: Chef::Provider::Package::Aix,
- cron: Chef::Provider::Cron::Aix,
- group: Chef::Provider::Group::Aix,
- ifconfig: Chef::Provider::Ifconfig::Aix,
- mount: Chef::Provider::Mount::Aix,
- package: Chef::Provider::Package::Aix,
- rpm_package: Chef::Provider::Package::Rpm,
- user: Chef::Provider::User::Aix,
-# service: Chef::Provider::Service::Aix,
+ bff_package: [ Chef::Resource::BffPackage, Chef::Provider::Package::Aix ],
+ cron: [ Chef::Resource::Cron, Chef::Provider::Cron::Aix ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Aix ],
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Aix ],
+ mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Aix ],
+ # TODO should be Chef::Resource::BffPackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Aix ],
+ rpm_package: [ Chef::Resource::RpmPackage, Chef::Provider::Package::Rpm ],
+ user: [ Chef::Resource::User, Chef::Provider::User::Aix ],
+# service: [ Chef::Resource::AixService, Chef::Provider::Service::Aix ],
"aix" => {
"aix" => {
@@ -695,7 +730,7 @@ describe Chef::ProviderResolver do
"hpux" => {
"hpux" => {
"3.1.4" => {
- group: Chef::Provider::Group::Usermod
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ]
}
}
}
@@ -705,15 +740,15 @@ describe Chef::ProviderResolver do
"netbsd" => {
"netbsd" => {
"3.1.4" => {
- group: Chef::Provider::Group::Groupmod,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Groupmod ],
},
},
},
},
"openbsd" => {
- group: Chef::Provider::Group::Usermod,
- package: Chef::Provider::Package::Openbsd,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+ package: [ Chef::Resource::OpenbsdPackage, Chef::Provider::Package::Openbsd ],
"openbsd" => {
"openbsd" => {
@@ -724,15 +759,15 @@ describe Chef::ProviderResolver do
},
"solaris2" => {
- group: Chef::Provider::Group::Usermod,
- ips_package: Chef::Provider::Package::Ips,
- package: Chef::Provider::Package::Ips,
- mount: Chef::Provider::Mount::Solaris,
- solaris_package: Chef::Provider::Package::Solaris,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+ ips_package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
+ package: [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
+ mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Solaris ],
+ solaris_package: [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
"smartos" => {
- smartos_package: Chef::Provider::Package::SmartOS,
- package: Chef::Provider::Package::SmartOS,
+ smartos_package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
+ package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
"smartos" => {
"3.1.4" => {
@@ -743,12 +778,11 @@ describe Chef::ProviderResolver do
"solaris2" => {
"nexentacore" => {
"3.1.4" => {
- package: Chef::Provider::Package::Solaris,
},
},
"omnios" => {
"3.1.4" => {
- user: Chef::Provider::User::Solaris,
+ user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
}
},
"openindiana" => {
@@ -760,11 +794,11 @@ describe Chef::ProviderResolver do
},
},
"solaris2" => {
- user: Chef::Provider::User::Solaris,
+ user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
"5.11" => {
+ package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
},
"5.9" => {
- package: Chef::Provider::Package::Solaris,
},
},
},
@@ -784,7 +818,8 @@ describe Chef::ProviderResolver do
"exherbo" => {
"exherbo" => {
"3.1.4" => {
- package: Chef::Provider::Package::Paludis
+ # TODO should be Chef::Resource::PaludisPackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Paludis ]
}
}
}
diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb
index 17ea498fe3..511e7e9397 100644
--- a/spec/unit/recipe_spec.rb
+++ b/spec/unit/recipe_spec.rb
@@ -409,8 +409,8 @@ describe Chef::Recipe do
end
it "does not copy the action from the first resource" do
- expect(original_resource.action).to eq(:score)
- expect(duplicated_resource.action).to eq(:nothing)
+ expect(original_resource.action).to eq([:score])
+ expect(duplicated_resource.action).to eq([:nothing])
end
it "does not copy the source location of the first resource" do
@@ -505,7 +505,7 @@ describe Chef::Recipe do
recipe.from_file(File.join(CHEF_SPEC_DATA, "recipes", "test.rb"))
res = recipe.resources(:file => "/etc/nsswitch.conf")
expect(res.name).to eql("/etc/nsswitch.conf")
- expect(res.action).to eql(:create)
+ expect(res.action).to eql([:create])
expect(res.owner).to eql("root")
expect(res.group).to eql("root")
expect(res.mode).to eql(0644)
diff --git a/spec/unit/registry_helper_spec.rb b/spec/unit/registry_helper_spec.rb
index 036a0834db..b2d0b7b125 100644
--- a/spec/unit/registry_helper_spec.rb
+++ b/spec/unit/registry_helper_spec.rb
@@ -21,6 +21,7 @@ require 'spec_helper'
describe Chef::Provider::RegistryKey do
let(:value1) { { :name => "one", :type => :string, :data => "1" } }
+ let(:value1_upcase_name) { {:name => "ONE", :type => :string, :data => "1"} }
let(:key_path) { 'HKCU\Software\OpscodeNumbers' }
let(:key) { 'Software\OpscodeNumbers' }
let(:key_parent) { 'Software' }
@@ -71,7 +72,20 @@ describe Chef::Provider::RegistryKey do
expect(@registry).to receive(:data_exists?).with(key_path, value1).and_return(true)
@registry.set_value(key_path, value1)
end
-
+ it "does nothing if case insensitive key and hive and value exist" do
+ expect(@registry).to receive(:key_exists!).with(key_path.downcase).and_return(true)
+ expect(@registry).to receive(:get_hive_and_key).with(key_path.downcase).and_return([@hive_mock, key])
+ expect(@registry).to receive(:value_exists?).with(key_path.downcase, value1).and_return(true)
+ expect(@registry).to receive(:data_exists?).with(key_path.downcase, value1).and_return(true)
+ @registry.set_value(key_path.downcase, value1)
+ end
+ it "does nothing if key and hive and value with a case insensitive name exist" do
+ expect(@registry).to receive(:key_exists!).with(key_path.downcase).and_return(true)
+ expect(@registry).to receive(:get_hive_and_key).with(key_path.downcase).and_return([@hive_mock, key])
+ expect(@registry).to receive(:value_exists?).with(key_path.downcase, value1_upcase_name).and_return(true)
+ expect(@registry).to receive(:data_exists?).with(key_path.downcase, value1_upcase_name).and_return(true)
+ @registry.set_value(key_path.downcase, value1_upcase_name)
+ end
it "updates value if key and hive and value exist, but data is different" do
expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
diff --git a/spec/unit/resource/breakpoint_spec.rb b/spec/unit/resource/breakpoint_spec.rb
index 9c867ebcc7..88ab34d568 100644
--- a/spec/unit/resource/breakpoint_spec.rb
+++ b/spec/unit/resource/breakpoint_spec.rb
@@ -37,7 +37,7 @@ describe Chef::Resource::Breakpoint do
end
it "defaults to the break action" do
- expect(@breakpoint.action).to eq(:break)
+ expect(@breakpoint.action).to eq([:break])
end
it "names itself after the line number of the file where it's created" do
diff --git a/spec/unit/resource/cron_spec.rb b/spec/unit/resource/cron_spec.rb
index 743552c1de..0978be6930 100644
--- a/spec/unit/resource/cron_spec.rb
+++ b/spec/unit/resource/cron_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::Cron do
end
it "should have a default action of 'create'" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
it "should accept create or delete for action" do
diff --git a/spec/unit/resource/directory_spec.rb b/spec/unit/resource/directory_spec.rb
index c452b2a914..e9e80806db 100644
--- a/spec/unit/resource/directory_spec.rb
+++ b/spec/unit/resource/directory_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::Directory do
end
it "should have a default action of 'create'" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
it "should accept create or delete for action" do
diff --git a/spec/unit/resource/dsc_resource_spec.rb b/spec/unit/resource/dsc_resource_spec.rb
index ae15f56eaf..06769d86ce 100644
--- a/spec/unit/resource/dsc_resource_spec.rb
+++ b/spec/unit/resource/dsc_resource_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::DscResource do
}
it "has a default action of `:run`" do
- expect(dsc_test_resource.action).to eq(:run)
+ expect(dsc_test_resource.action).to eq([:run])
end
it "has an allowed_actions attribute with only the `:run` and `:nothing` attributes" do
diff --git a/spec/unit/resource/dsc_script_spec.rb b/spec/unit/resource/dsc_script_spec.rb
index 71103ea590..4361b35b91 100644
--- a/spec/unit/resource/dsc_script_spec.rb
+++ b/spec/unit/resource/dsc_script_spec.rb
@@ -29,7 +29,7 @@ describe Chef::Resource::DscScript do
Chef::RunContext.new(node, {}, empty_events)
}
let(:dsc_test_resource) {
- Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context)
+ Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context)
}
let(:configuration_code) {'echo "This is supposed to create a configuration document."'}
let(:configuration_path) {'c:/myconfigs/formatc.ps1'}
@@ -38,7 +38,7 @@ describe Chef::Resource::DscScript do
let(:configuration_data_script) { 'c:/myconfigs/data/safedata.psd1' }
it "has a default action of `:run`" do
- expect(dsc_test_resource.action).to eq(:run)
+ expect(dsc_test_resource.action).to eq([:run])
end
it "has an allowed_actions attribute with only the `:run` and `:nothing` attributes" do
diff --git a/spec/unit/resource/env_spec.rb b/spec/unit/resource/env_spec.rb
index 566827a27e..9bee07c593 100644
--- a/spec/unit/resource/env_spec.rb
+++ b/spec/unit/resource/env_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::Env do
end
it "should have a default action of 'create'" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
{ :create => false, :delete => false, :modify => false, :flibber => true }.each do |action,bad_value|
diff --git a/spec/unit/resource/erl_call_spec.rb b/spec/unit/resource/erl_call_spec.rb
index 008d27372a..9abf2e7812 100644
--- a/spec/unit/resource/erl_call_spec.rb
+++ b/spec/unit/resource/erl_call_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::ErlCall do
end
it "should have a default action of run" do
- expect(@resource.action).to eql(:run)
+ expect(@resource.action).to eql([:run])
end
it "should accept run as an action" do
diff --git a/spec/unit/resource/file_spec.rb b/spec/unit/resource/file_spec.rb
index dd20f5f03a..76beaf15e1 100644
--- a/spec/unit/resource/file_spec.rb
+++ b/spec/unit/resource/file_spec.rb
@@ -29,7 +29,7 @@ describe Chef::Resource::File do
end
it "should have a default action of 'create'" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
it "should have a default content of nil" do
diff --git a/spec/unit/resource/group_spec.rb b/spec/unit/resource/group_spec.rb
index bcf9205f7e..a4029fc911 100644
--- a/spec/unit/resource/group_spec.rb
+++ b/spec/unit/resource/group_spec.rb
@@ -50,7 +50,7 @@ describe Chef::Resource::Group, "initialize" do
end
it "should set action to :create" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
%w{create remove modify manage}.each do |action|
diff --git a/spec/unit/resource/link_spec.rb b/spec/unit/resource/link_spec.rb
index 51221e0472..0246fcd13b 100644
--- a/spec/unit/resource/link_spec.rb
+++ b/spec/unit/resource/link_spec.rb
@@ -36,7 +36,7 @@ describe Chef::Resource::Link do
end
it "should have a default action of 'create'" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
{ :create => false, :delete => false, :blues => true }.each do |action,bad_value|
diff --git a/spec/unit/resource/mdadm_spec.rb b/spec/unit/resource/mdadm_spec.rb
index 866309ec5b..6ca99c58e5 100644
--- a/spec/unit/resource/mdadm_spec.rb
+++ b/spec/unit/resource/mdadm_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::Mdadm do
end
it "should have a default action of create" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
it "should accept create, assemble, stop as actions" do
diff --git a/spec/unit/resource/mount_spec.rb b/spec/unit/resource/mount_spec.rb
index ad95c06e04..acce26dcab 100644
--- a/spec/unit/resource/mount_spec.rb
+++ b/spec/unit/resource/mount_spec.rb
@@ -38,7 +38,7 @@ describe Chef::Resource::Mount do
end
it "should have a default action of mount" do
- expect(@resource.action).to eql(:mount)
+ expect(@resource.action).to eql([:mount])
end
it "should accept mount, umount and remount as actions" do
diff --git a/spec/unit/resource/ohai_spec.rb b/spec/unit/resource/ohai_spec.rb
index fe29755abf..3bc21a41d2 100644
--- a/spec/unit/resource/ohai_spec.rb
+++ b/spec/unit/resource/ohai_spec.rb
@@ -34,7 +34,7 @@ describe Chef::Resource::Ohai do
end
it "should have a default action of create" do
- expect(@resource.action).to eql(:reload)
+ expect(@resource.action).to eql([:reload])
end
it "should allow you to set the plugin attribute" do
diff --git a/spec/unit/resource/powershell_spec.rb b/spec/unit/resource/powershell_script_spec.rb
index 2505c4a3d7..2505c4a3d7 100644
--- a/spec/unit/resource/powershell_spec.rb
+++ b/spec/unit/resource/powershell_script_spec.rb
diff --git a/spec/unit/resource/registry_key_spec.rb b/spec/unit/resource/registry_key_spec.rb
index e2a864d73a..2e2811d026 100644
--- a/spec/unit/resource/registry_key_spec.rb
+++ b/spec/unit/resource/registry_key_spec.rb
@@ -45,7 +45,7 @@ describe Chef::Resource::RegistryKey, "initialize" do
end
it "should set action to :create" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
%w{create create_if_missing delete delete_key}.each do |action|
diff --git a/spec/unit/resource/route_spec.rb b/spec/unit/resource/route_spec.rb
index ffb9304511..ec1d369932 100644
--- a/spec/unit/resource/route_spec.rb
+++ b/spec/unit/resource/route_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::Route do
end
it "should have a default action of 'add'" do
- expect(@resource.action).to eql(:add)
+ expect(@resource.action).to eql([:add])
end
it "should accept add or delete for action" do
diff --git a/spec/unit/resource/ruby_block_spec.rb b/spec/unit/resource/ruby_block_spec.rb
index 5d83f7e367..8664564ac5 100644
--- a/spec/unit/resource/ruby_block_spec.rb
+++ b/spec/unit/resource/ruby_block_spec.rb
@@ -30,8 +30,8 @@ describe Chef::Resource::RubyBlock do
expect(@resource).to be_a_kind_of(Chef::Resource::RubyBlock)
end
- it "should have a default action of 'create'" do
- expect(@resource.action).to eql(:run)
+ it "should have a default action of 'run'" do
+ expect(@resource.action).to eql([:run])
end
it "should have a resource name of :ruby_block" do
@@ -46,7 +46,7 @@ describe Chef::Resource::RubyBlock do
it "allows the action to be 'create'" do
@resource.action :create
- expect(@resource.action).to eq(:create)
+ expect(@resource.action).to eq([:create])
end
describe "when it has been initialized with block code" do
diff --git a/spec/unit/resource/user_spec.rb b/spec/unit/resource/user_spec.rb
index f05de94fe0..3bf7e6187b 100644
--- a/spec/unit/resource/user_spec.rb
+++ b/spec/unit/resource/user_spec.rb
@@ -43,7 +43,7 @@ describe Chef::Resource::User, "initialize" do
end
it "should set action to :create" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
it "should set supports[:manage_home] to false" do
diff --git a/spec/unit/resource/windows_service_spec.rb b/spec/unit/resource/windows_service_spec.rb
index 8866cad1bf..45a295c24e 100644
--- a/spec/unit/resource/windows_service_spec.rb
+++ b/spec/unit/resource/windows_service_spec.rb
@@ -44,6 +44,6 @@ describe Chef::Resource::WindowsService, "initialize" do
it "allows the action to be 'configure_startup'" do
resource.action :configure_startup
- expect(resource.action).to eq(:configure_startup)
+ expect(resource.action).to eq([:configure_startup])
end
end
diff --git a/spec/unit/resource_resolver_spec.rb b/spec/unit/resource_resolver_spec.rb
index 09ff026575..b3bda9d945 100644
--- a/spec/unit/resource_resolver_spec.rb
+++ b/spec/unit/resource_resolver_spec.rb
@@ -31,19 +31,23 @@ describe Chef::ResourceResolver do
context 'instance methods' do
let(:resolver) do
- described_class.new(Chef::Node.new, 'execute[echo]')
+ described_class.new(Chef::Node.new, 'execute')
end
it '#resolve' do
- expect(resolver.resolve).to be_nil
+ expect(resolver.resolve).to eq Chef::Resource::Execute
end
it '#list' do
- expect(resolver.list).to be_empty
+ expect(resolver.list).to eq [ Chef::Resource::Execute ]
end
- it '#provided_by?' do
+ it '#provided_by? returns true when resource class is in the list' do
expect(resolver.provided_by?(Chef::Resource::Execute)).to be_truthy
end
+
+ it '#provided_by? returns false when resource class is not in the list' do
+ expect(resolver.provided_by?(Chef::Resource::File)).to be_falsey
+ end
end
end
diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb
index 8b0baff921..b9ba80068b 100644
--- a/spec/unit/resource_spec.rb
+++ b/spec/unit/resource_spec.rb
@@ -59,8 +59,8 @@ describe Chef::Resource do
end
describe "when declaring the identity attribute" do
- it "has no identity attribute by default" do
- expect(Chef::Resource.identity_attr).to be_nil
+ it "has :name as identity attribute by default" do
+ expect(Chef::Resource.identity_attr).to eq(:name)
end
it "sets an identity attribute" do
@@ -810,21 +810,21 @@ describe Chef::Resource do
end
it 'adds mappings for a single platform' do
- expect(Chef.resource_priority_map).to receive(:set).with(
+ expect(Chef.resource_handler_map).to receive(:set).with(
:dinobot, Chef::Resource::Klz, { platform: ['autobots'] }
)
klz.provides :dinobot, platform: ['autobots']
end
it 'adds mappings for multiple platforms' do
- expect(Chef.resource_priority_map).to receive(:set).with(
+ expect(Chef.resource_handler_map).to receive(:set).with(
:energy, Chef::Resource::Klz, { platform: ['autobots', 'decepticons']}
)
klz.provides :energy, platform: ['autobots', 'decepticons']
end
it 'adds mappings for all platforms' do
- expect(Chef.resource_priority_map).to receive(:set).with(
+ expect(Chef.resource_handler_map).to receive(:set).with(
:tape_deck, Chef::Resource::Klz, {}
)
klz.provides :tape_deck
@@ -977,4 +977,90 @@ describe Chef::Resource do
end
end
+
+ describe "#action" do
+ let(:resource_class) do
+ Class.new(described_class) do
+ allowed_actions(%i{one two})
+ end
+ end
+ let(:resource) { resource_class.new('test', nil) }
+ subject { resource.action }
+
+ context "with a no action" do
+ it { is_expected.to eq [:nothing] }
+ end
+
+ context "with a default action" do
+ let(:resource_class) do
+ Class.new(described_class) do
+ default_action(:one)
+ end
+ end
+ it { is_expected.to eq [:one] }
+ end
+
+ context "with a symbol action" do
+ before { resource.action(:one) }
+ it { is_expected.to eq [:one] }
+ end
+
+ context "with a string action" do
+ before { resource.action('two') }
+ it { is_expected.to eq [:two] }
+ end
+
+ context "with an array action" do
+ before { resource.action([:two, :one]) }
+ it { is_expected.to eq [:two, :one] }
+ end
+
+ context "with an assignment" do
+ before { resource.action = :one }
+ it { is_expected.to eq [:one] }
+ end
+
+ context "with an array assignment" do
+ before { resource.action = [:two, :one] }
+ it { is_expected.to eq [:two, :one] }
+ end
+
+ context "with an invalid action" do
+ it { expect { resource.action(:three) }.to raise_error Chef::Exceptions::ValidationFailed }
+ end
+
+ context "with an invalid assignment action" do
+ it { expect { resource.action = :three }.to raise_error Chef::Exceptions::ValidationFailed }
+ end
+ end
+
+ describe ".default_action" do
+ let(:default_action) { }
+ let(:resource_class) do
+ actions = default_action
+ Class.new(described_class) do
+ default_action(actions) if actions
+ end
+ end
+ subject { resource_class.default_action }
+
+ context "with no default actions" do
+ it { is_expected.to eq [:nothing] }
+ end
+
+ context "with a symbol default action" do
+ let(:default_action) { :one }
+ it { is_expected.to eq [:one] }
+ end
+
+ context "with a string default action" do
+ let(:default_action) { 'one' }
+ it { is_expected.to eq [:one] }
+ end
+
+ context "with an array default action" do
+ let(:default_action) { [:two, :one] }
+ it { is_expected.to eq [:two, :one] }
+ end
+ end
end
diff --git a/spec/unit/run_list/versioned_recipe_list_spec.rb b/spec/unit/run_list/versioned_recipe_list_spec.rb
index 209ac37fc1..9c3ecaa0dd 100644
--- a/spec/unit/run_list/versioned_recipe_list_spec.rb
+++ b/spec/unit/run_list/versioned_recipe_list_spec.rb
@@ -26,98 +26,165 @@ describe Chef::RunList::VersionedRecipeList do
end
end
+ let(:list) { described_class.new }
+
+ let(:versioned_recipes) { [] }
+
+ let(:recipes) { [] }
+
+ before do
+ recipes.each { |r| list << r }
+ versioned_recipes.each {|r| list.add_recipe r[:name], r[:version]}
+ end
+
describe "add_recipe" do
- before(:each) do
- @list = Chef::RunList::VersionedRecipeList.new
- @list << "apt"
- @list << "god"
- @list << "apache2"
- end
+
+ let(:recipes) { %w[ apt god apache2 ] }
it "should append the recipe to the end of the list" do
- @list.add_recipe "rails"
- expect(@list).to eq(["apt", "god", "apache2", "rails"])
+ list.add_recipe "rails"
+ expect(list).to eq(["apt", "god", "apache2", "rails"])
end
it "should not duplicate entries" do
- @list.add_recipe "apt"
- expect(@list).to eq(["apt", "god", "apache2"])
+ list.add_recipe "apt"
+ expect(list).to eq(["apt", "god", "apache2"])
end
it "should allow you to specify a version" do
- @list.add_recipe "rails", "1.0.0"
- expect(@list).to eq(["apt", "god", "apache2", "rails"])
- expect(@list.with_versions).to include({:name => "rails", :version => "1.0.0"})
+ list.add_recipe "rails", "1.0.0"
+ expect(list).to eq(["apt", "god", "apache2", "rails"])
+ expect(list.with_versions).to include({:name => "rails", :version => "1.0.0"})
end
it "should allow you to specify a version for a recipe that already exists" do
- @list.add_recipe "apt", "1.2.3"
- expect(@list).to eq(["apt", "god", "apache2"])
- expect(@list.with_versions).to include({:name => "apt", :version => "1.2.3"})
+ list.add_recipe "apt", "1.2.3"
+ expect(list).to eq(["apt", "god", "apache2"])
+ expect(list.with_versions).to include({:name => "apt", :version => "1.2.3"})
end
it "should allow you to specify the same version of a recipe twice" do
- @list.add_recipe "rails", "1.0.0"
- @list.add_recipe "rails", "1.0.0"
- expect(@list.with_versions).to include({:name => "rails", :version => "1.0.0"})
+ list.add_recipe "rails", "1.0.0"
+ list.add_recipe "rails", "1.0.0"
+ expect(list.with_versions).to include({:name => "rails", :version => "1.0.0"})
end
it "should allow you to spcify no version, even when a version already exists" do
- @list.add_recipe "rails", "1.0.0"
- @list.add_recipe "rails"
- expect(@list.with_versions).to include({:name => "rails", :version => "1.0.0"})
+ list.add_recipe "rails", "1.0.0"
+ list.add_recipe "rails"
+ expect(list.with_versions).to include({:name => "rails", :version => "1.0.0"})
end
it "should not allow multiple versions of the same recipe" do
- @list.add_recipe "rails", "1.0.0"
- expect {@list.add_recipe "rails", "0.1.0"}.to raise_error Chef::Exceptions::CookbookVersionConflict
+ list.add_recipe "rails", "1.0.0"
+ expect {list.add_recipe "rails", "0.1.0"}.to raise_error Chef::Exceptions::CookbookVersionConflict
end
end
describe "with_versions" do
- before(:each) do
- @recipes = [
+
+ let(:versioned_recipes) do
+ [
{:name => "apt", :version => "1.0.0"},
{:name => "god", :version => nil},
{:name => "apache2", :version => "0.0.1"}
]
- @list = Chef::RunList::VersionedRecipeList.new
- @recipes.each {|i| @list.add_recipe i[:name], i[:version]}
end
-
it "should return an array of hashes with :name and :version" do
- expect(@list.with_versions).to eq(@recipes)
+ expect(list.with_versions).to eq(versioned_recipes)
end
it "should retain the same order as the version-less list" do
- with_versions = @list.with_versions
- @list.each_with_index do |item, index|
+ with_versions = list.with_versions
+ list.each_with_index do |item, index|
expect(with_versions[index][:name]).to eq(item)
end
end
end
describe "with_version_constraints" do
- before(:each) do
- @recipes = [
- {:name => "apt", :version => "~> 1.2.0"},
- {:name => "god", :version => nil},
- {:name => "apache2", :version => "0.0.1"}
- ]
- @list = Chef::RunList::VersionedRecipeList.new
- @recipes.each {|i| @list.add_recipe i[:name], i[:version]}
- @constraints = @recipes.map do |x|
- { :name => x[:name],
- :version_constraint => Chef::VersionConstraint.new(x[:version])
- }
- end
+
+ let(:versioned_recipes) do
+ [
+ {:name => "apt", :version => "~> 1.2.0"},
+ {:name => "god", :version => nil},
+ {:name => "apache2", :version => "0.0.1"}
+ ]
end
+
it "should return an array of hashes with :name and :version_constraint" do
- @list.with_version_constraints.each do |x|
- expect(x).to have_key :name
- expect(x[:version_constraint]).not_to be nil
+ list.with_version_constraints.each_with_index do |recipe_spec, i|
+
+ expected_recipe = versioned_recipes[i]
+
+ expect(recipe_spec[:name]).to eq(expected_recipe[:name])
+ expect(recipe_spec[:version_constraint]).to eq(Chef::VersionConstraint.new(expected_recipe[:version]))
end
end
end
+
+ describe "with_fully_qualified_names_and_version_constraints" do
+
+ let(:fq_names) { list.with_fully_qualified_names_and_version_constraints }
+
+ context "with bare cookbook names" do
+
+ let(:recipes) { %w[ apache2 ] }
+
+ it "gives $cookbook_name::default" do
+ expect(fq_names).to eq( %w[ apache2::default ] )
+ end
+
+ end
+
+ context "with qualified recipe names but no versions" do
+
+ let(:recipes) { %w[ mysql::server ] }
+
+ it "returns the qualified recipe names" do
+ expect(fq_names).to eq( %w[ mysql::server ] )
+ end
+
+ end
+
+ context "with unqualified names that have version constraints" do
+
+ let(:versioned_recipes) do
+ [
+ {:name => "apt", :version => "~> 1.2.0"},
+ ]
+ end
+
+ it "gives qualified names with their versions" do
+ expect(fq_names).to eq([ "apt::default@~> 1.2.0" ])
+ end
+
+ it "does not mutate the recipe name" do
+ expect(fq_names).to eq([ "apt::default@~> 1.2.0" ])
+ expect(list).to eq( [ "apt" ] )
+ end
+
+ end
+
+ context "with fully qualified names that have version constraints" do
+
+ let(:versioned_recipes) do
+ [
+ {:name => "apt::cacher", :version => "~> 1.2.0"},
+ ]
+ end
+
+ it "gives qualified names with their versions" do
+ expect(fq_names).to eq([ "apt::cacher@~> 1.2.0" ])
+ end
+
+ it "does not mutate the recipe name" do
+ expect(fq_names).to eq([ "apt::cacher@~> 1.2.0" ])
+ expect(list).to eq( [ "apt::cacher" ] )
+ end
+
+ end
+ end
+
end
diff --git a/spec/unit/user_spec.rb b/spec/unit/user_spec.rb
index 5222c951b3..97cc32eb3e 100644
--- a/spec/unit/user_spec.rb
+++ b/spec/unit/user_spec.rb
@@ -16,6 +16,11 @@
# limitations under the License.
#
+# DEPRECATION NOTE
+# This code only remains to support users still operating with
+# Open Source Chef Server 11 and should be removed once support
+# for OSC 11 ends. New development should occur in user_spec.rb.
+
require 'spec_helper'
require 'chef/user'
@@ -26,141 +31,98 @@ describe Chef::User do
@user = Chef::User.new
end
- shared_examples_for "string fields with no contraints" do
- it "should let you set the public key" do
- expect(@user.send(method, "some_string")).to eq("some_string")
- end
-
- it "should return the current public key" do
- @user.send(method, "some_string")
- expect(@user.send(method)).to eq("some_string")
- end
-
- it "should throw an ArgumentError if you feed it something lame" do
- expect { @user.send(method, Hash.new) }.to raise_error(ArgumentError)
- end
- end
-
- shared_examples_for "boolean fields with no constraints" do
- it "should let you set the field" do
- expect(@user.send(method, true)).to eq(true)
- end
-
- it "should return the current field value" do
- @user.send(method, true)
- expect(@user.send(method)).to eq(true)
- end
-
- it "should return the false value when false" do
- @user.send(method, false)
- expect(@user.send(method)).to eq(false)
- end
-
- it "should throw an ArgumentError if you feed it anything but true or false" do
- expect { @user.send(method, Hash.new) }.to raise_error(ArgumentError)
- end
- end
-
describe "initialize" do
it "should be a Chef::User" do
expect(@user).to be_a_kind_of(Chef::User)
end
end
- describe "username" do
- it "should let you set the username to a string" do
- expect(@user.username("ops_master")).to eq("ops_master")
+ describe "name" do
+ it "should let you set the name to a string" do
+ expect(@user.name("ops_master")).to eq("ops_master")
end
- it "should return the current username" do
- @user.username "ops_master"
- expect(@user.username).to eq("ops_master")
+ it "should return the current name" do
+ @user.name "ops_master"
+ expect(@user.name).to eq("ops_master")
end
# It is not feasible to check all invalid characters. Here are a few
# that we probably care about.
it "should not accept invalid characters" do
# capital letters
- expect { @user.username "Bar" }.to raise_error(ArgumentError)
+ expect { @user.name "Bar" }.to raise_error(ArgumentError)
# slashes
- expect { @user.username "foo/bar" }.to raise_error(ArgumentError)
+ expect { @user.name "foo/bar" }.to raise_error(ArgumentError)
# ?
- expect { @user.username "foo?" }.to raise_error(ArgumentError)
+ expect { @user.name "foo?" }.to raise_error(ArgumentError)
# &
- expect { @user.username "foo&" }.to raise_error(ArgumentError)
+ expect { @user.name "foo&" }.to raise_error(ArgumentError)
end
it "should not accept spaces" do
- expect { @user.username "ops master" }.to raise_error(ArgumentError)
+ expect { @user.name "ops master" }.to raise_error(ArgumentError)
end
it "should throw an ArgumentError if you feed it anything but a string" do
- expect { @user.username Hash.new }.to raise_error(ArgumentError)
+ expect { @user.name Hash.new }.to raise_error(ArgumentError)
end
end
- describe "boolean fields" do
- describe "create_key" do
- it_should_behave_like "boolean fields with no constraints" do
- let(:method) { :create_key }
- end
+ describe "admin" do
+ it "should let you set the admin bit" do
+ expect(@user.admin(true)).to eq(true)
end
- end
- describe "string fields" do
- describe "public_key" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :public_key }
- end
+ it "should return the current admin value" do
+ @user.admin true
+ expect(@user.admin).to eq(true)
end
- describe "private_key" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :private_key }
- end
+ it "should default to false" do
+ expect(@user.admin).to eq(false)
end
- describe "display_name" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :display_name }
- end
+ it "should throw an ArgumentError if you feed it anything but true or false" do
+ expect { @user.name Hash.new }.to raise_error(ArgumentError)
end
+ end
- describe "first_name" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :first_name }
- end
+ describe "public_key" do
+ it "should let you set the public key" do
+ expect(@user.public_key("super public")).to eq("super public")
end
- describe "middle_name" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :middle_name }
- end
+ it "should return the current public key" do
+ @user.public_key("super public")
+ expect(@user.public_key).to eq("super public")
end
- describe "last_name" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :last_name }
- end
+ it "should throw an ArgumentError if you feed it something lame" do
+ expect { @user.public_key Hash.new }.to raise_error(ArgumentError)
end
+ end
- describe "email" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :email }
- end
+ describe "private_key" do
+ it "should let you set the private key" do
+ expect(@user.private_key("super private")).to eq("super private")
end
- describe "password" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :password }
- end
+ it "should return the private key" do
+ @user.private_key("super private")
+ expect(@user.private_key).to eq("super private")
+ end
+
+ it "should throw an ArgumentError if you feed it something lame" do
+ expect { @user.private_key Hash.new }.to raise_error(ArgumentError)
end
end
describe "when serializing to JSON" do
before(:each) do
- @user.username("black")
+ @user.name("black")
+ @user.public_key("crowes")
@json = @user.to_json
end
@@ -168,62 +130,16 @@ describe Chef::User do
expect(@json).to match(/^\{.+\}$/)
end
- it "includes the username value" do
- expect(@json).to include(%q{"username":"black"})
- end
-
- it "includes the display name when present" do
- @user.display_name("get_displayed")
- expect(@user.to_json).to include(%{"display_name":"get_displayed"})
- end
-
- it "does not include the display name if not present" do
- expect(@json).not_to include("display_name")
- end
-
- it "includes the first name when present" do
- @user.first_name("char")
- expect(@user.to_json).to include(%{"first_name":"char"})
- end
-
- it "does not include the first name if not present" do
- expect(@json).not_to include("first_name")
- end
-
- it "includes the middle name when present" do
- @user.middle_name("man")
- expect(@user.to_json).to include(%{"middle_name":"man"})
- end
-
- it "does not include the middle name if not present" do
- expect(@json).not_to include("middle_name")
- end
-
- it "includes the last name when present" do
- @user.last_name("der")
- expect(@user.to_json).to include(%{"last_name":"der"})
- end
-
- it "does not include the last name if not present" do
- expect(@json).not_to include("last_name")
- end
-
- it "includes the email when present" do
- @user.email("charmander@pokemon.poke")
- expect(@user.to_json).to include(%{"email":"charmander@pokemon.poke"})
- end
-
- it "does not include the email if not present" do
- expect(@json).not_to include("email")
+ it "includes the name value" do
+ expect(@json).to include(%q{"name":"black"})
end
- it "includes the public key when present" do
- @user.public_key("crowes")
- expect(@user.to_json).to include(%{"public_key":"crowes"})
+ it "includes the public key value" do
+ expect(@json).to include(%{"public_key":"crowes"})
end
- it "does not include the public key if not present" do
- expect(@json).not_to include("public_key")
+ it "includes the 'admin' flag" do
+ expect(@json).to include(%q{"admin":false})
end
it "includes the private key when present" do
@@ -251,18 +167,11 @@ describe Chef::User do
describe "when deserializing from JSON" do
before(:each) do
- user = {
- "username" => "mr_spinks",
- "display_name" => "displayed",
- "first_name" => "char",
- "middle_name" => "man",
- "last_name" => "der",
- "email" => "charmander@pokemon.poke",
- "password" => "password",
+ user = { "name" => "mr_spinks",
"public_key" => "turtles",
"private_key" => "pandas",
- "create_key" => false
- }
+ "password" => "password",
+ "admin" => true }
@user = Chef::User.from_json(Chef::JSONCompat.to_json(user))
end
@@ -270,277 +179,34 @@ describe Chef::User do
expect(@user).to be_a_kind_of(Chef::User)
end
- it "preserves the username" do
- expect(@user.username).to eq("mr_spinks")
- end
-
- it "preserves the display name if present" do
- expect(@user.display_name).to eq("displayed")
+ it "preserves the name" do
+ expect(@user.name).to eq("mr_spinks")
end
- it "preserves the first name if present" do
- expect(@user.first_name).to eq("char")
- end
-
- it "preserves the middle name if present" do
- expect(@user.middle_name).to eq("man")
+ it "preserves the public key" do
+ expect(@user.public_key).to eq("turtles")
end
- it "preserves the last name if present" do
- expect(@user.last_name).to eq("der")
+ it "preserves the admin status" do
+ expect(@user.admin).to be_truthy
end
- it "preserves the email if present" do
- expect(@user.email).to eq("charmander@pokemon.poke")
+ it "includes the private key if present" do
+ expect(@user.private_key).to eq("pandas")
end
it "includes the password if present" do
expect(@user.password).to eq("password")
end
- it "preserves the public key if present" do
- expect(@user.public_key).to eq("turtles")
- end
-
- it "includes the private key if present" do
- expect(@user.private_key).to eq("pandas")
- end
-
- it "includes the create key status if not nil" do
- expect(@user.create_key).to be_falsey
- end
end
- describe "Versioned API Interactions" do
- let(:response_406) { OpenStruct.new(:code => '406') }
- let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
-
- before (:each) do
- @user = Chef::User.new
- allow(@user).to receive(:chef_root_rest_v0).and_return(double('chef rest root v0 object'))
- allow(@user).to receive(:chef_root_rest_v1).and_return(double('chef rest root v1 object'))
- end
-
- describe "update" do
- before do
- # populate all fields that are valid between V0 and V1
- @user.username "some_username"
- @user.display_name "some_display_name"
- @user.first_name "some_first_name"
- @user.middle_name "some_middle_name"
- @user.last_name "some_last_name"
- @user.email "some_email"
- @user.password "some_password"
- end
-
- let(:payload) {
- {
- :username => "some_username",
- :display_name => "some_display_name",
- :first_name => "some_first_name",
- :middle_name => "some_middle_name",
- :last_name => "some_last_name",
- :email => "some_email",
- :password => "some_password"
- }
- }
-
- context "when server API V1 is valid on the Chef Server receiving the request" do
- context "when the user submits valid data" do
- it "properly updates the user" do
- expect(@user.chef_root_rest_v1).to receive(:put).with("users/some_username", payload).and_return({})
- @user.update
- end
- end
- end
-
- context "when server API V1 is not valid on the Chef Server receiving the request" do
- let(:payload) {
- {
- :username => "some_username",
- :display_name => "some_display_name",
- :first_name => "some_first_name",
- :middle_name => "some_middle_name",
- :last_name => "some_last_name",
- :email => "some_email",
- :password => "some_password",
- :public_key => "some_public_key"
- }
- }
-
- before do
- @user.public_key "some_public_key"
- allow(@user.chef_root_rest_v1).to receive(:put)
- end
-
- context "when the server returns a 400" do
- let(:response_400) { OpenStruct.new(:code => '400') }
- let(:exception_400) { Net::HTTPServerException.new("400 Bad Request", response_400) }
-
- context "when the 400 was due to public / private key fields no longer being supported" do
- let(:response_body_400) { '{"error":["Since Server API v1, all keys must be updated via the keys endpoint. "]}' }
-
- before do
- allow(response_400).to receive(:body).and_return(response_body_400)
- allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_400)
- end
-
- it "proceeds with the V0 PUT since it can handle public / private key fields" do
- expect(@user.chef_root_rest_v0).to receive(:put).with("users/some_username", payload).and_return({})
- @user.update
- end
-
- it "does not call server_client_api_version_intersection, since we know to proceed with V0 in this case" do
- expect(@user).to_not receive(:server_client_api_version_intersection)
- allow(@user.chef_root_rest_v0).to receive(:put).and_return({})
- @user.update
- end
- end # when the 400 was due to public / private key fields
-
- context "when the 400 was NOT due to public / private key fields no longer being supported" do
- let(:response_body_400) { '{"error":["Some other error. "]}' }
-
- before do
- allow(response_400).to receive(:body).and_return(response_body_400)
- allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_400)
- end
-
- it "will not proceed with the V0 PUT since the original bad request was not key related" do
- expect(@user.chef_root_rest_v0).to_not receive(:put).with("users/some_username", payload)
- expect { @user.update }.to raise_error(exception_400)
- end
-
- it "raises the original error" do
- expect { @user.update }.to raise_error(exception_400)
- end
-
- end
- end # when the server returns a 400
-
- context "when the server returns a 406" do
- # from spec/support/shared/unit/api_versioning.rb
- it_should_behave_like "version handling" do
- let(:object) { @user }
- let(:method) { :update }
- let(:http_verb) { :put }
- let(:rest_v1) { @user.chef_root_rest_v1 }
- end
-
- context "when the server supports API V0" do
- before do
- allow(@user).to receive(:server_client_api_version_intersection).and_return([0])
- allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_406)
- end
-
- it "properly updates the user" do
- expect(@user.chef_root_rest_v0).to receive(:put).with("users/some_username", payload).and_return({})
- @user.update
- end
- end # when the server supports API V0
- end # when the server returns a 406
-
- end # when server API V1 is not valid on the Chef Server receiving the request
- end # update
-
- describe "create" do
- let(:payload) {
- {
- :username => "some_username",
- :display_name => "some_display_name",
- :first_name => "some_first_name",
- :last_name => "some_last_name",
- :email => "some_email",
- :password => "some_password"
- }
- }
- before do
- @user.username "some_username"
- @user.display_name "some_display_name"
- @user.first_name "some_first_name"
- @user.last_name "some_last_name"
- @user.email "some_email"
- @user.password "some_password"
- end
-
- # from spec/support/shared/unit/user_and_client_shared.rb
- it_should_behave_like "user or client create" do
- let(:object) { @user }
- let(:error) { Chef::Exceptions::InvalidUserAttribute }
- let(:rest_v0) { @user.chef_root_rest_v0 }
- let(:rest_v1) { @user.chef_root_rest_v1 }
- let(:url) { "users" }
- end
-
- context "when handling API V1" do
- it "creates a new user via the API with a middle_name when it exists" do
- @user.middle_name "some_middle_name"
- expect(@user.chef_root_rest_v1).to receive(:post).with("users", payload.merge({:middle_name => "some_middle_name"})).and_return({})
- @user.create
- end
- end # when server API V1 is valid on the Chef Server receiving the request
-
- context "when API V1 is not supported by the server" do
- # from spec/support/shared/unit/api_versioning.rb
- it_should_behave_like "version handling" do
- let(:object) { @user }
- let(:method) { :create }
- let(:http_verb) { :post }
- let(:rest_v1) { @user.chef_root_rest_v1 }
- end
- end
-
- context "when handling API V0" do
- before do
- allow(@user).to receive(:server_client_api_version_intersection).and_return([0])
- allow(@user.chef_root_rest_v1).to receive(:post).and_raise(exception_406)
- end
-
- it "creates a new user via the API with a middle_name when it exists" do
- @user.middle_name "some_middle_name"
- expect(@user.chef_root_rest_v0).to receive(:post).with("users", payload.merge({:middle_name => "some_middle_name"})).and_return({})
- @user.create
- end
- end # when server API V1 is not valid on the Chef Server receiving the request
-
- end # create
-
- # DEPRECATION
- # This can be removed after API V0 support is gone
- describe "reregister" do
- let(:payload) {
- {
- "username" => "some_username",
- }
- }
-
- before do
- @user.username "some_username"
- end
-
- context "when server API V0 is valid on the Chef Server receiving the request" do
- it "creates a new object via the API" do
- expect(@user.chef_root_rest_v0).to receive(:put).with("users/#{@user.username}", payload.merge({"private_key" => true})).and_return({})
- @user.reregister
- end
- end # when server API V0 is valid on the Chef Server receiving the request
-
- context "when server API V0 is not supported by the Chef Server" do
- # from spec/support/shared/unit/api_versioning.rb
- it_should_behave_like "user and client reregister" do
- let(:object) { @user }
- let(:rest_v0) { @user.chef_root_rest_v0 }
- end
- end # when server API V0 is not supported by the Chef Server
- end # reregister
-
- end # Versioned API Interactions
-
describe "API Interactions" do
before (:each) do
@user = Chef::User.new
- @user.username "foobar"
- @http_client = double("Chef::REST mock")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
+ @user.name "foobar"
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
end
describe "list" do
@@ -552,6 +218,16 @@ describe Chef::User do
@osc_inflated_response = { "admin" => @user }
end
+ it "lists all clients on an OSC server" do
+ allow(@http_client).to receive(:get).with("users").and_return(@osc_response)
+ expect(Chef::User.list).to eq(@osc_response)
+ end
+
+ it "inflate all clients on an OSC server" do
+ allow(@http_client).to receive(:get).with("users").and_return(@osc_response)
+ expect(Chef::User.list(true)).to eq(@osc_inflated_response)
+ end
+
it "lists all clients on an OHC/OPC server" do
allow(@http_client).to receive(:get).with("users").and_return(@ohc_response)
# We expect that Chef::User.list will give a consistent response
@@ -565,15 +241,31 @@ describe Chef::User do
end
end
+ describe "create" do
+ it "creates a new user via the API" do
+ @user.password "password"
+ expect(@http_client).to receive(:post).with("users", {:name => "foobar", :admin => false, :password => "password"}).and_return({})
+ @user.create
+ end
+ end
+
describe "read" do
it "loads a named user from the API" do
- expect(@http_client).to receive(:get).with("users/foobar").and_return({"username" => "foobar", "admin" => true, "public_key" => "pubkey"})
+ expect(@http_client).to receive(:get).with("users/foobar").and_return({"name" => "foobar", "admin" => true, "public_key" => "pubkey"})
user = Chef::User.load("foobar")
- expect(user.username).to eq("foobar")
+ expect(user.name).to eq("foobar")
+ expect(user.admin).to eq(true)
expect(user.public_key).to eq("pubkey")
end
end
+ describe "update" do
+ it "updates an existing user on via the API" do
+ expect(@http_client).to receive(:put).with("users/foobar", {:name => "foobar", :admin => false}).and_return({})
+ @user.update
+ end
+ end
+
describe "destroy" do
it "deletes the specified user via the API" do
expect(@http_client).to receive(:delete).with("users/foobar")
diff --git a/spec/unit/user_v1_spec.rb b/spec/unit/user_v1_spec.rb
new file mode 100644
index 0000000000..8fd370a010
--- /dev/null
+++ b/spec/unit/user_v1_spec.rb
@@ -0,0 +1,584 @@
+#
+# Author:: Steven Danna (steve@opscode.com)
+# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+require 'chef/user_v1'
+require 'tempfile'
+
+describe Chef::UserV1 do
+ before(:each) do
+ @user = Chef::UserV1.new
+ end
+
+ shared_examples_for "string fields with no contraints" do
+ it "should let you set the public key" do
+ expect(@user.send(method, "some_string")).to eq("some_string")
+ end
+
+ it "should return the current public key" do
+ @user.send(method, "some_string")
+ expect(@user.send(method)).to eq("some_string")
+ end
+
+ it "should throw an ArgumentError if you feed it something lame" do
+ expect { @user.send(method, Hash.new) }.to raise_error(ArgumentError)
+ end
+ end
+
+ shared_examples_for "boolean fields with no constraints" do
+ it "should let you set the field" do
+ expect(@user.send(method, true)).to eq(true)
+ end
+
+ it "should return the current field value" do
+ @user.send(method, true)
+ expect(@user.send(method)).to eq(true)
+ end
+
+ it "should return the false value when false" do
+ @user.send(method, false)
+ expect(@user.send(method)).to eq(false)
+ end
+
+ it "should throw an ArgumentError if you feed it anything but true or false" do
+ expect { @user.send(method, Hash.new) }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe "initialize" do
+ it "should be a Chef::UserV1" do
+ expect(@user).to be_a_kind_of(Chef::UserV1)
+ end
+ end
+
+ describe "username" do
+ it "should let you set the username to a string" do
+ expect(@user.username("ops_master")).to eq("ops_master")
+ end
+
+ it "should return the current username" do
+ @user.username "ops_master"
+ expect(@user.username).to eq("ops_master")
+ end
+
+ # It is not feasible to check all invalid characters. Here are a few
+ # that we probably care about.
+ it "should not accept invalid characters" do
+ # capital letters
+ expect { @user.username "Bar" }.to raise_error(ArgumentError)
+ # slashes
+ expect { @user.username "foo/bar" }.to raise_error(ArgumentError)
+ # ?
+ expect { @user.username "foo?" }.to raise_error(ArgumentError)
+ # &
+ expect { @user.username "foo&" }.to raise_error(ArgumentError)
+ end
+
+
+ it "should not accept spaces" do
+ expect { @user.username "ops master" }.to raise_error(ArgumentError)
+ end
+
+ it "should throw an ArgumentError if you feed it anything but a string" do
+ expect { @user.username Hash.new }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe "boolean fields" do
+ describe "create_key" do
+ it_should_behave_like "boolean fields with no constraints" do
+ let(:method) { :create_key }
+ end
+ end
+ end
+
+ describe "string fields" do
+ describe "public_key" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :public_key }
+ end
+ end
+
+ describe "private_key" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :private_key }
+ end
+ end
+
+ describe "display_name" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :display_name }
+ end
+ end
+
+ describe "first_name" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :first_name }
+ end
+ end
+
+ describe "middle_name" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :middle_name }
+ end
+ end
+
+ describe "last_name" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :last_name }
+ end
+ end
+
+ describe "email" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :email }
+ end
+ end
+
+ describe "password" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :password }
+ end
+ end
+ end
+
+ describe "when serializing to JSON" do
+ before(:each) do
+ @user.username("black")
+ @json = @user.to_json
+ end
+
+ it "serializes as a JSON object" do
+ expect(@json).to match(/^\{.+\}$/)
+ end
+
+ it "includes the username value" do
+ expect(@json).to include(%q{"username":"black"})
+ end
+
+ it "includes the display name when present" do
+ @user.display_name("get_displayed")
+ expect(@user.to_json).to include(%{"display_name":"get_displayed"})
+ end
+
+ it "does not include the display name if not present" do
+ expect(@json).not_to include("display_name")
+ end
+
+ it "includes the first name when present" do
+ @user.first_name("char")
+ expect(@user.to_json).to include(%{"first_name":"char"})
+ end
+
+ it "does not include the first name if not present" do
+ expect(@json).not_to include("first_name")
+ end
+
+ it "includes the middle name when present" do
+ @user.middle_name("man")
+ expect(@user.to_json).to include(%{"middle_name":"man"})
+ end
+
+ it "does not include the middle name if not present" do
+ expect(@json).not_to include("middle_name")
+ end
+
+ it "includes the last name when present" do
+ @user.last_name("der")
+ expect(@user.to_json).to include(%{"last_name":"der"})
+ end
+
+ it "does not include the last name if not present" do
+ expect(@json).not_to include("last_name")
+ end
+
+ it "includes the email when present" do
+ @user.email("charmander@pokemon.poke")
+ expect(@user.to_json).to include(%{"email":"charmander@pokemon.poke"})
+ end
+
+ it "does not include the email if not present" do
+ expect(@json).not_to include("email")
+ end
+
+ it "includes the public key when present" do
+ @user.public_key("crowes")
+ expect(@user.to_json).to include(%{"public_key":"crowes"})
+ end
+
+ it "does not include the public key if not present" do
+ expect(@json).not_to include("public_key")
+ end
+
+ it "includes the private key when present" do
+ @user.private_key("monkeypants")
+ expect(@user.to_json).to include(%q{"private_key":"monkeypants"})
+ end
+
+ it "does not include the private key if not present" do
+ expect(@json).not_to include("private_key")
+ end
+
+ it "includes the password if present" do
+ @user.password "password"
+ expect(@user.to_json).to include(%q{"password":"password"})
+ end
+
+ it "does not include the password if not present" do
+ expect(@json).not_to include("password")
+ end
+
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
+ let(:jsonable) { @user }
+ end
+ end
+
+ describe "when deserializing from JSON" do
+ before(:each) do
+ user = {
+ "username" => "mr_spinks",
+ "display_name" => "displayed",
+ "first_name" => "char",
+ "middle_name" => "man",
+ "last_name" => "der",
+ "email" => "charmander@pokemon.poke",
+ "password" => "password",
+ "public_key" => "turtles",
+ "private_key" => "pandas",
+ "create_key" => false
+ }
+ @user = Chef::UserV1.from_json(Chef::JSONCompat.to_json(user))
+ end
+
+ it "should deserialize to a Chef::UserV1 object" do
+ expect(@user).to be_a_kind_of(Chef::UserV1)
+ end
+
+ it "preserves the username" do
+ expect(@user.username).to eq("mr_spinks")
+ end
+
+ it "preserves the display name if present" do
+ expect(@user.display_name).to eq("displayed")
+ end
+
+ it "preserves the first name if present" do
+ expect(@user.first_name).to eq("char")
+ end
+
+ it "preserves the middle name if present" do
+ expect(@user.middle_name).to eq("man")
+ end
+
+ it "preserves the last name if present" do
+ expect(@user.last_name).to eq("der")
+ end
+
+ it "preserves the email if present" do
+ expect(@user.email).to eq("charmander@pokemon.poke")
+ end
+
+ it "includes the password if present" do
+ expect(@user.password).to eq("password")
+ end
+
+ it "preserves the public key if present" do
+ expect(@user.public_key).to eq("turtles")
+ end
+
+ it "includes the private key if present" do
+ expect(@user.private_key).to eq("pandas")
+ end
+
+ it "includes the create key status if not nil" do
+ expect(@user.create_key).to be_falsey
+ end
+ end
+
+ describe "Versioned API Interactions" do
+ let(:response_406) { OpenStruct.new(:code => '406') }
+ let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+
+ before (:each) do
+ @user = Chef::UserV1.new
+ allow(@user).to receive(:chef_root_rest_v0).and_return(double('chef rest root v0 object'))
+ allow(@user).to receive(:chef_root_rest_v1).and_return(double('chef rest root v1 object'))
+ end
+
+ describe "update" do
+ before do
+ # populate all fields that are valid between V0 and V1
+ @user.username "some_username"
+ @user.display_name "some_display_name"
+ @user.first_name "some_first_name"
+ @user.middle_name "some_middle_name"
+ @user.last_name "some_last_name"
+ @user.email "some_email"
+ @user.password "some_password"
+ end
+
+ let(:payload) {
+ {
+ :username => "some_username",
+ :display_name => "some_display_name",
+ :first_name => "some_first_name",
+ :middle_name => "some_middle_name",
+ :last_name => "some_last_name",
+ :email => "some_email",
+ :password => "some_password"
+ }
+ }
+
+ context "when server API V1 is valid on the Chef Server receiving the request" do
+ context "when the user submits valid data" do
+ it "properly updates the user" do
+ expect(@user.chef_root_rest_v1).to receive(:put).with("users/some_username", payload).and_return({})
+ @user.update
+ end
+ end
+ end
+
+ context "when server API V1 is not valid on the Chef Server receiving the request" do
+ let(:payload) {
+ {
+ :username => "some_username",
+ :display_name => "some_display_name",
+ :first_name => "some_first_name",
+ :middle_name => "some_middle_name",
+ :last_name => "some_last_name",
+ :email => "some_email",
+ :password => "some_password",
+ :public_key => "some_public_key"
+ }
+ }
+
+ before do
+ @user.public_key "some_public_key"
+ allow(@user.chef_root_rest_v1).to receive(:put)
+ end
+
+ context "when the server returns a 400" do
+ let(:response_400) { OpenStruct.new(:code => '400') }
+ let(:exception_400) { Net::HTTPServerException.new("400 Bad Request", response_400) }
+
+ context "when the 400 was due to public / private key fields no longer being supported" do
+ let(:response_body_400) { '{"error":["Since Server API v1, all keys must be updated via the keys endpoint. "]}' }
+
+ before do
+ allow(response_400).to receive(:body).and_return(response_body_400)
+ allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_400)
+ end
+
+ it "proceeds with the V0 PUT since it can handle public / private key fields" do
+ expect(@user.chef_root_rest_v0).to receive(:put).with("users/some_username", payload).and_return({})
+ @user.update
+ end
+
+ it "does not call server_client_api_version_intersection, since we know to proceed with V0 in this case" do
+ expect(@user).to_not receive(:server_client_api_version_intersection)
+ allow(@user.chef_root_rest_v0).to receive(:put).and_return({})
+ @user.update
+ end
+ end # when the 400 was due to public / private key fields
+
+ context "when the 400 was NOT due to public / private key fields no longer being supported" do
+ let(:response_body_400) { '{"error":["Some other error. "]}' }
+
+ before do
+ allow(response_400).to receive(:body).and_return(response_body_400)
+ allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_400)
+ end
+
+ it "will not proceed with the V0 PUT since the original bad request was not key related" do
+ expect(@user.chef_root_rest_v0).to_not receive(:put).with("users/some_username", payload)
+ expect { @user.update }.to raise_error(exception_400)
+ end
+
+ it "raises the original error" do
+ expect { @user.update }.to raise_error(exception_400)
+ end
+
+ end
+ end # when the server returns a 400
+
+ context "when the server returns a 406" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "version handling" do
+ let(:object) { @user }
+ let(:method) { :update }
+ let(:http_verb) { :put }
+ let(:rest_v1) { @user.chef_root_rest_v1 }
+ end
+
+ context "when the server supports API V0" do
+ before do
+ allow(@user).to receive(:server_client_api_version_intersection).and_return([0])
+ allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_406)
+ end
+
+ it "properly updates the user" do
+ expect(@user.chef_root_rest_v0).to receive(:put).with("users/some_username", payload).and_return({})
+ @user.update
+ end
+ end # when the server supports API V0
+ end # when the server returns a 406
+
+ end # when server API V1 is not valid on the Chef Server receiving the request
+ end # update
+
+ describe "create" do
+ let(:payload) {
+ {
+ :username => "some_username",
+ :display_name => "some_display_name",
+ :first_name => "some_first_name",
+ :last_name => "some_last_name",
+ :email => "some_email",
+ :password => "some_password"
+ }
+ }
+ before do
+ @user.username "some_username"
+ @user.display_name "some_display_name"
+ @user.first_name "some_first_name"
+ @user.last_name "some_last_name"
+ @user.email "some_email"
+ @user.password "some_password"
+ end
+
+ # from spec/support/shared/unit/user_and_client_shared.rb
+ it_should_behave_like "user or client create" do
+ let(:object) { @user }
+ let(:error) { Chef::Exceptions::InvalidUserAttribute }
+ let(:rest_v0) { @user.chef_root_rest_v0 }
+ let(:rest_v1) { @user.chef_root_rest_v1 }
+ let(:url) { "users" }
+ end
+
+ context "when handling API V1" do
+ it "creates a new user via the API with a middle_name when it exists" do
+ @user.middle_name "some_middle_name"
+ expect(@user.chef_root_rest_v1).to receive(:post).with("users", payload.merge({:middle_name => "some_middle_name"})).and_return({})
+ @user.create
+ end
+ end # when server API V1 is valid on the Chef Server receiving the request
+
+ context "when API V1 is not supported by the server" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "version handling" do
+ let(:object) { @user }
+ let(:method) { :create }
+ let(:http_verb) { :post }
+ let(:rest_v1) { @user.chef_root_rest_v1 }
+ end
+ end
+
+ context "when handling API V0" do
+ before do
+ allow(@user).to receive(:server_client_api_version_intersection).and_return([0])
+ allow(@user.chef_root_rest_v1).to receive(:post).and_raise(exception_406)
+ end
+
+ it "creates a new user via the API with a middle_name when it exists" do
+ @user.middle_name "some_middle_name"
+ expect(@user.chef_root_rest_v0).to receive(:post).with("users", payload.merge({:middle_name => "some_middle_name"})).and_return({})
+ @user.create
+ end
+ end # when server API V1 is not valid on the Chef Server receiving the request
+
+ end # create
+
+ # DEPRECATION
+ # This can be removed after API V0 support is gone
+ describe "reregister" do
+ let(:payload) {
+ {
+ "username" => "some_username",
+ }
+ }
+
+ before do
+ @user.username "some_username"
+ end
+
+ context "when server API V0 is valid on the Chef Server receiving the request" do
+ it "creates a new object via the API" do
+ expect(@user.chef_root_rest_v0).to receive(:put).with("users/#{@user.username}", payload.merge({"private_key" => true})).and_return({})
+ @user.reregister
+ end
+ end # when server API V0 is valid on the Chef Server receiving the request
+
+ context "when server API V0 is not supported by the Chef Server" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "user and client reregister" do
+ let(:object) { @user }
+ let(:rest_v0) { @user.chef_root_rest_v0 }
+ end
+ end # when server API V0 is not supported by the Chef Server
+ end # reregister
+
+ end # Versioned API Interactions
+
+ describe "API Interactions" do
+ before (:each) do
+ @user = Chef::UserV1.new
+ @user.username "foobar"
+ @http_client = double("Chef::REST mock")
+ allow(Chef::REST).to receive(:new).and_return(@http_client)
+ end
+
+ describe "list" do
+ before(:each) do
+ Chef::Config[:chef_server_url] = "http://www.example.com"
+ @osc_response = { "admin" => "http://www.example.com/users/admin"}
+ @ohc_response = [ { "user" => { "username" => "admin" }} ]
+ allow(Chef::UserV1).to receive(:load).with("admin").and_return(@user)
+ @osc_inflated_response = { "admin" => @user }
+ end
+
+ it "lists all clients on an OHC/OPC server" do
+ allow(@http_client).to receive(:get).with("users").and_return(@ohc_response)
+ # We expect that Chef::UserV1.list will give a consistent response
+ # so OHC API responses should be transformed to OSC-style output.
+ expect(Chef::UserV1.list).to eq(@osc_response)
+ end
+
+ it "inflate all clients on an OHC/OPC server" do
+ allow(@http_client).to receive(:get).with("users").and_return(@ohc_response)
+ expect(Chef::UserV1.list(true)).to eq(@osc_inflated_response)
+ end
+ end
+
+ describe "read" do
+ it "loads a named user from the API" do
+ expect(@http_client).to receive(:get).with("users/foobar").and_return({"username" => "foobar", "admin" => true, "public_key" => "pubkey"})
+ user = Chef::UserV1.load("foobar")
+ expect(user.username).to eq("foobar")
+ expect(user.public_key).to eq("pubkey")
+ end
+ end
+
+ describe "destroy" do
+ it "deletes the specified user via the API" do
+ expect(@http_client).to receive(:delete).with("users/foobar")
+ @user.destroy
+ end
+ end
+ end
+end