Compare commits

1 Commits

Author SHA1 Message Date
unkinben c39caeb8bb fix: preload correct rke2 airgap images to break CNI bootstrap deadlock
ci/woodpecker/pr/ruby-validate Pipeline was successful
ci/woodpecker/pr/puppet-lint Pipeline was successful
ci/woodpecker/pr/yamllint Pipeline was successful
ci/woodpecker/pr/bolt-validate Pipeline was successful
ci/woodpecker/pr/erb-validate Pipeline was successful
ci/woodpecker/pr/epp-validate Pipeline was successful
ci/woodpecker/pr/ruby-check Pipeline was successful
ci/woodpecker/pr/puppet-validate Pipeline was canceled
New el9_8 nodes boot rke2 v1.33.11 (flannel v0.28.4 / calico v3.31.5) from
the rolling latest/1.33 repo, but the airgap preload pinned v1.33.4
(flannel v0.27.2 / calico v3.30.2), so canal's images were never on disk and
containerd fell back to the artifactapi mirror VIP, which is unreachable until
the flannel overlay it would provide is up. Classic bootstrap deadlock.

- bump pinned rke2_version 1.33.4 -> 1.33.11 so the versionlock, RPM and
  preloaded bundle all match the canal image tags the running binary requests
  (the default rke2-images bundle already contains the canal CNI images)
- wire the airgap archive source to the container_archive_source parameter
  (previously declared but unused) and refresh its default to the artifactapi
  github remote, so the pre-CNI-reachable source is hiera-overridable per node

Claude-Session: https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT
2026-08-08 18:11:27 +10:00
25 changed files with 27 additions and 407 deletions
-40
View File
@@ -367,7 +367,6 @@ ssh::server::options:
profiles::ssh::knownhosts::lines: profiles::ssh::knownhosts::lines:
- '@cert-authority * ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1HD97vYxLTniE4qNpGuftUlvmkEXIuX8+7nbENv/IzsGUghEDRtyThjQ7ojNKIsQ7f8wXr0gMcI+fAPfrbcOMHCAoYMomikwL0b3h95SZI40q3CyM+0DMnwiVVDX6C1QxkO2Rv9cszSkCa85NotJhXiUuTBI9BFcRPy+mAhbpAru+bfypYofI0wW97XNTl8Jgwmni5MgutBIQAokFIn5ux8iWxndCH3AqDtmkwC5DfQeQ+wZx7rkwqJEpJffQzrjb1gIM6P9hDCVBBVPh/3o80IJ69rFWrJAZUb+JpG4cXJH0NcSW+wqc3JCT/x3q8VlHwOTXSlNNKtOJCRx73mB8e1XTTy2a9FgpKDDg5XQXWHAViJDz1RTRL9gRefMylRgKz4bXoTuY9kJWM8hPTyUejtukbJThlBJc3OmDxBZBF7F0iqB11pHexok43OCEiANodVa36eWu9/5X032Vm48fZ1/akDPY/NSy3wAn7kwut+A0/JAHFHASrq+1mt9YurkJegI+YHXO6eEWpBIpmI7ORHJbGL4MhkHrxYzVamuP8CkU7tXzsv138+wpOcRHNp9yJY4PT40BZkRf/O3O+jt3pj9Dj8rvgywF2W6hFzywh3Y78upOprRkQlQtHfsI8EyrYI8/hUw2u3H+3yPXh3YjWfqvWVG1BRLRHBV7m90uaw==' - '@cert-authority * ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1HD97vYxLTniE4qNpGuftUlvmkEXIuX8+7nbENv/IzsGUghEDRtyThjQ7ojNKIsQ7f8wXr0gMcI+fAPfrbcOMHCAoYMomikwL0b3h95SZI40q3CyM+0DMnwiVVDX6C1QxkO2Rv9cszSkCa85NotJhXiUuTBI9BFcRPy+mAhbpAru+bfypYofI0wW97XNTl8Jgwmni5MgutBIQAokFIn5ux8iWxndCH3AqDtmkwC5DfQeQ+wZx7rkwqJEpJffQzrjb1gIM6P9hDCVBBVPh/3o80IJ69rFWrJAZUb+JpG4cXJH0NcSW+wqc3JCT/x3q8VlHwOTXSlNNKtOJCRx73mB8e1XTTy2a9FgpKDDg5XQXWHAViJDz1RTRL9gRefMylRgKz4bXoTuY9kJWM8hPTyUejtukbJThlBJc3OmDxBZBF7F0iqB11pHexok43OCEiANodVa36eWu9/5X032Vm48fZ1/akDPY/NSy3wAn7kwut+A0/JAHFHASrq+1mt9YurkJegI+YHXO6eEWpBIpmI7ORHJbGL4MhkHrxYzVamuP8CkU7tXzsv138+wpOcRHNp9yJY4PT40BZkRf/O3O+jt3pj9Dj8rvgywF2W6hFzywh3Y78upOprRkQlQtHfsI8EyrYI8/hUw2u3H+3yPXh3YjWfqvWVG1BRLRHBV7m90uaw=='
- '@cert-authority * ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDi80G0GtKMdRj4azPwxbJW46NG0y8seSVSnvFm2Ka/nckdUb/3lRlQuPYf1tkbqqFlcXjDM8+u0tzM2kLsgfn0Dpujm1fuoA66yGGweBjtxLFErH5+6+/KND5I9w8LMY2AtVztnBk/CVx8RwgrooABDRZiBH7OOAOpJqqFI7LvEsv61zC0nAqbYJ8uxfx+r4lJ9dUhK1woitEqC4npSRUn5KJK+KAEd7AzcUkZb6TO9A3oRPz1nQ/qU+QNMmVUi+wRj9kJR18mxErugyLLTNcFDBqSdHNun3eUNBUmoS2cAa8ZVscOLzbUGejVK9UY06Q7wu+J34Fzl8CN5tBqRHGZ3ykqGZ6gG+O0Egr9Rm3Obgkujpio2uTh27LhBy72vjgJ8kTFmPlzANTJFpKlsy91usSFPh8WZeIo6VpPLqnC32rjfNkfqHyPAeJ3+rdOkUZoDjMoZc3wxxLZTgMU5ud1w4LxQNRkNiaT/tRRNQF8o+pygkS7xxKduBBMzYdRS1OifaS4gyvP82oOyquWywhyFNtG7ph8FQwc34puM7FyxfaJ0XL/+zAfPMhDoOojvofADJo73R+FgVyer+mCJw4KtWJ4JzZrNTYz1Xl8WlhF8RDzOYfSH46qzJFa9FcRqgP4LUkgzdvcQ+1hBFpvpHtw3vl3DiqtZDDbK9SyrEtdnw=='
profiles::base::groups::local: profiles::base::groups::local:
admins: admins:
@@ -410,45 +409,6 @@ profiles::ceph::client::mons:
- 10.18.15.2 - 10.18.15.2
- 10.18.15.3 - 10.18.15.3
# de96a98f cluster topology (prodnxsr0001-0019) - single source of truth for
# /etc/ceph/ceph.conf rendered by profiles::ceph::client on the k8s (osd) and
# incus (mon/mgr/mds) roles. fsid/mons are overridden per-role in the role hiera.
# public_network is the /32 of every ceph host's ceph-public loopback.
profiles::ceph::client::cluster_public_ips:
- 198.18.23.1
- 198.18.23.2
- 198.18.23.3
- 198.18.23.4
- 198.18.23.5
- 198.18.23.6
- 198.18.23.7
- 198.18.23.8
- 198.18.23.9
- 198.18.23.10
- 198.18.23.11
- 198.18.23.12
- 198.18.23.13
- 198.18.23.14
- 198.18.23.15
- 198.18.23.16
- 198.18.23.17
- 198.18.23.18
- 198.18.23.19
profiles::ceph::client::mon_initial_members:
- prodnxsr0009
- prodnxsr0010
- prodnxsr0011
- prodnxsr0012
- prodnxsr0013
# two mds daemon instances per mon/mgr/mds host (rendered only where
# render_mds_config is true, i.e. the incus node role).
profiles::ceph::client::mds_instances:
prodnxsr0009: 2
prodnxsr0010: 2
prodnxsr0011: 2
prodnxsr0012: 2
prodnxsr0013: 2
profiles::ceph::conf::config: profiles::ceph::conf::config:
global: global:
auth_client_required: 'cephx' auth_client_required: 'cephx'
-4
View File
@@ -77,7 +77,6 @@ profiles::yum::global::repos:
baseurl: https://artifactapi.k8s.syd1.au.unkin.net/api/v1/local/rpm-internal/ baseurl: https://artifactapi.k8s.syd1.au.unkin.net/api/v1/local/rpm-internal/
gpgcheck: false gpgcheck: false
mirrorlist: absent mirrorlist: absent
metadata_expire: '60'
rpm-vendor: rpm-vendor:
name: rpm-vendor name: rpm-vendor
descr: rpm-vendor repository descr: rpm-vendor repository
@@ -85,7 +84,6 @@ profiles::yum::global::repos:
baseurl: https://artifactapi.k8s.syd1.au.unkin.net/api/v1/local/rpm-vendor/ baseurl: https://artifactapi.k8s.syd1.au.unkin.net/api/v1/local/rpm-vendor/
gpgcheck: false gpgcheck: false
mirrorlist: absent mirrorlist: absent
metadata_expire: '60'
# Per-release variants, resolved from the host's EL major version so el8 # Per-release variants, resolved from the host's EL major version so el8
# hosts pull rpm-internal-el8/rpm-vendor-el8, el9 hosts el9, etc. # hosts pull rpm-internal-el8/rpm-vendor-el8, el9 hosts el9, etc.
rpm-internal-release: rpm-internal-release:
@@ -95,7 +93,6 @@ profiles::yum::global::repos:
baseurl: https://artifactapi.k8s.syd1.au.unkin.net/api/v1/local/rpm-internal-el%{facts.os.release.major}/ baseurl: https://artifactapi.k8s.syd1.au.unkin.net/api/v1/local/rpm-internal-el%{facts.os.release.major}/
gpgcheck: false gpgcheck: false
mirrorlist: absent mirrorlist: absent
metadata_expire: '60'
rpm-vendor-release: rpm-vendor-release:
name: rpm-vendor-el%{facts.os.release.major} name: rpm-vendor-el%{facts.os.release.major}
descr: rpm-vendor-el%{facts.os.release.major} repository descr: rpm-vendor-el%{facts.os.release.major} repository
@@ -103,7 +100,6 @@ profiles::yum::global::repos:
baseurl: https://artifactapi.k8s.syd1.au.unkin.net/api/v1/local/rpm-vendor-el%{facts.os.release.major}/ baseurl: https://artifactapi.k8s.syd1.au.unkin.net/api/v1/local/rpm-vendor-el%{facts.os.release.major}/
gpgcheck: false gpgcheck: false
mirrorlist: absent mirrorlist: absent
metadata_expire: '60'
# Additional repositories - default to absent, roles can override with ensure: present # Additional repositories - default to absent, roles can override with ensure: present
# FRRouting repositories # FRRouting repositories
-2
View File
@@ -60,7 +60,6 @@ profiles::yum::global::repos:
baseurl: https://artifactapi.k8s.syd1.au.unkin.net/api/v1/local/rpm-internal-f%{facts.os.release.major}/ baseurl: https://artifactapi.k8s.syd1.au.unkin.net/api/v1/local/rpm-internal-f%{facts.os.release.major}/
gpgcheck: false gpgcheck: false
mirrorlist: absent mirrorlist: absent
metadata_expire: '60'
rpm-vendor: rpm-vendor:
name: rpm-vendor-f%{facts.os.release.major} name: rpm-vendor-f%{facts.os.release.major}
descr: rpm-vendor-f%{facts.os.release.major} repository descr: rpm-vendor-f%{facts.os.release.major} repository
@@ -68,4 +67,3 @@ profiles::yum::global::repos:
baseurl: https://artifactapi.k8s.syd1.au.unkin.net/api/v1/local/rpm-vendor-f%{facts.os.release.major}/ baseurl: https://artifactapi.k8s.syd1.au.unkin.net/api/v1/local/rpm-vendor-f%{facts.os.release.major}/
gpgcheck: false gpgcheck: false
mirrorlist: absent mirrorlist: absent
metadata_expire: '60'
+1 -3
View File
@@ -203,9 +203,7 @@ profiles::accounts::sysadmin::extra_groups:
- incus-admin - incus-admin
# manage cephfs mounts # manage cephfs mounts
profiles::ceph::client::manage_ceph_conf: true profiles::ceph::client::manage_ceph_conf: false
# mon/mgr/mds host: render the [mds] + [mds.*] sections
profiles::ceph::client::render_mds_config: true
profiles::ceph::client::manage_ceph_package: false profiles::ceph::client::manage_ceph_package: false
profiles::ceph::client::manage_ceph_paths: false profiles::ceph::client::manage_ceph_paths: false
profiles::ceph::client::fsid: 'de96a98f-3d23-465a-a899-86d3d67edab8' profiles::ceph::client::fsid: 'de96a98f-3d23-465a-a899-86d3d67edab8'
+1 -3
View File
@@ -11,8 +11,6 @@ hiera_include:
# manage rke2 # manage rke2
rke2::bootstrap_node: prodnxsr0001.main.unkin.net rke2::bootstrap_node: prodnxsr0001.main.unkin.net
rke2::join_url: https://join-k8s.service.consul:9345 rke2::join_url: https://join-k8s.service.consul:9345
# pull the airgap image bundle via artifactapi (host-reachable pre-CNI), not github directly
rke2::container_archive_source: 'https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote/github/rancher/rke2/releases/download'
rke2::manage_registries: true rke2::manage_registries: true
rke2::registries: rke2::registries:
docker.io: docker.io:
@@ -92,7 +90,7 @@ profiles::packages::include:
profiles::selinux::setenforce::mode: disabled profiles::selinux::setenforce::mode: disabled
profiles::ceph::client::manage_ceph_conf: true profiles::ceph::client::manage_ceph_conf: false
profiles::ceph::client::manage_ceph_package: false profiles::ceph::client::manage_ceph_package: false
profiles::ceph::client::manage_ceph_paths: false profiles::ceph::client::manage_ceph_paths: false
profiles::ceph::client::fsid: 'de96a98f-3d23-465a-a899-86d3d67edab8' profiles::ceph::client::fsid: 'de96a98f-3d23-465a-a899-86d3d67edab8'
-3
View File
@@ -30,9 +30,6 @@ rke2::config_hash:
- '--node-monitor-period=4s' - '--node-monitor-period=4s'
protect-kernel-defaults: true protect-kernel-defaults: true
disable-kube-proxy: false disable-kube-proxy: false
# taint the control-plane/etcd nodes so only tolerating workloads schedule here
node-taint:
- "node-role.kubernetes.io/control-plane=true:NoSchedule"
# configure consul service # configure consul service
consul::services: consul::services:
+1 -1
View File
@@ -33,7 +33,7 @@ profiles::packages::include:
profiles::selinux::setenforce::mode: disabled profiles::selinux::setenforce::mode: disabled
profiles::ceph::client::manage_ceph_conf: true profiles::ceph::client::manage_ceph_conf: false
profiles::ceph::client::manage_ceph_package: false profiles::ceph::client::manage_ceph_package: false
profiles::ceph::client::manage_ceph_paths: false profiles::ceph::client::manage_ceph_paths: false
profiles::ceph::client::fsid: 'de96a98f-3d23-465a-a899-86d3d67edab8' profiles::ceph::client::fsid: 'de96a98f-3d23-465a-a899-86d3d67edab8'
-9
View File
@@ -26,15 +26,6 @@ profiles::puppet::cobbler_enc::packages:
- 'requests' - 'requests'
- 'PyYAML' - 'PyYAML'
profiles::puppet::enc::repo: https://git.service.au-syd1.consul/unkinben/puppet-enc.git profiles::puppet::enc::repo: https://git.service.au-syd1.consul/unkinben/puppet-enc.git
# Deep-merged with the entries in roles/infra/puppet.yaml.
profiles::packages::include:
encapic:
ensure: '0.2.0'
profiles::puppet::encapic::encapi_url: https://encapi.k8s.syd1.au.unkin.net
profiles::puppet::server::external_nodes: '/usr/bin/encapic-enc'
profiles::puppet::r10k::r10k_repo: https://git.unkin.net/unkin/puppet-r10k.git profiles::puppet::r10k::r10k_repo: https://git.unkin.net/unkin/puppet-r10k.git
profiles::puppet::g10k::bin_path: '/usr/bin/g10k' profiles::puppet::g10k::bin_path: '/usr/bin/g10k'
profiles::puppet::g10k::cfg_path: '/etc/puppetlabs/r10k/r10k.yaml' profiles::puppet::g10k::cfg_path: '/etc/puppetlabs/r10k/r10k.yaml'
-8
View File
@@ -40,11 +40,3 @@ profiles::packages::include:
ensure: '0.1.1' ensure: '0.1.1'
openbao-plugin-secrets-gitea: openbao-plugin-secrets-gitea:
ensure: '0.1.0' ensure: '0.1.0'
openbao-plugin-secrets-apptoken:
ensure: '0.1.0'
openbao-plugin-secrets-netbox:
ensure: '0.1.0'
openbao-plugin-secrets-ghp:
ensure: '0.1.0'
openbao-plugin-secrets-arrstack:
ensure: '0.2.0'
-5
View File
@@ -1,8 +1,3 @@
--- ---
# physical hosts only (facts.virtual == 'physical'); merged 'unique' with the
# common hiera_include in profiles::base.
hiera_include:
- profiles::lldpd
profiles::packages::include: profiles::packages::include:
"%{hiera('lm-sensors::package')}": {} "%{hiera('lm-sensors::package')}": {}
+12 -33
View File
@@ -3,21 +3,13 @@
require 'facter' require 'facter'
require 'yaml' require 'yaml'
require 'net/http' require 'net/http'
require 'openssl'
require 'uri' require 'uri'
require 'fileutils' require 'fileutils'
# EncapiENC module: Fetches ENC data from encapi, caches it, and provides structured facts. # CobblerENC module: Fetches ENC data from Cobbler, caches it, and provides structured facts.
module EncapiENC module CobblerENC
CACHE_FILE = '/var/cache/puppet_enc.yaml' CACHE_FILE = '/var/cache/puppet_enc.yaml'
CACHE_TTL = 7 * 24 * 60 * 60 # 7 days in seconds CACHE_TTL = 7 * 24 * 60 * 60 # 7 days in seconds
# Facter runs under Puppet's vendored ruby, whose OpenSSL trusts only
# /opt/puppetlabs/puppet/ssl/cert.pem and never the system trust store, so the
# estate CA anchor profiles::pki::vaultca installs has to be named explicitly.
CA_BUNDLE_PATHS = [
'/etc/pki/ca-trust/source/anchors/vaultcaroot.pem',
'/usr/local/share/ca-certificates/vaultcaroot.pem'
].freeze
@enc_data = nil # In-memory cache for the ENC response @enc_data = nil # In-memory cache for the ENC response
def self.read_cache def self.read_cache
@@ -37,22 +29,9 @@ module EncapiENC
File.write(CACHE_FILE, cache_data.to_yaml) File.write(CACHE_FILE, cache_data.to_yaml)
end end
def self.ca_bundle def self.fetch_from_cobbler
CA_BUNDLE_PATHS.find { |path| File.exist?(path) } uri = URI("http://cobbler.main.unkin.net/cblr/svc/op/puppet/hostname/#{Facter.value(:fqdn) || Facter.value(:hostname)}")
end response = Net::HTTP.get_response(uri)
def self.http_client(uri)
client = Net::HTTP.new(uri.host, uri.port)
client.use_ssl = true
client.verify_mode = OpenSSL::SSL::VERIFY_PEER
bundle = ca_bundle
client.ca_file = bundle if bundle
client
end
def self.fetch_from_encapi
uri = URI("https://encapi.k8s.syd1.au.unkin.net/cblr/svc/op/puppet/hostname/#{Facter.value(:fqdn) || Facter.value(:hostname)}")
response = http_client(uri).request(Net::HTTP::Get.new(uri))
raise "Failed to fetch ENC data. HTTP #{response.code}" unless response.is_a?(Net::HTTPSuccess) raise "Failed to fetch ENC data. HTTP #{response.code}" unless response.is_a?(Net::HTTPSuccess)
@@ -62,7 +41,7 @@ module EncapiENC
def self.retrieve_enc_data def self.retrieve_enc_data
return @enc_data if @enc_data return @enc_data if @enc_data
@enc_data = fetch_from_encapi @enc_data = fetch_from_cobbler
write_cache(@enc_data) write_cache(@enc_data)
@enc_data @enc_data
end end
@@ -70,26 +49,26 @@ module EncapiENC
def self.fetch_enc_data def self.fetch_enc_data
retrieve_enc_data retrieve_enc_data
rescue StandardError => e rescue StandardError => e
Facter.warn("Error retrieving encapi ENC data: #{e.message}") Facter.warn("Error retrieving Cobbler ENC data: #{e.message}")
@enc_data = read_cache @enc_data = read_cache
return @enc_data unless @enc_data.empty? return @enc_data unless @enc_data.empty?
raise 'No cached ENC data available and encapi is unreachable.' raise 'No cached ENC data available and Cobbler is down.'
end end
def self.enc_role def self.enc_role
fetch_enc_data.fetch('classes', {}).keys.first || raise('ENC Role not found in encapi ENC response') fetch_enc_data.fetch('classes', {}).keys.first || raise('ENC Role not found in Cobbler ENC response')
end end
def self.enc_env def self.enc_env
fetch_enc_data.fetch('environment', nil) || raise('ENC Environment not found in encapi ENC response') fetch_enc_data.fetch('environment', nil) || raise('ENC Environment not found in Cobbler ENC response')
end end
end end
Facter.add('enc_role') do Facter.add('enc_role') do
setcode { EncapiENC.enc_role } setcode { CobblerENC.enc_role }
end end
Facter.add('enc_env') do Facter.add('enc_env') do
setcode { EncapiENC.enc_env } setcode { CobblerENC.enc_env }
end end
-104
View File
@@ -1,104 +0,0 @@
# frozen_string_literal: true
require 'facter'
require 'json'
# Exposes LLDP neighbour topology (switch/port each interface is cabled to) as
# the structured `lldp` fact, keyed by local interface. This is the only source
# of physical switch/port topology in the estate and feeds NetBox. Uses
# `lldpctl -f json0`: json0 wraps every node in an array regardless of
# cardinality, so one neighbour and many neighbours parse identically (plain
# `keyvalue` folds the neighbour's sysname into the key path, and plain `json`
# collapses single-element arrays into objects). Never raises: any error or a
# down daemon yields an empty hash so a puppet run can never break.
module LldpFact
SOCKETS = ['/run/lldpd.socket', '/var/run/lldpd.socket'].freeze
module_function
# First element of a json0 node (everything is array-wrapped), or the value
# itself if it is not an array; nil when absent.
def first(node)
node.is_a?(Array) ? node[0] : node
end
# Array form of a json0 node whatever its cardinality.
def list(node)
node.is_a?(Array) ? node : [node].compact
end
# Value string of a json0 leaf like [{ 'value' => 'x' }].
def leaf(node)
entry = first(node)
entry.is_a?(Hash) ? entry['value'] : entry
end
# Chassis MAC from its id list, preferring the entry typed 'mac'.
def chassis_mac(chassis)
ids = list(chassis['id'])
mac = ids.find { |id| id.is_a?(Hash) && id['type'] == 'mac' } || ids.first
mac.is_a?(Hash) ? mac['value'] : nil
end
# Topology record for one local interface, or nil when it has no neighbour.
def neighbour(iface)
chassis = first(iface['chassis'])
port = first(iface['port'])
return nil unless chassis && port
chassis_fields(chassis).merge(port_fields(port, first(iface['vlan'])))
end
def chassis_fields(chassis)
{
'neighbor_chassis_name' => leaf(chassis['name']),
'neighbor_chassis_mac' => chassis_mac(chassis),
'neighbor_chassis_descr' => leaf(chassis['descr'])
}
end
def port_fields(port, vlan)
port_id = first(port['id'])
vlan_h = vlan.is_a?(Hash) ? vlan : {}
{
'neighbor_port_id' => port_id.is_a?(Hash) ? port_id['value'] : port_id,
'neighbor_port_descr' => leaf(port['descr']),
'vlan_id' => vlan_h['vlan-id'],
'vlan_name' => vlan_h['value']
}
end
def interfaces(output)
lldp = first(JSON.parse(output)['lldp']) || {}
list(lldp['interface'])
end
# Map of local interface => topology record, skipping interfaces with no
# neighbour.
def collect(ifaces)
ifaces.each_with_object({}) do |iface, acc|
next unless iface.is_a?(Hash)
name = iface['name']
data = neighbour(iface)
acc[name] = data if name && data
end
end
def resolve
output = Facter::Core::Execution.execute('lldpctl -f json0 2>/dev/null', on_fail: nil)
return {} if output.to_s.empty?
collect(interfaces(output))
rescue StandardError
{}
end
end
Facter.add(:lldp) do
confine kernel: 'Linux'
confine { Facter.value(:is_virtual) == false }
confine { Facter::Core::Execution.which('lldpctl') }
confine { LldpFact::SOCKETS.any? { |path| File.exist?(path) } }
setcode { LldpFact.resolve }
end
+1 -8
View File
@@ -6,17 +6,10 @@ class rke2::install (
Stdlib::HTTPUrl $container_archive_source = $rke2::container_archive_source, Stdlib::HTTPUrl $container_archive_source = $rke2::container_archive_source,
){ ){
# versionlock rke2 before install so the lock exists before any upgrade is attempted # versionlock rke2
yum::versionlock{"rke2-${node_type}": yum::versionlock{"rke2-${node_type}":
ensure => present, ensure => present,
version => "${rke2_version}~${rke2_release}", version => "${rke2_version}~${rke2_release}",
before => Package["rke2-${node_type}"],
}
# lock rke2-common (a strict = version dep) so the rolling latest channel can't drift it ahead of the pinned server/agent
yum::versionlock{'rke2-common':
ensure => present,
version => "${rke2_version}~${rke2_release}",
} }
# install rke2 # install rke2
+3 -3
View File
@@ -1,8 +1,8 @@
# rke2 params # rke2 params
class rke2::params ( class rke2::params (
Enum['server', 'agent'] $node_type = 'agent', Enum['server', 'agent'] $node_type = 'agent',
String $rke2_version = '1.33.13', String $rke2_version = '1.33.11',
String $rke2_release = 'rke2r2', String $rke2_release = 'rke2r1',
Stdlib::Absolutepath $config_file = '/etc/rancher/rke2/config.yaml', Stdlib::Absolutepath $config_file = '/etc/rancher/rke2/config.yaml',
Hash $config_hash = {}, Hash $config_hash = {},
Stdlib::HTTPSUrl $join_url = 'https://127.0.0.1:9345', Stdlib::HTTPSUrl $join_url = 'https://127.0.0.1:9345',
@@ -11,7 +11,7 @@ class rke2::params (
Boolean $helm_install = false, Boolean $helm_install = false,
Hash $helm_repos = {}, Hash $helm_repos = {},
Array[String[1]] $extra_config_files = [], Array[String[1]] $extra_config_files = [],
Stdlib::HTTPUrl $container_archive_source = 'https://github.com/rancher/rke2/releases/download', Stdlib::HTTPUrl $container_archive_source = 'https://artifactapi.k8s.syd1.au.unkin.net/api/v1/remote/github/rancher/rke2/releases/download',
Boolean $manage_registries = false, Boolean $manage_registries = false,
Hash $registries = {}, Hash $registries = {},
) {} ) {}
+3 -31
View File
@@ -2,9 +2,6 @@
class profiles::ceph::client ( class profiles::ceph::client (
String $fsid, String $fsid,
Array[Stdlib::Host] $mons, Array[Stdlib::Host] $mons,
# cluster topology (single source of truth: hieradata/common.yaml)
Array[Stdlib::Host] $cluster_public_ips,
Array[Stdlib::Host] $mon_initial_members,
Stdlib::Absolutepath $config_file = '/etc/ceph/ceph.conf', Stdlib::Absolutepath $config_file = '/etc/ceph/ceph.conf',
Boolean $manage_ceph_conf = true, Boolean $manage_ceph_conf = true,
Boolean $manage_ceph_package = true, Boolean $manage_ceph_package = true,
@@ -13,24 +10,6 @@ class profiles::ceph::client (
String $group = 'ceph', String $group = 'ceph',
Stdlib::Filemode $mode = '0644', Stdlib::Filemode $mode = '0644',
Hash $keyrings = {}, Hash $keyrings = {},
# [global] tunables (defaults match the live hand-maintained ceph.conf)
String $auth_client_required = 'cephx',
String $auth_cluster_required = 'cephx',
String $auth_service_required = 'cephx',
Boolean $mon_allow_pool_delete = true,
Boolean $ms_bind_ipv4 = true,
Boolean $ms_bind_ipv6 = false,
Integer $osd_crush_chooseleaf_type = 1,
Integer $osd_pool_default_min_size = 2,
Integer $osd_pool_default_size = 3,
Integer $osd_pool_default_pg_num = 128,
# mds config sections; only rendered on mon/mgr/mds hosts (render_mds_config)
Boolean $render_mds_config = false,
Hash[String, Integer] $mds_instances = {},
Hash $mds_common = {
'keyring' => '/var/lib/ceph/mds/ceph-$id/keyring',
'mds_standby_replay' => true,
},
) { ) {
# dont run this on proxmox nodes # dont run this on proxmox nodes
@@ -43,13 +22,6 @@ class profiles::ceph::client (
} }
} }
# only depend on the package when this class manages it; on the ceph
# hosts the package is delivered by cephadm / profiles::packages instead.
$config_require = $manage_ceph_package ? {
true => Package['ceph-common'],
default => undef,
}
# manage the ceph directory # manage the ceph directory
if $manage_ceph_paths { if $manage_ceph_paths {
file { '/etc/ceph': file { '/etc/ceph':
@@ -57,11 +29,11 @@ class profiles::ceph::client (
owner => $owner, owner => $owner,
group => $group, group => $group,
mode => $mode, mode => $mode,
require => $config_require, require => Package['ceph-common'],
} }
} }
# render /etc/ceph/ceph.conf from cluster topology in hiera # create a basic client config
if $manage_ceph_conf { if $manage_ceph_conf {
file { $config_file: file { $config_file:
ensure => file, ensure => file,
@@ -69,7 +41,7 @@ class profiles::ceph::client (
group => $group, group => $group,
mode => $mode, mode => $mode,
content => template('profiles/ceph/client.conf.erb'), content => template('profiles/ceph/client.conf.erb'),
require => $config_require, require => Package['ceph-common'],
} }
} }
-3
View File
@@ -2,9 +2,6 @@ class profiles::ceph::osd (
Boolean $ensure_running = true, Boolean $ensure_running = true,
) { ) {
# tune the I/O scheduler on the disks backing ceph OSDs
include profiles::ceph::osd_scheduler
if $ensure_running and $facts['is_ceph_osd'] { if $ensure_running and $facts['is_ceph_osd'] {
$facts['ceph_services']['osd'].each |String $svc| { $facts['ceph_services']['osd'].each |String $svc| {
service { $svc: service { $svc:
@@ -1,32 +0,0 @@
class profiles::ceph::osd_scheduler (
String[1] $scheduler = 'none',
) {
$devices = $facts['ceph_osd_devices']
# no-op where the fact is absent/empty (VMs, non-OSD hosts have no ceph PVs)
if $devices =~ Array[String[1], 1] {
# strip /dev/ so the rule matches the udev KERNEL sysname (e.g. sda)
$kernel_names = $devices.map |$dev| { regsubst($dev, '^.*/', '') }
$sysname_matches = $kernel_names.map |$name| { "--sysname-match=${name}" }
file { '/etc/udev/rules.d/60-ceph-osd-scheduler.rules':
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
content => template('profiles/ceph/osd-scheduler.rules.erb'),
notify => Exec['ceph-osd-scheduler-reload'],
}
# apply immediately; udev re-applies on reboot and device re-add
$trigger = "udevadm trigger --subsystem-match=block --action=change ${join($sysname_matches, ' ')}"
exec { 'ceph-osd-scheduler-reload':
command => "udevadm control --reload-rules && ${trigger}",
path => ['/usr/bin', '/bin', '/usr/sbin', '/sbin'],
refreshonly => true,
}
}
}
+2 -1
View File
@@ -24,7 +24,8 @@ class profiles::dns::updater (
Stdlib::AbsolutePath $config_dir = '/etc/dns-updater', Stdlib::AbsolutePath $config_dir = '/etc/dns-updater',
Stdlib::AbsolutePath $master_basedir = lookup('profiles::dns::master::basedir'), Stdlib::AbsolutePath $master_basedir = lookup('profiles::dns::master::basedir'),
# dns-updater daemon (replaces the dns-update shell script). 'latest' so hosts # dns-updater daemon (replaces the dns-update shell script). 'latest' so hosts
# pick up new releases (e.g. the record filter). # pick up new releases (e.g. the record filter); rpm-internal metadata_expire
# is 1h so this does not thrash.
String $package_ensure = 'latest', String $package_ensure = 'latest',
Stdlib::AbsolutePath $api_socket = '/run/dns-updater/api.sock', Stdlib::AbsolutePath $api_socket = '/run/dns-updater/api.sock',
String $resync = '10m', String $resync = '10m',
-33
View File
@@ -1,33 +0,0 @@
# profiles::lldpd
#
# Runs lldpd on physical hosts so each machine learns its switch/port topology
# via LLDP. The `lldp` fact exposes that neighbour data for NetBox. Assigned
# via hiera_include from hieradata/virtual/physical.yaml (physicals only); the
# lldpd.service ships disabled, so it is explicitly enabled and started here.
class profiles::lldpd (
Boolean $enabled = true,
String $package = 'lldpd',
String $service = 'lldpd',
){
if $enabled {
package { $package:
ensure => installed,
}
service { $service:
ensure => running,
enable => true,
subscribe => Package[$package],
}
} else {
service { $service:
ensure => stopped,
enable => false,
}
package { $package:
ensure => absent,
}
}
}
+1 -2
View File
@@ -60,11 +60,10 @@ class profiles::puppet::agent (
require => Yumrepo[$use_yumrepo], require => Yumrepo[$use_yumrepo],
} }
# versionlock puppet-agent before install so the lock exists before any upgrade is attempted # versionlock puppet-agent
yum::versionlock{$use_package: yum::versionlock{$use_package:
ensure => $agent_versionlock_ensure, ensure => $agent_versionlock_ensure,
version => $agent_versionlock_version, version => $agent_versionlock_version,
before => Package[$use_package],
} }
} }
'Debian': { 'Debian': {
-32
View File
@@ -1,32 +0,0 @@
# Class: profiles::puppet::encapic
#
# Manages the configuration for the encapic ENC client. The package itself is
# installed through profiles::packages (pinned in hiera); this class owns the
# config so the encapi endpoint can change without repackaging.
class profiles::puppet::encapic (
Stdlib::HTTPUrl $encapi_url,
Stdlib::AbsolutePath $config_dir = '/etc/encapic',
String $config_name = 'encapic.conf',
String $owner = 'root',
String $group = 'root',
) {
# The RPM ships this file as %config(noreplace), so puppet must write it only
# once the package is present or the install overwrites it.
file { $config_dir:
ensure => directory,
mode => '0755',
owner => $owner,
group => $group,
require => Package['encapic'],
}
file { "${config_dir}/${config_name}":
ensure => file,
mode => '0644',
owner => $owner,
group => $group,
content => "ENCAPI_URL=${encapi_url}\n",
require => File[$config_dir],
}
}
@@ -12,7 +12,6 @@ class profiles::puppet::puppetmaster (
include profiles::puppet::g10k include profiles::puppet::g10k
include profiles::puppet::enc include profiles::puppet::enc
include profiles::puppet::cobbler_enc include profiles::puppet::cobbler_enc
include profiles::puppet::encapic
include profiles::puppet::autosign include profiles::puppet::autosign
include profiles::puppet::gems include profiles::puppet::gems
include profiles::helpers::certmanager include profiles::helpers::certmanager
-14
View File
@@ -130,20 +130,6 @@ class profiles::vault::server (
mode => '0600', mode => '0600',
} }
# go-plugin creates each secrets plugin's control socket under TMPDIR
# (/tmp/pluginNNN by default); systemd-tmpfiles-clean reaps aged /tmp files
# and severs the socket of a long-lived plugin, orphaning the process. Point
# TMPDIR at a per-start RuntimeDirectory on /run (tmpfs, no age cleanup).
systemd::manage_dropin { 'plugin-tmpdir.conf':
unit => 'vault.service',
service_entry => {
'RuntimeDirectory' => 'vault-plugins',
'RuntimeDirectoryMode' => '0700',
'Environment' => 'TMPDIR=/run/vault-plugins',
},
notify => Service['vault'],
}
service { 'vault': service { 'vault':
ensure => true, ensure => true,
enable => true, enable => true,
+2 -27
View File
@@ -1,28 +1,3 @@
[global] [global]
auth_client_required = <%= @auth_client_required %> fsid = <%= @fsid %>
auth_cluster_required = <%= @auth_cluster_required %> mon_host = <%= @mons.join(' ') %>
auth_service_required = <%= @auth_service_required %>
fsid = <%= @fsid %>
mon_allow_pool_delete = <%= @mon_allow_pool_delete %>
mon_initial_members = <%= @mon_initial_members.join(',') %>
mon_host = <%= @mons.join(',') %>
ms_bind_ipv4 = <%= @ms_bind_ipv4 %>
ms_bind_ipv6 = <%= @ms_bind_ipv6 %>
osd_crush_chooseleaf_type = <%= @osd_crush_chooseleaf_type %>
osd_pool_default_min_size = <%= @osd_pool_default_min_size %>
osd_pool_default_size = <%= @osd_pool_default_size %>
osd_pool_default_pg_num = <%= @osd_pool_default_pg_num %>
public_network = <%= @cluster_public_ips.map { |ip| "#{ip}/32" }.join(',') %>
<% if @render_mds_config -%>
[mds]
keyring = <%= @mds_common['keyring'] %>
mds_standby_replay = <%= @mds_common['mds_standby_replay'] %>
<% @mds_instances.sort.each do |host, count| -%>
<% (1..count).each do |instance| -%>
[mds.<%= host %>-<%= instance %>]
host = <%= host %>
<% end -%>
<% end -%>
<% end -%>
@@ -1,5 +0,0 @@
# Managed by puppet (profiles::ceph::osd_scheduler).
# Set the I/O scheduler to <%= @scheduler %> on ceph OSD block devices.
<% @kernel_names.sort.each do |dev| -%>
ACTION=="add|change", SUBSYSTEM=="block", KERNEL=="<%= dev %>", ATTR{queue/scheduler}="<%= @scheduler %>"
<% end -%>