From 3b67366bf438be26c04aa548280987dc10cb8b8e Mon Sep 17 00:00:00 2001 From: Tomas Touceda Date: Thu, 5 Aug 2021 14:56:29 -0300 Subject: [PATCH] Allow filtering by status as well as label and match query when listing hosts (#1562) --- changes/issue-1559-transfer-filter-by-status | 1 + server/datastore/mysql/hosts.go | 27 ++++++---- server/datastore/mysql/labels.go | 1 + server/datastore/mysql/labels_test.go | 56 ++++++++++++++++++++ 4 files changed, 74 insertions(+), 11 deletions(-) create mode 100644 changes/issue-1559-transfer-filter-by-status diff --git a/changes/issue-1559-transfer-filter-by-status b/changes/issue-1559-transfer-filter-by-status new file mode 100644 index 0000000000..fec83f38c0 --- /dev/null +++ b/changes/issue-1559-transfer-filter-by-status @@ -0,0 +1 @@ +* When filtering hosts to transfer to a team, allow to filter by label, status, and query string diff --git a/server/datastore/mysql/hosts.go b/server/datastore/mysql/hosts.go index 98f103b3aa..e0fb40efd6 100644 --- a/server/datastore/mysql/hosts.go +++ b/server/datastore/mysql/hosts.go @@ -351,6 +351,21 @@ func (d *Datastore) ListHosts(filter fleet.TeamFilter, opt fleet.HostListOptions WHERE TRUE AND %s `, d.whereFilterHostsByTeams(filter, "h"), ) + + sql, params = filterHostsByStatus(sql, opt, params) + sql, params = searchLike(sql, params, opt.MatchQuery, hostSearchColumns...) + + sql = appendListOptionsToSQL(sql, opt.ListOptions) + + hosts := []*fleet.Host{} + if err := d.db.Select(&hosts, sql, params...); err != nil { + return nil, errors.Wrap(err, "list hosts") + } + + return hosts, nil +} + +func filterHostsByStatus(sql string, opt fleet.HostListOptions, params []interface{}) (string, []interface{}) { switch opt.StatusFilter { case "new": sql += "AND DATE_ADD(h.created_at, INTERVAL 1 DAY) >= ?" @@ -365,17 +380,7 @@ func (d *Datastore) ListHosts(filter fleet.TeamFilter, opt fleet.HostListOptions sql += "AND DATE_ADD(h.seen_time, INTERVAL 30 DAY) <= ?" params = append(params, time.Now()) } - - sql, params = searchLike(sql, params, opt.MatchQuery, hostSearchColumns...) - - sql = appendListOptionsToSQL(sql, opt.ListOptions) - - hosts := []*fleet.Host{} - if err := d.db.Select(&hosts, sql, params...); err != nil { - return nil, errors.Wrap(err, "list hosts") - } - - return hosts, nil + return sql, params } func (d *Datastore) CleanupIncomingHosts(now time.Time) error { diff --git a/server/datastore/mysql/labels.go b/server/datastore/mysql/labels.go index f4c4db1a86..fd6716a333 100644 --- a/server/datastore/mysql/labels.go +++ b/server/datastore/mysql/labels.go @@ -412,6 +412,7 @@ func (d *Datastore) ListHostsInLabel(filter fleet.TeamFilter, lid uint, opt flee params := []interface{}{lid} + sql, params = filterHostsByStatus(sql, opt, params) sql, params = searchLike(sql, params, opt.MatchQuery, hostSearchColumns...) sql = appendListOptionsToSQL(sql, opt.ListOptions) diff --git a/server/datastore/mysql/labels_test.go b/server/datastore/mysql/labels_test.go index 8c6293e0af..f097179c27 100644 --- a/server/datastore/mysql/labels_test.go +++ b/server/datastore/mysql/labels_test.go @@ -332,6 +332,62 @@ func TestListHostsInLabel(t *testing.T) { } } +func TestListHostsInLabelAndStatus(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) + + 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) + } +} + func TestBuiltInLabels(t *testing.T) { db := CreateMySQLDS(t) defer db.Close()