diff options
author | Tim Smith <tsmith@chef.io> | 2017-07-27 14:58:20 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-27 14:58:20 -0700 |
commit | 21db74bf154b839edf0ee0ab2363e50bee4377a6 (patch) | |
tree | 0eadbb678dbdb89dc0aa53f1a4194132dd176e23 | |
parent | 041628e4bbdb541cb5011d564301bcda9804da4e (diff) | |
parent | c2d577d0f431a9e95d220f20cd00d38f4b117fec (diff) | |
download | chef-21db74bf154b839edf0ee0ab2363e50bee4377a6.tar.gz |
Merge pull request #5529 from chef/tas50/apt_preference
Apt: Add apt_preference resource from apt cookbooks
-rw-r--r-- | lib/chef/provider/apt_preference.rb | 99 | ||||
-rw-r--r-- | lib/chef/providers.rb | 1 | ||||
-rw-r--r-- | lib/chef/resource/apt_preference.rb | 36 | ||||
-rw-r--r-- | lib/chef/resources.rb | 1 | ||||
-rw-r--r-- | spec/unit/provider/apt_preference_spec.rb | 87 | ||||
-rw-r--r-- | spec/unit/resource/apt_preference_spec.rb | 41 |
6 files changed, 265 insertions, 0 deletions
diff --git a/lib/chef/provider/apt_preference.rb b/lib/chef/provider/apt_preference.rb new file mode 100644 index 0000000000..d3958ea6fa --- /dev/null +++ b/lib/chef/provider/apt_preference.rb @@ -0,0 +1,99 @@ +# +# Author:: Tim Smith (<tsmith@chef.io>) +# Copyright:: 2016-2017, 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 "chef/provider" +require "chef/dsl/declare_resource" +require "chef/provider/noop" +require "chef/mixin/which" +require "chef/log" + +class Chef + class Provider + class AptPreference < Chef::Provider + extend Chef::Mixin::Which + + provides :apt_preference do + which("apt-get") + end + + APT_PREFERENCE_DIR = "/etc/apt/preferences.d".freeze + + def load_current_resource + end + + action :add do + preference = build_pref( + new_resource.glob || new_resource.package_name, + new_resource.pin, + new_resource.pin_priority + ) + + declare_resource(:directory, APT_PREFERENCE_DIR) do + mode "0755" + action :create + end + + sanitized_prefname = safe_name(new_resource.package_name) + + # cleanup any existing pref files w/o the sanitized name (created by old apt cookbook) + if (sanitized_prefname != new_resource.package_name) && ::File.exist?("#{APT_PREFERENCE_DIR}/#{new_resource.package_name}.pref") + Chef::Log.warn "Replacing legacy #{new_resource.package_name}.pref with #{sanitized_prefname}.pref in #{APT_PREFERENCE_DIR}" + declare_resource(:file, "#{APT_PREFERENCE_DIR}/#{new_resource.package_name}.pref") do + action :delete + end + end + + # cleanup any existing pref files without the .pref extension (created by old apt cookbook) + if ::File.exist?("#{APT_PREFERENCE_DIR}/#{new_resource.package_name}") + Chef::Log.warn "Replacing legacy #{new_resource.package_name} with #{sanitized_prefname}.pref in #{APT_PREFERENCE_DIR}" + declare_resource(:file, "#{APT_PREFERENCE_DIR}/#{new_resource.package_name}") do + action :delete + end + end + + declare_resource(:file, "#{APT_PREFERENCE_DIR}/#{sanitized_prefname}.pref") do + mode "0644" + content preference + action :create + end + end + + action :delete do + sanitized_prefname = safe_name(new_resource.package_name) + + if ::File.exist?("#{APT_PREFERENCE_DIR}/#{sanitized_prefname}.pref") + Chef::Log.info "Un-pinning #{sanitized_prefname} from #{APT_PREFERENCE_DIR}" + declare_resource(:file, "#{APT_PREFERENCE_DIR}/#{sanitized_prefname}.pref") do + action :delete + end + end + end + + # Build preferences.d file contents + def build_pref(package_name, pin, pin_priority) + "Package: #{package_name}\nPin: #{pin}\nPin-Priority: #{pin_priority}\n" + end + + def safe_name(name) + name.tr(".", "_").gsub("*", "wildcard") + end + end + end +end + +Chef::Provider::Noop.provides :apt_preference diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb index 0ea1786594..5a4d287b89 100644 --- a/lib/chef/providers.rb +++ b/lib/chef/providers.rb @@ -17,6 +17,7 @@ # require "chef/provider/apt_update" +require "chef/provider/apt_preference" require "chef/provider/apt_repository" require "chef/provider/batch" require "chef/provider/cookbook_file" diff --git a/lib/chef/resource/apt_preference.rb b/lib/chef/resource/apt_preference.rb new file mode 100644 index 0000000000..603766d76b --- /dev/null +++ b/lib/chef/resource/apt_preference.rb @@ -0,0 +1,36 @@ +# +# Author:: Tim Smith (<tsmith@chef.io>) +# Copyright:: 2016-2017, 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 "chef/resource" + +class Chef + class Resource + class AptPreference < Chef::Resource + resource_name :apt_preference + provides :apt_preference + + property :package_name, String, name_property: true, regex: [/^([a-z]|[A-Z]|[0-9]|_|-|\.|\*|\+)+$/] + property :glob, String + property :pin, String, required: true + property :pin_priority, [String, Integer], required: true + + default_action :add + allowed_actions :add, :remove + end + end +end diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index 9f87cb2454..7761d9ea66 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -17,6 +17,7 @@ # require "chef/resource/apt_package" +require "chef/resource/apt_preference" require "chef/resource/apt_repository" require "chef/resource/apt_update" require "chef/resource/bash" diff --git a/spec/unit/provider/apt_preference_spec.rb b/spec/unit/provider/apt_preference_spec.rb new file mode 100644 index 0000000000..55c277e6fe --- /dev/null +++ b/spec/unit/provider/apt_preference_spec.rb @@ -0,0 +1,87 @@ +# +# Author:: Thom May (<thom@chef.io>) +# Author:: Tim Smith (<tim@chef.io>) +# Copyright:: 2016-2017, 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::Provider::AptPreference do + let(:new_resource) { Chef::Resource::AptPreference.new("libmysqlclient16.1*") } + let(:pref_dir) { Dir.mktmpdir("apt_pref_d") } + + before do + stub_const("Chef::Provider::AptPreference::APT_PREFERENCE_DIR", pref_dir) + new_resource.pin = "1.0.1" + new_resource.pin_priority 1001 + end + + let(:provider) do + node = Chef::Node.new + events = Chef::EventDispatch::Dispatcher.new + run_context = Chef::RunContext.new(node, {}, events) + Chef::Provider::AptPreference.new(new_resource, run_context) + end + + it "responds to load_current_resource" do + expect(provider).to respond_to(:load_current_resource) + end + + context "#action_add" do + context "without a preferences.d directory" do + before do + FileUtils.rmdir pref_dir + end + + it "creates the preferences.d directory" do + provider.run_action(:add) + expect(new_resource).to be_updated_by_last_action + expect(File.exist?(pref_dir)).to be true + expect(File.directory?(pref_dir)).to be true + end + end + + context "with a preferences.d directory" do + before do + FileUtils.mkdir pref_dir unless ::File.exist?(pref_dir) + FileUtils.touch("#{pref_dir}/libmysqlclient16.1*.pref") + FileUtils.touch("#{pref_dir}/libmysqlclient16.1*") + end + + # FileUtils.touch throws "Invalid argument @ utime_failed" in appveyer + it "creates a sanitized .pref file and removes the legacy cookbook files", :unix_only do + provider.run_action(:add) + expect(new_resource).to be_updated_by_last_action + expect(File).not_to exist("#{pref_dir}/libmysqlclient16.1*.pref") + expect(File).not_to exist("#{pref_dir}/libmysqlclient16.1*") + expect(File.read(::File.join(pref_dir, "libmysqlclient16_1wildcard.pref"))).to match(/Package: libmysqlclient16.1*.*Pin: 1.0.1.*Pin-Priority: 1001/m) + end + end + end + + context "#action_delete" do + before do + FileUtils.mkdir pref_dir unless ::File.exist?(pref_dir) + FileUtils.touch("#{pref_dir}/libmysqlclient16_1wildcard.pref") + end + + it "deletes the name santized .pref file" do + provider.run_action(:delete) + expect(new_resource).to be_updated_by_last_action + expect(File).not_to exist("#{pref_dir}/libmysqlclient16_1wildcard.pref") + end + end +end diff --git a/spec/unit/resource/apt_preference_spec.rb b/spec/unit/resource/apt_preference_spec.rb new file mode 100644 index 0000000000..801434b4f3 --- /dev/null +++ b/spec/unit/resource/apt_preference_spec.rb @@ -0,0 +1,41 @@ +# +# Author:: Tim Smith (<tsmith@chef.io>) +# Copyright:: 2016-2017, 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::AptPreference do + let(:node) { Chef::Node.new } + let(:events) { Chef::EventDispatch::Dispatcher.new } + let(:run_context) { Chef::RunContext.new(node, {}, events) } + let(:resource) { Chef::Resource::AptPreference.new("libmysqlclient16", run_context) } + + it "should create a new Chef::Resource::AptPreference" do + expect(resource).to be_a_kind_of(Chef::Resource) + expect(resource).to be_a_kind_of(Chef::Resource::AptPreference) + end + + it "should resolve to a Noop class when apt-get is not found" do + expect(Chef::Provider::AptPreference).to receive(:which).with("apt-get").and_return(false) + expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop) + end + + it "should resolve to a AptPreference class when apt-get is found" do + expect(Chef::Provider::AptPreference).to receive(:which).with("apt-get").and_return(true) + expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::AptPreference) + end +end |