Skip to content

Commit 39c9f72

Browse files
authored
Merge pull request #166 from puppetlabs/SOLARCH-432-peadm-plan-replace-missing-replica
Add add_replica plan
2 parents 1365215 + 8226dff commit 39c9f72

File tree

6 files changed

+242
-22
lines changed

6 files changed

+242
-22
lines changed

.github/workflows/test-add-replica.yaml

+14-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
name: "Test add_replica plan"
2+
name: "Add Replica test"
33

44
on:
55
workflow_dispatch:
@@ -15,15 +15,19 @@ on:
1515
version:
1616
description: 'PE version to install'
1717
required: true
18-
default: '2019.8.5'
18+
default: '2019.8.7'
19+
ssh-debugging:
20+
description: 'Boolean; whether or not to pause for ssh debugging'
21+
required: true
22+
default: 'false'
1923

2024
env:
2125
HONEYCOMB_WRITEKEY: 7f3c63a70eecc61d635917de46bea4e6
2226
HONEYCOMB_DATASET: litmus tests
2327

2428
jobs:
25-
smoke-test:
26-
name: "${{ matrix.architecture }}, ${{ matrix.image }}, PE ${{ matrix.version }}"
29+
test-add-replica:
30+
name: "PE ${{ matrix.version }} ${{ matrix.architecture }} on ${{ matrix.image }}"
2731
runs-on: ubuntu-20.04
2832
env:
2933
BOLT_GEM: true
@@ -41,6 +45,7 @@ jobs:
4145

4246
steps:
4347
- name: 'Start SSH session'
48+
if: ${{ github.event.inputs.ssh-debugging == 'true' }}
4449
uses: luchihoratiu/debug-via-ssh@main
4550
with:
4651
NGROK_AUTH_TOKEN: ${{ secrets.NGROK_AUTH_TOKEN }}
@@ -83,7 +88,7 @@ jobs:
8388
echo STEP_START=$(date +%s) >> $GITHUB_ENV
8489
echo ::endgroup::
8590
86-
- name: 'Provision test cluster'
91+
- name: 'Provision test cluster (specified architecture with added DR)'
8792
timeout-minutes: 15
8893
run: |
8994
echo ::group::prepare
@@ -140,15 +145,15 @@ jobs:
140145
echo ::endgroup::
141146
142147
- name: 'Run add_replica plan'
143-
timeout-minutes: 10
148+
timeout-minutes: 30
144149
run: |
145150
buildevents cmd $TRACE_ID $STEP_ID 'bolt plan run peadm_spec::add_replica' -- \
146-
bundle exec bolt plan run peadm_spec::add_replica \
151+
bundle exec bolt plan run peadm_spec::add_replica -v \
147152
--inventoryfile spec/fixtures/litmus_inventory.yaml \
148153
--modulepath spec/fixtures/modules
149-
154+
150155
- name: 'Wait as long as the file ${HOME}/pause file is present'
151-
if: ${{ always() }}
156+
if: ${{ always() && github.event.inputs.ssh-debugging == 'true' }}
152157
run: |
153158
while [ -f "${HOME}/pause" ] ; do
154159
echo "${HOME}/pause present, sleeping for 60 seconds..."

functions/get_targets.pp

+12-8
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@ function peadm::get_targets(
66
Variant[TargetSpec, Undef] $spec,
77
Optional[Integer[1,1]] $count = undef,
88
) {
9-
# If $spec is undef, return an empty array. Otherwise, if $count is 1, return
10-
# the result of get_target() in an array. If $count is undef, return
11-
# get_targets().
12-
$targets = $spec ? {
13-
undef => [ ],
14-
default => $count ? {
15-
1 => [get_target($spec)],
16-
undef => get_targets($spec),
9+
# If $spec is undef or empty array, return an empty array. Otherwise, if
10+
# $count is 1, return the result of get_target() in an array. If $count is
11+
# undef, return get_targets().
12+
case $spec {
13+
Undef, [ ]: {
14+
[ ] # Return empty array
15+
}
16+
default: {
17+
$count ? {
18+
1 => [get_target($spec)],
19+
undef => get_targets($spec),
20+
}
1721
}
1822
}
1923
}

plans/add_replica.pp

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# @summary Replace a replica host for a Standard or Large architecture.
2+
# Supported use cases:
3+
# 1: The existing replica is broken, we have a fresh new VM we want to provision the replica to.
4+
# The new replica should have the same certname as the broken one.
5+
# @param primary_host - The hostname and certname of the primary Puppet server
6+
# @param replica_host - The hostname and certname of the replica VM
7+
# @param replica_postgresql_host - The hostname and certname of the host with the replica PE-PosgreSQL database.
8+
# Can be a separate host in an XL architecture, or undef in Standard or Large.
9+
plan peadm::add_replica(
10+
# Standard or Large
11+
Peadm::SingleTargetSpec $primary_host,
12+
Peadm::SingleTargetSpec $replica_host,
13+
14+
# Extra Large
15+
Optional[Peadm::SingleTargetSpec] $replica_postgresql_host = undef,
16+
17+
# Common Configuration
18+
Optional[String] $token_file = undef,
19+
) {
20+
21+
$primary_target = peadm::get_targets($primary_host, 1)
22+
$replica_target = peadm::get_targets($replica_host, 1)
23+
$replica_postgresql_target = peadm::get_targets($replica_postgresql_host, 1)
24+
25+
$certdata = run_task('peadm::cert_data', $primary_target).first.value
26+
$primary_avail_group_letter = $certdata['extensions'][peadm::oid('peadm_availability_group')]
27+
$replica_avail_group_letter = $primary_avail_group_letter ? { 'A' => 'B', 'B' => 'A' }
28+
29+
# replica certname + any non-certname alt-names from the primary. Make sure
30+
# to Handle the case where there are no alt-names in the primary's certdata.
31+
$dns_alt_names = [$replica_target.peadm::certname()] + (pick($certdata['dns-alt-names'], []) - $certdata['certname'])
32+
33+
# This has the effect of revoking the node's certificate, if it exists
34+
run_command("puppet infrastructure forget ${replica_target.peadm::certname()}", $primary_target, _catch_errors => true)
35+
36+
run_task('peadm::agent_install', $replica_target,
37+
server => $primary_target.peadm::certname(),
38+
install_flags => [
39+
'--puppet-service-ensure', 'stopped',
40+
"extension_requests:${peadm::oid('peadm_role')}=puppet/server",
41+
"extension_requests:${peadm::oid('peadm_availability_group')}=${replica_avail_group_letter}",
42+
"main:certname=${replica_target.peadm::certname()}",
43+
"main:dns_alt_names=${dns_alt_names.join(',')}",
44+
],
45+
)
46+
47+
# clean the cert to make the plan idempotent
48+
run_task('peadm::ssl_clean', $replica_target,
49+
certname => $replica_target.peadm::certname(),
50+
)
51+
52+
# Manually submit a CSR
53+
run_task('peadm::submit_csr', $replica_target)
54+
55+
# On primary, if necessary, sign the certificate request
56+
run_task('peadm::sign_csr', $primary_target,
57+
certnames => [$replica_target.peadm::certname()],
58+
)
59+
60+
# On <replica_target>, run the puppet agent
61+
run_task('peadm::puppet_runonce', $replica_target)
62+
63+
# On the PE-PostgreSQL server in the <replacement-avail-group-letter> group
64+
65+
# Stop puppet and add the following two lines to
66+
# /opt/puppetlabs/server/data/postgresql/11/data/pg_ident.conf
67+
# pe-puppetdb-pe-puppetdb-map <replacement-replica-fqdn> pe-puppetdb
68+
# pe-puppetdb-pe-puppetdb-migrator-map <replacement-replica-fqdn> pe-puppetdb-migrator
69+
apply($replica_postgresql_target) {
70+
service { 'puppet':
71+
ensure => stopped,
72+
before => File_line['puppetdb-map', 'migrator-map'],
73+
}
74+
75+
file_line { 'puppetdb-map':
76+
path => '/opt/puppetlabs/server/data/postgresql/11/data/pg_ident.conf',
77+
line => "pe-puppetdb-pe-puppetdb-map ${replica_target.peadm::certname()} pe-puppetdb",
78+
}
79+
80+
file_line { 'migrator-map':
81+
path => '/opt/puppetlabs/server/data/postgresql/11/data/pg_ident.conf',
82+
line => "pe-puppetdb-pe-puppetdb-migrator-map ${replica_target.peadm::certname()} pe-puppetdb-migrator",
83+
}
84+
85+
service { 'pe-postgresql':
86+
ensure => running,
87+
subscribe => File_line['puppetdb-map', 'migrator-map'],
88+
}
89+
}
90+
91+
# Provision the new system as a replica
92+
run_task('peadm::provision_replica', $primary_target,
93+
replica => $replica_target.peadm::certname(),
94+
token_file => $token_file,
95+
96+
# Race condition, where the provision command checks PuppetDB status and
97+
# probably gets "starting", but fails out because that's not "running".
98+
# Can remove flag when that issue is fixed.
99+
legacy => true,
100+
)
101+
102+
# start puppet service on postgresql host
103+
run_command('systemctl start puppet.service', $replica_postgresql_target)
104+
105+
return("Added replica ${replica_target}")
106+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
plan peadm_spec::add_replica(
2+
){
3+
4+
$t = get_targets('*')
5+
wait_until_available($t)
6+
7+
parallelize($t) |$target| {
8+
$fqdn = run_command('hostname -f', $target)
9+
$target.set_var('certname', $fqdn.first['stdout'].chomp)
10+
}
11+
12+
$primary_host = $t.filter |$n| { $n.vars['role'] == 'primary' }
13+
$replica_host = $t.filter |$n| { $n.vars['role'] == 'replica' }
14+
$replica_postgresql_host = $t.filter |$n| { $n.vars['role'] == 'replica-pdb-postgresql' }
15+
16+
if $replica_host == [] {
17+
fail_plan('"replica" role missing from inventory, cannot continue')
18+
}
19+
20+
run_plan('peadm::add_replica',
21+
primary_host => $primary_host,
22+
replica_host => $replica_host,
23+
replica_postgresql_host => $replica_postgresql_host ? { [] => undef, default => $replica_postgresql_host },
24+
)
25+
26+
}

spec/functions/get_targets_spec.rb

+29-5
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,36 @@
55
# and functions we cannot do this right now.
66
# https://github.com/puppetlabs/bolt/issues/1688
77
describe 'peadm::get_targets' do
8-
let(:spec) do
9-
'some_value_goes_here'
8+
let(:pre_condition) do
9+
'type TargetSpec = Variant[String[1], Target, Array[TargetSpec]]'
1010
end
11-
let(:count) do
12-
'some_value_goes_here'
11+
12+
context 'undefined or empty arguments' do
13+
it { is_expected.to run.with_params([], 1).and_return([]) }
14+
it { is_expected.to run.with_params([]).and_return([]) }
15+
it { is_expected.to run.with_params(:undef, 1).and_return([]) }
16+
it { is_expected.to run.with_params(:undef).and_return([]) }
17+
end
18+
19+
context 'string arguments' do
20+
it 'converts a string input to a Target array without count' do
21+
skip 'Being able to stub the get_targets() function'
22+
is_expected.to run.with_params('fqdn').and_return(['fqdn'])
23+
end
24+
it 'converts a string input to a Target array with count' do
25+
skip 'Being able to stub the get_targets() function'
26+
is_expected.to run.with_params('fqdn', 1).and_return(['fqdn'])
27+
end
1328
end
1429

15-
xit { is_expected.to run.with_params(spec, count).and_return('some_value') }
30+
context 'array arguments' do
31+
it 'converts an array input to a Target array without count' do
32+
skip 'Being able to stub the get_targets() function'
33+
is_expected.to run.with_params(['fqdn']).and_return(['fqdn'])
34+
end
35+
it 'converts an array input to a Target array with count' do
36+
skip 'Being able to stub the get_targets() function'
37+
is_expected.to run.with_params(['fqdn'], 1).and_return(['fqdn'])
38+
end
39+
end
1640
end

spec/plans/add_replica_spec.rb

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
require 'spec_helper'
2+
3+
describe 'peadm::install' do
4+
include BoltSpec::Plans
5+
6+
def allow_standard_non_returning_calls(params)
7+
allow_apply
8+
allow_task('peadm::agent_install')
9+
allow_task('peadm::ssl_clean')
10+
allow_task('peadm::submit_csr')
11+
allow_task('peadm::sign_csr')
12+
allow_task('peadm::puppet_runonce')
13+
allow_task('peadm::provision_replica')
14+
allow_command('systemctl start puppet.service')
15+
allow_command("puppet infrastructure forget #{params['replica_host']}")
16+
allow_command("puppet node purge #{params['replica_host']}")
17+
end
18+
19+
describe 'basic functionality' do
20+
let(:params) { { 'primary_host' => 'primary', 'replica_host' => 'replica' } }
21+
let(:certdata) { { 'certname' => 'primary', 'extensions' => { '1.3.6.1.4.1.34380.1.1.9813' => 'A' } } }
22+
23+
it 'runs successfully when the primary doesn\'t have alt-names' do
24+
allow_standard_non_returning_calls(params)
25+
expect_task('peadm::cert_data').always_return(certdata)
26+
expect_task('peadm::agent_install')
27+
.with_params({ 'server' => 'primary',
28+
'install_flags' => [
29+
'--puppet-service-ensure', 'stopped',
30+
'extension_requests:1.3.6.1.4.1.34380.1.1.9812=puppet/server',
31+
'extension_requests:1.3.6.1.4.1.34380.1.1.9813=B',
32+
'main:certname=replica',
33+
'main:dns_alt_names=replica'
34+
] })
35+
36+
expect(run_plan('peadm::add_replica', params)).to be_ok
37+
end
38+
39+
it 'runs successfully when the primary has alt-names' do
40+
allow_standard_non_returning_calls(params)
41+
expect_task('peadm::cert_data').always_return(certdata.merge({ 'dns-alt-names' => ['primary', 'alt'] }))
42+
expect_task('peadm::agent_install')
43+
.with_params({ 'server' => 'primary',
44+
'install_flags' => [
45+
'--puppet-service-ensure', 'stopped',
46+
'extension_requests:1.3.6.1.4.1.34380.1.1.9812=puppet/server',
47+
'extension_requests:1.3.6.1.4.1.34380.1.1.9813=B',
48+
'main:certname=replica',
49+
'main:dns_alt_names=replica,alt'
50+
] })
51+
52+
expect(run_plan('peadm::add_replica', params)).to be_ok
53+
end
54+
end
55+
end

0 commit comments

Comments
 (0)