mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +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"
|
"gopkg.in/guregu/null.v3"
|
||||||
|
|
||||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||||
|
"github.com/fleetdm/fleet/v4/server/ptr"
|
||||||
"github.com/fleetdm/fleet/v4/server/service"
|
"github.com/fleetdm/fleet/v4/server/service"
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/olekukonko/tablewriter"
|
"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 {
|
func getQueriesCommand() *cli.Command {
|
||||||
return &cli.Command{
|
return &cli.Command{
|
||||||
Name: "queries",
|
Name: "queries",
|
||||||
|
|
@ -323,11 +368,24 @@ func getQueriesCommand() *cli.Command {
|
||||||
name := c.Args().First()
|
name := c.Args().First()
|
||||||
|
|
||||||
var teamID *uint
|
var teamID *uint
|
||||||
|
var teamName string
|
||||||
|
|
||||||
if tid := c.Uint(teamFlagName); tid != 0 {
|
if tid := c.Uint(teamFlagName); tid != 0 {
|
||||||
teamID = &tid
|
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 == "" {
|
if name == "" {
|
||||||
queries, err := client.GetQueries(teamID)
|
queries, err := client.GetQueries(teamID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -359,17 +417,12 @@ func getQueriesCommand() *cli.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(queries) == 0 {
|
if len(queries) == 0 {
|
||||||
fmt.Println("No queries found")
|
scope := "global"
|
||||||
return nil
|
if teamID != nil {
|
||||||
}
|
scope = "team"
|
||||||
|
|
||||||
var teamName string
|
|
||||||
if teamID != nil {
|
|
||||||
team, err := client.GetTeam(*teamID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("get team: %w", err)
|
|
||||||
}
|
}
|
||||||
teamName = team.Name
|
fmt.Printf("No %s queries found.\n", scope)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Bool(yamlFlagName) || c.Bool(jsonFlagName) {
|
if c.Bool(yamlFlagName) || c.Bool(jsonFlagName) {
|
||||||
|
|
@ -392,18 +445,21 @@ func getQueriesCommand() *cli.Command {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Default to printing as a table
|
// Default to printing as a table
|
||||||
data := [][]string{}
|
rows := [][]string{}
|
||||||
|
|
||||||
|
columns := []string{"name", "description", "query", "team", "schedule"}
|
||||||
for _, query := range queries {
|
for _, query := range queries {
|
||||||
data = append(data, []string{
|
rows = append(rows, queryToTableRow(query, teamName))
|
||||||
query.Name,
|
|
||||||
query.Description,
|
|
||||||
query.Query,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
columns := []string{"name", "description", "query"}
|
// Need to determine the number of inherited queries if we are viewing the
|
||||||
printTable(c, columns, data)
|
// queries for a team
|
||||||
|
nInheritedQueries, err := numberInheritedQueries(client, teamID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
printQueryTable(c, columns, rows, nInheritedQueries)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -520,10 +576,8 @@ func getPacksCommand() *cli.Command {
|
||||||
if err := printPack(c, pack); err != nil {
|
if err := printPack(c, pack); err != nil {
|
||||||
return fmt.Errorf("unable to print pack: %w", err)
|
return fmt.Errorf("unable to print pack: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
addQueries(pack)
|
addQueries(pack)
|
||||||
}
|
}
|
||||||
|
|
||||||
return printQueries()
|
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) {
|
func printTable(c *cli.Context, columns []string, data [][]string) {
|
||||||
table := defaultTable(c.App.Writer)
|
table := defaultTable(c.App.Writer)
|
||||||
table.SetHeader(columns)
|
table.SetHeader(columns)
|
||||||
|
|
|
||||||
|
|
@ -1053,16 +1053,42 @@ func TestGetQueries(t *testing.T) {
|
||||||
return nil, errors.New("invalid team ID")
|
return nil, errors.New("invalid team ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedGlobal := `+--------+-------------+-----------+
|
expectedGlobal := `+--------+-------------+-----------+-----------+--------------------------------+
|
||||||
| NAME | DESCRIPTION | QUERY |
|
| NAME | DESCRIPTION | QUERY | TEAM | SCHEDULE |
|
||||||
+--------+-------------+-----------+
|
+--------+-------------+-----------+-----------+--------------------------------+
|
||||||
| query1 | some desc | select 1; |
|
| query1 | some desc | select 1; | All teams | interval: 0 |
|
||||||
+--------+-------------+-----------+
|
| | | | | |
|
||||||
| query2 | some desc 2 | select 2; |
|
| | | | | platform: all |
|
||||||
+--------+-------------+-----------+
|
| | | | | |
|
||||||
| query4 | some desc 4 | select 4; |
|
| | | | | 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 := `---
|
expectedYAMLGlobal := `---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: query
|
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"}}
|
{"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 := `+--------+-------------+-----------+
|
expectedTeam := `+--------+-------------+-----------+--------+----------------------------+
|
||||||
| NAME | DESCRIPTION | QUERY |
|
| NAME | DESCRIPTION | QUERY | TEAM | SCHEDULE |
|
||||||
+--------+-------------+-----------+
|
+--------+-------------+-----------+--------+----------------------------+
|
||||||
| query3 | some desc 3 | select 3; |
|
| query3 | some desc 3 | select 3; | Foobar | interval: 3600 |
|
||||||
+--------+-------------+-----------+
|
| | | | | |
|
||||||
|
| | | | | platform: darwin |
|
||||||
|
| | | | | |
|
||||||
|
| | | | | min_osquery_version: 5.4.0 |
|
||||||
|
| | | | | |
|
||||||
|
| | | | | automations_enabled: false |
|
||||||
|
| | | | | |
|
||||||
|
| | | | | logging: snapshot |
|
||||||
|
+--------+-------------+-----------+--------+----------------------------+
|
||||||
`
|
`
|
||||||
|
|
||||||
expectedYAMLTeam := `---
|
expectedYAMLTeam := `---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: query
|
kind: query
|
||||||
|
|
@ -1149,7 +1184,12 @@ spec:
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetQuery(t *testing.T) {
|
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) {
|
ds.TeamFunc = func(ctx context.Context, tid uint) (*fleet.Team, error) {
|
||||||
if tid == 1 {
|
if tid == 1 {
|
||||||
|
|
@ -1339,11 +1379,19 @@ func TestGetQueriesAsObserver(t *testing.T) {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
setCurrentUserSession(tc.user)
|
setCurrentUserSession(tc.user)
|
||||||
|
|
||||||
expected := `+--------+-------------+-----------+
|
expected := `+--------+-------------+-----------+-----------+----------------------------+
|
||||||
| NAME | DESCRIPTION | QUERY |
|
| NAME | DESCRIPTION | QUERY | TEAM | SCHEDULE |
|
||||||
+--------+-------------+-----------+
|
+--------+-------------+-----------+-----------+----------------------------+
|
||||||
| query2 | some desc 2 | select 2; |
|
| query2 | some desc 2 | select 2; | All teams | interval: 0 |
|
||||||
+--------+-------------+-----------+
|
| | | | | |
|
||||||
|
| | | | | platform: all |
|
||||||
|
| | | | | |
|
||||||
|
| | | | | min_osquery_version: all |
|
||||||
|
| | | | | |
|
||||||
|
| | | | | automations_enabled: false |
|
||||||
|
| | | | | |
|
||||||
|
| | | | | logging: |
|
||||||
|
+--------+-------------+-----------+-----------+----------------------------+
|
||||||
`
|
`
|
||||||
expectedYaml := `---
|
expectedYaml := `---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
|
|
@ -1388,15 +1436,39 @@ spec:
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
expected := `+--------+-------------+-----------+
|
expected := `+--------+-------------+-----------+-----------+----------------------------+
|
||||||
| NAME | DESCRIPTION | QUERY |
|
| NAME | DESCRIPTION | QUERY | TEAM | SCHEDULE |
|
||||||
+--------+-------------+-----------+
|
+--------+-------------+-----------+-----------+----------------------------+
|
||||||
| query1 | some desc | select 1; |
|
| query1 | some desc | select 1; | All teams | interval: 0 |
|
||||||
+--------+-------------+-----------+
|
| | | | | |
|
||||||
| query2 | some desc 2 | select 2; |
|
| | | | | platform: all |
|
||||||
+--------+-------------+-----------+
|
| | | | | |
|
||||||
| query3 | some desc 3 | select 3; |
|
| | | | | 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 := `---
|
expectedYaml := `---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
|
|
@ -1498,13 +1570,29 @@ spec:
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
expected = `+--------+-------------+-----------+
|
expected = `+--------+-------------+-----------+-----------+----------------------------+
|
||||||
| NAME | DESCRIPTION | QUERY |
|
| NAME | DESCRIPTION | QUERY | TEAM | SCHEDULE |
|
||||||
+--------+-------------+-----------+
|
+--------+-------------+-----------+-----------+----------------------------+
|
||||||
| query1 | some desc | select 1; |
|
| query1 | some desc | select 1; | All teams | interval: 0 |
|
||||||
+--------+-------------+-----------+
|
| | | | | |
|
||||||
| query2 | some desc 2 | select 2; |
|
| | | | | 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"}))
|
assert.Equal(t, expected, runAppForTest(t, []string{"get", "queries"}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue