summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjtimberman <joshua@opscode.com>2014-08-26 10:28:44 -0600
committerLamont Granquist <lamont@scriptkiddie.org>2014-09-19 17:09:28 -0700
commit46ef0ff4aedfa2bdf440e78f52ac11b076baeab1 (patch)
tree216009aaec50cc460f898b59518f5d495343aa1d
parente7d3e2adf486131cfba78fc4eb7e31d1e36b7b0e (diff)
downloadchef-lcg/1921.tar.gz
Use homebrew for default package provider on OS Xlcg/1921
Via Chef RFC 016[0], fixes issue #1709, adding the `homebrew_package` resource with the `homebrew` provider, and setting it as the default for Mac OS X platforms (`mac_os_x`, `mac_os_x_server`). This will still require that homebrew is installed on the system somehow, but doesn't require use of the cookbook if users choose to install via some other method (pre-built images, for example). [0]: https://github.com/opscode/chef-rfc/blob/master/rfc016-homebrew-osx-package-provider.md Cleanup/fixes based on feedback * Use accessor methods instead of instance variables for new_resource and current_resource in the provider. * Use Chef::JSONCompat * Move etc require to top * Use let for homebrew_owner specs * Set homebrew owner attribute and remove ENV stubs * Add repeated stubs in LCR to a before block * Wrap action method tests in a context so provider.current_resource does the right thing * Add timeout for #get_response_from_command (bonus, per #opscode-cookbooks/homebrew issue 49) Better exception for homebrew owner mixin Create an exception class for when the owner isn't found, subclassed from Package. Update the exception output text.
-rw-r--r--CHANGELOG.md1
-rw-r--r--RELEASE_NOTES.md28
-rw-r--r--lib/chef/exceptions.rb1
-rw-r--r--lib/chef/mixin/homebrew_owner.rb58
-rw-r--r--lib/chef/platform/provider_mapping.rb4
-rw-r--r--lib/chef/provider/package/homebrew.rb121
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/resource/homebrew_package.rb34
-rw-r--r--lib/chef/resources.rb1
-rw-r--r--spec/unit/mixin/homebrew_owner_spec.rb65
-rw-r--r--spec/unit/provider/package/homebrew_spec.rb251
-rw-r--r--spec/unit/resource/homebrew_package_spec.rb36
12 files changed, 599 insertions, 2 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e5b7532632..73abae07d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -134,6 +134,7 @@
* Add partial_search dsl method to Chef::Search::Query, add result filtering to search.
* Transfer trusted certificates under :trusted_certs_dir during bootstrap.
* Set :ssl_verify_mode to :verify_peer by default.
+* Add homebrew provider for package resource, use it by default on OS X (Issue #1709)
## Last Release: 11.14.2
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index f5a74d6a43..12e0351623 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -66,6 +66,34 @@ In order to support configuring passwords for the users using shadow hashes two
User resource on Mac supports setting password both using plain-text password or using the shadow hash. You can simply set the `password` attribute to the plain text password to configure the password for the user. However this is not ideal since including plain text passwords in cookbooks (even if they are private) is not a good idea. In order to set passwords using shadow hash you can follow the instructions below based on your Mac OS X version.
+## Mac OS X default package provider is now Homebrew
+
+Per [Chef RFC 016](https://github.com/opscode/chef-rfc/blob/master/rfc016-homebrew-osx-package-provider.md), the default provider for the `package` resource on Mac OS X is now [Homebrew](http://brew.sh). The [homebrew cookbook's](https://supermarket.getchef.com/cookbooks/homebrew) default recipe, or some other method is still required for getting homebrew installed on the system. The cookbook won't be strictly required just to install packages from homebrew on OS X, though. To use this, simply use the `package` resource, or the `homebrew_package` shortcut resource:
+
+```ruby
+package 'emacs'
+```
+
+Or,
+
+```ruby
+homebrew_package 'emacs'
+```
+
+The macports provider will still be available, and can be used with the shortcut resource, or by using the `provider` attribute:
+
+```ruby
+macports_package 'emacs'
+```
+
+Or,
+
+```ruby
+package 'emacs' do
+ provider Chef::Provider::Package::Macports
+end
+```
+
### Mac OS X 10.7
10.7 calculates the password hash using **SALTED-SHA512**. Stored shadow hash length is 68 bytes; first 4 bytes being salt and the next 64 bytes being the shadow hash itself. You can use below code in order to calculate password hashes to be used in `password` attribute on Mac OS X 10.7:
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index 7298f1f4d1..38056439fb 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -119,6 +119,7 @@ class Chef
class DuplicateDataBagItem < RuntimeError; end
class PowershellCmdletException < RuntimeError; end
+ class CannotDetermineHomebrewOwner < Package; end
# A different version of a cookbook was added to a
# VersionedRecipeList than the one already there.
diff --git a/lib/chef/mixin/homebrew_owner.rb b/lib/chef/mixin/homebrew_owner.rb
new file mode 100644
index 0000000000..73bb22ddf5
--- /dev/null
+++ b/lib/chef/mixin/homebrew_owner.rb
@@ -0,0 +1,58 @@
+#
+# Author:: Joshua Timberman (<joshua@getchef.com>)
+# Author:: Graeme Mathieson (<mathie@woss.name>)
+#
+# Copyright 2011-2013, Opscode, Inc.
+# Copyright 2014, Chef Software, Inc <legal@getchef.com>
+#
+# 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.
+#
+# Ported from the homebrew cookbook's Homebrew::Mixin owner helpers
+#
+# This lives here in Chef::Mixin because Chef's namespacing makes it
+# awkward to use modules elsewhere (e.g., chef/provider/package/homebrew/owner)
+
+class Chef
+ module Mixin
+ module HomebrewOwner
+ def homebrew_owner(node)
+ @homebrew_owner ||= calculate_owner(node)
+ end
+
+ private
+
+ def calculate_owner(node)
+ owner = homebrew_owner_attr(node) || sudo_user || current_user
+ if owner == 'root'
+ raise Chef::Exceptions::CannotDetermineHomebrewOwner,
+ 'The homebrew owner is not specified and the current user is \"root\"' +
+ 'Homebrew does not support root installs, please specify the homebrew' +
+ 'owner by setting the attribute `node[\'homebrew\'][\'owner\']`.'
+ end
+ owner
+ end
+
+ def homebrew_owner_attr(node)
+ node['homebrew']['owner'] if node.attribute?('homebrew') && node['homebrew'].attribute?('owner')
+ end
+
+ def sudo_user
+ ENV['SUDO_USER']
+ end
+
+ def current_user
+ ENV['USER']
+ end
+ end
+ end
+end
diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb
index 7f79c38a6a..b10fecc53e 100644
--- a/lib/chef/platform/provider_mapping.rb
+++ b/lib/chef/platform/provider_mapping.rb
@@ -40,7 +40,7 @@ class Chef
{
:mac_os_x => {
:default => {
- :package => Chef::Provider::Package::Macports,
+ :package => Chef::Provider::Package::Homebrew,
:service => Chef::Provider::Service::Macosx,
:user => Chef::Provider::User::Dscl,
:group => Chef::Provider::Group::Dscl
@@ -48,7 +48,7 @@ class Chef
},
:mac_os_x_server => {
:default => {
- :package => Chef::Provider::Package::Macports,
+ :package => Chef::Provider::Package::Homebrew,
:service => Chef::Provider::Service::Macosx,
:user => Chef::Provider::User::Dscl,
:group => Chef::Provider::Group::Dscl
diff --git a/lib/chef/provider/package/homebrew.rb b/lib/chef/provider/package/homebrew.rb
new file mode 100644
index 0000000000..d964703c87
--- /dev/null
+++ b/lib/chef/provider/package/homebrew.rb
@@ -0,0 +1,121 @@
+#
+# Author:: Joshua Timberman (<joshua@getchef.com>)
+# Author:: Graeme Mathieson (<mathie@woss.name>)
+#
+# Copyright 2011-2013, Opscode, Inc.
+# Copyright 2014, Chef Software, Inc <legal@getchef.com>
+#
+# 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 'etc'
+require 'chef/mixin/homebrew_owner'
+
+class Chef
+ class Provider
+ class Package
+ class Homebrew < Chef::Provider::Package
+ include Chef::Mixin::HomebrewOwner
+ def load_current_resource
+ self.current_resource = Chef::Resource::Package.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+ current_resource.version(current_installed_version)
+ Chef::Log.debug("#{new_resource} current version is #{current_resource.version}") if current_resource.version
+
+ @candidate_version = candidate_version
+
+ Chef::Log.debug("#{new_resource} candidate version is #{@candidate_version}") if @candidate_version
+
+ current_resource
+ end
+
+ def install_package(name, version)
+ unless current_resource.version == version
+ brew('install', new_resource.options, name)
+ end
+ end
+
+ def upgrade_package(name, version)
+ current_version = current_resource.version
+
+ if current_version.nil? or current_version.empty?
+ install_package(name, version)
+ elsif current_version != version
+ brew('upgrade', new_resource.options, name)
+ end
+ end
+
+ def remove_package(name, version)
+ if current_resource.version
+ brew('uninstall', new_resource.options, name)
+ end
+ end
+
+ # Homebrew doesn't really have a notion of purging, do a "force remove"
+ def purge_package(name, version)
+ new_resource.options((new_resource.options || '') << ' --force').strip
+ remove_package(name, version)
+ end
+
+ def brew(*args)
+ get_response_from_command("brew #{args.join(' ')}")
+ end
+
+ # We implement a querying method that returns the JSON-as-Hash
+ # data for a formula per the Homebrew documentation. Previous
+ # implementations of this provider in the homebrew cookbook
+ # performed a bit of magic with the load path to get this
+ # information, but that is not any more robust than using the
+ # command-line interface that returns the same thing.
+ #
+ # https://github.com/Homebrew/homebrew/wiki/Querying-Brew
+ def brew_info
+ @brew_info ||= Chef::JSONCompat.from_json(brew('info', '--json=v1', new_resource.package_name)).first
+ end
+
+ # Some packages (formula) are "keg only" and aren't linked,
+ # because multiple versions installed can cause conflicts. We
+ # handle this by using the last installed version as the
+ # "current" (as in latest). Otherwise, we will use the version
+ # 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']
+ end
+
+ # Packages (formula) available to install should have a
+ # "stable" version, per the Homebrew project's acceptable
+ # formula documentation, so we will rely on that being the
+ # case. Older implementations of this provider in the homebrew
+ # cookbook would fall back to +brew_info['version']+, but the
+ # schema has changed, and homebrew is a constantly rolling
+ # forward project.
+ #
+ # https://github.com/Homebrew/homebrew/wiki/Acceptable-Formulae#stable-versions
+ def candidate_version
+ brew_info['versions']['stable']
+ end
+
+ private
+
+ def get_response_from_command(command)
+ home_dir = Etc.getpwnam(homebrew_owner(node)).dir
+
+ Chef::Log.debug "Executing '#{command}' as user '#{homebrew_owner(node)}'"
+ output = shell_out!(command, :timeout => 1800, :user => homebrew_owner(node), :environment => { 'HOME' => home_dir, 'RUBYOPT' => nil })
+ output.stdout.chomp
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index dd384c90a6..fec00d0e63 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -60,6 +60,7 @@ require 'chef/provider/package/easy_install'
require 'chef/provider/package/freebsd/port'
require 'chef/provider/package/freebsd/pkg'
require 'chef/provider/package/freebsd/pkgng'
+require 'chef/provider/package/homebrew'
require 'chef/provider/package/ips'
require 'chef/provider/package/macports'
require 'chef/provider/package/pacman'
diff --git a/lib/chef/resource/homebrew_package.rb b/lib/chef/resource/homebrew_package.rb
new file mode 100644
index 0000000000..c3fa9ddffc
--- /dev/null
+++ b/lib/chef/resource/homebrew_package.rb
@@ -0,0 +1,34 @@
+#
+# Author:: Joshua Timberman (<joshua@getchef.com>)
+# Author:: Graeme Mathieson (<mathie@woss.name>)
+#
+# Copyright 2011-2013, Opscode, Inc.
+# Copyright 2014, Chef Software, Inc <legal@getchef.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/package'
+require 'chef/resource/package'
+
+class Chef
+ class Resource
+ class HomebrewPackage < Chef::Resource::Package
+ def initialize(name, run_context=nil)
+ super
+ @resource_name = :homebrew_package
+ @provider = Chef::Provider::Package::Homebrew
+ end
+ end
+ end
+end
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index fa5c82761d..5b938095c6 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -40,6 +40,7 @@ require 'chef/resource/gem_package'
require 'chef/resource/git'
require 'chef/resource/group'
require 'chef/resource/http_request'
+require 'chef/resource/homebrew_package'
require 'chef/resource/ifconfig'
require 'chef/resource/link'
require 'chef/resource/log'
diff --git a/spec/unit/mixin/homebrew_owner_spec.rb b/spec/unit/mixin/homebrew_owner_spec.rb
new file mode 100644
index 0000000000..428cd827d9
--- /dev/null
+++ b/spec/unit/mixin/homebrew_owner_spec.rb
@@ -0,0 +1,65 @@
+#
+# Author:: Joshua Timberman (<joshua@getchef.com>)
+#
+# Copyright 2014, Chef Software, Inc <legal@getchef.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'spec_helper'
+require 'chef/mixin/homebrew_owner'
+
+class ExampleHomebrewOwner
+ include Chef::Mixin::HomebrewOwner
+end
+
+describe Chef::Mixin::HomebrewOwner do
+ before(:each) do
+ node.default['homebrew']['owner'] = nil
+ end
+
+ let(:homebrew_owner) { ExampleHomebrewOwner.new }
+ let(:node) { Chef::Node.new }
+
+ describe 'when the homebrew owner node attribute is set' do
+ it 'raises an exception if the owner is root' do
+ node.default['homebrew']['owner'] = 'root'
+ expect { homebrew_owner.homebrew_owner(node) }.to raise_exception(Chef::Exceptions::CannotDetermineHomebrewOwner)
+ end
+
+ it 'returns the owner set by attribute' do
+ node.default['homebrew']['owner'] = 'siouxsie'
+ expect(homebrew_owner.homebrew_owner(node)).to eql('siouxsie')
+ end
+ end
+
+ describe 'when the owner attribute is not set and we use sudo' do
+ before(:each) do
+ ENV.stub(:[]).with('SUDO_USER').and_return('john_lydon')
+ end
+
+ it 'uses the SUDO_USER environment variable' do
+ expect(homebrew_owner.homebrew_owner(node)).to eql('john_lydon')
+ end
+ end
+
+ describe 'when the owner attribute is not set and we arent using sudo' do
+ before(:each) do
+ ENV.stub(:[]).with('USER').and_return('sid_vicious')
+ ENV.stub(:[]).with('SUDO_USER').and_return(nil)
+ end
+
+ it 'uses the current user' do
+ expect(homebrew_owner.homebrew_owner(node)).to eql('sid_vicious')
+ end
+ end
+end
diff --git a/spec/unit/provider/package/homebrew_spec.rb b/spec/unit/provider/package/homebrew_spec.rb
new file mode 100644
index 0000000000..9f105c13b8
--- /dev/null
+++ b/spec/unit/provider/package/homebrew_spec.rb
@@ -0,0 +1,251 @@
+#
+# Author:: Joshua Timberman (<joshua@getchef.com>)
+# Copyright (c) 2014, Chef Software, Inc. <legal@getchef.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Provider::Package::Homebrew do
+ let(:node) { Chef::Node.new }
+ let(:events) { double('Chef::Events').as_null_object }
+ let(:run_context) { double('Chef::RunContext', node: node, events: events) }
+ let(:new_resource) { Chef::Resource::HomebrewPackage.new('emacs') }
+ let(:current_resource) { Chef::Resource::HomebrewPackage.new('emacs')}
+
+ let(:provider) do
+ Chef::Provider::Package::Homebrew.new(new_resource, run_context)
+ end
+
+ let(:uninstalled_brew_info) do
+ {
+ 'name' => 'emacs',
+ 'homepage' => 'http://www.gnu.org/software/emacs',
+ 'versions' => {
+ 'stable' => '24.3',
+ 'bottle' => false,
+ 'devel' => nil,
+ 'head' => nil
+ },
+ 'revision' => 0,
+ 'installed' => [],
+ 'linked_keg' => nil,
+ 'keg_only' => nil,
+ 'dependencies' => [],
+ 'conflicts_with' => [],
+ 'caveats' => nil,
+ 'options' => []
+ }
+ end
+
+ let(:installed_brew_info) do
+ {
+ 'name' => 'emacs',
+ 'homepage' => 'http://www.gnu.org/software/emacs/',
+ 'versions' => {
+ 'stable' => '24.3',
+ 'bottle' => false,
+ 'devel' => nil,
+ 'head' => 'HEAD'
+ },
+ 'revision' => 0,
+ 'installed' => [{ 'version' => '24.3' }],
+ 'linked_keg' => '24.3',
+ 'keg_only' => nil,
+ 'dependencies' => [],
+ 'conflicts_with' => [],
+ 'caveats' => '',
+ 'options' => []
+ }
+ end
+
+ let(:keg_only_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' => [{ 'version' => '24.3-keggy' }],
+ 'linked_keg' => nil,
+ 'keg_only' => true,
+ 'dependencies' => [],
+ 'conflicts_with' => [],
+ 'caveats' => '',
+ 'options' => []
+ }
+ end
+
+ before(:each) do
+ node.default['homebrew']['owner'] = 'sid_vicious'
+ allow(Etc).to receive(:getpwnam).with('sid_vicious').and_return('/Users/sid_vicious')
+ end
+
+ describe 'load_current_resource' do
+ before(:each) do
+ allow(provider).to receive(:current_installed_version).and_return(nil)
+ allow(provider).to receive(:candidate_version).and_return('24.3')
+ end
+
+ it 'creates a current resource with the name of the new resource' do
+ provider.load_current_resource
+ expect(provider.current_resource).to be_a(Chef::Resource::Package)
+ expect(provider.current_resource.name).to eql('emacs')
+ end
+
+ it 'creates a current resource with the version if the package is installed' do
+ expect(provider).to receive(:current_installed_version).and_return('24.3')
+ provider.load_current_resource
+ expect(provider.current_resource.version).to eql('24.3')
+ end
+
+ it 'creates a current resource with a nil version if the package is not installed' do
+ provider.load_current_resource
+ expect(provider.current_resource.version).to be_nil
+ end
+
+ it 'sets a candidate version if one exists' do
+ provider.load_current_resource
+ expect(provider.candidate_version).to eql('24.3')
+ end
+ end
+
+ describe 'current_installed_version' do
+ it 'returns the latest version from brew info if the package is keg only' do
+ allow(provider).to receive(:brew_info).and_return(keg_only_brew_info)
+ expect(provider.current_installed_version).to eql('24.3-keggy')
+ end
+
+ it 'returns the linked keg version if the package is not keg only' do
+ allow(provider).to receive(:brew_info).and_return(installed_brew_info)
+ expect(provider.current_installed_version).to eql('24.3')
+ end
+
+ it 'returns nil if the package is not installed' do
+ allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info)
+ expect(provider.current_installed_version).to be_nil
+ end
+ end
+
+ describe 'brew' do
+ it 'passes a single to the brew command and return stdout' do
+ allow(provider).to receive(:get_response_from_command).and_return('zombo')
+ expect(provider.brew).to eql('zombo')
+ end
+
+ it 'takes multiple arguments as an array' do
+ allow(provider).to receive(:get_response_from_command).and_return('homestarrunner')
+ expect(provider.brew('info', 'opts', 'bananas')).to eql('homestarrunner')
+ end
+ end
+
+ context 'when testing actions' do
+ before(:each) do
+ provider.current_resource = current_resource
+ end
+
+ describe 'install_package' do
+ before(:each) do
+ allow(provider).to receive(:candidate_version).and_return('24.3')
+ end
+
+ it 'installs the named package with brew install' do
+ allow(provider.new_resource).to receive(:version).and_return('24.3')
+ allow(provider.current_resource).to receive(:version).and_return(nil)
+ allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info)
+ expect(provider).to receive(:get_response_from_command).with('brew install emacs')
+ provider.install_package('emacs', '24.3')
+ end
+
+ it 'does not do anything if the package is installed' do
+ allow(provider.current_resource).to receive(:version).and_return('24.3')
+ allow(provider).to receive(:brew_info).and_return(installed_brew_info)
+ expect(provider).not_to receive(:get_response_from_command)
+ provider.install_package('emacs', '24.3')
+ end
+
+ it 'uses options to the brew command if specified' do
+ allow(provider.new_resource).to receive(:options).and_return('--cocoa')
+ allow(provider.current_resource).to receive(:version).and_return('24.3')
+ allow(provider).to receive(:get_response_from_command).with('brew install --cocoa emacs')
+ provider.install_package('emacs', '24.3')
+ end
+ end
+
+ describe 'upgrade_package' do
+ it 'uses brew upgrade to upgrade the package if it is installed' do
+ allow(provider.current_resource).to receive(:version).and_return('24')
+ allow(provider).to receive(:brew_info).and_return(installed_brew_info)
+ expect(provider).to receive(:get_response_from_command).with('brew upgrade emacs')
+ provider.upgrade_package('emacs', '24.3')
+ end
+
+ it 'does not do anything if the package version is already installed' do
+ allow(provider.current_resource).to receive(:version).and_return('24.3')
+ allow(provider).to receive(:brew_info).and_return(installed_brew_info)
+ expect(provider).not_to receive(:get_response_from_command)
+ provider.install_package('emacs', '24.3')
+ end
+
+ it 'uses brew install to install the package if it is not installed' do
+ allow(provider.current_resource).to receive(:version).and_return(nil)
+ allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info)
+ expect(provider).to receive(:get_response_from_command).with('brew install emacs')
+ provider.upgrade_package('emacs', '24.3')
+ end
+
+ it 'uses options to the brew command if specified' do
+ allow(provider.current_resource).to receive(:version).and_return('24')
+ allow(provider).to receive(:brew_info).and_return(installed_brew_info)
+ allow(provider.new_resource).to receive(:options).and_return('--cocoa')
+ expect(provider).to receive(:get_response_from_command).with('brew upgrade --cocoa emacs')
+ provider.upgrade_package('emacs', '24.3')
+ end
+ end
+
+ describe 'remove_package' do
+ it 'uninstalls the package with brew uninstall' do
+ allow(provider.current_resource).to receive(:version).and_return('24.3')
+ allow(provider).to receive(:brew_info).and_return(installed_brew_info)
+ expect(provider).to receive(:get_response_from_command).with('brew uninstall emacs')
+ provider.remove_package('emacs', '24.3')
+ end
+
+ it 'does not do anything if the package is not installed' do
+ allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info)
+ expect(provider).not_to receive(:get_response_from_command)
+ provider.remove_package('emacs', '24.3')
+ end
+ end
+
+ describe 'purge_package' do
+ it 'uninstalls the package with brew uninstall --force' do
+ allow(provider.current_resource).to receive(:version).and_return('24.3')
+ allow(provider).to receive(:brew_info).and_return(installed_brew_info)
+ expect(provider).to receive(:get_response_from_command).with('brew uninstall --force emacs')
+ provider.purge_package('emacs', '24.3')
+ end
+
+ it 'does not do anything if the package is not installed' do
+ allow(provider).to receive(:brew_info).and_return(uninstalled_brew_info)
+ expect(provider).not_to receive(:get_response_from_command)
+ provider.purge_package('emacs', '24.3')
+ end
+ end
+ end
+end
diff --git a/spec/unit/resource/homebrew_package_spec.rb b/spec/unit/resource/homebrew_package_spec.rb
new file mode 100644
index 0000000000..4b4f9afe5e
--- /dev/null
+++ b/spec/unit/resource/homebrew_package_spec.rb
@@ -0,0 +1,36 @@
+#
+# Author:: Joshua Timberman (<joshua@getchef.com>)
+# Copyright (c) 2014, Chef Software, Inc. <legal@getchef.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Resource::HomebrewPackage, 'initialize' do
+
+ let(:resource) { Chef::Resource::HomebrewPackage.new('emacs') }
+
+ it 'returns a Chef::Resource::HomebrewPackage' do
+ expect(resource).to be_a_kind_of(Chef::Resource::HomebrewPackage)
+ end
+
+ it 'sets the resource_name to :homebrew_package' do
+ expect(resource.resource_name).to eql(:homebrew_package)
+ end
+
+ it 'sets the provider to Chef::Provider::Package::Homebrew' do
+ expect(resource.provider).to eql(Chef::Provider::Package::Homebrew)
+ end
+
+end