diff --git a/README.md b/README.md index df2c70d5a..6632e0992 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ 4. [Usage - The classes and defined types available for configuration](#usage) * [Classes and Defined Types](#classes-and-defined-types) * [Class: apache](#class-apache) + * [Defined Type: apache::custom_config](#defined-type-apachecustom_config) * [Class: apache::default_mods](#class-apachedefault_mods) * [Defined Type: apache::mod](#defined-type-apachemod) * [Classes: apache::mod::*](#classes-apachemodname) @@ -429,6 +430,46 @@ Changes the location of the configuration directory your virtual host configurat The name of the Apache package to install. This is automatically detected in `::apache::params`. You may need to override this if you are using a non-standard Apache package, such as those from Red Hat's software collections. +####Defined Type: `apache::custom_config` + +Allows you to create custom configs for Apache. The configuration files will only be added to the Apache confd dir if the file is valid. An error will be raised during the puppet run if the file is invalid and `$verify_config` is `true`. + +```puppet + apache::custom_config { 'test': + content => '# Test', + } +``` + +**Parameters within `apache::custom_config`:** + +#####`ensure` + +Specify whether the configuration file is present or absent. Defaults to 'present'. Valid values are 'present' and 'absent'. + +#####`confdir` + +The directory to place the configuration file in. Defaults to `$::apache::confd_dir`. + +#####`content` + +The content of the configuration file. Only one of `$content` and `$source` can be specified. + +#####`priority` + +The priority of the configuration file, used for ordering. Defaults to '25'. + +#####`source` + +The source of the configuration file. Only one of `$content` and `$source` can be specified. + +#####`verify_command` + +The command to use to verify the configuration file. It should use a fully qualified command. Defaults to '/usr/sbin/apachectl -t'. The `$verify_command` will only be used if `$verify_config` is `true`. If the `$verify_command` fails the configuration file will be deleted, the Apache service will not be notified, and an error will be raised during the puppet run. + +#####`verify_config` + +Boolean to specify whether the configuration file should be validated before the Apache service is notified. Defaults to `true`. + ####Class: `apache::default_mods` Installs default Apache modules based on what OS you are running. diff --git a/manifests/custom_config.pp b/manifests/custom_config.pp new file mode 100644 index 000000000..543ace94e --- /dev/null +++ b/manifests/custom_config.pp @@ -0,0 +1,60 @@ +# See README.md for usage information +define apache::custom_config ( + $ensure = 'present', + $confdir = $::apache::confd_dir, + $content = undef, + $priority = '25', + $source = undef, + $verify_command = '/usr/sbin/apachectl -t', + $verify_config = true, +) { + + if $content and $source { + fail('Only one of $content and $source can be specified.') + } + + if $ensure == 'present' and ! $content and ! $source { + fail('One of $content and $source must be specified.') + } + + validate_re($ensure, '^(present|absent)$', + "${ensure} is not supported for ensure. + Allowed values are 'present' and 'absent'.") + + validate_bool($verify_config) + + ## Apache include does not always work with spaces in the filename + $filename = regsubst($name, ' ', '_', 'G') + + if ! $verify_config or $ensure == 'absent' { + $notifies = Service['httpd'] + } else { + $notifies = undef + } + + file { "apache_${name}": + ensure => $ensure, + path => "${confdir}/${priority}-${filename}.conf", + content => $content, + source => $source, + require => Package['httpd'], + notify => $notifies, + } + + if $ensure == 'present' and $verify_config { + exec { "service notify for ${name}": + command => $verify_command, + subscribe => File["apache_${name}"], + refreshonly => true, + notify => Service['httpd'], + before => Exec["remove ${name} if invalid"], + } + + exec { "remove ${name} if invalid": + command => "/bin/rm ${confdir}/${priority}-${filename}.conf", + unless => $verify_command, + subscribe => File["apache_${name}"], + refreshonly => true, + } + } +} diff --git a/spec/acceptance/apache_parameters_spec.rb b/spec/acceptance/apache_parameters_spec.rb index 983bbb167..3a21ab0eb 100644 --- a/spec/acceptance/apache_parameters_spec.rb +++ b/spec/acceptance/apache_parameters_spec.rb @@ -11,7 +11,7 @@ end if fact('osfamily') == 'FreeBSD' - describe file("#{confd_dir}/no-accf.conf.erb") do + describe file("#{$confd_dir}/no-accf.conf.erb") do it { is_expected.not_to be_file } end end @@ -80,9 +80,8 @@ class { 'apache': pp = <<-EOS class { 'apache': purge_configs => false, - purge_vdir => false, purge_vhost_dir => false, - vhost_dir => "#{confd_dir}.vhosts" + vhost_dir => "#{$confd_dir}.vhosts" } EOS shell("touch #{$confd_dir}/test.conf") @@ -105,9 +104,8 @@ class { 'apache': pp = <<-EOS class { 'apache': purge_configs => true, - purge_vdir => true, purge_vhost_dir => true, - vhost_dir => "#{confd_dir}.vhosts" + vhost_dir => "#{$confd_dir}.vhosts" } EOS shell("touch #{$confd_dir}/test.conf") @@ -202,7 +200,7 @@ class { 'apache': httpd_dir => '/tmp', service_ensure => stopped } end end - describe file("#{$confd_dir}/mime.conf") do + describe file("#{$mod_dir}/mime.conf") do it { is_expected.to be_file } it { is_expected.to contain 'AddLanguage eo .eo' } end diff --git a/spec/acceptance/custom_config_spec.rb b/spec/acceptance/custom_config_spec.rb new file mode 100644 index 000000000..fce6bb306 --- /dev/null +++ b/spec/acceptance/custom_config_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper_acceptance' +require_relative './version.rb' + +describe 'apache::custom_config define', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do + context 'invalid config' do + it 'should not add the config' do + pp = <<-EOS + class { 'apache': } + apache::custom_config { 'acceptance_test': + content => 'INVALID', + } + EOS + + apply_manifest(pp, :expect_failures => true) + end + + describe file("#{$confd_dir}/25-acceptance_test.conf") do + it { is_expected.not_to be_file } + end + end + + context 'valid config' do + it 'should add the config' do + pp = <<-EOS + class { 'apache': } + apache::custom_config { 'acceptance_test': + content => '# just a comment', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{$confd_dir}/25-acceptance_test.conf") do + it { is_expected.to contain '# just a comment' } + end + end +end diff --git a/spec/acceptance/version.rb b/spec/acceptance/version.rb index 27498354b..b88412b42 100644 --- a/spec/acceptance/version.rb +++ b/spec/acceptance/version.rb @@ -5,6 +5,7 @@ case _osfamily when 'RedHat' $confd_dir = '/etc/httpd/conf.d' + $mod_dir = '/etc/httpd/conf.d' $conf_file = '/etc/httpd/conf/httpd.conf' $ports_file = '/etc/httpd/conf/ports.conf' $vhost_dir = '/etc/httpd/conf.d' @@ -22,7 +23,8 @@ $apache_version = '2.2' end when 'Debian' - $confd_dir = '/etc/apache2/mods-available' + $confd_dir = '/etc/apache2/conf.d' + $mod_dir = '/etc/apache2/mods-available' $conf_file = '/etc/apache2/apache2.conf' $ports_file = '/etc/apache2/ports.conf' $vhost = '/etc/apache2/sites-available/15-default.conf' @@ -41,6 +43,7 @@ end when 'FreeBSD' $confd_dir = '/usr/local/etc/apache22/Includes' + $mod_dir = '/usr/local/etc/apache22/Modules' $conf_file = '/usr/local/etc/apache22/httpd.conf' $ports_file = '/usr/local/etc/apache22/Includes/ports.conf' $vhost = '/usr/local/etc/apache22/Vhosts/15-default.conf' diff --git a/spec/defines/custom_config_spec.rb b/spec/defines/custom_config_spec.rb new file mode 100644 index 000000000..187b8a7b5 --- /dev/null +++ b/spec/defines/custom_config_spec.rb @@ -0,0 +1,137 @@ +require 'spec_helper' + +describe 'apache::custom_config', :type => :define do + let :pre_condition do + 'class { "apache": }' + end + let :title do + 'rspec' + end + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/', + :lsbdistcodename => 'squeeze', + :operatingsystem => 'Debian', + :id => 'root', + :kernel => 'Linux', + :path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + } + end + context 'defaults with content' do + let :params do + { + 'content' => '# Test', + } + end + it { is_expected.to contain_exec("service notify for rspec").with({ + 'refreshonly' => 'true', + 'subscribe' => 'File[apache_rspec]', + 'command' => '/usr/sbin/apachectl -t', + 'notify' => 'Service[httpd]', + 'before' => 'Exec[remove rspec if invalid]', + }) + } + it { is_expected.to contain_exec("remove rspec if invalid").with({ + 'unless' => '/usr/sbin/apachectl -t', + 'subscribe' => 'File[apache_rspec]', + 'refreshonly' => 'true', + }) + } + it { is_expected.to contain_file("apache_rspec").with({ + 'ensure' => 'present', + 'content' => '# Test', + 'require' => 'Package[httpd]', + }) + } + end + context 'set everything with source' do + let :params do + { + 'confdir' => '/dne', + 'priority' => '30', + 'source' => 'puppet:///modules/apache/test', + 'verify_command' => '/bin/true', + } + end + it { is_expected.to contain_exec("service notify for rspec").with({ + 'command' => '/bin/true', + }) + } + it { is_expected.to contain_exec("remove rspec if invalid").with({ + 'command' => '/bin/rm /dne/30-rspec.conf', + 'unless' => '/bin/true', + }) + } + it { is_expected.to contain_file("apache_rspec").with({ + 'path' => '/dne/30-rspec.conf', + 'ensure' => 'present', + 'source' => 'puppet:///modules/apache/test', + 'require' => 'Package[httpd]', + }) + } + end + context 'verify_config => false' do + let :params do + { + 'content' => '# test', + 'verify_config' => false, + } + end + it { is_expected.to_not contain_exec('service notify for rspec') } + it { is_expected.to_not contain_exec('remove rspec if invalid') } + it { is_expected.to contain_file('apache_rspec').with({ + 'notify' => 'Service[httpd]' + }) + } + end + context 'ensure => absent' do + let :params do + { + 'ensure' => 'absent' + } + end + it { is_expected.to_not contain_exec('service notify for rspec') } + it { is_expected.to_not contain_exec('remove rspec if invalid') } + it { is_expected.to contain_file('apache_rspec').with({ + 'ensure' => 'absent', + }) + } + end + describe 'validation' do + context 'both content and source' do + let :params do + { + 'content' => 'foo', + 'source' => 'bar', + } + end + it do + expect { + should compile + }.to raise_error(Puppet::Error, /Only one of \$content and \$source can be specified\./) + end + end + context 'neither content nor source' do + it do + expect { + should compile + }.to raise_error(Puppet::Error, /One of \$content and \$source must be specified\./) + end + end + context 'bad ensure' do + let :params do + { + 'content' => 'foo', + 'ensure' => 'foo', + } + end + it do + expect { + should compile + }.to raise_error(Puppet::Error, /is not supported for ensure/) + end + end + end +end