[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, experimental, updated. debian/2.6.8-1-844-g7ec39d5

Matt Robinson matt at puppetlabs.com
Tue May 10 08:06:59 UTC 2011


The following commit has been merged in the experimental branch:
commit af42367dce6ab1ad18a1a8d10e4d54b5accae449
Author: Richard Crowley <r at rcrowley.org>
Date:   Tue Mar 1 20:44:11 2011 +0000

    (#6527) Added pip package provider.
    
    Python's pip package manager is analogous to RubyGems and should be included in Puppet.
    
    Reviewed-by: Matt Robinson <matt at puppetlabs.com>

diff --git a/lib/puppet/provider/package/pip.rb b/lib/puppet/provider/package/pip.rb
new file mode 100644
index 0000000..0079756
--- /dev/null
+++ b/lib/puppet/provider/package/pip.rb
@@ -0,0 +1,115 @@
+# Puppet package provider for Python's `pip` package management frontend.
+# <http://pip.openplans.org/>
+
+require 'puppet/provider/package'
+require 'xmlrpc/client'
+
+Puppet::Type.type(:package).provide :pip,
+  :parent => ::Puppet::Provider::Package do
+
+  desc "Python packages via `pip`."
+
+  has_feature :installable, :uninstallable, :upgradeable, :versionable
+
+  # Parse lines of output from `pip freeze`, which are structured as
+  # _package_==_version_.
+  def self.parse(line)
+    if line.chomp =~ /^([^=]+)==([^=]+)$/
+      {:ensure => $2, :name => $1, :provider => name}
+    else
+      nil
+    end
+  end
+
+  # Return an array of structured information about every installed package
+  # that's managed by `pip` or an empty array if `pip` is not available.
+  def self.instances
+    packages = []
+    execpipe "#{command :pip} freeze" do |process|
+      process.collect do |line|
+        next unless options = parse(line)
+        packages << new(options)
+      end
+    end
+    packages
+  rescue Puppet::DevError
+    []
+  end
+
+  # Return structured information about a particular package or `nil` if
+  # it is not installed or `pip` itself is not available.
+  def query
+    execpipe "#{command :pip} freeze" do |process|
+      process.each do |line|
+        options = self.class.parse(line)
+        return options if options[:name] == @resource[:name]
+      end
+    end
+    nil
+  rescue Puppet::DevError
+    nil
+  end
+
+  # Ask the PyPI API for the latest version number.  There is no local
+  # cache of PyPI's package list so this operation will always have to
+  # ask the web service.
+  def latest
+    client = XMLRPC::Client.new2("http://pypi.python.org/pypi")
+    client.http_header_extra = {"Content-Type" => "text/xml"}
+    result = client.call("package_releases", @resource[:name])
+    result.first
+  end
+
+  # Install a package.  The ensure parameter may specify installed,
+  # latest, a version number, or, in conjunction with the source
+  # parameter, an SCM revision.  In that case, the source parameter
+  # gives the fully-qualified URL to the repository.
+  def install
+    args = %w{install -q}
+    if @resource[:source]
+      args << "-e"
+      if String === @resource[:ensure]
+        args << "#{@resource[:source]}@#{@resource[:ensure]}#egg=#{
+          @resource[:name]}"
+      else
+        args << "#{@resource[:source]}#egg=#{@resource[:name]}"
+      end
+    else
+      case @resource[:ensure]
+      when String
+        args << "#{@resource[:name]}==#{@resource[:ensure]}"
+      when :latest
+        args << "--upgrade" << @resource[:name]
+      else
+        args << @resource[:name]
+      end
+    end
+    lazy_pip *args
+  end
+
+  # Uninstall a package.  Uninstall won't work reliably on Debian/Ubuntu
+  # unless this issue gets fixed.
+  # <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=562544>
+  def uninstall
+    lazy_pip "uninstall", "-y", "-q", @resource[:name]
+  end
+
+  def update
+    install
+  end
+
+  # Execute a `pip` command.  If Puppet doesn't yet know how to do so,
+  # try to teach it and if even that fails, raise the error.
+  private
+  def lazy_pip(*args)
+    pip *args
+  rescue NoMethodError => e
+    if pathname = `which pip`.chomp
+      self.class.commands :pip => pathname
+      pip *args
+    else
+      raise e
+    end
+  end
+
+end
diff --git a/spec/unit/provider/package/pip_spec.rb b/spec/unit/provider/package/pip_spec.rb
new file mode 100644
index 0000000..ad8201a
--- /dev/null
+++ b/spec/unit/provider/package/pip_spec.rb
@@ -0,0 +1,190 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
+
+provider_class = Puppet::Type.type(:package).provider(:pip)
+
+describe provider_class do
+
+  before do
+    @resource = stub("resource")
+    @provider = provider_class.new
+    @provider.instance_variable_set(:@resource, @resource)
+  end
+
+  describe "parse" do
+
+    it "should return a hash on valid input" do
+      provider_class.parse("Django==1.2.5").should == {
+        :ensure => "1.2.5",
+        :name => "Django",
+        :provider => :pip,
+      }
+    end
+
+    it "should return nil on invalid input" do
+      provider_class.parse("foo").should == nil
+    end
+
+  end
+
+  describe "instances" do
+
+    it "should return an array when pip is present" do
+      provider_class.expects(:command).with(:pip).returns("/fake/bin/pip")
+      p = stub("process")
+      p.expects(:collect).yields("Django==1.2.5")
+      provider_class.expects(:execpipe).with("/fake/bin/pip freeze").yields(p)
+      provider_class.instances
+    end
+
+    it "should return an empty array when pip is missing" do
+      provider_class.expects(:command).with(:pip).raises(
+        Puppet::DevError.new("Pretend pip isn't installed."))
+      provider_class.instances.should == []
+    end
+
+  end
+
+  describe "query" do
+
+    before do
+      @resource.stubs(:[]).with(:name).returns("Django")
+    end
+
+    it "should return a hash when pip and the package are present" do
+      @provider.expects(:command).with(:pip).returns("/fake/bin/pip")
+      p = stub("process")
+      p.expects(:each).yields("Django==1.2.5")
+      @provider.expects(:execpipe).with("/fake/bin/pip freeze").yields(p)
+      @provider.query.should == {
+        :ensure => "1.2.5",
+        :name => "Django",
+        :provider => :pip,
+      }
+    end
+
+    it "should return nil when pip is missing" do
+      @provider.expects(:command).with(:pip).raises(
+        Puppet::DevError.new("Pretend pip isn't installed."))
+      @provider.query.should == nil
+    end
+
+    it "should return nil when the package is missing" do
+      @provider.expects(:command).with(:pip).returns("/fake/bin/pip")
+      p = stub("process")
+      p.expects(:each).yields("sdsfdssdhdfyjymdgfcjdfjxdrssf==0.0.0")
+      @provider.expects(:execpipe).with("/fake/bin/pip freeze").yields(p)
+      @provider.query.should == nil
+    end
+
+  end
+
+  describe "latest" do
+
+    it "should find a version number for Django" do
+      @resource.stubs(:[]).with(:name).returns "Django"
+      @provider.latest.should_not == nil
+    end
+
+    it "should not find a version number for sdsfdssdhdfyjymdgfcjdfjxdrssf" do
+      @resource.stubs(:[]).with(:name).returns "sdsfdssdhdfyjymdgfcjdfjxdrssf"
+      @provider.latest.should == nil
+    end
+
+  end
+
+  describe "install" do
+
+    before do
+      @resource.stubs(:[]).with(:name).returns("sdsfdssdhdfyjymdgfcjdfjxdrssf")
+      @url = "git+https://example.com/sdsfdssdhdfyjymdgfcjdfjxdrssf.git"
+    end
+
+    it "should install" do
+      @resource.stubs(:[]).with(:ensure).returns(:installed)
+      @resource.stubs(:[]).with(:source).returns(nil)
+      @provider.expects(:lazy_pip).with do |*args|
+        "install" == args[0] && "sdsfdssdhdfyjymdgfcjdfjxdrssf" == args[-1]
+      end.returns nil
+      @provider.install
+    end
+
+    it "should install from SCM" do
+      @resource.stubs(:[]).with(:ensure).returns(:installed)
+      @resource.stubs(:[]).with(:source).returns(@url)
+      @provider.expects(:lazy_pip).with do |*args|
+        "#{@url}#egg=sdsfdssdhdfyjymdgfcjdfjxdrssf" == args[-1]
+      end.returns nil
+      @provider.install
+    end
+
+    it "should install a particular revision" do
+      @resource.stubs(:[]).with(:ensure).returns("0123456")
+      @resource.stubs(:[]).with(:source).returns(@url)
+      @provider.expects(:lazy_pip).with do |*args|
+        "#{@url}@0123456#egg=sdsfdssdhdfyjymdgfcjdfjxdrssf" == args[-1]
+      end.returns nil
+      @provider.install
+    end
+
+    it "should install a particular version" do
+      @resource.stubs(:[]).with(:ensure).returns("0.0.0")
+      @resource.stubs(:[]).with(:source).returns(nil)
+      @provider.expects(:lazy_pip).with do |*args|
+        "sdsfdssdhdfyjymdgfcjdfjxdrssf==0.0.0" == args[-1]
+      end.returns nil
+      @provider.install
+    end
+
+    it "should upgrade" do
+      @resource.stubs(:[]).with(:ensure).returns(:latest)
+      @resource.stubs(:[]).with(:source).returns(nil)
+      @provider.expects(:lazy_pip).with do |*args|
+        "--upgrade" == args[-2] && "sdsfdssdhdfyjymdgfcjdfjxdrssf" == args[-1]
+      end.returns nil
+      @provider.install
+    end
+
+  end
+
+  describe "uninstall" do
+
+    it "should uninstall" do
+      @resource.stubs(:[]).with(:name).returns("sdsfdssdhdfyjymdgfcjdfjxdrssf")
+      @provider.expects(:lazy_pip).returns(nil)
+      @provider.uninstall
+    end
+
+  end
+
+  describe "update" do
+
+    it "should just call install" do
+      @provider.expects(:install).returns(nil)
+      @provider.update
+    end
+
+  end
+
+  describe "lazy_pip" do
+
+    it "should succeed if pip is present" do
+      @provider.stubs(:pip).returns(nil)
+      @provider.method(:lazy_pip).call "freeze"
+    end
+
+    it "should retry if pip has not yet been found" do
+      @provider.stubs(:pip).raises(NoMethodError).returns("/fake/bin/pip")
+      @provider.method(:lazy_pip).call "freeze"
+    end
+
+    it "should fail if pip is missing" do
+      @provider.stubs(:pip).twice.raises(NoMethodError)
+      expect { @provider.method(:lazy_pip).call("freeze") }.to \
+        raise_error(NoMethodError)
+    end
+
+  end
+
+end

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list