384 lines
15 KiB
Nix
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.*"
|
|
''
|