feat: add etcd module/role (#215)

- add etcd module
- add etcd role, profile and hieradata

Reviewed-on: https://git.query.consul/unkinben/puppet-prod/pulls/215
This commit is contained in:
Ben Vincent 2025-01-26 20:00:20 +11:00
parent ab7ce3bbfa
commit afd3405c98
7 changed files with 260 additions and 0 deletions

View File

@ -135,6 +135,8 @@ lookup_options:
keepalived::vrrp_instance:
merge:
strategy: deep
profiles::etcd::node::initial_cluster_token:
convert_to: Sensitive
facts_path: '/opt/puppetlabs/facter/facts.d'

View File

@ -0,0 +1,2 @@
---
profiles::etcd::node::initial_cluster_token: ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAhLyXszXUU6Dkiw9bEJTH0RXGaV2751NzvLH94i7QHfNukvOslF/kaDOA+FwqG06xSKSKo24Qyj4ewYA3BzhN8XLf2E9uW2LuDrUoA6aXUP2tYPqiTw8zmmgsVV5t7Y5PeNcleV3KmfcJZJKp33yGCKtGF7ggvNvnied5slO6E1BDkcVnqO7sdyI0MqSvsvH4IvEmeiSWAcBRBnwVLIwfn10frIvUg0fH4uZR7DASfO/HstYWKAEacz4xYBv74TtVVtYHlPvnVwC20YIYDMrgBsm3XngyWIQvruQCgyIkRzHjUKCpp76HpyEqzdJdEdaywkODYNOT6ab1B5uUu9WaMjBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBADXLPOqFHdnVgJW5+iXJYcgCDK1Eyr+RwvMA+3VszYALU5B6OCH5maplwC5aUgiQZ7ew==]

View File

@ -0,0 +1,60 @@
---
hiera_include:
- profiles::etcd::node
profiles::etcd::node::members_lookup: true
profiles::etcd::node::members_role: roles::infra::etcd::node
profiles::etcd::node::config:
data-dir: /data/etcd
client-cert-auth: false
client-transport-security:
cert-file: /etc/pki/tls/vault/certificate.crt
key-file: /etc/pki/tls/vault/private.key
client-cert-auth: false
auto-tls: false
peer-transport-security:
cert-file: /etc/pki/tls/vault/certificate.crt
key-file: /etc/pki/tls/vault/private.key
client-cert-auth: false
auto-tls: false
allowed-cn:
max-wals: 5
max-snapshots: 5
snapshot-count: 10000
heartbeat-interval: 100
election-timeout: 1000
cipher-suites: [
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
]
tls-min-version: 'TLS1.2'
tls-max-version: 'TLS1.3'
profiles::pki::vault::alt_names:
- etcd.service.consul
- etcd.query.consul
- "etcd.service.%{facts.country}-%{facts.region}.consul"
profiles::ssh::sign::principals:
- etcd.query.consul
- etcd.service.consul
- etcd.service.%{facts.country}-%{facts.region}.consul
consul::services:
etcd:
service_name: 'etcd'
tags:
- 'etcd'
address: "%{facts.networking.ip}"
port: 2379
checks:
- id: 'etcd_tcp_check'
name: 'ETCD TCP Check'
tcp: "%{facts.networking.ip}:2379"
interval: '10s'
timeout: '1s'
profiles::consul::client::node_rules:
- resource: service
segment: etcd
disposition: write

View File

@ -0,0 +1,110 @@
# manage etcd
class etcd (
Boolean $manage_user = true,
Boolean $manage_group = true,
Boolean $manage_package = true,
Boolean $manage_service = true,
String[1] $package_name = 'etcd',
String[1] $user = 'etcd',
String[1] $group = 'etcd',
Stdlib::Absolutepath $config_path = '/etc/etcd',
Stdlib::Absolutepath $config_file = "${config_path}/etcd.yaml",
Hash $config = { 'data-dir' => '/var/lib/etcd' },
Integer $max_open_files = 40000,
) {
if downcase($facts['kernel']) != 'linux' {
fail("Module etcd only supports Linux, not ${facts['kernel']}")
}
if $facts['service_provider'] != 'systemd' {
fail('Module etcd only supported on systems using systemd')
}
if ! $config['data-dir'] {
fail('Module etcd requires data-dir be specified in config Hash')
}
if $manage_package {
package { $package_name:
ensure => installed,
}
}
if $manage_user {
user { 'etcd':
ensure => 'present',
name => $user,
forcelocal => true,
shell => '/bin/false',
gid => $group,
home => $config['data-dir'],
managehome => false,
system => true,
before => Systemd::Unit_file['etcd.service'],
}
}
if $manage_group {
group { 'etcd':
ensure => 'present',
name => $group,
forcelocal => true,
system => true,
before => Systemd::Unit_file['etcd.service'],
}
}
mkdir::p { $config_path: }
mkdir::p { $config['data-dir']: }
file { $config_file:
ensure => 'file',
owner => $user,
group => $group,
mode => '0600',
content => to_yaml($config),
notify => Systemd::Unit_file['etcd.service'],
require => Mkdir::P[$config_path],
}
file { 'etcd-data-dir':
ensure => 'directory',
path => $config['data-dir'],
owner => $user,
group => $group,
mode => '0700',
notify => Systemd::Unit_file['etcd.service'],
require => Mkdir::P[$config['data-dir']],
}
file { 'etcd-data-dir-wal.tmp':
ensure => 'directory',
path => "${config['data-dir']}/wal.tmp",
owner => $user,
group => $group,
mode => '0700',
notify => Systemd::Unit_file['etcd.service'],
require => File['etcd-data-dir'],
}
if $config['wal-dir'] {
mkdir::p { $config['wal-dir']: }
file { 'etcd-wal-dir':
ensure => 'directory',
path => $config['wal-dir'],
owner => $user,
group => $group,
mode => '0700',
notify => Systemd::Unit_file['etcd.service'],
require => Mkdir::P[$config['wal-dir']],
}
}
if $manage_service {
include ::systemd
systemd::unit_file { 'etcd.service':
content => template('etcd/etcd.service.erb'),
enable => true,
active => true,
require => Package[$package_name],
}
}
}

View File

@ -0,0 +1,17 @@
# DO NOT EDIT: This file is being managed by Puppet.
[Unit]
Description=etcd key-value store
Documentation=https://github.com/etcd-io/etcd
After=network.target
[Service]
User=<%= @user %>
Group=<%= @group %>
Type=notify
ExecStart=/usr/bin/etcd --config-file <%= @config_file %>
Restart=always
RestartSec=10s
LimitNOFILE=<%= @max_open_files %>
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,58 @@
# manage the use of the etcd module
class profiles::etcd::node (
Sensitive[String[1]] $initial_cluster_token,
Boolean $members_lookup = false,
String $members_role = undef,
Array $servers = [],
Stdlib::Port $client_port = 2379,
Stdlib::Port $peer_port = 2380,
Hash $config = {},
){
# if lookup is enabled
if $members_lookup {
# check that the role is also set
unless !($members_role == undef) {
fail("members_role must be provided for ${title} when members_lookup is True")
}
# if it is, find hosts, sort them so they dont cause changes every run
$servers_array = sort(query_nodes("enc_role='${members_role}' and region='${facts['region']}'", 'networking.fqdn'))
# else use provided array from params
}else{
$servers_array = sort($servers)
}
if length($servers_array) >= 3 {
# construct the initial-cluster string
$initial_cluster = $servers_array.map |$fqdn| {
# lookup the ip address for the current fqdn
$ip = query_nodes("networking.fqdn='${fqdn}'", 'networking.ip')[0]
# construct the string for this server
"${fqdn}=https://${ip}:${peer_port}"
}.join(',')
$defaults = {
'data-dir' => '/var/lib/etcd',
'name' => $facts['networking']['fqdn'],
'listen-client-urls' => "https://${facts['networking']['ip']}:${client_port}",
'listen-peer-urls' => "https://${facts['networking']['ip']}:${peer_port}",
'advertise-client-urls' => "https://${facts['networking']['ip']}:${client_port}",
'initial-advertise-peer-urls' => "https://${facts['networking']['ip']}:${peer_port}",
'initial-cluster-token' => $initial_cluster_token.unwrap,
'initial-cluster' => $initial_cluster,
'initial-cluster-state' => 'new',
}
$merged_config = merge($defaults, $config)
class { 'etcd':
config => $merged_config,
}
}
}

View File

@ -0,0 +1,11 @@
# a role to deploy etcd
class roles::infra::etcd::node {
if $facts['firstrun'] {
include profiles::defaults
include profiles::firstrun::init
}else{
include profiles::defaults
include profiles::base
include profiles::base::datavol
}
}