feat: add a few more config validations
Signed-off-by: Devin Buhl <devin@buhl.casa>
This commit is contained in:
1 file changed
+67
@@ -3,6 +3,8 @@ from shutil import which
|
|||||||
from typing import Callable, cast
|
from typing import Callable, cast
|
||||||
from zoneinfo import available_timezones
|
from zoneinfo import available_timezones
|
||||||
import netaddr
|
import netaddr
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
DISTRIBUTIONS = ["k3s", "talos"]
|
DISTRIBUTIONS = ["k3s", "talos"]
|
||||||
@@ -31,6 +33,46 @@ def validate_python_version() -> None:
|
|||||||
raise ValueError(f"Python {sys.version_info} is below 3.11. Please upgrade.")
|
raise ValueError(f"Python {sys.version_info} is below 3.11. Please upgrade.")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_ip(ip: str) -> str:
|
||||||
|
try:
|
||||||
|
netaddr.IPAddress(ip)
|
||||||
|
except netaddr.core.AddrFormatError as e:
|
||||||
|
raise ValueError(f"Invalid IP address {ip}") from e
|
||||||
|
return ip
|
||||||
|
|
||||||
|
|
||||||
|
def validate_network(cidr: str, family: int) -> str:
|
||||||
|
try:
|
||||||
|
network = netaddr.IPNetwork(cidr)
|
||||||
|
if network.version != family:
|
||||||
|
raise ValueError(f"Invalid CIDR family {network.version}")
|
||||||
|
except netaddr.core.AddrFormatError as e:
|
||||||
|
raise ValueError(f"Invalid CIDR {cidr}") from e
|
||||||
|
return cidr
|
||||||
|
|
||||||
|
|
||||||
|
def validate_node(node: dict, node_cidr: str, distribution: str) -> None:
|
||||||
|
if not node.get("name"):
|
||||||
|
raise ValueError(f"A node is missing a name")
|
||||||
|
if not re.match(r"^[a-z0-9-\.]+$", node.get('name')):
|
||||||
|
raise ValueError(f"Node {node.get('name')} has an invalid name")
|
||||||
|
if not node.get("ssh_user") and distribution not in ["k3s"]:
|
||||||
|
raise ValueError(f"Node {node.get('name')} is missing ssh_user")
|
||||||
|
if not node.get("talos_disk") and distribution in ["talos"]:
|
||||||
|
raise ValueError(f"Node {node.get('name')} is missing talos_disk")
|
||||||
|
if not node.get("talos_nic") and distribution in ["talos"]:
|
||||||
|
raise ValueError(f"Node {node.get('name')} is missing talos_nic")
|
||||||
|
ip = validate_ip(node.get("address"))
|
||||||
|
if netaddr.IPAddress(ip, 4) not in netaddr.IPNetwork(node_cidr):
|
||||||
|
raise ValueError(f"Node {node.get('name')} is not in the node CIDR {node_cidr}")
|
||||||
|
port = 50000 if distribution == "talos" else 22
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||||
|
sock.settimeout(5)
|
||||||
|
result = sock.connect_ex((ip, port))
|
||||||
|
if result != 0:
|
||||||
|
raise ValueError(f"Node {node.get('name')} port {port} is not open")
|
||||||
|
|
||||||
|
|
||||||
@required("bootstrap_distribution", "bootstrap_cloudflare")
|
@required("bootstrap_distribution", "bootstrap_cloudflare")
|
||||||
def validate_cli_tools(distribution: str, cloudflare: dict, **_) -> None:
|
def validate_cli_tools(distribution: str, cloudflare: dict, **_) -> None:
|
||||||
if distribution not in DISTRIBUTIONS:
|
if distribution not in DISTRIBUTIONS:
|
||||||
@@ -63,8 +105,33 @@ def validate_timezone(timezone: str, **_) -> None:
|
|||||||
raise ValueError(f"Invalid timezone {timezone}")
|
raise ValueError(f"Invalid timezone {timezone}")
|
||||||
|
|
||||||
|
|
||||||
|
@required("bootstrap_sops_age_pubkey")
|
||||||
|
def validate_age(key: str, **_) -> None:
|
||||||
|
if not re.match(r"^age1[a-z0-9]{0,58}$", key):
|
||||||
|
raise ValueError(f"Invalid Age public key {key}")
|
||||||
|
|
||||||
|
|
||||||
|
@required("bootstrap_node_network", "bootstrap_node_inventory", "bootstrap_distribution")
|
||||||
|
def validate_nodes(node_cidr: str, nodes: dict[list], distribution: str, **_) -> None:
|
||||||
|
node_cidr = validate_network(node_cidr, 4)
|
||||||
|
|
||||||
|
controllers = [node for node in nodes if node.get('controller') == True]
|
||||||
|
if len(controllers) < 1:
|
||||||
|
raise ValueError(f"Must have at least one controller node")
|
||||||
|
if len(controllers) % 2 == 0:
|
||||||
|
raise ValueError(f"Must have an odd number of controller nodes")
|
||||||
|
for node in controllers:
|
||||||
|
validate_node(node, node_cidr, distribution)
|
||||||
|
|
||||||
|
workers = [node for node in nodes if node.get('controller') == False]
|
||||||
|
for node in workers:
|
||||||
|
validate_node(node, node_cidr, distribution)
|
||||||
|
|
||||||
|
|
||||||
def validate(data: dict) -> None:
|
def validate(data: dict) -> None:
|
||||||
validate_python_version()
|
validate_python_version()
|
||||||
validate_cli_tools(data)
|
validate_cli_tools(data)
|
||||||
validate_distribution(data)
|
validate_distribution(data)
|
||||||
validate_timezone(data)
|
validate_timezone(data)
|
||||||
|
validate_age(data)
|
||||||
|
validate_nodes(data)
|
||||||
Reference in new issue
Block a user