diff options
-rw-r--r-- | lib/chef/provider/systemd_unit.rb | 2 | ||||
-rw-r--r-- | lib/chef/resource/file/verification/systemd_unit.rb | 67 | ||||
-rw-r--r-- | spec/unit/resource/file/verification/systemd_unit_spec.rb | 103 |
3 files changed, 172 insertions, 0 deletions
diff --git a/lib/chef/provider/systemd_unit.rb b/lib/chef/provider/systemd_unit.rb index a656fbbf80..5175dc6be9 100644 --- a/lib/chef/provider/systemd_unit.rb +++ b/lib/chef/provider/systemd_unit.rb @@ -20,6 +20,7 @@ require "chef/provider" require "chef/mixin/which" require "chef/mixin/shell_out" require "chef/resource/file" +require "chef/resource/file/verification/systemd_unit" require "iniparse" class Chef @@ -193,6 +194,7 @@ class Chef f.group "root" f.mode "0644" f.content new_resource.to_ini + f.verify :systemd_unit end.run_action(action) end diff --git a/lib/chef/resource/file/verification/systemd_unit.rb b/lib/chef/resource/file/verification/systemd_unit.rb new file mode 100644 index 0000000000..bda3dfc3eb --- /dev/null +++ b/lib/chef/resource/file/verification/systemd_unit.rb @@ -0,0 +1,67 @@ +# +# Author:: Mal Graty (<mal.graty@googlemail.com>) +# Copyright:: Copyright 2013-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/mixin/which" + +class Chef + class Resource + class File + class Verification + + # + # Systemd provides a binary for verifying the correctness of + # unit files. Unfortunately some units have constraints on the + # filename meaning that normal verification against temp files + # won't work. + # + # Working around that requires placing a copy of the temp file + # in a temp directory, under it's real name and running the + # verification tool against that file. + # + + class SystemdUnit < Chef::Resource::File::Verification + include Chef::Mixin::Which + + provides :systemd_unit + + def initialize(parent_resource, command, opts, &block) + super + @command = systemd_analyze_cmd + end + + def verify(path, opts = {}) + return true unless systemd_analyze_path + Dir.mktmpdir("chef-systemd-unit") do |dir| + temp = "#{dir}/#{::File.basename(@parent_resource.path)}" + ::FileUtils.cp(path, temp) + verify_command(temp, opts) + end + end + + def systemd_analyze_cmd + @systemd_analyze_cmd ||= "#{systemd_analyze_path} verify %{path}" + end + + def systemd_analyze_path + @systemd_analyze_path ||= which("systemd-analyze") + end + end + end + end + end +end diff --git a/spec/unit/resource/file/verification/systemd_unit_spec.rb b/spec/unit/resource/file/verification/systemd_unit_spec.rb new file mode 100644 index 0000000000..8a3d849582 --- /dev/null +++ b/spec/unit/resource/file/verification/systemd_unit_spec.rb @@ -0,0 +1,103 @@ +# +# Author:: Mal Graty (<mal.graty@googlemail.com>) +# Copyright:: Copyright 2014-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::File::Verification::SystemdUnit do + let(:command) { "#{systemd_analyze_path} verify %{path}" } + let(:opts) { { :future => true } } + let(:parent_resource) { Chef::Resource.new("llama") } + let(:systemd_analyze_path) { "/usr/bin/systemd-analyze" } + let(:systemd_dir) { "/etc/systemd/system" } + let(:temp_path) { "/tmp" } + let(:unit_name) { "sysstat-collect.timer" } + let(:unit_path) { "#{systemd_dir}/#{unit_name}" } + let(:unit_temp_path) { "#{systemd_dir}/.chef-#{unit_name}" } + let(:unit_test_path) { "#{temp_path}/#{unit_name}" } + + describe "verification registration" do + it "registers itself for later use" do + expect(Chef::Resource::File::Verification.lookup(:systemd_unit)).to eq(Chef::Resource::File::Verification::SystemdUnit) + end + end + + describe "#initialize" do + before(:each) do + allow_any_instance_of(Chef::Resource::File::Verification::SystemdUnit).to receive(:which) + .with("systemd-analyze") + .and_return(systemd_analyze_path) + end + + it "overwrites the @command variable with the verification command" do + v = Chef::Resource::File::Verification::SystemdUnit.new(parent_resource, :systemd_unit, {}) + expect(v.instance_variable_get(:@command)).to eql(command) + end + end + + describe "#verify" do + context "with the systemd-analyze binary available" do + before(:each) do + allow_any_instance_of(Chef::Resource::File::Verification::SystemdUnit).to receive(:which) + .with("systemd-analyze") + .and_return(systemd_analyze_path) + + allow(parent_resource).to receive(:path) + .and_return(unit_path) + allow(Dir).to receive(:mktmpdir) + .with("chef-systemd-unit") { |&b| b.call temp_path } + allow(FileUtils).to receive(:cp) + .with(unit_temp_path, unit_test_path) + end + + it "copies the temp file to secondary location under correct name" do + v = Chef::Resource::File::Verification::SystemdUnit.new(parent_resource, :systemd_unit, {}) + + expect(FileUtils).to receive(:cp).with(unit_temp_path, unit_test_path) + expect(v).to receive(:verify_command).with(unit_test_path, opts) + + v.verify(unit_temp_path, opts) + end + + it "returns the value given by #verify_command" do + v = Chef::Resource::File::Verification::SystemdUnit.new(parent_resource, :systemd_unit, {}) + + expect(v).to receive(:verify_command) + .with(unit_test_path, opts) + .and_return("foo") + + expect(v.verify(unit_temp_path, opts)).to eql("foo") + end + end + + context "with the systemd-analyze binary unavailable" do + before(:each) do + allow_any_instance_of(Chef::Resource::File::Verification::SystemdUnit).to receive(:which) + .with("systemd-analyze") + .and_return(false) + end + + it "skips verification" do + v = Chef::Resource::File::Verification::SystemdUnit.new(parent_resource, :systemd_unit, {}) + + expect(v).to_not receive(:verify_command) + + expect(v.verify(unit_temp_path)).to eq(true) + end + end + end +end |