homelab/pkgs/homelab-docs/services.nix
plasmagoat f9e276282a
Some checks failed
Test / tests (push) Failing after 1m30s
/ OpenTofu (push) Successful in 8s
docs
2025-07-30 01:56:53 +02:00

384 lines
15 KiB
Nix

{
writeShellScriptBin,
jq,
}:
writeShellScriptBin "homelab-docs-services" ''
#!/usr/bin/env bash
set -euo pipefail
cat << EOF
# Service Catalog
> Complete service documentation with core options, feature integrations, and smart defaults
>
> Generated on: $(date -R)
This document provides comprehensive documentation for homelab services, organized by:
- **Core Service Options**: The main service configuration
- **Feature Integrations**: Available monitoring, logging, and proxy features
- **Service Defaults**: What this service configures by default for each feature
EOF
# Extract comprehensive service information
echo "Extracting service information..." >&2
services_catalog=$(colmena eval -E '
{ nodes, pkgs, lib, ... }:
let
# Helper to extract option information
extractOptions = path: options:
lib.flatten (lib.mapAttrsToList (name: value:
let
currentPath = path ++ [name];
pathStr = lib.concatStringsSep "." currentPath;
in
if (value._type or null) == "option" then
[{
name = pathStr;
type = value.type.description or "unknown";
default = value.default or null;
defaultText = if value ? defaultText then value.defaultText.text or null else null;
description = value.description or "No description";
readOnly = value.readOnly or false;
}]
else if lib.isAttrs value && !(lib.hasAttr "_type" value) then
extractOptions currentPath value
else []
) options);
# Get first node for option definitions
firstNode = lib.head (lib.attrValues nodes);
homelabServices = firstNode.options.homelab.services or {};
# Process each service
serviceInfo = lib.mapAttrs (serviceName: serviceOptions:
let
allOptions = extractOptions [] serviceOptions;
# Separate core options from feature options
coreOptions = lib.filter (opt:
!(lib.hasPrefix "monitoring." opt.name) &&
!(lib.hasPrefix "logging." opt.name) &&
!(lib.hasPrefix "proxy." opt.name)
) allOptions;
monitoringOptions = lib.filter (opt: lib.hasPrefix "monitoring." opt.name) allOptions;
loggingOptions = lib.filter (opt: lib.hasPrefix "logging." opt.name) allOptions;
proxyOptions = lib.filter (opt: lib.hasPrefix "proxy." opt.name) allOptions;
# Get actual service configuration to see what defaults are set
serviceConfigs = lib.mapAttrs (nodeName: node:
let
serviceConfig = node.config.homelab.services.''${serviceName} or null;
in
if serviceConfig != null then {
exists = true;
enabled = serviceConfig.enable or false;
# Extract the computed configuration values
monitoring = serviceConfig.monitoring or {};
logging = serviceConfig.logging or {};
proxy = serviceConfig.proxy or {};
# Get other core options
coreConfig = removeAttrs serviceConfig ["monitoring" "logging" "proxy"];
} else {
exists = false;
}
) nodes;
# Find a node where this service exists to get default values
nodeWithService = lib.findFirst (nodeName: serviceConfigs.''${nodeName}.exists) null (lib.attrNames nodes);
exampleConfig = if nodeWithService != null then serviceConfigs.''${nodeWithService} else null;
in {
inherit serviceName;
coreOptions = coreOptions;
features = {
monitoring = {
available = monitoringOptions != [];
options = monitoringOptions;
defaults = if exampleConfig != null then exampleConfig.monitoring else {};
};
logging = {
available = loggingOptions != [];
options = loggingOptions;
defaults = if exampleConfig != null then exampleConfig.logging else {};
};
proxy = {
available = proxyOptions != [];
options = proxyOptions;
defaults = if exampleConfig != null then exampleConfig.proxy else {};
};
};
deployment = {
totalNodes = lib.length (lib.filter (cfg: cfg.exists) (lib.attrValues serviceConfigs));
enabledNodes = lib.length (lib.filter (cfg: cfg.exists && cfg.enabled) (lib.attrValues serviceConfigs));
};
}
) homelabServices;
in {
services = serviceInfo;
totalServices = lib.length (lib.attrNames serviceInfo);
}
')
total_services=$(echo "$services_catalog" | ${jq}/bin/jq -r '.totalServices')
echo "## Overview"
echo
echo "**Total Available Services:** $total_services"
echo
# Service matrix
echo "## Service Integration Matrix"
echo
echo "| Service | Core Options | Monitoring | Logging | Proxy | Deployments |"
echo "|---------|--------------|------------|---------|-------|-------------|"
echo "$services_catalog" | ${jq}/bin/jq -r '.services | keys[]' | sort | while read -r service; do
service_data=$(echo "$services_catalog" | ${jq}/bin/jq -r ".services[\"$service\"]")
core_count=$(echo "$service_data" | ${jq}/bin/jq -r '.coreOptions | length')
has_monitoring=$(echo "$service_data" | ${jq}/bin/jq -r '.features.monitoring.available')
has_logging=$(echo "$service_data" | ${jq}/bin/jq -r '.features.logging.available')
has_proxy=$(echo "$service_data" | ${jq}/bin/jq -r '.features.proxy.available')
enabled_deployments=$(echo "$service_data" | ${jq}/bin/jq -r '.deployment.enabledNodes')
monitoring_icon=$(if [[ "$has_monitoring" == "true" ]]; then echo "📊"; else echo ""; fi)
logging_icon=$(if [[ "$has_logging" == "true" ]]; then echo "📝"; else echo ""; fi)
proxy_icon=$(if [[ "$has_proxy" == "true" ]]; then echo "🔀"; else echo ""; fi)
echo "| \`$service\` | $core_count | $monitoring_icon | $logging_icon | $proxy_icon | $enabled_deployments |"
done
echo
echo "**Legend:** 📊📝🔀 = Feature available, = Feature not available"
echo
echo "## Service Documentation"
echo
# Process each service
echo "$services_catalog" | ${jq}/bin/jq -r '.services | keys[]' | sort | while read -r service; do
echo "### $service"
echo
service_data=$(echo "$services_catalog" | ${jq}/bin/jq -r ".services[\"$service\"]")
enabled_deployments=$(echo "$service_data" | ${jq}/bin/jq -r '.deployment.enabledNodes')
total_deployments=$(echo "$service_data" | ${jq}/bin/jq -r '.deployment.totalNodes')
if [[ "$total_deployments" -gt 0 ]]; then
echo "**Deployment Status:** $enabled_deployments/$total_deployments nodes have this service enabled"
else
echo "**Deployment Status:** Available but not configured"
fi
echo
# Core Service Configuration
echo "#### Core Service Options"
echo
echo "The main configuration options for $service:"
echo
echo '```nix'
echo "homelab.services.$service = {"
echo "$service_data" | ${jq}/bin/jq -r '.coreOptions[] | @base64' | while IFS= read -r option_b64; do
option=$(echo "$option_b64" | base64 -d)
name=$(echo "$option" | ${jq}/bin/jq -r '.name')
type=$(echo "$option" | ${jq}/bin/jq -r '.type')
default_val=$(echo "$option" | ${jq}/bin/jq -r '.default')
description=$(echo "$option" | ${jq}/bin/jq -r '.description')
read_only=$(echo "$option" | ${jq}/bin/jq -r '.readOnly')
if [[ "$read_only" == "true" ]]; then
continue
fi
clean_description=$(echo "$description" | sed 's/"/\\"/g' | tr -d $'\n\r')
if [[ "$default_val" == "null" ]]; then
echo " # $name = <$type>; # $clean_description"
else
echo " $name = $default_val; # $clean_description"
fi
done
echo "};"
echo '```'
echo
# Feature Integrations
has_monitoring=$(echo "$service_data" | ${jq}/bin/jq -r '.features.monitoring.available')
has_logging=$(echo "$service_data" | ${jq}/bin/jq -r '.features.logging.available')
has_proxy=$(echo "$service_data" | ${jq}/bin/jq -r '.features.proxy.available')
if [[ "$has_monitoring" == "true" || "$has_logging" == "true" || "$has_proxy" == "true" ]]; then
echo "#### Feature Integrations"
echo
# Monitoring Feature
if [[ "$has_monitoring" == "true" ]]; then
echo "##### 📊 Monitoring Integration"
echo
echo "Available monitoring options:"
echo
echo '```nix'
echo "homelab.services.$service = {"
echo " # ... core options above ..."
echo
echo "$service_data" | ${jq}/bin/jq -r '.features.monitoring.options[] | @base64' | while IFS= read -r option_b64; do
option=$(echo "$option_b64" | base64 -d)
name=$(echo "$option" | ${jq}/bin/jq -r '.name')
type=$(echo "$option" | ${jq}/bin/jq -r '.type')
default_val=$(echo "$option" | ${jq}/bin/jq -r '.default')
description=$(echo "$option" | ${jq}/bin/jq -r '.description')
read_only=$(echo "$option" | ${jq}/bin/jq -r '.readOnly')
if [[ "$read_only" == "true" ]]; then
continue
fi
clean_description=$(echo "$description" | sed 's/"/\\"/g' | tr -d $'\n\r')
if [[ "$default_val" == "null" ]]; then
echo " # $name = <$type>; # $clean_description"
else
echo " $name = $default_val; # $clean_description"
fi
done
echo "};"
echo '```'
# Show service-specific monitoring defaults
monitoring_defaults=$(echo "$service_data" | ${jq}/bin/jq -r '.features.monitoring.defaults')
if [[ "$monitoring_defaults" != "{}" && "$monitoring_defaults" != "null" ]]; then
echo
echo "**$service sets these monitoring defaults:**"
echo '```nix'
echo "$monitoring_defaults" | ${jq}/bin/jq -r 'to_entries[] | " \(.key) = \(.value);"'
echo '```'
fi
echo
fi
# Logging Feature
if [[ "$has_logging" == "true" ]]; then
echo "##### 📝 Logging Integration"
echo
echo "Available logging options:"
echo
echo '```nix'
echo "homelab.services.$service = {"
echo " # ... core options above ..."
echo
echo "$service_data" | ${jq}/bin/jq -r '.features.logging.options[] | @base64' | while IFS= read -r option_b64; do
option=$(echo "$option_b64" | base64 -d)
name=$(echo "$option" | ${jq}/bin/jq -r '.name')
type=$(echo "$option" | ${jq}/bin/jq -r '.type')
default_val=$(echo "$option" | ${jq}/bin/jq -r '.default')
description=$(echo "$option" | ${jq}/bin/jq -r '.description')
read_only=$(echo "$option" | ${jq}/bin/jq -r '.readOnly')
if [[ "$read_only" == "true" ]]; then
continue
fi
clean_description=$(echo "$description" | sed 's/"/\\"/g' | tr -d $'\n\r')
if [[ "$default_val" == "null" ]]; then
echo " # $name = <$type>; # $clean_description"
else
echo " $name = $default_val; # $clean_description"
fi
done
echo "};"
echo '```'
# Show service-specific logging defaults
logging_defaults=$(echo "$service_data" | ${jq}/bin/jq -r '.features.logging.defaults')
if [[ "$logging_defaults" != "{}" && "$logging_defaults" != "null" ]]; then
echo
echo "**$service sets these logging defaults:**"
echo '```nix'
echo "$logging_defaults" | ${jq}/bin/jq -r 'to_entries[] | " \(.key) = \(.value);"'
echo '```'
fi
echo
fi
# Proxy Feature
if [[ "$has_proxy" == "true" ]]; then
echo "##### 🔀 Proxy Integration"
echo
echo "Available proxy options:"
echo
echo '```nix'
echo "homelab.services.$service = {"
echo " # ... core options above ..."
echo
echo "$service_data" | ${jq}/bin/jq -r '.features.proxy.options[] | @base64' | while IFS= read -r option_b64; do
option=$(echo "$option_b64" | base64 -d)
name=$(echo "$option" | ${jq}/bin/jq -r '.name')
type=$(echo "$option" | ${jq}/bin/jq -r '.type')
default_val=$(echo "$option" | ${jq}/bin/jq -r '.default')
description=$(echo "$option" | ${jq}/bin/jq -r '.description')
read_only=$(echo "$option" | ${jq}/bin/jq -r '.readOnly')
if [[ "$read_only" == "true" ]]; then
continue
fi
clean_description=$(echo "$description" | sed 's/"/\\"/g' | tr -d $'\n\r')
if [[ "$default_val" == "null" ]]; then
echo " # $name = <$type>; # $clean_description"
else
echo " $name = $default_val; # $clean_description"
fi
done
echo "};"
echo '```'
# Show service-specific proxy defaults
proxy_defaults=$(echo "$service_data" | ${jq}/bin/jq -r '.features.proxy.defaults')
if [[ "$proxy_defaults" != "{}" && "$proxy_defaults" != "null" ]]; then
echo
echo "**$service sets these proxy defaults:**"
echo '```nix'
echo "$proxy_defaults" | ${jq}/bin/jq -r 'to_entries[] | " \(.key) = \(.value);"'
echo '```'
fi
echo
fi
fi
echo "---"
echo
done
echo "## Feature Reference"
echo
echo "### Integration Features"
echo
echo "Homelab services can integrate with three main features:"
echo
echo "- **📊 Monitoring**: Prometheus metrics and health checks"
echo "- **📝 Logging**: Centralized log collection with Promtail/Loki"
echo "- **🔀 Proxy**: Reverse proxy with SSL and authentication"
echo
echo "Each service can import these features and set service-specific defaults."
echo
echo "---"
echo
echo "*This documentation is generated from actual NixOS module evaluations.*"
''