require controls on either global or no-team (#41350)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #41307 # Details * Fixes a potential issue where running `fleetctl gitops` with only the global file, with no controls provided, could wipe out global controls that are provided in the "no team" file. * Fixes error message when controls are missing. # Checklist for submitter ## Testing - [X] Added/updated automated tests - [X] QA'd all new/changed functionality manually - [x] `fleetctl gitops -f /path/to/default.yml` without controls, gives `error: 'controls' must be set on global config, no-team.yml or unassigned.yml` - [x] `fleetctl gitops -f /path/to/default.yml` with empty controls works - [x] `fleetctl gitops -f /path/to/default.yml -f /path/to/no-team.yml` without controls, gives `error: 'controls' must be set on global config or no-team.yml` - [x] `fleetctl gitops -f /path/to/default.yml -f /path/to/unassigned.yml` without controls, gives `error: 'controls' must be set on global config or unassigned.yml` - [x] `fleetctl gitops -f /path/to/default.yml -f /path/to/no-team.yml` with empty controls in no-team.yml works - [x] `fleetctl gitops -f /path/to/default.yml -f /path/to/unassigned.yml` with empty controls in unassigned.yml works - [x] `fleetctl gitops -f /path/to/no-team.yml` gives error `global config must be provided alongside no-team.yml` - [x] `fleetctl gitops -f /path/to/no-team.yml` gives error `global config must be provided alongside unassigned.yml` - [x] `fleetctl gitops -f /path/to/some-real-team.yml` with no controls works For unreleased bug fixes in a release candidate, one of: - [X] Confirmed that the fix is not expected to adversely impact load test results
This commit is contained in:
@@ -303,7 +303,11 @@ func gitopsCommand() *cli.Command {
|
||||
}
|
||||
if !noTeamControls.Defined && !config.Controls.Defined {
|
||||
if appConfig.License.IsPremium() {
|
||||
return fmt.Errorf("'controls' must be set on global config or %s", noTeamFilename)
|
||||
suggestion := ", no-team.yml or unassigned.yml"
|
||||
if noTeamFilename != "" {
|
||||
suggestion = fmt.Sprintf(" or %s", noTeamFilename)
|
||||
}
|
||||
return fmt.Errorf("'controls' must be set on global config%s", suggestion)
|
||||
}
|
||||
return errors.New("'controls' must be set on global config")
|
||||
}
|
||||
|
||||
@@ -218,6 +218,7 @@ queries:
|
||||
policies:
|
||||
agent_options:
|
||||
labels:
|
||||
controls:
|
||||
org_settings:
|
||||
server_settings:
|
||||
server_url: https://example.com
|
||||
@@ -2396,7 +2397,7 @@ software:
|
||||
assert.True(t, strings.Contains(err.Error(), "calendar events are not supported on policies included in `no-team.yml`: \"Foobar\""), err.Error())
|
||||
})
|
||||
|
||||
t.Run("global and no-team.yml DO NOT define controls -- controls is now optional", func(t *testing.T) {
|
||||
t.Run("global and no-team.yml DO NOT define controls -- should fail", func(t *testing.T) {
|
||||
globalFileWithoutControlsAndSoftwareKeys := createGlobalFileWithoutControlsAndSoftwareKeys(t, fleetServerURL, orgName)
|
||||
|
||||
noTeamFilePathWithoutControls := filepath.Join(t.TempDir(), "no-team.yml")
|
||||
@@ -2409,16 +2410,94 @@ software:
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Dry run, controls is now optional so this should succeed.
|
||||
_ = RunAppForTest(t, []string{
|
||||
// Dry run.
|
||||
_, err = RunAppNoChecks([]string{
|
||||
"gitops", "-f", globalFileWithoutControlsAndSoftwareKeys.Name(), "-f", teamFileBasic.Name(), "-f",
|
||||
noTeamFileWithoutControls.Name(), "--dry-run",
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "must be set on global config or no-team.yml"), err.Error())
|
||||
|
||||
// Real run
|
||||
_ = RunAppForTest(t, []string{
|
||||
_, err = RunAppNoChecks([]string{
|
||||
"gitops", "-f", globalFileWithoutControlsAndSoftwareKeys.Name(), "-f", teamFileBasic.Name(), "-f",
|
||||
noTeamFileWithoutControls.Name(),
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "must be set on global config or no-team.yml"), err.Error())
|
||||
})
|
||||
|
||||
t.Run("global DOES NOT define controls -- should fail", func(t *testing.T) {
|
||||
globalFileWithoutControlsAndSoftwareKeys := createGlobalFileWithoutControlsAndSoftwareKeys(t, fleetServerURL, orgName)
|
||||
|
||||
// Dry run.
|
||||
_, err = RunAppNoChecks([]string{
|
||||
"gitops", "-f", globalFileWithoutControlsAndSoftwareKeys.Name(), "--dry-run",
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "must be set on global config, no-team.yml or unassigned.yml"), err.Error())
|
||||
|
||||
// Real run
|
||||
_, err = RunAppNoChecks([]string{
|
||||
"gitops", "-f", globalFileWithoutControlsAndSoftwareKeys.Name(),
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "must be set on global config, no-team.yml or unassigned.yml"), err.Error())
|
||||
})
|
||||
|
||||
t.Run("no-team provided without global -- should fail", func(t *testing.T) {
|
||||
noTeamFilePathWithoutControls := filepath.Join(t.TempDir(), "no-team.yml")
|
||||
noTeamFileWithoutControls, err := os.Create(noTeamFilePathWithoutControls)
|
||||
require.NoError(t, err)
|
||||
_, err = noTeamFileWithoutControls.WriteString(`
|
||||
policies:
|
||||
name: No team
|
||||
software:
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Dry run.
|
||||
_, err = RunAppNoChecks([]string{
|
||||
"gitops", "-f", teamFileBasic.Name(), "-f",
|
||||
noTeamFileWithoutControls.Name(), "--dry-run",
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "global config must be provided alongside no-team.yml"), err.Error())
|
||||
|
||||
_, err = RunAppNoChecks([]string{
|
||||
"gitops", "-f", teamFileBasic.Name(), "-f",
|
||||
noTeamFileWithoutControls.Name(),
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "global config must be provided alongside no-team.yml"), err.Error())
|
||||
})
|
||||
|
||||
t.Run("unassigned provided without global -- should fail", func(t *testing.T) {
|
||||
noTeamFilePathWithoutControls := filepath.Join(t.TempDir(), "unassigned.yml")
|
||||
noTeamFileWithoutControls, err := os.Create(noTeamFilePathWithoutControls)
|
||||
require.NoError(t, err)
|
||||
_, err = noTeamFileWithoutControls.WriteString(`
|
||||
policies:
|
||||
name: Unassigned
|
||||
software:
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Dry run
|
||||
_, err = RunAppNoChecks([]string{
|
||||
"gitops", "-f", teamFileBasic.Name(), "-f",
|
||||
noTeamFileWithoutControls.Name(), "--dry-run",
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "global config must be provided alongside unassigned.yml"), err.Error())
|
||||
|
||||
// Real run
|
||||
_, err = RunAppNoChecks([]string{
|
||||
"gitops", "-f", teamFileBasic.Name(), "-f",
|
||||
noTeamFileWithoutControls.Name(),
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "global config must be provided alongside unassigned.yml"), err.Error())
|
||||
})
|
||||
|
||||
t.Run("controls only defined in no-team.yml", func(t *testing.T) {
|
||||
|
||||
@@ -3825,8 +3825,9 @@ labels:
|
||||
require.NoError(t, err)
|
||||
require.Len(t, labels, 1)
|
||||
|
||||
// Step 2: Apply a minimal global config that omits policies, agent_options, controls, reports, labels.
|
||||
// Step 2: Apply a minimal global config that omits policies, agent_options, reports, labels.
|
||||
const minimalGlobalConfig = `
|
||||
controls:
|
||||
org_settings:
|
||||
server_settings:
|
||||
server_url: $FLEET_URL
|
||||
|
||||
@@ -432,6 +432,11 @@ func GitOpsFromFile(filePath, baseDir string, appConfig *fleet.EnrichedAppConfig
|
||||
if topKey == "name" || topKey == "labels" || topKey == "settings" || topKey == "org_settings" {
|
||||
continue
|
||||
}
|
||||
// "controls" can be set on _either_ global or "no team" file, and we can't say which it is if both
|
||||
// files aren't supplied, so play it safe and require it to be set on one or the other.
|
||||
if (result.IsNoTeam() || result.IsGlobal()) && topKey == "controls" {
|
||||
continue
|
||||
}
|
||||
// "agent_options" and "reports" are not supported in no-team/unassigned files.
|
||||
if result.IsNoTeam() && (topKey == "agent_options" || topKey == "reports") {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user