Skip to content

Commit

Permalink
SSVL: implement gitops support for labels include/exclude on software…
Browse files Browse the repository at this point in the history
… packages (#24663)
  • Loading branch information
mna authored Dec 17, 2024
1 parent fa2c399 commit 79ac8fa
Show file tree
Hide file tree
Showing 20 changed files with 718 additions and 37 deletions.
1 change: 1 addition & 0 deletions changes/24663-software-scoped-via-labels-gitops
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Added `fleetctl gitops` support to scope software installers by labels, with the `labels_include_any` or `labels_exclude_any` conditions.
40 changes: 40 additions & 0 deletions cmd/fleetctl/gitops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1902,6 +1902,10 @@ func TestGitOpsTeamSofwareInstallers(t *testing.T) {
{"testdata/gitops/team_software_installer_post_install_not_found.yml", "no such file or directory"},
{"testdata/gitops/team_software_installer_no_url.yml", "software URL is required"},
{"testdata/gitops/team_software_installer_invalid_self_service_value.yml", "\"packages.self_service\" must be a bool, found string"},
{"testdata/gitops/team_software_installer_invalid_both_include_exclude.yml", `only one of "labels_exclude_any" or "labels_include_any" can be specified`},
{"testdata/gitops/team_software_installer_valid_include.yml", ""},
{"testdata/gitops/team_software_installer_valid_exclude.yml", ""},
{"testdata/gitops/team_software_installer_invalid_unknown_label.yml", "some or all the labels provided don't exist"},
// team tests for setup experience software/script
{"testdata/gitops/team_setup_software_valid.yml", ""},
{"testdata/gitops/team_setup_software_invalid_script.yml", "no_such_script.sh: no such file"},
Expand Down Expand Up @@ -1931,6 +1935,22 @@ func TestGitOpsTeamSofwareInstallers(t *testing.T) {
Teams: nil,
}, nil
}
labelToIDs := map[string]uint{
fleet.BuiltinLabelMacOS14Plus: 1,
"a": 2,
"b": 3,
}
ds.LabelIDsByNameFunc = func(ctx context.Context, labels []string) (map[string]uint, error) {
// for this test, recognize labels a and b (as well as the built-in macos 14+ one)
ret := make(map[string]uint)
for _, lbl := range labels {
id, ok := labelToIDs[lbl]
if ok {
ret[lbl] = id
}
}
return ret, nil
}

_, err = runAppNoChecks([]string{"gitops", "-f", c.file})
if c.wantErr == "" {
Expand Down Expand Up @@ -1984,6 +2004,10 @@ func TestGitOpsNoTeamSoftwareInstallers(t *testing.T) {
{"testdata/gitops/no_team_software_installer_post_install_not_found.yml", "no such file or directory"},
{"testdata/gitops/no_team_software_installer_no_url.yml", "software URL is required"},
{"testdata/gitops/no_team_software_installer_invalid_self_service_value.yml", "\"packages.self_service\" must be a bool, found string"},
{"testdata/gitops/no_team_software_installer_invalid_both_include_exclude.yml", `only one of "labels_exclude_any" or "labels_include_any" can be specified`},
{"testdata/gitops/no_team_software_installer_valid_include.yml", ""},
{"testdata/gitops/no_team_software_installer_valid_exclude.yml", ""},
{"testdata/gitops/no_team_software_installer_invalid_unknown_label.yml", "some or all the labels provided don't exist"},
// No team tests for setup experience software/script
{"testdata/gitops/no_team_setup_software_valid.yml", ""},
{"testdata/gitops/no_team_setup_software_invalid_script.yml", "no_such_script.sh: no such file"},
Expand Down Expand Up @@ -2013,6 +2037,22 @@ func TestGitOpsNoTeamSoftwareInstallers(t *testing.T) {
Teams: nil,
}, nil
}
labelToIDs := map[string]uint{
fleet.BuiltinLabelMacOS14Plus: 1,
"a": 2,
"b": 3,
}
ds.LabelIDsByNameFunc = func(ctx context.Context, labels []string) (map[string]uint, error) {
// for this test, recognize labels a and b (as well as the built-in macos 14+ one)
ret := make(map[string]uint)
for _, lbl := range labels {
id, ok := labelToIDs[lbl]
if ok {
ret[lbl] = id
}
}
return ret, nil
}

t.Setenv("APPLE_BM_DEFAULT_TEAM", "")
globalFile := "./testdata/gitops/global_config_no_paths.yml"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: No team
controls:
policies:
software:
packages:
- url: ${SOFTWARE_INSTALLER_URL}/ruby.deb
install_script:
path: lib/install_ruby.sh
pre_install_query:
path: lib/query_ruby.yml
post_install_script:
path: lib/post_install_ruby.sh
uninstall_script:
path: lib/uninstall_ruby.sh
labels_include_any:
- a
labels_exclude_any:
- b
- url: ${SOFTWARE_INSTALLER_URL}/other.deb
self_service: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: No team
controls:
policies:
software:
packages:
- url: ${SOFTWARE_INSTALLER_URL}/ruby.deb
install_script:
path: lib/install_ruby.sh
pre_install_query:
path: lib/query_ruby.yml
post_install_script:
path: lib/post_install_ruby.sh
uninstall_script:
path: lib/uninstall_ruby.sh
labels_exclude_any:
- zzz
- url: ${SOFTWARE_INSTALLER_URL}/other.deb
self_service: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: No team
controls:
policies:
software:
packages:
- url: ${SOFTWARE_INSTALLER_URL}/ruby.deb
install_script:
path: lib/install_ruby.sh
pre_install_query:
path: lib/query_ruby.yml
post_install_script:
path: lib/post_install_ruby.sh
uninstall_script:
path: lib/uninstall_ruby.sh
labels_exclude_any:
- a
- b
- url: ${SOFTWARE_INSTALLER_URL}/other.deb
self_service: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: No team
controls:
policies:
software:
packages:
- url: ${SOFTWARE_INSTALLER_URL}/ruby.deb
install_script:
path: lib/install_ruby.sh
pre_install_query:
path: lib/query_ruby.yml
post_install_script:
path: lib/post_install_ruby.sh
uninstall_script:
path: lib/uninstall_ruby.sh
labels_include_any:
- a
- b
- url: ${SOFTWARE_INSTALLER_URL}/other.deb
self_service: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: "${TEST_TEAM_NAME}"
team_settings:
secrets:
- secret: "ABC"
features:
enable_host_users: true
enable_software_inventory: true
host_expiry_settings:
host_expiry_enabled: true
host_expiry_window: 30
agent_options:
controls:
policies:
queries:
software:
packages:
- url: ${SOFTWARE_INSTALLER_URL}/ruby.deb
install_script:
path: lib/install_ruby.sh
pre_install_query:
path: lib/query_ruby_apply.yml
post_install_script:
path: lib/post_install_ruby.sh
labels_include_any:
- a
labels_exclude_any:
- b
- url: ${SOFTWARE_INSTALLER_URL}/other.deb
self_service: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: "${TEST_TEAM_NAME}"
team_settings:
secrets:
- secret: "ABC"
features:
enable_host_users: true
enable_software_inventory: true
host_expiry_settings:
host_expiry_enabled: true
host_expiry_window: 30
agent_options:
controls:
policies:
queries:
software:
packages:
- url: ${SOFTWARE_INSTALLER_URL}/ruby.deb
install_script:
path: lib/install_ruby.sh
pre_install_query:
path: lib/query_ruby_apply.yml
post_install_script:
path: lib/post_install_ruby.sh
labels_include_any:
- zzz
- url: ${SOFTWARE_INSTALLER_URL}/other.deb
self_service: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: "${TEST_TEAM_NAME}"
team_settings:
secrets:
- secret: "ABC"
features:
enable_host_users: true
enable_software_inventory: true
host_expiry_settings:
host_expiry_enabled: true
host_expiry_window: 30
agent_options:
controls:
policies:
queries:
software:
packages:
- url: ${SOFTWARE_INSTALLER_URL}/ruby.deb
install_script:
path: lib/install_ruby.sh
pre_install_query:
path: lib/query_ruby_apply.yml
post_install_script:
path: lib/post_install_ruby.sh
labels_exclude_any:
- b
- url: ${SOFTWARE_INSTALLER_URL}/other.deb
self_service: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: "${TEST_TEAM_NAME}"
team_settings:
secrets:
- secret: "ABC"
features:
enable_host_users: true
enable_software_inventory: true
host_expiry_settings:
host_expiry_enabled: true
host_expiry_window: 30
agent_options:
controls:
policies:
queries:
software:
packages:
- url: ${SOFTWARE_INSTALLER_URL}/ruby.deb
install_script:
path: lib/install_ruby.sh
pre_install_query:
path: lib/query_ruby_apply.yml
post_install_script:
path: lib/post_install_ruby.sh
labels_include_any:
- a
- url: ${SOFTWARE_INSTALLER_URL}/other.deb
self_service: true
12 changes: 10 additions & 2 deletions ee/server/service/software_installers.go
Original file line number Diff line number Diff line change
Expand Up @@ -1158,7 +1158,7 @@ const (
)

func (svc *Service) BatchSetSoftwareInstallers(
ctx context.Context, tmName string, payloads []fleet.SoftwareInstallerPayload, dryRun bool,
ctx context.Context, tmName string, payloads []*fleet.SoftwareInstallerPayload, dryRun bool,
) (string, error) {
if err := svc.authz.Authorize(ctx, &fleet.Team{}, fleet.ActionRead); err != nil {
return "", err
Expand Down Expand Up @@ -1200,6 +1200,11 @@ func (svc *Service) BatchSetSoftwareInstallers(
fmt.Sprintf("Couldn't edit software. URL (%q) is invalid", payload.URL),
)
}
validatedLabels, err := svc.validateSoftwareLabels(ctx, payload.LabelsIncludeAny, payload.LabelsExcludeAny)
if err != nil {
return "", err
}
payload.ValidatedLabels = validatedLabels
}

// keyExpireTime is the current maximum time supported for retrieving
Expand Down Expand Up @@ -1239,7 +1244,7 @@ func (svc *Service) softwareBatchUpload(
requestUUID string,
teamID *uint,
userID uint,
payloads []fleet.SoftwareInstallerPayload,
payloads []*fleet.SoftwareInstallerPayload,
dryRun bool,
) {
var batchErr error
Expand Down Expand Up @@ -1351,6 +1356,9 @@ func (svc *Service) softwareBatchUpload(
UserID: userID,
URL: p.URL,
InstallDuringSetup: p.InstallDuringSetup,
LabelsIncludeAny: p.LabelsIncludeAny,
LabelsExcludeAny: p.LabelsExcludeAny,
ValidatedLabels: p.ValidatedLabels,
}

// set the filename before adding metadata, as it is used as fallback
Expand Down
4 changes: 4 additions & 0 deletions pkg/spec/gitops.go
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,10 @@ func parseSoftware(top map[string]json.RawMessage, result *GitOps, baseDir strin
multiError = multierror.Append(multiError, fmt.Errorf("software URL %q is too long, must be less than 256 characters", softwarePackageSpec.URL))
continue
}
if len(softwarePackageSpec.LabelsExcludeAny) > 0 && len(softwarePackageSpec.LabelsIncludeAny) > 0 {
multiError = multierror.Append(multiError, fmt.Errorf(`only one of "labels_exclude_any" or "labels_include_any" can be specified for software URL %q`, softwarePackageSpec.URL))
continue
}
result.Software.Packages = append(result.Software.Packages, &softwarePackageSpec)
}

Expand Down
Loading

0 comments on commit 79ac8fa

Please sign in to comment.