homelab framework module init (everything is a mess)
This commit is contained in:
parent
0347f4d325
commit
bcbcc8b17b
94 changed files with 7289 additions and 436 deletions
462
modules/nixos/global-config.nix
Normal file
462
modules/nixos/global-config.nix
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
# modules/global-config.nix
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
outputs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
cfg = config.homelab.global;
|
||||
|
||||
# Service type definition
|
||||
serviceType = types.submodule {
|
||||
options = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Enable this service";
|
||||
};
|
||||
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
description = "Human-readable description of the service";
|
||||
};
|
||||
|
||||
category = mkOption {
|
||||
type = types.enum ["monitoring" "networking" "storage" "security" "media" "development" "backup" "other"];
|
||||
default = "other";
|
||||
description = "Service category for organization";
|
||||
};
|
||||
|
||||
dependencies = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "List of other homelab services this depends on";
|
||||
};
|
||||
|
||||
ports = mkOption {
|
||||
type = types.listOf types.port;
|
||||
default = [];
|
||||
description = "Ports this service uses";
|
||||
};
|
||||
|
||||
tags = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "Additional tags for this service";
|
||||
};
|
||||
|
||||
priority = mkOption {
|
||||
type = types.int;
|
||||
default = 100;
|
||||
description = "Service priority (lower numbers start first)";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Type definitions
|
||||
monitoringEndpointType = types.submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = "Name of the monitoring endpoint";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
description = "Port number for the endpoint";
|
||||
};
|
||||
path = mkOption {
|
||||
type = types.str;
|
||||
default = "/metrics";
|
||||
description = "Path for the metrics endpoint";
|
||||
};
|
||||
jobName = mkOption {
|
||||
type = types.str;
|
||||
description = "Prometheus job name";
|
||||
};
|
||||
scrapeInterval = mkOption {
|
||||
type = types.str;
|
||||
default = "30s";
|
||||
description = "Prometheus scrape interval";
|
||||
};
|
||||
labels = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
description = "Additional labels for this endpoint";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
backupJobType = types.submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = "Name of the backup job";
|
||||
};
|
||||
backend = mkOption {
|
||||
type = types.enum ["restic" "borg" "rclone"];
|
||||
description = "Backup backend to use";
|
||||
};
|
||||
paths = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "List of paths to backup";
|
||||
};
|
||||
schedule = mkOption {
|
||||
type = types.str;
|
||||
default = "daily";
|
||||
description = "Backup schedule (cron format or preset)";
|
||||
};
|
||||
retention = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {
|
||||
daily = "7";
|
||||
weekly = "4";
|
||||
monthly = "6";
|
||||
yearly = "2";
|
||||
};
|
||||
description = "Retention policy";
|
||||
};
|
||||
excludePatterns = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "Patterns to exclude from backup";
|
||||
};
|
||||
preHook = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Script to run before backup";
|
||||
};
|
||||
postHook = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Script to run after backup";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
reverseProxyEntryType = types.submodule {
|
||||
options = {
|
||||
subdomain = mkOption {
|
||||
type = types.str;
|
||||
description = "Subdomain for the service";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
description = "Internal port to proxy to";
|
||||
};
|
||||
path = mkOption {
|
||||
type = types.str;
|
||||
default = "/";
|
||||
description = "Path prefix for the service";
|
||||
};
|
||||
enableAuth = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Enable authentication for this service";
|
||||
};
|
||||
enableSSL = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable SSL for this service";
|
||||
};
|
||||
customHeaders = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
description = "Custom headers to add";
|
||||
};
|
||||
websockets = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Enable websocket support";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Helper functions for services
|
||||
enabledServices = filterAttrs (name: service: service.enable) cfg.services;
|
||||
servicesByCategory = category: filterAttrs (name: service: service.enable && service.category == category) cfg.services;
|
||||
in {
|
||||
imports = [
|
||||
./motd
|
||||
];
|
||||
|
||||
options.homelab.global = {
|
||||
enable = mkEnableOption "Global homelab configuration";
|
||||
|
||||
hostname = mkOption {
|
||||
type = types.str;
|
||||
description = "Hostname for this system";
|
||||
};
|
||||
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
default = "procopius.dk";
|
||||
description = "Base domain for the homelab";
|
||||
};
|
||||
|
||||
environment = mkOption {
|
||||
type = types.enum ["production" "staging" "development"];
|
||||
default = "production";
|
||||
description = "Environment type";
|
||||
};
|
||||
|
||||
location = mkOption {
|
||||
type = types.str;
|
||||
default = "homelab";
|
||||
description = "Physical location identifier";
|
||||
};
|
||||
|
||||
tags = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "Tags for this system";
|
||||
};
|
||||
|
||||
services = mkOption {
|
||||
type = types.attrsOf serviceType;
|
||||
default = {};
|
||||
description = "Homelab services configuration";
|
||||
example = literalExpression ''
|
||||
{
|
||||
prometheus = {
|
||||
enable = true;
|
||||
description = "Metrics collection and monitoring";
|
||||
category = "monitoring";
|
||||
ports = [ 9090 ];
|
||||
tags = [ "metrics" "alerting" ];
|
||||
};
|
||||
|
||||
traefik = {
|
||||
enable = true;
|
||||
description = "Reverse proxy and load balancer";
|
||||
category = "networking";
|
||||
ports = [ 80 443 8080 ];
|
||||
tags = [ "proxy" "loadbalancer" ];
|
||||
priority = 10;
|
||||
};
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
monitoring = {
|
||||
endpoints = mkOption {
|
||||
type = types.listOf monitoringEndpointType;
|
||||
default = [];
|
||||
description = "Monitoring endpoints exposed by this system";
|
||||
};
|
||||
|
||||
nodeExporter = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable node exporter";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 9100;
|
||||
description = "Node exporter port";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
backups = {
|
||||
jobs = mkOption {
|
||||
type = types.listOf backupJobType;
|
||||
default = [];
|
||||
description = "Backup jobs for this system";
|
||||
};
|
||||
|
||||
globalExcludes = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
"*.tmp"
|
||||
"*.cache"
|
||||
"*/.git"
|
||||
"*/node_modules"
|
||||
"*/target"
|
||||
];
|
||||
description = "Global exclude patterns for all backup jobs";
|
||||
};
|
||||
};
|
||||
|
||||
reverseProxy = {
|
||||
entries = mkOption {
|
||||
type = types.listOf reverseProxyEntryType;
|
||||
default = [];
|
||||
description = "Reverse proxy entries for this system";
|
||||
};
|
||||
};
|
||||
|
||||
# Helper function to add monitoring endpoint
|
||||
addMonitoringEndpoint = mkOption {
|
||||
type = types.functionTo (types.functionTo types.anything);
|
||||
default = name: endpoint: {
|
||||
homelab.global.monitoring.endpoints = [
|
||||
(endpoint // {inherit name;})
|
||||
];
|
||||
};
|
||||
description = "Helper function to add monitoring endpoints";
|
||||
};
|
||||
|
||||
# Helper function to add backup job
|
||||
addBackupJob = mkOption {
|
||||
type = types.functionTo (types.functionTo types.anything);
|
||||
default = name: job: {
|
||||
homelab.global.backups.jobs = [
|
||||
(job // {inherit name;})
|
||||
];
|
||||
};
|
||||
description = "Helper function to add backup jobs";
|
||||
};
|
||||
|
||||
# Helper function to add reverse proxy entry
|
||||
addReverseProxyEntry = mkOption {
|
||||
type = types.functionTo (types.functionTo types.anything);
|
||||
default = subdomain: entry: {
|
||||
homelab.global.reverseProxy.entries = [
|
||||
(entry // {inherit subdomain;})
|
||||
];
|
||||
};
|
||||
description = "Helper function to add reverse proxy entries";
|
||||
};
|
||||
|
||||
# Helper functions
|
||||
enabledServicesList = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = attrNames enabledServices;
|
||||
description = "List of enabled service names";
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
servicesByPriority = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default =
|
||||
map (x: x.name) (sort (a: b: a.priority < b.priority)
|
||||
(mapAttrsToList (name: service: service // {inherit name;}) enabledServices));
|
||||
description = "Services sorted by priority";
|
||||
readOnly = true;
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
# Set hostname
|
||||
networking.hostName = cfg.hostname;
|
||||
|
||||
# Configure node exporter if enabled
|
||||
services.prometheus.exporters.node = mkIf cfg.monitoring.nodeExporter.enable {
|
||||
enable = true;
|
||||
port = cfg.monitoring.nodeExporter.port;
|
||||
enabledCollectors = [
|
||||
"systemd"
|
||||
"textfile"
|
||||
"filesystem"
|
||||
"loadavg"
|
||||
"meminfo"
|
||||
"netdev"
|
||||
"stat"
|
||||
];
|
||||
};
|
||||
|
||||
# Automatically add node exporter to monitoring endpoints
|
||||
homelab.global.monitoring.endpoints = mkIf cfg.monitoring.nodeExporter.enable [
|
||||
{
|
||||
name = "node-exporter";
|
||||
port = cfg.monitoring.nodeExporter.port;
|
||||
path = "/metrics";
|
||||
jobName = "node";
|
||||
labels = {
|
||||
instance = cfg.hostname;
|
||||
environment = cfg.environment;
|
||||
location = cfg.location;
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
# Export configuration for external consumption
|
||||
environment.etc."homelab/config.json".text = builtins.toJSON {
|
||||
inherit (cfg) hostname domain environment location tags;
|
||||
|
||||
services =
|
||||
mapAttrs (name: service: {
|
||||
inherit (service) enable description category dependencies ports tags priority;
|
||||
})
|
||||
cfg.services;
|
||||
|
||||
enabledServices = enabledServices;
|
||||
|
||||
servicesByCategory = {
|
||||
monitoring = servicesByCategory "monitoring";
|
||||
networking = servicesByCategory "networking";
|
||||
storage = servicesByCategory "storage";
|
||||
security = servicesByCategory "security";
|
||||
media = servicesByCategory "media";
|
||||
development = servicesByCategory "development";
|
||||
backup = servicesByCategory "backup";
|
||||
other = servicesByCategory "other";
|
||||
};
|
||||
|
||||
monitoring = {
|
||||
endpoints =
|
||||
map (endpoint: {
|
||||
name = endpoint.name;
|
||||
url = "http://${cfg.hostname}:${toString endpoint.port}${endpoint.path}";
|
||||
port = endpoint.port;
|
||||
path = endpoint.path;
|
||||
jobName = endpoint.jobName;
|
||||
scrapeInterval = endpoint.scrapeInterval;
|
||||
labels =
|
||||
endpoint.labels
|
||||
// {
|
||||
hostname = cfg.hostname;
|
||||
environment = cfg.environment;
|
||||
};
|
||||
})
|
||||
cfg.monitoring.endpoints;
|
||||
};
|
||||
|
||||
backups = {
|
||||
jobs = cfg.backups.jobs;
|
||||
};
|
||||
|
||||
reverseProxy = {
|
||||
entries =
|
||||
map (entry: {
|
||||
subdomain = entry.subdomain;
|
||||
url = "http://${cfg.hostname}:${toString entry.port}";
|
||||
port = entry.port;
|
||||
path = entry.path;
|
||||
domain = "${entry.subdomain}.${cfg.domain}";
|
||||
enableAuth = entry.enableAuth;
|
||||
enableSSL = entry.enableSSL;
|
||||
customHeaders = entry.customHeaders;
|
||||
websockets = entry.websockets;
|
||||
})
|
||||
cfg.reverseProxy.entries;
|
||||
};
|
||||
};
|
||||
|
||||
# Create a status command that shows service information
|
||||
environment.systemPackages = [
|
||||
# (pkgs.writeScriptBin "homelab-services" ''
|
||||
# #!/bin/bash
|
||||
# echo "🏠 Homelab Services Status"
|
||||
# echo "=========================="
|
||||
# echo
|
||||
|
||||
# ${concatStringsSep "\n" (mapAttrsToList (name: service: ''
|
||||
# echo "${name}: ${service.description}"
|
||||
# echo " Category: ${service.category}"
|
||||
# echo " Status: $(systemctl is-active ${name} 2>/dev/null || echo "not found")"
|
||||
# ${optionalString (service.ports != []) ''
|
||||
# echo " Ports: ${concatStringsSep ", " (map toString service.ports)}"
|
||||
# ''}
|
||||
# ${optionalString (service.tags != []) ''
|
||||
# echo " Tags: ${concatStringsSep ", " service.tags}"
|
||||
# ''}
|
||||
# echo
|
||||
# '')
|
||||
# enabledServices)}
|
||||
# '')
|
||||
];
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue