add --wide to show service detail

This commit is contained in:
tongjingran 2021-07-12 20:09:45 +08:00
parent 17a55bb41c
commit b8b7305fc8
5 changed files with 93 additions and 34 deletions

View File

@ -17,11 +17,14 @@ goc list [flags]
Run: list,
}
var listWide bool
func init() {
listCmd.Flags().StringVarP(&config.GocConfig.Host, "host", "", "127.0.0.1:7777", "specify the host of the goc server")
listCmd.Flags().StringVar(&config.GocConfig.Host, "host", "127.0.0.1:7777", "specify the host of the goc server")
listCmd.Flags().BoolVar(&listWide, "wide", false, "List all services with more information (such as pid)")
rootCmd.AddCommand(listCmd)
}
func list(cmd *cobra.Command, args []string) {
client.NewWorker("http://" + config.GocConfig.Host).ListAgents()
client.NewWorker("http://" + config.GocConfig.Host).ListAgents(listWide)
}

2
go.mod
View File

@ -10,7 +10,6 @@ require (
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
github.com/olekukonko/tablewriter v0.0.5
github.com/sirupsen/logrus v1.7.0
github.com/spf13/cobra v1.1.3
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
@ -18,6 +17,7 @@ require (
go.uber.org/zap v1.17.0
golang.org/x/mod v0.4.2
golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d
golang.org/x/tools v0.1.3
k8s.io/kubectl v0.21.2
k8s.io/test-infra v0.0.0-20210618100605-34aa2f2aa75b

1
go.sum
View File

@ -1100,7 +1100,6 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=

View File

@ -2,21 +2,22 @@ package client
import (
"encoding/json"
"errors"
"fmt"
"github.com/olekukonko/tablewriter"
"github.com/qiniu/goc/v2/pkg/log"
"golang.org/x/term"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"github.com/olekukonko/tablewriter"
"github.com/qiniu/goc/v2/pkg/log"
)
// Action provides methods to contact with the covered agent under test
type Action interface {
ListAgents()
ListAgents(bool)
}
const (
@ -24,10 +25,6 @@ const (
CoverAgentsListAPI = "/v2/rpcagents"
)
var (
ERROR_GOC_LIST = errors.New("goc list failed")
)
type client struct {
Host string
client *http.Client
@ -51,7 +48,7 @@ type gocCoveredAgent struct {
func NewWorker(host string) Action {
_, err := url.ParseRequestURI(host)
if err != nil {
log.Fatalf("Parse url %s failed, err: %v", host, err)
log.Fatalf("parse url %s failed, err: %v", host, err)
}
return &client{
Host: host,
@ -59,33 +56,64 @@ func NewWorker(host string) Action {
}
}
func (c *client) ListAgents() {
func (c *client) ListAgents(wide bool) {
u := fmt.Sprintf("%s%s", c.Host, CoverAgentsListAPI)
_, body, err := c.do("GET", u, "", nil)
if err != nil && isNetworkError(err) {
_, body, err = c.do("GET", u, "", nil)
}
if err != nil {
err = fmt.Errorf("%w: %v", ERROR_GOC_LIST, err)
err = fmt.Errorf("goc list failed: %v", err)
log.Fatalf(err.Error())
}
agents := gocListAgents{}
err = json.Unmarshal(body, &agents)
if err != nil {
err = fmt.Errorf("%w: json unmarshal failed: %v", ERROR_GOC_LIST, err)
err = fmt.Errorf("goc list failed: json unmarshal failed: %v", err)
log.Fatalf(err.Error())
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"ID", "RemoteIP", "Hostname", "Pid", "CMD"})
table.SetAutoFormatHeaders(false)
table.SetColumnAlignment([]int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER})
table.SetCenterSeparator("")
table.SetColumnSeparator("")
table.SetRowSeparator("")
table.SetHeaderLine(false)
table.SetBorder(false)
table.SetTablePadding(" ") // pad with 3 blank spaces
table.SetNoWhiteSpace(true)
table.SetReflowDuringAutoWrap(false)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
if wide {
table.SetHeader([]string{"ID", "REMOTEIP", "HOSTNAME", "PID", "CMD"})
table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
} else {
table.SetHeader([]string{"ID", "REMOTEIP", "CMD"})
table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
}
for _, agent := range agents.Items {
table.Append([]string{agent.Id, agent.RemoteIP, agent.Hostname, agent.Pid, agent.CmdLine})
if wide {
table.Append([]string{agent.Id, agent.RemoteIP, agent.Hostname, agent.Pid, agent.CmdLine})
} else {
preLen := len(agent.Id) + len(agent.RemoteIP) + 9
table.Append([]string{agent.Id, agent.RemoteIP, getSimpleCmdLine(preLen, agent.CmdLine)})
}
}
table.Render()
return
}
// getSimpleCmdLine
func getSimpleCmdLine(preLen int, cmdLine string) string {
pathLen := len(cmdLine)
width, _, err := term.GetSize(int(os.Stdin.Fd()))
if err != nil || width <= preLen+16 {
width = 16 + preLen // show at least 16 words of the command
}
if pathLen > width-preLen {
return cmdLine[:width-preLen]
}
return cmdLine
}
func (c *client) do(method, url, contentType string, body io.Reader) (*http.Response, []byte, error) {
req, err := http.NewRequest(method, url, body)
if err != nil {

View File

@ -2,21 +2,18 @@ package client
import (
"bytes"
"fmt"
"io"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)
func captureStdout(f func()) string {
r, w, err := os.Pipe()
if err != nil {
logrus.WithError(err).Fatal("os pipe fail")
}
r, w, _ := os.Pipe()
stdout := os.Stdout
os.Stdout = w
defer func() {
@ -33,7 +30,7 @@ func captureStdout(f func()) string {
}
func TestClientListAgents(t *testing.T) {
mockAgents := `{"items": [{"id": "testID", "remoteip": "1.1.1.1", "hostname": "testHost", "cmdline": "testCmd", "pid": "0"}]}`
mockAgents := `{"items": [{"id": "testID", "remoteip": "1.1.1.1", "hostname": "testHost", "cmdline": "./testCmd -f testArgs", "pid": "0"}]}`
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(mockAgents))
@ -41,14 +38,31 @@ func TestClientListAgents(t *testing.T) {
defer mockServer.Close()
c := NewWorker(mockServer.URL)
output := captureStdout(c.ListAgents)
expected := `+--------+----------+----------+-----+---------+
| ID | RemoteIP | Hostname | Pid | CMD |
+--------+----------+----------+-----+---------+
| testID | 1.1.1.1 | testHost | 0 | testCmd |
+--------+----------+----------+-----+---------+
`
assert.Equal(t, output, expected)
testCases := map[string]struct {
input bool
expected string
}{
"simple list": {
false,
`ID REMOTEIP CMD
testID 1.1.1.1 ./testCmd -f tes
`,
},
"wide list": {
true,
`ID REMOTEIP HOSTNAME PID CMD
testID 1.1.1.1 testHost 0 ./testCmd -f testArgs
`,
},
}
for name, tt := range testCases {
t.Run(name, func(t *testing.T) {
f := func() { c.ListAgents(tt.input) }
output := captureStdout(f)
fmt.Println(output)
assert.Equal(t, output, tt.expected)
})
}
}
func TestClientDo(t *testing.T) {
@ -58,3 +72,18 @@ func TestClientDo(t *testing.T) {
_, _, err := c.do(" ", "http://127.0.0.1:7777", "", nil) // a invalid method
assert.Contains(t, err.Error(), "invalid method")
}
func TestGetSimpleSvcName(t *testing.T) {
testCases := map[string]struct {
input string
expected string
}{
"short path": {"1234567890abc.go", "1234567890abc.go"},
"long path": {"1234567890abcdef.go", "1234567890abcdef"},
}
for name, tt := range testCases {
t.Run(name, func(t *testing.T) {
assert.Equal(t, getSimpleCmdLine(0, tt.input), tt.expected)
})
}
}