Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finishing touches for multi-instance support #343

Merged
merged 1 commit into from
Feb 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .fixtures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ fixtures:
yumrepo_core:
repo: "https://github.com/puppetlabs/puppetlabs-yumrepo_core"
puppet_version: ">= 6.0.0"
systemd:
repo: 'https://github.com/camptocamp/puppet-systemd.git'
puppet_version: "< 6.1.0"
fraenki marked this conversation as resolved.
Show resolved Hide resolved
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,33 @@ class { '::redis':
}
```

### Multiple instances


```puppet
fraenki marked this conversation as resolved.
Show resolved Hide resolved
$listening_ports = [6379,6380,6381,6382]

class { '::redis':
fraenki marked this conversation as resolved.
Show resolved Hide resolved
default_install => false,
service_enable => false,
service_ensure => 'stopped',
}

$listening_ports.each |$port| {
$port_string = sprintf('%d',$port)
redis::instance { $port_string:
service_enable => true,
service_ensure => 'running',
port => $port,
bind => $facts['networking']['ip'],
dbfilename => "${port}-dump.rdb",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes me wonder if we should have some code in instance.pp that does this and appendfilename so they're unique. Defaulting to the same file for multiple instances sounds like a bad idea. We can pass the default in config.pp as we do for other *file parameters.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I agree, I think having $port as the one and only unique identifier for Redis instances is sufficient. Adding $bind to the mix by introducing it as unique identifier could make things pretty complicated. I think this would only add choice (which is usually a good thing), but without real benefits.

appendfilename => "${port}-appendonly.aof",
appendfsync => 'always',
require => Class['Redis'],
}
}
```

### Manage repositories

Disabled by default but if you really want the module to manage the required
Expand Down Expand Up @@ -104,6 +131,10 @@ class { '::redis::sentinel':
}
```

### Soft dependency

This module requires `camptocamp/systemd` on Puppet versions older than 6.1.0.

## `redis::get()` function

This function is used to get data from redis.
Expand Down
6 changes: 0 additions & 6 deletions manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -353,10 +353,4 @@
~> Class['redis::service']
}

exec { 'systemd-reload-redis':
command => 'systemctl daemon-reload',
refreshonly => true,
path => '/bin:/usr/bin:/usr/local/bin',
}

}
9 changes: 7 additions & 2 deletions manifests/instance.pp
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,6 @@
Variant[Stdlib::Absolutepath, Enum['']] $unixsocket = "/var/run/redis/redis-server-${name}.sock",
Stdlib::Absolutepath $workdir = "${redis::workdir}/redis-server-${name}",
) {

if $title == 'default' {
$redis_file_name_orig = $config_file_orig
$redis_file_name = $config_file
Expand Down Expand Up @@ -330,7 +329,13 @@
mode => '0644',
content => template('redis/service_templates/redis.service.erb'),
}
~> Exec['systemd-reload-redis']

# Only necessary for Puppet < 6.1.0,
# See https://github.com/puppetlabs/puppet/commit/f8d5c60ddb130c6429ff12736bfdb4ae669a9fd4
if versioncmp($facts['puppetversion'],'6.1.0') < 0 {
include systemd::systemctl::daemon_reload
File["/etc/systemd/system/${service_name}.service"] ~> Class['systemd::systemctl::daemon_reload']
}

if $title != 'default' {
service { $service_name:
Expand Down
9 changes: 6 additions & 3 deletions manifests/ulimit.pp
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,12 @@
"defnode nofile Service/LimitNOFILE \"\"",
"set \$nofile/value \"${redis::ulimit}\"",
],
notify => [
Exec['systemd-reload-redis'],
],
}
# Only necessary for Puppet < 6.1.0,
# See https://github.com/puppetlabs/puppet/commit/f8d5c60ddb130c6429ff12736bfdb4ae669a9fd4
if versioncmp($facts['puppetversion'],'6.1.0') < 0 {
include systemd::systemctl::daemon_reload
Augeas['Systemd redis ulimit'] ~> Class['systemd::systemctl::daemon_reload']
}
} else {
case $facts['os']['family'] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
require 'spec_helper_acceptance'

describe 'redis::instance' do
describe 'redis::instance example' do
instances = [6379, 6380, 6381, 6382]
case fact('osfamily')
when 'Debian'
config_path = '/etc/redis'
redis_name = 'redis-server'
else
redis_name = 'redis'
config_path = '/etc'
redis_name = 'redis'
end

it 'runs successfully' do
pp = <<-EOS
class { 'redis':
$listening_ports = #{instances}

class { '::redis':
default_install => false,
service_enable => false,
fraenki marked this conversation as resolved.
Show resolved Hide resolved
service_ensure => 'stopped',
}

redis::instance {'redis1':
port => 7777,
$listening_ports.each |$port| {
$port_string = sprintf('%d',$port)
redis::instance { $port_string:
service_enable => true,
service_ensure => 'running',
port => $port,
bind => $facts['networking']['ip'],
dbfilename => "${port}-dump.rdb",
appendfilename => "${port}-appendonly.aof",
appendfsync => 'always',
require => Class['Redis'],
}
}

redis::instance {'redis2':
port => 8888,
}
EOS

# Apply twice to ensure no errors the second time.
Expand All @@ -34,29 +46,32 @@ class { 'redis':
it { is_expected.to be_installed }
end

describe service('redis-server-redis1') do
it { is_expected.to be_running }
describe service(redis_name) do
fraenki marked this conversation as resolved.
Show resolved Hide resolved
it { is_expected.not_to be_enabled }
it { is_expected.not_to be_running }
end

describe service('redis-server-redis2') do
it { is_expected.to be_running }
end
instances.each do |instance|
describe file("/etc/systemd/system/redis-server-#{instance}.service"), if: (fact('service_provider') == 'systemd') do
its(:content) { is_expected.to match %r{redis-server-#{instance}.conf} }
end

describe file("#{config_path}/redis-server-redis1.conf") do
its(:content) { is_expected.to match %r{port 7777} }
end
describe service("redis-server-#{instance}") do
it { is_expected.to be_enabled }
end

describe file("#{config_path}/redis-server-redis2.conf") do
its(:content) { is_expected.to match %r{port 8888} }
end
describe service("redis-server-#{instance}") do
it { is_expected.to be_running }
fraenki marked this conversation as resolved.
Show resolved Hide resolved
end

context 'redis should respond to ping command' do
describe command('redis-cli -h 127.0.0.1 -p 7777 ping') do
its(:stdout) { is_expected.to match %r{PONG} }
describe file("#{config_path}/redis-server-#{instance}.conf") do
its(:content) { is_expected.to match %r{port #{instance}} }
end

describe command('redis-cli -h 127.0.0.1 -p 8888 ping') do
its(:stdout) { is_expected.to match %r{PONG} }
context "redis instance #{instance} should respond to ping command" do
describe command("redis-cli -h #{fact('networking.ip')} -p #{instance} ping") do
ekohl marked this conversation as resolved.
Show resolved Hide resolved
its(:stdout) { is_expected.to match %r{PONG} }
end
end
end
end
58 changes: 53 additions & 5 deletions spec/classes/redis_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,21 @@
let(:service_file) { redis_service_file(service_provider: facts['service_provider']) }
let(:package_name) { manifest_vars[:package_name] }
let(:service_name) { manifest_vars[:service_name] }
let(:config_file) { manifest_vars[:config_file] }
let(:config_file_orig) { manifest_vars[:config_file_orig] }

on_supported_os.each do |os, facts|
context "on #{os}" do
let(:facts) { facts }

if facts[:operatingsystem] == 'Ubuntu' && facts[:operatingsystemmajrelease] == '16.04'
let(:systemd) { '' }
let(:servicetype) { 'forking' }
else
let(:systemd) { ' --supervised systemd' }
let(:servicetype) { 'notify' }
end

describe 'without parameters' do
it { is_expected.to compile.with_all_deps }
it { is_expected.to create_class('redis') }
Expand Down Expand Up @@ -108,11 +117,20 @@ class { 'redis::globals':
with_owner('root').
with_group('root').
with_mode('0444')
is_expected.to contain_augeas('Systemd redis ulimit').
with_incl("/etc/systemd/system/#{service_name}.service.d/limit.conf").
with_lens('Systemd.lns').
with_changes(['defnode nofile Service/LimitNOFILE ""', 'set $nofile/value "7777"']).
that_notifies('Exec[systemd-reload-redis]')
# Only necessary for Puppet < 6.1.0,
# See https://github.com/puppetlabs/puppet/commit/f8d5c60ddb130c6429ff12736bfdb4ae669a9fd4
if Puppet.version < '6.1'
is_expected.to contain_augeas('Systemd redis ulimit').
with_incl("/etc/systemd/system/#{service_name}.service.d/limit.conf").
with_lens('Systemd.lns').
with_changes(['defnode nofile Service/LimitNOFILE ""', 'set $nofile/value "7777"']).
that_notifies('Class[systemd::systemctl::daemon_reload]')
else
is_expected.to contain_augeas('Systemd redis ulimit').
with_incl("/etc/systemd/system/#{service_name}.service.d/limit.conf").
with_lens('Systemd.lns').
with_changes(['defnode nofile Service/LimitNOFILE ""', 'set $nofile/value "7777"'])
end
Comment on lines +120 to +133
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternative with less duplication

            is_expected.to contain_augeas('Systemd redis ulimit').
              with_incl("/etc/systemd/system/#{service_name}.service.d/limit.conf").
              with_lens('Systemd.lns').
              with_changes(['defnode nofile Service/LimitNOFILE ""', 'set $nofile/value "7777"'])
            # Only necessary for Puppet < 6.1.0,
            # See https://github.com/puppetlabs/puppet/commit/f8d5c60ddb130c6429ff12736bfdb4ae669a9fd4
            if Puppet.version < '6.1'
              is_expected.to contain_augeas('Systemd redis ulimit').
                 that_notifies('Class[systemd::systemctl::daemon_reload]')

You can probably even add an else with not_to

else
is_expected.not_to contain_file('/etc/systemd/system/redis-server.service.d/limit.conf')
is_expected.not_to contain_augeas('Systemd redis ulimit')
Expand Down Expand Up @@ -1329,6 +1347,36 @@ class { 'redis::globals':
end

it { is_expected.to contain_file(service_file) }

it do
content = <<-END.gsub(%r{^\s+\|}, '')
fraenki marked this conversation as resolved.
Show resolved Hide resolved
|[Unit]
|Description=Redis Advanced key-value store for instance default
|After=network.target
|After=network-online.target
|Wants=network-online.target
|
|[Service]
|RuntimeDirectory=redis
|RuntimeDirectoryMode=2755
|Type=#{servicetype}
|ExecStart=/usr/bin/redis-server #{config_file}#{systemd}
|ExecStop=/usr/bin/redis-server -p 6379 shutdown
|Restart=always
|User=redis
|Group=redis
|LimitNOFILE=65536
|
|[Install]
|WantedBy=multi-user.target
END

if facts['service_provider'] == 'systemd'
ekohl marked this conversation as resolved.
Show resolved Hide resolved
is_expected.to contain_file(service_file).with_content(content)
else
is_expected.to contain_file(service_file).with_content(%r{Required-Start:})
end
end
end

describe 'with parameter manage_service_file' do
Expand Down
1 change: 1 addition & 0 deletions spec/spec_helper_acceptance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
run_puppet_install_helper unless ENV['BEAKER_provision'] == 'no'
install_module_on(hosts)
install_module_dependencies_on(hosts)
install_module_from_forge('camptocamp/systemd', '>= 2.0.0 < 3.0.0')

RSpec.configure do |c|
# Readable test descriptions
Expand Down
12 changes: 10 additions & 2 deletions templates/service_templates/redis.service.erb
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
[Unit]
Description=Advanced key-value store for <%= @title %>
Description=Redis Advanced key-value store for instance <%= @title %>
fraenki marked this conversation as resolved.
Show resolved Hide resolved
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
RuntimeDirectory=redis
RuntimeDirectoryMode=2755
<%# Redis on Xenial is too old for systemd integration -%>
<% if @facts['os']['name'] == 'Ubuntu' and @facts['os']['release']['major'] == '16.04' -%>
Type=forking
ExecStart=/usr/bin/redis-server <%= @redis_file_name %>
<% else -%>
Type=notify
ExecStart=/usr/bin/redis-server <%= @redis_file_name %> --supervised systemd
<% end -%>
ExecStop=/usr/bin/redis-server -p <%= @port %> shutdown
TimeoutStopSec=0
Restart=always
User=<%= @service_user %>
Group=<%= @service_user %>
LimitNOFILE=<%= @ulimit %>

[Install]
WantedBy=multi-user.target