add goc list

This commit is contained in:
tongjingran 2021-07-12 16:39:16 +08:00
parent 2459e2b8bb
commit 17a55bb41c
5 changed files with 213 additions and 0 deletions

27
cmd/list.go Normal file
View File

@ -0,0 +1,27 @@
package cmd
import (
"github.com/qiniu/goc/v2/pkg/client"
"github.com/qiniu/goc/v2/pkg/config"
"github.com/spf13/cobra"
)
var listCmd = &cobra.Command{
Use: "list",
Short: "Lists all the registered services",
Long: "Lists all the registered services",
Example: `
goc list [flags]
`,
Run: list,
}
func init() {
listCmd.Flags().StringVarP(&config.GocConfig.Host, "host", "", "127.0.0.1:7777", "specify the host of the goc server")
rootCmd.AddCommand(listCmd)
}
func list(cmd *cobra.Command, args []string) {
client.NewWorker("http://" + config.GocConfig.Host).ListAgents()
}

3
go.mod
View File

@ -9,8 +9,11 @@ require (
github.com/mattn/go-colorable v0.1.8 // indirect
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
github.com/tongjingran/copy v1.4.2
go.uber.org/zap v1.17.0
golang.org/x/mod v0.4.2

5
go.sum
View File

@ -853,6 +853,8 @@ github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-sqlite3 v0.0.0-20160514122348-38ee283dabf1/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@ -932,6 +934,8 @@ github.com/octago/sflags v0.2.0/go.mod h1:G0bjdxh4qPRycF74a2B8pU36iTp9QHGx0w0dFZ
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@ -1096,6 +1100,7 @@ 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=

118
pkg/client/client.go Normal file
View File

@ -0,0 +1,118 @@
package client
import (
"encoding/json"
"errors"
"fmt"
"github.com/olekukonko/tablewriter"
"github.com/qiniu/goc/v2/pkg/log"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
)
// Action provides methods to contact with the covered agent under test
type Action interface {
ListAgents()
}
const (
// CoverAgentsListAPI list all the registered agents
CoverAgentsListAPI = "/v2/rpcagents"
)
var (
ERROR_GOC_LIST = errors.New("goc list failed")
)
type client struct {
Host string
client *http.Client
}
// gocListAgents response of the list request
type gocListAgents struct {
Items []gocCoveredAgent `json:"items"`
}
// gocCoveredAgent represents a covered client
type gocCoveredAgent struct {
Id string `json:"id"`
RemoteIP string `json:"remoteip"`
Hostname string `json:"hostname"`
CmdLine string `json:"cmdline"`
Pid string `json:"pid"`
}
// NewWorker creates a worker to contact with host
func NewWorker(host string) Action {
_, err := url.ParseRequestURI(host)
if err != nil {
log.Fatalf("Parse url %s failed, err: %v", host, err)
}
return &client{
Host: host,
client: http.DefaultClient,
}
}
func (c *client) ListAgents() {
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)
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)
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})
for _, agent := range agents.Items {
table.Append([]string{agent.Id, agent.RemoteIP, agent.Hostname, agent.Pid, agent.CmdLine})
}
table.Render()
return
}
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 {
return nil, nil, err
}
if contentType != "" {
req.Header.Set("Content-Type", contentType)
}
res, err := c.client.Do(req)
if err != nil {
return nil, nil, err
}
defer res.Body.Close()
responseBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return res, nil, err
}
return res, responseBody, nil
}
func isNetworkError(err error) bool {
if err == io.EOF {
return true
}
_, ok := err.(net.Error)
return ok
}

60
pkg/client/client_test.go Normal file
View File

@ -0,0 +1,60 @@
package client
import (
"bytes"
"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")
}
stdout := os.Stdout
os.Stdout = w
defer func() {
os.Stdout = stdout
}()
f()
w.Close()
var buf bytes.Buffer
io.Copy(&buf, r)
return buf.String()
}
func TestClientListAgents(t *testing.T) {
mockAgents := `{"items": [{"id": "testID", "remoteip": "1.1.1.1", "hostname": "testHost", "cmdline": "testCmd", "pid": "0"}]}`
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(mockAgents))
}))
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)
}
func TestClientDo(t *testing.T) {
c := &client{
client: http.DefaultClient,
}
_, _, err := c.do(" ", "http://127.0.0.1:7777", "", nil) // a invalid method
assert.Contains(t, err.Error(), "invalid method")
}