feat: 新增 18 个 K8s 资源配置模板
工作负载: Pod, StatefulSet, DaemonSet, Job, CronJob 服务发现: Ingress 存储管理: PersistentVolume, PersistentVolumeClaim, StorageClass 配置与密钥: ConfigMap, Secret 安全与权限: ServiceAccount, Role, ClusterRole, RoleBinding, ClusterRoleBinding, NetworkPolicy 集群管理: Namespace 总计支持 26 个组件,覆盖 K8s 核心资源全谱
This commit is contained in:
@@ -37,7 +37,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 🧩 一期支持组件
|
||||
## 🧩 支持组件(26 个)
|
||||
|
||||
| 分类 | 组件 | 输出格式 | 核心配置项 |
|
||||
|------|------|----------|------------|
|
||||
@@ -45,10 +45,28 @@
|
||||
| 缓存 | **Redis** | `.conf` | 单机/哨兵/集群模式、端口、内存限制、RDB/AOF 持久化、密码 |
|
||||
| 数据库 | **MySQL** | `.cnf` | 版本(5.7/8.0)、Buffer Pool 自动计算、字符集、慢查询、主从复制 |
|
||||
| 数据库 | **MongoDB** | `.conf` | 端口、WiredTiger 引擎、认证、TLS、副本集、Oplog、压缩算法 |
|
||||
| 云原生 | **K8s Deployment** | `.yaml` | 镜像、副本数、资源限制、健康检查、滚动更新策略 |
|
||||
| 云原生 | **K8s Service** | `.yaml` | ClusterIP/NodePort/LoadBalancer、端口映射、会话保持 |
|
||||
| 自动化运维 | **Ansible** | `.cfg` | SSH 连接、Pipelining、Become 提权、Forks 并发、日志、事实缓存 |
|
||||
| 自动化运维 | **Ansible Playbook** | `.yaml` | 变量、包管理、服务管理、文件复制、模板渲染、Shell 命令、Handlers |
|
||||
| K8S 工作负载 | **Pod** | `.yaml` | 容器镜像、端口、资源限制、环境变量、重启策略 |
|
||||
| K8S 工作负载 | **Deployment** | `.yaml` | 镜像、副本数、资源限制、健康检查、滚动更新策略 |
|
||||
| K8S 工作负载 | **StatefulSet** | `.yaml` | 有状态应用、稳定网络标识、PVC 模板、滚动更新 |
|
||||
| K8S 工作负载 | **DaemonSet** | `.yaml` | 节点级守护进程、主机网络、容忍度配置 |
|
||||
| K8S 工作负载 | **Job** | `.yaml` | 一次性任务、完成策略、重试限制、TTL 清理 |
|
||||
| K8S 工作负载 | **CronJob** | `.yaml` | Cron 表达式、并发策略、历史保留、暂停开关 |
|
||||
| K8S 服务发现 | **Service** | `.yaml` | ClusterIP/NodePort/LoadBalancer、端口映射、会话保持 |
|
||||
| K8S 服务发现 | **Ingress** | `.yaml` | 主机路由、路径匹配、TLS 终止、IngressClass |
|
||||
| K8S 存储 | **PersistentVolume** | `.yaml` | 容量、访问模式、回收策略、HostPath/NFS |
|
||||
| K8S 存储 | **PersistentVolumeClaim** | `.yaml` | 存储请求、StorageClass、访问模式 |
|
||||
| K8S 存储 | **StorageClass** | `.yaml` | Provisioner、回收策略、延迟绑定、卷扩展 |
|
||||
| K8S 配置与密钥 | **ConfigMap** | `.yaml` | 键值对配置、挂载为卷或环境变量 |
|
||||
| K8S 配置与密钥 | **Secret** | `.yaml` | Opaque/TLS/Docker Registry、Base64 编码 |
|
||||
| K8S 安全与权限 | **ServiceAccount** | `.yaml` | 自动挂载 Token、镜像拉取密钥 |
|
||||
| K8S 安全与权限 | **Role** | `.yaml` | 命名空间级 RBAC、资源+动词组合 |
|
||||
| K8S 安全与权限 | **ClusterRole** | `.yaml` | 集群级 RBAC、跨命名空间权限 |
|
||||
| K8S 安全与权限 | **RoleBinding** | `.yaml` | 将 Role 绑定到 User/Group/ServiceAccount |
|
||||
| K8S 安全与权限 | **ClusterRoleBinding** | `.yaml` | 将 ClusterRole 绑定到集群级主体 |
|
||||
| K8S 安全与权限 | **NetworkPolicy** | `.yaml` | 入站/出站流量控制、Pod 选择器、CIDR 规则 |
|
||||
| K8S 集群管理 | **Namespace** | `.yaml` | 命名空间创建、标签管理 |
|
||||
| 自动化运维 | **Ansible** | `.cfg` | SSH 连接、Pipelining、Become 提权、Forks、事实缓存 |
|
||||
| 自动化运维 | **Ansible Playbook** | `.yaml` | 变量、包管理、服务管理、文件复制、模板渲染、Handlers |
|
||||
|
||||
---
|
||||
|
||||
@@ -105,14 +123,32 @@ conf-template/
|
||||
│ │ └── CodePreview.vue # 右侧 Monaco Editor 代码预览
|
||||
│ └── schemas/
|
||||
│ ├── index.js # Schema 注册中心 + 工具函数
|
||||
│ ├── nginx.js # NGINX Schema 定义 + 模板生成
|
||||
│ ├── redis.js # Redis Schema 定义 + 模板生成
|
||||
│ ├── mysql.js # MySQL Schema 定义 + 模板生成
|
||||
│ ├── mongodb.js # MongoDB Schema 定义 + 模板生成
|
||||
│ ├── k8s-deployment.js # K8s Deployment Schema + YAML 生成
|
||||
│ ├── k8s-service.js # K8s Service Schema + YAML 生成
|
||||
│ ├── ansible.js # Ansible Schema + 配置生成
|
||||
│ └── ansible-playbook.js # Ansible Playbook Schema + YAML 生成
|
||||
│ ├── nginx.js # NGINX
|
||||
│ ├── redis.js # Redis
|
||||
│ ├── mysql.js # MySQL
|
||||
│ ├── mongodb.js # MongoDB
|
||||
│ ├── k8s-pod.js # K8s Pod
|
||||
│ ├── k8s-deployment.js # K8s Deployment
|
||||
│ ├── k8s-statefulset.js # K8s StatefulSet
|
||||
│ ├── k8s-daemonset.js # K8s DaemonSet
|
||||
│ ├── k8s-job.js # K8s Job
|
||||
│ ├── k8s-cronjob.js # K8s CronJob
|
||||
│ ├── k8s-service.js # K8s Service
|
||||
│ ├── k8s-ingress.js # K8s Ingress
|
||||
│ ├── k8s-pv.js # K8s PersistentVolume
|
||||
│ ├── k8s-pvc.js # K8s PersistentVolumeClaim
|
||||
│ ├── k8s-storageclass.js # K8s StorageClass
|
||||
│ ├── k8s-configmap.js # K8s ConfigMap
|
||||
│ ├── k8s-secret.js # K8s Secret
|
||||
│ ├── k8s-serviceaccount.js # K8s ServiceAccount
|
||||
│ ├── k8s-role.js # K8s Role
|
||||
│ ├── k8s-clusterrole.js # K8s ClusterRole
|
||||
│ ├── k8s-rolebinding.js # K8s RoleBinding
|
||||
│ ├── k8s-clusterrolebinding.js # K8s ClusterRoleBinding
|
||||
│ ├── k8s-networkpolicy.js # K8s NetworkPolicy
|
||||
│ ├── k8s-namespace.js # K8s Namespace
|
||||
│ ├── ansible.js # Ansible
|
||||
│ └── ansible-playbook.js # Ansible Playbook
|
||||
└── dist/ # 生产构建产物(gitignore)
|
||||
```
|
||||
|
||||
|
||||
+81
-2
@@ -6,6 +6,24 @@ import { k8sDeploymentSchema, generateK8sDeploymentYaml } from './k8s-deployment
|
||||
import { k8sServiceSchema, generateK8sServiceYaml } from './k8s-service'
|
||||
import { ansibleSchema, generateAnsibleCfg } from './ansible'
|
||||
import { ansiblePlaybookSchema, generateAnsiblePlaybookYaml } from './ansible-playbook'
|
||||
import { k8sPodSchema, generateK8sPodYaml } from './k8s-pod'
|
||||
import { k8sStatefulSetSchema, generateK8sStatefulSetYaml } from './k8s-statefulset'
|
||||
import { k8sDaemonSetSchema, generateK8sDaemonSetYaml } from './k8s-daemonset'
|
||||
import { k8sJobSchema, generateK8sJobYaml } from './k8s-job'
|
||||
import { k8sCronJobSchema, generateK8sCronJobYaml } from './k8s-cronjob'
|
||||
import { k8sIngressSchema, generateK8sIngressYaml } from './k8s-ingress'
|
||||
import { k8sPvSchema, generateK8sPvYaml } from './k8s-pv'
|
||||
import { k8sPvcSchema, generateK8sPvcYaml } from './k8s-pvc'
|
||||
import { k8sStorageClassSchema, generateK8sStorageClassYaml } from './k8s-storageclass'
|
||||
import { k8sConfigMapSchema, generateK8sConfigMapYaml } from './k8s-configmap'
|
||||
import { k8sSecretSchema, generateK8sSecretYaml } from './k8s-secret'
|
||||
import { k8sServiceAccountSchema, generateK8sServiceAccountYaml } from './k8s-serviceaccount'
|
||||
import { k8sRoleSchema, generateK8sRoleYaml } from './k8s-role'
|
||||
import { k8sClusterRoleSchema, generateK8sClusterRoleYaml } from './k8s-clusterrole'
|
||||
import { k8sRoleBindingSchema, generateK8sRoleBindingYaml } from './k8s-rolebinding'
|
||||
import { k8sClusterRoleBindingSchema, generateK8sClusterRoleBindingYaml } from './k8s-clusterrolebinding'
|
||||
import { k8sNetworkPolicySchema, generateK8sNetworkPolicyYaml } from './k8s-networkpolicy'
|
||||
import { k8sNamespaceSchema, generateK8sNamespaceYaml } from './k8s-namespace'
|
||||
|
||||
export const schemas = {
|
||||
nginx: nginxSchema,
|
||||
@@ -16,6 +34,24 @@ export const schemas = {
|
||||
'k8s-service': k8sServiceSchema,
|
||||
ansible: ansibleSchema,
|
||||
'ansible-playbook': ansiblePlaybookSchema,
|
||||
'k8s-pod': k8sPodSchema,
|
||||
'k8s-statefulset': k8sStatefulSetSchema,
|
||||
'k8s-daemonset': k8sDaemonSetSchema,
|
||||
'k8s-job': k8sJobSchema,
|
||||
'k8s-cronjob': k8sCronJobSchema,
|
||||
'k8s-ingress': k8sIngressSchema,
|
||||
'k8s-pv': k8sPvSchema,
|
||||
'k8s-pvc': k8sPvcSchema,
|
||||
'k8s-storageclass': k8sStorageClassSchema,
|
||||
'k8s-configmap': k8sConfigMapSchema,
|
||||
'k8s-secret': k8sSecretSchema,
|
||||
'k8s-serviceaccount': k8sServiceAccountSchema,
|
||||
'k8s-role': k8sRoleSchema,
|
||||
'k8s-clusterrole': k8sClusterRoleSchema,
|
||||
'k8s-rolebinding': k8sRoleBindingSchema,
|
||||
'k8s-clusterrolebinding': k8sClusterRoleBindingSchema,
|
||||
'k8s-networkpolicy': k8sNetworkPolicySchema,
|
||||
'k8s-namespace': k8sNamespaceSchema,
|
||||
}
|
||||
|
||||
export const generators = {
|
||||
@@ -27,6 +63,24 @@ export const generators = {
|
||||
'k8s-service': generateK8sServiceYaml,
|
||||
ansible: generateAnsibleCfg,
|
||||
'ansible-playbook': generateAnsiblePlaybookYaml,
|
||||
'k8s-pod': generateK8sPodYaml,
|
||||
'k8s-statefulset': generateK8sStatefulSetYaml,
|
||||
'k8s-daemonset': generateK8sDaemonSetYaml,
|
||||
'k8s-job': generateK8sJobYaml,
|
||||
'k8s-cronjob': generateK8sCronJobYaml,
|
||||
'k8s-ingress': generateK8sIngressYaml,
|
||||
'k8s-pv': generateK8sPvYaml,
|
||||
'k8s-pvc': generateK8sPvcYaml,
|
||||
'k8s-storageclass': generateK8sStorageClassYaml,
|
||||
'k8s-configmap': generateK8sConfigMapYaml,
|
||||
'k8s-secret': generateK8sSecretYaml,
|
||||
'k8s-serviceaccount': generateK8sServiceAccountYaml,
|
||||
'k8s-role': generateK8sRoleYaml,
|
||||
'k8s-clusterrole': generateK8sClusterRoleYaml,
|
||||
'k8s-rolebinding': generateK8sRoleBindingYaml,
|
||||
'k8s-clusterrolebinding': generateK8sClusterRoleBindingYaml,
|
||||
'k8s-networkpolicy': generateK8sNetworkPolicyYaml,
|
||||
'k8s-namespace': generateK8sNamespaceYaml,
|
||||
}
|
||||
|
||||
export const categories = [
|
||||
@@ -46,9 +100,34 @@ export const categories = [
|
||||
items: ['mysql', 'mongodb'],
|
||||
},
|
||||
{
|
||||
name: '云原生',
|
||||
name: 'K8S 工作负载',
|
||||
icon: 'Box',
|
||||
items: ['k8s-deployment', 'k8s-service'],
|
||||
items: ['k8s-pod', 'k8s-deployment', 'k8s-statefulset', 'k8s-daemonset', 'k8s-job', 'k8s-cronjob'],
|
||||
},
|
||||
{
|
||||
name: 'K8S 服务发现',
|
||||
icon: 'Connection',
|
||||
items: ['k8s-service', 'k8s-ingress'],
|
||||
},
|
||||
{
|
||||
name: 'K8S 存储',
|
||||
icon: 'Files',
|
||||
items: ['k8s-pv', 'k8s-pvc', 'k8s-storageclass'],
|
||||
},
|
||||
{
|
||||
name: 'K8S 配置与密钥',
|
||||
icon: 'Document',
|
||||
items: ['k8s-configmap', 'k8s-secret'],
|
||||
},
|
||||
{
|
||||
name: 'K8S 安全与权限',
|
||||
icon: 'Lock',
|
||||
items: ['k8s-serviceaccount', 'k8s-role', 'k8s-clusterrole', 'k8s-rolebinding', 'k8s-clusterrolebinding', 'k8s-networkpolicy'],
|
||||
},
|
||||
{
|
||||
name: 'K8S 集群管理',
|
||||
icon: 'Grid',
|
||||
items: ['k8s-namespace'],
|
||||
},
|
||||
{
|
||||
name: '自动化运维',
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
// K8s ClusterRole Schema - 由 ConfTemplate 生成
|
||||
// 生成时间: 自动生成
|
||||
|
||||
export const k8sClusterRoleSchema = {
|
||||
id: 'k8s-clusterrole',
|
||||
name: 'K8s ClusterRole',
|
||||
icon: 'ShieldCheck',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes 集群级 RBAC 角色',
|
||||
format: 'yaml',
|
||||
fileName: 'clusterrole.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'ClusterRole 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-cluster-role',
|
||||
default: 'my-cluster-role',
|
||||
required: true,
|
||||
tip: '将用作 ClusterRole 的 metadata.name',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '权限规则',
|
||||
fields: [
|
||||
{
|
||||
key: 'resources',
|
||||
label: '资源类型',
|
||||
type: 'select',
|
||||
multiple: true,
|
||||
options: [
|
||||
{ label: 'Pods', value: 'pods' },
|
||||
{ label: 'Services', value: 'services' },
|
||||
{ label: 'ConfigMaps', value: 'configmaps' },
|
||||
{ label: 'Deployments', value: 'deployments' },
|
||||
{ label: 'Secrets', value: 'secrets' },
|
||||
{ label: 'Nodes', value: 'nodes' },
|
||||
{ label: 'Namespaces', value: 'namespaces' },
|
||||
{ label: 'All (*)', value: '*' },
|
||||
],
|
||||
default: ['pods'],
|
||||
tip: '可多选,选择要授权的资源类型',
|
||||
},
|
||||
{
|
||||
key: 'verbs',
|
||||
label: '操作动词',
|
||||
type: 'select',
|
||||
multiple: true,
|
||||
options: [
|
||||
{ label: 'Get (读取)', value: 'get' },
|
||||
{ label: 'List (列表)', value: 'list' },
|
||||
{ label: 'Watch (监听)', value: 'watch' },
|
||||
{ label: 'Create (创建)', value: 'create' },
|
||||
{ label: 'Update (更新)', value: 'update' },
|
||||
{ label: 'Delete (删除)', value: 'delete' },
|
||||
{ label: 'Patch (补丁)', value: 'patch' },
|
||||
{ label: 'All (*)', value: '*' },
|
||||
],
|
||||
default: ['get', 'list', 'watch'],
|
||||
tip: '可多选,选择允许的操作',
|
||||
},
|
||||
{
|
||||
key: 'apiGroups',
|
||||
label: 'API 组',
|
||||
type: 'text',
|
||||
placeholder: '"" (核心组留空)',
|
||||
default: '',
|
||||
tip: 'API 组,核心资源(如 pods, services)留空',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sClusterRoleYaml(config) {
|
||||
const resources = Array.isArray(config.resources) ? config.resources : [config.resources || 'pods']
|
||||
const verbs = Array.isArray(config.verbs) ? config.verbs : [config.verbs || 'get']
|
||||
const apiGroups = config.apiGroups !== undefined && config.apiGroups !== ''
|
||||
? config.apiGroups.split(',').map(g => g.trim())
|
||||
: ['']
|
||||
|
||||
const rule = {
|
||||
apiGroups,
|
||||
resources,
|
||||
verbs,
|
||||
}
|
||||
|
||||
const clusterRole = {
|
||||
apiVersion: 'rbac.authorization.k8s.io/v1',
|
||||
kind: 'ClusterRole',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
},
|
||||
rules: [rule],
|
||||
}
|
||||
|
||||
const header = `# K8s ClusterRole - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'clusterrole.yaml'}
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(clusterRole)
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// K8s ClusterRoleBinding Schema - 由 ConfTemplate 生成
|
||||
// 生成时间: 自动生成
|
||||
|
||||
export const k8sClusterRoleBindingSchema = {
|
||||
id: 'k8s-clusterrolebinding',
|
||||
name: 'K8s ClusterRoleBinding',
|
||||
icon: 'Link2',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes 集群级角色绑定,将 ClusterRole 授予用户或服务账户',
|
||||
format: 'yaml',
|
||||
fileName: 'clusterrolebinding.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'ClusterRoleBinding 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-clusterrolebinding',
|
||||
default: 'my-clusterrolebinding',
|
||||
required: true,
|
||||
tip: '将用作 ClusterRoleBinding 的 metadata.name',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '角色引用',
|
||||
fields: [
|
||||
{
|
||||
key: 'clusterRoleName',
|
||||
label: 'ClusterRole 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-cluster-role',
|
||||
default: '',
|
||||
required: true,
|
||||
tip: '要绑定的 ClusterRole 名称',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '主体配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'subjectKind',
|
||||
label: '主体类型',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'User (用户)', value: 'User' },
|
||||
{ label: 'Group (用户组)', value: 'Group' },
|
||||
{ label: 'ServiceAccount (服务账户)', value: 'ServiceAccount' },
|
||||
],
|
||||
default: 'ServiceAccount',
|
||||
tip: '要授权的主体类型',
|
||||
},
|
||||
{
|
||||
key: 'subjectName',
|
||||
label: '主体名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-serviceaccount',
|
||||
default: '',
|
||||
required: true,
|
||||
tip: '用户、组或 ServiceAccount 的名称',
|
||||
},
|
||||
{
|
||||
key: 'subjectNamespace',
|
||||
label: '主体命名空间',
|
||||
type: 'text',
|
||||
placeholder: '仅 ServiceAccount 需要',
|
||||
default: '',
|
||||
tip: '仅当主体类型为 ServiceAccount 时需要填写',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sClusterRoleBindingYaml(config) {
|
||||
const subject = {
|
||||
kind: config.subjectKind || 'ServiceAccount',
|
||||
name: config.subjectName,
|
||||
}
|
||||
if (config.subjectNamespace && config.subjectKind === 'ServiceAccount') {
|
||||
subject.namespace = config.subjectNamespace
|
||||
}
|
||||
if (config.subjectKind === 'User' || config.subjectKind === 'Group') {
|
||||
subject.apiGroup = 'rbac.authorization.k8s.io'
|
||||
}
|
||||
|
||||
const clusterRoleBinding = {
|
||||
apiVersion: 'rbac.authorization.k8s.io/v1',
|
||||
kind: 'ClusterRoleBinding',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
},
|
||||
roleRef: {
|
||||
apiGroup: 'rbac.authorization.k8s.io',
|
||||
kind: 'ClusterRole',
|
||||
name: config.clusterRoleName,
|
||||
},
|
||||
subjects: [subject],
|
||||
}
|
||||
|
||||
const header = `# K8s ClusterRoleBinding - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'clusterrolebinding.yaml'}
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(clusterRoleBinding)
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
// K8s ConfigMap Schema - 由 ConfTemplate 生成
|
||||
// 生成时间: 自动生成
|
||||
|
||||
export const k8sConfigMapSchema = {
|
||||
id: 'k8s-configmap',
|
||||
name: 'K8s ConfigMap',
|
||||
icon: 'Settings',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes 配置映射,存储键值对配置数据',
|
||||
format: 'yaml',
|
||||
fileName: 'configmap.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'ConfigMap 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-config',
|
||||
default: 'my-config',
|
||||
required: true,
|
||||
tip: '将用作 ConfigMap 的 metadata.name',
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '数据配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'dataType',
|
||||
label: '数据类型',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '字面量 (Literal)', value: 'literal' },
|
||||
{ label: '环境变量注入 (EnvFrom)', value: 'envFrom' },
|
||||
{ label: '文件挂载 (File)', value: 'file' },
|
||||
],
|
||||
default: 'literal',
|
||||
tip: '决定 ConfigMap 数据的使用方式',
|
||||
},
|
||||
{
|
||||
key: 'dataKey',
|
||||
label: '数据键名',
|
||||
type: 'text',
|
||||
placeholder: 'app.conf',
|
||||
default: 'app.conf',
|
||||
required: true,
|
||||
tip: 'ConfigMap 中的键名,如配置文件名或环境变量名',
|
||||
},
|
||||
{
|
||||
key: 'dataValue',
|
||||
label: '数据值',
|
||||
type: 'textarea',
|
||||
placeholder: 'key1=value1\nkey2=value2',
|
||||
default: 'key1=value1\nkey2=value2',
|
||||
required: true,
|
||||
tip: '键值对,每行一个,格式 key=value',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '挂载配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'mountAsVolume',
|
||||
label: '作为 Volume 挂载',
|
||||
type: 'switch',
|
||||
default: true,
|
||||
tip: '是否将 ConfigMap 挂载为容器内的文件',
|
||||
},
|
||||
{
|
||||
key: 'mountPath',
|
||||
label: '挂载路径',
|
||||
type: 'text',
|
||||
placeholder: '/etc/config',
|
||||
default: '/etc/config',
|
||||
dependsOn: { key: 'mountAsVolume', value: true },
|
||||
tip: '容器内挂载目录路径',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sConfigMapYaml(config) {
|
||||
const data = {}
|
||||
if (config.dataValue) {
|
||||
const lines = config.dataValue.split('\n').filter(line => line.trim())
|
||||
for (const line of lines) {
|
||||
const eqIdx = line.indexOf('=')
|
||||
if (eqIdx > 0) {
|
||||
const k = line.substring(0, eqIdx).trim()
|
||||
const v = line.substring(eqIdx + 1).trim()
|
||||
data[k] = v
|
||||
} else {
|
||||
data[config.dataKey || 'data'] = line.trim()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data[config.dataKey || 'data'] = ''
|
||||
}
|
||||
|
||||
const configmap = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'ConfigMap',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
},
|
||||
data,
|
||||
}
|
||||
|
||||
const header = `# K8s ConfigMap - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'configmap.yaml'}
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(configmap)
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
export const k8sCronJobSchema = {
|
||||
id: 'k8s-cronjob',
|
||||
name: 'K8s CronJob',
|
||||
icon: 'Box',
|
||||
category: 'K8S 工作负载',
|
||||
description: 'Kubernetes CronJob — 定时调度批处理任务',
|
||||
format: 'yaml',
|
||||
fileName: 'cronjob.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'CronJob 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-cronjob',
|
||||
default: 'my-cronjob',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
{
|
||||
key: 'image',
|
||||
label: '容器镜像',
|
||||
type: 'text',
|
||||
placeholder: 'busybox:latest',
|
||||
default: 'busybox:latest',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'command',
|
||||
label: 'Command',
|
||||
type: 'text',
|
||||
placeholder: 'echo hello',
|
||||
default: 'echo hello',
|
||||
required: true,
|
||||
tip: '容器启动命令',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '调度配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'schedule',
|
||||
label: 'Cron 表达式',
|
||||
type: 'text',
|
||||
placeholder: '0 * * * *',
|
||||
default: '0 * * * *',
|
||||
required: true,
|
||||
tip: '标准 cron 格式: 分 时 日 月 周',
|
||||
},
|
||||
{
|
||||
key: 'concurrencyPolicy',
|
||||
label: '并发策略',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Allow - 允许并发', value: 'Allow' },
|
||||
{ label: 'Replace - 替换上一个', value: 'Replace' },
|
||||
{ label: 'Forbid - 禁止并发', value: 'Forbid' },
|
||||
],
|
||||
default: 'Allow',
|
||||
},
|
||||
{
|
||||
key: 'suspend',
|
||||
label: '暂停调度',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
tip: '暂停后不再创建新的 Job',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '历史与清理',
|
||||
fields: [
|
||||
{
|
||||
key: 'successfulJobsHistoryLimit',
|
||||
label: '成功 Job 保留数',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 10,
|
||||
default: 3,
|
||||
},
|
||||
{
|
||||
key: 'failedJobsHistoryLimit',
|
||||
label: '失败 Job 保留数',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 10,
|
||||
default: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Job 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'restartPolicy',
|
||||
label: '重启策略',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'OnFailure (推荐)', value: 'OnFailure' },
|
||||
{ label: 'Never', value: 'Never' },
|
||||
],
|
||||
default: 'OnFailure',
|
||||
},
|
||||
{
|
||||
key: 'backoffLimit',
|
||||
label: '重试次数上限',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 100,
|
||||
default: 6,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sCronJobYaml(config) {
|
||||
const container = {
|
||||
name: config.name,
|
||||
image: config.image,
|
||||
command: config.command.split(' '),
|
||||
}
|
||||
|
||||
const cronjob = {
|
||||
apiVersion: 'batch/v1',
|
||||
kind: 'CronJob',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
},
|
||||
spec: {
|
||||
schedule: config.schedule,
|
||||
concurrencyPolicy: config.concurrencyPolicy,
|
||||
successfulJobsHistoryLimit: config.successfulJobsHistoryLimit,
|
||||
failedJobsHistoryLimit: config.failedJobsHistoryLimit,
|
||||
suspend: config.suspend || undefined,
|
||||
jobTemplate: {
|
||||
spec: {
|
||||
backoffLimit: config.backoffLimit,
|
||||
template: {
|
||||
spec: {
|
||||
containers: [container],
|
||||
restartPolicy: config.restartPolicy,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const header = `# K8s CronJob - 由 ConfTemplate 生成\n# 生成时间: ${new Date().toLocaleString('zh-CN')}\n# 部署命令: kubectl apply -f ${config.fileName || 'cronjob.yaml'}\n`
|
||||
|
||||
return header + '\n' + toYaml(cronjob)
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
export const k8sDaemonSetSchema = {
|
||||
id: 'k8s-daemonset',
|
||||
name: 'K8s DaemonSet',
|
||||
icon: 'Box',
|
||||
category: 'K8S 工作负载',
|
||||
description: 'Kubernetes 守护进程集 — 在每个节点上运行一个 Pod 副本',
|
||||
format: 'yaml',
|
||||
fileName: 'daemonset.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'DaemonSet 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-daemonset',
|
||||
default: 'my-daemonset',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
{
|
||||
key: 'appName',
|
||||
label: '应用名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-app',
|
||||
default: 'my-app',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'image',
|
||||
label: '容器镜像',
|
||||
type: 'text',
|
||||
placeholder: 'fluentd:latest',
|
||||
default: 'fluentd:latest',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'containerPort',
|
||||
label: '容器端口',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 65535,
|
||||
default: 8080,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '资源限制',
|
||||
fields: [
|
||||
{
|
||||
key: 'cpuRequest',
|
||||
label: 'CPU Requests',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '50m', value: '50m' },
|
||||
{ label: '100m', value: '100m' },
|
||||
{ label: '200m', value: '200m' },
|
||||
{ label: '500m', value: '500m' },
|
||||
{ label: '1', value: '1' },
|
||||
],
|
||||
default: '100m',
|
||||
},
|
||||
{
|
||||
key: 'cpuLimit',
|
||||
label: 'CPU Limits',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '200m', value: '200m' },
|
||||
{ label: '500m', value: '500m' },
|
||||
{ label: '1', value: '1' },
|
||||
{ label: '2', value: '2' },
|
||||
],
|
||||
default: '500m',
|
||||
},
|
||||
{
|
||||
key: 'memoryRequest',
|
||||
label: 'Memory Requests',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '64Mi', value: '64Mi' },
|
||||
{ label: '128Mi', value: '128Mi' },
|
||||
{ label: '256Mi', value: '256Mi' },
|
||||
{ label: '512Mi', value: '512Mi' },
|
||||
],
|
||||
default: '128Mi',
|
||||
},
|
||||
{
|
||||
key: 'memoryLimit',
|
||||
label: 'Memory Limits',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '128Mi', value: '128Mi' },
|
||||
{ label: '256Mi', value: '256Mi' },
|
||||
{ label: '512Mi', value: '512Mi' },
|
||||
{ label: '1Gi', value: '1Gi' },
|
||||
],
|
||||
default: '256Mi',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '节点调度',
|
||||
fields: [
|
||||
{
|
||||
key: 'hostNetwork',
|
||||
label: '使用主机网络',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
tip: 'Pod 将使用宿主机网络命名空间',
|
||||
},
|
||||
{
|
||||
key: 'tolerationsEnabled',
|
||||
label: '添加 Toleration',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
tip: '允许在有污点的节点上运行',
|
||||
},
|
||||
{
|
||||
key: 'tolerationKey',
|
||||
label: 'Toleration Key',
|
||||
type: 'text',
|
||||
placeholder: 'node-role.kubernetes.io/master',
|
||||
default: '',
|
||||
dependsOn: { key: 'tolerationsEnabled', value: true },
|
||||
},
|
||||
{
|
||||
key: 'tolerationValue',
|
||||
label: 'Toleration Value',
|
||||
type: 'text',
|
||||
placeholder: '',
|
||||
default: '',
|
||||
dependsOn: { key: 'tolerationsEnabled', value: true },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '更新策略',
|
||||
fields: [
|
||||
{
|
||||
key: 'updateStrategy',
|
||||
label: '更新策略',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'RollingUpdate - 滚动更新 (推荐)', value: 'RollingUpdate' },
|
||||
{ label: 'OnDelete - 手动删除触发', value: 'OnDelete' },
|
||||
],
|
||||
default: 'RollingUpdate',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sDaemonSetYaml(config) {
|
||||
const labels = { app: config.appName }
|
||||
|
||||
const container = {
|
||||
name: config.appName,
|
||||
image: config.image,
|
||||
ports: [{ containerPort: config.containerPort, name: 'http' }],
|
||||
resources: {
|
||||
requests: { cpu: config.cpuRequest, memory: config.memoryRequest },
|
||||
limits: { cpu: config.cpuLimit, memory: config.memoryLimit },
|
||||
},
|
||||
}
|
||||
|
||||
const podSpec = {
|
||||
containers: [container],
|
||||
}
|
||||
|
||||
if (config.hostNetwork) {
|
||||
podSpec.hostNetwork = true
|
||||
}
|
||||
|
||||
if (config.tolerationsEnabled && config.tolerationKey) {
|
||||
const toleration = { key: config.tolerationKey, operator: 'Exists', effect: 'NoSchedule' }
|
||||
if (config.tolerationValue) {
|
||||
toleration.operator = 'Equal'
|
||||
toleration.value = config.tolerationValue
|
||||
}
|
||||
podSpec.tolerations = [toleration]
|
||||
}
|
||||
|
||||
const daemonset = {
|
||||
apiVersion: 'apps/v1',
|
||||
kind: 'DaemonSet',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
labels,
|
||||
},
|
||||
spec: {
|
||||
selector: { matchLabels: { app: config.appName } },
|
||||
updateStrategy: config.updateStrategy === 'RollingUpdate'
|
||||
? { type: 'RollingUpdate', rollingUpdate: { maxUnavailable: 1 } }
|
||||
: { type: 'OnDelete' },
|
||||
template: {
|
||||
metadata: { labels },
|
||||
spec: podSpec,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const header = `# K8s DaemonSet - 由 ConfTemplate 生成\n# 生成时间: ${new Date().toLocaleString('zh-CN')}\n# 部署命令: kubectl apply -f ${config.fileName || 'daemonset.yaml'}\n`
|
||||
|
||||
return header + '\n' + toYaml(daemonset)
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
// K8s Ingress schema + YAML generator for ConfTemplate
|
||||
// Generated: 2026-07-18
|
||||
|
||||
export const k8sIngressSchema = {
|
||||
id: 'k8s-ingress',
|
||||
name: 'K8s Ingress',
|
||||
icon: 'Globe',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes Ingress 网络入口资源',
|
||||
format: 'yaml',
|
||||
fileName: 'ingress.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'Ingress 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-ingress',
|
||||
default: 'my-ingress',
|
||||
required: true,
|
||||
tip: '将用作 Ingress 资源名称',
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
{
|
||||
key: 'ingressClassName',
|
||||
label: 'Ingress Class',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'nginx', value: 'nginx' },
|
||||
{ label: 'traefik', value: 'traefik' },
|
||||
{ label: '(不指定)', value: '' },
|
||||
],
|
||||
default: 'nginx',
|
||||
tip: '选择 Ingress 控制器类型',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '路由规则',
|
||||
fields: [
|
||||
{
|
||||
key: 'host',
|
||||
label: '域名',
|
||||
type: 'text',
|
||||
placeholder: 'example.com',
|
||||
default: 'example.com',
|
||||
required: true,
|
||||
tip: '匹配的主机名',
|
||||
},
|
||||
{
|
||||
key: 'path',
|
||||
label: '路径',
|
||||
type: 'text',
|
||||
placeholder: '/',
|
||||
default: '/',
|
||||
},
|
||||
{
|
||||
key: 'pathType',
|
||||
label: '路径类型',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Prefix', value: 'Prefix' },
|
||||
{ label: 'Exact', value: 'Exact' },
|
||||
{ label: 'ImplementationSpecific', value: 'ImplementationSpecific' },
|
||||
],
|
||||
default: 'Prefix',
|
||||
},
|
||||
{
|
||||
key: 'backendServiceName',
|
||||
label: '后端 Service 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-service',
|
||||
default: '',
|
||||
required: true,
|
||||
tip: '流量转发到的 Service 名称',
|
||||
},
|
||||
{
|
||||
key: 'backendServicePort',
|
||||
label: '后端 Service 端口',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 65535,
|
||||
default: 80,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'TLS 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'enableTls',
|
||||
label: '启用 TLS',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
tip: '开启 HTTPS 终止',
|
||||
},
|
||||
{
|
||||
key: 'tlsSecretName',
|
||||
label: 'TLS Secret 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-tls-secret',
|
||||
default: '',
|
||||
dependsOn: 'enableTls',
|
||||
tip: '包含 TLS 证书的 Secret 名称',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Annotations',
|
||||
fields: [
|
||||
{
|
||||
key: 'enableCors',
|
||||
label: '启用 CORS',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
tip: '添加 nginx.ingress.kubernetes.io/enable-cors 注解',
|
||||
},
|
||||
{
|
||||
key: 'rewriteTarget',
|
||||
label: '启用路径重写',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
tip: '添加 nginx.ingress.kubernetes.io/rewrite-target 注解',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sIngressYaml(config) {
|
||||
const annotations = {}
|
||||
if (config.enableCors) {
|
||||
annotations['nginx.ingress.kubernetes.io/enable-cors'] = 'true'
|
||||
}
|
||||
if (config.rewriteTarget) {
|
||||
annotations['nginx.ingress.kubernetes.io/rewrite-target'] = '/'
|
||||
}
|
||||
|
||||
const ingress = {
|
||||
apiVersion: 'networking.k8s.io/v1',
|
||||
kind: 'Ingress',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
annotations: Object.keys(annotations).length > 0 ? annotations : undefined,
|
||||
},
|
||||
spec: {
|
||||
ingressClassName: config.ingressClassName || undefined,
|
||||
rules: [
|
||||
{
|
||||
host: config.host,
|
||||
http: {
|
||||
paths: [
|
||||
{
|
||||
path: config.path,
|
||||
pathType: config.pathType,
|
||||
backend: {
|
||||
service: {
|
||||
name: config.backendServiceName,
|
||||
port: {
|
||||
number: Number(config.backendServicePort),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
if (config.enableTls && config.tlsSecretName) {
|
||||
ingress.spec.tls = [
|
||||
{
|
||||
hosts: [config.host],
|
||||
secretName: config.tlsSecretName,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
const header = `# K8s Ingress - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'ingress.yaml'}
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(ingress)
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
export const k8sJobSchema = {
|
||||
id: 'k8s-job',
|
||||
name: 'K8s Job',
|
||||
icon: 'Box',
|
||||
category: 'K8S 工作负载',
|
||||
description: 'Kubernetes Job — 运行一次性批处理任务',
|
||||
format: 'yaml',
|
||||
fileName: 'job.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'Job 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-job',
|
||||
default: 'my-job',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
{
|
||||
key: 'image',
|
||||
label: '容器镜像',
|
||||
type: 'text',
|
||||
placeholder: 'busybox:latest',
|
||||
default: 'busybox:latest',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'command',
|
||||
label: 'Command',
|
||||
type: 'text',
|
||||
placeholder: 'echo hello',
|
||||
default: 'echo hello',
|
||||
required: true,
|
||||
tip: '容器启动命令',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '执行控制',
|
||||
fields: [
|
||||
{
|
||||
key: 'backoffLimit',
|
||||
label: '重试次数上限',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 100,
|
||||
default: 6,
|
||||
tip: '失败后最多重试次数',
|
||||
},
|
||||
{
|
||||
key: 'completions',
|
||||
label: '完成数量',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 100,
|
||||
default: 1,
|
||||
tip: '需要成功完成的 Pod 数',
|
||||
},
|
||||
{
|
||||
key: 'parallelism',
|
||||
label: '并行度',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 100,
|
||||
default: 1,
|
||||
tip: '同时运行的 Pod 数',
|
||||
},
|
||||
{
|
||||
key: 'activeDeadlineSeconds',
|
||||
label: '超时时间 (秒)',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 86400,
|
||||
default: 0,
|
||||
tip: '0 表示不限制超时',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '重启与清理',
|
||||
fields: [
|
||||
{
|
||||
key: 'restartPolicy',
|
||||
label: '重启策略',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Never (推荐)', value: 'Never' },
|
||||
{ label: 'OnFailure', value: 'OnFailure' },
|
||||
],
|
||||
default: 'Never',
|
||||
},
|
||||
{
|
||||
key: 'ttlSecondsAfterFinished',
|
||||
label: '自动清理 (秒)',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 86400,
|
||||
default: 0,
|
||||
tip: '完成后自动删除 Job,0 表示保留',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sJobYaml(config) {
|
||||
const container = {
|
||||
name: config.name,
|
||||
image: config.image,
|
||||
command: config.command.split(' '),
|
||||
}
|
||||
|
||||
const job = {
|
||||
apiVersion: 'batch/v1',
|
||||
kind: 'Job',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
},
|
||||
spec: {
|
||||
backoffLimit: config.backoffLimit,
|
||||
completions: config.completions,
|
||||
parallelism: config.parallelism,
|
||||
template: {
|
||||
spec: {
|
||||
containers: [container],
|
||||
restartPolicy: config.restartPolicy,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if (config.activeDeadlineSeconds > 0) {
|
||||
job.spec.activeDeadlineSeconds = config.activeDeadlineSeconds
|
||||
}
|
||||
|
||||
if (config.ttlSecondsAfterFinished > 0) {
|
||||
job.spec.ttlSecondsAfterFinished = config.ttlSecondsAfterFinished
|
||||
}
|
||||
|
||||
const header = `# K8s Job - 由 ConfTemplate 生成\n# 生成时间: ${new Date().toLocaleString('zh-CN')}\n# 部署命令: kubectl apply -f ${config.fileName || 'job.yaml'}\n`
|
||||
|
||||
return header + '\n' + toYaml(job)
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// K8s Namespace Schema - 由 ConfTemplate 生成
|
||||
// 生成时间: 自动生成
|
||||
|
||||
export const k8sNamespaceSchema = {
|
||||
id: 'k8s-namespace',
|
||||
name: 'K8s Namespace',
|
||||
icon: 'Layers',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes 命名空间,用于资源隔离和组织',
|
||||
format: 'yaml',
|
||||
fileName: 'namespace.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'Namespace 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-namespace',
|
||||
default: '',
|
||||
required: true,
|
||||
tip: '命名空间名称,必须符合 DNS 命名规范',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '标签配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'labelKey',
|
||||
label: '标签键',
|
||||
type: 'text',
|
||||
placeholder: 'app',
|
||||
default: 'app',
|
||||
tip: '为命名空间添加标签键',
|
||||
},
|
||||
{
|
||||
key: 'labelValue',
|
||||
label: '标签值',
|
||||
type: 'text',
|
||||
placeholder: 'my-project',
|
||||
default: 'my-project',
|
||||
tip: '标签值',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sNamespaceYaml(config) {
|
||||
const ns = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'Namespace',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
},
|
||||
}
|
||||
|
||||
if (config.labelKey && config.labelValue) {
|
||||
ns.metadata.labels = { [config.labelKey]: config.labelValue }
|
||||
}
|
||||
|
||||
const header = `# K8s Namespace - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'namespace.yaml'}
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(ns)
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
// K8s NetworkPolicy Schema - 由 ConfTemplate 生成
|
||||
// 生成时间: 自动生成
|
||||
|
||||
export const k8sNetworkPolicySchema = {
|
||||
id: 'k8s-networkpolicy',
|
||||
name: 'K8s NetworkPolicy',
|
||||
icon: 'ShieldAlert',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes 网络策略,控制 Pod 的入站和出站流量',
|
||||
format: 'yaml',
|
||||
fileName: 'networkpolicy.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'NetworkPolicy 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-network-policy',
|
||||
default: 'my-network-policy',
|
||||
required: true,
|
||||
tip: '将用作 NetworkPolicy 的 metadata.name',
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Pod 选择器',
|
||||
fields: [
|
||||
{
|
||||
key: 'podSelectorLabelKey',
|
||||
label: '选择器标签键',
|
||||
type: 'text',
|
||||
placeholder: 'app',
|
||||
default: 'app',
|
||||
tip: '用于匹配 Pod 的标签键',
|
||||
},
|
||||
{
|
||||
key: 'podSelectorLabelValue',
|
||||
label: '选择器标签值',
|
||||
type: 'text',
|
||||
placeholder: 'myapp',
|
||||
default: 'myapp',
|
||||
tip: '用于匹配 Pod 的标签值',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '策略类型',
|
||||
fields: [
|
||||
{
|
||||
key: 'policyTypes',
|
||||
label: '策略类型',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '仅入站 (Ingress)', value: 'Ingress' },
|
||||
{ label: '仅出站 (Egress)', value: 'Egress' },
|
||||
{ label: '双向 (Ingress + Egress)', value: 'Both' },
|
||||
],
|
||||
default: 'Both',
|
||||
tip: '控制入站、出站或双向流量',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '入站规则 (Ingress)',
|
||||
fields: [
|
||||
{
|
||||
key: 'ingressFrom',
|
||||
label: '允许来源',
|
||||
type: 'text',
|
||||
placeholder: '10.0.0.0/8 或 namespace label',
|
||||
default: '',
|
||||
tip: 'IP CIDR 或命名空间标签,留空则允许所有来源',
|
||||
},
|
||||
{
|
||||
key: 'ingressPorts',
|
||||
label: '允许端口',
|
||||
type: 'text',
|
||||
placeholder: '80,443',
|
||||
default: '',
|
||||
tip: '逗号分隔的端口号列表',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '出站规则 (Egress)',
|
||||
fields: [
|
||||
{
|
||||
key: 'egressTo',
|
||||
label: '允许目标',
|
||||
type: 'text',
|
||||
placeholder: '10.0.0.0/8 或 namespace label',
|
||||
default: '',
|
||||
tip: 'IP CIDR 或命名空间标签,留空则允许所有目标',
|
||||
},
|
||||
{
|
||||
key: 'egressPorts',
|
||||
label: '允许端口',
|
||||
type: 'text',
|
||||
placeholder: '53,443',
|
||||
default: '',
|
||||
tip: '逗号分隔的端口号列表',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sNetworkPolicyYaml(config) {
|
||||
const labelKey = config.podSelectorLabelKey || 'app'
|
||||
const labelValue = config.podSelectorLabelValue || 'myapp'
|
||||
|
||||
const np = {
|
||||
apiVersion: 'networking.k8s.io/v1',
|
||||
kind: 'NetworkPolicy',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
},
|
||||
spec: {
|
||||
podSelector: {
|
||||
matchLabels: { [labelKey]: labelValue },
|
||||
},
|
||||
policyTypes: [],
|
||||
},
|
||||
}
|
||||
|
||||
// Determine policy types
|
||||
if (config.policyTypes === 'Both') {
|
||||
np.spec.policyTypes = ['Ingress', 'Egress']
|
||||
} else {
|
||||
np.spec.policyTypes = [config.policyTypes]
|
||||
}
|
||||
|
||||
// Ingress rules
|
||||
if (np.spec.policyTypes.includes('Ingress')) {
|
||||
const ingressRule = {}
|
||||
if (config.ingressFrom) {
|
||||
const from = config.ingressFrom.split(',').map(f => f.trim()).filter(Boolean)
|
||||
ingressRule.from = from.map(f => {
|
||||
if (f.includes('/')) {
|
||||
return { ipBlock: { cidr: f } }
|
||||
}
|
||||
return { namespaceSelector: { matchLabels: { name: f } } }
|
||||
})
|
||||
}
|
||||
if (config.ingressPorts) {
|
||||
const ports = config.ingressPorts.split(',').map(p => parseInt(p.trim())).filter(p => !isNaN(p))
|
||||
if (ports.length > 0) {
|
||||
ingressRule.ports = ports.map(p => ({ port: p, protocol: 'TCP' }))
|
||||
}
|
||||
}
|
||||
if (Object.keys(ingressRule).length > 0) {
|
||||
np.spec.ingress = [ingressRule]
|
||||
}
|
||||
}
|
||||
|
||||
// Egress rules
|
||||
if (np.spec.policyTypes.includes('Egress')) {
|
||||
const egressRule = {}
|
||||
if (config.egressTo) {
|
||||
const to = config.egressTo.split(',').map(t => t.trim()).filter(Boolean)
|
||||
egressRule.to = to.map(t => {
|
||||
if (t.includes('/')) {
|
||||
return { ipBlock: { cidr: t } }
|
||||
}
|
||||
return { namespaceSelector: { matchLabels: { name: t } } }
|
||||
})
|
||||
}
|
||||
if (config.egressPorts) {
|
||||
const ports = config.egressPorts.split(',').map(p => parseInt(p.trim())).filter(p => !isNaN(p))
|
||||
if (ports.length > 0) {
|
||||
egressRule.ports = ports.map(p => ({ port: p, protocol: 'TCP' }))
|
||||
}
|
||||
}
|
||||
if (Object.keys(egressRule).length > 0) {
|
||||
np.spec.egress = [egressRule]
|
||||
}
|
||||
}
|
||||
|
||||
const header = `# K8s NetworkPolicy - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'networkpolicy.yaml'}
|
||||
# 注意: 需要 CNI 插件支持 NetworkPolicy (如 Calico, Cilium)
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(np)
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
export const k8sPodSchema = {
|
||||
id: 'k8s-pod',
|
||||
name: 'K8s Pod',
|
||||
icon: 'Box',
|
||||
category: 'K8S 工作负载',
|
||||
description: 'Kubernetes Pod — 最小可部署单元',
|
||||
format: 'yaml',
|
||||
fileName: 'pod.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'Pod 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-pod',
|
||||
default: 'my-pod',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
{
|
||||
key: 'containerName',
|
||||
label: '容器名称',
|
||||
type: 'text',
|
||||
placeholder: 'main',
|
||||
default: 'main',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'image',
|
||||
label: '容器镜像',
|
||||
type: 'text',
|
||||
placeholder: 'nginx:1.24',
|
||||
default: 'nginx:1.24',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'imagePullPolicy',
|
||||
label: '镜像拉取策略',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'IfNotPresent (推荐)', value: 'IfNotPresent' },
|
||||
{ label: 'Always', value: 'Always' },
|
||||
{ label: 'Never', value: 'Never' },
|
||||
],
|
||||
default: 'IfNotPresent',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '容器端口',
|
||||
fields: [
|
||||
{
|
||||
key: 'containerPort',
|
||||
label: '容器端口',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 65535,
|
||||
default: 80,
|
||||
},
|
||||
{
|
||||
key: 'protocol',
|
||||
label: '协议',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'TCP', value: 'TCP' },
|
||||
{ label: 'UDP', value: 'UDP' },
|
||||
],
|
||||
default: 'TCP',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '启动命令 (可选)',
|
||||
fields: [
|
||||
{
|
||||
key: 'command',
|
||||
label: 'Command',
|
||||
type: 'text',
|
||||
placeholder: '/bin/sh',
|
||||
default: '',
|
||||
tip: '容器 ENTRYPOINT,留空则使用镜像默认值',
|
||||
},
|
||||
{
|
||||
key: 'args',
|
||||
label: 'Args',
|
||||
type: 'text',
|
||||
placeholder: '-c, echo hello',
|
||||
default: '',
|
||||
tip: '逗号分隔多个参数',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '环境变量 (可选)',
|
||||
fields: [
|
||||
{
|
||||
key: 'envVars',
|
||||
label: '环境变量',
|
||||
type: 'keyvalue',
|
||||
default: [],
|
||||
tip: '键值对,将注入为容器环境变量',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '资源限制',
|
||||
fields: [
|
||||
{
|
||||
key: 'cpuRequest',
|
||||
label: 'CPU Requests',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '50m', value: '50m' },
|
||||
{ label: '100m', value: '100m' },
|
||||
{ label: '200m', value: '200m' },
|
||||
{ label: '500m', value: '500m' },
|
||||
{ label: '1', value: '1' },
|
||||
{ label: '2', value: '2' },
|
||||
],
|
||||
default: '100m',
|
||||
},
|
||||
{
|
||||
key: 'cpuLimit',
|
||||
label: 'CPU Limits',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '200m', value: '200m' },
|
||||
{ label: '500m', value: '500m' },
|
||||
{ label: '1', value: '1' },
|
||||
{ label: '2', value: '2' },
|
||||
{ label: '4', value: '4' },
|
||||
],
|
||||
default: '500m',
|
||||
},
|
||||
{
|
||||
key: 'memoryRequest',
|
||||
label: 'Memory Requests',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '64Mi', value: '64Mi' },
|
||||
{ label: '128Mi', value: '128Mi' },
|
||||
{ label: '256Mi', value: '256Mi' },
|
||||
{ label: '512Mi', value: '512Mi' },
|
||||
{ label: '1Gi', value: '1Gi' },
|
||||
],
|
||||
default: '128Mi',
|
||||
},
|
||||
{
|
||||
key: 'memoryLimit',
|
||||
label: 'Memory Limits',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '128Mi', value: '128Mi' },
|
||||
{ label: '256Mi', value: '256Mi' },
|
||||
{ label: '512Mi', value: '512Mi' },
|
||||
{ label: '1Gi', value: '1Gi' },
|
||||
{ label: '2Gi', value: '2Gi' },
|
||||
],
|
||||
default: '256Mi',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '重启与调度',
|
||||
fields: [
|
||||
{
|
||||
key: 'restartPolicy',
|
||||
label: '重启策略',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Always (默认)', value: 'Always' },
|
||||
{ label: 'OnFailure', value: 'OnFailure' },
|
||||
{ label: 'Never', value: 'Never' },
|
||||
],
|
||||
default: 'Always',
|
||||
},
|
||||
{
|
||||
key: 'nodeName',
|
||||
label: '指定节点',
|
||||
type: 'text',
|
||||
placeholder: 'node-1',
|
||||
default: '',
|
||||
tip: '留空则由调度器自动分配',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '标签',
|
||||
fields: [
|
||||
{
|
||||
key: 'appLabel',
|
||||
label: 'app 标签值',
|
||||
type: 'text',
|
||||
default: '',
|
||||
tip: '留空则使用 Pod 名称',
|
||||
},
|
||||
{
|
||||
key: 'versionLabel',
|
||||
label: 'version 标签',
|
||||
type: 'text',
|
||||
placeholder: 'v1',
|
||||
default: 'v1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sPodYaml(config) {
|
||||
const labels = {
|
||||
app: config.appLabel || config.name,
|
||||
}
|
||||
if (config.versionLabel) {
|
||||
labels.version = config.versionLabel
|
||||
}
|
||||
|
||||
const container = {
|
||||
name: config.containerName,
|
||||
image: config.image,
|
||||
imagePullPolicy: config.imagePullPolicy,
|
||||
ports: [{
|
||||
containerPort: config.containerPort,
|
||||
protocol: config.protocol,
|
||||
}],
|
||||
resources: {
|
||||
requests: { cpu: config.cpuRequest, memory: config.memoryRequest },
|
||||
limits: { cpu: config.cpuLimit, memory: config.memoryLimit },
|
||||
},
|
||||
}
|
||||
|
||||
if (config.command) {
|
||||
container.command = config.command.split(',').map(s => s.trim())
|
||||
}
|
||||
if (config.args) {
|
||||
container.args = config.args.split(',').map(s => s.trim())
|
||||
}
|
||||
if (config.envVars && config.envVars.length > 0) {
|
||||
container.env = config.envVars.map(e => ({ name: e.key, value: e.value }))
|
||||
}
|
||||
|
||||
const podSpec = {
|
||||
containers: [container],
|
||||
restartPolicy: config.restartPolicy,
|
||||
}
|
||||
if (config.nodeName) {
|
||||
podSpec.nodeName = config.nodeName
|
||||
}
|
||||
|
||||
const pod = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'Pod',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
labels,
|
||||
},
|
||||
spec: podSpec,
|
||||
}
|
||||
|
||||
const header = `# K8s Pod - 由 ConfTemplate 生成\n# 生成时间: ${new Date().toLocaleString('zh-CN')}\n# 部署命令: kubectl apply -f ${config.fileName || 'pod.yaml'}\n`
|
||||
|
||||
return header + '\n' + toYaml(pod)
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
// K8s PersistentVolume schema + YAML generator for ConfTemplate
|
||||
// Generated: 2026-07-18
|
||||
|
||||
export const k8sPvSchema = {
|
||||
id: 'k8s-pv',
|
||||
name: 'K8s PersistentVolume',
|
||||
icon: 'Database',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes 持久化存储卷 (PV)',
|
||||
format: 'yaml',
|
||||
fileName: 'pv.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'PV 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-pv',
|
||||
default: 'my-pv',
|
||||
required: true,
|
||||
tip: 'PersistentVolume 资源名称',
|
||||
},
|
||||
{
|
||||
key: 'capacity',
|
||||
label: '存储容量',
|
||||
type: 'text',
|
||||
placeholder: '10Gi',
|
||||
default: '10Gi',
|
||||
required: true,
|
||||
tip: '例如 10Gi, 100Mi',
|
||||
},
|
||||
{
|
||||
key: 'accessModes',
|
||||
label: '访问模式',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'ReadWriteOnce', value: 'ReadWriteOnce' },
|
||||
{ label: 'ReadWriteMany', value: 'ReadWriteMany' },
|
||||
{ label: 'ReadOnlyMany', value: 'ReadOnlyMany' },
|
||||
],
|
||||
default: 'ReadWriteOnce',
|
||||
tip: '单节点读写 / 多节点读写 / 多节点只读',
|
||||
},
|
||||
{
|
||||
key: 'storageClassName',
|
||||
label: 'StorageClass 名称',
|
||||
type: 'text',
|
||||
placeholder: 'standard',
|
||||
default: 'standard',
|
||||
},
|
||||
{
|
||||
key: 'persistentVolumeReclaimPolicy',
|
||||
label: '回收策略',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Retain', value: 'Retain' },
|
||||
{ label: 'Recycle', value: 'Recycle' },
|
||||
{ label: 'Delete', value: 'Delete' },
|
||||
],
|
||||
default: 'Retain',
|
||||
tip: 'PV 释放后的处理方式',
|
||||
},
|
||||
{
|
||||
key: 'volumeMode',
|
||||
label: '卷模式',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Filesystem', value: 'Filesystem' },
|
||||
{ label: 'Block', value: 'Block' },
|
||||
],
|
||||
default: 'Filesystem',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'HostPath 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'hostPathEnabled',
|
||||
label: '启用 HostPath',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
tip: '使用节点本地路径作为存储',
|
||||
},
|
||||
{
|
||||
key: 'hostPath',
|
||||
label: 'HostPath 路径',
|
||||
type: 'text',
|
||||
placeholder: '/tmp/pv',
|
||||
default: '/tmp/pv',
|
||||
dependsOn: 'hostPathEnabled',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'NFS 配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'nfsEnabled',
|
||||
label: '启用 NFS',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
tip: '使用 NFS 共享存储',
|
||||
},
|
||||
{
|
||||
key: 'nfsServer',
|
||||
label: 'NFS 服务器地址',
|
||||
type: 'text',
|
||||
placeholder: '192.168.1.100',
|
||||
default: '',
|
||||
dependsOn: 'nfsEnabled',
|
||||
},
|
||||
{
|
||||
key: 'nfsPath',
|
||||
label: 'NFS 路径',
|
||||
type: 'text',
|
||||
placeholder: '/exports/data',
|
||||
default: '',
|
||||
dependsOn: 'nfsEnabled',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sPvYaml(config) {
|
||||
const spec = {
|
||||
capacity: {
|
||||
storage: config.capacity,
|
||||
},
|
||||
accessModes: [config.accessModes],
|
||||
persistentVolumeReclaimPolicy: config.persistentVolumeReclaimPolicy,
|
||||
storageClassName: config.storageClassName,
|
||||
volumeMode: config.volumeMode,
|
||||
}
|
||||
|
||||
if (config.hostPathEnabled) {
|
||||
spec.hostPath = {
|
||||
path: config.hostPath,
|
||||
}
|
||||
} else if (config.nfsEnabled) {
|
||||
spec.nfs = {
|
||||
server: config.nfsServer,
|
||||
path: config.nfsPath,
|
||||
}
|
||||
}
|
||||
|
||||
const pv = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'PersistentVolume',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
},
|
||||
spec,
|
||||
}
|
||||
|
||||
const header = `# K8s PersistentVolume - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'pv.yaml'}
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(pv)
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
// K8s PersistentVolumeClaim schema + YAML generator for ConfTemplate
|
||||
// Generated: 2026-07-18
|
||||
|
||||
export const k8sPvcSchema = {
|
||||
id: 'k8s-pvc',
|
||||
name: 'K8s PersistentVolumeClaim',
|
||||
icon: 'HardDrive',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes 持久化存储声明 (PVC)',
|
||||
format: 'yaml',
|
||||
fileName: 'pvc.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'PVC 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-pvc',
|
||||
default: 'my-pvc',
|
||||
required: true,
|
||||
tip: 'PersistentVolumeClaim 资源名称',
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
{
|
||||
key: 'accessModes',
|
||||
label: '访问模式',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'ReadWriteOnce', value: 'ReadWriteOnce' },
|
||||
{ label: 'ReadWriteMany', value: 'ReadWriteMany' },
|
||||
{ label: 'ReadOnlyMany', value: 'ReadOnlyMany' },
|
||||
],
|
||||
default: 'ReadWriteOnce',
|
||||
tip: '单节点读写 / 多节点读写 / 多节点只读',
|
||||
},
|
||||
{
|
||||
key: 'storageClassName',
|
||||
label: 'StorageClass 名称',
|
||||
type: 'text',
|
||||
placeholder: 'standard',
|
||||
default: 'standard',
|
||||
},
|
||||
{
|
||||
key: 'storageRequest',
|
||||
label: '请求存储容量',
|
||||
type: 'text',
|
||||
placeholder: '10Gi',
|
||||
default: '10Gi',
|
||||
required: true,
|
||||
tip: '例如 10Gi, 100Mi',
|
||||
},
|
||||
{
|
||||
key: 'volumeMode',
|
||||
label: '卷模式',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Filesystem', value: 'Filesystem' },
|
||||
{ label: 'Block', value: 'Block' },
|
||||
],
|
||||
default: 'Filesystem',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sPvcYaml(config) {
|
||||
const pvc = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'PersistentVolumeClaim',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
},
|
||||
spec: {
|
||||
accessModes: [config.accessModes],
|
||||
storageClassName: config.storageClassName,
|
||||
volumeMode: config.volumeMode,
|
||||
resources: {
|
||||
requests: {
|
||||
storage: config.storageRequest,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const header = `# K8s PersistentVolumeClaim - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'pvc.yaml'}
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(pvc)
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
// K8s Role Schema - 由 ConfTemplate 生成
|
||||
// 生成时间: 自动生成
|
||||
|
||||
export const k8sRoleSchema = {
|
||||
id: 'k8s-role',
|
||||
name: 'K8s Role',
|
||||
icon: 'Shield',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes 命名空间级 RBAC 角色',
|
||||
format: 'yaml',
|
||||
fileName: 'role.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'Role 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-role',
|
||||
default: 'my-role',
|
||||
required: true,
|
||||
tip: '将用作 Role 的 metadata.name',
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '权限规则',
|
||||
fields: [
|
||||
{
|
||||
key: 'resources',
|
||||
label: '资源类型',
|
||||
type: 'select',
|
||||
multiple: true,
|
||||
options: [
|
||||
{ label: 'Pods', value: 'pods' },
|
||||
{ label: 'Services', value: 'services' },
|
||||
{ label: 'ConfigMaps', value: 'configmaps' },
|
||||
{ label: 'Deployments', value: 'deployments' },
|
||||
{ label: 'Secrets', value: 'secrets' },
|
||||
{ label: 'All (*)', value: '*' },
|
||||
],
|
||||
default: ['pods'],
|
||||
tip: '可多选,选择要授权的资源类型',
|
||||
},
|
||||
{
|
||||
key: 'verbs',
|
||||
label: '操作动词',
|
||||
type: 'select',
|
||||
multiple: true,
|
||||
options: [
|
||||
{ label: 'Get (读取)', value: 'get' },
|
||||
{ label: 'List (列表)', value: 'list' },
|
||||
{ label: 'Watch (监听)', value: 'watch' },
|
||||
{ label: 'Create (创建)', value: 'create' },
|
||||
{ label: 'Update (更新)', value: 'update' },
|
||||
{ label: 'Delete (删除)', value: 'delete' },
|
||||
{ label: 'Patch (补丁)', value: 'patch' },
|
||||
{ label: 'All (*)', value: '*' },
|
||||
],
|
||||
default: ['get', 'list', 'watch'],
|
||||
tip: '可多选,选择允许的操作',
|
||||
},
|
||||
{
|
||||
key: 'apiGroups',
|
||||
label: 'API 组',
|
||||
type: 'text',
|
||||
placeholder: '"" (核心组留空)',
|
||||
default: '',
|
||||
tip: 'API 组,核心资源(如 pods, services)留空',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sRoleYaml(config) {
|
||||
const resources = Array.isArray(config.resources) ? config.resources : [config.resources || 'pods']
|
||||
const verbs = Array.isArray(config.verbs) ? config.verbs : [config.verbs || 'get']
|
||||
const apiGroups = config.apiGroups !== undefined && config.apiGroups !== ''
|
||||
? config.apiGroups.split(',').map(g => g.trim())
|
||||
: ['']
|
||||
|
||||
const rule = {
|
||||
apiGroups,
|
||||
resources,
|
||||
verbs,
|
||||
}
|
||||
|
||||
const role = {
|
||||
apiVersion: 'rbac.authorization.k8s.io/v1',
|
||||
kind: 'Role',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
},
|
||||
rules: [rule],
|
||||
}
|
||||
|
||||
const header = `# K8s Role - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'role.yaml'}
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(role)
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// K8s RoleBinding Schema - 由 ConfTemplate 生成
|
||||
// 生成时间: 自动生成
|
||||
|
||||
export const k8sRoleBindingSchema = {
|
||||
id: 'k8s-rolebinding',
|
||||
name: 'K8s RoleBinding',
|
||||
icon: 'Link',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes 命名空间级角色绑定,将 Role 授予用户或服务账户',
|
||||
format: 'yaml',
|
||||
fileName: 'rolebinding.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'RoleBinding 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-rolebinding',
|
||||
default: 'my-rolebinding',
|
||||
required: true,
|
||||
tip: '将用作 RoleBinding 的 metadata.name',
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '角色引用',
|
||||
fields: [
|
||||
{
|
||||
key: 'roleName',
|
||||
label: 'Role 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-role',
|
||||
default: '',
|
||||
required: true,
|
||||
tip: '要绑定的 Role 名称',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '主体配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'subjectKind',
|
||||
label: '主体类型',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'User (用户)', value: 'User' },
|
||||
{ label: 'Group (用户组)', value: 'Group' },
|
||||
{ label: 'ServiceAccount (服务账户)', value: 'ServiceAccount' },
|
||||
],
|
||||
default: 'ServiceAccount',
|
||||
tip: '要授权的主体类型',
|
||||
},
|
||||
{
|
||||
key: 'subjectName',
|
||||
label: '主体名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-serviceaccount',
|
||||
default: '',
|
||||
required: true,
|
||||
tip: '用户、组或 ServiceAccount 的名称',
|
||||
},
|
||||
{
|
||||
key: 'subjectNamespace',
|
||||
label: '主体命名空间',
|
||||
type: 'text',
|
||||
placeholder: '仅 ServiceAccount 需要',
|
||||
default: '',
|
||||
tip: '仅当主体类型为 ServiceAccount 时需要填写',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sRoleBindingYaml(config) {
|
||||
const subject = {
|
||||
kind: config.subjectKind || 'ServiceAccount',
|
||||
name: config.subjectName,
|
||||
}
|
||||
if (config.subjectNamespace && config.subjectKind === 'ServiceAccount') {
|
||||
subject.namespace = config.subjectNamespace
|
||||
}
|
||||
if (config.subjectKind === 'User') {
|
||||
subject.apiGroup = 'rbac.authorization.k8s.io'
|
||||
}
|
||||
if (config.subjectKind === 'Group') {
|
||||
subject.apiGroup = 'rbac.authorization.k8s.io'
|
||||
}
|
||||
|
||||
const roleBinding = {
|
||||
apiVersion: 'rbac.authorization.k8s.io/v1',
|
||||
kind: 'RoleBinding',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
},
|
||||
roleRef: {
|
||||
apiGroup: 'rbac.authorization.k8s.io',
|
||||
kind: 'Role',
|
||||
name: config.roleName,
|
||||
},
|
||||
subjects: [subject],
|
||||
}
|
||||
|
||||
const header = `# K8s RoleBinding - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'rolebinding.yaml'}
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(roleBinding)
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
// K8s Secret Schema - 由 ConfTemplate 生成
|
||||
// 生成时间: 自动生成
|
||||
|
||||
export const k8sSecretSchema = {
|
||||
id: 'k8s-secret',
|
||||
name: 'K8s Secret',
|
||||
icon: 'Lock',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes Secret,存储敏感数据如密码、证书等',
|
||||
format: 'yaml',
|
||||
fileName: 'secret.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'Secret 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-secret',
|
||||
default: 'my-secret',
|
||||
required: true,
|
||||
tip: '将用作 Secret 的 metadata.name',
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
{
|
||||
key: 'secretType',
|
||||
label: 'Secret 类型',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Opaque (通用)', value: 'Opaque' },
|
||||
{ label: 'Docker Registry (dockerconfigjson)', value: 'kubernetes.io/dockerconfigjson' },
|
||||
{ label: 'TLS 证书', value: 'kubernetes.io/tls' },
|
||||
{ label: 'Basic Auth (基础认证)', value: 'kubernetes.io/basic-auth' },
|
||||
],
|
||||
default: 'Opaque',
|
||||
tip: '不同类型对应不同的数据结构',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '数据配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'dataKey',
|
||||
label: '数据键名',
|
||||
type: 'text',
|
||||
placeholder: 'username',
|
||||
default: 'username',
|
||||
required: true,
|
||||
tip: 'Secret 中的键名',
|
||||
},
|
||||
{
|
||||
key: 'dataValue',
|
||||
label: '数据值',
|
||||
type: 'text',
|
||||
placeholder: 'base64 encoded or plain text',
|
||||
default: '',
|
||||
required: true,
|
||||
tip: '如果开启 Base64 编码,输入明文即可',
|
||||
},
|
||||
{
|
||||
key: 'encodeBase64',
|
||||
label: 'Base64 编码',
|
||||
type: 'switch',
|
||||
default: true,
|
||||
tip: '自动将数据值进行 Base64 编码',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '挂载配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'mountAsVolume',
|
||||
label: '作为 Volume 挂载',
|
||||
type: 'switch',
|
||||
default: true,
|
||||
tip: '是否将 Secret 挂载为容器内的文件',
|
||||
},
|
||||
{
|
||||
key: 'mountPath',
|
||||
label: '挂载路径',
|
||||
type: 'text',
|
||||
placeholder: '/etc/secrets',
|
||||
default: '/etc/secrets',
|
||||
dependsOn: { key: 'mountAsVolume', value: true },
|
||||
tip: '容器内挂载目录路径',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sSecretYaml(config) {
|
||||
const data = {}
|
||||
let value = config.dataValue || ''
|
||||
|
||||
if (config.encodeBase64) {
|
||||
try {
|
||||
value = btoa(unescape(encodeURIComponent(value)))
|
||||
} catch {
|
||||
// fallback: use value as-is
|
||||
}
|
||||
}
|
||||
|
||||
data[config.dataKey || 'data'] = value
|
||||
|
||||
const secret = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'Secret',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
},
|
||||
type: config.secretType || 'Opaque',
|
||||
data,
|
||||
}
|
||||
|
||||
const header = `# K8s Secret - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'secret.yaml'}
|
||||
# 注意: 生产环境建议使用 kubectl create secret 命令或 Sealed Secrets
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(secret)
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// K8s ServiceAccount Schema - 由 ConfTemplate 生成
|
||||
// 生成时间: 自动生成
|
||||
|
||||
export const k8sServiceAccountSchema = {
|
||||
id: 'k8s-serviceaccount',
|
||||
name: 'K8s ServiceAccount',
|
||||
icon: 'User',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes 服务账户,用于 Pod 的身份认证',
|
||||
format: 'yaml',
|
||||
fileName: 'serviceaccount.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'ServiceAccount 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-serviceaccount',
|
||||
default: 'my-serviceaccount',
|
||||
required: true,
|
||||
tip: '将用作 ServiceAccount 的 metadata.name',
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '账户配置',
|
||||
fields: [
|
||||
{
|
||||
key: 'automountServiceAccountToken',
|
||||
label: '自动挂载 Token',
|
||||
type: 'switch',
|
||||
default: true,
|
||||
tip: '自动将 API Token 挂载到使用此 ServiceAccount 的 Pod',
|
||||
},
|
||||
{
|
||||
key: 'enableImagePullSecrets',
|
||||
label: '配置镜像拉取 Secret',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
tip: '为此 ServiceAccount 配置私有镜像仓库认证',
|
||||
},
|
||||
{
|
||||
key: 'imagePullSecretName',
|
||||
label: '镜像拉取 Secret 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-registry-secret',
|
||||
default: '',
|
||||
dependsOn: { key: 'enableImagePullSecrets', value: true },
|
||||
tip: '已存在的 docker-registry Secret 名称',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sServiceAccountYaml(config) {
|
||||
const sa = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'ServiceAccount',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
},
|
||||
}
|
||||
|
||||
if (config.automountServiceAccountToken === false) {
|
||||
sa.automountServiceAccountToken = false
|
||||
}
|
||||
|
||||
if (config.enableImagePullSecrets && config.imagePullSecretName) {
|
||||
sa.imagePullSecrets = [{ name: config.imagePullSecretName }]
|
||||
}
|
||||
|
||||
const header = `# K8s ServiceAccount - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'serviceaccount.yaml'}
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(sa)
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
export const k8sStatefulSetSchema = {
|
||||
id: 'k8s-statefulset',
|
||||
name: 'K8s StatefulSet',
|
||||
icon: 'Box',
|
||||
category: 'K8S 工作负载',
|
||||
description: 'Kubernetes 有状态应用部署控制器,支持稳定网络标识和持久存储',
|
||||
format: 'yaml',
|
||||
fileName: 'statefulset.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'StatefulSet 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-statefulset',
|
||||
default: 'my-statefulset',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'namespace',
|
||||
label: '命名空间',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
default: 'default',
|
||||
},
|
||||
{
|
||||
key: 'appName',
|
||||
label: '应用名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-app',
|
||||
default: 'my-app',
|
||||
required: true,
|
||||
tip: '用作 selector 和 label 的 app 值',
|
||||
},
|
||||
{
|
||||
key: 'image',
|
||||
label: '容器镜像',
|
||||
type: 'text',
|
||||
placeholder: 'nginx:1.24',
|
||||
default: 'nginx:1.24',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: 'replicas',
|
||||
label: '副本数',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 100,
|
||||
default: 3,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '网络与服务',
|
||||
fields: [
|
||||
{
|
||||
key: 'containerPort',
|
||||
label: '容器端口',
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 65535,
|
||||
default: 80,
|
||||
},
|
||||
{
|
||||
key: 'serviceName',
|
||||
label: 'Headless Service 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-service',
|
||||
default: 'my-service',
|
||||
required: true,
|
||||
tip: '必须已存在的 Headless Service',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '持久存储',
|
||||
fields: [
|
||||
{
|
||||
key: 'volumeClaimEnabled',
|
||||
label: '启用 PVC',
|
||||
type: 'switch',
|
||||
default: true,
|
||||
tip: '为每个 Pod 创建独立的 PersistentVolumeClaim',
|
||||
},
|
||||
{
|
||||
key: 'storageClassName',
|
||||
label: 'StorageClass',
|
||||
type: 'text',
|
||||
placeholder: 'standard',
|
||||
default: 'standard',
|
||||
dependsOn: { key: 'volumeClaimEnabled', value: true },
|
||||
},
|
||||
{
|
||||
key: 'storageSize',
|
||||
label: '存储大小',
|
||||
type: 'text',
|
||||
placeholder: '10Gi',
|
||||
default: '10Gi',
|
||||
dependsOn: { key: 'volumeClaimEnabled', value: true },
|
||||
},
|
||||
{
|
||||
key: 'pvcAccessMode',
|
||||
label: '访问模式',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'ReadWriteOnce', value: 'ReadWriteOnce' },
|
||||
{ label: 'ReadWriteMany', value: 'ReadWriteMany' },
|
||||
{ label: 'ReadOnlyMany', value: 'ReadOnlyMany' },
|
||||
],
|
||||
default: 'ReadWriteOnce',
|
||||
dependsOn: { key: 'volumeClaimEnabled', value: true },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '资源限制',
|
||||
fields: [
|
||||
{
|
||||
key: 'cpuRequest',
|
||||
label: 'CPU Requests',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '50m', value: '50m' },
|
||||
{ label: '100m', value: '100m' },
|
||||
{ label: '200m', value: '200m' },
|
||||
{ label: '500m', value: '500m' },
|
||||
{ label: '1', value: '1' },
|
||||
{ label: '2', value: '2' },
|
||||
],
|
||||
default: '100m',
|
||||
},
|
||||
{
|
||||
key: 'cpuLimit',
|
||||
label: 'CPU Limits',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '200m', value: '200m' },
|
||||
{ label: '500m', value: '500m' },
|
||||
{ label: '1', value: '1' },
|
||||
{ label: '2', value: '2' },
|
||||
{ label: '4', value: '4' },
|
||||
],
|
||||
default: '500m',
|
||||
},
|
||||
{
|
||||
key: 'memoryRequest',
|
||||
label: 'Memory Requests',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '64Mi', value: '64Mi' },
|
||||
{ label: '128Mi', value: '128Mi' },
|
||||
{ label: '256Mi', value: '256Mi' },
|
||||
{ label: '512Mi', value: '512Mi' },
|
||||
{ label: '1Gi', value: '1Gi' },
|
||||
],
|
||||
default: '128Mi',
|
||||
},
|
||||
{
|
||||
key: 'memoryLimit',
|
||||
label: 'Memory Limits',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '128Mi', value: '128Mi' },
|
||||
{ label: '256Mi', value: '256Mi' },
|
||||
{ label: '512Mi', value: '512Mi' },
|
||||
{ label: '1Gi', value: '1Gi' },
|
||||
{ label: '2Gi', value: '2Gi' },
|
||||
],
|
||||
default: '256Mi',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '更新策略',
|
||||
fields: [
|
||||
{
|
||||
key: 'updateStrategy',
|
||||
label: '更新策略',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'RollingUpdate - 滚动更新 (推荐)', value: 'RollingUpdate' },
|
||||
{ label: 'OnDelete - 手动删除触发', value: 'OnDelete' },
|
||||
],
|
||||
default: 'RollingUpdate',
|
||||
},
|
||||
{
|
||||
key: 'partition',
|
||||
label: 'Partition',
|
||||
type: 'number',
|
||||
min: 0,
|
||||
max: 100,
|
||||
default: 0,
|
||||
tip: '仅更新序号 >= partition 的 Pod',
|
||||
dependsOn: { key: 'updateStrategy', value: 'RollingUpdate' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sStatefulSetYaml(config) {
|
||||
const labels = { app: config.appName }
|
||||
|
||||
const container = {
|
||||
name: config.appName,
|
||||
image: config.image,
|
||||
ports: [{ containerPort: config.containerPort, name: 'http' }],
|
||||
resources: {
|
||||
requests: { cpu: config.cpuRequest, memory: config.memoryRequest },
|
||||
limits: { cpu: config.cpuLimit, memory: config.memoryLimit },
|
||||
},
|
||||
}
|
||||
|
||||
const volumeMounts = []
|
||||
const volumeClaimTemplates = []
|
||||
|
||||
if (config.volumeClaimEnabled) {
|
||||
volumeMounts.push({ name: 'data', mountPath: '/data' })
|
||||
volumeClaimTemplates.push({
|
||||
metadata: { name: 'data' },
|
||||
spec: {
|
||||
accessModes: [config.pvcAccessMode],
|
||||
storageClassName: config.storageClassName,
|
||||
resources: { requests: { storage: config.storageSize } },
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (volumeMounts.length > 0) {
|
||||
container.volumeMounts = volumeMounts
|
||||
}
|
||||
|
||||
const statefulset = {
|
||||
apiVersion: 'apps/v1',
|
||||
kind: 'StatefulSet',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
namespace: config.namespace,
|
||||
labels,
|
||||
},
|
||||
spec: {
|
||||
replicas: config.replicas,
|
||||
serviceName: config.serviceName,
|
||||
selector: { matchLabels: { app: config.appName } },
|
||||
updateStrategy: config.updateStrategy === 'RollingUpdate'
|
||||
? { type: 'RollingUpdate', rollingUpdate: { partition: config.partition } }
|
||||
: { type: 'OnDelete' },
|
||||
template: {
|
||||
metadata: { labels },
|
||||
spec: { containers: [container] },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if (volumeClaimTemplates.length > 0) {
|
||||
statefulset.spec.volumeClaimTemplates = volumeClaimTemplates
|
||||
}
|
||||
|
||||
const header = `# K8s StatefulSet - 由 ConfTemplate 生成\n# 生成时间: ${new Date().toLocaleString('zh-CN')}\n# 部署命令: kubectl apply -f ${config.fileName || 'statefulset.yaml'}\n`
|
||||
|
||||
return header + '\n' + toYaml(statefulset)
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
// K8s StorageClass schema + YAML generator for ConfTemplate
|
||||
// Generated: 2026-07-18
|
||||
|
||||
export const k8sStorageClassSchema = {
|
||||
id: 'k8s-storageclass',
|
||||
name: 'K8s StorageClass',
|
||||
icon: 'Layers',
|
||||
category: '云原生',
|
||||
description: 'Kubernetes 存储类 (StorageClass)',
|
||||
format: 'yaml',
|
||||
fileName: 'storageclass.yaml',
|
||||
groups: [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'StorageClass 名称',
|
||||
type: 'text',
|
||||
placeholder: 'my-storageclass',
|
||||
default: 'my-storageclass',
|
||||
required: true,
|
||||
tip: 'StorageClass 资源名称',
|
||||
},
|
||||
{
|
||||
key: 'provisioner',
|
||||
label: 'Provisioner',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'kubernetes.io/aws-ebs', value: 'kubernetes.io/aws-ebs' },
|
||||
{ label: 'kubernetes.io/gce-pd', value: 'kubernetes.io/gce-pd' },
|
||||
{ label: 'kubernetes.io/no-provisioner', value: 'kubernetes.io/no-provisioner' },
|
||||
{ label: 'rancher.io/local-path', value: 'rancher.io/local-path' },
|
||||
],
|
||||
default: 'kubernetes.io/aws-ebs',
|
||||
required: true,
|
||||
tip: '存储卷供应器',
|
||||
},
|
||||
{
|
||||
key: 'reclaimPolicy',
|
||||
label: '回收策略',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Delete', value: 'Delete' },
|
||||
{ label: 'Retain', value: 'Retain' },
|
||||
],
|
||||
default: 'Delete',
|
||||
},
|
||||
{
|
||||
key: 'volumeBindingMode',
|
||||
label: '卷绑定模式',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'Immediate', value: 'Immediate' },
|
||||
{ label: 'WaitForFirstConsumer', value: 'WaitForFirstConsumer' },
|
||||
],
|
||||
default: 'Immediate',
|
||||
tip: 'Immediate 立即绑定,WaitForFirstConsumer 延迟到 Pod 调度时',
|
||||
},
|
||||
{
|
||||
key: 'allowVolumeExpansion',
|
||||
label: '允许卷扩展',
|
||||
type: 'switch',
|
||||
default: true,
|
||||
tip: '允许 PVC 扩容',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Provisioner 参数',
|
||||
fields: [
|
||||
{
|
||||
key: 'paramType',
|
||||
label: '卷类型',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'gp2', value: 'gp2' },
|
||||
{ label: 'premium-rs', value: 'premium-rs' },
|
||||
],
|
||||
default: 'gp2',
|
||||
tip: '存储卷类型 (AWS gp2 / GCE premium-rs)',
|
||||
},
|
||||
{
|
||||
key: 'paramFsType',
|
||||
label: '文件系统类型',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: 'ext4', value: 'ext4' },
|
||||
{ label: 'xfs', value: 'xfs' },
|
||||
],
|
||||
default: 'ext4',
|
||||
},
|
||||
{
|
||||
key: 'paramIopsPerGB',
|
||||
label: 'IOPS per GB',
|
||||
type: 'text',
|
||||
placeholder: '(可选)',
|
||||
default: '',
|
||||
tip: '每 GB 的 IOPS 配置,留空则不设置',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function toYaml(obj, indent = 0) {
|
||||
const lines = []
|
||||
const prefix = ' '.repeat(indent)
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value === null || value === undefined || value === '') continue
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
for (const item of value) {
|
||||
if (typeof item === 'object' && item !== null) {
|
||||
const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '')
|
||||
if (entries.length === 0) continue
|
||||
const first = entries[0]
|
||||
lines.push(`${prefix} - ${first[0]}: ${first[1]}`)
|
||||
for (let i = 1; i < entries.length; i++) {
|
||||
lines.push(`${prefix} ${entries[i][0]}: ${entries[i][1]}`)
|
||||
}
|
||||
} else {
|
||||
lines.push(`${prefix} - ${item}`)
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
lines.push(`${prefix}${key}:`)
|
||||
lines.push(toYaml(value, indent + 1))
|
||||
} else {
|
||||
lines.push(`${prefix}${key}: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
export function generateK8sStorageClassYaml(config) {
|
||||
const parameters = {
|
||||
type: config.paramType,
|
||||
fsType: config.paramFsType,
|
||||
}
|
||||
if (config.paramIopsPerGB) {
|
||||
parameters.iopsPerGB = config.paramIopsPerGB
|
||||
}
|
||||
|
||||
const storageClass = {
|
||||
apiVersion: 'storage.k8s.io/v1',
|
||||
kind: 'StorageClass',
|
||||
metadata: {
|
||||
name: config.name,
|
||||
annotations: {
|
||||
'storageclass.kubernetes.io/is-default-class': 'false',
|
||||
},
|
||||
},
|
||||
provisioner: config.provisioner,
|
||||
reclaimPolicy: config.reclaimPolicy,
|
||||
volumeBindingMode: config.volumeBindingMode,
|
||||
allowVolumeExpansion: config.allowVolumeExpansion,
|
||||
parameters,
|
||||
}
|
||||
|
||||
const header = `# K8s StorageClass - 由 ConfTemplate 生成
|
||||
# 生成时间: ${new Date().toLocaleString('zh-CN')}
|
||||
# 部署命令: kubectl apply -f ${config.fileName || 'storageclass.yaml'}
|
||||
`
|
||||
|
||||
return header + '\n' + toYaml(storageClass)
|
||||
}
|
||||
Reference in New Issue
Block a user