xuanwu-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -0
- package/bin/xuanwu +20 -0
- package/dist/api/client.d.ts +30 -0
- package/dist/api/client.js +332 -0
- package/dist/commands/build.d.ts +5 -0
- package/dist/commands/build.js +57 -0
- package/dist/commands/connect.d.ts +5 -0
- package/dist/commands/connect.js +67 -0
- package/dist/commands/deploy.d.ts +5 -0
- package/dist/commands/deploy.js +85 -0
- package/dist/commands/env.d.ts +5 -0
- package/dist/commands/env.js +119 -0
- package/dist/commands/logs.d.ts +5 -0
- package/dist/commands/logs.js +39 -0
- package/dist/commands/pods.d.ts +5 -0
- package/dist/commands/pods.js +56 -0
- package/dist/commands/scale.d.ts +5 -0
- package/dist/commands/scale.js +32 -0
- package/dist/commands/svc.d.ts +5 -0
- package/dist/commands/svc.js +100 -0
- package/dist/config/store.d.ts +18 -0
- package/dist/config/store.js +108 -0
- package/dist/config/types.d.ts +86 -0
- package/dist/config/types.js +5 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +142 -0
- package/dist/output/formatter.d.ts +15 -0
- package/dist/output/formatter.js +95 -0
- package/docs/DESIGN.md +363 -0
- package/docs//345/276/205/344/274/230/345/214/226.md +89 -0
- package/package.json +31 -0
- package/src/api/client.ts +380 -0
- package/src/commands/build.ts +67 -0
- package/src/commands/connect.ts +75 -0
- package/src/commands/deploy.ts +90 -0
- package/src/commands/env.ts +144 -0
- package/src/commands/logs.ts +47 -0
- package/src/commands/pods.ts +60 -0
- package/src/commands/scale.ts +35 -0
- package/src/commands/svc.ts +114 -0
- package/src/config/store.ts +86 -0
- package/src/config/types.ts +99 -0
- package/src/index.ts +127 -0
- package/src/output/formatter.ts +112 -0
- package/tsconfig.json +17 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* xuanwu-cli 主入口
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const commander_1 = require("commander");
|
|
40
|
+
const connect_1 = require("./commands/connect");
|
|
41
|
+
const env_1 = require("./commands/env");
|
|
42
|
+
const deploy_1 = require("./commands/deploy");
|
|
43
|
+
const svc_1 = require("./commands/svc");
|
|
44
|
+
const build_1 = require("./commands/build");
|
|
45
|
+
const scale_1 = require("./commands/scale");
|
|
46
|
+
const logs_1 = require("./commands/logs");
|
|
47
|
+
const pods_1 = require("./commands/pods");
|
|
48
|
+
const program = new commander_1.Command();
|
|
49
|
+
program
|
|
50
|
+
.name('xuanwu')
|
|
51
|
+
.description('玄武工厂平台 CLI 工具')
|
|
52
|
+
.version('1.0.0')
|
|
53
|
+
.option('-o, --output <format>', 'Output format (human|json)', 'human');
|
|
54
|
+
// 添加子命令
|
|
55
|
+
program.addCommand((0, connect_1.makeConnectCommand)());
|
|
56
|
+
program.addCommand((0, env_1.makeEnvCommand)());
|
|
57
|
+
program.addCommand((0, deploy_1.makeDeployCommand)());
|
|
58
|
+
program.addCommand((0, svc_1.makeSvcCommand)());
|
|
59
|
+
program.addCommand((0, build_1.makeBuildCommand)());
|
|
60
|
+
program.addCommand((0, scale_1.makeScaleCommand)());
|
|
61
|
+
program.addCommand((0, logs_1.makeLogsCommand)());
|
|
62
|
+
program.addCommand((0, pods_1.makePodsCommand)());
|
|
63
|
+
// 快捷命令:直接 deploy 也支持
|
|
64
|
+
program
|
|
65
|
+
.command('up <namespace> <service-name>')
|
|
66
|
+
.description('Build and deploy a service')
|
|
67
|
+
.action(async (namespace, serviceName) => {
|
|
68
|
+
const { makeBuildCommand } = await Promise.resolve().then(() => __importStar(require('./commands/build')));
|
|
69
|
+
const buildCmd = makeBuildCommand();
|
|
70
|
+
// 简化:只调用部署
|
|
71
|
+
console.log('Use: xuanwu build <namespace> <service-name> && xuanwu deploy <namespace> <service-name>');
|
|
72
|
+
});
|
|
73
|
+
// restart 命令
|
|
74
|
+
program
|
|
75
|
+
.command('restart <namespace> <service-name>')
|
|
76
|
+
.description('Restart a service')
|
|
77
|
+
.action(async (namespace, serviceName) => {
|
|
78
|
+
console.log('Restarting service... (not implemented)');
|
|
79
|
+
});
|
|
80
|
+
// exec 命令
|
|
81
|
+
program
|
|
82
|
+
.command('exec <namespace> <service-name>')
|
|
83
|
+
.description('Execute command in container')
|
|
84
|
+
.option('-c, --command <cmd>', 'Command to execute', 'ls -la')
|
|
85
|
+
.option('-p, --pod <pod-name>', 'Specific pod name')
|
|
86
|
+
.action(async (namespace, serviceName, options) => {
|
|
87
|
+
const { configStore } = await Promise.resolve().then(() => __importStar(require('./config/store')));
|
|
88
|
+
const { createClient } = await Promise.resolve().then(() => __importStar(require('./api/client')));
|
|
89
|
+
const { OutputFormatter } = await Promise.resolve().then(() => __importStar(require('./output/formatter')));
|
|
90
|
+
const conn = configStore.getDefaultConnection();
|
|
91
|
+
if (!conn) {
|
|
92
|
+
OutputFormatter.error('No connection configured');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const client = createClient(conn);
|
|
96
|
+
const result = await client.exec(namespace, serviceName, options.command, options.pod);
|
|
97
|
+
if (!result.success) {
|
|
98
|
+
OutputFormatter.error(result.error.message);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const data = result.data;
|
|
102
|
+
if (data.stdout)
|
|
103
|
+
console.log(data.stdout);
|
|
104
|
+
if (data.stderr)
|
|
105
|
+
console.error(data.stderr);
|
|
106
|
+
if (data.exitCode !== 0) {
|
|
107
|
+
process.exit(data.exitCode);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
// top 命令
|
|
111
|
+
program
|
|
112
|
+
.command('top <namespace> <service-name>')
|
|
113
|
+
.description('Show resource usage')
|
|
114
|
+
.option('-p, --pod <pod-name>', 'Specific pod name')
|
|
115
|
+
.action(async (namespace, serviceName, options) => {
|
|
116
|
+
const { configStore } = await Promise.resolve().then(() => __importStar(require('./config/store')));
|
|
117
|
+
const { createClient } = await Promise.resolve().then(() => __importStar(require('./api/client')));
|
|
118
|
+
const { OutputFormatter } = await Promise.resolve().then(() => __importStar(require('./output/formatter')));
|
|
119
|
+
const conn = configStore.getDefaultConnection();
|
|
120
|
+
if (!conn) {
|
|
121
|
+
OutputFormatter.error('No connection configured');
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const client = createClient(conn);
|
|
125
|
+
const result = await client.getMetrics(namespace, serviceName, options.pod);
|
|
126
|
+
if (!result.success) {
|
|
127
|
+
OutputFormatter.error(result.error.message);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const pods = result.data?.pods || [];
|
|
131
|
+
if (pods.length === 0) {
|
|
132
|
+
OutputFormatter.info('No pod metrics found');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
OutputFormatter.table(['Pod Name', 'CPU', 'Memory'], pods.map((p) => [
|
|
136
|
+
p.name,
|
|
137
|
+
p.containers?.[0]?.cpuRaw || 'N/A',
|
|
138
|
+
p.containers?.[0]?.memoryRaw || 'N/A'
|
|
139
|
+
]));
|
|
140
|
+
});
|
|
141
|
+
// 解析参数
|
|
142
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 输出格式化
|
|
3
|
+
*/
|
|
4
|
+
import { CLIResult } from '../config/types';
|
|
5
|
+
export type OutputFormat = 'human' | 'json';
|
|
6
|
+
export declare class OutputFormatter {
|
|
7
|
+
static format<T>(result: CLIResult<T>, format?: OutputFormat): string;
|
|
8
|
+
private static formatHuman;
|
|
9
|
+
private static formatItem;
|
|
10
|
+
private static formatObject;
|
|
11
|
+
static success(message: string): void;
|
|
12
|
+
static error(message: string): void;
|
|
13
|
+
static info(message: string): void;
|
|
14
|
+
static table(headers: string[], rows: string[][]): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 输出格式化
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OutputFormatter = void 0;
|
|
7
|
+
class OutputFormatter {
|
|
8
|
+
static format(result, format = 'human') {
|
|
9
|
+
if (format === 'json') {
|
|
10
|
+
return JSON.stringify(result, null, 2);
|
|
11
|
+
}
|
|
12
|
+
return this.formatHuman(result);
|
|
13
|
+
}
|
|
14
|
+
static formatHuman(result) {
|
|
15
|
+
if (!result.success) {
|
|
16
|
+
const error = result.error;
|
|
17
|
+
return `✗ Error: ${error.message}`;
|
|
18
|
+
}
|
|
19
|
+
const data = result.data;
|
|
20
|
+
// 数组列表
|
|
21
|
+
if (Array.isArray(data)) {
|
|
22
|
+
if (data.length === 0) {
|
|
23
|
+
return '(empty)';
|
|
24
|
+
}
|
|
25
|
+
return data.map(item => this.formatItem(item)).join('\n');
|
|
26
|
+
}
|
|
27
|
+
// 对象详情
|
|
28
|
+
if (typeof data === 'object' && data !== null) {
|
|
29
|
+
return this.formatObject(data);
|
|
30
|
+
}
|
|
31
|
+
return String(data);
|
|
32
|
+
}
|
|
33
|
+
static formatItem(item) {
|
|
34
|
+
if (item.name) {
|
|
35
|
+
return `${item.name}`;
|
|
36
|
+
}
|
|
37
|
+
return JSON.stringify(item);
|
|
38
|
+
}
|
|
39
|
+
static formatObject(obj) {
|
|
40
|
+
const lines = [];
|
|
41
|
+
// Namespace 列表
|
|
42
|
+
if (obj.identifier !== undefined || obj.name !== undefined) {
|
|
43
|
+
lines.push(`Name: ${obj.identifier || obj.name}`);
|
|
44
|
+
if (obj.status)
|
|
45
|
+
lines.push(`Status: ${obj.status}`);
|
|
46
|
+
if (obj.environment)
|
|
47
|
+
lines.push(`Environment: ${obj.environment}`);
|
|
48
|
+
return lines.join('\n');
|
|
49
|
+
}
|
|
50
|
+
// Service 列表
|
|
51
|
+
if (Array.isArray(obj)) {
|
|
52
|
+
return obj.map((s) => {
|
|
53
|
+
let line = [
|
|
54
|
+
s.name,
|
|
55
|
+
s.type ? `(${s.type})` : '',
|
|
56
|
+
s.status ? `→ ${s.status}` : ''
|
|
57
|
+
].filter(Boolean).join(' ');
|
|
58
|
+
if (s.endpoint)
|
|
59
|
+
line += `\n ${s.endpoint}`;
|
|
60
|
+
return line;
|
|
61
|
+
}).join('\n\n');
|
|
62
|
+
}
|
|
63
|
+
// 单个对象
|
|
64
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
65
|
+
if (value !== undefined && value !== null) {
|
|
66
|
+
lines.push(`${key}: ${JSON.stringify(value)}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return lines.join('\n');
|
|
70
|
+
}
|
|
71
|
+
// 打印成功消息
|
|
72
|
+
static success(message) {
|
|
73
|
+
console.log(`✓ ${message}`);
|
|
74
|
+
}
|
|
75
|
+
// 打印错误消息
|
|
76
|
+
static error(message) {
|
|
77
|
+
console.error(`✗ ${message}`);
|
|
78
|
+
}
|
|
79
|
+
// 打印信息
|
|
80
|
+
static info(message) {
|
|
81
|
+
console.log(message);
|
|
82
|
+
}
|
|
83
|
+
// 打印表格
|
|
84
|
+
static table(headers, rows) {
|
|
85
|
+
const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map(r => String(r[i] || '').length)));
|
|
86
|
+
// Header
|
|
87
|
+
console.log(headers.map((h, i) => h.padEnd(colWidths[i])).join(' '));
|
|
88
|
+
console.log(colWidths.map(w => '-'.repeat(w)).join(' '));
|
|
89
|
+
// Rows
|
|
90
|
+
for (const row of rows) {
|
|
91
|
+
console.log(row.map((c, i) => String(c || '').padEnd(colWidths[i])).join(' '));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.OutputFormatter = OutputFormatter;
|
package/docs/DESIGN.md
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
# xuanwu-cli 设计文档
|
|
2
|
+
|
|
3
|
+
## 一、概述
|
|
4
|
+
|
|
5
|
+
xuanwu-cli 是玄武工厂平台的命令行工具,用于通过命令行快速管理 K8s 环境和服务。支持环境创建、服务部署(应用/数据库/镜像)、构建、调试等操作。
|
|
6
|
+
|
|
7
|
+
### 设计原则
|
|
8
|
+
|
|
9
|
+
- **平台转发**: CLI 通过玄武工厂平台 API 转发请求,不直接连接 K8s
|
|
10
|
+
- **K8s 优先**: 状态实时查询 K8s,无缓存
|
|
11
|
+
- **最小配置**: 本地只保存连接信息
|
|
12
|
+
- **权限由平台控制**: 用户可访问的空间由平台 RBAC 配置决定
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 二、架构
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
20
|
+
│ xuanwu-cli │
|
|
21
|
+
│ │ │
|
|
22
|
+
│ ┌─────────────┴─────────────┐ │
|
|
23
|
+
│ │ 玄武工厂平台 API │ │
|
|
24
|
+
│ │ (endpoint + token) │ │
|
|
25
|
+
│ └─────────────┬─────────────┘ │
|
|
26
|
+
│ │ │
|
|
27
|
+
│ ┌─────────────┴─────────────┐ │
|
|
28
|
+
│ │ K8s (平台已配置) │ │
|
|
29
|
+
│ │ 权限由平台 RBAC 控制 │ │
|
|
30
|
+
│ └───────────────────────────┘ │
|
|
31
|
+
└─────────────────────────────────────────────────────────────┘
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 三、安装与配置
|
|
37
|
+
|
|
38
|
+
### 安装
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install -g xuanwu-cli
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 连接配置
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# 配置平台连接
|
|
48
|
+
xuanwu connect add <name> --endpoint <url> --token <api-token>
|
|
49
|
+
|
|
50
|
+
# 示例
|
|
51
|
+
xuanwu connect add prod --endpoint https://xuanwu.company.com --token sk-xxxxx
|
|
52
|
+
|
|
53
|
+
# 列出连接
|
|
54
|
+
xuanwu connect ls
|
|
55
|
+
|
|
56
|
+
# 使用连接
|
|
57
|
+
xuanwu connect use prod
|
|
58
|
+
|
|
59
|
+
# 删除连接
|
|
60
|
+
xuanwu connect rm prod
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 配置文件
|
|
64
|
+
|
|
65
|
+
存储位置: `~/.xuanwu/config.json`
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"version": "1.0",
|
|
70
|
+
"connections": [
|
|
71
|
+
{
|
|
72
|
+
"name": "prod",
|
|
73
|
+
"endpoint": "https://xuanwu.company.com",
|
|
74
|
+
"token": "sk-xxxxx",
|
|
75
|
+
"isDefault": true
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 四、命令参考
|
|
84
|
+
|
|
85
|
+
### 1. 环境管理 (env)
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# 列出可访问的环境
|
|
89
|
+
xuanwu env ls
|
|
90
|
+
|
|
91
|
+
# 创建环境(K8s namespace)
|
|
92
|
+
xuanwu env create <namespace>
|
|
93
|
+
|
|
94
|
+
# 查看环境详情
|
|
95
|
+
xuanwu env info <namespace>
|
|
96
|
+
|
|
97
|
+
# 删除环境
|
|
98
|
+
xuanwu env rm <namespace>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 2. 服务部署 (deploy)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# 部署应用(源码构建)
|
|
105
|
+
xuanwu deploy <namespace> <service-name> \
|
|
106
|
+
--type application \
|
|
107
|
+
--git <repo-url> \
|
|
108
|
+
--git-branch <branch> \
|
|
109
|
+
--build-type template \
|
|
110
|
+
--language <java-springboot|nodejs|python|golang>
|
|
111
|
+
|
|
112
|
+
# 部署应用(Dockerfile)
|
|
113
|
+
xuanwu deploy <namespace> <service-name> \
|
|
114
|
+
--type application \
|
|
115
|
+
--git <repo-url> \
|
|
116
|
+
--build-type dockerfile \
|
|
117
|
+
--dockerfile-path <path>
|
|
118
|
+
|
|
119
|
+
# 部署数据库
|
|
120
|
+
xuanwu deploy <namespace> <service-name> \
|
|
121
|
+
--type database \
|
|
122
|
+
--db-type <mysql|redis|postgres|elasticsearch> \
|
|
123
|
+
--db-version <version>
|
|
124
|
+
|
|
125
|
+
# 部署镜像
|
|
126
|
+
xuanwu deploy <namespace> <service-name> \
|
|
127
|
+
--type image \
|
|
128
|
+
--image <image:tag>
|
|
129
|
+
|
|
130
|
+
# 通用参数
|
|
131
|
+
--port <port> # 端口
|
|
132
|
+
--replicas <num> # 副本数
|
|
133
|
+
--cpu <value> # CPU 限制
|
|
134
|
+
--memory <value> # 内存限制
|
|
135
|
+
--domain <prefix> # 域名前缀
|
|
136
|
+
--env KEY=VALUE # 环境变量
|
|
137
|
+
--volume <path:size> # 持久化卷
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 3. 服务管理 (svc)
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# 列出服务
|
|
144
|
+
xuanwu svc ls <namespace>
|
|
145
|
+
|
|
146
|
+
# 服务状态
|
|
147
|
+
xuanwu svc status <namespace> <service-name>
|
|
148
|
+
|
|
149
|
+
# 服务详情
|
|
150
|
+
xuanwu svc describe <namespace> <service-name>
|
|
151
|
+
|
|
152
|
+
# 删除服务
|
|
153
|
+
xuanwu svc rm <namespace> <service-name>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 4. 构建 (build)
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# 构建服务
|
|
160
|
+
xuanwu build <namespace> <service-name>
|
|
161
|
+
|
|
162
|
+
# 构建状态
|
|
163
|
+
xuanwu build status <namespace> <service-name>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 5. 服务控制
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# 扩缩容
|
|
170
|
+
xuanwu scale <namespace> <service-name> -r <num>
|
|
171
|
+
|
|
172
|
+
# 重启
|
|
173
|
+
xuanwu restart <namespace> <service-name>
|
|
174
|
+
|
|
175
|
+
# 启动
|
|
176
|
+
xuanwu start <namespace> <service-name>
|
|
177
|
+
|
|
178
|
+
# 停止
|
|
179
|
+
xuanwu stop <namespace> <service-name>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 6. 调试 (logs/exec)
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# 查看日志
|
|
186
|
+
xuanwu logs <namespace> <service-name>
|
|
187
|
+
xuanwu logs <namespace> <service-name> -f # 实时
|
|
188
|
+
xuanwu logs <namespace> <service-name> -n 100 # 最近100行
|
|
189
|
+
|
|
190
|
+
# 执行命令
|
|
191
|
+
xuanwu exec <namespace> <service-name>
|
|
192
|
+
xuanwu exec <namespace> <service-name> -- <command>
|
|
193
|
+
|
|
194
|
+
# Pod 列表
|
|
195
|
+
xuanwu pods <namespace> <service-name>
|
|
196
|
+
|
|
197
|
+
# 资源使用
|
|
198
|
+
xuanwu top <namespace> <service-name>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 五、使用示例
|
|
204
|
+
|
|
205
|
+
### 示例1: 部署 MySQL
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# 1. 配置连接
|
|
209
|
+
xuanwu connect add dev --endpoint http://localhost:3000 --token dev-token
|
|
210
|
+
|
|
211
|
+
# 2. 创建环境
|
|
212
|
+
xuanwu env create shop-dev
|
|
213
|
+
|
|
214
|
+
# 3. 部署 MySQL
|
|
215
|
+
xuanwu deploy shop-dev mysql \
|
|
216
|
+
--type database \
|
|
217
|
+
--db-type mysql \
|
|
218
|
+
--db-version 8.0 \
|
|
219
|
+
--root-password mysecret
|
|
220
|
+
|
|
221
|
+
# 4. 查看状态
|
|
222
|
+
xuanwu svc status shop-dev mysql
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### 示例2: 部署 Spring Boot 应用
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
# 1. 部署应用(源码构建)
|
|
229
|
+
xuanwu deploy shop-dev api \
|
|
230
|
+
--type application \
|
|
231
|
+
--git https://gitlab.com/user/shop-api.git \
|
|
232
|
+
--git-branch main \
|
|
233
|
+
--build-type template \
|
|
234
|
+
--language java-springboot \
|
|
235
|
+
--env DB_HOST=mysql \
|
|
236
|
+
--env DB_PASSWORD=mysecret
|
|
237
|
+
|
|
238
|
+
# 2. 构建
|
|
239
|
+
xuanwu build shop-dev api
|
|
240
|
+
|
|
241
|
+
# 3. 部署
|
|
242
|
+
xuanwu deploy shop-dev api
|
|
243
|
+
|
|
244
|
+
# 4. 查看状态
|
|
245
|
+
xuanwu svc status shop-dev api
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 示例3: 部署 Nginx
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
xuanwu deploy shop-dev nginx \
|
|
252
|
+
--type image \
|
|
253
|
+
--image nginx:latest \
|
|
254
|
+
--port 80 \
|
|
255
|
+
--domain web
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 六、输出格式
|
|
261
|
+
|
|
262
|
+
### 人类可读(默认)
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
✓ Service deployed successfully
|
|
266
|
+
Namespace: shop-dev
|
|
267
|
+
Service: api
|
|
268
|
+
Type: application
|
|
269
|
+
Status: Running
|
|
270
|
+
Replicas: 2/2
|
|
271
|
+
Endpoint: https://api.shop-dev.dev.aimstek.cn
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### JSON 模式 (-o json)
|
|
275
|
+
|
|
276
|
+
```json
|
|
277
|
+
{
|
|
278
|
+
"success": true,
|
|
279
|
+
"namespace": "shop-dev",
|
|
280
|
+
"service": "api",
|
|
281
|
+
"type": "application",
|
|
282
|
+
"status": "Running",
|
|
283
|
+
"replicas": "2/2",
|
|
284
|
+
"image": "registry.com/api:v1.0.0",
|
|
285
|
+
"endpoint": "https://api.shop-dev.dev.aimstek.cn",
|
|
286
|
+
"updatedAt": "2026-03-09T10:30:00Z"
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 七、API 映射
|
|
293
|
+
|
|
294
|
+
| CLI 命令 | 平台 API | 说明 |
|
|
295
|
+
|----------|----------|------|
|
|
296
|
+
| `env ls` | `GET /api/deploy-spaces` | 列出空间 |
|
|
297
|
+
| `env create` | `POST /api/deploy-spaces` | 创建空间 |
|
|
298
|
+
| `env rm` | `DELETE /api/deploy-spaces/{id}` | 删除空间 |
|
|
299
|
+
| `deploy` | `POST /api/services` + 部署 | 创建并部署服务 |
|
|
300
|
+
| `svc ls` | `GET /api/k8s/deployments` | 列出服务 |
|
|
301
|
+
| `svc status` | `GET /api/k8s/deployments/{name}` | 服务状态 |
|
|
302
|
+
| `svc rm` | `DELETE /api/services/{id}` | 删除服务 |
|
|
303
|
+
| `build` | `POST /api/applications/{id}/build` | 构建 |
|
|
304
|
+
| `scale` | `POST /api/service-deploy-configs/{id}/scale` | 扩缩容 |
|
|
305
|
+
| `logs` | `GET /api/k8s/logs` | 日志 |
|
|
306
|
+
| `exec` | `POST /api/debug/pod-exec` | 容器执行 |
|
|
307
|
+
| `pods` | `GET /api/k8s/pods` | Pod 列表 |
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## 八、错误处理
|
|
312
|
+
|
|
313
|
+
```json
|
|
314
|
+
{
|
|
315
|
+
"success": false,
|
|
316
|
+
"error": {
|
|
317
|
+
"code": "UNAUTHORIZED",
|
|
318
|
+
"message": "Invalid or expired token",
|
|
319
|
+
"details": "Please run 'xuanwu connect use <name>' to set a valid connection"
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## 九、环境变量
|
|
327
|
+
|
|
328
|
+
| 变量 | 说明 |
|
|
329
|
+
|------|------|
|
|
330
|
+
| `XUANWU_ENDPOINT` | 平台 API 地址 |
|
|
331
|
+
| `XUANWU_TOKEN` | API Token |
|
|
332
|
+
| `XUANWU_OUTPUT` | 输出格式 (human/json) |
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## 十、文件结构
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
xuanwu-cli/
|
|
340
|
+
├── bin/
|
|
341
|
+
│ └── xuanwu # 入口脚本
|
|
342
|
+
├── src/
|
|
343
|
+
│ ├── index.ts # 主入口
|
|
344
|
+
│ ├── commands/
|
|
345
|
+
│ │ ├── connect.ts # 连接管理
|
|
346
|
+
│ │ ├── env.ts # 环境管理
|
|
347
|
+
│ │ ├── deploy.ts # 部署
|
|
348
|
+
│ │ ├── svc.ts # 服务管理
|
|
349
|
+
│ │ ├── build.ts # 构建
|
|
350
|
+
│ │ ├── scale.ts # 扩缩容
|
|
351
|
+
│ │ ├── logs.ts # 日志
|
|
352
|
+
│ │ └── exec.ts # 执行
|
|
353
|
+
│ ├── api/
|
|
354
|
+
│ │ └── client.ts # HTTP 客户端
|
|
355
|
+
│ ├── config/
|
|
356
|
+
│ │ ├── store.ts # 配置存储
|
|
357
|
+
│ │ └── types.ts # 类型定义
|
|
358
|
+
│ └── output/
|
|
359
|
+
│ └── formatter.ts # 输出格式化
|
|
360
|
+
├── package.json
|
|
361
|
+
├── tsconfig.json
|
|
362
|
+
└── README.md
|
|
363
|
+
```
|