diff --git a/changes/34351-gitops-inconsistent-lang b/changes/34351-gitops-inconsistent-lang new file mode 100644 index 0000000000..5caa05b80a --- /dev/null +++ b/changes/34351-gitops-inconsistent-lang @@ -0,0 +1 @@ +* Made `gitops` output language consistent. \ No newline at end of file diff --git a/cmd/fleetctl/fleetctl/gitops.go b/cmd/fleetctl/fleetctl/gitops.go index f6d527d3d8..fceb9bbd06 100644 --- a/cmd/fleetctl/fleetctl/gitops.go +++ b/cmd/fleetctl/fleetctl/gitops.go @@ -423,7 +423,7 @@ func gitopsCommand() *cli.Command { return fmt.Errorf("volume_purchasing_program team %s cannot be deleted", team.Name) } if flDryRun { - _, _ = fmt.Fprintf(c.App.Writer, "[!] would delete team %s\n", team.Name) + _, _ = fmt.Fprintf(c.App.Writer, "[!] would've deleted team %s\n", team.Name) } else { _, _ = fmt.Fprintf(c.App.Writer, "[-] deleting team %s\n", team.Name) if err := fleetClient.DeleteTeam(team.ID); err != nil { diff --git a/cmd/fleetctl/integrationtest/gitops/gitops_enterprise_integration_test.go b/cmd/fleetctl/integrationtest/gitops/gitops_enterprise_integration_test.go index 7b23149044..1de563fe2c 100644 --- a/cmd/fleetctl/integrationtest/gitops/gitops_enterprise_integration_test.go +++ b/cmd/fleetctl/integrationtest/gitops/gitops_enterprise_integration_test.go @@ -159,6 +159,44 @@ func (s *enterpriseIntegrationGitopsTestSuite) TearDownTest() { } } +func (s *enterpriseIntegrationGitopsTestSuite) assertDryRunOutput(t *testing.T, output string) { + allowedVerbs := []string{ + "deleted", + "updated", + "applied", + "added", + "created", + "set", + } + pattern := fmt.Sprintf("\\[([+\\-!])] would've (%s)", strings.Join(allowedVerbs, "|")) + reg := regexp.MustCompile(pattern) + for line := range strings.SplitSeq(output, "\n") { + if line != "" && !strings.Contains(line, "succeeded") { + assert.Regexp(t, reg, line, "on dry run") + } + } +} + +func (s *enterpriseIntegrationGitopsTestSuite) assertRealRunOutput(t *testing.T, output string) { + allowedVerbs := []string{ + "deleted", + "updated", + "applied", + "added", + "created", + "set", + "applying", // this is used when doing groups operations before the operation starts, e.g. "Applying 10 policies" + "deleting", // ditto + } + pattern := fmt.Sprintf("\\[([+\\-!])] (%s)", strings.Join(allowedVerbs, "|")) + reg := regexp.MustCompile(pattern) + for line := range strings.SplitSeq(output, "\n") { + if line != "" && !strings.Contains(line, "succeeded") { + assert.Regexp(t, reg, line, "on real run") + } + } +} + // TestFleetGitops runs `fleetctl gitops` command on configs in https://github.com/fleetdm/fleet-gitops repo. // Changes to that repo may cause this test to fail. func (s *enterpriseIntegrationGitopsTestSuite) TestFleetGitops() { @@ -221,16 +259,16 @@ team_settings: test.CreateInsertGlobalVPPToken(t, s.DS) // Apply the team to be deleted - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", deletedTeamFile.Name()}) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", deletedTeamFile.Name()})) // Dry run - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile, "--dry-run"}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile, "--dry-run"})) for _, fileName := range teamFileNames { // When running no-teams, global config must also be provided ... if strings.Contains(fileName, "no-team.yml") { - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", fileName, "-f", globalFile, "--dry-run"}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", fileName, "-f", globalFile, "--dry-run"})) } else { - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", fileName, "--dry-run"}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", fileName, "--dry-run"})) } } @@ -239,14 +277,14 @@ team_settings: for _, fileName := range teamFileNames { args = append(args, "-f", fileName) } - _ = fleetctl.RunAppForTest(t, args) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, args)) // Real run with all the files, but don't delete other teams args = []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile} for _, fileName := range teamFileNames { args = append(args, "-f", fileName) } - _ = fleetctl.RunAppForTest(t, args) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, args)) // Check that all the teams exist teamsJSON := fleetctl.RunAppForTest(t, []string{"get", "teams", "--config", fleetctlConfig.Name(), "--json"}) @@ -257,7 +295,7 @@ team_settings: for _, fileName := range teamFileNames { args = append(args, "-f", fileName) } - _ = fleetctl.RunAppForTest(t, args) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, args)) // Check that only the right teams exist teamsJSON = fleetctl.RunAppForTest(t, []string{"get", "teams", "--config", fleetctlConfig.Name(), "--json"}) @@ -265,13 +303,13 @@ team_settings: assert.NotContains(t, teamsJSON, deletedTeamName) // Real run with one file at a time - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile}) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile})) for _, fileName := range teamFileNames { // When running no-teams, global config must also be provided ... if strings.Contains(fileName, "no-team.yml") { - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", fileName, "-f", globalFile}) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", fileName, "-f", globalFile})) } else { - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", fileName}) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", fileName})) } } } @@ -352,9 +390,8 @@ team_settings: t.Setenv("FLEET_URL", s.Server.URL) // Apply configs - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()})) // Add bootstrap packages require.NoError(t, s.DS.InsertMDMAppleBootstrapPackage(context.Background(), &fleet.MDMAppleBootstrapPackage{ @@ -406,9 +443,8 @@ team_settings: }) // Re-apply configs and expect the macOS setup assets to be cleared - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()})) mysql.ExecAdhocSQL(t, s.DS, func(q sqlx.ExtContext) error { stmt := "SELECT COUNT(*) FROM mdm_apple_bootstrap_packages WHERE team_id IN (?, ?)" @@ -540,8 +576,8 @@ queries: t.Setenv("FLEET_URL", s.Server.URL) // Apply configs - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()})) groupedCAs, err := s.DS.GetGroupedCertificateAuthorities(t.Context(), false) require.NoError(t, err) @@ -608,8 +644,8 @@ queries: require.NoError(t, err) // Apply configs - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()})) groupedCAs, err = s.DS.GetGroupedCertificateAuthorities(t.Context(), false) require.NoError(t, err) @@ -645,8 +681,8 @@ queries: `) require.NoError(t, err) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()})) groupedCAs, err = s.DS.GetGroupedCertificateAuthorities(t.Context(), true) require.NoError(t, err) @@ -736,9 +772,8 @@ team_settings: t.Setenv("FLEET_URL", s.Server.URL) // Apply configs - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()})) // get the team ID team, err := s.DS.TeamByName(ctx, teamName) @@ -765,9 +800,8 @@ team_settings: require.NoError(t, err) // Apply configs - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()})) // the custom setting is not scoped by label anymore profs, _, err = s.DS.ListMDMConfigProfiles(ctx, nil, fleet.ListOptions{}) @@ -871,10 +905,8 @@ team_settings: testing_utils.StartSoftwareInstallerServer(t) // Apply configs - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name()})) // get the team ID team, err := s.DS.TeamByName(ctx, teamName) @@ -910,10 +942,10 @@ team_settings: require.NoError(t, err) // Apply configs - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, + []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, + []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name()})) // the installer is not scoped by label anymore meta, err = s.DS.GetSoftwareInstallerMetadataByTeamAndTitleID(ctx, nil, noTeamTitleID, false) @@ -995,9 +1027,9 @@ software: err = os.Rename(noTeamFile.Name(), noTeamFilePath) require.NoError(t, err) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, + []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath})) // Check script existance _, err = s.DS.GetSetupExperienceScript(ctx, nil) @@ -1101,14 +1133,15 @@ team_settings: // Test dry-run first output := fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "--dry-run"}) + s.assertDryRunOutput(t, output) // Check that webhook settings are mentioned in the output - require.Contains(t, output, "applying webhook settings for 'No team'") require.Contains(t, output, "would've applied webhook settings for 'No team'") // Apply the configuration (non-dry-run) output = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath}) + s.assertRealRunOutput(t, output) // Verify the output mentions webhook settings were applied require.Contains(t, output, "applying webhook settings for 'No team'") @@ -1340,8 +1373,8 @@ queries: err = globalFile.Close() require.NoError(t, err) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()})) profiles, err := s.DS.ListMDMAppleConfigProfiles(ctx, nil) require.NoError(t, err) @@ -1370,8 +1403,8 @@ queries: err = globalFile.Close() require.NoError(t, err) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()})) // Check profile does not exist profiles, err = s.DS.ListMDMAppleConfigProfiles(ctx, nil) @@ -1484,10 +1517,10 @@ team_settings: t.Setenv("FLEET_URL", s.Server.URL) // Apply configs - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, + []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, + []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name()})) appConfig, err := s.DS.AppConfig(ctx) require.NoError(t, err) @@ -1498,10 +1531,10 @@ team_settings: assert.True(t, team.Config.MDM.MacOSSetup.ManualAgentInstall.Value) // Apply global configs without no-team - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFileOnlyClear.Name(), "-f", teamFileClear.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFileOnlyClear.Name(), "-f", teamFileClear.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, + []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFileOnlyClear.Name(), "-f", teamFileClear.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, + []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFileOnlyClear.Name(), "-f", teamFileClear.Name()})) appConfig, err = s.DS.AppConfig(ctx) require.NoError(t, err) assert.False(t, appConfig.MDM.MacOSSetup.ManualAgentInstall.Value) @@ -1510,9 +1543,9 @@ team_settings: assert.False(t, team.Config.MDM.MacOSSetup.ManualAgentInstall.Value) // Apply global configs only - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFileOnlySet.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFileOnlySet.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, + []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFileOnlySet.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFileOnlySet.Name()})) appConfig, err = s.DS.AppConfig(ctx) require.NoError(t, err) assert.True(t, appConfig.MDM.MacOSSetup.ManualAgentInstall.Value) @@ -1632,8 +1665,8 @@ queries: require.NoError(t, err) // Apply the configs - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath})) // Verify the script was saved _, err = s.DS.GetSetupExperienceScript(ctx, nil) @@ -1962,8 +1995,8 @@ labels: require.NoError(t, err) // Apply the configs - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name()})) // Verify the label was created and has the correct hosts labels, err := s.DS.LabelsByName(ctx, []string{"my-label"}) @@ -2057,10 +2090,8 @@ team_settings: t.Setenv("FLEET_URL", s.Server.URL) testing_utils.StartSoftwareInstallerServer(t) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "--dry-run"}) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath})) // the ipa installer was created for no team titles, _, _, err := s.DS.ListSoftwareTitles(ctx, fleet.SoftwareTitleListOptions{AvailableForInstall: true, TeamID: ptr.Uint(0)}, @@ -2112,10 +2143,8 @@ team_settings: err = teamFile.Close() require.NoError(t, err) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()})) // get the team ID team, err := s.DS.TeamByName(ctx, teamName) @@ -2156,10 +2185,8 @@ team_settings: `, teamName)), 0o644) require.NoError(t, err) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()})) // the ipa installer was created for the team titles, _, _, err = s.DS.ListSoftwareTitles(ctx, fleet.SoftwareTitleListOptions{AvailableForInstall: true, TeamID: &team.ID}, @@ -2191,10 +2218,8 @@ team_settings: err = os.WriteFile(teamFile.Name(), []byte(fmt.Sprintf(teamTemplate, "", teamName)), 0o644) require.NoError(t, err) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", teamFile.Name()})) titles, _, _, err = s.DS.ListSoftwareTitles(ctx, fleet.SoftwareTitleListOptions{AvailableForInstall: true, TeamID: &team.ID}, fleet.TeamFilter{User: test.UserAdmin}) @@ -2279,10 +2304,8 @@ team_settings: testing_utils.StartSoftwareInstallerServer(t) // Apply configs - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name(), "--dry-run"}) - _ = fleetctl.RunAppForTest(t, - []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name()}) + s.assertDryRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name(), "--dry-run"})) + s.assertRealRunOutput(t, fleetctl.RunAppForTest(t, []string{"gitops", "--config", fleetctlConfig.Name(), "-f", globalFile.Name(), "-f", noTeamFilePath, "-f", teamFile.Name()})) // get the team ID team, err := s.DS.TeamByName(ctx, teamName) diff --git a/server/service/client.go b/server/service/client.go index 2df4da80f5..cc8404ede6 100644 --- a/server/service/client.go +++ b/server/service/client.go @@ -513,9 +513,10 @@ func numberWithPluralization(n int, singular string, plural string) string { } const ( - dryRunAppliedFormat = "[+] would've applied %s\n" - appliedFormat = "[+] applied %s\n" - applyingTeamFormat = "[+] applying %s for team %s\n" + dryRunAppliedFormat = "[+] would've applied %s\n" + appliedFormat = "[+] applied %s\n" + applyingTeamFormat = "[+] applying %s for team %s\n" + dryRunAppliedTeamFormat = "[+] would've applied %s for team %s\n" ) // ApplyGroup applies the given spec group to Fleet. @@ -956,7 +957,11 @@ func (c *Client) ApplyGroup( if opts.DryRun && (teamID == 0 || !ok) { logfn("[+] would've applied MDM profiles for new team %s\n", tmName) } else { - logfn("[+] applying MDM profiles for team %s\n", tmName) + if opts.DryRun { + logfn("[+] would've applied MDM profiles for new team %s\n", tmName) + } else { + logfn("[+] applying MDM profiles for team %s\n", tmName) + } if err := c.ApplyTeamProfiles(currentTeamName, profs, teamOpts); err != nil { return nil, nil, nil, nil, fmt.Errorf("applying custom settings for team %q: %w", tmName, err) } @@ -1017,6 +1022,10 @@ func (c *Client) ApplyGroup( } } } + format := applyingTeamFormat + if opts.DryRun { + format = dryRunAppliedTeamFormat + } if len(tmScriptsPayloads) > 0 { for tmName, scripts := range tmScriptsPayloads { // For non-dry run, currentTeamName and tmName are the same @@ -1032,7 +1041,7 @@ func (c *Client) ApplyGroup( for tmName, software := range tmSoftwarePackagesPayloads { // For non-dry run, currentTeamName and tmName are the same currentTeamName := getTeamName(tmName) - logfn(applyingTeamFormat, numberWithPluralization(len(software), "software package", "software packages"), tmName) + logfn(format, numberWithPluralization(len(software), "software package", "software packages"), tmName) installers, err := c.ApplyTeamSoftwareInstallers(currentTeamName, software, opts.ApplySpecOptions) if err != nil { return nil, nil, nil, nil, fmt.Errorf("applying software installers for team %q: %w", tmName, err) @@ -1044,7 +1053,7 @@ func (c *Client) ApplyGroup( for tmName, apps := range tmSoftwareAppsPayloads { // For non-dry run, currentTeamName and tmName are the same currentTeamName := getTeamName(tmName) - logfn(applyingTeamFormat, numberWithPluralization(len(apps), "app store app", "app store apps"), tmName) + logfn(format, numberWithPluralization(len(apps), "app store app", "app store apps"), tmName) appsResponse, err := c.ApplyTeamAppStoreAppsAssociation(currentTeamName, apps, opts.ApplySpecOptions) if err != nil { return nil, nil, nil, nil, fmt.Errorf("applying app store apps for team: %q: %w", tmName, err) @@ -2389,7 +2398,7 @@ func (c *Client) DoGitOps( return nil, nil, err } logFn( - "[+] Set icons on %s and deleted icons on %s\n", + "[+] set icons on %s and deleted icons on %s\n", numberWithPluralization(len(iconUpdates.IconsToUpdate)+len(iconUpdates.IconsToUpload), "software title", "software titles"), numberWithPluralization(len(iconUpdates.TitleIDsToRemoveIconsFrom), "title", "titles"), ) @@ -2569,20 +2578,24 @@ func (c *Client) doGitOpsNoTeamSetupAndSoftware( } } - logFn(applyingTeamFormat, numberWithPluralization(len(swPkgPayload), "software package", "software packages"), "'No team'") + format := applyingTeamFormat + if dryRun { + format = dryRunAppliedTeamFormat + } + + logFn(format, numberWithPluralization(len(swPkgPayload), "software package", "software packages"), "'No team'") softwareInstallers, err = c.ApplyNoTeamSoftwareInstallers(swPkgPayload, fleet.ApplySpecOptions{DryRun: dryRun}) if err != nil { return nil, nil, fmt.Errorf("applying software installers: %w", err) } - logFn(applyingTeamFormat, numberWithPluralization(len(appsPayload), "app store app", "app store apps"), "'No team'") + + logFn(format, numberWithPluralization(len(appsPayload), "app store app", "app store apps"), "'No team'") vppApps, err := c.ApplyNoTeamAppStoreAppsAssociation(appsPayload, fleet.ApplySpecOptions{DryRun: dryRun}) if err != nil { return nil, nil, fmt.Errorf("applying app store apps: %w", err) } - if dryRun { - logFn("[+] would've applied 'No Team' software packages\n") - } else { + if !dryRun { logFn("[+] applied 'No Team' software packages\n") } return softwareInstallers, vppApps, nil @@ -2636,9 +2649,8 @@ func (c *Client) doGitOpsNoTeamWebhookSettings( } } - logFn("[+] applying webhook settings for 'No team'\n") - if !dryRun { + logFn("[+] applying webhook settings for 'No team'\n") // Apply the webhook settings to team ID 0 using the PATCH endpoint var teamResp interface{} err := c.authenticatedRequest(teamPayload, "PATCH", "/api/latest/fleet/teams/0", &teamResp) @@ -2684,7 +2696,12 @@ func (c *Client) doGitOpsLabels(config *spec.GitOps, logFn func(format string, a return nil, nil } - logFn("[+] syncing %s (%d new and %d updated)\n", numberWithPluralization(len(config.Labels), "label", "labels"), len(config.Labels)-numUpdates, numUpdates) + if dryRun { + logFn("[+] would've applied %s (%d new and %d updated)\n", numberWithPluralization(len(config.Labels), "label", "labels"), len(config.Labels)-numUpdates, numUpdates) + } else { + logFn("[+] applying %s (%d new and %d updated)\n", numberWithPluralization(len(config.Labels), "label", "labels"), len(config.Labels)-numUpdates, numUpdates) + } + err = c.ApplyLabels(config.Labels) if err != nil { return nil, err @@ -2807,7 +2824,12 @@ func (c *Client) doGitOpsPolicies(config *spec.GitOps, teamSoftwareInstallers [] if len(config.Policies) > 0 { numPolicies := len(config.Policies) - logFn("[+] syncing %s\n", numberWithPluralization(numPolicies, "policy", "policies")) + if dryRun { + logFn("[+] would've applied %s\n", numberWithPluralization(numPolicies, "policy", "policies")) + } else { + logFn("[+] applying %s\n", numberWithPluralization(numPolicies, "policy", "policies")) + } + if !dryRun { totalApplied := 0 for i := 0; i < len(config.Policies); i += batchSize { @@ -2825,7 +2847,7 @@ func (c *Client) doGitOpsPolicies(config *spec.GitOps, teamSoftwareInstallers [] if err := c.ApplyPolicies(policiesSpec); err != nil { return fmt.Errorf("error applying policies: %w", err) } - logFn("[+] synced %s\n", numberWithPluralization(totalApplied, "policy", "policies")) + logFn("[+] applied %s\n", numberWithPluralization(totalApplied, "policy", "policies")) } } } @@ -2848,7 +2870,11 @@ func (c *Client) doGitOpsPolicies(config *spec.GitOps, teamSoftwareInstallers [] } } if len(policiesToDelete) > 0 { - logFn("[-] deleting %s\n", numberWithPluralization(len(policiesToDelete), "policy", "policies")) + if dryRun { + logFn("[-] would've deleted %s\n", numberWithPluralization(len(policiesToDelete), "policy", "policies")) + } else { + logFn("[-] deleting %s\n", numberWithPluralization(len(policiesToDelete), "policy", "policies")) + } if !dryRun { totalDeleted := 0 for i := 0; i < len(policiesToDelete); i += batchSize { @@ -2885,7 +2911,11 @@ func (c *Client) doGitOpsQueries(config *spec.GitOps, logFn func(format string, } if len(config.Queries) > 0 { numQueries := len(config.Queries) - logFn("[+] syncing %s\n", numberWithPluralization(numQueries, "query", "queries")) + if dryRun { + logFn("[+] would've applied %s\n", numberWithPluralization(numQueries, "query", "queries")) + } else { + logFn("[+] applying %s\n", numberWithPluralization(numQueries, "query", "queries")) + } if !dryRun { appliedCount := 0 for i := 0; i < len(config.Queries); i += batchSize { @@ -2898,7 +2928,7 @@ func (c *Client) doGitOpsQueries(config *spec.GitOps, logFn func(format string, if err := c.ApplyQueries(config.Queries[i:end]); err != nil { return fmt.Errorf("error applying queries: %w", err) } - logFn("[+] synced %s\n", numberWithPluralization(appliedCount, "query", "queries")) + logFn("[+] applied %s\n", numberWithPluralization(appliedCount, "query", "queries")) } } } @@ -2921,8 +2951,10 @@ func (c *Client) doGitOpsQueries(config *spec.GitOps, logFn func(format string, } } if len(queriesToDelete) > 0 { - logFn("[-] deleting %s\n", numberWithPluralization(len(queriesToDelete), "query", "queries")) - if !dryRun { + if dryRun { + logFn("[-] would've deleted %s\n", numberWithPluralization(len(queriesToDelete), "query", "queries")) + } else { + logFn("[-] deleting %s\n", numberWithPluralization(len(queriesToDelete), "query", "queries")) deleteCount := 0 for i := 0; i < len(queriesToDelete); i += batchSize { end := i + batchSize