This commit is contained in:
Oliver
2025-10-16 16:38:39 -03:00
parent 8ed294f3b7
commit 1808864021
14 changed files with 176 additions and 34 deletions

View File

@@ -15,7 +15,8 @@ localfile="$1"
remotefile="$2" remotefile="$2"
remotetmp_base="/var/tmp/4server" remotetmp_base="/var/tmp/4server"
while read -r host; do # Read hosts from file descriptor 3 to prevent ssh from consuming stdin
while read -r host <&3; do
host_env_file="$host_vars_dir/$host/$host.env" host_env_file="$host_vars_dir/$host/$host.env"
if [ ! -f "$host_env_file" ]; then if [ ! -f "$host_env_file" ]; then
@@ -28,24 +29,19 @@ while read -r host; do
source "$host_env_file" source "$host_env_file"
set +a set +a
NEBULA_KEY=$(<"$host_vars_dir/$host/$host.key") NEBULA_KEY=$(<"$host_vars_dir/$host/$host.key")
NEBULA_CRT=$(<"$host_vars_dir/$host/$host.crt") NEBULA_CRT=$(<"$host_vars_dir/$host/$host.crt")
SSH_PRIVATE=$(<"$host_vars_dir/$host/$host") SSH_PRIVATE=$(<"$host_vars_dir/$host/$host")
SSH_PUBLIC=$(<"$host_vars_dir/$host/$host.pub") SSH_PUBLIC=$(<"$host_vars_dir/$host/$host.pub")
content=$(<"$localfile") content=$(<"$localfile")
for key in "${keys[@]}"; do for key in "${keys[@]}"; do
value="${!key}" # indirect reference value="${!key}" # indirect reference
# Replace placeholder {{KEY}} with value using Bash's parameter expansion # Replace placeholder {{KEY}} with value using Bash's parameter expansion
content="${content//\{\{$key\}\}/$value}" content="${content//\{\{$key\}\}/$value}"
done done
# Copy content to remote temporary file # Copy content to remote temporary file
remotetmp="${remotetmp_base}_${host}" remotetmp="${remotetmp_base}_${host}"
@@ -55,7 +51,5 @@ done
# Move temporary file to final location with doas # Move temporary file to final location with doas
ssh "$host" "doas mv '$remotetmp' '$remotefile'" ssh "$host" "doas mv '$remotetmp' '$remotefile'"
done < /app/host_vars/hosts done 3< /app/host_vars/hosts

View File

@@ -1,8 +1,5 @@
#!/bin/bash #!/bin/bash
rex "doas sqlite3 /4server/data/contracts.db <<EOF
#!/bin/bash UPDATE containers
SET affiliate = '{\"utm_source\":\"OD8N\",\"utm_medium\":\"direct\",\"utm_campaign\":\"none\"}';
rex "doas sqlite3 /4server/data/contracts.db <<'EOF'
ALTER TABLE containers
ALTER COLUMN contract DROP NOT NULL;
EOF" EOF"

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from fastapi import FastAPI, HTTPException, Depends, Response from fastapi import FastAPI, HTTPException, Depends, Response
from fastapi.security.api_key import APIKeyHeader from fastapi.security.api_key import APIKeyHeader
from fastapi.responses import RedirectResponse from fastapi.responses import RedirectResponse, PlainTextResponse
from pydantic import BaseModel from pydantic import BaseModel
import psutil import psutil
import sqlite3 import sqlite3
@@ -14,6 +14,8 @@ import json
import re import re
from collections import deque from collections import deque
import time import time
from pathlib import Path
from pathlib import Path
@@ -277,6 +279,18 @@ def list_images():
images = run_command(["docker", "images", "--format", "{{.Repository}}:{{.Tag}}"]) images = run_command(["docker", "images", "--format", "{{.Repository}}:{{.Tag}}"])
return {"images": images.split("\n")} return {"images": images.split("\n")}
@app.get("/system/cpu", dependencies=[Depends(verify_api_key)])
def get_cpu_log():
CPU_LOG_PATH = Path("/4server/data/log/cpu.log")
if not CPU_LOG_PATH.exists():
raise HTTPException(status_code=404, detail="CPU log file not found")
try:
with CPU_LOG_PATH.open("r") as f:
content = f.read()
return PlainTextResponse(content)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error reading CPU log: {e}")
@app.get("/system/info", dependencies=[Depends(verify_api_key)]) @app.get("/system/info", dependencies=[Depends(verify_api_key)])
def get_system_info(): def get_system_info():

39
app/sbin/cpu Executable file
View File

@@ -0,0 +1,39 @@
#!/bin/sh
# Log file
OUTPUT="/4server/data/log/cpu_idle.log"
# Sampling interval in seconds
INTERVAL=60
while true; do
DATA="" # buffer to store idle samples
# Current date for the measurement period
DATE=$(date "+%Y-%m-%d")
echo "Starting measurement for $DATE"
while [ "$(date +%H:%M)" != "23:45" ]; do
# Get idle CPU percentage
IDLE=$(mpstat 1 1 | awk '/Average/ {print $12}')
# Append to buffer
DATA="$DATA$IDLE\n"
sleep $INTERVAL
done
# Write all data to log file with date
# Only one line per day: Date + space-separated idle samples
echo -n "$DATE " >> "$OUTPUT"
echo -e "$DATA" | tr '\n' ' ' >> "$OUTPUT"
echo >> "$OUTPUT" # newline at the end
echo "Measurement for $DATE written to $OUTPUT"
# Wait until 00:15 to start next day
while [ "$(date +%H:%M)" != "00:15" ]; do
sleep 30
done
done

View File

@@ -2,10 +2,10 @@
# Load functions # Load functions
source /4server/sbin/ODOO_19/ODOO_19.lib source /4server/sbin/ODOO_19/ODOO_19.lib
if [[ -z "$UUID" ]]; then
# Config variables echo "Error: UUID not set. Aborting."
UUID="${UUID:-default}" exit 1
STAGING="${STAGING:-false}" fi
POSTGRES_HOST="${POSTGRES_HOST:-beedb}" POSTGRES_HOST="${POSTGRES_HOST:-beedb}"
POSTGRES_PORT="${POSTGRES_PORT:-5432}" POSTGRES_PORT="${POSTGRES_PORT:-5432}"
@@ -15,13 +15,16 @@ ODOO_DB_USER="${UUID}"
export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql') export ODOO_DB_PASSWORD=$(echo "$SECRET" | jq -r '.psql')
BASEURL="${BASEURL:-/4server/data/$UUID}" BASEURL="${BASEURL:-/4server/data/$UUID}"
BACKUPURL="/4backup/$UUID"
doas docker stop "$UUID" doas docker stop "$UUID"
doas docker rm "$UUID" doas docker rm "$UUID"
if [ -n "${UUID:-}" ]; then if [ -n "${UUID:-}" ]; then
echo "Removing directory: $BASEURL" echo "Removing directory: $BASEURL"
doas rm -rf "$BASEURL" #doas rm -rf "$BASEURL"
echo "Removing backup directory $BACKUPURL"
#doas rm -rf $BACKUPURL
fi fi

View File

@@ -15,6 +15,30 @@ if [[ -z "$UUID" ]]; then
exit 1 exit 1
fi fi
get_container_status() {
local uuid="$1"
# Get the container ID or name matching the UUID
CONTAINER_ID=$(docker ps -a --filter "name=$uuid" --format "{{.ID}}")
if [[ -z "$CONTAINER_ID" ]]; then
echo "not_found"
return
fi
STATUS=$(docker inspect -f '{{.State.Status}}' "$CONTAINER_ID")
echo "$STATUS"
}
# Check if container exists
STATUS=$(get_container_status "$UUID")
if [[ "$STATUS" == "running" ]]; then
echo "Container $UUID is still running. Aborting deletion."
exit 2
fi
get_contract_info get_contract_info
# Extract the second part of UUID (split by "-") # Extract the second part of UUID (split by "-")
@@ -34,3 +58,11 @@ case "$SECOND_PART" in
;; ;;
esac esac
sqlite3 "/4server/data/contracts.db" <<SQL
DELETE FROM containers WHERE UUID='$UUID';
SQL
echo "Container $UUID successfully nuked."

View File

@@ -107,6 +107,7 @@ doas docker run -d --name "$UUID" \
--label "traefik.http.routers.$UUID.service=$UUID" \ --label "traefik.http.routers.$UUID.service=$UUID" \
docker.odoo4projects.com/4projects/odoo_17:$BRANCH docker.odoo4projects.com/4projects/odoo_17:$BRANCH
doas docker exec "$UUID" rm /var/lib/odoo/.local/share/Odoo/filestore
doas docker exec "$UUID" ln -s /home/odoo/.local/share/Odoo/filestore /var/lib/odoo/.local/share/Odoo/filestore doas docker exec "$UUID" ln -s /home/odoo/.local/share/Odoo/filestore /var/lib/odoo/.local/share/Odoo/filestore
doas docker exec $UUID chown -R odoo:odoo /home/odoo/.local doas docker exec $UUID chown -R odoo:odoo /home/odoo/.local

View File

@@ -33,6 +33,7 @@ docker run -d \
-e NODE_ENV=production \ -e NODE_ENV=production \
-e WEBHOOK_URL="https://${UUID}.odoo4projects.com/" \ -e WEBHOOK_URL="https://${UUID}.odoo4projects.com/" \
-e GENERIC_TIMEZONE="UTC-3" \ -e GENERIC_TIMEZONE="UTC-3" \
-e N8N_CUSTOM_EXTENSIONS="/usr/local/share/n8n/custom" \
-v "/4server/data/${UUID}/n8n:/home/node/.n8n" \ -v "/4server/data/${UUID}/n8n:/home/node/.n8n" \
-v "/4server/data/${UUID}/data:/data" \ -v "/4server/data/${UUID}/data:/data" \
--label "traefik.enable=true" \ --label "traefik.enable=true" \
@@ -42,7 +43,6 @@ docker run -d \
--label "traefik.http.routers.${UUID}.tls.certresolver=production" \ --label "traefik.http.routers.${UUID}.tls.certresolver=production" \
--label "traefik.http.services.${UUID}.loadbalancer.server.port=5678" \ --label "traefik.http.services.${UUID}.loadbalancer.server.port=5678" \
--network 4server_4projects \ --network 4server_4projects \
n8nio/n8n:latest docker.odoo4projects.com/4projects/n8n:release
echo "Started $1" echo "Started $1"

View File

@@ -15,6 +15,20 @@ if [[ -z "$UUID" ]]; then
exit 1 exit 1
fi fi
DOMAIN_FILE="/4server/data/$UUID/etc/domain"
DB_FILE="/4server/data/contracts.db"
if [ -f "$DOMAIN_FILE" ]; then
DOMAINS=$(paste -sd "," "$DOMAIN_FILE")
sqlite3 "$DB_FILE" <<SQL
UPDATE containers
SET domains='$DOMAINS'
WHERE UUID='$UUID';
SQL
fi
get_contract_info get_contract_info
# Extract the second part of UUID (split by "-") # Extract the second part of UUID (split by "-")

6
app/sbin/stopContainer Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
exec > /4server/data/log/stopContainer.log 2>&1
echo "$(date '+%Y-%m-%d %H:%M') Stop container $1"
docker stop $1

17
app/templates/init.d/cpu Executable file
View File

@@ -0,0 +1,17 @@
#!/sbin/openrc-run
# OpenRC service for /4server/sbin/cpu
name="cpu"
description="Logs cpu usage"
command="/4server/sbin/cpu"
command_background="yes"
pidfile="/run/cpu.pid"
output_log="/4server/data/log/cpu.log"
error_log="/4server/data/log/cpu.log"
depend() {
need localmount
after bootmisc
}

View File

@@ -68,6 +68,13 @@ rex doas chown root:root /etc/init.d/cleanTmp
rex doas rc-update add cleanTmp default rex doas rc-update add cleanTmp default
rex doas rc-service cleanTmp restart rex doas rc-service cleanTmp restart
#INSTALL cpu service
template templates/init.d/cpu /etc/init.d/cpu
rex doas chmod 0755 /etc/init.d/cpu
rex doas chown root:root /etc/init.d/cpu
rex doas rc-update add cpu default
rex doas rc-service cpu restart
### Infrastructure ### Infrastructure
##### Docker ##### Docker
rex doas rc-service docker start rex doas rc-service docker start

Binary file not shown.

View File

@@ -4,6 +4,7 @@ set -euo pipefail
VAULT_FILE="/app/vault/host_vars.img" VAULT_FILE="/app/vault/host_vars.img"
MAPPER_NAME="host_vars_crypt" MAPPER_NAME="host_vars_crypt"
MOUNT_POINT="/app/host_vars" MOUNT_POINT="/app/host_vars"
LOOP_DEVICE="/dev/loop50"
mkdir -p "$MOUNT_POINT" mkdir -p "$MOUNT_POINT"
@@ -13,19 +14,36 @@ if cryptsetup status "$MAPPER_NAME" >/dev/null 2>&1; then
cryptsetup close "$MAPPER_NAME" cryptsetup close "$MAPPER_NAME"
fi fi
# Open # Detach loop device if already in use
if losetup "$LOOP_DEVICE" >/dev/null 2>&1; then
echo "Detaching stale loop device $LOOP_DEVICE..."
losetup -d "$LOOP_DEVICE"
fi
# Create loop device if missing
if [ ! -e "$LOOP_DEVICE" ]; then
echo "Creating loop device $LOOP_DEVICE..."
mknod "$LOOP_DEVICE" b 7 50
chmod 660 "$LOOP_DEVICE"
fi
# Attach vault file to loop device
echo "Attaching $VAULT_FILE to $LOOP_DEVICE..."
losetup "$LOOP_DEVICE" "$VAULT_FILE"
# Open encrypted volume
echo "Opening encrypted volume..." echo "Opening encrypted volume..."
cryptsetup open "$VAULT_FILE" "$MAPPER_NAME" cryptsetup open "$LOOP_DEVICE" "$MAPPER_NAME"
# Format if needed # Format if needed
if ! blkid /dev/mapper/"$MAPPER_NAME" >/dev/null 2>&1; then if ! blkid "/dev/mapper/$MAPPER_NAME" >/dev/null 2>&1; then
echo "No filesystem found, creating ext4..." echo "No filesystem found, creating ext4..."
mkfs.ext4 /dev/mapper/"$MAPPER_NAME" mkfs.ext4 "/dev/mapper/$MAPPER_NAME"
fi fi
# Mount # Mount
echo "Mounting at $MOUNT_POINT..." echo "Mounting at $MOUNT_POINT..."
mount /dev/mapper/"$MAPPER_NAME" "$MOUNT_POINT" mount "/dev/mapper/$MAPPER_NAME" "$MOUNT_POINT"
echo "Vault is mounted at $MOUNT_POINT" echo "Vault is mounted at $MOUNT_POINT"