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:
Juan Fernandez 2023-07-27 09:29:09 -04:00 committed by GitHub
parent 6f77911ffe
commit e4cc0c3098
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 211 additions and 57 deletions

View file

@ -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, &notFoundErr) {
// 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)

View file

@ -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"}))
}