diff options
31 files changed, 1283 insertions, 2 deletions
diff --git a/.fixtures.yml b/.fixtures.yml new file mode 100644 index 0000000..60ccf07 --- /dev/null +++ b/.fixtures.yml @@ -0,0 +1,6 @@ +# This file can be used to install module depdencies for unit testing +# See https://github.com/puppetlabs/puppetlabs_spec_helper#using-fixtures for details +--- +fixtures: + forge_modules: +# stdlib: "puppetlabs/stdlib" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..543dd6a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.rb eol=lf +*.erb eol=lf +*.pp eol=lf +*.sh eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..56efb9c --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +.*.sw[op] +.metadata +.yardoc +.yardwarns +*.iml +/.bundle/ +/.idea/ +/.vagrant/ +/coverage/ +/bin/ +/doc/ +/Gemfile.local +/Gemfile.lock +/junit/ +/log/ +/pkg/ +/spec/fixtures/manifests/ +/spec/fixtures/modules/ +/tmp/ +/vendor/ +/convert_report.txt +.DS_Store diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..35e4209 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,70 @@ +--- +stages: + - test_2.4.1 + - test_2.1.9 + +before_script: + - bundle -v + - rm Gemfile.lock || true + - gem update --system + - gem update bundler + - gem --version + - bundle -v + - bundle install --without system_tests + +rubocop-2.4.1: + stage: test_2.4.1 + image: ruby:2.4.1 + script: + - bundle exec rake rubocop + +syntax-2.4.1: + stage: test_2.4.1 + image: ruby:2.4.1 + script: + - bundle exec rake syntax lint + +metadata-2.4.1: + stage: test_2.4.1 + image: ruby:2.4.1 + script: + - bundle exec rake metadata_lint + +rspec-puppet-2.4.1: + stage: test_2.4.1 + image: ruby:2.4.1 + variables: + PUPPET_GEM_VERSION: ~> 4.0 + CHECK: spec + script: + - bundle update + - bundle exec rake $CHECK + +rubocop-2.1.9: + stage: test_2.1.9 + image: ruby:2.1.9 + script: + - bundle exec rake rubocop + +syntax-2.1.9: + stage: test_2.1.9 + image: ruby:2.1.9 + script: + - bundle exec rake syntax lint + +metadata-2.1.9: + stage: test_2.1.9 + image: ruby:2.1.9 + script: + - bundle exec rake metadata_lint + +rspec-puppet-2.1.9: + stage: test_2.1.9 + image: ruby:2.1.9 + variables: + PUPPET_GEM_VERSION: ~> 4.0 + CHECK: spec + script: + - bundle update + - bundle exec rake $CHECK + diff --git a/.pdkignore b/.pdkignore new file mode 100644 index 0000000..56efb9c --- /dev/null +++ b/.pdkignore @@ -0,0 +1,22 @@ +.*.sw[op] +.metadata +.yardoc +.yardwarns +*.iml +/.bundle/ +/.idea/ +/.vagrant/ +/coverage/ +/bin/ +/doc/ +/Gemfile.local +/Gemfile.lock +/junit/ +/log/ +/pkg/ +/spec/fixtures/manifests/ +/spec/fixtures/modules/ +/tmp/ +/vendor/ +/convert_report.txt +.DS_Store diff --git a/.project b/.project new file mode 100644 index 0000000..c93c021 --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>puppetlabs-mailalias_core</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>com.puppetlabs.geppetto.pp.dsl.ui.modulefileBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.xtext.ui.shared.xtextBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>com.puppetlabs.geppetto.pp.dsl.ui.puppetNature</nature> + <nature>org.eclipse.xtext.ui.shared.xtextNature</nature> + </natures> +</projectDescription> @@ -0,0 +1,2 @@ +--color +--format documentation diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..40a58e0 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,107 @@ +--- +require: rubocop-rspec +AllCops: + DisplayCopNames: true + TargetRubyVersion: '2.1' + Include: + - "./**/*.rb" + Exclude: + - bin/* + - ".vendor/**/*" + - Gemfile + - Rakefile + - pkg/**/* + - spec/fixtures/**/* + - vendor/**/* +Metrics/LineLength: + Description: People have wide screens, use them. + Max: 200 +RSpec/BeforeAfterAll: + Description: Beware of using after(:all) as it may cause state to leak between tests. + A necessary evil in acceptance testing. + Exclude: + - spec/acceptance/**/*.rb +RSpec/HookArgument: + Description: Prefer explicit :each argument, matching existing module's style + EnforcedStyle: each +Style/BlockDelimiters: + Description: Prefer braces for chaining. Mostly an aesthetical choice. Better to + be consistent then. + EnforcedStyle: braces_for_chaining +Style/ClassAndModuleChildren: + Description: Compact style reduces the required amount of indentation. + EnforcedStyle: compact +Style/EmptyElse: + Description: Enforce against empty else clauses, but allow `nil` for clarity. + EnforcedStyle: empty +Style/FormatString: + Description: Following the main puppet project's style, prefer the % format format. + EnforcedStyle: percent +Style/FormatStringToken: + Description: Following the main puppet project's style, prefer the simpler template + tokens over annotated ones. + EnforcedStyle: template +Style/Lambda: + Description: Prefer the keyword for easier discoverability. + EnforcedStyle: literal +Style/RegexpLiteral: + Description: Community preference. See https://github.com/voxpupuli/modulesync_config/issues/168 + EnforcedStyle: percent_r +Style/TernaryParentheses: + Description: Checks for use of parentheses around ternary conditions. Enforce parentheses + on complex expressions for better readability, but seriously consider breaking + it up. + EnforcedStyle: require_parentheses_when_complex +Style/TrailingCommaInArguments: + Description: Prefer always trailing comma on multiline argument lists. This makes + diffs, and re-ordering nicer. + EnforcedStyleForMultiline: comma +Style/TrailingCommaInLiteral: + Description: Prefer always trailing comma on multiline literals. This makes diffs, + and re-ordering nicer. + EnforcedStyleForMultiline: comma +Style/SymbolArray: + Description: Using percent style obscures symbolic intent of array's contents. + EnforcedStyle: brackets +RSpec/MessageSpies: + EnforcedStyle: receive +Style/CollectionMethods: + Enabled: true +Style/MethodCalledOnDoEndBlock: + Enabled: true +Style/StringMethods: + Enabled: true +Layout/EndOfLine: + Enabled: false +Metrics/AbcSize: + Enabled: false +Metrics/BlockLength: + Enabled: false +Metrics/ClassLength: + Enabled: false +Metrics/CyclomaticComplexity: + Enabled: false +Metrics/MethodLength: + Enabled: false +Metrics/ModuleLength: + Enabled: false +Metrics/ParameterLists: + Enabled: false +Metrics/PerceivedComplexity: + Enabled: false +RSpec/DescribeClass: + Enabled: false +RSpec/ExampleLength: + Enabled: false +RSpec/MessageExpectation: + Enabled: false +RSpec/MultipleExpectations: + Enabled: false +RSpec/NestedGroups: + Enabled: false +Style/AsciiComments: + Enabled: false +Style/IfUnlessModifier: + Enabled: false +Style/SymbolProc: + Enabled: false diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6b1f5fb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,48 @@ +--- +sudo: false +dist: trusty +language: ruby +cache: bundler +before_install: + - bundle -v + - rm Gemfile.lock || true + - gem update --system + - gem update bundler + - gem --version + - bundle -v +script: + - 'bundle exec rake $CHECK' +bundler_args: --without system_tests +rvm: + - 2.4.1 +env: + - PUPPET_GEM_VERSION="~> 5.0" CHECK=spec +matrix: + fast_finish: true + include: + - + env: CHECK=rubocop + - + env: CHECK="syntax lint" + - + env: CHECK=metadata_lint + - + env: CHECK=spec + - + env: PUPPET_GEM_VERSION="~> 4.0" CHECK=spec + rvm: 2.1.9 +branches: + only: + - master + - /^v\d/ +notifications: + email: false +deploy: + provider: puppetforge + user: puppet + password: + secure: "" + on: + tags: true + all_branches: true + condition: "$DEPLOY_TO_FORGE = yes" diff --git a/.yardopts b/.yardopts new file mode 100644 index 0000000..29c933b --- /dev/null +++ b/.yardopts @@ -0,0 +1 @@ +--markup markdown diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4c954cd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## Release 0.1.0 + +**Features** + +**Bugfixes** + +**Known Issues** @@ -0,0 +1,141 @@ +source ENV['GEM_SOURCE'] || 'https://rubygems.org' + +def location_for(place_or_version, fake_version = nil) + if place_or_version =~ %r{\A(git[:@][^#]*)#(.*)} + [fake_version, { git: Regexp.last_match(1), branch: Regexp.last_match(2), require: false }].compact + elsif place_or_version =~ %r{\Afile:\/\/(.*)} + ['>= 0', { path: File.expand_path(Regexp.last_match(1)), require: false }] + else + [place_or_version, { require: false }] + end +end + +def gem_type(place_or_version) + if place_or_version =~ %r{\Agit[:@]} + :git + elsif !place_or_version.nil? && place_or_version.start_with?('file:') + :file + else + :gem + end +end + +ruby_version_segments = Gem::Version.new(RUBY_VERSION.dup).segments +minor_version = ruby_version_segments[0..1].join('.') + +group :development do + gem "fast_gettext", '1.1.0', require: false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.1.0') + gem "fast_gettext", require: false if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.1.0') + gem "json_pure", '<= 2.0.1', require: false if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0') + gem "json", '= 1.8.1', require: false if Gem::Version.new(RUBY_VERSION.dup) == Gem::Version.new('2.1.9') + gem "puppet-module-posix-default-r#{minor_version}", require: false, platforms: [:ruby] + gem "puppet-module-posix-dev-r#{minor_version}", require: false, platforms: [:ruby] + gem "puppet-module-win-default-r#{minor_version}", require: false, platforms: [:mswin, :mingw, :x64_mingw] + gem "puppet-module-win-dev-r#{minor_version}", require: false, platforms: [:mswin, :mingw, :x64_mingw] + + # Puppet Strings for documentation purposes + gem 'puppet-strings' + gem 'rake' +end + +group :system_tests do + gem "puppet-module-posix-system-r#{minor_version}", require: false, platforms: [:ruby] + gem "puppet-module-win-system-r#{minor_version}", require: false, platforms: [:mswin, :mingw, :x64_mingw] + gem "beaker", *location_for(ENV['BEAKER_VERSION'] || '~> 3.13') + gem "beaker-abs", *location_for(ENV['BEAKER_ABS_VERSION'] || '~> 0.1') + gem "beaker-pe", require: false + gem "beaker-hostgenerator" + gem "beaker-rspec" +end + + +puppet_version = ENV['PUPPET_GEM_VERSION'] +puppet_type = gem_type(puppet_version) +facter_version = ENV['FACTER_GEM_VERSION'] +hiera_version = ENV['HIERA_GEM_VERSION'] + +def puppet_older_than?(version) + puppet_version = ENV['PUPPET_GEM_VERSION'] + !puppet_version.nil? && + Gem::Version.correct?(puppet_version) && + Gem::Requirement.new("< #{version}").satisfied_by?(Gem::Version.new(puppet_version.dup)) +end + +gems = {} + +gems['puppet'] = location_for(puppet_version) + +# If facter or hiera versions have been specified via the environment +# variables, use those versions. If not, and if the puppet version is < 3.5.0, +# use known good versions of both for puppet < 3.5.0. +if facter_version + gems['facter'] = location_for(facter_version) +elsif puppet_type == :gem && puppet_older_than?('3.5.0') + gems['facter'] = ['>= 1.6.11', '<= 1.7.5', require: false] +end + +if hiera_version + gems['hiera'] = location_for(ENV['HIERA_GEM_VERSION']) +elsif puppet_type == :gem && puppet_older_than?('3.5.0') + gems['hiera'] = ['>= 1.0.0', '<= 1.3.0', require: false] +end + +if Gem.win_platform? && (puppet_type != :gem || puppet_older_than?('3.5.0')) + # For Puppet gems < 3.5.0 (tested as far back as 3.0.0) on Windows + if puppet_type == :gem + gems['ffi'] = ['1.9.0', require: false] + gems['minitar'] = ['0.5.4', require: false] + gems['win32-eventlog'] = ['0.5.3', '<= 0.6.5', require: false] + gems['win32-process'] = ['0.6.5', '<= 0.7.5', require: false] + gems['win32-security'] = ['~> 0.1.2', '<= 0.2.5', require: false] + gems['win32-service'] = ['0.7.2', '<= 0.8.8', require: false] + else + gems['ffi'] = ['~> 1.9.0', require: false] + gems['minitar'] = ['~> 0.5.4', require: false] + gems['win32-eventlog'] = ['~> 0.5', '<= 0.6.5', require: false] + gems['win32-process'] = ['~> 0.6', '<= 0.7.5', require: false] + gems['win32-security'] = ['~> 0.1', '<= 0.2.5', require: false] + gems['win32-service'] = ['~> 0.7', '<= 0.8.8', require: false] + end + + gems['win32-dir'] = ['~> 0.3', '<= 0.4.9', require: false] + + if RUBY_VERSION.start_with?('1.') + gems['win32console'] = ['1.3.2', require: false] + # sys-admin was removed in Puppet 3.7.0 and doesn't compile under Ruby 2.x + gems['sys-admin'] = ['1.5.6', require: false] + end + + # Puppet < 3.7.0 requires these. + # Puppet >= 3.5.0 gem includes these as requirements. + # The following versions are tested to work with 3.0.0 <= puppet < 3.7.0. + gems['win32-api'] = ['1.4.8', require: false] + gems['win32-taskscheduler'] = ['0.2.2', require: false] + gems['windows-api'] = ['0.4.3', require: false] + gems['windows-pr'] = ['1.2.3', require: false] +elsif Gem.win_platform? + # If we're using a Puppet gem on Windows which handles its own win32-xxx gem + # dependencies (>= 3.5.0), set the maximum versions (see PUP-6445). + gems['win32-dir'] = ['<= 0.4.9', require: false] + gems['win32-eventlog'] = ['<= 0.6.5', require: false] + gems['win32-process'] = ['<= 0.7.5', require: false] + gems['win32-security'] = ['<= 0.2.5', require: false] + gems['win32-service'] = ['<= 0.8.8', require: false] +end + +gems.each do |gem_name, gem_params| + gem gem_name, *gem_params +end + +# Evaluate Gemfile.local and ~/.gemfile if they exist +extra_gemfiles = [ + "#{__FILE__}.local", + File.join(Dir.home, '.gemfile'), +] + +extra_gemfiles.each do |gemfile| + if File.file?(gemfile) && File.readable?(gemfile) + eval(File.read(gemfile), binding) + end +end +# vim: syntax=ruby @@ -1,2 +1,89 @@ -# puppetlabs-mailalias -Manage Mail Alias +# mailalias + +#### Table of Contents + +1. [Description](#description) +2. [Usage - Configuration options and additional functionality](#usage) +3. [Reference - An under-the-hood peek at what the module is doing and how](#reference) +4. [Limitations - OS compatibility, etc.](#limitations) +5. [Development - Guide for contributing to the module](#development) + +## Description + +The mailalias module is used to manage entries in the local alias database. + +### Beginning with mailalias +To manage a mail alias, add the mailalias type to a class: +``` +mailalias { 'ftp': + ensure => present, + recipient => 'root', +} +``` +This example will redirect mail for the ftp account to root's mailbox. + +## Usage +The mailalias module is used to manage entries in `/etc/aliases`, which creates an email alias in the local alias database. + +For details on usage, please see [the puppet docs](https://puppet.com/docs/puppet/latest/types/mailalias.html). + +#### file +A file containing the alias’s contents. The file and the recipient entries are mutually exclusive. +``` +mailalias { 'usenet': + ensure => present, + file => '/tmp/foo/usenet-alias', +} +``` +This will result in an entry such as `usenet: :include: /tmp/foo/usenet-alias` + +#### recipient +Where email should be sent. Multiple values should be specified as an array. The file and the recipient entries are mutually exclusive. +``` +mailalias { 'ftp': + ensure => present, + recipient => 'root', +} +``` +This will result in an entry such as `ftp: root` + +#### target +The file in which to store the aliases. Only used by those providers that write to disk. +``` +mailalias { 'ftp': + ensure => present, + recipient => 'root', + target => `/etc/mail/aliases` +} +``` +This will ensure the entry exists in the file specified, such as: +``` +$ cat /etc/mail/aliases +ftp: root +``` + +## Reference + +This module is documented using Puppet Strings. + +For a quick primer on how Strings works, please see [this blog post](https://puppet.com/blog/using-puppet-strings-generate-great-documentation-puppet-modules) or the [README.md](https://github.com/puppetlabs/puppet-strings/blob/master/README.md) for Puppet Strings. + +To generate documentation locally, run +``` +bundle install +bundle exec puppet strings generate ./lib/**/*.rb +``` +This command will create a browsable `\_index.html` file in the `doc` directory. The references available here are all generated from YARD-style comments embedded in the code base. When any development happens on this module, the impacted documentation should also be updated. + + +## Limitations + +This module is only supported on platforms that have `sendmail` available. + +## Development + +Puppet Labs modules on the Puppet Forge are open projects, and community contributions are essential for keeping them great. We can't access the huge number of platforms and myriad of hardware, software, and deployment configurations that Puppet is intended to serve. + +We want to keep it as easy as possible to contribute changes so that our modules work in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. + +For more information, see our [module contribution guide.](https://docs.puppetlabs.com/forge/contributing.html) diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..81381e0 --- /dev/null +++ b/Rakefile @@ -0,0 +1,2 @@ +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-syntax/tasks/puppet-syntax' diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..5fd5e89 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,57 @@ +version: 1.1.x.{build} +skip_commits: + message: /^\(?doc\)?.*/ +clone_depth: 10 +init: + - SET + - 'mkdir C:\ProgramData\PuppetLabs\code && exit 0' + - 'mkdir C:\ProgramData\PuppetLabs\facter && exit 0' + - 'mkdir C:\ProgramData\PuppetLabs\hiera && exit 0' + - 'mkdir C:\ProgramData\PuppetLabs\puppet\var && exit 0' +environment: + matrix: + - + RUBY_VERSION: 24-x64 + CHECK: syntax lint + - + RUBY_VERSION: 24-x64 + CHECK: metadata_lint + - + RUBY_VERSION: 24-x64 + CHECK: rubocop + - + PUPPET_GEM_VERSION: ~> 4.0 + RUBY_VERSION: 21 + CHECK: spec + - + PUPPET_GEM_VERSION: ~> 4.0 + RUBY_VERSION: 21-x64 + CHECK: spec + - + PUPPET_GEM_VERSION: ~> 5.0 + RUBY_VERSION: 24 + CHECK: spec + - + PUPPET_GEM_VERSION: ~> 5.0 + RUBY_VERSION: 24-x64 + CHECK: spec +matrix: + fast_finish: true +install: + - set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% + - bundle install --jobs 4 --retry 2 --without system_tests + - type Gemfile.lock +build: off +test_script: + - bundle exec puppet -V + - ruby -v + - gem -v + - bundle -v + - bundle exec rake %CHECK% +notifications: + - provider: Email + to: + - nobody@nowhere.com + on_build_success: false + on_build_failure: false + on_build_status_changed: false diff --git a/lib/puppet/provider/mailalias/aliases.rb b/lib/puppet/provider/mailalias/aliases.rb new file mode 100644 index 0000000..c8e5e3e --- /dev/null +++ b/lib/puppet/provider/mailalias/aliases.rb @@ -0,0 +1,52 @@ +require 'puppet/provider/parsedfile' + +Puppet::Type.type(:mailalias).provide( + :aliases, + parent: Puppet::Provider::ParsedFile, + default_target: '/etc/aliases', + filetype: :flat, +) do + + desc 'The alias provider for mailalias.' + + text_line :comment, match: %r{^#} + text_line :blank, match: %r{^\s*$} + + record_line :aliases, fields: %w[name recipient], separator: %r{\s*:\s*}, block_eval: :instance do + def post_parse(record) + if record[:recipient] + record[:recipient] = record[:recipient].split(%r{\s*,\s*}).map { |d| d.gsub(%r{^['"]|['"]$}, '') } + end + record + end + + def process(line) + ret = {} + records = line.split(':', 4) + ret[:name] = records[0].strip + if records.length == 4 && 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].map { |d| + # Quote aliases that have non-alpha chars + if d =~ %r{[^-+\w@.]} + '"%s"' % d + else + d + 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..59c88ef --- /dev/null +++ b/lib/puppet/type/mailalias.rb @@ -0,0 +1,60 @@ +# Creates an email alias in the local alias database. +module Puppet + Type.newtype(:mailalias) do + desc <<-DESC + Creates an email alias in the local alias database. + + @example using mailalias to redirect mail for the ftp account to root's mailbox + mailalias { 'ftp': + ensure => present, + recipient => 'root', + } + DESC + + 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) + raise 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 do + if @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile) + @resource.class.defaultprovider.default_target + else + nil + end + end + end + + validate do + if self[:recipient] && self[:file] + # rubocop:disable Style/SignalException + # We need to override this cop because puppet overrides `fail`. We need + # to use the puppet implementation of `fail` rather than the default + # ruby implementation + fail _('You cannot specify both a recipient and a file') + end + end + end +end diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..782e7f6 --- /dev/null +++ b/metadata.json @@ -0,0 +1,84 @@ +{ + "name": "puppetlabs-mailalias_core", + "version": "0.1.0", + "author": "Puppet Labs", + "summary": "Creates an email alias in the local alias database.", + "license": "Apache-2.0", + "source": "https://github.com/puppetlabs/puppetlabs-mailalias_core", + "project_page": "https://github.com/puppetlabs/puppetlabs-mailalias_core/blob/master/README.md", + "issues_url": "https://tickets.puppetlabs.com/projects/MODULES", + "dependencies": [ + + ], + "operatingsystem_support": [ + { + "operatingsystem": "CentOS", + "operatingsystemrelease": [ + "7" + ] + }, + { + "operatingsystem": "OracleLinux", + "operatingsystemrelease": [ + "7" + ] + }, + { + "operatingsystem": "RedHat", + "operatingsystemrelease": [ + "7" + ] + }, + { + "operatingsystem": "Scientific", + "operatingsystemrelease": [ + "7" + ] + }, + { + "operatingsystem": "Debian", + "operatingsystemrelease": [ + "8" + ] + }, + { + "operatingsystem": "Ubuntu", + "operatingsystemrelease": [ + "16.04" + ] + }, + { + "operatingsystem": "Fedora", + "operatingsystemrelease": [ + "25" + ] + }, + { + "operatingsystem": "Darwin", + "operatingsystemrelease": [ + "16" + ] + }, + { + "operatingsystem": "SLES", + "operatingsystemrelease": [ + "12" + ] + }, + { + "operatingsystem": "Solaris", + "operatingsystemrelease": [ + "11" + ] + } + ], + "requirements": [ + { + "name": "puppet", + "version_requirement": ">= 4.7.0 < 6.0.0" + } + ], + "pdk-version": "1.4.1", + "template-url": "file:///opt/puppetlabs/pdk/share/cache/pdk-templates.git", + "template-ref": "1.4.1-0-g52adbbb" +} diff --git a/spec/acceptance/nodesets/default.yml b/spec/acceptance/nodesets/default.yml new file mode 100644 index 0000000..b53e1a1 --- /dev/null +++ b/spec/acceptance/nodesets/default.yml @@ -0,0 +1,19 @@ +--- +HOSTS: + ubuntu1604-64-1: + pe_dir: + pe_ver: + pe_upgrade_dir: + pe_upgrade_ver: + hypervisor: vmpooler + platform: ubuntu-16.04-amd64 + packaging_platform: ubuntu-16.04-amd64 + template: ubuntu-1604-x86_64 + roles: + - agent + - default +CONFIG: + type: agent + nfs_server: none + consoleport: 443 + pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/spec/acceptance/tests/create_spec.rb b/spec/acceptance/tests/create_spec.rb new file mode 100644 index 0000000..82adfec --- /dev/null +++ b/spec/acceptance/tests/create_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper_acceptance' + +RSpec.context 'Mailalias: should create an email alias' do + name = "pl#{rand(999_999).to_i}" + + before(:all) do + non_windows_agents.each do |agent| + on(agent, 'cp /etc/aliases /tmp/aliases', acceptable_exit_codes: [0, 1]) + end + end + + after(:all) do + non_windows_agents.each do |agent| + on(agent, 'mv /tmp/aliases /etc/aliases', acceptable_exit_codes: [0, 1]) + end + end + + non_windows_agents.each do |agent| + it 'creates a mailalias with puppet' do + args = ['ensure=present', + 'recipient="foo,bar,baz"'] + on(agent, puppet_resource('mailalias', name, args)) + end + + it 'verifies the alias exists' do + on(agent, 'cat /etc/aliases') do |res| + assert_match(%r{#{name}:.*foo,bar,baz}, res.stdout, 'mailalias not in aliases file') + end + end + end +end diff --git a/spec/acceptance/tests/destroy_spec.rb b/spec/acceptance/tests/destroy_spec.rb new file mode 100644 index 0000000..0f805a5 --- /dev/null +++ b/spec/acceptance/tests/destroy_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper_acceptance' + +RSpec.context 'should delete an email alias' do + name = "pl#{rand(999_999).to_i}" + + before(:all) do + non_windows_agents.each do |agent| + # (setup) backup alias file + on(agent, 'cp /etc/aliases /tmp/aliases', acceptable_exit_codes: [0, 1]) + + # (setup) create a mailalias + on(agent, "echo '#{name}: foo,bar,baz' >> /etc/aliases") + + # (setup) verify the alias exists + on(agent, 'cat /etc/aliases') do |res| + assert_match(%r{#{name}:.*foo,bar,baz}, res.stdout, 'mailalias not in aliases file') + end + end + end + + after(:all) do + non_windows_agents.each do |agent| + # (teardown) restore the alias file + on(agent, 'mv /tmp/aliases /etc/aliases', acceptable_exit_codes: [0, 1]) + end + end + + non_windows_agents.each do |agent| + it 'deletes the aliases database with puppet' do + args = ['ensure=absent', + 'recipient="foo,bar,baz"'] + on(agent, puppet_resource('mailalias', name, args)) + end + + it 'verifies the alias is absent' do + on(agent, 'cat /etc/aliases') do |res| + assert_no_match(%r{#{name}:.*foo,bar,baz}, res.stdout, 'mailalias was not removed from aliases file') + end + end + end +end diff --git a/spec/acceptance/tests/modify_spec.rb b/spec/acceptance/tests/modify_spec.rb new file mode 100644 index 0000000..f4ea7bf --- /dev/null +++ b/spec/acceptance/tests/modify_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper_acceptance' + +RSpec.context 'should modify an email alias' do + name = "pl#{rand(999_999).to_i}" + + before(:all) do + non_windows_agents.each do |agent| + #------- SETUP -------# + # (setup) backup alias file + on(agent, 'cp /etc/aliases /tmp/aliases', acceptable_exit_codes: [0, 1]) + + # (setup) create a mailalias + on(agent, "echo '#{name}: foo,bar,baz' >> /etc/aliases") + + # (setup) verify the alias exists + on(agent, 'cat /etc/aliases') do |res| + assert_match(%r{#{name}:.*foo,bar,baz}, res.stdout, 'mailalias not in aliases file') + end + end + end + + after(:all) do + non_windows_agents.each do |agent| + # (teardown) restore the alias file + on(agent, 'mv /tmp/aliases /etc/aliases', acceptable_exit_codes: [0, 1]) + end + end + + non_windows_agents.each do |agent| + it 'modifies the aliases database with puppet' do + args = ['ensure=present', + 'recipient="foo,bar,baz,blarvitz"'] + on(agent, puppet_resource('mailalias', name, args)) + end + + it 'verifies the updated alias is present' do + on(agent, 'cat /etc/aliases') do |res| + assert_match(%r{#{name}:.*foo,bar,baz,blarvitz}, res.stdout, 'updated mailalias not in aliases file') + end + end + end +end diff --git a/spec/acceptance/tests/query_spec.rb b/spec/acceptance/tests/query_spec.rb new file mode 100644 index 0000000..17e0bbf --- /dev/null +++ b/spec/acceptance/tests/query_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper_acceptance' + +RSpec.context 'should be able to find an exisitng email alias' do + name = "pl#{rand(999_999).to_i}" + + before(:all) do + non_windows_agents.each do |agent| + # (setup) backup alias file + on(agent, 'cp /etc/aliases /tmp/aliases', acceptable_exit_codes: [0, 1]) + + # (setup) create a mailalias + on(agent, "echo '#{name}: foo,bar,baz' >> /etc/aliases") + + # (setup) verify the alias exists + on(agent, 'cat /etc/aliases') do |res| + assert_match(%r{#{name}:.*foo,bar,baz}, res.stdout, 'mailalias not in aliases file') + end + end + end + + after(:all) do + non_windows_agents.each do |agent| + # (teardown) restore the alias file + on(agent, 'mv /tmp/aliases /etc/aliases', acceptable_exit_codes: [0, 1]) + end + end + + non_windows_agents.each do |agent| + it 'queries for the mail alias with puppet' do + on(agent, puppet_resource('mailalias', name)) do + fail_test "didn't find the scheduled_task #{name}" unless stdout.include? 'present' + end + end + end +end diff --git a/spec/default_facts.yml b/spec/default_facts.yml new file mode 100644 index 0000000..3248be5 --- /dev/null +++ b/spec/default_facts.yml @@ -0,0 +1,8 @@ +# Use default_module_facts.yml for module specific facts. +# +# Facts specified here will override the values provided by rspec-puppet-facts. +--- +concat_basedir: "/tmp" +ipaddress: "172.16.254.254" +is_pe: false +macaddress: "AA:AA:AA:AA:AA:AA" 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..ae9dc10 --- /dev/null +++ b/spec/integration/provider/mailalias/aliases_spec.rb @@ -0,0 +1,9 @@ +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_behaves_like 'all parsedfile providers', provider_class +end diff --git a/spec/lib/puppet_spec/files.rb b/spec/lib/puppet_spec/files.rb new file mode 100644 index 0000000..5fef530 --- /dev/null +++ b/spec/lib/puppet_spec/files.rb @@ -0,0 +1,123 @@ +require 'fileutils' +require 'tempfile' +require 'tmpdir' +require 'pathname' + +# A support module for testing files. +module PuppetSpec::Files + @global_tempfiles = [] + + def self.cleanup + until @global_tempfiles.empty? + path = @global_tempfiles.pop + begin + Dir.unstub(:entries) + FileUtils.rm_rf path, secure: true + end + end + end + + def make_absolute(path) + PuppetSpec::Files.make_absolute(path) + end + + def self.make_absolute(path) + path = File.expand_path(path) + path[0] = 'c' if Puppet.features.microsoft_windows? + path + end + + def tmpfile(name, dir = nil) + PuppetSpec::Files.tmpfile(name, dir) + end + + def self.tmpfile(name, dir = nil) + # Generate a temporary file, just for the name... + source = dir ? Tempfile.new(name, dir) : Tempfile.new(name) + path = Puppet::FileSystem.expand_path(source.path.encode(Encoding::UTF_8)) + source.close! + + record_tmp(File.expand_path(path)) + + path + end + + def file_containing(name, contents) + PuppetSpec::Files.file_containing(name, contents) + end + + def self.file_containing(name, contents) + file = tmpfile(name) + File.open(file, 'wb') { |f| f.write(contents) } + file + end + + def script_containing(name, contents) + PuppetSpec::Files.script_containing(name, contents) + end + + def self.script_containing(name, contents) + file = tmpfile(name) + if Puppet.features.microsoft_windows? + file += '.bat' + text = contents[:windows] + else + text = contents[:posix] + end + File.open(file, 'wb') { |f| f.write(text) } + Puppet::FileSystem.chmod(0o755, file) + file + end + + def tmpdir(name) + PuppetSpec::Files.tmpdir(name) + end + + def self.tmpdir(name) + dir = Puppet::FileSystem.expand_path(Dir.mktmpdir(name).encode!(Encoding::UTF_8)) + + record_tmp(dir) + + dir + end + + def dir_containing(name, contents_hash) + PuppetSpec::Files.dir_containing(name, contents_hash) + end + + def self.dir_containing(name, contents_hash) + dir_contained_in(tmpdir(name), contents_hash) + end + + def dir_contained_in(dir, contents_hash) + PuppetSpec::Files.dir_contained_in(dir, contents_hash) + end + + def self.dir_contained_in(dir, contents_hash) + contents_hash.each do |k, v| + if v.is_a?(Hash) + Dir.mkdir(tmp = File.join(dir, k)) + dir_contained_in(tmp, v) + else + file = File.join(dir, k) + File.open(file, 'wb') { |f| f.write(v) } + end + end + dir + end + + def self.record_tmp(tmp) + # ...record it for cleanup, + @global_tempfiles << tmp + end + + def expect_file_mode(file, mode) + actual_mode = '%o' % Puppet::FileSystem.stat(file).mode + target_mode = if Puppet.features.microsoft_windows? + mode + else + '10' + '%04i' % mode.to_i + end + expect(actual_mode).to eq(target_mode) + end +end diff --git a/spec/shared_behaviours/all_parsedfile_providers.rb b/spec/shared_behaviours/all_parsedfile_providers.rb new file mode 100644 index 0000000..d697a14 --- /dev/null +++ b/spec/shared_behaviours/all_parsedfile_providers.rb @@ -0,0 +1,21 @@ +shared_examples_for 'all parsedfile providers' do |provider, *files| + if files.empty? + files = my_fixtures + end + + files.flatten.each do |file| + it "should rewrite #{file} reasonably unchanged" do + provider.stubs(:default_target).returns(file) + provider.prefetch + + text = provider.to_file(provider.target_records(file)) + text.gsub!(%r{^# HEADER.+\n}, '') + + oldlines = File.readlines(file) + newlines = text.chomp.split "\n" + oldlines.zip(newlines).each do |old, new| + expect(new.gsub(%r{\s+}, '')).to eq(old.chomp.gsub(%r{\s+}, '')) + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..9885e2d --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,44 @@ +require 'puppetlabs_spec_helper/module_spec_helper' +require 'rspec-puppet-facts' + +begin + require 'spec_helper_local' if File.file?(File.join(File.dirname(__FILE__), 'spec_helper_local.rb')) +rescue LoadError => loaderror + warn "Could not require spec_helper_local: #{loaderror.message}" +end + +include RspecPuppetFacts + +default_facts = { + puppetversion: Puppet.version, + facterversion: Facter.version, +} + +default_facts_path = File.expand_path(File.join(File.dirname(__FILE__), 'default_facts.yml')) +default_module_facts_path = File.expand_path(File.join(File.dirname(__FILE__), 'default_module_facts.yml')) + +if File.exist?(default_facts_path) && File.readable?(default_facts_path) + default_facts.merge!(YAML.safe_load(File.read(default_facts_path))) +end + +if File.exist?(default_module_facts_path) && File.readable?(default_module_facts_path) + default_facts.merge!(YAML.safe_load(File.read(default_module_facts_path))) +end + +RSpec.configure do |c| + c.default_facts = default_facts +end + +dir = File.expand_path(File.dirname(__FILE__)) +$LOAD_PATH.unshift File.join(dir, 'lib') + +# So everyone else doesn't have to include this base constant. +module PuppetSpec + FIXTURE_DIR = File.join(File.expand_path(File.dirname(__FILE__)), 'fixtures') unless defined?(FIXTURE_DIR) +end + +require 'puppet_spec/files' + +Pathname.glob("#{dir}/shared_behaviours/**/*.rb") do |behaviour| + require behaviour.relative_path_from(Pathname.new(dir)) +end diff --git a/spec/spec_helper_acceptance.rb b/spec/spec_helper_acceptance.rb new file mode 100644 index 0000000..0adde24 --- /dev/null +++ b/spec/spec_helper_acceptance.rb @@ -0,0 +1,24 @@ +require 'puppet' +require 'beaker-rspec' +require 'beaker/module_install_helper' +require 'beaker/puppet_install_helper' + +$LOAD_PATH << File.join(__dir__, 'acceptance/lib') + +def beaker_opts + { debug: true, trace: true, expect_failures: true, acceptable_exit_codes: (0...256) } +end + +def non_windows_agents + agents.reject { |agent| agent['platform'].include?('windows') } +end + +RSpec.configure do |c| + c.before :suite do + unless ENV['BEAKER_provision'] == 'no' + run_puppet_install_helper + install_module_on(hosts_as(hosts)) + install_module_dependencies_on(hosts) + end + end +end diff --git a/spec/unit/type/mailalias_spec.rb b/spec/unit/type/mailalias_spec.rb new file mode 100644 index 0000000..1fb195e --- /dev/null +++ b/spec/unit/type/mailalias_spec.rb @@ -0,0 +1,54 @@ +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 'is initially absent as a recipient' do + expect(recipient_resource.retrieve_resource[:recipient]).to eq(:absent) + end + + it 'is initially absent as an included file' do + expect(file_resource.retrieve_resource[:file]).to eq(:absent) + end + + it 'tries 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 'tries 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 'fails when file is not an absolute path' do + expect { + Puppet::Type.type(:mailalias).new(name: 'x', file: 'afile') + }.to raise_error Puppet::Error, %r{File paths must be fully qualified} + end + + it 'fails 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, %r{cannot specify both a recipient and a file} + end +end |