homelab framework module init (everything is a mess)
Some checks failed
Test / tests (push) Has been cancelled
/ OpenTofu (push) Has been cancelled

This commit is contained in:
plasmagoat 2025-07-28 02:05:13 +02:00
parent 0347f4d325
commit bcbcc8b17b
94 changed files with 7289 additions and 436 deletions

18
scripts/config.nix Normal file
View file

@ -0,0 +1,18 @@
{
nodes,
lib,
...
}: let
extractGlobal = name: node:
if node ? config.homelab.global
then {
${name} = {
hostname = node.config.homelab.global.hostname;
monitoring = map (e: "${e.name}:${toString e.port}") node.config.homelab.global.monitoring.endpoints;
backups = map (b: "${b.name}(${b.backend})") node.config.homelab.global.backups.jobs;
proxy = map (p: "${p.subdomain}.${node.config.homelab.global.domain}") node.config.homelab.global.reverseProxy.entries;
};
}
else {};
in
lib.foldl (acc: name: acc // (extractGlobal name nodes.${name})) {} (builtins.attrNames nodes)

115
scripts/deploy-homelab.sh Executable file
View file

@ -0,0 +1,115 @@
# Helper script: scripts/deploy-homelab.sh
#!/bin/bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${GREEN}=== Homelab Deployment Script ===${NC}"
# Function to print colored output
log() {
echo -e "${GREEN}[INFO]${NC} $1"
}
warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if colmena is available
if ! command -v colmena &> /dev/null; then
error "colmena is not installed. Please install it first."
exit 1
fi
# Parse arguments
COMMAND=${1:-"deploy"}
TARGET=${2:-""}
case $COMMAND in
"deploy")
if [ -n "$TARGET" ]; then
log "Deploying to specific target: $TARGET"
colmena apply --on "$TARGET"
else
log "Deploying to all targets"
colmena apply
fi
;;
"build")
if [ -n "$TARGET" ]; then
log "Building specific target: $TARGET"
colmena build --on "$TARGET"
else
log "Building all targets"
colmena build
fi
;;
"status")
log "Checking deployment status"
colmena apply --dry-run
;;
"config")
log "Showing global configuration summary"
# Extract global configs from all nodes
colmena eval ./scripts/config.nix | jq .
;;
"backup-status")
log "Checking backup status across all nodes"
if [ -n "$TARGET" ]; then
colmena exec --on "$TARGET" -- backup-status
else
colmena exec -- backup-status
fi
;;
"monitoring")
log "Collecting monitoring endpoints"
nix eval --json .#colmena --apply 'colmena:
let
lib = (import <nixpkgs> {}).lib;
nodes = removeAttrs colmena ["meta"];
collectEndpoints = lib.flatten (
lib.mapAttrsToList (name: node:
if node ? config.homelab.global.monitoring.endpoints then
map (e: {
node = name;
hostname = node.config.homelab.global.hostname;
endpoint = "${e.name}:${toString e.port}${e.path}";
job = e.jobName;
}) node.config.homelab.global.monitoring.endpoints
else []
) nodes
);
in collectEndpoints
' | jq .
;;
"help")
echo "Usage: $0 [COMMAND] [TARGET]"
echo ""
echo "Commands:"
echo " deploy [TARGET] Deploy to all nodes or specific target"
echo " build [TARGET] Build configuration for all nodes or specific target"
echo " status Show deployment status (dry-run)"
echo " config Show global configuration summary"
echo " backup-status Check backup status on all nodes"
echo " monitoring List all monitoring endpoints"
echo " help Show this help message"
echo ""
echo "Examples:"
echo " $0 deploy media-server # Deploy only to media-server"
echo " $0 build # Build all configurations"
echo " $0 config # Show global config summary"
;;
*)
error "Unknown command: $COMMAND"
echo "Run '$0 help' for usage information"
exit 1
;;
esac

41
scripts/generate-docs.sh Executable file
View file

@ -0,0 +1,41 @@
# scripts/generate-docs.sh
#!/bin/bash
echo "# Homelab Global Configuration Documentation"
echo
echo "This document describes the global configuration system for the NixOS homelab."
echo
echo "## Available Services"
echo
# List all service modules
find modules/nixos/services -name "*.nix" | while read -r file; do
service=$(basename "$file" .nix)
echo "### $service"
echo
# Extract description from the module
grep -m1 "mkEnableOption" "$file" | sed 's/.*mkEnableOption "\([^"]*\)".*/\1/' || echo "Service module for $service"
echo
done
echo "## Configuration Examples"
echo
echo "### Basic Media Server Setup"
echo '```nix'
echo 'media-server = { ... }: {'
echo ' homelab.global = {'
echo ' enable = true;'
echo ' hostname = "media-server";'
echo ' domain = "homelab.local";'
echo ' };'
echo ' services.jellyfin.enable = true;'
echo '};'
echo '```'
echo
echo "### Monitoring Configuration"
echo '```nix'
echo 'monitoring = { nodes, ... }: {'
echo ' services.prometheus.scrapeConfigs = collectMonitoringEndpoints nodes;'
echo '};'
echo '```'# modules/global-config.nix

View file

@ -0,0 +1,79 @@
# scripts/validate-config.nix
{
lib,
pkgs,
}: let
inherit (lib) types mkOption;
# Validation functions
validateBackupJob = job: let
errors =
[]
++ (
if job.paths == []
then ["Backup job '${job.name}' has no paths defined"]
else []
)
++ (
if !(builtins.elem job.backend ["restic" "borg" "rclone"])
then ["Invalid backup backend: ${job.backend}"]
else []
)
++ (
if job.schedule == ""
then ["Backup job '${job.name}' has no schedule defined"]
else []
);
in
errors;
validateMonitoringEndpoint = endpoint: let
errors =
[]
++ (
if endpoint.port < 1 || endpoint.port > 65535
then ["Invalid port ${toString endpoint.port} for endpoint '${endpoint.name}'"]
else []
)
++ (
if endpoint.jobName == ""
then ["Monitoring endpoint '${endpoint.name}' has no job name"]
else []
);
in
errors;
validateReverseProxyEntry = entry: let
errors =
[]
++ (
if entry.subdomain == ""
then ["Reverse proxy entry has no subdomain defined"]
else []
)
++ (
if entry.port < 1 || entry.port > 65535
then ["Invalid port ${toString entry.port} for subdomain '${entry.subdomain}'"]
else []
);
in
errors;
validateGlobalConfig = config: let
backupErrors = lib.flatten (map validateBackupJob config.backups.jobs);
monitoringErrors = lib.flatten (map validateMonitoringEndpoint config.monitoring.endpoints);
proxyErrors = lib.flatten (map validateReverseProxyEntry config.reverseProxy.entries);
allErrors = backupErrors ++ monitoringErrors ++ proxyErrors;
in
if allErrors == []
then {
valid = true;
errors = [];
}
else {
valid = false;
errors = allErrors;
};
in {
inherit validateGlobalConfig validateBackupJob validateMonitoringEndpoint validateReverseProxyEntry;
}