diff options
-rw-r--r-- | acceptance/tests/resource/mailalias/create.rb | 32 | ||||
-rw-r--r-- | acceptance/tests/resource/mailalias/destroy.rb | 41 | ||||
-rw-r--r-- | acceptance/tests/resource/mailalias/modify.rb | 41 | ||||
-rw-r--r-- | acceptance/tests/resource/mailalias/query.rb | 35 | ||||
-rw-r--r-- | lib/puppet/provider/mailalias/aliases.rb | 50 | ||||
-rw-r--r-- | lib/puppet/type/mailalias.rb | 46 | ||||
-rw-r--r-- | spec/fixtures/integration/provider/mailalias/aliases/test1 | 31 | ||||
-rw-r--r-- | spec/integration/provider/mailalias/aliases_spec.rb | 10 | ||||
-rw-r--r-- | spec/unit/type/mailalias_spec.rb | 49 |
9 files changed, 335 insertions, 0 deletions
diff --git a/acceptance/tests/resource/mailalias/create.rb b/acceptance/tests/resource/mailalias/create.rb new file mode 100644 index 0000000..e1d40b9 --- /dev/null +++ b/acceptance/tests/resource/mailalias/create.rb @@ -0,0 +1,32 @@ +test_name "should create an email alias" + +confine :except, :platform => 'windows' + +tag 'audit:low', + 'audit:refactor', # Use block style `test_name` + 'audit:acceptance' # Could be done at the integration (or unit) layer though + # actual changing of resources could irreparably damage a + # host running this, or require special permissions. + +name = "pl#{rand(999999).to_i}" +agents.each do |agent| + teardown do + #(teardown) restore the alias file + on(agent, "mv /tmp/aliases /etc/aliases", :acceptable_exit_codes => [0,1]) + end + + #------- SETUP -------# + step "(setup) backup alias file" + on(agent, "cp /etc/aliases /tmp/aliases", :acceptable_exit_codes => [0,1]) + + #------- TESTS -------# + step "create a mailalias with puppet" + args = ['ensure=present', + 'recipient="foo,bar,baz"'] + on(agent, puppet_resource('mailalias', name, args)) + + step "verify the alias exists" + on(agent, "cat /etc/aliases") do |res| + assert_match(/#{name}:.*foo,bar,baz/, res.stdout, "mailalias not in aliases file") + end +end diff --git a/acceptance/tests/resource/mailalias/destroy.rb b/acceptance/tests/resource/mailalias/destroy.rb new file mode 100644 index 0000000..63bc3d0 --- /dev/null +++ b/acceptance/tests/resource/mailalias/destroy.rb @@ -0,0 +1,41 @@ +test_name "should delete an email alias" + +confine :except, :platform => 'windows' + +tag 'audit:low', + 'audit:refactor', # Use block style `test_name` + 'audit:acceptance' # Could be done at the integration (or unit) layer though + # actual changing of resources could irreparably damage a + # host running this, or require special permissions. + +name = "pl#{rand(999999).to_i}" +agents.each do |agent| + teardown do + #(teardown) restore the alias file + on(agent, "mv /tmp/aliases /etc/aliases", :acceptable_exit_codes => [0,1]) + end + + #------- SETUP -------# + step "(setup) backup alias file" + on(agent, "cp /etc/aliases /tmp/aliases", :acceptable_exit_codes => [0,1]) + + step "(setup) create a mailalias" + on(agent, "echo '#{name}: foo,bar,baz' >> /etc/aliases") + + step "(setup) verify the alias exists" + on(agent, "cat /etc/aliases") do |res| + assert_match(/#{name}:.*foo,bar,baz/, res.stdout, "mailalias not in aliases file") + end + + #------- TESTS -------# + step "delete the aliases database with puppet" + args = ['ensure=absent', + 'recipient="foo,bar,baz"'] + on(agent, puppet_resource('mailalias', name, args)) + + + step "verify the alias is absent" + on(agent, "cat /etc/aliases") do |res| + assert_no_match(/#{name}:.*foo,bar,baz/, res.stdout, "mailalias was not removed from aliases file") + end +end diff --git a/acceptance/tests/resource/mailalias/modify.rb b/acceptance/tests/resource/mailalias/modify.rb new file mode 100644 index 0000000..832043b --- /dev/null +++ b/acceptance/tests/resource/mailalias/modify.rb @@ -0,0 +1,41 @@ +test_name "should modify an email alias" + +confine :except, :platform => 'windows' + +tag 'audit:low', + 'audit:refactor', # Use block style `test_name` + 'audit:acceptance' # Could be done at the integration (or unit) layer though + # actual changing of resources could irreparably damage a + # host running this, or require special permissions. + +name = "pl#{rand(999999).to_i}" +agents.each do |agent| + teardown do + #(teardown) restore the alias file + on(agent, "mv /tmp/aliases /etc/aliases", :acceptable_exit_codes => [0,1]) + end + + #------- SETUP -------# + step "(setup) backup alias file" + on(agent, "cp /etc/aliases /tmp/aliases", :acceptable_exit_codes => [0,1]) + + step "(setup) create a mailalias" + on(agent, "echo '#{name}: foo,bar,baz' >> /etc/aliases") + + step "(setup) verify the alias exists" + on(agent, "cat /etc/aliases") do |res| + assert_match(/#{name}:.*foo,bar,baz/, res.stdout, "mailalias not in aliases file") + end + + #------- TESTS -------# + step "modify the aliases database with puppet" + args = ['ensure=present', + 'recipient="foo,bar,baz,blarvitz"'] + on(agent, puppet_resource('mailalias', name, args)) + + + step "verify the updated alias is present" + on(agent, "cat /etc/aliases") do |res| + assert_match(/#{name}:.*foo,bar,baz,blarvitz/, res.stdout, "updated mailalias not in aliases file") + end +end diff --git a/acceptance/tests/resource/mailalias/query.rb b/acceptance/tests/resource/mailalias/query.rb new file mode 100644 index 0000000..40e495a --- /dev/null +++ b/acceptance/tests/resource/mailalias/query.rb @@ -0,0 +1,35 @@ +test_name "should be able to find an exisitng email alias" + +confine :except, :platform => 'windows' + +tag 'audit:low', + 'audit:refactor', # Use block style `test_name` + 'audit:acceptance' # Could be done at the integration (or unit) layer though + # actual changing of resources could irreparably damage a + # host running this, or require special permissions. + +name = "pl#{rand(999999).to_i}" +agents.each do |agent| + teardown do + #(teardown) restore the alias file + on(agent, "mv /tmp/aliases /etc/aliases", :acceptable_exit_codes => [0,1]) + end + + #------- SETUP -------# + step "(setup) backup alias file" + on(agent, "cp /etc/aliases /tmp/aliases", :acceptable_exit_codes => [0,1]) + + step "(setup) create a mailalias" + on(agent, "echo '#{name}: foo,bar,baz' >> /etc/aliases") + + step "(setup) verify the alias exists" + on(agent, "cat /etc/aliases") do |res| + assert_match(/#{name}:.*foo,bar,baz/, res.stdout, "mailalias not in aliases file") + end + + #------- TESTS -------# + step "query for the mail alias with puppet" + on(agent, puppet_resource('mailalias', name)) do + fail_test "didn't find the scheduled_task #{name}" unless stdout.include? 'present' + end +end diff --git a/lib/puppet/provider/mailalias/aliases.rb b/lib/puppet/provider/mailalias/aliases.rb new file mode 100644 index 0000000..038d450 --- /dev/null +++ b/lib/puppet/provider/mailalias/aliases.rb @@ -0,0 +1,50 @@ +require 'puppet/provider/parsedfile' + +Puppet::Type.type(:mailalias).provide( + :aliases, + :parent => Puppet::Provider::ParsedFile, + :default_target => "/etc/aliases", + :filetype => :flat +) do + text_line :comment, :match => /^#/ + text_line :blank, :match => /^\s*$/ + + record_line :aliases, :fields => %w{name recipient}, :separator => /\s*:\s*/, :block_eval => :instance do + def post_parse(record) + if record[:recipient] + record[:recipient] = record[:recipient].split(/\s*,\s*/).collect { |d| d.gsub(/^['"]|['"]$/, '') } + end + record + end + + def process(line) + ret = {} + records = line.split(':',4) + ret[:name] = records[0].strip + if records.length == 4 and records[2].strip == 'include' + ret[:file] = records[3].strip + else + records = line.split(':',2) + ret[:recipient] = records[1].strip + end + ret + end + + def to_line(record) + if record[:recipient] + dest = record[:recipient].collect do |d| + # Quote aliases that have non-alpha chars + if d =~ /[^-+\w@.]/ + '"%s"' % d + else + d + end + end.join(",") + "#{record[:name]}: #{dest}" + elsif record[:file] + "#{record[:name]}: :include: #{record[:file]}" + end + end + end +end + diff --git a/lib/puppet/type/mailalias.rb b/lib/puppet/type/mailalias.rb new file mode 100644 index 0000000..b5df7b8 --- /dev/null +++ b/lib/puppet/type/mailalias.rb @@ -0,0 +1,46 @@ +module Puppet + Type.newtype(:mailalias) do + @doc = "Creates an email alias in the local alias database." + + ensurable + + newparam(:name, :namevar => true) do + desc "The alias name." + end + + newproperty(:recipient, :array_matching => :all) do + desc "Where email should be sent. Multiple values + should be specified as an array. The file and the + recipient entries are mutually exclusive." + end + + newproperty(:file) do + desc "A file containing the alias's contents. The file and the + recipient entries are mutually exclusive." + + validate do |value| + unless Puppet::Util.absolute_path?(value) + fail Puppet::Error, _("File paths must be fully qualified, not '%{value}'") % { value: value } + end + end + end + + newproperty(:target) do + desc "The file in which to store the aliases. Only used by + those providers that write to disk." + + defaultto { if @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile) + @resource.class.defaultprovider.default_target + else + nil + end + } + end + + validate do + if self[:recipient] && self[:file] + self.fail _("You cannot specify both a recipient and a file") + end + end + end +end diff --git a/spec/fixtures/integration/provider/mailalias/aliases/test1 b/spec/fixtures/integration/provider/mailalias/aliases/test1 new file mode 100644 index 0000000..a69be8a --- /dev/null +++ b/spec/fixtures/integration/provider/mailalias/aliases/test1 @@ -0,0 +1,31 @@ +# Basic system aliases -- these MUST be present +MAILER-DAEMON: postmaster +postmaster: root + +# General redirections for pseudo accounts +bin: root +daemon: root +named: root +nobody: root +uucp: root +www: root +ftp-bugs: root +postfix: root + +# Put your local aliases here. + +# Well-known aliases +manager: root +dumper: root +operator: root +abuse: postmaster + +# trap decode to catch security attacks +decode: root + +# Other tests +anothertest: "|/path/to/rt-mailgate --queue 'another test' --action correspond --url http://my.com/" +test: "|/path/to/rt-mailgate --queue 'test' --action correspond --url http://my.com/" + +# Included file +incfile: :include: /tmp/somefile diff --git a/spec/integration/provider/mailalias/aliases_spec.rb b/spec/integration/provider/mailalias/aliases_spec.rb new file mode 100644 index 0000000..64cef1c --- /dev/null +++ b/spec/integration/provider/mailalias/aliases_spec.rb @@ -0,0 +1,10 @@ +#! /usr/bin/env ruby +require 'spec_helper' +require 'shared_behaviours/all_parsedfile_providers' + +provider_class = Puppet::Type.type(:mailalias).provider(:aliases) + +describe provider_class do + # #1560, in which we corrupt the format of complex mail aliases. + it_should_behave_like "all parsedfile providers", provider_class +end diff --git a/spec/unit/type/mailalias_spec.rb b/spec/unit/type/mailalias_spec.rb new file mode 100644 index 0000000..eb701c6 --- /dev/null +++ b/spec/unit/type/mailalias_spec.rb @@ -0,0 +1,49 @@ +#! /usr/bin/env ruby +require 'spec_helper' + +describe Puppet::Type.type(:mailalias) do + include PuppetSpec::Files + + let :tmpfile_path do tmpfile('afile') end + let :target do tmpfile('mailalias') end + let :recipient_resource do + described_class.new(:name => "luke", :recipient => "yay", :target => target) + end + + let :file_resource do + described_class.new(:name => "lukefile", :file => tmpfile_path, :target => target) + end + + it "should be initially absent as a recipient" do + expect(recipient_resource.retrieve_resource[:recipient]).to eq(:absent) + end + + it "should be initially absent as an included file" do + expect(file_resource.retrieve_resource[:file]).to eq(:absent) + end + + it "should try and set the recipient when it does the sync" do + expect(recipient_resource.retrieve_resource[:recipient]).to eq(:absent) + recipient_resource.property(:recipient).expects(:set).with(["yay"]) + recipient_resource.property(:recipient).sync + end + + it "should try and set the included file when it does the sync" do + expect(file_resource.retrieve_resource[:file]).to eq(:absent) + file_resource.property(:file).expects(:set).with(tmpfile_path) + file_resource.property(:file).sync + end + + it "should fail when file is not an absolute path" do + expect { + Puppet::Type.type(:mailalias).new(:name => 'x', :file => 'afile') + }.to raise_error Puppet::Error, /File paths must be fully qualified/ + end + + it "should fail when both file and recipient are specified" do + expect { + Puppet::Type.type(:mailalias).new(:name => 'x', :file => tmpfile_path, + :recipient => 'foo@example.com') + }.to raise_error Puppet::Error, /cannot specify both a recipient and a file/ + end +end |