diff --git a/.fixtures.yml b/.fixtures.yml index 1c772f2..9aa06d2 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -1,5 +1,7 @@ fixtures: repositories: firewall: 'https://github.com/puppetlabs/puppetlabs-firewall.git' stdlib: 'https://github.com/puppetlabs/puppetlabs-stdlib.git' svcprop: 'https://github.com/bolthole/puppet-svcprop.git' + systemd: 'https://github.com/camptocamp/puppet-systemd.git' + selinux: 'https://github.com/voxpupuli/puppet-selinux.git' diff --git a/README.md b/README.md index 569eaa3..434cdd3 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,140 @@ # puppet-memcached [![Build Status](https://secure.travis-ci.org/saz/puppet-memcached.png)](http://travis-ci.org/saz/puppet-memcached) Manage memcached via Puppet ## Show some love If you find this module useful, send some bitcoins to 1Na3YFUmdxKxJLiuRXQYJU2kiNqA3KY2j9 ### Supported Puppet versions * Puppet >= 5 * Last version supporting Puppet 3: v3.0.2 ## How to use ``` Starting with version 3.0.0, memcached will be listening on 127.0.0.1 only. This should make setups more secure (e.g. if there are no firewall rules in place). To change this behavior, you need to set listen_ip to '0.0.0.0'. ``` ### Use roughly 90% of memory ```ruby class { 'memcached': } ``` ### Set a fixed memory limit in MB ```ruby class { 'memcached': max_memory => 2048 } ``` ### Use 12% of available memory ```ruby class { 'memcached': max_memory => '12%' } ``` +### Install multiple memcached instances + +the multiinstance support uses a systemd instance unit file. This will be placed +at `/etc/systemd/system/memcached@.service`. It allows us to manage multiple +instances via the same unit file. To start a simple instance, you only need to +know the desired TCP port: + +```puppet +memcached::instance{'11222':} +``` + +that's it! It will bind to localhost and listen to TCP port 11222. You might +want to tune the systemd limits, for example the number of file descriptors +(LimitNOFILE) or the number of processes (LimitNPROC): + +```puppet +memcached::instance{'11222': + limits => { + 'LimitNOFILE' => 8192, + 'LimitNPROC' => 16384, + } +} +``` + +All systemd limits are documented in the [systemd documentation](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Process%20Properties). + +Another usecase. Let's assume your name is Eric and you want to change the +actual memcached parameters, for example to bind it to every interface: + +```puppet +memcached::instance{'11222': + override_content => "[Service]\nEnvironment='LISTEN=-l 0.0.0.0'", +} +``` + +Maybe Eric also wants to override the cache size (the unit is MB): + +```puppet +memcached::instance{'11222': + override_content => "[Service]\nEnvironment=CACHESIZE=4096\n", +} +``` + +last but not least, Eric might also want to override the maximum amount +of connections (the default is 1024): + +```puppet +memcached::instance{'11222': + override_content => "[Service]\nEnvironment=MAXCONN=4096\n", +} +``` + +Now Eric wants to use all those three settings at the same time: + +```puppet +memcached::instance{'11222': + override_content => "[Service]\nEnvironment=MAXCONN=4096\nEnvironment=CACHESIZE=4096\nEnvironment='LISTEN=-l 0.0.0.0'\n", +} +``` + +Instead of passing a long string with multiple `\n`, Eric can also put the +content in a file and provide that: + +```puppet +memcached::instance{'11222': + override_source => "${module_name}/memcached_11222_override.conf\n", +} +``` + ### Other class parameters * $package_ensure = 'present' * $logfile = '/var/log/memcached.log' * $logstdout = false (Set this to true to disable logging to a file/syslog entirely, useful when memcached runs in containers) * $pidfile = '/var/run/memcached.pid' (Debian family only, set to false to disable pidfile) * $max_memory = false * $max_item_size = false * $min_item_size = false * $factor = false * $lock_memory = false (WARNING: good if used intelligently, google for -k key) * $listen_ip = '127.0.0.1' * $tcp_port = 11211 * $udp_port = 11211 * $manage_firewall = false * $user = '' (OS specific setting, see params.pp) * $max_connections = 8192 * $verbosity = undef * $unix_socket = undef * $install_dev = false (TRUE if 'libmemcached-dev' package should be installed) * $processorcount = $::processorcount * $service_restart = true (restart service after configuration changes, false to prevent restarts) * $use_sasl = false (start memcached with SASL support) * $use_tls = false (start memcached with TLS support) * $tls_cert_chain = undef * $tls_key = undef * $tls_ca_cert = undef * $tls_verify_mode = 1 (0: None, 1: Request, 2: Require, 3: Once) * $large_mem_pages = false (try to use large memory pages) diff --git a/manifests/init.pp b/manifests/init.pp index beb8fc4..80510a8 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,144 +1,144 @@ # == Class: memcached # # Manage memcached # # == Parameters # [* syslog *] # Boolean. # If true will pipe output to /bin/logger, sends to syslog. # class memcached ( Enum['present', 'latest', 'absent'] $package_ensure = 'present', Boolean $service_manage = true, Optional[Stdlib::Absolutepath] $logfile = $memcached::params::logfile, Boolean $logstdout = false, Boolean $syslog = false, Optional[Stdlib::Absolutepath] $pidfile = '/var/run/memcached.pid', Boolean $manage_firewall = false, $max_memory = '95%', Optional[Variant[Integer, String]] $max_item_size = undef, Optional[Variant[Integer, String]] $min_item_size = undef, Optional[Variant[Integer, String]] $factor = undef, Boolean $lock_memory = false, Optional[Variant[Stdlib::Compat::Ip_address,Array[Stdlib::Compat::Ip_address]]] $listen_ip = '127.0.0.1', Integer $tcp_port = 11211, Integer $udp_port = 11211, String $user = $memcached::params::user, Integer $max_connections = 8192, Optional[String] $verbosity = undef, Optional[String] $unix_socket = undef, String $unix_socket_mask = '0755', Boolean $install_dev = false, Variant[String,Integer] $processorcount = $facts['processors']['count'], Boolean $service_restart = true, Boolean $auto_removal = false, Boolean $use_sasl = false, Boolean $use_tls = false, Optional[Stdlib::Absolutepath] $tls_cert_chain = undef, Optional[Stdlib::Absolutepath] $tls_key = undef, Optional[Stdlib::Absolutepath] $tls_ca_cert = undef, Optional[Integer] $tls_verify_mode = 1, Boolean $use_registry = $memcached::params::use_registry, String $registry_key = 'HKLM\System\CurrentControlSet\services\memcached\ImagePath', Boolean $large_mem_pages = false, Boolean $use_svcprop = $memcached::params::use_svcprop, String $svcprop_fmri = 'memcached:default', String $svcprop_key = 'memcached/options', Optional[Array[String]] $extended_opts = undef, String $config_tmpl = $memcached::params::config_tmpl, - Boolean $disable_cachedump = false + Boolean $disable_cachedump = false, ) inherits memcached::params { # Logging to syslog and file are mutually exclusive # Fail if both options are defined if $syslog and str2bool($logfile) { fail 'Define either syslog or logfile as logging destinations but not both.' } if $use_tls { if $tls_cert_chain == undef or $tls_key == undef { fail 'tls_cert_chain and tls_key should be set when use_tls is true.' } } if $package_ensure == 'absent' { $service_ensure = 'stopped' $service_enable = false } else { $service_ensure = 'running' $service_enable = true } # Handle if $listen_ip is not an array $real_listen_ip = [$listen_ip] package { $memcached::params::package_name: ensure => $package_ensure, provider => $memcached::params::package_provider, } if $install_dev { package { $memcached::params::dev_package_name: ensure => $package_ensure, require => Package[$memcached::params::package_name], } } if $manage_firewall { firewall { "100_tcp_${tcp_port}_for_memcached": dport => $tcp_port, proto => 'tcp', action => 'accept', } firewall { "100_udp_${udp_port}_for_memcached": dport => $udp_port, proto => 'udp', action => 'accept', } } if $service_restart and $service_manage { $service_notify_real = Service[$memcached::params::service_name] } else { $service_notify_real = undef } if ( $memcached::params::config_file ) { file { $memcached::params::config_file: ensure => 'file', owner => 'root', group => 0, mode => '0644', content => template($config_tmpl), require => Package[$memcached::params::package_name], notify => $service_notify_real, } } if $service_manage { service { $memcached::params::service_name: ensure => $service_ensure, enable => $service_enable, hasrestart => true, hasstatus => $memcached::params::service_hasstatus, } } if $use_registry { registry_value { $registry_key: ensure => 'present', type => 'string', data => template($config_tmpl), notify => $service_notify_real, } } if $use_svcprop { svcprop { $svcprop_key: fmri => $svcprop_fmri, property => $svcprop_key, value => template($memcached::params::config_tmpl), notify => $service_notify_real, } } } diff --git a/manifests/instance.pp b/manifests/instance.pp new file mode 100644 index 0000000..eef7bb9 --- /dev/null +++ b/manifests/instance.pp @@ -0,0 +1,70 @@ +# +# @summary Manage multiple memcached instances +# +# @param manage_firewall enable/disable fireall management via puppetlabs/firewall +# @param port the udp and tcp port to listen on. By default, the instance name is used +# @param limits systemd limits for the service +# @param override_content overrides for the unit, as string +# @param override_source overrides for the unit, as file resource +# +# @author pglushchenko +# @author Tim Meusel +# +define memcached::instance ( + Boolean $manage_firewall = false, + Stdlib::Port::Unprivileged $port = Integer($name), + Optional[Systemd::ServiceLimits] $limits = undef, + Optional[String[1]] $override_content = undef, + Optional[Stdlib::Filesource] $override_source = undef, +) { + + unless $facts['kernel'] == 'Linux' { + fail("memcached::instance currently only works with Linux, you are running ${facts['kernel']}") + } + require memcached + require memcached::instance::servicefile + + $service_name = "memcached@${port}.service" + if $manage_firewall { + firewall { "100_tcp_${port}_for_memcached": + dport => $port, + proto => 'tcp', + action => 'accept', + } + } + + service { $service_name: + ensure => 'running', + enable => true, + } + + if $limits { + systemd::service_limits { $service_name: + limits => $limits, + } + } + + if $override_content or $override_source { + if $override_content and $override_source { + fail('memcached::instance: you can only set override_content OR override_source, dont set both') + } + # eager is required to reload systemd before we reload the service + systemd::dropin_file { "${service_name}-override.conf": + unit => $service_name, + source => $override_source, + content => $override_content, + daemon_reload => 'eager', + notify => Service[$service_name], + } + } + + if $facts['os']['selinux']['enabled'] { + selinux::port { "allow-${service_name}": + ensure => 'present', + seltype => 'memcache_port_t', + protocol => 'tcp', + port => $port, + before => Service[$service_name], + } + } +} diff --git a/manifests/instance/servicefile.pp b/manifests/instance/servicefile.pp new file mode 100644 index 0000000..11d50f7 --- /dev/null +++ b/manifests/instance/servicefile.pp @@ -0,0 +1,13 @@ +# +# @summary helper class to configure the memcache multiinstance unit file *once* +# +# @api private +# +# @author Tim Meusel +# +class memcached::instance::servicefile { + assert_private() + systemd::unit_file { 'memcached@.service': + content => epp("${module_name}/memcached@.service.epp"), + } +} diff --git a/metadata.json b/metadata.json index aa11084..cc1266e 100644 --- a/metadata.json +++ b/metadata.json @@ -1,61 +1,63 @@ { "name": "saz-memcached", "version": "3.7.0", "author": "saz", "summary": "Manage memcached via Puppet", "license": "Apache-2.0", "source": "git://github.com/saz/puppet-memcached.git", "project_page": "https://github.com/saz/puppet-memcached", "issues_url": "https://github.com/saz/puppet-memcached/issues", "description": "Manage memcached via Puppet", "requirements": [ {"name":"puppet","version_requirement":">= 5.5.8 < 7.0.0" } ], "dependencies": [ {"name":"puppetlabs/stdlib","version_requirement":">= 4.13.1 < 7.0.0"}, - {"name":"puppetlabs/firewall","version_requirement":">= 0.1.0 < 3.0.0"} + {"name":"puppetlabs/firewall","version_requirement":">= 0.1.0 < 3.0.0"}, + {"name":"camptocamp/systemd","version_requirement":">= 2.10.0 < 3.0.0"}, + {"name":"puppet/selinux","version_requirement":">= 3.2.0 < 4.0.0"} ], "operatingsystem_support": [ { "operatingsystem": "RedHat", "operatingsystemrelease": [ "7" ] }, { "operatingsystem": "CentOS", "operatingsystemrelease": [ "7" ] }, { "operatingsystem": "OracleLinux", "operatingsystemrelease": [ "7" ] }, { "operatingsystem": "Scientific", "operatingsystemrelease": [ "7" ] }, { "operatingsystem": "Debian", "operatingsystemrelease": [ "9", "10" ] }, { "operatingsystem": "Ubuntu", "operatingsystemrelease": [ "18.04", "20.04" ] }, { "operatingsystem": "Windows" } ] } diff --git a/spec/acceptance/init_spec.rb b/spec/acceptance/init_spec.rb index 6acbb9a..bb5cf77 100644 --- a/spec/acceptance/init_spec.rb +++ b/spec/acceptance/init_spec.rb @@ -1,17 +1,22 @@ require 'spec_helper_acceptance' describe 'memcached' do context 'with all defaults' do let(:pp) do 'include memcached' end + it 'works idempotently with no errors' do apply_manifest(pp, catch_failures: true) apply_manifest(pp, catch_changes: true) end describe service('memcached') do it { is_expected.to be_enabled } it { is_expected.to be_running } end + describe port(11_211) do + it { is_expected.to be_listening.on('127.0.0.1').with('tcp') } + it { is_expected.to be_listening.on('127.0.0.1').with('udp') } + end end end diff --git a/spec/acceptance/instance_spec.rb b/spec/acceptance/instance_spec.rb new file mode 100644 index 0000000..63d3653 --- /dev/null +++ b/spec/acceptance/instance_spec.rb @@ -0,0 +1,95 @@ +require 'spec_helper_acceptance' + +describe 'memcached' do + context 'with all defaults' do + let :pp1 do + <<-PUPPET + memcached::instance{'11222':} + memcached::instance{'11223':} + PUPPET + end + + it 'works idempotently with no errors' do + apply_manifest(pp1, catch_failures: true) + apply_manifest(pp1, catch_changes: true) + end + describe service('memcached') do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + describe service('memcached@11222') do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + describe service('memcached@11223') do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + describe port(11_222) do + it { is_expected.to be_listening.on('127.0.0.1').with('tcp') } + it { is_expected.not_to be_listening.on('127.0.0.1').with('udp') } + end + describe port(11_223) do + it { is_expected.to be_listening.on('127.0.0.1').with('tcp') } + it { is_expected.not_to be_listening.on('127.0.0.1').with('udp') } + end + end + context 'with service limits' do + let :pp2 do + <<-PUPPET + memcached::instance{'11222': + limits => { + 'LimitNOFILE' => 8192, + 'LimitNPROC' => 16384, + } + } + PUPPET + end + + it 'works idempotently with no errors' do + apply_manifest(pp2, catch_failures: true) + apply_manifest(pp2, catch_changes: true) + end + describe service('memcached@11222') do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + describe port(11_222) do + it { is_expected.to be_listening.on('127.0.0.1').with('tcp') } + it { is_expected.not_to be_listening.on('127.0.0.1').with('udp') } + end + + describe file('/etc/systemd/system/memcached@11222.service.d/90-limits.conf') do + its(:content) { is_expected.to match %r{LimitNOFILE=8192\nLimitNPROC=16384} } + end + end + context 'with LISTEN override' do + let :pp3 do + <<-PUPPET + memcached::instance{'11222': + override_content => "[Service]\nEnvironment='LISTEN=-l 0.0.0.0'\n", + } + PUPPET + end + + it 'works idempotently with no errors' do + apply_manifest(pp3, catch_failures: true) + apply_manifest(pp3, catch_changes: true) + end + describe service('memcached@11222') do + it { is_expected.to be_enabled } + it { is_expected.to be_running } + end + + describe port(11_222) do + it { is_expected.to be_listening.on('0.0.0.0').with('tcp') } + it { is_expected.not_to be_listening.on('0.0.0.0').with('udp') } + end + + describe file('/etc/systemd/system/memcached@11222.service.d/memcached@11222.service-override.conf') do + its(:content) { is_expected.to match %r{Environment='LISTEN=-l 0.0.0.0'} } + end + end +end diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb index 1c3310f..87b61e7 100644 --- a/spec/classes/init_spec.rb +++ b/spec/classes/init_spec.rb @@ -1,537 +1,57 @@ require 'spec_helper' describe 'memcached' do - let :node do 'rspec.puppet.com' end on_supported_os.each do |os, facts| context "on #{os} " do let :facts do facts end context 'with all defaults' do it { is_expected.to compile.with_all_deps } it { is_expected.to contain_class('memcached::params') } it { is_expected.to contain_service('memcached').with_ensure('running').with_enable('true') } it { is_expected.to contain_package('memcached').with_ensure('present') } it { is_expected.not_to contain_firewall('100_tcp_11211_for_memcached') } it { is_expected.not_to contain_firewall('100_udp_11211_for_memcached') } context 'on RedHat', if: facts[:os]['family'] == 'RedHat' do it { is_expected.to contain_file('/etc/sysconfig/memcached').with_ensure('file') } end context 'on Debian', if: facts[:os]['family'] == 'Debian' do it { is_expected.to contain_file('/etc/memcached.conf').with_ensure('file') } end end describe 'with manage_firewall parameter' do context 'with manage_firewall set to true' do let(:params) { { manage_firewall: true } } it { is_expected.to contain_class('memcached') } it { is_expected.to contain_firewall('100_tcp_11211_for_memcached') } it { is_expected.to contain_firewall('100_udp_11211_for_memcached') } end end describe 'when setting use_tls to true and unset tls_ca_cert' do let :params do - { - 'use_tls' => true, - 'tls_cert_chain' => '/path/to/cert', - 'tls_key' => '/path/to/key', - 'tls_ca_cert' => '/path/to/cacert', - 'tls_verify_mode' => 0 - } + { + 'use_tls' => true, + 'tls_cert_chain' => '/path/to/cert', + 'tls_key' => '/path/to/key', + 'tls_ca_cert' => '/path/to/cacert', + 'tls_verify_mode' => 0 + } end context 'on RedHat', if: facts[:os]['family'] == 'RedHat' do - it { is_expected.to contain_file('/etc/sysconfig/memcached').with_content("PORT=\"11211\"\nUSER=\"memcached\"\nMAXCONN=\"8192\"\nCACHESIZE=\"462\"\nOPTIONS=\"-l 127.0.0.1 -U 11211 -t 1 -Z -o ssl_chain_cert=/path/to/cert -o ssl_key=/path/to/key -o ssl_ca_cert=/path/to/cacert -o ssl_verify_mode=0 >> /var/log/memcached.log 2>&1\"\n") } + it { is_expected.to contain_file('/etc/sysconfig/memcached').with_content("PORT=\"11211\"\nUSER=\"memcached\"\nMAXCONN=\"8192\"\nCACHESIZE=\"462\"\nOPTIONS=\"-l 127.0.0.1 -U 11211 -t 1 -Z -o ssl_chain_cert=/path/to/cert -o ssl_key=/path/to/key -o ssl_ca_cert=/path/to/cacert -o ssl_verify_mode=0 >> /var/log/memcached.log 2>&1\"\n") } end end end end - let :default_params do - { - package_ensure: 'present', - logfile: '/var/log/memcached.log', - lock_memory: false, - listen_ip: '127.0.0.1', - tcp_port: 11_211, - udp_port: 11_211, - user: 'nobody', - max_connections: 8192, - install_dev: false, - processorcount: 1, - use_sasl: false, - use_tls: false, - large_mem_pages: false, - pidfile: '/var/run/memcached.pid', - disable_cachedump: false, - tls_verify_mode: 1 - } - end - -# -# [{}, -# { -# package_ensure: 'latest', -# logfile: '/var/log/memcached.log', -# max_memory: 2, -# lock_memory: true, -# listen_ip: '127.0.0.1', -# tcp_port: 11_212, -# udp_port: 11_213, -# user: 'somebdy', -# max_connections: 8193, -# verbosity: 'vvv', -# processorcount: 3, -# use_sasl: true, -# large_mem_pages: true, -# extended_opts: %w[lru_crawler lru_maintainer] -# }, -# { -# package_ensure: 'present', -# logfile: '/var/log/memcached.log', -# max_memory: '20%', -# lock_memory: false, -# listen_ip: '127.0.0.1', -# tcp_port: 11_212, -# udp_port: 11_213, -# user: 'somebdy', -# max_connections: 8193, -# verbosity: 'vvv', -# install_dev: true, -# processorcount: 1, -# extended_opts: %w[lru_crawler lru_maintainer], -# disable_cachedump: true -# }, -# { -# pidfile: '/var/log/memcached.pid', -# disable_cachedump: true -# }, -# { -# package_ensure: 'absent', -# install_dev: true -# }, -# { -# listen_ip: ['127.0.0.1', '127.0.0.2'] -# }, -# { -# use_tls: true, -# tls_cert_chain: '/path/to/cert', -# tls_key: '/path/to/key', -# tls_ca_cert: '/path/to/cacert' -# }, -# { -# use_tls: true, -# tls_cert_chain: '/path/to/cert', -# tls_key: '/path/to/key' -# }, -# { -# service_manage: false -# }].each do |param_set| -# describe "when #{param_set == {} ? 'using default' : 'specifying'} class parameters" do -# let :param_hash do -# default_params.merge(param_set) -# end -# -# let :params do -# param_set -# end -# -# ['FreeBSD'].each do |osfamily| -# context "on osfamily #{osfamily}" do -# let(:facts) do -# { osfamily: osfamily, -# memorysize: '1000 MB', -# processorcount: '1' } -# end -# -# describe "on supported osfamily: #{osfamily}" do -# it { is_expected.to compile.with_all_deps } -# it { is_expected.to contain_class('memcached') } -# -# it { is_expected.to contain_class('memcached::params') } -# -# it { is_expected.not_to contain_firewall('100_tcp_11211_for_memcached') } -# it { is_expected.not_to contain_firewall('100_udp_11211_for_memcached') } -# -# it do -# if param_hash[:install_dev] -# is_expected.to contain_package('libmemcached').with_ensure(param_hash[:package_ensure]) -# end -# end -# -# it do -# is_expected.to contain_file('/etc/rc.conf.d/memcached').with( -# 'owner' => 'root', -# 'group' => 0 -# ) -# end -# -# it do -# if param_hash[:service_manage] == false -# is_expected.not_to contain_service('memcached') -# elsif param_hash[:package_ensure] == 'absent' -# is_expected.to contain_service('memcached').with( -# 'ensure' => 'stopped', -# 'enable' => false -# ) -# else -# is_expected.to contain_service('memcached').with( -# 'ensure' => 'running', -# 'enable' => true, -# 'hasrestart' => true, -# 'hasstatus' => false -# ) -# end -# end -# -# it 'compiles the template based on the class parameters' do -# flags = ['-d'] -# flags.push("-u #{param_hash[:user]}") -# flags.push("-P #{param_hash[:pidfile]}") if param_hash[:pidfile] -# flags.push("-t #{param_hash[:processorcount]}") -# -# if param_hash[:listen_ip] != '' -# if param_hash[:listen_ip].is_a?(String) -# flags.push("-l #{param_hash[:listen_ip]}") -# else -# flags.push("-l #{param_hash[:listen_ip].join(',')}") -# end -# end -# -# flags.push('-k') if param_hash[:lock_memory] -# flags.push("-c #{param_hash[:max_connections]}") -# flags.push("-p #{param_hash[:tcp_port]}") -# flags.push("-U #{param_hash[:udp_port]}") -# flags.push('-' + param_hash[:verbosity]) if param_hash[:verbosity] -# -# if param_hash[:max_memory] -# if param_hash[:max_memory].is_a?(String) && param_hash[:max_memory].end_with?('%') -# flags.push('-m 200') -# else -# flags.push("-m #{param_hash[:max_memory]}") -# end -# else -# flags.push('-m 950') -# end -# -# flags.push('-S') if param_hash[:use_sasl] -# -# if param_hash[:use_tls] -# flags.push('-Z') -# flags.push("-o ssl_chain_cert=#{param_hash[:tls_cert_chain]}") -# flags.push("-o ssl_key=#{param_hash[:tls_key]}") -# if param_hash[:tls_ca_cert] -# flags.push("-o ssl_ca_cert=#{param_hash[:tls_ca_cert]}") -# end -# flags.push("-o ssl_verify_mode=#{param_hash[:tls_verify_mode]}") -# end -# -# flags.push('-L') if param_hash[:large_mem_pages] -# flags.push('-X') if param_hash[:disable_cachedump] -# flags.push('-o lru_crawler,lru_maintainer') if param_hash[:extended_opts] -# -# enabled = 'YES' -# enabled = 'NO' if param_hash[:service_manage] == false -# -# is_expected.to contain_file('/etc/rc.conf.d/memcached').with_content( -# "### MANAGED BY PUPPET\n### DO NOT EDIT\n\nmemcached_enable=\"#{enabled}\"\nmemcached_flags=\"#{flags.join(' ')}\"\n" -# ) -# end -# end -# end -# end -# -# ['Debian'].each do |osfamily| -# let :facts do -# { -# osfamily: osfamily, -# memorysize: '1000 MB', -# processorcount: '1' -# } -# end -# -# describe "on supported osfamily: #{osfamily}" do -# it { is_expected.to compile.with_all_deps } -# it { is_expected.to contain_class('memcached') } -# -# it { is_expected.to contain_class('memcached::params') } -# -# it { is_expected.to contain_package('memcached').with_ensure(param_hash[:package_ensure]) } -# -# it { is_expected.not_to contain_firewall('100_tcp_11211_for_memcached') } -# it { is_expected.not_to contain_firewall('100_udp_11211_for_memcached') } -# -# it do -# if param_hash[:install_dev] -# is_expected.to contain_package('libmemcached-dev').with_ensure(param_hash[:package_ensure]) -# end -# end -# -# it do -# is_expected.to contain_file('/etc/memcached.conf').with( -# 'owner' => 'root', -# 'group' => 0 -# ) -# end -# -# it do -# if param_hash[:service_manage] == false -# is_expected.not_to contain_service('memcached') -# elsif param_hash[:package_ensure] == 'absent' -# is_expected.to contain_service('memcached').with( -# 'ensure' => 'stopped', -# 'enable' => false -# ) -# else -# is_expected.to contain_service('memcached').with( -# 'ensure' => 'running', -# 'enable' => true, -# 'hasrestart' => true, -# 'hasstatus' => false -# ) -# end -# end -# -# it 'compiles the template based on the class parameters' do -# content = param_value( -# catalogue, -# 'file', -# '/etc/memcached.conf', -# 'content' -# ) -# expected_lines = [ -# "logfile #{param_hash[:logfile]}", -# "-p #{param_hash[:tcp_port]}", -# "-U #{param_hash[:udp_port]}", -# "-u #{param_hash[:user]}", -# "-c #{param_hash[:max_connections]}", -# "-t #{param_hash[:processorcount]}" -# ] -# if param_hash[:max_memory] -# if param_hash[:max_memory].is_a?(String) && param_hash[:max_memory].end_with?('%') -# expected_lines.push('-m 200') -# else -# expected_lines.push("-m #{param_hash[:max_memory]}") -# end -# else -# expected_lines.push('-m 950') -# end -# if param_hash[:listen_ip] != '' -# if param_hash[:listen_ip].is_a?(String) -# expected_lines.push("-l #{param_hash[:listen_ip]}") -# else -# expected_lines.push("-l #{param_hash[:listen_ip].join(',')}") -# end -# end -# expected_lines.push('-k') if param_hash[:lock_memory] -# if param_hash[:pidfile] -# expected_lines.push("-P #{param_hash[:pidfile]}") -# end -# expected_lines.push('-vvv') if param_hash[:verbosity] -# expected_lines.push('-S') if param_hash[:use_sasl] -# if param_hash[:use_tls] -# expected_lines.push('-Z') -# expected_lines.push("-o ssl_chain_cert=#{param_hash[:tls_cert_chain]}") -# expected_lines.push("-o ssl_key=#{param_hash[:tls_key]}") -# if param_hash[:tls_ca_cert] -# expected_lines.push("-o ssl_ca_cert=#{param_hash[:tls_ca_cert]}") -# end -# expected_lines.push("-o ssl_verify_mode=#{param_hash[:tls_verify_mode]}") -# end -# expected_lines.push('-L') if param_hash[:large_mem_pages] -# expected_lines.push('-X') if param_hash[:disable_cachedump] == true -# if param_hash[:extended_opts] -# expected_lines.push('-o lru_crawler,lru_maintainer') -# end -# (content.split("\n") & expected_lines).should =~ expected_lines -# end -# end -# end -# ['Redhat'].each do |_osfamily| -# describe 'on supported platform' do -# it 'fails' do -# end -# end -# end -# end -# end -# -# context 'with osfamily = Solaris' do -# let :facts do -# { -# osfamily: 'Solaris', -# memorysize: '1000 MB', -# processorcount: '1' -# } -# end -# -# describe 'when using default class parameters' do -# let :param_hash do -# default_params -# end -# -# let :params do -# {} -# end -# -# it { is_expected.to contain_class('memcached::params') } -# -# it { is_expected.to contain_package('memcached').with_ensure('present') } -# -# it { is_expected.not_to contain_firewall('100_tcp_11211_for_memcached') } -# it { is_expected.not_to contain_firewall('100_udp_11211_for_memcached') } -# -# it do -# is_expected.to contain_service('memcached').with( -# 'ensure' => 'running', -# 'enable' => true, -# 'hasrestart' => true, -# 'hasstatus' => false -# ) -# end -# -# it do -# is_expected.to contain_svcprop('memcached/options').with( -# 'fmri' => 'memcached:default', -# 'property' => 'memcached/options', -# 'value' => '"-m" "950" "-l" "127.0.0.1" "-p" "11211" "-U" "11211" "-u" "nobody" "-c" "8192" "-t" "1"', -# 'notify' => 'Service[memcached]' -# ) -# end -# end -# end -# -# context 'with osfamily = FreeBSD' do -# let :facts do -# { -# osfamily: 'FreeBSD', -# memorysize: '1000 MB', -# processorcount: '2' -# } -# end -# -# describe 'when using default class parameters' do -# let :params do -# default_params -# end -# -# it do -# is_expected.not_to contain_svcprop('memcached/options').with( -# 'fmri' => 'memcached:default', -# 'property' => 'memcached/options', -# 'value' => '"-m" "950" "-l" "127.0.0.1" "-p" "11211" "-U" "11211" "-u" "nobody" "-c" "8192" "-t" "1"', -# 'notify' => 'Service[memcached]' -# ) -# end -# end -# end -# context 'with osfamily = Redhat' do -# let :facts do -# { -# osfamily: 'RedHat', -# memorysize: '1000 MB', -# processorcount: '4' -# } -# end -# -# describe 'when listen_ip is an array' do -# let :custom_params do -# { -# 'listen_ip' => ['127.0.0.1', '127.0.0.2'] -# } -# end -# -# let :param_hash do -# default_params.merge(custom_params) -# end -# -# let :params do -# custom_params -# end -# -# it do -# is_expected.to contain_file('/etc/sysconfig/memcached').with_content( -# "PORT=\"11211\"\nUSER=\"memcached\"\nMAXCONN=\"8192\"\nCACHESIZE=\"950\"\nOPTIONS=\"-l 127.0.0.1,127.0.0.2 -U 11211 -t 4 >> /var/log/memcached.log 2>&1\"\n" -# ) -# end -# end -# -# describe 'when using custom class parameters' do -# let :custom_params do -# { -# 'disable_cachedump' => true -# } -# end -# -# let :param_hash do -# default_params.merge(custom_params) -# end -# -# let :params do -# custom_params -# end -# -# it do -# is_expected.to contain_file('/etc/sysconfig/memcached').with_content( -# "PORT=\"11211\"\nUSER=\"memcached\"\nMAXCONN=\"8192\"\nCACHESIZE=\"950\"\nOPTIONS=\"-l 127.0.0.1 -U 11211 -X -t 4 >> /var/log/memcached.log 2>&1\"\n" -# ) -# end -# end -# -# describe 'when setting logstdout to true' do -# let :custom_params do -# { -# 'logstdout' => true -# } -# end -# -# let :param_hash do -# default_params.merge(custom_params) -# end -# -# let :params do -# custom_params -# end -# -# it do -# is_expected.to contain_file('/etc/sysconfig/memcached').with_content( -# "PORT=\"11211\"\nUSER=\"memcached\"\nMAXCONN=\"8192\"\nCACHESIZE=\"950\"\nOPTIONS=\"-l 127.0.0.1 -U 11211 -t 4\"\n" -# ) -# end -# end -# -# describe 'when setting use_tls to true' do -# let :custom_params do -# { -# 'use_tls' => true, -# 'tls_cert_chain' => '/path/to/cert', -# 'tls_key' => '/path/to/key', -# 'tls_ca_cert' => '/path/to/cacert', -# 'tls_verify_mode' => 0 -# } -# end -# -# let :param_hash do -# default_params.merge(custom_params) -# end -# -# let :params do -# custom_params -# end -# -# it do -# is_expected.to contain_file('/etc/sysconfig/memcached').with_content( -# "PORT=\"11211\"\nUSER=\"memcached\"\nMAXCONN=\"8192\"\nCACHESIZE=\"950\"\nOPTIONS=\"-l 127.0.0.1 -U 11211 -t 4 -Z -o ssl_chain_cert=/path/to/cert -o ssl_key=/path/to/key -o ssl_ca_cert=/path/to/cacert -o ssl_verify_mode=0 >> /var/log/memcached.log 2>&1\"\n" -# ) -# end -# end -# end # vim: expandtab shiftwidth=2 softtabstop=2 diff --git a/spec/defines/instance_spec.rb b/spec/defines/instance_spec.rb new file mode 100644 index 0000000..b840760 --- /dev/null +++ b/spec/defines/instance_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' + +describe 'memcached::instance', type: :define do + on_supported_os.each do |os, facts| + context "on #{os} " do + let :facts do + facts + end + + context 'with minimal params' do + let(:title) { '3489' } + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_class('memcached::instance::servicefile') } + it { is_expected.to contain_service('memcached@3489.service') } + it { is_expected.to contain_systemd__unit_file('memcached@.service') } + + context 'on selinux', if: facts[:os]['selinux']['enabled'] == true do + it { is_expected.to contain_selinux__port('allow-memcached@3489.service') } + end + context 'without selinux', if: facts[:os]['family'] != 'RedHat' do + it { is_expected.not_to contain_selinux__port('allow-memcached@3489.service') } + end + end + + context 'with limits' do + let(:title) { '3489' } + + let :params do + { + limits: { + LimitNOFILE: 8192 + } + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_class('memcached::instance::servicefile') } + it { is_expected.to contain_service('memcached@3489.service') } + it { is_expected.to contain_systemd__unit_file('memcached@.service') } + it { is_expected.to contain_systemd__Service_limits('memcached@3489.service') } + end + + context 'with overrides' do + let(:title) { '3489' } + + let :params do + { + override_content: "[Service]\nEnvironment='LISTEN=-l 0.0.0.0'" + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_class('memcached::instance::servicefile') } + it { is_expected.to contain_service('memcached@3489.service') } + it { is_expected.to contain_systemd__unit_file('memcached@.service') } + it { is_expected.to contain_systemd__dropin_file('memcached@3489.service-override.conf') } + end + end + end +end diff --git a/templates/memcached@.service.epp b/templates/memcached@.service.epp new file mode 100644 index 0000000..03aba93 --- /dev/null +++ b/templates/memcached@.service.epp @@ -0,0 +1,93 @@ +# THIS FILE IS MANAGED BY PUPPET +# It's not recommended to modify this file in-place, because it will be +# overwritten during upgrades. If you want to customize, the best +# way is to use the "systemctl edit" command to create an override unit. +# +# For example, to pass additional options, create an override unit +# (as is done by systemctl edit) and enter the following: +# +# [Service] +# Environment=OPTIONS="-l 127.0.0.1,::1" +# +# To use the "instanced" version of this, just start 'memcached@11211' or +# whatever port you'd like. + + +[Unit] +Description=memcached daemon +After=network.target + +[Service] +User=<%= $memcached::user %> +Environment=CACHESIZE=64 +Environment=MAXCONN=1024 +Environment="LISTEN=-l 127.0.0.1" +Environment="OPTIONS=-U 0 -v" +ExecStart=/usr/bin/memcached -p %i -m ${CACHESIZE} -c ${MAXCONN} $LISTEN $OPTIONS +Restart=always + +# Set up a new file system namespace and mounts private /tmp and /var/tmp +# directories so this service cannot access the global directories and +# other processes cannot access this service's directories. +PrivateTmp=true + +# Mounts the /usr, /boot, and /etc directories read-only for processes +# invoked by this unit. +ProtectSystem=full + +# Ensures that the service process and all its children can never gain new +# privileges +NoNewPrivileges=true + +# Sets up a new /dev namespace for the executed processes and only adds API +# pseudo devices such as /dev/null, /dev/zero or /dev/random (as well as +# the pseudo TTY subsystem) to it, but no physical devices such as /dev/sda. +PrivateDevices=true + +# Required for dropping privileges and running as a different user +CapabilityBoundingSet=CAP_SETGID CAP_SETUID CAP_SYS_RESOURCE + +# Restricts the set of socket address families accessible to the processes +# of this unit. Protects against vulnerabilities such as CVE-2016-8655 +RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX + + +# Some security features are not in the older versions of systemd used by +# e.g. RHEL7/CentOS 7. The below settings are automatically edited at package +# build time to uncomment them if the target platform supports them. + +# Attempts to create memory mappings that are writable and executable at +# the same time, or to change existing memory mappings to become executable +# are prohibited. +MemoryDenyWriteExecute=true + +# Explicit module loading will be denied. This allows to turn off module +# load and unload operations on modular kernels. It is recommended to turn +# this on for most services that do not need special file systems or extra +# kernel modules to work. +ProtectKernelModules=true + +# Kernel variables accessible through /proc/sys, /sys, /proc/sysrq-trigger, +# /proc/latency_stats, /proc/acpi, /proc/timer_stats, /proc/fs and /proc/irq +# will be made read-only to all processes of the unit. Usually, tunable +# kernel variables should only be written at boot-time, with the sysctl.d(5) +# mechanism. Almost no services need to write to these at runtime; it is hence +# recommended to turn this on for most services. +ProtectKernelTunables=true + +# The Linux Control Groups (cgroups(7)) hierarchies accessible through +# /sys/fs/cgroup will be made read-only to all processes of the unit. +# Except for container managers no services should require write access +# to the control groups hierarchies; it is hence recommended to turn this +# on for most services +ProtectControlGroups=true + +# Any attempts to enable realtime scheduling in a process of the unit are +# refused. +RestrictRealtime=true + +# Takes away the ability to create or manage any kind of namespace +RestrictNamespaces=true + +[Install] +WantedBy=multi-user.target