{ lib, config, pkgs, ... }: with lib; let cfg = config.roles.k3s-node; in { options.roles.k3s-node = { enable = mkEnableOption "Enable k3s node"; role = mkOption { type = types.enum [ "server" "agent" ]; default = "agent"; description = "k3s role: server (control plane) or agent (worker)"; }; serverAddr = mkOption { type = types.str; default = "https://10.0.0.222:6443"; description = "URL of k3s server to join (required for agents, used for HA servers)"; }; tokenFile = mkOption { type = types.path; default = "/etc/k3s/token"; description = "Path to file containing the cluster join token"; }; clusterInit = mkOption { type = types.bool; default = false; description = "Initialize a new cluster (first server only)"; }; extraFlags = mkOption { type = types.listOf types.str; default = []; description = "Additional flags to pass to k3s"; }; gracefulNodeShutdown = mkOption { type = types.bool; default = true; description = "Enable graceful node shutdown"; }; openFirewall = mkOption { type = types.bool; default = true; description = "Open firewall ports for k3s"; }; }; config = mkIf cfg.enable { # k3s service configuration services.k3s = { enable = true; role = cfg.role; tokenFile = cfg.tokenFile; extraFlags = cfg.extraFlags; gracefulNodeShutdown.enable = cfg.gracefulNodeShutdown; serverAddr = if (cfg.role == "agent" || !cfg.clusterInit) then cfg.serverAddr else ""; clusterInit = cfg.role == "server" && cfg.clusterInit; }; # Firewall rules for k3s networking.firewall = mkIf cfg.openFirewall { allowedTCPPorts = [ 6443 # k3s API server 10250 # kubelet metrics ] ++ optionals (cfg.role == "server") [ 2379 # etcd clients (HA) 2380 # etcd peers (HA) ]; allowedUDPPorts = [ 8472 # flannel VXLAN ]; }; }; }