diff --git a/REFERENCE.md b/REFERENCE.md
index 6cae62a6..9f897504 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -60,6 +60,7 @@
* [`backup_classification`](#backup_classification): A task to call the classification api and write to file
* [`cert_data`](#cert_data): Return certificate data related to the Puppet agent
* [`cert_valid_status`](#cert_valid_status): Check primary for valid state of a certificate
+* [`check_pe_master_rules`](#check_pe_master_rules): Checks if the PE Master group rules have already been updated to support 'pe_compiler_legacy' as a pp_auth_role
* [`classify_compilers`](#classify_compilers): Classify compilers as legacy or non-legacy
* [`code_manager`](#code_manager): Perform various code manager actions
* [`code_manager_enabled`](#code_manager_enabled): Run on a PE primary node to check if Code Manager is enabled.
@@ -74,6 +75,7 @@
* [`infrastatus`](#infrastatus): Runs puppet infra status and returns the output
* [`mkdir_p_file`](#mkdir_p_file): Create a file with the specified content at the specified location
* [`mv`](#mv): Wrapper task for mv command
+* [`node_group_unpin`](#node_group_unpin): Unpins nodes from a specified PE node group
* [`os_identification`](#os_identification): Return the operating system runnin gon the target as a string
* [`pe_install`](#pe_install): Install Puppet Enterprise from a tarball
* [`pe_ldap_config`](#pe_ldap_config): Set the ldap config in the PE console
@@ -90,6 +92,7 @@
* [`ssl_clean`](#ssl_clean): Clean an agent's certificate
* [`submit_csr`](#submit_csr): Submit a certificate signing request
* [`transform_classification_groups`](#transform_classification_groups): Transform the user groups from a source backup to a list of groups on the target server
+* [`update_pe_master_rules`](#update_pe_master_rules): Updates the PE Master group rules to support 'pe_compiler_legacy' as a pp_auth_role
* [`validate_rbac_token`](#validate_rbac_token): Check an RBAC token stored in a file is valid
* [`wait_until_service_ready`](#wait_until_service_ready): Return when the orchestrator service is healthy, or timeout after 15 seconds
@@ -129,7 +132,6 @@ Supported use cases:
* `peadm::subplans::modify_certificate`
* `peadm::subplans::prepare_agent`
* `peadm::uninstall`: Single-entry-point plan for uninstalling Puppet Enterprise
-* `peadm::update_compiler_extensions`
* `peadm::util::code_sync_status`
* `peadm::util::copy_file`
* `peadm::util::db_disable_pglogical`
@@ -1110,6 +1112,12 @@ Data type: `String`
The certifcate name to check validation of
+### `check_pe_master_rules`
+
+Checks if the PE Master group rules have already been updated to support 'pe_compiler_legacy' as a pp_auth_role
+
+**Supports noop?** false
+
### `classify_compilers`
Classify compilers as legacy or non-legacy
@@ -1326,6 +1334,26 @@ Data type: `String`
New path of file
+### `node_group_unpin`
+
+Unpins nodes from a specified PE node group
+
+**Supports noop?** false
+
+#### Parameters
+
+##### `node_certnames`
+
+Data type: `Array[String]`
+
+The certnames of the nodes to unpin
+
+##### `group_name`
+
+Data type: `String`
+
+The name of the node group to unpin the nodes from
+
### `os_identification`
Return the operating system runnin gon the target as a string
@@ -1622,6 +1650,12 @@ Data type: `String`
Location of target node group yaml file and where to create the transformed file
+### `update_pe_master_rules`
+
+Updates the PE Master group rules to support 'pe_compiler_legacy' as a pp_auth_role
+
+**Supports noop?** false
+
### `validate_rbac_token`
Check an RBAC token stored in a file is valid
diff --git a/manifests/setup/legacy_compiler_group.pp b/manifests/setup/legacy_compiler_group.pp
index 0fb161e4..4eff95b6 100644
--- a/manifests/setup/legacy_compiler_group.pp
+++ b/manifests/setup/legacy_compiler_group.pp
@@ -1,20 +1,19 @@
# @api private
class peadm::setup::legacy_compiler_group (
String[1] $primary_host,
- Optional[String] $internal_compiler_a_pool_address = undef,
- Optional[String] $internal_compiler_b_pool_address = undef,
+ Optional[String] $internal_compiler_a_pool_address = undef,
+ Optional[String] $internal_compiler_b_pool_address = undef,
) {
Node_group {
purge_behavior => none,
}
node_group { 'PE Legacy Compiler':
- parent => 'PE Master',
- rule => ['and',
- ['=', ['trusted', 'extensions', peadm::oid('peadm_legacy_compiler')], 'true'],
- ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler'],
- ],
- classes => {
+ ensure => 'present',
+ parent => 'PE Master',
+ purge_behavior => 'rule',
+ rule => ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler_legacy'],
+ classes => {
'puppet_enterprise::profile::master' => {
'puppetdb_host' => [$internal_compiler_a_pool_address, $internal_compiler_b_pool_address].filter |$_| { $_ },
'puppetdb_port' => [8081],
@@ -23,21 +22,20 @@
}
node_group { 'PE Legacy Compiler Group A':
- ensure => 'present',
- parent => 'PE Legacy Compiler',
- rule => ['and',
- ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler'],
+ ensure => 'present',
+ parent => 'PE Legacy Compiler',
+ purge_behavior => 'rule',
+ rule => ['and',
+ ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler_legacy'],
['=', ['trusted', 'extensions', peadm::oid('peadm_availability_group')], 'A'],
- ['=', ['trusted', 'extensions', peadm::oid('peadm_legacy_compiler')], 'true'],
],
- classes => {
- 'puppet_enterprise::profile::master' => {
+ classes => {
+ 'puppet_enterprise::profile::master' => {
'puppetdb_host' => [$internal_compiler_b_pool_address, $internal_compiler_a_pool_address].filter |$_| { $_ },
'puppetdb_port' => [8081],
},
},
- data => {
- # Workaround for GH-118
+ data => {
'puppet_enterprise::profile::master::puppetdb' => {
'ha_enabled_replicas' => [],
},
@@ -45,21 +43,20 @@
}
node_group { 'PE Legacy Compiler Group B':
- ensure => 'present',
- parent => 'PE Legacy Compiler',
- rule => ['and',
- ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler'],
+ ensure => 'present',
+ parent => 'PE Legacy Compiler',
+ purge_behavior => 'rule',
+ rule => ['and',
+ ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler_legacy'],
['=', ['trusted', 'extensions', peadm::oid('peadm_availability_group')], 'B'],
- ['=', ['trusted', 'extensions', peadm::oid('peadm_legacy_compiler')], 'true'],
],
- classes => {
- 'puppet_enterprise::profile::master' => {
+ classes => {
+ 'puppet_enterprise::profile::master' => {
'puppetdb_host' => [$internal_compiler_a_pool_address, $internal_compiler_b_pool_address].filter |$_| { $_ },
'puppetdb_port' => [8081],
},
},
- data => {
- # Workaround for GH-118
+ data => {
'puppet_enterprise::profile::master::puppetdb' => {
'ha_enabled_replicas' => [],
},
@@ -67,6 +64,8 @@
}
node_group { 'PE Compiler':
- rule => ['and', ['=', ['trusted', 'extensions', peadm::oid('peadm_legacy_compiler')], 'false']],
+ parent => 'PE Master',
+ purge_behavior => 'rule',
+ rule => ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler'],
}
}
diff --git a/manifests/setup/node_manager.pp b/manifests/setup/node_manager.pp
index f74cb217..9fcc336b 100644
--- a/manifests/setup/node_manager.pp
+++ b/manifests/setup/node_manager.pp
@@ -81,8 +81,9 @@
# PE Compiler group comes from default PE and already has the pe compiler role
node_group { 'PE Compiler':
- parent => 'PE Master',
- rule => ['and', ['=', ['trusted', 'extensions', peadm::oid('peadm_legacy_compiler')], 'false']],
+ parent => 'PE Master',
+ purge_behavior => 'rule',
+ rule => ['and', ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler']],
}
# This group should pin the primary, and also map to any pe-postgresql nodes
@@ -116,14 +117,14 @@
# Configure the A pool for compilers. There are up to two pools for DR, each
# having an affinity for one "availability zone" or the other.
node_group { 'PE Compiler Group A':
- ensure => 'present',
- parent => 'PE Compiler',
- rule => ['and',
+ ensure => 'present',
+ purge_behavior => 'rule',
+ parent => 'PE Compiler',
+ rule => ['and',
['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler'],
['=', ['trusted', 'extensions', peadm::oid('peadm_availability_group')], 'A'],
- ['=', ['trusted', 'extensions', peadm::oid('peadm_legacy_compiler')], 'false'],
],
- classes => {
+ classes => {
'puppet_enterprise::profile::puppetdb' => {
'database_host' => pick($postgresql_a_host, $notconf),
},
@@ -134,7 +135,7 @@
'puppetdb_port' => [8081],
},
},
- data => {
+ data => {
# Workaround for GH-118
'puppet_enterprise::profile::master::puppetdb' => {
'ha_enabled_replicas' => [],
@@ -175,14 +176,14 @@
}
node_group { 'PE Compiler Group B':
- ensure => 'present',
- parent => 'PE Compiler',
- rule => ['and',
+ ensure => 'present',
+ purge_behavior => 'rule',
+ parent => 'PE Compiler',
+ rule => ['and',
['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler'],
['=', ['trusted', 'extensions', peadm::oid('peadm_availability_group')], 'B'],
- ['=', ['trusted', 'extensions', peadm::oid('peadm_legacy_compiler')], 'false'],
],
- classes => {
+ classes => {
'puppet_enterprise::profile::puppetdb' => {
'database_host' => pick($postgresql_b_host, $notconf),
},
@@ -193,7 +194,7 @@
'puppetdb_port' => [8081],
},
},
- data => {
+ data => {
# Workaround for GH-118
'puppet_enterprise::profile::master::puppetdb' => {
'ha_enabled_replicas' => [],
@@ -202,12 +203,10 @@
}
node_group { 'PE Legacy Compiler':
- parent => 'PE Master',
- rule => ['and',
- ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler'],
- ['=', ['trusted', 'extensions', peadm::oid('peadm_legacy_compiler')], 'true'],
- ],
- classes => {
+ parent => 'PE Master',
+ purge_behavior => 'rule',
+ rule => ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler_legacy'],
+ classes => {
'puppet_enterprise::profile::master' => {
'puppetdb_host' => [$internal_compiler_a_pool_address, $internal_compiler_b_pool_address].filter |$_| { $_ },
'puppetdb_port' => [8081],
@@ -218,20 +217,20 @@
# Configure the A pool for legacy compilers. There are up to two pools for DR, each
# having an affinity for one "availability zone" or the other.
node_group { 'PE Legacy Compiler Group A':
- ensure => 'present',
- parent => 'PE Legacy Compiler',
- rule => ['and',
- ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler'],
+ ensure => 'present',
+ parent => 'PE Legacy Compiler',
+ purge_behavior => 'rule',
+ rule => ['and',
+ ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler_legacy'],
['=', ['trusted', 'extensions', peadm::oid('peadm_availability_group')], 'A'],
- ['=', ['trusted', 'extensions', peadm::oid('peadm_legacy_compiler')], 'true'],
],
- classes => {
+ classes => {
'puppet_enterprise::profile::master' => {
'puppetdb_host' => [$internal_compiler_b_pool_address, $internal_compiler_a_pool_address].filter |$_| { $_ },
'puppetdb_port' => [8081],
},
},
- data => {
+ data => {
# Workaround for GH-118
'puppet_enterprise::profile::master::puppetdb' => {
'ha_enabled_replicas' => [],
@@ -242,20 +241,20 @@
# Configure the B pool for legacy compilers. There are up to two pools for DR, each
# having an affinity for one "availability zone" or the other.
node_group { 'PE Legacy Compiler Group B':
- ensure => 'present',
- parent => 'PE Legacy Compiler',
- rule => ['and',
- ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler'],
+ ensure => 'present',
+ parent => 'PE Legacy Compiler',
+ purge_behavior => 'rule',
+ rule => ['and',
+ ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler_legacy'],
['=', ['trusted', 'extensions', peadm::oid('peadm_availability_group')], 'B'],
- ['=', ['trusted', 'extensions', peadm::oid('peadm_legacy_compiler')], 'true'],
],
- classes => {
+ classes => {
'puppet_enterprise::profile::master' => {
'puppetdb_host' => [$internal_compiler_a_pool_address, $internal_compiler_a_pool_address].filter |$_| { $_ },
'puppetdb_port' => [8081],
},
},
- data => {
+ data => {
# Workaround for GH-118
'puppet_enterprise::profile::master::puppetdb' => {
'ha_enabled_replicas' => [],
diff --git a/plans/add_compilers.pp b/plans/add_compilers.pp
index 879d9fb2..a57e3c39 100644
--- a/plans/add_compilers.pp
+++ b/plans/add_compilers.pp
@@ -16,6 +16,12 @@
$compiler_targets = peadm::get_targets($compiler_hosts)
$primary_target = peadm::get_targets($primary_host, 1)
+ # Check if PE Master rules have been updated to support pe_compiler_legacy
+ $rules_check = run_task('peadm::check_pe_master_rules', $primary_host).first.value
+ unless $rules_check['updated'] {
+ fail_plan('Please run the Convert plan to convert your Puppet infrastructure to be managed by this version of PEADM.')
+ }
+
# Get current peadm config to determine where to setup additional rules for
# compiler's secondary PuppetDB instances
$peadm_config = run_task('peadm::get_peadm_config', $primary_target).first.value
diff --git a/plans/convert.pp b/plans/convert.pp
index 1ff1771a..585575b1 100644
--- a/plans/convert.pp
+++ b/plans/convert.pp
@@ -60,6 +60,48 @@
out::message('# Gathering information')
+ $cert_extensions_temp = run_task('peadm::cert_data', $all_targets).reduce({}) |$memo,$result| {
+ $memo + { $result.target.peadm::certname() => $result['extensions'] }
+ }
+
+ # Add legacy compiler role to compilers that are missing it
+ $compilers_with_legacy_compiler_flag = $cert_extensions_temp.filter |$name, $exts| {
+ ($name in $compiler_targets.map |$t| { $t.name } or $name in $legacy_compiler_targets.map |$t| { $t.name }) and
+ $exts and $exts[peadm::oid('peadm_legacy_compiler')] != undef
+ }
+
+ if $compilers_with_legacy_compiler_flag.size > 0 {
+ $legacy_compilers_with_flag = $compilers_with_legacy_compiler_flag.filter |$name,$exts| {
+ $exts[peadm::oid('peadm_legacy_compiler')] == 'true'
+ }.keys
+
+ $modern_compilers_with_flag = $compilers_with_legacy_compiler_flag.filter |$name,$exts| {
+ $exts[peadm::oid('peadm_legacy_compiler')] == 'false'
+ }.keys
+
+ if $modern_compilers_with_flag.size > 0 {
+ run_plan('peadm::modify_certificate', $modern_compilers_with_flag,
+ primary_host => $primary_target,
+ remove_extensions => [peadm::oid('peadm_legacy_compiler')],
+ )
+ }
+
+ if $legacy_compilers_with_flag.size > 0 {
+ run_plan('peadm::modify_certificate', $legacy_compilers_with_flag,
+ primary_host => $primary_target,
+ add_extensions => {
+ 'pp_auth_role' => 'pe_compiler_legacy',
+ },
+ remove_extensions => [peadm::oid('peadm_legacy_compiler'), peadm::oid('pp_auth_role')],
+ )
+ }
+
+ run_task('peadm::puppet_runonce', peadm::flatten_compact([
+ $compiler_targets,
+ $legacy_compiler_targets,
+ ]))
+ }
+
# Get trusted fact information for all compilers. Use peadm::certname() as
# the hash key because the apply block below will break trying to parse the
# $compiler_extensions variable if it has Target-type hash keys.
@@ -214,7 +256,6 @@
add_extensions => {
peadm::oid('pp_auth_role') => 'pe_compiler',
peadm::oid('peadm_availability_group') => 'A',
- peadm::oid('peadm_legacy_compiler') => 'false',
},
)
},
@@ -224,7 +265,6 @@
add_extensions => {
peadm::oid('pp_auth_role') => 'pe_compiler',
peadm::oid('peadm_availability_group') => 'B',
- peadm::oid('peadm_legacy_compiler') => 'false',
},
)
},
@@ -232,9 +272,8 @@
run_plan('peadm::modify_certificate', $legacy_compiler_a_targets,
primary_host => $primary_target,
add_extensions => {
- peadm::oid('pp_auth_role') => 'pe_compiler',
+ peadm::oid('pp_auth_role') => 'pe_compiler_legacy',
peadm::oid('peadm_availability_group') => 'A',
- peadm::oid('peadm_legacy_compiler') => 'true',
},
)
},
@@ -242,9 +281,8 @@
run_plan('peadm::modify_certificate', $legacy_compiler_b_targets,
primary_host => $primary_target,
add_extensions => {
- peadm::oid('pp_auth_role') => 'pe_compiler',
+ peadm::oid('pp_auth_role') => 'pe_compiler_legacy',
peadm::oid('peadm_availability_group') => 'B',
- peadm::oid('peadm_legacy_compiler') => 'true',
},
)
},
@@ -283,6 +321,14 @@
include peadm::setup::convert_node_manager
}
+
+ # Unpin legacy compilers from PE Master group
+ if $legacy_compiler_targets {
+ run_task('peadm::node_group_unpin', $primary_target,
+ node_certnames => $legacy_compiler_targets.map |$target| { $target.peadm::certname() },
+ group_name => 'PE Master',
+ )
+ }
}
else {
# lint:ignore:strict_indent
@@ -314,6 +360,9 @@
run_command('systemctl restart pe-puppetserver.service pe-puppetdb.service', $compiler_targets)
}
+ # Update PE Master rules to support legacy compilers
+ run_task('peadm::update_pe_master_rules', $primary_target)
+
# Run puppet on all targets again to ensure everything is fully up-to-date
run_task('peadm::puppet_runonce', $all_targets)
}
diff --git a/plans/convert_compiler_to_legacy.pp b/plans/convert_compiler_to_legacy.pp
index c75924bd..c612684b 100644
--- a/plans/convert_compiler_to_legacy.pp
+++ b/plans/convert_compiler_to_legacy.pp
@@ -7,6 +7,12 @@
$primary_target = peadm::get_targets($primary_host, 1)
$convert_legacy_compiler_targets = peadm::get_targets($legacy_hosts)
+ # Check if PE Master rules have been updated to support pe_compiler_legacy
+ $rules_check = run_task('peadm::check_pe_master_rules', $primary_target).first.value
+ unless $rules_check['updated'] {
+ fail_plan('Please run the Convert plan to convert your Puppet infrastructure to be managed by this version of PEADM.')
+ }
+
$cluster = run_task('peadm::get_peadm_config', $primary_host).first.value
$error = getvar('cluster.error')
if $error {
@@ -102,7 +108,7 @@
run_plan('peadm::modify_certificate', $compiler_targets,
primary_host => $primary_target,
add_extensions => {
- peadm::oid('peadm_legacy_compiler') => 'false',
+ peadm::oid('pp_auth_role') => 'pe_compiler_legacy',
},
)
},
@@ -110,9 +116,8 @@
run_plan('peadm::modify_certificate', $legacy_compiler_a_targets,
primary_host => $primary_target,
add_extensions => {
- peadm::oid('pp_auth_role') => 'pe_compiler',
+ peadm::oid('pp_auth_role') => 'pe_compiler_legacy',
peadm::oid('peadm_availability_group') => 'A',
- peadm::oid('peadm_legacy_compiler') => 'true',
},
)
},
@@ -120,9 +125,8 @@
run_plan('peadm::modify_certificate', $legacy_compiler_b_targets,
primary_host => $primary_target,
add_extensions => {
- peadm::oid('pp_auth_role') => 'pe_compiler',
+ peadm::oid('pp_auth_role') => 'pe_compiler_legacy',
peadm::oid('peadm_availability_group') => 'B',
- peadm::oid('peadm_legacy_compiler') => 'true',
},
)
},
diff --git a/plans/subplans/component_install.pp b/plans/subplans/component_install.pp
index c117ccb6..2fad74e8 100644
--- a/plans/subplans/component_install.pp
+++ b/plans/subplans/component_install.pp
@@ -21,13 +21,11 @@
$certificate_extensions = {
peadm::oid('pp_auth_role') => 'pe_compiler',
peadm::oid('peadm_availability_group') => $avail_group_letter,
- peadm::oid('peadm_legacy_compiler') => false,
}
} elsif $role == 'pe_compiler_legacy' {
$certificate_extensions = {
- peadm::oid('pp_auth_role') => 'pe_compiler',
+ peadm::oid('pp_auth_role') => 'pe_compiler_legacy',
peadm::oid('peadm_availability_group') => $avail_group_letter,
- peadm::oid('peadm_legacy_compiler') => true,
}
} else {
$certificate_extensions = {
diff --git a/plans/subplans/configure.pp b/plans/subplans/configure.pp
index f2ebada6..6cc6e1d7 100644
--- a/plans/subplans/configure.pp
+++ b/plans/subplans/configure.pp
@@ -174,5 +174,9 @@
$legacy_compiler_targets,
]))
+ # Update PE Master rules to support legacy compilers
+ run_task('peadm::update_pe_master_rules', $primary_host)
+ run_task('peadm::puppet_runonce', $legacy_compiler_targets)
+
return("Configuration of Puppet Enterprise ${arch['architecture']} succeeded.")
}
diff --git a/plans/subplans/install.pp b/plans/subplans/install.pp
index 693c056c..96af47f0 100644
--- a/plans/subplans/install.pp
+++ b/plans/subplans/install.pp
@@ -287,7 +287,6 @@
extension_requests => {
peadm::oid('pp_auth_role') => 'pe_compiler',
peadm::oid('peadm_availability_group') => 'A',
- peadm::oid('peadm_legacy_compiler') => 'false',
}
)
},
@@ -296,25 +295,22 @@
extension_requests => {
peadm::oid('pp_auth_role') => 'pe_compiler',
peadm::oid('peadm_availability_group') => 'B',
- peadm::oid('peadm_legacy_compiler') => 'false',
}
)
},
background('compiler-a-csr.yaml') || {
run_plan('peadm::util::insert_csr_extension_requests', $legacy_a_targets,
extension_requests => {
- peadm::oid('pp_auth_role') => 'pe_compiler',
+ peadm::oid('pp_auth_role') => 'pe_compiler_legacy',
peadm::oid('peadm_availability_group') => 'A',
- peadm::oid('peadm_legacy_compiler') => 'true',
}
)
},
background('compiler-b-csr.yaml') || {
run_plan('peadm::util::insert_csr_extension_requests', $legacy_b_targets,
extension_requests => {
- peadm::oid('pp_auth_role') => 'pe_compiler',
+ peadm::oid('pp_auth_role') => 'pe_compiler_legacy',
peadm::oid('peadm_availability_group') => 'B',
- peadm::oid('peadm_legacy_compiler') => 'true',
}
)
},
diff --git a/plans/update_compiler_extensions.pp b/plans/update_compiler_extensions.pp
deleted file mode 100644
index 784f919e..00000000
--- a/plans/update_compiler_extensions.pp
+++ /dev/null
@@ -1,25 +0,0 @@
-# @api private
-plan peadm::update_compiler_extensions (
- TargetSpec $compiler_hosts,
- Peadm::SingleTargetSpec $primary_host,
- Boolean $legacy = false,
-) {
- $primary_target = peadm::get_targets($primary_host, 1)
- $host_targets = peadm::get_targets($compiler_hosts)
-
- run_plan('peadm::modify_certificate', $host_targets,
- primary_host => $primary_target,
- add_extensions => { peadm::oid('peadm_legacy_compiler') => String($legacy) },
- )
-
- run_task('peadm::puppet_runonce', $primary_target)
- run_task('peadm::puppet_runonce', $host_targets)
-
- if $legacy {
- run_command('systemctl restart pe-puppetserver.service', $host_targets)
- } else {
- run_command('systemctl restart pe-puppetserver.service pe-puppetdb.service', $host_targets)
- }
-
- return("Added legacy cert with value ${legacy} to compiler hosts ${compiler_hosts}")
-}
diff --git a/plans/upgrade.pp b/plans/upgrade.pp
index 9b8a4116..5fe36cc4 100644
--- a/plans/upgrade.pp
+++ b/plans/upgrade.pp
@@ -135,22 +135,11 @@
peadm::assert_supported_pe_version($_version, $permit_unsafe_versions)
- # Gather certificate extension information from all systems
- $cert_extensions_temp = run_task('peadm::cert_data', $all_targets).reduce({}) |$memo,$result| {
- $memo + { $result.target.peadm::certname => $result['extensions'] }
+ $rules_check = run_task('peadm::check_pe_master_rules', $primary_target).first.value
+ unless $rules_check['updated'] {
+ fail_plan('Please run the Convert plan to convert your Puppet infrastructure to be managed by this version of PEADM.')
}
- $compiler_missing_legacy_targets = $cert_extensions_temp.filter |$name,$exts| {
- ($name in $compiler_targets.map |$t| { $t.name }) and (peadm::oid('peadm_legacy_compiler') in $exts and $exts[peadm::oid('peadm_legacy_compiler')] == undef)
- }.keys
-
- run_plan('peadm::modify_certificate', $compiler_missing_legacy_targets,
- primary_host => $primary_target,
- add_extensions => {
- peadm::oid('peadm_legacy_compiler') => 'false',
- },
- )
-
# Gather certificate extension information from all systems
$cert_extensions = run_task('peadm::cert_data', $all_targets).reduce({}) |$memo,$result| {
$memo + { $result.target.peadm::certname => $result['extensions'] }
@@ -188,8 +177,8 @@
$compiler_m1_nonlegacy_targets = $compiler_targets.filter |$target| {
($cert_extensions.dig($target.peadm::certname, peadm::oid('peadm_availability_group'))
== $cert_extensions.dig($primary_target[0].peadm::certname, peadm::oid('peadm_availability_group'))) and
- ($cert_extensions.dig($target.peadm::certname, peadm::oid('peadm_legacy_compiler'))
- == 'false')
+ ($cert_extensions.dig($target.peadm::certname, peadm::oid('pp_auth_role'))
+ == 'pe_compiler')
}
$compiler_m2_targets = $compiler_targets.filter |$target| {
@@ -200,8 +189,8 @@
$compiler_m2_nonlegacy_targets = $compiler_targets.filter |$target| {
($cert_extensions.dig($target.peadm::certname, peadm::oid('peadm_availability_group'))
== $cert_extensions.dig($replica_target[0].peadm::certname, peadm::oid('peadm_availability_group'))) and
- ($cert_extensions.dig($target.peadm::certname, peadm::oid('peadm_legacy_compiler'))
- == 'false')
+ ($cert_extensions.dig($target.peadm::certname, peadm::oid('pp_auth_role'))
+ == 'pe_compiler')
}
peadm::plan_step('preparation') || {
diff --git a/spec/plans/add_compilers_spec.rb b/spec/plans/add_compilers_spec.rb
index 9dbb4f94..76acfa1f 100644
--- a/spec/plans/add_compilers_spec.rb
+++ b/spec/plans/add_compilers_spec.rb
@@ -47,10 +47,18 @@ def allow_standard_non_returning_calls
}
end
+ let(:pe_rule_check) do
+ {
+ 'updated' => 'true',
+ 'message' => 'a message'
+ }
+ end
+
it 'runs successfully when no alt-names are specified' do
allow_standard_non_returning_calls
expect_task('peadm::get_peadm_config').always_return(cfg)
+ expect_task('peadm::check_pe_master_rules').always_return(pe_rule_check)
expect_task('peadm::get_psql_version').with_targets(['server_a'])
expect_plan('peadm::subplans::component_install')
@@ -65,6 +73,7 @@ def allow_standard_non_returning_calls
cfg['role-letter']['server']['B'] = 'server_b'
expect_task('peadm::get_peadm_config').always_return(cfg)
+ expect_task('peadm::check_pe_master_rules').always_return(pe_rule_check)
expect_task('peadm::get_psql_version').with_targets(['server_b'])
expect_plan('peadm::subplans::component_install')
@@ -79,6 +88,7 @@ def allow_standard_non_returning_calls
allow_standard_non_returning_calls
expect_task('peadm::get_peadm_config').always_return(cfg)
+ expect_task('peadm::check_pe_master_rules').always_return(pe_rule_check)
expect_task('peadm::get_psql_version').with_targets(['custom_postgresql'])
expect_plan('peadm::subplans::component_install')
@@ -94,6 +104,7 @@ def allow_standard_non_returning_calls
cfg['params']['replica_postgresql_host'] = 'external_postgresql'
expect_task('peadm::get_peadm_config').always_return(cfg)
+ expect_task('peadm::check_pe_master_rules').always_return(pe_rule_check)
expect_task('peadm::get_psql_version').with_targets(['external_postgresql'])
expect_plan('peadm::subplans::component_install')
@@ -109,6 +120,7 @@ def allow_standard_non_returning_calls
cfg['role-letter']['server']['B'] = 'replica'
expect_task('peadm::get_peadm_config').always_return(cfg)
+ expect_task('peadm::check_pe_master_rules').always_return(pe_rule_check)
expect_task('peadm::get_psql_version').with_targets(['external_postgresql'])
expect_plan('peadm::subplans::component_install')
@@ -124,6 +136,7 @@ def allow_standard_non_returning_calls
cfg['params']['replica_postgresql_host'] = 'replica_external_postgresql'
expect_task('peadm::get_peadm_config').always_return(cfg)
+ expect_task('peadm::check_pe_master_rules').always_return(pe_rule_check)
expect_task('peadm::get_psql_version').with_targets(['replica_external_postgresql'])
expect_plan('peadm::subplans::component_install')
diff --git a/spec/plans/convert_spec.rb b/spec/plans/convert_spec.rb
index 39ec7367..99e453a4 100644
--- a/spec/plans/convert_spec.rb
+++ b/spec/plans/convert_spec.rb
@@ -9,7 +9,7 @@
end
let(:params) do
- { 'primary_host' => 'primary' }
+ { 'primary_host' => 'primary', 'legacy_compilers' => ['pe_compiler_legacy'] }
end
it 'single primary no dr valid' do
@@ -18,9 +18,11 @@
allow_any_task
allow_apply
- expect_task('peadm::cert_data').return_for_targets('primary' => trustedjson)
+ expect_task('peadm::cert_data').return_for_targets('primary' => trustedjson).be_called_times(2)
expect_task('peadm::read_file').always_return({ 'content' => '2021.7.9' })
expect_task('peadm::get_group_rules').return_for_targets('primary' => { '_output' => '{"rules": []}' })
+ expect_task('peadm::node_group_unpin').with_targets('primary').with_params({ 'node_certnames' => ['pe_compiler_legacy'], 'group_name' => 'PE Master' })
+ expect_task('peadm::check_legacy_compilers').with_targets('primary').with_params({ 'legacy_compilers' => 'pe_compiler_legacy' }).return_for_targets('primary' => { '_output' => '' })
# For some reason, expect_plan() was not working??
allow_plan('peadm::modify_certificate').always_return({})
diff --git a/spec/plans/upgrade_spec.rb b/spec/plans/upgrade_spec.rb
index 41852f01..b92b828f 100644
--- a/spec/plans/upgrade_spec.rb
+++ b/spec/plans/upgrade_spec.rb
@@ -20,6 +20,13 @@ def allow_standard_non_returning_calls
JSON.parse File.read(File.expand_path(File.join(fixtures, 'plans', 'trusted-compiler.json')))
end
+ let(:pe_rule_check) do
+ {
+ 'updated' => 'true',
+ 'message' => 'a message'
+ }
+ end
+
it 'minimum variables to run' do
allow_standard_non_returning_calls
expect_task('peadm::get_group_rules').return_for_targets('primary' => { '_output' => '{"rules": []}' })
@@ -28,7 +35,8 @@ def allow_standard_non_returning_calls
.with_params('path' => '/opt/puppetlabs/server/pe_build')
.always_return({ 'content' => '2021.7.3' })
- expect_task('peadm::cert_data').return_for_targets('primary' => trusted_primary).be_called_times(2)
+ expect_task('peadm::cert_data').return_for_targets('primary' => trusted_primary).be_called_times(1)
+ expect_task('peadm::check_pe_master_rules').always_return(pe_rule_check)
expect(run_plan('peadm::upgrade',
'primary_host' => 'primary',
@@ -44,7 +52,8 @@ def allow_standard_non_returning_calls
.always_return({ 'content' => '2021.7.3' })
expect_task('peadm::cert_data').return_for_targets('primary' => trusted_primary,
- 'compiler' => trusted_compiler).be_called_times(2)
+ 'compiler' => trusted_compiler).be_called_times(1)
+ expect_task('peadm::check_pe_master_rules').always_return(pe_rule_check).be_called_times(1)
expect(run_plan('peadm::upgrade',
'primary_host' => 'primary',
@@ -93,7 +102,7 @@ def allow_standard_non_returning_calls
.with_params('path' => '/opt/puppetlabs/server/pe_build')
.always_return({ 'content' => installed_version })
- expect_task('peadm::cert_data').return_for_targets('primary' => trusted_primary).be_called_times(2)
+ expect_task('peadm::cert_data').return_for_targets('primary' => trusted_primary).be_called_times(1)
expect_task('peadm::get_group_rules').return_for_targets('primary' => { '_output' => '{"rules": []}' })
end
@@ -109,6 +118,7 @@ def allow_standard_non_returning_calls
# uploading runs afoul of the fact that write_file creates a source tempfile,
# and we can't expect_upload() because we don't have the tempfile name.
allow_any_upload
+ expect_task('peadm::check_pe_master_rules').always_return(pe_rule_check)
expect(run_plan('peadm::upgrade',
'primary_host' => 'primary',
@@ -120,6 +130,7 @@ def allow_standard_non_returning_calls
it 'warns if upgrading to 2023.3+ from 2023.0- without r10k_known_hosts set' do
# This is fairly horrible, but expect_out_message doesn't take a regex.
expect_out_message.with_params(r10k_warning)
+ expect_task('peadm::check_pe_master_rules').always_return(pe_rule_check)
expect(run_plan('peadm::upgrade',
'primary_host' => 'primary',
@@ -132,6 +143,7 @@ def allow_standard_non_returning_calls
it 'does not warn if r10k_known_hosts is not set' do
expect_out_message.with_params(r10k_warning).not_be_called
+ expect_task('peadm::check_pe_master_rules').always_return(pe_rule_check)
expect(run_plan('peadm::upgrade',
'primary_host' => 'primary',
diff --git a/tasks/check_pe_master_rules.json b/tasks/check_pe_master_rules.json
new file mode 100644
index 00000000..7eb2f3b8
--- /dev/null
+++ b/tasks/check_pe_master_rules.json
@@ -0,0 +1,10 @@
+{
+ "description": "Checks if the PE Master group rules have already been updated to support 'pe_compiler_legacy' as a pp_auth_role",
+ "input_method": "stdin",
+ "private": true,
+ "implementations": [
+ {"name": "check_pe_master_rules.rb"}
+ ],
+ "parameters": {},
+ "supports_noop": false
+}
\ No newline at end of file
diff --git a/tasks/check_pe_master_rules.rb b/tasks/check_pe_master_rules.rb
new file mode 100755
index 00000000..bed87f8b
--- /dev/null
+++ b/tasks/check_pe_master_rules.rb
@@ -0,0 +1,163 @@
+#!/opt/puppetlabs/puppet/bin/ruby
+# frozen_string_literal: true
+
+require 'json'
+require 'net/https'
+require 'puppet'
+
+# CheckPeMasterRules task class
+class CheckPeMasterRules
+ def initialize(params)
+ @params = params
+ end
+
+ def https_client
+ client = Net::HTTP.new(Puppet.settings[:certname], 4433)
+ client.use_ssl = true
+ client.cert = @cert ||= OpenSSL::X509::Certificate.new(File.read(Puppet.settings[:hostcert]))
+ client.key = @key ||= OpenSSL::PKey::RSA.new(File.read(Puppet.settings[:hostprivkey]))
+ client.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ client.ca_file = Puppet.settings[:localcacert]
+ client
+ end
+
+ def get_pe_master_group_id
+ net = https_client
+ res = net.get('/classifier-api/v1/groups')
+
+ unless res.code == '200'
+ raise "Failed to fetch groups: HTTP #{res.code} - #{res.body}"
+ end
+
+ groups = JSON.parse(res.body)
+ pe_master_group = groups.find { |group| group['name'] == 'PE Master' }
+
+ raise 'Could not find PE Master group' unless pe_master_group
+ pe_master_group['id']
+ rescue JSON::ParserError => e
+ raise "Invalid JSON response from server: #{e.message}"
+ rescue StandardError => e
+ raise "Error fetching PE Master group ID: #{e.message}"
+ end
+
+ def get_current_rules(group_id)
+ net = https_client
+ url = "/classifier-api/v1/groups/#{group_id}/rules"
+ req = Net::HTTP::Get.new(url)
+ res = net.request(req)
+
+ unless res.code == '200'
+ raise "Failed to fetch rules: HTTP #{res.code} - #{res.body}"
+ end
+
+ JSON.parse(res.body)['rule']
+ rescue JSON::ParserError => e
+ raise "Invalid JSON response from server: #{e.message}"
+ rescue StandardError => e
+ raise "Error fetching rules: #{e.message}"
+ end
+
+ def check_rules_updated(rules)
+ # If not an array, return false
+ return false unless rules.is_a?(Array)
+
+ # Check if there is at least 2 elements
+ if rules.length > 1
+ # Check if the first element is an 'or' rule for pe_compiler and pe_compiler_legacy
+ if rules[1].is_a?(Array) && rules[1][0] == 'or'
+ # Look for the pe_compiler and pe_compiler_legacy rules
+ pe_compiler_found = false
+ pe_compiler_legacy_found = false
+
+ rules[1][1..-1].each do |rule|
+ next unless rule.is_a?(Array) &&
+ rule[0] == '=' &&
+ rule[1].is_a?(Array) &&
+ rule[1] == ['trusted', 'extensions', 'pp_auth_role']
+
+ pe_compiler_found = true if rule[2] == 'pe_compiler'
+ pe_compiler_legacy_found = true if rule[2] == 'pe_compiler_legacy'
+ end
+
+ return pe_compiler_found && pe_compiler_legacy_found
+ end
+ end
+
+ false
+ end
+
+ def https_pdb_client(port = 8081)
+ client = Net::HTTP.new(Puppet.settings[:certname], port)
+ client.use_ssl = true
+ client.cert = @cert ||= OpenSSL::X509::Certificate.new(File.read(Puppet.settings[:hostcert]))
+ client.key = @key ||= OpenSSL::PKey::RSA.new(File.read(Puppet.settings[:hostprivkey]))
+ client.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ client.ca_file = Puppet.settings[:localcacert]
+ client
+ end
+
+ def check_nodes_with_legacy_compiler_oid
+ pdb = https_pdb_client
+ pdb_request = Net::HTTP::Get.new('/pdb/query/v4')
+ pdb_request.set_form_data({
+ 'query' => 'inventory[certname,trusted.extensions] {
+ trusted.extensions."1.3.6.1.4.1.34380.1.1.9814" is not null
+ }'
+ })
+
+ response = pdb.request(pdb_request)
+
+ unless response.code == '200'
+ raise "Failed to query PuppetDB: HTTP #{response.code} - #{response.body}"
+ end
+
+ nodes = JSON.parse(response.body)
+
+ {
+ 'nodes_found' => !nodes.empty?,
+ 'count' => nodes.size,
+ 'nodes' => nodes.map { |n| n['certname'] }
+ }
+ rescue JSON::ParserError => e
+ raise "Invalid JSON response from PuppetDB: #{e.message}"
+ rescue StandardError => e
+ raise "Error checking for legacy compiler OID: #{e.message}"
+ end
+
+ def execute!
+ group_id = get_pe_master_group_id
+ current_rules = get_current_rules(group_id)
+
+ rules_updated = check_rules_updated(current_rules)
+ legacy_compiler_nodes = check_nodes_with_legacy_compiler_oid
+
+ # Overall status is updated only if rules are updated AND no nodes have legacy compiler OID
+ is_updated = rules_updated && !legacy_compiler_nodes['nodes_found']
+
+ message = if !rules_updated
+ 'PE Master rules need to be updated to support pe_compiler_legacy'
+ elsif legacy_compiler_nodes['nodes_found']
+ 'PE Master rules are updated, but nodes with legacy compiler OID still exist'
+ else
+ 'PE Master rules have been updated with pe_compiler_legacy support and no legacy compiler OIDs found'
+ end
+
+ result = {
+ 'updated' => is_updated,
+ 'message' => message,
+ 'legacy_compiler_oid' => legacy_compiler_nodes
+ }
+
+ puts result.to_json
+ rescue StandardError => e
+ puts({ 'error' => e.message, 'updated' => false }.to_json)
+ exit 1
+ end
+end
+
+# Run the task unless an environment flag has been set
+unless ENV['RSPEC_UNIT_TEST_MODE']
+ Puppet.initialize_settings
+ task = CheckPeMasterRules.new(JSON.parse(STDIN.read))
+ task.execute!
+end
diff --git a/tasks/get_peadm_config.rb b/tasks/get_peadm_config.rb
index 9eb3aa02..8d24117f 100755
--- a/tasks/get_peadm_config.rb
+++ b/tasks/get_peadm_config.rb
@@ -22,7 +22,7 @@ def execute!
def config
# Compute values
- primary = groups.pinned('PE Master')
+ primary = groups.pinned('PE Certificate Authority')
replica = groups.pinned('PE HA Replica')
server_a = server('puppet/server', 'A', [primary, replica].compact)
server_b = server('puppet/server', 'B', [primary, replica].compact)
@@ -94,8 +94,7 @@ def groups
def compilers
@compilers ||=
pdb_query('inventory[certname,trusted.extensions] {
- trusted.extensions.pp_auth_role = "pe_compiler" and
- trusted.extensions."1.3.6.1.4.1.34380.1.1.9814" = "false"
+ trusted.extensions.pp_auth_role = "pe_compiler"
}').map do |c|
{
'certname' => c['certname'],
@@ -108,8 +107,7 @@ def compilers
def legacy_compilers
@legacy_compilers ||=
pdb_query('inventory[certname,trusted.extensions] {
- trusted.extensions.pp_auth_role = "pe_compiler" and
- trusted.extensions."1.3.6.1.4.1.34380.1.1.9814" = "true"
+ trusted.extensions.pp_auth_role = "pe_compiler_legacy"
}').map do |c|
{
'certname' => c['certname'],
diff --git a/tasks/node_group_unpin.json b/tasks/node_group_unpin.json
new file mode 100644
index 00000000..c94f3654
--- /dev/null
+++ b/tasks/node_group_unpin.json
@@ -0,0 +1,17 @@
+{
+ "description": "Unpins nodes from a specified PE node group",
+ "parameters": {
+ "node_certnames": {
+ "type": "Array[String]",
+ "description": "The certnames of the nodes to unpin"
+ },
+ "group_name": {
+ "type": "String",
+ "description": "The name of the node group to unpin the nodes from"
+ }
+ },
+ "input_method": "stdin",
+ "implementations": [
+ {"name": "node_group_unpin.rb"}
+ ]
+}
\ No newline at end of file
diff --git a/tasks/node_group_unpin.rb b/tasks/node_group_unpin.rb
new file mode 100755
index 00000000..2a2e5637
--- /dev/null
+++ b/tasks/node_group_unpin.rb
@@ -0,0 +1,118 @@
+#!/opt/puppetlabs/puppet/bin/ruby
+# frozen_string_literal: true
+
+require 'json'
+require 'yaml'
+require 'net/https'
+require 'puppet'
+
+# NodeGroupUnpin task class
+class NodeGroupUnpin
+ def initialize(params)
+ @params = params
+ raise "Missing required parameter 'node_certnames'" unless @params['node_certnames']
+ raise "'node_certnames' must be an array" unless @params['node_certnames'].is_a?(Array)
+ raise "Missing required parameter 'group_name'" unless @params['group_name']
+ @auth = YAML.load_file('/etc/puppetlabs/puppet/classifier.yaml')
+ rescue Errno::ENOENT
+ raise 'Could not find classifier.yaml at /etc/puppetlabs/puppet/classifier.yaml'
+ end
+
+ def https_client
+ client = Net::HTTP.new(Puppet.settings[:certname], 4433)
+ client.use_ssl = true
+ client.cert = @cert ||= OpenSSL::X509::Certificate.new(File.read(Puppet.settings[:hostcert]))
+ client.key = @key ||= OpenSSL::PKey::RSA.new(File.read(Puppet.settings[:hostprivkey]))
+ client.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ client.ca_file = Puppet.settings[:localcacert]
+ client
+ end
+
+ def groups
+ @groups ||= begin
+ net = https_client
+ res = net.get('/classifier-api/v1/groups')
+
+ unless res.code == '200'
+ raise "Failed to fetch groups: HTTP #{res.code} - #{res.body}"
+ end
+
+ NodeGroup.new(JSON.parse(res.body))
+ rescue JSON::ParserError => e
+ raise "Invalid JSON response from server: #{e.message}"
+ rescue StandardError => e
+ raise "Error fetching groups: #{e.message}"
+ end
+ end
+
+ def unpin_node(group, nodes)
+ raise 'Invalid group object' unless group.is_a?(Hash) && group['id'] && group['name']
+
+ net = https_client
+ begin
+ data = { "nodes": nodes }.to_json
+ url = "/classifier-api/v1/groups/#{group['id']}/unpin"
+
+ req = Net::HTTP::Post.new(url)
+ req['Content-Type'] = 'application/json'
+ req.body = data
+
+ res = net.request(req)
+
+ case res.code
+ when '204'
+ puts "Successfully unpinned nodes '#{nodes.join(', ')}' from group '#{group['name']}'"
+ else
+ begin
+ error_body = JSON.parse(res.body.to_s)
+ raise "Failed to unpin nodes: #{error_body['kind'] || error_body}"
+ rescue JSON::ParserError
+ raise "Invalid response from server (status #{res.code}): #{res.body}"
+ end
+ end
+ rescue StandardError => e
+ raise "Error during unpin request: #{e.message}"
+ end
+ end
+
+ # Utility class to aid in retrieving useful information from the node group
+ # data
+ class NodeGroup
+ attr_reader :data
+
+ def initialize(data)
+ @data = data
+ end
+
+ # Aids in digging into node groups by name, rather than UUID
+ def dig(name, *args)
+ group = @data.find { |obj| obj['name'] == name }
+ if group.nil?
+ nil
+ elsif args.empty?
+ group
+ else
+ group.dig(*args)
+ end
+ end
+ end
+
+ def execute!
+ group_name = @params['group_name']
+ node_certnames = @params['node_certnames']
+ group = groups.dig(group_name)
+ if group
+ unpin_node(group, node_certnames)
+ puts "Unpinned #{node_certnames.join(', ')} from #{group_name}"
+ else
+ puts "Group #{group_name} not found"
+ end
+ end
+end
+
+# Run the task unless an environment flag has been set
+unless ENV['RSPEC_UNIT_TEST_MODE']
+ Puppet.initialize_settings
+ task = NodeGroupUnpin.new(JSON.parse(STDIN.read))
+ task.execute!
+end
diff --git a/tasks/update_pe_master_rules.json b/tasks/update_pe_master_rules.json
new file mode 100644
index 00000000..e64663e9
--- /dev/null
+++ b/tasks/update_pe_master_rules.json
@@ -0,0 +1,8 @@
+{
+ "description": "Updates the PE Master group rules to support 'pe_compiler_legacy' as a pp_auth_role",
+ "input_method": "stdin",
+ "private": true,
+ "implementations": [
+ {"name": "update_pe_master_rules.rb"}
+ ]
+}
\ No newline at end of file
diff --git a/tasks/update_pe_master_rules.rb b/tasks/update_pe_master_rules.rb
new file mode 100755
index 00000000..947b187a
--- /dev/null
+++ b/tasks/update_pe_master_rules.rb
@@ -0,0 +1,119 @@
+#!/opt/puppetlabs/puppet/bin/ruby
+# frozen_string_literal: true
+
+require 'json'
+require 'net/https'
+require 'puppet'
+
+# UpdatePeMasterRules task class
+class UpdatePeMasterRules
+ def initialize(params)
+ @params = params
+ end
+
+ def https_client
+ client = Net::HTTP.new(Puppet.settings[:certname], 4433)
+ client.use_ssl = true
+ client.cert = @cert ||= OpenSSL::X509::Certificate.new(File.read(Puppet.settings[:hostcert]))
+ client.key = @key ||= OpenSSL::PKey::RSA.new(File.read(Puppet.settings[:hostprivkey]))
+ client.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ client.ca_file = Puppet.settings[:localcacert]
+ client
+ end
+
+ def get_pe_master_group_id
+ net = https_client
+ res = net.get('/classifier-api/v1/groups')
+
+ unless res.code == '200'
+ raise "Failed to fetch groups: HTTP #{res.code} - #{res.body}"
+ end
+
+ groups = JSON.parse(res.body)
+ pe_master_group = groups.find { |group| group['name'] == 'PE Master' }
+
+ raise 'Could not find PE Master group' unless pe_master_group
+ pe_master_group['id']
+ rescue JSON::ParserError => e
+ raise "Invalid JSON response from server: #{e.message}"
+ rescue StandardError => e
+ raise "Error fetching PE Master group ID: #{e.message}"
+ end
+
+ def get_current_rules(group_id)
+ net = https_client
+ url = "/classifier-api/v1/groups/#{group_id}/rules"
+ req = Net::HTTP::Get.new(url)
+ res = net.request(req)
+
+ unless res.code == '200'
+ raise "Failed to fetch rules: HTTP #{res.code} - #{res.body}"
+ end
+
+ JSON.parse(res.body)['rule']
+ rescue JSON::ParserError => e
+ raise "Invalid JSON response from server: #{e.message}"
+ rescue StandardError => e
+ raise "Error fetching rules: #{e.message}"
+ end
+
+ def modify_pe_master_rules(rules)
+ # If not an array, return as is
+ return rules unless rules.is_a?(Array)
+
+ # Make a copy of the rules to avoid modifying the original
+ result = rules.dup
+
+ result[1] = [
+ 'or',
+ ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler'],
+ ['=', ['trusted', 'extensions', 'pp_auth_role'], 'pe_compiler_legacy'],
+ ]
+
+ result
+ end
+
+ def update_rules(group_id)
+ net = https_client
+ begin
+ current_rules = get_current_rules(group_id)
+
+ # Transform rules recursively to handle nested structures
+ new_rules = modify_pe_master_rules(current_rules)
+
+ # Update the group with the modified rules
+ url = "/classifier-api/v1/groups/#{group_id}"
+ req = Net::HTTP::Post.new(url)
+ req['Content-Type'] = 'application/json'
+ req.body = { rule: new_rules }.to_json
+
+ res = net.request(req)
+
+ case res.code
+ when '200', '201', '204'
+ puts "Successfully transformed pe_compiler rule to use regex match for *_compiler roles in group #{group_id}"
+ else
+ begin
+ error_body = JSON.parse(res.body.to_s)
+ raise "Failed to update rules: #{error_body['kind'] || error_body}"
+ rescue JSON::ParserError
+ raise "Invalid response from server (status #{res.code}): #{res.body}"
+ end
+ end
+ rescue StandardError => e
+ raise "Error during rules update: #{e.message}"
+ end
+ end
+
+ def execute!
+ group_id = get_pe_master_group_id
+ update_rules(group_id)
+ end
+end
+
+# Run the task unless an environment flag has been set
+unless ENV['RSPEC_UNIT_TEST_MODE']
+ Puppet.initialize_settings
+ task = UpdatePeMasterRules.new(JSON.parse(STDIN.read))
+ task.execute!
+end