mirror of
https://github.com/fleetdm/fleet
synced 2026-05-21 16:08:47 +00:00
Fixed format issues with fleetctl get queries (#12983)
Output from `fleetctl get queries` should include the team the query is in and also the scheduling information.
This commit is contained in:
parent
6f77911ffe
commit
e4cc0c3098
2 changed files with 211 additions and 57 deletions
|
|
@ -18,6 +18,7 @@ import (
|
|||
"gopkg.in/guregu/null.v3"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/fleetdm/fleet/v4/server/ptr"
|
||||
"github.com/fleetdm/fleet/v4/server/service"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
|
@ -298,6 +299,50 @@ func getCommand() *cli.Command {
|
|||
}
|
||||
}
|
||||
|
||||
func queryToTableRow(query fleet.Query, teamName string) []string {
|
||||
platform := "all"
|
||||
if query.Platform != "" {
|
||||
platform = query.Platform
|
||||
}
|
||||
|
||||
minOsqueryVersion := "all"
|
||||
if query.MinOsqueryVersion != "" {
|
||||
minOsqueryVersion = query.MinOsqueryVersion
|
||||
}
|
||||
|
||||
scheduleInfo := fmt.Sprintf("interval: %d\nplatform: %s\nmin_osquery_version: %s\nautomations_enabled: %t\nlogging: %s",
|
||||
query.Interval,
|
||||
platform,
|
||||
minOsqueryVersion,
|
||||
query.AutomationsEnabled,
|
||||
query.Logging,
|
||||
)
|
||||
|
||||
teamNameOut := teamName
|
||||
if teamName == "" {
|
||||
teamNameOut = "All teams"
|
||||
}
|
||||
|
||||
return []string{
|
||||
query.Name,
|
||||
query.Description,
|
||||
query.Query,
|
||||
teamNameOut,
|
||||
scheduleInfo,
|
||||
}
|
||||
}
|
||||
|
||||
func numberInheritedQueries(client *service.Client, teamID *uint) (*int, error) {
|
||||
if teamID != nil {
|
||||
globalQueries, err := client.GetQueries(nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not list global queries: %w", err)
|
||||
}
|
||||
return ptr.Int(len(globalQueries)), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func getQueriesCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "queries",
|
||||
|
|
@ -323,11 +368,24 @@ func getQueriesCommand() *cli.Command {
|
|||
name := c.Args().First()
|
||||
|
||||
var teamID *uint
|
||||
var teamName string
|
||||
|
||||
if tid := c.Uint(teamFlagName); tid != 0 {
|
||||
teamID = &tid
|
||||
team, err := client.GetTeam(*teamID)
|
||||
if err != nil {
|
||||
var notFoundErr service.NotFoundErr
|
||||
if errors.As(err, ¬FoundErr) {
|
||||
// Do not error out, just inform the user and 'gracefully' exit.
|
||||
fmt.Println("Team not found.")
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("get team: %w", err)
|
||||
}
|
||||
teamName = team.Name
|
||||
}
|
||||
|
||||
// if name wasn't provided, list all queries
|
||||
// if name wasn't provided, list either all global queries or all team queries...
|
||||
if name == "" {
|
||||
queries, err := client.GetQueries(teamID)
|
||||
if err != nil {
|
||||
|
|
@ -359,17 +417,12 @@ func getQueriesCommand() *cli.Command {
|
|||
}
|
||||
|
||||
if len(queries) == 0 {
|
||||
fmt.Println("No queries found")
|
||||
return nil
|
||||
}
|
||||
|
||||
var teamName string
|
||||
if teamID != nil {
|
||||
team, err := client.GetTeam(*teamID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get team: %w", err)
|
||||
scope := "global"
|
||||
if teamID != nil {
|
||||
scope = "team"
|
||||
}
|
||||
teamName = team.Name
|
||||
fmt.Printf("No %s queries found.\n", scope)
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.Bool(yamlFlagName) || c.Bool(jsonFlagName) {
|
||||
|
|
@ -392,18 +445,21 @@ func getQueriesCommand() *cli.Command {
|
|||
}
|
||||
} else {
|
||||
// Default to printing as a table
|
||||
data := [][]string{}
|
||||
rows := [][]string{}
|
||||
|
||||
columns := []string{"name", "description", "query", "team", "schedule"}
|
||||
for _, query := range queries {
|
||||
data = append(data, []string{
|
||||
query.Name,
|
||||
query.Description,
|
||||
query.Query,
|
||||
})
|
||||
rows = append(rows, queryToTableRow(query, teamName))
|
||||
}
|
||||
|
||||
columns := []string{"name", "description", "query"}
|
||||
printTable(c, columns, data)
|
||||
// Need to determine the number of inherited queries if we are viewing the
|
||||
// queries for a team
|
||||
nInheritedQueries, err := numberInheritedQueries(client, teamID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printQueryTable(c, columns, rows, nInheritedQueries)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -520,10 +576,8 @@ func getPacksCommand() *cli.Command {
|
|||
if err := printPack(c, pack); err != nil {
|
||||
return fmt.Errorf("unable to print pack: %w", err)
|
||||
}
|
||||
|
||||
addQueries(pack)
|
||||
}
|
||||
|
||||
return printQueries()
|
||||
}
|
||||
|
||||
|
|
@ -1021,6 +1075,18 @@ func getUserRolesCommand() *cli.Command {
|
|||
}
|
||||
}
|
||||
|
||||
func printQueryTable(c *cli.Context, columns []string, data [][]string, nInheritedQueries *int) {
|
||||
table := defaultTable(c.App.Writer)
|
||||
table.SetHeader(columns)
|
||||
table.SetReflowDuringAutoWrap(false)
|
||||
table.AppendBulk(data)
|
||||
table.Render()
|
||||
|
||||
if nInheritedQueries != nil {
|
||||
fmt.Printf("Not showing %d inherited queries. To see global queries, run this command without the `--team` flag.\n", *nInheritedQueries)
|
||||
}
|
||||
}
|
||||
|
||||
func printTable(c *cli.Context, columns []string, data [][]string) {
|
||||
table := defaultTable(c.App.Writer)
|
||||
table.SetHeader(columns)
|
||||
|
|
|
|||
|
|
@ -1053,16 +1053,42 @@ func TestGetQueries(t *testing.T) {
|
|||
return nil, errors.New("invalid team ID")
|
||||
}
|
||||
|
||||
expectedGlobal := `+--------+-------------+-----------+
|
||||
| NAME | DESCRIPTION | QUERY |
|
||||
+--------+-------------+-----------+
|
||||
| query1 | some desc | select 1; |
|
||||
+--------+-------------+-----------+
|
||||
| query2 | some desc 2 | select 2; |
|
||||
+--------+-------------+-----------+
|
||||
| query4 | some desc 4 | select 4; |
|
||||
+--------+-------------+-----------+
|
||||
expectedGlobal := `+--------+-------------+-----------+-----------+--------------------------------+
|
||||
| NAME | DESCRIPTION | QUERY | TEAM | SCHEDULE |
|
||||
+--------+-------------+-----------+-----------+--------------------------------+
|
||||
| query1 | some desc | select 1; | All teams | interval: 0 |
|
||||
| | | | | |
|
||||
| | | | | platform: all |
|
||||
| | | | | |
|
||||
| | | | | min_osquery_version: all |
|
||||
| | | | | |
|
||||
| | | | | automations_enabled: false |
|
||||
| | | | | |
|
||||
| | | | | logging: |
|
||||
+--------+-------------+-----------+-----------+--------------------------------+
|
||||
| query2 | some desc 2 | select 2; | All teams | interval: 0 |
|
||||
| | | | | |
|
||||
| | | | | platform: all |
|
||||
| | | | | |
|
||||
| | | | | min_osquery_version: all |
|
||||
| | | | | |
|
||||
| | | | | automations_enabled: false |
|
||||
| | | | | |
|
||||
| | | | | logging: |
|
||||
+--------+-------------+-----------+-----------+--------------------------------+
|
||||
| query4 | some desc 4 | select 4; | All teams | interval: 60 |
|
||||
| | | | | |
|
||||
| | | | | platform: darwin,windows |
|
||||
| | | | | |
|
||||
| | | | | min_osquery_version: 5.3.0 |
|
||||
| | | | | |
|
||||
| | | | | automations_enabled: true |
|
||||
| | | | | |
|
||||
| | | | | logging: |
|
||||
| | | | | differential_ignore_removals |
|
||||
+--------+-------------+-----------+-----------+--------------------------------+
|
||||
`
|
||||
|
||||
expectedYAMLGlobal := `---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
|
|
@ -1111,12 +1137,21 @@ spec:
|
|||
{"kind":"query","apiVersion":"v1","spec":{"name":"query4","description":"some desc 4","query":"select 4;","team":"","interval":60,"observer_can_run":true,"platform":"darwin,windows","min_osquery_version":"5.3.0","automations_enabled":true,"logging":"differential_ignore_removals"}}
|
||||
`
|
||||
|
||||
expectedTeam := `+--------+-------------+-----------+
|
||||
| NAME | DESCRIPTION | QUERY |
|
||||
+--------+-------------+-----------+
|
||||
| query3 | some desc 3 | select 3; |
|
||||
+--------+-------------+-----------+
|
||||
expectedTeam := `+--------+-------------+-----------+--------+----------------------------+
|
||||
| NAME | DESCRIPTION | QUERY | TEAM | SCHEDULE |
|
||||
+--------+-------------+-----------+--------+----------------------------+
|
||||
| query3 | some desc 3 | select 3; | Foobar | interval: 3600 |
|
||||
| | | | | |
|
||||
| | | | | platform: darwin |
|
||||
| | | | | |
|
||||
| | | | | min_osquery_version: 5.4.0 |
|
||||
| | | | | |
|
||||
| | | | | automations_enabled: false |
|
||||
| | | | | |
|
||||
| | | | | logging: snapshot |
|
||||
+--------+-------------+-----------+--------+----------------------------+
|
||||
`
|
||||
|
||||
expectedYAMLTeam := `---
|
||||
apiVersion: v1
|
||||
kind: query
|
||||
|
|
@ -1149,7 +1184,12 @@ spec:
|
|||
}
|
||||
|
||||
func TestGetQuery(t *testing.T) {
|
||||
_, ds := runServerWithMockedDS(t)
|
||||
_, ds := runServerWithMockedDS(t, &service.TestServerOpts{
|
||||
License: &fleet.LicenseInfo{
|
||||
Tier: fleet.TierPremium,
|
||||
Expiration: time.Now().Add(24 * time.Hour),
|
||||
},
|
||||
})
|
||||
|
||||
ds.TeamFunc = func(ctx context.Context, tid uint) (*fleet.Team, error) {
|
||||
if tid == 1 {
|
||||
|
|
@ -1339,11 +1379,19 @@ func TestGetQueriesAsObserver(t *testing.T) {
|
|||
t.Run(tc.name, func(t *testing.T) {
|
||||
setCurrentUserSession(tc.user)
|
||||
|
||||
expected := `+--------+-------------+-----------+
|
||||
| NAME | DESCRIPTION | QUERY |
|
||||
+--------+-------------+-----------+
|
||||
| query2 | some desc 2 | select 2; |
|
||||
+--------+-------------+-----------+
|
||||
expected := `+--------+-------------+-----------+-----------+----------------------------+
|
||||
| NAME | DESCRIPTION | QUERY | TEAM | SCHEDULE |
|
||||
+--------+-------------+-----------+-----------+----------------------------+
|
||||
| query2 | some desc 2 | select 2; | All teams | interval: 0 |
|
||||
| | | | | |
|
||||
| | | | | platform: all |
|
||||
| | | | | |
|
||||
| | | | | min_osquery_version: all |
|
||||
| | | | | |
|
||||
| | | | | automations_enabled: false |
|
||||
| | | | | |
|
||||
| | | | | logging: |
|
||||
+--------+-------------+-----------+-----------+----------------------------+
|
||||
`
|
||||
expectedYaml := `---
|
||||
apiVersion: v1
|
||||
|
|
@ -1388,15 +1436,39 @@ spec:
|
|||
},
|
||||
})
|
||||
|
||||
expected := `+--------+-------------+-----------+
|
||||
| NAME | DESCRIPTION | QUERY |
|
||||
+--------+-------------+-----------+
|
||||
| query1 | some desc | select 1; |
|
||||
+--------+-------------+-----------+
|
||||
| query2 | some desc 2 | select 2; |
|
||||
+--------+-------------+-----------+
|
||||
| query3 | some desc 3 | select 3; |
|
||||
+--------+-------------+-----------+
|
||||
expected := `+--------+-------------+-----------+-----------+----------------------------+
|
||||
| NAME | DESCRIPTION | QUERY | TEAM | SCHEDULE |
|
||||
+--------+-------------+-----------+-----------+----------------------------+
|
||||
| query1 | some desc | select 1; | All teams | interval: 0 |
|
||||
| | | | | |
|
||||
| | | | | platform: all |
|
||||
| | | | | |
|
||||
| | | | | min_osquery_version: all |
|
||||
| | | | | |
|
||||
| | | | | automations_enabled: false |
|
||||
| | | | | |
|
||||
| | | | | logging: |
|
||||
+--------+-------------+-----------+-----------+----------------------------+
|
||||
| query2 | some desc 2 | select 2; | All teams | interval: 0 |
|
||||
| | | | | |
|
||||
| | | | | platform: all |
|
||||
| | | | | |
|
||||
| | | | | min_osquery_version: all |
|
||||
| | | | | |
|
||||
| | | | | automations_enabled: false |
|
||||
| | | | | |
|
||||
| | | | | logging: |
|
||||
+--------+-------------+-----------+-----------+----------------------------+
|
||||
| query3 | some desc 3 | select 3; | All teams | interval: 0 |
|
||||
| | | | | |
|
||||
| | | | | platform: all |
|
||||
| | | | | |
|
||||
| | | | | min_osquery_version: all |
|
||||
| | | | | |
|
||||
| | | | | automations_enabled: false |
|
||||
| | | | | |
|
||||
| | | | | logging: |
|
||||
+--------+-------------+-----------+-----------+----------------------------+
|
||||
`
|
||||
expectedYaml := `---
|
||||
apiVersion: v1
|
||||
|
|
@ -1498,13 +1570,29 @@ spec:
|
|||
},
|
||||
}, nil
|
||||
}
|
||||
expected = `+--------+-------------+-----------+
|
||||
| NAME | DESCRIPTION | QUERY |
|
||||
+--------+-------------+-----------+
|
||||
| query1 | some desc | select 1; |
|
||||
+--------+-------------+-----------+
|
||||
| query2 | some desc 2 | select 2; |
|
||||
+--------+-------------+-----------+
|
||||
expected = `+--------+-------------+-----------+-----------+----------------------------+
|
||||
| NAME | DESCRIPTION | QUERY | TEAM | SCHEDULE |
|
||||
+--------+-------------+-----------+-----------+----------------------------+
|
||||
| query1 | some desc | select 1; | All teams | interval: 0 |
|
||||
| | | | | |
|
||||
| | | | | platform: all |
|
||||
| | | | | |
|
||||
| | | | | min_osquery_version: all |
|
||||
| | | | | |
|
||||
| | | | | automations_enabled: false |
|
||||
| | | | | |
|
||||
| | | | | logging: |
|
||||
+--------+-------------+-----------+-----------+----------------------------+
|
||||
| query2 | some desc 2 | select 2; | All teams | interval: 0 |
|
||||
| | | | | |
|
||||
| | | | | platform: all |
|
||||
| | | | | |
|
||||
| | | | | min_osquery_version: all |
|
||||
| | | | | |
|
||||
| | | | | automations_enabled: false |
|
||||
| | | | | |
|
||||
| | | | | logging: |
|
||||
+--------+-------------+-----------+-----------+----------------------------+
|
||||
`
|
||||
assert.Equal(t, expected, runAppForTest(t, []string{"get", "queries"}))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue