fix get hosts command to properly output json/yaml (#1774)
* fix get hosts command to properly output json/yaml based on command line flag * add changes file * added tests for get hosts when specifiying host * added additional hosts to be returned in test cases * go fmt
This commit is contained in:
@@ -0,0 +1 @@
|
||||
* fix `fleetctl get host <hostname>` to properly output JSON when the command line flag is supplied i.e `fleetctl get host --json foobar`
|
||||
+12
-4
@@ -132,6 +132,16 @@ func printHost(c *cli.Context, host *service.HostResponse) error {
|
||||
return printSpec(c, spec)
|
||||
}
|
||||
|
||||
func printHostDetail(c *cli.Context, host *service.HostDetailResponse) error {
|
||||
spec := specGeneric{
|
||||
Kind: fleet.HostKind,
|
||||
Version: fleet.ApiVersion,
|
||||
Spec: host,
|
||||
}
|
||||
|
||||
return printSpec(c, spec)
|
||||
}
|
||||
|
||||
func printConfig(c *cli.Context, config *fleet.AppConfig) error {
|
||||
spec := specGeneric{
|
||||
Kind: fleet.AppConfigKind,
|
||||
@@ -368,7 +378,7 @@ func getPacksCommand() *cli.Command {
|
||||
return errors.Wrap(err, "could not list packs")
|
||||
}
|
||||
|
||||
if c.Bool(yamlFlagName) {
|
||||
if c.Bool(yamlFlagName) || c.Bool(jsonFlagName) {
|
||||
for _, pack := range packs {
|
||||
if err := printPack(c, pack); err != nil {
|
||||
return errors.Wrap(err, "unable to print pack")
|
||||
@@ -625,12 +635,10 @@ func getHostsCommand() *cli.Command {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get host")
|
||||
}
|
||||
b, err := yaml.Marshal(host)
|
||||
err = printHostDetail(c, host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(string(b))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
+123
-53
@@ -1,7 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/ghodss/yaml"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -192,6 +197,7 @@ func TestGetHosts(t *testing.T) {
|
||||
server, ds := runServerWithMockedDS(t)
|
||||
defer server.Close()
|
||||
|
||||
// this func is called when no host is specified i.e. `fleetctl get hosts --json`
|
||||
ds.ListHostsFunc = func(filter fleet.TeamFilter, opt fleet.HostListOptions) ([]*fleet.Host, error) {
|
||||
hosts := []*fleet.Host{
|
||||
{
|
||||
@@ -207,67 +213,131 @@ func TestGetHosts(t *testing.T) {
|
||||
ComputerName: "test_host",
|
||||
Hostname: "test_host",
|
||||
},
|
||||
{
|
||||
UpdateCreateTimestamps: fleet.UpdateCreateTimestamps{
|
||||
CreateTimestamp: fleet.CreateTimestamp{CreatedAt: time.Time{}},
|
||||
UpdateTimestamp: fleet.UpdateTimestamp{UpdatedAt: time.Time{}},
|
||||
},
|
||||
HostSoftware: fleet.HostSoftware{},
|
||||
DetailUpdatedAt: time.Time{},
|
||||
LabelUpdatedAt: time.Time{},
|
||||
LastEnrolledAt: time.Time{},
|
||||
SeenTime: time.Time{},
|
||||
ComputerName: "test_host2",
|
||||
Hostname: "test_host2",
|
||||
},
|
||||
}
|
||||
return hosts, nil
|
||||
}
|
||||
|
||||
expectedText := `+------+-----------+----------+-----------------+--------+
|
||||
| UUID | HOSTNAME | PLATFORM | OSQUERY VERSION | STATUS |
|
||||
+------+-----------+----------+-----------------+--------+
|
||||
| | test_host | | | mia |
|
||||
+------+-----------+----------+-----------------+--------+
|
||||
// these are run when host is specified `fleetctl get hosts --json test_host`
|
||||
ds.HostByIdentifierFunc = func(identifier string) (*fleet.Host, error) {
|
||||
require.NotEmpty(t, identifier)
|
||||
return &fleet.Host{
|
||||
UpdateCreateTimestamps: fleet.UpdateCreateTimestamps{
|
||||
CreateTimestamp: fleet.CreateTimestamp{CreatedAt: time.Time{}},
|
||||
UpdateTimestamp: fleet.UpdateTimestamp{UpdatedAt: time.Time{}},
|
||||
},
|
||||
HostSoftware: fleet.HostSoftware{},
|
||||
DetailUpdatedAt: time.Time{},
|
||||
LabelUpdatedAt: time.Time{},
|
||||
LastEnrolledAt: time.Time{},
|
||||
SeenTime: time.Time{},
|
||||
ComputerName: "test_host",
|
||||
Hostname: "test_host"}, nil
|
||||
}
|
||||
|
||||
ds.LoadHostSoftwareFunc = func(host *fleet.Host) error {
|
||||
return nil
|
||||
}
|
||||
ds.ListLabelsForHostFunc = func(hid uint) ([]*fleet.Label, error) {
|
||||
return make([]*fleet.Label, 0), nil
|
||||
}
|
||||
ds.ListPacksForHostFunc = func(hid uint) (packs []*fleet.Pack, err error) {
|
||||
return make([]*fleet.Pack, 0), nil
|
||||
}
|
||||
|
||||
expectedText := `+------+------------+----------+-----------------+--------+
|
||||
| UUID | HOSTNAME | PLATFORM | OSQUERY VERSION | STATUS |
|
||||
+------+------------+----------+-----------------+--------+
|
||||
| | test_host | | | mia |
|
||||
+------+------------+----------+-----------------+--------+
|
||||
| | test_host2 | | | mia |
|
||||
+------+------------+----------+-----------------+--------+
|
||||
`
|
||||
|
||||
expectedYaml := `---
|
||||
apiVersion: v1
|
||||
kind: host
|
||||
spec:
|
||||
build: ""
|
||||
code_name: ""
|
||||
computer_name: test_host
|
||||
config_tls_refresh: 0
|
||||
cpu_brand: ""
|
||||
cpu_logical_cores: 0
|
||||
cpu_physical_cores: 0
|
||||
cpu_subtype: ""
|
||||
cpu_type: ""
|
||||
created_at: "0001-01-01T00:00:00Z"
|
||||
detail_updated_at: "0001-01-01T00:00:00Z"
|
||||
display_text: test_host
|
||||
distributed_interval: 0
|
||||
gigs_disk_space_available: 0
|
||||
hardware_model: ""
|
||||
hardware_serial: ""
|
||||
hardware_vendor: ""
|
||||
hardware_version: ""
|
||||
hostname: test_host
|
||||
id: 0
|
||||
label_updated_at: "0001-01-01T00:00:00Z"
|
||||
last_enrolled_at: "0001-01-01T00:00:00Z"
|
||||
logger_tls_period: 0
|
||||
memory: 0
|
||||
os_version: ""
|
||||
osquery_version: ""
|
||||
pack_stats: null
|
||||
percent_disk_space_available: 0
|
||||
platform: ""
|
||||
platform_like: ""
|
||||
primary_ip: ""
|
||||
primary_mac: ""
|
||||
refetch_requested: false
|
||||
seen_time: "0001-01-01T00:00:00Z"
|
||||
status: mia
|
||||
team_id: null
|
||||
team_name: null
|
||||
updated_at: "0001-01-01T00:00:00Z"
|
||||
uptime: 0
|
||||
uuid: ""
|
||||
`
|
||||
expectedJson := "{\"kind\":\"host\",\"apiVersion\":\"v1\",\"spec\":{\"created_at\":\"0001-01-01T00:00:00Z\",\"updated_at\":\"0001-01-01T00:00:00Z\",\"id\":0,\"detail_updated_at\":\"0001-01-01T00:00:00Z\",\"label_updated_at\":\"0001-01-01T00:00:00Z\",\"last_enrolled_at\":\"0001-01-01T00:00:00Z\",\"seen_time\":\"0001-01-01T00:00:00Z\",\"refetch_requested\":false,\"hostname\":\"test_host\",\"uuid\":\"\",\"platform\":\"\",\"osquery_version\":\"\",\"os_version\":\"\",\"build\":\"\",\"platform_like\":\"\",\"code_name\":\"\",\"uptime\":0,\"memory\":0,\"cpu_type\":\"\",\"cpu_subtype\":\"\",\"cpu_brand\":\"\",\"cpu_physical_cores\":0,\"cpu_logical_cores\":0,\"hardware_vendor\":\"\",\"hardware_model\":\"\",\"hardware_version\":\"\",\"hardware_serial\":\"\",\"computer_name\":\"test_host\",\"primary_ip\":\"\",\"primary_mac\":\"\",\"distributed_interval\":0,\"config_tls_refresh\":0,\"logger_tls_period\":0,\"team_id\":null,\"pack_stats\":null,\"team_name\":null,\"gigs_disk_space_available\":0,\"percent_disk_space_available\":0,\"status\":\"mia\",\"display_text\":\"test_host\"}}\n"
|
||||
tests := []struct {
|
||||
name string
|
||||
goldenFile string
|
||||
unmarshaler func(data []byte, v interface{}) error
|
||||
scanner func(s string) []string
|
||||
args []string
|
||||
}{
|
||||
{
|
||||
name: "get hosts --json",
|
||||
goldenFile: "expectedListHostsJson.json",
|
||||
unmarshaler: json.Unmarshal,
|
||||
scanner: func(s string) []string {
|
||||
var parts []string
|
||||
scanner := bufio.NewScanner(bytes.NewBufferString(s))
|
||||
for scanner.Scan() {
|
||||
parts = append(parts, scanner.Text())
|
||||
}
|
||||
return parts
|
||||
},
|
||||
args: []string{"get", "hosts", "--json"},
|
||||
},
|
||||
{
|
||||
name: "get hosts --json test_host",
|
||||
goldenFile: "expectedHostDetailResponseJson.json",
|
||||
unmarshaler: json.Unmarshal,
|
||||
scanner: func(s string) []string {
|
||||
return []string{s}
|
||||
},
|
||||
args: []string{"get", "hosts", "--json", "test_host"},
|
||||
},
|
||||
{
|
||||
name: "get hosts --yaml",
|
||||
goldenFile: "expectedListHostsYaml.yml",
|
||||
unmarshaler: yaml.Unmarshal,
|
||||
scanner: func(s string) []string {
|
||||
return []string{s}
|
||||
},
|
||||
args: []string{"get", "hosts", "--yaml"},
|
||||
},
|
||||
{
|
||||
name: "get hosts --yaml test_host",
|
||||
goldenFile: "expectedHostDetailResponseYaml.yml",
|
||||
unmarshaler: yaml.Unmarshal,
|
||||
scanner: func(s string) []string {
|
||||
return splitYaml(s)
|
||||
},
|
||||
args: []string{"get", "hosts", "--yaml", "test_host"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
expected, err := ioutil.ReadFile(filepath.Join("testdata", tt.goldenFile))
|
||||
require.NoError(t, err)
|
||||
expectedResults := tt.scanner(string(expected))
|
||||
expectedSpecs := make([]specGeneric, len(expectedResults))
|
||||
for i, result := range expectedResults {
|
||||
var got specGeneric
|
||||
require.NoError(t, tt.unmarshaler([]byte(result), &got))
|
||||
expectedSpecs[i] = got
|
||||
}
|
||||
actualResult := tt.scanner(runAppForTest(t, tt.args))
|
||||
actualSpecs := make([]specGeneric, len(actualResult))
|
||||
for i, result := range actualResult {
|
||||
var spec specGeneric
|
||||
require.NoError(t, tt.unmarshaler([]byte(result), &spec))
|
||||
actualSpecs[i] = spec
|
||||
}
|
||||
require.Equal(t, expectedSpecs, actualSpecs)
|
||||
})
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedText, runAppForTest(t, []string{"get", "hosts"}))
|
||||
assert.Equal(t, expectedYaml, runAppForTest(t, []string{"get", "hosts", "--yaml"}))
|
||||
assert.Equal(t, expectedJson, runAppForTest(t, []string{"get", "hosts", "--json"}))
|
||||
}
|
||||
|
||||
func TestGetConfig(t *testing.T) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"kind":"host","apiVersion":"v1","spec":{"created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","id":0,"detail_updated_at":"0001-01-01T00:00:00Z","label_updated_at":"0001-01-01T00:00:00Z","last_enrolled_at":"0001-01-01T00:00:00Z","seen_time":"0001-01-01T00:00:00Z","refetch_requested":false,"hostname":"test_host","uuid":"","platform":"","osquery_version":"","os_version":"","build":"","platform_like":"","code_name":"","uptime":0,"memory":0,"cpu_type":"","cpu_subtype":"","cpu_brand":"","cpu_physical_cores":0,"cpu_logical_cores":0,"hardware_vendor":"","hardware_model":"","hardware_version":"","hardware_serial":"","computer_name":"test_host","primary_ip":"","primary_mac":"","distributed_interval":0,"config_tls_refresh":0,"logger_tls_period":0,"team_id":null,"pack_stats":null,"team_name":null,"gigs_disk_space_available":0,"percent_disk_space_available":0,"labels":[],"packs":[],"status":"mia","display_text":"test_host"}}
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: host
|
||||
spec:
|
||||
build: ""
|
||||
code_name: ""
|
||||
computer_name: test_host
|
||||
config_tls_refresh: 0
|
||||
cpu_brand: ""
|
||||
cpu_logical_cores: 0
|
||||
cpu_physical_cores: 0
|
||||
cpu_subtype: ""
|
||||
cpu_type: ""
|
||||
created_at: "0001-01-01T00:00:00Z"
|
||||
detail_updated_at: "0001-01-01T00:00:00Z"
|
||||
display_text: test_host
|
||||
distributed_interval: 0
|
||||
gigs_disk_space_available: 0
|
||||
hardware_model: ""
|
||||
hardware_serial: ""
|
||||
hardware_vendor: ""
|
||||
hardware_version: ""
|
||||
hostname: test_host
|
||||
id: 0
|
||||
label_updated_at: "0001-01-01T00:00:00Z"
|
||||
labels: []
|
||||
last_enrolled_at: "0001-01-01T00:00:00Z"
|
||||
logger_tls_period: 0
|
||||
memory: 0
|
||||
os_version: ""
|
||||
osquery_version: ""
|
||||
pack_stats: null
|
||||
packs: []
|
||||
percent_disk_space_available: 0
|
||||
platform: ""
|
||||
platform_like: ""
|
||||
primary_ip: ""
|
||||
primary_mac: ""
|
||||
refetch_requested: false
|
||||
seen_time: "0001-01-01T00:00:00Z"
|
||||
status: mia
|
||||
team_id: null
|
||||
team_name: null
|
||||
updated_at: "0001-01-01T00:00:00Z"
|
||||
uptime: 0
|
||||
uuid: ""
|
||||
@@ -0,0 +1,2 @@
|
||||
{"kind":"host","apiVersion":"v1","spec":{"created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","id":0,"detail_updated_at":"0001-01-01T00:00:00Z","label_updated_at":"0001-01-01T00:00:00Z","last_enrolled_at":"0001-01-01T00:00:00Z","seen_time":"0001-01-01T00:00:00Z","refetch_requested":false,"hostname":"test_host","uuid":"","platform":"","osquery_version":"","os_version":"","build":"","platform_like":"","code_name":"","uptime":0,"memory":0,"cpu_type":"","cpu_subtype":"","cpu_brand":"","cpu_physical_cores":0,"cpu_logical_cores":0,"hardware_vendor":"","hardware_model":"","hardware_version":"","hardware_serial":"","computer_name":"test_host","primary_ip":"","primary_mac":"","distributed_interval":0,"config_tls_refresh":0,"logger_tls_period":0,"team_id":null,"pack_stats":null,"team_name":null,"gigs_disk_space_available":0,"percent_disk_space_available":0,"status":"mia","display_text":"test_host"}}
|
||||
{"kind":"host","apiVersion":"v1","spec":{"created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","id":0,"detail_updated_at":"0001-01-01T00:00:00Z","label_updated_at":"0001-01-01T00:00:00Z","last_enrolled_at":"0001-01-01T00:00:00Z","seen_time":"0001-01-01T00:00:00Z","refetch_requested":false,"hostname":"test_host2","uuid":"","platform":"","osquery_version":"","os_version":"","build":"","platform_like":"","code_name":"","uptime":0,"memory":0,"cpu_type":"","cpu_subtype":"","cpu_brand":"","cpu_physical_cores":0,"cpu_logical_cores":0,"hardware_vendor":"","hardware_model":"","hardware_version":"","hardware_serial":"","computer_name":"test_host2","primary_ip":"","primary_mac":"","distributed_interval":0,"config_tls_refresh":0,"logger_tls_period":0,"team_id":null,"pack_stats":null,"team_name":null,"gigs_disk_space_available":0,"percent_disk_space_available":0,"status":"mia","display_text":"test_host2"}}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: host
|
||||
spec:
|
||||
build: ""
|
||||
code_name: ""
|
||||
computer_name: test_host
|
||||
config_tls_refresh: 0
|
||||
cpu_brand: ""
|
||||
cpu_logical_cores: 0
|
||||
cpu_physical_cores: 0
|
||||
cpu_subtype: ""
|
||||
cpu_type: ""
|
||||
created_at: "0001-01-01T00:00:00Z"
|
||||
detail_updated_at: "0001-01-01T00:00:00Z"
|
||||
display_text: test_host
|
||||
distributed_interval: 0
|
||||
gigs_disk_space_available: 0
|
||||
hardware_model: ""
|
||||
hardware_serial: ""
|
||||
hardware_vendor: ""
|
||||
hardware_version: ""
|
||||
hostname: test_host
|
||||
id: 0
|
||||
label_updated_at: "0001-01-01T00:00:00Z"
|
||||
last_enrolled_at: "0001-01-01T00:00:00Z"
|
||||
logger_tls_period: 0
|
||||
memory: 0
|
||||
os_version: ""
|
||||
osquery_version: ""
|
||||
pack_stats: null
|
||||
percent_disk_space_available: 0
|
||||
platform: ""
|
||||
platform_like: ""
|
||||
primary_ip: ""
|
||||
primary_mac: ""
|
||||
refetch_requested: false
|
||||
seen_time: "0001-01-01T00:00:00Z"
|
||||
status: mia
|
||||
team_id: null
|
||||
team_name: null
|
||||
updated_at: "0001-01-01T00:00:00Z"
|
||||
uptime: 0
|
||||
uuid: ""
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: host
|
||||
spec:
|
||||
build: ""
|
||||
code_name: ""
|
||||
computer_name: test_host2
|
||||
config_tls_refresh: 0
|
||||
cpu_brand: ""
|
||||
cpu_logical_cores: 0
|
||||
cpu_physical_cores: 0
|
||||
cpu_subtype: ""
|
||||
cpu_type: ""
|
||||
created_at: "0001-01-01T00:00:00Z"
|
||||
detail_updated_at: "0001-01-01T00:00:00Z"
|
||||
display_text: test_host2
|
||||
distributed_interval: 0
|
||||
gigs_disk_space_available: 0
|
||||
hardware_model: ""
|
||||
hardware_serial: ""
|
||||
hardware_vendor: ""
|
||||
hardware_version: ""
|
||||
hostname: test_host2
|
||||
id: 0
|
||||
label_updated_at: "0001-01-01T00:00:00Z"
|
||||
last_enrolled_at: "0001-01-01T00:00:00Z"
|
||||
logger_tls_period: 0
|
||||
memory: 0
|
||||
os_version: ""
|
||||
osquery_version: ""
|
||||
pack_stats: null
|
||||
percent_disk_space_available: 0
|
||||
platform: ""
|
||||
platform_like: ""
|
||||
primary_ip: ""
|
||||
primary_mac: ""
|
||||
refetch_requested: false
|
||||
seen_time: "0001-01-01T00:00:00Z"
|
||||
status: mia
|
||||
team_id: null
|
||||
team_name: null
|
||||
updated_at: "0001-01-01T00:00:00Z"
|
||||
uptime: 0
|
||||
uuid: ""
|
||||
@@ -26,7 +26,7 @@ func hostResponseForHost(ctx context.Context, svc fleet.Service, host *fleet.Hos
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HostDetailresponse is the response struct that contains the full host information
|
||||
// HostDetailResponse is the response struct that contains the full host information
|
||||
// with the HostDetail details.
|
||||
type HostDetailResponse struct {
|
||||
fleet.HostDetail
|
||||
|
||||
Reference in New Issue
Block a user