goc/pkg/client/client.go

212 lines
5.4 KiB
Go
Raw Normal View History

2021-09-02 09:48:11 +00:00
/*
Copyright 2021 Qiniu Cloud (qiniu.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
2021-07-12 08:39:16 +00:00
package client
import (
2024-11-19 03:32:10 +00:00
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"golang.org/x/term"
"github.com/ar0c/goc/v2/pkg/log"
"github.com/olekukonko/tablewriter"
2021-07-12 08:39:16 +00:00
)
// Action provides methods to contact with the covered agent under test
type Action interface {
2024-11-19 03:32:10 +00:00
ListAgents(bool)
Profile(string)
2021-07-12 08:39:16 +00:00
}
const (
2024-11-19 03:32:10 +00:00
// CoverAgentsListAPI list all the registered agents
CoverAgentsListAPI = "/v2/rpcagents"
//CoverProfileAPI is provided by the covered service to get profiles
CoverProfileAPI = "/v2/cover/profile"
2021-07-12 08:39:16 +00:00
)
type client struct {
2024-11-19 03:32:10 +00:00
Host string
client *http.Client
2021-07-12 08:39:16 +00:00
}
// gocListAgents response of the list request
type gocListAgents struct {
2024-11-19 03:32:10 +00:00
Items []gocCoveredAgent `json:"items"`
2021-07-12 08:39:16 +00:00
}
// gocCoveredAgent represents a covered client
type gocCoveredAgent struct {
2024-11-19 03:32:10 +00:00
Id string `json:"id"`
RemoteIP string `json:"remoteip"`
Hostname string `json:"hostname"`
CmdLine string `json:"cmdline"`
Pid string `json:"pid"`
2021-07-12 08:39:16 +00:00
}
2021-07-21 13:09:39 +00:00
type gocProfile struct {
2024-11-19 03:32:10 +00:00
Profile string `json:"profile"`
2021-07-21 13:09:39 +00:00
}
2021-07-12 08:39:16 +00:00
// NewWorker creates a worker to contact with host
func NewWorker(host string) Action {
2024-11-19 03:32:10 +00:00
_, err := url.ParseRequestURI(host)
if err != nil {
log.Fatalf("parse url %s failed, err: %v", host, err)
}
return &client{
Host: host,
client: http.DefaultClient,
}
2021-07-12 08:39:16 +00:00
}
2023-09-08 09:27:32 +00:00
// ListAgents Deprecated
2021-07-12 12:09:45 +00:00
func (c *client) ListAgents(wide bool) {
2024-11-19 03:32:10 +00:00
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 {
log.Fatalf("goc list failed: %v", err)
}
agents := gocListAgents{}
err = json.Unmarshal(body, &agents)
if err != nil {
log.Fatalf("goc list failed: json unmarshal failed: %v", err)
}
table := tablewriter.NewWriter(os.Stdout)
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)
table.SetAutoWrapText(false)
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 {
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
2021-07-12 08:39:16 +00:00
}
2021-07-21 13:09:39 +00:00
func (c *client) Profile(output string) {
2024-11-19 03:32:10 +00:00
u := fmt.Sprintf("%s%s", c.Host, CoverProfileAPI)
res, profile, err := c.do("GET", u, "application/json", nil)
if err != nil && isNetworkError(err) {
res, profile, err = c.do("GET", u, "application/json", nil)
}
if err == nil && res.StatusCode != 200 {
log.Fatalf(string(profile))
}
var profileText gocProfile
err = json.Unmarshal(profile, &profileText)
if err != nil {
log.Fatalf("profile unmarshal failed: %v", err)
}
if output == "" {
fmt.Fprint(os.Stdout, profileText.Profile)
} else {
var dir, filename string = filepath.Split(output)
if dir != "" {
err = os.MkdirAll(dir, os.ModePerm)
if err != nil {
log.Fatalf("failed to create directory %s, err:%v", dir, err)
}
}
if filename == "" {
output += "coverage.cov"
}
f, err := os.Create(output)
if err != nil {
log.Fatalf("failed to create file %s, err:%v", output, err)
}
defer f.Close()
_, err = io.Copy(f, bytes.NewReader([]byte(profileText.Profile)))
if err != nil {
log.Fatalf("failed to write file: %v, err: %v", output, err)
}
}
2021-07-21 13:09:39 +00:00
}
2021-07-12 12:09:45 +00:00
// getSimpleCmdLine
func getSimpleCmdLine(preLen int, cmdLine string) string {
2024-11-19 03:32:10 +00:00
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
2021-07-12 12:09:45 +00:00
}
2021-07-12 08:39:16 +00:00
func (c *client) do(method, url, contentType string, body io.Reader) (*http.Response, []byte, error) {
2024-11-19 03:32:10 +00:00
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
2021-07-12 08:39:16 +00:00
}
func isNetworkError(err error) bool {
2024-11-19 03:32:10 +00:00
if err == io.EOF {
return true
}
_, ok := err.(net.Error)
return ok
2021-07-12 08:39:16 +00:00
}