This makes cnetstat's table-printing functions generic to the type of
data being printed. This will let us print either connections or
summary statistics using the same code.
This commit is contained in:
Noah Lavine 2020-09-25 10:27:57 -04:00
Родитель 807dfc56f1
Коммит 4cb0a6f4f9
3 изменённых файлов: 68 добавлений и 56 удалений

Просмотреть файл

@ -23,32 +23,6 @@ type KubeConnection struct {
container ContainerPath
}
// I'm not using the standard json module for JSON output because I
// want to flatten the KubeConnection before printing it
func writeKubeConnectionAsJSON(kc *KubeConnection, w io.Writer) error {
_, err := fmt.Fprintf(w,
"{\"protocol\": %v,"+
"\"local_host\": %v, "+
"\"local_port\": %v, "+
"\"remote_host\": %v, "+
"\"remote_port\": %v, "+
"\"connection_state\": %v, "+
"\"pod_namespace\": %v, "+
"\"pod_name\": %v, "+
"\"container_name\": %v}",
kc.conn.protocol,
kc.conn.localHost,
kc.conn.localPort,
kc.conn.remoteHost,
kc.conn.remotePort,
kc.conn.connectionState,
kc.container.PodNamespace,
kc.container.PodName,
kc.container.ContainerName)
return err
}
const subprocessTimeout = 5 * time.Second
const ppidColon string = "PPid:"
@ -123,14 +97,6 @@ func getKubeConnections(connections []Connection, pidMap map[int]ContainerPath)
return kubeConnections
}
// Convert empty strings to "-". Why? Because that's what netstat does
func emptyToDash(val string) string {
if len(val) > 0 {
return val
} else {
return "-"
}
}
var kubeConnectionHeaders = []string{
"Namespace", "Pod", "Container", "Protocol",
@ -140,9 +106,9 @@ var kubeConnectionHeaders = []string{
func (kc KubeConnection) Fields() []string {
return []string{
emptyToDash(kc.container.PodNamespace),
emptyToDash(kc.container.PodName),
emptyToDash(kc.container.ContainerName),
kc.container.PodNamespace,
kc.container.PodName,
kc.container.ContainerName,
kc.conn.protocol,
kc.conn.localHost,
kc.conn.localPort,
@ -227,20 +193,15 @@ func cnetstat() error {
kubeConnections := getKubeConnections(allConnections, pidMap)
table := make([]Fielder, len(kubeConnections))
for i, _ := range kubeConnections {
table[i] = &kubeConnections[i]
}
switch format {
case "json":
for _, conn := range kubeConnections {
err := writeKubeConnectionAsJSON(&conn, os.Stdout)
if err != nil {
return err
}
os.Stdout.WriteString("\n")
}
printJsonTable(table, kubeConnectionHeaders, os.Stdout)
case "table":
table := make([]Fielder, len(kubeConnections))
for i, _ := range kubeConnections {
table[i] = &kubeConnections[i]
}
prettyPrintTable(table, kubeConnectionHeaders, os.Stdout)
}

Просмотреть файл

@ -18,9 +18,18 @@ func max(a, b int) int {
return a
}
// Convert empty strings to "-". Why? Because that's what netstat does
func emptyToDash(val string) string {
if len(val) > 0 {
return val
} else {
return "-"
}
}
// Print a table. The fields will print in the order returned by
// Fielder.Fields(). header is the first row of fields to print, which
// can be used for column titles.
// can be used for column titles. Empty fields will be printed as "-".
func prettyPrintTable(rows []Fielder, header []string, f io.Writer) {
w := bufio.NewWriter(f)
@ -49,9 +58,32 @@ func prettyPrintTable(rows []Fielder, header []string, f io.Writer) {
w.WriteString("\n")
for _, row := range rows {
for i, field := range row.Fields() {
fmt.Fprintf(w, "%-*s", fieldWidths[i], field)
fmt.Fprintf(w, "%-*s", fieldWidths[i], emptyToDash(field))
}
w.WriteString("\n")
}
w.Flush()
}
// Print a table as a series of JSON rows, one row per line of
// output. Each row will be a JSON object where property names come
// from header and values come from the row being printed.
func printJsonTable(rows []Fielder, fieldNames []string, f io.Writer) {
w := bufio.NewWriter(f)
for _, row := range rows {
w.WriteString("{")
for i, field := range row.Fields() {
// Using fprintf with a buffered writer
// results in two buffers, but this still
// seems better than the alternatives.
fmt.Fprintf(w, "\"%s\": \"%s\"", fieldNames[i], field)
if i < (len(fieldNames) - 1) {
w.WriteString(", ")
}
}
w.WriteString("}\n")
}
w.Flush()
}

Просмотреть файл

@ -15,22 +15,41 @@ func (t TestTable) Fields() []string {
return []string{t.a, t.b, t.c}
}
var testTable = []Fielder{
&TestTable{a: "a", b: "b", c: "cc"},
&TestTable{a: "aaa", b: "b", c: "c"},
&TestTable{a: "A", b: "", c: "c"},
}
var testFields = []string{"AAA", "B", "C"}
const expectedTable = `AAA B C
a b cc
aaa b c
A - c
`
func TestPrettyPrintTable(t *testing.T) {
var buf bytes.Buffer
table := []Fielder{
&TestTable{a: "a", b: "b", c: "cc"},
&TestTable{a: "aaa", b: "b", c: "c"},
}
prettyPrintTable(table, []string{"AAA", "B", "C"}, &buf)
prettyPrintTable(testTable, testFields, &buf)
written := buf.String()
if written != expectedTable {
t.Errorf("prettyPrintTable wrote %#v, expected %#v", written, expectedTable)
}
}
const expectedJson = `{"AAA": "a", "B": "b", "C": "cc"}
{"AAA": "aaa", "B": "b", "C": "c"}
{"AAA": "A", "B": "", "C": "c"}
`
func testPrintJsonTable(t *testing.T) {
var buf bytes.Buffer
printJsonTable(testTable, testFields, &buf)
written := buf.String()
if written != expectedJson {
t.Errorf("printJsonTable wrote %#v, expected %#v", written, expectedJson)
}
}