* Employs port -q installed, much faster than port list installed * Handles upgrades correctly now * Makes use of internal port revision for ensure => latest upgrades * Versionable, now works with ensure => specified_version * Does not handle port variants at all yet. * Does not allow manual specification of revision, only version * Test coverage expanded
Signed-off-by: Nigel Kersten <ni...@puppetlabs.com> --- lib/puppet/provider/package/darwinport.rb | 86 ------------------- lib/puppet/provider/package/macports.rb | 103 +++++++++++++++++++++++ spec/unit/provider/package/macports_spec.rb | 121 +++++++++++++++++++++++++++ 3 files changed, 224 insertions(+), 86 deletions(-) delete mode 100755 lib/puppet/provider/package/darwinport.rb create mode 100755 lib/puppet/provider/package/macports.rb create mode 100755 spec/unit/provider/package/macports_spec.rb diff --git a/lib/puppet/provider/package/darwinport.rb b/lib/puppet/provider/package/darwinport.rb deleted file mode 100755 index c5f9ba2..0000000 --- a/lib/puppet/provider/package/darwinport.rb +++ /dev/null @@ -1,86 +0,0 @@ -require 'puppet/provider/package' - -Puppet::Type.type(:package).provide :darwinport, :parent => Puppet::Provider::Package do - desc "Package management using DarwinPorts on OS X." - - confine :operatingsystem => :darwin - commands :port => "/opt/local/bin/port" - - def self.eachpkgashash - # list out all of the packages - open("| #{command(:port)} list installed") { |process| - regex = %r{(\S+)\s+@(\S+)\s+(\S+)} - fields = [:name, :ensure, :location] - hash = {} - - # now turn each returned line into a package object - process.each { |line| - hash.clear - - if match = regex.match(line) - fields.zip(match.captures) { |field,value| - hash[field] = value - } - - hash.delete :location - hash[:provider] = self.name - yield hash.dup - else - raise Puppet::DevError, - "Failed to match dpkg line #{line}" - end - } - } - end - - def self.instances - packages = [] - - eachpkgashash do |hash| - packages << new(hash) - end - - packages - end - - def install - should = @resource.should(:ensure) - - # Seems like you can always say 'upgrade' - output = port "upgrade", @resource[:name] - if output =~ /^Error: No port/ - raise Puppet::ExecutionFailure, "Could not find package #{@resource[:name]}" - end - end - - def query - version = nil - self.class.eachpkgashash do |hash| - return hash if hash[:name] == @resource[:name] - end - - nil - end - - def latest - info = port :search, "^#{@resource[:name]}$" - - if $CHILD_STATUS != 0 or info =~ /^Error/ - return nil - end - - ary = info.split(/\s+/) - version = ary[2].sub(/^@/, '') - - version - end - - def uninstall - port :uninstall, @resource[:name] - end - - def update - install - end -end - diff --git a/lib/puppet/provider/package/macports.rb b/lib/puppet/provider/package/macports.rb new file mode 100755 index 0000000..c6d3f8b --- /dev/null +++ b/lib/puppet/provider/package/macports.rb @@ -0,0 +1,103 @@ +require 'puppet/provider/package' + +Puppet::Type.type(:package).provide :macports, :parent => Puppet::Provider::Package do + desc "Package management using MacPorts on OS X. + + Supports MacPorts versions and revisions, but not variants. + When specifying a version in the Puppet DSL, do not include the revision. + Revisions are only used internally for ensuring the latest version/revision of a port. + " + + confine :operatingsystem => :darwin + commands :port => "/opt/local/bin/port" + + has_feature :installable + has_feature :uninstallable + has_feature :upgradeable + has_feature :versionable + + + def self.parse_installed_query_line(line) + regex = /(\S+)\s+@(\S+)_(\S+)\s+\(active\)/ + fields = [:name, :ensure, :revision] + hash_from_line(line, regex, fields) + end + + def self.parse_info_query_line(line) + regex = /(\S+)\s+(\S+)/ + fields = [:version, :revision] + hash_from_line(line, regex, fields) + end + + def self.hash_from_line(line, regex, fields) + hash = {} + if match = regex.match(line) + fields.zip(match.captures) { |field, value| + hash[field] = value + } + hash[:provider] = self.name + return hash + end + nil + end + + def self.instances + packages = [] + port("-q", :installed).each do |line| + if hash = parse_installed_query_line(line) + packages << new(hash) + end + end + packages + end + + def install + should = @resource.should(:ensure) + if [:latest, :installed, :present].include?(should) + output = port("-q", :install, @resource[:name]) + else + output = port("-q", :install, @resource[:name], "@#{should}") + end + # MacPorts now correctly exits non-zero with appropriate errors in + # situations where a port cannot be found or installed. + end + + def query + return self.class.parse_installed_query_line(port("-q", :installed, @resource[:name])) + end + + def latest + # We need both the version and the revision to be confident + # we've got the latest revision of a specific version + # Note we're still not doing anything with variants here. + info_line = port("-q", :info, "--line", "--version", "--revision", @resource[:name]) + return nil if info_line == "" + + if newest = self.class.parse_info_query_line(info_line) + current = query + # We're doing some fiddling behind the scenes here to cope with updated revisions. + # If we're already at the latest version/revision, then just return the version + # so the current and desired values match. Otherwise return version and revision + # to trigger an upgrade to the latest revision. + if newest[:version] == current[:ensure] and newest[:revision] == current[:revision] + return current[:ensure] + else + return "#{newest[:version]}_#{newest[:revision]}" + end + end + nil + end + + def uninstall + port("-q", :uninstall, @resource[:name]) + end + + def update + if query[:name] == @resource[:name] # 'port upgrade' cannot install new ports + port("-q", :upgrade, @resource[:name]) + else + install + end + end +end + diff --git a/spec/unit/provider/package/macports_spec.rb b/spec/unit/provider/package/macports_spec.rb new file mode 100755 index 0000000..958f105 --- /dev/null +++ b/spec/unit/provider/package/macports_spec.rb @@ -0,0 +1,121 @@ +#!/usr/bin/env ruby + +Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } + +provider = Puppet::Type.type(:package).provider(:macports) + +describe provider do + before do + @resource_name = "foo" + @resource = Puppet::Type.type(:package).new(:name => @resource_name, + :provider => :macports) + @provider = @resource.provider + @provider.expects(:execute).never # forbid "manual" executions + @current_hash = {:name => @resource_name, + :ensure => "1.2.3", + :revision => "1", + :provider => :macports} + end + + it "should be installable" do + provider.should be_installable + end + + it "should be uninstallable" do + provider.should be_uninstallable + end + it "should be upgradeable" do + provider.should be_upgradeable + end + + it "should be versionable" do + provider.should be_versionable + end + + describe "when listing all instances" do + it "should call port -q installed" do + provider.expects(:port).with("-q", :installed).returns("") + provider.instances + end + + it "should create instances from active ports" do + provider.expects(:port).returns("foo @1.234.5_2 (active)") + provider.instances.size.should == 1 + end + + it "should ignore ports that aren't activated" do + provider.expects(:port).returns("foo @1.234.5_2") + provider.instances.size.should == 0 + end + end + + describe "when installing" do + it "should not specify a version when ensure is set to latest" do + @resource[:ensure] = :latest + @provider.expects(:port).with { |flag, method, name, version| + version.should be_nil + } + @provider.install + end + + it "should not specify a version when ensure is set to present" do + @resource[:ensure] = :present + @provider.expects(:port).with { |flag, method, name, version| + version.should be_nil + } + @provider.install + end + + it "should specify a version when ensure is set to a version" do + @resource[:ensure] = "1.2.3" + @provider.expects(:port).with { |flag, method, name, version| + version.should be + } + @provider.install + end + end + + describe "when querying for the latest version" do + before do + @new_info_line = "1.2.3 2" + @infoargs = ["-q", :info, "--line", "--version", "--revision", @resource_name] + end + it "should return nil when the package cannot be found" do + @resource[:name] = @resource_name + @provider.expects(:port).returns("") + @provider.latest.should == nil + end + + it "should return the current version if the installed port has the same revision" do + @current_hash[:revision] = "2" + @provider.expects(:port).with(*@infoargs).returns(@new_info_line) + @provider.expects(:query).returns(@current_hash) + @provider.latest.should == @current_hash[:ensure] + end + + it "should return the new version_revision if the installed port has a lower revision" do + @current_hash[:revision] = "1" + @provider.expects(:port).with(*@infoargs).returns(@new_info_line) + @provider.expects(:query).returns(@current_hash) + @provider.latest.should == "1.2.3_2" + end + end + + describe "when updating a port" do + it "should execute port upgrade if the port is installed" do + @resource[:name] = @resource_name + @resource[:ensure] = :present + @provider.expects(:query).returns(@current_hash) + @provider.expects(:port).with("-q", :upgrade, @resource_name) + @provider.update + end + + it "should execute port install if the port is not installed" do + @resource[:name] = @resource_name + @resource[:ensure] = :present + @provider.expects(:query).returns("") + @provider.expects(:port).with("-q", :install, @resource_name) + @provider.update + end + end +end -- 1.7.4.1 -- You received this message because you are subscribed to the Google Groups "Puppet Developers" group. To post to this group, send email to puppet-dev@googlegroups.com. To unsubscribe from this group, send email to puppet-dev+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.