@@ -0,0 +1 @@
|
||||
- Hosts that are restored from ABM no longer have old activities in their feed
|
||||
@@ -249,6 +249,7 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint
|
||||
LEFT OUTER JOIN
|
||||
host_software_installs hsi ON hsi.execution_id = hsr.execution_id
|
||||
WHERE hsr.host_id = :host_id AND
|
||||
hsr.host_deleted_at IS NULL AND
|
||||
exit_code IS NULL AND
|
||||
hsi.execution_id IS NULL AND
|
||||
(sync_request = 0 OR hsr.created_at >= DATE_SUB(NOW(), INTERVAL :max_wait_time SECOND))`,
|
||||
@@ -256,11 +257,13 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint
|
||||
COUNT(*) c
|
||||
FROM host_software_installs hsi
|
||||
WHERE hsi.host_id = :host_id AND hsi.software_installer_id IS NOT NULL AND
|
||||
hsi.host_deleted_at IS NULL AND
|
||||
hsi.status = :software_status_install_pending`,
|
||||
`SELECT
|
||||
COUNT(*) c
|
||||
FROM host_software_installs hsi
|
||||
WHERE hsi.host_id = :host_id AND hsi.software_installer_id IS NOT NULL AND
|
||||
hsi.host_deleted_at IS NULL AND
|
||||
hsi.status = :software_status_uninstall_pending`,
|
||||
`
|
||||
SELECT
|
||||
@@ -328,6 +331,7 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint
|
||||
setup_experience_scripts ses ON ses.id = hsr.setup_experience_script_id
|
||||
WHERE
|
||||
hsr.host_id = :host_id AND
|
||||
hsr.host_deleted_at IS NULL AND
|
||||
hsr.exit_code IS NULL AND
|
||||
(
|
||||
hsr.sync_request = 0 OR
|
||||
@@ -371,6 +375,7 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint
|
||||
host_display_names hdn ON hdn.host_id = hsi.host_id
|
||||
WHERE
|
||||
hsi.host_id = :host_id AND
|
||||
hsi.host_deleted_at IS NULL AND
|
||||
hsi.status = :software_status_install_pending
|
||||
`,
|
||||
// list pending software uninstalls
|
||||
@@ -407,6 +412,7 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint
|
||||
host_display_names hdn ON hdn.host_id = hsi.host_id
|
||||
WHERE
|
||||
hsi.host_id = :host_id AND
|
||||
hsi.host_deleted_at IS NULL AND
|
||||
hsi.status = :software_status_uninstall_pending
|
||||
`,
|
||||
// list pending VPP installs
|
||||
@@ -433,15 +439,15 @@ SELECT
|
||||
) AS details
|
||||
FROM
|
||||
host_vpp_software_installs hvsi
|
||||
INNER JOIN
|
||||
INNER JOIN
|
||||
nano_view_queue nvq ON nvq.command_uuid = hvsi.command_uuid
|
||||
LEFT OUTER JOIN
|
||||
LEFT OUTER JOIN
|
||||
users u ON hvsi.user_id = u.id
|
||||
LEFT OUTER JOIN
|
||||
LEFT OUTER JOIN
|
||||
host_display_names hdn ON hdn.host_id = hvsi.host_id
|
||||
LEFT OUTER JOIN
|
||||
LEFT OUTER JOIN
|
||||
vpp_apps vpa ON hvsi.adam_id = vpa.adam_id AND hvsi.platform = vpa.platform
|
||||
LEFT OUTER JOIN
|
||||
LEFT OUTER JOIN
|
||||
software_titles st ON st.id = vpa.title_id
|
||||
WHERE
|
||||
nvq.status IS NULL
|
||||
|
||||
@@ -391,13 +391,15 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) {
|
||||
|
||||
test.CreateInsertGlobalVPPToken(t, ds)
|
||||
|
||||
// create three hosts
|
||||
// create four hosts
|
||||
h1 := test.NewHost(t, ds, "h1.local", "10.10.10.1", "1", "1", time.Now())
|
||||
nanoEnrollAndSetHostMDMData(t, ds, h1, false)
|
||||
h2 := test.NewHost(t, ds, "h2.local", "10.10.10.2", "2", "2", time.Now())
|
||||
nanoEnrollAndSetHostMDMData(t, ds, h2, false)
|
||||
h3 := test.NewHost(t, ds, "h3.local", "10.10.10.3", "3", "3", time.Now())
|
||||
nanoEnrollAndSetHostMDMData(t, ds, h3, false)
|
||||
h4 := test.NewHost(t, ds, "h4.local", "10.10.10.4", "4", "4", time.Now())
|
||||
nanoEnrollAndSetHostMDMData(t, ds, h4, false)
|
||||
|
||||
// create a couple of named scripts
|
||||
scr1, err := ds.NewScript(ctx, &fleet.Script{
|
||||
@@ -581,6 +583,22 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) {
|
||||
err = ds.DeleteSoftwareInstaller(ctx, sw3Meta.InstallerID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Setup host 4. We will create upcoming activities, then
|
||||
// delete and "restore" the host, similar to what would happen
|
||||
// if you delete an ABM DEP host.
|
||||
_, err = ds.NewHostScriptExecutionRequest(ctx, &fleet.HostScriptRequestPayload{HostID: h4.ID, ScriptID: &scr1.ID, ScriptContents: scr1.ScriptContents, UserID: &u.ID})
|
||||
require.NoError(t, err)
|
||||
// h4A := hsr.ExecutionID
|
||||
// h4Bar, err := ds.InsertSoftwareInstallRequest(ctx, h4.ID, sw2Meta.InstallerID, false, nil)
|
||||
_, err = ds.InsertSoftwareInstallRequest(ctx, h4.ID, sw2Meta.InstallerID, false, nil)
|
||||
require.NoError(t, err)
|
||||
// Delete the host
|
||||
err = ds.DeleteHost(ctx, h4.ID)
|
||||
require.NoError(t, err)
|
||||
// DEP "restore" the host
|
||||
err = ds.RestoreMDMApplePendingDEPHost(ctx, h4)
|
||||
require.NoError(t, err)
|
||||
|
||||
// force-set the order of the created_at timestamps
|
||||
endTime := SetOrderedCreatedAtTimestamps(t, ds, time.Now(), "host_script_results", "execution_id", h1A, h1B)
|
||||
endTime = SetOrderedCreatedAtTimestamps(t, ds, endTime, "host_software_installs", "execution_id", h1FooFailed, h1Bar)
|
||||
@@ -685,6 +703,12 @@ func testListHostUpcomingActivities(t *testing.T, ds *Datastore) {
|
||||
wantExecs: []string{},
|
||||
wantMeta: &fleet.PaginationMetadata{HasNextResults: false, HasPreviousResults: false, TotalResults: 0},
|
||||
},
|
||||
{
|
||||
opts: fleet.ListOptions{},
|
||||
hostID: h4.ID,
|
||||
wantExecs: []string{},
|
||||
wantMeta: &fleet.PaginationMetadata{HasNextResults: false, HasPreviousResults: false, TotalResults: 0},
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(fmt.Sprintf("%v: %#v", c.hostID, c.opts), func(t *testing.T) {
|
||||
|
||||
@@ -241,6 +241,7 @@ func (ds *Datastore) ListPendingHostScriptExecutions(ctx context.Context, hostID
|
||||
host_script_results
|
||||
WHERE
|
||||
host_id = ? AND
|
||||
host_deleted_at IS NULL AND
|
||||
%s
|
||||
%s
|
||||
ORDER BY
|
||||
@@ -504,7 +505,7 @@ func (ds *Datastore) deletePendingHostScriptExecutionsForPolicy(ctx context.Cont
|
||||
deleteStmt := fmt.Sprintf(`
|
||||
DELETE FROM
|
||||
host_script_results
|
||||
WHERE
|
||||
WHERE
|
||||
policy_id = ? AND
|
||||
script_id IN (
|
||||
SELECT id FROM scripts WHERE scripts.global_or_team_id = ?
|
||||
|
||||
@@ -26,6 +26,7 @@ func TestScripts(t *testing.T) {
|
||||
fn func(t *testing.T, ds *Datastore)
|
||||
}{
|
||||
{"HostScriptResult", testHostScriptResult},
|
||||
{"DEPRestoredHost", testListPendingScriptDEPRestoration},
|
||||
{"Scripts", testScripts},
|
||||
{"ListScripts", testListScripts},
|
||||
{"GetHostScriptDetails", testGetHostScriptDetails},
|
||||
@@ -259,6 +260,54 @@ func testHostScriptResult(t *testing.T, ds *Datastore) {
|
||||
require.EqualValues(t, -1, *unsignedScriptResult.ExitCode)
|
||||
}
|
||||
|
||||
func testListPendingScriptDEPRestoration(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
|
||||
host := test.NewHost(t, ds, "host", "10.0.0.1", "1", "uuid1", time.Now())
|
||||
|
||||
// no script saved yet
|
||||
pending, err := ds.ListPendingHostScriptExecutions(ctx, host.ID, false)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, pending)
|
||||
|
||||
// create a createdScript execution request (with a user)
|
||||
u := test.NewUser(t, ds, "Bob", "bob@example.com", true)
|
||||
createdScript, err := ds.NewHostScriptExecutionRequest(ctx, &fleet.HostScriptRequestPayload{
|
||||
HostID: host.ID,
|
||||
ScriptContents: "echo",
|
||||
UserID: &u.ID,
|
||||
SyncRequest: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotZero(t, createdScript.ID)
|
||||
require.NotEmpty(t, createdScript.ExecutionID)
|
||||
require.Equal(t, uint(1), createdScript.HostID)
|
||||
require.NotEmpty(t, createdScript.ExecutionID)
|
||||
require.Equal(t, "echo", createdScript.ScriptContents)
|
||||
require.Nil(t, createdScript.ExitCode)
|
||||
require.Empty(t, createdScript.Output)
|
||||
require.NotNil(t, createdScript.UserID)
|
||||
require.Equal(t, u.ID, *createdScript.UserID)
|
||||
require.True(t, createdScript.SyncRequest)
|
||||
|
||||
// the script execution is now listed as pending for this host
|
||||
pending, err = ds.ListPendingHostScriptExecutions(ctx, host.ID, false)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pending, 1)
|
||||
require.Equal(t, createdScript.ID, pending[0].ID)
|
||||
|
||||
err = ds.DeleteHost(ctx, host.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ds.RestoreMDMApplePendingDEPHost(ctx, host)
|
||||
require.NoError(t, err)
|
||||
|
||||
// the script execution is no longer listed as pending for this host
|
||||
pending, err = ds.ListPendingHostScriptExecutions(ctx, host.ID, false)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pending, 0)
|
||||
}
|
||||
|
||||
func testScripts(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
@@ -2259,7 +2259,7 @@ INNER JOIN software_cve scve ON scve.software_id = s.id
|
||||
-- get the status of the latest attempt; 27-1 is the length of the timestamp
|
||||
SUBSTRING(MAX(CONCAT(created_at, COALESCE(status, ''))), 27) AS hsi_status
|
||||
FROM host_software_installs
|
||||
WHERE host_id = :host_id AND removed = 0
|
||||
WHERE host_id = :host_id AND removed = 0 AND host_deleted_at IS NULL
|
||||
GROUP BY host_id, software_installer_id
|
||||
UNION
|
||||
-- get latest install attempt
|
||||
@@ -2271,7 +2271,7 @@ INNER JOIN software_cve scve ON scve.software_id = s.id
|
||||
NULL as hsi_uninstalled_at, NULL as hsi_uninstall_execution_id,
|
||||
NULL as hsi_status
|
||||
FROM host_software_installs
|
||||
WHERE host_id = :host_id AND removed = 0 AND uninstall = 0
|
||||
WHERE host_id = :host_id AND removed = 0 AND uninstall = 0 AND host_deleted_at IS NULL
|
||||
GROUP BY host_id, software_installer_id
|
||||
UNION
|
||||
-- get latest uninstall attempt
|
||||
@@ -2283,7 +2283,7 @@ INNER JOIN software_cve scve ON scve.software_id = s.id
|
||||
SUBSTRING(MAX(CONCAT(created_at, execution_id)), 27) AS hsi_uninstall_execution_id,
|
||||
NULL as hsi_status
|
||||
FROM host_software_installs
|
||||
WHERE host_id = :host_id AND removed = 0 AND uninstall = 1
|
||||
WHERE host_id = :host_id AND removed = 0 AND uninstall = 1 AND host_deleted_at IS NULL
|
||||
GROUP BY host_id, software_installer_id
|
||||
) as hsi_group
|
||||
GROUP BY hsi_group.host_id, hsi_group.software_installer_id
|
||||
@@ -2311,7 +2311,7 @@ INNER JOIN software_cve scve ON scve.software_id = s.id
|
||||
-- on host (via installer or VPP app). If only available for install is
|
||||
-- requested, then the software installed on host clause is empty.
|
||||
( %s hsi.host_id IS NOT NULL OR hvsi.host_id IS NOT NULL )
|
||||
AND
|
||||
AND
|
||||
-- label membership check
|
||||
(
|
||||
-- do the label membership check only for software installers
|
||||
@@ -2346,9 +2346,9 @@ INNER JOIN software_cve scve ON scve.software_id = s.id
|
||||
|
||||
UNION
|
||||
|
||||
-- exclude any, ignore software that depends on labels created
|
||||
-- _after_ the label_updated_at timestamp of the host (because
|
||||
-- we don't have results for that label yet, the host may or may
|
||||
-- exclude any, ignore software that depends on labels created
|
||||
-- _after_ the label_updated_at timestamp of the host (because
|
||||
-- we don't have results for that label yet, the host may or may
|
||||
-- not be a member).
|
||||
SELECT
|
||||
COUNT(*) AS count_installer_labels,
|
||||
@@ -2358,7 +2358,7 @@ INNER JOIN software_cve scve ON scve.software_id = s.id
|
||||
software_installer_labels sil
|
||||
LEFT OUTER JOIN labels lbl
|
||||
ON lbl.id = sil.label_id
|
||||
LEFT OUTER JOIN label_membership lm
|
||||
LEFT OUTER JOIN label_membership lm
|
||||
ON lm.label_id = sil.label_id AND lm.host_id = :host_id
|
||||
WHERE
|
||||
sil.software_installer_id = si.id
|
||||
@@ -2473,9 +2473,9 @@ INNER JOIN software_cve scve ON scve.software_id = s.id
|
||||
|
||||
UNION
|
||||
|
||||
-- exclude any, ignore software that depends on labels created
|
||||
-- _after_ the label_updated_at timestamp of the host (because
|
||||
-- we don't have results for that label yet, the host may or may
|
||||
-- exclude any, ignore software that depends on labels created
|
||||
-- _after_ the label_updated_at timestamp of the host (because
|
||||
-- we don't have results for that label yet, the host may or may
|
||||
-- not be a member).
|
||||
SELECT
|
||||
COUNT(*) AS count_installer_labels,
|
||||
@@ -2485,7 +2485,7 @@ INNER JOIN software_cve scve ON scve.software_id = s.id
|
||||
software_installer_labels sil
|
||||
LEFT OUTER JOIN labels lbl
|
||||
ON lbl.id = sil.label_id
|
||||
LEFT OUTER JOIN label_membership lm
|
||||
LEFT OUTER JOIN label_membership lm
|
||||
ON lm.label_id = sil.label_id AND lm.host_id = :host_id
|
||||
WHERE
|
||||
sil.software_installer_id = si.id
|
||||
|
||||
@@ -25,6 +25,8 @@ func (ds *Datastore) ListPendingSoftwareInstalls(ctx context.Context, hostID uin
|
||||
host_software_installs
|
||||
WHERE
|
||||
host_id = ?
|
||||
AND
|
||||
host_deleted_at IS NULL
|
||||
AND
|
||||
status = ?
|
||||
ORDER BY
|
||||
@@ -1020,7 +1022,7 @@ func (ds *Datastore) GetHostLastInstallData(ctx context.Context, hostID, install
|
||||
MAX(id)
|
||||
FROM host_software_installs
|
||||
WHERE
|
||||
software_installer_id = :installer_id AND host_id = :host_id
|
||||
software_installer_id = :installer_id AND host_id = :host_id AND host_deleted_at IS NULL
|
||||
GROUP BY
|
||||
host_id, software_installer_id)
|
||||
`
|
||||
@@ -1709,24 +1711,24 @@ func (ds *Datastore) IsSoftwareInstallerLabelScoped(ctx context.Context, install
|
||||
|
||||
UNION
|
||||
|
||||
-- exclude any, ignore software that depends on labels created
|
||||
-- _after_ the label_updated_at timestamp of the host (because
|
||||
-- we don't have results for that label yet, the host may or may
|
||||
-- exclude any, ignore software that depends on labels created
|
||||
-- _after_ the label_updated_at timestamp of the host (because
|
||||
-- we don't have results for that label yet, the host may or may
|
||||
-- not be a member).
|
||||
SELECT
|
||||
COUNT(*) AS count_installer_labels,
|
||||
COUNT(lm.label_id) AS count_host_labels,
|
||||
SUM(CASE
|
||||
WHEN
|
||||
lbl.created_at IS NOT NULL AND (SELECT label_updated_at FROM hosts WHERE id = :host_id) >= lbl.created_at THEN 1
|
||||
ELSE
|
||||
0
|
||||
SUM(CASE
|
||||
WHEN
|
||||
lbl.created_at IS NOT NULL AND (SELECT label_updated_at FROM hosts WHERE id = :host_id) >= lbl.created_at THEN 1
|
||||
ELSE
|
||||
0
|
||||
END) as count_host_updated_after_labels
|
||||
FROM
|
||||
software_installer_labels sil
|
||||
LEFT OUTER JOIN labels lbl
|
||||
ON lbl.id = sil.label_id
|
||||
LEFT OUTER JOIN label_membership lm
|
||||
LEFT OUTER JOIN label_membership lm
|
||||
ON lm.label_id = sil.label_id AND lm.host_id = :host_id
|
||||
WHERE
|
||||
sil.software_installer_id = :installer_id
|
||||
@@ -1756,7 +1758,7 @@ func (ds *Datastore) IsSoftwareInstallerLabelScoped(ctx context.Context, install
|
||||
return res, nil
|
||||
}
|
||||
|
||||
const labelScopedFilter = `
|
||||
const labelScopedFilter = `
|
||||
SELECT
|
||||
1
|
||||
FROM (
|
||||
|
||||
@@ -55,6 +55,7 @@ func testListPendingSoftwareInstalls(t *testing.T, ds *Datastore) {
|
||||
|
||||
host1 := test.NewHost(t, ds, "host1", "1", "host1key", "host1uuid", time.Now())
|
||||
host2 := test.NewHost(t, ds, "host2", "2", "host2key", "host2uuid", time.Now())
|
||||
host3 := test.NewHost(t, ds, "host3", "3", "host3key", "host3uuid", time.Now())
|
||||
user1 := test.NewUser(t, ds, "Alice", "alice@example.com", true)
|
||||
|
||||
err := ds.UpsertSecretVariables(ctx, []fleet.SecretVariable{
|
||||
@@ -200,6 +201,21 @@ func testListPendingSoftwareInstalls(t *testing.T, ds *Datastore) {
|
||||
require.Equal(t, installerID3, exec2.InstallerID)
|
||||
require.Equal(t, "SELECT 3", exec2.PreInstallCondition)
|
||||
require.True(t, exec2.SelfService)
|
||||
|
||||
// Create install request, don't fulfil it, delete and restore host.
|
||||
// Should not appear in list of pending installs for that host.
|
||||
_, err = ds.InsertSoftwareInstallRequest(ctx, host3.ID, installerID1, false, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ds.DeleteHost(ctx, host3.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = ds.RestoreMDMApplePendingDEPHost(ctx, host3)
|
||||
require.NoError(t, err)
|
||||
|
||||
hostInstalls4, err := ds.ListPendingSoftwareInstalls(ctx, host3.ID)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, hostInstalls4)
|
||||
}
|
||||
|
||||
func testSoftwareInstallRequests(t *testing.T, ds *Datastore) {
|
||||
|
||||
Reference in New Issue
Block a user