Turn talos patches into templates and resolve them

This commit is contained in:
2025-11-08 05:47:24 +01:00
parent 0642fde397
commit 3290a76193
11 changed files with 64 additions and 15 deletions

3
.prettierrc Normal file
View File

@@ -0,0 +1,3 @@
{
"bracketSpacing": false
}

View File

@@ -18,6 +18,12 @@ dns:
- 1.1.1.1 - 1.1.1.1
- 8.8.8.8 - 8.8.8.8
ntp: nl.pool.ntp.org ntp: nl.pool.ntp.org
installDisk: /dev/sda
install: false install: false
patch: !patch patches:
- hostname - !patch hostname
- !patch install-disk
- !patch network
- !patch vip
patchesControlplane:
- !patch allow-controlplane-workloads

View File

@@ -1,3 +1,4 @@
netmask: 255.255.252.0 netmask: 255.255.252.0
gateway: 10.0.0.1 gateway: 10.0.0.1
install: true install: true
controlplaneIp: 10.0.2.1

View File

@@ -1,2 +1,5 @@
netmask: 255.255.255.0 netmask: 255.255.255.0
gateway: 192.168.1.1 gateway: 192.168.1.1
clusterName: testing
controlplaneIp: 192.168.1.100
instalDisk: /dev/vda

View File

@@ -1,4 +1,4 @@
serial: talos-vm serial: talos-vm
interface: enp1s0 interface: eth0
ip: 192.168.1.2 ip: 192.168.1.2
install: true install: true

View File

@@ -1,4 +1,4 @@
--- ---
machine: machine:
network: network:
hostname: talos-vm hostname: {{hostname}}

View File

@@ -1,4 +1,4 @@
--- ---
machine: machine:
install: install:
disk: /dev/vda disk: {{installDisk}}

View File

@@ -2,10 +2,10 @@
machine: machine:
network: network:
interfaces: interfaces:
- interface: eth0 - interface: {{interface}}
dhcp: false dhcp: false
addresses: addresses:
- 192.168.1.2 - {{ip}}
routes: routes:
- network: 0.0.0.0/0 - network: 0.0.0.0/0
gateway: 192.168.1.1 gateway: {{gateway}}

View File

@@ -2,6 +2,6 @@
machine: machine:
network: network:
interfaces: interfaces:
- interface: eth0 - interface: {{interface}}
vip: vip:
ip: 192.168.1.100 ip: {{controlplaneIp}}

View File

@@ -1,2 +1,3 @@
PyYAML==6.0.3 PyYAML==6.0.3
requests==2.32.5 requests==2.32.5
Jinja2==3.1.6

View File

@@ -8,9 +8,29 @@ import pathlib
import requests import requests
import yaml import yaml
from jinja2 import Environment, FileSystemLoader, StrictUndefined, Template
NODES = pathlib.Path("nodes") NODES = pathlib.Path("nodes")
SCHEMATICS = pathlib.Path("schematics") SCHEMATICS = pathlib.Path("schematics")
PATCHES = Environment(loader=FileSystemLoader("patches"), undefined=StrictUndefined)
TEMPLATES = Environment(loader=FileSystemLoader("templates"), undefined=StrictUndefined)
def node_encoder(node: dict):
class Inner(json.JSONEncoder):
def default(self, o):
if isinstance(o, Template):
try:
rendered = o.render(node)
except Exception as e:
e.add_note(f"While rendering for: {node['hostname']}")
raise e
# Parse the rendered yaml and convert it to a json patch
return json.dumps(yaml.safe_load(rendered))
return super().default(o)
return Inner
@functools.cache @functools.cache
@@ -24,18 +44,28 @@ def get_schematic_id(schematic: str):
def schematic_constructor(loader: yaml.SafeLoader, node: yaml.nodes.ScalarNode): def schematic_constructor(loader: yaml.SafeLoader, node: yaml.nodes.ScalarNode):
"""Load specified schematic file and get the assocatied schematic id""" """Load specified schematic file and get the assocatied schematic id"""
filename = loader.construct_scalar(node) schematic_name = loader.construct_yaml_str(node)
try: try:
schematic = SCHEMATICS.joinpath(filename).with_suffix(".yaml").read_text() schematic = SCHEMATICS.joinpath(schematic_name).with_suffix(".yaml").read_text()
return get_schematic_id(schematic) return get_schematic_id(schematic)
except Exception: except Exception:
raise yaml.MarkedYAMLError("Failed to load schematic", node.start_mark) raise yaml.MarkedYAMLError("Failed to load schematic", node.start_mark)
def patch_constructor(loader: yaml.SafeLoader, node: yaml.nodes.ScalarNode):
patch_name = loader.construct_scalar(node)
try:
template = PATCHES.get_template(f"{patch_name}.yaml")
return template
except Exception:
raise yaml.MarkedYAMLError("Failed to load patch", node.start_mark)
def get_loader(): def get_loader():
"""Add special constructors to yaml loader""" """Add special constructors to yaml loader"""
loader = yaml.SafeLoader loader = yaml.SafeLoader
loader.add_constructor("!schematic", schematic_constructor) loader.add_constructor("!schematic", schematic_constructor)
loader.add_constructor("!patch", patch_constructor)
return loader return loader
@@ -65,7 +95,7 @@ def walk_files(root: pathlib.Path):
def main(): def main():
data = [] nodes = []
for fullname in walk_files(NODES): for fullname in walk_files(NODES):
filename = str(fullname.relative_to(NODES).parent) + "/" + fullname.stem filename = str(fullname.relative_to(NODES).parent) + "/" + fullname.stem
@@ -74,10 +104,15 @@ def main():
yml_data = get_defaults(fullname.parent, NODES) | yml_data yml_data = get_defaults(fullname.parent, NODES) | yml_data
yml_data["hostname"] = fullname.stem yml_data["hostname"] = fullname.stem
yml_data["filename"] = filename yml_data["filename"] = filename
data.append(yml_data) nodes.append(yml_data)
final_nodes = []
for node in nodes:
# Quick and dirty way to resolve all the templates using a custom encoder
final_nodes.append(json.loads(json.dumps(node, cls=node_encoder(node))))
# Dump everything to json # Dump everything to json
print(json.dumps(data, indent=4)) print(json.dumps(final_nodes, indent=4))
if __name__ == "__main__": if __name__ == "__main__":