[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