mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 08:58:41 +00:00
add team_id filter to fleetctl & api (#1596)
* add team_id filter to fleetctl via get hosts --team flag & api via api/v1/fleet/hosts and api/v1/fleet/labels/id/hosts * update tests & add changes file
This commit is contained in:
parent
3b4bbf8f8e
commit
8bdad712d8
9 changed files with 134 additions and 3 deletions
2
changes/issue-1325-filter-by-team_id
Normal file
2
changes/issue-1325-filter-by-team_id
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
* add the `team_id` query parameter to `/api/v1/fleet/hosts` & `/api/v1/fleet/labels/{id}/hosts` which will filter the hosts by the specified team_id
|
||||
* add a `--team` command line flag to fleetctl to allow filtering hosts by team i.e. `fleetctl get hosts --team=1`
|
||||
|
|
@ -561,6 +561,11 @@ func getHostsCommand() *cli.Command {
|
|||
Aliases: []string{"host", "h"},
|
||||
Usage: "List information about one or more hosts",
|
||||
Flags: []cli.Flag{
|
||||
&cli.UintFlag{
|
||||
Name: "team",
|
||||
Usage: "filter hosts by team_id",
|
||||
Required: false,
|
||||
},
|
||||
jsonFlag(),
|
||||
yamlFlag(),
|
||||
configFlag(),
|
||||
|
|
@ -576,7 +581,11 @@ func getHostsCommand() *cli.Command {
|
|||
identifier := c.Args().First()
|
||||
|
||||
if identifier == "" {
|
||||
hosts, err := client.GetHosts()
|
||||
query := ""
|
||||
if c.Uint("team") > 0 {
|
||||
query = fmt.Sprintf("team_id=%d", c.Uint("team"))
|
||||
}
|
||||
hosts, err := client.GetHosts(query)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not list hosts")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -345,6 +345,7 @@ func (d *Datastore) ListHosts(filter fleet.TeamFilter, opt fleet.HostListOptions
|
|||
)
|
||||
|
||||
sql, params = filterHostsByStatus(sql, opt, params)
|
||||
sql, params = filterHostsByTeam(sql, opt, params)
|
||||
sql, params = searchLike(sql, params, opt.MatchQuery, hostSearchColumns...)
|
||||
|
||||
sql = appendListOptionsToSQL(sql, opt.ListOptions)
|
||||
|
|
@ -357,6 +358,14 @@ func (d *Datastore) ListHosts(filter fleet.TeamFilter, opt fleet.HostListOptions
|
|||
return hosts, nil
|
||||
}
|
||||
|
||||
func filterHostsByTeam(sql string, opt fleet.HostListOptions, params []interface{}) (string, []interface{}) {
|
||||
if opt.TeamFilter != nil {
|
||||
sql += ` AND h.team_id = ?`
|
||||
params = append(params, *opt.TeamFilter)
|
||||
}
|
||||
return sql, params
|
||||
}
|
||||
|
||||
func filterHostsByStatus(sql string, opt fleet.HostListOptions, params []interface{}) (string, []interface{}) {
|
||||
switch opt.StatusFilter {
|
||||
case "new":
|
||||
|
|
|
|||
|
|
@ -389,10 +389,31 @@ func TestListHostsQuery(t *testing.T) {
|
|||
|
||||
filter := fleet.TeamFilter{User: test.UserAdmin}
|
||||
|
||||
team1, err := ds.NewTeam(&fleet.Team{Name: "team1"})
|
||||
require.NoError(t, err)
|
||||
team2, err := ds.NewTeam(&fleet.Team{Name: "team2"})
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, host := range hosts {
|
||||
require.NoError(t, ds.AddHostsToTeam(&team1.ID, []uint{host.ID}))
|
||||
}
|
||||
|
||||
gotHosts, err := ds.ListHosts(filter, fleet.HostListOptions{})
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, len(hosts), len(gotHosts))
|
||||
|
||||
gotHosts, err = ds.ListHosts(filter, fleet.HostListOptions{TeamFilter: &team1.ID})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(hosts), len(gotHosts))
|
||||
|
||||
gotHosts, err = ds.ListHosts(filter, fleet.HostListOptions{TeamFilter: &team2.ID})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(gotHosts))
|
||||
|
||||
gotHosts, err = ds.ListHosts(filter, fleet.HostListOptions{TeamFilter: nil})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(hosts), len(gotHosts))
|
||||
|
||||
gotHosts, err = ds.ListHosts(filter, fleet.HostListOptions{ListOptions: fleet.ListOptions{MatchQuery: "00"}})
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 10, len(gotHosts))
|
||||
|
|
|
|||
|
|
@ -413,6 +413,7 @@ func (d *Datastore) ListHostsInLabel(filter fleet.TeamFilter, lid uint, opt flee
|
|||
params := []interface{}{lid}
|
||||
|
||||
sql, params = filterHostsByStatus(sql, opt, params)
|
||||
sql, params = filterHostsByTeam(sql, opt, params)
|
||||
sql, params = searchLike(sql, params, opt.MatchQuery, hostSearchColumns...)
|
||||
|
||||
sql = appendListOptionsToSQL(sql, opt.ListOptions)
|
||||
|
|
|
|||
|
|
@ -388,6 +388,83 @@ func TestListHostsInLabelAndStatus(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestListHostsInLabelAndTeamFilter(t *testing.T) {
|
||||
db := CreateMySQLDS(t)
|
||||
defer db.Close()
|
||||
|
||||
h1, err := db.NewHost(&fleet.Host{
|
||||
DetailUpdatedAt: time.Now(),
|
||||
LabelUpdatedAt: time.Now(),
|
||||
SeenTime: time.Now(),
|
||||
OsqueryHostID: "1",
|
||||
NodeKey: "1",
|
||||
UUID: "1",
|
||||
Hostname: "foo.local",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
lastSeenTime := time.Now().Add(-1000 * time.Hour)
|
||||
h2, err := db.NewHost(&fleet.Host{
|
||||
DetailUpdatedAt: lastSeenTime,
|
||||
LabelUpdatedAt: lastSeenTime,
|
||||
SeenTime: lastSeenTime,
|
||||
OsqueryHostID: "2",
|
||||
NodeKey: "2",
|
||||
UUID: "2",
|
||||
Hostname: "bar.local",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
l1 := &fleet.LabelSpec{
|
||||
ID: 1,
|
||||
Name: "label foo",
|
||||
Query: "query1",
|
||||
}
|
||||
err = db.ApplyLabelSpecs([]*fleet.LabelSpec{l1})
|
||||
require.Nil(t, err)
|
||||
|
||||
team1, err := db.NewTeam(&fleet.Team{Name: "team1"})
|
||||
require.NoError(t, err)
|
||||
|
||||
team2, err := db.NewTeam(&fleet.Team{Name: "team2"})
|
||||
require.NoError(t, err)
|
||||
|
||||
db.AddHostsToTeam(&team1.ID, []uint{h1.ID})
|
||||
|
||||
filter := fleet.TeamFilter{User: test.UserAdmin}
|
||||
for _, h := range []*fleet.Host{h1, h2} {
|
||||
err = db.RecordLabelQueryExecutions(h, map[uint]bool{l1.ID: true}, time.Now())
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
{
|
||||
hosts, err := db.ListHostsInLabel(filter, l1.ID, fleet.HostListOptions{StatusFilter: fleet.StatusOnline})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, hosts, 1)
|
||||
assert.Equal(t, "foo.local", hosts[0].Hostname)
|
||||
}
|
||||
|
||||
{
|
||||
hosts, err := db.ListHostsInLabel(filter, l1.ID, fleet.HostListOptions{StatusFilter: fleet.StatusMIA})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, hosts, 1)
|
||||
assert.Equal(t, "bar.local", hosts[0].Hostname)
|
||||
}
|
||||
|
||||
{
|
||||
hosts, err := db.ListHostsInLabel(filter, l1.ID, fleet.HostListOptions{TeamFilter: &team1.ID})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, hosts, 1)
|
||||
assert.Equal(t, "foo.local", hosts[0].Hostname)
|
||||
}
|
||||
|
||||
{
|
||||
hosts, err := db.ListHostsInLabel(filter, l1.ID, fleet.HostListOptions{TeamFilter: &team2.ID})
|
||||
require.Nil(t, err)
|
||||
require.Len(t, hosts, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuiltInLabels(t *testing.T) {
|
||||
db := CreateMySQLDS(t)
|
||||
defer db.Close()
|
||||
|
|
|
|||
|
|
@ -109,6 +109,8 @@ type HostListOptions struct {
|
|||
AdditionalFilters []string
|
||||
// StatusFilter selects the online status of the hosts.
|
||||
StatusFilter HostStatus
|
||||
// TeamFilter selects the hosts for specified team
|
||||
TeamFilter *uint
|
||||
}
|
||||
|
||||
type HostUser struct {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import (
|
|||
)
|
||||
|
||||
// GetHosts retrieves the list of all Hosts
|
||||
func (c *Client) GetHosts() ([]HostResponse, error) {
|
||||
response, err := c.AuthenticatedDo("GET", "/api/v1/fleet/hosts", "", nil)
|
||||
func (c *Client) GetHosts(query string) ([]HostResponse, error) {
|
||||
response, err := c.AuthenticatedDo("GET", "/api/v1/fleet/hosts", query, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GET /api/v1/fleet/hosts")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,6 +183,16 @@ func hostListOptionsFromRequest(r *http.Request) (fleet.HostListOptions, error)
|
|||
hopt.AdditionalFilters = strings.Split(additionalInfoFiltersString, ",")
|
||||
}
|
||||
|
||||
team_id := r.URL.Query().Get("team_id")
|
||||
if team_id != "" {
|
||||
id, err := strconv.Atoi(team_id)
|
||||
if err != nil {
|
||||
return hopt, err
|
||||
}
|
||||
tid := uint(id)
|
||||
hopt.TeamFilter = &tid
|
||||
}
|
||||
|
||||
return hopt, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue