refactor: rename toolkit to site-migration-toolkit and rewrite wrapper in Python

This commit is contained in:
Haitao Pan 2026-07-01 09:29:45 +08:00
parent fbbf5d7b4f
commit abd3d770e9
6 changed files with 101 additions and 71 deletions

View File

@ -2,7 +2,7 @@ name: Deploy Environment & Migrate Data (Data Sync)
# ============================================================================= # =============================================================================
# IaC ↔ 数据流单向迁移流水线 (Site Recovery) # IaC ↔ 数据流单向迁移流水线 (Site Recovery)
# 1. 动态 Provision (基于 Terraform 创建新环境 site-recovery) # 1. 动态 Provision (基于 Terraform 创建新环境 site-migration-toolkit)
# 2. 部署基础服务 (Bootstrap 部署,跑空环境) # 2. 部署基础服务 (Bootstrap 部署,跑空环境)
# 3. 生产环境克隆与单向同步 (通过 site_migration Ansible 角色) # 3. 生产环境克隆与单向同步 (通过 site_migration Ansible 角色)
# ============================================================================= # =============================================================================
@ -63,12 +63,12 @@ concurrency:
env: env:
VAULT_ADDR: https://vault.svc.plus VAULT_ADDR: https://vault.svc.plus
VAULT_ROLE: github-actions-site-recovery VAULT_ROLE: github-actions-site-migration-toolkit
VAULT_KV: kv/data/CICD VAULT_KV: kv/data/CICD
VAULT_KV_OPENCLAW: kv/data/openclaw VAULT_KV_OPENCLAW: kv/data/openclaw
VPS_ROOT: infra/iac_modules/terraform-hcl-standard/vultr-vps VPS_ROOT: infra/iac_modules/terraform-hcl-standard/vultr-vps
# 指向 site-recovery 的 Terraform 目录 # 指向 site-migration-toolkit 的 Terraform 目录
ENV_DIR: infra/iac_modules/terraform-hcl-standard/vultr-vps/envs/site-recovery ENV_DIR: infra/iac_modules/terraform-hcl-standard/vultr-vps/envs/site-migration-toolkit
PLAYBOOKS_DIR: infra/playbooks PLAYBOOKS_DIR: infra/playbooks
jobs: jobs:
@ -127,8 +127,8 @@ jobs:
- name: generate.py render - name: generate.py render
working-directory: ${{ env.VPS_ROOT }} working-directory: ${{ env.VPS_ROOT }}
# 强制指定 envs/site-recovery 生成 tfvars 和显式 HCL # 强制指定 envs/site-migration-toolkit 生成 tfvars 和显式 HCL
run: python3 scripts/generate.py render --resources config/resources/site-recovery.yaml --workdir envs/site-recovery run: python3 scripts/generate.py render --resources config/resources/site-migration-toolkit.yaml --workdir envs/site-migration-toolkit
- name: Terraform init - name: Terraform init
working-directory: ${{ env.ENV_DIR }} working-directory: ${{ env.ENV_DIR }}
@ -141,7 +141,7 @@ jobs:
run: | run: |
terraform init -input=false \ terraform init -input=false \
-backend-config="bucket=${TF_STATE_BUCKET}" \ -backend-config="bucket=${TF_STATE_BUCKET}" \
-backend-config="key=site-recovery/terraform.tfstate" \ -backend-config="key=site-migration-toolkit/terraform.tfstate" \
-backend-config="region=${TF_STATE_REGION}" -backend-config="region=${TF_STATE_REGION}"
- name: Terraform Apply / Destroy - name: Terraform Apply / Destroy
@ -158,8 +158,8 @@ jobs:
env: env:
AWS_ACCESS_KEY_ID: ${{ steps.vault.outputs.TF_STATE_ACCESS_KEY }} AWS_ACCESS_KEY_ID: ${{ steps.vault.outputs.TF_STATE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ steps.vault.outputs.TF_STATE_SECRET_KEY }} AWS_SECRET_ACCESS_KEY: ${{ steps.vault.outputs.TF_STATE_SECRET_KEY }}
# 同样指定 envs/site-recovery # 同样指定 envs/site-migration-toolkit
run: python3 scripts/generate.py inventory --resources config/resources/site-recovery.yaml --workdir envs/site-recovery run: python3 scripts/generate.py inventory --resources config/resources/site-migration-toolkit.yaml --workdir envs/site-migration-toolkit
- name: Build deploy matrix - name: Build deploy matrix
id: matrix id: matrix
@ -174,7 +174,7 @@ jobs:
if: ${{ github.event.inputs.terraform_action == 'apply' }} if: ${{ github.event.inputs.terraform_action == 'apply' }}
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: site-recovery-cmdb name: site-migration-toolkit-cmdb
path: | path: |
${{ env.ENV_DIR }}/cmdb.json ${{ env.ENV_DIR }}/cmdb.json
${{ env.ENV_DIR }}/inventory.ini ${{ env.ENV_DIR }}/inventory.ini
@ -201,7 +201,7 @@ jobs:
- name: Download CMDB - name: Download CMDB
uses: actions/download-artifact@v8 uses: actions/download-artifact@v8
with: with:
name: site-recovery-cmdb name: site-migration-toolkit-cmdb
path: cmdb path: cmdb
- name: Load Vault secrets - name: Load Vault secrets
@ -256,7 +256,7 @@ jobs:
if: ${{ github.event.inputs.run_provision_and_deploy == 'true' }} if: ${{ github.event.inputs.run_provision_and_deploy == 'true' }}
uses: actions/download-artifact@v8 uses: actions/download-artifact@v8
with: with:
name: site-recovery-cmdb name: site-migration-toolkit-cmdb
path: cmdb path: cmdb
- name: Checkout playbooks - name: Checkout playbooks
@ -317,7 +317,7 @@ jobs:
- name: Download CMDB - name: Download CMDB
uses: actions/download-artifact@v8 uses: actions/download-artifact@v8
with: with:
name: site-recovery-cmdb name: site-migration-toolkit-cmdb
path: cmdb path: cmdb
- name: Checkout playbooks - name: Checkout playbooks

View File

@ -8,10 +8,10 @@ help:
@echo " make restore - Execute offline restore on the target host" @echo " make restore - Execute offline restore on the target host"
migrate: migrate:
@bash scripts/run_toolkit.sh migrate $(RUN_ARGS) @python3 scripts/run_toolkit.py migrate $(RUN_ARGS)
backup: backup:
@bash scripts/run_toolkit.sh backup $(RUN_ARGS) @python3 scripts/run_toolkit.py backup $(RUN_ARGS)
restore: restore:
@bash scripts/run_toolkit.sh restore $(RUN_ARGS) @python3 scripts/run_toolkit.py restore $(RUN_ARGS)

View File

@ -1,4 +1,4 @@
# Site Migration & Backup Toolkit (site-recovery) # Site Migration & Backup Toolkit (site-migration-toolkit)
*🇨🇳 中文版在下方 | Chinese version below* *🇨🇳 中文版在下方 | Chinese version below*

View File

@ -30,21 +30,21 @@
### 4. 运行前置条件 (Vault OIDC 授权) ### 4. 运行前置条件 (Vault OIDC 授权)
由于我们新建了独立的 `site-recovery` 仓库,该仓库需要通过 GitHub Actions 的 OIDC 身份认证到 Vault 获取凭证。在首次运行流水线之前,你必须使用拥有管理员权限的凭据在你的终端(或 Vault 所在的 `vault.svc.plus` 主机)执行以下操作,以授权新的流水线拉取密钥: 由于我们新建了独立的 `site-migration-toolkit` 仓库,该仓库需要通过 GitHub Actions 的 OIDC 身份认证到 Vault 获取凭证。在首次运行流水线之前,你必须使用拥有管理员权限的凭据在你的终端(或 Vault 所在的 `vault.svc.plus` 主机)执行以下操作,以授权新的流水线拉取密钥:
```bash ```bash
export VAULT_ADDR=https://vault.svc.plus export VAULT_ADDR=https://vault.svc.plus
export VAULT_TOKEN="hvs.xxxxxxxxx" # 请替换为你拥有管理员权限的真实 Token export VAULT_TOKEN="hvs.xxxxxxxxx" # 请替换为你拥有管理员权限的真实 Token
vault write auth/jwt/role/github-actions-site-recovery - <<EOF vault write auth/jwt/role/github-actions-site-migration-toolkit - <<EOF
{ {
"role_type": "jwt", "role_type": "jwt",
"user_claim": "repository", "user_claim": "repository",
"bound_audiences": ["vault"], "bound_audiences": ["vault"],
"bound_claims_type": "glob", "bound_claims_type": "glob",
"bound_claims": { "bound_claims": {
"repository": "ai-workspace-infra/site-recovery", "repository": "ai-workspace-infra/site-migration-toolkit",
"sub": "repo:ai-workspace-infra/site-recovery:*" "sub": "repo:ai-workspace-infra/site-migration-toolkit:*"
}, },
"token_policies": ["github-actions-xworkspace-console"], "token_policies": ["github-actions-xworkspace-console"],
"token_ttl": "20m", "token_ttl": "20m",

80
scripts/run_toolkit.py Executable file
View File

@ -0,0 +1,80 @@
#!/usr/bin/env python3
import os
import sys
import argparse
import subprocess
from pathlib import Path
def get_inventory_path(toolkit_root: Path) -> Path:
"""Resolve the location of the Ansible inventory."""
# 1. Inside toolkit root (e.g. CI/CD environment or manual copy)
if (toolkit_root / "cmdb" / "inventory").is_file():
return toolkit_root / "cmdb" / "inventory"
if (toolkit_root / "cmdb" / "inventory.ini").is_file():
return toolkit_root / "cmdb" / "inventory.ini"
# 2. Adjacent cmdb directory (local execution structure)
adjacent_cmdb = toolkit_root.parent / "cmdb"
if (adjacent_cmdb / "inventory").is_file():
return adjacent_cmdb / "inventory"
if (adjacent_cmdb / "inventory.ini").is_file():
return adjacent_cmdb / "inventory.ini"
return None
def main():
parser = argparse.ArgumentParser(description="AI Workspace Site Migration & Backup Toolkit Wrapper")
parser.add_argument("action", choices=["migrate", "backup", "restore"], help="Toolkit action to execute")
# Capture all remaining arguments (e.g., -e "var=value") to pass seamlessly to ansible-playbook
args, unknown_args = parser.parse_known_args()
# Resolve paths dynamically
script_dir = Path(__file__).resolve().parent
toolkit_root = script_dir.parent
playbooks_dir = toolkit_root.parent / "playbooks"
if not playbooks_dir.is_dir():
print(f"[ERROR] Cannot find playbooks directory at {playbooks_dir}")
print("Ensure 'playbooks' repository is checked out adjacently.")
sys.exit(1)
playbook_file = f"{args.action}_site.yml"
if not (playbooks_dir / playbook_file).is_file():
print(f"[ERROR] Playbook '{playbook_file}' not found in {playbooks_dir}")
sys.exit(1)
inventory_path = get_inventory_path(toolkit_root)
print("=" * 60)
print(f"[INFO] Action: {args.action.upper()}")
print(f"[INFO] Playbook: {playbook_file}")
if inventory_path:
print(f"[INFO] Inventory: {inventory_path}")
print("=" * 60)
cmd = ["ansible-playbook"]
if inventory_path:
cmd.extend(["-i", str(inventory_path)])
else:
print("[WARNING] No valid cmdb/inventory file found. Proceeding without explicit -i flag.")
cmd.append(playbook_file)
if unknown_args:
cmd.extend(unknown_args)
print(f"[EXEC] {' '.join(cmd)}\n")
try:
# Execute Ansible directly from the playbooks directory
subprocess.run(cmd, cwd=playbooks_dir, check=True)
print(f"\n[SUCCESS] Toolkit action '{args.action}' completed.")
except subprocess.CalledProcessError as e:
print(f"\n[FATAL] Toolkit action '{args.action}' failed (Exit Code: {e.returncode}).")
sys.exit(e.returncode)
except KeyboardInterrupt:
print(f"\n[WARN] Toolkit action '{args.action}' interrupted by user.")
sys.exit(130)
if __name__ == "__main__":
main()

View File

@ -1,50 +0,0 @@
#!/bin/bash
set -euo pipefail
ACTION=$1
if [[ -z "$ACTION" ]]; then
echo "Usage: $0 <migrate|backup|restore>"
exit 1
fi
PLAYBOOK_DIR="../playbooks"
INVENTORY_DIR="../cmdb"
if [[ ! -d "$PLAYBOOK_DIR" ]]; then
echo "[ERROR] Cannot find playbooks directory at $PLAYBOOK_DIR"
echo "Make sure ai-workspace-infra/playbooks is checked out."
exit 1
fi
if [[ ! -f "$INVENTORY_DIR/inventory" ]] && [[ ! -f "$INVENTORY_DIR/inventory.ini" ]] && [[ ! -f "cmdb/inventory" ]]; then
echo "[WARNING] Cannot find cmdb/inventory. Assuming manual inventory or default."
fi
# In GitHub Actions, cmdb is at the root of site-recovery. Locally it might be ../cmdb.
INV_PATH="cmdb/inventory"
if [[ ! -f "$INV_PATH" ]]; then
if [[ -f "../cmdb/inventory" ]]; then
INV_PATH="../cmdb/inventory"
elif [[ -f "../cmdb/inventory.ini" ]]; then
INV_PATH="../cmdb/inventory.ini"
fi
fi
PLAYBOOK_FILE="${ACTION}_site.yml"
echo "[INFO] Starting Toolkit Action: $ACTION"
echo "[INFO] Playbook: $PLAYBOOK_DIR/$PLAYBOOK_FILE"
cd "$PLAYBOOK_DIR"
if [[ -f "../site-recovery/$INV_PATH" ]]; then
ansible-playbook -i "../site-recovery/$INV_PATH" "$PLAYBOOK_FILE" "${@:2}"
elif [[ -f "../cmdb/inventory" ]]; then
ansible-playbook -i "../cmdb/inventory" "$PLAYBOOK_FILE" "${@:2}"
else
echo "[WARNING] No inventory found. Running playbook without explicit inventory flag."
ansible-playbook "$PLAYBOOK_FILE" "${@:2}"
fi
echo "[INFO] Toolkit Action $ACTION completed."