Please review pull request #332: Ticket/2.7.x/11803 pmt should have an uninstall command opened by (kelseyhightower)
Description:
Without this patch, the module face does not have an action for
uninstalling Puppet modules.
This patch adds an uninstall action to the module face, with initial
support for uninstalling Puppet modules from the default module paths or
specific directories.
This patch also includes the related spec tests.
Example Usage:
Running the following commands will uninstall a module from all
module paths or a specific directory
$ puppet module uninstall apache
Removed /etc/puppet/modules/apache
$ puppet module uninstall --target-directory /usr/share/puppet/modules apache
Removed /usr/share/puppet/modules/apache
Running the Tests:
The uninstall spec tests can be run with the following commands:$ rspec spec/unit/face/module/uninstall_spec.rb $ rspec spec/unit/module_tool/uninstaller_spec.rb
- Opened: Fri Jan 13 13:16:36 UTC 2012
- Based on: puppetlabs:2.7.x (2ac94f94b9af6d8025ee0e771c4c7141ca9705c6)
- Requested merge: kelseyhightower:ticket/2.7.x/11803_PMT_should_have_an_uninstall_command (b350f87fac47db58096eae1c21da2825cab3f76b)
Diff follows:
diff --git a/lib/puppet/face/module/uninstall.rb b/lib/puppet/face/module/uninstall.rb
new file mode 100644
index 0000000..19c983e
--- /dev/null
+++ b/lib/puppet/face/module/uninstall.rb
@@ -0,0 +1,50 @@
+Puppet::Face.define(:module, '1.0.0') do
+ action(:uninstall) do
+ summary "Uninstall a puppet module."
+ description <<-EOT
+ Uninstall a puppet module from the modulepath or a specific
+ target directory which defaults to
+ #{Puppet.settings[:modulepath].split(File::PATH_SEPARATOR).join(', ')}.
+ EOT
+
+ returns "Array of strings representing paths of uninstalled files."
+
+ examples <<-EOT
+ Uninstall a module from all directories in the modulepath:
+
+ $ puppet module uninstall ssh
+ Removed /etc/puppet/modules/ssh
+
+ Uninstall a module from a specific directory:
+
+ $ puppet module uninstall --target-directory /usr/share/puppet/modules ssh
+ Removed /usr/share/puppet/modules/ssh
+ EOT
+
+ arguments "<name>"
+
+ option "--target-directory=", "-t=" do
+ default_to { Puppet.settings[:modulepath].split(File::PATH_SEPARATOR) }
+ summary "The target directory to search from modules."
+ description <<-EOT
+ The target directory to search for modules.
+ EOT
+ end
+
+ when_invoked do |name, options|
+
+ if options[:target_directory].is_a?(Array)
+ options[:target_directories] = options[:target_directory]
+ else
+ options[:target_directories] = [ options[:target_directory] ]
+ end
+ options.delete(:target_directory)
+
+ Puppet::Module::Tool::Applications::Uninstaller.run(name, options)
+ end
+
+ when_rendering :console do |removed_modules|
+ removed_modules.map { |path| "Removed #{path}" }.join('\n')
+ end
+ end
+end
diff --git a/lib/puppet/module_tool/applications.rb b/lib/puppet/module_tool/applications.rb
index e7d54dc..24bcbe2 100644
--- a/lib/puppet/module_tool/applications.rb
+++ b/lib/puppet/module_tool/applications.rb
@@ -8,5 +8,6 @@ module Applications
require 'puppet/module_tool/applications/installer'
require 'puppet/module_tool/applications/searcher'
require 'puppet/module_tool/applications/unpacker'
+ require 'puppet/module_tool/applications/uninstaller'
end
end
diff --git a/lib/puppet/module_tool/applications/uninstaller.rb b/lib/puppet/module_tool/applications/uninstaller.rb
new file mode 100644
index 0000000..e7bcaf5
--- /dev/null
+++ b/lib/puppet/module_tool/applications/uninstaller.rb
@@ -0,0 +1,33 @@
+module Puppet::Module::Tool
+ module Applications
+ class Uninstaller < Application
+
+ def initialize(name, options = {})
+ @name = name
+ @target_directories = options[:target_directories]
+ @removed_dirs = []
+ end
+
+ def run
+ uninstall
+ Puppet.notice "#{@name} is not installed" if @removed_dirs.empty?
+ @removed_dirs
+ end
+
+ private
+
+ def uninstall
+ # TODO: Check for broken dependencies before uninstalling modules.
+ #
+ # Search each path in the target directories for the specified module
+ # and delete the directory.
+ @target_directories.each do |target|
+ if File.directory? target
+ module_path = File.join(target, @name)
+ @removed_dirs << FileUtils.rm_rf(module_path).first if File.directory?(module_path)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/unit/face/module/uninstall_spec.rb b/spec/unit/face/module/uninstall_spec.rb
new file mode 100644
index 0000000..6f3be6e
--- /dev/null
+++ b/spec/unit/face/module/uninstall_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+require 'puppet/face'
+require 'puppet/module_tool'
+
+describe "puppet module uninstall" do
+ subject { Puppet::Face[:module, :current] }
+
+ let(:options) do
+ {}
+ end
+
+ describe "option validation" do
+ context "without any options" do
+ it "should require a name" do
+ pattern = /wrong number of arguments/
+ expect { subject.uninstall }.to raise_error ArgumentError, pattern
+ end
+
+ it "should not require any options" do
+ Puppet::Module::Tool::Applications::UnInstaller.expects(:run).once
+ subject.uninstall("puppetlabs-apache")
+ end
+ end
+
+ it "should accept the --target-directory option" do
+ options[:target_directory] = "/foo/puppet/modules"
+ expected_options = { :target_directories => ["/foo/puppet/modules"] }
+ Puppet::Module::Tool::Applications::UnInstaller.expects(:run).with("puppetlabs-apache", expected_options).once
+ subject.uninstall("puppetlabs-apache", options)
+ end
+ end
+
+ describe "inline documentation" do
+ subject { Puppet::Face[:module, :current].get_action :uninstall }
+
+ its(:summary) { should =~ /uninstall.*module/im }
+ its(:description) { should =~ /uninstall.*module/im }
+ its(:returns) { should =~ /array of strings/i }
+ its(:examples) { should_not be_empty }
+
+ %w{ license copyright summary description returns examples }.each do |doc|
+ context "of the" do
+ its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ }
+ end
+ end
+ end
+end
diff --git a/spec/unit/module_tool/uninstaller_spec.rb b/spec/unit/module_tool/uninstaller_spec.rb
new file mode 100644
index 0000000..ae35b8b
--- /dev/null
+++ b/spec/unit/module_tool/uninstaller_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+require 'puppet/module_tool'
+require 'tmpdir'
+
+describe Puppet::Module::Tool::Applications::Uninstaller do
+ include PuppetSpec::Files
+
+ describe "instances" do
+ before do
+ @tmp_module_path1 = tmpdir("uninstaller_module_path1")
+ @tmp_module_path2 = tmpdir("uninstaller_module_path2")
+ @options = {
+ :target_directories => [ @tmp_module_path1, @tmp_module_path2 ]
+ }
+ end
+
+ it "should return an empty list if the module is not installed" do
+ described_class.new('foo', @options).run.should == []
+ end
+
+ it "should uninstall an installed module" do
+ foo_module_path = File.join(@tmp_module_path1, 'foo')
+ Dir.mkdir(foo_module_path)
+ described_class.new('foo', @options).run.should == [ foo_module_path ]
+ end
+
+ it "should only uninstall the requested module" do
+ foo_module_path = File.join(@tmp_module_path1, 'foo')
+ bar_module_path = File.join(@tmp_module_path1, 'bar')
+ Dir.mkdir(foo_module_path)
+ Dir.mkdir(bar_module_path)
+ described_class.new('foo', @options).run.should == [ foo_module_path ]
+ end
+
+ it "should uninstall the module from all target directories" do
+ foo1_module_path = File.join(@tmp_module_path1, 'foo')
+ foo2_module_path = File.join(@tmp_module_path2, 'foo')
+ Dir.mkdir(foo1_module_path)
+ Dir.mkdir(foo2_module_path)
+ described_class.new('foo', @options).run.should == [ foo1_module_path, foo2_module_path ]
+ end
+ end
+end
-- You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to [email protected].
For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.
