diff --git a/REFERENCE.md b/REFERENCE.md index 2951060f..b8735c23 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -16,6 +16,7 @@ ### Functions +* [`peadm::amend_recovery_defaults_by_pe_version`](#peadm--amend_recovery_defaults_by_pe_version) * [`peadm::assert_supported_architecture`](#peadm--assert_supported_architecture): Assert that the architecture given is a supported one * [`peadm::assert_supported_bolt_version`](#peadm--assert_supported_bolt_version): Assert that the Bolt executable running PEAdm is a supported version * [`peadm::assert_supported_pe_version`](#peadm--assert_supported_pe_version): Assert that the PE version given is supported by PEAdm @@ -39,6 +40,7 @@ * [`peadm::recovery_opts_all`](#peadm--recovery_opts_all) * [`peadm::recovery_opts_default`](#peadm--recovery_opts_default) * [`peadm::update_pe_conf`](#peadm--update_pe_conf): Update the pe.conf file on a target with the provided hash +* [`peadm::validated_pe_version_for_backup_restore`](#peadm--validated_pe_version_for_backup_restore): Verify that *pe_version* string is a valid SemVer. If not, warn, and return "0.0.0" as a permissive default. * [`peadm::wait_until_service_ready`](#peadm--wait_until_service_ready): A convenience function to help remember port numbers for services and handle running the wait_until_service_ready task ### Data types @@ -139,6 +141,36 @@ Supported use cases: ## Functions +### `peadm::amend_recovery_defaults_by_pe_version` + +Type: Puppet Language + +The peadm::amend_recovery_defaults_by_pe_version function. + +#### `peadm::amend_recovery_defaults_by_pe_version(Hash $base_opts, Peadm::Pe_version $pe_version, Boolean $opt_value)` + +The peadm::amend_recovery_defaults_by_pe_version function. + +Returns: `Any` + +##### `base_opts` + +Data type: `Hash` + + + +##### `pe_version` + +Data type: `Peadm::Pe_version` + + + +##### `opt_value` + +Data type: `Boolean` + + + ### `peadm::assert_supported_architecture` Type: Puppet Language @@ -787,12 +819,18 @@ Type: Puppet Language The peadm::migration_opts_default function. -#### `peadm::migration_opts_default()` +#### `peadm::migration_opts_default(Peadm::Pe_version $pe_version)` The peadm::migration_opts_default function. Returns: `Any` +##### `pe_version` + +Data type: `Peadm::Pe_version` + + + ### `peadm::node_manager_yaml_location` Type: Ruby 4.x API @@ -853,24 +891,36 @@ Type: Puppet Language The peadm::recovery_opts_all function. -#### `peadm::recovery_opts_all()` +#### `peadm::recovery_opts_all(Peadm::Pe_version $pe_version)` The peadm::recovery_opts_all function. Returns: `Any` +##### `pe_version` + +Data type: `Peadm::Pe_version` + + + ### `peadm::recovery_opts_default` Type: Puppet Language The peadm::recovery_opts_default function. -#### `peadm::recovery_opts_default()` +#### `peadm::recovery_opts_default(Peadm::Pe_version $pe_version)` The peadm::recovery_opts_default function. Returns: `Any` +##### `pe_version` + +Data type: `Peadm::Pe_version` + + + ### `peadm::update_pe_conf` Type: Puppet Language @@ -895,6 +945,26 @@ Data type: `Hash` The hash to update the pe.conf file with +### `peadm::validated_pe_version_for_backup_restore` + +Type: Puppet Language + +Verify that *pe_version* string is a valid SemVer. +If not, warn, and return "0.0.0" as a permissive default. + +#### `peadm::validated_pe_version_for_backup_restore(Optional[String] $pe_version)` + +Verify that *pe_version* string is a valid SemVer. +If not, warn, and return "0.0.0" as a permissive default. + +Returns: `Any` + +##### `pe_version` + +Data type: `Optional[String]` + + + ### `peadm::wait_until_service_ready` Type: Puppet Language @@ -1014,6 +1084,8 @@ Struct[{ 'orchestrator' => Optional[Boolean], 'puppetdb' => Optional[Boolean], 'rbac' => Optional[Boolean], + 'hac' => Optional[Boolean], + 'patching' => Optional[Boolean], }] ``` diff --git a/functions/amend_recovery_defaults_by_pe_version.pp b/functions/amend_recovery_defaults_by_pe_version.pp new file mode 100644 index 00000000..d6de2cb1 --- /dev/null +++ b/functions/amend_recovery_defaults_by_pe_version.pp @@ -0,0 +1,25 @@ +function peadm::amend_recovery_defaults_by_pe_version ( + Hash $base_opts, + Peadm::Pe_version $pe_version, + Boolean $opt_value, +) { + # work around puppet-lint check_unquoted_string_in_case + $pe_2025_0 = SemVerRange('>= 2025.0') + $pe_2023_7 = SemVerRange('>= 2023.7') + case $pe_version { + $pe_2025_0: { + $base_opts + { + 'hac' => $opt_value, + 'patching' => $opt_value, + } + } + $pe_2023_7: { + $base_opts + { + 'hac' => $opt_value, + } + } + default: { + $base_opts + } + } +} diff --git a/functions/migration_opts_default.pp b/functions/migration_opts_default.pp index 99b57e55..2b770a40 100644 --- a/functions/migration_opts_default.pp +++ b/functions/migration_opts_default.pp @@ -1,5 +1,7 @@ -function peadm::migration_opts_default () { - { +function peadm::migration_opts_default ( + Peadm::Pe_version $pe_version, +) { + $base_opts = { 'activity' => true, 'ca' => true, 'classifier' => true, @@ -9,4 +11,5 @@ function peadm::migration_opts_default () { 'puppetdb' => true, 'rbac' => true, } + peadm::amend_recovery_defaults_by_pe_version($base_opts, $pe_version, true) } diff --git a/functions/recovery_opts_all.pp b/functions/recovery_opts_all.pp index e738ab5e..6f6f59c0 100644 --- a/functions/recovery_opts_all.pp +++ b/functions/recovery_opts_all.pp @@ -1,5 +1,7 @@ -function peadm::recovery_opts_all () { - { +function peadm::recovery_opts_all ( + Peadm::Pe_version $pe_version, +) { + $base_opts = { 'activity' => true, 'ca' => true, 'classifier' => true, @@ -9,4 +11,5 @@ function peadm::recovery_opts_all () { 'puppetdb' => true, 'rbac' => true, } + peadm::amend_recovery_defaults_by_pe_version($base_opts, $pe_version, true) } diff --git a/functions/recovery_opts_default.pp b/functions/recovery_opts_default.pp index 5e4b2f4d..5c9bf57c 100644 --- a/functions/recovery_opts_default.pp +++ b/functions/recovery_opts_default.pp @@ -1,5 +1,7 @@ -function peadm::recovery_opts_default () { - { +function peadm::recovery_opts_default ( + Peadm::Pe_version $pe_version, +) { + $base_opts = { 'activity' => false, 'ca' => true, 'classifier' => false, @@ -9,4 +11,5 @@ function peadm::recovery_opts_default () { 'puppetdb' => true, 'rbac' => false, } + peadm::amend_recovery_defaults_by_pe_version($base_opts, $pe_version, false) } diff --git a/functions/validated_pe_version_for_backup_restore.pp b/functions/validated_pe_version_for_backup_restore.pp new file mode 100644 index 00000000..73efc781 --- /dev/null +++ b/functions/validated_pe_version_for_backup_restore.pp @@ -0,0 +1,23 @@ +# Verify that *pe_version* string is a valid SemVer. +# If not, warn, and return "0.0.0" as a permissive default. +function peadm::validated_pe_version_for_backup_restore( + Optional[String] $pe_version, +) { + # work around puppet-lint check_unquoted_string_in_case + $semverrange = SemVerRange('>=0.0.0') + case $pe_version { + # Validate that the value is a SemVer value. + $semverrange: { + $pe_version + } + default: { + $msg = @("WARN") + WARNING: Retrieved a missing or unparseable PE version of '${pe_version}'. + Newer service databases released in 2023.7+ will be skipped from defaults. + (host-action-collector, patching) + |-WARN + out::message($msg) + '0.0.0' + } + } +} diff --git a/plans/backup.pp b/plans/backup.pp index 80835eb0..4e3e15a6 100644 --- a/plans/backup.pp +++ b/plans/backup.pp @@ -35,10 +35,12 @@ getvar('cluster.params.compiler_hosts'), ) + $pe_version = peadm::validated_pe_version_for_backup_restore(getvar('cluster.pe_version')) + $recovery_opts = $backup_type? { - 'recovery' => peadm::recovery_opts_default(), - 'migration' => peadm::migration_opts_default(), - 'custom' => peadm::recovery_opts_all() + $backup, + 'recovery' => peadm::recovery_opts_default($pe_version), + 'migration' => peadm::migration_opts_default($pe_version), + 'custom' => peadm::recovery_opts_all($pe_version) + $backup, } $timestamp = Timestamp.new().strftime('%Y-%m-%dT%H%M%SZ') @@ -55,6 +57,10 @@ 'activity' => $primary_target, 'rbac' => $primary_target, 'puppetdb' => $puppetdb_postgresql_target, + # (host-action-collector db will be filtered for pe version by recovery_opts) + 'hac' => $primary_target, + # (patching db will be filtered for pe version by recovery_opts) + 'patching' => $primary_target, }.filter |$key,$_| { $recovery_opts[$key] == true } diff --git a/plans/restore.pp b/plans/restore.pp index 453bf8e5..8e8975f6 100644 --- a/plans/restore.pp +++ b/plans/restore.pp @@ -34,7 +34,7 @@ # try to load the cluster configuration by running peadm::get_peadm_config, but allow for errors to happen $_cluster = run_task('peadm::get_peadm_config', $targets, { '_catch_errors' => true }).first.value - if $_cluster == undef or getvar('_cluster.params') == undef { + if $_cluster == undef or getvar('_cluster.params') == undef or getvar('_cluster.pe_version') == undef { # failed to get cluster config, load from backup out::message('Failed to get cluster configuration, loading from backup...') $result = download_file("${recovery_directory}/peadm/peadm_config.json", 'peadm_config.json', $targets).first.value @@ -59,11 +59,13 @@ getvar('cluster.params.compiler_hosts'), ) + $pe_version = peadm::validated_pe_version_for_backup_restore(getvar('cluster.pe_version')) + $recovery_opts = $restore_type? { - 'recovery' => peadm::recovery_opts_default(), + 'recovery' => peadm::recovery_opts_default($pe_version), 'recovery-db' => { 'puppetdb' => true, }, - 'migration' => peadm::migration_opts_default(), - 'custom' => peadm::recovery_opts_all() + $restore, + 'migration' => peadm::migration_opts_default($pe_version), + 'custom' => peadm::recovery_opts_all($pe_version) + $restore, } $primary_target = peadm::get_targets(getvar('cluster.params.primary_host'), 1) @@ -97,6 +99,10 @@ 'activity' => [$primary_target], 'rbac' => [$primary_target], 'puppetdb' => $puppetdb_postgresql_targets, + # (host-action-collector db will be filtered for pe version by recovery_opts) + 'hac' => $primary_target, + # (patching db will be filtered for pe version by recovery_opts) + 'patching' => $primary_target, }.filter |$key,$_| { $recovery_opts[$key] == true } @@ -203,7 +209,7 @@ if getvar('recovery_opts.orchestrator') { out::message('# Restoring orchestrator secret keys') run_command(@("CMD"/L), $primary_target) - cp -rp ${shellquote($recovery_directory)}/orchestrator/secrets/* /etc/puppetlabs/orchestration-services/conf.d/secrets/ + cp -rp ${shellquote($recovery_directory)}/orchestrator/secrets/* /etc/puppetlabs/orchestration-services/conf.d/secrets/ | CMD } # lint:endignore diff --git a/spec/fixtures/peadm_config.json b/spec/fixtures/peadm_config.json index cbd5db76..de2c0a5a 100644 --- a/spec/fixtures/peadm_config.json +++ b/spec/fixtures/peadm_config.json @@ -1,3 +1,4 @@ { + "pe_version" : "2023.7.0", "params": { "primary_host": "primary", "primary_postgresql_host": "postgres" } } diff --git a/spec/fixtures/peadm_config.no_pe_version.json b/spec/fixtures/peadm_config.no_pe_version.json new file mode 100644 index 00000000..cbd5db76 --- /dev/null +++ b/spec/fixtures/peadm_config.no_pe_version.json @@ -0,0 +1,3 @@ +{ + "params": { "primary_host": "primary", "primary_postgresql_host": "postgres" } +} diff --git a/spec/functions/amend_recovery_defaults_by_pe_version_spec.rb b/spec/functions/amend_recovery_defaults_by_pe_version_spec.rb new file mode 100644 index 00000000..6bd5d418 --- /dev/null +++ b/spec/functions/amend_recovery_defaults_by_pe_version_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe 'peadm::amend_recovery_defaults_by_pe_version' do + it 'just returns the base opts if version < 2023.7' do + is_expected.to run.with_params({}, '2023.6.0', true).and_return({}) + end + + it 'adds hac if version >= 2023.7' do + is_expected.to run.with_params({}, '2023.7.0', true).and_return({ 'hac' => true }) + end + + it 'adds hac false based on opt_value' do + is_expected.to run.with_params({}, '2023.7.0', false).and_return({ 'hac' => false }) + end +end diff --git a/spec/functions/migration_opts_default_spec.rb b/spec/functions/migration_opts_default_spec.rb new file mode 100644 index 00000000..c9df7743 --- /dev/null +++ b/spec/functions/migration_opts_default_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe 'peadm::migration_opts_default' do + it 'returns pre 2023.7 defaults' do + is_expected.to run.with_params('2023.6.0').and_return( + { + 'activity' => true, + 'ca' => true, + 'classifier' => true, + 'code' => false, + 'config' => false, + 'orchestrator' => true, + 'puppetdb' => true, + 'rbac' => true, + }, + ) + end + + it 'returns 2023.7+ defaults with hac' do + is_expected.to run.with_params('2023.7.0').and_return( + { + 'activity' => true, + 'ca' => true, + 'classifier' => true, + 'code' => false, + 'config' => false, + 'orchestrator' => true, + 'puppetdb' => true, + 'rbac' => true, + 'hac' => true, + }, + ) + end + + it 'returns 2025.0+ defaults with hac and patching' do + is_expected.to run.with_params('2025.0.0').and_return( + { + 'activity' => true, + 'ca' => true, + 'classifier' => true, + 'code' => false, + 'config' => false, + 'orchestrator' => true, + 'puppetdb' => true, + 'rbac' => true, + 'hac' => true, + 'patching' => true, + }, + ) + end +end diff --git a/spec/functions/recovery_opts_all_spec.rb b/spec/functions/recovery_opts_all_spec.rb new file mode 100644 index 00000000..7d12ebda --- /dev/null +++ b/spec/functions/recovery_opts_all_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe 'peadm::recovery_opts_all' do + it 'returns pre 2023.7 defaults' do + is_expected.to run.with_params('2023.6.0').and_return( + { + 'activity' => true, + 'ca' => true, + 'classifier' => true, + 'code' => true, + 'config' => true, + 'orchestrator' => true, + 'puppetdb' => true, + 'rbac' => true, + }, + ) + end + + it 'returns 2023.7+ defaults with hac' do + is_expected.to run.with_params('2023.7.0').and_return( + { + 'activity' => true, + 'ca' => true, + 'classifier' => true, + 'code' => true, + 'config' => true, + 'orchestrator' => true, + 'puppetdb' => true, + 'rbac' => true, + 'hac' => true, + }, + ) + end + + it 'returns 2023.7+ defaults with hac if >2023.7 <2025.0' do + is_expected.to run.with_params('2024.9999.0').and_return( + { + 'activity' => true, + 'ca' => true, + 'classifier' => true, + 'code' => true, + 'config' => true, + 'orchestrator' => true, + 'puppetdb' => true, + 'rbac' => true, + 'hac' => true, + }, + ) + end + + it 'returns 2025.0+ defaults with hac and patching' do + is_expected.to run.with_params('2025.0.0').and_return( + { + 'activity' => true, + 'ca' => true, + 'classifier' => true, + 'code' => true, + 'config' => true, + 'orchestrator' => true, + 'puppetdb' => true, + 'rbac' => true, + 'hac' => true, + 'patching' => true, + }, + ) + end +end diff --git a/spec/functions/recovery_opts_default_spec.rb b/spec/functions/recovery_opts_default_spec.rb new file mode 100644 index 00000000..7a775456 --- /dev/null +++ b/spec/functions/recovery_opts_default_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe 'peadm::recovery_opts_default' do + it 'returns pre 2023.7 defaults' do + is_expected.to run.with_params('2023.6.0').and_return( + { + 'activity' => false, + 'ca' => true, + 'classifier' => false, + 'code' => true, + 'config' => true, + 'orchestrator' => false, + 'puppetdb' => true, + 'rbac' => false, + }, + ) + end + + it 'returns 2023.7+ defaults with hac' do + is_expected.to run.with_params('2023.7.0').and_return( + { + 'activity' => false, + 'ca' => true, + 'classifier' => false, + 'code' => true, + 'config' => true, + 'orchestrator' => false, + 'puppetdb' => true, + 'rbac' => false, + 'hac' => false, + }, + ) + end + + it 'returns 2025.0+ defaults with hac and patching' do + is_expected.to run.with_params('2025.0.0').and_return( + { + 'activity' => false, + 'ca' => true, + 'classifier' => false, + 'code' => true, + 'config' => true, + 'orchestrator' => false, + 'puppetdb' => true, + 'rbac' => false, + 'hac' => false, + 'patching' => false, + }, + ) + end +end diff --git a/spec/plans/backup_spec.rb b/spec/plans/backup_spec.rb index d6b2ff75..20243f45 100644 --- a/spec/plans/backup_spec.rb +++ b/spec/plans/backup_spec.rb @@ -20,6 +20,8 @@ 'orchestrator' => false, 'puppetdb' => false, 'rbac' => false, + 'hac' => false, + 'patching' => false, } } end @@ -30,6 +32,16 @@ 'backup' => {} # set all to true } end + let(:pe_version) { '2023.7.0' } + let(:cluster) do + { + 'pe_version' => pe_version, + 'params' => { + 'primary_host' => 'primary', + 'primary_postgresql_host' => 'postgres', + } + } + end before(:each) do # define a zero timestamp @@ -40,12 +52,7 @@ allow_apply - expect_task('peadm::get_peadm_config').always_return({ - 'params' => { - 'primary_host' => 'primary', - 'primary_postgresql_host' => 'postgres', - } - }) + expect_task('peadm::get_peadm_config').always_return(cluster) end it 'runs with backup type recovery' do @@ -89,27 +96,83 @@ expect(run_plan('peadm::backup', classifier_only)).to be_ok end - it 'runs with backup type custom, all backup params set to true' do - expect_task('peadm::backup_classification').with_params({ 'directory' => '/tmp/pe-backup-1970-01-01T000000Z/classifier' }) + shared_context('all 2023.6.0 backups') do + before(:each) do + expect_task('peadm::backup_classification').with_params({ 'directory' => '/tmp/pe-backup-1970-01-01T000000Z/classifier' }) + + expect_out_message.with_params('# Backing up classification') + expect_out_message.with_params('# Backing up database pe-orchestrator') + expect_out_message.with_params('# Backing up database pe-activity') + expect_out_message.with_params('# Backing up database pe-rbac') + expect_out_message.with_params('# Backing up database pe-puppetdb') + + expect_command("/opt/puppetlabs/bin/puppet-backup create --dir=/tmp/pe-backup-1970-01-01T000000Z/ca --scope=certs\n") + expect_command("/opt/puppetlabs/bin/puppet-backup create --dir=/tmp/pe-backup-1970-01-01T000000Z/code --scope=code\n") + expect_command('chown pe-postgres /tmp/pe-backup-1970-01-01T000000Z/config') + expect_command("/opt/puppetlabs/bin/puppet-backup create --dir=/tmp/pe-backup-1970-01-01T000000Z/config --scope=config\n") + expect_command("test -f /etc/puppetlabs/console-services/conf.d/secrets/keys.json && cp -rp /etc/puppetlabs/console-services/conf.d/secrets /tmp/pe-backup-1970-01-01T000000Z/rbac/ || echo secret ldap key doesnt exist\n") + expect_command("cp -rp /etc/puppetlabs/orchestration-services/conf.d/secrets /tmp/pe-backup-1970-01-01T000000Z/orchestrator/\n") + expect_command('/opt/puppetlabs/server/bin/pg_dump -Fd -Z3 -j4 -f /tmp/pe-backup-1970-01-01T000000Z/orchestrator/pe-orchestrator.dump.d "sslmode=verify-ca host=primary user=pe-orchestrator sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=pe-orchestrator"' + "\n") + expect_command('/opt/puppetlabs/server/bin/pg_dump -Fd -Z3 -j4 -f /tmp/pe-backup-1970-01-01T000000Z/activity/pe-activity.dump.d "sslmode=verify-ca host=primary user=pe-activity sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=pe-activity"' + "\n") + expect_command('/opt/puppetlabs/server/bin/pg_dump -Fd -Z3 -j4 -f /tmp/pe-backup-1970-01-01T000000Z/rbac/pe-rbac.dump.d "sslmode=verify-ca host=primary user=pe-rbac sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=pe-rbac"' + "\n") + expect_command('/opt/puppetlabs/server/bin/pg_dump -Fd -Z3 -j4 -f /tmp/pe-backup-1970-01-01T000000Z/puppetdb/pe-puppetdb.dump.d "sslmode=verify-ca host=postgres user=pe-puppetdb sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=pe-puppetdb"' + "\n") + expect_command('umask 0077 && cd /tmp && tar -czf /tmp/pe-backup-1970-01-01T000000Z.tar.gz pe-backup-1970-01-01T000000Z && rm -rf /tmp/pe-backup-1970-01-01T000000Z' + "\n") + end + end - expect_out_message.with_params('# Backing up classification') - expect_out_message.with_params('# Backing up database pe-orchestrator') - expect_out_message.with_params('# Backing up database pe-activity') - expect_out_message.with_params('# Backing up database pe-rbac') - expect_out_message.with_params('# Backing up database pe-puppetdb') + context '>= 2025.0.0' do + let(:pe_version) { '2025.0.0' } - expect_command("/opt/puppetlabs/bin/puppet-backup create --dir=/tmp/pe-backup-1970-01-01T000000Z/ca --scope=certs\n") - expect_command("/opt/puppetlabs/bin/puppet-backup create --dir=/tmp/pe-backup-1970-01-01T000000Z/code --scope=code\n") - expect_command('chown pe-postgres /tmp/pe-backup-1970-01-01T000000Z/config') - expect_command("/opt/puppetlabs/bin/puppet-backup create --dir=/tmp/pe-backup-1970-01-01T000000Z/config --scope=config\n") - expect_command("test -f /etc/puppetlabs/console-services/conf.d/secrets/keys.json && cp -rp /etc/puppetlabs/console-services/conf.d/secrets /tmp/pe-backup-1970-01-01T000000Z/rbac/ || echo secret ldap key doesnt exist\n") - expect_command("cp -rp /etc/puppetlabs/orchestration-services/conf.d/secrets /tmp/pe-backup-1970-01-01T000000Z/orchestrator/\n") - expect_command('/opt/puppetlabs/server/bin/pg_dump -Fd -Z3 -j4 -f /tmp/pe-backup-1970-01-01T000000Z/orchestrator/pe-orchestrator.dump.d "sslmode=verify-ca host=primary user=pe-orchestrator sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=pe-orchestrator"' + "\n") - expect_command('/opt/puppetlabs/server/bin/pg_dump -Fd -Z3 -j4 -f /tmp/pe-backup-1970-01-01T000000Z/activity/pe-activity.dump.d "sslmode=verify-ca host=primary user=pe-activity sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=pe-activity"' + "\n") - expect_command('/opt/puppetlabs/server/bin/pg_dump -Fd -Z3 -j4 -f /tmp/pe-backup-1970-01-01T000000Z/rbac/pe-rbac.dump.d "sslmode=verify-ca host=primary user=pe-rbac sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=pe-rbac"' + "\n") - expect_command('/opt/puppetlabs/server/bin/pg_dump -Fd -Z3 -j4 -f /tmp/pe-backup-1970-01-01T000000Z/puppetdb/pe-puppetdb.dump.d "sslmode=verify-ca host=postgres user=pe-puppetdb sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=pe-puppetdb"' + "\n") - expect_command('umask 0077 && cd /tmp && tar -czf /tmp/pe-backup-1970-01-01T000000Z.tar.gz pe-backup-1970-01-01T000000Z && rm -rf /tmp/pe-backup-1970-01-01T000000Z' + "\n") + include_context('all 2023.6.0 backups') + + it 'runs with backup type custom, all backup params set to true' do + expect_out_message.with_params('# Backing up database pe-hac') + expect_out_message.with_params('# Backing up database pe-patching') + + expect_command('/opt/puppetlabs/server/bin/pg_dump -Fd -Z3 -j4 -f /tmp/pe-backup-1970-01-01T000000Z/hac/pe-hac.dump.d "sslmode=verify-ca host=primary user=pe-hac sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=pe-hac"' + "\n") + expect_command('/opt/puppetlabs/server/bin/pg_dump -Fd -Z3 -j4 -f /tmp/pe-backup-1970-01-01T000000Z/patching/pe-patching.dump.d "sslmode=verify-ca host=primary user=pe-patching sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=pe-patching"' + "\n") + + expect(run_plan('peadm::backup', all_backup_options)).to be_ok + end + end + + context '>= 2023.7.0 < 2025.0' do + let(:pe_version) { '2023.7.0' } + + include_context('all 2023.6.0 backups') + + it 'runs with backup type custom, all backup params set to true' do + expect_out_message.with_params('# Backing up database pe-hac') + + expect_command('/opt/puppetlabs/server/bin/pg_dump -Fd -Z3 -j4 -f /tmp/pe-backup-1970-01-01T000000Z/hac/pe-hac.dump.d "sslmode=verify-ca host=primary user=pe-hac sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=pe-hac"' + "\n") + + expect(run_plan('peadm::backup', all_backup_options)).to be_ok + end + end + + context '< 2023.7.0' do + let(:pe_version) { '2023.6.0' } + + include_context('all 2023.6.0 backups') + + it 'ignores hac' do + expect(run_plan('peadm::backup', all_backup_options)).to be_ok + end + end + + context 'pe_version unknown' do + let(:pe_version) { nil } + + include_context('all 2023.6.0 backups') + + it 'warns that hac is ignored' do + expect_out_message.with_params(<<~MSG.strip) + WARNING: Retrieved a missing or unparseable PE version of ''. + Newer service databases released in 2023.7+ will be skipped from defaults. + (host-action-collector, patching) + MSG - expect(run_plan('peadm::backup', all_backup_options)).to be_ok + expect(run_plan('peadm::backup', all_backup_options)).to be_ok + end end end diff --git a/spec/plans/restore_spec.rb b/spec/plans/restore_spec.rb index 52754b8e..960e49f0 100644 --- a/spec/plans/restore_spec.rb +++ b/spec/plans/restore_spec.rb @@ -24,6 +24,13 @@ 'restore_type' => 'recovery-db' } end + let(:all_recovery_options) do + { + 'targets' => 'primary', + 'input_file' => backup_tarball, + 'restore_type' => 'custom', # defaults to all + } + end let(:classifier_only_params) do { 'targets' => 'primary', @@ -38,11 +45,22 @@ 'orchestrator' => false, 'puppetdb' => false, 'rbac' => false, + 'hac' => false, + 'patching' => false, } } end - let(:cluster) { { 'params' => { 'primary_host' => 'primary', 'primary_postgresql_host' => 'postgres' } } } + let(:pe_version) { '2023.7.0' } + let(:cluster) do + { + 'pe_version' => pe_version, + 'params' => { + 'primary_host' => 'primary', + 'primary_postgresql_host' => 'postgres', + }, + } + end before(:each) do allow_apply @@ -57,20 +75,26 @@ expect_task('peadm::get_peadm_config').always_return(cluster) end + def expect_restore_for_db(name, server) + database = "pe-#{name}" + expect_out_message.with_params("# Restoring database #{database}") + + expect_command(%(su - pe-postgres -s /bin/bash -c "/opt/puppetlabs/server/bin/psql --tuples-only -d '#{database}' -c 'DROP SCHEMA IF EXISTS pglogical CASCADE;'"\n)).be_called_times(2) + expect_command(%(su - pe-postgres -s /bin/bash -c "/opt/puppetlabs/server/bin/psql -d '#{database}' -c 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'"\n)) + expect_command(%(su - pe-postgres -s /bin/bash -c "/opt/puppetlabs/server/bin/psql -d '#{database}' -c 'ALTER USER \\"#{database}\\" WITH SUPERUSER;'"\n)) + expect_command(%(/opt/puppetlabs/server/bin/pg_restore -j 4 -d "sslmode=verify-ca host=#{server} sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=#{database} user=#{database}" -Fd /input/file/#{name}/#{database}.dump.d\n)) + expect_command(%(su - pe-postgres -s /bin/bash -c "/opt/puppetlabs/server/bin/psql -d '#{database}' -c 'ALTER USER \\"#{database}\\" WITH NOSUPERUSER;'"\n)) + expect_command(%(su - pe-postgres -s /bin/bash -c "/opt/puppetlabs/server/bin/psql -d '#{database}' -c 'DROP EXTENSION IF EXISTS pglogical CASCADE;'"\n)) + end + it 'runs with recovery params', valid_cluster: true do - expect_out_message.with_params('# Restoring database pe-puppetdb') expect_out_message.with_params('# Restoring ca, certs, code and config for recovery') expect_command("umask 0077 && cd /input && tar -xzf /input/file.tar.gz\n") expect_command("/opt/puppetlabs/bin/puppet-backup restore --scope=certs,code,config --tempdir=/input/file --force /input/file/recovery/pe_backup-*tgz\n") expect_command("systemctl stop pe-console-services pe-nginx pxp-agent pe-puppetserver pe-orchestration-services puppet pe-puppetdb\n") expect_command("test -f /input/file/rbac/keys.json && cp -rp /input/file/keys.json /etc/puppetlabs/console-services/conf.d/secrets/ || echo secret ldap key doesnt exist\n") - expect_command("su - pe-postgres -s /bin/bash -c \"/opt/puppetlabs/server/bin/psql --tuples-only -d 'pe-puppetdb' -c 'DROP SCHEMA IF EXISTS pglogical CASCADE;'\"\n").be_called_times(2) - expect_command("su - pe-postgres -s /bin/bash -c \"/opt/puppetlabs/server/bin/psql -d 'pe-puppetdb' -c 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'\"\n") - expect_command('su - pe-postgres -s /bin/bash -c "/opt/puppetlabs/server/bin/psql -d \'pe-puppetdb\' -c \'ALTER USER \\"pe-puppetdb\\" WITH SUPERUSER;\'"' + "\n") - expect_command('/opt/puppetlabs/server/bin/pg_restore -j 4 -d "sslmode=verify-ca host=postgres sslcert=/etc/puppetlabs/puppetdb/ssl/primary.cert.pem sslkey=/etc/puppetlabs/puppetdb/ssl/primary.private_key.pem sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem dbname=pe-puppetdb user=pe-puppetdb" -Fd /input/file/puppetdb/pe-puppetdb.dump.d' + "\n") - expect_command('su - pe-postgres -s /bin/bash -c "/opt/puppetlabs/server/bin/psql -d \'pe-puppetdb\' -c \'ALTER USER \\"pe-puppetdb\\" WITH NOSUPERUSER;\'"' + "\n") - expect_command('su - pe-postgres -s /bin/bash -c "/opt/puppetlabs/server/bin/psql -d \'pe-puppetdb\' -c \'DROP EXTENSION IF EXISTS pglogical CASCADE;\'"' + "\n") + expect_restore_for_db('puppetdb', 'postgres') expect_command("/opt/puppetlabs/bin/puppet-infrastructure configure --no-recover\n") expect(run_plan('peadm::restore', recovery_params)).to be_ok @@ -104,21 +128,113 @@ expect(run_plan('peadm::restore', classifier_only_params)).to be_ok end - it 'runs with recovery params, no valid cluster', valid_cluster: false do - allow_any_command - + def expect_peadm_config_fallback(backup_dir, file) # simulate a failure to get the cluster configuration expect_task('peadm::get_peadm_config').always_return({}) expect_out_message.with_params('Failed to get cluster configuration, loading from backup...') - # download mocked to return the path to the file fixtures/peadm_config.json + # download mocked to return the path to a fixtures file expect_download("#{backup_dir}/peadm/peadm_config.json").return do |targets, _source, _destination, _params| results = targets.map do |target| - Bolt::Result.new(target, value: { 'path' => File.expand_path(File.join(fixtures, 'peadm_config.json')) }) + Bolt::Result.new(target, value: { 'path' => File.expand_path(File.join(fixtures, file)) }) end Bolt::ResultSet.new(results) end + end + + it 'runs with recovery params, no valid cluster', valid_cluster: false do + allow_any_command + + expect_peadm_config_fallback(backup_dir, 'peadm_config.json') + expect(run_plan('peadm::restore', recovery_params)).to be_ok end + + shared_context('all 2023.6.0 backups') do + before(:each) do + expect_out_message.with_params('# Restoring ca and ssl certificates') + expect_out_message.with_params('# Restoring code') + expect_out_message.with_params('# Restoring config') + + expect_command("umask 0077 && cd /input && tar -xzf /input/file.tar.gz\n") + expect_task('peadm::restore_classification').with_params( + { + 'classification_file' => "#{backup_dir}/classifier/classification_backup.json", + }, + ) + expect_command("/opt/puppetlabs/bin/puppet-backup restore --scope=certs --tempdir=/input/file --force /input/file/ca/pe_backup-*tgz\n") + expect_command("/opt/puppetlabs/bin/puppet-backup restore --scope=code --tempdir=/input/file --force /input/file/code/pe_backup-*tgz\n") + expect_command("/opt/puppetlabs/bin/puppet-backup restore --scope=config --tempdir=/input/file --force /input/file/config/pe_backup-*tgz\n") + expect_command("systemctl stop pe-console-services pe-nginx pxp-agent pe-puppetserver pe-orchestration-services puppet pe-puppetdb\n") + expect_command("cp -rp /input/file/orchestrator/secrets/* /etc/puppetlabs/orchestration-services/conf.d/secrets/\n") + expect_command("test -f /input/file/rbac/keys.json && cp -rp /input/file/keys.json /etc/puppetlabs/console-services/conf.d/secrets/ || echo secret ldap key doesnt exist\n") + expect_restore_for_db('activity', 'primary') + expect_restore_for_db('orchestrator', 'primary') + expect_restore_for_db('puppetdb', 'postgres') + expect_restore_for_db('rbac', 'primary') + expect_command("/opt/puppetlabs/bin/puppet-infrastructure configure --no-recover\n") + end + end + + context '>= 2025.0.0' do + let(:pe_version) { '2025.0.0' } + + include_context('all 2023.6.0 backups') + + it 'runs with backup type custom, all params set to true', valid_cluster: true do + expect_restore_for_db('hac', 'primary') + expect_restore_for_db('patching', 'primary') + + expect(run_plan('peadm::restore', all_recovery_options)).to be_ok + end + end + + context '>= 2023.7.0 < 2025.0' do + let(:pe_version) { '2023.7.0' } + + include_context('all 2023.6.0 backups') + + it 'runs with backup type custom, all params set to true', valid_cluster: true do + expect_restore_for_db('hac', 'primary') + + expect(run_plan('peadm::restore', all_recovery_options)).to be_ok + end + end + + context '< 2023.7.0' do + let(:pe_version) { '2023.6.0' } + + include_context('all 2023.6.0 backups') + + it 'ignores hac', valid_cluster: true do + expect(run_plan('peadm::restore', all_recovery_options)).to be_ok + end + end + + # restoring an older backup that does not have the pe_version in it + context 'no valid cluster, pe_version missing from recovery params (older backup)' do + let(:cluster) do + { + 'params' => { + 'primary_host' => 'primary', + 'primary_postgresql_host' => 'postgres', + }, + } + end + + include_context('all 2023.6.0 backups') + + it 'warns that hac is ignored', valid_cluster: false do + expect_out_message.with_params(<<~MSG.strip) + WARNING: Retrieved a missing or unparseable PE version of ''. + Newer service databases released in 2023.7+ will be skipped from defaults. + (host-action-collector, patching) + MSG + + expect_peadm_config_fallback(backup_dir, 'peadm_config.no_pe_version.json') + + expect(run_plan('peadm::restore', all_recovery_options)).to be_ok + end + end end diff --git a/types/recovery_opts.pp b/types/recovery_opts.pp index eb392752..eacfd532 100644 --- a/types/recovery_opts.pp +++ b/types/recovery_opts.pp @@ -7,4 +7,6 @@ 'orchestrator' => Optional[Boolean], 'puppetdb' => Optional[Boolean], 'rbac' => Optional[Boolean], + 'hac' => Optional[Boolean], + 'patching' => Optional[Boolean], }]