Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
owner: ModelEngine-Group
repository: 'DataMate'
access-token: ${{ secrets.ACCESS_TOKEN }}
branch: develop/sealed-secrets

- name: DataMate Package
run: |
Expand All @@ -57,6 +58,27 @@ jobs:
sed -i "s/latest/${{ inputs.version }}/g" helm/datamate/values.yaml
sed -i 's#HOME_PAGE_URL: *""#HOME_PAGE_URL: "/data/management"#g' helm/datamate/values.yaml

- name: Sealed-Secrets Helm Chart
run: |
mkdir -p helm/sealed-secrets
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm repo update
helm pull sealed-secrets/sealed-secrets --version 2.18.6 -d helm/sealed-secrets

- name: Download kubeseal
run: |
mkdir -p tools/bin
if [ "${{ inputs.aarch }}" = "arm64" ]; then
KUBESEAL_ARCH="arm64"
else
KUBESEAL_ARCH="amd64"
fi
wget -q "https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.27.2/kubeseal-0.27.2-linux-${KUBESEAL_ARCH}.tar.gz"
tar xzf kubeseal-0.27.2-linux-${KUBESEAL_ARCH}.tar.gz kubeseal
mv kubeseal tools/bin/kubeseal
chmod +x tools/bin/kubeseal
rm kubeseal-0.27.2-linux-${KUBESEAL_ARCH}.tar.gz

- name: DeerFlow Package
if: inputs.deer-flow == true
run: |
Expand Down Expand Up @@ -95,6 +117,10 @@ jobs:
docker pull quay.io/kuberay/operator:v1.4.2 --platform ${{ inputs.aarch }}
docker save -o images/datamate/kuberay-operator.tar quay.io/kuberay/operator:v1.4.2
docker rmi quay.io/kuberay/operator:v1.4.2
# Sealed-secrets controller (Docker v2 manifest for offline compat)
docker pull bitnami/sealed-secrets-controller:0.27.0 --platform ${{ inputs.aarch }}
docker save -o images/datamate/sealed-secrets-controller.tar bitnami/sealed-secrets-controller:0.27.0
docker rmi bitnami/sealed-secrets-controller:0.27.0

- name: Download DeerFlow Image
if: inputs.deer-flow == true
Expand Down Expand Up @@ -144,4 +170,4 @@ jobs:
path: |
helm/
images/
tools/
tools/
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
.idea
196 changes: 196 additions & 0 deletions tools/generate-sealed-secrets.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
#!/bin/bash
### Generate SealedSecret resources using the cluster's sealed-secrets controller.
###
### Usage:
### ./generate-sealed-secrets.sh [--namespace <ns>] [--controller-name <name>]
### [--skip-label-studio] [--skip-milvus] [--cleanup]
###
### Password sourcing priority:
### 1. Interactive prompt
### 2. Auto-generated random value (for JWT_SECRET, MinIO keys, tokens)
###
### Required env vars (entered interactively):
### DB_PASSWORD, CERT_PASS, DOMAIN, HOME_PAGE_URL
### LABEL_STUDIO_PASSWORD, POSTGRE_PASSWORD
### JWT_SECRET, LABEL_STUDIO_USER_TOKEN, MINIO_ACCESS_KEY, MINIO_SECRET_KEY
### (empty = auto-generate where applicable)

set -e

NAMESPACE="datamate"
CONTROLLER_NAME="sealed-secrets"
SKIP_LABEL_STUDIO=false
SKIP_MILVUS=false
CLEANUP=false

WORK_DIR="$(cd "$(dirname "$0")" && pwd)"
KUBESEAL="${WORK_DIR}/bin/kubeseal"
TMP_DIR=$(mktemp -d)
trap "rm -rf $TMP_DIR" EXIT

# ========== Argument Parsing ==========
while [[ "$#" -gt 0 ]]; do
case $1 in
-n|--namespace) NAMESPACE="$2"; shift 2 ;;
--controller-name) CONTROLLER_NAME="$2"; shift 2 ;;
--skip-label-studio) SKIP_LABEL_STUDIO=true; shift ;;
--skip-milvus) SKIP_MILVUS=false; shift ;;
--cleanup) CLEANUP=true; shift ;;
*) echo "Unknown parameter: $1"; exit 1 ;;
esac
done

# ========== Utility Functions ==========
log_info() { echo -e "\033[32m[INFO]\033[0m $*"; }
log_warn() { echo -e "\033[33m[WARN]\033[0m $*"; }
log_error() { echo -e "\033[31m[ERROR]\033[0m $*"; }

random_hex() { head -c 32 /dev/urandom 2>/dev/null | xxd -p -c 64 || openssl rand -hex 32; }

# ========== Check sealed-secrets controller ==========
log_info "Waiting for sealed-secrets controller..."
kubectl wait pod -l app.kubernetes.io/instance="${CONTROLLER_NAME}" \
-n "${NAMESPACE}" --for=condition=Ready --timeout=120s

# ========== Check kubeseal ==========
if [ ! -f "$KUBESEAL" ]; then
KUBESEAL="$(command -v kubeseal 2>/dev/null || echo "")"
fi
if [ -z "$KUBESEAL" ]; then
log_error "kubeseal not found at ${WORK_DIR}/bin/kubeseal or in PATH"
exit 1
fi
log_info "Using kubeseal: $KUBESEAL"
chmod +x "$KUBESEAL" 2>/dev/null || true

# ========== Secret Collection ==========
prompt_or_default() {
local var_name="$1" prompt="$2" gen_random="$3"
if [ "$gen_random" = true ]; then
local generated
generated=$(random_hex)
eval "$var_name=\"$generated\""
log_info "Auto-generated ${var_name}"
return 0
fi
# Interactive prompt
local is_sensitive=false
case "$var_name" in
*_PASSWORD|*_SECRET|*_TOKEN|CERT_PASS|DB_PASSWORD|MINIO_*) is_sensitive=true ;;
esac
if [ "$is_sensitive" = true ]; then
read -rsp "Enter ${prompt}: " value
echo ""
else
read -rp "Enter ${prompt}: " value
fi
eval "$var_name=\"$value\""
}

log_info "Collecting secrets..."

# DataMate core secrets
prompt_or_default DB_PASSWORD "database password" false
prompt_or_default CERT_PASS "SSL certificate password (enter to skip)" false
prompt_or_default DOMAIN "domain" false
HOME_PAGE_URL="${HOME_PAGE_URL:-/data/management}"
prompt_or_default JWT_SECRET "JWT secret" true

# Label Studio secrets
if [ "$SKIP_LABEL_STUDIO" = false ]; then
prompt_or_default LABEL_STUDIO_PASSWORD "Label Studio admin password" false
prompt_or_default POSTGRE_PASSWORD "Label Studio PostgreSQL password (same as DB_PASSWORD)" false
if [ -z "$POSTGRE_PASSWORD" ] && [ -n "$DB_PASSWORD" ]; then
POSTGRE_PASSWORD="$DB_PASSWORD"
log_info "Using DB_PASSWORD as POSTGRE_PASSWORD"
fi
prompt_or_default LABEL_STUDIO_USER_TOKEN "Label Studio API token" true
fi

# Milvus / MinIO secrets
if [ "$SKIP_MILVUS" = false ]; then
prompt_or_default MINIO_ACCESS_KEY "MinIO access key" true
prompt_or_default MINIO_SECRET_KEY "MinIO secret key" true
fi

# ========== Generate SealedSecret YAML ==========
SEAL_ARGS="--controller-name=${CONTROLLER_NAME} --namespace=${NAMESPACE} -o yaml"

create_sealed_secret() {
local secret_name="$1" namespace="$2" output_file="$3"
shift 3
local raw_secret="${TMP_DIR}/${secret_name}-raw.yaml"

# Build raw Secret YAML
cat > "$raw_secret" <<SECRET_EOF
apiVersion: v1
kind: Secret
metadata:
name: ${secret_name}
namespace: ${namespace}
type: Opaque
stringData:
SECRET_EOF

for pair in "$@"; do
key="${pair%%=*}"
value="${pair#*=}"
echo " ${key}: \"${value}\"" >> "$raw_secret"
done

"$KUBESEAL" ${SEAL_ARGS} -f "$raw_secret" > "$output_file"
log_info "Created SealedSecret: ${output_file}"
}

# Datamate secret
create_sealed_secret "datamate-conf" "${NAMESPACE}" "${TMP_DIR}/datamate-sealed.yaml" \
"DB_PASSWORD=${DB_PASSWORD}" \
"CERT_PASS=${CERT_PASS}" \
"DOMAIN=${DOMAIN}" \
"HOME_PAGE_URL=${HOME_PAGE_URL}" \
"JWT_SECRET=${JWT_SECRET}" \
"LABEL_STUDIO_PASSWORD=${LABEL_STUDIO_PASSWORD}" \
"LABEL_STUDIO_USER_TOKEN=${LABEL_STUDIO_USER_TOKEN}"

# Label Studio secret
if [ "$SKIP_LABEL_STUDIO" = false ]; then
create_sealed_secret "label-studio-env" "${NAMESPACE}" "${TMP_DIR}/label-studio-sealed.yaml" \
"POSTGRE_PASSWORD=${POSTGRE_PASSWORD}" \
"LABEL_STUDIO_PASSWORD=${LABEL_STUDIO_PASSWORD}" \
"LABEL_STUDIO_USER_TOKEN=${LABEL_STUDIO_USER_TOKEN}"
fi

# Milvus/MinIO secret
if [ "$SKIP_MILVUS" = false ]; then
create_sealed_secret "milvus-minio-secret" "${NAMESPACE}" "${TMP_DIR}/milvus-sealed.yaml" \
"accesskey=${MINIO_ACCESS_KEY}" \
"secretkey=${MINIO_SECRET_KEY}"
fi

# ========== Apply SealedSecrets ==========
log_info "Applying SealedSecret resources..."

for f in "$TMP_DIR"/*-sealed.yaml; do
[ -f "$f" ] || continue
kubectl apply -f "$f" -n "${NAMESPACE}"
done

# ========== Verify ==========
log_info "Verifying secret decryption..."
for secret_name in datamate-conf label-studio-env milvus-minio-secret; do
case "$secret_name" in
label-studio-env) [ "$SKIP_LABEL_STUDIO" = true ] && continue ;;
milvus-minio-secret) [ "$SKIP_MILVUS" = true ] && continue ;;
esac
if kubectl get secret "$secret_name" -n "${NAMESPACE}" > /dev/null 2>&1; then
log_info "✓ Secret ${secret_name} decrypted successfully"
else
log_warn "Secret ${secret_name} not yet available (may need controller restart)"
fi
done

if [ "$CLEANUP" = true ]; then
rm -f "$TMP_DIR"/*-sealed.yaml "$TMP_DIR"/*-raw.yaml
fi

log_info "Sealed-secrets generation complete!"
43 changes: 41 additions & 2 deletions tools/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ function get_cert_pass() {
function helm_install() {
local release_name="$1"
local chart_path="$2"
shift 2

local helm_args=()

Expand All @@ -242,6 +243,11 @@ function helm_install() {
helm_args+=("--namespace" "$NAMESPACE")
helm_args+=("--create-namespace")

# Append extra args (e.g., --set key=value)
for arg in "$@"; do
helm_args+=("$arg")
done

log_info "即将执行: helm ${helm_args[*]}"

if ! helm "${helm_args[@]}"; then
Expand All @@ -250,8 +256,31 @@ function helm_install() {
fi
}

function install_sealed_secrets() {
local chart_tgz
chart_tgz=$(ls "${HELM_PATH}/sealed-secrets/sealed-secrets-"*.tgz 2>/dev/null | head -1)
if [ -z "$chart_tgz" ]; then
log_error "sealed-secrets Helm chart not found in ${HELM_PATH}/sealed-secrets/"
exit 1
fi
log_info "Installing sealed-secrets controller..."
helm upgrade --install sealed-secrets "$chart_tgz" \
-n "$NAMESPACE" --create-namespace \
--set image.registry="${REPO}" \
--set image.tag=0.27.0 \
--set image.pullPolicy=IfNotPresent \
--wait --timeout 120s
log_info "sealed-secrets controller installed."
}

function install_datamate() {
helm_install "datamate" "${HELM_PATH}/datamate"
local extra_args=()
if [ "$DATAMATE_JWT_ENABLE" == "true" ]; then
extra_args+=("--set" "datamate.jwt.enable=true")
fi
helm_install "datamate" "${HELM_PATH}/datamate" \
--set public.secrets.create=false \
"${extra_args[@]}"
}

function install_milvus() {
Expand All @@ -263,6 +292,17 @@ function install_label_studio() {
}

function install() {
# 1. Install sealed-secrets controller first
install_sealed_secrets

# 2. Generate sealed secrets (from .env or interactive input)
log_info "Generating SealedSecret resources..."
bash "${WORK_DIR}/generate-sealed-secrets.sh" \
-n "$NAMESPACE" \
$([ "$INSTALL_MILVUS" = false ] && echo "--skip-milvus") \
$([ "$INSTALL_LABEL_STUDIO" = false ] && echo "--skip-label-studio")

# 3. Install DataMate components
install_datamate
if [ "$INSTALL_MILVUS" == "true" ]; then
install_milvus
Expand Down Expand Up @@ -334,7 +374,6 @@ function main() {

read_value
read_storage_value
get_cert_pass
load_images "datamate"
if [ "$INSTALL_MILVUS" == "true" ]; then
load_images "milvus"
Expand Down
Loading