From 22faf0062867c8913f94e6aa023d12fb3fde39e2 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Fri, 24 Jul 2026 22:42:28 +1000 Subject: [PATCH 1/2] Add profiles::puppet::migrate to repoint VM agents onto puppet-on-k8s Migrate VM puppet agents off the legacy VM puppetmasters and onto the new puppet-on-kubernetes servers, per-wave via hiera and reversible without re-enrolment. Changing server/ca_server alone is insufficient: the agent's ssldir holds a cert signed by the OLD CA that the new CA neither trusts nor recognises. This switches migrated nodes to a FRESH ssldir so the agent generates a new CSR (autosigned on the k8s CA) while the old creds stay on disk for rollback. - Add profiles::puppet::migrate: opt-in (hiera_include) toggle that owns the fresh ssldir directory and documents per-node/per-role/common wiring plus rollback in its class header. - Extend profiles::puppet::client with optional $ssldir and $report_server params (default undef); the ERB template omits both lines when unset, so unmigrated nodes render a byte-identical puppet.conf. - puppet.conf stays owned solely by client.pp's template; migrate.pp adds no competing File resource. --- site/profiles/manifests/puppet/client.pp | 24 ++--- site/profiles/manifests/puppet/migrate.pp | 90 +++++++++++++++++++ .../templates/puppet/client/puppet.conf.erb | 5 +- 3 files changed, 107 insertions(+), 12 deletions(-) create mode 100644 site/profiles/manifests/puppet/migrate.pp diff --git a/site/profiles/manifests/puppet/client.pp b/site/profiles/manifests/puppet/client.pp index b58c33a..58699d2 100644 --- a/site/profiles/manifests/puppet/client.pp +++ b/site/profiles/manifests/puppet/client.pp @@ -4,17 +4,19 @@ # # site/profile/manifests/puppet/client.pp class profiles::puppet::client ( - Array $dns_alt_names = [$trusted['certname']], - String $server = 'puppetmaster', - String $ca_server = 'puppetca', - String $environment = 'develop', - Integer $runinterval = 1800, - Integer $runtimeout = 3600, - Boolean $show_diff = true, - Boolean $usecacheonfailure = false, - Integer $facts_soft_limit = 4096, - Boolean $splay = true, - Integer $splaylimit = 600, + Array $dns_alt_names = [$trusted['certname']], + String $server = 'puppetmaster', + String $ca_server = 'puppetca', + Optional[String] $report_server = undef, + String $environment = 'develop', + Integer $runinterval = 1800, + Integer $runtimeout = 3600, + Boolean $show_diff = true, + Boolean $usecacheonfailure = false, + Integer $facts_soft_limit = 4096, + Boolean $splay = true, + Integer $splaylimit = 600, + Optional[Stdlib::Absolutepath] $ssldir = undef, ) { # dont manage puppet.conf if this is a puppetmaster diff --git a/site/profiles/manifests/puppet/migrate.pp b/site/profiles/manifests/puppet/migrate.pp new file mode 100644 index 0000000..133b8dc --- /dev/null +++ b/site/profiles/manifests/puppet/migrate.pp @@ -0,0 +1,90 @@ +# Class: profiles::puppet::migrate +# +# Repoints a VM puppet agent from the legacy VM puppetmasters onto the new +# puppet-on-kubernetes servers. +# +# The agent's existing ssldir holds a client certificate signed by the OLD CA; +# the new k8s CA will neither trust that cert nor recognise the agent. Rather +# than revoke/clean the old creds (which would burn the rollback path), this +# class switches the agent to a FRESH ssldir. On the next run the agent finds +# an empty ssldir, generates a new key + CSR, submits it to the new CA +# (autosign = true on the k8s side signs it immediately), and then pulls its +# catalog from the new compilers. The OLD ssldir is left untouched on disk, so +# reverting the hiera below restores the node to the legacy masters with no +# re-enrolment. +# +# The catalog that performs the switch is compiled by the OLD server (the node +# still points at it when it applies this change). profiles::puppet::client +# rewrites puppet.conf with the new server / ca_server / report_server / ssldir +# and notifies Service['puppet'], so the very next agent run lands on k8s. +# +# NOTE: this is deliberately hiera-driven per node/role so waves can be rolled +# out and rolled back one target at a time. The class only manages the fresh +# ssldir; the actual puppet.conf changes ride on profiles::puppet::client's +# existing template via its $server / $ca_server / $report_server / $ssldir +# params (see the hiera block below). Do NOT add a second File resource for +# puppet.conf here -- client.pp already owns it. +# +# == Wiring (hiera_include convention) +# +# This class is opt-in via hiera_include; it is not pulled in by +# profiles::base. To migrate a target add the class to hiera_include AND flip +# the client params so puppet.conf carries the new endpoints + fresh ssldir. +# +# --- Canary: one node -> hieradata/nodes/.yaml ------------------- +# hiera_include: +# - profiles::puppet::migrate +# +# profiles::puppet::migrate::enabled: true +# +# # repoint the agent (client.pp owns puppet.conf; migrate.pp owns ssldir) +# profiles::puppet::client::server: 'puppet.k8s.syd1.au.unkin.net' +# profiles::puppet::client::ca_server: 'puppetca.k8s.syd1.au.unkin.net' +# profiles::puppet::client::report_server: 'puppet.k8s.syd1.au.unkin.net' +# profiles::puppet::client::ssldir: '/etc/puppetlabs/puppet/ssl-k8s' +# --------------------------------------------------------------------------- +# +# --- Wave: a whole role -> hieradata/roles//[/].yaml ----------- +# (same keys as the canary block above) +# --------------------------------------------------------------------------- +# +# --- Final flip: hieradata/common.yaml (retires the legacy masters) -------- +# profiles::puppet::client::server: 'puppet.k8s.syd1.au.unkin.net' +# profiles::puppet::client::ca_server: 'puppetca.k8s.syd1.au.unkin.net' +# profiles::puppet::client::report_server: 'puppet.k8s.syd1.au.unkin.net' +# profiles::puppet::client::ssldir: '/etc/puppetlabs/puppet/ssl-k8s' +# # profiles::puppet::migrate no longer needs to be in hiera_include once the +# # fleet default points at k8s and the fresh ssldir exists everywhere. +# --------------------------------------------------------------------------- +# +# == Rollback (per target) +# +# Delete the migration block from the node/role hiera (or set the client keys +# back to the legacy values and drop the ssldir override). The next run +# rewrites puppet.conf back to the legacy ssldir, which still contains the +# original CA-signed cert, and the node is back on the VM masters. +# +# site/profiles/manifests/puppet/migrate.pp +class profiles::puppet::migrate ( + Boolean $enabled = false, + String $new_server = 'puppet.k8s.syd1.au.unkin.net', + String $new_ca_server = 'puppetca.k8s.syd1.au.unkin.net', + Stdlib::Absolutepath $new_ssldir = '/etc/puppetlabs/puppet/ssl-k8s', +) { + + # never touch the puppet masters themselves (same guard as client.pp) + if $enabled and $facts['enc_role'] != 'roles::infra::puppet::master' { + + # Ensure the fresh ssldir exists with agent-correct ownership/perms. The + # puppet agent will create it on demand, but pre-creating it keeps the very + # first post-switch run from racing directory creation and makes the + # migration state auditable. Contents (keys, certs, crl) are populated by + # the agent against the new CA -- we only own the directory itself. + file { $new_ssldir: + ensure => 'directory', + owner => 'root', + group => 'root', + mode => '0771', + } + } +} diff --git a/site/profiles/templates/puppet/client/puppet.conf.erb b/site/profiles/templates/puppet/client/puppet.conf.erb index d9bc058..5a52172 100644 --- a/site/profiles/templates/puppet/client/puppet.conf.erb +++ b/site/profiles/templates/puppet/client/puppet.conf.erb @@ -1,12 +1,15 @@ [main] dns_alt_names = <%= @dns_alt_names_string %> +<% unless @ssldir.nil? -%> +ssldir = <%= @ssldir %> +<% end -%> [agent] server = <%= @server %> ca_server = <%= @ca_server %> environment = <%= @environment %> report = true -report_server = <%= @server %> +report_server = <%= @report_server.nil? ? @server : @report_server %> runinterval = <%= @runinterval %> runtimeout = <%= @runtimeout %> show_diff = <%= @show_diff %> -- 2.47.3 From 7d75dcd188a7036b54cd08be001fddeb16e40a73 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Fri, 24 Jul 2026 23:48:59 +1000 Subject: [PATCH 2/2] puppet::migrate: drive k8s cutover from a single enabled boolean Wire profiles::puppet::migrate into profiles::base (shipping enabled=>false) so every node evaluates it. client.pp now includes migrate and folds new_server/new_ca_server/new_ssldir into the effective server/ca_server/ report_server/ssldir it renders, so flipping the one profiles::puppet::migrate::enabled key at any hiera layer (node/role/common) repoints a target -- no per-node client param overrides. Explicit client::ssldir/::report_server still override the migrate-derived value; unmigrated nodes render a byte-identical puppet.conf. --- site/profiles/manifests/base.pp | 3 ++ site/profiles/manifests/puppet/client.pp | 43 +++++++++++++++ site/profiles/manifests/puppet/migrate.pp | 53 +++++++++---------- .../templates/puppet/client/puppet.conf.erb | 10 ++-- 4 files changed, 76 insertions(+), 33 deletions(-) diff --git a/site/profiles/manifests/base.pp b/site/profiles/manifests/base.pp index f9d2f80..cc1cab5 100644 --- a/site/profiles/manifests/base.pp +++ b/site/profiles/manifests/base.pp @@ -12,6 +12,9 @@ class profiles::base () { # manage the puppet agent include profiles::puppet::agent include profiles::puppet::client + # k8s migration switch; ships disabled, flip its ::enabled boolean in + # hiera (node/role/common) to repoint a target onto puppet-on-k8s. + include profiles::puppet::migrate # include the base profiles include profiles::base::repos diff --git a/site/profiles/manifests/puppet/client.pp b/site/profiles/manifests/puppet/client.pp index 58699d2..9e1a6a7 100644 --- a/site/profiles/manifests/puppet/client.pp +++ b/site/profiles/manifests/puppet/client.pp @@ -19,6 +19,49 @@ class profiles::puppet::client ( Optional[Stdlib::Absolutepath] $ssldir = undef, ) { + # Pull in the k8s-migration switch. When + # profiles::puppet::migrate::enabled is true at ANY hiera layer + # (node/role/common) the effective endpoints below flip to the k8s + # servers and a fresh ssldir -- no other hiera keys required. This class + # never includes client.pp, so there is no include cycle. + include profiles::puppet::migrate + + # Effective values fed to the template. Precedence differs by param type: + # + # ssldir / report_server (Optional, default undef): an explicit client + # param wins; else the migrate-derived value when enabled; else the + # legacy default (undef -> template omits ssldir; report_server -> server). + # + # server / ca_server (String, no undef sentinel): the migrate value wins + # when migrate::enabled, otherwise the client param (which is the legacy + # default unless an operator set it explicitly in hiera). To pin a + # bespoke server while enabled, leave enabled => false for that target + # and set profiles::puppet::client::server directly. + $migrate_on = $profiles::puppet::migrate::enabled + + $effective_server = $migrate_on ? { + true => $profiles::puppet::migrate::new_server, + default => $server, + } + $effective_ca_server = $migrate_on ? { + true => $profiles::puppet::migrate::new_ca_server, + default => $ca_server, + } + # report_server: explicit param wins; else follow the effective server. + $effective_report_server = $report_server ? { + undef => $effective_server, + default => $report_server, + } + # ssldir: explicit param wins; else the fresh k8s ssldir when migrating; + # else undef (template omits the key, i.e. puppet's built-in default). + $effective_ssldir = $ssldir ? { + undef => $migrate_on ? { + true => $profiles::puppet::migrate::new_ssldir, + default => undef, + }, + default => $ssldir, + } + # dont manage puppet.conf if this is a puppetmaster if $facts['enc_role'] != 'roles::infra::puppet::master' { diff --git a/site/profiles/manifests/puppet/migrate.pp b/site/profiles/manifests/puppet/migrate.pp index 133b8dc..5bf287e 100644 --- a/site/profiles/manifests/puppet/migrate.pp +++ b/site/profiles/manifests/puppet/migrate.pp @@ -21,48 +21,45 @@ # NOTE: this is deliberately hiera-driven per node/role so waves can be rolled # out and rolled back one target at a time. The class only manages the fresh # ssldir; the actual puppet.conf changes ride on profiles::puppet::client's -# existing template via its $server / $ca_server / $report_server / $ssldir -# params (see the hiera block below). Do NOT add a second File resource for -# puppet.conf here -- client.pp already owns it. +# existing template. client.pp `include`s this class and, when enabled, folds +# new_server / new_ca_server / new_ssldir into the effective server / +# ca_server / report_server / ssldir it renders. Do NOT add a second File +# resource for puppet.conf here -- client.pp already owns it. # -# == Wiring (hiera_include convention) +# == Wiring (single boolean) # -# This class is opt-in via hiera_include; it is not pulled in by -# profiles::base. To migrate a target add the class to hiera_include AND flip -# the client params so puppet.conf carries the new endpoints + fresh ssldir. +# This class ships wired into profiles::base with enabled => false, so every +# node evaluates it but nothing changes until the boolean is flipped. Because +# client.pp includes it and reads its params, setting the ONE key below at any +# hiera layer repoints a target -- no client.pp param overrides needed. +# +# The endpoints (new_server / new_ca_server / new_ssldir) are param defaults +# on this class; override them in hiera too if the k8s names ever change. # # --- Canary: one node -> hieradata/nodes/.yaml ------------------- -# hiera_include: -# - profiles::puppet::migrate -# # profiles::puppet::migrate::enabled: true -# -# # repoint the agent (client.pp owns puppet.conf; migrate.pp owns ssldir) -# profiles::puppet::client::server: 'puppet.k8s.syd1.au.unkin.net' -# profiles::puppet::client::ca_server: 'puppetca.k8s.syd1.au.unkin.net' -# profiles::puppet::client::report_server: 'puppet.k8s.syd1.au.unkin.net' -# profiles::puppet::client::ssldir: '/etc/puppetlabs/puppet/ssl-k8s' # --------------------------------------------------------------------------- # # --- Wave: a whole role -> hieradata/roles//[/].yaml ----------- -# (same keys as the canary block above) +# profiles::puppet::migrate::enabled: true # --------------------------------------------------------------------------- # -# --- Final flip: hieradata/common.yaml (retires the legacy masters) -------- -# profiles::puppet::client::server: 'puppet.k8s.syd1.au.unkin.net' -# profiles::puppet::client::ca_server: 'puppetca.k8s.syd1.au.unkin.net' -# profiles::puppet::client::report_server: 'puppet.k8s.syd1.au.unkin.net' -# profiles::puppet::client::ssldir: '/etc/puppetlabs/puppet/ssl-k8s' -# # profiles::puppet::migrate no longer needs to be in hiera_include once the -# # fleet default points at k8s and the fresh ssldir exists everywhere. +# --- Estate flip -> hieradata/common.yaml (retires the legacy masters) ----- +# profiles::puppet::migrate::enabled: true # --------------------------------------------------------------------------- # +# Manual override: profiles::puppet::client::ssldir and ::report_server are +# Optional and, when set explicitly in hiera, win over the migrate-derived +# value. server / ca_server have no undef sentinel, so while enabled => true +# the migrate endpoints win; to point a target at a bespoke server leave +# enabled => false for it and set profiles::puppet::client::server directly. +# # == Rollback (per target) # -# Delete the migration block from the node/role hiera (or set the client keys -# back to the legacy values and drop the ssldir override). The next run -# rewrites puppet.conf back to the legacy ssldir, which still contains the -# original CA-signed cert, and the node is back on the VM masters. +# Remove the profiles::puppet::migrate::enabled key (or set it back to false) +# at the node/role/common layer. The next run rewrites puppet.conf back to the +# legacy server + legacy ssldir, which still contains the original CA-signed +# cert, and the node is back on the VM masters. # # site/profiles/manifests/puppet/migrate.pp class profiles::puppet::migrate ( diff --git a/site/profiles/templates/puppet/client/puppet.conf.erb b/site/profiles/templates/puppet/client/puppet.conf.erb index 5a52172..b3d31d5 100644 --- a/site/profiles/templates/puppet/client/puppet.conf.erb +++ b/site/profiles/templates/puppet/client/puppet.conf.erb @@ -1,15 +1,15 @@ [main] dns_alt_names = <%= @dns_alt_names_string %> -<% unless @ssldir.nil? -%> -ssldir = <%= @ssldir %> +<% unless @effective_ssldir.nil? -%> +ssldir = <%= @effective_ssldir %> <% end -%> [agent] -server = <%= @server %> -ca_server = <%= @ca_server %> +server = <%= @effective_server %> +ca_server = <%= @effective_ca_server %> environment = <%= @environment %> report = true -report_server = <%= @report_server.nil? ? @server : @report_server %> +report_server = <%= @effective_report_server.nil? ? @effective_server : @effective_report_server %> runinterval = <%= @runinterval %> runtimeout = <%= @runtimeout %> show_diff = <%= @show_diff %> -- 2.47.3