Populate terragrunt-enc: encapi ENC data as code
ci/woodpecker/pr/plan Pipeline failed
ci/woodpecker/pr/pre-commit Pipeline was successful

terragrunt-enc is the single source of truth for encapi ENC data (Puppet
statuses, roles, and node classifications), managed with Terraform/Terragrunt.
It supersedes Cobbler classification for Puppet and the terraform-incus
dual-write prototype (PR #39).

- Add modules/encapi (encapi_status / encapi_role / encapi_node, nodes
  FK-ordered after their role and status).
- Add config/encapi leaf: 3 statuses, 51 roles, and 143 node assignments
  (130 container VMs env production + 13 prodnxsr physicals env develop),
  all YAML-driven.
- Add config/root.hcl (consul backend infra/terraform/enc/<leaf>/state) and
  the encapi provider from the artifactapi registry (v0.1.0).
- Add Makefile, Woodpecker pre-commit/plan/apply pipelines, pre-commit
  config, ci/extract_incus_nodes.py, and expand the README.
This commit is contained in:
Ben Vincent
2026-07-24 23:12:03 +10:00
parent 1e01bc9673
commit 054110d748
17 changed files with 908 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
.terraform
.terraform.lock.hcl
env
.terragrunt-cache
tfplan
+22
View File
@@ -0,0 +1,22 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: end-of-file-fixer
types: [yaml]
- id: trailing-whitespace
types: [yaml]
- repo: https://github.com/gruntwork-io/pre-commit
rev: v0.1.30
hooks:
- id: tofu-fmt
- id: terragrunt-hcl-fmt
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.37.1
hooks:
- id: yamllint
args:
[
"-d {extends: relaxed, rules: {line-length: disable}}",
"-s",
]
+22
View File
@@ -0,0 +1,22 @@
when:
- event: push
branch: main
steps:
- name: apply
image: git.unkin.net/unkin/almalinux9-opentofu:20260606
environment:
VAULT_AUTH_METHOD: kubernetes
commands:
- dnf install vault -y
- make apply
backend_options:
kubernetes:
serviceAccountName: terraform-enc
resources:
requests:
memory: 512Mi
cpu: 1
limits:
memory: 2Gi
cpu: 2
+21
View File
@@ -0,0 +1,21 @@
when:
- event: pull_request
steps:
- name: plan
image: git.unkin.net/unkin/almalinux9-opentofu:20260606
environment:
VAULT_AUTH_METHOD: kubernetes
commands:
- dnf install vault -y
- make plan
backend_options:
kubernetes:
serviceAccountName: terraform-enc
resources:
requests:
memory: 512Mi
cpu: 1
limits:
memory: 2Gi
cpu: 2
+18
View File
@@ -0,0 +1,18 @@
when:
- event: pull_request
steps:
- name: pre-commit
image: git.unkin.net/unkin/almalinux9-opentofu:20260606
commands:
- uvx pre-commit run --all-files
backend_options:
kubernetes:
serviceAccountName: default
resources:
requests:
memory: 512Mi
cpu: 1
limits:
memory: 2Gi
cpu: 2
+51
View File
@@ -0,0 +1,51 @@
.PHONY: init plan apply apply-if-changes format pre-commit
VAULT_AUTH_METHOD ?= approle
VAULT_K8S_ROLE ?= woodpecker_terraform_enc
VAULT_K8S_MOUNT ?= auth/k8s/au/syd1
VAULT_K8S_JWT_PATH ?= /var/run/secrets/kubernetes.io/serviceaccount/token
define vault_env
@export VAULT_ADDR="https://vault.service.consul:8200" && \
if [ "$(VAULT_AUTH_METHOD)" = "kubernetes" ]; then \
export VAULT_TOKEN=$$(vault write -field=token $(VAULT_K8S_MOUNT)/login role=$(VAULT_K8S_ROLE) jwt=$$(cat $(VAULT_K8S_JWT_PATH))); \
else \
export VAULT_TOKEN=$$(vault write -field=token auth/approle/login role_id=$$VAULT_ROLEID); \
fi && \
export CONSUL_HTTP_TOKEN=$$(vault read -field=token consul_root/au/syd1/creds/terraform-enc) && \
export ENCAPI_WRITE_TOKEN=$$(vault kv get -field=ENCAPI_WRITE_TOKEN kv/kubernetes/namespace/encapi/default/environment)
endef
init:
@$(call vault_env) && \
terragrunt run --all --non-interactive init -- -upgrade
plan: init
@$(call vault_env) && \
terragrunt run --all --parallelism 4 --non-interactive plan
apply-if-changes: init
@$(call vault_env) && \
terragrunt run --all --parallelism 4 --non-interactive plan -- -detailed-exitcode -out=tfplan; \
EXIT_CODE=$$?; \
if [ $$EXIT_CODE -eq 2 ]; then \
$(call vault_env) && \
terragrunt run --all --parallelism 2 --non-interactive apply -- tfplan; \
elif [ $$EXIT_CODE -eq 0 ]; then \
echo "No changes detected, skipping apply."; \
else \
exit $$EXIT_CODE; \
fi
apply: init
@$(call vault_env) && \
terragrunt run --all --parallelism 2 --non-interactive apply
format:
@echo "Formatting OpenTofu files..."
@tofu fmt -recursive .
@echo "Formatting Terragrunt files..."
@terragrunt hcl fmt
pre-commit:
@uvx pre-commit run --all-files
+58 -1
View File
@@ -1,3 +1,60 @@
# terragrunt-enc
Single source of truth for encapi ENC data (statuses, roles, nodes), managed via Terraform/Terragrunt. Supersedes Cobbler node classification for Puppet.
Single source of truth for [encapi](https://git.unkin.net/unkin/encapi) ENC
data, managed with Terraform/Terragrunt. This repo owns every Puppet node
classification the estate has:
- **statuses** — Puppet environments (`production`, `develop`, `testing`)
- **roles** — the Puppet role classes (`roles::…`) a node can be assigned
- **nodes** — the certname → role + environment mapping for all 143 hosts
(130 container VMs + 13 `prodnxsr*` physicals)
It **supersedes Cobbler** as the classification source for Puppet: encapi is the
live ENC, and this repo is the declarative, reviewed input to it. It also
supersedes the dual-write approach that was prototyped in terraform-incus
(PR #39) — terraform-incus is no longer involved in ENC data.
## Layout
```
config/
root.hcl # terragrunt root: consul backend (infra/terraform/enc/<leaf>/state)
encapi/
terragrunt.hcl # leaf: feeds the three YAMLs into modules/encapi
statuses.yaml # Puppet environments
roles.yaml # Puppet role classes
nodes.yaml # certname -> {role, environment} for all 143 hosts
modules/
encapi/ # encapi_status / encapi_role / encapi_node resources,
# ordered so nodes depend on their role + status (FK)
ci/
extract_incus_nodes.py # regenerates the container-VM block of nodes.yaml
# from a terraform-incus checkout
```
The encapi provider comes from the internal registry
(`artifactapi.k8s.syd1.au.unkin.net/terraform-unkin/encapi` v0.1.0). Writes to
encapi are authenticated with `ENCAPI_WRITE_TOKEN`, read from Vault by the
Makefile.
## Data provenance
- **130 container VMs** (`environment: production`) are extracted from
terraform-incus `config/instances/<host>/config.yaml`: certname is
`<host>.main.unkin.net`, role is `cobbler_mgmt_classes[0]` (default
`roles::base`). terraform-incus itself is not modified — the data is copied in
here as plain config. Re-run `ci/extract_incus_nodes.py --incus-repo <path>`
to refresh after VM changes.
- **13 `prodnxsr*` physical nodes** (`environment: develop`) are the bare-metal
hypervisor / k8s nodes, not managed by the incus module. Their roles come from
the PuppetDB `enc_role` fact and are maintained by hand in `nodes.yaml`.
## Usage
CI (Woodpecker) plans on every PR and applies on merge to `main`, authenticating
to Vault via kubernetes auth (`terraform-enc` ServiceAccount). Locally:
```sh
make plan # approle auth by default (needs env with VAULT_ROLEID)
make apply
```
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""Regenerate the container-VM portion of config/encapi/nodes.yaml from a
checkout of terraform-incus.
encapi is the single source of truth for Puppet ENC data, but the 130 container
VMs are *defined* in terraform-incus (config/instances/<host>/config.yaml). This
script lifts that classification into plain config data here:
certname = <dir basename>.<cobbler_domain> (cobbler_domain = main.unkin.net)
role = cobbler_mgmt_classes[0] (default roles::base)
environment = production
It only rewrites the "container VMs" block; the prodnxsr physical nodes below
the marker are maintained by hand and left untouched.
Usage:
python3 ci/extract_incus_nodes.py --incus-repo /path/to/terraform-incus
"""
import argparse
import os
import sys
try:
import yaml
except ImportError:
sys.exit("PyYAML required: pip install pyyaml")
PHYS_MARKER = "# --- prodnxsr physical nodes (develop) ---"
VM_MARKER = "# --- container VMs (production) ---"
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--incus-repo", required=True,
help="Path to a terraform-incus checkout")
ap.add_argument("--domain", default="main.unkin.net")
ap.add_argument("--nodes-file",
default=os.path.join(os.path.dirname(__file__),
"..", "config", "encapi", "nodes.yaml"))
args = ap.parse_args()
inst = os.path.join(args.incus_repo, "config", "instances")
dirs = sorted(d for d in os.listdir(inst)
if os.path.isdir(os.path.join(inst, d))
and os.path.exists(os.path.join(inst, d, "config.yaml")))
vm_lines = []
for d in dirs:
with open(os.path.join(inst, d, "config.yaml")) as f:
cfg = yaml.safe_load(f) or {}
classes = cfg.get("cobbler_mgmt_classes") or []
role = classes[0] if classes else "roles::base"
vm_lines.append(f"{d}.{args.domain}:")
vm_lines.append(f" role: {role}")
vm_lines.append(" environment: production")
nodes_file = os.path.abspath(args.nodes_file)
with open(nodes_file) as f:
existing = f.read().splitlines()
# Preserve everything from the physical-nodes marker onward.
try:
idx = existing.index(PHYS_MARKER)
except ValueError:
sys.exit(f"marker not found in {nodes_file}: {PHYS_MARKER!r}")
tail = existing[idx:]
# Preserve only the top-of-file comment block, stopping at the VM marker.
header = []
for ln in existing[:idx]:
if ln == VM_MARKER:
break
header.append(ln)
while header and header[-1] == "":
header.pop()
out = header + ["", VM_MARKER] + vm_lines + ["", *tail]
with open(nodes_file, "w") as f:
f.write("\n".join(out).rstrip("\n") + "\n")
print(f"wrote {len(dirs)} container VM records to {nodes_file}")
if __name__ == "__main__":
main()
+444
View File
@@ -0,0 +1,444 @@
# encapi node classifications.
#
# 130 container VMs (environment=production): extracted from
# terraform-incus config/instances/<host>/config.yaml. certname is
# <host>.main.unkin.net (dir basename + cobbler_domain), role is
# cobbler_mgmt_classes[0] (defaulting to roles::base if absent). See
# ci/extract_incus_nodes.py, which regenerates this section.
#
# 13 prodnxsr* physical hypervisor/k8s nodes (environment=develop): NOT
# managed by the incus instance module; roles come from the PuppetDB
# enc_role fact. Maintained by hand below.
# --- container VMs (production) ---
ausyd1nxvm2000.main.unkin.net:
role: roles::infra::storage::vault
environment: production
ausyd1nxvm2001.main.unkin.net:
role: roles::infra::storage::vault
environment: production
ausyd1nxvm2002.main.unkin.net:
role: roles::infra::storage::vault
environment: production
ausyd1nxvm2003.main.unkin.net:
role: roles::infra::storage::vault
environment: production
ausyd1nxvm2004.main.unkin.net:
role: roles::infra::storage::vault
environment: production
ausyd1nxvm2005.main.unkin.net:
role: roles::infra::storage::consul
environment: production
ausyd1nxvm2006.main.unkin.net:
role: roles::infra::storage::consul
environment: production
ausyd1nxvm2007.main.unkin.net:
role: roles::infra::storage::consul
environment: production
ausyd1nxvm2008.main.unkin.net:
role: roles::infra::storage::consul
environment: production
ausyd1nxvm2009.main.unkin.net:
role: roles::infra::storage::consul
environment: production
ausyd1nxvm2010.main.unkin.net:
role: roles::infra::puppetdb::api
environment: production
ausyd1nxvm2011.main.unkin.net:
role: roles::infra::puppetdb::api
environment: production
ausyd1nxvm2012.main.unkin.net:
role: roles::infra::puppetdb::api
environment: production
ausyd1nxvm2013.main.unkin.net:
role: roles::infra::puppetboard::server
environment: production
ausyd1nxvm2014.main.unkin.net:
role: roles::infra::puppetboard::server
environment: production
ausyd1nxvm2015.main.unkin.net:
role: roles::infra::metrics::grafana
environment: production
ausyd1nxvm2016.main.unkin.net:
role: roles::infra::metrics::grafana
environment: production
ausyd1nxvm2017.main.unkin.net:
role: roles::infra::auth::glauth
environment: production
ausyd1nxvm2018.main.unkin.net:
role: roles::infra::auth::glauth
environment: production
ausyd1nxvm2019.main.unkin.net:
role: roles::infra::auth::glauth
environment: production
ausyd1nxvm2020.main.unkin.net:
role: roles::infra::proxy::jumphost
environment: production
ausyd1nxvm2021.main.unkin.net:
role: roles::infra::proxy::jumphost
environment: production
ausyd1nxvm2022.main.unkin.net:
role: roles::infra::proxy::jumphost
environment: production
ausyd1nxvm2023.main.unkin.net:
role: roles::infra::git::runner
environment: production
ausyd1nxvm2024.main.unkin.net:
role: roles::infra::git::runner
environment: production
ausyd1nxvm2025.main.unkin.net:
role: roles::infra::git::runner
environment: production
ausyd1nxvm2026.main.unkin.net:
role: roles::infra::nomad::server
environment: production
ausyd1nxvm2027.main.unkin.net:
role: roles::infra::nomad::server
environment: production
ausyd1nxvm2028.main.unkin.net:
role: roles::infra::nomad::server
environment: production
ausyd1nxvm2029.main.unkin.net:
role: roles::infra::dns::master
environment: production
ausyd1nxvm2030.main.unkin.net:
role: roles::infra::dns::master
environment: production
ausyd1nxvm2031.main.unkin.net:
role: roles::infra::dns::master
environment: production
ausyd1nxvm2032.main.unkin.net:
role: roles::infra::dns::resolver
environment: production
ausyd1nxvm2033.main.unkin.net:
role: roles::infra::dns::resolver
environment: production
ausyd1nxvm2034.main.unkin.net:
role: roles::infra::dns::resolver
environment: production
ausyd1nxvm2035.main.unkin.net:
role: roles::apps::music::gonic
environment: production
ausyd1nxvm2036.main.unkin.net:
role: roles::apps::music::gonic
environment: production
ausyd1nxvm2037.main.unkin.net:
role: roles::apps::music::gonic
environment: production
ausyd1nxvm2038.main.unkin.net:
role: roles::infra::metrics::prometheus
environment: production
ausyd1nxvm2039.main.unkin.net:
role: roles::infra::metrics::prometheus
environment: production
ausyd1nxvm2040.main.unkin.net:
role: roles::infra::nomad::agentv2
environment: production
ausyd1nxvm2041.main.unkin.net:
role: roles::infra::nomad::agentv2
environment: production
ausyd1nxvm2042.main.unkin.net:
role: roles::infra::nomad::agentv2
environment: production
ausyd1nxvm2043.main.unkin.net:
role: roles::infra::nomad::agentv2
environment: production
ausyd1nxvm2044.main.unkin.net:
role: roles::infra::nomad::agentv2
environment: production
ausyd1nxvm2045.main.unkin.net:
role: roles::apps::media::nzbget
environment: production
ausyd1nxvm2046.main.unkin.net:
role: roles::apps::media::sonarr
environment: production
ausyd1nxvm2047.main.unkin.net:
role: roles::apps::media::radarr
environment: production
ausyd1nxvm2048.main.unkin.net:
role: roles::apps::media::lidarr
environment: production
ausyd1nxvm2049.main.unkin.net:
role: roles::apps::media::readarr
environment: production
ausyd1nxvm2050.main.unkin.net:
role: roles::apps::media::prowlarr
environment: production
ausyd1nxvm2051.main.unkin.net:
role: roles::apps::media::jellyfin
environment: production
ausyd1nxvm2052.main.unkin.net:
role: roles::infra::puppet::master
environment: production
ausyd1nxvm2053.main.unkin.net:
role: roles::infra::puppet::master
environment: production
ausyd1nxvm2054.main.unkin.net:
role: roles::infra::puppet::master
environment: production
ausyd1nxvm2055.main.unkin.net:
role: roles::infra::puppet::master
environment: production
ausyd1nxvm2056.main.unkin.net:
role: roles::infra::puppet::master
environment: production
ausyd1nxvm2057.main.unkin.net:
role: roles::infra::pki::certbot
environment: production
ausyd1nxvm2058.main.unkin.net:
role: roles::infra::reposync::syncer
environment: production
ausyd1nxvm2059.main.unkin.net:
role: roles::infra::reposync::repo
environment: production
ausyd1nxvm2060.main.unkin.net:
role: roles::infra::reposync::repo
environment: production
ausyd1nxvm2061.main.unkin.net:
role: roles::infra::reposync::repo
environment: production
ausyd1nxvm2062.main.unkin.net:
role: roles::infra::incus::imagehost
environment: production
ausyd1nxvm2063.main.unkin.net:
role: roles::infra::storage::edgecache
environment: production
ausyd1nxvm2064.main.unkin.net:
role: roles::infra::storage::edgecache
environment: production
ausyd1nxvm2065.main.unkin.net:
role: roles::infra::puppetdb::sql
environment: production
ausyd1nxvm2066.main.unkin.net:
role: roles::infra::puppetdb::sql
environment: production
ausyd1nxvm2067.main.unkin.net:
role: roles::infra::puppetdb::sql
environment: production
ausyd1nxvm2068.main.unkin.net:
role: roles::apps::jupyter::hub
environment: production
ausyd1nxvm2069.main.unkin.net:
role: roles::infra::halb::haproxy2
environment: production
ausyd1nxvm2070.main.unkin.net:
role: roles::infra::halb::haproxy2
environment: production
ausyd1nxvm2071.main.unkin.net:
role: roles::infra::halb::haproxy2
environment: production
ausyd1nxvm2072.main.unkin.net:
role: roles::infra::halb::haproxy2
environment: production
ausyd1nxvm2073.main.unkin.net:
role: roles::infra::halb::haproxy2
environment: production
ausyd1nxvm2074.main.unkin.net:
role: roles::infra::dhcp::server
environment: production
ausyd1nxvm2075.main.unkin.net:
role: roles::infra::dhcp::server
environment: production
ausyd1nxvm2076.main.unkin.net:
role: roles::infra::dhcp::server
environment: production
ausyd1nxvm2077.main.unkin.net:
role: roles::infra::sql::shared
environment: production
ausyd1nxvm2078.main.unkin.net:
role: roles::infra::sql::shared
environment: production
ausyd1nxvm2079.main.unkin.net:
role: roles::infra::sql::shared
environment: production
ausyd1nxvm2080.main.unkin.net:
role: roles::infra::git::server
environment: production
ausyd1nxvm2081.main.unkin.net:
role: roles::infra::git::server
environment: production
ausyd1nxvm2082.main.unkin.net:
role: roles::infra::git::server
environment: production
ausyd1nxvm2083.main.unkin.net:
role: roles::infra::git::redis
environment: production
ausyd1nxvm2084.main.unkin.net:
role: roles::infra::git::redis
environment: production
ausyd1nxvm2085.main.unkin.net:
role: roles::infra::git::redis
environment: production
ausyd1nxvm2086.main.unkin.net:
role: roles::infra::metrics::vmstorage
environment: production
ausyd1nxvm2087.main.unkin.net:
role: roles::infra::metrics::vmstorage
environment: production
ausyd1nxvm2088.main.unkin.net:
role: roles::infra::metrics::vmstorage
environment: production
ausyd1nxvm2089.main.unkin.net:
role: roles::infra::metrics::vmstorage
environment: production
ausyd1nxvm2090.main.unkin.net:
role: roles::infra::metrics::vmstorage
environment: production
ausyd1nxvm2091.main.unkin.net:
role: roles::infra::metrics::vminsert
environment: production
ausyd1nxvm2092.main.unkin.net:
role: roles::infra::metrics::vminsert
environment: production
ausyd1nxvm2093.main.unkin.net:
role: roles::infra::metrics::vminsert
environment: production
ausyd1nxvm2094.main.unkin.net:
role: roles::infra::metrics::vmselect
environment: production
ausyd1nxvm2095.main.unkin.net:
role: roles::infra::metrics::vmselect
environment: production
ausyd1nxvm2096.main.unkin.net:
role: roles::infra::metrics::vmselect
environment: production
ausyd1nxvm2097.main.unkin.net:
role: roles::infra::puppet::master
environment: production
ausyd1nxvm2098.main.unkin.net:
role: roles::infra::cobbler::server
environment: production
ausyd1nxvm2099.main.unkin.net:
role: roles::infra::metrics::vmagent
environment: production
ausyd1nxvm2100.main.unkin.net:
role: roles::infra::metrics::vmagent
environment: production
ausyd1nxvm2101.main.unkin.net:
role: roles::infra::metrics::vmagent
environment: production
ausyd1nxvm2102.main.unkin.net:
role: roles::infra::logs::vlinsert
environment: production
ausyd1nxvm2103.main.unkin.net:
role: roles::infra::logs::vlinsert
environment: production
ausyd1nxvm2104.main.unkin.net:
role: roles::infra::logs::vlinsert
environment: production
ausyd1nxvm2105.main.unkin.net:
role: roles::infra::logs::vlselect
environment: production
ausyd1nxvm2106.main.unkin.net:
role: roles::infra::logs::vlselect
environment: production
ausyd1nxvm2107.main.unkin.net:
role: roles::infra::logs::vlselect
environment: production
ausyd1nxvm2108.main.unkin.net:
role: roles::infra::logs::vlstorage
environment: production
ausyd1nxvm2109.main.unkin.net:
role: roles::infra::logs::vlstorage
environment: production
ausyd1nxvm2110.main.unkin.net:
role: roles::infra::logs::vlstorage
environment: production
ausyd1nxvm2111.main.unkin.net:
role: roles::infra::logs::vlstorage
environment: production
ausyd1nxvm2112.main.unkin.net:
role: roles::infra::logs::vlstorage
environment: production
ausyd1nxvm2113.main.unkin.net:
role: roles::infra::logs::vlagent
environment: production
ausyd1nxvm2114.main.unkin.net:
role: roles::infra::logs::vlagent
environment: production
ausyd1nxvm2115.main.unkin.net:
role: roles::infra::ceph::rgw
environment: production
ausyd1nxvm2116.main.unkin.net:
role: roles::infra::ceph::rgw
environment: production
ausyd1nxvm2117.main.unkin.net:
role: roles::infra::ceph::rgw
environment: production
ausyd1nxvm2118.main.unkin.net:
role: roles::infra::ceph::rgw
environment: production
ausyd1nxvm2119.main.unkin.net:
role: roles::infra::ceph::rgw
environment: production
ausyd1nxvm2120.main.unkin.net:
role: roles::base
environment: production
ausyd1nxvm2121.main.unkin.net:
role: roles::infra::mail::gateway
environment: production
ausyd1nxvm2122.main.unkin.net:
role: roles::infra::mail::gateway
environment: production
ausyd1nxvm2123.main.unkin.net:
role: roles::infra::mail::gateway
environment: production
ausyd1nxvm2124.main.unkin.net:
role: roles::infra::mail::backend
environment: production
ausyd1nxvm2125.main.unkin.net:
role: roles::infra::mail::backend
environment: production
ausyd1nxvm2126.main.unkin.net:
role: roles::infra::mail::backend
environment: production
ausyd1nxvm2127.main.unkin.net:
role: roles::infra::dns::externaldns
environment: production
ausyd1nxvm2128.main.unkin.net:
role: roles::infra::dns::externaldns
environment: production
ausyd1nxvm2129.main.unkin.net:
role: roles::infra::dns::externaldns
environment: production
# --- prodnxsr physical nodes (develop) ---
prodnxsr0001.main.unkin.net:
role: roles::infra::k8s::control
environment: develop
prodnxsr0002.main.unkin.net:
role: roles::infra::k8s::control
environment: develop
prodnxsr0003.main.unkin.net:
role: roles::infra::k8s::control
environment: develop
prodnxsr0004.main.unkin.net:
role: roles::infra::k8s::compute
environment: develop
prodnxsr0005.main.unkin.net:
role: roles::infra::k8s::compute
environment: develop
prodnxsr0006.main.unkin.net:
role: roles::infra::k8s::compute
environment: develop
prodnxsr0007.main.unkin.net:
role: roles::infra::k8s::compute
environment: develop
prodnxsr0008.main.unkin.net:
role: roles::infra::k8s::compute
environment: develop
prodnxsr0009.main.unkin.net:
role: roles::infra::incus::node
environment: develop
prodnxsr0010.main.unkin.net:
role: roles::infra::incus::node
environment: develop
prodnxsr0011.main.unkin.net:
role: roles::infra::incus::node
environment: develop
prodnxsr0012.main.unkin.net:
role: roles::infra::incus::node
environment: develop
prodnxsr0013.main.unkin.net:
role: roles::infra::incus::node
environment: develop
+55
View File
@@ -0,0 +1,55 @@
# encapi roles: every distinct Puppet role class used across
# config/instances/*/config.yaml (cobbler_mgmt_classes[0]) PLUS the roles
# run by the 13 prodnxsr physical nodes (from PuppetDB enc_role fact).
# Values are empty maps; add description/default_params here when needed.
roles::apps::jupyter::hub: {}
roles::apps::media::jellyfin: {}
roles::apps::media::lidarr: {}
roles::apps::media::nzbget: {}
roles::apps::media::prowlarr: {}
roles::apps::media::radarr: {}
roles::apps::media::readarr: {}
roles::apps::media::sonarr: {}
roles::apps::music::gonic: {}
roles::base: {}
roles::infra::auth::glauth: {}
roles::infra::ceph::rgw: {}
roles::infra::cobbler::server: {}
roles::infra::dhcp::server: {}
roles::infra::dns::externaldns: {}
roles::infra::dns::master: {}
roles::infra::dns::resolver: {}
roles::infra::git::redis: {}
roles::infra::git::runner: {}
roles::infra::git::server: {}
roles::infra::halb::haproxy2: {}
roles::infra::incus::imagehost: {}
roles::infra::incus::node: {}
roles::infra::k8s::compute: {}
roles::infra::k8s::control: {}
roles::infra::logs::vlagent: {}
roles::infra::logs::vlinsert: {}
roles::infra::logs::vlselect: {}
roles::infra::logs::vlstorage: {}
roles::infra::mail::backend: {}
roles::infra::mail::gateway: {}
roles::infra::metrics::grafana: {}
roles::infra::metrics::prometheus: {}
roles::infra::metrics::vmagent: {}
roles::infra::metrics::vminsert: {}
roles::infra::metrics::vmselect: {}
roles::infra::metrics::vmstorage: {}
roles::infra::nomad::agentv2: {}
roles::infra::nomad::server: {}
roles::infra::pki::certbot: {}
roles::infra::proxy::jumphost: {}
roles::infra::puppetboard::server: {}
roles::infra::puppetdb::api: {}
roles::infra::puppetdb::sql: {}
roles::infra::puppet::master: {}
roles::infra::reposync::repo: {}
roles::infra::reposync::syncer: {}
roles::infra::sql::shared: {}
roles::infra::storage::consul: {}
roles::infra::storage::edgecache: {}
roles::infra::storage::vault: {}
+10
View File
@@ -0,0 +1,10 @@
# encapi statuses == Puppet environments (Cobbler "status").
# Seeded from what the estate actually runs (all nodes today report
# catalog_environment=develop) plus production (the environment the incus
# instance module pins via puppetca_certificate) and testing (implicit).
production:
description: Production environment
develop:
description: Development environment (current default across the estate)
testing:
description: Implicit/transient environment
+19
View File
@@ -0,0 +1,19 @@
locals {
statuses = yamldecode(file("${get_terragrunt_dir()}/statuses.yaml"))
roles = yamldecode(file("${get_terragrunt_dir()}/roles.yaml"))
nodes = yamldecode(file("${get_terragrunt_dir()}/nodes.yaml"))
}
include "root" {
path = find_in_parent_folders("root.hcl")
}
terraform {
source = "${get_repo_root()}/modules/encapi"
}
inputs = {
statuses = local.statuses
roles = local.roles
nodes = local.nodes
}
+15
View File
@@ -0,0 +1,15 @@
generate "backend" {
path = "backend.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
terraform {
backend "consul" {
address = "https://consul.service.consul"
path = "infra/terraform/enc/$${path_relative_to_include()}/state"
scheme = "https"
lock = true
ca_file = "/etc/pki/tls/certs/ca-bundle.crt"
}
}
EOF
}
+29
View File
@@ -0,0 +1,29 @@
resource "encapi_status" "this" {
for_each = var.statuses
name = each.key
description = each.value.description
}
resource "encapi_role" "this" {
for_each = var.roles
name = each.key
description = each.value.description
default_params = each.value.default_params == null ? null : jsonencode(each.value.default_params)
}
resource "encapi_node" "this" {
for_each = var.nodes
certname = each.key
role = each.value.role
environment = each.value.environment
params = each.value.params == null ? null : jsonencode(each.value.params)
# Nodes FK-require their role and status to exist first.
depends_on = [
encapi_role.this,
encapi_status.this,
]
}
+10
View File
@@ -0,0 +1,10 @@
provider "encapi" {
endpoint = var.encapi_endpoint
# token defaults to the ENCAPI_WRITE_TOKEN environment variable
}
variable "encapi_endpoint" {
description = "The encapi server base URL."
type = string
default = "https://encapi.k8s.syd1.au.unkin.net"
}
+37
View File
@@ -0,0 +1,37 @@
variable "statuses" {
description = <<EOT
Map of encapi statuses (Puppet environments) to seed. Keyed by status name.
Each value may carry an optional description.
EOT
type = map(object({
description = optional(string)
}))
default = {}
}
variable "roles" {
description = <<EOT
Map of encapi roles to seed. Keyed by role class name (e.g. roles::base).
Each value may carry an optional description and a default_params object that
is jsonencode()'d into the role's inheritable defaults.
EOT
type = map(object({
description = optional(string)
default_params = optional(any)
}))
default = {}
}
variable "nodes" {
description = <<EOT
Map of encapi node assignments to seed. Keyed by certname. Each value pins the
node to a role and environment (which must exist as a role/status above), with
optional per-node params jsonencode()'d into the ENC output.
EOT
type = map(object({
role = string
environment = string
params = optional(any)
}))
default = {}
}
+8
View File
@@ -0,0 +1,8 @@
terraform {
required_providers {
encapi = {
source = "artifactapi.k8s.syd1.au.unkin.net/terraform-unkin/encapi"
version = "0.1.0"
}
}
}