summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortyler-ball <tyleraball@gmail.com>2014-09-19 15:25:46 -0700
committertyler-ball <tyleraball@gmail.com>2014-09-19 16:08:44 -0700
commit7e2c0b1518866ad046115ed804945937b6cd4f0a (patch)
treecac30646df462d3fde566a99c47e6a91d52ad8d4
parente7d3e2adf486131cfba78fc4eb7e31d1e36b7b0e (diff)
downloadchef-7e2c0b1518866ad046115ed804945937b6cd4f0a.tar.gz
Restoring https://github.com/opscode/chef/pull/1921 to master - somehow it got deleted from the git log
-rw-r--r--CHANGELOG.md1
-rw-r--r--RELEASE_NOTES.md28
-rw-r--r--lib/chef/exceptions.rb4
-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, 600 insertions, 4 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..eb16abdaa6 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -120,6 +120,8 @@ class Chef
class PowershellCmdletException < RuntimeError; end
+ class CannotDetermineHomebrewOwner < Package; end
+
# A different version of a cookbook was added to a
# VersionedRecipeList than the one already there.
class CookbookVersionConflict < ArgumentError ; end
@@ -181,8 +183,6 @@ class Chef
class ChildConvergeError < RuntimeError; end
- class NoProviderAvailable < RuntimeError; end
-
class MissingRole < RuntimeError
NULL = Object.new
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