From c4b88e7532cf0ffbcea19fc59ac66fdeb2a97dab Mon Sep 17 00:00:00 2001 From: Jahziel Villasana-Espinoza Date: Tue, 7 Oct 2025 15:12:01 -0400 Subject: [PATCH 01/10] add basic support for .ipa metadata extraction --- ee/server/service/software_installers.go | 27 ++++++++--- pkg/file/file.go | 47 +++++++++++++++++-- .../20251006163522_InHouseAppsSupport.go | 3 +- server/fleet/software_installer.go | 2 + 4 files changed, 67 insertions(+), 12 deletions(-) diff --git a/ee/server/service/software_installers.go b/ee/server/service/software_installers.go index 2a8abf94b1d..9e76aec0b95 100644 --- a/ee/server/service/software_installers.go +++ b/ee/server/service/software_installers.go @@ -69,7 +69,12 @@ func (svc *Service) UploadSoftwareInstaller(ctx context.Context, payload *fleet. payload.PostInstallScript = file.Dos2UnixNewlines(payload.PostInstallScript) payload.UninstallScript = file.Dos2UnixNewlines(payload.UninstallScript) - if _, err := svc.addMetadataToSoftwarePayload(ctx, payload, true); err != nil { + failOnBlankScript := true + if strings.HasSuffix(payload.Filename, ".ipa") { + failOnBlankScript = false + } + + if _, err := svc.addMetadataToSoftwarePayload(ctx, payload, failOnBlankScript); err != nil { return nil, ctxerr.Wrap(ctx, err, "adding metadata to payload") } @@ -96,6 +101,21 @@ func (svc *Service) UploadSoftwareInstaller(ctx context.Context, payload *fleet. return nil, ctxerr.Wrap(ctx, err, "storing software installer") } + if payload.Extension == "ipa" { + fmt.Println("processing IPA upload") + if err := svc.ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{TeamID: payload.TeamID, Name: payload.Title, StorageID: payload.StorageID, Platform: payload.Platform}); err != nil { + return nil, ctxerr.Wrap(ctx, err, "insert in-house app") + } + + // TODO: other processing (e.g. labels) + return &fleet.SoftwareInstaller{ + TeamID: payload.TeamID, + Name: payload.Title, + StorageID: payload.StorageID, + Platform: payload.Platform, + }, nil + } + // Update $PACKAGE_ID/$UPGRADE_CODE in uninstall script if err := preProcessUninstallScript(payload); err != nil { return nil, &fleet.BadRequestError{ @@ -1573,11 +1593,6 @@ func (svc *Service) addMetadataToSoftwarePayload(ctx context.Context, payload *f payload.Extension = meta.Extension payload.UpgradeCode = meta.UpgradeCode - if payload.Extension == "ipa" { - fmt.Println("processing IPA upload") - return meta.Extension, nil - } - // reset the reader (it was consumed to extract metadata) if err := payload.InstallerFile.Rewind(); err != nil { return "", ctxerr.Wrap(ctx, err, "resetting installer file reader") diff --git a/pkg/file/file.go b/pkg/file/file.go index b86cdb81793..d68916b3b68 100644 --- a/pkg/file/file.go +++ b/pkg/file/file.go @@ -23,6 +23,7 @@ import ( "github.com/fleetdm/fleet/v4/pkg/secure" "github.com/fleetdm/fleet/v4/server/fleet" "github.com/rs/zerolog" + "howett.net/plist" ) var ( @@ -48,16 +49,53 @@ func ExtractIPAMetadata(tfr *fleet.TempFileReader) (*InstallerMetadata, error) { return nil, fmt.Errorf("rewind reader: %w", err) } - r, err := zip.NewReader(tfr, 1000) + fmt.Printf("tfr.Name(): %v\n", tfr.Name()) + + r, err := zip.OpenReader(tfr.Name()) if err != nil { return nil, err } + var plistData struct { + BundleID string `plist:"CFBundleIdentifier"` + Name string `plist:"CFBundleName"` + Version string `plist:"CFBundleShortVersionString"` + } for _, f := range r.File { fmt.Printf("f.Name: %v\n", f.Name) + if strings.Contains(f.Name, "Info.plist") { + // Get data from plist file + archiveFile, err := f.Open() + if err != nil { + return nil, fmt.Errorf("could not open archive %s: %w", f.Name, err) + } + defer archiveFile.Close() + + rawData, err := io.ReadAll(archiveFile) + if err != nil { + return nil, err + } + _, err = plist.Unmarshal(rawData, &plistData) + if err != nil { + return nil, err + } + + fmt.Printf("plistData: %+v\n", plistData) + + } + } + + if plistData.BundleID == "" { + return nil, errors.New("couldn't find bundle identifier for in-house app") } - return &InstallerMetadata{SHASum: h.Sum(nil), PackageIDs: []string{"com.foo.bar"}}, nil + return &InstallerMetadata{ + BundleIdentifier: plistData.BundleID, + SHASum: h.Sum(nil), + PackageIDs: []string{plistData.BundleID}, + Name: plistData.Name, + Version: plistData.Version, + }, nil } // ExtractInstallerMetadata extracts the software name and version from the @@ -90,9 +128,8 @@ func ExtractInstallerMetadata(tfr *fleet.TempFileReader) (*InstallerMetadata, er if err != nil { err = errors.Join(ErrInvalidTarball, err) } - // TODO: implement this - // case "ipa": - // meta, err = ExtractIPAMetadata(tfr) + case "ipa": + meta, err = ExtractIPAMetadata(tfr) default: return nil, ErrUnsupportedType } diff --git a/server/datastore/mysql/migrations/tables/20251006163522_InHouseAppsSupport.go b/server/datastore/mysql/migrations/tables/20251006163522_InHouseAppsSupport.go index dbea048e398..3bc77bd4061 100644 --- a/server/datastore/mysql/migrations/tables/20251006163522_InHouseAppsSupport.go +++ b/server/datastore/mysql/migrations/tables/20251006163522_InHouseAppsSupport.go @@ -16,7 +16,8 @@ CREATE TABLE in_house_apps ( title_id int unsigned DEFAULT NULL, team_id int unsigned DEFAULT NULL, global_or_team_id int unsigned NOT NULL DEFAULT '0', - name varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + name VARCHAR(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + version VARCHAR(255) NOT NULL DEFAULT '', storage_id VARCHAR(64) COLLATE utf8mb4_unicode_ci NOT NULL, created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP, updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, diff --git a/server/fleet/software_installer.go b/server/fleet/software_installer.go index dc6b891b79a..f661ba1bd84 100644 --- a/server/fleet/software_installer.go +++ b/server/fleet/software_installer.go @@ -600,6 +600,8 @@ func SoftwareInstallerPlatformFromExtension(ext string) (string, error) { return "windows", nil case "pkg": return "darwin", nil + case "ipa": // TODO(JVE): what about iPads? Can we get the platforms from the Info.plist file? + return "ios", nil default: return "", fmt.Errorf("unsupported file type: %s", ext) } From e8ce2444519a5ccd75faa613412f88c6babc569b Mon Sep 17 00:00:00 2001 From: jkatz01 Date: Tue, 7 Oct 2025 18:34:43 -0400 Subject: [PATCH 02/10] add titleID to InsertInHouseApp --- ee/server/service/software_installers.go | 9 ++++++++- server/datastore/mysql/in_house_apps.go | 19 +++++++++++++++---- server/datastore/mysql/in_house_apps_test.go | 2 +- server/fleet/datastore.go | 2 +- server/fleet/in_house_apps.go | 1 + server/mock/datastore_mock.go | 4 ++-- 6 files changed, 28 insertions(+), 9 deletions(-) diff --git a/ee/server/service/software_installers.go b/ee/server/service/software_installers.go index 9e76aec0b95..4c7329555a8 100644 --- a/ee/server/service/software_installers.go +++ b/ee/server/service/software_installers.go @@ -103,13 +103,20 @@ func (svc *Service) UploadSoftwareInstaller(ctx context.Context, payload *fleet. if payload.Extension == "ipa" { fmt.Println("processing IPA upload") - if err := svc.ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{TeamID: payload.TeamID, Name: payload.Title, StorageID: payload.StorageID, Platform: payload.Platform}); err != nil { + gotID, err := svc.ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{TeamID: payload.TeamID, Name: payload.Title, BundleID: payload.BundleIdentifier, StorageID: payload.StorageID, Platform: payload.Platform}) + if err != nil { return nil, ctxerr.Wrap(ctx, err, "insert in-house app") } + var titleID *uint + if gotID != uint(0) { + titleID = &gotID + } + // TODO: other processing (e.g. labels) return &fleet.SoftwareInstaller{ TeamID: payload.TeamID, + TitleID: titleID, Name: payload.Title, StorageID: payload.StorageID, Platform: payload.Platform, diff --git a/server/datastore/mysql/in_house_apps.go b/server/datastore/mysql/in_house_apps.go index 26e75f06889..69d6e4ed676 100644 --- a/server/datastore/mysql/in_house_apps.go +++ b/server/datastore/mysql/in_house_apps.go @@ -8,17 +8,18 @@ import ( "github.com/jmoiron/sqlx" ) -func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHouseAppPayload) error { +func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHouseAppPayload) (titleID uint, err error) { stmt := ` INSERT INTO in_house_apps ( team_id, + title_id, global_or_team_id, name, storage_id, platform ) - VALUES (?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?) ` var tid *uint @@ -30,10 +31,20 @@ func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHous tid = payload.TeamID } } + titleID, err = ds.getOrGenerateSoftwareInstallerTitleID(ctx, &fleet.UploadSoftwareInstallerPayload{ + TeamID: tid, + Title: payload.Name, + BundleIdentifier: payload.BundleID, + Source: "ios_apps"}, // TODO: what about iPad apps + ) + if err != nil { + return 0, err + } - err := ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error { + err = ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error { args := []any{ tid, + titleID, globalOrTeamID, payload.Name, payload.StorageID, @@ -52,5 +63,5 @@ func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHous return nil }) - return ctxerr.Wrap(ctx, err, "insert in house app") + return titleID, ctxerr.Wrap(ctx, err, "insert in house app") } diff --git a/server/datastore/mysql/in_house_apps_test.go b/server/datastore/mysql/in_house_apps_test.go index a6734dcc560..eca6ed023d1 100644 --- a/server/datastore/mysql/in_house_apps_test.go +++ b/server/datastore/mysql/in_house_apps_test.go @@ -27,7 +27,7 @@ func TestInHouseApps(t *testing.T) { func testInHouseAppsCrud(t *testing.T, ds *Datastore) { ctx := context.Background() - err := ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{ + _, err := ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{ Name: "foo", StorageID: "testingtesting123", Platform: "ios", diff --git a/server/fleet/datastore.go b/server/fleet/datastore.go index a94305714a7..b532b4496b2 100644 --- a/server/fleet/datastore.go +++ b/server/fleet/datastore.go @@ -2409,7 +2409,7 @@ type Datastore interface { // GetCurrentTime gets the current time from the database GetCurrentTime(ctx context.Context) (time.Time, error) - InsertInHouseApp(ctx context.Context, payload *InHouseAppPayload) error + InsertInHouseApp(ctx context.Context, payload *InHouseAppPayload) (titleID uint, err error) } type AndroidDatastore interface { diff --git a/server/fleet/in_house_apps.go b/server/fleet/in_house_apps.go index 03e6ca407c0..66208be0e87 100644 --- a/server/fleet/in_house_apps.go +++ b/server/fleet/in_house_apps.go @@ -3,6 +3,7 @@ package fleet type InHouseAppPayload struct { TeamID *uint Name string + BundleID string StorageID string Platform string } diff --git a/server/mock/datastore_mock.go b/server/mock/datastore_mock.go index 235155aa2c4..2241ca2b40b 100644 --- a/server/mock/datastore_mock.go +++ b/server/mock/datastore_mock.go @@ -1549,7 +1549,7 @@ type BatchApplyCertificateAuthoritiesFunc func(ctx context.Context, ops fleet.Ce type GetCurrentTimeFunc func(ctx context.Context) (time.Time, error) -type InsertInHouseAppFunc func(ctx context.Context, payload *fleet.InHouseAppPayload) error +type InsertInHouseAppFunc func(ctx context.Context, payload *fleet.InHouseAppPayload) (titleID uint, err error) type DataStore struct { HealthCheckFunc HealthCheckFunc @@ -9188,7 +9188,7 @@ func (s *DataStore) GetCurrentTime(ctx context.Context) (time.Time, error) { return s.GetCurrentTimeFunc(ctx) } -func (s *DataStore) InsertInHouseApp(ctx context.Context, payload *fleet.InHouseAppPayload) error { +func (s *DataStore) InsertInHouseApp(ctx context.Context, payload *fleet.InHouseAppPayload) (titleID uint, err error) { s.mu.Lock() s.InsertInHouseAppFuncInvoked = true s.mu.Unlock() From a5c111c5cc3cc7f71dba2885443792e8a78e332e Mon Sep 17 00:00:00 2001 From: jkatz01 Date: Wed, 8 Oct 2025 11:59:07 -0400 Subject: [PATCH 03/10] add labels to in_house_app_labels --- ee/server/service/software_installers.go | 39 +++++++++++++++++-- server/datastore/mysql/in_house_apps.go | 13 ++++++- server/datastore/mysql/software_installers.go | 1 + server/fleet/in_house_apps.go | 11 +++--- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/ee/server/service/software_installers.go b/ee/server/service/software_installers.go index 4c7329555a8..4b7256a2078 100644 --- a/ee/server/service/software_installers.go +++ b/ee/server/service/software_installers.go @@ -103,16 +103,45 @@ func (svc *Service) UploadSoftwareInstaller(ctx context.Context, payload *fleet. if payload.Extension == "ipa" { fmt.Println("processing IPA upload") - gotID, err := svc.ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{TeamID: payload.TeamID, Name: payload.Title, BundleID: payload.BundleIdentifier, StorageID: payload.StorageID, Platform: payload.Platform}) + gotTitleID, err := svc.ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{ + TeamID: payload.TeamID, + Name: payload.Title, + BundleID: payload.BundleIdentifier, + StorageID: payload.StorageID, + Platform: payload.Platform, + ValidatedLabels: payload.ValidatedLabels}) if err != nil { return nil, ctxerr.Wrap(ctx, err, "insert in-house app") } - var titleID *uint - if gotID != uint(0) { - titleID = &gotID + // This could totally just be called later, need to refactor + var teamName *string + if payload.TeamID != nil && *payload.TeamID != 0 { + t, err := svc.ds.Team(ctx, *payload.TeamID) + if err != nil { + return nil, ctxerr.Wrap(ctx, err, "getting team name on upload software installer") + } + teamName = &t.Name } + actLabelsIncl, actLabelsExcl := activitySoftwareLabelsFromValidatedLabels(payload.ValidatedLabels) // TODO: needs to insert into in_house_app_labels + if err := svc.NewActivity(ctx, vc.User, fleet.ActivityTypeAddedSoftware{ + SoftwareTitle: payload.Title, + SoftwarePackage: payload.Filename, + TeamName: teamName, + TeamID: payload.TeamID, + SelfService: payload.SelfService, + SoftwareTitleID: gotTitleID, + LabelsIncludeAny: actLabelsIncl, + LabelsExcludeAny: actLabelsExcl, + }); err != nil { + return nil, ctxerr.Wrap(ctx, err, "creating activity for added software") + } + + var titleID *uint + if gotTitleID != uint(0) { + titleID = &gotTitleID + } // TODO: other processing (e.g. labels) return &fleet.SoftwareInstaller{ TeamID: payload.TeamID, @@ -120,6 +149,8 @@ func (svc *Service) UploadSoftwareInstaller(ctx context.Context, payload *fleet. Name: payload.Title, StorageID: payload.StorageID, Platform: payload.Platform, + // LabelsIncludeAny: actLabelsIncl, // TODO: return labels in json response + // LabelsExcludeAny: actLabelsExcl, }, nil } diff --git a/server/datastore/mysql/in_house_apps.go b/server/datastore/mysql/in_house_apps.go index 69d6e4ed676..019ab39c013 100644 --- a/server/datastore/mysql/in_house_apps.go +++ b/server/datastore/mysql/in_house_apps.go @@ -31,6 +31,7 @@ func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHous tid = payload.TeamID } } + titleID, err = ds.getOrGenerateSoftwareInstallerTitleID(ctx, &fleet.UploadSoftwareInstallerPayload{ TeamID: tid, Title: payload.Name, @@ -51,7 +52,7 @@ func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHous payload.Platform, } - _, err := tx.ExecContext(ctx, stmt, args...) + res, err := tx.ExecContext(ctx, stmt, args...) if err != nil { if IsDuplicate(err) { // already exists for this team/no team @@ -60,6 +61,16 @@ func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHous return err } + id64, err := res.LastInsertId() + installerID := uint(id64) + if err != nil { + return err + } + + if err := setOrUpdateSoftwareInstallerLabelsDB(ctx, tx, installerID, *payload.ValidatedLabels, softwareTypeInHouse); err != nil { + return ctxerr.Wrap(ctx, err, "upsert in house app labels") + } + return nil }) diff --git a/server/datastore/mysql/software_installers.go b/server/datastore/mysql/software_installers.go index 223eff99a2b..cc951cea495 100644 --- a/server/datastore/mysql/software_installers.go +++ b/server/datastore/mysql/software_installers.go @@ -503,6 +503,7 @@ type softwareType string const ( softwareTypeInstaller softwareType = "software_installer" softwareTypeVPP softwareType = "vpp_app_team" + softwareTypeInHouse softwareType = "in_house_app" ) // setOrUpdateSoftwareInstallerLabelsDB sets or updates the label associations for the specified software diff --git a/server/fleet/in_house_apps.go b/server/fleet/in_house_apps.go index 66208be0e87..93cb9b56e72 100644 --- a/server/fleet/in_house_apps.go +++ b/server/fleet/in_house_apps.go @@ -1,9 +1,10 @@ package fleet type InHouseAppPayload struct { - TeamID *uint - Name string - BundleID string - StorageID string - Platform string + TeamID *uint + Name string + BundleID string + StorageID string + Platform string + ValidatedLabels *LabelIdentsWithScope } From c9d74c330fb3507b4d62fff55c282b710f9ea827 Mon Sep 17 00:00:00 2001 From: jkatz01 Date: Wed, 8 Oct 2025 14:15:30 -0400 Subject: [PATCH 04/10] refactor and more testing for ipa --- ee/server/service/software_installers.go | 62 ++----------- server/datastore/mysql/in_house_apps.go | 13 +-- server/datastore/mysql/in_house_apps_test.go | 27 ++++-- server/datastore/mysql/software_installers.go | 89 +++++++++++++++++-- server/fleet/datastore.go | 6 +- server/mock/datastore_mock.go | 14 ++- 6 files changed, 135 insertions(+), 76 deletions(-) diff --git a/ee/server/service/software_installers.go b/ee/server/service/software_installers.go index 4b7256a2078..1cbc507ff52 100644 --- a/ee/server/service/software_installers.go +++ b/ee/server/service/software_installers.go @@ -101,59 +101,6 @@ func (svc *Service) UploadSoftwareInstaller(ctx context.Context, payload *fleet. return nil, ctxerr.Wrap(ctx, err, "storing software installer") } - if payload.Extension == "ipa" { - fmt.Println("processing IPA upload") - gotTitleID, err := svc.ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{ - TeamID: payload.TeamID, - Name: payload.Title, - BundleID: payload.BundleIdentifier, - StorageID: payload.StorageID, - Platform: payload.Platform, - ValidatedLabels: payload.ValidatedLabels}) - if err != nil { - return nil, ctxerr.Wrap(ctx, err, "insert in-house app") - } - - // This could totally just be called later, need to refactor - var teamName *string - if payload.TeamID != nil && *payload.TeamID != 0 { - t, err := svc.ds.Team(ctx, *payload.TeamID) - if err != nil { - return nil, ctxerr.Wrap(ctx, err, "getting team name on upload software installer") - } - teamName = &t.Name - } - - actLabelsIncl, actLabelsExcl := activitySoftwareLabelsFromValidatedLabels(payload.ValidatedLabels) // TODO: needs to insert into in_house_app_labels - if err := svc.NewActivity(ctx, vc.User, fleet.ActivityTypeAddedSoftware{ - SoftwareTitle: payload.Title, - SoftwarePackage: payload.Filename, - TeamName: teamName, - TeamID: payload.TeamID, - SelfService: payload.SelfService, - SoftwareTitleID: gotTitleID, - LabelsIncludeAny: actLabelsIncl, - LabelsExcludeAny: actLabelsExcl, - }); err != nil { - return nil, ctxerr.Wrap(ctx, err, "creating activity for added software") - } - - var titleID *uint - if gotTitleID != uint(0) { - titleID = &gotTitleID - } - // TODO: other processing (e.g. labels) - return &fleet.SoftwareInstaller{ - TeamID: payload.TeamID, - TitleID: titleID, - Name: payload.Title, - StorageID: payload.StorageID, - Platform: payload.Platform, - // LabelsIncludeAny: actLabelsIncl, // TODO: return labels in json response - // LabelsExcludeAny: actLabelsExcl, - }, nil - } - // Update $PACKAGE_ID/$UPGRADE_CODE in uninstall script if err := preProcessUninstallScript(payload); err != nil { return nil, &fleet.BadRequestError{ @@ -209,6 +156,15 @@ func (svc *Service) UploadSoftwareInstaller(ctx context.Context, payload *fleet. if payload.TeamID != nil { tmID = *payload.TeamID } + + if payload.Extension == "ipa" { + addedInstaller, err := svc.ds.GetInHouseAppMetadataByTeamAndTitleID(ctx, &tmID, titleID) + if err != nil { + return nil, err + } + return addedInstaller, nil + } + addedInstaller, err := svc.ds.GetSoftwareInstallerMetadataByTeamAndTitleID(ctx, &tmID, titleID, true) if err != nil { return nil, ctxerr.Wrap(ctx, err, "getting added software installer") diff --git a/server/datastore/mysql/in_house_apps.go b/server/datastore/mysql/in_house_apps.go index 019ab39c013..edfc01f96bb 100644 --- a/server/datastore/mysql/in_house_apps.go +++ b/server/datastore/mysql/in_house_apps.go @@ -8,7 +8,7 @@ import ( "github.com/jmoiron/sqlx" ) -func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHouseAppPayload) (titleID uint, err error) { +func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHouseAppPayload) (uint, uint, error) { stmt := ` INSERT INTO in_house_apps ( @@ -32,16 +32,17 @@ func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHous } } - titleID, err = ds.getOrGenerateSoftwareInstallerTitleID(ctx, &fleet.UploadSoftwareInstallerPayload{ + titleID, err := ds.getOrGenerateSoftwareInstallerTitleID(ctx, &fleet.UploadSoftwareInstallerPayload{ TeamID: tid, Title: payload.Name, BundleIdentifier: payload.BundleID, Source: "ios_apps"}, // TODO: what about iPad apps ) if err != nil { - return 0, err + return 0, 0, err } + var installerID uint err = ds.withRetryTxx(ctx, func(tx sqlx.ExtContext) error { args := []any{ tid, @@ -62,17 +63,17 @@ func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHous } id64, err := res.LastInsertId() - installerID := uint(id64) + installerID = uint(id64) if err != nil { return err } - if err := setOrUpdateSoftwareInstallerLabelsDB(ctx, tx, installerID, *payload.ValidatedLabels, softwareTypeInHouse); err != nil { + if err := setOrUpdateSoftwareInstallerLabelsDB(ctx, tx, installerID, *payload.ValidatedLabels, softwareTypeInHouseApp); err != nil { return ctxerr.Wrap(ctx, err, "upsert in house app labels") } return nil }) - return titleID, ctxerr.Wrap(ctx, err, "insert in house app") + return installerID, titleID, ctxerr.Wrap(ctx, err, "insert in house app") } diff --git a/server/datastore/mysql/in_house_apps_test.go b/server/datastore/mysql/in_house_apps_test.go index eca6ed023d1..99595740b60 100644 --- a/server/datastore/mysql/in_house_apps_test.go +++ b/server/datastore/mysql/in_house_apps_test.go @@ -27,11 +27,28 @@ func TestInHouseApps(t *testing.T) { func testInHouseAppsCrud(t *testing.T, ds *Datastore) { ctx := context.Background() - _, err := ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{ - Name: "foo", - StorageID: "testingtesting123", - Platform: "ios", - }) + team, err := ds.NewTeam(ctx, &fleet.Team{Name: "team 1"}) require.NoError(t, err) + + payload := fleet.UploadSoftwareInstallerPayload{ + TeamID: &team.ID, + Title: "foo", + BundleIdentifier: "com.foo", + StorageID: "testingtesting123", + Platform: "ios", + Extension: "ipa", + } + _, _, err = ds.MatchOrCreateSoftwareInstaller(ctx, &payload) + require.Error(t, err, "ValidatedLabels must not be nil") + + payload.ValidatedLabels = &fleet.LabelIdentsWithScope{} + installerID, titleID, err := ds.MatchOrCreateSoftwareInstaller(ctx, &payload) + require.NoError(t, err) + require.NotZero(t, installerID) + require.NotZero(t, titleID) + + installer, err := ds.GetInHouseAppMetadataByTeamAndTitleID(ctx, &team.ID, titleID) + require.NoError(t, err) + require.Equal(t, payload.Title, installer.SoftwareTitle) } diff --git a/server/datastore/mysql/software_installers.go b/server/datastore/mysql/software_installers.go index cc951cea495..85720a17c2a 100644 --- a/server/datastore/mysql/software_installers.go +++ b/server/datastore/mysql/software_installers.go @@ -197,6 +197,23 @@ func (ds *Datastore) MatchOrCreateSoftwareInstaller(ctx context.Context, payload return 0, 0, errors.New("validated labels must not be nil") } + // Insert in house app instead of software installer + // TODO: match if existing in house app + if payload.Extension == "ipa" { + fmt.Println("processing IPA upload") + installerID, titleID, err := ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{ + TeamID: payload.TeamID, + Name: payload.Title, + BundleID: payload.BundleIdentifier, + StorageID: payload.StorageID, + Platform: payload.Platform, + ValidatedLabels: payload.ValidatedLabels}) + if err != nil { + return 0, 0, ctxerr.Wrap(ctx, err, "insert in house app") + } + return installerID, titleID, err + } + titleID, err = ds.getOrGenerateSoftwareInstallerTitleID(ctx, payload) if err != nil { return 0, 0, ctxerr.Wrap(ctx, err, "get or generate software installer title ID") @@ -501,9 +518,9 @@ func (ds *Datastore) addSoftwareTitleToMatchingSoftware(ctx context.Context, tit type softwareType string const ( - softwareTypeInstaller softwareType = "software_installer" - softwareTypeVPP softwareType = "vpp_app_team" - softwareTypeInHouse softwareType = "in_house_app" + softwareTypeInstaller softwareType = "software_installer" + softwareTypeVPP softwareType = "vpp_app_team" + softwareTypeInHouseApp softwareType = "in_house_app" ) // setOrUpdateSoftwareInstallerLabelsDB sets or updates the label associations for the specified software @@ -778,7 +795,7 @@ WHERE // TODO: do we want to include labels on other queries that return software installer metadata // (e.g., GetSoftwareInstallerMetadataByID)? - labels, err := ds.getSoftwareInstallerLabels(ctx, dest.InstallerID) + labels, err := ds.getSoftwareInstallerLabels(ctx, dest.InstallerID, softwareTypeInstaller) if err != nil { return nil, ctxerr.Wrap(ctx, err, "get software installer labels") } @@ -826,23 +843,77 @@ WHERE return &dest, nil } -func (ds *Datastore) getSoftwareInstallerLabels(ctx context.Context, installerID uint) ([]fleet.SoftwareScopeLabel, error) { +func (ds *Datastore) GetInHouseAppMetadataByTeamAndTitleID(ctx context.Context, teamID *uint, titleID uint) (*fleet.SoftwareInstaller, error) { query := ` +SELECT + iha.id, + iha.team_id, + iha.title_id, + COALESCE(iha.name, '') AS software_title, + iha.platform, + iha.storage_id +FROM + in_house_apps iha + JOIN software_titles st ON st.id = iha.title_id +WHERE + iha.title_id = ? AND iha.global_or_team_id = ?` + + var tmID uint + if teamID != nil { + tmID = *teamID + } + + var dest fleet.SoftwareInstaller + err := sqlx.GetContext(ctx, ds.reader(ctx), &dest, query, titleID, tmID) + if err != nil { + if err == sql.ErrNoRows { + return nil, ctxerr.Wrap(ctx, notFound("InHouseApp"), "get in house app metadata") + } + return nil, ctxerr.Wrap(ctx, err, "get in house app metadata") + } + + // TODO: do we want to include labels on other queries that return software installer metadata + // (e.g., GetSoftwareInstallerMetadataByID)? + labels, err := ds.getSoftwareInstallerLabels(ctx, dest.InstallerID, softwareTypeInHouseApp) + if err != nil { + return nil, ctxerr.Wrap(ctx, err, "get in house app labels") + } + var exclAny, inclAny []fleet.SoftwareScopeLabel + for _, l := range labels { + if l.Exclude { + exclAny = append(exclAny, l) + } else { + inclAny = append(inclAny, l) + } + } + + if len(inclAny) > 0 && len(exclAny) > 0 { + // there's a bug somewhere + level.Warn(ds.logger).Log("msg", "in house app has both include and exclude labels", "installer_id", dest.InstallerID, "include", fmt.Sprintf("%v", inclAny), "exclude", fmt.Sprintf("%v", exclAny)) + } + dest.LabelsExcludeAny = exclAny + dest.LabelsIncludeAny = inclAny + + return &dest, nil +} + +func (ds *Datastore) getSoftwareInstallerLabels(ctx context.Context, installerID uint, softwareType softwareType) ([]fleet.SoftwareScopeLabel, error) { + query := fmt.Sprintf(` SELECT label_id, exclude, l.name as label_name, si.title_id FROM - software_installer_labels sil - JOIN software_installers si ON si.id = sil.software_installer_id + %[1]s_labels sil + JOIN %[1]ss si ON si.id = sil.%[1]s_id JOIN labels l ON l.id = sil.label_id WHERE - software_installer_id = ?` + %[1]s_id = ?`, softwareType) var labels []fleet.SoftwareScopeLabel if err := sqlx.SelectContext(ctx, ds.reader(ctx), &labels, query, installerID); err != nil { - return nil, ctxerr.Wrap(ctx, err, "get software installer labels") + return nil, ctxerr.Wrap(ctx, err, fmt.Sprintf("get %s labels", softwareType)) } return labels, nil diff --git a/server/fleet/datastore.go b/server/fleet/datastore.go index b532b4496b2..265bd4ab113 100644 --- a/server/fleet/datastore.go +++ b/server/fleet/datastore.go @@ -1983,6 +1983,10 @@ type Datastore interface { // (if set) post-install scripts, otherwise those fields are left empty. GetSoftwareInstallerMetadataByTeamAndTitleID(ctx context.Context, teamID *uint, titleID uint, withScriptContents bool) (*SoftwareInstaller, error) + // GetInHouseAppMetadataByTeamAndTitleID returns the in house app corresponding + // to the specific team and title ids. + GetInHouseAppMetadataByTeamAndTitleID(ctx context.Context, teamID *uint, titleID uint) (*SoftwareInstaller, error) + // GetSoftwareInstallersPendingUninstallScriptPopulation returns a map of software installers to storage IDs that: // 1. need uninstall scripts populated // 2. can have uninstall scripts auto-generated by Fleet @@ -2409,7 +2413,7 @@ type Datastore interface { // GetCurrentTime gets the current time from the database GetCurrentTime(ctx context.Context) (time.Time, error) - InsertInHouseApp(ctx context.Context, payload *InHouseAppPayload) (titleID uint, err error) + InsertInHouseApp(ctx context.Context, payload *InHouseAppPayload) (installerID uint, titleID uint, err error) } type AndroidDatastore interface { diff --git a/server/mock/datastore_mock.go b/server/mock/datastore_mock.go index 2241ca2b40b..8e6b6b05b4c 100644 --- a/server/mock/datastore_mock.go +++ b/server/mock/datastore_mock.go @@ -1549,7 +1549,7 @@ type BatchApplyCertificateAuthoritiesFunc func(ctx context.Context, ops fleet.Ce type GetCurrentTimeFunc func(ctx context.Context) (time.Time, error) -type InsertInHouseAppFunc func(ctx context.Context, payload *fleet.InHouseAppPayload) (titleID uint, err error) +type InsertInHouseAppFunc func(ctx context.Context, payload *fleet.InHouseAppPayload) (installerID uint, titleID uint, err error) type DataStore struct { HealthCheckFunc HealthCheckFunc @@ -3403,6 +3403,9 @@ type DataStore struct { GetSoftwareInstallerMetadataByTeamAndTitleIDFunc GetSoftwareInstallerMetadataByTeamAndTitleIDFunc GetSoftwareInstallerMetadataByTeamAndTitleIDFuncInvoked bool + GetInHouseAppMetadataByTeamAndTitleIDFunc GetSoftwareInstallerMetadataByTeamAndTitleIDFunc + GetInHouseAppMetadataByTeamAndTitleIDFuncInvoked bool + GetSoftwareInstallersPendingUninstallScriptPopulationFunc GetSoftwareInstallersPendingUninstallScriptPopulationFunc GetSoftwareInstallersPendingUninstallScriptPopulationFuncInvoked bool @@ -8166,6 +8169,13 @@ func (s *DataStore) GetSoftwareInstallerMetadataByTeamAndTitleID(ctx context.Con return s.GetSoftwareInstallerMetadataByTeamAndTitleIDFunc(ctx, teamID, titleID, withScriptContents) } +func (s *DataStore) GetInHouseAppMetadataByTeamAndTitleID(ctx context.Context, teamID *uint, titleID uint, withScriptContents bool) (*fleet.SoftwareInstaller, error) { + s.mu.Lock() + s.GetInHouseAppMetadataByTeamAndTitleIDFuncInvoked = true + s.mu.Unlock() + return s.GetInHouseAppMetadataByTeamAndTitleIDFunc(ctx, teamID, titleID, withScriptContents) +} + func (s *DataStore) GetSoftwareInstallersPendingUninstallScriptPopulation(ctx context.Context) (map[uint]string, error) { s.mu.Lock() s.GetSoftwareInstallersPendingUninstallScriptPopulationFuncInvoked = true @@ -9188,7 +9198,7 @@ func (s *DataStore) GetCurrentTime(ctx context.Context) (time.Time, error) { return s.GetCurrentTimeFunc(ctx) } -func (s *DataStore) InsertInHouseApp(ctx context.Context, payload *fleet.InHouseAppPayload) (titleID uint, err error) { +func (s *DataStore) InsertInHouseApp(ctx context.Context, payload *fleet.InHouseAppPayload) (installerID uint, titleID uint, err error) { s.mu.Lock() s.InsertInHouseAppFuncInvoked = true s.mu.Unlock() From 999db55c49cbf7b06088be762664ee86c9d9e571 Mon Sep 17 00:00:00 2001 From: jkatz01 Date: Wed, 8 Oct 2025 15:40:24 -0400 Subject: [PATCH 05/10] code cleanup --- pkg/file/file.go | 59 ----------------- pkg/file/ipa.go | 65 +++++++++++++++++++ server/datastore/mysql/in_house_apps.go | 13 ++-- server/datastore/mysql/software_installers.go | 5 +- server/fleet/datastore.go | 2 - server/mock/datastore_mock.go | 16 +---- 6 files changed, 75 insertions(+), 85 deletions(-) create mode 100644 pkg/file/ipa.go diff --git a/pkg/file/file.go b/pkg/file/file.go index d68916b3b68..5f7fd936bef 100644 --- a/pkg/file/file.go +++ b/pkg/file/file.go @@ -2,7 +2,6 @@ package file import ( "archive/tar" - "archive/zip" "bufio" "bytes" "compress/gzip" @@ -23,7 +22,6 @@ import ( "github.com/fleetdm/fleet/v4/pkg/secure" "github.com/fleetdm/fleet/v4/server/fleet" "github.com/rs/zerolog" - "howett.net/plist" ) var ( @@ -41,63 +39,6 @@ type InstallerMetadata struct { UpgradeCode string } -func ExtractIPAMetadata(tfr *fleet.TempFileReader) (*InstallerMetadata, error) { - // TODO(JVE): fill me in! needs to unzip the file, then use the binary plist reader we have to get the metadata - h := sha256.New() - _, _ = io.Copy(h, tfr) // writes to a hash cannot fail - if err := tfr.Rewind(); err != nil { - return nil, fmt.Errorf("rewind reader: %w", err) - } - - fmt.Printf("tfr.Name(): %v\n", tfr.Name()) - - r, err := zip.OpenReader(tfr.Name()) - if err != nil { - return nil, err - } - - var plistData struct { - BundleID string `plist:"CFBundleIdentifier"` - Name string `plist:"CFBundleName"` - Version string `plist:"CFBundleShortVersionString"` - } - for _, f := range r.File { - fmt.Printf("f.Name: %v\n", f.Name) - if strings.Contains(f.Name, "Info.plist") { - // Get data from plist file - archiveFile, err := f.Open() - if err != nil { - return nil, fmt.Errorf("could not open archive %s: %w", f.Name, err) - } - defer archiveFile.Close() - - rawData, err := io.ReadAll(archiveFile) - if err != nil { - return nil, err - } - _, err = plist.Unmarshal(rawData, &plistData) - if err != nil { - return nil, err - } - - fmt.Printf("plistData: %+v\n", plistData) - - } - } - - if plistData.BundleID == "" { - return nil, errors.New("couldn't find bundle identifier for in-house app") - } - - return &InstallerMetadata{ - BundleIdentifier: plistData.BundleID, - SHASum: h.Sum(nil), - PackageIDs: []string{plistData.BundleID}, - Name: plistData.Name, - Version: plistData.Version, - }, nil -} - // ExtractInstallerMetadata extracts the software name and version from the // installer file and returns them along with the sha256 hash of the bytes. The // format of the installer is determined based on the magic bytes of the content. diff --git a/pkg/file/ipa.go b/pkg/file/ipa.go new file mode 100644 index 00000000000..e26aa5deb05 --- /dev/null +++ b/pkg/file/ipa.go @@ -0,0 +1,65 @@ +package file + +import ( + "archive/zip" + "crypto/sha256" + "errors" + "fmt" + "io" + "strings" + + "github.com/fleetdm/fleet/v4/server/fleet" + "howett.net/plist" +) + +func ExtractIPAMetadata(tfr *fleet.TempFileReader) (*InstallerMetadata, error) { + h := sha256.New() + _, _ = io.Copy(h, tfr) // writes to a hash cannot fail + if err := tfr.Rewind(); err != nil { + return nil, fmt.Errorf("rewind reader: %w", err) + } + + fmt.Printf("tfr.Name(): %v\n", tfr.Name()) + + r, err := zip.OpenReader(tfr.Name()) + if err != nil { + return nil, err + } + + var plistData struct { + BundleID string `plist:"CFBundleIdentifier"` + Name string `plist:"CFBundleName"` + Version string `plist:"CFBundleShortVersionString"` + } + for _, f := range r.File { + if strings.Contains(f.Name, "Info.plist") { + // Get data from plist file + archiveFile, err := f.Open() + if err != nil { + return nil, fmt.Errorf("could not open archive %s: %w", f.Name, err) + } + defer archiveFile.Close() + + rawData, err := io.ReadAll(archiveFile) + if err != nil { + return nil, err + } + _, err = plist.Unmarshal(rawData, &plistData) + if err != nil { + return nil, err + } + } + } + + if plistData.BundleID == "" { + return nil, errors.New("couldn't find bundle identifier for in-house app") + } + + return &InstallerMetadata{ + BundleIdentifier: plistData.BundleID, + SHASum: h.Sum(nil), + PackageIDs: []string{plistData.BundleID}, + Name: plistData.Name, + Version: plistData.Version, + }, nil +} diff --git a/server/datastore/mysql/in_house_apps.go b/server/datastore/mysql/in_house_apps.go index edfc01f96bb..ebc77f5383d 100644 --- a/server/datastore/mysql/in_house_apps.go +++ b/server/datastore/mysql/in_house_apps.go @@ -8,7 +8,7 @@ import ( "github.com/jmoiron/sqlx" ) -func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHouseAppPayload) (uint, uint, error) { +func (ds *Datastore) insertInHouseApp(ctx context.Context, payload *fleet.InHouseAppPayload) (uint, uint, error) { stmt := ` INSERT INTO in_house_apps ( @@ -19,8 +19,7 @@ func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHous storage_id, platform ) - VALUES (?, ?, ?, ?, ?, ?) - ` + VALUES (?, ?, ?, ?, ?, ?)` var tid *uint var globalOrTeamID uint @@ -39,7 +38,7 @@ func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHous Source: "ios_apps"}, // TODO: what about iPad apps ) if err != nil { - return 0, 0, err + return 0, 0, ctxerr.Wrap(ctx, err, "insertInHouseApp") } var installerID uint @@ -59,13 +58,13 @@ func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHous // already exists for this team/no team err = alreadyExists("InHouseApp", payload.Name) } - return err + return ctxerr.Wrap(ctx, err, "insertInHouseApp") } id64, err := res.LastInsertId() installerID = uint(id64) if err != nil { - return err + ctxerr.Wrap(ctx, err, "insertInHouseApp") } if err := setOrUpdateSoftwareInstallerLabelsDB(ctx, tx, installerID, *payload.ValidatedLabels, softwareTypeInHouseApp); err != nil { @@ -75,5 +74,5 @@ func (ds *Datastore) InsertInHouseApp(ctx context.Context, payload *fleet.InHous return nil }) - return installerID, titleID, ctxerr.Wrap(ctx, err, "insert in house app") + return installerID, titleID, ctxerr.Wrap(ctx, err, "insertInHouseApp") } diff --git a/server/datastore/mysql/software_installers.go b/server/datastore/mysql/software_installers.go index 85720a17c2a..3ffb66f31c4 100644 --- a/server/datastore/mysql/software_installers.go +++ b/server/datastore/mysql/software_installers.go @@ -198,10 +198,9 @@ func (ds *Datastore) MatchOrCreateSoftwareInstaller(ctx context.Context, payload } // Insert in house app instead of software installer - // TODO: match if existing in house app + // TODO(JK): match if there is an existing in house app if payload.Extension == "ipa" { - fmt.Println("processing IPA upload") - installerID, titleID, err := ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{ + installerID, titleID, err := ds.insertInHouseApp(ctx, &fleet.InHouseAppPayload{ TeamID: payload.TeamID, Name: payload.Title, BundleID: payload.BundleIdentifier, diff --git a/server/fleet/datastore.go b/server/fleet/datastore.go index 265bd4ab113..69ba0a320e7 100644 --- a/server/fleet/datastore.go +++ b/server/fleet/datastore.go @@ -2412,8 +2412,6 @@ type Datastore interface { // GetCurrentTime gets the current time from the database GetCurrentTime(ctx context.Context) (time.Time, error) - - InsertInHouseApp(ctx context.Context, payload *InHouseAppPayload) (installerID uint, titleID uint, err error) } type AndroidDatastore interface { diff --git a/server/mock/datastore_mock.go b/server/mock/datastore_mock.go index 8e6b6b05b4c..4811273b42d 100644 --- a/server/mock/datastore_mock.go +++ b/server/mock/datastore_mock.go @@ -1549,8 +1549,6 @@ type BatchApplyCertificateAuthoritiesFunc func(ctx context.Context, ops fleet.Ce type GetCurrentTimeFunc func(ctx context.Context) (time.Time, error) -type InsertInHouseAppFunc func(ctx context.Context, payload *fleet.InHouseAppPayload) (installerID uint, titleID uint, err error) - type DataStore struct { HealthCheckFunc HealthCheckFunc HealthCheckFuncInvoked bool @@ -3844,9 +3842,6 @@ type DataStore struct { GetCurrentTimeFunc GetCurrentTimeFunc GetCurrentTimeFuncInvoked bool - InsertInHouseAppFunc InsertInHouseAppFunc - InsertInHouseAppFuncInvoked bool - mu sync.Mutex } @@ -8169,11 +8164,11 @@ func (s *DataStore) GetSoftwareInstallerMetadataByTeamAndTitleID(ctx context.Con return s.GetSoftwareInstallerMetadataByTeamAndTitleIDFunc(ctx, teamID, titleID, withScriptContents) } -func (s *DataStore) GetInHouseAppMetadataByTeamAndTitleID(ctx context.Context, teamID *uint, titleID uint, withScriptContents bool) (*fleet.SoftwareInstaller, error) { +func (s *DataStore) GetInHouseAppMetadataByTeamAndTitleID(ctx context.Context, teamID *uint, titleID uint) (*fleet.SoftwareInstaller, error) { s.mu.Lock() s.GetInHouseAppMetadataByTeamAndTitleIDFuncInvoked = true s.mu.Unlock() - return s.GetInHouseAppMetadataByTeamAndTitleIDFunc(ctx, teamID, titleID, withScriptContents) + return s.GetInHouseAppMetadataByTeamAndTitleIDFunc(ctx, teamID, titleID) } func (s *DataStore) GetSoftwareInstallersPendingUninstallScriptPopulation(ctx context.Context) (map[uint]string, error) { @@ -9197,10 +9192,3 @@ func (s *DataStore) GetCurrentTime(ctx context.Context) (time.Time, error) { s.mu.Unlock() return s.GetCurrentTimeFunc(ctx) } - -func (s *DataStore) InsertInHouseApp(ctx context.Context, payload *fleet.InHouseAppPayload) (installerID uint, titleID uint, err error) { - s.mu.Lock() - s.InsertInHouseAppFuncInvoked = true - s.mu.Unlock() - return s.InsertInHouseAppFunc(ctx, payload) -} From 40cfe8162b2a70e52bf2be09b81b55c8e5e464ae Mon Sep 17 00:00:00 2001 From: jkatz01 Date: Wed, 8 Oct 2025 16:16:08 -0400 Subject: [PATCH 06/10] fix mock datastore error --- server/mock/datastore_mock.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/mock/datastore_mock.go b/server/mock/datastore_mock.go index 4811273b42d..13086b6347a 100644 --- a/server/mock/datastore_mock.go +++ b/server/mock/datastore_mock.go @@ -1257,6 +1257,8 @@ type ValidateOrbitSoftwareInstallerAccessFunc func(ctx context.Context, hostID u type GetSoftwareInstallerMetadataByTeamAndTitleIDFunc func(ctx context.Context, teamID *uint, titleID uint, withScriptContents bool) (*fleet.SoftwareInstaller, error) +type GetInHouseAppMetadataByTeamAndTitleIDFunc func(ctx context.Context, teamID *uint, titleID uint) (*fleet.SoftwareInstaller, error) + type GetSoftwareInstallersPendingUninstallScriptPopulationFunc func(ctx context.Context) (map[uint]string, error) type GetMSIInstallersWithoutUpgradeCodeFunc func(ctx context.Context) (map[uint]string, error) @@ -3401,7 +3403,7 @@ type DataStore struct { GetSoftwareInstallerMetadataByTeamAndTitleIDFunc GetSoftwareInstallerMetadataByTeamAndTitleIDFunc GetSoftwareInstallerMetadataByTeamAndTitleIDFuncInvoked bool - GetInHouseAppMetadataByTeamAndTitleIDFunc GetSoftwareInstallerMetadataByTeamAndTitleIDFunc + GetInHouseAppMetadataByTeamAndTitleIDFunc GetInHouseAppMetadataByTeamAndTitleIDFunc GetInHouseAppMetadataByTeamAndTitleIDFuncInvoked bool GetSoftwareInstallersPendingUninstallScriptPopulationFunc GetSoftwareInstallersPendingUninstallScriptPopulationFunc From a90ac2aa833add862ad84a69e5ceb244804bb2b1 Mon Sep 17 00:00:00 2001 From: Martin Angers Date: Wed, 8 Oct 2025 08:43:19 -0400 Subject: [PATCH 07/10] DB migration for upcoming activities (#33954) --- server/datastore/mysql/hosts.go | 5 + server/datastore/mysql/hosts_test.go | 13 +++ ...1007145400_AddInHouseAppsToUnifiedQueue.go | 97 +++++++++++++++++++ ...45400_AddInHouseAppsToUnifiedQueue_test.go | 46 +++++++++ server/datastore/mysql/schema.sql | 46 ++++++++- 5 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 server/datastore/mysql/migrations/tables/20251007145400_AddInHouseAppsToUnifiedQueue.go create mode 100644 server/datastore/mysql/migrations/tables/20251007145400_AddInHouseAppsToUnifiedQueue_test.go diff --git a/server/datastore/mysql/hosts.go b/server/datastore/mysql/hosts.go index 9342c49d05a..09ce27071de 100644 --- a/server/datastore/mysql/hosts.go +++ b/server/datastore/mysql/hosts.go @@ -566,6 +566,11 @@ var hostRefs = []string{ "host_mdm_commands", "microsoft_compliance_partner_host_statuses", "host_identity_scep_certificates", + // unlike for host_software_installs, where we use soft-delete so that + // existing activities can still access the installation details, this is not + // needed for in-house apps as the activity contains the MDM command UUID and + // can access the request/response without this table's entry. + "host_in_house_software_installs", } // NOTE: The following tables are explicity excluded from hostRefs list and accordingly are not diff --git a/server/datastore/mysql/hosts_test.go b/server/datastore/mysql/hosts_test.go index 7ae9fd8bc37..0e8dcaa95dd 100644 --- a/server/datastore/mysql/hosts_test.go +++ b/server/datastore/mysql/hosts_test.go @@ -8194,6 +8194,19 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) { `, certSerial, host.ID, "test-host", time.Now().Add(-1*time.Hour), time.Now().Add(24*time.Hour), "-----BEGIN CERTIFICATE-----", []byte{0x04}) require.NoError(t, err) + err = ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{ + Name: "test", + StorageID: uuid.NewString(), + Platform: string(fleet.MacOSPlatform), + }) + require.NoError(t, err) + var inHouseID uint + err = ds.writer(ctx).Get(&inHouseID, "SELECT id FROM in_house_apps WHERE name = ?", "test") + require.NoError(t, err) + _, err = ds.writer(ctx).Exec("INSERT INTO host_in_house_software_installs (host_id, in_house_app_id, command_uuid, platform) VALUES (?, ?, ?, ?)", + host.ID, inHouseID, uuid.NewString(), fleet.MacOSPlatform) + require.NoError(t, err) + // Check there's an entry for the host in all the associated tables. for _, hostRef := range hostRefs { var ok bool diff --git a/server/datastore/mysql/migrations/tables/20251007145400_AddInHouseAppsToUnifiedQueue.go b/server/datastore/mysql/migrations/tables/20251007145400_AddInHouseAppsToUnifiedQueue.go new file mode 100644 index 00000000000..de2112c3e5e --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20251007145400_AddInHouseAppsToUnifiedQueue.go @@ -0,0 +1,97 @@ +package tables + +import ( + "database/sql" + "fmt" +) + +func init() { + MigrationClient.AddMigration(Up_20251007145400, Down_20251007145400) +} + +func Up_20251007145400(tx *sql.Tx) error { + // Note that at the moment of this migration, in-house apps uninstall is not + // supported, so we don't add it to the enum. + _, err := tx.Exec(` +ALTER TABLE upcoming_activities + CHANGE COLUMN activity_type activity_type ENUM('script', 'software_install', 'software_uninstall', 'vpp_app_install', 'in_house_app_install') + COLLATE utf8mb4_unicode_ci NOT NULL +`) + if err != nil { + return fmt.Errorf("failed to alter upcoming_activities activity_type: %w", err) + } + + // Note that at the moment of this migration, auto-install and self-service is not + // supported for in-house apps, so we don't need to add columns for e.g. policy_id. + // See https://www.figma.com/design/zcc45sBgdiDZT11iKjLolh/-30936-Deploy-custom--in-house--iOS-app?node-id=5363-11227&t=S1pEnokvQ83v8eJk-0 + _, err = tx.Exec(` +CREATE TABLE in_house_app_upcoming_activities ( + upcoming_activity_id BIGINT UNSIGNED NOT NULL, + + -- those are all columns and not JSON fields because we need FKs on them to + -- do processing ON DELETE, otherwise we'd have to check for existence of + -- each one when executing the activity (we need the enqueue next activity + -- action to be efficient). + in_house_app_id INT UNSIGNED NOT NULL, + + -- Using DATETIME instead of TIMESTAMP to prevent future Y2K38 issues + created_at DATETIME(6) NOT NULL DEFAULT NOW(6), + updated_at DATETIME(6) NOT NULL DEFAULT NOW(6) ON UPDATE NOW(6), + + PRIMARY KEY (upcoming_activity_id), + CONSTRAINT fk_in_house_app_upcoming_activities_upcoming_activity_id + FOREIGN KEY (upcoming_activity_id) REFERENCES upcoming_activities (id) ON DELETE CASCADE, + CONSTRAINT fk_in_house_app_upcoming_activities_in_house_app_id + FOREIGN KEY (in_house_app_id) REFERENCES in_house_apps (id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci +`, + ) + if err != nil { + return fmt.Errorf("failed to create in_house_app_upcoming_activities table: %w", err) + } + + // Note that at the time of this migration, in-house apps do not support + // auto-install and self-service installs so those columns have not been added. + // See https://www.figma.com/design/zcc45sBgdiDZT11iKjLolh/-30936-Deploy-custom--in-house--iOS-app?node-id=5363-11227&t=S1pEnokvQ83v8eJk-0 + _, err = tx.Exec(` +-- This table is the in-house app equivalent of the host_vpp_software_installs table. +-- It tracks the installation of in-house software on particular hosts. +CREATE TABLE host_in_house_software_installs ( + id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + host_id INT(10) UNSIGNED NOT NULL, + + in_house_app_id INT(10) UNSIGNED NOT NULL, + + -- This is the UUID of the MDM command issued to install the app + command_uuid VARCHAR(127) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + user_id INT(10) UNSIGNED NULL, + platform VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + removed TINYINT NOT NULL DEFAULT '0', + canceled TINYINT NOT NULL DEFAULT '0', + + verification_command_uuid VARCHAR(127) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + verification_at DATETIME(6) DEFAULT NULL, + verification_failed_at DATETIME(6) DEFAULT NULL, + + -- Using DATETIME instead of TIMESTAMP to prevent future Y2K38 issues + created_at DATETIME(6) NOT NULL DEFAULT NOW(6), + updated_at DATETIME(6) NOT NULL DEFAULT NOW(6) ON UPDATE NOW(6), + + PRIMARY KEY(id), + UNIQUE INDEX idx_host_in_house_software_installs_command_uuid (command_uuid), + CONSTRAINT fk_host_in_house_software_installs_user_id + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL, + CONSTRAINT fk_host_in_house_software_installs_in_house_app_id + FOREIGN KEY (in_house_app_id) REFERENCES in_house_apps (id) ON DELETE CASCADE, + INDEX idx_host_in_house_software_installs_verification ((verification_at IS NULL AND verification_failed_at IS NULL)) +) DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci`) + if err != nil { + return fmt.Errorf("failed to create table host_in_house_software_installs: %w", err) + } + + return nil +} + +func Down_20251007145400(tx *sql.Tx) error { + return nil +} diff --git a/server/datastore/mysql/migrations/tables/20251007145400_AddInHouseAppsToUnifiedQueue_test.go b/server/datastore/mysql/migrations/tables/20251007145400_AddInHouseAppsToUnifiedQueue_test.go new file mode 100644 index 00000000000..46155579d16 --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20251007145400_AddInHouseAppsToUnifiedQueue_test.go @@ -0,0 +1,46 @@ +package tables + +import ( + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" +) + +func TestUp_20251007145400(t *testing.T) { + db := applyUpToPrev(t) + + hostID := insertHost(t, db, nil) + contentIDs := insertScriptContents(t, db, 1) + + // create an upcoming activity for script run on that host + execID := uuid.NewString() + uaID := execNoErrLastID(t, db, `INSERT INTO upcoming_activities ( + host_id, activity_type, execution_id, payload + ) VALUES (?, ?, ?, ?)`, hostID, "script", execID, `{}`) + + execNoErr(t, db, `INSERT INTO script_upcoming_activities ( + upcoming_activity_id, script_content_id + ) VALUES (?, ?)`, uaID, contentIDs[0]) + + // Apply current migration. + applyNext(t, db) + + assertRowCount(t, db, "upcoming_activities", 1) + + // activity type is still "script" + var activityType string + err := db.Get(&activityType, "SELECT activity_type FROM upcoming_activities WHERE id = ?", uaID) + require.NoError(t, err) + require.Equal(t, "script", activityType) + + // activity can now be in_house_app_install + execID2 := uuid.NewString() + uaID2 := execNoErrLastID(t, db, `INSERT INTO upcoming_activities ( + host_id, activity_type, execution_id, payload + ) VALUES (?, ?, ?, ?)`, hostID, "in_house_app_install", execID2, `{}`) + + err = db.Get(&activityType, "SELECT activity_type FROM upcoming_activities WHERE id = ?", uaID2) + require.NoError(t, err) + require.Equal(t, "in_house_app_install", activityType) +} diff --git a/server/datastore/mysql/schema.sql b/server/datastore/mysql/schema.sql index 2e5cad92f14..768b4f90a99 100644 --- a/server/datastore/mysql/schema.sql +++ b/server/datastore/mysql/schema.sql @@ -600,6 +600,31 @@ CREATE TABLE `host_identity_scep_serials` ( /*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `host_in_house_software_installs` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `host_id` int unsigned NOT NULL, + `in_house_app_id` int unsigned NOT NULL, + `command_uuid` varchar(127) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `user_id` int unsigned DEFAULT NULL, + `platform` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `removed` tinyint NOT NULL DEFAULT '0', + `canceled` tinyint NOT NULL DEFAULT '0', + `verification_command_uuid` varchar(127) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `verification_at` datetime(6) DEFAULT NULL, + `verification_failed_at` datetime(6) DEFAULT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`id`), + UNIQUE KEY `idx_host_in_house_software_installs_command_uuid` (`command_uuid`), + KEY `fk_host_in_house_software_installs_user_id` (`user_id`), + KEY `fk_host_in_house_software_installs_in_house_app_id` (`in_house_app_id`), + KEY `idx_host_in_house_software_installs_verification` ((((`verification_at` is null) and (`verification_failed_at` is null)))), + CONSTRAINT `fk_host_in_house_software_installs_in_house_app_id` FOREIGN KEY (`in_house_app_id`) REFERENCES `in_house_apps` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_host_in_house_software_installs_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `host_issues` ( `host_id` int unsigned NOT NULL, `failing_policies_count` int unsigned NOT NULL DEFAULT '0', @@ -1082,6 +1107,19 @@ CREATE TABLE `in_house_app_labels` ( /*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `in_house_app_upcoming_activities` ( + `upcoming_activity_id` bigint unsigned NOT NULL, + `in_house_app_id` int unsigned NOT NULL, + `created_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + `updated_at` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), + PRIMARY KEY (`upcoming_activity_id`), + KEY `fk_in_house_app_upcoming_activities_in_house_app_id` (`in_house_app_id`), + CONSTRAINT `fk_in_house_app_upcoming_activities_in_house_app_id` FOREIGN KEY (`in_house_app_id`) REFERENCES `in_house_apps` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_in_house_app_upcoming_activities_upcoming_activity_id` FOREIGN KEY (`upcoming_activity_id`) REFERENCES `upcoming_activities` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `in_house_apps` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `title_id` int unsigned DEFAULT NULL, @@ -1594,9 +1632,9 @@ CREATE TABLE `migration_status_tables` ( `is_applied` tinyint(1) NOT NULL, `tstamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) -) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=426 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=427 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -INSERT INTO `migration_status_tables` VALUES (1,0,1,'2020-01-01 01:01:01'),(2,20161118193812,1,'2020-01-01 01:01:01'),(3,20161118211713,1,'2020-01-01 01:01:01'),(4,20161118212436,1,'2020-01-01 01:01:01'),(5,20161118212515,1,'2020-01-01 01:01:01'),(6,20161118212528,1,'2020-01-01 01:01:01'),(7,20161118212538,1,'2020-01-01 01:01:01'),(8,20161118212549,1,'2020-01-01 01:01:01'),(9,20161118212557,1,'2020-01-01 01:01:01'),(10,20161118212604,1,'2020-01-01 01:01:01'),(11,20161118212613,1,'2020-01-01 01:01:01'),(12,20161118212621,1,'2020-01-01 01:01:01'),(13,20161118212630,1,'2020-01-01 01:01:01'),(14,20161118212641,1,'2020-01-01 01:01:01'),(15,20161118212649,1,'2020-01-01 01:01:01'),(16,20161118212656,1,'2020-01-01 01:01:01'),(17,20161118212758,1,'2020-01-01 01:01:01'),(18,20161128234849,1,'2020-01-01 01:01:01'),(19,20161230162221,1,'2020-01-01 01:01:01'),(20,20170104113816,1,'2020-01-01 01:01:01'),(21,20170105151732,1,'2020-01-01 01:01:01'),(22,20170108191242,1,'2020-01-01 01:01:01'),(23,20170109094020,1,'2020-01-01 01:01:01'),(24,20170109130438,1,'2020-01-01 01:01:01'),(25,20170110202752,1,'2020-01-01 01:01:01'),(26,20170111133013,1,'2020-01-01 01:01:01'),(27,20170117025759,1,'2020-01-01 01:01:01'),(28,20170118191001,1,'2020-01-01 01:01:01'),(29,20170119234632,1,'2020-01-01 01:01:01'),(30,20170124230432,1,'2020-01-01 01:01:01'),(31,20170127014618,1,'2020-01-01 01:01:01'),(32,20170131232841,1,'2020-01-01 01:01:01'),(33,20170223094154,1,'2020-01-01 01:01:01'),(34,20170306075207,1,'2020-01-01 01:01:01'),(35,20170309100733,1,'2020-01-01 01:01:01'),(36,20170331111922,1,'2020-01-01 01:01:01'),(37,20170502143928,1,'2020-01-01 01:01:01'),(38,20170504130602,1,'2020-01-01 01:01:01'),(39,20170509132100,1,'2020-01-01 01:01:01'),(40,20170519105647,1,'2020-01-01 01:01:01'),(41,20170519105648,1,'2020-01-01 01:01:01'),(42,20170831234300,1,'2020-01-01 01:01:01'),(43,20170831234301,1,'2020-01-01 01:01:01'),(44,20170831234303,1,'2020-01-01 01:01:01'),(45,20171116163618,1,'2020-01-01 01:01:01'),(46,20171219164727,1,'2020-01-01 01:01:01'),(47,20180620164811,1,'2020-01-01 01:01:01'),(48,20180620175054,1,'2020-01-01 01:01:01'),(49,20180620175055,1,'2020-01-01 01:01:01'),(50,20191010101639,1,'2020-01-01 01:01:01'),(51,20191010155147,1,'2020-01-01 01:01:01'),(52,20191220130734,1,'2020-01-01 01:01:01'),(53,20200311140000,1,'2020-01-01 01:01:01'),(54,20200405120000,1,'2020-01-01 01:01:01'),(55,20200407120000,1,'2020-01-01 01:01:01'),(56,20200420120000,1,'2020-01-01 01:01:01'),(57,20200504120000,1,'2020-01-01 01:01:01'),(58,20200512120000,1,'2020-01-01 01:01:01'),(59,20200707120000,1,'2020-01-01 01:01:01'),(60,20201011162341,1,'2020-01-01 01:01:01'),(61,20201021104586,1,'2020-01-01 01:01:01'),(62,20201102112520,1,'2020-01-01 01:01:01'),(63,20201208121729,1,'2020-01-01 01:01:01'),(64,20201215091637,1,'2020-01-01 01:01:01'),(65,20210119174155,1,'2020-01-01 01:01:01'),(66,20210326182902,1,'2020-01-01 01:01:01'),(67,20210421112652,1,'2020-01-01 01:01:01'),(68,20210506095025,1,'2020-01-01 01:01:01'),(69,20210513115729,1,'2020-01-01 01:01:01'),(70,20210526113559,1,'2020-01-01 01:01:01'),(71,20210601000001,1,'2020-01-01 01:01:01'),(72,20210601000002,1,'2020-01-01 01:01:01'),(73,20210601000003,1,'2020-01-01 01:01:01'),(74,20210601000004,1,'2020-01-01 01:01:01'),(75,20210601000005,1,'2020-01-01 01:01:01'),(76,20210601000006,1,'2020-01-01 01:01:01'),(77,20210601000007,1,'2020-01-01 01:01:01'),(78,20210601000008,1,'2020-01-01 01:01:01'),(79,20210606151329,1,'2020-01-01 01:01:01'),(80,20210616163757,1,'2020-01-01 01:01:01'),(81,20210617174723,1,'2020-01-01 01:01:01'),(82,20210622160235,1,'2020-01-01 01:01:01'),(83,20210623100031,1,'2020-01-01 01:01:01'),(84,20210623133615,1,'2020-01-01 01:01:01'),(85,20210708143152,1,'2020-01-01 01:01:01'),(86,20210709124443,1,'2020-01-01 01:01:01'),(87,20210712155608,1,'2020-01-01 01:01:01'),(88,20210714102108,1,'2020-01-01 01:01:01'),(89,20210719153709,1,'2020-01-01 01:01:01'),(90,20210721171531,1,'2020-01-01 01:01:01'),(91,20210723135713,1,'2020-01-01 01:01:01'),(92,20210802135933,1,'2020-01-01 01:01:01'),(93,20210806112844,1,'2020-01-01 01:01:01'),(94,20210810095603,1,'2020-01-01 01:01:01'),(95,20210811150223,1,'2020-01-01 01:01:01'),(96,20210818151827,1,'2020-01-01 01:01:01'),(97,20210818151828,1,'2020-01-01 01:01:01'),(98,20210818182258,1,'2020-01-01 01:01:01'),(99,20210819131107,1,'2020-01-01 01:01:01'),(100,20210819143446,1,'2020-01-01 01:01:01'),(101,20210903132338,1,'2020-01-01 01:01:01'),(102,20210915144307,1,'2020-01-01 01:01:01'),(103,20210920155130,1,'2020-01-01 01:01:01'),(104,20210927143115,1,'2020-01-01 01:01:01'),(105,20210927143116,1,'2020-01-01 01:01:01'),(106,20211013133706,1,'2020-01-01 01:01:01'),(107,20211013133707,1,'2020-01-01 01:01:01'),(108,20211102135149,1,'2020-01-01 01:01:01'),(109,20211109121546,1,'2020-01-01 01:01:01'),(110,20211110163320,1,'2020-01-01 01:01:01'),(111,20211116184029,1,'2020-01-01 01:01:01'),(112,20211116184030,1,'2020-01-01 01:01:01'),(113,20211202092042,1,'2020-01-01 01:01:01'),(114,20211202181033,1,'2020-01-01 01:01:01'),(115,20211207161856,1,'2020-01-01 01:01:01'),(116,20211216131203,1,'2020-01-01 01:01:01'),(117,20211221110132,1,'2020-01-01 01:01:01'),(118,20220107155700,1,'2020-01-01 01:01:01'),(119,20220125105650,1,'2020-01-01 01:01:01'),(120,20220201084510,1,'2020-01-01 01:01:01'),(121,20220208144830,1,'2020-01-01 01:01:01'),(122,20220208144831,1,'2020-01-01 01:01:01'),(123,20220215152203,1,'2020-01-01 01:01:01'),(124,20220223113157,1,'2020-01-01 01:01:01'),(125,20220307104655,1,'2020-01-01 01:01:01'),(126,20220309133956,1,'2020-01-01 01:01:01'),(127,20220316155700,1,'2020-01-01 01:01:01'),(128,20220323152301,1,'2020-01-01 01:01:01'),(129,20220330100659,1,'2020-01-01 01:01:01'),(130,20220404091216,1,'2020-01-01 01:01:01'),(131,20220419140750,1,'2020-01-01 01:01:01'),(132,20220428140039,1,'2020-01-01 01:01:01'),(133,20220503134048,1,'2020-01-01 01:01:01'),(134,20220524102918,1,'2020-01-01 01:01:01'),(135,20220526123327,1,'2020-01-01 01:01:01'),(136,20220526123328,1,'2020-01-01 01:01:01'),(137,20220526123329,1,'2020-01-01 01:01:01'),(138,20220608113128,1,'2020-01-01 01:01:01'),(139,20220627104817,1,'2020-01-01 01:01:01'),(140,20220704101843,1,'2020-01-01 01:01:01'),(141,20220708095046,1,'2020-01-01 01:01:01'),(142,20220713091130,1,'2020-01-01 01:01:01'),(143,20220802135510,1,'2020-01-01 01:01:01'),(144,20220818101352,1,'2020-01-01 01:01:01'),(145,20220822161445,1,'2020-01-01 01:01:01'),(146,20220831100036,1,'2020-01-01 01:01:01'),(147,20220831100151,1,'2020-01-01 01:01:01'),(148,20220908181826,1,'2020-01-01 01:01:01'),(149,20220914154915,1,'2020-01-01 01:01:01'),(150,20220915165115,1,'2020-01-01 01:01:01'),(151,20220915165116,1,'2020-01-01 01:01:01'),(152,20220928100158,1,'2020-01-01 01:01:01'),(153,20221014084130,1,'2020-01-01 01:01:01'),(154,20221027085019,1,'2020-01-01 01:01:01'),(155,20221101103952,1,'2020-01-01 01:01:01'),(156,20221104144401,1,'2020-01-01 01:01:01'),(157,20221109100749,1,'2020-01-01 01:01:01'),(158,20221115104546,1,'2020-01-01 01:01:01'),(159,20221130114928,1,'2020-01-01 01:01:01'),(160,20221205112142,1,'2020-01-01 01:01:01'),(161,20221216115820,1,'2020-01-01 01:01:01'),(162,20221220195934,1,'2020-01-01 01:01:01'),(163,20221220195935,1,'2020-01-01 01:01:01'),(164,20221223174807,1,'2020-01-01 01:01:01'),(165,20221227163855,1,'2020-01-01 01:01:01'),(166,20221227163856,1,'2020-01-01 01:01:01'),(167,20230202224725,1,'2020-01-01 01:01:01'),(168,20230206163608,1,'2020-01-01 01:01:01'),(169,20230214131519,1,'2020-01-01 01:01:01'),(170,20230303135738,1,'2020-01-01 01:01:01'),(171,20230313135301,1,'2020-01-01 01:01:01'),(172,20230313141819,1,'2020-01-01 01:01:01'),(173,20230315104937,1,'2020-01-01 01:01:01'),(174,20230317173844,1,'2020-01-01 01:01:01'),(175,20230320133602,1,'2020-01-01 01:01:01'),(176,20230330100011,1,'2020-01-01 01:01:01'),(177,20230330134823,1,'2020-01-01 01:01:01'),(178,20230405232025,1,'2020-01-01 01:01:01'),(179,20230408084104,1,'2020-01-01 01:01:01'),(180,20230411102858,1,'2020-01-01 01:01:01'),(181,20230421155932,1,'2020-01-01 01:01:01'),(182,20230425082126,1,'2020-01-01 01:01:01'),(183,20230425105727,1,'2020-01-01 01:01:01'),(184,20230501154913,1,'2020-01-01 01:01:01'),(185,20230503101418,1,'2020-01-01 01:01:01'),(186,20230515144206,1,'2020-01-01 01:01:01'),(187,20230517140952,1,'2020-01-01 01:01:01'),(188,20230517152807,1,'2020-01-01 01:01:01'),(189,20230518114155,1,'2020-01-01 01:01:01'),(190,20230520153236,1,'2020-01-01 01:01:01'),(191,20230525151159,1,'2020-01-01 01:01:01'),(192,20230530122103,1,'2020-01-01 01:01:01'),(193,20230602111827,1,'2020-01-01 01:01:01'),(194,20230608103123,1,'2020-01-01 01:01:01'),(195,20230629140529,1,'2020-01-01 01:01:01'),(196,20230629140530,1,'2020-01-01 01:01:01'),(197,20230711144622,1,'2020-01-01 01:01:01'),(198,20230721135421,1,'2020-01-01 01:01:01'),(199,20230721161508,1,'2020-01-01 01:01:01'),(200,20230726115701,1,'2020-01-01 01:01:01'),(201,20230807100822,1,'2020-01-01 01:01:01'),(202,20230814150442,1,'2020-01-01 01:01:01'),(203,20230823122728,1,'2020-01-01 01:01:01'),(204,20230906152143,1,'2020-01-01 01:01:01'),(205,20230911163618,1,'2020-01-01 01:01:01'),(206,20230912101759,1,'2020-01-01 01:01:01'),(207,20230915101341,1,'2020-01-01 01:01:01'),(208,20230918132351,1,'2020-01-01 01:01:01'),(209,20231004144339,1,'2020-01-01 01:01:01'),(210,20231009094541,1,'2020-01-01 01:01:01'),(211,20231009094542,1,'2020-01-01 01:01:01'),(212,20231009094543,1,'2020-01-01 01:01:01'),(213,20231009094544,1,'2020-01-01 01:01:01'),(214,20231016091915,1,'2020-01-01 01:01:01'),(215,20231024174135,1,'2020-01-01 01:01:01'),(216,20231025120016,1,'2020-01-01 01:01:01'),(217,20231025160156,1,'2020-01-01 01:01:01'),(218,20231031165350,1,'2020-01-01 01:01:01'),(219,20231106144110,1,'2020-01-01 01:01:01'),(220,20231107130934,1,'2020-01-01 01:01:01'),(221,20231109115838,1,'2020-01-01 01:01:01'),(222,20231121054530,1,'2020-01-01 01:01:01'),(223,20231122101320,1,'2020-01-01 01:01:01'),(224,20231130132828,1,'2020-01-01 01:01:01'),(225,20231130132931,1,'2020-01-01 01:01:01'),(226,20231204155427,1,'2020-01-01 01:01:01'),(227,20231206142340,1,'2020-01-01 01:01:01'),(228,20231207102320,1,'2020-01-01 01:01:01'),(229,20231207102321,1,'2020-01-01 01:01:01'),(230,20231207133731,1,'2020-01-01 01:01:01'),(231,20231212094238,1,'2020-01-01 01:01:01'),(232,20231212095734,1,'2020-01-01 01:01:01'),(233,20231212161121,1,'2020-01-01 01:01:01'),(234,20231215122713,1,'2020-01-01 01:01:01'),(235,20231219143041,1,'2020-01-01 01:01:01'),(236,20231224070653,1,'2020-01-01 01:01:01'),(237,20240110134315,1,'2020-01-01 01:01:01'),(238,20240119091637,1,'2020-01-01 01:01:01'),(239,20240126020642,1,'2020-01-01 01:01:01'),(240,20240126020643,1,'2020-01-01 01:01:01'),(241,20240129162819,1,'2020-01-01 01:01:01'),(242,20240130115133,1,'2020-01-01 01:01:01'),(243,20240131083822,1,'2020-01-01 01:01:01'),(244,20240205095928,1,'2020-01-01 01:01:01'),(245,20240205121956,1,'2020-01-01 01:01:01'),(246,20240209110212,1,'2020-01-01 01:01:01'),(247,20240212111533,1,'2020-01-01 01:01:01'),(248,20240221112844,1,'2020-01-01 01:01:01'),(249,20240222073518,1,'2020-01-01 01:01:01'),(250,20240222135115,1,'2020-01-01 01:01:01'),(251,20240226082255,1,'2020-01-01 01:01:01'),(252,20240228082706,1,'2020-01-01 01:01:01'),(253,20240301173035,1,'2020-01-01 01:01:01'),(254,20240302111134,1,'2020-01-01 01:01:01'),(255,20240312103753,1,'2020-01-01 01:01:01'),(256,20240313143416,1,'2020-01-01 01:01:01'),(257,20240314085226,1,'2020-01-01 01:01:01'),(258,20240314151747,1,'2020-01-01 01:01:01'),(259,20240320145650,1,'2020-01-01 01:01:01'),(260,20240327115530,1,'2020-01-01 01:01:01'),(261,20240327115617,1,'2020-01-01 01:01:01'),(262,20240408085837,1,'2020-01-01 01:01:01'),(263,20240415104633,1,'2020-01-01 01:01:01'),(264,20240430111727,1,'2020-01-01 01:01:01'),(265,20240515200020,1,'2020-01-01 01:01:01'),(266,20240521143023,1,'2020-01-01 01:01:01'),(267,20240521143024,1,'2020-01-01 01:01:01'),(268,20240601174138,1,'2020-01-01 01:01:01'),(269,20240607133721,1,'2020-01-01 01:01:01'),(270,20240612150059,1,'2020-01-01 01:01:01'),(271,20240613162201,1,'2020-01-01 01:01:01'),(272,20240613172616,1,'2020-01-01 01:01:01'),(273,20240618142419,1,'2020-01-01 01:01:01'),(274,20240625093543,1,'2020-01-01 01:01:01'),(275,20240626195531,1,'2020-01-01 01:01:01'),(276,20240702123921,1,'2020-01-01 01:01:01'),(277,20240703154849,1,'2020-01-01 01:01:01'),(278,20240707134035,1,'2020-01-01 01:01:01'),(279,20240707134036,1,'2020-01-01 01:01:01'),(280,20240709124958,1,'2020-01-01 01:01:01'),(281,20240709132642,1,'2020-01-01 01:01:01'),(282,20240709183940,1,'2020-01-01 01:01:01'),(283,20240710155623,1,'2020-01-01 01:01:01'),(284,20240723102712,1,'2020-01-01 01:01:01'),(285,20240725152735,1,'2020-01-01 01:01:01'),(286,20240725182118,1,'2020-01-01 01:01:01'),(287,20240726100517,1,'2020-01-01 01:01:01'),(288,20240730171504,1,'2020-01-01 01:01:01'),(289,20240730174056,1,'2020-01-01 01:01:01'),(290,20240730215453,1,'2020-01-01 01:01:01'),(291,20240730374423,1,'2020-01-01 01:01:01'),(292,20240801115359,1,'2020-01-01 01:01:01'),(293,20240802101043,1,'2020-01-01 01:01:01'),(294,20240802113716,1,'2020-01-01 01:01:01'),(295,20240814135330,1,'2020-01-01 01:01:01'),(296,20240815000000,1,'2020-01-01 01:01:01'),(297,20240815000001,1,'2020-01-01 01:01:01'),(298,20240816103247,1,'2020-01-01 01:01:01'),(299,20240820091218,1,'2020-01-01 01:01:01'),(300,20240826111228,1,'2020-01-01 01:01:01'),(301,20240826160025,1,'2020-01-01 01:01:01'),(302,20240829165448,1,'2020-01-01 01:01:01'),(303,20240829165605,1,'2020-01-01 01:01:01'),(304,20240829165715,1,'2020-01-01 01:01:01'),(305,20240829165930,1,'2020-01-01 01:01:01'),(306,20240829170023,1,'2020-01-01 01:01:01'),(307,20240829170033,1,'2020-01-01 01:01:01'),(308,20240829170044,1,'2020-01-01 01:01:01'),(309,20240905105135,1,'2020-01-01 01:01:01'),(310,20240905140514,1,'2020-01-01 01:01:01'),(311,20240905200000,1,'2020-01-01 01:01:01'),(312,20240905200001,1,'2020-01-01 01:01:01'),(313,20241002104104,1,'2020-01-01 01:01:01'),(314,20241002104105,1,'2020-01-01 01:01:01'),(315,20241002104106,1,'2020-01-01 01:01:01'),(316,20241002210000,1,'2020-01-01 01:01:01'),(317,20241003145349,1,'2020-01-01 01:01:01'),(318,20241004005000,1,'2020-01-01 01:01:01'),(319,20241008083925,1,'2020-01-01 01:01:01'),(320,20241009090010,1,'2020-01-01 01:01:01'),(321,20241017163402,1,'2020-01-01 01:01:01'),(322,20241021224359,1,'2020-01-01 01:01:01'),(323,20241022140321,1,'2020-01-01 01:01:01'),(324,20241025111236,1,'2020-01-01 01:01:01'),(325,20241025112748,1,'2020-01-01 01:01:01'),(326,20241025141855,1,'2020-01-01 01:01:01'),(327,20241110152839,1,'2020-01-01 01:01:01'),(328,20241110152840,1,'2020-01-01 01:01:01'),(329,20241110152841,1,'2020-01-01 01:01:01'),(330,20241116233322,1,'2020-01-01 01:01:01'),(331,20241122171434,1,'2020-01-01 01:01:01'),(332,20241125150614,1,'2020-01-01 01:01:01'),(333,20241203125346,1,'2020-01-01 01:01:01'),(334,20241203130032,1,'2020-01-01 01:01:01'),(335,20241205122800,1,'2020-01-01 01:01:01'),(336,20241209164540,1,'2020-01-01 01:01:01'),(337,20241210140021,1,'2020-01-01 01:01:01'),(338,20241219180042,1,'2020-01-01 01:01:01'),(339,20241220100000,1,'2020-01-01 01:01:01'),(340,20241220114903,1,'2020-01-01 01:01:01'),(341,20241220114904,1,'2020-01-01 01:01:01'),(342,20241224000000,1,'2020-01-01 01:01:01'),(343,20241230000000,1,'2020-01-01 01:01:01'),(344,20241231112624,1,'2020-01-01 01:01:01'),(345,20250102121439,1,'2020-01-01 01:01:01'),(346,20250121094045,1,'2020-01-01 01:01:01'),(347,20250121094500,1,'2020-01-01 01:01:01'),(348,20250121094600,1,'2020-01-01 01:01:01'),(349,20250121094700,1,'2020-01-01 01:01:01'),(350,20250124194347,1,'2020-01-01 01:01:01'),(351,20250127162751,1,'2020-01-01 01:01:01'),(352,20250213104005,1,'2020-01-01 01:01:01'),(353,20250214205657,1,'2020-01-01 01:01:01'),(354,20250217093329,1,'2020-01-01 01:01:01'),(355,20250219090511,1,'2020-01-01 01:01:01'),(356,20250219100000,1,'2020-01-01 01:01:01'),(357,20250219142401,1,'2020-01-01 01:01:01'),(358,20250224184002,1,'2020-01-01 01:01:01'),(359,20250225085436,1,'2020-01-01 01:01:01'),(360,20250226000000,1,'2020-01-01 01:01:01'),(361,20250226153445,1,'2020-01-01 01:01:01'),(362,20250304162702,1,'2020-01-01 01:01:01'),(363,20250306144233,1,'2020-01-01 01:01:01'),(364,20250313163430,1,'2020-01-01 01:01:01'),(365,20250317130944,1,'2020-01-01 01:01:01'),(366,20250318165922,1,'2020-01-01 01:01:01'),(367,20250320132525,1,'2020-01-01 01:01:01'),(368,20250320200000,1,'2020-01-01 01:01:01'),(369,20250326161930,1,'2020-01-01 01:01:01'),(370,20250326161931,1,'2020-01-01 01:01:01'),(371,20250331042354,1,'2020-01-01 01:01:01'),(372,20250331154206,1,'2020-01-01 01:01:01'),(373,20250401155831,1,'2020-01-01 01:01:01'),(374,20250408133233,1,'2020-01-01 01:01:01'),(375,20250410104321,1,'2020-01-01 01:01:01'),(376,20250421085116,1,'2020-01-01 01:01:01'),(377,20250422095806,1,'2020-01-01 01:01:01'),(378,20250424153059,1,'2020-01-01 01:01:01'),(379,20250430103833,1,'2020-01-01 01:01:01'),(380,20250430112622,1,'2020-01-01 01:01:01'),(381,20250501162727,1,'2020-01-01 01:01:01'),(382,20250502154517,1,'2020-01-01 01:01:01'),(383,20250502222222,1,'2020-01-01 01:01:01'),(384,20250507170845,1,'2020-01-01 01:01:01'),(385,20250513162912,1,'2020-01-01 01:01:01'),(386,20250519161614,1,'2020-01-01 01:01:01'),(387,20250519170000,1,'2020-01-01 01:01:01'),(388,20250520153848,1,'2020-01-01 01:01:01'),(389,20250528115932,1,'2020-01-01 01:01:01'),(390,20250529102706,1,'2020-01-01 01:01:01'),(391,20250603105558,1,'2020-01-01 01:01:01'),(392,20250609102714,1,'2020-01-01 01:01:01'),(393,20250609112613,1,'2020-01-01 01:01:01'),(394,20250613103810,1,'2020-01-01 01:01:01'),(395,20250616193950,1,'2020-01-01 01:01:01'),(396,20250624140757,1,'2020-01-01 01:01:01'),(397,20250626130239,1,'2020-01-01 01:01:01'),(398,20250629131032,1,'2020-01-01 01:01:01'),(399,20250701155654,1,'2020-01-01 01:01:01'),(400,20250707095725,1,'2020-01-01 01:01:01'),(401,20250716152435,1,'2020-01-01 01:01:01'),(402,20250718091828,1,'2020-01-01 01:01:01'),(403,20250728122229,1,'2020-01-01 01:01:01'),(404,20250731122715,1,'2020-01-01 01:01:01'),(405,20250731151000,1,'2020-01-01 01:01:01'),(406,20250803000000,1,'2020-01-01 01:01:01'),(407,20250805083116,1,'2020-01-01 01:01:01'),(408,20250807140441,1,'2020-01-01 01:01:01'),(409,20250808000000,1,'2020-01-01 01:01:01'),(410,20250811155036,1,'2020-01-01 01:01:01'),(411,20250813205039,1,'2020-01-01 01:01:01'),(412,20250814123333,1,'2020-01-01 01:01:01'),(413,20250815130115,1,'2020-01-01 01:01:01'),(414,20250816115553,1,'2020-01-01 01:01:01'),(415,20250817154557,1,'2020-01-01 01:01:01'),(416,20250825113751,1,'2020-01-01 01:01:01'),(417,20250827113140,1,'2020-01-01 01:01:01'),(418,20250828120836,1,'2020-01-01 01:01:01'),(419,20250902112642,1,'2020-01-01 01:01:01'),(420,20250904091745,1,'2020-01-01 01:01:01'),(421,20250905090000,1,'2020-01-01 01:01:01'),(422,20250922083056,1,'2020-01-01 01:01:01'),(423,20250923120000,1,'2020-01-01 01:01:01'),(424,20250926123048,1,'2020-01-01 01:01:01'),(425,20251006163522,1,'2020-01-01 01:01:01'); +INSERT INTO `migration_status_tables` VALUES (1,0,1,'2020-01-01 01:01:01'),(2,20161118193812,1,'2020-01-01 01:01:01'),(3,20161118211713,1,'2020-01-01 01:01:01'),(4,20161118212436,1,'2020-01-01 01:01:01'),(5,20161118212515,1,'2020-01-01 01:01:01'),(6,20161118212528,1,'2020-01-01 01:01:01'),(7,20161118212538,1,'2020-01-01 01:01:01'),(8,20161118212549,1,'2020-01-01 01:01:01'),(9,20161118212557,1,'2020-01-01 01:01:01'),(10,20161118212604,1,'2020-01-01 01:01:01'),(11,20161118212613,1,'2020-01-01 01:01:01'),(12,20161118212621,1,'2020-01-01 01:01:01'),(13,20161118212630,1,'2020-01-01 01:01:01'),(14,20161118212641,1,'2020-01-01 01:01:01'),(15,20161118212649,1,'2020-01-01 01:01:01'),(16,20161118212656,1,'2020-01-01 01:01:01'),(17,20161118212758,1,'2020-01-01 01:01:01'),(18,20161128234849,1,'2020-01-01 01:01:01'),(19,20161230162221,1,'2020-01-01 01:01:01'),(20,20170104113816,1,'2020-01-01 01:01:01'),(21,20170105151732,1,'2020-01-01 01:01:01'),(22,20170108191242,1,'2020-01-01 01:01:01'),(23,20170109094020,1,'2020-01-01 01:01:01'),(24,20170109130438,1,'2020-01-01 01:01:01'),(25,20170110202752,1,'2020-01-01 01:01:01'),(26,20170111133013,1,'2020-01-01 01:01:01'),(27,20170117025759,1,'2020-01-01 01:01:01'),(28,20170118191001,1,'2020-01-01 01:01:01'),(29,20170119234632,1,'2020-01-01 01:01:01'),(30,20170124230432,1,'2020-01-01 01:01:01'),(31,20170127014618,1,'2020-01-01 01:01:01'),(32,20170131232841,1,'2020-01-01 01:01:01'),(33,20170223094154,1,'2020-01-01 01:01:01'),(34,20170306075207,1,'2020-01-01 01:01:01'),(35,20170309100733,1,'2020-01-01 01:01:01'),(36,20170331111922,1,'2020-01-01 01:01:01'),(37,20170502143928,1,'2020-01-01 01:01:01'),(38,20170504130602,1,'2020-01-01 01:01:01'),(39,20170509132100,1,'2020-01-01 01:01:01'),(40,20170519105647,1,'2020-01-01 01:01:01'),(41,20170519105648,1,'2020-01-01 01:01:01'),(42,20170831234300,1,'2020-01-01 01:01:01'),(43,20170831234301,1,'2020-01-01 01:01:01'),(44,20170831234303,1,'2020-01-01 01:01:01'),(45,20171116163618,1,'2020-01-01 01:01:01'),(46,20171219164727,1,'2020-01-01 01:01:01'),(47,20180620164811,1,'2020-01-01 01:01:01'),(48,20180620175054,1,'2020-01-01 01:01:01'),(49,20180620175055,1,'2020-01-01 01:01:01'),(50,20191010101639,1,'2020-01-01 01:01:01'),(51,20191010155147,1,'2020-01-01 01:01:01'),(52,20191220130734,1,'2020-01-01 01:01:01'),(53,20200311140000,1,'2020-01-01 01:01:01'),(54,20200405120000,1,'2020-01-01 01:01:01'),(55,20200407120000,1,'2020-01-01 01:01:01'),(56,20200420120000,1,'2020-01-01 01:01:01'),(57,20200504120000,1,'2020-01-01 01:01:01'),(58,20200512120000,1,'2020-01-01 01:01:01'),(59,20200707120000,1,'2020-01-01 01:01:01'),(60,20201011162341,1,'2020-01-01 01:01:01'),(61,20201021104586,1,'2020-01-01 01:01:01'),(62,20201102112520,1,'2020-01-01 01:01:01'),(63,20201208121729,1,'2020-01-01 01:01:01'),(64,20201215091637,1,'2020-01-01 01:01:01'),(65,20210119174155,1,'2020-01-01 01:01:01'),(66,20210326182902,1,'2020-01-01 01:01:01'),(67,20210421112652,1,'2020-01-01 01:01:01'),(68,20210506095025,1,'2020-01-01 01:01:01'),(69,20210513115729,1,'2020-01-01 01:01:01'),(70,20210526113559,1,'2020-01-01 01:01:01'),(71,20210601000001,1,'2020-01-01 01:01:01'),(72,20210601000002,1,'2020-01-01 01:01:01'),(73,20210601000003,1,'2020-01-01 01:01:01'),(74,20210601000004,1,'2020-01-01 01:01:01'),(75,20210601000005,1,'2020-01-01 01:01:01'),(76,20210601000006,1,'2020-01-01 01:01:01'),(77,20210601000007,1,'2020-01-01 01:01:01'),(78,20210601000008,1,'2020-01-01 01:01:01'),(79,20210606151329,1,'2020-01-01 01:01:01'),(80,20210616163757,1,'2020-01-01 01:01:01'),(81,20210617174723,1,'2020-01-01 01:01:01'),(82,20210622160235,1,'2020-01-01 01:01:01'),(83,20210623100031,1,'2020-01-01 01:01:01'),(84,20210623133615,1,'2020-01-01 01:01:01'),(85,20210708143152,1,'2020-01-01 01:01:01'),(86,20210709124443,1,'2020-01-01 01:01:01'),(87,20210712155608,1,'2020-01-01 01:01:01'),(88,20210714102108,1,'2020-01-01 01:01:01'),(89,20210719153709,1,'2020-01-01 01:01:01'),(90,20210721171531,1,'2020-01-01 01:01:01'),(91,20210723135713,1,'2020-01-01 01:01:01'),(92,20210802135933,1,'2020-01-01 01:01:01'),(93,20210806112844,1,'2020-01-01 01:01:01'),(94,20210810095603,1,'2020-01-01 01:01:01'),(95,20210811150223,1,'2020-01-01 01:01:01'),(96,20210818151827,1,'2020-01-01 01:01:01'),(97,20210818151828,1,'2020-01-01 01:01:01'),(98,20210818182258,1,'2020-01-01 01:01:01'),(99,20210819131107,1,'2020-01-01 01:01:01'),(100,20210819143446,1,'2020-01-01 01:01:01'),(101,20210903132338,1,'2020-01-01 01:01:01'),(102,20210915144307,1,'2020-01-01 01:01:01'),(103,20210920155130,1,'2020-01-01 01:01:01'),(104,20210927143115,1,'2020-01-01 01:01:01'),(105,20210927143116,1,'2020-01-01 01:01:01'),(106,20211013133706,1,'2020-01-01 01:01:01'),(107,20211013133707,1,'2020-01-01 01:01:01'),(108,20211102135149,1,'2020-01-01 01:01:01'),(109,20211109121546,1,'2020-01-01 01:01:01'),(110,20211110163320,1,'2020-01-01 01:01:01'),(111,20211116184029,1,'2020-01-01 01:01:01'),(112,20211116184030,1,'2020-01-01 01:01:01'),(113,20211202092042,1,'2020-01-01 01:01:01'),(114,20211202181033,1,'2020-01-01 01:01:01'),(115,20211207161856,1,'2020-01-01 01:01:01'),(116,20211216131203,1,'2020-01-01 01:01:01'),(117,20211221110132,1,'2020-01-01 01:01:01'),(118,20220107155700,1,'2020-01-01 01:01:01'),(119,20220125105650,1,'2020-01-01 01:01:01'),(120,20220201084510,1,'2020-01-01 01:01:01'),(121,20220208144830,1,'2020-01-01 01:01:01'),(122,20220208144831,1,'2020-01-01 01:01:01'),(123,20220215152203,1,'2020-01-01 01:01:01'),(124,20220223113157,1,'2020-01-01 01:01:01'),(125,20220307104655,1,'2020-01-01 01:01:01'),(126,20220309133956,1,'2020-01-01 01:01:01'),(127,20220316155700,1,'2020-01-01 01:01:01'),(128,20220323152301,1,'2020-01-01 01:01:01'),(129,20220330100659,1,'2020-01-01 01:01:01'),(130,20220404091216,1,'2020-01-01 01:01:01'),(131,20220419140750,1,'2020-01-01 01:01:01'),(132,20220428140039,1,'2020-01-01 01:01:01'),(133,20220503134048,1,'2020-01-01 01:01:01'),(134,20220524102918,1,'2020-01-01 01:01:01'),(135,20220526123327,1,'2020-01-01 01:01:01'),(136,20220526123328,1,'2020-01-01 01:01:01'),(137,20220526123329,1,'2020-01-01 01:01:01'),(138,20220608113128,1,'2020-01-01 01:01:01'),(139,20220627104817,1,'2020-01-01 01:01:01'),(140,20220704101843,1,'2020-01-01 01:01:01'),(141,20220708095046,1,'2020-01-01 01:01:01'),(142,20220713091130,1,'2020-01-01 01:01:01'),(143,20220802135510,1,'2020-01-01 01:01:01'),(144,20220818101352,1,'2020-01-01 01:01:01'),(145,20220822161445,1,'2020-01-01 01:01:01'),(146,20220831100036,1,'2020-01-01 01:01:01'),(147,20220831100151,1,'2020-01-01 01:01:01'),(148,20220908181826,1,'2020-01-01 01:01:01'),(149,20220914154915,1,'2020-01-01 01:01:01'),(150,20220915165115,1,'2020-01-01 01:01:01'),(151,20220915165116,1,'2020-01-01 01:01:01'),(152,20220928100158,1,'2020-01-01 01:01:01'),(153,20221014084130,1,'2020-01-01 01:01:01'),(154,20221027085019,1,'2020-01-01 01:01:01'),(155,20221101103952,1,'2020-01-01 01:01:01'),(156,20221104144401,1,'2020-01-01 01:01:01'),(157,20221109100749,1,'2020-01-01 01:01:01'),(158,20221115104546,1,'2020-01-01 01:01:01'),(159,20221130114928,1,'2020-01-01 01:01:01'),(160,20221205112142,1,'2020-01-01 01:01:01'),(161,20221216115820,1,'2020-01-01 01:01:01'),(162,20221220195934,1,'2020-01-01 01:01:01'),(163,20221220195935,1,'2020-01-01 01:01:01'),(164,20221223174807,1,'2020-01-01 01:01:01'),(165,20221227163855,1,'2020-01-01 01:01:01'),(166,20221227163856,1,'2020-01-01 01:01:01'),(167,20230202224725,1,'2020-01-01 01:01:01'),(168,20230206163608,1,'2020-01-01 01:01:01'),(169,20230214131519,1,'2020-01-01 01:01:01'),(170,20230303135738,1,'2020-01-01 01:01:01'),(171,20230313135301,1,'2020-01-01 01:01:01'),(172,20230313141819,1,'2020-01-01 01:01:01'),(173,20230315104937,1,'2020-01-01 01:01:01'),(174,20230317173844,1,'2020-01-01 01:01:01'),(175,20230320133602,1,'2020-01-01 01:01:01'),(176,20230330100011,1,'2020-01-01 01:01:01'),(177,20230330134823,1,'2020-01-01 01:01:01'),(178,20230405232025,1,'2020-01-01 01:01:01'),(179,20230408084104,1,'2020-01-01 01:01:01'),(180,20230411102858,1,'2020-01-01 01:01:01'),(181,20230421155932,1,'2020-01-01 01:01:01'),(182,20230425082126,1,'2020-01-01 01:01:01'),(183,20230425105727,1,'2020-01-01 01:01:01'),(184,20230501154913,1,'2020-01-01 01:01:01'),(185,20230503101418,1,'2020-01-01 01:01:01'),(186,20230515144206,1,'2020-01-01 01:01:01'),(187,20230517140952,1,'2020-01-01 01:01:01'),(188,20230517152807,1,'2020-01-01 01:01:01'),(189,20230518114155,1,'2020-01-01 01:01:01'),(190,20230520153236,1,'2020-01-01 01:01:01'),(191,20230525151159,1,'2020-01-01 01:01:01'),(192,20230530122103,1,'2020-01-01 01:01:01'),(193,20230602111827,1,'2020-01-01 01:01:01'),(194,20230608103123,1,'2020-01-01 01:01:01'),(195,20230629140529,1,'2020-01-01 01:01:01'),(196,20230629140530,1,'2020-01-01 01:01:01'),(197,20230711144622,1,'2020-01-01 01:01:01'),(198,20230721135421,1,'2020-01-01 01:01:01'),(199,20230721161508,1,'2020-01-01 01:01:01'),(200,20230726115701,1,'2020-01-01 01:01:01'),(201,20230807100822,1,'2020-01-01 01:01:01'),(202,20230814150442,1,'2020-01-01 01:01:01'),(203,20230823122728,1,'2020-01-01 01:01:01'),(204,20230906152143,1,'2020-01-01 01:01:01'),(205,20230911163618,1,'2020-01-01 01:01:01'),(206,20230912101759,1,'2020-01-01 01:01:01'),(207,20230915101341,1,'2020-01-01 01:01:01'),(208,20230918132351,1,'2020-01-01 01:01:01'),(209,20231004144339,1,'2020-01-01 01:01:01'),(210,20231009094541,1,'2020-01-01 01:01:01'),(211,20231009094542,1,'2020-01-01 01:01:01'),(212,20231009094543,1,'2020-01-01 01:01:01'),(213,20231009094544,1,'2020-01-01 01:01:01'),(214,20231016091915,1,'2020-01-01 01:01:01'),(215,20231024174135,1,'2020-01-01 01:01:01'),(216,20231025120016,1,'2020-01-01 01:01:01'),(217,20231025160156,1,'2020-01-01 01:01:01'),(218,20231031165350,1,'2020-01-01 01:01:01'),(219,20231106144110,1,'2020-01-01 01:01:01'),(220,20231107130934,1,'2020-01-01 01:01:01'),(221,20231109115838,1,'2020-01-01 01:01:01'),(222,20231121054530,1,'2020-01-01 01:01:01'),(223,20231122101320,1,'2020-01-01 01:01:01'),(224,20231130132828,1,'2020-01-01 01:01:01'),(225,20231130132931,1,'2020-01-01 01:01:01'),(226,20231204155427,1,'2020-01-01 01:01:01'),(227,20231206142340,1,'2020-01-01 01:01:01'),(228,20231207102320,1,'2020-01-01 01:01:01'),(229,20231207102321,1,'2020-01-01 01:01:01'),(230,20231207133731,1,'2020-01-01 01:01:01'),(231,20231212094238,1,'2020-01-01 01:01:01'),(232,20231212095734,1,'2020-01-01 01:01:01'),(233,20231212161121,1,'2020-01-01 01:01:01'),(234,20231215122713,1,'2020-01-01 01:01:01'),(235,20231219143041,1,'2020-01-01 01:01:01'),(236,20231224070653,1,'2020-01-01 01:01:01'),(237,20240110134315,1,'2020-01-01 01:01:01'),(238,20240119091637,1,'2020-01-01 01:01:01'),(239,20240126020642,1,'2020-01-01 01:01:01'),(240,20240126020643,1,'2020-01-01 01:01:01'),(241,20240129162819,1,'2020-01-01 01:01:01'),(242,20240130115133,1,'2020-01-01 01:01:01'),(243,20240131083822,1,'2020-01-01 01:01:01'),(244,20240205095928,1,'2020-01-01 01:01:01'),(245,20240205121956,1,'2020-01-01 01:01:01'),(246,20240209110212,1,'2020-01-01 01:01:01'),(247,20240212111533,1,'2020-01-01 01:01:01'),(248,20240221112844,1,'2020-01-01 01:01:01'),(249,20240222073518,1,'2020-01-01 01:01:01'),(250,20240222135115,1,'2020-01-01 01:01:01'),(251,20240226082255,1,'2020-01-01 01:01:01'),(252,20240228082706,1,'2020-01-01 01:01:01'),(253,20240301173035,1,'2020-01-01 01:01:01'),(254,20240302111134,1,'2020-01-01 01:01:01'),(255,20240312103753,1,'2020-01-01 01:01:01'),(256,20240313143416,1,'2020-01-01 01:01:01'),(257,20240314085226,1,'2020-01-01 01:01:01'),(258,20240314151747,1,'2020-01-01 01:01:01'),(259,20240320145650,1,'2020-01-01 01:01:01'),(260,20240327115530,1,'2020-01-01 01:01:01'),(261,20240327115617,1,'2020-01-01 01:01:01'),(262,20240408085837,1,'2020-01-01 01:01:01'),(263,20240415104633,1,'2020-01-01 01:01:01'),(264,20240430111727,1,'2020-01-01 01:01:01'),(265,20240515200020,1,'2020-01-01 01:01:01'),(266,20240521143023,1,'2020-01-01 01:01:01'),(267,20240521143024,1,'2020-01-01 01:01:01'),(268,20240601174138,1,'2020-01-01 01:01:01'),(269,20240607133721,1,'2020-01-01 01:01:01'),(270,20240612150059,1,'2020-01-01 01:01:01'),(271,20240613162201,1,'2020-01-01 01:01:01'),(272,20240613172616,1,'2020-01-01 01:01:01'),(273,20240618142419,1,'2020-01-01 01:01:01'),(274,20240625093543,1,'2020-01-01 01:01:01'),(275,20240626195531,1,'2020-01-01 01:01:01'),(276,20240702123921,1,'2020-01-01 01:01:01'),(277,20240703154849,1,'2020-01-01 01:01:01'),(278,20240707134035,1,'2020-01-01 01:01:01'),(279,20240707134036,1,'2020-01-01 01:01:01'),(280,20240709124958,1,'2020-01-01 01:01:01'),(281,20240709132642,1,'2020-01-01 01:01:01'),(282,20240709183940,1,'2020-01-01 01:01:01'),(283,20240710155623,1,'2020-01-01 01:01:01'),(284,20240723102712,1,'2020-01-01 01:01:01'),(285,20240725152735,1,'2020-01-01 01:01:01'),(286,20240725182118,1,'2020-01-01 01:01:01'),(287,20240726100517,1,'2020-01-01 01:01:01'),(288,20240730171504,1,'2020-01-01 01:01:01'),(289,20240730174056,1,'2020-01-01 01:01:01'),(290,20240730215453,1,'2020-01-01 01:01:01'),(291,20240730374423,1,'2020-01-01 01:01:01'),(292,20240801115359,1,'2020-01-01 01:01:01'),(293,20240802101043,1,'2020-01-01 01:01:01'),(294,20240802113716,1,'2020-01-01 01:01:01'),(295,20240814135330,1,'2020-01-01 01:01:01'),(296,20240815000000,1,'2020-01-01 01:01:01'),(297,20240815000001,1,'2020-01-01 01:01:01'),(298,20240816103247,1,'2020-01-01 01:01:01'),(299,20240820091218,1,'2020-01-01 01:01:01'),(300,20240826111228,1,'2020-01-01 01:01:01'),(301,20240826160025,1,'2020-01-01 01:01:01'),(302,20240829165448,1,'2020-01-01 01:01:01'),(303,20240829165605,1,'2020-01-01 01:01:01'),(304,20240829165715,1,'2020-01-01 01:01:01'),(305,20240829165930,1,'2020-01-01 01:01:01'),(306,20240829170023,1,'2020-01-01 01:01:01'),(307,20240829170033,1,'2020-01-01 01:01:01'),(308,20240829170044,1,'2020-01-01 01:01:01'),(309,20240905105135,1,'2020-01-01 01:01:01'),(310,20240905140514,1,'2020-01-01 01:01:01'),(311,20240905200000,1,'2020-01-01 01:01:01'),(312,20240905200001,1,'2020-01-01 01:01:01'),(313,20241002104104,1,'2020-01-01 01:01:01'),(314,20241002104105,1,'2020-01-01 01:01:01'),(315,20241002104106,1,'2020-01-01 01:01:01'),(316,20241002210000,1,'2020-01-01 01:01:01'),(317,20241003145349,1,'2020-01-01 01:01:01'),(318,20241004005000,1,'2020-01-01 01:01:01'),(319,20241008083925,1,'2020-01-01 01:01:01'),(320,20241009090010,1,'2020-01-01 01:01:01'),(321,20241017163402,1,'2020-01-01 01:01:01'),(322,20241021224359,1,'2020-01-01 01:01:01'),(323,20241022140321,1,'2020-01-01 01:01:01'),(324,20241025111236,1,'2020-01-01 01:01:01'),(325,20241025112748,1,'2020-01-01 01:01:01'),(326,20241025141855,1,'2020-01-01 01:01:01'),(327,20241110152839,1,'2020-01-01 01:01:01'),(328,20241110152840,1,'2020-01-01 01:01:01'),(329,20241110152841,1,'2020-01-01 01:01:01'),(330,20241116233322,1,'2020-01-01 01:01:01'),(331,20241122171434,1,'2020-01-01 01:01:01'),(332,20241125150614,1,'2020-01-01 01:01:01'),(333,20241203125346,1,'2020-01-01 01:01:01'),(334,20241203130032,1,'2020-01-01 01:01:01'),(335,20241205122800,1,'2020-01-01 01:01:01'),(336,20241209164540,1,'2020-01-01 01:01:01'),(337,20241210140021,1,'2020-01-01 01:01:01'),(338,20241219180042,1,'2020-01-01 01:01:01'),(339,20241220100000,1,'2020-01-01 01:01:01'),(340,20241220114903,1,'2020-01-01 01:01:01'),(341,20241220114904,1,'2020-01-01 01:01:01'),(342,20241224000000,1,'2020-01-01 01:01:01'),(343,20241230000000,1,'2020-01-01 01:01:01'),(344,20241231112624,1,'2020-01-01 01:01:01'),(345,20250102121439,1,'2020-01-01 01:01:01'),(346,20250121094045,1,'2020-01-01 01:01:01'),(347,20250121094500,1,'2020-01-01 01:01:01'),(348,20250121094600,1,'2020-01-01 01:01:01'),(349,20250121094700,1,'2020-01-01 01:01:01'),(350,20250124194347,1,'2020-01-01 01:01:01'),(351,20250127162751,1,'2020-01-01 01:01:01'),(352,20250213104005,1,'2020-01-01 01:01:01'),(353,20250214205657,1,'2020-01-01 01:01:01'),(354,20250217093329,1,'2020-01-01 01:01:01'),(355,20250219090511,1,'2020-01-01 01:01:01'),(356,20250219100000,1,'2020-01-01 01:01:01'),(357,20250219142401,1,'2020-01-01 01:01:01'),(358,20250224184002,1,'2020-01-01 01:01:01'),(359,20250225085436,1,'2020-01-01 01:01:01'),(360,20250226000000,1,'2020-01-01 01:01:01'),(361,20250226153445,1,'2020-01-01 01:01:01'),(362,20250304162702,1,'2020-01-01 01:01:01'),(363,20250306144233,1,'2020-01-01 01:01:01'),(364,20250313163430,1,'2020-01-01 01:01:01'),(365,20250317130944,1,'2020-01-01 01:01:01'),(366,20250318165922,1,'2020-01-01 01:01:01'),(367,20250320132525,1,'2020-01-01 01:01:01'),(368,20250320200000,1,'2020-01-01 01:01:01'),(369,20250326161930,1,'2020-01-01 01:01:01'),(370,20250326161931,1,'2020-01-01 01:01:01'),(371,20250331042354,1,'2020-01-01 01:01:01'),(372,20250331154206,1,'2020-01-01 01:01:01'),(373,20250401155831,1,'2020-01-01 01:01:01'),(374,20250408133233,1,'2020-01-01 01:01:01'),(375,20250410104321,1,'2020-01-01 01:01:01'),(376,20250421085116,1,'2020-01-01 01:01:01'),(377,20250422095806,1,'2020-01-01 01:01:01'),(378,20250424153059,1,'2020-01-01 01:01:01'),(379,20250430103833,1,'2020-01-01 01:01:01'),(380,20250430112622,1,'2020-01-01 01:01:01'),(381,20250501162727,1,'2020-01-01 01:01:01'),(382,20250502154517,1,'2020-01-01 01:01:01'),(383,20250502222222,1,'2020-01-01 01:01:01'),(384,20250507170845,1,'2020-01-01 01:01:01'),(385,20250513162912,1,'2020-01-01 01:01:01'),(386,20250519161614,1,'2020-01-01 01:01:01'),(387,20250519170000,1,'2020-01-01 01:01:01'),(388,20250520153848,1,'2020-01-01 01:01:01'),(389,20250528115932,1,'2020-01-01 01:01:01'),(390,20250529102706,1,'2020-01-01 01:01:01'),(391,20250603105558,1,'2020-01-01 01:01:01'),(392,20250609102714,1,'2020-01-01 01:01:01'),(393,20250609112613,1,'2020-01-01 01:01:01'),(394,20250613103810,1,'2020-01-01 01:01:01'),(395,20250616193950,1,'2020-01-01 01:01:01'),(396,20250624140757,1,'2020-01-01 01:01:01'),(397,20250626130239,1,'2020-01-01 01:01:01'),(398,20250629131032,1,'2020-01-01 01:01:01'),(399,20250701155654,1,'2020-01-01 01:01:01'),(400,20250707095725,1,'2020-01-01 01:01:01'),(401,20250716152435,1,'2020-01-01 01:01:01'),(402,20250718091828,1,'2020-01-01 01:01:01'),(403,20250728122229,1,'2020-01-01 01:01:01'),(404,20250731122715,1,'2020-01-01 01:01:01'),(405,20250731151000,1,'2020-01-01 01:01:01'),(406,20250803000000,1,'2020-01-01 01:01:01'),(407,20250805083116,1,'2020-01-01 01:01:01'),(408,20250807140441,1,'2020-01-01 01:01:01'),(409,20250808000000,1,'2020-01-01 01:01:01'),(410,20250811155036,1,'2020-01-01 01:01:01'),(411,20250813205039,1,'2020-01-01 01:01:01'),(412,20250814123333,1,'2020-01-01 01:01:01'),(413,20250815130115,1,'2020-01-01 01:01:01'),(414,20250816115553,1,'2020-01-01 01:01:01'),(415,20250817154557,1,'2020-01-01 01:01:01'),(416,20250825113751,1,'2020-01-01 01:01:01'),(417,20250827113140,1,'2020-01-01 01:01:01'),(418,20250828120836,1,'2020-01-01 01:01:01'),(419,20250902112642,1,'2020-01-01 01:01:01'),(420,20250904091745,1,'2020-01-01 01:01:01'),(421,20250905090000,1,'2020-01-01 01:01:01'),(422,20250922083056,1,'2020-01-01 01:01:01'),(423,20250923120000,1,'2020-01-01 01:01:01'),(424,20250926123048,1,'2020-01-01 01:01:01'),(425,20251006163522,1,'2020-01-01 01:01:01'),(426,20251007145400,1,'2020-01-01 01:01:01'); /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `mobile_device_management_solutions` ( @@ -2596,7 +2634,7 @@ CREATE TABLE `upcoming_activities` ( `priority` int NOT NULL DEFAULT '0', `user_id` int unsigned DEFAULT NULL, `fleet_initiated` tinyint(1) NOT NULL DEFAULT '0', - `activity_type` enum('script','software_install','software_uninstall','vpp_app_install') COLLATE utf8mb4_unicode_ci NOT NULL, + `activity_type` enum('script','software_install','software_uninstall','vpp_app_install','in_house_app_install') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, `execution_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, `payload` json NOT NULL, `activated_at` datetime(6) DEFAULT NULL, @@ -2608,7 +2646,7 @@ CREATE TABLE `upcoming_activities` ( KEY `idx_upcoming_activities_host_id_activity_type` (`activity_type`,`host_id`), KEY `fk_upcoming_activities_user_id` (`user_id`), CONSTRAINT `fk_upcoming_activities_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; From a334f9b6b51ae04c1a74ad874c75ee48dbc7c98e Mon Sep 17 00:00:00 2001 From: jkatz01 Date: Wed, 8 Oct 2025 17:21:41 -0400 Subject: [PATCH 08/10] fix more CI errors --- server/datastore/mysql/hosts_test.go | 9 +++++---- server/datastore/mysql/schema.sql | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/server/datastore/mysql/hosts_test.go b/server/datastore/mysql/hosts_test.go index 0e8dcaa95dd..98ad5b6f5fd 100644 --- a/server/datastore/mysql/hosts_test.go +++ b/server/datastore/mysql/hosts_test.go @@ -8194,10 +8194,11 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) { `, certSerial, host.ID, "test-host", time.Now().Add(-1*time.Hour), time.Now().Add(24*time.Hour), "-----BEGIN CERTIFICATE-----", []byte{0x04}) require.NoError(t, err) - err = ds.InsertInHouseApp(ctx, &fleet.InHouseAppPayload{ - Name: "test", - StorageID: uuid.NewString(), - Platform: string(fleet.MacOSPlatform), + _, _, err = ds.insertInHouseApp(ctx, &fleet.InHouseAppPayload{ + Name: "test", + StorageID: uuid.NewString(), + Platform: string(fleet.MacOSPlatform), + ValidatedLabels: &fleet.LabelIdentsWithScope{}, }) require.NoError(t, err) var inHouseID uint diff --git a/server/datastore/mysql/schema.sql b/server/datastore/mysql/schema.sql index 768b4f90a99..f9b5965c63b 100644 --- a/server/datastore/mysql/schema.sql +++ b/server/datastore/mysql/schema.sql @@ -1126,6 +1126,7 @@ CREATE TABLE `in_house_apps` ( `team_id` int unsigned DEFAULT NULL, `global_or_team_id` int unsigned NOT NULL DEFAULT '0', `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', + `version` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', `storage_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, From cc7fe3908dcb5f62e1b3557f517ff76b1dbde36c Mon Sep 17 00:00:00 2001 From: jkatz01 Date: Thu, 9 Oct 2025 09:48:27 -0400 Subject: [PATCH 09/10] fix linter errors --- ee/server/service/software_installers.go | 5 +---- server/datastore/mysql/in_house_apps.go | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ee/server/service/software_installers.go b/ee/server/service/software_installers.go index 1cbc507ff52..ed0470d2630 100644 --- a/ee/server/service/software_installers.go +++ b/ee/server/service/software_installers.go @@ -69,10 +69,7 @@ func (svc *Service) UploadSoftwareInstaller(ctx context.Context, payload *fleet. payload.PostInstallScript = file.Dos2UnixNewlines(payload.PostInstallScript) payload.UninstallScript = file.Dos2UnixNewlines(payload.UninstallScript) - failOnBlankScript := true - if strings.HasSuffix(payload.Filename, ".ipa") { - failOnBlankScript = false - } + failOnBlankScript := strings.HasSuffix(payload.Filename, ".ipa") if _, err := svc.addMetadataToSoftwarePayload(ctx, payload, failOnBlankScript); err != nil { return nil, ctxerr.Wrap(ctx, err, "adding metadata to payload") diff --git a/server/datastore/mysql/in_house_apps.go b/server/datastore/mysql/in_house_apps.go index ebc77f5383d..6ead2b42396 100644 --- a/server/datastore/mysql/in_house_apps.go +++ b/server/datastore/mysql/in_house_apps.go @@ -62,9 +62,9 @@ func (ds *Datastore) insertInHouseApp(ctx context.Context, payload *fleet.InHous } id64, err := res.LastInsertId() - installerID = uint(id64) + installerID = uint(id64) //nolint:gosec // dismiss G115 if err != nil { - ctxerr.Wrap(ctx, err, "insertInHouseApp") + return ctxerr.Wrap(ctx, err, "insertInHouseApp") } if err := setOrUpdateSoftwareInstallerLabelsDB(ctx, tx, installerID, *payload.ValidatedLabels, softwareTypeInHouseApp); err != nil { From f2f391a50a0cc43e279802879d77534f4d30fe74 Mon Sep 17 00:00:00 2001 From: jkatz01 Date: Thu, 9 Oct 2025 10:30:26 -0400 Subject: [PATCH 10/10] fix reversed logic from previous linter change --- ee/server/service/software_installers.go | 2 +- server/datastore/mysql/in_house_apps_test.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ee/server/service/software_installers.go b/ee/server/service/software_installers.go index ed0470d2630..555f9df167e 100644 --- a/ee/server/service/software_installers.go +++ b/ee/server/service/software_installers.go @@ -69,7 +69,7 @@ func (svc *Service) UploadSoftwareInstaller(ctx context.Context, payload *fleet. payload.PostInstallScript = file.Dos2UnixNewlines(payload.PostInstallScript) payload.UninstallScript = file.Dos2UnixNewlines(payload.UninstallScript) - failOnBlankScript := strings.HasSuffix(payload.Filename, ".ipa") + failOnBlankScript := !strings.HasSuffix(payload.Filename, ".ipa") if _, err := svc.addMetadataToSoftwarePayload(ctx, payload, failOnBlankScript); err != nil { return nil, ctxerr.Wrap(ctx, err, "adding metadata to payload") diff --git a/server/datastore/mysql/in_house_apps_test.go b/server/datastore/mysql/in_house_apps_test.go index 99595740b60..3f43e18f7f7 100644 --- a/server/datastore/mysql/in_house_apps_test.go +++ b/server/datastore/mysql/in_house_apps_test.go @@ -39,6 +39,7 @@ func testInHouseAppsCrud(t *testing.T, ds *Datastore) { Platform: "ios", Extension: "ipa", } + // TODO(JK): test with svc.UploadSoftwareInstaller _, _, err = ds.MatchOrCreateSoftwareInstaller(ctx, &payload) require.Error(t, err, "ValidatedLabels must not be nil")