Compare commits
8 Commits
b34e1ef872
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| fbc5e6edff | |||
| 4b7e604313 | |||
| 1be07db276 | |||
| 89f24a7fef | |||
| 7d04ad7532 | |||
| b0f1d5d600 | |||
| 71d487c59a | |||
| f6d9274306 |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,4 +1,8 @@
|
|||||||
# Claude Code
|
# Vibe Coding
|
||||||
.mcp.json
|
|
||||||
.claude/
|
.claude/
|
||||||
|
.oprncode/
|
||||||
|
|
||||||
llmdoc/
|
llmdoc/
|
||||||
|
.sisyphus/
|
||||||
|
|
||||||
|
.mcp.json
|
||||||
343
README.md
Normal file
343
README.md
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
# 云计算和系统管理工具集
|
||||||
|
|
||||||
|
> 一个跨云平台和 Linux 系统管理的自动化脚本工具集
|
||||||
|
|
||||||
|
[](https://github.com/wangdefa/tools)
|
||||||
|
[](https://www.gnu.org/software/bash/)
|
||||||
|
[](LICENSE)
|
||||||
|
|
||||||
|
## 📋 项目简介
|
||||||
|
|
||||||
|
该工具集为系统管理员、云工程师和 DevOps 人员提供一站式的自动化管理解决方案,简化云基础设施管理、系统配置和部署流程。
|
||||||
|
|
||||||
|
### ✨ 核心特性
|
||||||
|
|
||||||
|
- 🌐 **跨云平台支持**:GCP、Oracle Cloud Infrastructure (OCI)
|
||||||
|
- 🔧 **系统管理自动化**:RAID、磁盘分区、环境配置
|
||||||
|
- 📦 **模块化设计**:可重用的公共库和独立脚本
|
||||||
|
- 🚀 **远程加载支持**:脚本可独立运行,无需本地依赖
|
||||||
|
- 📝 **统一规范**:标准化的命名、日志和错误处理
|
||||||
|
- 🎨 **友好输出**:彩色终端输出和详细日志
|
||||||
|
|
||||||
|
## 🗂️ 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
tools/
|
||||||
|
├── gcp/ # Google Cloud Platform 工具
|
||||||
|
│ ├── create_ai_projects.sh # 批量创建 AI 项目
|
||||||
|
│ └── delete_all_projects.sh # 批量删除项目
|
||||||
|
├── oci/ # Oracle Cloud Infrastructure 工具
|
||||||
|
│ └── create_instance.sh # 批量部署虚拟机
|
||||||
|
├── linux/ # Linux 系统管理工具
|
||||||
|
│ ├── create_raid0_array.sh # 创建 RAID 0 阵列
|
||||||
|
│ ├── repartition_disks.sh # 重新分区磁盘
|
||||||
|
│ └── install_oh_my_zsh.sh # 安装 Oh My Zsh
|
||||||
|
├── common/ # 公共函数库
|
||||||
|
│ ├── logging.sh # 日志系统
|
||||||
|
│ ├── error_handler.sh # 错误处理
|
||||||
|
│ ├── remote_loader.sh # 远程加载器
|
||||||
|
│ ├── demo_usage.sh # 功能演示
|
||||||
|
│ └── README.md # 库文档
|
||||||
|
└── examples/ # 使用示例
|
||||||
|
├── remote_example.sh # 纯远程模式示例
|
||||||
|
├── hybrid_loader_template.sh # 混合模式模板
|
||||||
|
└── REMOTE_LOADER_README.md # 远程加载文档
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
### 前置要求
|
||||||
|
|
||||||
|
- Bash 4.0+
|
||||||
|
- curl 或 wget(用于远程加载)
|
||||||
|
- 对应云平台的 CLI 工具(gcloud、oci-cli 等)
|
||||||
|
|
||||||
|
### 基本使用
|
||||||
|
|
||||||
|
#### 1. 本地模式(默认)
|
||||||
|
|
||||||
|
克隆仓库后直接运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://gitea.bcde.io/wangdefa/tools.git
|
||||||
|
cd tools
|
||||||
|
|
||||||
|
# 运行 Linux 工具
|
||||||
|
sudo ./linux/create_raid0_array.sh
|
||||||
|
|
||||||
|
# 运行 GCP 工具
|
||||||
|
./gcp/create_ai_projects.sh -n 5 --prefix my-project
|
||||||
|
|
||||||
|
# 运行 OCI 工具
|
||||||
|
./oci/create_instance.sh -n 3 --shape VM.Standard.A1.Flex
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 远程模式(独立运行)
|
||||||
|
|
||||||
|
无需克隆仓库,直接下载并运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 下载并运行 RAID 创建脚本
|
||||||
|
curl -fsSL https://gitea.bcde.io/wangdefa/tools/raw/branch/main/linux/create_raid0_array.sh | bash
|
||||||
|
|
||||||
|
# 或使用 wget
|
||||||
|
wget -qO- https://gitea.bcde.io/wangdefa/tools/raw/branch/main/linux/create_raid0_array.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. 强制远程模式
|
||||||
|
|
||||||
|
在本地环境中强制使用远程库(获取最新版本):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
FORCE_REMOTE=1 ./linux/create_raid0_array.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 功能模块
|
||||||
|
|
||||||
|
### GCP 管理工具
|
||||||
|
|
||||||
|
#### create_ai_projects.sh - 批量创建 AI 项目
|
||||||
|
|
||||||
|
批量创建 Google Cloud AI Platform 项目并配置相关服务。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gcp/create_ai_projects.sh -n 10 --prefix ml-project --region us-central1
|
||||||
|
```
|
||||||
|
|
||||||
|
**主要功能:**
|
||||||
|
- 批量创建项目
|
||||||
|
- 自动启用 AI Platform API
|
||||||
|
- 配置项目计费
|
||||||
|
- 设置项目权限
|
||||||
|
|
||||||
|
#### delete_all_projects.sh - 批量删除项目
|
||||||
|
|
||||||
|
批量删除 Google Cloud 项目(带保护机制)。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gcp/delete_all_projects.sh --prefix ml-project --confirm
|
||||||
|
```
|
||||||
|
|
||||||
|
**安全特性:**
|
||||||
|
- 二次确认机制
|
||||||
|
- 排除生产项目
|
||||||
|
- 详细删除日志
|
||||||
|
|
||||||
|
### OCI 管理工具
|
||||||
|
|
||||||
|
#### create_instance.sh - 批量部署虚拟机
|
||||||
|
|
||||||
|
在 Oracle Cloud 上批量创建和配置虚拟机实例。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./oci/create_instance.sh -n 5 \
|
||||||
|
--shape VM.Standard.A1.Flex \
|
||||||
|
--shape_config 2+12 \
|
||||||
|
--image_name "Canonical-Ubuntu-20.04-aarch64-2025.05.20-0"
|
||||||
|
```
|
||||||
|
|
||||||
|
**功能特性:**
|
||||||
|
- 自动创建 VCN 和子网
|
||||||
|
- IPv4/IPv6 双栈支持
|
||||||
|
- 自定义实例规格
|
||||||
|
- 自动配置 SSH 访问
|
||||||
|
- 密码自动生成和标签记录
|
||||||
|
|
||||||
|
### Linux 系统管理工具
|
||||||
|
|
||||||
|
#### create_raid0_array.sh - 创建 RAID 0 阵列
|
||||||
|
|
||||||
|
自动创建并挂载 RAID 0 存储阵列。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ./linux/create_raid0_array.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**功能:**
|
||||||
|
- 自动检测可用磁盘
|
||||||
|
- 创建 RAID 0 阵列
|
||||||
|
- 格式化为 ext4
|
||||||
|
- 配置自动挂载
|
||||||
|
|
||||||
|
#### repartition_disks.sh - 重新分区磁盘
|
||||||
|
|
||||||
|
删除所有非系统盘的分区并创建新的 GPT 分区表。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ./linux/repartition_disks.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**安全特性:**
|
||||||
|
- 自动识别系统盘并排除
|
||||||
|
- 逐个磁盘处理
|
||||||
|
- 详细操作日志
|
||||||
|
|
||||||
|
#### install_oh_my_zsh.sh - 安装 Oh My Zsh
|
||||||
|
|
||||||
|
一键安装和配置 Oh My Zsh 开发环境。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./linux/install_oh_my_zsh.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**配置项:**
|
||||||
|
- 自动安装 Zsh
|
||||||
|
- 配置 Oh My Zsh
|
||||||
|
- 安装常用插件
|
||||||
|
- 切换默认 Shell
|
||||||
|
|
||||||
|
## 🌐 远程库加载
|
||||||
|
|
||||||
|
所有脚本支持智能远程库加载机制,可在无本地依赖的情况下运行。
|
||||||
|
|
||||||
|
### 加载策略
|
||||||
|
|
||||||
|
1. **FORCE_REMOTE=1** → 强制使用远程库
|
||||||
|
2. **本地库存在** → 优先使用本地(默认)
|
||||||
|
3. **本地不存在** → 自动回退远程
|
||||||
|
4. **远程失败** → 友好错误提示
|
||||||
|
|
||||||
|
### 环境变量
|
||||||
|
|
||||||
|
| 变量 | 说明 | 默认值 |
|
||||||
|
|------|------|--------|
|
||||||
|
| `REMOTE_LIB_URL` | 远程仓库 URL | `https://gitea.bcde.io/wangdefa/tools/raw/branch/main` |
|
||||||
|
| `FORCE_REMOTE` | 强制使用远程库 | `0` |
|
||||||
|
|
||||||
|
### 使用示例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用本地库(默认)
|
||||||
|
./linux/create_raid0_array.sh
|
||||||
|
|
||||||
|
# 强制使用远程库
|
||||||
|
FORCE_REMOTE=1 ./linux/create_raid0_array.sh
|
||||||
|
|
||||||
|
# 使用自定义仓库 URL
|
||||||
|
REMOTE_LIB_URL=https://example.com/repo ./script.sh
|
||||||
|
|
||||||
|
# 完全独立运行(无需克隆)
|
||||||
|
curl -fsSL https://gitea.bcde.io/wangdefa/tools/raw/branch/main/linux/create_raid0_array.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
详细文档:[examples/REMOTE_LOADER_README.md](examples/REMOTE_LOADER_README.md)
|
||||||
|
|
||||||
|
## 📚 公共库
|
||||||
|
|
||||||
|
### logging.sh - 日志系统
|
||||||
|
|
||||||
|
提供统一的日志输出功能,支持多级别和彩色输出。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source common/logging.sh
|
||||||
|
|
||||||
|
log_debug "调试信息"
|
||||||
|
log_info "普通信息"
|
||||||
|
log_warning "警告信息"
|
||||||
|
log_error "错误信息(不退出)"
|
||||||
|
log_success "成功信息"
|
||||||
|
```
|
||||||
|
|
||||||
|
**功能:**
|
||||||
|
- 5 个日志级别(DEBUG/INFO/WARNING/ERROR/SUCCESS)
|
||||||
|
- 彩色终端输出
|
||||||
|
- 时间戳支持
|
||||||
|
- 日志文件输出
|
||||||
|
|
||||||
|
### error_handler.sh - 错误处理
|
||||||
|
|
||||||
|
提供错误检查和命令执行辅助函数。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source common/error_handler.sh
|
||||||
|
|
||||||
|
check_command "jq" # 检查命令是否存在
|
||||||
|
check_file "/path/to/file" # 检查文件是否存在
|
||||||
|
check_not_empty "$var" "var_name" # 检查变量非空
|
||||||
|
check_return $? "操作失败" # 检查返回码
|
||||||
|
|
||||||
|
run_command "描述" command args # 执行命令并检查结果
|
||||||
|
retry_command 3 2 "失败" command # 重试执行命令
|
||||||
|
```
|
||||||
|
|
||||||
|
详细文档:[common/README.md](common/README.md)
|
||||||
|
|
||||||
|
## 🔧 开发
|
||||||
|
|
||||||
|
### 编码规范
|
||||||
|
|
||||||
|
本项目遵循严格的 Bash 编码规范:
|
||||||
|
|
||||||
|
- **命名规范**:snake_case(函数、变量、文件)
|
||||||
|
- **文件命名**:`动词_名词.sh`(如 `create_raid0_array.sh`)
|
||||||
|
- **函数注释**:使用标准化的文档注释格式
|
||||||
|
- **错误处理**:使用 `set -euo pipefail` 严格模式
|
||||||
|
- **代码风格**:4 空格缩进,ShellCheck 检查
|
||||||
|
|
||||||
|
### 添加新脚本
|
||||||
|
|
||||||
|
1. 参考 [examples/hybrid_loader_template.sh](examples/hybrid_loader_template.sh) 创建脚本
|
||||||
|
2. 添加远程库加载支持
|
||||||
|
3. 使用公共库函数
|
||||||
|
4. 遵循命名和注释规范
|
||||||
|
5. 执行 `shellcheck` 检查
|
||||||
|
6. 更新文档
|
||||||
|
|
||||||
|
### 测试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 语法检查
|
||||||
|
bash -n script.sh
|
||||||
|
|
||||||
|
# ShellCheck 检查
|
||||||
|
shellcheck script.sh
|
||||||
|
|
||||||
|
# 功能测试
|
||||||
|
./common/demo_usage.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🤝 贡献
|
||||||
|
|
||||||
|
欢迎贡献代码、报告问题或提出建议!
|
||||||
|
|
||||||
|
1. Fork 本仓库
|
||||||
|
2. 创建特性分支 (`git checkout -b feature/amazing-feature`)
|
||||||
|
3. 提交更改 (`git commit -m 'feat: add amazing feature'`)
|
||||||
|
4. 推送到分支 (`git push origin feature/amazing-feature`)
|
||||||
|
5. 创建 Pull Request
|
||||||
|
|
||||||
|
## 📝 更新日志
|
||||||
|
|
||||||
|
### v2.1.0 (2025-01-XX)
|
||||||
|
|
||||||
|
- ✨ 新增远程库加载支持
|
||||||
|
- ✨ 新增混合加载模式(本地优先 + 远程回退)
|
||||||
|
- ✨ 新增环境变量配置(FORCE_REMOTE、REMOTE_LIB_URL)
|
||||||
|
- 📝 完善文档系统
|
||||||
|
- 🔧 统一文件命名规范
|
||||||
|
|
||||||
|
### v2.0.0 (2025-01-XX)
|
||||||
|
|
||||||
|
- ✨ 重构所有脚本使用公共库
|
||||||
|
- ✨ 新增统一日志系统
|
||||||
|
- ✨ 新增错误处理机制
|
||||||
|
- 📝 添加完整的函数注释
|
||||||
|
- 🐛 修复 Bash 兼容性问题
|
||||||
|
|
||||||
|
## 📄 许可证
|
||||||
|
|
||||||
|
本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件
|
||||||
|
|
||||||
|
## 👥 作者
|
||||||
|
|
||||||
|
Cloud Tools Project
|
||||||
|
|
||||||
|
## 🔗 相关链接
|
||||||
|
|
||||||
|
- **仓库地址**: https://gitea.bcde.io/wangdefa/tools
|
||||||
|
- **问题反馈**: https://gitea.bcde.io/wangdefa/tools/issues
|
||||||
|
- **在线文档**: https://gitea.bcde.io/wangdefa/tools/wiki
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
⭐ 如果这个项目对你有帮助,请给我们一个星标!
|
||||||
@@ -48,20 +48,31 @@ load_common_libs() {
|
|||||||
|
|
||||||
# 使用远程库
|
# 使用远程库
|
||||||
if [[ "$use_remote" == "true" ]]; then
|
if [[ "$use_remote" == "true" ]]; then
|
||||||
|
# 下载到临时文件(避免 process substitution 与 set -u 的交互问题)
|
||||||
|
local temp_loader
|
||||||
|
temp_loader=$(mktemp)
|
||||||
|
|
||||||
if command -v curl &>/dev/null; then
|
if command -v curl &>/dev/null; then
|
||||||
echo "[INFO] 使用 curl 下载远程库..." >&2
|
echo "[INFO] 使用 curl 下载远程库..." >&2
|
||||||
|
if curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" -o "$temp_loader" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
elif command -v wget &>/dev/null; then
|
elif command -v wget &>/dev/null; then
|
||||||
echo "[INFO] 使用 wget 下载远程库..." >&2
|
echo "[INFO] 使用 wget 下载远程库..." >&2
|
||||||
|
if wget -qO "$temp_loader" "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(wget -qO- "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$temp_loader"
|
||||||
echo "[ERROR] 无法加载公共库" >&2
|
echo "[ERROR] 无法加载公共库" >&2
|
||||||
echo "[ERROR] - 本地库不存在" >&2
|
echo "[ERROR] - 本地库不存在" >&2
|
||||||
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
||||||
|
|||||||
@@ -6,8 +6,10 @@
|
|||||||
# 版本: 1.0.0
|
# 版本: 1.0.0
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
# 远程仓库 URL 配置
|
# 远程仓库 URL 配置(如果调用者未定义,则使用默认值)
|
||||||
readonly REMOTE_BASE_URL="${REMOTE_LIB_URL:-https://gitea.bcde.io/wangdefa/tools/raw/branch/main}"
|
if [[ -z "${REMOTE_BASE_URL:-}" ]]; then
|
||||||
|
readonly REMOTE_BASE_URL="${REMOTE_LIB_URL:-https://gitea.bcde.io/wangdefa/tools/raw/branch/main}"
|
||||||
|
fi
|
||||||
|
|
||||||
# 临时目录用于存储下载的文件
|
# 临时目录用于存储下载的文件
|
||||||
readonly REMOTE_TMP_DIR="$(mktemp -d)"
|
readonly REMOTE_TMP_DIR="$(mktemp -d)"
|
||||||
|
|||||||
@@ -48,20 +48,31 @@ load_common_libs() {
|
|||||||
|
|
||||||
# 使用远程库
|
# 使用远程库
|
||||||
if [[ "$use_remote" == "true" ]]; then
|
if [[ "$use_remote" == "true" ]]; then
|
||||||
|
# 下载到临时文件(避免 process substitution 与 set -u 的交互问题)
|
||||||
|
local temp_loader
|
||||||
|
temp_loader=$(mktemp)
|
||||||
|
|
||||||
if command -v curl &>/dev/null; then
|
if command -v curl &>/dev/null; then
|
||||||
echo "[INFO] 使用 curl 下载远程库..." >&2
|
echo "[INFO] 使用 curl 下载远程库..." >&2
|
||||||
|
if curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" -o "$temp_loader" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
elif command -v wget &>/dev/null; then
|
elif command -v wget &>/dev/null; then
|
||||||
echo "[INFO] 使用 wget 下载远程库..." >&2
|
echo "[INFO] 使用 wget 下载远程库..." >&2
|
||||||
|
if wget -qO "$temp_loader" "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(wget -qO- "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$temp_loader"
|
||||||
echo "[ERROR] 无法加载公共库" >&2
|
echo "[ERROR] 无法加载公共库" >&2
|
||||||
echo "[ERROR] - 本地库不存在" >&2
|
echo "[ERROR] - 本地库不存在" >&2
|
||||||
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
||||||
@@ -269,23 +280,24 @@ enable_services() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# 创建服务账号
|
# 创建服务账号(用于 API 密钥身份验证)
|
||||||
#
|
#
|
||||||
create_service_account() {
|
create_service_account() {
|
||||||
local project_id=$1
|
local project_id=$1
|
||||||
local max_retries=$2
|
local max_retries=$2
|
||||||
local retry_delay=$3
|
local retry_delay=$3
|
||||||
|
local sa_name="vertex-ai-runner"
|
||||||
|
local sa_email="${sa_name}@${project_id}.iam.gserviceaccount.com"
|
||||||
|
|
||||||
log_info "创建服务账号 service-account@$project_id.iam.gserviceaccount.com"
|
log_info "创建服务账号 $sa_email"
|
||||||
|
|
||||||
# 检查服务账号是否已存在
|
if gcloud iam service-accounts describe "$sa_email" --project="$project_id" &>/dev/null; then
|
||||||
if gcloud iam service-accounts describe "service-account@$project_id.iam.gserviceaccount.com" --project="$project_id" &>/dev/null; then
|
|
||||||
log_warning "服务账号已存在,跳过创建"
|
log_warning "服务账号已存在,跳过创建"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if retry_command "$max_retries" "$retry_delay" gcloud iam service-accounts create service-account \
|
if retry_command "$max_retries" "$retry_delay" gcloud iam service-accounts create "$sa_name" \
|
||||||
--display-name="AI Platform Service Account" \
|
--display-name="Vertex AI Runner" \
|
||||||
--project="$project_id"; then
|
--project="$project_id"; then
|
||||||
log_success "服务账号创建成功"
|
log_success "服务账号创建成功"
|
||||||
return 0
|
return 0
|
||||||
@@ -296,48 +308,124 @@ create_service_account() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# 添加 IAM 策略
|
# 授予服务账号 Vertex AI 角色
|
||||||
#
|
#
|
||||||
add_iam_policy() {
|
grant_vertex_ai_role() {
|
||||||
local project_id=$1
|
local project_id=$1
|
||||||
local max_retries=$2
|
local max_retries=$2
|
||||||
local retry_delay=$3
|
local retry_delay=$3
|
||||||
|
local sa_email="vertex-ai-runner@${project_id}.iam.gserviceaccount.com"
|
||||||
|
|
||||||
log_info "授予 aiplatform.serviceAgent 角色"
|
log_info "授予 Vertex AI Platform Express User 角色"
|
||||||
if retry_command "$max_retries" "$retry_delay" gcloud projects add-iam-policy-binding "$project_id" \
|
if retry_command "$max_retries" "$retry_delay" gcloud projects add-iam-policy-binding "$project_id" \
|
||||||
--member="serviceAccount:service-account@$project_id.iam.gserviceaccount.com" \
|
--member="serviceAccount:$sa_email" \
|
||||||
--role="roles/aiplatform.serviceAgent"; then
|
--role="roles/aiplatform.expressUser" \
|
||||||
log_success "IAM 策略授予成功"
|
--condition=None; then
|
||||||
|
log_success "角色授予成功"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
log_error "授予 IAM 策略失败 (已重试)"
|
log_error "授予角色失败 (已重试)"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# 创建服务账号密钥
|
# 启用 API Keys 服务
|
||||||
#
|
#
|
||||||
create_service_account_key() {
|
enable_api_keys_service() {
|
||||||
local project_id=$1
|
local project_id=$1
|
||||||
local max_retries=$2
|
local max_retries=$2
|
||||||
local retry_delay=$3
|
local retry_delay=$3
|
||||||
|
|
||||||
log_info "创建服务账号密钥"
|
log_info "启用 apikeys.googleapis.com 服务"
|
||||||
|
if retry_command "$max_retries" "$retry_delay" gcloud services enable apikeys.googleapis.com --project="$project_id"; then
|
||||||
|
log_success "API Keys 服务启用成功"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_error "启用 apikeys.googleapis.com 服务失败 (已重试)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# 检查密钥文件是否已存在
|
#
|
||||||
if [ -f "pass-$project_id.json" ]; then
|
# 创建 Google Cloud API 密钥(绑定服务账号)
|
||||||
log_warning "密钥文件 pass-$project_id.json 已存在,跳过创建"
|
#
|
||||||
|
create_api_key() {
|
||||||
|
local project_id=$1
|
||||||
|
local max_retries=$2
|
||||||
|
local retry_delay=$3
|
||||||
|
local key_file="apikey-$project_id.txt"
|
||||||
|
local sa_email="vertex-ai-runner@${project_id}.iam.gserviceaccount.com"
|
||||||
|
|
||||||
|
log_info "创建 Google Cloud API 密钥(绑定服务账号 $sa_email)"
|
||||||
|
|
||||||
|
if [ -f "$key_file" ]; then
|
||||||
|
log_warning "API 密钥文件 $key_file 已存在,跳过创建"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if retry_command "$max_retries" "$retry_delay" gcloud iam service-accounts keys create "pass-$project_id.json" \
|
local key_name="vertex-ai-key"
|
||||||
--iam-account="service-account@$project_id.iam.gserviceaccount.com" \
|
local attempt=1
|
||||||
--project="$project_id"; then
|
local create_success=false
|
||||||
log_success "服务账号密钥创建成功: pass-$project_id.json"
|
|
||||||
|
while [ $attempt -le "$max_retries" ]; do
|
||||||
|
log_info "尝试创建 API 密钥 (第 $attempt/$max_retries 次)"
|
||||||
|
|
||||||
|
local temp_error
|
||||||
|
temp_error=$(mktemp)
|
||||||
|
|
||||||
|
if gcloud services api-keys create \
|
||||||
|
--display-name="$key_name" \
|
||||||
|
--api-target=service=aiplatform.googleapis.com \
|
||||||
|
--service-account="$sa_email" \
|
||||||
|
--project="$project_id" 2>"$temp_error"; then
|
||||||
|
|
||||||
|
log_success "API 密钥创建命令执行成功"
|
||||||
|
rm -f "$temp_error"
|
||||||
|
create_success=true
|
||||||
|
break
|
||||||
|
else
|
||||||
|
log_warning "创建失败 (第 $attempt/$max_retries 次)"
|
||||||
|
if [ -s "$temp_error" ]; then
|
||||||
|
log_error "错误: $(cat "$temp_error")"
|
||||||
|
fi
|
||||||
|
rm -f "$temp_error"
|
||||||
|
|
||||||
|
if [ $attempt -lt "$max_retries" ]; then
|
||||||
|
log_info "将在 $retry_delay 秒后重试..."
|
||||||
|
sleep "$retry_delay"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
((attempt++))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$create_success" != "true" ]; then
|
||||||
|
log_error "创建 API 密钥命令执行失败"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "等待 API 密钥创建完成..."
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
log_info "查找已创建的 API 密钥..."
|
||||||
|
local key_resource_name
|
||||||
|
key_resource_name=$(gcloud services api-keys list --project="$project_id" --filter="displayName=$key_name" --format="value(name)" 2>/dev/null | head -n 1)
|
||||||
|
|
||||||
|
if [ -z "$key_resource_name" ]; then
|
||||||
|
log_error "未找到已创建的 API 密钥"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "获取 API 密钥字符串: $key_resource_name"
|
||||||
|
local api_key_value
|
||||||
|
api_key_value=$(gcloud services api-keys get-key-string "$key_resource_name" --format="value(keyString)" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -n "$api_key_value" ]; then
|
||||||
|
echo "$api_key_value" > "$key_file"
|
||||||
|
log_success "API 密钥创建成功: $key_file"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
log_error "创建服务账号密钥失败 (已重试)"
|
log_error "无法获取 API 密钥字符串"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -348,6 +436,7 @@ START_NUM="$DEFAULT_START_NUM"
|
|||||||
REPEAT_NUM="$DEFAULT_REPEAT_NUM"
|
REPEAT_NUM="$DEFAULT_REPEAT_NUM"
|
||||||
MAX_RETRIES="$DEFAULT_MAX_RETRIES"
|
MAX_RETRIES="$DEFAULT_MAX_RETRIES"
|
||||||
RETRY_DELAY="$DEFAULT_RETRY_DELAY"
|
RETRY_DELAY="$DEFAULT_RETRY_DELAY"
|
||||||
|
DEBUG_MODE=false
|
||||||
|
|
||||||
# 解析命令行参数
|
# 解析命令行参数
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
@@ -511,17 +600,25 @@ for i in $(seq "$START_NUM" "$END_NUM"); do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$project_success" = true ]; then
|
if [ "$project_success" = true ]; then
|
||||||
log_debug "步骤5: 添加IAM策略"
|
log_debug "步骤5: 授予 Vertex AI 角色"
|
||||||
if ! add_iam_policy "$PROJECT_ID" "$MAX_RETRIES" "$RETRY_DELAY"; then
|
if ! grant_vertex_ai_role "$PROJECT_ID" "$MAX_RETRIES" "$RETRY_DELAY"; then
|
||||||
log_debug "添加IAM策略失败"
|
log_debug "授予角色失败"
|
||||||
project_success=false
|
project_success=false
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$project_success" = true ]; then
|
if [ "$project_success" = true ]; then
|
||||||
log_debug "步骤6: 创建服务账号密钥"
|
log_debug "步骤6: 启用 API Keys 服务"
|
||||||
if ! create_service_account_key "$PROJECT_ID" "$MAX_RETRIES" "$RETRY_DELAY"; then
|
if ! enable_api_keys_service "$PROJECT_ID" "$MAX_RETRIES" "$RETRY_DELAY"; then
|
||||||
log_debug "创建服务账号密钥失败"
|
log_debug "启用 API Keys 服务失败"
|
||||||
|
project_success=false
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$project_success" = true ]; then
|
||||||
|
log_debug "步骤7: 创建 API 密钥"
|
||||||
|
if ! create_api_key "$PROJECT_ID" "$MAX_RETRIES" "$RETRY_DELAY"; then
|
||||||
|
log_debug "创建 API 密钥失败"
|
||||||
project_success=false
|
project_success=false
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -529,10 +626,10 @@ for i in $(seq "$START_NUM" "$END_NUM"); do
|
|||||||
# 统计结果
|
# 统计结果
|
||||||
if [ "$project_success" = true ]; then
|
if [ "$project_success" = true ]; then
|
||||||
log_success "项目 $PROJECT_ID 所有操作处理完成"
|
log_success "项目 $PROJECT_ID 所有操作处理完成"
|
||||||
((successful_projects++))
|
((successful_projects++)) || true
|
||||||
else
|
else
|
||||||
log_warning "项目 $PROJECT_ID 处理过程中出现错误,部分操作可能未完成。"
|
log_warning "项目 $PROJECT_ID 处理过程中出现错误,部分操作可能未完成。"
|
||||||
((failed_projects++))
|
((failed_projects++)) || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 添加延迟,避免 API 限制
|
# 添加延迟,避免 API 限制
|
||||||
@@ -556,8 +653,8 @@ if [ "$successful_projects" -gt 0 ]; then
|
|||||||
log_success "成功创建的项目:"
|
log_success "成功创建的项目:"
|
||||||
for i in $(seq "$START_NUM" "$END_NUM"); do
|
for i in $(seq "$START_NUM" "$END_NUM"); do
|
||||||
PROJECT_ID="${PROJECT_ID_PREFIX}$(printf "%02d" "$i")"
|
PROJECT_ID="${PROJECT_ID_PREFIX}$(printf "%02d" "$i")"
|
||||||
if [ -f "pass-$PROJECT_ID.json" ]; then
|
if [ -f "apikey-$PROJECT_ID.txt" ]; then
|
||||||
log_info " ✓ $PROJECT_ID (密钥文件: pass-$PROJECT_ID.json)"
|
log_info " ✓ $PROJECT_ID (API 密钥文件: apikey-$PROJECT_ID.txt)"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# 文件名: delete_all_projects.sh
|
# 文件名: delete_all_projects.sh
|
||||||
# 描述: 删除 GCP 账户下的所有项目(危险操作)
|
# 描述: 删除 GCP 账户下的所有项目(危险操作)
|
||||||
# 作者: Cloud Tools Project
|
# 作者: Cloud Tools Project
|
||||||
# 版本: 2.1.0(支持远程库加载)
|
# 版本: 2.1.1(支持远程库加载)
|
||||||
# 警告: 此操作不可逆,所有项目数据将永久丢失
|
# 警告: 此操作不可逆,所有项目数据将永久丢失
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
@@ -49,20 +49,31 @@ load_common_libs() {
|
|||||||
|
|
||||||
# 使用远程库
|
# 使用远程库
|
||||||
if [[ "$use_remote" == "true" ]]; then
|
if [[ "$use_remote" == "true" ]]; then
|
||||||
|
# 下载到临时文件(避免 process substitution 与 set -u 的交互问题)
|
||||||
|
local temp_loader
|
||||||
|
temp_loader=$(mktemp)
|
||||||
|
|
||||||
if command -v curl &>/dev/null; then
|
if command -v curl &>/dev/null; then
|
||||||
echo "[INFO] 使用 curl 下载远程库..." >&2
|
echo "[INFO] 使用 curl 下载远程库..." >&2
|
||||||
|
if curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" -o "$temp_loader" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
elif command -v wget &>/dev/null; then
|
elif command -v wget &>/dev/null; then
|
||||||
echo "[INFO] 使用 wget 下载远程库..." >&2
|
echo "[INFO] 使用 wget 下载远程库..." >&2
|
||||||
|
if wget -qO "$temp_loader" "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(wget -qO- "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$temp_loader"
|
||||||
echo "[ERROR] 无法加载公共库" >&2
|
echo "[ERROR] 无法加载公共库" >&2
|
||||||
echo "[ERROR] - 本地库不存在" >&2
|
echo "[ERROR] - 本地库不存在" >&2
|
||||||
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
||||||
@@ -79,32 +90,39 @@ readonly LOG_FILE="deleted_projects_$(date +%Y%m%d_%H%M%S).log"
|
|||||||
log_set_file "$LOG_FILE"
|
log_set_file "$LOG_FILE"
|
||||||
|
|
||||||
#
|
#
|
||||||
# 获取并展示所有项目
|
# 获取所有项目 ID 列表
|
||||||
|
# 返回: 项目 ID 列表(每行一个),通过 stdout 返回
|
||||||
|
# 退出码: 0 成功,1 失败
|
||||||
#
|
#
|
||||||
get_and_display_projects() {
|
get_projects() {
|
||||||
# 获取所有项目
|
|
||||||
log_warning "获取项目列表..."
|
log_warning "获取项目列表..."
|
||||||
local projects
|
local projects
|
||||||
projects=$(gcloud projects list --format="value(projectId)")
|
projects=$(gcloud projects list --format="value(projectId)")
|
||||||
|
|
||||||
if [ -z "$projects" ]; then
|
if [ -z "$projects" ]; then
|
||||||
log_warning "没有找到任何项目"
|
log_warning "没有找到任何项目"
|
||||||
exit 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 显示所有项目
|
|
||||||
log_warning "以下项目将被删除:"
|
|
||||||
echo "$projects" | nl
|
|
||||||
echo
|
|
||||||
|
|
||||||
# 统计项目数量
|
|
||||||
local project_count
|
|
||||||
project_count=$(echo "$projects" | wc -l)
|
|
||||||
log_warning "总计: $project_count 个项目"
|
|
||||||
|
|
||||||
echo "$projects"
|
echo "$projects"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# 显示项目列表(带编号)
|
||||||
|
# 参数: $1 - 项目列表(换行符分隔)
|
||||||
|
# 输出: 到 stderr
|
||||||
|
#
|
||||||
|
display_projects() {
|
||||||
|
local projects="$1"
|
||||||
|
local project_count
|
||||||
|
project_count=$(echo "$projects" | wc -l | tr -d ' ')
|
||||||
|
|
||||||
|
log_warning "以下项目将被删除:"
|
||||||
|
echo "$projects" | nl >&2
|
||||||
|
echo >&2
|
||||||
|
log_warning "总计: $project_count 个项目"
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# 删除单个项目
|
# 删除单个项目
|
||||||
#
|
#
|
||||||
@@ -143,13 +161,22 @@ main() {
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 获取并展示项目列表
|
# 获取项目列表
|
||||||
local projects
|
local projects
|
||||||
projects=$(get_and_display_projects)
|
projects=$(get_projects)
|
||||||
|
|
||||||
|
# 检查是否有项目
|
||||||
|
if [ -z "$projects" ]; then
|
||||||
|
log_warning "没有找到任何项目,退出"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 显示项目列表
|
||||||
|
display_projects "$projects"
|
||||||
|
|
||||||
# 统计项目数量
|
# 统计项目数量
|
||||||
local project_count
|
local project_count
|
||||||
project_count=$(echo "$projects" | wc -l)
|
project_count=$(echo "$projects" | wc -l | tr -d ' ')
|
||||||
|
|
||||||
# 第二次确认
|
# 第二次确认
|
||||||
log_error "最后确认:您将要删除 $project_count 个项目!"
|
log_error "最后确认:您将要删除 $project_count 个项目!"
|
||||||
@@ -169,9 +196,9 @@ main() {
|
|||||||
|
|
||||||
for project_id in $projects; do
|
for project_id in $projects; do
|
||||||
if delete_project "$project_id" "$LOG_FILE"; then
|
if delete_project "$project_id" "$LOG_FILE"; then
|
||||||
((deleted_count++))
|
((deleted_count++)) || true
|
||||||
else
|
else
|
||||||
((failed_count++))
|
((failed_count++)) || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 添加短暂延迟,避免 API 限制
|
# 添加短暂延迟,避免 API 限制
|
||||||
|
|||||||
@@ -49,20 +49,31 @@ load_common_libs() {
|
|||||||
|
|
||||||
# 使用远程库
|
# 使用远程库
|
||||||
if [[ "$use_remote" == "true" ]]; then
|
if [[ "$use_remote" == "true" ]]; then
|
||||||
|
# 下载到临时文件(避免 process substitution 与 set -u 的交互问题)
|
||||||
|
local temp_loader
|
||||||
|
temp_loader=$(mktemp)
|
||||||
|
|
||||||
if command -v curl &>/dev/null; then
|
if command -v curl &>/dev/null; then
|
||||||
echo "[INFO] 使用 curl 下载远程库..." >&2
|
echo "[INFO] 使用 curl 下载远程库..." >&2
|
||||||
|
if curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" -o "$temp_loader" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
elif command -v wget &>/dev/null; then
|
elif command -v wget &>/dev/null; then
|
||||||
echo "[INFO] 使用 wget 下载远程库..." >&2
|
echo "[INFO] 使用 wget 下载远程库..." >&2
|
||||||
|
if wget -qO "$temp_loader" "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(wget -qO- "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$temp_loader"
|
||||||
echo "[ERROR] 无法加载公共库" >&2
|
echo "[ERROR] 无法加载公共库" >&2
|
||||||
echo "[ERROR] - 本地库不存在" >&2
|
echo "[ERROR] - 本地库不存在" >&2
|
||||||
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
||||||
|
|||||||
@@ -48,20 +48,31 @@ load_common_libs() {
|
|||||||
|
|
||||||
# 使用远程库
|
# 使用远程库
|
||||||
if [[ "$use_remote" == "true" ]]; then
|
if [[ "$use_remote" == "true" ]]; then
|
||||||
|
# 下载到临时文件(避免 process substitution 与 set -u 的交互问题)
|
||||||
|
local temp_loader
|
||||||
|
temp_loader=$(mktemp)
|
||||||
|
|
||||||
if command -v curl &>/dev/null; then
|
if command -v curl &>/dev/null; then
|
||||||
echo "[INFO] 使用 curl 下载远程库..." >&2
|
echo "[INFO] 使用 curl 下载远程库..." >&2
|
||||||
|
if curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" -o "$temp_loader" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
elif command -v wget &>/dev/null; then
|
elif command -v wget &>/dev/null; then
|
||||||
echo "[INFO] 使用 wget 下载远程库..." >&2
|
echo "[INFO] 使用 wget 下载远程库..." >&2
|
||||||
|
if wget -qO "$temp_loader" "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(wget -qO- "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$temp_loader"
|
||||||
echo "[ERROR] 无法加载公共库" >&2
|
echo "[ERROR] 无法加载公共库" >&2
|
||||||
echo "[ERROR] - 本地库不存在" >&2
|
echo "[ERROR] - 本地库不存在" >&2
|
||||||
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
||||||
|
|||||||
@@ -49,20 +49,31 @@ load_common_libs() {
|
|||||||
|
|
||||||
# 使用远程库
|
# 使用远程库
|
||||||
if [[ "$use_remote" == "true" ]]; then
|
if [[ "$use_remote" == "true" ]]; then
|
||||||
|
# 下载到临时文件(避免 process substitution 与 set -u 的交互问题)
|
||||||
|
local temp_loader
|
||||||
|
temp_loader=$(mktemp)
|
||||||
|
|
||||||
if command -v curl &>/dev/null; then
|
if command -v curl &>/dev/null; then
|
||||||
echo "[INFO] 使用 curl 下载远程库..." >&2
|
echo "[INFO] 使用 curl 下载远程库..." >&2
|
||||||
|
if curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" -o "$temp_loader" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
elif command -v wget &>/dev/null; then
|
elif command -v wget &>/dev/null; then
|
||||||
echo "[INFO] 使用 wget 下载远程库..." >&2
|
echo "[INFO] 使用 wget 下载远程库..." >&2
|
||||||
|
if wget -qO "$temp_loader" "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(wget -qO- "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$temp_loader"
|
||||||
echo "[ERROR] 无法加载公共库" >&2
|
echo "[ERROR] 无法加载公共库" >&2
|
||||||
echo "[ERROR] - 本地库不存在" >&2
|
echo "[ERROR] - 本地库不存在" >&2
|
||||||
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
||||||
|
|||||||
@@ -48,20 +48,31 @@ load_common_libs() {
|
|||||||
|
|
||||||
# 使用远程库
|
# 使用远程库
|
||||||
if [[ "$use_remote" == "true" ]]; then
|
if [[ "$use_remote" == "true" ]]; then
|
||||||
|
# 下载到临时文件(避免 process substitution 与 set -u 的交互问题)
|
||||||
|
local temp_loader
|
||||||
|
temp_loader=$(mktemp)
|
||||||
|
|
||||||
if command -v curl &>/dev/null; then
|
if command -v curl &>/dev/null; then
|
||||||
echo "[INFO] 使用 curl 下载远程库..." >&2
|
echo "[INFO] 使用 curl 下载远程库..." >&2
|
||||||
|
if curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" -o "$temp_loader" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(curl -fsSL "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
elif command -v wget &>/dev/null; then
|
elif command -v wget &>/dev/null; then
|
||||||
echo "[INFO] 使用 wget 下载远程库..." >&2
|
echo "[INFO] 使用 wget 下载远程库..." >&2
|
||||||
|
if wget -qO "$temp_loader" "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null; then
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
if source <(wget -qO- "${REMOTE_BASE_URL}/common/remote_loader.sh" 2>/dev/null); then
|
if source "$temp_loader"; then
|
||||||
|
rm -f "$temp_loader"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$temp_loader"
|
||||||
echo "[ERROR] 无法加载公共库" >&2
|
echo "[ERROR] 无法加载公共库" >&2
|
||||||
echo "[ERROR] - 本地库不存在" >&2
|
echo "[ERROR] - 本地库不存在" >&2
|
||||||
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
echo "[ERROR] - 远程下载失败(需要 curl 或 wget)" >&2
|
||||||
@@ -77,7 +88,7 @@ load_common_libs
|
|||||||
readonly DEFAULT_NUMBER=1
|
readonly DEFAULT_NUMBER=1
|
||||||
readonly DEFAULT_SHAPE="VM.Standard.A1.Flex"
|
readonly DEFAULT_SHAPE="VM.Standard.A1.Flex"
|
||||||
readonly DEFAULT_SHAPE_CONFIG="1+6"
|
readonly DEFAULT_SHAPE_CONFIG="1+6"
|
||||||
readonly DEFAULT_IMAGE_NAME="Canonical-Ubuntu-20.04-aarch64-2025.05.20-0"
|
readonly DEFAULT_IMAGE_NAME="Canonical-Ubuntu-20.04-aarch64-2025.07.23-0"
|
||||||
readonly DEFAULT_BOOT_VOLUME_SIZE=0
|
readonly DEFAULT_BOOT_VOLUME_SIZE=0
|
||||||
readonly DEFAULT_VPU=120
|
readonly DEFAULT_VPU=120
|
||||||
readonly DEFAULT_DOMAIN=0
|
readonly DEFAULT_DOMAIN=0
|
||||||
@@ -361,9 +372,18 @@ main() {
|
|||||||
local fifo
|
local fifo
|
||||||
fifo=$(mktemp -u)
|
fifo=$(mktemp -u)
|
||||||
mkfifo "$fifo"
|
mkfifo "$fifo"
|
||||||
trap 'rm -f "$fifo"' EXIT
|
|
||||||
|
# 启动后台任务
|
||||||
configure_network "$ocid" "$fifo" &
|
configure_network "$ocid" "$fifo" &
|
||||||
|
local bg_pid=$!
|
||||||
|
|
||||||
|
# 读取结果
|
||||||
read subnet_id < "$fifo"
|
read subnet_id < "$fifo"
|
||||||
|
|
||||||
|
# 等待后台任务完成并清理
|
||||||
|
wait "$bg_pid"
|
||||||
|
rm -f "$fifo"
|
||||||
|
|
||||||
log_success "网络配置完成 (子网ID: $subnet_id)"
|
log_success "网络配置完成 (子网ID: $subnet_id)"
|
||||||
else
|
else
|
||||||
log_info "使用指定的子网ID: $subnet_id"
|
log_info "使用指定的子网ID: $subnet_id"
|
||||||
|
|||||||
Reference in New Issue
Block a user