summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith84@gmail.com>2020-03-11 10:52:53 -0700
committerTim Smith <tsmith84@gmail.com>2020-03-12 11:58:59 -0700
commit10e8ae47e255fb1ced9f594b0ce94f5f332a414c (patch)
tree1876e427810019e030b5bcfdc4ab1075717b5798
parente5e0f8f399e2a52ace04702225d327a56db5438c (diff)
downloadchef-10e8ae47e255fb1ced9f594b0ce94f5f332a414c.tar.gz
Add alternatives resource for Linux
This comes from https://github.com/vkhatri/chef-alternatives Signed-off-by: Tim Smith <tsmith@chef.io>
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/alternatives.rb51
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/default.rb2
-rw-r--r--lib/chef/resource/alternatives.rb131
-rw-r--r--lib/chef/resources.rb1
-rw-r--r--spec/unit/resource/alternatives_spec.rb50
5 files changed, 234 insertions, 1 deletions
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/alternatives.rb b/kitchen-tests/cookbooks/end_to_end/recipes/alternatives.rb
new file mode 100644
index 0000000000..8e0a0bb178
--- /dev/null
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/alternatives.rb
@@ -0,0 +1,51 @@
+#
+# Cookbook:: end_to_end
+# Recipe:: alternatives
+#
+
+file "/usr/local/sample-binary-1" do
+ content '#!/bin/bash
+ echo sample-binary-v1
+ '
+ mode "500"
+end
+
+file "/usr/local/sample-binary-2" do
+ content '#!/bin/bash
+ echo sample-binary-v2
+ '
+ mode "550"
+end
+
+alternatives "sample-binary v1" do
+ link_name "sample-binary"
+ path "/usr/local/sample-binary-1"
+ priority 100
+ action :install
+end
+
+alternatives "sample-binary v2" do
+ link_name "sample-binary"
+ path "/usr/local/sample-binary-2"
+ priority 101
+ action :install
+end
+
+alternatives "set sample-binary v1" do
+ link_name "sample-binary"
+ path "/usr/local/sample-binary-1"
+ action :set
+end
+
+alternatives "sample-binary-test v1" do
+ link_name "sample-binary-test"
+ path "/usr/local/sample-binary-1"
+ priority 100
+ action :install
+end
+
+alternatives "sample-binary-test v1" do
+ link_name "sample-binary-test"
+ path "/usr/local/sample-binary-1"
+ action :remove
+end
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/default.rb b/kitchen-tests/cookbooks/end_to_end/recipes/default.rb
index dc8b44cbf4..eeccb9ffd7 100644
--- a/kitchen-tests/cookbooks/end_to_end/recipes/default.rb
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/default.rb
@@ -118,5 +118,5 @@ end
end
include_recipe "::chef-vault" unless includes_recipe?("end_to_end::chef-vault")
-
+include_recipe "::alternatives"
include_recipe "::tests"
diff --git a/lib/chef/resource/alternatives.rb b/lib/chef/resource/alternatives.rb
new file mode 100644
index 0000000000..75916f7269
--- /dev/null
+++ b/lib/chef/resource/alternatives.rb
@@ -0,0 +1,131 @@
+#
+# Copyright:: 2020, Chef Software Inc.
+# Copyright:: 2016-2020, Virender Khatri
+#
+# 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_relative "../resource"
+
+class Chef
+ class Resource
+ class Alternatives < Chef::Resource
+ unified_mode true
+
+ provides(:alternatives) { true }
+
+ description "The alternatives resource allows for configuration of command alternatives in Linux using the alternatives or update-alternatives packages."
+ introduced "16.0"
+
+ property :link_name, String, name_property: true
+ property :link, String, default: lazy { |n| "/usr/bin/#{n.link_name}" }
+ property :path, String
+ property :priority, [String, Integer], coerce: proc { |n| n.to_i }
+
+ def define_resource_requirements
+ requirements.assert(:install, :set, :remove) do |a|
+ a.assertion do
+ !new_resource.path.nil?
+ end
+
+ a.failure_message("Could not set alternatives for #{new_resource.link_name}, must provide :path property")
+ end
+
+ requirements.assert(:install, :set, :remove) do |a|
+ a.assertion do
+ ::File.exist?(new_resource.path)
+ end
+
+ a.whyrun("Assuming file #{new_resource.path} already exists or was created already")
+ a.failure_message("Could not set alternatives for #{new_resource.link_name}, missing #{new_resource.path}")
+ end
+ end
+
+ action :install do
+ raise "missing :priority" unless new_resource.priority
+
+ if path_priority != new_resource.priority
+ converge_by("adding alternative #{new_resource.link} #{new_resource.link_name} #{new_resource.path} #{new_resource.priority}") do
+ output = shell_out("#{alternatives_cmd} --install #{new_resource.link} #{new_resource.link_name} #{new_resource.path} #{new_resource.priority}")
+ unless output.exitstatus == 0
+ raise "failed to add alternative #{new_resource.link} #{new_resource.link_name} #{new_resource.path} #{new_resource.priority}"
+ end
+ end
+ end
+ end
+
+ action :set do
+ if current_path != new_resource.path
+ converge_by("setting alternative #{new_resource.link_name} #{new_resource.path}") do
+ output = shell_out("#{alternatives_cmd} --set #{new_resource.link_name} #{new_resource.path}")
+ unless output.exitstatus == 0
+ raise "failed to set alternative #{new_resource.link_name} #{new_resource.path} \n #{output.stdout.strip}"
+ end
+ end
+ end
+ end
+
+ action :remove do
+ if path_exists?
+ converge_by("removing alternative #{new_resource.link_name} #{new_resource.path}") do
+ shell_out("#{alternatives_cmd} --remove #{new_resource.link_name} #{new_resource.path}")
+ end
+ end
+ end
+
+ action :auto do
+ converge_by("setting auto alternative #{new_resource.link_name}") do
+ shell_out("#{alternatives_cmd} --auto #{new_resource.link_name}")
+ end
+ end
+
+ action :refresh do
+ converge_by("refreshing alternative #{new_resource.link_name}") do
+ shell_out("#{alternatives_cmd} --refresh #{new_resource.link_name}")
+ end
+ end
+
+ action_class do
+ def alternatives_cmd
+ if platform_family?("rhel", "amazon", "fedora")
+ "alternatives"
+ else
+ "update-alternatives"
+ end
+ end
+
+ def path_priority
+ # https://rubular.com/r/IcUlEU0mSNaMm3
+ escaped_path = Regexp.new(Regexp.escape("#{new_resource.path} - priority ") + "(.*)")
+ match = shell_out("#{alternatives_cmd} --display #{new_resource.link_name}").stdout.match(escaped_path)
+
+ match.nil? ? nil : match[1].to_i
+ end
+
+ def current_path
+ # https://rubular.com/r/ylsuvzUtquRPqc
+ match = shell_out("#{alternatives_cmd} --display #{new_resource.link_name}").stdout.match(/link currently points to (.*)/)
+ match.nil? ? nil : match[1]
+ end
+
+ def path_exists?
+ # https://rubular.com/r/ogvDdq8h2IKRff
+ escaped_path = Regexp.new(Regexp.escape("#{new_resource.path} - priority"))
+ shell_out("#{alternatives_cmd} --display #{new_resource.link_name}").stdout.match(escaped_path)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index 7d9a24c830..f0736e2429 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -16,6 +16,7 @@
# limitations under the License.
#
+require_relative "resource/alternatives"
require_relative "resource/apt_package"
require_relative "resource/apt_preference"
require_relative "resource/apt_repository"
diff --git a/spec/unit/resource/alternatives_spec.rb b/spec/unit/resource/alternatives_spec.rb
new file mode 100644
index 0000000000..7540e73a97
--- /dev/null
+++ b/spec/unit/resource/alternatives_spec.rb
@@ -0,0 +1,50 @@
+#
+# Author:: Tim Smith (<tsmith@chef.io>)
+# Copyright:: 2020, 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::Alternatives do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:resource) { Chef::Resource::Alternatives.new("fakey_fakerton", run_context) }
+
+ it "the link_name property is the name_property" do
+ expect(resource.link_name).to eql("fakey_fakerton")
+ end
+
+ it "sets the default action as :install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "coerces priority value to an Integer" do
+ resource.priority("1")
+ expect(resource.priority).to eql(1)
+ end
+
+ it "builds a default value for link based on link_name value" do
+ expect(resource.link).to eql("/usr/bin/fakey_fakerton")
+ end
+
+ it "supports :install, :auto, :refresh, and :remove actions" do
+ expect { resource.action :install }.not_to raise_error
+ expect { resource.action :auto }.not_to raise_error
+ expect { resource.action :refresh }.not_to raise_error
+ expect { resource.action :remove }.not_to raise_error
+ end
+end