Add support for install/uninstall script overrides, pre-install query, post-install script in FMA GitOps (#31803)
Also removed the automatic install flag on YAML FMAs as it's undocumented/unspec'd Fixes #25636. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [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/guides/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) ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually
This commit is contained in:
+28
-23
@@ -162,9 +162,9 @@ type SoftwarePackage struct {
|
||||
}
|
||||
|
||||
type Software struct {
|
||||
Packages []SoftwarePackage `json:"packages"`
|
||||
AppStoreApps []fleet.TeamSpecAppStoreApp `json:"app_store_apps"`
|
||||
FleetMaintainedApps []fleet.FleetMaintainedAppsSpec `json:"fleet_maintained_apps"`
|
||||
Packages []SoftwarePackage `json:"packages"`
|
||||
AppStoreApps []fleet.TeamSpecAppStoreApp `json:"app_store_apps"`
|
||||
FleetMaintainedApps []fleet.MaintainedAppSpec `json:"fleet_maintained_apps"`
|
||||
}
|
||||
|
||||
type GitOps struct {
|
||||
@@ -186,7 +186,7 @@ type GitOps struct {
|
||||
type GitOpsSoftware struct {
|
||||
Packages []*fleet.SoftwarePackageSpec
|
||||
AppStoreApps []*fleet.TeamSpecAppStoreApp
|
||||
FleetMaintainedApps []*fleet.FleetMaintainedAppsSpec
|
||||
FleetMaintainedApps []*fleet.MaintainedAppSpec
|
||||
}
|
||||
|
||||
type Logf func(format string, a ...interface{})
|
||||
@@ -1095,6 +1095,28 @@ func parseSoftware(top map[string]json.RawMessage, result *GitOps, baseDir strin
|
||||
continue
|
||||
}
|
||||
|
||||
item = item.ResolveSoftwarePackagePaths(baseDir)
|
||||
|
||||
// handle secrets
|
||||
if item.InstallScript.Path != "" {
|
||||
if err := gatherFileSecrets(result, item.InstallScript.Path); err != nil {
|
||||
multiError = multierror.Append(multiError, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if item.PostInstallScript.Path != "" {
|
||||
if err := gatherFileSecrets(result, item.PostInstallScript.Path); err != nil {
|
||||
multiError = multierror.Append(multiError, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if item.UninstallScript.Path != "" {
|
||||
if err := gatherFileSecrets(result, item.UninstallScript.Path); err != nil {
|
||||
multiError = multierror.Append(multiError, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
result.Software.FleetMaintainedApps = append(result.Software.FleetMaintainedApps, &item)
|
||||
}
|
||||
for _, item := range software.Packages {
|
||||
@@ -1117,9 +1139,9 @@ func parseSoftware(top map[string]json.RawMessage, result *GitOps, baseDir strin
|
||||
continue
|
||||
}
|
||||
|
||||
softwarePackageSpec = resolveSoftwarePackagePaths(filepath.Dir(softwarePackageSpec.ReferencedYamlPath), softwarePackageSpec)
|
||||
softwarePackageSpec = softwarePackageSpec.ResolveSoftwarePackagePaths(filepath.Dir(softwarePackageSpec.ReferencedYamlPath))
|
||||
} else {
|
||||
softwarePackageSpec = resolveSoftwarePackagePaths(baseDir, item.SoftwarePackageSpec)
|
||||
softwarePackageSpec = item.SoftwarePackageSpec.ResolveSoftwarePackagePaths(baseDir)
|
||||
}
|
||||
if softwarePackageSpec.InstallScript.Path != "" {
|
||||
if err := gatherFileSecrets(result, softwarePackageSpec.InstallScript.Path); err != nil {
|
||||
@@ -1194,23 +1216,6 @@ func gatherFileSecrets(result *GitOps, filePath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveSoftwarePackagePaths(baseDir string, softwareSpec fleet.SoftwarePackageSpec) fleet.SoftwarePackageSpec {
|
||||
if softwareSpec.PreInstallQuery.Path != "" {
|
||||
softwareSpec.PreInstallQuery.Path = resolveApplyRelativePath(baseDir, softwareSpec.PreInstallQuery.Path)
|
||||
}
|
||||
if softwareSpec.InstallScript.Path != "" {
|
||||
softwareSpec.InstallScript.Path = resolveApplyRelativePath(baseDir, softwareSpec.InstallScript.Path)
|
||||
}
|
||||
if softwareSpec.PostInstallScript.Path != "" {
|
||||
softwareSpec.PostInstallScript.Path = resolveApplyRelativePath(baseDir, softwareSpec.PostInstallScript.Path)
|
||||
}
|
||||
if softwareSpec.UninstallScript.Path != "" {
|
||||
softwareSpec.UninstallScript.Path = resolveApplyRelativePath(baseDir, softwareSpec.UninstallScript.Path)
|
||||
}
|
||||
|
||||
return softwareSpec
|
||||
}
|
||||
|
||||
func getDuplicateNames[T any](slice []T, getComparableString func(T) string) []string {
|
||||
// We are using the allKeys map as a set here. True means the item is a duplicate.
|
||||
allKeys := make(map[string]bool)
|
||||
|
||||
+19
-1
@@ -105,6 +105,9 @@ func TestValidGitOpsYaml(t *testing.T) {
|
||||
"FLEET_SECRET_NAME": "secret_name",
|
||||
"FLEET_SECRET_length": "10",
|
||||
"FLEET_SECRET_BANANA": "bread",
|
||||
"FLEET_SECRET_CLEMENTINE": "not-an-orange",
|
||||
"FLEET_SECRET_DURIAN": "fruity", // not used
|
||||
"FLEET_SECRET_EGGPLANT": "parmesan",
|
||||
},
|
||||
filePath: "testdata/team_config_no_paths.yml",
|
||||
isTeam: true,
|
||||
@@ -119,6 +122,9 @@ func TestValidGitOpsYaml(t *testing.T) {
|
||||
"FLEET_SECRET_NAME": "secret_name",
|
||||
"FLEET_SECRET_length": "10",
|
||||
"FLEET_SECRET_BANANA": "bread",
|
||||
"FLEET_SECRET_CLEMENTINE": "not-an-orange",
|
||||
"FLEET_SECRET_DURIAN": "fruity", // not used
|
||||
"FLEET_SECRET_EGGPLANT": "parmesan",
|
||||
},
|
||||
filePath: "testdata/team_config.yml",
|
||||
isTeam: true,
|
||||
@@ -133,6 +139,9 @@ func TestValidGitOpsYaml(t *testing.T) {
|
||||
"FLEET_SECRET_NAME": "secret_name",
|
||||
"FLEET_SECRET_length": "10",
|
||||
"FLEET_SECRET_BANANA": "bread",
|
||||
"FLEET_SECRET_CLEMENTINE": "not-an-orange",
|
||||
"FLEET_SECRET_DURIAN": "fruity", // not used
|
||||
"FLEET_SECRET_EGGPLANT": "parmesan",
|
||||
},
|
||||
filePath: "testdata/team_config_only_sha256.yml",
|
||||
isTeam: true,
|
||||
@@ -188,6 +197,7 @@ func TestValidGitOpsYaml(t *testing.T) {
|
||||
assert.Equal(t, "SampleSecret123", secrets.([]*fleet.EnrollSecret)[0].Secret)
|
||||
assert.Equal(t, "ABC", secrets.([]*fleet.EnrollSecret)[1].Secret)
|
||||
require.Len(t, gitops.Software.Packages, 2)
|
||||
require.Len(t, gitops.FleetSecrets, 6)
|
||||
for _, pkg := range gitops.Software.Packages {
|
||||
if strings.Contains(pkg.URL, "MicrosoftTeams") {
|
||||
assert.Equal(t, "testdata/lib/uninstall.sh", pkg.UninstallScript.Path)
|
||||
@@ -200,8 +210,16 @@ func TestValidGitOpsYaml(t *testing.T) {
|
||||
switch fma.Slug {
|
||||
case "slack/darwin":
|
||||
require.ElementsMatch(t, fma.Categories, []string{"Productivity", "Communication"})
|
||||
require.Empty(t, fma.PreInstallQuery)
|
||||
require.Empty(t, fma.PostInstallScript)
|
||||
require.Empty(t, fma.InstallScript)
|
||||
require.Empty(t, fma.UninstallScript)
|
||||
case "box-drive/windows":
|
||||
require.ElementsMatch(t, fma.Categories, []string{"Productivity", "Developer tools"})
|
||||
require.NotEmpty(t, fma.PreInstallQuery)
|
||||
require.NotEmpty(t, fma.PostInstallScript)
|
||||
require.NotEmpty(t, fma.InstallScript)
|
||||
require.NotEmpty(t, fma.UninstallScript)
|
||||
default:
|
||||
assert.FailNow(t, "unexpected slug found in gitops file", "slug: %s", fma.Slug)
|
||||
}
|
||||
@@ -240,6 +258,7 @@ func TestValidGitOpsYaml(t *testing.T) {
|
||||
activityExpiryWindow, ok := activityExpirySettings["activity_expiry_window"].(float64)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, 30, int(activityExpiryWindow))
|
||||
require.Len(t, gitops.FleetSecrets, 4)
|
||||
|
||||
// Check labels
|
||||
require.Len(t, gitops.Labels, 2)
|
||||
@@ -274,7 +293,6 @@ func TestValidGitOpsYaml(t *testing.T) {
|
||||
assert.True(t, ok, "windows_migration_enabled not found")
|
||||
_, ok = gitops.Controls.WindowsUpdates.(map[string]interface{})
|
||||
assert.True(t, ok, "windows_updates not found")
|
||||
require.Len(t, gitops.FleetSecrets, 4)
|
||||
assert.Equal(t, "fleet_secret", gitops.FleetSecrets["FLEET_SECRET_FLEET_SECRET_"])
|
||||
assert.Equal(t, "secret_name", gitops.FleetSecrets["FLEET_SECRET_NAME"])
|
||||
assert.Equal(t, "10", gitops.FleetSecrets["FLEET_SECRET_length"])
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
echo $FLEET_SECRET_EGGPLANT
|
||||
+1
@@ -0,0 +1 @@
|
||||
echo $FLEET_SECRET_EGGPLANT
|
||||
+1
@@ -0,0 +1 @@
|
||||
- query: SELECT 1 FROM osquery_info
|
||||
+1
@@ -0,0 +1 @@
|
||||
echo $FLEET_SECRET_CLEMENTINE
|
||||
Vendored
+8
@@ -52,6 +52,14 @@ software:
|
||||
- Productivity
|
||||
- Communication
|
||||
- slug: box-drive/windows
|
||||
install_script:
|
||||
path: ./lib/install.sh
|
||||
uninstall_script:
|
||||
path: ./lib/uninstall-box.sh
|
||||
post_install_script:
|
||||
path: ./lib/post-install.sh
|
||||
pre_install_query:
|
||||
path: ./lib/preinstall-query.yml
|
||||
self_service: true
|
||||
categories:
|
||||
- Productivity
|
||||
|
||||
+8
@@ -154,6 +154,14 @@ software:
|
||||
- Productivity
|
||||
- Communication
|
||||
- slug: box-drive/windows
|
||||
install_script:
|
||||
path: ./lib/install.sh
|
||||
uninstall_script:
|
||||
path: ./lib/uninstall-box.sh
|
||||
post_install_script:
|
||||
path: ./lib/post-install.sh
|
||||
pre_install_query:
|
||||
path: ./lib/preinstall-query.yml
|
||||
self_service: true
|
||||
categories:
|
||||
- Productivity
|
||||
|
||||
@@ -56,3 +56,11 @@ software:
|
||||
categories:
|
||||
- Productivity
|
||||
- Developer tools
|
||||
install_script:
|
||||
path: ./lib/install.sh
|
||||
uninstall_script:
|
||||
path: ./lib/uninstall-box.sh
|
||||
post_install_script:
|
||||
path: ./lib/post-install.sh
|
||||
pre_install_query:
|
||||
path: ./lib/preinstall-query.yml
|
||||
|
||||
Reference in New Issue
Block a user