feat(k8s): M14 Namespace 管理看板(资源配额 + 命名空间 CRUD)

- 后端: ListNamespacesDetailed/CreateNamespace/DeleteNamespace/ListResourceQuotas/ListLimitRanges
- 后端: GetNamespacesDetail handler (6 API calls aggregate, not N×3)
- 后端: CreateNamespaceHandler + DeleteNamespaceHandler (system NS protection)
- 前端: NamespaceManagementPage.tsx (~280行) — 表格+概览卡片+创建/删除弹框
- 路由: GET/POST/DELETE /:clusterCode/namespaces + GET /namespace/:ns/detail
- 验证: 9/9 通过 (urllib), TS 零错误, Vite 构建成功
This commit is contained in:
attacker committed 2026-06-24 23:46:52 +08:00
1 parent c7b61968bc
commit 8240a88a84
33 files changed
+2029 -71

No files matched your search

+232
View File
@@ -1023,6 +1023,134 @@ func (c *Client) ListNamespaces() ([]string, error) {
return names, nil
}
// NamespaceItem holds full namespace metadata
type NamespaceItem struct {
Metadata ObjectMeta `json:"metadata"`
Spec NamespaceSpec `json:"spec"`
Status NamespaceStatus `json:"status"`
}
// NamespaceSpec holds namespace spec
type NamespaceSpec struct {
Finalizers []string `json:"finalizers"`
}
// NamespaceStatus holds namespace phase
type NamespaceStatus struct {
Phase string `json:"phase"`
}
// NamespaceList wraps a list of NamespaceItem
type NamespaceList struct {
Items []NamespaceItem `json:"items"`
}
// ListNamespacesDetailed returns all namespaces with full metadata
func (c *Client) ListNamespacesDetailed() (*NamespaceList, error) {
var result NamespaceList
if err := c.get("/api/v1/namespaces", &result); err != nil {
return nil, err
}
return &result, nil
}
// GetNamespace returns a single namespace by name
func (c *Client) GetNamespace(name string) (*NamespaceItem, error) {
var result NamespaceItem
path := fmt.Sprintf("/api/v1/namespaces/%s", name)
if err := c.get(path, &result); err != nil {
return nil, err
}
return &result, nil
}
// CreateNamespace creates a new namespace
func (c *Client) CreateNamespace(name string, labels map[string]string) (*NamespaceItem, error) {
body := map[string]interface{}{
"apiVersion": "v1",
"kind": "Namespace",
"metadata": map[string]interface{}{
"name": name,
"labels": labels,
},
}
var result NamespaceItem
if err := c.post("/api/v1/namespaces", body, &result); err != nil {
return nil, err
}
return &result, nil
}
// DeleteNamespace deletes a namespace by name
func (c *Client) DeleteNamespace(name string) error {
path := fmt.Sprintf("/api/v1/namespaces/%s", name)
return c.delete(path)
}
// ResourceQuotaItem represents a K8s ResourceQuota
type ResourceQuotaItem struct {
Metadata ObjectMeta `json:"metadata"`
Spec ResourceQuotaSpec `json:"spec"`
Status ResourceQuotaStatus `json:"status"`
}
// ResourceQuotaSpec holds hard limits
type ResourceQuotaSpec struct {
Hard map[string]string `json:"hard"`
Scopes []string `json:"scopes,omitempty"`
}
// ResourceQuotaStatus holds used/hard counts
type ResourceQuotaStatus struct {
Hard map[string]string `json:"hard"`
Used map[string]string `json:"used"`
}
// ListResourceQuotas returns ResourceQuotas in a namespace, or all namespaces if ns is empty
func (c *Client) ListResourceQuotas(namespace string) ([]ResourceQuotaItem, error) {
var result struct {
Items []ResourceQuotaItem `json:"items"`
}
var path string
if namespace == "" {
path = "/api/v1/resourcequotas"
} else {
path = fmt.Sprintf("/api/v1/namespaces/%s/resourcequotas", namespace)
}
if err := c.get(path, &result); err != nil {
return nil, err
}
return result.Items, nil
}
// LimitRangeItem represents a K8s LimitRange
type LimitRangeItem struct {
Metadata ObjectMeta `json:"metadata"`
Spec LimitRangeSpec `json:"spec"`
}
// LimitRangeSpec holds limit items
type LimitRangeSpec struct {
Limits []map[string]interface{} `json:"limits"`
}
// ListLimitRanges returns LimitRanges in a namespace, or all namespaces if ns is empty
func (c *Client) ListLimitRanges(namespace string) ([]LimitRangeItem, error) {
var result struct {
Items []LimitRangeItem `json:"items"`
}
var path string
if namespace == "" {
path = "/api/v1/limitranges"
} else {
path = fmt.Sprintf("/api/v1/namespaces/%s/limitranges", namespace)
}
if err := c.get(path, &result); err != nil {
return nil, err
}
return result.Items, nil
}
// --- ConfigMap ---
// ConfigMapList response
@@ -1366,6 +1494,110 @@ func (c *Client) ListEvents(namespace string) (*EventList, error) {
return &result, nil
}
// ============================================================================
// RBAC — ServiceAccount / Role / RoleBinding
// ============================================================================
// --- ServiceAccount ---
type ServiceAccountList struct {
Items []ServiceAccountItem `json:"items"`
}
type ServiceAccountItem struct {
Metadata ObjectMeta `json:"metadata"`
Spec struct {
ImagePullSecrets []struct {
Name string `json:"name"`
} `json:"imagePullSecrets"`
Secrets []struct {
Name string `json:"name"`
} `json:"secrets"`
} `json:"secrets"`
}
type ServiceAccountRaw struct {
Metadata ObjectMeta `json:"metadata"`
Secrets []struct {
Name string `json:"name"`
} `json:"secrets"`
ImagePullSecrets []struct {
Name string `json:"name"`
} `json:"imagePullSecrets"`
}
// ListServiceAccounts fetches ServiceAccounts in a namespace
func (c *Client) ListServiceAccounts(namespace string) ([]ServiceAccountRaw, error) {
var result struct {
Items []ServiceAccountRaw `json:"items"`
}
path := fmt.Sprintf("/api/v1/namespaces/%s/serviceaccounts", namespace)
if err := c.get(path, &result); err != nil {
return nil, err
}
return result.Items, nil
}
// --- Role ---
type RoleList struct {
Items []RoleItem `json:"items"`
}
type RoleItem struct {
Metadata ObjectMeta `json:"metadata"`
Rules []PolicyRule `json:"rules"`
}
type PolicyRule struct {
Verbs []string `json:"verbs"`
Resources []string `json:"resources"`
APIGroups []string `json:"apiGroups"`
}
// ListRoles fetches Roles in a namespace
func (c *Client) ListRoles(namespace string) (*RoleList, error) {
var result RoleList
path := fmt.Sprintf("/apis/rbac.authorization.k8s.io/v1/namespaces/%s/roles", namespace)
if err := c.get(path, &result); err != nil {
return nil, err
}
return &result, nil
}
// --- RoleBinding ---
type RoleBindingList struct {
Items []RoleBindingItem `json:"items"`
}
type RoleBindingItem struct {
Metadata ObjectMeta `json:"metadata"`
RoleRef RoleRef `json:"roleRef"`
Subjects []Subject `json:"subjects"`
}
type RoleRef struct {
Kind string `json:"kind"`
Name string `json:"name"`
}
type Subject struct {
Kind string `json:"kind"`
Name string `json:"name"`
Namespace string `json:"namespace"`
}
// ListRoleBindings fetches RoleBindings in a namespace
func (c *Client) ListRoleBindings(namespace string) (*RoleBindingList, error) {
var result RoleBindingList
path := fmt.Sprintf("/apis/rbac.authorization.k8s.io/v1/namespaces/%s/rolebindings", namespace)
if err := c.get(path, &result); err != nil {
return nil, err
}
return &result, nil
}
// ============================================================================
// YAML Apply (Dynamic resource apply)
// ============================================================================