diff --git a/apps/ghost/config/example.env b/apps/ghost/config/example.env new file mode 100644 index 0000000..930add4 --- /dev/null +++ b/apps/ghost/config/example.env @@ -0,0 +1,13 @@ +GHOST_NAMESPACE=ghost +GHOST_HOST=blog.${DOMAIN} +GHOST_TITLE="My Blog" +GHOST_EMAIL= +GHOST_STORAGE_SIZE=10Gi +GHOST_MARIADB_STORAGE_SIZE=8Gi +GHOST_DATABASE_HOST=mariadb.mariadb.svc.cluster.local +GHOST_DATABASE_USER=ghost +GHOST_DATABASE_NAME=ghost + +# Secrets +GHOST_PASSWORD= +GHOST_DATABASE_PASSWORD= diff --git a/apps/mysql/config/example.env b/apps/mysql/config/example.env new file mode 100644 index 0000000..cb23f44 --- /dev/null +++ b/apps/mysql/config/example.env @@ -0,0 +1,11 @@ +MARIADB_NAMESPACE=mariadb +MARIADB_RELEASE_NAME=mariadb +MARIADB_USER=app +MARIADB_DATABASE=app_database +MARIADB_STORAGE=8Gi +MARIADB_TAG=11.4.5 +MARIADB_PORT=3306 + +# Secrets +MARIADB_PASSWORD= +MARIADB_ROOT_PASSWORD= diff --git a/apps/nextcloud/config/example.env b/apps/nextcloud/config/example.env new file mode 100644 index 0000000..b58780a --- /dev/null +++ b/apps/nextcloud/config/example.env @@ -0,0 +1,20 @@ +# Config +NEXTCLOUD_ADMIN_USER=admin +NEXTCLOUD_TRUSTED_DOMAINS=$DOMAIN +NEXTCLOUD_DOMAIN=nextcloud.$DOMAIN +NEXTCLOUD_STORAGE=5Gi +NEXTCLOUD_NFS_STORAGE=100Gi +PHP_MEMORY_LIMIT=4G +PHP_UPLOAD_LIMIT=1G + +NEXTCLOUD_IMAGE=nextcloud:26 +NEXTCLOUD_DB_TYPE=postgres +NEXTCLOUD_DB_HOST=postgres.postgres.svc.cluster.local +NEXTCLOUD_DB_USER=nextcloud +NEXTCLOUD_DB_NAME=nextcloud +POSTGRES_ADMIN_USER=$POSTGRES_USER + +# Secrets +NEXTCLOUD_ADMIN_PASSWORD= +NEXTCLOUD_DB_PASSWORD= +POSTGRES_ADMIN_PASSWORD=$POSTGRES_PASSWORD diff --git a/apps/postgres/config/example.env b/apps/postgres/config/example.env new file mode 100644 index 0000000..3f0d5d1 --- /dev/null +++ b/apps/postgres/config/example.env @@ -0,0 +1,4 @@ +POSTGRES_DB=postgres +POSTGRES_USER=postgres +POSTGRES_PASSWORD=OhPostgres! +POSTGRES_STORAGE=10Gi \ No newline at end of file diff --git a/apps/redis/config/example.env b/apps/redis/config/example.env new file mode 100644 index 0000000..e69de29 diff --git a/bin/generate-config b/bin/generate-config new file mode 100755 index 0000000..f16bea9 --- /dev/null +++ b/bin/generate-config @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# This script generates config.env and secrets.env files for an app +# by evaluating variables in the app's .env file and splitting them +# into regular config and secret variables based on the "# Secrets" marker +# +# Usage: bin/generate-config + +set -e + +# Verify that an app name was provided +if [ $# -lt 1 ]; then + echo "Error: Missing app name" + echo "Usage: $(basename "$0") " + exit 1 +fi + +# Source environment variables from load-env.sh +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_DIR="$(dirname "$SCRIPT_DIR")" +if [ -f "$REPO_DIR/load-env.sh" ]; then + source "$REPO_DIR/load-env.sh" +fi + +APP_NAME="$1" +APP_DIR="$APPS_DIR/$APP_NAME" +ENV_FILE="$APP_DIR/config/.env" +CONFIG_FILE="$APP_DIR/config/config.env" +SECRETS_FILE="$APP_DIR/config/secrets.env" + +# Check if the app exists +if [ ! -d "$APP_DIR" ]; then + echo "Error: App '$APP_NAME' not found" + exit 1 +fi + +# Check if the .env file exists +if [ ! -f "$ENV_FILE" ]; then + echo "Error: Environment file not found: $ENV_FILE" + exit 1 +fi + +# Process the .env file +echo "Generating config files for $APP_NAME..." + +# Create temporary files for processed content +TMP_FILE="$APP_DIR/config/processed.env" # $(mktemp) + +# Process the file with envsubst to expand variables +envsubst < "$ENV_FILE" > $TMP_FILE + +# Initialize header for output files +echo "# Generated by \`generate-config\` on $(date)" > "$CONFIG_FILE" +echo "# Generated by \`generate-config\` on $(date)" > "$SECRETS_FILE" + +# Find the line number of the "# Secrets" marker +SECRETS_LINE=$(grep -n "^# Secrets" $TMP_FILE | cut -d':' -f1) + +if [ -n "$SECRETS_LINE" ]; then + # Extract non-comment lines with "=" before the "# Secrets" marker + head -n $((SECRETS_LINE - 1)) $TMP_FILE | grep -v "^#" | grep "=" >> "$CONFIG_FILE" + + # Extract non-comment lines with "=" after the "# Secrets" marker + tail -n +$((SECRETS_LINE + 1)) $TMP_FILE | grep -v "^#" | grep "=" >> "$SECRETS_FILE" +else + # No secrets marker found, put everything in config + grep -v "^#" $TMP_FILE | grep "=" >> "$CONFIG_FILE" +fi + +# Clean up +rm -f "$TMP_FILE" + +echo "Generated:" +echo " - $CONFIG_FILE" +echo " - $SECRETS_FILE" \ No newline at end of file diff --git a/infrastructure_setup/setup-utils.sh b/infrastructure_setup/setup-utils.sh index ca5fcb8..7ae2863 100755 --- a/infrastructure_setup/setup-utils.sh +++ b/infrastructure_setup/setup-utils.sh @@ -8,8 +8,30 @@ cd "$SCRIPT_DIR" # Install gomplate if command -v gomplate &> /dev/null; then echo "gomplate is already installed." - exit 0 +else + curl -sSL https://github.com/hairyhenderson/gomplate/releases/latest/download/gomplate_linux-amd64 -o $HOME/.local/bin/gomplate + chmod +x $HOME/.local/bin/gomplate + echo "gomplate installed successfully." +fi + +# Install kustomize +if command -v kustomize &> /dev/null; then + echo "kustomize is already installed." +else + curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash + mv kustomize $HOME/.local/bin/ + echo "kustomize installed successfully." +fi + +## Install yq +if command -v yq &> /dev/null; then + echo "yq is already installed." +else + VERSION=v4.45.4 + BINARY=yq_linux_amd64 + wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY}.tar.gz -O - | tar xz + mv ${BINARY} $HOME/.local/bin/yq + chmod +x $HOME/.local/bin/yq + rm yq.1 + echo "yq installed successfully." fi -curl -sSL https://github.com/hairyhenderson/gomplate/releases/latest/download/gomplate_linux-amd64 -o $HOME/.local/bin/gomplate -chmod +x $HOME/.local/bin/gomplate -echo "gomplate installed successfully." diff --git a/load-env.sh b/load-env.sh index 11b95e8..705ac0d 100755 --- a/load-env.sh +++ b/load-env.sh @@ -1,16 +1,178 @@ #!/usr/bin/env bash +# This script sources environment variables from: +# 1. The root .env file +# 2. App-specific .env files from enabled apps (with install=true in manifest.yaml) +# Dependencies are respected - if app A requires app B, app B's .env is sourced first +# set -e PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ENV_FILE="$PROJECT_DIR/.env" BIN_DIR="$PROJECT_DIR/bin" +APPS_DIR="$PROJECT_DIR/apps" +# Check if yq is installed +if ! command -v yq &> /dev/null; then + echo "Error: yq is not installed. Please install it first." + echo "You can install it with: wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq && chmod +x /usr/local/bin/yq" + exit 1 +fi + +# Source the main .env file if [ ! -f "$ENV_FILE" ]; then echo "Error: Environment file not found: $ENV_FILE" exit 1 fi +# Turn on allexport to automatically export all variables set -a source "$ENV_FILE" set +a -export PATH="$BIN_DIR:$PATH" +# Function to parse YAML using yq +parse_yaml() { + local yaml_file=$1 + + # Extract the values we need using yq + local name=$(yq eval '.name' "$yaml_file") + local install=$(yq eval '.install' "$yaml_file") + + # Convert boolean to 1/0 for consistency + if [ "$install" = "true" ]; then + install="1" + elif [ "$install" = "false" ]; then + install="0" + fi + + # Get dependencies as space-separated string + local requires="" + if yq eval 'has("requires")' "$yaml_file" | grep -q "true"; then + requires=$(yq eval '.requires[].name' "$yaml_file" | tr '\n' ' ' | sed 's/ $//') + fi + + # Return the parsed data as a single line + echo "$name|$install|$requires" +} + +# Resolve dependencies and create a list of apps to source in the right order +resolve_dependencies() { + local apps=() + local apps_to_install=() + local deps_map=() + + # Parse all manifest files + for manifest in "$APPS_DIR"/*/manifest.yaml; do + local app_dir=$(dirname "$manifest") + local app_name=$(basename "$app_dir") + + local parsed_data=$(parse_yaml "$manifest") + IFS='|' read -r name install requires <<< "$parsed_data" + + # Add to our arrays + apps+=("$name") + if [ "$install" = "1" ] || [ "$install" = "true" ]; then + apps_to_install+=("$name") + deps_map+=("$name:$requires") + fi + done + + # Create an ordered list with dependencies first + local ordered=() + + # First add apps with no dependencies + for app in "${apps_to_install[@]}"; do + local has_deps=false + for dep_entry in "${deps_map[@]}"; do + local app_name=$(echo "$dep_entry" | cut -d':' -f1) + local deps=$(echo "$dep_entry" | cut -d':' -f2) + + if [ "$app_name" = "$app" ] && [ -n "$deps" ]; then + has_deps=true + break + fi + done + + if [ "$has_deps" = false ]; then + ordered+=("$app") + fi + done + + # Now add apps with resolved dependencies + local remaining=() + for app in "${apps_to_install[@]}"; do + if ! echo " ${ordered[*]} " | grep -q " $app "; then + remaining+=("$app") + fi + done + + while [ ${#remaining[@]} -gt 0 ]; do + local progress=false + + for app in "${remaining[@]}"; do + local all_deps_resolved=true + + # Find the dependencies for this app + local app_deps="" + for dep_entry in "${deps_map[@]}"; do + local app_name=$(echo "$dep_entry" | cut -d':' -f1) + local deps=$(echo "$dep_entry" | cut -d':' -f2) + + if [ "$app_name" = "$app" ]; then + app_deps="$deps" + break + fi + done + + # Check if all dependencies are in the ordered list + if [ -n "$app_deps" ]; then + for dep in $app_deps; do + if ! echo " ${ordered[*]} " | grep -q " $dep "; then + all_deps_resolved=false + break + fi + done + fi + + if [ "$all_deps_resolved" = true ]; then + ordered+=("$app") + progress=true + fi + done + + # If no progress was made, we have a circular dependency + if [ "$progress" = false ]; then + echo "Warning: Circular dependency detected in app manifests" + # Add remaining apps to avoid getting stuck + ordered+=("${remaining[@]}") + break + fi + + # Update remaining list + local new_remaining=() + for app in "${remaining[@]}"; do + if ! echo " ${ordered[*]} " | grep -q " $app "; then + new_remaining+=("$app") + fi + done + remaining=("${new_remaining[@]}") + done + + echo "${ordered[@]}" +} + +# Get ordered list of apps to source +ordered_apps=($(resolve_dependencies)) + +# Source app .env files in dependency order +# echo "Sourcing app environment files..." +for app in "${ordered_apps[@]}"; do + app_env_file="$APPS_DIR/$app/config/.env" + if [ -f "$app_env_file" ]; then + # echo " - $app" + set -a + source "$app_env_file" + set +a + fi +done + +# Add bin directory to PATH +export PATH="$BIN_DIR:$PATH" \ No newline at end of file