summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/chef/platform/provider_mapping.rb3
-rw-r--r--lib/chef/provider/package/freebsd_next_gen.rb131
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/version/platform.rb2
-rw-r--r--spec/unit/provider/package/freebsd_next_gen_spec.rb204
-rw-r--r--spec/unit/version/platform_spec.rb2
6 files changed, 342 insertions, 1 deletions
diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb
index 92a7278d2f..3fe3f97dbb 100644
--- a/lib/chef/platform/provider_mapping.rb
+++ b/lib/chef/platform/provider_mapping.rb
@@ -64,6 +64,9 @@ class Chef
:service => Chef::Provider::Service::Freebsd,
:user => Chef::Provider::User::Pw,
:cron => Chef::Provider::Cron
+ },
+ ">= 10.0" => {
+ :package => Chef::Provider::Package::FreebsdNextGen
}
},
:ubuntu => {
diff --git a/lib/chef/provider/package/freebsd_next_gen.rb b/lib/chef/provider/package/freebsd_next_gen.rb
new file mode 100644
index 0000000000..d7dcd757bc
--- /dev/null
+++ b/lib/chef/provider/package/freebsd_next_gen.rb
@@ -0,0 +1,131 @@
+#
+# Authors:: Richard Manyanza (liseki@nyikacraftsmen.com)
+# Copyright:: Copyright (c) 2013 Richard Manyanza
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/provider/package'
+require 'chef/mixin/shell_out'
+require 'chef/resource/package'
+require 'chef/mixin/get_source_from_package'
+
+class Chef
+ class Provider
+ class Package
+ class FreebsdNextGen < Chef::Provider::Package
+ include Chef::Mixin::ShellOut
+
+ include Chef::Mixin::GetSourceFromPackage
+
+ def initialize(*args)
+ super
+ @current_resource = Chef::Resource::Package.new(@new_resource.name)
+ end
+
+ def load_current_resource
+ @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 = case @new_resource.source
+ when /^ports$/i
+ ports_candidate_version
+ else
+ package_candidate_version
+ end
+
+ Chef::Log.debug("#{@new_resource} ports candidate version is #{@candidate_version}") if @candidate_version
+
+ @current_resource
+ end
+
+ def install_package(name, version)
+ unless @current_resource.version
+ case @new_resource.source
+ when /^ports$/i
+ shell_out!("make -DBATCH install", :timeout => 1800, :env => nil, :cwd => port_path).status
+ when /^(http|ftp|\/)/
+ shell_out!("pkg add#{expand_options(@new_resource.options)} #{@new_resource.source}", :env => { 'LC_ALL' => nil }).status
+ Chef::Log.debug("#{@new_resource} installed from: #{@new_resource.source}")
+ else
+ shell_out!("pkg install#{expand_options(@new_resource.options || '-y')} #{name}", :env => { 'LC_ALL' => nil }).status
+ end
+ end
+ end
+
+ def remove_package(name, version)
+ shell_out!("pkg delete#{expand_options(@new_resource.options || '-y')} #{name}#{version ? '-' + version : ''}", :env => nil).status
+ end
+
+
+
+
+ private
+
+ def current_installed_version
+ pkg_info = shell_out!("pkg info \"#{@new_resource.package_name}\"", :env => nil, :returns => [0,70])
+ pkg_info.stdout[/^#{Regexp.escape(@new_resource.package_name)}-(.+)/, 1]
+ end
+
+ def package_candidate_version
+ if @new_resource.source
+ @new_resource.source[/#{Regexp.escape(@new_resource.package_name)}-(.+)\.[[:alpha:]]{3}/, 1]
+ else
+ repo_candidate_version
+ end
+ end
+
+ def repo_candidate_version
+ if @new_resource.options && @new_resource.options.match(/(r .+)\b/)
+ options = "-#{$1}"
+ end
+
+ pkg_query = shell_out!("pkg rquery#{expand_options(options)} '%v' #{@new_resource.package_name}", :env => nil, :returns => [0,69])
+ pkg_query.stdout.strip.split(/\n/).first
+ end
+
+ def ports_candidate_version
+ ports_makefile_variable_value("PORTVERSION")
+ end
+
+ def port_path
+ case @new_resource.package_name
+ # When the package name starts with a '/' treat it as the full path to the ports directory
+ when /^\//
+ @new_resource.package_name
+ # Otherwise if the package name contains a '/' not at the start (like 'www/wordpress') treat as a relative
+ # path from /usr/ports
+ when /\//
+ "/usr/ports/#{@new_resource.package_name}"
+ # Otherwise look up the path to the ports directory using 'whereis'
+ else
+ whereis = shell_out!("whereis -s #{@new_resource.package_name}", :env => nil)
+ unless path = whereis.stdout[/^#{Regexp.escape(@new_resource.package_name)}:\s+(.+)$/, 1]
+ raise Chef::Exceptions::Package, "Could not find port with the name #{@new_resource.package_name}"
+ end
+ path
+ end
+ end
+
+ def ports_makefile_variable_value(variable)
+ make_v = shell_out!("make -V #{variable}", :cwd => port_path, :env => nil, :returns => [0,1])
+ make_v.stdout.strip.split(/\n/).first
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index 6cfd12827a..e5843596b0 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -56,6 +56,7 @@ require 'chef/provider/package/apt'
require 'chef/provider/package/dpkg'
require 'chef/provider/package/easy_install'
require 'chef/provider/package/freebsd'
+require 'chef/provider/package/freebsd_next_gen'
require 'chef/provider/package/ips'
require 'chef/provider/package/macports'
require 'chef/provider/package/pacman'
diff --git a/lib/chef/version/platform.rb b/lib/chef/version/platform.rb
index 2921341cd2..81e7614646 100644
--- a/lib/chef/version/platform.rb
+++ b/lib/chef/version/platform.rb
@@ -31,6 +31,8 @@ class Chef
[ $1.to_i, $2.to_i, 0 ]
when /^(\d+)$/
[ $1.to_i, 0, 0 ]
+ when /^(\d+).(\d+)-[a-z]+\d?(-p(\d+))?$/i # Match FreeBSD
+ [ $1.to_i, $2.to_i, ($4 ? $4.to_i : 0)]
else
msg = "'#{str.to_s}' does not match 'x.y.z', 'x.y' or 'x'"
raise Chef::Exceptions::InvalidPlatformVersion.new( msg )
diff --git a/spec/unit/provider/package/freebsd_next_gen_spec.rb b/spec/unit/provider/package/freebsd_next_gen_spec.rb
new file mode 100644
index 0000000000..6eb5faa55f
--- /dev/null
+++ b/spec/unit/provider/package/freebsd_next_gen_spec.rb
@@ -0,0 +1,204 @@
+#
+# Authors:: Richard Manyanza (liseki@nyikacraftsmen.com)
+# Copyright:: Copyright (c) 2013 Richard Manyanza
+# 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 'ostruct'
+
+
+describe Chef::Provider::Package::FreebsdNextGen 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("zsh")
+ @provider = Chef::Provider::Package::FreebsdNextGen.new(@new_resource, @run_context)
+ end
+
+
+ describe "initialization" do
+ it "should create a current resource with the name of the new resource" do
+ @provider.current_resource.is_a?(Chef::Resource::Package).should be_true
+ @provider.current_resource.name.should == 'zsh'
+ end
+ end
+
+
+ describe "loading current resource" do
+ before(:each) do
+ @provider.stub!(:current_installed_version).and_return(nil)
+ @provider.stub!(:ports_candidate_version).and_return("5.0.2_1")
+ @provider.stub!(:repo_candidate_version).and_return("5.0.2_5")
+ end
+
+ it "should set current version if the package is installed" do
+ @provider.should_receive(:current_installed_version).and_return("5.0.2")
+ @provider.load_current_resource
+ @provider.current_resource.version.should == "5.0.2"
+ end
+
+ it "should not set current version if the package is not installed" do
+ @provider.load_current_resource
+ @provider.current_resource.version.should be_nil
+ end
+
+ it "should set candidate version from port if building port" do
+ @new_resource.source('ports')
+ @provider.load_current_resource
+ @provider.candidate_version.should == "5.0.2_1"
+ end
+
+ it "should set candidate version from file if adding local or remote package" do
+ @new_resource.source('/root/packages/zsh-5.0.2_3.txz')
+ @provider.load_current_resource
+ @provider.candidate_version.should == "5.0.2_3"
+ end
+ end
+
+
+ describe "querying repository for package version" do
+ before(:each) do
+ @provider.stub!(:current_installed_version).and_return(nil)
+ @pkg_query = OpenStruct.new(:stdout => "5.0.2_1")
+ end
+
+ it "should make call to repository" do
+ @provider.should_receive(:shell_out!).with("pkg rquery '%v' zsh", :env => nil, :returns => [0,69]).and_return(@pkg_query)
+ @provider.load_current_resource
+ @provider.candidate_version.should == "5.0.2_1"
+ end
+
+ it "should be able to use custom repository option" do
+ @provider.should_receive(:shell_out!).with("pkg rquery -r http://pkgrepo.example.com '%v' zsh", :env => nil, :returns => [0,69]).and_return(@pkg_query)
+ @new_resource.options('-r http://pkgrepo.example.com')
+ @provider.load_current_resource
+ end
+ end
+
+
+ describe "querying package state" do
+ it "should return version number if package is installed" do
+ pkg_info = OpenStruct.new(:stdout => "zsh-4.3.6_7")
+ @provider.should_receive(:shell_out!).with('pkg info "zsh"', :env => nil, :returns => [0,70]).and_return(pkg_info)
+ @provider.send(:current_installed_version).should == "4.3.6_7"
+ end
+
+ it "should not return a version number if package is not installed" do
+ pkg_info = OpenStruct.new(:stdout => "pkg: No package(s) matching zsh")
+ @provider.should_receive(:shell_out!).with('pkg info "zsh"', :env => nil, :returns => [0,70]).and_return(pkg_info)
+ @provider.send(:current_installed_version).should be_nil
+ end
+
+ it "should return the port path for a valid port name" do
+ whereis = OpenStruct.new(:stdout => "zsh: /usr/ports/shells/zsh")
+ @provider.should_receive(:shell_out!).with("whereis -s zsh", :env => nil).and_return(whereis)
+ @provider.send(:port_path).should == '/usr/ports/shells/zsh'
+ end
+
+ it "should return the ports candidate version when given a valid port path" do
+ @provider.stub!(:port_path).and_return("/usr/ports/shells/zsh")
+ make_v = OpenStruct.new(:stdout => "4.3.6\n")
+ @provider.should_receive(:shell_out!).with("make -V PORTVERSION", {:cwd=>"/usr/ports/shells/zsh", :returns=>[0, 1], :env=>nil}).and_return(make_v)
+ @provider.send(:ports_candidate_version).should == "4.3.6"
+ end
+ end
+
+
+ describe "installing a binary package" do
+ before do
+ @provider.stub!(:current_installed_version).and_return(nil)
+ @status = OpenStruct.new(:status => true)
+ end
+
+ it "should be able to install from a package repository" do
+ @provider.should_receive(:shell_out!).with("pkg install -y zsh", :env => { 'LC_ALL' => nil }).and_return(@status)
+ @new_resource.source.should be_nil
+ @provider.install_package('zsh', nil)
+ end
+
+ it "should be able to install from a custom repository" do
+ @provider.should_receive(:shell_out!).with("pkg install -r http://pkgrepo.example.com zsh", :env => { 'LC_ALL' => nil }).and_return(@status)
+ @new_resource.source.should be_nil
+ @new_resource.options('-r http://pkgrepo.example.com')
+ @provider.install_package('zsh', nil)
+ end
+
+ it "should be able to install a local package" do
+ @provider.should_receive(:shell_out!).with("pkg add /root/packages/zsh-5.0.2.txz", :env => { 'LC_ALL' => nil }).and_return(@status)
+ @new_resource.source('/root/packages/zsh-5.0.2.txz')
+ @provider.install_package('zsh', '5.0.2')
+ end
+
+ it "should be able to install a remote package" do
+ @provider.should_receive(:shell_out!).with("pkg add http://pkgrepo.eg.com/All/zsh-5.0.2.txz", :env => { 'LC_ALL' => nil }).and_return(@status)
+ @new_resource.source('http://pkgrepo.eg.com/All/zsh-5.0.2.txz')
+ @provider.install_package('zsh', '5.0.2')
+ end
+ end
+
+
+ describe "building a binary package" do
+ before do
+ @provider.stub!(:current_installed_version).and_return(nil)
+ @status = OpenStruct.new(:status => true)
+ end
+
+ it "should handle a regular port name" do
+ @provider.should_receive(:shell_out!).with('whereis -s zsh', :env => nil).and_return(OpenStruct.new(:stdout => 'zsh: /usr/ports/shells/zsh'))
+ @provider.should_receive(:shell_out!).with('make -DBATCH install', :timeout => 1800, :env => nil, :cwd => '/usr/ports/shells/zsh').and_return(@status)
+ @new_resource.source('ports')
+ @provider.install_package('zsh', '5.0.2')
+ end
+
+ it "should handle a port name including section" do
+ @provider.should_receive(:shell_out!).with('make -DBATCH install', :timeout => 1800, :env => nil, :cwd => '/usr/ports/shells/zsh').and_return(@status)
+ @new_resource.package_name('shells/zsh')
+ @new_resource.source('ports')
+ @provider.install_package('zsh', '5.0.2')
+ end
+
+ it "should handle a custom port location" do
+ @provider.should_receive(:shell_out!).with('make -DBATCH install', :timeout => 1800, :env => nil, :cwd => '/master/ports/shells/zsh').and_return(@status)
+ @new_resource.package_name('/master/ports/shells/zsh')
+ @new_resource.source('ports')
+ @provider.install_package('zsh', '5.0.2')
+ end
+ end
+
+
+ describe "removing a binary package" do
+ before do
+ @status = OpenStruct.new(:status => true)
+ end
+
+ it "should be able to remove package with version number" do
+ @provider.should_receive(:shell_out!).with("pkg delete -y zsh-5.0.2_1", :env => nil).and_return(@status)
+ @provider.remove_package("zsh", "5.0.2_1")
+ end
+
+ it "should be able to remove package without version number" do
+ @provider.should_receive(:shell_out!).with("pkg delete -y zsh", :env => nil).and_return(@status)
+ @provider.remove_package("zsh", nil)
+ end
+
+ it "should be able to pass custom options" do
+ @provider.should_receive(:shell_out!).with("pkg delete -Df zsh-5.0.2_1", :env => nil).and_return(@status)
+ @new_resource.options('-Df')
+ @provider.remove_package("zsh", "5.0.2_1")
+ end
+ end
+end
diff --git a/spec/unit/version/platform_spec.rb b/spec/unit/version/platform_spec.rb
index 8b5b3503dc..69f42e58b2 100644
--- a/spec/unit/version/platform_spec.rb
+++ b/spec/unit/version/platform_spec.rb
@@ -30,7 +30,7 @@ describe Chef::Version::Platform do
end
describe "when creating valid Versions" do
- good_versions = %w(1 1.2 1.2.3 1000.80.50000 0.300.25 001.02.00003)
+ good_versions = %w(1 1.2 1.2.3 1000.80.50000 0.300.25 001.02.00003 1.2-STABLE 10.0-BETA3 9.1-RELEASE-p3)
good_versions.each do |v|
it "should accept '#{v}'" do
Chef::Version::Platform.new v