summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaire McQuin <claire@getchef.com>2014-12-16 15:18:29 -0800
committerClaire McQuin <claire@getchef.com>2014-12-16 15:18:29 -0800
commit4dff9551528b00cfe5d7864755b7d7650210d688 (patch)
tree81e4f72b32155ec7b8255ca04498f8bff5fea34d
parent04285f88ce599db6cbcf3d20a9f00d328f318455 (diff)
parent1d5b9bf57df6812b6f6a2961d0995aa6c2d8f695 (diff)
downloadchef-4dff9551528b00cfe5d7864755b7d7650210d688.tar.gz
Merge branch 'master' into audit-mode
-rw-r--r--CHANGELOG.md32
-rw-r--r--distro/common/man/man8/chef-apply.886
-rw-r--r--kitchen-tests/cookbooks/webapp/metadata.rb2
-rw-r--r--lib/chef/application/client.rb4
-rw-r--r--lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb2
-rw-r--r--lib/chef/digester.rb1
-rw-r--r--lib/chef/event_loggers/windows_eventlog.rb8
-rw-r--r--lib/chef/exceptions.rb6
-rw-r--r--lib/chef/knife.rb7
-rw-r--r--lib/chef/knife/cookbook_site_install.rb44
-rw-r--r--lib/chef/knife/core/bootstrap_context.rb2
-rw-r--r--lib/chef/knife/ssl_fetch.rb13
-rw-r--r--lib/chef/monkey_patches/net_http.rb4
-rw-r--r--lib/chef/provider/execute.rb91
-rw-r--r--lib/chef/provider/link.rb2
-rw-r--r--lib/chef/provider/lwrp_base.rb4
-rw-r--r--lib/chef/provider/mount/solaris.rb2
-rw-r--r--lib/chef/provider/package/apt.rb4
-rw-r--r--lib/chef/provider/package/homebrew.rb10
-rw-r--r--lib/chef/provider/package/windows/msi.rb2
-rw-r--r--lib/chef/provider/script.rb38
-rw-r--r--lib/chef/provider/subversion.rb6
-rw-r--r--lib/chef/resource.rb5
-rw-r--r--lib/chef/resource/execute.rb25
-rw-r--r--lib/chef/resource/lwrp_base.rb2
-rw-r--r--lib/chef/resource/script.rb11
-rw-r--r--lib/chef/whitelist.rb4
-rw-r--r--lib/chef/win32/api/file.rb20
-rw-r--r--spec/data/lwrp/providers/buck_passer.rb1
-rw-r--r--spec/data/lwrp/resources/bar.rb1
-rw-r--r--spec/functional/resource/bash_spec.rb88
-rw-r--r--spec/functional/resource/execute_spec.rb133
-rw-r--r--spec/functional/resource/link_spec.rb32
-rw-r--r--spec/spec_helper.rb4
-rw-r--r--spec/support/platform_helpers.rb15
-rw-r--r--spec/support/shared/unit/execute_resource.rb10
-rw-r--r--spec/support/shared/unit/script_resource.rb56
-rw-r--r--spec/unit/application/client_spec.rb13
-rw-r--r--spec/unit/cookbook/syntax_check_spec.rb5
-rw-r--r--spec/unit/http/validate_content_length_spec.rb9
-rw-r--r--spec/unit/knife/cookbook_site_install_spec.rb269
-rw-r--r--spec/unit/knife/core/bootstrap_context_spec.rb4
-rw-r--r--spec/unit/knife/ssl_fetch_spec.rb53
-rw-r--r--spec/unit/knife_spec.rb158
-rw-r--r--spec/unit/mixin/shell_out_spec.rb79
-rw-r--r--spec/unit/node_spec.rb34
-rw-r--r--spec/unit/provider/execute_spec.rb206
-rw-r--r--spec/unit/provider/git_spec.rb5
-rw-r--r--spec/unit/provider/link_spec.rb10
-rw-r--r--spec/unit/provider/package/apt_spec.rb521
-rw-r--r--spec/unit/provider/package/homebrew_spec.rb26
-rw-r--r--spec/unit/provider/package/windows/msi_spec.rb21
-rw-r--r--spec/unit/provider/script_spec.rb104
-rw-r--r--spec/unit/provider/subversion_spec.rb10
-rw-r--r--spec/unit/resource/apt_package_spec.rb8
-rw-r--r--spec/unit/resource/script_spec.rb8
-rw-r--r--spec/unit/run_context_spec.rb7
57 files changed, 1505 insertions, 822 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 06ad8087b4..cf6c2b5b7f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-## Unreleased
+## 12.1.0 (Unreleased)
* [**Vasiliy Tolstov**](https://github.com/vtolstov):
cleanup cookbook path from stale files (when using chef-solo with a tarball url)
@@ -22,6 +22,36 @@
### Chef Contributions
* ruby 1.9.3 support is dropped
* Update Chef to use RSpec 3.
+* Cleaned up script and execute provider + specs
+* Added deprecation warnings around the use of command attribute in script resources
+
+## 12.0.3
+* [**Phil Dibowitz**](https://github.com/jaymzh):
+[Issue 2594](https://github.com/opscode/chef/issues/2594) Restore missing require in `digester`.
+
+## 12.0.2
+* [Issue 2578](https://github.com/opscode/chef/issues/2578) Check that `installed` is not empty for `keg_only` formula in Homebrew provider
+* [Issue 2609](https://github.com/opscode/chef/issues/2609) Resolve the circular dependency between ProviderResolver and Resource.
+* [Issue 2596](https://github.com/opscode/chef/issues/2596) Fix nodes not writing to disk
+* [Issue 2580](https://github.com/opscode/chef/issues/2580) Make sure the relative paths are preserved when using link resource.
+* [Pull 2630](https://github.com/opscode/chef/pull/2630) Improve knife's SSL error messaging
+* [Issue 2606](https://github.com/opscode/chef/issues/2606) chef 12 ignores default_release for apt_package
+* [Issue 2578](https://github.com/opscode/chef/issues/2578) Check that `installed` is not empty for `keg_only` formula in Homebrew provider.
+* [**gh2k**](https://github.com/gh2k):
+ [Issue 2625](https://github.com/opscode/chef/issues/2625) Fix missing `shell_out!` for `windows_package` resource
+* [**BackSlasher**](https://github.com/BackSlasher):
+ [Issue 2634](https://github.com/opscode/chef/issues/2634) Fix `option ':command' is not a valid option` error in subversion provider.
+* [**Seth Vargo**](https://github.com/sethvargo):
+ [Issue 2345](https://github.com/opscode/chef/issues/2345) Allow knife to install cookbooks with metadata.json.
+
+## 12.0.1
+
+* [Issue 2552](https://github.com/opscode/chef/issues/2552) Create constant for LWRP before calling `provides`
+* [Issue 2545](https://github.com/opscode/chef/issues/2545) `path` attribute of `execute` resource is restored to provide backwards compatibility with Chef 11.
+* [Issue 2565](https://github.com/opscode/chef/issues/2565) Fix `Chef::Knife::Core::BootstrapContext` constructor for knife-windows compat.
+* [Issue 2566](https://github.com/opscode/chef/issues/2566) Make sure Client doesn't raise error when interval is set on Windows.
+* [Issue 2560](https://github.com/opscode/chef/issues/2560) Fix `uninitialized constant Windows::Constants` in `windows_eventlog`.
+* [Issue 2563](https://github.com/opscode/chef/issues/2563) Make sure the Chef Client rpm packages are signed with GPG keys correctly.
## 12.0.0
diff --git a/distro/common/man/man8/chef-apply.8 b/distro/common/man/man8/chef-apply.8
new file mode 100644
index 0000000000..b12f01e886
--- /dev/null
+++ b/distro/common/man/man8/chef-apply.8
@@ -0,0 +1,86 @@
+.\" Man page generated from reStructuredText.
+.
+.TH "CHEF-APPLY" "8" "Chef 12.0" "" "chef-client"
+.SH NAME
+chef-apply \- The man page for the chef-apply command line tool.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.sp
+chef\-apply allows a single recipe to be run from the command line.
+.SH OPTIONS
+.sp
+This command has the following syntax:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+chef\-apply name_of_recipe.rb
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+This tool has the following options:
+.INDENT 0.0
+.TP
+.B \fB\-e RECIPE_TEXT\fP, \fB\-\-execute RECIPE_TEXT\fP
+Use to execute a resource using a string.
+.TP
+.B \fB\-l LEVEL\fP, \fB\-\-log_level LEVEL\fP
+The level of logging that will be stored in a log file.
+.TP
+.B \fB\-s\fP, \fB\-\-stdin\fP
+Use to execute a resource using standard input.
+.TP
+.B \fB\-v\fP, \fB\-\-version\fP
+The version of the chef\-client\&.
+.TP
+.B \fB\-W\fP, \fB\-\-why\-run\fP
+Use to run the executable in why\-run mode, which is a type of chef\-client run that does everything except modify the system. Use why\-run mode to understand why the chef\-client makes the decisions that it makes and to learn more about the current and proposed state of the system.
+.TP
+.B \fB\-h\fP, \fB\-\-help\fP
+Shows help for the command.
+.UNINDENT
+.SH EXAMPLES
+.sp
+To use chef\-apply to run a recipe named \fBmachinations.rb\fP, enter the following:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ chef\-apply machinations.rb
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SH AUTHOR
+Chef
+.\" Generated by docutils manpage writer.
+.
diff --git a/kitchen-tests/cookbooks/webapp/metadata.rb b/kitchen-tests/cookbooks/webapp/metadata.rb
index ecfb419953..c26ad23979 100644
--- a/kitchen-tests/cookbooks/webapp/metadata.rb
+++ b/kitchen-tests/cookbooks/webapp/metadata.rb
@@ -7,6 +7,6 @@ long_description 'Installs/Configures webapp'
version '0.1.0'
depends 'apache2'
-depends 'database'
+depends 'database', '~> 2.3.1'
depends 'mysql'
depends 'php'
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index 470ece35a2..40772c0f8f 100644
--- a/lib/chef/application/client.rb
+++ b/lib/chef/application/client.rb
@@ -272,7 +272,9 @@ class Chef::Application::Client < Chef::Application
Chef::Config[:splay] = nil
end
- Chef::Application.fatal!(unforked_interval_error_message) if !Chef::Config[:client_fork] && Chef::Config[:interval]
+ if !Chef::Config[:client_fork] && Chef::Config[:interval] && !Chef::Platform.windows?
+ Chef::Application.fatal!(unforked_interval_error_message)
+ end
if Chef::Config[:json_attribs]
config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs])
diff --git a/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb b/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb
index 9acfe4b936..0b14750744 100644
--- a/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb
+++ b/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb
@@ -64,7 +64,7 @@ class Chef
end
def minimize(file_contents, entry)
- object = Chef::JSONCompat.from_json(file_contents)
+ object = Chef::JSONCompat.parse(file_contents)
object = data_handler.normalize(object, entry)
object = data_handler.minimize(object, entry)
Chef::JSONCompat.to_json_pretty(object)
diff --git a/lib/chef/digester.rb b/lib/chef/digester.rb
index 0805bccee3..75c4e76859 100644
--- a/lib/chef/digester.rb
+++ b/lib/chef/digester.rb
@@ -19,6 +19,7 @@
#
require 'openssl'
+require 'singleton'
class Chef
class Digester
diff --git a/lib/chef/event_loggers/windows_eventlog.rb b/lib/chef/event_loggers/windows_eventlog.rb
index 6d45d4fab4..6f5ef627fb 100644
--- a/lib/chef/event_loggers/windows_eventlog.rb
+++ b/lib/chef/event_loggers/windows_eventlog.rb
@@ -20,9 +20,11 @@ require 'chef/event_loggers/base'
require 'chef/platform/query_helpers'
if Chef::Platform::windows? and not Chef::Platform::windows_server_2003?
- [:INFINITE, :WAIT_FAILED, :FORMAT_MESSAGE_IGNORE_INSERTS, :ERROR_INSUFFICIENT_BUFFER].each do |c|
- # These are redefined in 'win32/eventlog'
- Windows::Constants.send(:remove_const, c)
+ if defined? Windows::Constants
+ [:INFINITE, :WAIT_FAILED, :FORMAT_MESSAGE_IGNORE_INSERTS, :ERROR_INSUFFICIENT_BUFFER].each do |c|
+ # These are redefined in 'win32/eventlog'
+ Windows::Constants.send(:remove_const, c) if Windows::Constants.const_defined? c
+ end
end
require 'win32/eventlog'
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index f710266530..b204f6ef2a 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -45,6 +45,7 @@ class Chef
class FileNotFound < RuntimeError; end
class Package < RuntimeError; end
class Service < RuntimeError; end
+ class Script < RuntimeError; end
class Route < RuntimeError; end
class SearchIndex < RuntimeError; end
class Override < RuntimeError; end
@@ -149,6 +150,11 @@ class Chef
class IllegalVersionConstraint < NotImplementedError; end
class MetadataNotValid < StandardError; end
+ class MetadataNotFound < StandardError
+ def initialize
+ super "No metadata.rb or metadata.json!"
+ end
+ end
# File operation attempted but no permissions to perform it
class InsufficientPermissions < RuntimeError; end
diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb
index 3f234d7ce3..51ccb99955 100644
--- a/lib/chef/knife.rb
+++ b/lib/chef/knife.rb
@@ -428,6 +428,13 @@ class Chef
raise # make sure exit passes through.
when Net::HTTPServerException, Net::HTTPFatalError
humanize_http_exception(e)
+ when OpenSSL::SSL::SSLError
+ ui.error "Could not establish a secure connection to the server."
+ ui.info "Use `knife ssl check` to troubleshoot your SSL configuration."
+ ui.info "If your Chef Server uses a self-signed certificate, you can use"
+ ui.info "`knife ssl fetch` to make knife trust the server's certificates."
+ ui.info ""
+ ui.info "Original Exception: #{e.class.name}: #{e.message}"
when Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError
ui.error "Network Error: #{e.message}"
ui.info "Check your knife configuration and network settings"
diff --git a/lib/chef/knife/cookbook_site_install.rb b/lib/chef/knife/cookbook_site_install.rb
index 913d171b3c..edf8dd14f0 100644
--- a/lib/chef/knife/cookbook_site_install.rb
+++ b/lib/chef/knife/cookbook_site_install.rb
@@ -17,11 +17,11 @@
#
require 'chef/knife'
+require 'chef/exceptions'
require 'shellwords'
class Chef
class Knife
-
class CookbookSiteInstall < Knife
deps do
@@ -107,11 +107,8 @@ class Chef
end
end
-
unless config[:no_deps]
- md = Chef::Cookbook::Metadata.new
- md.from_file(File.join(@install_path, @cookbook_name, "metadata.rb"))
- md.dependencies.each do |cookbook, version_list|
+ preferred_metadata.dependencies.each do |cookbook, version_list|
# Doesn't do versions.. yet
nv = self.class.new
nv.config = config
@@ -144,6 +141,7 @@ class Chef
def extract_cookbook(upstream_file, version)
ui.info("Uncompressing #{@cookbook_name} version #{version}.")
+ # FIXME: Detect if we have the bad tar from git on Windows: https://github.com/opscode/chef/issues/1753
shell_out!("tar zxvf #{convert_path upstream_file}", :cwd => @install_path)
end
@@ -153,11 +151,37 @@ class Chef
end
def convert_path(upstream_file)
- if ENV['MSYSTEM'] == 'MINGW32'
- return upstream_file.sub(/^([[:alpha:]]):/, '/\1')
- else
- return Shellwords.escape upstream_file
- end
+ # converts a Windows path (C:\foo) to a mingw path (/c/foo)
+ if ENV['MSYSTEM'] == 'MINGW32'
+ return upstream_file.sub(/^([[:alpha:]]):/, '/\1')
+ else
+ return Shellwords.escape upstream_file
+ end
+ end
+
+ # Get the preferred metadata path on disk. Chef prefers the metadata.rb
+ # over the metadata.json.
+ #
+ # @raise if there is no metadata in the cookbook
+ #
+ # @return [Chef::Cookbook::Metadata]
+ def preferred_metadata
+ md = Chef::Cookbook::Metadata.new
+
+ rb = File.join(@install_path, @cookbook_name, "metadata.rb")
+ if File.exist?(rb)
+ md.from_file(rb)
+ return md
+ end
+
+ json = File.join(@install_path, @cookbook_name, "metadata.json")
+ if File.exist?(json)
+ json = IO.read(json)
+ md.from_json(json)
+ return md
+ end
+
+ raise Chef::Exceptions::MetadataNotFound
end
end
end
diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb
index ffc36436ec..f7fee023de 100644
--- a/lib/chef/knife/core/bootstrap_context.rb
+++ b/lib/chef/knife/core/bootstrap_context.rb
@@ -30,7 +30,7 @@ class Chef
#
class BootstrapContext
- def initialize(config, run_list, chef_config, secret)
+ def initialize(config, run_list, chef_config, secret = nil)
@config = config
@run_list = run_list
@chef_config = chef_config
diff --git a/lib/chef/knife/ssl_fetch.rb b/lib/chef/knife/ssl_fetch.rb
index 5626a5610d..745aca5786 100644
--- a/lib/chef/knife/ssl_fetch.rb
+++ b/lib/chef/knife/ssl_fetch.rb
@@ -136,6 +136,19 @@ TRUST_TRUST
remote_cert_chain.each do |cert|
write_cert(cert)
end
+ rescue OpenSSL::SSL::SSLError => e
+ # 'unknown protocol' usually means you tried to connect to a non-ssl
+ # service. We handle that specially here, any other error we let bubble
+ # up (probably a bug of some sort).
+ raise unless e.message.include?("unknown protocol")
+
+ ui.error("The service at the given URI (#{uri}) does not accept SSL connections")
+
+ if uri.scheme == "http"
+ https_uri = uri.to_s.sub(/^http/, 'https')
+ ui.error("Perhaps you meant to connect to '#{https_uri}'?")
+ end
+ exit 1
end
diff --git a/lib/chef/monkey_patches/net_http.rb b/lib/chef/monkey_patches/net_http.rb
index 083db2216b..9c8044a9a7 100644
--- a/lib/chef/monkey_patches/net_http.rb
+++ b/lib/chef/monkey_patches/net_http.rb
@@ -42,6 +42,10 @@ if Net::HTTP.instance_methods.map {|m| m.to_s}.include?("proxy_uri")
# from -e:1:in `<main>'
#
# https://bugs.ruby-lang.org/issues/9129
+ #
+ # NOTE: This should be fixed in Ruby 2.2.0, and backported to Ruby 2.0 and
+ # 2.1 (not yet released so the version/patchlevel required isn't known
+ # yet).
Net::HTTP.new("::1", 80).proxy_uri
rescue URI::InvalidURIError
class Net::HTTP
diff --git a/lib/chef/provider/execute.rb b/lib/chef/provider/execute.rb
index 48b2a344d1..b44112c19e 100644
--- a/lib/chef/provider/execute.rb
+++ b/lib/chef/provider/execute.rb
@@ -18,67 +18,86 @@
require 'chef/log'
require 'chef/provider'
+require 'forwardable'
class Chef
class Provider
class Execute < Chef::Provider
+ extend Forwardable
provides :execute
+ def_delegators :@new_resource, :command, :returns, :environment, :user, :group, :cwd, :umask, :creates
+
def load_current_resource
- true
+ current_resource = Chef::Resource::Execute.new(new_resource.name)
+ current_resource
end
def whyrun_supported?
true
end
- def action_run
- opts = {}
-
- if sentinel_file = sentinel_file_if_exists
- Chef::Log.debug("#{@new_resource} sentinel file #{sentinel_file} exists - nothing to do")
- return false
- end
+ def define_resource_requirements
+ # @todo: this should change to raise in some appropriate major version bump.
+ if creates && creates_relative? && !cwd
+ Chef::Log.warn "Providing a relative path for the creates attribute without the cwd is deprecated and will be changed to fail (CHEF-3819)"
+ end
+ end
+ def timeout
# original implementation did not specify a timeout, but ShellOut
# *always* times out. So, set a very long default timeout
- opts[:timeout] = @new_resource.timeout || 3600
- opts[:returns] = @new_resource.returns if @new_resource.returns
- opts[:environment] = @new_resource.environment if @new_resource.environment
- opts[:user] = @new_resource.user if @new_resource.user
- opts[:group] = @new_resource.group if @new_resource.group
- opts[:cwd] = @new_resource.cwd if @new_resource.cwd
- opts[:umask] = @new_resource.umask if @new_resource.umask
- opts[:log_level] = :info
- opts[:log_tag] = @new_resource.to_s
- if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.info? && !@new_resource.sensitive
- opts[:live_stream] = STDOUT
+ new_resource.timeout || 3600
+ end
+
+ def action_run
+ if creates && sentinel_file.exist?
+ Chef::Log.debug("#{new_resource} sentinel file #{sentinel_file} exists - nothing to do")
+ return false
end
- description = @new_resource.sensitive ? "sensitive resource" : @new_resource.command
+
converge_by("execute #{description}") do
- result = shell_out!(@new_resource.command, opts)
- Chef::Log.info("#{@new_resource} ran successfully")
+ result = shell_out!(command, opts)
+ Chef::Log.info("#{new_resource} ran successfully")
end
end
private
- def sentinel_file_if_exists
- if sentinel_file = @new_resource.creates
- relative = Pathname(sentinel_file).relative?
- cwd = @new_resource.cwd
- if relative && !cwd
- Chef::Log.warn "You have provided relative path for execute#creates (#{sentinel_file}) without execute#cwd (see CHEF-3819)"
- end
-
- if ::File.exists?(sentinel_file)
- sentinel_file
- elsif cwd && relative
- sentinel_file = ::File.join(cwd, sentinel_file)
- sentinel_file if ::File.exists?(sentinel_file)
- end
+ def sensitive?
+ !!new_resource.sensitive
+ end
+
+ def opts
+ opts = {}
+ opts[:timeout] = timeout
+ opts[:returns] = returns if returns
+ opts[:environment] = environment if environment
+ opts[:user] = user if user
+ opts[:group] = group if group
+ opts[:cwd] = cwd if cwd
+ opts[:umask] = umask if umask
+ opts[:log_level] = :info
+ opts[:log_tag] = new_resource.to_s
+ if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.info? && !sensitive?
+ opts[:live_stream] = STDOUT
end
+ opts
+ end
+
+ def description
+ sensitive? ? "sensitive resource" : command
+ end
+
+ def creates_relative?
+ Pathname(creates).relative?
+ end
+
+ def sentinel_file
+ Pathname.new(Chef::Util::PathHelper.cleanpath(
+ ( cwd && creates_relative? ) ? ::File.join(cwd, creates) : creates
+ ))
end
end
end
diff --git a/lib/chef/provider/link.rb b/lib/chef/provider/link.rb
index 417d6a21b0..c811c13cdf 100644
--- a/lib/chef/provider/link.rb
+++ b/lib/chef/provider/link.rb
@@ -86,7 +86,7 @@ class Chef
end
def canonicalize(path)
- Chef::Util::PathHelper.canonical_path(path)
+ Chef::Platform.windows? ? path.gsub('/', '\\') : path
end
def action_create
diff --git a/lib/chef/provider/lwrp_base.rb b/lib/chef/provider/lwrp_base.rb
index 135a3f6b7c..121abf5fdb 100644
--- a/lib/chef/provider/lwrp_base.rb
+++ b/lib/chef/provider/lwrp_base.rb
@@ -92,10 +92,8 @@ class Chef
provider_class = Chef::Provider.const_get(class_name)
else
provider_class = Class.new(self)
- provider_class.class_from_file(filename)
-
- class_name = convert_to_class_name(provider_name)
Chef::Provider.const_set(class_name, provider_class)
+ provider_class.class_from_file(filename)
Chef::Log.debug("Loaded contents of #{filename} into a provider named #{provider_name} defined in Chef::Provider::#{class_name}")
end
diff --git a/lib/chef/provider/mount/solaris.rb b/lib/chef/provider/mount/solaris.rb
index cf04150322..d8cec24138 100644
--- a/lib/chef/provider/mount/solaris.rb
+++ b/lib/chef/provider/mount/solaris.rb
@@ -88,7 +88,7 @@ class Chef
# FIXME: Should remount always do the remount or only if the options change?
actual_options = options || []
actual_options.delete('noauto')
- mount_options = actual_options.empty? ? '' : ",#{actual_options.join(',')}"
+ mount_options = actual_options.empty? ? '' : ",#{actual_options.join(',')}"
shell_out!("mount -o remount#{mount_options} #{mount_point}")
end
diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb
index eb2c038eaa..fd132c817c 100644
--- a/lib/chef/provider/package/apt.rb
+++ b/lib/chef/provider/package/apt.rb
@@ -46,8 +46,8 @@ class Chef
end
def default_release_options
- # Use apt::Default-Release option only if provider was explicitly defined
- "-o APT::Default-Release=#{@new_resource.default_release}" if @new_resource.provider && @new_resource.default_release
+ # Use apt::Default-Release option only if provider supports it
+ "-o APT::Default-Release=#{@new_resource.default_release}" if @new_resource.respond_to?(:default_release) && @new_resource.default_release
end
def check_package_state(package)
diff --git a/lib/chef/provider/package/homebrew.rb b/lib/chef/provider/package/homebrew.rb
index 822f4c8a42..1b407a1901 100644
--- a/lib/chef/provider/package/homebrew.rb
+++ b/lib/chef/provider/package/homebrew.rb
@@ -94,7 +94,15 @@ class Chef
# that brew thinks is linked as the current version.
#
def current_installed_version
- brew_info['keg_only'] ? brew_info['installed'].last['version'] : brew_info['linked_keg']
+ if brew_info['keg_only']
+ if brew_info['installed'].empty?
+ nil
+ else
+ brew_info['installed'].last['version']
+ end
+ else
+ brew_info['linked_keg']
+ end
end
# Packages (formula) available to install should have a
diff --git a/lib/chef/provider/package/windows/msi.rb b/lib/chef/provider/package/windows/msi.rb
index cc07909d8e..e43a307cca 100644
--- a/lib/chef/provider/package/windows/msi.rb
+++ b/lib/chef/provider/package/windows/msi.rb
@@ -19,6 +19,7 @@
# TODO: Allow @new_resource.source to be a Product Code as a GUID for uninstall / network install
require 'chef/win32/api/installer' if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+require 'chef/mixin/shell_out'
class Chef
class Provider
@@ -26,6 +27,7 @@ class Chef
class Windows
class MSI
include Chef::ReservedNames::Win32::API::Installer if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ include Chef::Mixin::ShellOut
def initialize(resource)
@new_resource = resource
diff --git a/lib/chef/provider/script.rb b/lib/chef/provider/script.rb
index 1615517553..ea286cb0e4 100644
--- a/lib/chef/provider/script.rb
+++ b/lib/chef/provider/script.rb
@@ -18,10 +18,13 @@
require 'tempfile'
require 'chef/provider/execute'
+require 'forwardable'
class Chef
class Provider
class Script < Chef::Provider::Execute
+ extend Forwardable
+
provides :bash
provides :csh
provides :perl
@@ -29,30 +32,40 @@ class Chef
provides :ruby
provides :script
+ def_delegators :@new_resource, :code, :interpreter, :flags
+
def initialize(new_resource, run_context)
super
- @code = @new_resource.code
+ end
+
+ def command
+ "\"#{interpreter}\" #{flags} \"#{script_file.path}\""
+ end
+
+ def load_current_resource
+ super
+ # @todo Chef-13: change this to an exception
+ if code.nil?
+ Chef::Log.warn "#{@new_resource}: No code attribute was given, resource does nothing, this behavior is deprecated and will be removed in Chef-13"
+ end
end
def action_run
- script_file.puts(@code)
+ script_file.puts(code)
script_file.close
set_owner_and_group
- @new_resource.command("\"#{interpreter}\" #{flags} \"#{script_file.path}\"")
super
- converge_by(nil) do
- # ensure script is unlinked at end of converge!
- unlink_script_file
- end
+
+ unlink_script_file
end
def set_owner_and_group
# FileUtils itself implements a no-op if +user+ or +group+ are nil
# You can prove this by running FileUtils.chown(nil,nil,'/tmp/file')
# as an unprivileged user.
- FileUtils.chown(@new_resource.user, @new_resource.group, script_file.path)
+ FileUtils.chown(new_resource.user, new_resource.group, script_file.path)
end
def script_file
@@ -60,16 +73,9 @@ class Chef
end
def unlink_script_file
- @script_file && @script_file.close!
+ script_file && script_file.close!
end
- def interpreter
- @new_resource.interpreter
- end
-
- def flags
- @new_resource.flags
- end
end
end
end
diff --git a/lib/chef/provider/subversion.rb b/lib/chef/provider/subversion.rb
index f4a0e6fc13..5f36483c32 100644
--- a/lib/chef/provider/subversion.rb
+++ b/lib/chef/provider/subversion.rb
@@ -62,7 +62,7 @@ class Chef
def action_checkout
if target_dir_non_existent_or_empty?
converge_by("perform checkout of #{@new_resource.repository} into #{@new_resource.destination}") do
- shell_out!(run_options(command: checkout_command))
+ shell_out!(checkout_command, run_options)
end
else
Chef::Log.debug "#{@new_resource} checkout destination #{@new_resource.destination} already exists or is a non-empty directory - nothing to do"
@@ -79,7 +79,7 @@ class Chef
def action_force_export
converge_by("export #{@new_resource.repository} into #{@new_resource.destination}") do
- shell_out!(run_options(command: export_command))
+ shell_out!(export_command, run_options)
end
end
@@ -90,7 +90,7 @@ class Chef
Chef::Log.debug "#{@new_resource} current revision: #{current_rev} target revision: #{revision_int}"
unless current_revision_matches_target_revision?
converge_by("sync #{@new_resource.destination} from #{@new_resource.repository}") do
- shell_out!(run_options(command: sync_command))
+ shell_out!(sync_command, run_options)
Chef::Log.info "#{@new_resource} updated to revision: #{revision_int}"
end
end
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index 8d964da66d..d2718c859e 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -29,7 +29,6 @@ require 'chef/resource/conditional_action_not_nothing'
require 'chef/resource_collection'
require 'chef/node_map'
require 'chef/node'
-require 'chef/provider_resolver'
require 'chef/platform'
require 'chef/mixin/deprecation'
@@ -833,3 +832,7 @@ F
end
end
end
+
+# We require this at the BOTTOM of this file to avoid circular requires (it is used
+# at runtime but not load time)
+require 'chef/provider_resolver'
diff --git a/lib/chef/resource/execute.rb b/lib/chef/resource/execute.rb
index 980035b079..6853b62887 100644
--- a/lib/chef/resource/execute.rb
+++ b/lib/chef/resource/execute.rb
@@ -36,6 +36,7 @@ class Chef
@cwd = nil
@environment = nil
@group = nil
+ @path = nil
@returns = 0
@timeout = nil
@user = nil
@@ -94,6 +95,16 @@ class Chef
)
end
+ def path(arg=nil)
+ Chef::Log.warn "'path' attribute of 'execute' is not used by any provider in Chef 11 and Chef 12. Use 'environment' attribute to configure 'PATH'. This attribute will be removed in Chef 13."
+
+ set_or_return(
+ :path,
+ arg,
+ :kind_of => [ Array ]
+ )
+ end
+
def returns(arg=nil)
set_or_return(
:returns,
@@ -106,7 +117,7 @@ class Chef
set_or_return(
:timeout,
arg,
- :kind_of => [ Integer ]
+ :kind_of => [ Integer, Float ]
)
end
@@ -135,12 +146,12 @@ class Chef
end
set_guard_inherited_attributes(
- :cwd,
- :environment,
- :group,
- :user,
- :umask
- )
+ :cwd,
+ :environment,
+ :group,
+ :user,
+ :umask
+ )
end
end
diff --git a/lib/chef/resource/lwrp_base.rb b/lib/chef/resource/lwrp_base.rb
index a4606be842..20d177f507 100644
--- a/lib/chef/resource/lwrp_base.rb
+++ b/lib/chef/resource/lwrp_base.rb
@@ -46,11 +46,11 @@ class Chef
else
resource_class = Class.new(self)
+ Chef::Resource.const_set(class_name, resource_class)
resource_class.resource_name = rname
resource_class.run_context = run_context
resource_class.class_from_file(filename)
- Chef::Resource.const_set(class_name, resource_class)
Chef::Log.debug("Loaded contents of #{filename} into a resource named #{rname} defined in Chef::Resource::#{class_name}")
end
diff --git a/lib/chef/resource/script.rb b/lib/chef/resource/script.rb
index 479295922c..fd0fd5a7fd 100644
--- a/lib/chef/resource/script.rb
+++ b/lib/chef/resource/script.rb
@@ -24,11 +24,13 @@ class Chef
class Resource
class Script < Chef::Resource::Execute
+ # Chef-13: go back to using :name as the identity attr
identity_attr :command
def initialize(name, run_context=nil)
super
@resource_name = :script
+ # Chef-13: the command variable should be initialized to nil
@command = name
@code = nil
@interpreter = nil
@@ -36,6 +38,15 @@ class Chef
@default_guard_interpreter = :default
end
+ def command(arg=nil)
+ unless arg.nil?
+ # Chef-13: change this to raise if the user is trying to set a value here
+ Chef::Log.warn "Specifying command attribute on a script resource is a coding error, use the 'code' attribute, or the execute resource"
+ Chef::Log.warn "This attribute is deprecated and must be fixed or this code will fail on Chef-13"
+ end
+ super
+ end
+
def code(arg=nil)
set_or_return(
:code,
diff --git a/lib/chef/whitelist.rb b/lib/chef/whitelist.rb
index ad52215f11..86c229d22c 100644
--- a/lib/chef/whitelist.rb
+++ b/lib/chef/whitelist.rb
@@ -57,7 +57,9 @@ class Chef
all_data = all_data[part]
end
- unless all_data[parts[-1]]
+ # Note: You can't do all_data[parts[-1]] here because the value
+ # may be false-y
+ unless all_data.key?(parts[-1])
Chef::Log.warn("Could not find whitelist attribute #{item}.")
return nil
end
diff --git a/lib/chef/win32/api/file.rb b/lib/chef/win32/api/file.rb
index da9713e119..86b2b942c2 100644
--- a/lib/chef/win32/api/file.rb
+++ b/lib/chef/win32/api/file.rb
@@ -457,11 +457,25 @@ BOOL WINAPI DeviceIoControl(
# takes the given path pre-pends "\\?\" and
# UTF-16LE encodes it. Used to prepare paths
# to be passed to the *W vesion of WinAPI File
- # functions
+ # functions.
+ # This function is used by the "Link" resources where we need
+ # preserve relative paths because symbolic links can actually
+ # point to a relative path (relative to the link itself).
def encode_path(path)
+ (path_prepender << path.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR)).to_wstring
+ end
+
+ # Expands the path, prepends "\\?\" and UTF-16LE encodes it.
+ # This function is used by the "File" resources where we need
+ # convert relative paths to fully qualified paths.
+ def canonical_encode_path(path)
Chef::Util::PathHelper.canonical_path(path).to_wstring
end
+ def path_prepender
+ "\\\\?\\"
+ end
+
# retrieves a file search handle and passes it
# to +&block+ along with the find_data. also
# ensures the handle is closed on exit of the block
@@ -474,7 +488,7 @@ BOOL WINAPI DeviceIoControl(
# broader fix to map all the paths starting with "/" to
# SYSTEM_DRIVE on windows.
path = ::File.expand_path(path) if path.start_with? "/"
- path = encode_path(path)
+ path = canonical_encode_path(path)
find_data = WIN32_FIND_DATA.new
handle = FindFirstFileW(path, find_data)
if handle == INVALID_HANDLE_VALUE
@@ -491,7 +505,7 @@ BOOL WINAPI DeviceIoControl(
# ensures the handle is closed on exit of the block
def file_handle(path, &block)
begin
- path = encode_path(path)
+ path = canonical_encode_path(path)
handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nil)
diff --git a/spec/data/lwrp/providers/buck_passer.rb b/spec/data/lwrp/providers/buck_passer.rb
index 8d5156af81..c56ab94f85 100644
--- a/spec/data/lwrp/providers/buck_passer.rb
+++ b/spec/data/lwrp/providers/buck_passer.rb
@@ -1,3 +1,4 @@
+provides 'buck_passer'
action :pass_buck do
lwrp_foo :prepared_thumbs do
action :prepare_thumbs
diff --git a/spec/data/lwrp/resources/bar.rb b/spec/data/lwrp/resources/bar.rb
index bded6eeac3..b6359648db 100644
--- a/spec/data/lwrp/resources/bar.rb
+++ b/spec/data/lwrp/resources/bar.rb
@@ -1 +1,2 @@
+provides "lwrp_bar" # This makes sure that we cover the case of lwrps using provides
actions :pass_buck, :prepare_eyes, :watch_paint_dry
diff --git a/spec/functional/resource/bash_spec.rb b/spec/functional/resource/bash_spec.rb
new file mode 100644
index 0000000000..209ec4a12f
--- /dev/null
+++ b/spec/functional/resource/bash_spec.rb
@@ -0,0 +1,88 @@
+#
+# Author:: Serdar Sutay (<serdar@opscode.com>)
+# Copyright:: Copyright (c) 2014 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 'functional/resource/base'
+
+describe Chef::Resource::Bash, :unix_only do
+ let(:code) { "echo hello" }
+ let(:resource) {
+ resource = Chef::Resource::Bash.new("foo_resource", run_context)
+ resource.code(code)
+ resource
+ }
+
+ describe "when setting the command attribute" do
+ let (:command) { 'wizard racket' }
+
+ # in Chef-12 the `command` attribute is largely useless, but does set the identity attribute
+ # so that notifications need to target the value of the command. it will not run the `command`
+ # and if it is given without a code block then it does nothing and always succeeds.
+ describe "in Chef-12", :chef_lt_13_only do
+ it "gets the commmand attribute from the name" do
+ expect(resource.command).to eql("foo_resource")
+ end
+
+ it "sets the resource identity to the command name" do
+ resource.command command
+ expect(resource.identity).to eql(command)
+ end
+
+ it "warns when the code is not present and a useless `command` is present" do
+ expect(Chef::Log).to receive(:warn).with(/coding error/)
+ expect(Chef::Log).to receive(:warn).with(/deprecated/)
+ resource.code nil
+ resource.command command
+ expect { resource.run_action(:run) }.not_to raise_error
+ end
+
+ describe "when the code is not present" do
+ let(:code) { nil }
+ it "warns" do
+ expect(Chef::Log).to receive(:warn)
+ expect { resource.run_action(:run) }.not_to raise_error
+ end
+ end
+ end
+
+ # in Chef-13 the `command` attribute needs to be for internal use only
+ describe "in Chef-13", :chef_gte_13_only do
+ it "should raise an exception when trying to set the command" do
+ expect { resource.command command }.to raise_error # FIXME: add a real error in Chef-13
+ end
+
+ it "should initialize the command to nil" do
+ expect(resource.command).to be_nil
+ end
+
+ describe "when the code is not present" do
+ let(:code) { nil }
+ it "raises an exception" do
+ expect { resource.run_action(:run) }.to raise_error # FIXME: add a real error in Chef-13
+ expect { resource.run_action(:run) }.not_to raise_error
+ end
+ end
+ end
+ end
+
+ it "times out when a timeout is set on the resource" do
+ resource.code 'sleep 600'
+ resource.timeout 0.1
+ expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::CommandTimeout)
+ end
+end
diff --git a/spec/functional/resource/execute_spec.rb b/spec/functional/resource/execute_spec.rb
index 39fef76ab0..cebcc52fcf 100644
--- a/spec/functional/resource/execute_spec.rb
+++ b/spec/functional/resource/execute_spec.rb
@@ -20,94 +20,99 @@ require 'spec_helper'
require 'functional/resource/base'
describe Chef::Resource::Execute do
- let(:execute_resource) {
- exec_resource = Chef::Resource::Execute.new("foo_resource", run_context)
-
- exec_resource.environment(resource_environment) if resource_environment
- exec_resource.cwd(resource_cwd) if resource_cwd
- exec_resource.command("echo hello")
- if guard
- if guard_options
- exec_resource.only_if(guard, guard_options)
- else
- exec_resource.only_if(guard)
- end
- end
- exec_resource
+ let(:resource) {
+ resource = Chef::Resource::Execute.new("foo_resource", run_context)
+ resource.command("echo hello")
+ resource
}
- let(:resource_environment) { nil }
- let(:resource_cwd) { nil }
- let(:guard) { nil }
- let(:guard_options) { nil }
-
describe "when guard is ruby block" do
it "guard can still run" do
- execute_resource.only_if do
- true
- end
- execute_resource.run_action(:run)
- expect(execute_resource).to be_updated_by_last_action
+ resource.only_if { true }
+ resource.run_action(:run)
+ expect(resource).to be_updated_by_last_action
end
end
describe "when parent resource sets :cwd" do
- let(:resource_cwd) { CHEF_SPEC_DATA }
-
let(:guard) { %{ruby -e 'exit 1 unless File.exists?("./big_json_plus_one.json")'} }
- it "guard inherits :cwd from resource" do
- execute_resource.run_action(:run)
- expect(execute_resource).to be_updated_by_last_action
+ it "guard inherits :cwd from resource and runs" do
+ resource.cwd CHEF_SPEC_DATA
+ resource.only_if guard
+ resource.run_action(:run)
+ expect(resource).to be_updated_by_last_action
+ end
+
+ it "guard inherits :cwd from resource and does not run" do
+ resource.cwd CHEF_SPEC_DATA
+ resource.not_if guard
+ resource.run_action(:run)
+ expect(resource).not_to be_updated_by_last_action
end
end
+ # We use ruby command so that we don't need to deal with platform specific
+ # commands while testing execute resource. We set it so that the resource
+ # will be updated if the ENV variable is set to what we are intending
+ #
+ # FIXME: yeah, but invoking ruby is slow...
describe "when parent resource sets :environment" do
- let(:resource_environment) do
- {
+ before do
+ resource.environment({
"SAWS_SECRET" => "supersecret",
- "SAWS_KEY" => "qwerty"
- }
+ "SAWS_KEY" => "qwerty",
+ })
end
- # We use ruby command so that we don't need to deal with platform specific
- # commands while testing execute resource. We set it so that the resource
- # will be updated if the ENV variable is set to what we are intending
- let(:guard) { %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] != "supersecret"'} }
-
- it "guard inherits :environment value from resource" do
- execute_resource.run_action(:run)
- expect(execute_resource).to be_updated_by_last_action
+ it "guard inherits :environment value from resource and runs" do
+ resource.only_if %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] != "supersecret"'}
+ resource.run_action(:run)
+ expect(resource).to be_updated_by_last_action
end
- describe "when guard sets additional values in the :environment" do
- let(:guard) { %{ruby -e 'exit 1 if ENV["SGCE_SECRET"] != "regularsecret"'} }
-
- let(:guard_options) do
- {
- :environment => { 'SGCE_SECRET' => "regularsecret" }
- }
- end
+ it "guard inherits :environment value from resource and does not run" do
+ resource.only_if %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] == "supersecret"'}
+ resource.run_action(:run)
+ expect(resource).not_to be_updated_by_last_action
+ end
- it "guard sees merged value for in its ENV" do
- execute_resource.run_action(:run)
- expect(execute_resource).to be_updated_by_last_action
- end
+ it "guard adds additional values in its :environment and runs" do
+ resource.only_if %{ruby -e 'exit 1 if ENV["SGCE_SECRET"] != "regularsecret"'}, {
+ :environment => { 'SGCE_SECRET' => "regularsecret" }
+ }
+ resource.run_action(:run)
+ expect(resource).to be_updated_by_last_action
end
- describe "when guard sets same value in the :environment" do
- let(:guard) { %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] != "regularsecret"'} }
+ it "guard adds additional values in its :environment and does not run" do
+ resource.only_if %{ruby -e 'exit 1 if ENV["SGCE_SECRET"] == "regularsecret"'}, {
+ :environment => { 'SGCE_SECRET' => "regularsecret" }
+ }
+ resource.run_action(:run)
+ expect(resource).not_to be_updated_by_last_action
+ end
- let(:guard_options) do
- {
- :environment => { 'SAWS_SECRET' => "regularsecret" }
- }
- end
+ it "guard overwrites value with its :environment and runs" do
+ resource.only_if %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] != "regularsecret"'}, {
+ :environment => { 'SAWS_SECRET' => "regularsecret" }
+ }
+ resource.run_action(:run)
+ expect(resource).to be_updated_by_last_action
+ end
- it "guard sees value from guard options in its ENV" do
- execute_resource.run_action(:run)
- expect(execute_resource).to be_updated_by_last_action
- end
+ it "guard overwrites value with its :environment and does not runs" do
+ resource.only_if %{ruby -e 'exit 1 if ENV["SAWS_SECRET"] == "regularsecret"'}, {
+ :environment => { 'SAWS_SECRET' => "regularsecret" }
+ }
+ resource.run_action(:run)
+ expect(resource).not_to be_updated_by_last_action
end
end
+
+ it "times out when a timeout is set on the resource" do
+ resource.command %{ruby -e 'sleep 600'}
+ resource.timeout 0.1
+ expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::CommandTimeout)
+ end
end
diff --git a/spec/functional/resource/link_spec.rb b/spec/functional/resource/link_spec.rb
index b4c6412e5d..d39a0c2ef6 100644
--- a/spec/functional/resource/link_spec.rb
+++ b/spec/functional/resource/link_spec.rb
@@ -72,8 +72,8 @@ describe Chef::Resource::Link do
end
end
- def paths_eql?(path1, path2)
- Chef::Util::PathHelper.paths_eql?(path1, path2)
+ def canonicalize(path)
+ windows? ? path.gsub('/', '\\') : path
end
def symlink(a, b)
@@ -180,7 +180,7 @@ describe Chef::Resource::Link do
it 'links to the target file' do
expect(symlink?(target_file)).to be_truthy
- expect(paths_eql?(readlink(target_file), to)).to be_truthy
+ expect(readlink(target_file)).to eq(canonicalize(to))
end
it 'marks the resource updated' do
expect(resource).to be_updated
@@ -201,7 +201,7 @@ describe Chef::Resource::Link do
it 'leaves the file linked' do
expect(symlink?(target_file)).to be_truthy
- expect(paths_eql?(readlink(target_file), to)).to be_truthy
+ expect(readlink(target_file)).to eq(canonicalize(to))
end
it 'does not mark the resource updated' do
expect(resource).not_to be_updated
@@ -279,7 +279,7 @@ describe Chef::Resource::Link do
before(:each) do
symlink(to, target_file)
expect(symlink?(target_file)).to be_truthy
- expect(paths_eql?(readlink(target_file), to)).to be_truthy
+ expect(readlink(target_file)).to eq(canonicalize(to))
end
include_context 'create symbolic link is noop'
include_context 'delete succeeds'
@@ -294,7 +294,7 @@ describe Chef::Resource::Link do
File.open(@other_target, 'w') { |file| file.write('eek') }
symlink(@other_target, target_file)
expect(symlink?(target_file)).to be_truthy
- expect(paths_eql?(readlink(target_file), @other_target)).to be_truthy
+ expect(readlink(target_file)).to eq(canonicalize(@other_target))
end
after(:each) do
File.delete(@other_target)
@@ -311,7 +311,7 @@ describe Chef::Resource::Link do
nonexistent = File.join(test_file_dir, make_tmpname('nonexistent_spec'))
symlink(nonexistent, target_file)
expect(symlink?(target_file)).to be_truthy
- expect(paths_eql?(readlink(target_file), nonexistent)).to be_truthy
+ expect(readlink(target_file)).to eq(canonicalize(nonexistent))
end
include_context 'create symbolic link succeeds'
include_context 'delete succeeds'
@@ -393,7 +393,7 @@ describe Chef::Resource::Link do
File.open(@other_target, "w") { |file| file.write("eek") }
symlink(@other_target, to)
expect(symlink?(to)).to be_truthy
- expect(paths_eql?(readlink(to), @other_target)).to be_truthy
+ expect(readlink(to)).to eq(canonicalize(@other_target))
end
after(:each) do
File.delete(@other_target)
@@ -408,7 +408,7 @@ describe Chef::Resource::Link do
@other_target = File.join(test_file_dir, make_tmpname("other_spec"))
symlink(@other_target, to)
expect(symlink?(to)).to be_truthy
- expect(paths_eql?(readlink(to), @other_target)).to be_truthy
+ expect(readlink(to)).to eq(canonicalize(@other_target))
end
context 'and the link does not yet exist' do
include_context 'create symbolic link succeeds'
@@ -441,7 +441,7 @@ describe Chef::Resource::Link do
before(:each) do
symlink(to, target_file)
expect(symlink?(target_file)).to be_truthy
- expect(paths_eql?(readlink(target_file), to)).to be_truthy
+ expect(readlink(target_file)).to eq(canonicalize(to))
end
include_context 'create symbolic link is noop'
include_context 'delete succeeds'
@@ -450,7 +450,7 @@ describe Chef::Resource::Link do
before(:each) do
symlink(absolute_to, target_file)
expect(symlink?(target_file)).to be_truthy
- expect(paths_eql?(readlink(target_file), absolute_to)).to be_truthy
+ expect(readlink(target_file)).to eq(canonicalize(absolute_to))
end
include_context 'create symbolic link succeeds'
include_context 'delete succeeds'
@@ -478,7 +478,7 @@ describe Chef::Resource::Link do
before(:each) do
symlink(to, target_file)
expect(symlink?(target_file)).to be_truthy
- expect(paths_eql?(readlink(target_file), to)).to be_truthy
+ expect(readlink(target_file)).to eq(canonicalize(to))
end
include_context 'create hard link succeeds'
it_behaves_like 'delete errors out'
@@ -552,7 +552,7 @@ describe Chef::Resource::Link do
File.open(@other_target, "w") { |file| file.write("eek") }
symlink(@other_target, to)
expect(symlink?(to)).to be_truthy
- expect(paths_eql?(readlink(to), @other_target)).to be_truthy
+ expect(readlink(to)).to eq(canonicalize(@other_target))
end
after(:each) do
File.delete(@other_target)
@@ -564,7 +564,7 @@ describe Chef::Resource::Link do
# OS X gets angry about this sort of link. Bug in OS X, IMO.
pending('OS X/FreeBSD/AIX symlink? and readlink working on hard links to symlinks') if (os_x? or freebsd? or aix?)
expect(symlink?(target_file)).to be_truthy
- expect(paths_eql?(readlink(target_file), @other_target)).to be_truthy
+ expect(readlink(target_file)).to eq(canonicalize(@other_target))
end
include_context 'delete is noop'
end
@@ -574,7 +574,7 @@ describe Chef::Resource::Link do
@other_target = File.join(test_file_dir, make_tmpname("other_spec"))
symlink(@other_target, to)
expect(symlink?(to)).to be_truthy
- expect(paths_eql?(readlink(to), @other_target)).to be_truthy
+ expect(readlink(to)).to eq(canonicalize(@other_target))
end
context 'and the link does not yet exist' do
it 'links to the target file' do
@@ -587,7 +587,7 @@ describe Chef::Resource::Link do
expect(File.exists?(target_file)).to be_falsey
end
expect(symlink?(target_file)).to be_truthy
- expect(paths_eql?(readlink(target_file), @other_target)).to be_truthy
+ expect(readlink(target_file)).to eq(canonicalize(@other_target))
end
include_context 'delete is noop'
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index e3de80f3f1..b155e6da24 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -129,6 +129,10 @@ RSpec.configure do |config|
config.filter_run_excluding :ruby_gte_19_only => true unless ruby_gte_19?
config.filter_run_excluding :ruby_20_only => true unless ruby_20?
config.filter_run_excluding :ruby_gte_20_only => true unless ruby_gte_20?
+ # chef_gte_XX_only and chef_lt_XX_only pair up correctly with the same XX
+ # number. please conserve this pattern & resist filling out all the operators
+ config.filter_run_excluding :chef_gte_13_only => true unless chef_gte_13?
+ config.filter_run_excluding :chef_lt_13_only => true unless chef_lt_13?
config.filter_run_excluding :requires_root => true unless root?
config.filter_run_excluding :requires_root_or_running_windows => true unless (root? || windows?)
config.filter_run_excluding :requires_unprivileged_user => true if root?
diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb
index 8d17af3993..959580c953 100644
--- a/spec/support/platform_helpers.rb
+++ b/spec/support/platform_helpers.rb
@@ -1,7 +1,10 @@
require 'fcntl'
require 'chef/mixin/shell_out'
-include Chef::Mixin::ShellOut
+
+class ShellHelpers
+ extend Chef::Mixin::ShellOut
+end
def ruby_gte_20?
RUBY_VERSION.to_f >= 2.0
@@ -11,6 +14,14 @@ def ruby_lt_20?
!ruby_gte_20?
end
+def chef_gte_13?
+ Chef::VERSION.split('.').first.to_i >= 13
+end
+
+def chef_lt_13?
+ Chef::VERSION.split('.').first.to_i < 13
+end
+
def ruby_gte_19?
RUBY_VERSION.to_f >= 1.9
end
@@ -78,7 +89,7 @@ end
def mac_osx_106?
if File.exists? "/usr/bin/sw_vers"
- result = shell_out("/usr/bin/sw_vers")
+ result = ShellHelpers.shell_out("/usr/bin/sw_vers")
result.stdout.each_line do |line|
if line =~ /^ProductVersion:\s10.6.*$/
return true
diff --git a/spec/support/shared/unit/execute_resource.rb b/spec/support/shared/unit/execute_resource.rb
index d6fb88db5c..e969a2ebd5 100644
--- a/spec/support/shared/unit/execute_resource.rb
+++ b/spec/support/shared/unit/execute_resource.rb
@@ -76,6 +76,16 @@ shared_examples_for "an execute resource" do
expect(@resource.group).to eql(1)
end
+ it "should accept an array for the execution path in Chef-12 and log deprecation message", :chef_lt_13_only do
+ expect(Chef::Log).to receive(:warn).at_least(:once)
+ @resource.path ["woot"]
+ expect(@resource.path).to eql(["woot"])
+ end
+
+ it "should raise an exception in chef-13", :chef_gte_13_only do
+ expect(@resource.path [ "woot" ]).to raise_error
+ end
+
it "should accept an integer for the return code" do
@resource.returns 1
expect(@resource.returns).to eql(1)
diff --git a/spec/support/shared/unit/script_resource.rb b/spec/support/shared/unit/script_resource.rb
index 58f7f98f19..18ee94606e 100644
--- a/spec/support/shared/unit/script_resource.rb
+++ b/spec/support/shared/unit/script_resource.rb
@@ -21,47 +21,56 @@ require 'spec_helper'
shared_examples_for "a script resource" do
- before(:each) do
- @resource = script_resource
- end
-
it "should create a new Chef::Resource::Script" do
- expect(@resource).to be_a_kind_of(Chef::Resource)
- expect(@resource).to be_a_kind_of(Chef::Resource::Script)
+ expect(script_resource).to be_a_kind_of(Chef::Resource)
+ expect(script_resource).to be_a_kind_of(Chef::Resource::Script)
end
it "should have a resource name of :script" do
- expect(@resource.resource_name).to eql(resource_name)
+ expect(script_resource.resource_name).to eql(resource_name)
end
- it "should set command to the argument provided to new" do
- expect(@resource.command).to eql(resource_instance_name)
+ it "should set command to nil on the resource", :chef_gte_13_only do
+ expect(script_resource.command).to be nil
+ end
+
+ it "should set command to the name on the resource", :chef_lt_13_only do
+ expect(script_resource.command).to eql script_resource.name
end
it "should accept a string for the code" do
- @resource.code "hey jude"
- expect(@resource.code).to eql("hey jude")
+ script_resource.code "hey jude"
+ expect(script_resource.code).to eql("hey jude")
end
it "should accept a string for the flags" do
- @resource.flags "-f"
- expect(@resource.flags).to eql("-f")
+ script_resource.flags "-f"
+ expect(script_resource.flags).to eql("-f")
end
- describe "when executing guards" do
- let(:resource) { @resource }
-
- before(:each) do
- node = Chef::Node.new
+ it "should raise an exception if users set command on the resource", :chef_gte_13_only do
+ expect { script_resource.command('foo') }.to raise_error(Chef::Exceptions::Script)
+ end
- node.automatic[:platform] = "debian"
- node.automatic[:platform_version] = "6.0"
+ it "should not raise an exception if users set command on the resource", :chef_lt_13_only do
+ expect { script_resource.command('foo') }.not_to raise_error
+ end
- events = Chef::EventDispatch::Dispatcher.new
- run_context = Chef::RunContext.new(node, {}, events)
+ describe "when executing guards" do
+ let(:resource) {
+ resource = script_resource
resource.run_context = run_context
resource.code 'echo hi'
- end
+ resource
+ }
+ let(:node) {
+ node = Chef::Node.new
+ node.automatic[:platform] = "debian"
+ node.automatic[:platform_version] = "6.0"
+ node
+ }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
it "inherits exactly the :cwd, :environment, :group, :path, :user, and :umask attributes from a parent resource class" do
inherited_difference = Chef::Resource::Script.guard_inherited_attributes -
@@ -87,4 +96,3 @@ shared_examples_for "a script resource" do
end
end
end
-
diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb
index 1b4adcafea..3554b78c13 100644
--- a/spec/unit/application/client_spec.rb
+++ b/spec/unit/application/client_spec.rb
@@ -52,6 +52,7 @@ describe Chef::Application::Client, "reconfigure" do
context "when interval is given" do
before do
Chef::Config[:interval] = 600
+ allow(Chef::Platform).to receive(:windows?).and_return(false)
end
it "should terminate with message" do
@@ -65,6 +66,18 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
end
end
+ context "when interval is given on windows" do
+ before do
+ Chef::Config[:interval] = 600
+ allow(Chef::Platform).to receive(:windows?).and_return(true)
+ end
+
+ it "should not terminate" do
+ expect(Chef::Application).not_to receive(:fatal!)
+ @app.reconfigure
+ end
+ end
+
context "when configured to run once" do
before do
Chef::Config[:once] = true
diff --git a/spec/unit/cookbook/syntax_check_spec.rb b/spec/unit/cookbook/syntax_check_spec.rb
index 67d31cdca5..471fc01831 100644
--- a/spec/unit/cookbook/syntax_check_spec.rb
+++ b/spec/unit/cookbook/syntax_check_spec.rb
@@ -43,6 +43,7 @@ describe Chef::Cookbook::SyntaxCheck do
before do
Chef::Log.logger = Logger.new(StringIO.new)
+ @original_log_level = Chef::Log.level
Chef::Log.level = :warn # suppress "Syntax OK" messages
@attr_files = %w{default.rb smokey.rb}.map { |f| File.join(cookbook_path, 'attributes', f) }
@@ -61,6 +62,10 @@ describe Chef::Cookbook::SyntaxCheck do
@template_files = basenames.map { |f| File.join(cookbook_path, 'templates', 'default', f)}
end
+ after do
+ Chef::Log.level = @original_log_level
+ end
+
it "creates a syntax checker given the cookbook name when Chef::Config.cookbook_path is set" do
Chef::Config[:cookbook_path] = File.dirname(cookbook_path)
syntax_check = Chef::Cookbook::SyntaxCheck.for_cookbook(:openldap)
diff --git a/spec/unit/http/validate_content_length_spec.rb b/spec/unit/http/validate_content_length_spec.rb
index 34b6a61a3a..79bd539af3 100644
--- a/spec/unit/http/validate_content_length_spec.rb
+++ b/spec/unit/http/validate_content_length_spec.rb
@@ -83,12 +83,17 @@ describe Chef::HTTP::ValidateContentLength do
let(:debug_stream) { StringIO.new }
let(:debug_output) { debug_stream.string }
- before(:each) {
+ before(:each) do
+ @original_log_level = Chef::Log.level
Chef::Log.level = :debug
allow(Chef::Log).to receive(:debug) do |message|
debug_stream.puts message
end
- }
+ end
+
+ after(:each) do
+ Chef::Log.level = @original_log_level
+ end
describe "without response body" do
let(:request_type) { :direct }
diff --git a/spec/unit/knife/cookbook_site_install_spec.rb b/spec/unit/knife/cookbook_site_install_spec.rb
index c4bd8f67d2..b3eef32b39 100644
--- a/spec/unit/knife/cookbook_site_install_spec.rb
+++ b/spec/unit/knife/cookbook_site_install_spec.rb
@@ -19,132 +19,173 @@
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
describe Chef::Knife::CookbookSiteInstall do
+ let(:knife) { Chef::Knife::CookbookSiteInstall.new }
+ let(:stdout) { StringIO.new }
+ let(:stderr) { StringIO.new }
+ let(:downloader) { Hash.new }
+ let(:repo) { double(:sanity_check => true, :reset_to_default_state => true,
+ :prepare_to_import => true, :finalize_updates_to => true,
+ :merge_updates_from => true) }
+ let(:install_path) { if Chef::Platform.windows?
+ 'C:/tmp/chef'
+ else
+ '/var/tmp/chef'
+ end }
+
before(:each) do
require 'chef/knife/core/cookbook_scm_repo'
- @stdout = StringIO.new
- @knife = Chef::Knife::CookbookSiteInstall.new
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- @knife.config = {}
- if Chef::Platform.windows?
- @install_path = 'C:/tmp/chef'
- else
- @install_path = '/var/tmp/chef'
- end
- @knife.config[:cookbook_path] = [ @install_path ]
-
- @stdout = StringIO.new
- @stderr = StringIO.new
- allow(@knife).to receive(:stderr).and_return(@stdout)
- allow(@knife).to receive(:stdout).and_return(@stdout)
-
- #Assume all external commands would have succeed. :(
+
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
+ knife.config = {}
+ knife.config[:cookbook_path] = [ install_path ]
+
+ allow(knife).to receive(:stderr).and_return(stderr)
+ allow(knife).to receive(:stdout).and_return(stdout)
+
+ # Assume all external commands would have succeed. :(
allow(File).to receive(:unlink)
allow(File).to receive(:rmtree)
- allow(@knife).to receive(:shell_out!).and_return(true)
-
- #CookbookSiteDownload Stup
- @downloader = {}
- allow(@knife).to receive(:download_cookbook_to).and_return(@downloader)
- allow(@downloader).to receive(:version) do
- if @knife.name_args.size == 2
- @knife.name_args[1]
+ allow(knife).to receive(:shell_out!).and_return(true)
+
+ # CookbookSiteDownload Stup
+ allow(knife).to receive(:download_cookbook_to).and_return(downloader)
+ allow(downloader).to receive(:version) do
+ if knife.name_args.size == 2
+ knife.name_args[1]
else
"0.3.0"
end
end
- #Stubs for CookbookSCMRepo
- @repo = double(:sanity_check => true, :reset_to_default_state => true,
- :prepare_to_import => true, :finalize_updates_to => true,
- :merge_updates_from => true)
- allow(Chef::Knife::CookbookSCMRepo).to receive(:new).and_return(@repo)
+ # Stubs for CookbookSCMRepo
+ allow(Chef::Knife::CookbookSCMRepo).to receive(:new).and_return(repo)
end
-
describe "run" do
- it "should return an error if a cookbook name is not provided" do
- @knife.name_args = []
- expect(@knife.ui).to receive(:error).with("Please specify a cookbook to download and install.")
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- it "should return an error if more than two arguments are given" do
- @knife.name_args = ["foo", "bar", "baz"]
- expect(@knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- it "should return an error if the second argument is not a version" do
- @knife.name_args = ["getting-started", "1pass"]
- expect(@knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- it "should return an error if the second argument is a four-digit version" do
- @knife.name_args = ["getting-started", "0.0.0.1"]
- expect(@knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- it "should return an error if the second argument is a one-digit version" do
- @knife.name_args = ["getting-started", "1"]
- expect(@knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
- expect { @knife.run }.to raise_error(SystemExit)
- end
-
- it "should install the specified version if second argument is a three-digit version" do
- @knife.name_args = ["getting-started", "0.1.0"]
- @knife.config[:no_deps] = true
- upstream_file = File.join(@install_path, "getting-started.tar.gz")
- expect(@knife).to receive(:download_cookbook_to).with(upstream_file)
- expect(@knife).to receive(:extract_cookbook).with(upstream_file, "0.1.0")
- expect(@knife).to receive(:clear_existing_files).with(File.join(@install_path, "getting-started"))
- expect(@repo).to receive(:merge_updates_from).with("getting-started", "0.1.0")
- @knife.run
- end
-
- it "should install the specified version if second argument is a two-digit version" do
- @knife.name_args = ["getting-started", "0.1"]
- @knife.config[:no_deps] = true
- upstream_file = File.join(@install_path, "getting-started.tar.gz")
- expect(@knife).to receive(:download_cookbook_to).with(upstream_file)
- expect(@knife).to receive(:extract_cookbook).with(upstream_file, "0.1")
- expect(@knife).to receive(:clear_existing_files).with(File.join(@install_path, "getting-started"))
- expect(@repo).to receive(:merge_updates_from).with("getting-started", "0.1")
- @knife.run
- end
-
- it "should install the latest version if only a cookbook name is given" do
- @knife.name_args = ["getting-started"]
- @knife.config[:no_deps] = true
- upstream_file = File.join(@install_path, "getting-started.tar.gz")
- expect(@knife).to receive(:download_cookbook_to).with(upstream_file)
- expect(@knife).to receive(:extract_cookbook).with(upstream_file, "0.3.0")
- expect(@knife).to receive(:clear_existing_files).with(File.join(@install_path, "getting-started"))
- expect(@repo).to receive(:merge_updates_from).with("getting-started", "0.3.0")
- @knife.run
- end
-
- it "should not create/reset git branches if use_current_branch is set" do
- @knife.name_args = ["getting-started"]
- @knife.config[:use_current_branch] = true
- @knife.config[:no_deps] = true
- upstream_file = File.join(@install_path, "getting-started.tar.gz")
- expect(@repo).not_to receive(:prepare_to_import)
- expect(@repo).not_to receive(:reset_to_default_state)
- @knife.run
- end
-
- it "should not raise an error if cookbook_path is a string" do
- @knife.config[:cookbook_path] = @install_path
- @knife.config[:no_deps] = true
- @knife.name_args = ["getting-started"]
- upstream_file = File.join(@install_path, "getting-started.tar.gz")
- expect(@knife).to receive(:download_cookbook_to).with(upstream_file)
- expect(@knife).to receive(:extract_cookbook).with(upstream_file, "0.3.0")
- expect(@knife).to receive(:clear_existing_files).with(File.join(@install_path, "getting-started"))
- expect(@repo).to receive(:merge_updates_from).with("getting-started", "0.3.0")
- expect { @knife.run }.not_to raise_error
+ it "raises an error if a cookbook name is not provided" do
+ knife.name_args = []
+ expect(knife.ui).to receive(:error).with("Please specify a cookbook to download and install.")
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "raises an error if more than two arguments are given" do
+ knife.name_args = ["foo", "bar", "baz"]
+ expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "raises an error if the second argument is not a version" do
+ knife.name_args = ["getting-started", "1pass"]
+ expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "raises an error if the second argument is a four-digit version" do
+ knife.name_args = ["getting-started", "0.0.0.1"]
+ expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "raises an error if the second argument is a one-digit version" do
+ knife.name_args = ["getting-started", "1"]
+ expect(knife.ui).to receive(:error).with("Installing multiple cookbooks at once is not supported.")
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "installs the specified version if second argument is a three-digit version" do
+ knife.name_args = ["getting-started", "0.1.0"]
+ knife.config[:no_deps] = true
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
+ expect(knife).to receive(:download_cookbook_to).with(upstream_file)
+ expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.1.0")
+ expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
+ expect(repo).to receive(:merge_updates_from).with("getting-started", "0.1.0")
+ knife.run
+ end
+
+ it "installs the specified version if second argument is a two-digit version" do
+ knife.name_args = ["getting-started", "0.1"]
+ knife.config[:no_deps] = true
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
+ expect(knife).to receive(:download_cookbook_to).with(upstream_file)
+ expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.1")
+ expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
+ expect(repo).to receive(:merge_updates_from).with("getting-started", "0.1")
+ knife.run
+ end
+
+ it "installs the latest version if only a cookbook name is given" do
+ knife.name_args = ["getting-started"]
+ knife.config[:no_deps] = true
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
+ expect(knife).to receive(:download_cookbook_to).with(upstream_file)
+ expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.3.0")
+ expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
+ expect(repo).to receive(:merge_updates_from).with("getting-started", "0.3.0")
+ knife.run
+ end
+
+ it "does not create/reset git branches if use_current_branch is set" do
+ knife.name_args = ["getting-started"]
+ knife.config[:use_current_branch] = true
+ knife.config[:no_deps] = true
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
+ expect(repo).not_to receive(:prepare_to_import)
+ expect(repo).not_to receive(:reset_to_default_state)
+ knife.run
end
+
+ it "does not raise an error if cookbook_path is a string" do
+ knife.config[:cookbook_path] = install_path
+ knife.config[:no_deps] = true
+ knife.name_args = ["getting-started"]
+ upstream_file = File.join(install_path, "getting-started.tar.gz")
+ expect(knife).to receive(:download_cookbook_to).with(upstream_file)
+ expect(knife).to receive(:extract_cookbook).with(upstream_file, "0.3.0")
+ expect(knife).to receive(:clear_existing_files).with(File.join(install_path, "getting-started"))
+ expect(repo).to receive(:merge_updates_from).with("getting-started", "0.3.0")
+ expect { knife.run }.not_to raise_error
+ end
+ end # end of run
+
+ let(:metadata) { Chef::Cookbook::Metadata.new }
+ let(:rb_metadata_path) { File.join(install_path, "post-punk-kitchen", "metadata.rb") }
+ let(:json_metadata_path) { File.join(install_path, "post-punk-kitchen", "metadata.json") }
+
+ describe "preferred_metadata" do
+ before do
+ allow(Chef::Cookbook::Metadata).to receive(:new).and_return(metadata)
+ allow(File).to receive(:exist?).and_return(false)
+ knife.instance_variable_set(:@cookbook_name, "post-punk-kitchen")
+ knife.instance_variable_set(:@install_path, install_path)
+ end
+
+ it "returns a populated Metadata object if metadata.rb exists" do
+ allow(File).to receive(:exist?).with(rb_metadata_path).and_return(true)
+ expect(metadata).to receive(:from_file).with(rb_metadata_path)
+ knife.preferred_metadata
+ end
+
+ it "returns a populated Metadata object if metadata.json exists" do
+ allow(File).to receive(:exist?).with(json_metadata_path).and_return(true)
+ #expect(IO).to receive(:read).with(json_metadata_path)
+ allow(IO).to receive(:read)
+ expect(metadata).to receive(:from_json)
+ knife.preferred_metadata
+ end
+
+ it "prefers metadata.rb over metadata.json" do
+ allow(File).to receive(:exist?).with(rb_metadata_path).and_return(true)
+ allow(File).to receive(:exist?).with(json_metadata_path).and_return(true)
+ allow(IO).to receive(:read)
+ expect(metadata).to receive(:from_file).with(rb_metadata_path)
+ expect(metadata).not_to receive(:from_json)
+ knife.preferred_metadata
+ end
+
+ it "rasies an error if it finds no metadata file" do
+ expect { knife.preferred_metadata }.to raise_error(Chef::Exceptions::MetadataNotFound)
+ end
+
end
end
diff --git a/spec/unit/knife/core/bootstrap_context_spec.rb b/spec/unit/knife/core/bootstrap_context_spec.rb
index af8fa3f698..e7d2464fa1 100644
--- a/spec/unit/knife/core/bootstrap_context_spec.rb
+++ b/spec/unit/knife/core/bootstrap_context_spec.rb
@@ -34,6 +34,10 @@ describe Chef::Knife::Core::BootstrapContext do
subject(:bootstrap_context) { described_class.new(config, run_list, chef_config, secret) }
+ it "initializes with Chef 11 parameters" do
+ expect{described_class.new(config, run_list, chef_config)}.not_to raise_error
+ end
+
it "runs chef with the first-boot.json in the _default environment" do
expect(bootstrap_context.start_chef).to eq "chef-client -j /etc/chef/first-boot.json -E _default"
end
diff --git a/spec/unit/knife/ssl_fetch_spec.rb b/spec/unit/knife/ssl_fetch_spec.rb
index 24101dbe7a..cd0e423459 100644
--- a/spec/unit/knife/ssl_fetch_spec.rb
+++ b/spec/unit/knife/ssl_fetch_spec.rb
@@ -130,22 +130,55 @@ E
before do
Chef::Config.trusted_certs_dir = trusted_certs_dir
-
- expect(TCPSocket).to receive(:new).with("foo.example.com", 8443).and_return(tcp_socket)
- expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_fetch.noverify_peer_ssl_context).and_return(ssl_socket)
- expect(ssl_socket).to receive(:connect)
- expect(ssl_socket).to receive(:peer_cert_chain).and_return([self_signed_crt])
end
after do
FileUtils.rm_rf(trusted_certs_dir)
end
- it "fetches the cert chain and writes the certs to the trusted_certs_dir" do
- run
- stored_cert_path = File.join(trusted_certs_dir, "example_local.crt")
- expect(File).to exist(stored_cert_path)
- expect(File.read(stored_cert_path)).to eq(File.read(self_signed_crt_path))
+ context "when the TLS connection is successful" do
+
+ before do
+ expect(TCPSocket).to receive(:new).with("foo.example.com", 8443).and_return(tcp_socket)
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_fetch.noverify_peer_ssl_context).and_return(ssl_socket)
+ expect(ssl_socket).to receive(:connect)
+ expect(ssl_socket).to receive(:peer_cert_chain).and_return([self_signed_crt])
+ end
+
+ it "fetches the cert chain and writes the certs to the trusted_certs_dir" do
+ run
+ stored_cert_path = File.join(trusted_certs_dir, "example_local.crt")
+ expect(File).to exist(stored_cert_path)
+ expect(File.read(stored_cert_path)).to eq(File.read(self_signed_crt_path))
+ end
+
+ end
+
+ context "when connecting to a non-SSL service (like HTTP)" do
+
+ let(:name_args) { %w{http://foo.example.com} }
+
+ let(:unknown_protocol_error) { OpenSSL::SSL::SSLError.new("SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: unknown protocol") }
+
+ before do
+ expect(TCPSocket).to receive(:new).with("foo.example.com", 80).and_return(tcp_socket)
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_fetch.noverify_peer_ssl_context).and_return(ssl_socket)
+ expect(ssl_socket).to receive(:connect).and_raise(unknown_protocol_error)
+
+ expect(ssl_fetch).to receive(:exit).with(1)
+ end
+
+ it "tells the user their URL is for a non-ssl service" do
+ expected_error_text = <<-ERROR_TEXT
+ERROR: The service at the given URI (http://foo.example.com) does not accept SSL connections
+ERROR: Perhaps you meant to connect to 'https://foo.example.com'?
+ERROR_TEXT
+
+ run
+ expect(stderr).to include(expected_error_text)
+ end
+
end
+
end
end
diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb
index 8f652e58c7..2ccf8493ad 100644
--- a/spec/unit/knife_spec.rb
+++ b/spec/unit/knife_spec.rb
@@ -25,6 +25,11 @@ require 'spec_helper'
require 'uri'
describe Chef::Knife do
+
+ let(:stderr) { StringIO.new }
+
+ let(:knife) { Chef::Knife.new }
+
before(:each) do
Chef::Log.logger = Logger.new(StringIO.new)
@@ -32,16 +37,14 @@ describe Chef::Knife do
# Prevent gratuitous code reloading:
allow(Chef::Knife).to receive(:load_commands)
- @knife = Chef::Knife.new
- allow(@knife.ui).to receive(:puts)
- allow(@knife.ui).to receive(:print)
+ allow(knife.ui).to receive(:puts)
+ allow(knife.ui).to receive(:print)
allow(Chef::Log).to receive(:init)
allow(Chef::Log).to receive(:level)
[:debug, :info, :warn, :error, :crit].each do |level_sym|
allow(Chef::Log).to receive(level_sym)
end
allow(Chef::Knife).to receive(:puts)
- @stderr = StringIO.new
end
after(:each) do
@@ -217,7 +220,10 @@ describe Chef::Knife do
end
it "exits if no subcommand matches the CLI args" do
- allow(Chef::Knife.ui).to receive(:stderr).and_return(@stderr)
+ stdout = StringIO.new
+
+ allow(Chef::Knife.ui).to receive(:stderr).and_return(stderr)
+ allow(Chef::Knife.ui).to receive(:stdout).and_return(stdout)
expect(Chef::Knife.ui).to receive(:fatal)
expect {Chef::Knife.run(%w{fuuu uuuu fuuuu})}.to raise_error(SystemExit) { |e| expect(e.status).not_to eq(0) }
end
@@ -269,157 +275,181 @@ describe Chef::Knife do
let(:fake_config) { "/does/not/exist/knife.rb" }
before do
- @knife.config[:verbosity] = 1
- @knife.config[:config_file] = fake_config
+ knife.config[:verbosity] = 1
+ knife.config[:config_file] = fake_config
config_loader = double("Chef::WorkstationConfigLoader", :load => true, :no_config_found? => false, :chef_config_dir => "/etc/chef", :config_location => fake_config)
allow(config_loader).to receive(:explicit_config_file=).with(fake_config).and_return(fake_config)
allow(Chef::WorkstationConfigLoader).to receive(:new).and_return(config_loader)
end
it "prints the path to the configuration file used" do
- @stdout, @stderr, @stdin = StringIO.new, StringIO.new, StringIO.new
- @knife.ui = Chef::Knife::UI.new(@stdout, @stderr, @stdin, {})
+ stdout, stderr, stdin = StringIO.new, StringIO.new, StringIO.new
+ knife.ui = Chef::Knife::UI.new(stdout, stderr, stdin, {})
expect(Chef::Log).to receive(:info).with("Using configuration from #{fake_config}")
- @knife.configure_chef
+ knife.configure_chef
end
end
end
end
describe "when first created" do
+
+ let(:knife) { KnifeSpecs::TestYourself.new(%w{with some args -s scrogramming}) }
+
before do
unless KnifeSpecs.const_defined?(:TestYourself)
Kernel.load(File.join(CHEF_SPEC_DATA, 'knife_subcommand', 'test_yourself.rb'))
end
- @knife = KnifeSpecs::TestYourself.new(%w{with some args -s scrogramming})
end
it "it parses the options passed to it" do
- expect(@knife.config[:scro]).to eq('scrogramming')
+ expect(knife.config[:scro]).to eq('scrogramming')
end
it "extracts its command specific args from the full arg list" do
- expect(@knife.name_args).to eq(%w{with some args})
+ expect(knife.name_args).to eq(%w{with some args})
end
it "does not have lazy dependencies loaded" do
- expect(@knife.class.test_deps_loaded).not_to be_truthy
+ expect(knife.class.test_deps_loaded).not_to be_truthy
end
end
describe "when formatting exceptions" do
+
+ let(:stdout) { StringIO.new }
+ let(:stderr) { StringIO.new }
+ let(:stdin) { StringIO.new }
+
+ let(:ui) { Chef::Knife::UI.new(stdout, stderr, stdin, {}) }
+
before do
- @stdout, @stderr, @stdin = StringIO.new, StringIO.new, StringIO.new
- @knife.ui = Chef::Knife::UI.new(@stdout, @stderr, @stdin, {})
- expect(@knife).to receive(:exit).with(100)
+ knife.ui = ui
+ expect(knife).to receive(:exit).with(100)
end
it "formats 401s nicely" do
response = Net::HTTPUnauthorized.new("1.1", "401", "Unauthorized")
response.instance_variable_set(:@read, true) # I hate you, net/http.
allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "y u no syncronize your clock?"))
- allow(@knife).to receive(:run).and_raise(Net::HTTPServerException.new("401 Unauthorized", response))
- @knife.run_with_pretty_exceptions
- expect(@stderr.string).to match(/ERROR: Failed to authenticate to/)
- expect(@stderr.string).to match(/Response: y u no syncronize your clock\?/)
+ allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("401 Unauthorized", response))
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to match(/ERROR: Failed to authenticate to/)
+ expect(stderr.string).to match(/Response: y u no syncronize your clock\?/)
end
it "formats 403s nicely" do
response = Net::HTTPForbidden.new("1.1", "403", "Forbidden")
response.instance_variable_set(:@read, true) # I hate you, net/http.
allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "y u no administrator"))
- allow(@knife).to receive(:run).and_raise(Net::HTTPServerException.new("403 Forbidden", response))
- allow(@knife).to receive(:username).and_return("sadpanda")
- @knife.run_with_pretty_exceptions
- expect(@stderr.string).to match(%r[ERROR: You authenticated successfully to http.+ as sadpanda but you are not authorized for this action])
- expect(@stderr.string).to match(%r[Response: y u no administrator])
+ allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("403 Forbidden", response))
+ allow(knife).to receive(:username).and_return("sadpanda")
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to match(%r[ERROR: You authenticated successfully to http.+ as sadpanda but you are not authorized for this action])
+ expect(stderr.string).to match(%r[Response: y u no administrator])
end
it "formats 400s nicely" do
response = Net::HTTPBadRequest.new("1.1", "400", "Bad Request")
response.instance_variable_set(:@read, true) # I hate you, net/http.
allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "y u search wrong"))
- allow(@knife).to receive(:run).and_raise(Net::HTTPServerException.new("400 Bad Request", response))
- @knife.run_with_pretty_exceptions
- expect(@stderr.string).to match(%r[ERROR: The data in your request was invalid])
- expect(@stderr.string).to match(%r[Response: y u search wrong])
+ allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("400 Bad Request", response))
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to match(%r[ERROR: The data in your request was invalid])
+ expect(stderr.string).to match(%r[Response: y u search wrong])
end
it "formats 404s nicely" do
response = Net::HTTPNotFound.new("1.1", "404", "Not Found")
response.instance_variable_set(:@read, true) # I hate you, net/http.
allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "nothing to see here"))
- allow(@knife).to receive(:run).and_raise(Net::HTTPServerException.new("404 Not Found", response))
- @knife.run_with_pretty_exceptions
- expect(@stderr.string).to match(%r[ERROR: The object you are looking for could not be found])
- expect(@stderr.string).to match(%r[Response: nothing to see here])
+ allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("404 Not Found", response))
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to match(%r[ERROR: The object you are looking for could not be found])
+ expect(stderr.string).to match(%r[Response: nothing to see here])
end
it "formats 500s nicely" do
response = Net::HTTPInternalServerError.new("1.1", "500", "Internal Server Error")
response.instance_variable_set(:@read, true) # I hate you, net/http.
allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "sad trombone"))
- allow(@knife).to receive(:run).and_raise(Net::HTTPFatalError.new("500 Internal Server Error", response))
- @knife.run_with_pretty_exceptions
- expect(@stderr.string).to match(%r[ERROR: internal server error])
- expect(@stderr.string).to match(%r[Response: sad trombone])
+ allow(knife).to receive(:run).and_raise(Net::HTTPFatalError.new("500 Internal Server Error", response))
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to match(%r[ERROR: internal server error])
+ expect(stderr.string).to match(%r[Response: sad trombone])
end
it "formats 502s nicely" do
response = Net::HTTPBadGateway.new("1.1", "502", "Bad Gateway")
response.instance_variable_set(:@read, true) # I hate you, net/http.
allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "sadder trombone"))
- allow(@knife).to receive(:run).and_raise(Net::HTTPFatalError.new("502 Bad Gateway", response))
- @knife.run_with_pretty_exceptions
- expect(@stderr.string).to match(%r[ERROR: bad gateway])
- expect(@stderr.string).to match(%r[Response: sadder trombone])
+ allow(knife).to receive(:run).and_raise(Net::HTTPFatalError.new("502 Bad Gateway", response))
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to match(%r[ERROR: bad gateway])
+ expect(stderr.string).to match(%r[Response: sadder trombone])
end
it "formats 503s nicely" do
response = Net::HTTPServiceUnavailable.new("1.1", "503", "Service Unavailable")
response.instance_variable_set(:@read, true) # I hate you, net/http.
allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "saddest trombone"))
- allow(@knife).to receive(:run).and_raise(Net::HTTPFatalError.new("503 Service Unavailable", response))
- @knife.run_with_pretty_exceptions
- expect(@stderr.string).to match(%r[ERROR: Service temporarily unavailable])
- expect(@stderr.string).to match(%r[Response: saddest trombone])
+ allow(knife).to receive(:run).and_raise(Net::HTTPFatalError.new("503 Service Unavailable", response))
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to match(%r[ERROR: Service temporarily unavailable])
+ expect(stderr.string).to match(%r[Response: saddest trombone])
end
it "formats other HTTP errors nicely" do
response = Net::HTTPPaymentRequired.new("1.1", "402", "Payment Required")
response.instance_variable_set(:@read, true) # I hate you, net/http.
allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "nobugfixtillyoubuy"))
- allow(@knife).to receive(:run).and_raise(Net::HTTPServerException.new("402 Payment Required", response))
- @knife.run_with_pretty_exceptions
- expect(@stderr.string).to match(%r[ERROR: Payment Required])
- expect(@stderr.string).to match(%r[Response: nobugfixtillyoubuy])
+ allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("402 Payment Required", response))
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to match(%r[ERROR: Payment Required])
+ expect(stderr.string).to match(%r[Response: nobugfixtillyoubuy])
end
it "formats NameError and NoMethodError nicely" do
- allow(@knife).to receive(:run).and_raise(NameError.new("Undefined constant FUUU"))
- @knife.run_with_pretty_exceptions
- expect(@stderr.string).to match(%r[ERROR: knife encountered an unexpected error])
- expect(@stderr.string).to match(%r[This may be a bug in the 'knife' knife command or plugin])
- expect(@stderr.string).to match(%r[Exception: NameError: Undefined constant FUUU])
+ allow(knife).to receive(:run).and_raise(NameError.new("Undefined constant FUUU"))
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to match(%r[ERROR: knife encountered an unexpected error])
+ expect(stderr.string).to match(%r[This may be a bug in the 'knife' knife command or plugin])
+ expect(stderr.string).to match(%r[Exception: NameError: Undefined constant FUUU])
end
it "formats missing private key errors nicely" do
- allow(@knife).to receive(:run).and_raise(Chef::Exceptions::PrivateKeyMissing.new('key not there'))
- allow(@knife).to receive(:api_key).and_return("/home/root/.chef/no-key-here.pem")
- @knife.run_with_pretty_exceptions
- expect(@stderr.string).to match(%r[ERROR: Your private key could not be loaded from /home/root/.chef/no-key-here.pem])
- expect(@stderr.string).to match(%r[Check your configuration file and ensure that your private key is readable])
+ allow(knife).to receive(:run).and_raise(Chef::Exceptions::PrivateKeyMissing.new('key not there'))
+ allow(knife).to receive(:api_key).and_return("/home/root/.chef/no-key-here.pem")
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to match(%r[ERROR: Your private key could not be loaded from /home/root/.chef/no-key-here.pem])
+ expect(stderr.string).to match(%r[Check your configuration file and ensure that your private key is readable])
end
it "formats connection refused errors nicely" do
- allow(@knife).to receive(:run).and_raise(Errno::ECONNREFUSED.new('y u no shut up'))
- @knife.run_with_pretty_exceptions
+ allow(knife).to receive(:run).and_raise(Errno::ECONNREFUSED.new('y u no shut up'))
+ knife.run_with_pretty_exceptions
# Errno::ECONNREFUSED message differs by platform
# *nix = Errno::ECONNREFUSED: Connection refused
# win32: Errno::ECONNREFUSED: No connection could be made because the target machine actively refused it.
- expect(@stderr.string).to match(%r[ERROR: Network Error: .* - y u no shut up])
- expect(@stderr.string).to match(%r[Check your knife configuration and network settings])
+ expect(stderr.string).to match(%r[ERROR: Network Error: .* - y u no shut up])
+ expect(stderr.string).to match(%r[Check your knife configuration and network settings])
end
+
+ it "formats SSL errors nicely and suggests to use `knife ssl check` and `knife ssl fetch`" do
+ error = OpenSSL::SSL::SSLError.new("SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed")
+ allow(knife).to receive(:run).and_raise(error)
+
+ knife.run_with_pretty_exceptions
+
+ expected_message=<<-MSG
+ERROR: Could not establish a secure connection to the server.
+Use `knife ssl check` to troubleshoot your SSL configuration.
+If your Chef Server uses a self-signed certificate, you can use
+`knife ssl fetch` to make knife trust the server's certificates.
+MSG
+ expect(stderr.string).to include(expected_message)
+ end
+
end
end
diff --git a/spec/unit/mixin/shell_out_spec.rb b/spec/unit/mixin/shell_out_spec.rb
index afce4dc826..3dc9d42574 100644
--- a/spec/unit/mixin/shell_out_spec.rb
+++ b/spec/unit/mixin/shell_out_spec.rb
@@ -23,10 +23,10 @@
require 'spec_helper'
describe Chef::Mixin::ShellOut do
- include Chef::Mixin::ShellOut
-
+ let(:shell_out_class) { Class.new { include Chef::Mixin::ShellOut } }
+ subject(:shell_out_obj) { shell_out_class.new }
describe '#run_command_compatible_options' do
- subject { run_command_compatible_options(command_args) }
+ subject { shell_out_obj.run_command_compatible_options(command_args) }
let(:command_args) { [ cmd, options ] }
let(:cmd) { "echo '#{rand(1000)}'" }
@@ -117,7 +117,6 @@ describe Chef::Mixin::ShellOut do
ENV.update(@original_env)
end
- let(:shell_out) { Chef::Mixin::ShellOut }
let(:cmd) { "echo '#{rand(1000)}'" }
describe "#shell_out" do
@@ -126,30 +125,30 @@ describe Chef::Mixin::ShellOut do
describe "and environment is an option" do
it "should not change environment['LC_ALL'] when set to nil" do
options = { :environment => { 'LC_ALL' => nil } }
- expect(shell_out).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out.shell_out(cmd, options)
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out(cmd, options)
end
it "should not change environment['LC_ALL'] when set to non-nil" do
options = { :environment => { 'LC_ALL' => 'en_US.UTF-8' } }
- expect(shell_out).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out.shell_out(cmd, options)
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out(cmd, options)
end
it "should set environment['LC_ALL'] to 'en_US.UTF-8' when 'LC_ALL' not present" do
options = { :environment => { 'HOME' => '/Users/morty' } }
- expect(shell_out).to receive(:shell_out_command).with(cmd, {
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, {
:environment => { 'HOME' => '/Users/morty', 'LC_ALL' => Chef::Config[:internal_locale] },
}).and_return(true)
- shell_out.shell_out(cmd, options)
+ shell_out_obj.shell_out(cmd, options)
end
it "should not mutate the options hash when it adds LC_ALL" do
options = { :environment => { 'HOME' => '/Users/morty' } }
- expect(shell_out).to receive(:shell_out_command).with(cmd, {
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, {
:environment => { 'HOME' => '/Users/morty', 'LC_ALL' => Chef::Config[:internal_locale] },
}).and_return(true)
- shell_out.shell_out(cmd, options)
+ shell_out_obj.shell_out(cmd, options)
expect(options[:environment].has_key?('LC_ALL')).to be false
end
end
@@ -157,30 +156,30 @@ describe Chef::Mixin::ShellOut do
describe "and env is an option" do
it "should not change env when set to nil" do
options = { :env => { 'LC_ALL' => nil } }
- expect(shell_out).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out.shell_out(cmd, options)
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out(cmd, options)
end
it "should not change env when set to non-nil" do
options = { :env => { 'LC_ALL' => 'de_DE.UTF-8'}}
- expect(shell_out).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out.shell_out(cmd, options)
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out(cmd, options)
end
it "should set env['LC_ALL'] to 'en_US.UTF-8' when 'LC_ALL' not present" do
options = { :env => { 'HOME' => '/Users/morty' } }
- expect(shell_out).to receive(:shell_out_command).with(cmd, {
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, {
:env => { 'HOME' => '/Users/morty', 'LC_ALL' => Chef::Config[:internal_locale] },
}).and_return(true)
- shell_out.shell_out(cmd, options)
+ shell_out_obj.shell_out(cmd, options)
end
it "should not mutate the options hash when it adds LC_ALL" do
options = { :env => { 'HOME' => '/Users/morty' } }
- expect(shell_out).to receive(:shell_out_command).with(cmd, {
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, {
:env => { 'HOME' => '/Users/morty', 'LC_ALL' => Chef::Config[:internal_locale] },
}).and_return(true)
- shell_out.shell_out(cmd, options)
+ shell_out_obj.shell_out(cmd, options)
expect(options[:env].has_key?('LC_ALL')).to be false
end
end
@@ -188,20 +187,20 @@ describe Chef::Mixin::ShellOut do
describe "and no env/environment option is present" do
it "should add environment option and set environment['LC_ALL'] to 'en_US.UTF_8'" do
options = { :user => 'morty' }
- expect(shell_out).to receive(:shell_out_command).with(cmd, {
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, {
:user => 'morty', :environment => { 'LC_ALL' => Chef::Config[:internal_locale] },
}).and_return(true)
- shell_out.shell_out(cmd, options)
+ shell_out_obj.shell_out(cmd, options)
end
end
end
describe "when the last argument is not a Hash" do
it "should add environment options and set environment['LC_ALL'] to 'en_US.UTF-8'" do
- expect(shell_out).to receive(:shell_out_command).with(cmd, {
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, {
:environment => { 'LC_ALL' => Chef::Config[:internal_locale] },
}).and_return(true)
- shell_out.shell_out(cmd)
+ shell_out_obj.shell_out(cmd)
end
end
@@ -213,56 +212,56 @@ describe Chef::Mixin::ShellOut do
describe "and environment is an option" do
it "should not change environment['LC_ALL'] when set to nil" do
options = { :environment => { 'LC_ALL' => nil } }
- expect(shell_out).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out.shell_out_with_systems_locale(cmd, options)
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out_with_systems_locale(cmd, options)
end
it "should not change environment['LC_ALL'] when set to non-nil" do
options = { :environment => { 'LC_ALL' => 'en_US.UTF-8' } }
- expect(shell_out).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out.shell_out_with_systems_locale(cmd, options)
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out_with_systems_locale(cmd, options)
end
it "should no longer set environment['LC_ALL'] to nil when 'LC_ALL' not present" do
options = { :environment => { 'HOME' => '/Users/morty' } }
- expect(shell_out).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out.shell_out_with_systems_locale(cmd, options)
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out_with_systems_locale(cmd, options)
end
end
describe "and env is an option" do
it "should not change env when set to nil" do
options = { :env => { 'LC_ALL' => nil } }
- expect(shell_out).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out.shell_out_with_systems_locale(cmd, options)
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out_with_systems_locale(cmd, options)
end
it "should not change env when set to non-nil" do
options = { :env => { 'LC_ALL' => 'en_US.UTF-8'}}
- expect(shell_out).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out.shell_out_with_systems_locale(cmd, options)
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out_with_systems_locale(cmd, options)
end
it "should no longer set env['LC_ALL'] to nil when 'LC_ALL' not present" do
options = { :env => { 'HOME' => '/Users/morty' } }
- expect(shell_out).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out.shell_out_with_systems_locale(cmd, options)
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out_with_systems_locale(cmd, options)
end
end
describe "and no env/environment option is present" do
it "should no longer add environment option and set environment['LC_ALL'] to nil" do
options = { :user => 'morty' }
- expect(shell_out).to receive(:shell_out_command).with(cmd, options).and_return(true)
- shell_out.shell_out_with_systems_locale(cmd, options)
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd, options).and_return(true)
+ shell_out_obj.shell_out_with_systems_locale(cmd, options)
end
end
end
describe "when the last argument is not a Hash" do
it "should no longer add environment options and set environment['LC_ALL'] to nil" do
- expect(shell_out).to receive(:shell_out_command).with(cmd).and_return(true)
- shell_out.shell_out_with_systems_locale(cmd)
+ expect(shell_out_obj).to receive(:shell_out_command).with(cmd).and_return(true)
+ shell_out_obj.shell_out_with_systems_locale(cmd)
end
end
end
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index 1daaf9ec52..0bc76db272 100644
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -1213,6 +1213,40 @@ describe Chef::Node do
node.save
end
+ it "should save false-y whitelisted attributes" do
+ Chef::Config[:default_attribute_whitelist] = [
+ "foo/bar/baz"
+ ]
+
+ data = {
+ "default" => {
+ "foo" => {
+ "bar" => {
+ "baz" => false,
+ },
+ "other" => {
+ "stuff" => true,
+ }
+ }
+ }
+ }
+
+ selected_data = {
+ "default" => {
+ "foo" => {
+ "bar" => {
+ "baz" => false,
+ }
+ }
+ }
+ }
+
+ node.name("falsey-monkey")
+ allow(node).to receive(:for_json).and_return(data)
+ expect(@rest).to receive(:put_rest).with("nodes/falsey-monkey", selected_data).and_return("foo")
+ node.save
+ end
+
it "should not save any attributes if the whitelist is empty" do
Chef::Config[:automatic_attribute_whitelist] = []
diff --git a/spec/unit/provider/execute_spec.rb b/spec/unit/provider/execute_spec.rb
index ea16d8cf54..794994533e 100644
--- a/spec/unit/provider/execute_spec.rb
+++ b/spec/unit/provider/execute_spec.rb
@@ -15,91 +15,161 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "spec_helper"))
-#require 'spec_helper'
+
+require 'spec_helper'
describe Chef::Provider::Execute do
- before do
- @node = Chef::Node.new
- @cookbook_collection = Chef::CookbookCollection.new([])
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events)
- @new_resource = Chef::Resource::Execute.new("foo_resource", @run_context)
- @new_resource.timeout 3600
- @new_resource.returns 0
- @new_resource.creates "/foo_resource"
- @provider = Chef::Provider::Execute.new(@new_resource, @run_context)
- @current_resource = Chef::Resource::Ifconfig.new("foo_resource", @run_context)
- @provider.current_resource = @current_resource
- Chef::Log.level = :info
- # FIXME: There should be a test for how STDOUT.tty? changes the live_stream option being passed
- allow(STDOUT).to receive(:tty?).and_return(true)
- end
+
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:provider) { Chef::Provider::Execute.new(new_resource, run_context) }
+ let(:current_resource) { Chef::Resource::Ifconfig.new("foo_resource", run_context) }
let(:opts) do
{
- timeout: @new_resource.timeout,
- returns: @new_resource.returns,
- log_level: :info,
- log_tag: @new_resource.to_s,
- live_stream: STDOUT
+ timeout: 3600,
+ returns: 0,
+ log_level: :info,
+ log_tag: new_resource.to_s,
+ live_stream: STDOUT,
}
end
- it "should execute foo_resource" do
- allow(@provider).to receive(:load_current_resource)
- expect(@provider).to receive(:shell_out!).with(@new_resource.command, opts)
- expect(@provider).to receive(:converge_by).with("execute foo_resource").and_call_original
- expect(Chef::Log).not_to receive(:warn)
+ let(:new_resource) { Chef::Resource::Execute.new("foo_resource", run_context) }
- @provider.run_action(:run)
- expect(@new_resource).to be_updated
+ before do
+ @original_log_level = Chef::Log.level
+ Chef::Log.level = :info
+ allow(STDOUT).to receive(:tty?).and_return(true)
end
- it "should honor sensitive attribute" do
- @new_resource.sensitive true
- @provider = Chef::Provider::Execute.new(@new_resource, @run_context)
- allow(@provider).to receive(:load_current_resource)
- # Since the resource is sensitive, it should not have :live_stream set
- expect(@provider).to receive(:shell_out!).with(@new_resource.command, opts.reject { |k| k == :live_stream })
- expect(Chef::Log).not_to receive(:warn)
- expect(@provider).to receive(:converge_by).with("execute sensitive resource").and_call_original
- @provider.run_action(:run)
- expect(@new_resource).to be_updated
+ after do
+ Chef::Log.level = @original_log_level
end
- it "should do nothing if the sentinel file exists" do
- allow(@provider).to receive(:load_current_resource)
- expect(File).to receive(:exists?).with(@new_resource.creates).and_return(true)
- expect(@provider).not_to receive(:shell_out!)
- expect(Chef::Log).not_to receive(:warn)
-
- @provider.run_action(:run)
- expect(@new_resource).not_to be_updated
+ describe "#initialize" do
+ it "should return a Chef::Provider::Execute provider" do
+ expect(provider.class).to eql(Chef::Provider::Execute)
+ end
end
- it "should respect cwd options for 'creates'" do
- @new_resource.cwd "/tmp"
- @new_resource.creates "foo_resource"
- allow(@provider).to receive(:load_current_resource)
- expect(File).to receive(:exists?).with(@new_resource.creates).and_return(false)
- expect(File).to receive(:exists?).with(File.join("/tmp", @new_resource.creates)).and_return(true)
- expect(Chef::Log).not_to receive(:warn)
- expect(@provider).not_to receive(:shell_out!)
-
- @provider.run_action(:run)
- expect(@new_resource).not_to be_updated
+ describe "#load_current_resource" do
+ before do
+ expect(Chef::Resource::Execute).to receive(:new).with(new_resource.name).and_return(current_resource)
+ end
+
+ it "should return the current resource" do
+ expect(provider.load_current_resource).to eql(current_resource)
+ end
+
+ it "our timeout should default to 3600" do
+ provider.load_current_resource
+ expect(provider.timeout).to eql(3600)
+ end
end
- it "should warn if user specified relative path without cwd" do
- @new_resource.creates "foo_resource"
- allow(@provider).to receive(:load_current_resource)
- expect(Chef::Log).to receive(:warn).with(/relative path/)
- expect(File).to receive(:exists?).with(@new_resource.creates).and_return(true)
- expect(@provider).not_to receive(:shell_out!)
+ describe "#action_run" do
+ it "runs shell_out with the default options" do
+ expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
+ expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
+ expect(Chef::Log).not_to receive(:warn)
+ provider.run_action(:run)
+ expect(new_resource).to be_updated
+ end
- @provider.run_action(:run)
- expect(@new_resource).not_to be_updated
+ it "if you pass a command attribute, it runs the command" do
+ new_resource.command "/usr/argelbargle/bin/oogachacka 12345"
+ expect(provider).to receive(:shell_out!).with(new_resource.command, opts)
+ expect(provider).to receive(:converge_by).with("execute #{new_resource.command}").and_call_original
+ expect(Chef::Log).not_to receive(:warn)
+ provider.run_action(:run)
+ expect(new_resource).to be_updated
+ end
+
+ it "should honor sensitive attribute" do
+ new_resource.sensitive true
+ # Since the resource is sensitive, it should not have :live_stream set
+ opts.delete(:live_stream)
+ expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
+ expect(provider).to receive(:converge_by).with("execute sensitive resource").and_call_original
+ expect(Chef::Log).not_to receive(:warn)
+ provider.run_action(:run)
+ expect(new_resource).to be_updated
+ end
+
+ it "should do nothing if the sentinel file exists" do
+ new_resource.creates "/foo_resource"
+ expect(FileTest).to receive(:exist?).with(new_resource.creates).and_return(true)
+ expect(provider).not_to receive(:shell_out!)
+ expect(Chef::Log).not_to receive(:warn)
+ provider.run_action(:run)
+ expect(new_resource).not_to be_updated
+ end
+
+ describe "when the user specifies a relative path without cwd" do
+ before do
+ expect(new_resource.cwd).to be_falsey
+ new_resource.creates "foo_resource"
+ end
+
+ it "should warn in Chef-12", :chef_lt_13_only do
+ expect(Chef::Log).to receive(:warn).with(/relative path/)
+ expect(FileTest).to receive(:exist?).with(new_resource.creates).and_return(true)
+ expect(provider).not_to receive(:shell_out!)
+ provider.run_action(:run)
+ expect(new_resource).not_to be_updated
+ end
+
+ it "should raise if user specified relative path without cwd for Chef-13", :chef_gte_13_only do
+ expect(Chef::Log).to receive(:warn).with(/relative path/)
+ expect(FileTest).to receive(:exist?).with(new_resource.creates).and_return(true)
+ expect(provider).not_to receive(:shell_out!)
+ expect { provider.run_action(:run) }.to raise_error # @todo: add a real error for Chef-13
+ end
+ end
+
+ it "should respect cwd options for 'creates'" do
+ new_resource.cwd "/tmp"
+ new_resource.creates "foo_resource"
+ expect(FileTest).not_to receive(:exist?).with(new_resource.creates)
+ expect(FileTest).to receive(:exist?).with(File.join("/tmp", new_resource.creates)).and_return(true)
+ expect(Chef::Log).not_to receive(:warn)
+ expect(provider).not_to receive(:shell_out!)
+
+ provider.run_action(:run)
+ expect(new_resource).not_to be_updated
+ end
+
+ it "should unset the live_stream if STDOUT is not a tty" do
+ expect(STDOUT).to receive(:tty?).and_return(false)
+ opts.delete(:live_stream)
+ expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
+ expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
+ expect(Chef::Log).not_to receive(:warn)
+ provider.run_action(:run)
+ expect(new_resource).to be_updated
+ end
+
+ it "should unset the live_stream if chef is running as a daemon" do
+ allow(Chef::Config).to receive(:[]).and_call_original
+ expect(Chef::Config).to receive(:[]).with(:daemon).and_return(true)
+ opts.delete(:live_stream)
+ expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
+ expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
+ expect(Chef::Log).not_to receive(:warn)
+ provider.run_action(:run)
+ expect(new_resource).to be_updated
+ end
+
+ it "should unset the live_stream if we are not running with a log level of at least :info" do
+ expect(Chef::Log).to receive(:info?).and_return(false)
+ opts.delete(:live_stream)
+ expect(provider).to receive(:shell_out!).with(new_resource.name, opts)
+ expect(provider).to receive(:converge_by).with("execute foo_resource").and_call_original
+ expect(Chef::Log).not_to receive(:warn)
+ provider.run_action(:run)
+ expect(new_resource).to be_updated
+ end
end
end
-
diff --git a/spec/unit/provider/git_spec.rb b/spec/unit/provider/git_spec.rb
index 1e282c098d..0106244665 100644
--- a/spec/unit/provider/git_spec.rb
+++ b/spec/unit/provider/git_spec.rb
@@ -22,6 +22,7 @@ describe Chef::Provider::Git do
before(:each) do
allow(STDOUT).to receive(:tty?).and_return(true)
+ @original_log_level = Chef::Log.level
Chef::Log.level = :info
@current_resource = Chef::Resource::Git.new("web2.0 app")
@@ -38,6 +39,10 @@ describe Chef::Provider::Git do
@provider.current_resource = @current_resource
end
+ after(:each) do
+ Chef::Log.level = @original_log_level
+ end
+
context "determining the revision of the currently deployed checkout" do
before do
diff --git a/spec/unit/provider/link_spec.rb b/spec/unit/provider/link_spec.rb
index ab5b439eac..0f95ce997e 100644
--- a/spec/unit/provider/link_spec.rb
+++ b/spec/unit/provider/link_spec.rb
@@ -38,8 +38,8 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
result
end
- def paths_eql?(path1, path2)
- Chef::Util::PathHelper.paths_eql?(path1, path2)
+ def canonicalize(path)
+ Chef::Platform.windows? ? path.gsub('/', '\\') : path
end
describe "when the target is a symlink" do
@@ -68,7 +68,7 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
expect(provider.current_resource.link_type).to eq(:symbolic)
end
it "should update the source of the existing link with the links target" do
- expect(paths_eql?(provider.current_resource.to, "#{CHEF_SPEC_DATA}/fofile")).to be_truthy
+ expect(provider.current_resource.to).to eq(canonicalize("#{CHEF_SPEC_DATA}/fofile"))
end
it "should set the owner" do
expect(provider.current_resource.owner).to eq(501)
@@ -110,7 +110,7 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
expect(provider.current_resource.link_type).to eq(:symbolic)
end
it "should update the source of the existing link to the link's target" do
- expect(paths_eql?(provider.current_resource.to, "#{CHEF_SPEC_DATA}/fofile")).to be_truthy
+ expect(provider.current_resource.to).to eq(canonicalize("#{CHEF_SPEC_DATA}/fofile"))
end
it "should not set the owner" do
expect(provider.current_resource.owner).to be_nil
@@ -221,7 +221,7 @@ describe Chef::Resource::Link, :not_supported_on_win2k3 do
expect(provider.current_resource.link_type).to eq(:hard)
end
it "should update the source of the existing link to the link's target" do
- expect(paths_eql?(provider.current_resource.to, "#{CHEF_SPEC_DATA}/fofile")).to be_truthy
+ expect(provider.current_resource.to).to eq(canonicalize("#{CHEF_SPEC_DATA}/fofile"))
end
it "should not set the owner" do
expect(provider.current_resource.owner).to eq(nil)
diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb
index edca3e4c22..e53fdc3f27 100644
--- a/spec/unit/provider/package/apt_spec.rb
+++ b/spec/unit/provider/package/apt_spec.rb
@@ -20,46 +20,51 @@ require 'spec_helper'
require 'ostruct'
describe Chef::Provider::Package::Apt do
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Package.new("irssi", @run_context)
-
- @status = double("Status", :exitstatus => 0)
- @provider = Chef::Provider::Package::Apt.new(@new_resource, @run_context)
- @stdin = StringIO.new
- @stdout =<<-PKG_STATUS
+ # XXX: sorry this is ugly and was done quickly to get 12.0.2 out, this file needs a rewrite to use
+ # let blocks and shared examples
+ [ Chef::Resource::Package, Chef::Resource::AptPackage ].each do |resource_klass|
+ describe "when the new_resource is a #{resource_klass}" do
+
+ before(:each) do
+ @node = Chef::Node.new
+ @events = Chef::EventDispatch::Dispatcher.new
+ @run_context = Chef::RunContext.new(@node, {}, @events)
+ @new_resource = resource_klass.new("irssi", @run_context)
+
+ @status = double("Status", :exitstatus => 0)
+ @provider = Chef::Provider::Package::Apt.new(@new_resource, @run_context)
+ @stdin = StringIO.new
+ @stdout =<<-PKG_STATUS
irssi:
Installed: (none)
Candidate: 0.8.14-1ubuntu4
Version table:
0.8.14-1ubuntu4 0
500 http://us.archive.ubuntu.com/ubuntu/ lucid/main Packages
-PKG_STATUS
- @stderr = ""
- @shell_out = OpenStruct.new(:stdout => @stdout,:stdin => @stdin,:stderr => @stderr,:status => @status,:exitstatus => 0)
- @timeout = 900
- end
-
- describe "when loading current resource" do
-
- it "should create a current resource with the name of the new_resource" do
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache policy #{@new_resource.package_name}",
- :timeout => @timeout
- ).and_return(@shell_out)
- @provider.load_current_resource
+ PKG_STATUS
+ @stderr = ""
+ @shell_out = OpenStruct.new(:stdout => @stdout,:stdin => @stdin,:stderr => @stderr,:status => @status,:exitstatus => 0)
+ @timeout = 900
+ end
- current_resource = @provider.current_resource
- expect(current_resource).to be_a(Chef::Resource::Package)
- expect(current_resource.name).to eq("irssi")
- expect(current_resource.package_name).to eq("irssi")
- expect(current_resource.version).to be_nil
- end
+ describe "when loading current resource" do
- it "should set the installed version if package has one" do
- @stdout.replace(<<-INSTALLED)
+ it "should create a current resource with the name of the new_resource" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache policy #{@new_resource.package_name}",
+ :timeout => @timeout
+ ).and_return(@shell_out)
+ @provider.load_current_resource
+
+ current_resource = @provider.current_resource
+ expect(current_resource).to be_a(Chef::Resource::Package)
+ expect(current_resource.name).to eq("irssi")
+ expect(current_resource.package_name).to eq("irssi")
+ expect(current_resource.version).to be_nil
+ end
+
+ it "should set the installed version if package has one" do
+ @stdout.replace(<<-INSTALLED)
sudo:
Installed: 1.7.2p1-1ubuntu5.3
Candidate: 1.7.2p1-1ubuntu5.3
@@ -70,29 +75,29 @@ sudo:
100 /var/lib/dpkg/status
1.7.2p1-1ubuntu5 0
500 http://us.archive.ubuntu.com/ubuntu/ lucid/main Packages
-INSTALLED
- expect(@provider).to receive(:shell_out!).and_return(@shell_out)
- @provider.load_current_resource
- expect(@provider.current_resource.version).to eq("1.7.2p1-1ubuntu5.3")
- expect(@provider.candidate_version).to eql("1.7.2p1-1ubuntu5.3")
- end
-
- # libmysqlclient-dev is a real package in newer versions of debian + ubuntu
- # list of virtual packages: http://www.debian.org/doc/packaging-manuals/virtual-package-names-list.txt
- it "should not install the virtual package there is a single provider package and it is installed" do
- @new_resource.package_name("libmysqlclient15-dev")
- virtual_package_out=<<-VPKG_STDOUT
+ INSTALLED
+ expect(@provider).to receive(:shell_out!).and_return(@shell_out)
+ @provider.load_current_resource
+ expect(@provider.current_resource.version).to eq("1.7.2p1-1ubuntu5.3")
+ expect(@provider.candidate_version).to eql("1.7.2p1-1ubuntu5.3")
+ end
+
+ # libmysqlclient-dev is a real package in newer versions of debian + ubuntu
+ # list of virtual packages: http://www.debian.org/doc/packaging-manuals/virtual-package-names-list.txt
+ it "should not install the virtual package there is a single provider package and it is installed" do
+ @new_resource.package_name("libmysqlclient15-dev")
+ virtual_package_out=<<-VPKG_STDOUT
libmysqlclient15-dev:
Installed: (none)
Candidate: (none)
Version table:
-VPKG_STDOUT
- virtual_package = double(:stdout => virtual_package_out,:exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache policy libmysqlclient15-dev",
- :timeout => @timeout
- ).and_return(virtual_package)
- showpkg_out =<<-SHOWPKG_STDOUT
+ VPKG_STDOUT
+ virtual_package = double(:stdout => virtual_package_out,:exitstatus => 0)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache policy libmysqlclient15-dev",
+ :timeout => @timeout
+ ).and_return(virtual_package)
+ showpkg_out =<<-SHOWPKG_STDOUT
Package: libmysqlclient15-dev
Versions:
@@ -109,13 +114,13 @@ Reverse Provides:
libmysqlclient-dev 5.1.41-3ubuntu12.7
libmysqlclient-dev 5.1.41-3ubuntu12.10
libmysqlclient-dev 5.1.41-3ubuntu12
-SHOWPKG_STDOUT
- showpkg = double(:stdout => showpkg_out,:exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache showpkg libmysqlclient15-dev",
- :timeout => @timeout
- ).and_return(showpkg)
- real_package_out=<<-RPKG_STDOUT
+ SHOWPKG_STDOUT
+ showpkg = double(:stdout => showpkg_out,:exitstatus => 0)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache showpkg libmysqlclient15-dev",
+ :timeout => @timeout
+ ).and_return(showpkg)
+ real_package_out=<<-RPKG_STDOUT
libmysqlclient-dev:
Installed: 5.1.41-3ubuntu12.10
Candidate: 5.1.41-3ubuntu12.10
@@ -127,29 +132,29 @@ libmysqlclient-dev:
500 http://security.ubuntu.com/ubuntu/ lucid-security/main Packages
5.1.41-3ubuntu12 0
500 http://us.archive.ubuntu.com/ubuntu/ lucid/main Packages
-RPKG_STDOUT
- real_package = double(:stdout => real_package_out,:exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache policy libmysqlclient-dev",
- :timeout => @timeout
- ).and_return(real_package)
- @provider.load_current_resource
- end
+ RPKG_STDOUT
+ real_package = double(:stdout => real_package_out,:exitstatus => 0)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache policy libmysqlclient-dev",
+ :timeout => @timeout
+ ).and_return(real_package)
+ @provider.load_current_resource
+ end
- it "should raise an exception if you specify a virtual package with multiple provider packages" do
- @new_resource.package_name("mp3-decoder")
- virtual_package_out=<<-VPKG_STDOUT
+ it "should raise an exception if you specify a virtual package with multiple provider packages" do
+ @new_resource.package_name("mp3-decoder")
+ virtual_package_out=<<-VPKG_STDOUT
mp3-decoder:
Installed: (none)
Candidate: (none)
Version table:
-VPKG_STDOUT
- virtual_package = double(:stdout => virtual_package_out,:exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache policy mp3-decoder",
- :timeout => @timeout
- ).and_return(virtual_package)
- showpkg_out=<<-SHOWPKG_STDOUT
+ VPKG_STDOUT
+ virtual_package = double(:stdout => virtual_package_out,:exitstatus => 0)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache policy mp3-decoder",
+ :timeout => @timeout
+ ).and_return(virtual_package)
+ showpkg_out=<<-SHOWPKG_STDOUT
Package: mp3-decoder
Versions:
@@ -169,191 +174,193 @@ vlc 1.0.6-1ubuntu1
opencubicplayer 1:0.1.17-2
mpg321 0.2.10.6
mpg123 1.12.1-0ubuntu1
-SHOWPKG_STDOUT
- showpkg = double(:stdout => showpkg_out,:exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache showpkg mp3-decoder",
- :timeout => @timeout
- ).and_return(showpkg)
- expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
- end
-
- it "should run apt-cache policy with the default_release option, if there is one and provider is explicitly defined" do
- @new_resource = Chef::Resource::AptPackage.new("irssi", @run_context)
- @provider = Chef::Provider::Package::Apt.new(@new_resource, @run_context)
-
- allow(@new_resource).to receive(:default_release).and_return("lenny-backports")
- allow(@new_resource).to receive(:provider).and_return("Chef::Provider::Package::Apt")
- expect(@provider).to receive(:shell_out!).with(
- "apt-cache -o APT::Default-Release=lenny-backports policy irssi",
- :timeout => @timeout
- ).and_return(@shell_out)
- @provider.load_current_resource
- end
-
- it "raises an exception if a source is specified (CHEF-5113)" do
- @new_resource.source "pluto"
- @provider.define_resource_requirements
- expect(@provider).to receive(:shell_out!).with("apt-cache policy irssi", {:timeout=>900}).and_return(@shell_out)
- expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
- end
- end
-
- context "after loading the current resource" do
- before do
- @current_resource = Chef::Resource::Package.new("irssi", @run_context)
- @provider.current_resource = @current_resource
- end
-
- describe "install_package" do
- it "should run apt-get install with the package name and version" do
- expect(@provider).to receive(:shell_out!). with(
- "apt-get -q -y install irssi=0.8.12-7",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil},
- :timeout => @timeout
- )
- @provider.install_package("irssi", "0.8.12-7")
- end
-
- it "should run apt-get install with the package name and version and options if specified" do
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y --force-yes install irssi=0.8.12-7",
- :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @new_resource.options("--force-yes")
- @provider.install_package("irssi", "0.8.12-7")
- end
-
- it "should run apt-get install with the package name and version and default_release if there is one and provider is explicitly defined" do
- @new_resource = nil
- @new_resource = Chef::Resource::AptPackage.new("irssi", @run_context)
- @new_resource.default_release("lenny-backports")
- @new_resource.provider = @provider
- @provider.new_resource = @new_resource
-
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y -o APT::Default-Release=lenny-backports install irssi=0.8.12-7",
- :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
-
- @provider.install_package("irssi", "0.8.12-7")
- end
- end
-
- describe Chef::Provider::Package::Apt, "upgrade_package" do
-
- it "should run install_package with the name and version" do
- expect(@provider).to receive(:install_package).with("irssi", "0.8.12-7")
- @provider.upgrade_package("irssi", "0.8.12-7")
- end
- end
-
- describe Chef::Provider::Package::Apt, "remove_package" do
-
- it "should run apt-get remove with the package name" do
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y remove irssi",
- :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil},
- :timeout => @timeout
- )
- @provider.remove_package("irssi", "0.8.12-7")
- end
-
- it "should run apt-get remove with the package name and options if specified" do
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y --force-yes remove irssi",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @new_resource.options("--force-yes")
-
- @provider.remove_package("irssi", "0.8.12-7")
- end
- end
-
- describe "when purging a package" do
-
- it "should run apt-get purge with the package name" do
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y purge irssi",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @provider.purge_package("irssi", "0.8.12-7")
- end
-
- it "should run apt-get purge with the package name and options if specified" do
- expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y --force-yes purge irssi",
- :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @new_resource.options("--force-yes")
-
- @provider.purge_package("irssi", "0.8.12-7")
- end
- end
-
- describe "when preseeding a package" do
- before(:each) do
- allow(@provider).to receive(:get_preseed_file).and_return("/tmp/irssi-0.8.12-7.seed")
- end
-
- it "should get the full path to the preseed response file" do
- expect(@provider).to receive(:get_preseed_file).with("irssi", "0.8.12-7").and_return("/tmp/irssi-0.8.12-7.seed")
- file = @provider.get_preseed_file("irssi", "0.8.12-7")
-
- expect(@provider).to receive(:shell_out!).with(
- "debconf-set-selections /tmp/irssi-0.8.12-7.seed",
- :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil},
- :timeout => @timeout
- )
-
- @provider.preseed_package(file)
- end
-
- it "should run debconf-set-selections on the preseed file if it has changed" do
- expect(@provider).to receive(:shell_out!).with(
- "debconf-set-selections /tmp/irssi-0.8.12-7.seed",
- :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil},
- :timeout => @timeout
- )
- file = @provider.get_preseed_file("irssi", "0.8.12-7")
- @provider.preseed_package(file)
- end
+ SHOWPKG_STDOUT
+ showpkg = double(:stdout => showpkg_out,:exitstatus => 0)
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-cache showpkg mp3-decoder",
+ :timeout => @timeout
+ ).and_return(showpkg)
+ expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
+ end
- it "should not run debconf-set-selections if the preseed file has not changed" do
- allow(@provider).to receive(:check_package_state)
- @current_resource.version "0.8.11"
- @new_resource.response_file "/tmp/file"
- allow(@provider).to receive(:get_preseed_file).and_return(false)
- expect(@provider).not_to receive(:shell_out!)
- @provider.run_action(:reconfig)
- end
- end
+ it "should run apt-cache policy with the default_release option, if there is one on the resource" do
+ @new_resource = Chef::Resource::AptPackage.new("irssi", @run_context)
+ @provider = Chef::Provider::Package::Apt.new(@new_resource, @run_context)
- describe "when reconfiguring a package" do
- it "should run dpkg-reconfigure package" do
- expect(@provider).to receive(:shell_out!).with(
- "dpkg-reconfigure irssi",
- :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
- :timeout => @timeout
- )
- @provider.reconfig_package("irssi", "0.8.12-7")
- end
- end
-
- describe "when installing a virtual package" do
- it "should install the package without specifying a version" do
- @provider.is_virtual_package = true
+ allow(@new_resource).to receive(:default_release).and_return("lenny-backports")
+ allow(@new_resource).to receive(:provider).and_return(nil)
expect(@provider).to receive(:shell_out!).with(
- "apt-get -q -y install libmysqlclient-dev",
- :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
+ "apt-cache -o APT::Default-Release=lenny-backports policy irssi",
:timeout => @timeout
- )
- @provider.install_package("libmysqlclient-dev", "not_a_real_version")
+ ).and_return(@shell_out)
+ @provider.load_current_resource
+ end
+
+ it "raises an exception if a source is specified (CHEF-5113)" do
+ @new_resource.source "pluto"
+ @provider.define_resource_requirements
+ expect(@provider).to receive(:shell_out!).with("apt-cache policy irssi", {:timeout=>900}).and_return(@shell_out)
+ expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
+ end
+ end
+
+ context "after loading the current resource" do
+ before do
+ @current_resource = resource_klass.new("irssi", @run_context)
+ @provider.current_resource = @current_resource
+ end
+
+ describe "install_package" do
+ it "should run apt-get install with the package name and version" do
+ expect(@provider).to receive(:shell_out!). with(
+ "apt-get -q -y install irssi=0.8.12-7",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil},
+ :timeout => @timeout
+ )
+ @provider.install_package("irssi", "0.8.12-7")
+ end
+
+ it "should run apt-get install with the package name and version and options if specified" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get -q -y --force-yes install irssi=0.8.12-7",
+ :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
+ :timeout => @timeout
+ )
+ @new_resource.options("--force-yes")
+ @provider.install_package("irssi", "0.8.12-7")
+ end
+
+ it "should run apt-get install with the package name and version and default_release if there is one and provider is explicitly defined" do
+ @new_resource = nil
+ @new_resource = Chef::Resource::AptPackage.new("irssi", @run_context)
+ @new_resource.default_release("lenny-backports")
+ @new_resource.provider = nil
+ @provider.new_resource = @new_resource
+
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get -q -y -o APT::Default-Release=lenny-backports install irssi=0.8.12-7",
+ :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
+ :timeout => @timeout
+ )
+
+ @provider.install_package("irssi", "0.8.12-7")
+ end
+ end
+
+ describe resource_klass, "upgrade_package" do
+
+ it "should run install_package with the name and version" do
+ expect(@provider).to receive(:install_package).with("irssi", "0.8.12-7")
+ @provider.upgrade_package("irssi", "0.8.12-7")
+ end
+ end
+
+ describe resource_klass, "remove_package" do
+
+ it "should run apt-get remove with the package name" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get -q -y remove irssi",
+ :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil},
+ :timeout => @timeout
+ )
+ @provider.remove_package("irssi", "0.8.12-7")
+ end
+
+ it "should run apt-get remove with the package name and options if specified" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get -q -y --force-yes remove irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
+ :timeout => @timeout
+ )
+ @new_resource.options("--force-yes")
+
+ @provider.remove_package("irssi", "0.8.12-7")
+ end
+ end
+
+ describe "when purging a package" do
+
+ it "should run apt-get purge with the package name" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get -q -y purge irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
+ :timeout => @timeout
+ )
+ @provider.purge_package("irssi", "0.8.12-7")
+ end
+
+ it "should run apt-get purge with the package name and options if specified" do
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get -q -y --force-yes purge irssi",
+ :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
+ :timeout => @timeout
+ )
+ @new_resource.options("--force-yes")
+
+ @provider.purge_package("irssi", "0.8.12-7")
+ end
+ end
+
+ describe "when preseeding a package" do
+ before(:each) do
+ allow(@provider).to receive(:get_preseed_file).and_return("/tmp/irssi-0.8.12-7.seed")
+ end
+
+ it "should get the full path to the preseed response file" do
+ expect(@provider).to receive(:get_preseed_file).with("irssi", "0.8.12-7").and_return("/tmp/irssi-0.8.12-7.seed")
+ file = @provider.get_preseed_file("irssi", "0.8.12-7")
+
+ expect(@provider).to receive(:shell_out!).with(
+ "debconf-set-selections /tmp/irssi-0.8.12-7.seed",
+ :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil},
+ :timeout => @timeout
+ )
+
+ @provider.preseed_package(file)
+ end
+
+ it "should run debconf-set-selections on the preseed file if it has changed" do
+ expect(@provider).to receive(:shell_out!).with(
+ "debconf-set-selections /tmp/irssi-0.8.12-7.seed",
+ :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil},
+ :timeout => @timeout
+ )
+ file = @provider.get_preseed_file("irssi", "0.8.12-7")
+ @provider.preseed_package(file)
+ end
+
+ it "should not run debconf-set-selections if the preseed file has not changed" do
+ allow(@provider).to receive(:check_package_state)
+ @current_resource.version "0.8.11"
+ @new_resource.response_file "/tmp/file"
+ allow(@provider).to receive(:get_preseed_file).and_return(false)
+ expect(@provider).not_to receive(:shell_out!)
+ @provider.run_action(:reconfig)
+ end
+ end
+
+ describe "when reconfiguring a package" do
+ it "should run dpkg-reconfigure package" do
+ expect(@provider).to receive(:shell_out!).with(
+ "dpkg-reconfigure irssi",
+ :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
+ :timeout => @timeout
+ )
+ @provider.reconfig_package("irssi", "0.8.12-7")
+ end
+ end
+
+ describe "when installing a virtual package" do
+ it "should install the package without specifying a version" do
+ @provider.is_virtual_package = true
+ expect(@provider).to receive(:shell_out!).with(
+ "apt-get -q -y install libmysqlclient-dev",
+ :env => {"DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil },
+ :timeout => @timeout
+ )
+ @provider.install_package("libmysqlclient-dev", "not_a_real_version")
+ end
+ end
end
end
end
diff --git a/spec/unit/provider/package/homebrew_spec.rb b/spec/unit/provider/package/homebrew_spec.rb
index dccd8edf11..d01d455b09 100644
--- a/spec/unit/provider/package/homebrew_spec.rb
+++ b/spec/unit/provider/package/homebrew_spec.rb
@@ -93,6 +93,27 @@ describe Chef::Provider::Package::Homebrew do
}
end
+ let(:keg_only_uninstalled_brew_info) do
+ {
+ 'name' => 'emacs-kegger',
+ 'homepage' => 'http://www.gnu.org/software/emacs/',
+ 'versions' => {
+ 'stable' => '24.3-keggy',
+ 'bottle' => false,
+ 'devel' => nil,
+ 'head' => 'HEAD'
+ },
+ 'revision' => 0,
+ 'installed' => [],
+ 'linked_keg' => nil,
+ 'keg_only' => true,
+ 'dependencies' => [],
+ 'conflicts_with' => [],
+ 'caveats' => '',
+ 'options' => []
+ }
+ end
+
before(:each) do
end
@@ -141,6 +162,11 @@ describe Chef::Provider::Package::Homebrew do
allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info)
expect(provider.current_installed_version).to be_nil
end
+
+ it 'returns nil if the package is keg only and not installed' do
+ allow(provider).to receive(:brew_info).and_return(keg_only_uninstalled_brew_info)
+ expect(provider.current_installed_version).to be_nil
+ end
end
describe 'brew' do
diff --git a/spec/unit/provider/package/windows/msi_spec.rb b/spec/unit/provider/package/windows/msi_spec.rb
index e539bbbb79..bef202847f 100644
--- a/spec/unit/provider/package/windows/msi_spec.rb
+++ b/spec/unit/provider/package/windows/msi_spec.rb
@@ -18,13 +18,22 @@
require 'spec_helper'
-describe Chef::Provider::Package::Windows::MSI, :windows_only do
+describe Chef::Provider::Package::Windows::MSI do
let(:node) { double('Chef::Node') }
let(:events) { double('Chef::Events').as_null_object } # mock all the methods
let(:run_context) { double('Chef::RunContext', :node => node, :events => events) }
let(:new_resource) { Chef::Resource::WindowsPackage.new("calculator.msi") }
let(:provider) { Chef::Provider::Package::Windows::MSI.new(new_resource) }
+ before(:each) do
+ stub_const("File::ALT_SEPARATOR", "\\")
+ allow(::File).to receive(:absolute_path).with("calculator.msi").and_return("calculator.msi")
+ end
+
+ it "responds to shell_out!" do
+ expect(provider).to respond_to(:shell_out!)
+ end
+
describe "expand_options" do
it "returns an empty string if passed no options" do
expect(provider.expand_options(nil)).to eql ""
@@ -51,10 +60,16 @@ describe Chef::Provider::Package::Windows::MSI, :windows_only do
end
describe "install_package" do
- # calls shell_out!
+ it "calls msiexec /qn /i" do
+ expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/i \"calculator.msi\"/, kind_of(Hash))
+ provider.install_package("unused", "unused")
+ end
end
describe "remove_package" do
- # calls shell_out!
+ it "calls msiexec /qn /x" do
+ expect(provider).to receive(:shell_out!).with(/msiexec \/qn \/x \"calculator.msi\"/, kind_of(Hash))
+ provider.remove_package("unused", "unused")
+ end
end
end
diff --git a/spec/unit/provider/script_spec.rb b/spec/unit/provider/script_spec.rb
index 4979475b79..d1759981aa 100644
--- a/spec/unit/provider/script_spec.rb
+++ b/spec/unit/provider/script_spec.rb
@@ -19,77 +19,95 @@
require 'spec_helper'
describe Chef::Provider::Script, "action_run" do
- before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Script.new('run some perl code')
- @new_resource.code "$| = 1; print 'i like beans'"
- @new_resource.interpreter 'perl'
+ let(:node) { Chef::Node.new }
+
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+
+ let(:new_resource) {
+ new_resource = Chef::Resource::Script.new('run some perl code')
+ new_resource.code "$| = 1; print 'i like beans'"
+ new_resource.interpreter 'perl'
+ new_resource
+ }
- @provider = Chef::Provider::Script.new(@new_resource, @run_context)
+ let(:provider) { Chef::Provider::Script.new(new_resource, run_context) }
- @script_file = StringIO.new
- allow(@script_file).to receive(:path).and_return('/tmp/the_script_file')
+ let(:tempfile) { Tempfile.open("rspec-provider-script") }
- allow(@provider).to receive(:shell_out!).and_return(true)
+ before(:each) do
+ allow(provider).to receive(:shell_out!).and_return(true)
+ allow(provider).to receive(:script_file).and_return(tempfile)
end
- it "creates a temporary file to store the script" do
- expect(@provider.script_file).to be_an_instance_of(Tempfile)
+ context "#script_file" do
+ it "creates a temporary file to store the script" do
+ allow(provider).to receive(:script_file).and_call_original
+ expect(provider.script_file).to be_an_instance_of(Tempfile)
+ end
end
- it "unlinks the tempfile when finished" do
- tempfile_path = @provider.script_file.path
- @provider.unlink_script_file
- expect(File.exist?(tempfile_path)).to be_falsey
+ context "#unlink_script_file" do
+ it "unlinks the tempfile" do
+ tempfile_path = tempfile.path
+ provider.unlink_script_file
+ expect(File.exist?(tempfile_path)).to be false
+ end
end
- it "sets the owner and group for the script file" do
- @new_resource.user 'toor'
- @new_resource.group 'wheel'
- allow(@provider).to receive(:script_file).and_return(@script_file)
- expect(FileUtils).to receive(:chown).with('toor', 'wheel', "/tmp/the_script_file")
- @provider.set_owner_and_group
+ context "#set_owner_and_group" do
+ it "sets the owner and group for the script file" do
+ new_resource.user 'toor'
+ new_resource.group 'wheel'
+ expect(FileUtils).to receive(:chown).with('toor', 'wheel', tempfile.path)
+ provider.set_owner_and_group
+ end
end
context "with the script file set to the correct owner and group" do
before do
- allow(@provider).to receive(:set_owner_and_group)
- allow(@provider).to receive(:script_file).and_return(@script_file)
+ allow(provider).to receive(:set_owner_and_group)
end
+
describe "when writing the script to the file" do
it "should put the contents of the script in the temp file" do
- @provider.action_run
- @script_file.rewind
- expect(@script_file.string).to eq("$| = 1; print 'i like beans'\n")
+ allow(provider).to receive(:unlink_script_file) # stub to avoid remove
+ provider.action_run
+ expect(IO.read(tempfile.path)).to eq("$| = 1; print 'i like beans'\n")
+ provider.unlink_script_file
end
it "closes before executing the script and unlinks it when finished" do
- @provider.action_run
- expect(@script_file).to be_closed
+ tempfile_path = tempfile.path
+ provider.action_run
+ expect(tempfile).to be_closed
+ expect(File.exist?(tempfile_path)).to be false
end
-
end
describe "when running the script" do
- it 'should set the command to "interpreter" "tempfile"' do
- @provider.action_run
- expect(@new_resource.command).to eq('"perl" "/tmp/the_script_file"')
- end
+ let (:default_opts) {
+ {timeout: 3600, returns: 0, log_level: :info, log_tag: "script[run some perl code]", live_stream: STDOUT}
+ }
- describe "with flags set on the resource" do
- before do
- @new_resource.flags '-f'
- end
+ before do
+ allow(STDOUT).to receive(:tty?).and_return(true)
+ end
- it "should set the command to 'interpreter flags tempfile'" do
- @provider.action_run
- expect(@new_resource.command).to eq('"perl" -f "/tmp/the_script_file"')
- end
+ it 'should set the command to "interpreter" "tempfile"' do
+ expect(provider.command).to eq(%Q{"perl" "#{tempfile.path}"})
+ end
+ it 'should call shell_out! with the command' do
+ expect(provider).to receive(:shell_out!).with(provider.command, default_opts).and_return(true)
+ provider.action_run
end
+ it "should set the command to 'interpreter flags tempfile'" do
+ new_resource.flags '-f'
+ expect(provider.command).to eq(%Q{"perl" -f "#{tempfile.path}"})
+ end
end
end
diff --git a/spec/unit/provider/subversion_spec.rb b/spec/unit/provider/subversion_spec.rb
index b372f0df7a..9ca11b8d82 100644
--- a/spec/unit/provider/subversion_spec.rb
+++ b/spec/unit/provider/subversion_spec.rb
@@ -198,7 +198,7 @@ describe Chef::Provider::Subversion do
it "runs an export with the --force option" do
allow(::File).to receive(:directory?).with("/my/deploy").and_return(true)
expected_cmd = "svn export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir"
- expect(@provider).to receive(:shell_out!).with(command: expected_cmd)
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, {})
@provider.run_action(:force_export)
expect(@resource).to be_updated
end
@@ -206,7 +206,7 @@ describe Chef::Provider::Subversion do
it "runs the checkout command for action_checkout" do
allow(::File).to receive(:directory?).with("/my/deploy").and_return(true)
expected_cmd = "svn checkout -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir"
- expect(@provider).to receive(:shell_out!).with(command: expected_cmd)
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, {})
@provider.run_action(:checkout)
expect(@resource).to be_updated
end
@@ -230,7 +230,7 @@ describe Chef::Provider::Subversion do
@resource.user "whois"
@resource.group "thisis"
expected_cmd = "svn checkout -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir"
- expect(@provider).to receive(:shell_out!).with(command: expected_cmd, user: "whois", group: "thisis")
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, {user: "whois", group: "thisis"})
@provider.run_action(:checkout)
expect(@resource).to be_updated
end
@@ -255,7 +255,7 @@ describe Chef::Provider::Subversion do
allow(@provider).to receive(:find_current_revision).and_return("11410")
allow(@provider).to receive(:current_revision_matches_target_revision?).and_return(false)
expected_cmd = "svn update -q -r12345 /my/deploy/dir"
- expect(@provider).to receive(:shell_out!).with(command: expected_cmd)
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, {})
@provider.run_action(:sync)
expect(@resource).to be_updated
end
@@ -272,7 +272,7 @@ describe Chef::Provider::Subversion do
it "runs the export_command on action_export" do
allow(::File).to receive(:directory?).with("/my/deploy").and_return(true)
expected_cmd = "svn export --force -q -r12345 http://svn.example.org/trunk/ /my/deploy/dir"
- expect(@provider).to receive(:shell_out!).with(command: expected_cmd)
+ expect(@provider).to receive(:shell_out!).with(expected_cmd, {})
@provider.run_action(:export)
expect(@resource).to be_updated
end
diff --git a/spec/unit/resource/apt_package_spec.rb b/spec/unit/resource/apt_package_spec.rb
index be8eb9c324..3c31f63dd7 100644
--- a/spec/unit/resource/apt_package_spec.rb
+++ b/spec/unit/resource/apt_package_spec.rb
@@ -29,12 +29,10 @@ describe Chef::Resource::AptPackage, "initialize" do
os: "linux",
)
- before(:each) do
- @resource = Chef::Resource::AptPackage.new("foo")
- end
+ let(:resource) { Chef::Resource::AptPackage.new("foo") }
it "should support default_release" do
- @resource.default_release("lenny-backports")
- expect(@resource.default_release).to eql("lenny-backports")
+ resource.default_release("lenny-backports")
+ expect(resource.default_release).to eql("lenny-backports")
end
end
diff --git a/spec/unit/resource/script_spec.rb b/spec/unit/resource/script_spec.rb
index 9d744baaa5..4affee8e8c 100644
--- a/spec/unit/resource/script_spec.rb
+++ b/spec/unit/resource/script_spec.rb
@@ -29,18 +29,16 @@ describe Chef::Resource::Script do
expect(script_resource.interpreter).to eql("naaaaNaNaNaaNaaNaaNaa")
end
- describe "when it has interpreter and flags" do
+ context "when it has interpreter and flags" do
before do
- script_resource.command("grep")
script_resource.interpreter("gcc")
script_resource.flags("-al")
end
- it "returns the command as its identity" do
- expect(script_resource.identity).to eq("grep")
+ it "returns the name as its identity" do
+ expect(script_resource.identity).to eq(resource_instance_name)
end
end
it_behaves_like "a script resource"
end
-
diff --git a/spec/unit/run_context_spec.rb b/spec/unit/run_context_spec.rb
index bb251c9cf4..cc112f332a 100644
--- a/spec/unit/run_context_spec.rb
+++ b/spec/unit/run_context_spec.rb
@@ -21,10 +21,11 @@
require 'spec_helper'
require 'support/lib/library_load_order'
-Chef::Log.level = :debug
describe Chef::RunContext do
before(:each) do
+ @original_log_level = Chef::Log.level
+ Chef::Log.level = :debug
@chef_repo_path = File.expand_path(File.join(CHEF_SPEC_DATA, "run_context", "cookbooks"))
cl = Chef::CookbookLoader.new(@chef_repo_path)
cl.load_cookbooks
@@ -35,6 +36,10 @@ describe Chef::RunContext do
@run_context = Chef::RunContext.new(@node, @cookbook_collection, @events)
end
+ after(:each) do
+ Chef::Log.level = @original_log_level
+ end
+
it "has a cookbook collection" do
expect(@run_context.cookbook_collection).to eq(@cookbook_collection)
end