Improve .pkg metadata extraction for names and bundle IDs, let custom package metadata extraction tool check an entire directory at a time (#29249)
For #24083, #26597. # Checklist for submitter If some of the following don't apply, delete the relevant line. <!-- Note that API documentation changes are now addressed by the product design team. --> - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/Committing-Changes.md#changes-files) for more information. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [x] Added/updated automated tests - [x] Manual QA for all new/changed functionality
This commit is contained in:
@@ -0,0 +1 @@
|
||||
* Revised PKG custom package parsing to pick the correct app name and bundle ID in more instances.
|
||||
+47
-16
@@ -29,6 +29,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
@@ -362,20 +363,33 @@ func getDistributionInfo(d *distributionXML) (name string, identifier string, ve
|
||||
packageIDs = append(packageIDs, id)
|
||||
}
|
||||
|
||||
out:
|
||||
// look in all the bundle versions for one that has a `path` attribute
|
||||
// that is not nested, this is generally the case for packages that distribute
|
||||
// `.app` files, which are ultimately picked up as an installed app by osquery
|
||||
var potentialBundles []distributionBundle
|
||||
for _, pkg := range d.PkgRefs {
|
||||
for _, versions := range pkg.BundleVersions {
|
||||
for _, bundle := range versions.Bundles {
|
||||
if base, isValid := isValidAppFilePath(bundle.Path); isValid {
|
||||
identifier = bundle.ID
|
||||
name = base
|
||||
appVersion = bundle.CFBundleShortVersionString
|
||||
break out
|
||||
}
|
||||
}
|
||||
potentialBundles = append(potentialBundles, versions.Bundles...)
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer paths that refer to Applications for name, bundle ID, etc.
|
||||
slices.SortFunc(potentialBundles, func(a distributionBundle, b distributionBundle) int {
|
||||
if strings.HasPrefix(a.Path, "Applications/") && !strings.HasPrefix(b.Path, "Applications/") {
|
||||
return -1
|
||||
}
|
||||
if strings.HasPrefix(b.Path, "Applications/") && !strings.HasPrefix(a.Path, "Applications/") {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
|
||||
for _, bundle := range potentialBundles {
|
||||
if base, isValid := isValidAppFilePath(bundle.Path); isValid {
|
||||
identifier = bundle.ID
|
||||
name = strings.TrimSuffix(base, ".app")
|
||||
appVersion = bundle.CFBundleShortVersionString
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,6 +405,14 @@ out:
|
||||
}
|
||||
}
|
||||
|
||||
// if the identifier is still empty, try to use the product id, and make sure it's in the package IDs list
|
||||
if identifier == "" && d.Product.ID != "" {
|
||||
identifier = d.Product.ID
|
||||
if !slices.Contains(packageIDs, identifier) {
|
||||
packageIDs = append(packageIDs, identifier)
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get the identifier based on the choices list, if we have one. Some .pkgs have multiple
|
||||
// sub-pkgs inside, so the choices list helps us be a bit smarter.
|
||||
if identifier == "" && len(d.ChoicesOutline.Lines) > 0 {
|
||||
@@ -435,11 +457,6 @@ out:
|
||||
}
|
||||
}
|
||||
|
||||
// if the identifier is still empty, try to use the product id
|
||||
if identifier == "" && d.Product.ID != "" {
|
||||
identifier = d.Product.ID
|
||||
}
|
||||
|
||||
// if package IDs are still empty, use the identifier as the package ID
|
||||
if len(packageIDs) == 0 && identifier != "" {
|
||||
packageIDs = append(packageIDs, identifier)
|
||||
@@ -451,9 +468,11 @@ out:
|
||||
name = d.Title
|
||||
}
|
||||
|
||||
if _, ok := knownBadNames[name]; name == "" || ok {
|
||||
name = identifier
|
||||
if _, ok := knownBadNames[name]; ok {
|
||||
name = ""
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
// Try to find a <choice> tag that matches the bundle ID for this app. It might have the app
|
||||
// name, so if we find it we can use that.
|
||||
for _, c := range d.Choices {
|
||||
@@ -463,6 +482,18 @@ out:
|
||||
}
|
||||
}
|
||||
|
||||
if name == "" { // Fall back to any bundle ID in packages for name matching vs. choices
|
||||
for _, c := range d.Choices {
|
||||
if slices.Contains(packageIDs, c.PkgRef.ID) && c.Title != "" {
|
||||
name = c.Title
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if name == "" { // fall back to bundle ID
|
||||
name = identifier
|
||||
}
|
||||
|
||||
// for the version, try to use the top-level product version, if not,
|
||||
// fallback to any version definition alongside the name or the first
|
||||
// version in a pkg-ref we find.
|
||||
|
||||
+17
-17
@@ -63,28 +63,28 @@ func TestParseRealDistributionFiles(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
file: "distribution-1password.xml",
|
||||
expectedName: "1Password.app",
|
||||
expectedName: "1Password",
|
||||
expectedVersion: "8.10.34",
|
||||
expectedBundleID: "com.1password.1password",
|
||||
expectedPackageIDs: []string{"com.1password.1password"},
|
||||
},
|
||||
{
|
||||
file: "distribution-chrome.xml",
|
||||
expectedName: "Google Chrome.app",
|
||||
expectedName: "Google Chrome",
|
||||
expectedVersion: "126.0.6478.62",
|
||||
expectedBundleID: "com.google.Chrome",
|
||||
expectedPackageIDs: []string{"com.google.Chrome"},
|
||||
},
|
||||
{
|
||||
file: "distribution-edge.xml",
|
||||
expectedName: "Microsoft Edge.app",
|
||||
expectedName: "Microsoft Edge",
|
||||
expectedVersion: "126.0.2592.56",
|
||||
expectedBundleID: "com.microsoft.edgemac",
|
||||
expectedPackageIDs: []string{"com.microsoft.edgemac"},
|
||||
},
|
||||
{
|
||||
file: "distribution-firefox.xml",
|
||||
expectedName: "Firefox.app",
|
||||
expectedName: "Firefox",
|
||||
expectedVersion: "99.0",
|
||||
expectedBundleID: "org.mozilla.firefox",
|
||||
expectedPackageIDs: []string{"org.mozilla.firefox"},
|
||||
@@ -105,7 +105,7 @@ func TestParseRealDistributionFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
file: "distribution-microsoft-teams.xml",
|
||||
expectedName: "Microsoft Teams.app",
|
||||
expectedName: "Microsoft Teams",
|
||||
expectedVersion: "24124.1412.2911.3341",
|
||||
expectedBundleID: "com.microsoft.teams2",
|
||||
expectedPackageIDs: []string{
|
||||
@@ -115,14 +115,14 @@ func TestParseRealDistributionFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
file: "distribution-zoom.xml",
|
||||
expectedName: "zoom.us.app",
|
||||
expectedName: "zoom.us",
|
||||
expectedVersion: "6.0.11.35001",
|
||||
expectedBundleID: "us.zoom.xos",
|
||||
expectedPackageIDs: []string{"us.zoom.pkg.videomeeting"},
|
||||
},
|
||||
{
|
||||
file: "distribution-acrobatreader.xml",
|
||||
expectedName: "Adobe Acrobat Reader.app",
|
||||
expectedName: "Adobe Acrobat Reader",
|
||||
expectedVersion: "24.002.20857",
|
||||
expectedBundleID: "com.adobe.Reader",
|
||||
expectedPackageIDs: []string{
|
||||
@@ -132,14 +132,14 @@ func TestParseRealDistributionFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
file: "distribution-airtame.xml",
|
||||
expectedName: "Airtame.app",
|
||||
expectedName: "Airtame",
|
||||
expectedVersion: "4.10.1",
|
||||
expectedBundleID: "com.airtame.airtame-application",
|
||||
expectedPackageIDs: []string{"com.airtame.airtame-application"},
|
||||
},
|
||||
{
|
||||
file: "distribution-boxdrive.xml",
|
||||
expectedName: "Box.app",
|
||||
expectedName: "Box",
|
||||
expectedVersion: "2.38.173",
|
||||
expectedBundleID: "com.box.desktop",
|
||||
expectedPackageIDs: []string{
|
||||
@@ -149,7 +149,7 @@ func TestParseRealDistributionFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
file: "distribution-iriunwebcam.xml",
|
||||
expectedName: "IriunWebcam.app",
|
||||
expectedName: "IriunWebcam",
|
||||
expectedVersion: "2.8.8",
|
||||
expectedBundleID: "com.iriun.macwebcam",
|
||||
// Note: "com.iriun.pkg.multicam" is part of the installer package, but it is not actually installed by default.
|
||||
@@ -158,7 +158,7 @@ func TestParseRealDistributionFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
file: "distribution-microsoftexcel.xml",
|
||||
expectedName: "Microsoft Excel.app",
|
||||
expectedName: "Microsoft Excel",
|
||||
expectedVersion: "16.86",
|
||||
expectedBundleID: "com.microsoft.Excel",
|
||||
expectedPackageIDs: []string{
|
||||
@@ -168,7 +168,7 @@ func TestParseRealDistributionFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
file: "distribution-microsoftword.xml",
|
||||
expectedName: "Microsoft Word.app",
|
||||
expectedName: "Microsoft Word",
|
||||
expectedVersion: "16.86",
|
||||
expectedBundleID: "com.microsoft.Word",
|
||||
expectedPackageIDs: []string{
|
||||
@@ -178,7 +178,7 @@ func TestParseRealDistributionFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
file: "distribution-miscrosoftpowerpoint.xml",
|
||||
expectedName: "Microsoft PowerPoint.app",
|
||||
expectedName: "Microsoft PowerPoint",
|
||||
expectedVersion: "16.86",
|
||||
expectedBundleID: "com.microsoft.Powerpoint",
|
||||
expectedPackageIDs: []string{
|
||||
@@ -188,7 +188,7 @@ func TestParseRealDistributionFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
file: "distribution-ringcentral.xml",
|
||||
expectedName: "RingCentral.app",
|
||||
expectedName: "RingCentral",
|
||||
expectedVersion: "24.1.32.9774",
|
||||
expectedBundleID: "com.ringcentral.glip",
|
||||
expectedPackageIDs: []string{"com.ringcentral.glip"},
|
||||
@@ -202,7 +202,7 @@ func TestParseRealDistributionFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
file: "test-zero-installkbytes.xml",
|
||||
expectedName: "ZeroInstallSize.app",
|
||||
expectedName: "ZeroInstallSize",
|
||||
expectedVersion: "1.2.3",
|
||||
expectedBundleID: "com.bozo.zeroinstallsize",
|
||||
expectedPackageIDs: []string{"com.bozo.zeroinstallsize.app"},
|
||||
@@ -211,8 +211,8 @@ func TestParseRealDistributionFiles(t *testing.T) {
|
||||
file: "distribution-sentinelone.xml",
|
||||
expectedName: "SentinelOne",
|
||||
expectedVersion: "24.3.2.7753",
|
||||
expectedBundleID: "com.sentinelone.pkg.sentinel-agent",
|
||||
expectedPackageIDs: []string{"com.sentinelone.pkg.sentinel-agent"},
|
||||
expectedBundleID: "com.sentinelone.sentinel-agent",
|
||||
expectedPackageIDs: []string{"com.sentinelone.pkg.sentinel-agent", "com.sentinelone.sentinel-agent"},
|
||||
},
|
||||
{
|
||||
file: "distribution-cold-turkey.xml",
|
||||
|
||||
@@ -12484,7 +12484,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerHostRequests() {
|
||||
// Upload another package for another platform
|
||||
payloadDummy := &fleet.UploadSoftwareInstallerPayload{
|
||||
Filename: "dummy_installer.pkg",
|
||||
Title: "DummyApp.app",
|
||||
Title: "DummyApp",
|
||||
TeamID: teamID,
|
||||
}
|
||||
s.uploadSoftwareInstaller(t, payloadDummy, http.StatusOK, "")
|
||||
@@ -13546,7 +13546,7 @@ func (s *integrationEnterpriseTestSuite) TestPKGNewSoftwareTitleFlow() {
|
||||
{Name: "foo", Version: "0.0.1", Source: "homebrew"},
|
||||
{Name: "foo", Version: "0.0.3", Source: "homebrew"},
|
||||
{Name: "bar", Version: "0.0.4", Source: "apps"},
|
||||
{Name: "DummyApp.app", Version: "1.0.0", Source: "apps", BundleIdentifier: "com.example.dummy"},
|
||||
{Name: "DummyApp", Version: "1.0.0", Source: "apps", BundleIdentifier: "com.example.dummy"},
|
||||
}
|
||||
_, err = s.ds.UpdateHostSoftware(ctx, host.ID, software)
|
||||
require.NoError(t, err)
|
||||
@@ -13577,7 +13577,7 @@ func (s *integrationEnterpriseTestSuite) TestPKGNewSoftwareTitleFlow() {
|
||||
require.Len(t, resp.SoftwareTitles, 3)
|
||||
require.ElementsMatch(
|
||||
t,
|
||||
[]string{"foo", "bar", "DummyApp.app"},
|
||||
[]string{"foo", "bar", "DummyApp"},
|
||||
[]string{
|
||||
resp.SoftwareTitles[0].Name,
|
||||
resp.SoftwareTitles[1].Name,
|
||||
@@ -13591,8 +13591,8 @@ func (s *integrationEnterpriseTestSuite) TestPKGNewSoftwareTitleFlow() {
|
||||
{Name: "foo", Version: "0.0.1", Source: "homebrew"},
|
||||
{Name: "foo", Version: "0.0.3", Source: "homebrew"},
|
||||
{Name: "bar", Version: "0.0.4", Source: "apps"},
|
||||
{Name: "DummyApp.app", Version: "1.0.0", Source: "apps", BundleIdentifier: "com.example.dummy"},
|
||||
{Name: "AppDummy.app", Version: "2.0.0", Source: "apps", BundleIdentifier: "com.example.dummy"},
|
||||
{Name: "DummyApp", Version: "1.0.0", Source: "apps", BundleIdentifier: "com.example.dummy"},
|
||||
{Name: "AppDummy", Version: "2.0.0", Source: "apps", BundleIdentifier: "com.example.dummy"},
|
||||
}
|
||||
_, err = s.ds.UpdateHostSoftware(ctx, host.ID, software)
|
||||
require.NoError(t, err)
|
||||
@@ -13613,7 +13613,7 @@ func (s *integrationEnterpriseTestSuite) TestPKGNewSoftwareTitleFlow() {
|
||||
require.Len(t, resp.SoftwareTitles, 3)
|
||||
require.ElementsMatch(
|
||||
t,
|
||||
[]string{"foo", "bar", "DummyApp.app"},
|
||||
[]string{"foo", "bar", "DummyApp"},
|
||||
[]string{
|
||||
resp.SoftwareTitles[0].Name,
|
||||
resp.SoftwareTitles[1].Name,
|
||||
@@ -13645,7 +13645,7 @@ func (s *integrationEnterpriseTestSuite) TestPKGNoVersion() {
|
||||
"team_id", fmt.Sprintf("%d", team.ID),
|
||||
)
|
||||
require.Len(t, resp.SoftwareTitles, 1)
|
||||
require.Equal(t, "NoVersion.app", resp.SoftwareTitles[0].Name)
|
||||
require.Equal(t, "NoVersion", resp.SoftwareTitles[0].Name)
|
||||
require.Equal(t, "", resp.SoftwareTitles[0].SoftwarePackage.Version)
|
||||
}
|
||||
|
||||
@@ -13759,7 +13759,7 @@ func (s *integrationEnterpriseTestSuite) TestPKGSoftwareAlreadyReported() {
|
||||
{Name: "foo", Version: "0.0.3", Source: "homebrew"},
|
||||
{Name: "bar", Version: "0.0.4", Source: "apps"},
|
||||
// note: the source is not "apps"
|
||||
{Name: "DummyApp.app", Version: "1.0.0", Source: "homebrew", BundleIdentifier: "com.example.dummy"},
|
||||
{Name: "DummyApp", Version: "1.0.0", Source: "homebrew", BundleIdentifier: "com.example.dummy"},
|
||||
}
|
||||
_, err = s.ds.UpdateHostSoftware(ctx, host.ID, software)
|
||||
require.NoError(t, err)
|
||||
@@ -13780,7 +13780,7 @@ func (s *integrationEnterpriseTestSuite) TestPKGSoftwareAlreadyReported() {
|
||||
require.Len(t, resp.SoftwareTitles, 3)
|
||||
require.ElementsMatch(
|
||||
t,
|
||||
[]string{"foo", "bar", "DummyApp.app"},
|
||||
[]string{"foo", "bar", "DummyApp"},
|
||||
[]string{
|
||||
resp.SoftwareTitles[0].Name,
|
||||
resp.SoftwareTitles[1].Name,
|
||||
@@ -13805,7 +13805,7 @@ func (s *integrationEnterpriseTestSuite) TestPKGSoftwareAlreadyReported() {
|
||||
require.Len(t, resp.SoftwareTitles, 3)
|
||||
require.ElementsMatch(
|
||||
t,
|
||||
[]string{"foo", "bar", "DummyApp.app"},
|
||||
[]string{"foo", "bar", "DummyApp"},
|
||||
[]string{
|
||||
resp.SoftwareTitles[0].Name,
|
||||
resp.SoftwareTitles[1].Name,
|
||||
@@ -13845,7 +13845,7 @@ func (s *integrationEnterpriseTestSuite) TestPKGSoftwareReconciliation() {
|
||||
{Name: "foo", Version: "0.0.3", Source: "homebrew"},
|
||||
{Name: "bar", Version: "0.0.4", Source: "apps"},
|
||||
// note: the source is not "apps"
|
||||
{Name: "DummyApp.app", Version: "1.0.0", Source: "homebrew", BundleIdentifier: "com.example.dummy"},
|
||||
{Name: "DummyApp", Version: "1.0.0", Source: "homebrew", BundleIdentifier: "com.example.dummy"},
|
||||
}
|
||||
_, err = s.ds.UpdateHostSoftware(ctx, host.ID, software)
|
||||
require.NoError(t, err)
|
||||
@@ -13870,7 +13870,7 @@ func (s *integrationEnterpriseTestSuite) TestPKGSoftwareReconciliation() {
|
||||
require.Len(t, resp.SoftwareTitles, 1)
|
||||
require.ElementsMatch(
|
||||
t,
|
||||
[]string{"DummyApp.app"},
|
||||
[]string{"DummyApp"},
|
||||
[]string{resp.SoftwareTitles[0].Name},
|
||||
)
|
||||
|
||||
@@ -13888,7 +13888,7 @@ func (s *integrationEnterpriseTestSuite) TestPKGSoftwareReconciliation() {
|
||||
require.Len(t, resp.SoftwareTitles, 3)
|
||||
require.ElementsMatch(
|
||||
t,
|
||||
[]string{"foo", "bar", "DummyApp.app"},
|
||||
[]string{"foo", "bar", "DummyApp"},
|
||||
[]string{
|
||||
resp.SoftwareTitles[0].Name,
|
||||
resp.SoftwareTitles[1].Name,
|
||||
@@ -14787,7 +14787,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers
|
||||
"GET", "/api/latest/fleet/software/titles",
|
||||
listSoftwareTitlesRequest{},
|
||||
http.StatusOK, &resp,
|
||||
"query", "DummyApp.app",
|
||||
"query", "DummyApp",
|
||||
"team_id", fmt.Sprintf("%d", team1.ID),
|
||||
)
|
||||
require.Len(t, resp.SoftwareTitles, 1)
|
||||
@@ -14938,7 +14938,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers
|
||||
// Populate software for host1Team1 (to have a software title
|
||||
// that doesn't have an associated installer)
|
||||
software := []fleet.Software{
|
||||
{Name: "Foobar.app", Version: "0.0.1", Source: "apps"},
|
||||
{Name: "Foobar", Version: "0.0.1", Source: "apps"},
|
||||
}
|
||||
_, err = s.ds.UpdateHostSoftware(ctx, host1Team1.ID, software)
|
||||
require.NoError(t, err)
|
||||
@@ -14951,7 +14951,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers
|
||||
"GET", "/api/latest/fleet/software/titles",
|
||||
listSoftwareTitlesRequest{},
|
||||
http.StatusOK, &resp,
|
||||
"query", "Foobar.app",
|
||||
"query", "Foobar",
|
||||
"team_id", fmt.Sprintf("%d", team1.ID),
|
||||
)
|
||||
require.Len(t, resp.SoftwareTitles, 1)
|
||||
@@ -15383,7 +15383,7 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsSoftwareInstallers
|
||||
"status": "installed",
|
||||
"policy_id": %d,
|
||||
"policy_name": "%s"
|
||||
}`, host1Team1.ID, host1Team1.DisplayName(), "DummyApp.app", "dummy_installer.pkg", host1LastInstall.ExecutionID, policy1Team1.ID, policy1Team1.Name), 0)
|
||||
}`, host1Team1.ID, host1Team1.DisplayName(), "DummyApp", "dummy_installer.pkg", host1LastInstall.ExecutionID, policy1Team1.ID, policy1Team1.Name), 0)
|
||||
|
||||
// host2Team1 posts the installation result for ruby.deb.
|
||||
s.Do("POST", "/api/fleet/orbit/software_install/result", json.RawMessage(fmt.Sprintf(`{
|
||||
@@ -16315,7 +16315,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallersWithoutBundleIden
|
||||
require.NoError(t, err)
|
||||
|
||||
software := []fleet.Software{
|
||||
{Name: "DummyApp.app", Version: "0.0.2", Source: "apps"},
|
||||
{Name: "DummyApp", Version: "0.0.2", Source: "apps"},
|
||||
}
|
||||
// we must ingest the title with an empty bundle identifier for this
|
||||
// test to be valid
|
||||
@@ -17132,7 +17132,7 @@ func (s *integrationEnterpriseTestSuite) TestAutomaticPolicies() {
|
||||
require.Len(t, ts.InheritedPolicies, 0)
|
||||
|
||||
// Delete and try again with automatic policy turned on.
|
||||
pkgTitleID := getSoftwareTitleID(t, s.ds, "DummyApp.app", "apps")
|
||||
pkgTitleID := getSoftwareTitleID(t, s.ds, "DummyApp", "apps")
|
||||
s.Do("DELETE", fmt.Sprintf("/api/latest/fleet/software/titles/%d/available_for_install", pkgTitleID), nil, http.StatusNoContent,
|
||||
"team_id", fmt.Sprintf("%d", team1.ID))
|
||||
|
||||
@@ -17145,20 +17145,20 @@ func (s *integrationEnterpriseTestSuite) TestAutomaticPolicies() {
|
||||
}
|
||||
s.uploadSoftwareInstaller(t, pkgPayload, http.StatusOK, "")
|
||||
|
||||
pkgTitleID = getSoftwareTitleID(t, s.ds, "DummyApp.app", "apps")
|
||||
pkgTitleID = getSoftwareTitleID(t, s.ds, "DummyApp", "apps")
|
||||
respTitle := getSoftwareTitleResponse{}
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/software/titles/%d?team_id=%d", pkgTitleID, team1.ID), listSoftwareTitlesRequest{}, http.StatusOK, &respTitle)
|
||||
require.NotNil(t, respTitle.SoftwareTitle)
|
||||
require.NotNil(t, respTitle.SoftwareTitle.SoftwarePackage)
|
||||
require.Len(t, respTitle.SoftwareTitle.SoftwarePackage.AutomaticInstallPolicies, 1)
|
||||
require.Equal(t, "[Install software] DummyApp.app (pkg)", respTitle.SoftwareTitle.SoftwarePackage.AutomaticInstallPolicies[0].Name)
|
||||
require.Equal(t, "[Install software] DummyApp (pkg)", respTitle.SoftwareTitle.SoftwarePackage.AutomaticInstallPolicies[0].Name)
|
||||
|
||||
// Check a policy was created on team1.
|
||||
ts = listTeamPoliciesResponse{}
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/policies", team1.ID), nil, http.StatusOK, &ts)
|
||||
require.Len(t, ts.Policies, 1)
|
||||
require.Len(t, ts.InheritedPolicies, 0)
|
||||
require.Equal(t, "[Install software] DummyApp.app (pkg)", ts.Policies[0].Name)
|
||||
require.Equal(t, "[Install software] DummyApp (pkg)", ts.Policies[0].Name)
|
||||
|
||||
// Upload dummy_installer.pkg to team2 with automatic policy.
|
||||
pkgPayload = &fleet.UploadSoftwareInstallerPayload{
|
||||
@@ -17174,7 +17174,7 @@ func (s *integrationEnterpriseTestSuite) TestAutomaticPolicies() {
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/teams/%d/policies", team2.ID), nil, http.StatusOK, &ts)
|
||||
require.Len(t, ts.Policies, 1)
|
||||
require.Len(t, ts.InheritedPolicies, 0)
|
||||
require.Equal(t, "[Install software] DummyApp.app (pkg)", ts.Policies[0].Name)
|
||||
require.Equal(t, "[Install software] DummyApp (pkg)", ts.Policies[0].Name)
|
||||
|
||||
// Upload ruby.deb to team1 with automatic policy.
|
||||
payloadRubyDEB := &fleet.UploadSoftwareInstallerPayload{
|
||||
@@ -17561,7 +17561,7 @@ func (s *integrationEnterpriseTestSuite) TestBatchSoftwareUploadWithSHAs() {
|
||||
require.NotNil(t, pkgTitleID)
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/software/titles/%d", *pkgTitleID), getSoftwareTitleRequest{}, http.StatusOK, &stResp, "team_id", fmt.Sprint(team2.ID))
|
||||
require.NotNil(t, stResp.SoftwareTitle.SoftwarePackage)
|
||||
require.Equal(t, "DummyApp.app", stResp.SoftwareTitle.Name)
|
||||
require.Equal(t, "DummyApp", stResp.SoftwareTitle.Name)
|
||||
require.Equal(t, pkgURL, stResp.SoftwareTitle.SoftwarePackage.URL)
|
||||
require.Equal(t, softwareToInstall[2].InstallScript, stResp.SoftwareTitle.SoftwarePackage.InstallScript)
|
||||
require.Equal(t, softwareToInstall[2].UninstallScript, stResp.SoftwareTitle.SoftwarePackage.UninstallScript)
|
||||
@@ -17630,7 +17630,7 @@ done
|
||||
require.NotNil(t, pkgTitleID)
|
||||
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/software/titles/%d", *pkgTitleID), getSoftwareTitleRequest{}, http.StatusOK, &stResp, "team_id", fmt.Sprint(team2.ID))
|
||||
require.NotNil(t, stResp.SoftwareTitle.SoftwarePackage)
|
||||
require.Equal(t, "DummyApp.app", stResp.SoftwareTitle.Name)
|
||||
require.Equal(t, "DummyApp", stResp.SoftwareTitle.Name)
|
||||
require.Equal(t, pkgURL, stResp.SoftwareTitle.SoftwarePackage.URL)
|
||||
require.Equal(t, file.GetInstallScript("pkg"), stResp.SoftwareTitle.SoftwarePackage.InstallScript)
|
||||
require.Equal(t, expectedUninstallScript, stResp.SoftwareTitle.SoftwarePackage.UninstallScript)
|
||||
|
||||
@@ -2050,7 +2050,7 @@ func (s *integrationMDMTestSuite) createTeamDeviceForSetupExperienceWithProfileS
|
||||
payloadDummy := &fleet.UploadSoftwareInstallerPayload{
|
||||
InstallScript: "install",
|
||||
Filename: "dummy_installer.pkg",
|
||||
Title: "DummyApp.app",
|
||||
Title: "DummyApp",
|
||||
TeamID: &tm.ID,
|
||||
}
|
||||
s.uploadSoftwareInstaller(t, payloadDummy, http.StatusOK, "")
|
||||
@@ -2236,7 +2236,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
|
||||
require.Equal(t, "script.sh", statusResp.Results.Script.Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Script.Status)
|
||||
require.Len(t, statusResp.Results.Software, 1)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Software[0].Status)
|
||||
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
|
||||
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
|
||||
@@ -2300,7 +2300,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
|
||||
statusResp = getOrbitSetupExperienceStatusResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/setup_experience/status", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *enrolledHost.OrbitNodeKey)), http.StatusOK, &statusResp)
|
||||
// Software is now running, script is still pending
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusRunning, statusResp.Results.Software[0].Status)
|
||||
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
|
||||
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
|
||||
@@ -2328,7 +2328,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
|
||||
require.Equal(t, "script.sh", statusResp.Results.Script.Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Script.Status)
|
||||
require.Len(t, statusResp.Results.Software, 1)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusSuccess, statusResp.Results.Software[0].Status)
|
||||
|
||||
// no MDM command got enqueued due to the /status call (device not released yet)
|
||||
@@ -2339,7 +2339,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
|
||||
// Software is installed, now we should run the script
|
||||
statusResp = getOrbitSetupExperienceStatusResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/setup_experience/status", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *enrolledHost.OrbitNodeKey)), http.StatusOK, &statusResp)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusSuccess, statusResp.Results.Software[0].Status)
|
||||
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
|
||||
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
|
||||
@@ -2410,7 +2410,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptAu
|
||||
// release of the device, as all setup experience steps are now complete.
|
||||
statusResp = getOrbitSetupExperienceStatusResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/setup_experience/status", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *enrolledHost.OrbitNodeKey)), http.StatusOK, &statusResp)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusSuccess, statusResp.Results.Software[0].Status)
|
||||
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
|
||||
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
|
||||
@@ -2570,7 +2570,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowWithSoftwareAndScriptFo
|
||||
require.Equal(t, "script.sh", statusResp.Results.Script.Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Script.Status)
|
||||
require.Len(t, statusResp.Results.Software, 1)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Software[0].Status)
|
||||
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
|
||||
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
|
||||
@@ -3309,7 +3309,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceVPPInstallError() {
|
||||
|
||||
// Add the VPP app to setup experience
|
||||
vppTitleID := getSoftwareTitleID(t, s.ds, "App 5", "apps")
|
||||
installerTitleID := getSoftwareTitleID(t, s.ds, "DummyApp.app", "apps")
|
||||
installerTitleID := getSoftwareTitleID(t, s.ds, "DummyApp", "apps")
|
||||
var swInstallResp putSetupExperienceSoftwareResponse
|
||||
s.DoJSON("PUT", "/api/v1/fleet/setup_experience/software", putSetupExperienceSoftwareRequest{TeamID: team.ID, TitleIDs: []uint{vppTitleID, installerTitleID}}, http.StatusOK, &swInstallResp)
|
||||
|
||||
@@ -3404,7 +3404,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceVPPInstallError() {
|
||||
require.Equal(t, "script.sh", statusResp.Results.Script.Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Script.Status)
|
||||
require.Len(t, statusResp.Results.Software, 2)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Software[0].Status)
|
||||
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
|
||||
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
|
||||
@@ -3452,7 +3452,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceVPPInstallError() {
|
||||
require.Equal(t, "script.sh", statusResp.Results.Script.Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Script.Status)
|
||||
require.Len(t, statusResp.Results.Software, 2)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusSuccess, statusResp.Results.Software[0].Status)
|
||||
require.Equal(t, "App 5", statusResp.Results.Software[1].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Software[1].Status)
|
||||
@@ -3466,7 +3466,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceVPPInstallError() {
|
||||
require.Equal(t, "script.sh", statusResp.Results.Script.Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Script.Status)
|
||||
require.Len(t, statusResp.Results.Software, 2)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusSuccess, statusResp.Results.Software[0].Status)
|
||||
|
||||
// App 5 has no licenses available, so we should get a status failed here and setup experience
|
||||
@@ -3477,7 +3477,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceVPPInstallError() {
|
||||
// Software installations are done, now we should run the script
|
||||
statusResp = getOrbitSetupExperienceStatusResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/setup_experience/status", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *enrolledHost.OrbitNodeKey)), http.StatusOK, &statusResp)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusSuccess, statusResp.Results.Software[0].Status)
|
||||
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
|
||||
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
|
||||
@@ -3508,7 +3508,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceVPPInstallError() {
|
||||
// release of the device, as all setup experience steps are now complete.
|
||||
statusResp = getOrbitSetupExperienceStatusResponse{}
|
||||
s.DoJSON("POST", "/api/fleet/orbit/setup_experience/status", json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q}`, *enrolledHost.OrbitNodeKey)), http.StatusOK, &statusResp)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusSuccess, statusResp.Results.Software[0].Status)
|
||||
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
|
||||
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
|
||||
@@ -3597,7 +3597,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowCancelScript() {
|
||||
require.Equal(t, "script.sh", statusResp.Results.Script.Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Script.Status)
|
||||
require.Len(t, statusResp.Results.Software, 1)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Software[0].Status)
|
||||
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
|
||||
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
|
||||
@@ -3644,7 +3644,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowCancelScript() {
|
||||
require.Equal(t, "script.sh", statusResp.Results.Script.Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusPending, statusResp.Results.Script.Status)
|
||||
require.Len(t, statusResp.Results.Software, 1)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusFailure, statusResp.Results.Software[0].Status)
|
||||
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
|
||||
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
|
||||
@@ -3688,7 +3688,7 @@ func (s *integrationMDMTestSuite) TestSetupExperienceFlowCancelScript() {
|
||||
require.Equal(t, "script.sh", statusResp.Results.Script.Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusFailure, statusResp.Results.Script.Status)
|
||||
require.Len(t, statusResp.Results.Software, 1)
|
||||
require.Equal(t, "DummyApp.app", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, "DummyApp", statusResp.Results.Software[0].Name)
|
||||
require.Equal(t, fleet.SetupExperienceStatusFailure, statusResp.Results.Software[0].Status)
|
||||
require.NotNil(t, statusResp.Results.Software[0].SoftwareTitleID)
|
||||
require.NotZero(t, *statusResp.Results.Software[0].SoftwareTitleID)
|
||||
|
||||
@@ -15934,7 +15934,7 @@ func (s *integrationMDMTestSuite) TestCancelUpcomingActivity() {
|
||||
payload := &fleet.UploadSoftwareInstallerPayload{
|
||||
InstallScript: "install",
|
||||
Filename: "dummy_installer.pkg",
|
||||
Title: "DummyApp.app",
|
||||
Title: "DummyApp",
|
||||
}
|
||||
s.uploadSoftwareInstaller(t, payload, http.StatusOK, "")
|
||||
swTitleID := getSoftwareTitleID(t, s.ds, payload.Title, "apps")
|
||||
@@ -16011,7 +16011,7 @@ func (s *integrationMDMTestSuite) TestCancelUpcomingActivity() {
|
||||
// cancel the uninstall, confirm canceled activity
|
||||
s.Do("DELETE", fmt.Sprintf("/api/latest/fleet/hosts/%d/activities/upcoming/%s", mdmHost.ID, hostActivitiesResp.Activities[1].UUID), nil, http.StatusNoContent)
|
||||
lastCanceledActID = s.lastActivityOfTypeMatches(fleet.ActivityTypeCanceledUninstallSoftware{}.ActivityName(),
|
||||
fmt.Sprintf(`{"host_id": %d, "host_display_name": %q, "software_title": "DummyApp.app", "software_title_id": %d}`, mdmHost.ID, mdmHost.DisplayName(), swTitleID), 0)
|
||||
fmt.Sprintf(`{"host_id": %d, "host_display_name": %q, "software_title": "DummyApp", "software_title_id": %d}`, mdmHost.ID, mdmHost.DisplayName(), swTitleID), 0)
|
||||
|
||||
// record an uninstall result post-cancelation
|
||||
s.DoJSON("POST", "/api/fleet/orbit/scripts/result",
|
||||
@@ -16064,7 +16064,7 @@ func (s *integrationMDMTestSuite) TestCancelUpcomingActivity() {
|
||||
// cancel the software install, confirm canceled activity
|
||||
s.Do("DELETE", fmt.Sprintf("/api/latest/fleet/hosts/%d/activities/upcoming/%s", mdmHost.ID, hostActivitiesResp.Activities[1].UUID), nil, http.StatusNoContent)
|
||||
lastCanceledActID = s.lastActivityOfTypeMatches(fleet.ActivityTypeCanceledInstallSoftware{}.ActivityName(),
|
||||
fmt.Sprintf(`{"host_id": %d, "host_display_name": %q, "software_title": "DummyApp.app", "software_title_id": %d}`, mdmHost.ID, mdmHost.DisplayName(), swTitleID), 0)
|
||||
fmt.Sprintf(`{"host_id": %d, "host_display_name": %q, "software_title": "DummyApp", "software_title_id": %d}`, mdmHost.ID, mdmHost.DisplayName(), swTitleID), 0)
|
||||
|
||||
// record a software install result post-cancelation
|
||||
s.Do("POST", "/api/fleet/orbit/software_install/result", json.RawMessage(fmt.Sprintf(`{
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/pkg/file"
|
||||
@@ -15,62 +17,101 @@ import (
|
||||
|
||||
func main() {
|
||||
url := flag.String("url", "", "URL of the custom package")
|
||||
path := flag.String("path", "", "File path of the custom package")
|
||||
path := flag.String("path", "", "File path of the custom package (or a directory of packages)")
|
||||
flag.Parse()
|
||||
|
||||
if *url == "" && *path == "" {
|
||||
log.Fatal("missing -url or -path argument")
|
||||
}
|
||||
if *url != "" && *path != "" {
|
||||
log.Fatal("cannot set both -url and -path")
|
||||
}
|
||||
switch {
|
||||
case *url != "" && *path != "":
|
||||
log.Fatalf("-url and -path are mutually exclusive")
|
||||
case *url != "":
|
||||
metadata, err := processPackageFromUrl(*url)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
output(metadata)
|
||||
case *path != "":
|
||||
pathInfo, err := os.Stat(*path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if pathInfo.IsDir() {
|
||||
files, err := os.ReadDir(*path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
metadata, err := processPackage(*url, *path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, selectedFile := range files {
|
||||
if selectedFile.IsDir() || strings.HasPrefix(selectedFile.Name(), ".") {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("File: %s\n", selectedFile.Name())
|
||||
metadata, err := processPackageFromLocal(filepath.Join(*path, selectedFile.Name()))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
output(metadata)
|
||||
}
|
||||
} else {
|
||||
metadata, err := processPackageFromLocal(*path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
output(metadata)
|
||||
}
|
||||
default:
|
||||
flag.Usage()
|
||||
}
|
||||
}
|
||||
|
||||
func output(metadata *file.InstallerMetadata) {
|
||||
slices.Sort(metadata.PackageIDs)
|
||||
fmt.Printf(
|
||||
"- Name: '%s'\n- Bundle Identifier: '%s'\n- Package IDs: '%s'\n- Version: %s\n\n",
|
||||
metadata.Name, metadata.BundleIdentifier, strings.Join(metadata.PackageIDs, ","), metadata.Version,
|
||||
)
|
||||
}
|
||||
|
||||
func processPackage(url, path string) (*file.InstallerMetadata, error) {
|
||||
var tfr *fleet.TempFileReader
|
||||
if url != "" {
|
||||
client := fleethttp.NewClient()
|
||||
client.Transport = fleethttp.NewSizeLimitTransport(fleet.MaxSoftwareInstallerSize)
|
||||
func processPackageFromUrl(url string) (*file.InstallerMetadata, error) {
|
||||
client := fleethttp.NewClient()
|
||||
client.Transport = fleethttp.NewSizeLimitTransport(fleet.MaxSoftwareInstallerSize)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create http request: %s", err)
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create http request: %s", err)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get request: %s", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get request: %s", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Allow all 2xx and 3xx status codes in this pass.
|
||||
if resp.StatusCode >= 400 {
|
||||
return nil, fmt.Errorf("get request failed with status: %d", resp.StatusCode)
|
||||
}
|
||||
// Allow all 2xx and 3xx status codes in this pass.
|
||||
if resp.StatusCode >= 400 {
|
||||
return nil, fmt.Errorf("get request failed with status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
tfr, err = fleet.NewTempFileReader(resp.Body, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading custom package: %d", resp.StatusCode)
|
||||
}
|
||||
defer tfr.Close()
|
||||
} else { // -path
|
||||
fp, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open file: %s", err)
|
||||
}
|
||||
tfr = &fleet.TempFileReader{
|
||||
File: fp,
|
||||
}
|
||||
tfr, err := fleet.NewTempFileReader(resp.Body, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading custom package: %d", resp.StatusCode)
|
||||
}
|
||||
defer tfr.Close()
|
||||
|
||||
metadata, err := file.ExtractInstallerMetadata(tfr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("extract installer metadata: %s", err)
|
||||
}
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func processPackageFromLocal(path string) (*file.InstallerMetadata, error) {
|
||||
fp, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open file: %s", err)
|
||||
}
|
||||
tfr := &fleet.TempFileReader{
|
||||
File: fp,
|
||||
}
|
||||
|
||||
metadata, err := file.ExtractInstallerMetadata(tfr)
|
||||
|
||||
Reference in New Issue
Block a user