From 4b1a56083cf8fe6bc153c1c2e67674fbba84e1fe Mon Sep 17 00:00:00 2001 From: petergmurphy Date: Wed, 15 Jan 2025 15:12:11 +0000 Subject: [PATCH] (PE-39577) Optimise legacy compiler support --- plans/convert.pp | 14 +++- tasks/get_peadm_config.rb | 2 +- tasks/node_group_unpin.json | 17 +++++ tasks/node_group_unpin.rb | 130 ++++++++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 tasks/node_group_unpin.json create mode 100644 tasks/node_group_unpin.rb diff --git a/plans/convert.pp b/plans/convert.pp index 1ff1771a..234b9110 100644 --- a/plans/convert.pp +++ b/plans/convert.pp @@ -232,7 +232,7 @@ 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') => 'legacy_compiler', peadm::oid('peadm_availability_group') => 'A', peadm::oid('peadm_legacy_compiler') => 'true', }, @@ -242,7 +242,7 @@ 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') => 'legacy_compiler', peadm::oid('peadm_availability_group') => 'B', peadm::oid('peadm_legacy_compiler') => 'true', }, @@ -283,6 +283,16 @@ include peadm::setup::convert_node_manager } + + # Unpin legacy compilers from PE Master group + if $legacy_compiler_targets { + $legacy_compiler_targets.each |$target| { + run_task('peadm::node_group_unpin', $primary_target, + selected_node => $target.peadm::certname(), + group => 'PE Master', + ) + } + } } else { # lint:ignore:strict_indent diff --git a/tasks/get_peadm_config.rb b/tasks/get_peadm_config.rb index 9eb3aa02..04118b8f 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) diff --git a/tasks/node_group_unpin.json b/tasks/node_group_unpin.json new file mode 100644 index 00000000..a9322aaf --- /dev/null +++ b/tasks/node_group_unpin.json @@ -0,0 +1,17 @@ +{ + "description": "Unpins a node from a specified PE node group", + "parameters": { + "selected_node": { + "type": "String", + "description": "The certname of the node to unpin" + }, + "group": { + "type": "String", + "description": "The name of the node group to unpin the node 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 100644 index 00000000..a903df32 --- /dev/null +++ b/tasks/node_group_unpin.rb @@ -0,0 +1,130 @@ +#!/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 'selected_node'" unless @params['selected_node'] + raise "Missing required parameter 'group'" unless @params['group'] + @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 + puts 'Debug: Fetching groups' + @groups ||= begin + net = https_client + puts 'Debug: Making GET request to /classifier-api/v1/groups' + res = net.get('/classifier-api/v1/groups') + puts "Debug: Response code: #{res.code}" + puts "Debug: Response body preview: #{res.body[0..100]}..." if res.body + + 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 + puts "Debug: JSON parse error: #{e.message}" + raise "Invalid JSON response from server: #{e.message}" + rescue StandardError => e + puts "Debug: Error in groups method: #{e.class} - #{e.message}" + raise "Error fetching groups: #{e.message}" + end + end + + def pe_master_group + groups.dig('PE Master') + end + + def unpin_node(group, node) + raise 'Invalid group object' unless group.is_a?(Hash) && group['id'] && group['name'] + + net = https_client + begin + data = { "nodes": [node] }.to_json + url = "/classifier-api/v1/groups/#{group['id']}/unpin" + + puts "Debug: Making POST request to #{url}" + puts "Debug: Request body: #{data}" + + req = Net::HTTP::Post.new(url) + req['Content-Type'] = 'application/json' + req.body = data + + res = net.request(req) + puts "Debug: Response code: #{res.code}" + puts "Debug: Response body: #{res.body}" if res.body + + case res.code + when '204' + puts "Successfully unpinned node '#{node}' from group '#{group['name']}'" + else + begin + error_body = JSON.parse(res.body.to_s) + raise "Failed to unpin node: #{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 = pe_master_group + if group + unpin_node(group, @params['selected_node']) + puts "Unpinned #{@params['selected_node']} from #{@params['group']}" + else + puts "Group #{@params['group']} 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