From ba40dab16dfa3e4b5cf9aae8ba40dfd856c27537 Mon Sep 17 00:00:00 2001 From: Thom May Date: Thu, 26 Apr 2018 15:02:10 +0100 Subject: [PATCH] Fix up creating archives Signed-off-by: Thom May --- lib/mixlib/archive.rb | 6 ++++ lib/mixlib/archive/lib_archive.rb | 28 +++++++++++++-- lib/mixlib/archive/tar.rb | 9 +++-- spec/fixtures/fixture_a | 1 + spec/fixtures/fixture_b | 1 + spec/fixtures/fixture_c | 1 + spec/mixlib/lib_archive_spec.rb | 57 +++++++++++++++++++++++++++++++ spec/mixlib/tar_spec.rb | 17 +++++++++ spec/spec_helper.rb | 15 ++++++++ 9 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 spec/fixtures/fixture_a create mode 100644 spec/fixtures/fixture_b create mode 100644 spec/fixtures/fixture_c create mode 100644 spec/mixlib/lib_archive_spec.rb diff --git a/lib/mixlib/archive.rb b/lib/mixlib/archive.rb index 6a259fa..1d9c849 100644 --- a/lib/mixlib/archive.rb +++ b/lib/mixlib/archive.rb @@ -1,12 +1,18 @@ require "mixlib/archive/tar" require "mixlib/archive/version" require "mixlib/log" +require "find" module Mixlib class Archive attr_reader :archiver alias_method :extractor, :archiver + def self.archive_directory(path, archive, gzip: false, format: :tar, compression: :none) + targets = Find.find(path).collect { |fn| fn } + new(archive).create(targets, gzip: gzip) + end + def initialize(archive, empty: false) @empty = empty diff --git a/lib/mixlib/archive/lib_archive.rb b/lib/mixlib/archive/lib_archive.rb index 29aa1ac..3e36c47 100644 --- a/lib/mixlib/archive/lib_archive.rb +++ b/lib/mixlib/archive/lib_archive.rb @@ -19,6 +19,7 @@ def initialize(archive, options = {}) def extract(destination, perms: true, ignore: []) ignore_re = Regexp.union(ignore) flags = perms ? ::Archive::EXTRACT_PERM : nil + FileUtils.mkdir_p(destination) Dir.chdir(destination) do reader = ::Archive::Reader.open_filename(@archive) @@ -47,17 +48,38 @@ def create(files, gzip: false) ::Archive.write_open_filename(archive, compression, format) do |tar| files.each do |fn| tar.new_entry do |entry| + content = nil entry.pathname = fn - entry.copy_stat(fn) - tar.write_header(entry) + stat = File.lstat(fn) if File.file?(fn) content = File.read(fn) - tar.write_data(content) + entry.size = content.size end + entry.mode = stat.mode + entry.filetype = resolve_type(stat.ftype) + entry.atime = stat.atime + entry.mtime = stat.mtime + entry.symlink = File.readlink(fn) if File.symlink?(fn) + tar.write_header(entry) + + tar.write_data(content) unless content.nil? end end end end + + def resolve_type(type) + case type + when "characterSpecial" + ::Archive::Entry::CHARACTER_SPECIAL + when "blockSpecial" + ::Archive::Entry::BLOCK_SPECIAL + when "link" + ::Archive::Entry::SYMBOLIC_LINK + else + ::Archive::Entry.const_get(type.upcase) + end + end end end end diff --git a/lib/mixlib/archive/tar.rb b/lib/mixlib/archive/tar.rb index 626b033..b3cb640 100644 --- a/lib/mixlib/archive/tar.rb +++ b/lib/mixlib/archive/tar.rb @@ -1,4 +1,5 @@ require "rubygems/package" +require "tempfile" require "zlib" module Mixlib @@ -85,10 +86,14 @@ def create(files, gzip: false) target.close if gzip - Zlib::GzipWriter.open(archive) do |gz| + Zlib::GzipWriter.open(archive, Zlib::BEST_COMPRESSION) do |gz| gz.mtime = File.mtime(target.path) gz.orig_name = File.basename(archive) - gz.write IO.binread(target.path) + File.open(target.path) do |file| + while (chunk = file.read(16 * 1024)) + gz.write(chunk) + end + end end else FileUtils.mv(target.path, archive) diff --git a/spec/fixtures/fixture_a b/spec/fixtures/fixture_a new file mode 100644 index 0000000..5bd4f75 --- /dev/null +++ b/spec/fixtures/fixture_a @@ -0,0 +1 @@ +fixture_a diff --git a/spec/fixtures/fixture_b b/spec/fixtures/fixture_b new file mode 100644 index 0000000..9e8889b --- /dev/null +++ b/spec/fixtures/fixture_b @@ -0,0 +1 @@ +fixture_b diff --git a/spec/fixtures/fixture_c b/spec/fixtures/fixture_c new file mode 100644 index 0000000..c8d4725 --- /dev/null +++ b/spec/fixtures/fixture_c @@ -0,0 +1 @@ +fixture_c diff --git a/spec/mixlib/lib_archive_spec.rb b/spec/mixlib/lib_archive_spec.rb new file mode 100644 index 0000000..f140409 --- /dev/null +++ b/spec/mixlib/lib_archive_spec.rb @@ -0,0 +1,57 @@ +require "spec_helper" + +describe Mixlib::Archive::LibArchive do + let(:tar_archive) { "#{fixtures_path}/test.tar" } + let(:tgz_archive) { "#{fixtures_path}/test.tgz" } + let(:tar_gz_archive) { "#{fixtures_path}/test.tar.gz" } + + let(:extraction) { lambda { |f| } } + + let(:destination) { Dir.mktmpdir } + + let(:gzip_header) { [0x1F, 0x8B].pack("C*") } + + before do + allow(IO).to receive(:binread).and_return(nil) + end + + after do + FileUtils.remove_entry destination + end + + describe "#reader" + + describe "#create" do + let(:test_root) { Dir.mktmpdir(nil) } + let(:archive_path) { File.join(test_root, "test.tar.gz") } + let(:file_paths) { %w{ . .. fixture_a fixture_b fixture_c } } + + context "using the correct options" do + it "requests the correct tar format" do + expect(::Archive).to receive(:write_open_filename).with(archive_path, 0, ::Archive::FORMAT_TAR_PAX_RESTRICTED) + Mixlib::Archive::LibArchive.new(archive_path).create([]) + end + + it "requests gzip compression" do + expect(::Archive).to receive(:write_open_filename).with(archive_path, ::Archive::COMPRESSION_GZIP, ::Archive::FORMAT_TAR_PAX_RESTRICTED) + Mixlib::Archive::LibArchive.new(archive_path).create([], gzip: true) + end + + it "requests no compression" do + expect(::Archive).to receive(:write_open_filename).with(archive_path, ::Archive::COMPRESSION_NONE, ::Archive::FORMAT_TAR_PAX_RESTRICTED) + Mixlib::Archive::LibArchive.new(archive_path).create([], gzip: false) + end + end + + it "creates a tarball" do + Dir.chdir(fixtures_path) do + Mixlib::Archive::LibArchive.new(archive_path).create(file_paths, gzip: true) + end + expect(File.file?(archive_path)).to be true + target = File.join(test_root, "target") + Mixlib::Archive::LibArchive.new(archive_path).extract(target) + expect(Dir.entries(target)).to match_array file_paths + end + + end +end diff --git a/spec/mixlib/tar_spec.rb b/spec/mixlib/tar_spec.rb index 03e4d6c..10e8de3 100644 --- a/spec/mixlib/tar_spec.rb +++ b/spec/mixlib/tar_spec.rb @@ -56,4 +56,21 @@ end end + + describe "#create" do + let(:test_root) { Dir.mktmpdir(nil) } + let(:archive_path) { File.join(test_root, "test.tar.gz") } + let(:file_paths) { %w{ . .. fixture_a fixture_b fixture_c } } + + it "creates a tarball" do + Dir.chdir(fixtures_path) do + Mixlib::Archive::Tar.new(archive_path).create(file_paths, gzip: true) + end + expect(File.file?(archive_path)).to be true + target = File.join(test_root, "target") + Mixlib::Archive::LibArchive.new(archive_path).extract(target, ignore: %w{ . .. }) + expect(Dir.entries(target)).to match_array file_paths + end + + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 331ffa0..8366e4d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,3 +3,18 @@ require "mixlib/archive" require "mixlib/archive/tar" require "mixlib/archive/lib_archive" + +module Fixtures + def fixtures_path + spec_root.join("fixtures") + end + + def spec_root + Pathname.new(File.expand_path(File.dirname(__FILE__))) + end +end + +RSpec.configure do |config| + config.raise_errors_for_deprecations! + config.include Fixtures +end