From afd3405c98b6a890e4cbf3a97387a68499c0bf38 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 26 Jan 2025 20:00:20 +1100 Subject: [PATCH] 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 --- hieradata/common.yaml | 2 + hieradata/roles/infra/etcd/node.eyaml | 2 + hieradata/roles/infra/etcd/node.yaml | 60 +++++++++++++ modules/etcd/manifests/init.pp | 110 ++++++++++++++++++++++++ modules/etcd/templates/etcd.service.erb | 17 ++++ site/profiles/manifests/etcd/node.pp | 58 +++++++++++++ site/roles/manifests/infra/etcd/node.pp | 11 +++ 7 files changed, 260 insertions(+) create mode 100644 hieradata/roles/infra/etcd/node.eyaml create mode 100644 hieradata/roles/infra/etcd/node.yaml create mode 100644 modules/etcd/manifests/init.pp create mode 100644 modules/etcd/templates/etcd.service.erb create mode 100644 site/profiles/manifests/etcd/node.pp create mode 100644 site/roles/manifests/infra/etcd/node.pp diff --git a/hieradata/common.yaml b/hieradata/common.yaml index 518df23..c1379c8 100644 --- a/hieradata/common.yaml +++ b/hieradata/common.yaml @@ -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' diff --git a/hieradata/roles/infra/etcd/node.eyaml b/hieradata/roles/infra/etcd/node.eyaml new file mode 100644 index 0000000..40ffd6b --- /dev/null +++ b/hieradata/roles/infra/etcd/node.eyaml @@ -0,0 +1,2 @@ +--- +profiles::etcd::node::initial_cluster_token: ENC[PKCS7,MIIBiQYJKoZIhvcNAQcDoIIBejCCAXYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAhLyXszXUU6Dkiw9bEJTH0RXGaV2751NzvLH94i7QHfNukvOslF/kaDOA+FwqG06xSKSKo24Qyj4ewYA3BzhN8XLf2E9uW2LuDrUoA6aXUP2tYPqiTw8zmmgsVV5t7Y5PeNcleV3KmfcJZJKp33yGCKtGF7ggvNvnied5slO6E1BDkcVnqO7sdyI0MqSvsvH4IvEmeiSWAcBRBnwVLIwfn10frIvUg0fH4uZR7DASfO/HstYWKAEacz4xYBv74TtVVtYHlPvnVwC20YIYDMrgBsm3XngyWIQvruQCgyIkRzHjUKCpp76HpyEqzdJdEdaywkODYNOT6ab1B5uUu9WaMjBMBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBADXLPOqFHdnVgJW5+iXJYcgCDK1Eyr+RwvMA+3VszYALU5B6OCH5maplwC5aUgiQZ7ew==] diff --git a/hieradata/roles/infra/etcd/node.yaml b/hieradata/roles/infra/etcd/node.yaml new file mode 100644 index 0000000..ff0231b --- /dev/null +++ b/hieradata/roles/infra/etcd/node.yaml @@ -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 diff --git a/modules/etcd/manifests/init.pp b/modules/etcd/manifests/init.pp new file mode 100644 index 0000000..fdf7c56 --- /dev/null +++ b/modules/etcd/manifests/init.pp @@ -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], + } + } +} diff --git a/modules/etcd/templates/etcd.service.erb b/modules/etcd/templates/etcd.service.erb new file mode 100644 index 0000000..967a95d --- /dev/null +++ b/modules/etcd/templates/etcd.service.erb @@ -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 diff --git a/site/profiles/manifests/etcd/node.pp b/site/profiles/manifests/etcd/node.pp new file mode 100644 index 0000000..0a13f60 --- /dev/null +++ b/site/profiles/manifests/etcd/node.pp @@ -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, + } + } +} diff --git a/site/roles/manifests/infra/etcd/node.pp b/site/roles/manifests/infra/etcd/node.pp new file mode 100644 index 0000000..39a1793 --- /dev/null +++ b/site/roles/manifests/infra/etcd/node.pp @@ -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 + } +}