diff options
author | Tim Smith <tsmith@chef.io> | 2018-12-06 22:22:48 -0800 |
---|---|---|
committer | Tim Smith <tsmith@chef.io> | 2019-04-15 10:27:10 -0700 |
commit | 49ca8ca5070d171b8d64d3158f93531ee0573c4b (patch) | |
tree | 3241093f3747132c967217a562f51f8d41262da5 | |
parent | ce11470525cf03df6b464112db9501d7c49fd29b (diff) | |
download | chef-49ca8ca5070d171b8d64d3158f93531ee0573c4b.tar.gz |
Add a new archive_file resource from the libarchive cookbook
This adds the archive_file resource from the libarchive cookbook. I've updated the property names in that cookbook to allow us to add a :create action later on. This code matches the code in the cookbook nearly 100%.
Signed-off-by: Tim Smith <tsmith@chef.io>
-rw-r--r-- | Gemfile.lock | 4 | ||||
-rw-r--r-- | chef.gemspec | 3 | ||||
-rw-r--r-- | lib/chef/resource/archive_file.rb | 132 | ||||
-rw-r--r-- | lib/chef/resources.rb | 1 | ||||
-rw-r--r-- | spec/unit/resource/archive_file_spec.rb | 40 |
5 files changed, 178 insertions, 2 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index 9a72f0b45b..f01b89a943 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -35,6 +35,7 @@ PATH diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) ffi (~> 1.9, >= 1.9.25) + ffi-libarchive ffi-yajl (~> 2.2) highline (~> 1.6, >= 1.6.9) iniparse (~> 1.4) @@ -59,6 +60,7 @@ PATH diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) ffi (~> 1.9, >= 1.9.25) + ffi-libarchive ffi-yajl (~> 2.2) highline (~> 1.6, >= 1.6.9) iniparse (~> 1.4) @@ -136,6 +138,8 @@ GEM ffi (1.10.0) ffi (1.10.0-x64-mingw32) ffi (1.10.0-x86-mingw32) + ffi-libarchive (0.4.6) + ffi (~> 1.0) ffi-win32-extensions (1.0.3) ffi ffi-yajl (2.3.1) diff --git a/chef.gemspec b/chef.gemspec index bb0c1f290e..f6d8e93ab7 100644 --- a/chef.gemspec +++ b/chef.gemspec @@ -32,9 +32,8 @@ Gem::Specification.new do |s| s.add_dependency "highline", "~> 1.6", ">= 1.6.9" s.add_dependency "erubis", "~> 2.7" s.add_dependency "diff-lcs", "~> 1.2", ">= 1.2.4" - + s.add_dependency "ffi-libarchive" s.add_dependency "chef-zero", ">= 14.0.11" - s.add_dependency "plist", "~> 3.2" s.add_dependency "iniparse", "~> 1.4" s.add_dependency "addressable" diff --git a/lib/chef/resource/archive_file.rb b/lib/chef/resource/archive_file.rb new file mode 100644 index 0000000000..1c0a073dde --- /dev/null +++ b/lib/chef/resource/archive_file.rb @@ -0,0 +1,132 @@ +# +# Copyright:: Copyright 2017-2018, Chef Software Inc. +# Author:: Jamie Winsor (<jamie@vialstudios.com>) +# Author:: Tim Smith (<tsmith@chef.io>) +# +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "chef/resource" + +class Chef + class Resource + class ArchiveFile < Chef::Resource + + resource_name :archive_file + provides :archive_file + provides :libarchive_file # legacy cookbook name + + introduced "15.0" + + property :path, String, + name_property: true, + coerce: proc { |f| ::File.expand_path(f) }, + description: "" + + property :owner, String, + description: "" + + property :group, String, + description: "" + + property :mode, [String, Integer], + description: "", + default: "755" + + property :destination, String, + description: "", + required: true + + property :options, [Array, Symbol], + description: "", + default: lazy { [] } + + # backwards compatibility for the legacy cookbook names + alias_method :extract_options, :options + alias_method :extract_to, :destination + + action :extract do + require "fileutils" + + unless ::File.exist?(new_resource.path) + raise Errno::ENOENT, "No archive found at #{new_resource.path}!" + end + + unless Dir.exist?(new_resource.destination) + converge_by("create directory #{new_resource.destination}") do + FileUtils.mkdir_p(new_resource.destination, mode: new_resource.mode.to_i) + end + end + + converge_by("extract #{new_resource.path} to #{new_resource.destination}") do + extract(new_resource.path, new_resource.destination, + Array(new_resource.options)) + end + + if new_resource.owner || new_resource.group + converge_by("set owner of #{new_resource.destination} to #{new_resource.owner}:#{new_resource.group}") do + FileUtils.chown_R(new_resource.owner, new_resource.group, new_resource.destination) + end + end + end + + action_class do + # This can't be a constant since we might not have required 'ffi-libarchive' yet. + def extract_option_map + { + owner: Archive::EXTRACT_OWNER, + permissions: Archive::EXTRACT_PERM, + time: Archive::EXTRACT_TIME, + no_overwrite: Archive::EXTRACT_NO_OVERWRITE, + acl: Archive::EXTRACT_ACL, + fflags: Archive::EXTRACT_FFLAGS, + extended_information: Archive::EXTRACT_XATTR, + xattr: Archive::EXTRACT_XATTR, + no_overwrite_newer: Archive::EXTRACT_NO_OVERWRITE_NEWER, + } + end + + # @param [String] src + # @param [String] dest + # @param [Array] options + # + # @return [Boolean] was the file extraction performed or not + def extract(src, dest, options = []) + require "ffi-libarchive" + + flags = [options].flatten.map { |option| extract_option_map[option] }.compact.reduce(:|) + modified = false + + Dir.chdir(dest) do + archive = Archive::Reader.open_filename(src) + + archive.each_entry do |e| + pathname = ::File.expand_path(e.pathname) + if ::File.exist?(pathname) + modified = true unless ::File.mtime(pathname) == e.mtime + else + modified = true + end + + archive.extract(e, flags.to_i) + end + archive.close + end + modified + end + end + end + end +end diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index 0a74fcea23..59cae3558a 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -20,6 +20,7 @@ require "chef/resource/apt_package" require "chef/resource/apt_preference" require "chef/resource/apt_repository" require "chef/resource/apt_update" +require "chef/resource/archive_file" require "chef/resource/bash" require "chef/resource/batch" require "chef/resource/breakpoint" diff --git a/spec/unit/resource/archive_file_spec.rb b/spec/unit/resource/archive_file_spec.rb new file mode 100644 index 0000000000..053c8d614a --- /dev/null +++ b/spec/unit/resource/archive_file_spec.rb @@ -0,0 +1,40 @@ +# +# Copyright:: Copyright 2018, Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Resource::ArchiveFile do + + let(:resource) { Chef::Resource::ArchiveFile.new("foo") } + + it "has a resource name of :archive_file" do + expect(resource.resource_name).to eql(:archive_file) + end + + it "has a name property of path" do + expect(resource.path).to eql("foo") + end + + it "sets the default action as :extract" do + expect(resource.action).to eql([:extract]) + end + + it "supports :extract action" do + expect { resource.action :extract }.not_to raise_error + end + +end |