241 lines
7.3 KiB
Bash
241 lines
7.3 KiB
Bash
#!/bin/bash
|
||
|
||
# ==============================================================================
|
||
# 🛠️ BASE UTILITY FUNCTIONS (iklim.co)
|
||
# ==============================================================================
|
||
# Tüm ortamlar (Dev, Test, Prod) tarafından ortak kullanılan çekirdek fonksiyonlar.
|
||
# Bu dosya doğrudan çalıştırılmaz, ortam scriptleri tarafından 'source' edilir.
|
||
|
||
# Belirtilen env dosyasını sisteme yükler (export eder).
|
||
source_env_file() {
|
||
local path="$1"
|
||
if [ -f "$path" ]; then
|
||
set -o allexport
|
||
source "$path"
|
||
set +o allexport
|
||
fi
|
||
}
|
||
|
||
# Klasördeki tüm .env.secrets.* dosyalarını otomatik bulur ve yükler.
|
||
# (.example ve .shared dosyalarını atlar, onları ana akış yönetir).
|
||
source_service_secret_files() {
|
||
local file
|
||
for file in .env.secrets.*; do
|
||
[ -f "$file" ] || continue
|
||
[[ "$file" == *.example ]] && continue
|
||
[ "$file" = ".env.secrets.shared" ] && continue
|
||
source_env_file "$file"
|
||
done
|
||
}
|
||
|
||
# Kritik bir env dosyasının varlığını kontrol eder, yoksa scripti durdurur.
|
||
require_env_file() {
|
||
local path="$1"
|
||
local description="$2"
|
||
if [ ! -f "$path" ]; then
|
||
log_message "ERROR" "$description not found at $path"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# Env dosyalarındaki değişken isimlerini tarayıp 'envsubst' için liste oluşturur.
|
||
# Template dosyalarını doldururken hangi değişkenlerin çözüleceğini belirler.
|
||
envsubst_vars_from_files() {
|
||
local file
|
||
for file in "$ENV_PATH" "$ENV_SECRETS_SHARED_PATH" .env.secrets.*; do
|
||
[ -f "$file" ] || continue
|
||
[[ "$file" == *.example ]] && continue
|
||
grep -E '^[A-Za-z_][A-Za-z_0-9]*=' "$file" | cut -d= -f1
|
||
done | sort -u | sed 's/^/\$/' | tr '\n' ' '
|
||
}
|
||
|
||
# Belirtilen bir değişkenin değerini hiyerarşik olarak (env -> shared -> secrets) arar.
|
||
lookup_env_value() {
|
||
local name="$1"
|
||
local file
|
||
local value=""
|
||
|
||
for file in "$ENV_PATH" "$ENV_SECRETS_SHARED_PATH" .env.secrets.*; do
|
||
[ -f "$file" ] || continue
|
||
[[ "$file" == *.example ]] && continue
|
||
if grep -q "^${name}=" "$file"; then
|
||
value="$(grep "^${name}=" "$file" | tail -n1 | cut -d '=' -f2-)"
|
||
fi
|
||
done
|
||
|
||
printf '%s' "$value"
|
||
}
|
||
|
||
# Matematiksel veya mantıksal işlem gerektiren env değerlerini hesaplar.
|
||
# Örn: Milisaniye cinsinden JWT süresini saniyeye çevirir.
|
||
refresh_calculated_env_vars() {
|
||
export JWT_ACCESS_TOKEN_EXPIRATION_SEC=$(( JWT_ACCESS_TOKEN_EXPIRATION / 1000 ))
|
||
}
|
||
|
||
# Tüm çevre dosyalarını (ana env, ortak sırlar ve servis sırları) tazeleyerek yükler.
|
||
refresh_env_vars() {
|
||
source_env_file "$ENV_PATH"
|
||
source_env_file "$ENV_SECRETS_SHARED_PATH"
|
||
source_service_secret_files
|
||
refresh_calculated_env_vars
|
||
log_message "INFO" "Environment variables refreshed from all .env and .env.secrets.* files 🔄"
|
||
}
|
||
|
||
# Bir değişkeni hem shell'e export eder hem de (Dev ortamında) terminale bilgi basar.
|
||
export_variable() {
|
||
local name="$1"
|
||
local value
|
||
|
||
if [ $# -ge 2 ]; then
|
||
value="$2"
|
||
else
|
||
log_message "DEBUG" "Looking for ${name} value in env files"
|
||
value="$(lookup_env_value "$name")"
|
||
fi
|
||
|
||
export "${name}=${value}"
|
||
if [[ "$ENVIRONMENT" == "dev" ]]; then
|
||
log_message "DEBUG" "Env variable ${name} is set to: ${value:0:5}... 🌎"
|
||
fi
|
||
}
|
||
|
||
# --- 🔐 Ortak Vault Yardımcıları ---
|
||
|
||
# Vault kilidini açar (Unseal). Dev, Test ve tek-node kurulumlar için VIP path kullanır.
|
||
# Prod HA Raft cluster için common-functions-prod.sh bu fonksiyonu override eder.
|
||
unseal_vault() {
|
||
local vault_addr=$1
|
||
local _curl="curl -s"
|
||
[[ "${VAULT_SKIP_VERIFY:-false}" == "true" ]] && _curl="curl -sk"
|
||
|
||
local RESPONSE_JSON ERROR_MSG sealed
|
||
RESPONSE_JSON=$($_curl $vault_addr/v1/sys/health)
|
||
ERROR_MSG=$(echo "$RESPONSE_JSON" | jq -r '.errors[]?')
|
||
if [[ -n "$ERROR_MSG" ]]; then
|
||
log_message "ERROR" "$ERROR_MSG"
|
||
exit 1
|
||
fi
|
||
sealed=$(echo $RESPONSE_JSON | jq .sealed)
|
||
if [ "$sealed" = "true" ]; then
|
||
log_message "INFO" "🔓🗝️ Unsealing Vault ($vault_addr)..."
|
||
RESPONSE_JSON=$($_curl --request PUT -H "Content-Type: application/json" \
|
||
--data "{\"key\": \"$VAULT_UNSEAL_KEY\"}" $vault_addr/v1/sys/unseal)
|
||
ERROR_MSG=$(echo "$RESPONSE_JSON" | jq -r '.errors[]?')
|
||
if [[ -n "$ERROR_MSG" ]]; then
|
||
log_message "ERROR" "$ERROR_MSG"
|
||
exit 1
|
||
fi
|
||
log_message "SUCCESS" "Vault unsealed successfully"
|
||
else
|
||
log_message "INFO" "Vault is already unsealed"
|
||
fi
|
||
}
|
||
|
||
# --- 📝 Ortak Log Yardımcıları ---
|
||
|
||
# Log seviyesini numerik değere çevirir
|
||
_get_log_level_num() {
|
||
case "${1^^}" in
|
||
TRACE) echo 1 ;;
|
||
DEBUG) echo 2 ;;
|
||
INFO) echo 3 ;;
|
||
SUCCESS) echo 4 ;;
|
||
WARN) echo 5 ;;
|
||
ERROR) echo 6 ;;
|
||
FATAL) echo 7 ;;
|
||
NONE) echo 99 ;;
|
||
*) echo 3 ;; # Default to INFO
|
||
esac
|
||
}
|
||
|
||
# Log seviyesine uygun emojiyi döndürür
|
||
_get_log_level_emoji() {
|
||
case "${1^^}" in
|
||
TRACE) echo "🔎" ;;
|
||
DEBUG) echo "🪲" ;;
|
||
INFO) echo "ℹ️" ;;
|
||
SUCCESS) echo "✅" ;;
|
||
WARN) echo "⚠️" ;;
|
||
ERROR) echo "❌" ;;
|
||
FATAL) echo "☠️" ;;
|
||
*) echo "🤔" ;;
|
||
esac
|
||
}
|
||
|
||
# Timestamp'li log fonksiyonu. GLOBAL_LOG_LEVEL'a göre filtreler.
|
||
# Kullanım: log_message "INFO" "Bu bir log mesajıdır"
|
||
log_message() {
|
||
local level="${1^^}"
|
||
local message="$2"
|
||
|
||
local current_level_num=$(_get_log_level_num "$level")
|
||
local global_level_str="${GLOBAL_LOG_LEVEL:-INFO}"
|
||
local global_level_num=$(_get_log_level_num "$global_level_str")
|
||
local emoji=$(_get_log_level_emoji "$level")
|
||
local env_name="${SPRING_PROFILES_ACTIVE:-UNKNOWN}"
|
||
env_name="${env_name^^}"
|
||
|
||
if [ "$current_level_num" -ge "$global_level_num" ]; then
|
||
#local timestamp=$(TZ="Europe/Istanbul" date +"%Y-%m-%dT%H:%M:%S%:z")
|
||
local timestamp=$(TZ="Europe/Istanbul" date +"%H:%M:%S")
|
||
if [ "$current_level_num" -ge 5 ]; then
|
||
# ERROR ve FATAL hata akışına (stderr) basılır
|
||
echo "[$timestamp] [$env_name] $emoji [$level] $message" >&2
|
||
else
|
||
echo "[$timestamp] [$env_name] $emoji [$level] $message"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# Kayıtlı tüm log dosyalarının son satırlarını basar ve dosyaları temizler.
|
||
log_tail() {
|
||
for logfile in "${LOG_FILES[@]}"; do
|
||
tail -n 5 "$logfile"
|
||
rm -f "$logfile"
|
||
done
|
||
}
|
||
|
||
# Bir log dosyası oluşana kadar bekler ve başından (veya bir pattern'den) kesit basar.
|
||
log_head() {
|
||
local logfile=$1
|
||
local pattern=$2
|
||
local lines=5
|
||
while [ ! -f "$logfile" ] || [ "$(wc -l < "$logfile")" -lt $lines ]; do
|
||
sleep 0.5
|
||
done
|
||
if [ -z "$pattern" ]; then
|
||
head -n $lines "$logfile"
|
||
else
|
||
local start_line
|
||
start_line=$(grep -nm1 "$pattern" "$logfile" | cut -d: -f1)
|
||
if [ -z "$start_line" ]; then
|
||
head -n $lines "$logfile"
|
||
else
|
||
sed -n "${start_line},$((start_line + lines - 1))p" "$logfile"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# --- 🐳 Ortak Swarm Yardımcıları ---
|
||
|
||
# Docker Swarm üzerindeki bir servisi rolling-restart (güncelleme) yöntemiyle tazeler.
|
||
swarm_service_update() {
|
||
local stack="$1"
|
||
local service="$2"
|
||
local image="$3"
|
||
local full_name="${stack}_${service}"
|
||
if docker service inspect "$full_name" >/dev/null 2>&1; then
|
||
log_message "INFO" "🔄 Updating $full_name → $image"
|
||
docker service update \
|
||
--image "$image" \
|
||
--with-registry-auth \
|
||
--update-order start-first \
|
||
--update-failure-action rollback \
|
||
"$full_name"
|
||
log_message "SUCCESS" "$full_name updated"
|
||
else
|
||
log_message "ERROR" "Service $full_name does not exist. Manual stack deploy required."
|
||
return 1
|
||
fi
|
||
}
|