From 086cd94a464d00111e4a524e073657c5f49d5d89 Mon Sep 17 00:00:00 2001 From: montaguelhz <1443171175@qq.com> Date: Sat, 4 Nov 2023 20:09:41 +0800 Subject: [PATCH] [feat] add address for client status Signed-off-by: montaguelhz <1443171175@qq.com> --- internal/errno/errno.go | 1 + internal/task/step/container.go | 12 ++++ internal/task/task/common/client_status.go | 84 ++++++++++++++++++++++ internal/tui/client/status.go | 6 +- pkg/module/docker_cli.go | 7 ++ 5 files changed, 108 insertions(+), 2 deletions(-) diff --git a/internal/errno/errno.go b/internal/errno/errno.go index fd40bbc00..339794712 100644 --- a/internal/errno/errno.go +++ b/internal/errno/errno.go @@ -572,6 +572,7 @@ var ( ERR_INSPECT_CONTAINER_FAILED = EC(630012, "get container low-level information failed") ERR_GET_CONTAINER_LOGS_FAILED = EC(630013, "get container logs failed") ERR_UPDATE_CONTAINER_FAILED = EC(630014, "update container failed") + ERR_TOP_CONTAINER_FAILED = EC(630015, "top container failed") // 690: execuetr task (others) ERR_START_CRONTAB_IN_CONTAINER_FAILED = EC(690000, "start crontab in container failed") diff --git a/internal/task/step/container.go b/internal/task/step/container.go index 5dd0607d3..40a984787 100644 --- a/internal/task/step/container.go +++ b/internal/task/step/container.go @@ -162,6 +162,12 @@ type ( Success *bool module.ExecOptions } + + TopContainer struct { + ContainerId string + Out *string + module.ExecOptions + } ) func (s *EngineInfo) Execute(ctx *context.Context) error { @@ -337,3 +343,9 @@ func (s *UpdateContainer) Execute(ctx *context.Context) error { out, err := cli.Execute(s.ExecOptions) return PostHandle(s.Success, s.Out, out, err, errno.ERR_UPDATE_CONTAINER_FAILED.FD("(%s update ID)", s.ExecWithEngine)) } + +func (s *TopContainer) Execute(ctx *context.Context) error { + cli := ctx.Module().DockerCli().TopContainer(s.ContainerId) + out, err := cli.Execute(s.ExecOptions) + return PostHandle(nil, s.Out, out, err, errno.ERR_TOP_CONTAINER_FAILED.FD("(%s top ID)", s.ExecWithEngine)) +} diff --git a/internal/task/task/common/client_status.go b/internal/task/task/common/client_status.go index 4d4d9a581..20d6a3dd6 100644 --- a/internal/task/task/common/client_status.go +++ b/internal/task/task/common/client_status.go @@ -24,6 +24,8 @@ package common import ( "fmt" + "regexp" + "strings" "github.com/opencurve/curveadm/cli/cli" comm "github.com/opencurve/curveadm/internal/common" @@ -33,6 +35,7 @@ import ( "github.com/opencurve/curveadm/internal/task/task" tui "github.com/opencurve/curveadm/internal/tui/common" "github.com/opencurve/curveadm/internal/utils" + "github.com/opencurve/curveadm/pkg/module" ) type ( @@ -41,6 +44,13 @@ type ( containerId string status *string memStorage *utils.SafeMap + address *string + } + + step2GetAddress struct { + containerId string + address *string + execOptions module.ExecOptions } ClientStatus struct { @@ -49,6 +59,7 @@ type ( Kind string ContainerId string Status string + Address string AuxInfo string } ) @@ -69,6 +80,7 @@ func setClientStatus(memStorage *utils.SafeMap, id string, status ClientStatus) func (s *step2FormatClientStatus) Execute(ctx *context.Context) error { status := *s.status + address := *s.address if len(status) == 0 { // container losed status = comm.CLIENT_STATUS_LOSED } @@ -81,11 +93,76 @@ func (s *step2FormatClientStatus) Execute(ctx *context.Context) error { Kind: client.Kind, ContainerId: s.containerId, Status: status, + Address: address, AuxInfo: client.AuxInfo, }) return nil } +func (s *step2GetAddress) Execute(ctx *context.Context) error { + cmd := ctx.Module().DockerCli().TopContainer(s.containerId) + out, err := cmd.Execute(s.execOptions) + if err != nil { + return err + } + + lines := strings.Split(out, "\n") + var pid string + if len(lines) > 1 { + reg := regexp.MustCompile(`\s+`) + res := reg.Split(lines[1], -1) + if len(res) > 1 { + pid = res[1] + } + } + + if len(pid) == 0 { + return nil + } + + // execute "ss" command in container + cli := ctx.Module().Shell().SocketStatistics("") + cli.AddOption("--no-header") + cli.AddOption("--processes") + cli.AddOption("--listening") + command, err := cli.String() + if err != nil { + return err + } + + cmd = ctx.Module().DockerCli().ContainerExec(s.containerId, command) + out, err = cmd.Execute(s.execOptions) + if err != nil { + return err + } + + // handle output + lines = strings.Split(out, "\n") + for _, line := range lines { + address := s.extractAddress(line, pid) + if len(address) > 0 { + *s.address = address + return nil + } + } + + return nil +} + +// e.g: tcp LISTEN 0 128 10.246.159.123:2379 *:* users:(("etcd",pid=7,fd=5)) +// e.g: tcp LISTEN 0 128 *:2379 *:* users:(("etcd",pid=7,fd=5)) +func (s *step2GetAddress) extractAddress(line, pid string) string { + + regex, err := regexp.Compile(`^.* ((\d+\.\d+\.\d+\.\d+)|\*:\d+).*pid=` + pid + ".*$") + if err == nil { + mu := regex.FindStringSubmatch(line) + if len(mu) > 1 { + return mu[1] + } + } + return "" +} + func NewGetClientStatusTask(curveadm *cli.CurveAdm, v interface{}) (*task.Task, error) { client := v.(storage.Client) hc, err := curveadm.GetHost(client.Host) @@ -100,6 +177,7 @@ func NewGetClientStatusTask(curveadm *cli.CurveAdm, v interface{}) (*task.Task, // add step var status string + var address string t.AddStep(&step.ListContainers{ ShowAll: true, Format: `"{{.Status}}"`, @@ -107,11 +185,17 @@ func NewGetClientStatusTask(curveadm *cli.CurveAdm, v interface{}) (*task.Task, Out: &status, ExecOptions: curveadm.ExecOptions(), }) + t.AddStep(&step2GetAddress{ + containerId: containerId, + address: &address, + execOptions: curveadm.ExecOptions(), + }) t.AddStep(&step2FormatClientStatus{ client: client, containerId: containerId, status: &status, memStorage: curveadm.MemStorage(), + address: &address, }) return t, nil diff --git a/internal/tui/client/status.go b/internal/tui/client/status.go index b61ed0c31..d0ddc2225 100644 --- a/internal/tui/client/status.go +++ b/internal/tui/client/status.go @@ -39,7 +39,7 @@ func statusDecorate(status string) string { return status } -func sortStatues(statuses []task.ClientStatus) { +func sortStatuses(statuses []task.ClientStatus) { sort.Slice(statuses, func(i, j int) bool { s1, s2 := statuses[i], statuses[j] if s1.Kind == s2.Kind { @@ -59,6 +59,7 @@ func FormatStatus(statuses []task.ClientStatus, verbose bool) string { "Host", "Container Id", "Status", + "Address", "Aux Info", } first, second := tui.FormatTitle(title) @@ -66,7 +67,7 @@ func FormatStatus(statuses []task.ClientStatus, verbose bool) string { lines = append(lines, second) // status - sortStatues(statuses) + sortStatuses(statuses) for _, status := range statuses { lines = append(lines, []interface{}{ status.Id, @@ -74,6 +75,7 @@ func FormatStatus(statuses []task.ClientStatus, verbose bool) string { status.Host, tui.TrimContainerId(status.ContainerId), tui.DecorateMessage{Message: status.Status, Decorate: statusDecorate}, + status.Address, status.AuxInfo, }) } diff --git a/pkg/module/docker_cli.go b/pkg/module/docker_cli.go index 1668a2311..cf955d214 100644 --- a/pkg/module/docker_cli.go +++ b/pkg/module/docker_cli.go @@ -46,6 +46,7 @@ const ( TEMPLATE_INSPECT_CONTAINER = "{{.engine}} inspect {{.options}} {{.container}}" TEMPLATE_CONTAINER_LOGS = "{{.engine}} logs {{.options}} {{.container}}" TEMPLATE_UPDATE_CONTAINER = "{{.engine}} update {{.options}} {{.container}}" + TEMPLATE_TOP_CONTAINER = "{{.engine}} top {{.container}}" ) type DockerCli struct { @@ -168,3 +169,9 @@ func (cli *DockerCli) UpdateContainer(containerId string) *DockerCli { cli.data["container"] = containerId return cli } + +func (cli *DockerCli) TopContainer(containerId string) *DockerCli { + cli.tmpl = template.Must(template.New("TopContainer").Parse(TEMPLATE_TOP_CONTAINER)) + cli.data["container"] = containerId + return cli +}