Show host display name in query results. (#15173)
Query report now shows the host display name instead of hostname. #14763 # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [x] Added/updated tests - [x] Manual QA for all new/changed functionality
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Query report now shows the host display name instead of hostname.
|
||||
@@ -91,7 +91,8 @@ func (ds *Datastore) OverwriteQueryResultRows(ctx context.Context, rows []*fleet
|
||||
// (to avoid having to left join hosts).
|
||||
func (ds *Datastore) QueryResultRows(ctx context.Context, queryID uint) ([]*fleet.ScheduledQueryResultRow, error) {
|
||||
selectStmt := `
|
||||
SELECT qr.query_id, qr.host_id, COALESCE(h.hostname, '') as hostname, qr.last_fetched, qr.data
|
||||
SELECT qr.query_id, qr.host_id, qr.last_fetched, qr.data,
|
||||
h.hostname, h.computer_name, h.hardware_model, h.hardware_serial
|
||||
FROM query_results qr
|
||||
LEFT JOIN hosts h ON (qr.host_id=h.id)
|
||||
WHERE query_id = ?
|
||||
|
||||
+12
-8
@@ -632,23 +632,27 @@ func (h *Host) IsEligibleForBitLockerEncryption() bool {
|
||||
(needsEncryption || encryptedWithoutKey)
|
||||
}
|
||||
|
||||
// DisplayName returns ComputerName if it isn't empty. Otherwise, it returns Hostname if it isn't
|
||||
// HostDisplayName returns ComputerName if it isn't empty. Otherwise, it returns Hostname if it isn't
|
||||
// empty. If Hostname is empty and both HardwareSerial and HardwareModel are not empty, it returns a
|
||||
// composite string with HardwareModel and HardwareSerial. If all else fails, it returns an empty
|
||||
// string.
|
||||
func (h *Host) DisplayName() string {
|
||||
func HostDisplayName(ComputerName string, Hostname string, HardwareModel string, HardwareSerial string) string {
|
||||
switch {
|
||||
case h.ComputerName != "":
|
||||
return h.ComputerName
|
||||
case h.Hostname != "":
|
||||
return h.Hostname
|
||||
case h.HardwareModel != "" && h.HardwareSerial != "":
|
||||
return fmt.Sprintf("%s (%s)", h.HardwareModel, h.HardwareSerial)
|
||||
case ComputerName != "":
|
||||
return ComputerName
|
||||
case Hostname != "":
|
||||
return Hostname
|
||||
case HardwareModel != "" && HardwareSerial != "":
|
||||
return fmt.Sprintf("%s (%s)", HardwareModel, HardwareSerial)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Host) DisplayName() string {
|
||||
return HostDisplayName(h.ComputerName, h.Hostname, h.HardwareModel, h.HardwareSerial)
|
||||
}
|
||||
|
||||
type HostIssues struct {
|
||||
TotalIssuesCount int `json:"total_issues_count" db:"total_issues_count" csv:"issues"` // when exporting in CSV, we want that value as the "issues" column
|
||||
FailingPoliciesCount int `json:"failing_policies_count" db:"failing_policies_count" csv:"-"`
|
||||
|
||||
+18
-3
@@ -1,6 +1,7 @@
|
||||
package fleet
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -380,7 +381,7 @@ func MapQueryReportResultsToRows(rows []*ScheduledQueryResultRow) ([]HostQueryRe
|
||||
}
|
||||
results = append(results, HostQueryResultRow{
|
||||
HostID: row.HostID,
|
||||
Hostname: row.Hostname,
|
||||
Hostname: row.HostDisplayName(),
|
||||
LastFetched: row.LastFetched,
|
||||
Columns: columns,
|
||||
})
|
||||
@@ -421,11 +422,25 @@ type ScheduledQueryResultRow struct {
|
||||
QueryID uint `db:"query_id"`
|
||||
// HostID is the unique identifier of the host.
|
||||
HostID uint `db:"host_id"`
|
||||
// Hostname is the host's hostname.
|
||||
Hostname string `db:"hostname"`
|
||||
// Hostname is the host's hostname. NullString is used in case host does not exist.
|
||||
Hostname sql.NullString `db:"hostname"`
|
||||
// ComputerName is the host's computer_name.
|
||||
ComputerName sql.NullString `db:"computer_name"`
|
||||
// HardwareModel is the host's hardware_model.
|
||||
HardwareModel sql.NullString `db:"hardware_model"`
|
||||
// HardwareSerial is the host's hardware_serial.
|
||||
HardwareSerial sql.NullString `db:"hardware_serial"`
|
||||
// Data holds a single result row. It holds a map where the map keys
|
||||
// are column names and map values are the values.
|
||||
Data json.RawMessage `db:"data"`
|
||||
// LastFetched is the time this result was received.
|
||||
LastFetched time.Time `db:"last_fetched"`
|
||||
}
|
||||
|
||||
func (s *ScheduledQueryResultRow) HostDisplayName() string {
|
||||
// If host does not exist, all values below default to empty string
|
||||
return HostDisplayName(
|
||||
s.ComputerName.String, s.Hostname.String,
|
||||
s.HardwareModel.String, s.HardwareSerial.String,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package fleet
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -228,7 +229,7 @@ func TestMapQueryReportResultRows(t *testing.T) {
|
||||
rows: []*ScheduledQueryResultRow{
|
||||
{
|
||||
HostID: 1,
|
||||
Hostname: "macOS host",
|
||||
Hostname: sql.NullString{String: "macOS host", Valid: true},
|
||||
LastFetched: macOSUSBDevicesLastFetched,
|
||||
Data: json.RawMessage(`{
|
||||
"class": "9",
|
||||
@@ -247,7 +248,7 @@ func TestMapQueryReportResultRows(t *testing.T) {
|
||||
},
|
||||
{
|
||||
HostID: 1,
|
||||
Hostname: "macOS host",
|
||||
Hostname: sql.NullString{String: "macOS host", Valid: true},
|
||||
LastFetched: macOSUSBDevicesLastFetched,
|
||||
Data: json.RawMessage(`{
|
||||
"class": "9",
|
||||
@@ -266,7 +267,7 @@ func TestMapQueryReportResultRows(t *testing.T) {
|
||||
},
|
||||
{
|
||||
HostID: 2,
|
||||
Hostname: "ubuntu host",
|
||||
Hostname: sql.NullString{String: "ubuntu host", Valid: true},
|
||||
LastFetched: ubuntuUSBDevicesLastFetched,
|
||||
Data: json.RawMessage(`{
|
||||
"class": "9",
|
||||
@@ -350,7 +351,7 @@ func TestMapQueryReportResultRows(t *testing.T) {
|
||||
rows: []*ScheduledQueryResultRow{
|
||||
{
|
||||
HostID: 1,
|
||||
Hostname: "macOS host",
|
||||
Hostname: sql.NullString{String: "macOS host", Valid: true},
|
||||
LastFetched: macOSOsqueryInfoLastFetched,
|
||||
Data: json.RawMessage(`{
|
||||
"build_distro": "10.14",
|
||||
@@ -396,7 +397,7 @@ func TestMapQueryReportResultRows(t *testing.T) {
|
||||
rows: []*ScheduledQueryResultRow{
|
||||
{
|
||||
HostID: 3,
|
||||
Hostname: "bar",
|
||||
Hostname: sql.NullString{String: "bar", Valid: true},
|
||||
LastFetched: time.Now(),
|
||||
Data: json.RawMessage(`invalid JSON`),
|
||||
},
|
||||
@@ -408,7 +409,7 @@ func TestMapQueryReportResultRows(t *testing.T) {
|
||||
rows: []*ScheduledQueryResultRow{
|
||||
{
|
||||
HostID: 3,
|
||||
Hostname: "bar",
|
||||
Hostname: sql.NullString{String: "bar", Valid: true},
|
||||
LastFetched: time.Now(),
|
||||
Data: json.RawMessage(`{"foobar": 1}`),
|
||||
},
|
||||
|
||||
@@ -8163,6 +8163,7 @@ func (s *integrationTestSuite) TestQueryReports() {
|
||||
SeenTime: time.Now(),
|
||||
NodeKey: ptr.String("2"),
|
||||
UUID: "2",
|
||||
ComputerName: "Foo Local2",
|
||||
Hostname: "foo.local2",
|
||||
OsqueryHostID: ptr.String("2"),
|
||||
PrimaryIP: "192.168.1.2",
|
||||
@@ -8339,7 +8340,7 @@ func (s *integrationTestSuite) TestQueryReports() {
|
||||
return gqrr.Results[i].Columns["usb_port"] < gqrr.Results[j].Columns["usb_port"]
|
||||
})
|
||||
require.Equal(t, host2Team1.ID, gqrr.Results[0].HostID)
|
||||
require.Equal(t, host2Team1.Hostname, gqrr.Results[0].Hostname)
|
||||
require.Equal(t, host2Team1.DisplayName(), gqrr.Results[0].Hostname)
|
||||
require.NotZero(t, gqrr.Results[0].LastFetched)
|
||||
require.Equal(t, map[string]string{
|
||||
"class": "239",
|
||||
@@ -8356,7 +8357,7 @@ func (s *integrationTestSuite) TestQueryReports() {
|
||||
"version": "0.19",
|
||||
}, gqrr.Results[0].Columns)
|
||||
require.Equal(t, host2Team1.ID, gqrr.Results[1].HostID)
|
||||
require.Equal(t, host2Team1.Hostname, gqrr.Results[1].Hostname)
|
||||
require.Equal(t, host2Team1.DisplayName(), gqrr.Results[1].Hostname)
|
||||
require.NotZero(t, gqrr.Results[1].LastFetched)
|
||||
require.Equal(t, map[string]string{
|
||||
"class": "0",
|
||||
@@ -8383,7 +8384,7 @@ func (s *integrationTestSuite) TestQueryReports() {
|
||||
return gqrr.Results[i].Columns["version"] > gqrr.Results[j].Columns["version"]
|
||||
})
|
||||
require.Equal(t, host1Global.ID, gqrr.Results[0].HostID)
|
||||
require.Equal(t, host1Global.Hostname, gqrr.Results[0].Hostname)
|
||||
require.Equal(t, host1Global.DisplayName(), gqrr.Results[0].Hostname)
|
||||
require.NotZero(t, gqrr.Results[0].LastFetched)
|
||||
require.Equal(t, map[string]string{
|
||||
"build_distro": "centos7",
|
||||
@@ -8400,7 +8401,7 @@ func (s *integrationTestSuite) TestQueryReports() {
|
||||
"watcher": "3570",
|
||||
}, gqrr.Results[0].Columns)
|
||||
require.Equal(t, host2Team1.ID, gqrr.Results[1].HostID)
|
||||
require.Equal(t, host2Team1.Hostname, gqrr.Results[1].Hostname)
|
||||
require.Equal(t, host2Team1.DisplayName(), gqrr.Results[1].Hostname)
|
||||
require.NotZero(t, gqrr.Results[1].LastFetched)
|
||||
require.Equal(t, map[string]string{
|
||||
"build_distro": "10.14",
|
||||
|
||||
Reference in New Issue
Block a user