mirror of
https://github.com/F1bonacc1/process-compose.git
synced 2024-09-11 11:35:26 +03:00
Get Process Ports
This commit is contained in:
parent
43ac3cc86f
commit
c51b8a7d39
4
go.mod
4
go.mod
@ -5,6 +5,7 @@ go 1.20
|
||||
require (
|
||||
github.com/InVisionApp/go-health/v2 v2.1.3
|
||||
github.com/adrg/xdg v0.4.0
|
||||
github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5
|
||||
github.com/f1bonacc1/glippy v0.0.0-20230203184233-82c6562cecd1
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/gdamore/tcell/v2 v2.6.0
|
||||
@ -16,10 +17,13 @@ require (
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/swaggo/swag v1.16.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
//github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5
|
||||
)
|
||||
|
||||
replace github.com/InVisionApp/go-health/v2 => github.com/f1bonacc1/go-health/v2 v2.1.3
|
||||
|
||||
replace github.com/cakturk/go-netstat => github.com/mololab/netstat v0.0.0-20221129160431-27c9226a45b1
|
||||
|
||||
require (
|
||||
github.com/InVisionApp/go-logger v1.0.1 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
|
6
go.sum
6
go.sum
@ -4,6 +4,8 @@ github.com/InVisionApp/go-logger v1.0.1 h1:WFL19PViM1mHUmUWfsv5zMo379KSWj2MRmBlz
|
||||
github.com/InVisionApp/go-logger v1.0.1/go.mod h1:+cGTDSn+P8105aZkeOfIhdd7vFO5X1afUHcjvanY0L8=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
|
||||
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
|
||||
@ -14,6 +16,8 @@ github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQ
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 h1:BjkPE3785EwPhhyuFkbINB+2a1xATwk8SNDWnJiD41g=
|
||||
github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5/go.mod h1:jtAfVaU/2cu1+wdSRPWE2c1N2qeAA3K4RH9pYgqwets=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
@ -128,6 +132,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mololab/netstat v0.0.0-20221129160431-27c9226a45b1 h1:3k8Fbl7pFlde2/847ox7l/Wqql9Ww644JsQoPaY3/1o=
|
||||
github.com/mololab/netstat v0.0.0-20221129160431-27c9226a45b1/go.mod h1:jtAfVaU/2cu1+wdSRPWE2c1N2qeAA3K4RH9pYgqwets=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
|
@ -84,36 +84,8 @@ processes:
|
||||
success_threshold: 1
|
||||
failure_threshold: 3
|
||||
|
||||
# nginx:
|
||||
# command: "docker run -d --rm -p80:80 --name nginx_test nginx"
|
||||
# # availability:
|
||||
# # restart: on_failure
|
||||
# is_daemon: true
|
||||
# shutdown:
|
||||
# command: "docker stop nginx_test"
|
||||
# signal: 15
|
||||
# timeout_seconds: 5
|
||||
# liveness_probe:
|
||||
# exec:
|
||||
# command: "[ $(docker inspect -f '{{.State.Running}}' nginx_test) = 'true' ]"
|
||||
# initial_delay_seconds: 5
|
||||
# period_seconds: 2
|
||||
# timeout_seconds: 5
|
||||
# success_threshold: 1
|
||||
# failure_threshold: 3
|
||||
# readiness_probe:
|
||||
# http_get:
|
||||
# host: 127.0.0.1
|
||||
# path: "/"
|
||||
# port: 80
|
||||
# initial_delay_seconds: 5
|
||||
# period_seconds: 10
|
||||
# timeout_seconds: 5
|
||||
# success_threshold: 1
|
||||
# failure_threshold: 3
|
||||
# availability:
|
||||
# restart: "always"
|
||||
# backoff_seconds: 2
|
||||
server:
|
||||
command: "python3 -m http.server 4040"
|
||||
|
||||
kcalc:
|
||||
command: "kcalc"
|
||||
|
@ -215,3 +215,23 @@ func (api *PcApi) GetHostName(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"name": name})
|
||||
}
|
||||
|
||||
// @Schemes
|
||||
// @Description Retrieves process open ports
|
||||
// @Tags Process
|
||||
// @Summary Get process ports
|
||||
// @Produce json
|
||||
// @Param name path string true "Process Name"
|
||||
// @Success 200 {object} object "Process Ports"
|
||||
// @Router /process/ports/{name} [get]
|
||||
func (api *PcApi) GetProcessPorts(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
|
||||
ports, err := api.project.GetProcessPorts(name)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ports)
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ func InitRoutes(useLogger bool, handler *PcApi) *gin.Engine {
|
||||
r.GET("/processes", handler.GetProcesses)
|
||||
r.GET("/process/:name", handler.GetProcess)
|
||||
r.GET("/process/info/:name", handler.GetProcessInfo)
|
||||
r.GET("/process/ports/:name", handler.GetProcessPorts)
|
||||
r.GET("/process/logs/:name/:endOffset/:limit", handler.GetProcessLogs)
|
||||
r.PATCH("/process/stop/:name", handler.StopProcess)
|
||||
r.POST("/process/start/:name", handler.StartProcess)
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cakturk/go-netstat/netstat"
|
||||
"github.com/f1bonacc1/process-compose/src/types"
|
||||
"io"
|
||||
"math/rand"
|
||||
@ -160,7 +161,7 @@ func (p *Process) getBackoff() time.Duration {
|
||||
|
||||
func (p *Process) getProcessEnvironment() []string {
|
||||
env := []string{
|
||||
"PC_PROC_NAME=" + p.getName(),
|
||||
"PC_PROC_NAME=" + p.procConf.Name,
|
||||
"PC_REPLICA_NUM=" + strconv.Itoa(p.procConf.ReplicaNum),
|
||||
}
|
||||
env = append(env, os.Environ()...)
|
||||
@ -528,3 +529,20 @@ func (p *Process) validateProcess() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Process) getOpenPorts(ports *types.ProcessPorts) error {
|
||||
socks, err := netstat.TCPSocks(func(s *netstat.SockTabEntry) bool {
|
||||
return s.State == netstat.Listen
|
||||
})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to get open ports for %s", p.getName())
|
||||
return err
|
||||
}
|
||||
for _, e := range socks {
|
||||
if e.Process != nil && e.Process.Pid == p.procState.Pid {
|
||||
log.Debug().Msgf("%s is listening on %d", p.getName(), e.LocalAddr.Port)
|
||||
ports.TcpPorts = append(ports.TcpPorts, e.LocalAddr.Port)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -25,4 +25,5 @@ type IProject interface {
|
||||
StartProcess(name string) error
|
||||
RestartProcess(name string) error
|
||||
ScaleProcess(name string, scale int) error
|
||||
GetProcessPorts(name string) (*types.ProcessPorts, error)
|
||||
}
|
||||
|
@ -260,6 +260,24 @@ func (p *ProjectRunner) GetProcessInfo(name string) (*types.ProcessConfig, error
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProjectRunner) GetProcessPorts(name string) (*types.ProcessPorts, error) {
|
||||
proc := p.getRunningProcess(name)
|
||||
if proc == nil {
|
||||
return nil, fmt.Errorf("can't get ports: process %s is not running", name)
|
||||
}
|
||||
|
||||
ports := &types.ProcessPorts{
|
||||
Name: name,
|
||||
TcpPorts: make([]uint16, 0),
|
||||
UdpPorts: make([]uint16, 0),
|
||||
}
|
||||
err := proc.getOpenPorts(ports)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ports, nil
|
||||
}
|
||||
|
||||
func (p *ProjectRunner) ShutDownProject() {
|
||||
p.runProcMutex.Lock()
|
||||
defer p.runProcMutex.Unlock()
|
||||
|
@ -72,8 +72,11 @@ func (p *PcClient) GetLexicographicProcessNames() ([]string, error) {
|
||||
}
|
||||
|
||||
func (p *PcClient) GetProcessInfo(name string) (*types.ProcessConfig, error) {
|
||||
config, err := GetProcessInfo(p.address, p.port, name)
|
||||
return config, err
|
||||
return GetProcessInfo(p.address, p.port, name)
|
||||
}
|
||||
|
||||
func (p *PcClient) GetProcessPorts(name string) (*types.ProcessPorts, error) {
|
||||
return GetProcessPorts(p.address, p.port, name)
|
||||
}
|
||||
|
||||
func (p *PcClient) GetProcessState(name string) (*types.ProcessState, error) {
|
||||
|
@ -65,7 +65,6 @@ func GetProcessInfo(address string, port int, name string) (*types.ProcessConfig
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
//Create a variable of the same type as our model
|
||||
var sResp types.ProcessConfig
|
||||
|
||||
//Decode the data
|
||||
@ -76,3 +75,21 @@ func GetProcessInfo(address string, port int, name string) (*types.ProcessConfig
|
||||
|
||||
return &sResp, nil
|
||||
}
|
||||
|
||||
func GetProcessPorts(address string, port int, name string) (*types.ProcessPorts, error) {
|
||||
url := fmt.Sprintf("http://%s:%d/process/ports/%s", address, port, name)
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var sResp types.ProcessPorts
|
||||
|
||||
//Decode the data
|
||||
if err := json.NewDecoder(resp.Body).Decode(&sResp); err != nil {
|
||||
log.Err(err).Msgf("what I got: %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &sResp, nil
|
||||
}
|
||||
|
30
src/cmd/ports.go
Normal file
30
src/cmd/ports.go
Normal file
@ -0,0 +1,30 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/f1bonacc1/process-compose/src/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// portsCmd represents the ports command
|
||||
var portsCmd = &cobra.Command{
|
||||
Use: "ports [PROCESS]",
|
||||
Short: "Get the ports that a process is listening on",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
name := args[0]
|
||||
ports, err := client.GetProcessPorts(pcAddress, port, name)
|
||||
if err != nil {
|
||||
logFatal(err, "failed to get process %s ports", name)
|
||||
return
|
||||
}
|
||||
log.Info().Msgf("Process %s TCP ports: %v", name, ports.TcpPorts)
|
||||
fmt.Printf("Process %s TCP ports: %v\n", name, ports.TcpPorts)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
processCmd.AddCommand(portsCmd)
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/f1bonacc1/process-compose/src/api"
|
||||
"github.com/f1bonacc1/process-compose/src/loader"
|
||||
"github.com/rs/zerolog/log"
|
||||
@ -79,3 +80,10 @@ func getConfigDefault() []string {
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func logFatal(err error, format string, args ...interface{}) {
|
||||
fmt.Printf(format, args...)
|
||||
fmt.Printf(": %v\n", err)
|
||||
log.Err(err).Msgf(format, args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -122,6 +122,35 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/process/ports/{name}": {
|
||||
"get": {
|
||||
"description": "Retrieves process open ports",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Process"
|
||||
],
|
||||
"summary": "Get process ports",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Process Name",
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Process Ports",
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/process/restart/{name}": {
|
||||
"post": {
|
||||
"description": "Restarts the process",
|
||||
|
@ -110,6 +110,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/process/ports/{name}": {
|
||||
"get": {
|
||||
"description": "Retrieves process open ports",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Process"
|
||||
],
|
||||
"summary": "Get process ports",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Process Name",
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Process Ports",
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/process/restart/{name}": {
|
||||
"post": {
|
||||
"description": "Restarts the process",
|
||||
|
@ -90,6 +90,25 @@ paths:
|
||||
summary: Get process logs
|
||||
tags:
|
||||
- Process
|
||||
/process/ports/{name}:
|
||||
get:
|
||||
description: Retrieves process open ports
|
||||
parameters:
|
||||
- description: Process Name
|
||||
in: path
|
||||
name: name
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Process Ports
|
||||
schema:
|
||||
type: object
|
||||
summary: Get process ports
|
||||
tags:
|
||||
- Process
|
||||
/process/restart/{name}:
|
||||
post:
|
||||
description: Restarts the process
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (pv *pcView) createProcInfoForm(info *types.ProcessConfig) *tview.Form {
|
||||
func (pv *pcView) createProcInfoForm(info *types.ProcessConfig, ports *types.ProcessPorts) *tview.Form {
|
||||
f := tview.NewForm()
|
||||
f.SetCancelFunc(func() {
|
||||
pv.pages.RemovePage(PageDialog)
|
||||
@ -23,8 +23,11 @@ func (pv *pcView) createProcInfoForm(info *types.ProcessConfig) *tview.Form {
|
||||
addStringIfNotEmpty("Working Directory:", info.WorkingDir, f)
|
||||
addStringIfNotEmpty("Log Location:", info.LogLocation, f)
|
||||
f.AddInputField("Replica:", fmt.Sprintf("%d/%d", info.ReplicaNum+1, info.Replicas), 0, nil, nil)
|
||||
addSliceIfNotEmpty("Environment:", info.Environment, f)
|
||||
addSliceIfNotEmpty("Depends On:", mapKeysToSlice(info.DependsOn), f)
|
||||
addDropDownIfNotEmpty("Environment:", info.Environment, f)
|
||||
addCSVIfNotEmpty("Depends On:", mapKeysToSlice(info.DependsOn), f)
|
||||
if ports != nil {
|
||||
addCSVIfNotEmpty("TCP Ports:", ports.TcpPorts, f)
|
||||
}
|
||||
f.AddCheckbox("Is Disabled:", info.Disabled, nil)
|
||||
f.AddCheckbox("Is Daemon:", info.IsDaemon, nil)
|
||||
f.AddButton("Close", func() {
|
||||
@ -40,12 +43,19 @@ func addStringIfNotEmpty(label, value string, f *tview.Form) {
|
||||
}
|
||||
}
|
||||
|
||||
func addSliceIfNotEmpty(label string, value []string, f *tview.Form) {
|
||||
func addDropDownIfNotEmpty(label string, value []string, f *tview.Form) {
|
||||
if len(value) > 0 {
|
||||
f.AddDropDown(label, value, 0, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func addCSVIfNotEmpty[K comparable](label string, value []K, f *tview.Form) {
|
||||
if len(value) > 0 {
|
||||
csvPorts := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(value)), ":"), "[]")
|
||||
f.AddInputField(label, csvPorts, 0, nil, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// mapKeysToSlice extract keys of map as slice,
|
||||
func mapKeysToSlice[K comparable, V any](m map[K]V) []K {
|
||||
keys := make([]K, len(m))
|
||||
|
@ -246,7 +246,8 @@ func (pv *pcView) showInfo() {
|
||||
pv.showError(err.Error())
|
||||
return
|
||||
}
|
||||
form := pv.createProcInfoForm(info)
|
||||
ports, _ := pv.project.GetProcessPorts(name)
|
||||
form := pv.createProcInfoForm(info, ports)
|
||||
pv.showDialog(form, 0, 0)
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,12 @@ type ProcessState struct {
|
||||
IsRunning bool
|
||||
}
|
||||
|
||||
type ProcessPorts struct {
|
||||
Name string `json:"name"`
|
||||
TcpPorts []uint16 `json:"tcp_ports"`
|
||||
UdpPorts []uint16 `json:"udp_ports"`
|
||||
}
|
||||
|
||||
type ProcessesState struct {
|
||||
States []ProcessState `json:"data"`
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user