diff --git a/lib/puppet/module_tool/applications/unpacker.rb b/lib/puppet/module_tool/applications/unpacker.rb index e9b0b50d1..3ed6058f3 100644 --- a/lib/puppet/module_tool/applications/unpacker.rb +++ b/lib/puppet/module_tool/applications/unpacker.rb @@ -1,48 +1,67 @@ require 'pathname' require 'tmpdir' module Puppet::ModuleTool module Applications class Unpacker < Application def initialize(filename, options = {}) @filename = Pathname.new(filename) parsed = parse_filename(filename) super(options) @module_dir = Pathname.new(options[:target_dir]) + parsed[:dir_name] end def run extract_module_to_install_dir # Return the Pathname object representing the directory where the # module release archive was unpacked the to, and the module release # name. @module_dir end + # Obtain a suitable temporary path for building and unpacking tarballs + # + # @return [Pathname] path to temporary build location + def build_dir + Puppet::Forge::Cache.base_path + "tmp-unpacker-#{Digest::SHA1.hexdigest(@filename.basename.to_s)}" + end + private def extract_module_to_install_dir delete_existing_installation_or_abort! - build_dir = Puppet::Forge::Cache.base_path + "tmp-unpacker-#{Digest::SHA1.hexdigest(@filename.basename.to_s)}" build_dir.mkpath begin - unless system "tar xzf #{@filename} -C #{build_dir}" - raise RuntimeError, "Could not extract contents of module archive." + begin + if Facter.value('operatingsystem') == "Solaris" + # Solaris tar is not as safe and works differently, so we prefer + # gnutar instead. + if Puppet::Util.which('gtar') + Puppet::Util.execute("gtar xzf #{@filename} -C #{build_dir}") + else + raise RuntimeError, "Cannot find the command 'gtar'. Make sure GNU tar is installed, and is in your PATH." + end + else + Puppet::Util.execute("tar xzf #{@filename} -C #{build_dir}") + end + rescue Puppet::ExecutionFailure => e + raise RuntimeError, "Could not extract contents of module archive: #{e.message}" end + # grab the first directory extracted = build_dir.children.detect { |c| c.directory? } FileUtils.mv extracted, @module_dir ensure build_dir.rmtree end end def delete_existing_installation_or_abort! return unless @module_dir.exist? FileUtils.rm_rf(@module_dir, :secure => true) end end end end diff --git a/spec/unit/module_tool/applications/unpacker_spec.rb b/spec/unit/module_tool/applications/unpacker_spec.rb new file mode 100644 index 000000000..7db0b7d0a --- /dev/null +++ b/spec/unit/module_tool/applications/unpacker_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' +require 'puppet/module_tool/applications' +require 'puppet_spec/modules' + +describe Puppet::ModuleTool::Applications::Unpacker, :fails_on_windows => true do + include PuppetSpec::Files + + let(:target) { tmpdir("unpacker") } + + context "initialization" do + it "should support filename and basic options" do + Puppet::ModuleTool::Applications::Unpacker.new("myusername-mytarball-1.0.0.tar.gz", :target_dir => target) + end + + it "should raise ArgumentError when filename is invalid" do + expect { Puppet::ModuleTool::Applications::Unpacker.new("invalid.tar.gz", :target_dir => target) }.to raise_error(ArgumentError) + end + end + + context "#run" do + let(:cache_base_path) { Pathname.new(tmpdir("unpacker")) } + let(:filename) { tmpdir("module") + "/myusername-mytarball-1.0.0.tar.gz" } + let(:build_dir) { Pathname.new(tmpdir("build_dir")) } + let(:unpacker) do + Puppet::ModuleTool::Applications::Unpacker.new(filename, :target_dir => target) + end + + before :each do + # Mock redhat for most test cases + Facter.stubs(:value).with("operatingsystem").returns("Redhat") + build_dir.stubs(:mkpath => nil, :rmtree => nil, :children => []) + unpacker.stubs(:build_dir).at_least_once.returns(build_dir) + FileUtils.stubs(:mv) + end + + context "on linux" do + it "should attempt to untar file to temporary location using system tar" do + Puppet::Util.expects(:execute).with("tar xzf #{filename} -C #{build_dir}").returns(true) + unpacker.run + end + end + + context "on solaris" do + before :each do + Facter.expects(:value).with("operatingsystem").returns("Solaris") + end + + it "should attempt to untar file to temporary location using gnu tar" do + Puppet::Util.stubs(:which).with('gtar').returns('/usr/sfw/bin/gtar') + Puppet::Util.expects(:execute).with("gtar xzf #{filename} -C #{build_dir}").returns(true) + unpacker.run + end + + it "should throw exception if gtar is not in the path exists" do + Puppet::Util.stubs(:which).with('gtar').returns(nil) + expect { unpacker.run }.to raise_error RuntimeError, "Cannot find the command 'gtar'. Make sure GNU tar is installed, and is in your PATH." + end + end + end + +end