Update host details, list host filters, and MDM summary to include macOS declarations (#17866)
Issue #17619 --------- Co-authored-by: Roberto Dip <dip.jesusr@gmail.com>
This commit is contained in:
co-authored by
Roberto Dip
parent
bb63da41b7
commit
1edd9f07bb
@@ -352,16 +352,38 @@ COALESCE(detail, '') AS detail
|
||||
FROM
|
||||
host_mdm_apple_profiles
|
||||
WHERE
|
||||
host_uuid = ? AND NOT (operation_type = '%s' AND COALESCE(status, '%s') IN('%s', '%s'))
|
||||
|
||||
UNION ALL
|
||||
SELECT
|
||||
declaration_uuid AS profile_uuid,
|
||||
declaration_name AS name,
|
||||
declaration_identifier AS identifier,
|
||||
-- internally, a NULL status implies that the cron needs to pick up
|
||||
-- this profile, for the user that difference doesn't exist, the
|
||||
-- profile is effectively pending. This is consistent with all our
|
||||
-- aggregation functions.
|
||||
COALESCE(status, '%s') AS status,
|
||||
COALESCE(operation_type, '') AS operation_type,
|
||||
COALESCE(detail, '') AS detail
|
||||
FROM
|
||||
host_mdm_apple_declarations
|
||||
WHERE
|
||||
host_uuid = ? AND NOT (operation_type = '%s' AND COALESCE(status, '%s') IN('%s', '%s'))`,
|
||||
fleet.MDMDeliveryPending,
|
||||
fleet.MDMOperationTypeRemove,
|
||||
fleet.MDMDeliveryPending,
|
||||
fleet.MDMDeliveryVerifying,
|
||||
fleet.MDMDeliveryVerified,
|
||||
fleet.MDMDeliveryPending,
|
||||
fleet.MDMOperationTypeRemove,
|
||||
fleet.MDMDeliveryPending,
|
||||
fleet.MDMDeliveryVerifying,
|
||||
fleet.MDMDeliveryVerified,
|
||||
)
|
||||
|
||||
var profiles []fleet.HostMDMAppleProfile
|
||||
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &profiles, stmt, hostUUID); err != nil {
|
||||
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &profiles, stmt, hostUUID, hostUUID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return profiles, nil
|
||||
@@ -2190,57 +2212,251 @@ func subqueryAppleProfileStatus(status fleet.MDMDeliveryStatus) (string, []any,
|
||||
return query, args, nil
|
||||
}
|
||||
|
||||
// subqueryAppleDeclarationStatus builds out the subquery for declaration status
|
||||
func subqueryAppleDeclarationStatus() (string, []any, error) {
|
||||
const declNamedStmt = `
|
||||
CASE WHEN EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
host_mdm_apple_declarations d1
|
||||
WHERE
|
||||
h.uuid = d1.host_uuid
|
||||
AND d1.status = :failed) THEN
|
||||
'declarations_failed'
|
||||
WHEN EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
host_mdm_apple_declarations d2
|
||||
WHERE
|
||||
h.uuid = d2.host_uuid
|
||||
AND(d2.status IS NULL
|
||||
OR d2.status = :pending)
|
||||
AND NOT EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
host_mdm_apple_declarations d3
|
||||
WHERE
|
||||
h.uuid = d3.host_uuid
|
||||
AND d3.status = :failed)) THEN
|
||||
'declarations_pending'
|
||||
WHEN EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
host_mdm_apple_declarations d4
|
||||
WHERE
|
||||
h.uuid = d4.host_uuid
|
||||
AND d4.status = :verifying
|
||||
AND NOT EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
host_mdm_apple_declarations d5
|
||||
WHERE (h.uuid = d5.host_uuid
|
||||
AND(d5.status IS NULL
|
||||
OR d5.status IN(:pending, :failed))))) THEN
|
||||
'declarations_verifying'
|
||||
WHEN EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
host_mdm_apple_declarations d6
|
||||
WHERE
|
||||
h.uuid = d6.host_uuid
|
||||
AND d6.status = :verified
|
||||
AND NOT EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
host_mdm_apple_declarations d7
|
||||
WHERE (h.uuid = d7.host_uuid
|
||||
AND(d7.status IS NULL
|
||||
OR d7.status IN(:pending, :failed, :verifying))))) THEN
|
||||
'declarations_verified'
|
||||
ELSE
|
||||
''
|
||||
END`
|
||||
|
||||
// TODO: do we need to differentiate between install and remove?
|
||||
arg := map[string]any{
|
||||
// "install": fleet.MDMOperationTypeInstall,
|
||||
// "remove": fleet.MDMOperationTypeRemove,
|
||||
"verifying": fleet.MDMDeliveryVerifying,
|
||||
"failed": fleet.MDMDeliveryFailed,
|
||||
"verified": fleet.MDMDeliveryVerified,
|
||||
"pending": fleet.MDMDeliveryPending,
|
||||
}
|
||||
query, args, err := sqlx.Named(declNamedStmt, arg)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("subqueryAppleDeclarationStatus: %w", err)
|
||||
}
|
||||
|
||||
return query, args, nil
|
||||
}
|
||||
|
||||
func subqueryOSSettingsStatusMac() (string, []any, error) {
|
||||
var profArgs []any
|
||||
profFailed, profFailedArgs, err := subqueryAppleProfileStatus(fleet.MDMDeliveryFailed)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
profArgs = append(profArgs, profFailedArgs...)
|
||||
|
||||
profPending, profPendingArgs, err := subqueryAppleProfileStatus(fleet.MDMDeliveryPending)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
profArgs = append(profArgs, profPendingArgs...)
|
||||
|
||||
profVerifying, profVerifyingArgs, err := subqueryAppleProfileStatus(fleet.MDMDeliveryVerifying)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
profArgs = append(profArgs, profVerifyingArgs...)
|
||||
|
||||
profVerified, profVerifiedArgs, err := subqueryAppleProfileStatus(fleet.MDMDeliveryVerified)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
profArgs = append(profArgs, profVerifiedArgs...)
|
||||
|
||||
profStmt := fmt.Sprintf(`
|
||||
CASE WHEN EXISTS (%s) THEN
|
||||
'profiles_failed'
|
||||
WHEN EXISTS (%s) THEN
|
||||
'profiles_pending'
|
||||
WHEN EXISTS (%s) THEN
|
||||
'profiles_verifying'
|
||||
WHEN EXISTS (%s) THEN
|
||||
'profiles_verified'
|
||||
ELSE
|
||||
''
|
||||
END`,
|
||||
profFailed,
|
||||
profPending,
|
||||
profVerifying,
|
||||
profVerified,
|
||||
)
|
||||
|
||||
declStmt, declArgs, err := subqueryAppleDeclarationStatus()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
stmt := fmt.Sprintf(`
|
||||
CASE (%s)
|
||||
WHEN 'profiles_failed' THEN
|
||||
'failed'
|
||||
WHEN 'profiles_pending' THEN (
|
||||
CASE (%s)
|
||||
WHEN 'declarations_failed' THEN
|
||||
'failed'
|
||||
ELSE
|
||||
'pending'
|
||||
END)
|
||||
WHEN 'profiles_verifying' THEN (
|
||||
CASE (%s)
|
||||
WHEN 'declarations_failed' THEN
|
||||
'failed'
|
||||
WHEN 'declarations_pending' THEN
|
||||
'pending'
|
||||
ELSE
|
||||
'verifying'
|
||||
END)
|
||||
WHEN 'profiles_verified' THEN (
|
||||
CASE (%s)
|
||||
WHEN 'declarations_failed' THEN
|
||||
'failed'
|
||||
WHEN 'declarations_pending' THEN
|
||||
'pending'
|
||||
WHEN 'declarations_verifying' THEN
|
||||
'verifying'
|
||||
ELSE
|
||||
'verified'
|
||||
END)
|
||||
ELSE
|
||||
REPLACE((%s), 'declarations_', '')
|
||||
END`, profStmt, declStmt, declStmt, declStmt, declStmt)
|
||||
|
||||
args := append(profArgs, declArgs...)
|
||||
args = append(args, declArgs...)
|
||||
args = append(args, declArgs...)
|
||||
args = append(args, declArgs...)
|
||||
|
||||
// FIXME(roberto): we found issues in MySQL 5.7.17 (only that version,
|
||||
// which we must support for now) with prepared statements on this
|
||||
// query. The results returned by the DB were always different what
|
||||
// expected unless the arguments are inlined in the query.
|
||||
//
|
||||
// We decided to do this given:
|
||||
//
|
||||
// - The time constraints we were given to develop DDM
|
||||
// - The fact that all the variables in this query are really strings managed by us
|
||||
// - The imminent deprecation of MySQL 5.7
|
||||
return fmt.Sprintf(strings.Replace(stmt, "?", "'%s'", -1), args...), []any{}, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetMDMAppleProfilesSummary(ctx context.Context, teamID *uint) (*fleet.MDMProfilesSummary, error) {
|
||||
var args []interface{}
|
||||
|
||||
subqueryFailed, subqueryFailedArgs, err := subqueryAppleProfileStatus(fleet.MDMDeliveryFailed)
|
||||
subquery, args, err := subqueryOSSettingsStatusMac()
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "building failed subquery")
|
||||
return nil, ctxerr.Wrap(ctx, err, "building os settings subquery")
|
||||
}
|
||||
args = append(args, subqueryFailedArgs...)
|
||||
|
||||
subqueryPending, subqueryPendingArgs, err := subqueryAppleProfileStatus(fleet.MDMDeliveryPending)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "building pending subquery")
|
||||
}
|
||||
args = append(args, subqueryPendingArgs...)
|
||||
|
||||
subqueryVerifying, subqueryVerifyingArgs, err := subqueryAppleProfileStatus(fleet.MDMDeliveryVerifying)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "building verifying subquery")
|
||||
}
|
||||
args = append(args, subqueryVerifyingArgs...)
|
||||
|
||||
subqueryVerified, subqueryVerifiedArgs, err := subqueryAppleProfileStatus(fleet.MDMDeliveryVerified)
|
||||
if err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "building verified subquery")
|
||||
}
|
||||
args = append(args, subqueryVerifiedArgs...)
|
||||
|
||||
sqlFmt := `
|
||||
SELECT
|
||||
COUNT(CASE WHEN EXISTS (%s) THEN 1 END) AS failed,
|
||||
COUNT(CASE WHEN EXISTS (%s) THEN 1 END) AS pending,
|
||||
COUNT(CASE WHEN EXISTS (%s) THEN 1 END) AS verifying,
|
||||
COUNT(CASE WHEN EXISTS (%s) THEN 1 END) AS verified
|
||||
FROM
|
||||
hosts h
|
||||
WHERE
|
||||
h.platform = 'darwin' AND %s`
|
||||
SELECT
|
||||
%s as status,
|
||||
COUNT(id) as count
|
||||
FROM
|
||||
hosts h
|
||||
GROUP BY status, platform, team_id HAVING platform = 'darwin' AND status IN (?, ?, ?, ?) AND %s`
|
||||
|
||||
teamFilter := "h.team_id IS NULL"
|
||||
args = append(args, fleet.MDMDeliveryFailed, fleet.MDMDeliveryPending, fleet.MDMDeliveryVerifying, fleet.MDMDeliveryVerified)
|
||||
|
||||
teamFilter := "team_id IS NULL"
|
||||
if teamID != nil && *teamID > 0 {
|
||||
teamFilter = "h.team_id = ?"
|
||||
teamFilter = "team_id = ?"
|
||||
args = append(args, *teamID)
|
||||
}
|
||||
|
||||
stmt := fmt.Sprintf(sqlFmt, subqueryFailed, subqueryPending, subqueryVerifying, subqueryVerified, teamFilter)
|
||||
var res fleet.MDMProfilesSummary
|
||||
err = sqlx.GetContext(ctx, ds.reader(ctx), &res, stmt, args...)
|
||||
stmt := fmt.Sprintf(sqlFmt, subquery, teamFilter)
|
||||
|
||||
var dest []struct {
|
||||
Count uint `db:"count"`
|
||||
Status string `db:"status"`
|
||||
}
|
||||
|
||||
err = sqlx.SelectContext(ctx, ds.reader(ctx), &dest, stmt, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
byStatus := make(map[string]uint)
|
||||
for _, s := range dest {
|
||||
if _, ok := byStatus[s.Status]; ok {
|
||||
return nil, fmt.Errorf("duplicate status %s", s.Status)
|
||||
}
|
||||
byStatus[s.Status] = s.Count
|
||||
}
|
||||
|
||||
var res fleet.MDMProfilesSummary
|
||||
for s, c := range byStatus {
|
||||
switch fleet.MDMDeliveryStatus(s) {
|
||||
case fleet.MDMDeliveryFailed:
|
||||
res.Failed = c
|
||||
case fleet.MDMDeliveryPending:
|
||||
res.Pending = c
|
||||
case fleet.MDMDeliveryVerifying:
|
||||
res.Verifying = c
|
||||
case fleet.MDMDeliveryVerified:
|
||||
res.Verified = c
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown status %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1787,13 +1787,19 @@ func testAggregateMacOSSettingsStatusWithFileVault(t *testing.T, ds *Datastore)
|
||||
expectedIDs = append(expectedIDs, h.ID)
|
||||
}
|
||||
|
||||
gotHosts, err := ds.ListHosts(ctx, fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String("admin")}}, fleet.HostListOptions{MacOSSettingsFilter: status, TeamFilter: teamID})
|
||||
gotHosts, err := ds.ListHosts(
|
||||
ctx,
|
||||
fleet.TeamFilter{User: &fleet.User{GlobalRole: ptr.String("admin")}},
|
||||
fleet.HostListOptions{MacOSSettingsFilter: status, TeamFilter: teamID},
|
||||
)
|
||||
gotIDs := []uint{}
|
||||
for _, h := range gotHosts {
|
||||
gotIDs = append(gotIDs, h.ID)
|
||||
}
|
||||
|
||||
return assert.NoError(t, err) && assert.Len(t, gotHosts, len(expected)) && assert.ElementsMatch(t, expectedIDs, gotIDs)
|
||||
return assert.NoError(t, err) &&
|
||||
assert.Len(t, gotHosts, len(expected)) &&
|
||||
assert.ElementsMatch(t, expectedIDs, gotIDs)
|
||||
}
|
||||
|
||||
var hosts []*fleet.Host
|
||||
@@ -2615,7 +2621,7 @@ func TestMDMAppleFileVaultSummary(t *testing.T) {
|
||||
|
||||
allProfilesSummary, err = ds.GetMDMAppleProfilesSummary(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fvProfileSummary)
|
||||
require.NotNil(t, allProfilesSummary)
|
||||
require.Equal(t, uint(2), allProfilesSummary.Pending)
|
||||
require.Equal(t, uint(0), allProfilesSummary.Failed)
|
||||
require.Equal(t, uint(1), allProfilesSummary.Verifying)
|
||||
@@ -2641,7 +2647,7 @@ func TestMDMAppleFileVaultSummary(t *testing.T) {
|
||||
|
||||
allProfilesSummary, err = ds.GetMDMAppleProfilesSummary(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fvProfileSummary)
|
||||
require.NotNil(t, allProfilesSummary)
|
||||
require.Equal(t, uint(2), allProfilesSummary.Pending)
|
||||
require.Equal(t, uint(0), allProfilesSummary.Failed)
|
||||
require.Equal(t, uint(1), allProfilesSummary.Verifying)
|
||||
@@ -2669,7 +2675,7 @@ func TestMDMAppleFileVaultSummary(t *testing.T) {
|
||||
|
||||
allProfilesSummary, err = ds.GetMDMAppleProfilesSummary(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fvProfileSummary)
|
||||
require.NotNil(t, allProfilesSummary)
|
||||
require.Equal(t, uint(2), allProfilesSummary.Pending)
|
||||
require.Equal(t, uint(0), allProfilesSummary.Failed)
|
||||
require.Equal(t, uint(1), allProfilesSummary.Verifying)
|
||||
@@ -2691,7 +2697,7 @@ func TestMDMAppleFileVaultSummary(t *testing.T) {
|
||||
|
||||
allProfilesSummary, err = ds.GetMDMAppleProfilesSummary(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fvProfileSummary)
|
||||
require.NotNil(t, allProfilesSummary)
|
||||
require.Equal(t, uint(2), allProfilesSummary.Pending)
|
||||
require.Equal(t, uint(1), allProfilesSummary.Failed)
|
||||
require.Equal(t, uint(1), allProfilesSummary.Verifying)
|
||||
@@ -2713,7 +2719,7 @@ func TestMDMAppleFileVaultSummary(t *testing.T) {
|
||||
|
||||
allProfilesSummary, err = ds.GetMDMAppleProfilesSummary(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fvProfileSummary)
|
||||
require.NotNil(t, allProfilesSummary)
|
||||
require.Equal(t, uint(3), allProfilesSummary.Pending)
|
||||
require.Equal(t, uint(1), allProfilesSummary.Failed)
|
||||
require.Equal(t, uint(1), allProfilesSummary.Verifying)
|
||||
@@ -2743,7 +2749,7 @@ func TestMDMAppleFileVaultSummary(t *testing.T) {
|
||||
|
||||
allProfilesSummary, err = ds.GetMDMAppleProfilesSummary(ctx, &tm.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fvProfileSummary)
|
||||
require.NotNil(t, allProfilesSummary)
|
||||
require.Equal(t, uint(0), allProfilesSummary.Pending)
|
||||
require.Equal(t, uint(0), allProfilesSummary.Failed)
|
||||
require.Equal(t, uint(1), allProfilesSummary.Verifying)
|
||||
@@ -2769,7 +2775,7 @@ func TestMDMAppleFileVaultSummary(t *testing.T) {
|
||||
|
||||
allProfilesSummary, err = ds.GetMDMAppleProfilesSummary(ctx, &tm.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fvProfileSummary)
|
||||
require.NotNil(t, allProfilesSummary)
|
||||
require.Equal(t, uint(0), allProfilesSummary.Pending)
|
||||
require.Equal(t, uint(0), allProfilesSummary.Failed)
|
||||
require.Equal(t, uint(0), allProfilesSummary.Verifying)
|
||||
|
||||
@@ -1210,34 +1210,22 @@ func filterHostsByMacOSSettingsStatus(sql string, opt fleet.HostListOptions, par
|
||||
return sql, params, nil
|
||||
}
|
||||
|
||||
newSQL := ""
|
||||
whereStatus := ""
|
||||
// macOS settings filter is not compatible with the "all teams" option so append the "no
|
||||
// team" filter here (note that filterHostsByTeam applies the "no team" filter if TeamFilter == 0)
|
||||
if opt.TeamFilter == nil {
|
||||
// macOS settings filter is not compatible with the "all teams" option so append the "no
|
||||
// team" filter here (note that filterHostsByTeam applies the "no team" filter if TeamFilter == 0)
|
||||
newSQL += ` AND h.team_id IS NULL`
|
||||
whereStatus += ` AND h.team_id IS NULL`
|
||||
}
|
||||
|
||||
var subquery string
|
||||
var subqueryParams []any
|
||||
var err error
|
||||
switch opt.MacOSSettingsFilter {
|
||||
case fleet.OSSettingsFailed:
|
||||
subquery, subqueryParams, err = subqueryAppleProfileStatus(fleet.MDMDeliveryFailed)
|
||||
case fleet.OSSettingsPending:
|
||||
subquery, subqueryParams, err = subqueryAppleProfileStatus(fleet.MDMDeliveryPending)
|
||||
case fleet.OSSettingsVerifying:
|
||||
subquery, subqueryParams, err = subqueryAppleProfileStatus(fleet.MDMDeliveryVerifying)
|
||||
case fleet.OSSettingsVerified:
|
||||
subquery, subqueryParams, err = subqueryAppleProfileStatus(fleet.MDMDeliveryVerified)
|
||||
}
|
||||
subqueryStatus, paramsStatus, err := subqueryOSSettingsStatusMac()
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("building subquery for %s filter: %w", opt.MacOSSettingsFilter, err)
|
||||
}
|
||||
if subquery != "" {
|
||||
newSQL += fmt.Sprintf(` AND EXISTS (%s)`, subquery)
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return sql + newSQL, append(params, subqueryParams...), nil
|
||||
whereStatus += fmt.Sprintf(` AND %s = ?`, subqueryStatus)
|
||||
paramsStatus = append(paramsStatus, opt.MacOSSettingsFilter)
|
||||
|
||||
return sql + whereStatus, append(params, paramsStatus...), nil
|
||||
}
|
||||
|
||||
func filterHostsByMacOSDiskEncryptionStatus(sql string, opt fleet.HostListOptions, params []interface{}) (string, []interface{}) {
|
||||
@@ -1285,30 +1273,16 @@ func (ds *Datastore) filterHostsByOSSettingsStatus(sql string, opt fleet.HostLis
|
||||
sqlFmt += ` AND h.team_id IS NULL`
|
||||
}
|
||||
var whereMacOS, whereWindows string
|
||||
sqlFmt += ` AND ((h.platform = 'windows' AND (%s)) OR (h.platform = 'darwin' AND (%s)))`
|
||||
sqlFmt += `
|
||||
AND ((h.platform = 'windows' AND (%s))
|
||||
OR (h.platform = 'darwin' AND (%s)))`
|
||||
|
||||
// construct the WHERE for macOS
|
||||
var subqueryMacOS string
|
||||
var paramsMacOS []interface{}
|
||||
var err error
|
||||
switch opt.OSSettingsFilter {
|
||||
case fleet.OSSettingsFailed:
|
||||
subqueryMacOS, paramsMacOS, err = subqueryAppleProfileStatus(fleet.MDMDeliveryFailed)
|
||||
case fleet.OSSettingsPending:
|
||||
subqueryMacOS, paramsMacOS, err = subqueryAppleProfileStatus(fleet.MDMDeliveryPending)
|
||||
case fleet.OSSettingsVerifying:
|
||||
subqueryMacOS, paramsMacOS, err = subqueryAppleProfileStatus(fleet.MDMDeliveryVerifying)
|
||||
case fleet.OSSettingsVerified:
|
||||
subqueryMacOS, paramsMacOS, err = subqueryAppleProfileStatus(fleet.MDMDeliveryVerified)
|
||||
}
|
||||
whereMacOS, paramsMacOS, err := subqueryOSSettingsStatusMac()
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("building subquery for %s filter: %w", opt.OSSettingsFilter, err)
|
||||
}
|
||||
if subqueryMacOS != "" {
|
||||
whereMacOS = "EXISTS (" + subqueryMacOS + ")"
|
||||
} else {
|
||||
whereMacOS = "FALSE"
|
||||
return "", nil, err
|
||||
}
|
||||
whereMacOS += ` = ?`
|
||||
paramsMacOS = append(paramsMacOS, opt.OSSettingsFilter)
|
||||
|
||||
// construct the WHERE for windows
|
||||
whereWindows = `hmdm.name = ? AND hmdm.enrolled = 1 AND hmdm.is_server = 0`
|
||||
|
||||
@@ -364,6 +364,7 @@ func testLabelsListHostsInLabelAndStatus(t *testing.T, db *Datastore) {
|
||||
NodeKey: ptr.String("1"),
|
||||
UUID: "1",
|
||||
Hostname: "foo.local",
|
||||
Platform: "darwin",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -377,6 +378,7 @@ func testLabelsListHostsInLabelAndStatus(t *testing.T, db *Datastore) {
|
||||
NodeKey: ptr.String("2"),
|
||||
UUID: "2",
|
||||
Hostname: "bar.local",
|
||||
Platform: "darwin",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
h3, err := db.NewHost(context.Background(), &fleet.Host{
|
||||
@@ -388,6 +390,7 @@ func testLabelsListHostsInLabelAndStatus(t *testing.T, db *Datastore) {
|
||||
NodeKey: ptr.String("3"),
|
||||
UUID: "3",
|
||||
Hostname: "baz.local",
|
||||
Platform: "darwin",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -427,6 +430,7 @@ func testLabelsListHostsInLabelAndTeamFilter(deferred bool, t *testing.T, db *Da
|
||||
NodeKey: ptr.String("1"),
|
||||
UUID: "1",
|
||||
Hostname: "foo.local",
|
||||
Platform: "darwin",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
@@ -440,6 +444,7 @@ func testLabelsListHostsInLabelAndTeamFilter(deferred bool, t *testing.T, db *Da
|
||||
NodeKey: ptr.String("2"),
|
||||
UUID: "2",
|
||||
Hostname: "bar.local",
|
||||
Platform: "darwin",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -43,12 +44,12 @@ func TestMDMShared(t *testing.T) {
|
||||
{"TestMDMEULA", testMDMEULA},
|
||||
{"TestGetHostCertAssociationsToExpire", testSCEPRenewalHelpers},
|
||||
{"TestSCEPRenewalHelpers", testSCEPRenewalHelpers},
|
||||
{"TestMDMProfilesSummaryAndHostFilters", testMDMProfilesSummaryAndHostFilters},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
defer TruncateTables(t, ds)
|
||||
|
||||
c.fn(t, ds)
|
||||
})
|
||||
}
|
||||
@@ -3330,3 +3331,372 @@ func testSCEPRenewalHelpers(t *testing.T, ds *Datastore) {
|
||||
require.NoError(t, err)
|
||||
checkSCEPRenew(assocs[0], nil)
|
||||
}
|
||||
|
||||
func testMDMProfilesSummaryAndHostFilters(t *testing.T, ds *Datastore) {
|
||||
// TODO: Expand this test to include:
|
||||
// - more scenarios for windows
|
||||
// - disk encryption (mac and windows)
|
||||
// - more scenarios for labels
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
checkSummaryWindows := func(t *testing.T, teamID *uint, expected fleet.MDMProfilesSummary) {
|
||||
ps, err := ds.GetMDMWindowsProfilesSummary(ctx, teamID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ps)
|
||||
require.Equal(t, expected, *ps)
|
||||
}
|
||||
|
||||
checkSummaryMac := func(t *testing.T, teamID *uint, expected fleet.MDMProfilesSummary) {
|
||||
ps, err := ds.GetMDMAppleProfilesSummary(ctx, teamID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ps)
|
||||
require.Equal(t, expected, *ps)
|
||||
}
|
||||
|
||||
checkListHostsFilterOSSettings := func(t *testing.T, teamID *uint, status fleet.OSSettingsStatus, expectedIDs []uint) {
|
||||
gotHosts, err := ds.ListHosts(ctx, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{TeamFilter: teamID, OSSettingsFilter: status})
|
||||
require.NoError(t, err)
|
||||
if len(expectedIDs) != len(gotHosts) {
|
||||
gotIDs := make([]uint, len(gotHosts))
|
||||
for _, h := range gotHosts {
|
||||
gotIDs = append(gotIDs, h.ID)
|
||||
}
|
||||
require.Len(t, gotHosts, len(expectedIDs), fmt.Sprintf("status: %s expected: %v got: %v", status, expectedIDs, gotIDs))
|
||||
|
||||
}
|
||||
for _, h := range gotHosts {
|
||||
require.Contains(t, expectedIDs, h.ID)
|
||||
}
|
||||
|
||||
count, err := ds.CountHosts(ctx, fleet.TeamFilter{User: test.UserAdmin}, fleet.HostListOptions{TeamFilter: teamID, OSSettingsFilter: status})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(expectedIDs), count, "status: %s", status)
|
||||
}
|
||||
|
||||
type hostIDsByProfileStatus map[fleet.MDMDeliveryStatus][]uint
|
||||
|
||||
checkExpected := func(t *testing.T, teamID *uint, ep hostIDsByProfileStatus) {
|
||||
expectSummaryWindows := map[fleet.MDMDeliveryStatus]uint{}
|
||||
expectSummaryMac := map[fleet.MDMDeliveryStatus]uint{}
|
||||
for status, ids := range ep {
|
||||
if len(ids) > 0 {
|
||||
for _, id := range ids {
|
||||
if id < 5 {
|
||||
expectSummaryWindows[status]++
|
||||
} else {
|
||||
expectSummaryMac[status]++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkSummaryMac(t, teamID, fleet.MDMProfilesSummary{
|
||||
Pending: expectSummaryMac[fleet.MDMDeliveryPending],
|
||||
Failed: expectSummaryMac[fleet.MDMDeliveryFailed],
|
||||
Verifying: expectSummaryMac[fleet.MDMDeliveryVerifying],
|
||||
Verified: expectSummaryMac[fleet.MDMDeliveryVerified],
|
||||
})
|
||||
|
||||
checkSummaryWindows(t, teamID, fleet.MDMProfilesSummary{
|
||||
Pending: expectSummaryWindows[fleet.MDMDeliveryPending],
|
||||
Failed: expectSummaryWindows[fleet.MDMDeliveryFailed],
|
||||
Verifying: expectSummaryWindows[fleet.MDMDeliveryVerifying],
|
||||
Verified: expectSummaryWindows[fleet.MDMDeliveryVerified],
|
||||
})
|
||||
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsVerified, ep[fleet.MDMDeliveryVerified])
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsVerifying, ep[fleet.MDMDeliveryVerifying])
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsFailed, ep[fleet.MDMDeliveryFailed])
|
||||
checkListHostsFilterOSSettings(t, teamID, fleet.OSSettingsPending, ep[fleet.MDMDeliveryPending])
|
||||
}
|
||||
|
||||
// checkWinHostProfiles := func(t *testing.T, hostUUID string, statusByProfUUID map[string]string) {
|
||||
// profs, err := ds.GetHostMDMWindowsProfiles(ctx, hostUUID)
|
||||
// require.NoError(t, err)
|
||||
// require.Len(t, profs, len(statusByProfUUID))
|
||||
// for _, prof := range profs {
|
||||
// ep, ok := statusByProfUUID[prof.ProfileUUID]
|
||||
// require.True(t, ok)
|
||||
// require.Equal(t, ep, prof.Status)
|
||||
// }
|
||||
// }
|
||||
|
||||
checkMacHostProfiles := func(t *testing.T, hostUUID string, statusByProfUUID map[string]string) {
|
||||
profs, err := ds.GetHostMDMAppleProfiles(ctx, hostUUID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, profs, len(statusByProfUUID))
|
||||
for _, prof := range profs {
|
||||
ep, ok := statusByProfUUID[prof.ProfileUUID]
|
||||
require.True(t, ok)
|
||||
require.NotNil(t, prof.Status)
|
||||
require.Equal(t, fleet.MDMDeliveryStatus(ep), *prof.Status)
|
||||
}
|
||||
}
|
||||
|
||||
upsertHostProfileStatus := func(t *testing.T, hostUUID string, profUUID string, status *fleet.MDMDeliveryStatus) {
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
var table string
|
||||
var profType string
|
||||
switch {
|
||||
case strings.HasPrefix(profUUID, "a"):
|
||||
table = "host_mdm_apple_profiles"
|
||||
profType = "profile"
|
||||
case strings.HasPrefix(profUUID, "w"):
|
||||
table = "host_mdm_windows_profiles"
|
||||
profType = "profile"
|
||||
case strings.HasPrefix(profUUID, "d"):
|
||||
table = "host_mdm_apple_declarations"
|
||||
profType = "declaration"
|
||||
default:
|
||||
require.FailNow(t, "unknown profile type")
|
||||
}
|
||||
stmt := fmt.Sprintf(`INSERT INTO %s (host_uuid, %s_uuid, status) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE status = ?`, table, profType)
|
||||
_, err := q.ExecContext(ctx, stmt, hostUUID, profUUID, status, status)
|
||||
if err != nil {
|
||||
require.NoError(t, err)
|
||||
return err
|
||||
}
|
||||
stmt = fmt.Sprintf(`UPDATE %s SET operation_type = ? WHERE host_uuid = ? AND %s_uuid = ?`, table, profType)
|
||||
_, err = q.ExecContext(ctx, stmt, fleet.MDMOperationTypeInstall, hostUUID, profUUID)
|
||||
require.NoError(t, err)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
cleanupTables := func(t *testing.T) {
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, `DELETE FROM host_mdm_windows_profiles`)
|
||||
return err
|
||||
})
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, `DELETE FROM host_mdm_apple_profiles`)
|
||||
return err
|
||||
})
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, `DELETE FROM host_mdm_apple_declarations`)
|
||||
return err
|
||||
})
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, `DELETE FROM host_disk_encryption_keys`)
|
||||
return err
|
||||
})
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, `DELETE FROM host_disks`)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// updateHostDisks := func(t *testing.T, hostID uint, encrypted bool, updated_at time.Time) {
|
||||
// ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
// stmt := `UPDATE host_disks SET encrypted = ?, updated_at = ? where host_id = ?`
|
||||
// _, err := q.ExecContext(ctx, stmt, encrypted, updated_at, hostID)
|
||||
// return err
|
||||
// })
|
||||
// }
|
||||
|
||||
// Create some hosts
|
||||
var hosts []*fleet.Host
|
||||
macHostsByID := make(map[uint]*fleet.Host, 5)
|
||||
winHostsByID := make(map[uint]*fleet.Host, 5)
|
||||
for i := 0; i < 10; i++ {
|
||||
p := "windows"
|
||||
if i >= 5 {
|
||||
p = "darwin"
|
||||
}
|
||||
u := uuid.New().String()
|
||||
h, err := ds.NewHost(ctx, &fleet.Host{
|
||||
DetailUpdatedAt: time.Now(),
|
||||
LabelUpdatedAt: time.Now(),
|
||||
PolicyUpdatedAt: time.Now(),
|
||||
SeenTime: time.Now(),
|
||||
NodeKey: &u,
|
||||
UUID: u,
|
||||
Hostname: u,
|
||||
Platform: p,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, h)
|
||||
hosts = append(hosts, h)
|
||||
if p == "darwin" {
|
||||
macHostsByID[h.ID] = h
|
||||
} else {
|
||||
winHostsByID[h.ID] = h
|
||||
}
|
||||
|
||||
require.NoError(t, ds.SetOrUpdateMDMData(ctx, h.ID, false, true, "https://example.com", false, fleet.WellKnownMDMFleet, ""))
|
||||
}
|
||||
|
||||
checkExpected(t, nil, nil)
|
||||
|
||||
upsertHostProfileStatus(t, hosts[0].UUID, "w1", &fleet.MDMDeliveryPending)
|
||||
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||||
})
|
||||
|
||||
// add some mac profiles with different statuses
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "a1", &fleet.MDMDeliveryFailed)
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "a2", &fleet.MDMDeliveryPending)
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "a3", &fleet.MDMDeliveryVerifying)
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "a4", &fleet.MDMDeliveryVerified)
|
||||
|
||||
// add some mac declarations with different statuses
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d1", &fleet.MDMDeliveryFailed)
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d2", &fleet.MDMDeliveryPending)
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d3", &fleet.MDMDeliveryVerifying)
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d4", &fleet.MDMDeliveryVerified)
|
||||
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||||
fleet.MDMDeliveryFailed: []uint{hosts[9].ID},
|
||||
})
|
||||
expectedHostProfiles := map[string]string{
|
||||
"a1": "failed",
|
||||
"a2": "pending",
|
||||
"a3": "verifying",
|
||||
"a4": "verified",
|
||||
"d1": "failed",
|
||||
"d2": "pending",
|
||||
"d3": "verifying",
|
||||
"d4": "verified",
|
||||
}
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// set failed mac profile to pending, still failed because of failed declaration
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "a1", &fleet.MDMDeliveryPending)
|
||||
expectedHostProfiles["a1"] = "pending"
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||||
fleet.MDMDeliveryFailed: []uint{hosts[9].ID},
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// set failed mac declaration to pending, now host stsatus is pending
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d1", &fleet.MDMDeliveryPending)
|
||||
expectedHostProfiles["d1"] = "pending"
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID, hosts[9].ID},
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// set pending mac declaration to failed, host status is now failed
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d2", &fleet.MDMDeliveryFailed)
|
||||
expectedHostProfiles["d2"] = "failed"
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||||
fleet.MDMDeliveryFailed: []uint{hosts[9].ID},
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// set failed mac declaration to verifying, host status is now pending
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d2", &fleet.MDMDeliveryVerifying)
|
||||
expectedHostProfiles["d2"] = "verifying"
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID, hosts[9].ID},
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// set pending mac profiles to verifying, host status is still pending because d1 is still pending
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "a1", &fleet.MDMDeliveryVerifying)
|
||||
expectedHostProfiles["a1"] = "verifying"
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "a2", &fleet.MDMDeliveryVerifying)
|
||||
expectedHostProfiles["a2"] = "verifying"
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID, hosts[9].ID},
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// set pending mac declarations to verifying, host status is now verifying
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d1", &fleet.MDMDeliveryVerifying)
|
||||
expectedHostProfiles["d1"] = "verifying"
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||||
fleet.MDMDeliveryVerifying: []uint{hosts[9].ID},
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// set a mac profile to failed, host status is now failed
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "a1", &fleet.MDMDeliveryFailed)
|
||||
expectedHostProfiles["a1"] = "failed"
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||||
fleet.MDMDeliveryFailed: []uint{hosts[9].ID},
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// set mac profiles to verified, host status is now verifying because declarations are still
|
||||
// verifying
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "a1", &fleet.MDMDeliveryVerified)
|
||||
expectedHostProfiles["a1"] = "verified"
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "a2", &fleet.MDMDeliveryVerified)
|
||||
expectedHostProfiles["a2"] = "verified"
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "a3", &fleet.MDMDeliveryVerified)
|
||||
expectedHostProfiles["a3"] = "verified"
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "a4", &fleet.MDMDeliveryVerified)
|
||||
expectedHostProfiles["a4"] = "verified"
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||||
fleet.MDMDeliveryVerifying: []uint{hosts[9].ID},
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// set mac declarations to verified, host status is now verified
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d1", &fleet.MDMDeliveryVerified)
|
||||
expectedHostProfiles["d1"] = "verified"
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d2", &fleet.MDMDeliveryVerified)
|
||||
expectedHostProfiles["d2"] = "verified"
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d3", &fleet.MDMDeliveryVerified)
|
||||
expectedHostProfiles["d3"] = "verified"
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||||
fleet.MDMDeliveryVerified: []uint{hosts[9].ID},
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// set a mac declaration to nil, host status is now pending
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d1", nil)
|
||||
expectedHostProfiles["d1"] = "pending"
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID, hosts[9].ID},
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// works as expected if we remove mac declarations
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, `DELETE FROM host_mdm_apple_declarations`)
|
||||
return err
|
||||
})
|
||||
delete(expectedHostProfiles, "d1")
|
||||
delete(expectedHostProfiles, "d2")
|
||||
delete(expectedHostProfiles, "d3")
|
||||
delete(expectedHostProfiles, "d4")
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||||
fleet.MDMDeliveryVerified: []uint{hosts[9].ID}, // all profiles were verified
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// works as expected if we remove mac profiles
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, `DELETE FROM host_mdm_apple_profiles`)
|
||||
return err
|
||||
})
|
||||
delete(expectedHostProfiles, "a1")
|
||||
delete(expectedHostProfiles, "a2")
|
||||
delete(expectedHostProfiles, "a3")
|
||||
delete(expectedHostProfiles, "a4")
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID},
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
// works as expected if declarations but no profiles
|
||||
upsertHostProfileStatus(t, hosts[9].UUID, "d1", &fleet.MDMDeliveryPending)
|
||||
expectedHostProfiles["d1"] = "pending"
|
||||
checkExpected(t, nil, hostIDsByProfileStatus{
|
||||
fleet.MDMDeliveryPending: []uint{hosts[0].ID, hosts[9].ID},
|
||||
})
|
||||
checkMacHostProfiles(t, hosts[9].UUID, expectedHostProfiles)
|
||||
|
||||
cleanupTables(t)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user