fix(images): normalize shell scripts to LF line endings
CodeQL Advanced / Analyze (actions) (push) Canceled after 0s
CodeQL Advanced / Analyze (java-kotlin) (push) Canceled after 0s
CodeQL Advanced / Analyze (javascript-typescript) (push) Canceled after 0s
CodeQL Advanced / Analyze (python) (push) Canceled after 0s
Docker Image Build & Push / manifest (push) Canceled after 0s
Docker Image Build & Push / build-and-push-arm (push) Canceled after 0s
Docker Image Build & Push / build-and-push-amd (push) Canceled after 0s
Docker Image Build & Push / manifest (push) Canceled after 0s
Docker Image Build & Push / build-and-push-arm (push) Canceled after 0s
Docker Image Build & Push / build-and-push-amd (push) Canceled after 0s
Docker Image Build & Push / manifest (push) Canceled after 0s
Docker Image Build & Push / build-and-push-arm (push) Canceled after 0s
Docker Image Build & Push / build-and-push-amd (push) Canceled after 0s
Docker Image Build & Push / manifest (push) Canceled after 0s
Docker Image Build & Push / build-and-push-arm (push) Canceled after 0s
Docker Image Build & Push / build-and-push-amd (push) Canceled after 0s
Docker Image Build & Push / manifest (push) Canceled after 0s
Docker Image Build & Push / build-and-push-arm (push) Canceled after 0s
Docker Image Build & Push / build-and-push-amd (push) Canceled after 0s
Docker Image Build & Push / manifest (push) Canceled after 0s
Docker Image Build & Push / build-and-push-arm (push) Canceled after 0s
Docker Image Build & Push / build-and-push-amd (push) Canceled after 0s
Python Backend Docker Image CI / Build and Push Backend Python Docker Image (push) Canceled after 0s
Backend Docker Image CI / Build and Push Backend Docker Image (push) Canceled after 0s
Database Docker Image CI / Build and Push Database Docker Image (push) Canceled after 0s
Frontend Docker Image CI / Build and Push Frontend Docker Image (push) Canceled after 0s
Gateway Docker Image CI / Build and Push Gateway Docker Image (push) Canceled after 0s
Runtime Docker Image CI / Build and Push Runtime Docker Image (push) Canceled after 0s

database image crashed at start: CRLF shebang in start.sh made the kernel
look for interpreter '/bin/bash\r'. Other image Dockerfiles mask this with
dos2unix at build time, but scripts/images/database/Dockerfile does not.
Convert all tracked .sh files to LF and pin eol=lf via .gitattributes.
This commit is contained in:
apescale
2026-08-31 06:43:31 +08:00
parent 98b52fdb31
commit 54e067ed54
16 changed files with 1321 additions and 1319 deletions
+2
View File
@@ -0,0 +1,2 @@
*.sh text eol=lf
*.bat text eol=crlf
@@ -1,23 +1,23 @@
#!/bin/bash #!/bin/bash
set -e set -e
if [ -d "${LOCAL_FILES_DOCUMENT_ROOT}" ] && [ "${LOCAL_FILES_SERVING_ENABLED}" = "true" ]; then if [ -d "${LOCAL_FILES_DOCUMENT_ROOT}" ] && [ "${LOCAL_FILES_SERVING_ENABLED}" = "true" ]; then
echo "Using local document root: ${LOCAL_FILES_DOCUMENT_ROOT}" echo "Using local document root: ${LOCAL_FILES_DOCUMENT_ROOT}"
fi fi
# 启动应用 # 启动应用
echo "==========================================" echo "=========================================="
echo "Starting DeepData Backend Service..." echo "Starting DeepData Backend Service..."
echo "Host: ${HOST:-0.0.0.0}" echo "Host: ${HOST:-0.0.0.0}"
echo "Port: ${PORT:-18000}" echo "Port: ${PORT:-18000}"
echo "Debug: ${DEBUG:-false}" echo "Debug: ${DEBUG:-false}"
# 转换 LOG_LEVEL 为小写(uvicorn 要求小写) # 转换 LOG_LEVEL 为小写(uvicorn 要求小写)
LOG_LEVEL_LOWER=$(echo "${LOG_LEVEL:-info}" | tr '[:upper:]' '[:lower:]') LOG_LEVEL_LOWER=$(echo "${LOG_LEVEL:-info}" | tr '[:upper:]' '[:lower:]')
# 使用 uvicorn 启动应用 # 使用 uvicorn 启动应用
exec uvicorn app.main:app \ exec uvicorn app.main:app \
--host "${HOST:-0.0.0.0}" \ --host "${HOST:-0.0.0.0}" \
--port "${PORT:-18000}" \ --port "${PORT:-18000}" \
--log-level "${LOG_LEVEL_LOWER}" \ --log-level "${LOG_LEVEL_LOWER}" \
${DEBUG:+--reload} ${DEBUG:+--reload}
+6 -6
View File
@@ -1,7 +1,7 @@
export LOG_LEVEL=DEBUG export LOG_LEVEL=DEBUG
export DEBUG=true export DEBUG=true
uvicorn app.main:app \ uvicorn app.main:app \
--host 0.0.0.0 \ --host 0.0.0.0 \
--port 18000 \ --port 18000 \
--reload --reload
+21 -21
View File
@@ -1,22 +1,22 @@
#!/bin/bash #!/bin/bash
# 遇到错误即退出 # 遇到错误即退出
set -e set -e
# 使用 -n 判断环境变量 HOME_PAGE_URL 是否非空(即已设置且有值) # 使用 -n 判断环境变量 HOME_PAGE_URL 是否非空(即已设置且有值)
if [ -n "$HOME_PAGE_URL" ]; then if [ -n "$HOME_PAGE_URL" ]; then
echo "检测到 HOME_PAGE_URL 环境变量已配置,值为: $HOME_PAGE_URL。准备更新数据库..." echo "检测到 HOME_PAGE_URL 环境变量已配置,值为: $HOME_PAGE_URL。准备更新数据库..."
# 只有变量非空时,才会进入这里执行 SQL # 只有变量非空时,才会进入这里执行 SQL
psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "deepdata" -v my_url="$HOME_PAGE_URL" <<-EOSQL psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "deepdata" -v my_url="$HOME_PAGE_URL" <<-EOSQL
UPDATE t_sys_param UPDATE t_sys_param
SET param_value = :'my_url' SET param_value = :'my_url'
WHERE id = 'sys.home.page.url'; WHERE id = 'sys.home.page.url';
EOSQL EOSQL
echo "sys.home.page.url 更新完成!" echo "sys.home.page.url 更新完成!"
else else
# 如果变量为空或未设置,打印提示并直接跳过 # 如果变量为空或未设置,打印提示并直接跳过
echo "未配置 HOME_PAGE_URL 环境变量或值为空,跳过 sys.home.page.url 的更新操作。" echo "未配置 HOME_PAGE_URL 环境变量或值为空,跳过 sys.home.page.url 的更新操作。"
fi fi
+8 -8
View File
@@ -1,8 +1,8 @@
#!/bin/bash #!/bin/bash
set -e set -e
umask 0022 umask 0022
echo "Starting main application..." echo "Starting main application..."
exec "$@" exec "$@"
@@ -1,97 +1,97 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
run_once=false run_once=false
if [ "${1:-}" = "--once" ]; then if [ "${1:-}" = "--once" ]; then
run_once=true run_once=true
shift shift
fi fi
LOG_ROOT="${1:-${LOG_ROTATION_PATH:-/tmp/ray}}" LOG_ROOT="${1:-${LOG_ROTATION_PATH:-/tmp/ray}}"
LOG_ROTATION_MAX_SIZE="${LOG_ROTATION_MAX_SIZE:-100MB}" LOG_ROTATION_MAX_SIZE="${LOG_ROTATION_MAX_SIZE:-100MB}"
LOG_ROTATION_BACKUP_COUNT="${LOG_ROTATION_BACKUP_COUNT:-30}" LOG_ROTATION_BACKUP_COUNT="${LOG_ROTATION_BACKUP_COUNT:-30}"
LOG_ROTATION_INTERVAL_SECONDS="${LOG_ROTATION_INTERVAL_SECONDS:-300}" LOG_ROTATION_INTERVAL_SECONDS="${LOG_ROTATION_INTERVAL_SECONDS:-300}"
parse_size_bytes() { parse_size_bytes() {
local value local value
value="$(printf '%s' "$1" | tr '[:lower:]' '[:upper:]')" value="$(printf '%s' "$1" | tr '[:lower:]' '[:upper:]')"
value="${value// /}" value="${value// /}"
case "$value" in case "$value" in
*GB) echo $((${value%GB} * 1024 * 1024 * 1024)) ;; *GB) echo $((${value%GB} * 1024 * 1024 * 1024)) ;;
*G) echo $((${value%G} * 1024 * 1024 * 1024)) ;; *G) echo $((${value%G} * 1024 * 1024 * 1024)) ;;
*MB) echo $((${value%MB} * 1024 * 1024)) ;; *MB) echo $((${value%MB} * 1024 * 1024)) ;;
*M) echo $((${value%M} * 1024 * 1024)) ;; *M) echo $((${value%M} * 1024 * 1024)) ;;
*KB) echo $((${value%KB} * 1024)) ;; *KB) echo $((${value%KB} * 1024)) ;;
*K) echo $((${value%K} * 1024)) ;; *K) echo $((${value%K} * 1024)) ;;
*B) echo "${value%B}" ;; *B) echo "${value%B}" ;;
*) echo "$value" ;; *) echo "$value" ;;
esac esac
} }
file_size_bytes() { file_size_bytes() {
wc -c < "$1" | tr -d ' ' wc -c < "$1" | tr -d ' '
} }
backup_path() { backup_path() {
local file="$1" local file="$1"
local index="$2" local index="$2"
local ext="${file##*.}" local ext="${file##*.}"
local stem="${file%.$ext}" local stem="${file%.$ext}"
echo "${stem}.${index}.${ext}" echo "${stem}.${index}.${ext}"
} }
is_backup_file() { is_backup_file() {
local name local name
name="$(basename "$1")" name="$(basename "$1")"
[[ "$name" =~ \.[0-9]+\.(log|out|err)$ ]] [[ "$name" =~ \.[0-9]+\.(log|out|err)$ ]]
} }
rotate_file() { rotate_file() {
local file="$1" local file="$1"
local max_bytes="$2" local max_bytes="$2"
local backup_count="$3" local backup_count="$3"
[ -f "$file" ] || return 0 [ -f "$file" ] || return 0
is_backup_file "$file" && return 0 is_backup_file "$file" && return 0
local current_size local current_size
current_size="$(file_size_bytes "$file")" current_size="$(file_size_bytes "$file")"
[ "$current_size" -lt "$max_bytes" ] && return 0 [ "$current_size" -lt "$max_bytes" ] && return 0
rm -f "$(backup_path "$file" "$backup_count")" rm -f "$(backup_path "$file" "$backup_count")"
local index local index
for ((index = backup_count - 1; index >= 1; index--)); do for ((index = backup_count - 1; index >= 1; index--)); do
local src dst local src dst
src="$(backup_path "$file" "$index")" src="$(backup_path "$file" "$index")"
dst="$(backup_path "$file" "$((index + 1))")" dst="$(backup_path "$file" "$((index + 1))")"
[ -f "$src" ] && mv "$src" "$dst" [ -f "$src" ] && mv "$src" "$dst"
done done
cp -p "$file" "$(backup_path "$file" 1)" cp -p "$file" "$(backup_path "$file" 1)"
: > "$file" : > "$file"
} }
rotate_once() { rotate_once() {
[ -d "$LOG_ROOT" ] || return 0 [ -d "$LOG_ROOT" ] || return 0
local max_bytes local max_bytes
max_bytes="$(parse_size_bytes "$LOG_ROTATION_MAX_SIZE")" max_bytes="$(parse_size_bytes "$LOG_ROTATION_MAX_SIZE")"
while IFS= read -r -d '' file; do while IFS= read -r -d '' file; do
rotate_file "$file" "$max_bytes" "$LOG_ROTATION_BACKUP_COUNT" rotate_file "$file" "$max_bytes" "$LOG_ROTATION_BACKUP_COUNT"
done < <( done < <(
find "$LOG_ROOT" -type f \( -name '*.log' -o -name '*.out' -o -name '*.err' \) -print0 2>/dev/null || true find "$LOG_ROOT" -type f \( -name '*.log' -o -name '*.out' -o -name '*.err' \) -print0 2>/dev/null || true
) )
} }
if [ "$run_once" = true ]; then if [ "$run_once" = true ]; then
rotate_once rotate_once
exit 0 exit 0
fi fi
while true; do while true; do
rotate_once rotate_once
sleep "$LOG_ROTATION_INTERVAL_SECONDS" sleep "$LOG_ROTATION_INTERVAL_SECONDS"
done done
+32 -32
View File
@@ -1,32 +1,32 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
LOG_DIR="${POSTGRES_LOG_DIR:-/var/log/deepdata/database}" LOG_DIR="${POSTGRES_LOG_DIR:-/var/log/deepdata/database}"
LOG_ROTATION_BACKUP_COUNT="${LOG_ROTATION_BACKUP_COUNT:-30}" LOG_ROTATION_BACKUP_COUNT="${LOG_ROTATION_BACKUP_COUNT:-30}"
LOG_ROTATION_INTERVAL_SECONDS="${LOG_ROTATION_INTERVAL_SECONDS:-300}" LOG_ROTATION_INTERVAL_SECONDS="${LOG_ROTATION_INTERVAL_SECONDS:-300}"
prune_once() { prune_once() {
[ -d "$LOG_DIR" ] || return 0 [ -d "$LOG_DIR" ] || return 0
mapfile -t files < <( mapfile -t files < <(
find "$LOG_DIR" -maxdepth 1 -type f -name 'postgresql-*.log' -printf '%T@ %p\n' 2>/dev/null \ find "$LOG_DIR" -maxdepth 1 -type f -name 'postgresql-*.log' -printf '%T@ %p\n' 2>/dev/null \
| sort -rn \ | sort -rn \
| sed 's/^[^ ]* //' | sed 's/^[^ ]* //'
) )
local index local index
for ((index = LOG_ROTATION_BACKUP_COUNT; index < ${#files[@]}; index++)); do for ((index = LOG_ROTATION_BACKUP_COUNT; index < ${#files[@]}; index++)); do
rm -f "${files[$index]}" rm -f "${files[$index]}"
done done
} }
if [ "${1:-}" = "--once" ]; then if [ "${1:-}" = "--once" ]; then
prune_once prune_once
exit 0 exit 0
fi fi
while true; do while true; do
prune_once prune_once
sleep "$LOG_ROTATION_INTERVAL_SECONDS" sleep "$LOG_ROTATION_INTERVAL_SECONDS"
done done
+7 -7
View File
@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
set -e set -e
/usr/local/bin/prune-postgres-logs.sh & /usr/local/bin/prune-postgres-logs.sh &
exec docker-entrypoint.sh "$@" exec docker-entrypoint.sh "$@"
+92 -92
View File
@@ -1,92 +1,92 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
LOG_DIR="${NGINX_LOG_DIR:-/var/log/deepdata/frontend}" LOG_DIR="${NGINX_LOG_DIR:-/var/log/deepdata/frontend}"
LOG_ROTATION_MAX_SIZE="${LOG_ROTATION_MAX_SIZE:-100MB}" LOG_ROTATION_MAX_SIZE="${LOG_ROTATION_MAX_SIZE:-100MB}"
LOG_ROTATION_BACKUP_COUNT="${LOG_ROTATION_BACKUP_COUNT:-30}" LOG_ROTATION_BACKUP_COUNT="${LOG_ROTATION_BACKUP_COUNT:-30}"
LOG_ROTATION_INTERVAL_SECONDS="${LOG_ROTATION_INTERVAL_SECONDS:-300}" LOG_ROTATION_INTERVAL_SECONDS="${LOG_ROTATION_INTERVAL_SECONDS:-300}"
parse_size_bytes() { parse_size_bytes() {
local value local value
value="$(printf '%s' "$1" | tr '[:lower:]' '[:upper:]')" value="$(printf '%s' "$1" | tr '[:lower:]' '[:upper:]')"
value="${value// /}" value="${value// /}"
case "$value" in case "$value" in
*GB) echo $((${value%GB} * 1024 * 1024 * 1024)) ;; *GB) echo $((${value%GB} * 1024 * 1024 * 1024)) ;;
*G) echo $((${value%G} * 1024 * 1024 * 1024)) ;; *G) echo $((${value%G} * 1024 * 1024 * 1024)) ;;
*MB) echo $((${value%MB} * 1024 * 1024)) ;; *MB) echo $((${value%MB} * 1024 * 1024)) ;;
*M) echo $((${value%M} * 1024 * 1024)) ;; *M) echo $((${value%M} * 1024 * 1024)) ;;
*KB) echo $((${value%KB} * 1024)) ;; *KB) echo $((${value%KB} * 1024)) ;;
*K) echo $((${value%K} * 1024)) ;; *K) echo $((${value%K} * 1024)) ;;
*B) echo "${value%B}" ;; *B) echo "${value%B}" ;;
*) echo "$value" ;; *) echo "$value" ;;
esac esac
} }
file_size_bytes() { file_size_bytes() {
wc -c < "$1" | tr -d ' ' wc -c < "$1" | tr -d ' '
} }
backup_path() { backup_path() {
local file="$1" local file="$1"
local index="$2" local index="$2"
local ext="${file##*.}" local ext="${file##*.}"
local stem="${file%.$ext}" local stem="${file%.$ext}"
echo "${stem}.${index}.${ext}" echo "${stem}.${index}.${ext}"
} }
rotate_file() { rotate_file() {
local file="$1" local file="$1"
local max_bytes="$2" local max_bytes="$2"
local backup_count="$3" local backup_count="$3"
[ -f "$file" ] || return 1 [ -f "$file" ] || return 1
local current_size local current_size
current_size="$(file_size_bytes "$file")" current_size="$(file_size_bytes "$file")"
[ "$current_size" -lt "$max_bytes" ] && return 1 [ "$current_size" -lt "$max_bytes" ] && return 1
rm -f "$(backup_path "$file" "$backup_count")" rm -f "$(backup_path "$file" "$backup_count")"
local index local index
for ((index = backup_count - 1; index >= 1; index--)); do for ((index = backup_count - 1; index >= 1; index--)); do
local src dst local src dst
src="$(backup_path "$file" "$index")" src="$(backup_path "$file" "$index")"
dst="$(backup_path "$file" "$((index + 1))")" dst="$(backup_path "$file" "$((index + 1))")"
[ -f "$src" ] && mv "$src" "$dst" [ -f "$src" ] && mv "$src" "$dst"
done done
mv "$file" "$(backup_path "$file" 1)" mv "$file" "$(backup_path "$file" 1)"
install -o nginx -g nginx -m 0644 /dev/null "$file" install -o nginx -g nginx -m 0644 /dev/null "$file"
return 0 return 0
} }
rotate_once() { rotate_once() {
mkdir -p "$LOG_DIR" mkdir -p "$LOG_DIR"
touch "$LOG_DIR/access.log" "$LOG_DIR/error.log" touch "$LOG_DIR/access.log" "$LOG_DIR/error.log"
chown -R nginx:nginx "$LOG_DIR" 2>/dev/null || true chown -R nginx:nginx "$LOG_DIR" 2>/dev/null || true
local max_bytes local max_bytes
max_bytes="$(parse_size_bytes "$LOG_ROTATION_MAX_SIZE")" max_bytes="$(parse_size_bytes "$LOG_ROTATION_MAX_SIZE")"
local rotated=false local rotated=false
for file in "$LOG_DIR/access.log" "$LOG_DIR/error.log"; do for file in "$LOG_DIR/access.log" "$LOG_DIR/error.log"; do
if rotate_file "$file" "$max_bytes" "$LOG_ROTATION_BACKUP_COUNT"; then if rotate_file "$file" "$max_bytes" "$LOG_ROTATION_BACKUP_COUNT"; then
rotated=true rotated=true
fi fi
done done
if [ "$rotated" = true ]; then if [ "$rotated" = true ]; then
nginx -s reopen || true nginx -s reopen || true
fi fi
} }
if [ "${1:-}" = "--once" ]; then if [ "${1:-}" = "--once" ]; then
rotate_once rotate_once
exit 0 exit 0
fi fi
while true; do while true; do
rotate_once rotate_once
sleep "$LOG_ROTATION_INTERVAL_SECONDS" sleep "$LOG_ROTATION_INTERVAL_SECONDS"
done done
+62 -62
View File
@@ -1,62 +1,62 @@
#!/bin/bash #!/bin/bash
function set_proxy_protocol() { function set_proxy_protocol() {
if [ "${REAL_IP_MODE}" != "proxy_protocol" ]; then if [ "${REAL_IP_MODE}" != "proxy_protocol" ]; then
echo "REAL_IP_MODE is ${REAL_IP_MODE}, no need to update nginx configuration file." echo "REAL_IP_MODE is ${REAL_IP_MODE}, no need to update nginx configuration file."
return 0 return 0
fi fi
sed -i 's/listen 3000;/listen 3000 proxy_protocol;/' /opt/frontend/http_backend.conf sed -i 's/listen 3000;/listen 3000 proxy_protocol;/' /opt/frontend/http_backend.conf
sed -i 's/listen 3000 ssl;/listen 3000 ssl proxy_protocol;/' /opt/frontend/https_backend.conf sed -i 's/listen 3000 ssl;/listen 3000 ssl proxy_protocol;/' /opt/frontend/https_backend.conf
sed -i '/access_log.*main/a\ set_real_ip_from 0.0.0.0/0;' /opt/frontend/nginx.conf sed -i '/access_log.*main/a\ set_real_ip_from 0.0.0.0/0;' /opt/frontend/nginx.conf
sed -i '/access_log.*main/a\ real_ip_header proxy_protocol;' /opt/frontend/nginx.conf sed -i '/access_log.*main/a\ real_ip_header proxy_protocol;' /opt/frontend/nginx.conf
echo "Nginx configuration file updated." echo "Nginx configuration file updated."
} }
mkdir -p /var/log/deepdata/frontend mkdir -p /var/log/deepdata/frontend
touch /var/log/deepdata/frontend/access.log /var/log/deepdata/frontend/error.log touch /var/log/deepdata/frontend/access.log /var/log/deepdata/frontend/error.log
chown -R nginx:nginx /var/log/deepdata/frontend 2>/dev/null || true chown -R nginx:nginx /var/log/deepdata/frontend 2>/dev/null || true
/usr/local/bin/rotate-nginx-logs.sh & /usr/local/bin/rotate-nginx-logs.sh &
if [ -f "/cert/server.pem" ]; then if [ -f "/cert/server.pem" ]; then
cp /cert/server.pem /etc/nginx/cert/server.pem cp /cert/server.pem /etc/nginx/cert/server.pem
chown nginx:nginx /etc/nginx/cert/server.pem chown nginx:nginx /etc/nginx/cert/server.pem
fi fi
if [ -f "/cert/server.key" ]; then if [ -f "/cert/server.key" ]; then
# Private key MUST be encrypted. Plaintext keys are not accepted. # Private key MUST be encrypted. Plaintext keys are not accepted.
if grep -q "ENCRYPTED" /cert/server.key 2>/dev/null; then if grep -q "ENCRYPTED" /cert/server.key 2>/dev/null; then
if [ -z "$CERT_PASS" ]; then if [ -z "$CERT_PASS" ]; then
echo "ERROR: CERT_PASS is required to decrypt the private key" echo "ERROR: CERT_PASS is required to decrypt the private key"
exit 1 exit 1
fi fi
echo "$CERT_PASS" | openssl pkey -in /cert/server.key -out /etc/nginx/cert/server.key -passin stdin echo "$CERT_PASS" | openssl pkey -in /cert/server.key -out /etc/nginx/cert/server.key -passin stdin
chmod 600 /etc/nginx/cert/server.key chmod 600 /etc/nginx/cert/server.key
else else
echo "ERROR: /cert/server.key is not encrypted. Please encrypt it before deployment." echo "ERROR: /cert/server.key is not encrypted. Please encrypt it before deployment."
exit 1 exit 1
fi fi
chown nginx:nginx /etc/nginx/cert/server.key chown nginx:nginx /etc/nginx/cert/server.key
fi fi
set_proxy_protocol set_proxy_protocol
cp /opt/frontend/nginx.conf /etc/nginx/nginx.conf cp /opt/frontend/nginx.conf /etc/nginx/nginx.conf
if [ -f "/etc/nginx/cert/server.pem" ]; then if [ -f "/etc/nginx/cert/server.pem" ]; then
cp /opt/frontend/https_backend.conf /etc/nginx/conf.d/default.conf cp /opt/frontend/https_backend.conf /etc/nginx/conf.d/default.conf
cp /opt/frontend/routes.inc /etc/nginx/conf.d/routes.inc cp /opt/frontend/routes.inc /etc/nginx/conf.d/routes.inc
echo "Switching to HTTPS config" echo "Switching to HTTPS config"
else else
cp /opt/frontend/http_backend.conf /etc/nginx/conf.d/default.conf cp /opt/frontend/http_backend.conf /etc/nginx/conf.d/default.conf
cp /opt/frontend/routes.inc /etc/nginx/conf.d/routes.inc cp /opt/frontend/routes.inc /etc/nginx/conf.d/routes.inc
if [ -n "$DOMAIN" ]; then if [ -n "$DOMAIN" ]; then
cron cron
certbot --nginx "-d ${DOMAIN//,/ -d }" certbot --nginx "-d ${DOMAIN//,/ -d }"
echo "Switching to HTTPS config, Domain: $DOMAIN" echo "Switching to HTTPS config, Domain: $DOMAIN"
echo "0 0 1 * * root /usr/local/bin/certbot renew --quiet" | tee /etc/cron.d/certbot-renew echo "0 0 1 * * root /usr/local/bin/certbot renew --quiet" | tee /etc/cron.d/certbot-renew
chmod 0644 /etc/cron.d/certbot-renew chmod 0644 /etc/cron.d/certbot-renew
else else
echo "Switching to HTTP config" echo "Switching to HTTP config"
fi fi
fi fi
exec nginx -g "daemon off;" exec nginx -g "daemon off;"
+5 -5
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
set -e set -e
echo "Starting main application..." echo "Starting main application..."
exec "$@" exec "$@"
+35 -35
View File
@@ -1,35 +1,35 @@
#!/bin/bash #!/bin/bash
set -e set -e
# 1. 初始化环境 (原有的逻辑:注入用户自定义算子) # 1. 初始化环境 (原有的逻辑:注入用户自定义算子)
if [ -d "/opt/runtime/user" ]; then if [ -d "/opt/runtime/user" ]; then
cp -r /opt/runtime/user/* /opt/runtime/deepdata/ops/user 2>/dev/null || true cp -r /opt/runtime/user/* /opt/runtime/deepdata/ops/user 2>/dev/null || true
fi fi
# 2. 根据 RAY_MODE 环境变量决定启动模式 # 2. 根据 RAY_MODE 环境变量决定启动模式
if [ "${RAY_MODE}" = "head" ]; then if [ "${RAY_MODE}" = "head" ]; then
echo "🚀 Starting Ray Head..." echo "🚀 Starting Ray Head..."
# ✅ 修复 Head 分支: # ✅ 修复 Head 分支:
# Ray 2.x 已废弃 CLI 参数 --redis-address,改为自动读取环境变量 RAY_REDIS_ADDRESS。 # Ray 2.x 已废弃 CLI 参数 --redis-address,改为自动读取环境变量 RAY_REDIS_ADDRESS。
# 只要确保 docker-compose.yml 中配置了 RAY_REDIS_ADDRESS 环境变量即可。 # 只要确保 docker-compose.yml 中配置了 RAY_REDIS_ADDRESS 环境变量即可。
exec ray start --head \ exec ray start --head \
--port=${RAY_HEAD_PORT:-6379} \ --port=${RAY_HEAD_PORT:-6379} \
--redis-password=${RAY_REDIS_PASSWORD} \ --redis-password=${RAY_REDIS_PASSWORD} \
--dashboard-host=0.0.0.0 \ --dashboard-host=0.0.0.0 \
--block --block
elif [ "${RAY_MODE}" = "worker" ]; then elif [ "${RAY_MODE}" = "worker" ]; then
echo "🚀 Starting Ray Worker connecting to ${RAY_HEAD_ADDRESS}..." echo "🚀 Starting Ray Worker connecting to ${RAY_HEAD_ADDRESS}..."
# ✅ 修复 Worker 分支: # ✅ 修复 Worker 分支:
# Ray 2.x 移除了 --worker 参数。只要不传 --head,默认即为 Worker 模式。 # Ray 2.x 移除了 --worker 参数。只要不传 --head,默认即为 Worker 模式。
exec ray start \ exec ray start \
--address=${RAY_HEAD_ADDRESS} \ --address=${RAY_HEAD_ADDRESS} \
--redis-password=${RAY_REDIS_PASSWORD} \ --redis-password=${RAY_REDIS_PASSWORD} \
--num-cpus=${NUM_CPUS:-4} \ --num-cpus=${NUM_CPUS:-4} \
--block --block
else else
echo "🔧 Starting Runtime (Legacy Mode / Job Execution)..." echo "🔧 Starting Runtime (Legacy Mode / Job Execution)..."
# 默认行为:执行 docker-compose.yml command 传入的指令,或 Ray Job 的入口点 # 默认行为:执行 docker-compose.yml command 传入的指令,或 Ray Job 的入口点
exec "$@" exec "$@"
fi fi
+12 -12
View File
@@ -1,12 +1,12 @@
#!/bin/bash #!/bin/bash
set -e set -e
cp -r /opt/runtime/user/* /opt/runtime/deepdata/ops/user cp -r /opt/runtime/user/* /opt/runtime/deepdata/ops/user
if [ "${LOG_ROTATION_ENABLED:-true}" = "true" ]; then if [ "${LOG_ROTATION_ENABLED:-true}" = "true" ]; then
LOG_ROTATION_PATH="${LOG_ROTATION_PATH:-/tmp/ray}" /usr/local/bin/log-rotate-copytruncate.sh "${LOG_ROTATION_PATH:-/tmp/ray}" & LOG_ROTATION_PATH="${LOG_ROTATION_PATH:-/tmp/ray}" /usr/local/bin/log-rotate-copytruncate.sh "${LOG_ROTATION_PATH:-/tmp/ray}" &
fi fi
echo "Starting main application..." echo "Starting main application..."
exec "$@" exec "$@"
+248 -248
View File
@@ -1,248 +1,248 @@
#!/bin/bash #!/bin/bash
### Collect secrets for DeepData installation. ### Collect secrets for DeepData installation.
### ###
### Usage: ### Usage:
### scripts/k8s/collect-secrets.sh [--component deepdata|milvus] [--namespace <ns>] ### scripts/k8s/collect-secrets.sh [--component deepdata|milvus] [--namespace <ns>]
### ###
### Modes (auto-detected): ### Modes (auto-detected):
### 1. Sealed-secrets mode: controller running + kubeseal available → encrypt → apply SealedSecret ### 1. Sealed-secrets mode: controller running + kubeseal available → encrypt → apply SealedSecret
### 2. Plain mode: no controller → Helm creates Secret with base64 (dev mode) ### 2. Plain mode: no controller → Helm creates Secret with base64 (dev mode)
### ###
### Password priority: .env file values > interactive prompt > auto-generated ### Password priority: .env file values > interactive prompt > auto-generated
### ###
### Output: writes shell script variables to stdout (eval by caller) ### Output: writes shell script variables to stdout (eval by caller)
# NOTE: NOT using 'set -e' because interactive prompts (read) can fail # NOTE: NOT using 'set -e' because interactive prompts (read) can fail
# when stdin is not a TTY. We validate critical values explicitly instead. # when stdin is not a TTY. We validate critical values explicitly instead.
COMPONENT="${1:-deepdata}" COMPONENT="${1:-deepdata}"
NAMESPACE="${NAMESPACE:-deepdata}" NAMESPACE="${NAMESPACE:-deepdata}"
ENV_FILE="${ENV_FILE:-.env}" ENV_FILE="${ENV_FILE:-.env}"
# Parse --component and --namespace from args if passed differently # Parse --component and --namespace from args if passed differently
while [[ "$#" -gt 0 ]]; do while [[ "$#" -gt 0 ]]; do
case $1 in case $1 in
--component) COMPONENT="$2"; shift 2 ;; --component) COMPONENT="$2"; shift 2 ;;
--namespace|-n) NAMESPACE="$2"; shift 2 ;; --namespace|-n) NAMESPACE="$2"; shift 2 ;;
*) shift ;; *) shift ;;
esac esac
done done
TMP_DIR=$(mktemp -d) TMP_DIR=$(mktemp -d)
trap "rm -rf $TMP_DIR" EXIT trap "rm -rf $TMP_DIR" EXIT
# ========== Detect Mode ========== # ========== Detect Mode ==========
detect_sealed_secrets() { detect_sealed_secrets() {
kubectl get deployment -n "$NAMESPACE" sealed-secrets >/dev/null 2>&1 && return 0 kubectl get deployment -n "$NAMESPACE" sealed-secrets >/dev/null 2>&1 && return 0
kubectl get deployment -n kube-system sealed-secrets >/dev/null 2>&1 && return 0 kubectl get deployment -n kube-system sealed-secrets >/dev/null 2>&1 && return 0
kubectl get pod -n "$NAMESPACE" -l app.kubernetes.io/name=sealed-secrets --no-headers 2>/dev/null | grep -q Running && return 0 kubectl get pod -n "$NAMESPACE" -l app.kubernetes.io/name=sealed-secrets --no-headers 2>/dev/null | grep -q Running && return 0
return 1 return 1
} }
KUBESEAL="$(command -v kubeseal 2>/dev/null || echo "")" KUBESEAL="$(command -v kubeseal 2>/dev/null || echo "")"
[ -z "$KUBESEAL" ] && KUBESEAL="$HOME/bin/kubeseal" [ -z "$KUBESEAL" ] && KUBESEAL="$HOME/bin/kubeseal"
[ ! -x "$KUBESEAL" ] && [ -x "./tools/bin/kubeseal" ] && KUBESEAL="./tools/bin/kubeseal" [ ! -x "$KUBESEAL" ] && [ -x "./tools/bin/kubeseal" ] && KUBESEAL="./tools/bin/kubeseal"
[ ! -x "$KUBESEAL" ] && [ -x "../tools/bin/kubeseal" ] && KUBESEAL="../tools/bin/kubeseal" [ ! -x "$KUBESEAL" ] && [ -x "../tools/bin/kubeseal" ] && KUBESEAL="../tools/bin/kubeseal"
# Auto-download kubeseal if controller exists but binary is missing # Auto-download kubeseal if controller exists but binary is missing
if detect_sealed_secrets && [ ! -x "$KUBESEAL" ]; then if detect_sealed_secrets && [ ! -x "$KUBESEAL" ]; then
echo "[INFO] Sealed Secrets controller detected, but kubeseal not found. Downloading..." >&2 echo "[INFO] Sealed Secrets controller detected, but kubeseal not found. Downloading..." >&2
KUBESEAL_URL="https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.27.2/kubeseal-0.27.2-$(uname -s | tr '[:upper:]' '[:lower:]')-$(dpkg --print-architecture 2>/dev/null || uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')" KUBESEAL_URL="https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.27.2/kubeseal-0.27.2-$(uname -s | tr '[:upper:]' '[:lower:]')-$(dpkg --print-architecture 2>/dev/null || uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')"
KUBESEAL_DIR="$HOME/bin" KUBESEAL_DIR="$HOME/bin"
mkdir -p "$KUBESEAL_DIR" mkdir -p "$KUBESEAL_DIR"
curl -sSL "$KUBESEAL_URL" -o "$KUBESEAL_DIR/kubeseal" && chmod +x "$KUBESEAL_DIR/kubeseal" curl -sSL "$KUBESEAL_URL" -o "$KUBESEAL_DIR/kubeseal" && chmod +x "$KUBESEAL_DIR/kubeseal"
KUBESEAL="$KUBESEAL_DIR/kubeseal" KUBESEAL="$KUBESEAL_DIR/kubeseal"
echo "[INFO] Installed kubeseal to $KUBESEAL" >&2 echo "[INFO] Installed kubeseal to $KUBESEAL" >&2
fi fi
if detect_sealed_secrets && [ -x "$KUBESEAL" ]; then if detect_sealed_secrets && [ -x "$KUBESEAL" ]; then
MODE="sealed" MODE="sealed"
else else
MODE="plain" MODE="plain"
fi fi
echo "# DeepData secrets collection — component: $COMPONENT, mode: $MODE" >&2 echo "# DeepData secrets collection — component: $COMPONENT, mode: $MODE" >&2
# ========== Helper ========== # ========== Helper ==========
random_hex() { random_hex() {
openssl rand -hex 16 2>/dev/null || python3 -c "import secrets;print(secrets.token_hex(16))" openssl rand -hex 16 2>/dev/null || python3 -c "import secrets;print(secrets.token_hex(16))"
} }
seal_secret() { seal_secret() {
local name="$1" output="$2" local name="$1" output="$2"
shift 2 shift 2
local raw="${TMP_DIR}/${name}-raw.yaml" local raw="${TMP_DIR}/${name}-raw.yaml"
{ {
echo "apiVersion: v1" echo "apiVersion: v1"
echo "kind: Secret" echo "kind: Secret"
echo "metadata:" echo "metadata:"
echo " name: ${name}" echo " name: ${name}"
echo " namespace: ${NAMESPACE}" echo " namespace: ${NAMESPACE}"
echo "type: Opaque" echo "type: Opaque"
echo "stringData:" echo "stringData:"
for pair in "$@"; do for pair in "$@"; do
key="${pair%%=*}" key="${pair%%=*}"
value="${pair#*=}" value="${pair#*=}"
echo " ${key}: \"${value}\"" echo " ${key}: \"${value}\""
done done
} > "$raw" } > "$raw"
"$KUBESEAL" --controller-name=sealed-secrets --namespace="${NAMESPACE}" -o yaml -f "$raw" > "$output" "$KUBESEAL" --controller-name=sealed-secrets --namespace="${NAMESPACE}" -o yaml -f "$raw" > "$output"
echo "[INFO] Created SealedSecret: $output" >&2 echo "[INFO] Created SealedSecret: $output" >&2
} }
# ========== Load .env (targeted: only reads known secret keys) ========== # ========== Load .env (targeted: only reads known secret keys) ==========
if [ -f "$ENV_FILE" ]; then if [ -f "$ENV_FILE" ]; then
echo "[INFO] Loading secrets from ${ENV_FILE}..." >&2 echo "[INFO] Loading secrets from ${ENV_FILE}..." >&2
# Read .env line by line, only extract vars matching our known keys. # Read .env line by line, only extract vars matching our known keys.
# Ignores comments, blank lines, and unrelated vars like pgsql_host/shell vars. # Ignores comments, blank lines, and unrelated vars like pgsql_host/shell vars.
while IFS='=' read -r key value; do while IFS='=' read -r key value; do
# Skip blank lines and comments # Skip blank lines and comments
[ -z "$key" ] && continue [ -z "$key" ] && continue
case "$key" in case "$key" in
\#*) continue ;; \#*) continue ;;
esac esac
# Strip leading/trailing whitespace from key # Strip leading/trailing whitespace from key
key="$(echo "$key" | xargs)" key="$(echo "$key" | xargs)"
case "$key" in case "$key" in
DB_PASSWORD|CERT_PASS|DOMAIN|HOME_PAGE_URL|JWT_SECRET|\ DB_PASSWORD|CERT_PASS|DOMAIN|HOME_PAGE_URL|JWT_SECRET|\
LABEL_STUDIO_PASSWORD|LABEL_STUDIO_USER_TOKEN|POSTGRE_PASSWORD|\ LABEL_STUDIO_PASSWORD|LABEL_STUDIO_USER_TOKEN|POSTGRE_PASSWORD|\
MINIO_ACCESS_KEY|MINIO_SECRET_KEY) MINIO_ACCESS_KEY|MINIO_SECRET_KEY)
val="$value" val="$value"
case "$val" in case "$val" in
\'*|\"*) val="${val:1}" ;; \'*|\"*) val="${val:1}" ;;
esac esac
case "$val" in case "$val" in
*\'|*\") val="${val:0:-1}" ;; *\'|*\") val="${val:0:-1}" ;;
esac esac
printf -v "$key" '%s' "$val" printf -v "$key" '%s' "$val"
;; ;;
esac esac
done < "$ENV_FILE" done < "$ENV_FILE"
[ -n "$DB_PASSWORD" ] && echo "[INFO] Loaded DB_PASSWORD from $ENV_FILE" >&2 [ -n "$DB_PASSWORD" ] && echo "[INFO] Loaded DB_PASSWORD from $ENV_FILE" >&2
[ -n "$CERT_PASS" ] && echo "[INFO] Loaded CERT_PASS from $ENV_FILE" >&2 [ -n "$CERT_PASS" ] && echo "[INFO] Loaded CERT_PASS from $ENV_FILE" >&2
[ -n "$JWT_SECRET" ] && echo "[INFO] Loaded JWT_SECRET from $ENV_FILE" >&2 [ -n "$JWT_SECRET" ] && echo "[INFO] Loaded JWT_SECRET from $ENV_FILE" >&2
else else
echo "[WARN] No .env file found at $(pwd)/${ENV_FILE} — will prompt for secrets" >&2 echo "[WARN] No .env file found at $(pwd)/${ENV_FILE} — will prompt for secrets" >&2
fi fi
# ========== DeepData ========== # ========== DeepData ==========
if [ "$COMPONENT" = "deepdata" ]; then if [ "$COMPONENT" = "deepdata" ]; then
# Collect # Collect
[ -z "$DB_PASSWORD" ] && read -rsp "Enter DB_PASSWORD: " DB_PASSWORD && echo "" >&2 [ -z "$DB_PASSWORD" ] && read -rsp "Enter DB_PASSWORD: " DB_PASSWORD && echo "" >&2
[ -z "$JWT_SECRET" ] && JWT_SECRET=$(openssl rand -hex 32 2>/dev/null || python3 -c "import secrets;print(secrets.token_hex(32))") && echo "[INFO] Auto-generated JWT_SECRET" >&2 [ -z "$JWT_SECRET" ] && JWT_SECRET=$(openssl rand -hex 32 2>/dev/null || python3 -c "import secrets;print(secrets.token_hex(32))") && echo "[INFO] Auto-generated JWT_SECRET" >&2
[ -z "$DOMAIN" ] && read -rp "Enter DOMAIN (enter to skip): " DOMAIN >&2 [ -z "$DOMAIN" ] && read -rp "Enter DOMAIN (enter to skip): " DOMAIN >&2
[ -z "$CERT_PASS" ] && read -rsp "Enter CERT_PASS (enter to skip): " CERT_PASS && echo "" >&2 [ -z "$CERT_PASS" ] && read -rsp "Enter CERT_PASS (enter to skip): " CERT_PASS && echo "" >&2
HOME_PAGE_URL="${HOME_PAGE_URL:-/data/management}" HOME_PAGE_URL="${HOME_PAGE_URL:-/data/management}"
[ -z "$LABEL_STUDIO_PASSWORD" ] && read -rsp "Enter LABEL_STUDIO_PASSWORD (enter to skip): " LABEL_STUDIO_PASSWORD && echo "" >&2 [ -z "$LABEL_STUDIO_PASSWORD" ] && read -rsp "Enter LABEL_STUDIO_PASSWORD (enter to skip): " LABEL_STUDIO_PASSWORD && echo "" >&2
[ -z "$LABEL_STUDIO_USER_TOKEN" ] && LABEL_STUDIO_USER_TOKEN=$(random_hex)$(random_hex) && echo "[INFO] Auto-generated LABEL_STUDIO_USER_TOKEN" >&2 [ -z "$LABEL_STUDIO_USER_TOKEN" ] && LABEL_STUDIO_USER_TOKEN=$(random_hex)$(random_hex) && echo "[INFO] Auto-generated LABEL_STUDIO_USER_TOKEN" >&2
# ===== MANDATORY CHECK: DB_PASSWORD must not be empty ===== # ===== MANDATORY CHECK: DB_PASSWORD must not be empty =====
if [ -z "$DB_PASSWORD" ]; then if [ -z "$DB_PASSWORD" ]; then
echo "[FATAL] DB_PASSWORD is empty! Cannot install without a database password." >&2 echo "[FATAL] DB_PASSWORD is empty! Cannot install without a database password." >&2
echo "[FATAL] Set DB_PASSWORD in .env or ensure interactive prompts work." >&2 echo "[FATAL] Set DB_PASSWORD in .env or ensure interactive prompts work." >&2
echo "SECRETS_CREATE=SKIP" echo "SECRETS_CREATE=SKIP"
echo "HELM_VALUES_FILE=" echo "HELM_VALUES_FILE="
exit 0 exit 0
fi fi
if [ "$MODE" = "sealed" ]; then if [ "$MODE" = "sealed" ]; then
# Clean up any old stale Secret (prevents Helm conflict) # Clean up any old stale Secret (prevents Helm conflict)
if kubectl get secret deepdata-conf -n "$NAMESPACE" >/dev/null 2>&1; then if kubectl get secret deepdata-conf -n "$NAMESPACE" >/dev/null 2>&1; then
echo "[INFO] Removing old deepdata-conf Secret..." >&2 echo "[INFO] Removing old deepdata-conf Secret..." >&2
kubectl delete secret deepdata-conf -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 kubectl delete secret deepdata-conf -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1
fi fi
# Clean up old SealedSecret (apply is idempotent, but belt-and-suspenders) # Clean up old SealedSecret (apply is idempotent, but belt-and-suspenders)
kubectl delete sealedsecret deepdata-conf -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true kubectl delete sealedsecret deepdata-conf -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true
# DeepData-conf # DeepData-conf
seal_secret "deepdata-conf" "${TMP_DIR}/deepdata-sealed.yaml" \ seal_secret "deepdata-conf" "${TMP_DIR}/deepdata-sealed.yaml" \
"DB_PASSWORD=${DB_PASSWORD}" \ "DB_PASSWORD=${DB_PASSWORD}" \
"CERT_PASS=${CERT_PASS}" \ "CERT_PASS=${CERT_PASS}" \
"DOMAIN=${DOMAIN}" \ "DOMAIN=${DOMAIN}" \
"HOME_PAGE_URL=${HOME_PAGE_URL}" \ "HOME_PAGE_URL=${HOME_PAGE_URL}" \
"JWT_SECRET=${JWT_SECRET}" \ "JWT_SECRET=${JWT_SECRET}" \
"LABEL_STUDIO_PASSWORD=${LABEL_STUDIO_PASSWORD}" \ "LABEL_STUDIO_PASSWORD=${LABEL_STUDIO_PASSWORD}" \
"LABEL_STUDIO_USER_TOKEN=${LABEL_STUDIO_USER_TOKEN}" "LABEL_STUDIO_USER_TOKEN=${LABEL_STUDIO_USER_TOKEN}"
kubectl apply -f "${TMP_DIR}/deepdata-sealed.yaml" -n "$NAMESPACE" >/dev/null kubectl apply -f "${TMP_DIR}/deepdata-sealed.yaml" -n "$NAMESPACE" >/dev/null
# Add Helm ownership labels so 'helm install --force' accepts the Secret # Add Helm ownership labels so 'helm install --force' accepts the Secret
kubectl wait --for=jsonpath='{.data.DB_PASSWORD}' --timeout=30s secret/deepdata-conf -n "$NAMESPACE" >/dev/null 2>&1 || true kubectl wait --for=jsonpath='{.data.DB_PASSWORD}' --timeout=30s secret/deepdata-conf -n "$NAMESPACE" >/dev/null 2>&1 || true
kubectl annotate secret deepdata-conf -n "$NAMESPACE" \ kubectl annotate secret deepdata-conf -n "$NAMESPACE" \
meta.helm.sh/release-name=deepdata \ meta.helm.sh/release-name=deepdata \
meta.helm.sh/release-namespace="${NAMESPACE}" \ meta.helm.sh/release-namespace="${NAMESPACE}" \
--overwrite >/dev/null 2>&1 --overwrite >/dev/null 2>&1
kubectl label secret deepdata-conf -n "$NAMESPACE" \ kubectl label secret deepdata-conf -n "$NAMESPACE" \
app.kubernetes.io/managed-by=Helm \ app.kubernetes.io/managed-by=Helm \
--overwrite >/dev/null 2>&1 --overwrite >/dev/null 2>&1
# Label Studio # Label Studio
if [ -n "$LABEL_STUDIO_PASSWORD" ]; then if [ -n "$LABEL_STUDIO_PASSWORD" ]; then
POSTGRE_PASSWORD="${POSTGRE_PASSWORD:-$DB_PASSWORD}" POSTGRE_PASSWORD="${POSTGRE_PASSWORD:-$DB_PASSWORD}"
seal_secret "label-studio-env" "${TMP_DIR}/label-studio-sealed.yaml" \ seal_secret "label-studio-env" "${TMP_DIR}/label-studio-sealed.yaml" \
"POSTGRE_PASSWORD=${POSTGRE_PASSWORD}" \ "POSTGRE_PASSWORD=${POSTGRE_PASSWORD}" \
"LABEL_STUDIO_PASSWORD=${LABEL_STUDIO_PASSWORD}" \ "LABEL_STUDIO_PASSWORD=${LABEL_STUDIO_PASSWORD}" \
"LABEL_STUDIO_USER_TOKEN=${LABEL_STUDIO_USER_TOKEN}" "LABEL_STUDIO_USER_TOKEN=${LABEL_STUDIO_USER_TOKEN}"
kubectl apply -f "${TMP_DIR}/label-studio-sealed.yaml" -n "$NAMESPACE" >/dev/null kubectl apply -f "${TMP_DIR}/label-studio-sealed.yaml" -n "$NAMESPACE" >/dev/null
fi fi
echo "SECRETS_CREATE=false" echo "SECRETS_CREATE=false"
echo "HELM_VALUES_FILE=" echo "HELM_VALUES_FILE="
else else
# Plain — delete any old SealedSecret and stale Secret first # Plain — delete any old SealedSecret and stale Secret first
kubectl delete sealedsecret deepdata-conf -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true kubectl delete sealedsecret deepdata-conf -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true
if kubectl get secret deepdata-conf -n "$NAMESPACE" >/dev/null 2>&1; then if kubectl get secret deepdata-conf -n "$NAMESPACE" >/dev/null 2>&1; then
echo "[INFO] Removing old deepdata-conf Secret..." >&2 echo "[INFO] Removing old deepdata-conf Secret..." >&2
kubectl delete secret deepdata-conf -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 kubectl delete secret deepdata-conf -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1
fi fi
# Write extra values to /tmp (survives script exit for Helm to read) # Write extra values to /tmp (survives script exit for Helm to read)
values_file="/tmp/deepdata-secret-values-$$.yaml" values_file="/tmp/deepdata-secret-values-$$.yaml"
cat > "$values_file" <<VALUES_EOF cat > "$values_file" <<VALUES_EOF
public: public:
secrets: secrets:
data: data:
DB_PASSWORD: "${DB_PASSWORD}" DB_PASSWORD: "${DB_PASSWORD}"
JWT_SECRET: "${JWT_SECRET}" JWT_SECRET: "${JWT_SECRET}"
CERT_PASS: "${CERT_PASS}" CERT_PASS: "${CERT_PASS}"
DOMAIN: "${DOMAIN}" DOMAIN: "${DOMAIN}"
HOME_PAGE_URL: "${HOME_PAGE_URL}" HOME_PAGE_URL: "${HOME_PAGE_URL}"
LABEL_STUDIO_PASSWORD: "${LABEL_STUDIO_PASSWORD}" LABEL_STUDIO_PASSWORD: "${LABEL_STUDIO_PASSWORD}"
LABEL_STUDIO_USER_TOKEN: "${LABEL_STUDIO_USER_TOKEN}" LABEL_STUDIO_USER_TOKEN: "${LABEL_STUDIO_USER_TOKEN}"
VALUES_EOF VALUES_EOF
echo "[INFO] Wrote plain secret values to ${values_file}" >&2 echo "[INFO] Wrote plain secret values to ${values_file}" >&2
echo "SECRETS_CREATE=true" echo "SECRETS_CREATE=true"
echo "HELM_VALUES_FILE=${values_file}" echo "HELM_VALUES_FILE=${values_file}"
fi fi
# ========== Milvus ========== # ========== Milvus ==========
elif [ "$COMPONENT" = "milvus" ]; then elif [ "$COMPONENT" = "milvus" ]; then
# Only needed if secret doesn't already exist # Only needed if secret doesn't already exist
if kubectl get secret milvus-minio-secret -n "$NAMESPACE" >/dev/null 2>&1; then if kubectl get secret milvus-minio-secret -n "$NAMESPACE" >/dev/null 2>&1; then
echo "# milvus-minio-secret already exists — skipping" >&2 echo "# milvus-minio-secret already exists — skipping" >&2
exit 0 exit 0
fi fi
[ -z "$MINIO_ACCESS_KEY" ] && MINIO_ACCESS_KEY=$(random_hex) && echo "[INFO] Auto-generated MINIO_ACCESS_KEY" >&2 [ -z "$MINIO_ACCESS_KEY" ] && MINIO_ACCESS_KEY=$(random_hex) && echo "[INFO] Auto-generated MINIO_ACCESS_KEY" >&2
[ -z "$MINIO_SECRET_KEY" ] && MINIO_SECRET_KEY=$(random_hex)$(random_hex) && echo "[INFO] Auto-generated MINIO_SECRET_KEY" >&2 [ -z "$MINIO_SECRET_KEY" ] && MINIO_SECRET_KEY=$(random_hex)$(random_hex) && echo "[INFO] Auto-generated MINIO_SECRET_KEY" >&2
if [ "$MODE" = "sealed" ]; then if [ "$MODE" = "sealed" ]; then
seal_secret "milvus-minio-secret" "${TMP_DIR}/milvus-sealed.yaml" \ seal_secret "milvus-minio-secret" "${TMP_DIR}/milvus-sealed.yaml" \
"accesskey=${MINIO_ACCESS_KEY}" \ "accesskey=${MINIO_ACCESS_KEY}" \
"secretkey=${MINIO_SECRET_KEY}" "secretkey=${MINIO_SECRET_KEY}"
kubectl apply -f "${TMP_DIR}/milvus-sealed.yaml" -n "$NAMESPACE" >/dev/null kubectl apply -f "${TMP_DIR}/milvus-sealed.yaml" -n "$NAMESPACE" >/dev/null
kubectl wait --for=jsonpath='{.data.accesskey}' --timeout=30s secret/milvus-minio-secret -n "$NAMESPACE" >/dev/null 2>&1 || true kubectl wait --for=jsonpath='{.data.accesskey}' --timeout=30s secret/milvus-minio-secret -n "$NAMESPACE" >/dev/null 2>&1 || true
kubectl annotate secret milvus-minio-secret -n "$NAMESPACE" \ kubectl annotate secret milvus-minio-secret -n "$NAMESPACE" \
meta.helm.sh/release-name=milvus-minio \ meta.helm.sh/release-name=milvus-minio \
meta.helm.sh/release-namespace="${NAMESPACE}" \ meta.helm.sh/release-namespace="${NAMESPACE}" \
--overwrite >/dev/null 2>&1 --overwrite >/dev/null 2>&1
else else
kubectl create secret generic milvus-minio-secret \ kubectl create secret generic milvus-minio-secret \
--from-literal=accesskey="$MINIO_ACCESS_KEY" \ --from-literal=accesskey="$MINIO_ACCESS_KEY" \
--from-literal=secretkey="$MINIO_SECRET_KEY" \ --from-literal=secretkey="$MINIO_SECRET_KEY" \
-n "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - >/dev/null -n "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - >/dev/null
echo "[INFO] Created milvus-minio-secret (plain)" >&2 echo "[INFO] Created milvus-minio-secret (plain)" >&2
fi fi
fi fi
+167 -167
View File
@@ -1,167 +1,167 @@
#!/bin/bash #!/bin/bash
# #
# DeepData Node Cleanup Script # DeepData Node Cleanup Script
# Remove labels and taints from nodes that were configured for DeepData deployment # Remove labels and taints from nodes that were configured for DeepData deployment
# #
# Usage: ./node-cleanup.sh [--dry-run] [--nodes NODE1,NODE2] # Usage: ./node-cleanup.sh [--dry-run] [--nodes NODE1,NODE2]
# #
set -e set -e
# Colors for output # Colors for output
RED='\033[0;31m' RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
BLUE='\033[0;34m' BLUE='\033[0;34m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
# Default values # Default values
DRY_RUN=false DRY_RUN=false
NAMESPACE="deepdata" NAMESPACE="deepdata"
LABEL_KEY="node-role.kubernetes.io/deepdata" LABEL_KEY="node-role.kubernetes.io/deepdata"
LABEL_VALUE="true" LABEL_VALUE="true"
TAINT_EFFECT="NoSchedule" TAINT_EFFECT="NoSchedule"
# Parse arguments # Parse arguments
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
--namespace) --namespace)
NAMESPACE="$2" NAMESPACE="$2"
shift 2 shift 2
;; ;;
--dry-run) --dry-run)
DRY_RUN=true DRY_RUN=true
shift shift
;; ;;
--nodes) --nodes)
PROVIDED_NODES="$2" PROVIDED_NODES="$2"
shift 2 shift 2
;; ;;
--label-key) --label-key)
LABEL_KEY="$2" LABEL_KEY="$2"
shift 2 shift 2
;; ;;
*) *)
echo "Unknown option: $1" echo "Unknown option: $1"
exit 1 exit 1
;; ;;
esac esac
done done
echo -e "${BLUE}=====================================${NC}" echo -e "${BLUE}=====================================${NC}"
echo -e "${BLUE} DeepData Node Cleanup${NC}" echo -e "${BLUE} DeepData Node Cleanup${NC}"
echo -e "${BLUE}=====================================${NC}" echo -e "${BLUE}=====================================${NC}"
echo "" echo ""
# Check if kubectl is available # Check if kubectl is available
if ! command -v kubectl &> /dev/null; then if ! command -v kubectl &> /dev/null; then
echo -e "${RED}Error: kubectl is not installed${NC}" echo -e "${RED}Error: kubectl is not installed${NC}"
exit 1 exit 1
fi fi
# Check if connected to cluster # Check if connected to cluster
if ! kubectl cluster-info &> /dev/null; then if ! kubectl cluster-info &> /dev/null; then
echo -e "${RED}Error: Cannot connect to Kubernetes cluster${NC}" echo -e "${RED}Error: Cannot connect to Kubernetes cluster${NC}"
exit 1 exit 1
fi fi
# Determine nodes to clean up # Determine nodes to clean up
if [ "$PROVIDED_NODES" != "" ]; then if [ "$PROVIDED_NODES" != "" ]; then
# Use provided nodes # Use provided nodes
IFS=',' read -ra SELECTED_NODES <<< "$PROVIDED_NODES" IFS=',' read -ra SELECTED_NODES <<< "$PROVIDED_NODES"
else else
# Find nodes with the deepdata label directly from Kubernetes # Find nodes with the deepdata label directly from Kubernetes
echo -e "${YELLOW}Finding nodes with $LABEL_KEY=$LABEL_VALUE label...${NC}" echo -e "${YELLOW}Finding nodes with $LABEL_KEY=$LABEL_VALUE label...${NC}"
NODES=$(kubectl get nodes -l "$LABEL_KEY=$LABEL_VALUE" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}') NODES=$(kubectl get nodes -l "$LABEL_KEY=$LABEL_VALUE" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
if [ -z "$NODES" ]; then if [ -z "$NODES" ]; then
echo -e "${GREEN}No nodes found with $LABEL_KEY=$LABEL_VALUE label.${NC}" echo -e "${GREEN}No nodes found with $LABEL_KEY=$LABEL_VALUE label.${NC}"
echo -e "${YELLOW}Cleanup not needed - no nodes were labeled.${NC}" echo -e "${YELLOW}Cleanup not needed - no nodes were labeled.${NC}"
exit 0 exit 0
fi fi
SELECTED_NODES=() SELECTED_NODES=()
while IFS= read -r NODE; do while IFS= read -r NODE; do
if [ -n "$NODE" ]; then if [ -n "$NODE" ]; then
SELECTED_NODES+=("$NODE") SELECTED_NODES+=("$NODE")
fi fi
done <<< "$NODES" done <<< "$NODES"
fi fi
if [ ${#SELECTED_NODES[@]} -eq 0 ]; then if [ ${#SELECTED_NODES[@]} -eq 0 ]; then
echo -e "${YELLOW}No nodes to clean up.${NC}" echo -e "${YELLOW}No nodes to clean up.${NC}"
exit 0 exit 0
fi fi
echo -e "${GREEN}Nodes to clean up:${NC}" echo -e "${GREEN}Nodes to clean up:${NC}"
for NODE in "${SELECTED_NODES[@]}"; do for NODE in "${SELECTED_NODES[@]}"; do
echo " - $NODE" echo " - $NODE"
done done
echo "" echo ""
echo -e "${BLUE}Summary:${NC}" echo -e "${BLUE}Summary:${NC}"
echo " Label to remove: $LABEL_KEY" echo " Label to remove: $LABEL_KEY"
# Check if any node has the taint # Check if any node has the taint
HAS_TAINTS=false HAS_TAINTS=false
for NODE in "${SELECTED_NODES[@]}"; do for NODE in "${SELECTED_NODES[@]}"; do
TAINT_COUNT=$(kubectl get node "$NODE" -o jsonpath='{range .spec.taints[*]}{.key}{"\n"}{end}' | grep -c "^${LABEL_KEY}$" || echo "0") TAINT_COUNT=$(kubectl get node "$NODE" -o jsonpath='{range .spec.taints[*]}{.key}{"\n"}{end}' | grep -c "^${LABEL_KEY}$" || echo "0")
if [ "$TAINT_COUNT" -gt 0 ]; then if [ "$TAINT_COUNT" -gt 0 ]; then
HAS_TAINTS=true HAS_TAINTS=true
break break
fi fi
done done
if [ "$HAS_TAINTS" = true ]; then if [ "$HAS_TAINTS" = true ]; then
echo " Taint to remove: $LABEL_KEY=$LABEL_VALUE:$TAINT_EFFECT" echo " Taint to remove: $LABEL_KEY=$LABEL_VALUE:$TAINT_EFFECT"
fi fi
echo "" echo ""
read -p "Remove labels and taints? (y/n) [y]: " CONFIRM read -p "Remove labels and taints? (y/n) [y]: " CONFIRM
CONFIRM=${CONFIRM:-y} CONFIRM=${CONFIRM:-y}
if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then
echo -e "${YELLOW}Cancelled.${NC}" echo -e "${YELLOW}Cancelled.${NC}"
exit 0 exit 0
fi fi
echo "" echo ""
echo -e "${GREEN}Removing configuration...${NC}" echo -e "${GREEN}Removing configuration...${NC}"
# Remove labels from selected nodes # Remove labels from selected nodes
for NODE in "${SELECTED_NODES[@]}"; do for NODE in "${SELECTED_NODES[@]}"; do
if [ "$DRY_RUN" = true ]; then if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] kubectl label node $NODE $LABEL_KEY-" echo "[DRY-RUN] kubectl label node $NODE $LABEL_KEY-"
else else
kubectl label node "$NODE" "$LABEL_KEY-" --overwrite kubectl label node "$NODE" "$LABEL_KEY-" --overwrite
echo -e " ${GREEN}✓${NC} Removed label from $NODE" echo -e " ${GREEN}✓${NC} Removed label from $NODE"
fi fi
done done
# Remove taints (check if node has the taint) # Remove taints (check if node has the taint)
for NODE in "${SELECTED_NODES[@]}"; do for NODE in "${SELECTED_NODES[@]}"; do
# Check if node has the taint # Check if node has the taint
HAS_TAINT=$(kubectl get node "$NODE" -o jsonpath='{range .spec.taints[*]}{.key}{"\n"}{end}' | grep -c "^${LABEL_KEY}$" || echo "0") HAS_TAINT=$(kubectl get node "$NODE" -o jsonpath='{range .spec.taints[*]}{.key}{"\n"}{end}' | grep -c "^${LABEL_KEY}$" || echo "0")
if [ "$HAS_TAINT" -gt 0 ]; then if [ "$HAS_TAINT" -gt 0 ]; then
if [ "$DRY_RUN" = true ]; then if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] kubectl taint node $NODE $LABEL_KEY=$LABEL_VALUE:$TAINT_EFFECT-" echo "[DRY-RUN] kubectl taint node $NODE $LABEL_KEY=$LABEL_VALUE:$TAINT_EFFECT-"
else else
kubectl taint node "$NODE" "$LABEL_KEY=$LABEL_VALUE:$TAINT_EFFECT-" --overwrite || true kubectl taint node "$NODE" "$LABEL_KEY=$LABEL_VALUE:$TAINT_EFFECT-" --overwrite || true
echo -e " ${GREEN}✓${NC} Removed taint from $NODE" echo -e " ${GREEN}✓${NC} Removed taint from $NODE"
fi fi
else else
echo -e " ${YELLOW}○${NC} No taint to remove from $NODE" echo -e " ${YELLOW}○${NC} No taint to remove from $NODE"
fi fi
done done
echo "" echo ""
echo -e "${GREEN}Cleanup complete!${NC}" echo -e "${GREEN}Cleanup complete!${NC}"
# Clean up Helm args temp file if it exists # Clean up Helm args temp file if it exists
if [ -f /tmp/deepdata-helm-args.sh ]; then if [ -f /tmp/deepdata-helm-args.sh ]; then
rm /tmp/deepdata-helm-args.sh rm /tmp/deepdata-helm-args.sh
echo -e "${GREEN}Removed /tmp/deepdata-helm-args.sh${NC}" echo -e "${GREEN}Removed /tmp/deepdata-helm-args.sh${NC}"
fi fi
exit 0 exit 0
+504 -504
View File
File diff suppressed because it is too large Load Diff