diff --git a/changes/41631-not-installed b/changes/41631-not-installed new file mode 100644 index 0000000000..3ea369eda4 --- /dev/null +++ b/changes/41631-not-installed @@ -0,0 +1,2 @@ +- Fixed issue where the `include_available_for_install` query param wasn't being applied correctly +to the `GET /api/latest/fleet/hosts/{id}/software` endpoint. diff --git a/server/service/hosts.go b/server/service/hosts.go index b1520fd643..007be2d5ea 100644 --- a/server/service/hosts.go +++ b/server/service/hosts.go @@ -3735,19 +3735,15 @@ func getHostSoftwareEndpoint(ctx context.Context, request interface{}, svc fleet } func (svc *Service) ListHostSoftware(ctx context.Context, hostID uint, opts fleet.HostSoftwareTitleListOptions) ([]*fleet.HostSoftwareWithInstaller, *fleet.PaginationMetadata, error) { - // When accessed via "My device", we default to only showing inventory (excluding software available for install - // but not in inventory), unless we're asked to filter to self-service software only. - // - // Otherwise (e.g. host software UI within Fleet's admin interface), the default is to show both installed and - // available-for-install software, to maintain existing API behavior. This behavior can be explicitly overridden - // if needed (see opts.IncludeAvailableForInstallExplicitlySet). + // Default to only showing inventory (excluding software available for install but not in + // inventory). Callers can explicitly set include_available_for_install=true to also see + // library items. See #41631. var includeAvailableForInstall bool var host *fleet.Host if !svc.authz.IsAuthenticatedWith(ctx, authzctx.AuthnDeviceToken) && !svc.authz.IsAuthenticatedWith(ctx, authzctx.AuthnDeviceCertificate) && !svc.authz.IsAuthenticatedWith(ctx, authzctx.AuthnDeviceURL) { - includeAvailableForInstall = true if err := svc.authz.Authorize(ctx, &fleet.Host{}, fleet.ActionList); err != nil { return nil, nil, err diff --git a/server/service/integration_enterprise_test.go b/server/service/integration_enterprise_test.go index c77acb7342..553508cfbb 100644 --- a/server/service/integration_enterprise_test.go +++ b/server/service/integration_enterprise_test.go @@ -11989,9 +11989,17 @@ func (s *integrationEnterpriseTestSuite) TestListHostSoftware() { return err }) - // available installer is returned by user-authenticated endpoint + // default (no include_available_for_install param) should only return installed software, not library items (#41631) getHostSw = getHostSoftwareResponse{} s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host.ID), nil, http.StatusOK, &getHostSw) + require.Len(t, getHostSw.Software, 2) // foo and bar only — ruby is not installed, just available + require.Equal(t, getHostSw.Software[0].Name, "bar") + require.Equal(t, getHostSw.Software[1].Name, "foo") + require.Len(t, getHostSw.Software[1].InstalledVersions, 2) + + // user authenticated endpoint, explicitly request to include available for install software + getHostSw = getHostSoftwareResponse{} + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software?include_available_for_install=true", host.ID), nil, http.StatusOK, &getHostSw) require.Len(t, getHostSw.Software, 3) // foo, bar and ruby.deb require.Equal(t, getHostSw.Software[0].Name, "bar") require.Equal(t, getHostSw.Software[1].Name, "foo") @@ -12005,7 +12013,7 @@ func (s *integrationEnterpriseTestSuite) TestListHostSoftware() { require.True(t, *getHostSw.Software[2].SoftwarePackage.SelfService) require.Nil(t, getHostSw.Software[2].Status) - // user authenticated endpoint, but explicitly request to not include available for install software + // user authenticated endpoint, explicitly request to not include available for install software getHostSw = getHostSoftwareResponse{} s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software?include_available_for_install=false", host.ID), nil, http.StatusOK, &getHostSw) require.Len(t, getHostSw.Software, 2) // foo and bar @@ -12110,9 +12118,16 @@ func (s *integrationEnterpriseTestSuite) TestListHostSoftware() { s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/%d/install", host.ID, titleID), nil, http.StatusAccepted, &installResp) - // still returned by user-authenticated endpoint, now pending + // default (no param) should still only return installed software, even after install is requested (#41631) getHostSw = getHostSoftwareResponse{} s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host.ID), nil, http.StatusOK, &getHostSw) + require.Len(t, getHostSw.Software, 2) // foo and bar — ruby has a pending install but is not yet installed + require.Equal(t, getHostSw.Software[0].Name, "bar") + require.Equal(t, getHostSw.Software[1].Name, "foo") + + // with include_available_for_install=true, the pending install is visible + getHostSw = getHostSoftwareResponse{} + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software?include_available_for_install=true", host.ID), nil, http.StatusOK, &getHostSw) require.Len(t, getHostSw.Software, 3) // foo, bar and ruby.deb require.Equal(t, getHostSw.Software[0].Name, "bar") require.Equal(t, getHostSw.Software[1].Name, "foo") @@ -14173,7 +14188,7 @@ func (s *integrationEnterpriseTestSuite) TestBatchSetSoftwareInstallersSideEffec // Get the install response, should be pending getHostSoftwareResp := getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &getHostSoftwareResp, "include_available_for_install", "true") require.Equal(t, fleet.SoftwareInstallPending, *getHostSoftwareResp.Software[0].Status) // Switch self-service flag @@ -14214,12 +14229,12 @@ func (s *integrationEnterpriseTestSuite) TestBatchSetSoftwareInstallersSideEffec // install should no longer be pending afterPreinstallHostResp := getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &afterPreinstallHostResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &afterPreinstallHostResp, "include_available_for_install", "true") require.Nil(t, afterPreinstallHostResp.Software[0].Status) // install software fully s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/%d/install", h.ID, titlesResp.SoftwareTitles[0].ID), nil, http.StatusAccepted, &installResp) - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &getHostSoftwareResp, "include_available_for_install", "true") installUUID := getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall.InstallUUID s.Do("POST", "/api/fleet/orbit/software_install/result", json.RawMessage(fmt.Sprintf(`{ "orbit_node_key": %q, @@ -14237,7 +14252,7 @@ func (s *integrationEnterpriseTestSuite) TestBatchSetSoftwareInstallersSideEffec // install should show as complete hostResp := getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &hostResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &hostResp, "include_available_for_install", "true") require.Equal(t, fleet.SoftwareInstalled, *hostResp.Software[0].Status) // update install script @@ -14261,7 +14276,7 @@ func (s *integrationEnterpriseTestSuite) TestBatchSetSoftwareInstallersSideEffec // install should still show as complete hostResp = getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &hostResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &hostResp, "include_available_for_install", "true") require.Equal(t, fleet.SoftwareInstalled, *hostResp.Software[0].Status) trailer = " " // add a character to the response for the installer HTTP call to ensure the file hashes differently @@ -14283,7 +14298,7 @@ func (s *integrationEnterpriseTestSuite) TestBatchSetSoftwareInstallersSideEffec // install should be nulled out hostResp = getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &hostResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &hostResp, "include_available_for_install", "true") require.Nil(t, hostResp.Software[0].Status) // install details record should still show as installed @@ -14296,7 +14311,7 @@ func (s *integrationEnterpriseTestSuite) TestBatchSetSoftwareInstallersSideEffec s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/%d/install", h.ID, titlesResp.SoftwareTitles[0].ID), nil, http.StatusAccepted, &pendingResp) // install should show as pending - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &afterPreinstallHostResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &afterPreinstallHostResp, "include_available_for_install", "true") require.Equal(t, fleet.SoftwareInstallPending, *afterPreinstallHostResp.Software[0].Status) installUUID = afterPreinstallHostResp.Software[0].SoftwarePackage.LastInstall.InstallUUID @@ -14305,7 +14320,7 @@ func (s *integrationEnterpriseTestSuite) TestBatchSetSoftwareInstallersSideEffec s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/%d/uninstall", h2.ID, titlesResp.SoftwareTitles[0].ID), nil, http.StatusAccepted, &uninstallResp) // uninstall should show as pending - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h2.ID), nil, http.StatusOK, &afterPreinstallHostResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h2.ID), nil, http.StatusOK, &afterPreinstallHostResp, "include_available_for_install", "true") require.Equal(t, fleet.SoftwareUninstallPending, *afterPreinstallHostResp.Software[0].Status) // delete all installers @@ -14689,7 +14704,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() { // Get the results, should be pending getHostSoftwareResp := getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &getHostSoftwareResp, "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 1) require.NotNil(t, getHostSoftwareResp.Software[0].SoftwarePackage) require.NotNil(t, getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall) @@ -14724,7 +14739,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() { require.NoError(t, s.ds.UpdateHostRefetchRequested(context.Background(), h4.ID, false)) s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/%d/install", h2.ID, titleID), nil, http.StatusAccepted, &resp) - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h2.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h2.ID), nil, http.StatusOK, &getHostSoftwareResp, "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 1) installUUID2 := getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall.InstallUUID s.Do("POST", "/api/fleet/orbit/software_install/result", json.RawMessage(fmt.Sprintf(`{ @@ -14735,13 +14750,24 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() { "install_script_output": "ok" }`, *h2.OrbitNodeKey, installUUID2)), http.StatusNoContent) + software := []fleet.Software{ + {Name: payload.Title, Version: payload.Version, Source: "deb_packages"}, + } + _, err = s.ds.UpdateHostSoftware(context.Background(), h2.ID, software) + require.NoError(t, err) + + // Note: no need to use the "include_available_for_install" query param + // since we simulated ingesting the software above + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h2.ID), nil, http.StatusOK, &getHostSoftwareResp) + require.Len(t, getHostSoftwareResp.Software, 1) + // Verify refetch requested is set after successful install var hostResp getHostResponse s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d", h2.ID), nil, http.StatusOK, &hostResp) require.True(t, hostResp.Host.RefetchRequested, "RefetchRequested should be true after successful software install") s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/%d/install", h3.ID, titleID), nil, http.StatusAccepted, &resp) - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h3.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h3.ID), nil, http.StatusOK, &getHostSoftwareResp, "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 1) installUUID3 := getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall.InstallUUID s.Do("POST", "/api/fleet/orbit/software_install/result", json.RawMessage(fmt.Sprintf(`{ @@ -14761,7 +14787,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() { // Server-side retries queue up to MaxSoftwareInstallAttempts attempts. for attempt := 2; attempt <= fleet.MaxSoftwareInstallAttempts; attempt++ { getHostSoftwareResp = getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h3.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h3.ID), nil, http.StatusOK, &getHostSoftwareResp, "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 1) require.NotNil(t, getHostSoftwareResp.Software[0].SoftwarePackage) require.NotNil(t, getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall) @@ -14778,7 +14804,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() { } s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/%d/install", h4.ID, titleID), nil, http.StatusAccepted, &resp) - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h4.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h4.ID), nil, http.StatusOK, &getHostSoftwareResp, "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 1) installUUID4a := getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall.InstallUUID s.Do("POST", "/api/fleet/orbit/software_install/result", json.RawMessage(fmt.Sprintf(`{ @@ -14795,7 +14821,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() { // Exhaust automatic retries for h4 so it reaches terminal "failed" state. for attempt := 2; attempt <= fleet.MaxSoftwareInstallAttempts; attempt++ { getHostSoftwareResp = getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h4.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h4.ID), nil, http.StatusOK, &getHostSoftwareResp, "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 1) require.NotNil(t, getHostSoftwareResp.Software[0].SoftwarePackage) require.NotNil(t, getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall) @@ -14810,7 +14836,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() { } s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/%d/install", h4.ID, titleID), nil, http.StatusAccepted, &resp) - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h4.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h4.ID), nil, http.StatusOK, &getHostSoftwareResp, "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 1) installUUID4b := getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall.InstallUUID _ = installUUID4b @@ -14941,7 +14967,8 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() { } distributedResp := submitDistributedQueryResultsResponse{} s.DoJSON("POST", "/api/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp) - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h2.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h2.ID), nil, http.StatusOK, &getHostSoftwareResp, + "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 1) assert.NotNil(t, getHostSoftwareResp.Software[0].Status) assert.NotNil(t, getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall) @@ -14962,7 +14989,8 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() { distributedResp = submitDistributedQueryResultsResponse{} s.DoJSON("POST", "/api/osquery/distributed/write", distributedReq, http.StatusOK, &distributedResp) - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h2.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h2.ID), nil, http.StatusOK, &getHostSoftwareResp, + "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 1) assert.Nil(t, getHostSoftwareResp.Software[0].Status) assert.Nil(t, getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall) @@ -15000,7 +15028,8 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() { // Do uninstall on h s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/%d/uninstall", h.ID, titleID), nil, http.StatusAccepted, &resp) - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &getHostSoftwareResp, + "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 1) assert.NotNil(t, getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall) assert.Equal(t, fleet.SoftwareUninstallPending, *getHostSoftwareResp.Software[0].Status) @@ -15073,7 +15102,8 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() { s.DoRawNoAuth("GET", fmt.Sprintf("/api/v1/fleet/device/%s/software/uninstall/%s/results", token, uninstallExecutionID), nil, http.StatusOK) // Software should be available for install again - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &getHostSoftwareResp, + "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 1) assert.NotNil(t, getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall) require.NotNil(t, getHostSoftwareResp.Software[0].SoftwarePackage.LastUninstall) @@ -15087,7 +15117,8 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() { // Since host_script_results does not use fine-grained timestamps yet, we adjust beforeUninstall = beforeUninstall.Add(-time.Second) s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/%d/uninstall", h.ID, titleID), nil, http.StatusAccepted, &resp) - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", h.ID), nil, http.StatusOK, &getHostSoftwareResp, + "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 1) assert.NotNil(t, getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall) assert.Equal(t, fleet.SoftwareUninstallPending, *getHostSoftwareResp.Software[0].Status) @@ -15261,7 +15292,8 @@ func (s *integrationEnterpriseTestSuite) TestSelfServiceSoftwareInstallUninstall // Do uninstall on host s.DoRawNoAuth("POST", fmt.Sprintf("/api/v1/fleet/device/%s/software/uninstall/%d", token, titleIDSS), nil, http.StatusAccepted) var getHostSoftwareResp getHostSoftwareResponse - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host1.ID), nil, http.StatusOK, &getHostSoftwareResp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host1.ID), nil, http.StatusOK, &getHostSoftwareResp, + "include_available_for_install", "true") require.Len(t, getHostSoftwareResp.Software, 2) assert.NotNil(t, getHostSoftwareResp.Software[0].SoftwarePackage.LastInstall) assert.Equal(t, fleet.SoftwareUninstallPending, *getHostSoftwareResp.Software[0].Status) @@ -17169,9 +17201,9 @@ func (s *integrationEnterpriseTestSuite) TestVPPAppsWithoutMDM() { } s.uploadSoftwareInstaller(t, pkgPayload, http.StatusOK, "") - // We don't see VPP, but we do still see the installers + // We don't see VPP, but we do still see the installers (need include_available_for_install=true since it's not in osquery inventory) resp := getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", orbitHost.ID), getHostSoftwareRequest{}, http.StatusOK, &resp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software?include_available_for_install=true", orbitHost.ID), getHostSoftwareRequest{}, http.StatusOK, &resp) assert.Len(t, resp.Software, 1) assert.NotNil(t, resp.Software[0].SoftwarePackage) assert.Nil(t, resp.Software[0].AppStoreApp) @@ -20724,8 +20756,9 @@ func (s *integrationEnterpriseTestSuite) TestListHostSoftwareWithLabelScoping() http.StatusNoContent) // Software is now installed on the host. We should see it in the host software list + // (need include_available_for_install=true since the install was tracked via an installer, not osquery inventory) getHostSw := getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software?include_available_for_install=true", host.ID), nil, http.StatusOK, &getHostSw) require.Len(t, getHostSw.Software, 1) require.Equal(t, getHostSw.Software[0].Name, "ruby") @@ -20770,7 +20803,7 @@ func (s *integrationEnterpriseTestSuite) TestListHostSoftwareWithLabelScoping() updateInstallerLabel(installerID, lbl2.ID, true) // We should still see the software at this point, because we haven't uninstalled it yet - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software?include_available_for_install=true", host.ID), nil, http.StatusOK, &getHostSw) require.Len(t, getHostSw.Software, 1) // installer should be out of scope since the label is "exclude any" @@ -20785,7 +20818,7 @@ func (s *integrationEnterpriseTestSuite) TestListHostSoftwareWithLabelScoping() // uninstall the software s.DoJSON("POST", fmt.Sprintf("/api/latest/fleet/hosts/%d/software/%d/uninstall", host.ID, titleID), nil, http.StatusAccepted, &resp) - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software?include_available_for_install=true", host.ID), nil, http.StatusOK, &getHostSw) require.Len(t, getHostSw.Software, 1) // TODO this is a corner case where we have visibility on a descoped uninstall, but this will disappear // once we complete the uninstall because at that point we'll be relying solely on inventory to determine software diff --git a/server/service/integration_mdm_setup_experience_test.go b/server/service/integration_mdm_setup_experience_test.go index 6a9f595dbd..82214f15c5 100644 --- a/server/service/integration_mdm_setup_experience_test.go +++ b/server/service/integration_mdm_setup_experience_test.go @@ -4082,9 +4082,10 @@ func (s *integrationMDMTestSuite) TestSetupExperienceAndroid() { require.Equal(t, app2.AdamID, getHostSw.Software[1].AppStoreApp.AppStoreID) require.Nil(t, getHostSw.Software[1].Status) - // the software now shows up in the host inventory + // the software now shows up when including available-for-install (tracked via installer, not osquery inventory) getHostSw = getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host.ID), nil, http.StatusOK, &getHostSw, "order_key", "name") + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host.ID), nil, http.StatusOK, &getHostSw, "order_key", "name", + "include_available_for_install", "true") require.Len(t, getHostSw.Software, 2) require.NotNil(t, getHostSw.Software[0].AppStoreApp) require.Equal(t, app1.AdamID, getHostSw.Software[0].AppStoreApp.AppStoreID) diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go index e6b2528f7e..f8391ab5cb 100644 --- a/server/service/integration_mdm_test.go +++ b/server/service/integration_mdm_test.go @@ -14249,7 +14249,7 @@ func (s *integrationMDMTestSuite) TestVPPApps() { // Check list host software getHostSw := getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw, "include_available_for_install", "true") gotSW := getHostSw.Software require.Len(t, gotSW, 2) // App 1 and App 2 got1, got2 := gotSW[0], gotSW[1] @@ -14276,7 +14276,7 @@ func (s *integrationMDMTestSuite) TestVPPApps() { // Check with a query getHostSw = getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw, "query", "App 1") + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw, "query", "App 1", "include_available_for_install", "true") require.Len(t, getHostSw.Software, 1) // App 1 only got1 = getHostSw.Software[0] require.Equal(t, got1.Name, "App 1") @@ -14332,7 +14332,7 @@ func (s *integrationMDMTestSuite) TestVPPApps() { // Check list host software getHostSw = getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw, "include_available_for_install", "true") gotSW = getHostSw.Software require.Len(t, gotSW, 2) // App 1 and App 2 got1 = gotSW[0] @@ -14507,7 +14507,7 @@ func (s *integrationMDMTestSuite) TestVPPApps() { // Check list host software getHostSw = getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", installHost.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", installHost.ID), nil, http.StatusOK, &getHostSw, "include_available_for_install", "true") require.Len(t, getHostSw.Software, install.hostCount+install.extraAvailable) var foundInstalledApp bool for index := range getHostSw.Software { @@ -17956,7 +17956,7 @@ func (s *integrationMDMTestSuite) TestVPPAppsMDMFiltering() { s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", orbitHost.ID), getHostSoftwareRequest{}, http.StatusOK, &resp) assert.Len(t, resp.Software, 0) - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), getHostSoftwareRequest{}, http.StatusOK, &resp) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), getHostSoftwareRequest{}, http.StatusOK, &resp, "include_available_for_install", "true") assert.Len(t, resp.Software, 1) } @@ -19759,7 +19759,7 @@ func (s *integrationMDMTestSuite) TestSoftwareCategories() { host.ID, titleID), nil, http.StatusAccepted, &installResp) getHostSw := getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host.ID), nil, http.StatusOK, &getHostSw, "include_available_for_install", "true") require.Len(t, getHostSw.Software, 1) require.Equal(t, getHostSw.Software[0].Name, "ruby") require.NotNil(t, getHostSw.Software[0].SoftwarePackage) @@ -19789,7 +19789,7 @@ func (s *integrationMDMTestSuite) TestSoftwareCategories() { require.Equal(t, cat3.Name, getDeviceSw.Software[0].SoftwarePackage.Categories[0]) getHostSw = getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", host.ID), nil, http.StatusOK, &getHostSw, "include_available_for_install", "true") require.Len(t, getHostSw.Software, 1) require.Equal(t, getHostSw.Software[0].Name, "ruby") require.NotNil(t, getHostSw.Software[0].SoftwarePackage) diff --git a/server/service/integration_vpp_install_test.go b/server/service/integration_vpp_install_test.go index 06a076b41d..ed7f437f43 100644 --- a/server/service/integration_vpp_install_test.go +++ b/server/service/integration_vpp_install_test.go @@ -505,7 +505,7 @@ func (s *integrationMDMTestSuite) TestVPPAppInstallVerification() { // Check list host software getHostSw := getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw, "include_available_for_install", "true") gotSW := getHostSw.Software require.Len(t, gotSW, 2) // App 1 and App 2 got1, got2 := gotSW[0], gotSW[1] @@ -543,7 +543,7 @@ func (s *integrationMDMTestSuite) TestVPPAppInstallVerification() { require.Equal(t, 0, countResp.Count) // We should instead have 1 pending - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw, "include_available_for_install", "true") gotSW = getHostSw.Software require.Len(t, gotSW, 2) // App 1 and App 2 checkVPPApp(gotSW[0], addedApp, installCmdUUID, fleet.SoftwareInstallPending) @@ -570,7 +570,7 @@ func (s *integrationMDMTestSuite) TestVPPAppInstallVerification() { require.Equal(t, 0, countResp.Count) // We should instead have 1 pending - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw, "include_available_for_install", "true") gotSW = getHostSw.Software require.Len(t, gotSW, 2) // App 1 and App 2 checkVPPApp(gotSW[0], addedApp, installCmdUUID, fleet.SoftwareInstallPending) @@ -589,7 +589,7 @@ func (s *integrationMDMTestSuite) TestVPPAppInstallVerification() { checkCommandsInFlight(0) - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw, "include_available_for_install", "true") gotSW = getHostSw.Software require.Len(t, gotSW, 2) // App 1 and App 2 checkVPPApp(gotSW[0], addedApp, installCmdUUID, fleet.SoftwareInstalled) @@ -665,7 +665,7 @@ func (s *integrationMDMTestSuite) TestVPPAppInstallVerification() { // Check list host software getHostSw = getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw, "include_available_for_install", "true") gotSW = getHostSw.Software require.Len(t, gotSW, 2) // App 1 and App 2 got1, got2 = gotSW[0], gotSW[1] @@ -757,7 +757,7 @@ func (s *integrationMDMTestSuite) TestVPPAppInstallVerification() { s.Do("DELETE", fmt.Sprintf("/api/latest/fleet/hosts/%d/activities/upcoming/%s", selfServiceHost.ID, listUpcomingAct.Activities[0].UUID), nil, http.StatusNoContent) getHostSw = getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw, "include_available_for_install", "true") gotSW = getHostSw.Software require.Len(t, gotSW, 2) // App 1 and App 2 got1, got2 = gotSW[0], gotSW[1] @@ -814,7 +814,7 @@ func (s *integrationMDMTestSuite) TestVPPAppInstallVerification() { }) getHostSw = getHostSoftwareResponse{} - s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw) + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/software", mdmHost.ID), nil, http.StatusOK, &getHostSw, "include_available_for_install", "true") gotSW = getHostSw.Software require.Len(t, gotSW, 2) // App 1 and App 2 got1, got2 = gotSW[0], gotSW[1]