mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 08:55:24 +00:00
Remove aggregate host counts from GET /hosts
responses (#7510)
This commit is contained in:
parent
d98a53f945
commit
aa0102d6b6
1
changes/issue-7489-remove-hosts-count-aggregate-stats
Normal file
1
changes/issue-7489-remove-hosts-count-aggregate-stats
Normal file
@ -0,0 +1 @@
|
||||
* Removed the `hosts_count` field from the `mobile_device_management_solution` and `munki_issue` top-level objects in the `GET /hosts` API endpoint response, as it was a pre-computed count that is updated at intervals, not a live count, and it could be confusing when it did not match the number of hosts returned.
|
@ -1743,9 +1743,9 @@ If `additional_info_filters` is not specified, no `additional` information will
|
||||
|
||||
If `software_id` is specified, an additional top-level key `"software"` is returned with the software object corresponding to the `software_id`. See [List all software](#list-all-software) response payload for details about this object.
|
||||
|
||||
If `mdm_id` is specified, an additional top-level key `"mobile_device_management_solution"` is returned with the aggregated statistics corresponding to the `mdm_id` (and, if provided, `team_id`). See [Get aggregated host's mobile device management (MDM) and Munki information](#get-aggregated-hosts-mobile-device-management-mdm-and-munki-information) response payload for details about this object. Note that the statistics are for the corresponding `mdm_id` (and `team_id` if provided) only, and do not take into account other potential filters such as `mdm_enrollment_status`.
|
||||
If `mdm_id` is specified, an additional top-level key `"mobile_device_management_solution"` is returned with the information corresponding to the `mdm_id`.
|
||||
|
||||
If `munki_issue_id` is specified, an additional top-level key `"munki_issue"` is returned with the aggregated statistics corresponding to the `munki_issue_id` (and, if provided, `team_id`). See [Get aggregated host's mobile device management (MDM) and Munki information](#get-aggregated-hosts-mobile-device-management-mdm-and-munki-information) response payload for details about this object.
|
||||
If `munki_issue_id` is specified, an additional top-level key `"munki_issue"` is returned with the information corresponding to the `munki_issue_id`.
|
||||
|
||||
#### Example
|
||||
|
||||
@ -1826,6 +1826,32 @@ If `munki_issue_id` is specified, an additional top-level key `"munki_issue"` is
|
||||
|
||||
> Note: the response above assumes a [GeoIP database is configured](https://fleetdm.com/docs/deploying/configuration#geo-ip), otherwise the `geolocation` object won't be included.
|
||||
|
||||
Response payload with the `mdm_id` filter provided:
|
||||
|
||||
```json
|
||||
{
|
||||
"hosts": [...],
|
||||
"mobile_device_management_solution": {
|
||||
"server_url": "http://some.url/mdm",
|
||||
"name": "MDM Vendor Name",
|
||||
"id": 999
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response payload with the `munki_issue_id` filter provided:
|
||||
|
||||
```json
|
||||
{
|
||||
"hosts": [...],
|
||||
"munki_issue": {
|
||||
"id": 1,
|
||||
"name": "Could not retrieve managed install primary manifest",
|
||||
"type": "error"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Count hosts
|
||||
|
||||
`GET /api/v1/fleet/hosts/count`
|
||||
|
@ -1670,7 +1670,7 @@ func (ds *Datastore) getOrInsertMDMSolution(ctx context.Context, serverURL strin
|
||||
return ds.optimisticGetOrInsert(ctx, readStmt, insStmt)
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetMunkiVersion(ctx context.Context, hostID uint) (string, error) {
|
||||
func (ds *Datastore) GetHostMunkiVersion(ctx context.Context, hostID uint) (string, error) {
|
||||
var version string
|
||||
err := sqlx.GetContext(ctx, ds.reader, &version, `SELECT version FROM host_munki_info WHERE deleted_at is NULL AND host_id = ?`, hostID)
|
||||
if err != nil {
|
||||
@ -1683,7 +1683,7 @@ func (ds *Datastore) GetMunkiVersion(ctx context.Context, hostID uint) (string,
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetMDM(ctx context.Context, hostID uint) (*fleet.HostMDM, error) {
|
||||
func (ds *Datastore) GetHostMDM(ctx context.Context, hostID uint) (*fleet.HostMDM, error) {
|
||||
var hmdm fleet.HostMDM
|
||||
err := sqlx.GetContext(ctx, ds.reader, &hmdm, `
|
||||
SELECT
|
||||
@ -1703,7 +1703,26 @@ func (ds *Datastore) GetMDM(ctx context.Context, hostID uint) (*fleet.HostMDM, e
|
||||
return &hmdm, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetMunkiIssues(ctx context.Context, hostID uint) ([]*fleet.HostMunkiIssue, error) {
|
||||
func (ds *Datastore) GetMDMSolution(ctx context.Context, mdmID uint) (*fleet.MDMSolution, error) {
|
||||
var solution fleet.MDMSolution
|
||||
err := sqlx.GetContext(ctx, ds.reader, &solution, `
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
server_url
|
||||
FROM
|
||||
mobile_device_management_solutions
|
||||
WHERE id = ?`, mdmID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ctxerr.Wrap(ctx, notFound("MDMSolution").WithID(mdmID))
|
||||
}
|
||||
return nil, ctxerr.Wrapf(ctx, err, "select mobile_device_management_solutions for id %d", mdmID)
|
||||
}
|
||||
return &solution, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetHostMunkiIssues(ctx context.Context, hostID uint) ([]*fleet.HostMunkiIssue, error) {
|
||||
var issues []*fleet.HostMunkiIssue
|
||||
err := sqlx.SelectContext(ctx, ds.reader, &issues, `
|
||||
SELECT
|
||||
@ -1725,6 +1744,25 @@ func (ds *Datastore) GetMunkiIssues(ctx context.Context, hostID uint) ([]*fleet.
|
||||
return issues, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetMunkiIssue(ctx context.Context, munkiIssueID uint) (*fleet.MunkiIssue, error) {
|
||||
var issue fleet.MunkiIssue
|
||||
err := sqlx.GetContext(ctx, ds.reader, &issue, `
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
issue_type
|
||||
FROM
|
||||
munki_issues
|
||||
WHERE id = ?`, munkiIssueID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ctxerr.Wrap(ctx, notFound("MunkiIssue").WithID(munkiIssueID))
|
||||
}
|
||||
return nil, ctxerr.Wrapf(ctx, err, "select munki_issues for id %d", munkiIssueID)
|
||||
}
|
||||
return &issue, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) AggregatedMunkiVersion(ctx context.Context, teamID *uint) ([]fleet.AggregatedMunkiVersion, time.Time, error) {
|
||||
id := uint(0)
|
||||
|
||||
|
@ -948,7 +948,7 @@ func testHostsListMunkiIssueID(t *testing.T, ds *Datastore) {
|
||||
err = ds.SetOrUpdateMunkiInfo(ctx, hostIDs[0], "1.0.0", []string{strings.Repeat("Z", maxMunkiIssueNameLen)}, []string{strings.Repeat("💞", maxMunkiIssueNameLen)})
|
||||
require.NoError(t, err)
|
||||
|
||||
issues, err := ds.GetMunkiIssues(ctx, hostIDs[0])
|
||||
issues, err := ds.GetHostMunkiIssues(ctx, hostIDs[0])
|
||||
require.NoError(t, err)
|
||||
require.Len(t, issues, 2)
|
||||
names := []string{issues[0].Name, issues[1].Name}
|
||||
@ -961,7 +961,7 @@ func testHostsListMunkiIssueID(t *testing.T, ds *Datastore) {
|
||||
err = ds.SetOrUpdateMunkiInfo(ctx, hostIDs[0], "1.0.0", []string{strings.Repeat("A", maxMunkiIssueNameLen+1)}, []string{strings.Repeat("☺", maxMunkiIssueNameLen+1)})
|
||||
require.NoError(t, err)
|
||||
|
||||
issues, err = ds.GetMunkiIssues(ctx, hostIDs[0])
|
||||
issues, err = ds.GetHostMunkiIssues(ctx, hostIDs[0])
|
||||
require.NoError(t, err)
|
||||
require.Len(t, issues, 2)
|
||||
names = []string{issues[0].Name, issues[1].Name}
|
||||
@ -3795,26 +3795,40 @@ func assertHostDeviceMapping(t *testing.T, got, want []*fleet.HostDeviceMapping)
|
||||
}
|
||||
|
||||
func testHostMDMAndMunki(t *testing.T, ds *Datastore) {
|
||||
_, err := ds.GetMunkiVersion(context.Background(), 123)
|
||||
_, err := ds.GetHostMunkiVersion(context.Background(), 123)
|
||||
require.True(t, fleet.IsNotFound(err))
|
||||
|
||||
require.NoError(t, ds.SetOrUpdateMunkiInfo(context.Background(), 123, "1.2.3", nil, nil))
|
||||
require.NoError(t, ds.SetOrUpdateMunkiInfo(context.Background(), 999, "9.0", nil, nil))
|
||||
require.NoError(t, ds.SetOrUpdateMunkiInfo(context.Background(), 123, "1.3.0", []string{"a", "b"}, []string{"c"}))
|
||||
|
||||
version, err := ds.GetMunkiVersion(context.Background(), 123)
|
||||
version, err := ds.GetHostMunkiVersion(context.Background(), 123)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "1.3.0", version)
|
||||
|
||||
issues, err := ds.GetMunkiIssues(context.Background(), 123)
|
||||
issues, err := ds.GetHostMunkiIssues(context.Background(), 123)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, issues, 3)
|
||||
|
||||
var aMunkiIssueID uint
|
||||
for _, iss := range issues {
|
||||
assert.NotZero(t, iss.MunkiIssueID)
|
||||
if iss.Name == "a" {
|
||||
aMunkiIssueID = iss.MunkiIssueID
|
||||
}
|
||||
assert.False(t, iss.HostIssueCreatedAt.IsZero())
|
||||
}
|
||||
|
||||
// get a Munki Issue
|
||||
miss, err := ds.GetMunkiIssue(context.Background(), aMunkiIssueID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "a", miss.Name)
|
||||
|
||||
// get an invalid munki issue
|
||||
_, err = ds.GetMunkiIssue(context.Background(), aMunkiIssueID+1000)
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, sql.ErrNoRows)
|
||||
|
||||
// ignore IDs and timestamps in slice comparison
|
||||
issues[0].MunkiIssueID, issues[0].HostIssueCreatedAt = 0, time.Time{}
|
||||
issues[1].MunkiIssueID, issues[1].HostIssueCreatedAt = 0, time.Time{}
|
||||
@ -3825,29 +3839,29 @@ func testHostMDMAndMunki(t *testing.T, ds *Datastore) {
|
||||
{Name: "c", IssueType: "warning"},
|
||||
}, issues)
|
||||
|
||||
version, err = ds.GetMunkiVersion(context.Background(), 999)
|
||||
version, err = ds.GetHostMunkiVersion(context.Background(), 999)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "9.0", version)
|
||||
|
||||
issues, err = ds.GetMunkiIssues(context.Background(), 999)
|
||||
issues, err = ds.GetHostMunkiIssues(context.Background(), 999)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, issues, 0)
|
||||
|
||||
// simulate uninstall
|
||||
require.NoError(t, ds.SetOrUpdateMunkiInfo(context.Background(), 123, "", nil, nil))
|
||||
|
||||
_, err = ds.GetMunkiVersion(context.Background(), 123)
|
||||
_, err = ds.GetHostMunkiVersion(context.Background(), 123)
|
||||
require.True(t, fleet.IsNotFound(err))
|
||||
issues, err = ds.GetMunkiIssues(context.Background(), 123)
|
||||
issues, err = ds.GetHostMunkiIssues(context.Background(), 123)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, issues, 0)
|
||||
|
||||
_, err = ds.GetMDM(context.Background(), 432)
|
||||
_, err = ds.GetHostMDM(context.Background(), 432)
|
||||
require.True(t, fleet.IsNotFound(err), err)
|
||||
|
||||
require.NoError(t, ds.SetOrUpdateMDMData(context.Background(), 432, true, "url", false))
|
||||
|
||||
hmdm, err := ds.GetMDM(context.Background(), 432)
|
||||
hmdm, err := ds.GetHostMDM(context.Background(), 432)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, hmdm.Enrolled)
|
||||
assert.Equal(t, "url", hmdm.ServerURL)
|
||||
@ -3860,7 +3874,7 @@ func testHostMDMAndMunki(t *testing.T, ds *Datastore) {
|
||||
require.NoError(t, ds.SetOrUpdateMDMData(context.Background(), 455, true, "https://kandji.io", true)) // kandji mdm name
|
||||
require.NoError(t, ds.SetOrUpdateMDMData(context.Background(), 432, false, "url3", true))
|
||||
|
||||
hmdm, err = ds.GetMDM(context.Background(), 432)
|
||||
hmdm, err = ds.GetHostMDM(context.Background(), 432)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, hmdm.Enrolled)
|
||||
assert.Equal(t, "url3", hmdm.ServerURL)
|
||||
@ -3870,7 +3884,7 @@ func testHostMDMAndMunki(t *testing.T, ds *Datastore) {
|
||||
assert.NotEqual(t, urlMDMID, *hmdm.MDMID)
|
||||
assert.Equal(t, fleet.UnknownMDMName, hmdm.Name)
|
||||
|
||||
hmdm, err = ds.GetMDM(context.Background(), 455)
|
||||
hmdm, err = ds.GetHostMDM(context.Background(), 455)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, hmdm.Enrolled)
|
||||
assert.Equal(t, "https://kandji.io", hmdm.ServerURL)
|
||||
@ -3880,10 +3894,21 @@ func testHostMDMAndMunki(t *testing.T, ds *Datastore) {
|
||||
kandjiID1 := *hmdm.MDMID
|
||||
assert.Equal(t, fleet.WellKnownMDMKandji, hmdm.Name)
|
||||
|
||||
// get mdm solution
|
||||
mdmSol, err := ds.GetMDMSolution(context.Background(), kandjiID1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "https://kandji.io", mdmSol.ServerURL)
|
||||
require.Equal(t, fleet.WellKnownMDMKandji, mdmSol.Name)
|
||||
|
||||
// get unknown mdm solution
|
||||
_, err = ds.GetMDMSolution(context.Background(), kandjiID1+1000)
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, sql.ErrNoRows)
|
||||
|
||||
// switch to simplemdm in an update
|
||||
require.NoError(t, ds.SetOrUpdateMDMData(context.Background(), 455, true, "https://simplemdm.com", false)) // now simplemdm name
|
||||
|
||||
hmdm, err = ds.GetMDM(context.Background(), 455)
|
||||
hmdm, err = ds.GetHostMDM(context.Background(), 455)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, hmdm.Enrolled)
|
||||
assert.Equal(t, "https://simplemdm.com", hmdm.ServerURL)
|
||||
@ -3895,7 +3920,7 @@ func testHostMDMAndMunki(t *testing.T, ds *Datastore) {
|
||||
// switch back to "url"
|
||||
require.NoError(t, ds.SetOrUpdateMDMData(context.Background(), 455, false, "url", false))
|
||||
|
||||
hmdm, err = ds.GetMDM(context.Background(), 455)
|
||||
hmdm, err = ds.GetHostMDM(context.Background(), 455)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, hmdm.Enrolled)
|
||||
assert.Equal(t, "url", hmdm.ServerURL)
|
||||
@ -3908,7 +3933,7 @@ func testHostMDMAndMunki(t *testing.T, ds *Datastore) {
|
||||
// even though this is another Kandji, the URL is different.
|
||||
require.NoError(t, ds.SetOrUpdateMDMData(context.Background(), 455, true, "https://kandji.io/2", false))
|
||||
|
||||
hmdm, err = ds.GetMDM(context.Background(), 455)
|
||||
hmdm, err = ds.GetHostMDM(context.Background(), 455)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, hmdm.Enrolled)
|
||||
assert.Equal(t, "https://kandji.io/2", hmdm.ServerURL)
|
||||
@ -3961,7 +3986,7 @@ func testMunkiIssuesBatchSize(t *testing.T, ds *Datastore) {
|
||||
|
||||
// try those errors/warning with some hosts
|
||||
require.NoError(t, ds.SetOrUpdateMunkiInfo(context.Background(), 123, "1.2.3", []string{"a", "b"}, []string{"C"}))
|
||||
issues, err := ds.GetMunkiIssues(ctx, 123)
|
||||
issues, err := ds.GetHostMunkiIssues(ctx, 123)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, issues, 3)
|
||||
for _, iss := range issues {
|
||||
@ -3969,7 +3994,7 @@ func testMunkiIssuesBatchSize(t *testing.T, ds *Datastore) {
|
||||
}
|
||||
|
||||
require.NoError(t, ds.SetOrUpdateMunkiInfo(context.Background(), 123, "1.2.3", []string{"c", "z"}, []string{"D", "E", "Z"}))
|
||||
issues, err = ds.GetMunkiIssues(ctx, 123)
|
||||
issues, err = ds.GetHostMunkiIssues(ctx, 123)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, issues, 5)
|
||||
for _, iss := range issues {
|
||||
@ -4054,18 +4079,24 @@ func testAggregatedHostMDMAndMunki(t *testing.T, ds *Datastore) {
|
||||
issues[2].ID = 0
|
||||
assert.ElementsMatch(t, issues, []fleet.AggregatedMunkiIssue{
|
||||
{
|
||||
Name: "a",
|
||||
IssueType: "error",
|
||||
MunkiIssue: fleet.MunkiIssue{
|
||||
Name: "a",
|
||||
IssueType: "error",
|
||||
},
|
||||
HostsCount: 2,
|
||||
},
|
||||
{
|
||||
Name: "b",
|
||||
IssueType: "error",
|
||||
MunkiIssue: fleet.MunkiIssue{
|
||||
Name: "b",
|
||||
IssueType: "error",
|
||||
},
|
||||
HostsCount: 1,
|
||||
},
|
||||
{
|
||||
Name: "c",
|
||||
IssueType: "warning",
|
||||
MunkiIssue: fleet.MunkiIssue{
|
||||
Name: "c",
|
||||
IssueType: "warning",
|
||||
},
|
||||
HostsCount: 2,
|
||||
},
|
||||
})
|
||||
@ -4157,13 +4188,17 @@ func testAggregatedHostMDMAndMunki(t *testing.T, ds *Datastore) {
|
||||
issues[1].ID = 0
|
||||
assert.ElementsMatch(t, issues, []fleet.AggregatedMunkiIssue{
|
||||
{
|
||||
Name: "d",
|
||||
IssueType: "error",
|
||||
MunkiIssue: fleet.MunkiIssue{
|
||||
Name: "d",
|
||||
IssueType: "error",
|
||||
},
|
||||
HostsCount: 2,
|
||||
},
|
||||
{
|
||||
Name: "f",
|
||||
IssueType: "warning",
|
||||
MunkiIssue: fleet.MunkiIssue{
|
||||
Name: "f",
|
||||
IssueType: "warning",
|
||||
},
|
||||
HostsCount: 1,
|
||||
},
|
||||
})
|
||||
|
@ -240,9 +240,9 @@ type Datastore interface {
|
||||
// ListPoliciesForHost lists the policies that a host will check and whether they are passing
|
||||
ListPoliciesForHost(ctx context.Context, host *Host) ([]*HostPolicy, error)
|
||||
|
||||
GetMunkiVersion(ctx context.Context, hostID uint) (string, error)
|
||||
GetMunkiIssues(ctx context.Context, hostID uint) ([]*HostMunkiIssue, error)
|
||||
GetMDM(ctx context.Context, hostID uint) (*HostMDM, error)
|
||||
GetHostMunkiVersion(ctx context.Context, hostID uint) (string, error)
|
||||
GetHostMunkiIssues(ctx context.Context, hostID uint) ([]*HostMunkiIssue, error)
|
||||
GetHostMDM(ctx context.Context, hostID uint) (*HostMDM, error)
|
||||
|
||||
AggregatedMunkiVersion(ctx context.Context, teamID *uint) ([]AggregatedMunkiVersion, time.Time, error)
|
||||
AggregatedMunkiIssues(ctx context.Context, teamID *uint) ([]AggregatedMunkiIssue, time.Time, error)
|
||||
@ -250,6 +250,9 @@ type Datastore interface {
|
||||
AggregatedMDMSolutions(ctx context.Context, teamID *uint) ([]AggregatedMDMSolutions, time.Time, error)
|
||||
GenerateAggregatedMunkiAndMDM(ctx context.Context) error
|
||||
|
||||
GetMunkiIssue(ctx context.Context, munkiIssueID uint) (*MunkiIssue, error)
|
||||
GetMDMSolution(ctx context.Context, mdmID uint) (*MDMSolution, error)
|
||||
|
||||
OSVersions(ctx context.Context, teamID *uint, platform *string, name *string, version *string) (*OSVersions, error)
|
||||
UpdateOSVersions(ctx context.Context) error
|
||||
|
||||
|
@ -422,11 +422,17 @@ type AggregatedMunkiVersion struct {
|
||||
HostsCount int `json:"hosts_count" db:"hosts_count"`
|
||||
}
|
||||
|
||||
// MunkiIssue represents a single munki issue, as returned by the list hosts
|
||||
// endpoint when a muniki issue ID is provided as filter.
|
||||
type MunkiIssue struct {
|
||||
ID uint `json:"id" db:"id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
IssueType string `json:"type" db:"issue_type"`
|
||||
}
|
||||
|
||||
type AggregatedMunkiIssue struct {
|
||||
ID uint `json:"id" db:"id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
IssueType string `json:"type" db:"issue_type"`
|
||||
HostsCount int `json:"hosts_count" db:"hosts_count"`
|
||||
MunkiIssue
|
||||
HostsCount int `json:"hosts_count" db:"hosts_count"`
|
||||
}
|
||||
|
||||
type AggregatedMDMStatus struct {
|
||||
@ -436,11 +442,17 @@ type AggregatedMDMStatus struct {
|
||||
HostsCount int `json:"hosts_count" db:"hosts_count"`
|
||||
}
|
||||
|
||||
// MDMSolution represents a single MDM solution, as returned by the list hosts
|
||||
// endpoint when an MDM Solution ID is provided as filter.
|
||||
type MDMSolution struct {
|
||||
ID uint `json:"id" db:"id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
ServerURL string `json:"server_url" db:"server_url"`
|
||||
}
|
||||
|
||||
type AggregatedMDMSolutions struct {
|
||||
ID uint `json:"id,omitempty" db:"id"`
|
||||
Name string `json:"name,omitempty" db:"name"`
|
||||
HostsCount int `json:"hosts_count" db:"hosts_count"`
|
||||
ServerURL string `json:"server_url" db:"server_url"`
|
||||
MDMSolution
|
||||
HostsCount int `json:"hosts_count" db:"hosts_count"`
|
||||
}
|
||||
|
||||
type AggregatedMacadminsData struct {
|
||||
|
@ -298,8 +298,8 @@ type Service interface {
|
||||
|
||||
MacadminsData(ctx context.Context, id uint) (*MacadminsData, error)
|
||||
AggregatedMacadminsData(ctx context.Context, teamID *uint) (*AggregatedMacadminsData, error)
|
||||
AggregatedMDMSolutions(ctx context.Context, teamID *uint, mdmID uint) (*AggregatedMDMSolutions, error)
|
||||
AggregatedMunkiIssue(ctx context.Context, teamID *uint, munkiIssueID uint) (*AggregatedMunkiIssue, error)
|
||||
GetMDMSolution(ctx context.Context, mdmID uint) (*MDMSolution, error)
|
||||
GetMunkiIssue(ctx context.Context, munkiIssueID uint) (*MunkiIssue, error)
|
||||
|
||||
// OSVersions returns a list of operating systems and associated host counts, which may be
|
||||
// filtered using the following optional criteria: team id, platform, or name and version.
|
||||
|
@ -189,11 +189,11 @@ type SetOrUpdateDeviceAuthTokenFunc func(ctx context.Context, hostID uint, authT
|
||||
|
||||
type ListPoliciesForHostFunc func(ctx context.Context, host *fleet.Host) ([]*fleet.HostPolicy, error)
|
||||
|
||||
type GetMunkiVersionFunc func(ctx context.Context, hostID uint) (string, error)
|
||||
type GetHostMunkiVersionFunc func(ctx context.Context, hostID uint) (string, error)
|
||||
|
||||
type GetMunkiIssuesFunc func(ctx context.Context, hostID uint) ([]*fleet.HostMunkiIssue, error)
|
||||
type GetHostMunkiIssuesFunc func(ctx context.Context, hostID uint) ([]*fleet.HostMunkiIssue, error)
|
||||
|
||||
type GetMDMFunc func(ctx context.Context, hostID uint) (*fleet.HostMDM, error)
|
||||
type GetHostMDMFunc func(ctx context.Context, hostID uint) (*fleet.HostMDM, error)
|
||||
|
||||
type AggregatedMunkiVersionFunc func(ctx context.Context, teamID *uint) ([]fleet.AggregatedMunkiVersion, time.Time, error)
|
||||
|
||||
@ -205,6 +205,10 @@ type AggregatedMDMSolutionsFunc func(ctx context.Context, teamID *uint) ([]fleet
|
||||
|
||||
type GenerateAggregatedMunkiAndMDMFunc func(ctx context.Context) error
|
||||
|
||||
type GetMunkiIssueFunc func(ctx context.Context, munkiIssueID uint) (*fleet.MunkiIssue, error)
|
||||
|
||||
type GetMDMSolutionFunc func(ctx context.Context, mdmID uint) (*fleet.MDMSolution, error)
|
||||
|
||||
type OSVersionsFunc func(ctx context.Context, teamID *uint, platform *string, name *string, version *string) (*fleet.OSVersions, error)
|
||||
|
||||
type UpdateOSVersionsFunc func(ctx context.Context) error
|
||||
@ -708,14 +712,14 @@ type DataStore struct {
|
||||
ListPoliciesForHostFunc ListPoliciesForHostFunc
|
||||
ListPoliciesForHostFuncInvoked bool
|
||||
|
||||
GetMunkiVersionFunc GetMunkiVersionFunc
|
||||
GetMunkiVersionFuncInvoked bool
|
||||
GetHostMunkiVersionFunc GetHostMunkiVersionFunc
|
||||
GetHostMunkiVersionFuncInvoked bool
|
||||
|
||||
GetMunkiIssuesFunc GetMunkiIssuesFunc
|
||||
GetMunkiIssuesFuncInvoked bool
|
||||
GetHostMunkiIssuesFunc GetHostMunkiIssuesFunc
|
||||
GetHostMunkiIssuesFuncInvoked bool
|
||||
|
||||
GetMDMFunc GetMDMFunc
|
||||
GetMDMFuncInvoked bool
|
||||
GetHostMDMFunc GetHostMDMFunc
|
||||
GetHostMDMFuncInvoked bool
|
||||
|
||||
AggregatedMunkiVersionFunc AggregatedMunkiVersionFunc
|
||||
AggregatedMunkiVersionFuncInvoked bool
|
||||
@ -732,6 +736,12 @@ type DataStore struct {
|
||||
GenerateAggregatedMunkiAndMDMFunc GenerateAggregatedMunkiAndMDMFunc
|
||||
GenerateAggregatedMunkiAndMDMFuncInvoked bool
|
||||
|
||||
GetMunkiIssueFunc GetMunkiIssueFunc
|
||||
GetMunkiIssueFuncInvoked bool
|
||||
|
||||
GetMDMSolutionFunc GetMDMSolutionFunc
|
||||
GetMDMSolutionFuncInvoked bool
|
||||
|
||||
OSVersionsFunc OSVersionsFunc
|
||||
OSVersionsFuncInvoked bool
|
||||
|
||||
@ -1530,19 +1540,19 @@ func (s *DataStore) ListPoliciesForHost(ctx context.Context, host *fleet.Host) (
|
||||
return s.ListPoliciesForHostFunc(ctx, host)
|
||||
}
|
||||
|
||||
func (s *DataStore) GetMunkiVersion(ctx context.Context, hostID uint) (string, error) {
|
||||
s.GetMunkiVersionFuncInvoked = true
|
||||
return s.GetMunkiVersionFunc(ctx, hostID)
|
||||
func (s *DataStore) GetHostMunkiVersion(ctx context.Context, hostID uint) (string, error) {
|
||||
s.GetHostMunkiVersionFuncInvoked = true
|
||||
return s.GetHostMunkiVersionFunc(ctx, hostID)
|
||||
}
|
||||
|
||||
func (s *DataStore) GetMunkiIssues(ctx context.Context, hostID uint) ([]*fleet.HostMunkiIssue, error) {
|
||||
s.GetMunkiIssuesFuncInvoked = true
|
||||
return s.GetMunkiIssuesFunc(ctx, hostID)
|
||||
func (s *DataStore) GetHostMunkiIssues(ctx context.Context, hostID uint) ([]*fleet.HostMunkiIssue, error) {
|
||||
s.GetHostMunkiIssuesFuncInvoked = true
|
||||
return s.GetHostMunkiIssuesFunc(ctx, hostID)
|
||||
}
|
||||
|
||||
func (s *DataStore) GetMDM(ctx context.Context, hostID uint) (*fleet.HostMDM, error) {
|
||||
s.GetMDMFuncInvoked = true
|
||||
return s.GetMDMFunc(ctx, hostID)
|
||||
func (s *DataStore) GetHostMDM(ctx context.Context, hostID uint) (*fleet.HostMDM, error) {
|
||||
s.GetHostMDMFuncInvoked = true
|
||||
return s.GetHostMDMFunc(ctx, hostID)
|
||||
}
|
||||
|
||||
func (s *DataStore) AggregatedMunkiVersion(ctx context.Context, teamID *uint) ([]fleet.AggregatedMunkiVersion, time.Time, error) {
|
||||
@ -1570,6 +1580,16 @@ func (s *DataStore) GenerateAggregatedMunkiAndMDM(ctx context.Context) error {
|
||||
return s.GenerateAggregatedMunkiAndMDMFunc(ctx)
|
||||
}
|
||||
|
||||
func (s *DataStore) GetMunkiIssue(ctx context.Context, munkiIssueID uint) (*fleet.MunkiIssue, error) {
|
||||
s.GetMunkiIssueFuncInvoked = true
|
||||
return s.GetMunkiIssueFunc(ctx, munkiIssueID)
|
||||
}
|
||||
|
||||
func (s *DataStore) GetMDMSolution(ctx context.Context, mdmID uint) (*fleet.MDMSolution, error) {
|
||||
s.GetMDMSolutionFuncInvoked = true
|
||||
return s.GetMDMSolutionFunc(ctx, mdmID)
|
||||
}
|
||||
|
||||
func (s *DataStore) OSVersions(ctx context.Context, teamID *uint, platform *string, name *string, version *string) (*fleet.OSVersions, error) {
|
||||
s.OSVersionsFuncInvoked = true
|
||||
return s.OSVersionsFunc(ctx, teamID, platform, name, version)
|
||||
|
@ -72,12 +72,12 @@ type listHostsResponse struct {
|
||||
// MDMSolution is populated with the MDM solution corresponding to the mdm_id
|
||||
// filter if one is provided with the request (and it exists in the
|
||||
// database). It is nil otherwise and absent of the JSON response payload.
|
||||
MDMSolution *fleet.AggregatedMDMSolutions `json:"mobile_device_management_solution,omitempty"`
|
||||
MDMSolution *fleet.MDMSolution `json:"mobile_device_management_solution,omitempty"`
|
||||
// MunkiIssue is populated with the munki issue corresponding to the
|
||||
// munki_issue_id filter if one is provided with the request (and it exists
|
||||
// in the database). It is nil otherwise and absent of the JSON response
|
||||
// payload.
|
||||
MunkiIssue *fleet.AggregatedMunkiIssue `json:"munki_issue,omitempty"`
|
||||
MunkiIssue *fleet.MunkiIssue `json:"munki_issue,omitempty"`
|
||||
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
@ -96,20 +96,20 @@ func listHostsEndpoint(ctx context.Context, request interface{}, svc fleet.Servi
|
||||
}
|
||||
}
|
||||
|
||||
var mdmSolution *fleet.AggregatedMDMSolutions
|
||||
var mdmSolution *fleet.MDMSolution
|
||||
if req.Opts.MDMIDFilter != nil {
|
||||
var err error
|
||||
mdmSolution, err = svc.AggregatedMDMSolutions(ctx, req.Opts.TeamFilter, *req.Opts.MDMIDFilter)
|
||||
if err != nil {
|
||||
mdmSolution, err = svc.GetMDMSolution(ctx, *req.Opts.MDMIDFilter)
|
||||
if err != nil && !fleet.IsNotFound(err) { // ignore not found, just return nil for the MDM solution in that case
|
||||
return listHostsResponse{Err: err}, nil
|
||||
}
|
||||
}
|
||||
|
||||
var munkiIssue *fleet.AggregatedMunkiIssue
|
||||
var munkiIssue *fleet.MunkiIssue
|
||||
if req.Opts.MunkiIssueIDFilter != nil {
|
||||
var err error
|
||||
munkiIssue, err = svc.AggregatedMunkiIssue(ctx, req.Opts.TeamFilter, *req.Opts.MunkiIssueIDFilter)
|
||||
if err != nil {
|
||||
munkiIssue, err = svc.GetMunkiIssue(ctx, *req.Opts.MunkiIssueIDFilter)
|
||||
if err != nil && !fleet.IsNotFound(err) { // ignore not found, just return nil for the munki issue in that case
|
||||
return listHostsResponse{Err: err}, nil
|
||||
}
|
||||
}
|
||||
@ -136,67 +136,20 @@ func listHostsEndpoint(ctx context.Context, request interface{}, svc fleet.Servi
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (svc *Service) AggregatedMDMSolutions(ctx context.Context, teamID *uint, mdmID uint) (*fleet.AggregatedMDMSolutions, error) {
|
||||
if err := svc.authz.Authorize(ctx, &fleet.Host{TeamID: teamID}, fleet.ActionList); err != nil {
|
||||
func (svc *Service) GetMDMSolution(ctx context.Context, mdmID uint) (*fleet.MDMSolution, error) {
|
||||
// require list hosts permission to view this information
|
||||
if err := svc.authz.Authorize(ctx, &fleet.Host{}, fleet.ActionList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if teamID != nil {
|
||||
_, err := svc.ds.Team(ctx, *teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// it is expected that there will be relatively few MDM solutions. This
|
||||
// returns the slice of all aggregated stats (one entry per mdm_id), and we
|
||||
// then iterate to return only the one that was requested (the slice is
|
||||
// stored as-is in a JSON field in the database).
|
||||
sols, _, err := svc.ds.AggregatedMDMSolutions(ctx, teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, sol := range sols {
|
||||
// don't take the address of the loop variable (although it could be ok
|
||||
// here, but just bad practice)
|
||||
sol := sol
|
||||
if sol.ID == mdmID {
|
||||
return &sol, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
return svc.ds.GetMDMSolution(ctx, mdmID)
|
||||
}
|
||||
|
||||
func (svc *Service) AggregatedMunkiIssue(ctx context.Context, teamID *uint, munkiIssueID uint) (*fleet.AggregatedMunkiIssue, error) {
|
||||
if err := svc.authz.Authorize(ctx, &fleet.Host{TeamID: teamID}, fleet.ActionList); err != nil {
|
||||
func (svc *Service) GetMunkiIssue(ctx context.Context, munkiIssueID uint) (*fleet.MunkiIssue, error) {
|
||||
// require list hosts permission to view this information
|
||||
if err := svc.authz.Authorize(ctx, &fleet.Host{}, fleet.ActionList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if teamID != nil {
|
||||
_, err := svc.ds.Team(ctx, *teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// This returns the slice of all aggregated stats (one entry per
|
||||
// munki_issue_id), and we then iterate to return only the one that was
|
||||
// requested (the slice is stored as-is in a JSON field in the database).
|
||||
issues, _, err := svc.ds.AggregatedMunkiIssues(ctx, teamID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, iss := range issues {
|
||||
// don't take the address of the loop variable (although it could be ok
|
||||
// here, but just bad practice)
|
||||
iss := iss
|
||||
if iss.ID == munkiIssueID {
|
||||
return &iss, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
return svc.ds.GetMunkiIssue(ctx, munkiIssueID)
|
||||
}
|
||||
|
||||
func (svc *Service) ListHosts(ctx context.Context, opt fleet.HostListOptions) ([]*fleet.Host, error) {
|
||||
@ -982,7 +935,7 @@ func (svc *Service) MacadminsData(ctx context.Context, id uint) (*fleet.Macadmin
|
||||
}
|
||||
|
||||
var munkiInfo *fleet.HostMunkiInfo
|
||||
switch version, err := svc.ds.GetMunkiVersion(ctx, id); {
|
||||
switch version, err := svc.ds.GetHostMunkiVersion(ctx, id); {
|
||||
case err != nil && !fleet.IsNotFound(err):
|
||||
return nil, err
|
||||
case err == nil:
|
||||
@ -990,7 +943,7 @@ func (svc *Service) MacadminsData(ctx context.Context, id uint) (*fleet.Macadmin
|
||||
}
|
||||
|
||||
var mdm *fleet.HostMDM
|
||||
switch hmdm, err := svc.ds.GetMDM(ctx, id); {
|
||||
switch hmdm, err := svc.ds.GetHostMDM(ctx, id); {
|
||||
case err != nil && !fleet.IsNotFound(err):
|
||||
return nil, err
|
||||
case err == nil:
|
||||
@ -998,7 +951,7 @@ func (svc *Service) MacadminsData(ctx context.Context, id uint) (*fleet.Macadmin
|
||||
}
|
||||
|
||||
var munkiIssues []*fleet.HostMunkiIssue
|
||||
switch issues, err := svc.ds.GetMunkiIssues(ctx, id); {
|
||||
switch issues, err := svc.ds.GetHostMunkiIssues(ctx, id); {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case err == nil:
|
||||
|
@ -1120,7 +1120,6 @@ func (s *integrationTestSuite) TestListHosts() {
|
||||
assert.Nil(t, resp.MunkiIssue)
|
||||
require.NotNil(t, resp.MDMSolution)
|
||||
assert.Equal(t, mdmID, resp.MDMSolution.ID)
|
||||
assert.Equal(t, 1, resp.MDMSolution.HostsCount)
|
||||
assert.Equal(t, fleet.WellKnownMDMSimpleMDM, resp.MDMSolution.Name)
|
||||
assert.Equal(t, "https://simplemdm.com", resp.MDMSolution.ServerURL)
|
||||
|
||||
@ -1151,11 +1150,10 @@ func (s *integrationTestSuite) TestListHosts() {
|
||||
assert.Nil(t, resp.Software)
|
||||
assert.Nil(t, resp.MDMSolution)
|
||||
require.NotNil(t, resp.MunkiIssue)
|
||||
assert.Equal(t, fleet.AggregatedMunkiIssue{
|
||||
ID: errMunkiID,
|
||||
Name: "err",
|
||||
IssueType: "error",
|
||||
HostsCount: 1,
|
||||
assert.Equal(t, fleet.MunkiIssue{
|
||||
ID: errMunkiID,
|
||||
Name: "err",
|
||||
IssueType: "error",
|
||||
}, *resp.MunkiIssue)
|
||||
|
||||
// filters can be combined, no problem
|
||||
@ -2683,13 +2681,17 @@ func (s *integrationTestSuite) TestGetMacadminsData() {
|
||||
agg.Macadmins.MunkiIssues[1].ID = 0
|
||||
assert.ElementsMatch(t, agg.Macadmins.MunkiIssues, []fleet.AggregatedMunkiIssue{
|
||||
{
|
||||
Name: "error1",
|
||||
IssueType: "error",
|
||||
MunkiIssue: fleet.MunkiIssue{
|
||||
Name: "error1",
|
||||
IssueType: "error",
|
||||
},
|
||||
HostsCount: 1,
|
||||
},
|
||||
{
|
||||
Name: "warning1",
|
||||
IssueType: "warning",
|
||||
MunkiIssue: fleet.MunkiIssue{
|
||||
Name: "warning1",
|
||||
IssueType: "warning",
|
||||
},
|
||||
HostsCount: 1,
|
||||
},
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user