fix: K8s 集群添加缺少 code 必填字段
- KubernetesClusterDialog: 新增 code 表单字段 - 名称变更自动生成 code(小写+连字符) - handleSubmit 增加 code 校验 + toast 提示 - payload 增加 code 字段(后端 binding:required)
This commit is contained in:
1 parent
f24fe43ac0
commit
ce438c9f05
18 files changed
+3941
-144
No files matched your search
@@ -1316,3 +1316,171 @@ func (c *Client) resourceAPIPath(apiVersion, kind, namespace, name string) (stri
|
||||
}
|
||||
return "/api/v1/namespaces/" + ns + "/" + resource + "/" + name, true
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ReplicaSet — 用于 Deployment 回滚 / 历史版本查询 // Logan.Li
|
||||
// ============================================================================
|
||||
|
||||
// ReplicaSetList response
|
||||
type ReplicaSetList struct {
|
||||
Items []ReplicaSetItem `json:"items"`
|
||||
}
|
||||
|
||||
// ReplicaSetItem 表示一个 ReplicaSet(含完整 spec/status/metadata,用于回滚取 template)
|
||||
type ReplicaSetItem struct {
|
||||
Metadata ObjectMeta `json:"metadata"`
|
||||
Spec ReplicaSetSpec `json:"spec"`
|
||||
Status ReplicaSetStatus `json:"status"`
|
||||
}
|
||||
|
||||
// ReplicaSetSpec 与 DeploymentSpec 结构一致,关键是 template
|
||||
type ReplicaSetSpec struct {
|
||||
Replicas int `json:"replicas"`
|
||||
Template ReplicaSetTemplate `json:"template"`
|
||||
}
|
||||
|
||||
// ReplicaSetTemplate pod 模板
|
||||
type ReplicaSetTemplate struct {
|
||||
Metadata ObjectMeta `json:"metadata"`
|
||||
Spec PodSpec `json:"spec"`
|
||||
}
|
||||
|
||||
// ReplicaSetStatus 副本状态
|
||||
type ReplicaSetStatus struct {
|
||||
Replicas int `json:"replicas"`
|
||||
AvailableReplicas int `json:"availableReplicas"`
|
||||
ReadyReplicas int `json:"readyReplicas"`
|
||||
}
|
||||
|
||||
// OwnerReference 资源归属引用
|
||||
type OwnerReference struct {
|
||||
Kind string `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// ReplicaSetOwnerMeta 在 ListReplicaSets 时额外携带 ownerReferences
|
||||
type ReplicaSetOwnerMeta struct {
|
||||
Metadata struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
Annotations map[string]string `json:"annotations"`
|
||||
CreationTimestamp string `json:"creationTimestamp"`
|
||||
OwnerReferences []OwnerReference `json:"ownerReferences"`
|
||||
} `json:"metadata"`
|
||||
Spec struct {
|
||||
Replicas int `json:"replicas"`
|
||||
Template ReplicaSetTemplate `json:"template"`
|
||||
} `json:"spec"`
|
||||
Status ReplicaSetStatus `json:"status"`
|
||||
}
|
||||
|
||||
// ListReplicaSetsRaw 列出 namespace 下的 ReplicaSet(含 ownerReferences 原始字段)
|
||||
func (c *Client) ListReplicaSetsRaw(namespace string) ([]ReplicaSetOwnerMeta, error) {
|
||||
var result struct {
|
||||
Items []ReplicaSetOwnerMeta `json:"items"`
|
||||
}
|
||||
path := fmt.Sprintf("/apis/apps/v1/namespaces/%s/replicasets", namespace)
|
||||
if err := c.get(path, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.Items, nil
|
||||
}
|
||||
|
||||
// GetReplicaSet 获取单个 ReplicaSet 的完整 JSON(map 形式,便于取 template 直接 patch)
|
||||
func (c *Client) GetReplicaSet(namespace, name string) (map[string]interface{}, error) {
|
||||
path := fmt.Sprintf("/apis/apps/v1/namespaces/%s/replicasets/%s", namespace, name)
|
||||
var result map[string]interface{}
|
||||
if err := c.get(path, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PatchDeploymentSpecTemplate 用旧 ReplicaSet 的 template 回滚 Deployment
|
||||
// 通过 merge-patch 将 deployment.spec.template 整体替换为 rsTemplate
|
||||
func (c *Client) PatchDeploymentSpecTemplate(namespace, deploymentName string, rsTemplate map[string]interface{}) error {
|
||||
body := map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"template": rsTemplate,
|
||||
},
|
||||
}
|
||||
path := fmt.Sprintf("/apis/apps/v1/namespaces/%s/deployments/%s", namespace, deploymentName)
|
||||
return c.patch(path, body, nil)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Pod 删除 / 高级日志参数 // Logan.Li
|
||||
// ============================================================================
|
||||
|
||||
// DeletePod 删除指定 namespace 下的 Pod
|
||||
func (c *Client) DeletePod(namespace, name string) error {
|
||||
path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s", namespace, name)
|
||||
return c.delete(path)
|
||||
}
|
||||
|
||||
// LogOptions 日志查询高级参数(container/sinceTime/previous/tailLines)
|
||||
type LogOptions struct {
|
||||
Container string
|
||||
SinceTime string // RFC3339 格式,仅返回此时间之后的日志
|
||||
Previous bool // 返回已终止容器(previous instance)的日志
|
||||
TailLines int // 末尾 N 行,0 表示不限制
|
||||
}
|
||||
|
||||
// buildLogPath 构造 K8s pod log API 的完整查询路径
|
||||
func (c *Client) buildLogPath(namespace, podName string, opts LogOptions, follow bool) string {
|
||||
q := []string{}
|
||||
if opts.Container != "" {
|
||||
q = append(q, "container="+opts.Container)
|
||||
}
|
||||
if opts.TailLines > 0 {
|
||||
q = append(q, fmt.Sprintf("tailLines=%d", opts.TailLines))
|
||||
}
|
||||
if opts.SinceTime != "" {
|
||||
q = append(q, "sinceTime="+opts.SinceTime)
|
||||
}
|
||||
if opts.Previous {
|
||||
q = append(q, "previous=true")
|
||||
}
|
||||
if follow {
|
||||
q = append(q, "follow=true")
|
||||
}
|
||||
path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/log", namespace, podName)
|
||||
if len(q) > 0 {
|
||||
path += "?" + strings.Join(q, "&")
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// GetPodLogsWithOptions 支持高级参数的日志查询(非流式)
|
||||
func (c *Client) GetPodLogsWithOptions(namespace, podName string, opts LogOptions) (string, error) {
|
||||
url := c.BaseURL + c.buildLogPath(namespace, podName, opts, false)
|
||||
resp, err := c.HTTPClient.Get(url)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("GET logs: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
return "", fmt.Errorf("K8s API %d: %s", resp.StatusCode, string(body[:min(len(body), 300)]))
|
||||
}
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
// GetPodLogsStreamWithOptions 支持高级参数的日志流式查询(follow=true)
|
||||
func (c *Client) GetPodLogsStreamWithOptions(namespace, podName string, opts LogOptions) (io.ReadCloser, error) {
|
||||
url := c.BaseURL + c.buildLogPath(namespace, podName, opts, true)
|
||||
resp, err := c.HTTPClient.Get(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GET logs stream: %w", err)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
return nil, fmt.Errorf("K8s API %d: %s", resp.StatusCode, string(body[:min(len(body), 300)]))
|
||||
}
|
||||
return resp.Body, nil
|
||||
}
|
||||
Reference in new issue
Block a user