Skip to content

Commit

Permalink
Merge branch 'main' into lukeheath-patch-12
Browse files Browse the repository at this point in the history
  • Loading branch information
lukeheath authored Feb 17, 2025
2 parents 1c34584 + 07486ae commit a1d1adb
Show file tree
Hide file tree
Showing 20 changed files with 449 additions and 48 deletions.
2 changes: 2 additions & 0 deletions changes/24222-update-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Adds a daily job that keeps the App Store app version displayed in Fleet in sync with the actual
latest version.
24 changes: 24 additions & 0 deletions cmd/fleet/cron.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mdm"
apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple"
"github.com/fleetdm/fleet/v4/server/mdm/apple/vpp"
"github.com/fleetdm/fleet/v4/server/mdm/assets"
"github.com/fleetdm/fleet/v4/server/mdm/maintainedapps"
"github.com/fleetdm/fleet/v4/server/mdm/nanodep/godep"
Expand Down Expand Up @@ -1494,3 +1495,26 @@ func newMaintainedAppSchedule(

return s, nil
}

func newRefreshVPPAppVersionsSchedule(
ctx context.Context,
instanceID string,
ds fleet.Datastore,
logger kitlog.Logger,
) (*schedule.Schedule, error) {
const (
name = string(fleet.CronRefreshVPPAppVersions)
defaultInterval = 1 * time.Hour
)

logger = kitlog.With(logger, "cron", name)
s := schedule.New(
ctx, name, instanceID, defaultInterval, ds, ds,
schedule.WithLogger(logger),
schedule.WithJob("refresh_vpp_app_version", func(ctx context.Context) error {
return vpp.RefreshVersions(ctx, ds)
}),
)

return s, nil
}
6 changes: 6 additions & 0 deletions cmd/fleet/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,12 @@ the way that the Fleet server works.
}); err != nil {
initFatal(err, "failed to register maintained apps schedule")
}

if err := cronSchedules.StartCronSchedule(func() (fleet.CronSchedule, error) {
return newRefreshVPPAppVersionsSchedule(ctx, instanceID, ds, logger)
}); err != nil {
initFatal(err, "failed to register refresh vpp app versions schedule")
}
}

if license.IsPremium() && config.Activity.EnableAuditLog {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,25 +321,35 @@ const SoftwareInstallerCard = ({
);
};

const versionInfo = version ? (
<span>{version}</span>
) : (
<TooltipWrapper
tipContent={
<span>
Fleet couldn&apos;t read the version from {name}.{" "}
<CustomLink
newTab
url={`${LEARN_MORE_ABOUT_BASE_LINK}/read-package-version`}
text="Learn more"
variant="tooltip-link"
/>
</span>
}
>
Version (unknown)
</TooltipWrapper>
);
let versionInfo = <span>{version}</span>;

if (installerType === "vpp") {
versionInfo = (
<TooltipWrapper tipContent={<span>Updated every hour.</span>}>
<span>{version}</span>
</TooltipWrapper>
);
}

if (installerType === "package" && !version) {
versionInfo = (
<TooltipWrapper
tipContent={
<span>
Fleet couldn&apos;t read the version from {name}.{" "}
<CustomLink
newTab
url={`${LEARN_MORE_ABOUT_BASE_LINK}/read-package-version`}
text="Learn more"
variant="tooltip-link"
/>
</span>
}
>
<span>Version (unknown)</span>
</TooltipWrapper>
);
}

const renderDetails = () => {
return !uploadedAt ? (
Expand Down
2 changes: 1 addition & 1 deletion handbook/customer-success/customer-success.rituals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
moreInfoUrl: "https://fleetdm.com/handbook/company/why-this-way#why-make-work-visible" #URL used to highlight "description:" test in table
dri: "zayhanlon" # DRI for ritual (assignee if autoIssue) (TODO display GitHub proflie pic instead of name or title)
autoIssue: # Enables automation of GitHub issues
labels: [ "#g-customer-success" ] # label to be applied to issue
labels: [ ":help-customers" ] # label to be applied to issue
repo: "confidential"
-
task: "Process new requests"
Expand Down
4 changes: 1 addition & 3 deletions handbook/demand/demand.rituals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
moreInfoUrl: "https://fleetdm.com/handbook/company/why-this-way#why-make-work-visible" #URL used to highlight "description:" test in table
dri: "mikermcneil" # DRI for ritual (assignee if autoIssue) (TODO display GitHub proflie pic instead of name or title)
autoIssue: # « Enable automation of GitHub issues
labels: [ "#g-demand" ] # label to be applied to issue
labels: [ ":help-marketing" ] # label to be applied to issue
repo: "confidential"
-
task: "Settle event strategy"
Expand Down Expand Up @@ -73,9 +73,7 @@
dri: "Drew-P-drawers"
-
task: "Check ongoing events"

startedOn: "2024-10-21"

frequency: "Daily"
description: "Check event issues and complete steps."
moreInfoUrl: "https://fleetdm.com/handbook/engineering#book-an-event"
Expand Down
26 changes: 26 additions & 0 deletions server/datastore/mysql/vpp.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,12 @@ WHERE
return appSet, nil
}

func (ds *Datastore) InsertVPPApps(ctx context.Context, apps []*fleet.VPPApp) error {
return ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error {
return insertVPPApps(ctx, tx, apps)
})
}

func insertVPPApps(ctx context.Context, tx sqlx.ExtContext, apps []*fleet.VPPApp) error {
stmt := `
INSERT INTO vpp_apps
Expand Down Expand Up @@ -1623,3 +1629,23 @@ func (ds *Datastore) GetIncludedHostIDMapForVPPAppTx(ctx context.Context, tx sql
func (ds *Datastore) GetExcludedHostIDMapForVPPApp(ctx context.Context, vppAppTeamID uint) (map[uint]struct{}, error) {
return ds.getExcludedHostIDMapForSoftware(ctx, vppAppTeamID, softwareTypeVPP)
}

func (ds *Datastore) GetAllVPPApps(ctx context.Context) ([]*fleet.VPPApp, error) {
query := `
SELECT
adam_id,
title_id,
bundle_identifier,
icon_url,
name,
latest_version,
platform
FROM vpp_apps`

var apps []*fleet.VPPApp
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &apps, query); err != nil {
return nil, ctxerr.Wrap(ctx, err, "getting all VPP apps")
}

return apps, nil
}
33 changes: 33 additions & 0 deletions server/datastore/mysql/vpp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func TestVPP(t *testing.T) {
{"GetOrInsertSoftwareTitleForVPPApp", testGetOrInsertSoftwareTitleForVPPApp},
{"DeleteVPPAssignedToPolicy", testDeleteVPPAssignedToPolicy},
{"TestVPPTokenTeamAssignment", testVPPTokenTeamAssignment},
{"TestGetAllVPPApps", testGetAllVPPApps},
}

for _, c := range cases {
Expand Down Expand Up @@ -1889,3 +1890,35 @@ func testSetTeamVPPAppsWithLabels(t *testing.T, ds *Datastore) {
require.True(t, ok)
}
}

func testGetAllVPPApps(t *testing.T, ds *Datastore) {
ctx := context.Background()
dataToken, err := test.CreateVPPTokenData(time.Now().Add(24*time.Hour), "Org"+t.Name(), "Location"+t.Name())
require.NoError(t, err)
tok1, err := ds.InsertVPPToken(ctx, dataToken)
assert.NoError(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tok1.ID, []uint{})
assert.NoError(t, err)

app1 := &fleet.VPPApp{Name: "vpp_app_1", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "1", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b1"}
_, err = ds.InsertVPPAppWithTeam(ctx, app1, nil)
require.NoError(t, err)

app2 := &fleet.VPPApp{Name: "vpp_app_2", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "2", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b2"}
_, err = ds.InsertVPPAppWithTeam(ctx, app2, nil)
require.NoError(t, err)

app3 := &fleet.VPPApp{Name: "vpp_app_3", VPPAppTeam: fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: "3", Platform: fleet.MacOSPlatform}}, BundleIdentifier: "b3"}
_, err = ds.InsertVPPAppWithTeam(ctx, app3, nil)
require.NoError(t, err)

// this method doesn't pull the VPPAppTeamID
app1.AppTeamID = 0
app2.AppTeamID = 0
app3.AppTeamID = 0

apps, err := ds.GetAllVPPApps(ctx)
require.NoError(t, err)

require.Equal(t, apps, []*fleet.VPPApp{app1, app2, app3})
}
3 changes: 3 additions & 0 deletions server/fleet/cron_schedules.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ const (
CronCalendar CronScheduleName = "calendar"
CronUninstallSoftwareMigration CronScheduleName = "uninstall_software_migration"
CronMaintainedApps CronScheduleName = "maintained_apps"
// CronRefreshVPPAppVersions updates the versions of VPP apps in Fleet to the latest value. Runs
// every 1h.
CronRefreshVPPAppVersions CronScheduleName = "refresh_vpp_app_versions"
)

type CronSchedulesService interface {
Expand Down
6 changes: 6 additions & 0 deletions server/fleet/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -1840,6 +1840,12 @@ type Datastore interface {
SetTeamVPPApps(ctx context.Context, teamID *uint, appIDs []VPPAppTeam) error
InsertVPPAppWithTeam(ctx context.Context, app *VPPApp, teamID *uint) (*VPPApp, error)

// GetAllVPPApps returns all the VPP apps in Fleet, across all teams.
GetAllVPPApps(ctx context.Context) ([]*VPPApp, error)
// InsertVPPApps inserts the given VPP apps in the database.
InsertVPPApps(ctx context.Context, apps []*VPPApp) error

// InsertHostVPPSoftwareInstall(ctx context.Context, hostID uint, appID VPPAppID, commandUUID, associatedEventID string, selfService bool, policyID *uint) error
InsertHostVPPSoftwareInstall(ctx context.Context, hostID uint, appID VPPAppID, commandUUID, associatedEventID string, opts HostSoftwareInstallOptions) error
GetPastActivityDataForVPPAppInstall(ctx context.Context, commandResults *mdm.CommandResults) (*User, *ActivityInstalledAppStoreApp, error)

Expand Down
44 changes: 44 additions & 0 deletions server/mdm/apple/vpp/refresh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package vpp

import (
"context"

"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mdm/apple/itunes"
)

// RefreshVersions updatest the LatestVersion fields for the VPP apps stored in Fleet.
func RefreshVersions(ctx context.Context, ds fleet.Datastore) error {
apps, err := ds.GetAllVPPApps(ctx)
if err != nil {
return err
}

var adamIDs []string
appsByAdamID := make(map[string]*fleet.VPPApp)
for _, app := range apps {
adamIDs = append(adamIDs, app.AdamID)
appsByAdamID[app.AdamID] = app
}

meta, err := itunes.GetAssetMetadata(adamIDs, &itunes.AssetMetadataFilter{Entity: "software"})
if err != nil {
return err
}

var appsToUpdate []*fleet.VPPApp
for _, adamID := range adamIDs {
if m, ok := meta[adamID]; ok {
if m.Version != appsByAdamID[adamID].LatestVersion {
appsByAdamID[adamID].LatestVersion = m.Version
appsToUpdate = append(appsToUpdate, appsByAdamID[adamID])
}
}
}

if err := ds.InsertVPPApps(ctx, appsToUpdate); err != nil {
return err
}

return nil
}
24 changes: 24 additions & 0 deletions server/mock/datastore_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,10 @@ type SetTeamVPPAppsFunc func(ctx context.Context, teamID *uint, appIDs []fleet.V

type InsertVPPAppWithTeamFunc func(ctx context.Context, app *fleet.VPPApp, teamID *uint) (*fleet.VPPApp, error)

type GetAllVPPAppsFunc func(ctx context.Context) ([]*fleet.VPPApp, error)

type InsertVPPAppsFunc func(ctx context.Context, apps []*fleet.VPPApp) error

type InsertHostVPPSoftwareInstallFunc func(ctx context.Context, hostID uint, appID fleet.VPPAppID, commandUUID string, associatedEventID string, opts fleet.HostSoftwareInstallOptions) error

type GetPastActivityDataForVPPAppInstallFunc func(ctx context.Context, commandResults *mdm.CommandResults) (*fleet.User, *fleet.ActivityInstalledAppStoreApp, error)
Expand Down Expand Up @@ -2934,6 +2938,12 @@ type DataStore struct {
InsertVPPAppWithTeamFunc InsertVPPAppWithTeamFunc
InsertVPPAppWithTeamFuncInvoked bool

GetAllVPPAppsFunc GetAllVPPAppsFunc
GetAllVPPAppsFuncInvoked bool

InsertVPPAppsFunc InsertVPPAppsFunc
InsertVPPAppsFuncInvoked bool

InsertHostVPPSoftwareInstallFunc InsertHostVPPSoftwareInstallFunc
InsertHostVPPSoftwareInstallFuncInvoked bool

Expand Down Expand Up @@ -7020,6 +7030,20 @@ func (s *DataStore) InsertVPPAppWithTeam(ctx context.Context, app *fleet.VPPApp,
return s.InsertVPPAppWithTeamFunc(ctx, app, teamID)
}

func (s *DataStore) GetAllVPPApps(ctx context.Context) ([]*fleet.VPPApp, error) {
s.mu.Lock()
s.GetAllVPPAppsFuncInvoked = true
s.mu.Unlock()
return s.GetAllVPPAppsFunc(ctx)
}

func (s *DataStore) InsertVPPApps(ctx context.Context, apps []*fleet.VPPApp) error {
s.mu.Lock()
s.InsertVPPAppsFuncInvoked = true
s.mu.Unlock()
return s.InsertVPPAppsFunc(ctx, apps)
}

func (s *DataStore) InsertHostVPPSoftwareInstall(ctx context.Context, hostID uint, appID fleet.VPPAppID, commandUUID string, associatedEventID string, opts fleet.HostSoftwareInstallOptions) error {
s.mu.Lock()
s.InsertHostVPPSoftwareInstallFuncInvoked = true
Expand Down
Loading

0 comments on commit a1d1adb

Please sign in to comment.