diff --git a/.fixtures.yml b/.fixtures.yml index 6e1c7f66..7a844c86 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -3,6 +3,7 @@ fixtures: forge_modules: ruby_task_helper: "puppetlabs/ruby_task_helper" service: "puppetlabs/service" + package: "puppetlabs/package" repositories: facts: 'https://github.com/puppetlabs/puppetlabs-facts.git' puppet_agent: 'https://github.com/puppetlabs/puppetlabs-puppet_agent.git' diff --git a/.vscode/settings.json b/.vscode/settings.json index cc67606f..c4294b55 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "python.linting.pylintEnabled": true, - "python.linting.enabled": true + "python.linting.enabled": true, + "git.ignoreLimitWarning": true } \ No newline at end of file diff --git a/Gemfile b/Gemfile index 97f40f78..4c76d499 100644 --- a/Gemfile +++ b/Gemfile @@ -26,7 +26,7 @@ group :development do gem "puppet-module-win-dev-r#{minor_version}", '~> 1.0', require: false, platforms: [:mswin, :mingw, :x64_mingw] gem "puppet-debugger", '>= 0.18.0', require: false gem "bolt", '>= 3.17.0', require: false - gem "github_changelog_generator", require: false + gem "github_changelog_generator", '>= 1.16.4', require: false gem "octokit", '4.21.0', require: false end group :system_tests do diff --git a/documentation/automated_recovery.md b/documentation/automated_recovery.md index 64b3218e..989bfd69 100644 --- a/documentation/automated_recovery.md +++ b/documentation/automated_recovery.md @@ -22,7 +22,7 @@ Procedure: 2. Temporarily set both primary and replica server nodes so that they use the remaining healthy PE-PostgreSQL server - bolt plan run peadm::util::update_db_setting --target , primary_postgresql_host= + bolt plan run peadm::util::update_db_setting --target , primary_postgresql_host= override=true 3. Restart `pe-puppetdb.service` on Puppet server primary and replica diff --git a/metadata.json b/metadata.json index 213fe882..428976d2 100644 --- a/metadata.json +++ b/metadata.json @@ -32,6 +32,10 @@ "name": "puppetlabs/service", "version_requirement": ">= 1.3.0 < 3.0.0" }, + { + "name": "puppetlabs/package", + "version_requirement": ">= 2.1.0 < 3.0.0" + }, { "name": "puppetlabs/inifile", "version_requirement": ">= 5.2.0 < 6.0.0" diff --git a/plans/add_database.pp b/plans/add_database.pp index bda848dd..3527c2eb 100644 --- a/plans/add_database.pp +++ b/plans/add_database.pp @@ -12,6 +12,9 @@ ) { $primary_target = peadm::get_targets($primary_host, 1) + $postgresql_target = peadm::get_targets($targets, 1) + + $postgresql_host = $postgresql_target.peadm::certname() # Get current peadm config before making modifications and shutting down # PuppetDB @@ -21,13 +24,13 @@ # Bail if this is trying to be ran against Standard if $compilers.empty { - fail_plan('Plan Peadm::Add_database only applicable for L and XL deployments') + fail_plan('Plan peadm::add_database is only applicable for L and XL deployments') } # Existing nodes and their assignments $replica_host = $peadm_config['params']['replica_host'] - $primary_postgresql_host = $peadm_config['params']['primary_postgresql_host'] - $replica_postgresql_host = $peadm_config['params']['replica_postgresql_host'] + $postgresql_a_host = $peadm_config['role-letter']['postgresql']['A'] + $postgresql_b_host = $peadm_config['role-letter']['postgresql']['B'] $replica_target = peadm::get_targets($replica_host, 1) @@ -41,8 +44,8 @@ } else { # If array is empty then no external databases were previously configured $no_external_db = peadm::flatten_compact([ - $primary_postgresql_host, - $replica_postgresql_host + $postgresql_a_host, + $postgresql_b_host ]).empty # Pick operating mode based on array check @@ -67,26 +70,26 @@ # The letter which doesn't yet have a server assigned or in the event this # is a replacement operation, the letter this node was assigned to previously $avail_group_letter = peadm::flatten_compact($roles['postgresql'].map |$k,$v| { - if (! $v) or ($v == $targets.peadm::certname()) { + if (! $v) or ($v == $postgresql_host) { $k } })[0] # When in pair mode we assume the other PSQL node will serve as our source $source_db_host = peadm::flatten_compact([ - $primary_postgresql_host, - $replica_postgresql_host - ]).reject($targets.peadm::certname())[0] + $postgresql_a_host, + $postgresql_b_host + ]).reject($postgresql_host)[0] } - out::message("Adding PostgreSQL server ${targets.peadm::certname()} to availability group ${avail_group_letter}") - out::message("Using ${source_db_host} to populate ${targets.peadm::certname()}") + out::message("Adding PostgreSQL server ${postgresql_host} to availability group ${avail_group_letter}") + out::message("Using ${source_db_host} to populate ${postgresql_host}") $source_db_target = peadm::get_targets($source_db_host, 1) peadm::plan_step('init-db-node') || { # Install PSQL on new node to be used as external PuppetDB backend by using # puppet in lieu of installer - run_plan('peadm::subplans::component_install', $targets, + run_plan('peadm::subplans::component_install', $postgresql_target, primary_host => $primary_target, avail_group_letter => $avail_group_letter, role => 'puppet/puppetdb-database' @@ -95,7 +98,7 @@ # Stop Puppet to ensure catalogs are not being compiled for PE infrastructure nodes run_command('systemctl stop puppet.service', peadm::flatten_compact([ - $targets, + $postgresql_target, $compilers, $primary_target, $replica_target, @@ -108,44 +111,60 @@ peadm::plan_step('replicate-db') || { # Replicate content from source to newly installed PSQL server - run_plan('peadm::subplans::db_populate', $targets, source_host => $source_db_target.peadm::certname()) + run_plan('peadm::subplans::db_populate', $postgresql_target, source_host => $source_db_target.peadm::certname()) # Run Puppet on new PSQL node to fix up certificates and permissions - run_task('peadm::puppet_runonce', $targets) + run_task('peadm::puppet_runonce', $postgresql_target) } - if $operating_mode == 'init' { - - # Update classification and database.ini settings, assume a replica PSQL - # does not exist - peadm::plan_step('update-classification') || { + # Update classification and database.ini settings, assume a replica PSQL + # does not exist + peadm::plan_step('update-classification') || { + + # To ensure everything is functional when a replica exists but only a single + # PostgreSQL node has been deployed, configure alternate availability group + # to connect to other group's new node + if ($operating_mode == 'init' and $replica_host) { + $a_host = $avail_group_letter ? { 'A' => $postgresql_host, default => undef } + $b_host = $avail_group_letter ? { 'B' => $postgresql_host, default => undef } + $host = pick($a_host, $b_host) + out::verbose("In transitive state, setting classification to ${host}") run_plan('peadm::util::update_classification', $primary_target, - primary_postgresql_host => pick($primary_postgresql_host, $targets), - peadm_config => $peadm_config + postgresql_a_host => $host, + postgresql_b_host => $host, + peadm_config => $peadm_config + ) + } else { + run_plan('peadm::util::update_classification', $primary_target, + postgresql_a_host => $avail_group_letter ? { 'A' => $postgresql_host, default => undef }, + postgresql_b_host => $avail_group_letter ? { 'B' => $postgresql_host, default => undef }, + peadm_config => $peadm_config ) } + } - peadm::plan_step('update-db-settings') || { - run_plan('peadm::util::update_db_setting', peadm::flatten_compact([ - $compilers, - $primary_target, - $replica_target - ]), - primary_postgresql_host => $targets, - peadm_config => $peadm_config - ) + peadm::plan_step('update-db-settings') || { + run_plan('peadm::util::update_db_setting', peadm::flatten_compact([ + $compilers, + $primary_target, + $replica_target + ]), + postgresql_host => $postgresql_host, + peadm_config => $peadm_config + ) - # (Re-)Start PuppetDB now that we are done making modifications - run_command('systemctl restart pe-puppetdb.service', peadm::flatten_compact([ - $primary_target, - $replica_target - ])) - } + # (Re-)Start PuppetDB now that we are done making modifications + run_command('systemctl restart pe-puppetdb.service', peadm::flatten_compact([ + $primary_target, + $replica_target + ])) + } - # Clean up old puppetdb database on primary and those which were copied to - # new host. - peadm::plan_step('cleanup-db') || { + peadm::plan_step('cleanup-db') || { + if $operating_mode == 'init' { + # Clean up old puppetdb database on primary and those which were copied to + # new host. $target_db_purge = [ 'pe-activity', 'pe-classifier', @@ -157,7 +176,7 @@ # If a primary replica exists then pglogical is enabled and will prevent # the clean up of databases on our target because it opens a connection. if $replica_host { - run_plan('peadm::util::db_disable_pglogical', $targets, databases => $target_db_purge) + run_plan('peadm::util::db_disable_pglogical', $postgresql_target, databases => $target_db_purge) } # Clean up old databases @@ -167,39 +186,9 @@ $replica_target ]) - run_plan('peadm::util::db_purge', $clean_source, databases => ['pe-puppetdb']) - run_plan('peadm::util::db_purge', $targets, databases => $target_db_purge) - } - } else { - peadm::plan_step('update-classification') || { - run_plan('peadm::util::update_classification', $primary_target, - primary_postgresql_host => pick($primary_postgresql_host, $targets), - replica_postgresql_host => pick($replica_postgresql_host, $targets), - peadm_config => $peadm_config - ) - } - - # Plan needs to know which node is being added as well as primary and - # replica designation - peadm::plan_step('update-db-settings') || { - run_plan('peadm::util::update_db_setting', peadm::flatten_compact([ - $compilers, - $primary_target, - $replica_target - ]), - new_postgresql_host => $targets, - primary_postgresql_host => pick($primary_postgresql_host, $targets), - replica_postgresql_host => pick($replica_postgresql_host, $targets), - peadm_config => $peadm_config - ) - - # (Re-)Start PuppetDB now that we are done making modifications - run_command('systemctl restart pe-puppetdb.service', peadm::flatten_compact([ - $primary_target, - $replica_target - ])) - } - peadm::plan_step('cleanup-db') || { + run_plan('peadm::util::db_purge', $clean_source, databases => ['pe-puppetdb']) + run_plan('peadm::util::db_purge', $postgresql_target, databases => $target_db_purge) + } else { out::message("No databases to cleanup when in ${operating_mode}") } } @@ -212,7 +201,7 @@ peadm::plan_step('finalize') || { # Run Puppet to sweep up but no restarts should occur so do them in parallel run_task('peadm::puppet_runonce', peadm::flatten_compact([ - $targets, + $postgresql_target, $primary_target, $compilers, $replica_target @@ -220,7 +209,7 @@ # Start Puppet agent run_command('systemctl start puppet.service', peadm::flatten_compact([ - $targets, + $postgresql_target, $compilers, $primary_target, $replica_target, diff --git a/plans/add_replica.pp b/plans/add_replica.pp index 7f0e4705..c906332d 100644 --- a/plans/add_replica.pp +++ b/plans/add_replica.pp @@ -26,6 +26,11 @@ $replica_target = peadm::get_targets($replica_host, 1) $replica_postgresql_target = peadm::get_targets($replica_postgresql_host, 1) + run_command('systemctl stop puppet.service', peadm::flatten_compact([ + $primary_target, + $replica_postgresql_target, + ])) + $certdata = run_task('peadm::cert_data', $primary_target).first.value $primary_avail_group_letter = $certdata['extensions'][peadm::oid('peadm_availability_group')] $replica_avail_group_letter = $primary_avail_group_letter ? { 'A' => 'B', 'B' => 'A' } @@ -35,41 +40,15 @@ $dns_alt_names = [$replica_target.peadm::certname()] + (pick($certdata['dns-alt-names'], []) - $certdata['certname']) # This has the effect of revoking the node's certificate, if it exists - run_command("puppet infrastructure forget ${replica_target.peadm::certname()}", $primary_target, _catch_errors => true) - - # Check for and merge csr_attributes. - run_plan('peadm::util::insert_csr_extension_requests', $replica_target, - extension_requests => { - peadm::oid('peadm_role') => 'puppet/server', - peadm::oid('peadm_availability_group') => $replica_avail_group_letter - } - ) - - run_task('peadm::agent_install', $replica_target, - server => $primary_target.peadm::certname(), - install_flags => [ - '--puppet-service-ensure', 'stopped', - "main:certname=${replica_target.peadm::certname()}", - "main:dns_alt_names=${dns_alt_names.join(',')}", - ], - ) - - # clean the cert to make the plan idempotent - run_task('peadm::ssl_clean', $replica_target, - certname => $replica_target.peadm::certname(), - ) - - # Manually submit a CSR - run_task('peadm::submit_csr', $replica_target) + run_command("/opt/puppetlabs/bin/puppet infrastructure forget ${replica_target.peadm::certname()}", $primary_target, _catch_errors => true) - # On primary, if necessary, sign the certificate request - run_task('peadm::sign_csr', $primary_target, - certnames => [$replica_target.peadm::certname()], + run_plan('peadm::subplans::component_install', $replica_target, + primary_host => $primary_target, + avail_group_letter => $replica_avail_group_letter, + role => 'puppet/server', + dns_alt_names => $dns_alt_names ) - # On , run the puppet agent - run_task('peadm::puppet_runonce', $replica_target) - # On the PE-PostgreSQL server in the group # Stop puppet and add the following two lines to @@ -77,27 +56,35 @@ # pe-puppetdb-pe-puppetdb-map pe-puppetdb # pe-puppetdb-pe-puppetdb-migrator-map pe-puppetdb-migrator apply($replica_postgresql_target) { - service { 'puppet': - ensure => stopped, - before => File_line['puppetdb-map', 'migrator-map'], - } - - file_line { 'puppetdb-map': + file_line { 'pe-puppetdb-pe-puppetdb-map': path => '/opt/puppetlabs/server/data/postgresql/11/data/pg_ident.conf', line => "pe-puppetdb-pe-puppetdb-map ${replica_target.peadm::certname()} pe-puppetdb", } - - file_line { 'migrator-map': + file_line { 'pe-puppetdb-pe-puppetdb-migrator-map': path => '/opt/puppetlabs/server/data/postgresql/11/data/pg_ident.conf', line => "pe-puppetdb-pe-puppetdb-migrator-map ${replica_target.peadm::certname()} pe-puppetdb-migrator", } - - service { 'pe-postgresql': - ensure => running, - subscribe => File_line['puppetdb-map', 'migrator-map'], + file_line { 'pe-puppetdb-pe-puppetdb-read-map': + path => '/opt/puppetlabs/server/data/postgresql/11/data/pg_ident.conf', + line => "pe-puppetdb-pe-puppetdb-read-map ${replica_target.peadm::certname()} pe-puppetdb-read", } } + run_command('systemctl reload pe-postgresql.service', $replica_postgresql_target) + + run_plan('peadm::util::update_classification', $primary_target, + server_a_host => $replica_avail_group_letter ? { 'A' => $replica_host, default => undef }, + server_b_host => $replica_avail_group_letter ? { 'B' => $replica_host, default => undef }, + internal_compiler_a_pool_address => $replica_avail_group_letter ? { 'A' => $replica_host, default => undef }, + internal_compiler_b_pool_address => $replica_avail_group_letter ? { 'B' => $replica_host, default => undef } + ) + + # Source the global hiera.yaml from Primary and synchronize to new Replica + # Provision the new system as a replica + run_plan('peadm::util::sync_global_hiera', $replica_target, + primary_host => $primary_target + ) + # Provision the new system as a replica run_task('peadm::provision_replica', $primary_target, replica => $replica_target.peadm::certname(), @@ -109,8 +96,12 @@ legacy => true, ) - # start puppet service on postgresql host - run_command('systemctl start puppet.service', $replica_postgresql_target) + # start puppet service + run_command('systemctl start puppet.service', peadm::flatten_compact([ + $primary_target, + $replica_postgresql_target, + $replica_target + ])) return("Added replica ${replica_target}") } diff --git a/plans/subplans/component_install.pp b/plans/subplans/component_install.pp index 74e5a616..774b1403 100644 --- a/plans/subplans/component_install.pp +++ b/plans/subplans/component_install.pp @@ -7,18 +7,18 @@ # @param dns_alt_names _ A comma_separated list of DNS alt names for the component # @param role _ Optional PEADM role the component will serve plan peadm::subplans::component_install( - Peadm::SingleTargetSpec $targets, - Peadm::SingleTargetSpec $primary_host, - Enum['A', 'B'] $avail_group_letter, - Optional[String[1]] $dns_alt_names = undef, - Optional[String[1]] $role = undef + Peadm::SingleTargetSpec $targets, + Peadm::SingleTargetSpec $primary_host, + Enum['A', 'B'] $avail_group_letter, + Optional[Variant[String[1], Array]] $dns_alt_names = undef, + Optional[String[1]] $role = undef ){ $component_target = peadm::get_targets($targets, 1) $primary_target = peadm::get_targets($primary_host, 1) run_plan('peadm::subplans::prepare_agent', $component_target, primary_host => $primary_target, - dns_alt_name => $dns_alt_names, + dns_alt_names => peadm::flatten_compact([$dns_alt_names]), certificate_extensions => { peadm::oid('peadm_role') => $role, peadm::oid('peadm_availability_group') => $avail_group_letter, diff --git a/plans/subplans/modify_certificate.pp b/plans/subplans/modify_certificate.pp index b806b572..1b571aee 100644 --- a/plans/subplans/modify_certificate.pp +++ b/plans/subplans/modify_certificate.pp @@ -20,6 +20,7 @@ # Figure out some information from the existing certificate $certdata = run_task('peadm::cert_data', $target).first.value $certname = $certdata['certname'] + $target_is_primary = ($certname == $primary_certname) # These vars represent what the extensions currently are, vs. what they should be @@ -65,11 +66,13 @@ # fail the plan unless it's a known circumstance in which it's okay to proceed. # Scenario 1: the primary's cert can't be cleaned because it's already revoked. # Scenario 2: the primary's cert can't be cleaned because it's been deleted. + # Scenario 3: any component's cert can't be cleaned because it's been deleted. unless ($target_is_primary and ($ca_clean_result[merged_output] =~ /certificate revoked/ or - $ca_clean_result[merged_output] =~ /Could not find 'hostcert'/)) + $ca_clean_result[merged_output] =~ /Could not find 'hostcert'/)) or + ($ca_clean_result[merged_output] =~ /Could not find files to clean/) { - fail_plan($ca_clean_result) + fail_plan($ca_clean_result[merged_output]) } } diff --git a/plans/subplans/prepare_agent.pp b/plans/subplans/prepare_agent.pp index 70359106..507c33ac 100644 --- a/plans/subplans/prepare_agent.pp +++ b/plans/subplans/prepare_agent.pp @@ -11,7 +11,7 @@ $dns_alt_names_flag = $dns_alt_names? { undef => [], - default => ["main:dns_alt_names=${dns_alt_names}"], + default => ["main:dns_alt_names=${dns_alt_names.join(',')}"], } $status = run_task('package', $agent_target, @@ -29,6 +29,41 @@ "main:certname=${agent_target.peadm::certname()}", ], ) + } else { + run_command('systemctl stop puppet.service', $agent_target) + # If re-using a node which was previously part of the infrastructure then it + # might have a bad configuration which will prevent it from reconfiguring. Best + # example of this is a failed primary being added back into infrastructure as + # a replica + out::message('Ensuring node is set to query current primary for Puppet Agent operations') + run_command("/opt/puppetlabs/bin/puppet config set --section main server ${primary_target.peadm::certname()}", $agent_target) + run_command('/opt/puppetlabs/bin/puppet config delete --section agent server_list', $agent_target) + } + + # Obtain data about certificate from primary + $certstatus = run_task('peadm::cert_valid_status', $primary_target, + certname => $agent_target.peadm::certname()).first.value + + # Obtain data about certificate from agent + $certdata = run_task('peadm::cert_data', $agent_target).first.value + + # The invalid status is primarily serves as a way to catch revoked certificates. + # A primary server is the only thing that can reliably identify if agent + # certificates are revoked, if it is then skip the submit and sign process and + # just got directly to forcing a regeneration. + if ($certstatus['certificate-status'] == 'invalid') { + $force_regenerate = true + $skip_csr = true + } else { + # When the primary can't validate a certificate because it is missing but the + # agent claims it has one, clean the agent to get to an agreed upon state + # before moving onto the submit and sign process. + if $certdata['certificate-exists'] and $certstatus['reason'] =~ /The private key is missing from/ { + out::message("Agent: ${agent_target.peadm::certname()} has a local cert but Primary: ${primary_target.peadm::certname()} does not, force agent clean") + run_task('peadm::ssl_clean', $agent_target, certname => $agent_target.peadm::certname()) + } + $force_regenerate = false + $skip_csr = false } # Ensures scenarios where agent was pre-installed but never on-boarding and @@ -37,13 +72,18 @@ # # If necessary, manually submit a CSR # ignoring errors to simplify logic - run_task('peadm::submit_csr', $agent_target, {'_catch_errors' => true}) + unless $skip_csr { + run_task('peadm::submit_csr', $agent_target, {'_catch_errors' => true}) - # On primary, if necessary, sign the certificate request - run_task('peadm::sign_csr', $primary_target, { 'certnames' => [$agent_target.peadm::certname()] } ) + # On primary, if necessary, sign the certificate request + run_task('peadm::sign_csr', $primary_target, { 'certnames' => [$agent_target.peadm::certname()] } ) + } + # If agent certificate is good but lacks appropriate extensions, plan will still + # regenerate certificate run_plan('peadm::modify_certificate', $agent_target, - primary_host => $primary_target.peadm::certname(), - add_extensions => $certificate_extensions + primary_host => $primary_target.peadm::certname(), + add_extensions => $certificate_extensions, + force_regenerate => $force_regenerate ) } diff --git a/plans/util/sync_global_hiera.pp b/plans/util/sync_global_hiera.pp new file mode 100644 index 00000000..ada8ff11 --- /dev/null +++ b/plans/util/sync_global_hiera.pp @@ -0,0 +1,22 @@ +# @api private +plan peadm::util::sync_global_hiera ( + Peadm::SingleTargetSpec $targets, + Peadm::SingleTargetSpec $primary_host, +) { + + $primary_target = peadm::get_targets($primary_host, 1) + $replica_target = $targets + + # Source the global hiera.yaml from Primary and synchronize to new Replica + $global_hiera_yaml = run_task('peadm::read_file', $primary_target, + path => '/etc/puppetlabs/puppet/hiera.yaml', + ).first['content'] + + run_task('peadm::mkdir_p_file', $replica_target, + path => '/etc/puppetlabs/puppet/hiera.yaml', + owner => 'root', + group => 'root', + mode => '0644', + content => $global_hiera_yaml, + ) +} diff --git a/plans/util/update_classification.pp b/plans/util/update_classification.pp index 9dc65d02..0e88aa18 100644 --- a/plans/util/update_classification.pp +++ b/plans/util/update_classification.pp @@ -6,11 +6,12 @@ # Standard Peadm::SingleTargetSpec $targets, Optional[Hash] $peadm_config = undef, - Optional[Peadm::SingleTargetSpec] $replica_host = undef, + Optional[Peadm::SingleTargetSpec] $server_a_host = undef, + Optional[Peadm::SingleTargetSpec] $server_b_host = undef, # Extra Large - Optional[Peadm::SingleTargetSpec] $primary_postgresql_host = undef, - Optional[Peadm::SingleTargetSpec] $replica_postgresql_host = undef, + Optional[Peadm::SingleTargetSpec] $postgresql_a_host = undef, + Optional[Peadm::SingleTargetSpec] $postgresql_b_host = undef, # Common Configuration Optional[String] $compiler_pool_address = undef, @@ -18,44 +19,49 @@ Optional[String] $internal_compiler_b_pool_address = undef, ) { - # Convert inputs into targets. - $primary_target = peadm::get_targets($targets, 1) - $replica_target = peadm::get_targets($replica_host, 1) - $primary_postgresql_target = peadm::get_targets($primary_postgresql_host, 1) - $replica_postgresql_target = peadm::get_targets($replica_postgresql_host, 1) + $primary_target = peadm::get_targets($targets, 1) # Makes this more easily usable outside a plan if $peadm_config { - $current = $peadm_config['params'] + $current = $peadm_config } else { - $current = run_task('peadm::get_peadm_config', $primary_target).first.value['params'] + $current = run_task('peadm::get_peadm_config', $primary_target).first.value } - # When a replica in configured, the B side of the deployment requires that - # replica_postgresql_host to be set, if it is not then PuppetDB will be left - # non-functional. Doing this will allow both sides of the deployment to start - # up and be functional until the second PostgreSQL node can be provisioned and configured. - if (! $replica_postgresql_target.peadm::certname()) and $current['replica_host'] { - out::message('Overriding replica_postgresql_host while in transitive state') - $overridden_replica_postgresql_target = $primary_postgresql_target - } else { - $overridden_replica_postgresql_target = $replica_postgresql_target - } + out::verbose('Current config is...') + out::verbose($current) - $filtered = { - 'primary_host' => $primary_target.peadm::certname(), - 'replica_host' => $replica_target.peadm::certname(), - 'primary_postgresql_host' => $primary_postgresql_target.peadm::certname(), - 'replica_postgresql_host' => $overridden_replica_postgresql_target.peadm::certname(), - 'compiler_pool_address' => $compiler_pool_address, + $filtered_params = { + 'compiler_pool_address' => $compiler_pool_address, 'internal_compiler_a_pool_address' => $internal_compiler_a_pool_address, 'internal_compiler_b_pool_address' => $internal_compiler_b_pool_address }.filter |$parameter| { $parameter[1] } - $new = merge($current, $filtered) + $filtered_server = { + 'A' => $server_a_host, + 'B' => $server_b_host + }.filter |$parameter| { $parameter[1] } + + $filtered_psql = { + 'A' => $postgresql_a_host, + 'B' => $postgresql_b_host + }.filter |$parameter| { $parameter[1] } + + $filtered = { + 'params' => $filtered_params, + 'role-letter' => { + 'server' => $filtered_server, + 'postgresql' => $filtered_psql + } + } + + out::verbose('New values are...') + out::verbose($filtered) + + $new = deep_merge($current, $filtered) - out::message('Classification to be updated using the following hash...') - out::message($new) + out::verbose('Updating classification to...') + out::verbose($new) apply($primary_target) { class { 'peadm::setup::node_manager_yaml': @@ -63,14 +69,14 @@ } class { 'peadm::setup::node_manager': - primary_host => $new['primary_host'], - server_a_host => $new['primary_host'], - server_b_host => $new['replica_host'], - postgresql_a_host => $new['primary_postgresql_host'], - postgresql_b_host => $new['replica_postgresql_host'], - compiler_pool_address => $new['compiler_pool_address'], - internal_compiler_a_pool_address => $new['internal_compiler_a_pool_address'], - internal_compiler_b_pool_address => $new['internal_compiler_b_pool_address'], + primary_host => $primary_target.peadm::certname(), + server_a_host => $new['role-letter']['server']['A'], + server_b_host => $new['role-letter']['server']['B'], + postgresql_a_host => $new['role-letter']['postgresql']['A'], + postgresql_b_host => $new['role-letter']['postgresql']['B'], + compiler_pool_address => $new['params']['compiler_pool_address'], + internal_compiler_a_pool_address => $new['params']['internal_compiler_a_pool_address'], + internal_compiler_b_pool_address => $new['params']['internal_compiler_b_pool_address'], require => Class['peadm::setup::node_manager_yaml'], } } diff --git a/plans/util/update_db_setting.pp b/plans/util/update_db_setting.pp index 909581c5..aa74a0ff 100644 --- a/plans/util/update_db_setting.pp +++ b/plans/util/update_db_setting.pp @@ -4,30 +4,18 @@ # plan peadm::util::update_db_setting ( TargetSpec $targets, - Optional[Peadm::SingleTargetSpec] $new_postgresql_host = undef, - Optional[Peadm::SingleTargetSpec] $primary_postgresql_host = undef, - Optional[Peadm::SingleTargetSpec] $replica_postgresql_host = undef, - Optional[Hash] $peadm_config = undef, + Optional[Peadm::SingleTargetSpec] $postgresql_host = undef, + Optional[Hash] $peadm_config = undef, + Boolean $override = false ) { - # Convert inputs into targets. - $primary_postgresql_target = peadm::get_targets($primary_postgresql_host, 1) - $replica_postgresql_target = peadm::get_targets($replica_postgresql_host, 1) - - # Originally written to handle some additional logic which was eventually - # determined to not be useful and was pulled out. As a result could use - # more additional simplification. The goal is to match each infrastructure - # component to the PostgreSQL nodes which corresponds to their availability - # letter and if a match is not found, assume that new node is the match. - # - # FIX ME: Test removal of $primary_potsgresql_host and $replica_postgresql_host - # parameter check. Likely only parameter needed is the node be added. Section - # also needs to be parallelized, can't use built functionality of apply(). + # FIX ME: Section needs to be parallelized, can't use built in functionality + # of apply(). get_targets($targets).each |$target| { - # Availability group does not matter if only one PSQL node in the cluster - if ($primary_postgresql_host and $replica_postgresql_host) { - + if $override { + $db = $postgresql_host + } else { # Existing config used to dynamically pair nodes with appropriate PSQL # server $roles = $peadm_config['role-letter'] @@ -43,15 +31,13 @@ if $match { $db = $match } else { - $db = $new_postgresql_host + $db = $postgresql_host } - - $db_setting = "//${db}:5432/pe-puppetdb?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem&sslkey=/etc/puppetlabs/puppetdb/ssl/${target.peadm::certname()}.private_key.pk8&sslcert=/etc/puppetlabs/puppetdb/ssl/${$target.peadm::certname()}.cert.pem" - } else { - $db_setting = "//${primary_postgresql_host}:5432/pe-puppetdb?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem&sslkey=/etc/puppetlabs/puppetdb/ssl/${target.peadm::certname()}.private_key.pk8&sslcert=/etc/puppetlabs/puppetdb/ssl/${$target.peadm::certname()}.cert.pem" } - # Introduced new dependency for PEADM to enable modification of INI files + $db_setting = "//${db}:5432/pe-puppetdb?ssl=true&sslfactory=org.postgresql.ssl.jdbc4.LibPQFactory&sslmode=verify-full&sslrootcert=/etc/puppetlabs/puppet/ssl/certs/ca.pem&sslkey=/etc/puppetlabs/puppetdb/ssl/${target.peadm::certname()}.private_key.pk8&sslcert=/etc/puppetlabs/puppetdb/ssl/${$target.peadm::certname()}.cert.pem" + + # Introduces dependency so PEADM can modify INI files apply($target) { ini_setting { 'database_setting': ensure => present, diff --git a/spec/plans/add_replica_spec.rb b/spec/plans/add_replica_spec.rb index b6265f32..a903fbfe 100644 --- a/spec/plans/add_replica_spec.rb +++ b/spec/plans/add_replica_spec.rb @@ -11,33 +11,59 @@ def allow_standard_non_returning_calls describe 'basic functionality' do let(:params) { { 'primary_host' => 'primary', 'replica_host' => 'replica' } } - let(:certdata) { { 'certname' => 'primary', 'extensions' => { '1.3.6.1.4.1.34380.1.1.9813' => 'A' } } } + let(:cfg) { { 'params' => { 'primary_host' => 'primary' } } } + let(:certdata) do + { + 'certificate-exists' => true, + 'certname' => 'primary', + 'extensions' => { '1.3.6.1.4.1.34380.1.1.9813' => 'A' }, + 'dns-alt-names' => [] + } + end + let(:certstatus) do + { + 'certificate-status' => 'valid', + 'reason' => 'Expires - 2099-01-01 00:00:00 UTC' + } + end - it 'runs successfully when the primary doesn\'t have alt-names' do + it 'runs successfully when the primary does not have alt-names' do allow_standard_non_returning_calls - expect_task('peadm::cert_data').always_return(certdata) + expect_task('peadm::get_peadm_config').always_return(cfg) + expect_task('peadm::cert_data').always_return(certdata).be_called_times(4) + expect_task('peadm::cert_valid_status').always_return(certstatus) + expect_task('package').always_return({ 'status' => 'uninstalled' }) expect_task('peadm::agent_install') .with_params({ 'server' => 'primary', 'install_flags' => [ + 'main:dns_alt_names=replica', '--puppet-service-ensure', 'stopped', - 'main:certname=replica', - 'main:dns_alt_names=replica' + 'main:certname=replica' ] }) + expect_plan('peadm::util::sync_global_hiera') + expect_out_verbose.with_params('Current config is...') + expect_out_verbose.with_params('Updating classification to...') expect(run_plan('peadm::add_replica', params)).to be_ok end it 'runs successfully when the primary has alt-names' do allow_standard_non_returning_calls - expect_task('peadm::cert_data').always_return(certdata.merge({ 'dns-alt-names' => ['primary', 'alt'] })) + expect_task('peadm::get_peadm_config').always_return(cfg) + expect_task('peadm::cert_data').always_return(certdata.merge({ 'dns-alt-names' => ['primary', 'alt'] })).be_called_times(4) + expect_task('peadm::cert_valid_status').always_return(certstatus) + expect_task('package').always_return({ 'status' => 'uninstalled' }) expect_task('peadm::agent_install') .with_params({ 'server' => 'primary', 'install_flags' => [ + 'main:dns_alt_names=replica,alt', '--puppet-service-ensure', 'stopped', - 'main:certname=replica', - 'main:dns_alt_names=replica,alt' + 'main:certname=replica' ] }) + expect_plan('peadm::util::sync_global_hiera') + expect_out_verbose.with_params('Current config is...') + expect_out_verbose.with_params('Updating classification to...') expect(run_plan('peadm::add_replica', params)).to be_ok end end diff --git a/tasks/cert_valid_status.json b/tasks/cert_valid_status.json new file mode 100644 index 00000000..33483d8c --- /dev/null +++ b/tasks/cert_valid_status.json @@ -0,0 +1,13 @@ +{ + "description": "Check primary for valid state of a certificate", + "parameters": { + "certname": { + "type": "String", + "description": "The certifcate name to check validation of" + } + }, + "input_method": "stdin", + "implementations": [ + {"name": "cert_valid_status.rb"} + ] +} diff --git a/tasks/cert_valid_status.rb b/tasks/cert_valid_status.rb new file mode 100755 index 00000000..d7520757 --- /dev/null +++ b/tasks/cert_valid_status.rb @@ -0,0 +1,30 @@ +#!/opt/puppetlabs/puppet/bin/ruby +# frozen_string_literal: true + +require 'puppet' +require 'json' + +params = JSON.parse(STDIN.read) + +Puppet.initialize_settings + +Puppet.settings.use(:agent, :server, :master, :main) + +begin + cert_provider = Puppet::X509::CertProvider.new + ssl_provider = Puppet::SSL::SSLProvider.new + password = cert_provider.load_private_key_password + ssl_context = ssl_provider.load_context(certname: params['certname'], password: password) +rescue Puppet::SSL::CertVerifyError => e + status = { 'certificate-status' => 'invalid', 'reason' => e.message } +rescue Puppet::Error => e + status = { 'certificate-status' => 'unknown', 'reason' => e.message } +else + cert = ssl_context.client_chain.first + status = { 'certificate-status' => 'valid', 'reason' => "Expires - #{cert.not_after}" } +end + +result = status + +# Put the result to stdout +puts result.to_json