Skip to main content
Glama
pod_forward.go7.38 kB
package pod import ( "fmt" "strconv" "sync" "github.com/gin-gonic/gin" "github.com/weibaohui/k8m/pkg/comm/utils" "github.com/weibaohui/k8m/pkg/comm/utils/amis" "github.com/weibaohui/kom/kom" v1 "k8s.io/api/core/v1" ) type PortController struct{} func RegisterPortRoutes(api *gin.RouterGroup) { ctrl := &PortController{} api.POST("/pod/port_forward/ns/:ns/name/:name/container/:container_name/pod_port/:pod_port/local_port/:local_port/start", ctrl.StartPortForward) api.POST("/pod/port_forward/ns/:ns/name/:name/container/:container_name/pod_port/:pod_port/stop", ctrl.StopPortForward) api.GET("/pod/port_forward/ns/:ns/name/:name/port/list", ctrl.PortForwardList) } // PortInfo 结构体用于描述端口转发信息 // 包含容器名、端口名、协议、端口号、本地端口、转发状态等 type PortInfo struct { Cluster string `json:"cluster"` Namespace string `json:"namespace"` // Pod 命名空间 Name string `json:"name"` // pod名称 ContainerName string `json:"container_name"` PortName string `json:"port_name"` // 端口名称 Protocol string `json:"protocol"` // TCP/UDP/STCP LocalPort string `json:"local_port"` // 本地端口,转发端口 PodPort string `json:"pod_port"` // pod 端口 Status string `json:"status"` // running/failed/stopped StopCh chan struct{} `json:"-"` } // portForwardTable 用于维护所有端口转发的状态 var portForwardTable = make(map[string]*PortInfo) // key: cluster/ns/pod/port var portForwardTableMutex sync.RWMutex // @Summary 开始端口转发 // @Security BearerAuth // @Param cluster query string true "集群名称" // @Param ns path string true "命名空间" // @Param name path string true "Pod名称" // @Param container_name path string true "容器名称" // @Param pod_port path string true "Pod端口" // @Param local_port path string true "本地端口" // @Success 200 {object} string // @Router /k8s/cluster/{cluster}/pod/port_forward/ns/{ns}/name/{name}/container/{container_name}/pod_port/{pod_port}/local_port/{local_port}/start [post] func (pc *PortController) StartPortForward(c *gin.Context) { ctx := amis.GetContextWithUser(c) name := c.Param("name") ns := c.Param("ns") localPort := c.Param("local_port") podPort := c.Param("pod_port") containerName := c.Param("container_name") // 验证podPort是否为有效的整数 if _, err := strconv.Atoi(podPort); err != nil { amis.WriteJsonError(c, fmt.Errorf("无效的容器组端口号: %s", podPort)) return } selectedCluster, err := amis.GetSelectedCluster(c) if err != nil { amis.WriteJsonError(c, err) return } // 前端是界面点选而来 // // 检查pod是否存在 // var pod v1.Pod // err = kom.Cluster(selectedCluster).WithContext(ctx). // Resource(&v1.Pod{}). // Namespace(ns). // Name(name). // Get(&pod).Error // // if err != nil { // amis.WriteJsonError(c, err) // return // } stopCh := make(chan struct{}) key := getMapKey(selectedCluster, ns, name, containerName, podPort) if localPort == "" { localPort = getRandomPort() } go func() { portForwardTableMutex.Lock() portForwardTable[key] = &PortInfo{ Cluster: selectedCluster, Namespace: ns, Name: name, ContainerName: containerName, // 可后续补充 LocalPort: localPort, PodPort: podPort, Status: "running", StopCh: stopCh, } portForwardTableMutex.Unlock() err = kom.Cluster(selectedCluster).WithContext(ctx). Resource(&v1.Pod{}). Namespace(ns). Name(name). Ctl().Pod(). ContainerName(containerName). PortForward(localPort, podPort, stopCh).Error if err != nil { portForwardTableMutex.Lock() if pf, ok := portForwardTable[key]; ok { pf.Status = "failed" } portForwardTableMutex.Unlock() } }() amis.WriteJsonOK(c) } // @Summary 停止端口转发 // @Security BearerAuth // @Param cluster query string true "集群名称" // @Param ns path string true "命名空间" // @Param name path string true "Pod名称" // @Param container_name path string true "容器名称" // @Param pod_port path string true "Pod端口" // @Success 200 {object} string // @Router /k8s/cluster/{cluster}/pod/port_forward/ns/{ns}/name/{name}/container/{container_name}/pod_port/{pod_port}/stop [post] func (pc *PortController) StopPortForward(c *gin.Context) { name := c.Param("name") ns := c.Param("ns") containerName := c.Param("container_name") podPort := c.Param("pod_port") selectedCluster, err := amis.GetSelectedCluster(c) if err != nil { amis.WriteJsonError(c, err) return } key := getMapKey(selectedCluster, ns, name, containerName, podPort) portForwardTableMutex.Lock() if pf, ok := portForwardTable[key]; ok { pf.StopCh <- struct{}{} pf.Status = "stopped" pf.LocalPort = "" } portForwardTableMutex.Unlock() amis.WriteJsonOK(c) } // @Summary 列出端口转发信息 // @Security BearerAuth // @Param cluster query string true "集群名称" // @Param ns path string true "命名空间" // @Param name path string true "Pod名称" // @Success 200 {object} string // @Router /k8s/cluster/{cluster}/pod/port_forward/ns/{ns}/name/{name}/port/list [get] func (pc *PortController) PortForwardList(c *gin.Context) { ctx := amis.GetContextWithUser(c) name := c.Param("name") ns := c.Param("ns") selectedCluster, err := amis.GetSelectedCluster(c) if err != nil { amis.WriteJsonError(c, err) return } var containerPorts []*PortInfo var pod *v1.Pod err = kom.Cluster(selectedCluster).WithContext(ctx). Resource(&v1.Pod{}). Namespace(ns). Name(name). Get(&pod).Error if err != nil { amis.WriteJsonError(c, err) return } for _, container := range pod.Spec.Containers { for _, port := range container.Ports { key := getMapKey(selectedCluster, ns, name, container.Name, fmt.Sprintf("%d", port.ContainerPort)) status := "" localPort := "" portForwardTableMutex.RLock() if pf, ok := portForwardTable[key]; ok { status = pf.Status localPort = pf.LocalPort } portForwardTableMutex.RUnlock() portInfo := &PortInfo{ ContainerName: container.Name, PortName: port.Name, Protocol: string(port.Protocol), LocalPort: localPort, PodPort: fmt.Sprintf("%d", port.ContainerPort), Status: status, } containerPorts = append(containerPorts, portInfo) } } if len(containerPorts) > 0 { amis.WriteJsonData(c, containerPorts) return } amis.WriteJsonError(c, fmt.Errorf("无端口数据")) } func getMapKey(selectedCluster, ns, name, container, podPort string) string { key := fmt.Sprintf("%s/%s/%s/%s/%s", selectedCluster, ns, name, container, podPort) return key } func getRandomPort() string { // 随机取一个端口 // 如果重复了,就再取一个,直到不重复 for { // TODO 范围 做成一个配置 port := utils.RandInt(40000, 49999) portStr := fmt.Sprintf("%d", port) // 检查端口是否已被使用 portForwardTableMutex.RLock() isUsed := false for _, portInfo := range portForwardTable { if portInfo.LocalPort == portStr { isUsed = true break } } portForwardTableMutex.RUnlock() // 如果端口未被使用,则返回该端口 if !isUsed { return portStr } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/weibaohui/k8m'

If you have feedback or need assistance with the MCP directory API, please join our Discord server