From d6780ae0da8833c1cf6af1c425a2e57147064a7b Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Thu, 9 May 2024 16:41:33 -0300 Subject: [PATCH 1/6] Support environment variables in config profiles --- .github/workflows/dogfood-gitops.yml | 8 +- changes/17309-support-env-vars-profiles | 1 + pkg/spec/gitops.go | 2 +- pkg/spec/gitops_test.go | 37 +++++++- pkg/spec/testdata/agent-options.yml | 6 +- pkg/spec/testdata/org-settings.yml | 2 +- pkg/spec/testdata/team_config.yml | 2 +- pkg/spec/testdata/top.policies2.yml | 2 +- pkg/spec/testdata/top.queries.yml | 2 +- server/fleet/app.go | 3 + server/service/apple_mdm_test.go | 24 +++++ server/service/client.go | 18 +++- server/service/client_test.go | 116 +++++++++++++++++++++++- 13 files changed, 197 insertions(+), 26 deletions(-) create mode 100644 changes/17309-support-env-vars-profiles diff --git a/.github/workflows/dogfood-gitops.yml b/.github/workflows/dogfood-gitops.yml index 4cead2ddf249..931a0ece640a 100644 --- a/.github/workflows/dogfood-gitops.yml +++ b/.github/workflows/dogfood-gitops.yml @@ -46,13 +46,6 @@ jobs: ref: main path: fleet-gitops - - name: Apply env vars to profiles - env: - MANAGED_CHROME_ENROLLMENT_TOKEN: ${{ secrets.CLOUD_MANAGEMENT_ENROLLMENT_TOKEN }} - run: | - envsubst < ./it-and-security/lib/configuration-profiles/macos-chrome-enrollment.mobileconfig > ./it-and-security/lib/configuration-profiles/macos-chrome-enrollment.confidential.mobileconfig - mv ./it-and-security/lib/configuration-profiles/macos-chrome-enrollment.confidential.mobileconfig ./it-and-security/lib/configuration-profiles/macos-chrome-enrollment.mobileconfig - - name: Apply latest configuration to Fleet uses: ./fleet-gitops/.github/gitops-action with: @@ -76,3 +69,4 @@ jobs: DOGFOOD_SERVERS_CANARY_ENROLL_SECRET: ${{ secrets.DOGFOOD_SERVERS_CANARY_ENROLL_SECRET }} DOGFOOD_EXPLORE_DATA_ENROLL_SECRET: ${{ secrets.DOGFOOD_EXPLORE_DATA_ENROLL_SECRET }} DOGFOOD_CALENDAR_API_KEY: ${{ secrets.DOGFOOD_CALENDAR_API_KEY }} + MANAGED_CHROME_ENROLLMENT_TOKEN: ${{ secrets.CLOUD_MANAGEMENT_ENROLLMENT_TOKEN }} diff --git a/changes/17309-support-env-vars-profiles b/changes/17309-support-env-vars-profiles new file mode 100644 index 000000000000..9e95efa76cf8 --- /dev/null +++ b/changes/17309-support-env-vars-profiles @@ -0,0 +1 @@ +* Support environment variables in configuration profiles for GitOps. diff --git a/pkg/spec/gitops.go b/pkg/spec/gitops.go index 5e6f040a9901..6d1dbb10a603 100644 --- a/pkg/spec/gitops.go +++ b/pkg/spec/gitops.go @@ -56,7 +56,7 @@ type GitOps struct { Queries []*fleet.QuerySpec } -// GitOpsFromBytes parses a GitOps yaml file. +// GitOpsFromFile parses a GitOps yaml file. func GitOpsFromFile(filePath, baseDir string) (*GitOps, error) { b, err := os.ReadFile(filePath) if err != nil { diff --git a/pkg/spec/gitops_test.go b/pkg/spec/gitops_test.go index 5d24c2c4ae28..180478ac48a2 100644 --- a/pkg/spec/gitops_test.go +++ b/pkg/spec/gitops_test.go @@ -59,13 +59,19 @@ func gitOpsFromString(t *testing.T, s string) (*GitOps, error) { func TestValidGitOpsYaml(t *testing.T) { t.Parallel() tests := map[string]struct { - filePath string - isTeam bool + environment map[string]string + filePath string + isTeam bool }{ "global_config_no_paths": { filePath: "testdata/global_config_no_paths.yml", }, "global_config_with_paths": { + environment: map[string]string{ + "LINUX_OS": "linux", + "DISTRIBUTED_DENYLIST_DURATION": "0", + "ORG_NAME": "Fleet Device Management", + }, filePath: "testdata/global_config.yml", }, "team_config_no_paths": { @@ -73,6 +79,12 @@ func TestValidGitOpsYaml(t *testing.T) { isTeam: true, }, "team_config_with_paths": { + environment: map[string]string{ + "POLICY": "policy", + "LINUX_OS": "linux", + "DISTRIBUTED_DENYLIST_DURATION": "0", + "ENABLE_FAILING_POLICIES_WEBHOOK": "true", + }, filePath: "testdata/team_config.yml", isTeam: true, }, @@ -83,7 +95,17 @@ func TestValidGitOpsYaml(t *testing.T) { name := name t.Run( name, func(t *testing.T) { - t.Parallel() + if len(test.environment) > 0 { + for k, v := range test.environment { + os.Setenv(k, v) + } + t.Cleanup(func() { + for k := range test.environment { + os.Unsetenv(k) + } + }) + } + gitops, err := GitOpsFromFile(test.filePath, "./testdata") require.NoError(t, err) @@ -105,6 +127,9 @@ func TestValidGitOpsYaml(t *testing.T) { assert.True(t, ok, "server_settings not found") assert.Equal(t, "https://fleet.example.com", serverSettings.(map[string]interface{})["server_url"]) assert.Contains(t, gitops.OrgSettings, "org_info") + orgInfo, ok := gitops.OrgSettings["org_info"].(map[string]interface{}) + assert.True(t, ok) + assert.Equal(t, "Fleet Device Management", orgInfo["org_name"]) assert.Contains(t, gitops.OrgSettings, "smtp_settings") assert.Contains(t, gitops.OrgSettings, "sso_settings") assert.Contains(t, gitops.OrgSettings, "integrations") @@ -151,12 +176,13 @@ func TestValidGitOpsYaml(t *testing.T) { // Check agent options assert.NotNil(t, gitops.AgentOptions) - assert.Contains(t, string(*gitops.AgentOptions), "distributed_denylist_duration") + assert.Contains(t, string(*gitops.AgentOptions), "\"distributed_denylist_duration\":0") // Check queries require.Len(t, gitops.Queries, 3) assert.Equal(t, "Scheduled query stats", gitops.Queries[0].Name) assert.Equal(t, "orbit_info", gitops.Queries[1].Name) + assert.Equal(t, "darwin,linux,windows", gitops.Queries[1].Platform) assert.Equal(t, "osquery_info", gitops.Queries[2].Name) // Check policies @@ -165,6 +191,7 @@ func TestValidGitOpsYaml(t *testing.T) { assert.Equal(t, "Passing policy", gitops.Policies[1].Name) assert.Equal(t, "No root logins (macOS, Linux)", gitops.Policies[2].Name) assert.Equal(t, "🔥 Failing policy", gitops.Policies[3].Name) + assert.Equal(t, "linux", gitops.Policies[3].Platform) assert.Equal(t, "😊😊 Failing policy", gitops.Policies[4].Name) }, ) @@ -283,7 +310,7 @@ queries: ` gitOps, err = gitOpsFromString(t, config) require.Error(t, err) - require.Contains(t, err.Error(), "variable \"NOT_DEFINED\" not set") + require.Contains(t, err.Error(), "environment variable \"NOT_DEFINED\" not set") } func TestMixingGlobalAndTeamConfig(t *testing.T) { diff --git a/pkg/spec/testdata/agent-options.yml b/pkg/spec/testdata/agent-options.yml index b2d24ce03dea..ea535167d6f1 100644 --- a/pkg/spec/testdata/agent-options.yml +++ b/pkg/spec/testdata/agent-options.yml @@ -1,10 +1,10 @@ command_line_flags: - distributed_denylist_duration: 0 + distributed_denylist_duration: $DISTRIBUTED_DENYLIST_DURATION config: decorators: load: - - SELECT uuid AS host_uuid FROM system_info; - - SELECT hostname AS hostname FROM system_info; + - SELECT uuid AS host_uuid FROM system_info; + - SELECT hostname AS hostname FROM system_info; options: disable_distributed: false distributed_interval: 10 diff --git a/pkg/spec/testdata/org-settings.yml b/pkg/spec/testdata/org-settings.yml index 17855a1b8b39..22f17b97a53d 100644 --- a/pkg/spec/testdata/org-settings.yml +++ b/pkg/spec/testdata/org-settings.yml @@ -11,7 +11,7 @@ org_info: contact_url: https://fleetdm.com/company/contact org_logo_url: "" org_logo_url_light_background: "" - org_name: Fleet Device Management + org_name: $ORG_NAME smtp_settings: authentication_method: authmethod_plain authentication_type: authtype_username_password diff --git a/pkg/spec/testdata/team_config.yml b/pkg/spec/testdata/team_config.yml index 9dbf7f0def83..127ed82bcfc0 100644 --- a/pkg/spec/testdata/team_config.yml +++ b/pkg/spec/testdata/team_config.yml @@ -20,7 +20,7 @@ policies: - path: ./top.policies.yml - path: ./top.policies2.yml - path: ./empty.yml - - name: 😊😊 Failing policy + - name: 😊😊 Failing $POLICY platform: linux description: This policy should always fail. resolution: There is no resolution for this policy. diff --git a/pkg/spec/testdata/top.policies2.yml b/pkg/spec/testdata/top.policies2.yml index d4cf4d179ec8..501a557b773a 100644 --- a/pkg/spec/testdata/top.policies2.yml +++ b/pkg/spec/testdata/top.policies2.yml @@ -1,5 +1,5 @@ - name: 🔥 Failing policy - platform: linux + platform: $LINUX_OS description: This policy should always fail. resolution: There is no resolution for this policy. query: SELECT 1 FROM osquery_info WHERE start_time < 0; diff --git a/pkg/spec/testdata/top.queries.yml b/pkg/spec/testdata/top.queries.yml index 988f52158913..e11bae8886e9 100644 --- a/pkg/spec/testdata/top.queries.yml +++ b/pkg/spec/testdata/top.queries.yml @@ -12,7 +12,7 @@ - name: orbit_info query: SELECT * from orbit_info; interval: 0 - platform: darwin,linux,windows + platform: darwin,$LINUX_OS,windows min_osquery_version: all observer_can_run: false automations_enabled: true diff --git a/server/fleet/app.go b/server/fleet/app.go index ef5b06e5b2de..bb737fb8893e 100644 --- a/server/fleet/app.go +++ b/server/fleet/app.go @@ -1037,6 +1037,9 @@ type ApplySpecOptions struct { DryRun bool // TeamForPolicies is the name of the team to set in policy specs. TeamForPolicies string + // ExpandEnvConfigProfiles enables expansion of environment variables in + // configuration profiles. + ExpandEnvConfigProfiles bool } type ApplyTeamSpecOptions struct { diff --git a/server/service/apple_mdm_test.go b/server/service/apple_mdm_test.go index 8be8d3dc9c60..8d85d6a4d996 100644 --- a/server/service/apple_mdm_test.go +++ b/server/service/apple_mdm_test.go @@ -2804,6 +2804,30 @@ func mobileconfigForTest(name, identifier string) []byte { `, name, identifier, uuid.New().String())) } +func mobileconfigWithFooEnvForTest(name string) []byte { + return []byte(fmt.Sprintf(` + + + + PayloadContent + + PayloadDisplayName + %s + PayloadIdentifier + %s + PayloadType + Configuration + PayloadUUID + %s + PayloadVersion + 1 + someConfig + $FOO + + +`, name, uuid.New().String(), uuid.New().String())) +} + func declBytesForTest(identifier string, payloadContent string) []byte { tmpl := `{ "Type": "com.apple.configuration.decl%s", diff --git a/server/service/client.go b/server/service/client.go index 53d8cc2fae35..f8dac0f34945 100644 --- a/server/service/client.go +++ b/server/service/client.go @@ -303,7 +303,7 @@ func (c *Client) runAppConfigChecks(fn func(ac *fleet.EnrichedAppConfig) error) // getProfilesContents takes file paths and creates a slice of profile payloads // ready to batch-apply. -func getProfilesContents(baseDir string, profiles []fleet.MDMProfileSpec) ([]fleet.MDMProfileBatchPayload, error) { +func getProfilesContents(baseDir string, profiles []fleet.MDMProfileSpec, expandEnv bool) ([]fleet.MDMProfileBatchPayload, error) { // map to check for duplicate names extByName := make(map[string]string, len(profiles)) result := make([]fleet.MDMProfileBatchPayload, 0, len(profiles)) @@ -315,6 +315,13 @@ func getProfilesContents(baseDir string, profiles []fleet.MDMProfileSpec) ([]fle return nil, fmt.Errorf("applying fleet config: %w", err) } + if expandEnv { + fileContents, err = spec.ExpandEnvBytes(fileContents) + if err != nil { + return nil, fmt.Errorf("expanding environment on file %q: %w", profile.Path, err) + } + } + // by default, use the file name. macOS profiles use their PayloadDisplayName ext := filepath.Ext(filePath) name := strings.TrimSuffix(filepath.Base(filePath), ext) @@ -424,7 +431,7 @@ func (c *Client) ApplyGroup( // custom settings but windows is present and empty (but mac is absent), // shouldn't that clear the windows ones? if (windowsCustomSettings != nil && macosCustomSettings != nil) || len(allCustomSettings) > 0 { - fileContents, err := getProfilesContents(baseDir, allCustomSettings) + fileContents, err := getProfilesContents(baseDir, allCustomSettings, opts.ExpandEnvConfigProfiles) if err != nil { return nil, err } @@ -514,7 +521,7 @@ func (c *Client) ApplyGroup( tmFileContents := make(map[string][]fleet.MDMProfileBatchPayload, len(tmMDMSettings)) for k, paths := range tmMDMSettings { - fileContents, err := getProfilesContents(baseDir, paths) + fileContents, err := getProfilesContents(baseDir, paths, opts.ExpandEnvConfigProfiles) if err != nil { return nil, err } @@ -1094,7 +1101,10 @@ func (c *Client) DoGitOps( } // Apply org settings, scripts, enroll secrets, and controls - teamIDsByName, err := c.ApplyGroup(ctx, &group, baseDir, logf, fleet.ApplySpecOptions{DryRun: dryRun}) + teamIDsByName, err := c.ApplyGroup(ctx, &group, baseDir, logf, fleet.ApplySpecOptions{ + DryRun: dryRun, + ExpandEnvConfigProfiles: true, + }) if err != nil { return nil, err } diff --git a/server/service/client_test.go b/server/service/client_test.go index 8814f2e3f6bc..db4f1f289982 100644 --- a/server/service/client_test.go +++ b/server/service/client_test.go @@ -488,13 +488,49 @@ func TestExtractFilenameFromPath(t *testing.T) { func TestGetProfilesContents(t *testing.T) { tempDir := t.TempDir() darwinProfile := mobileconfigForTest("bar", "I") + darwinProfileWithFooEnv := ` + + + + PayloadContent + + PayloadDisplayName + bar + PayloadIdentifier + 123 + PayloadType + Configuration + PayloadUUID + 123 + PayloadVersion + 1 + someConfig + $FOO + +` windowsProfile := syncMLForTest("./some/path") + windowsProfileWithBarEnv := ` + + + ./some/path + + + + + + + ${BAR}/some/path + + +` tests := []struct { name string baseDir string setupFiles [][2]string labels []string + environment map[string]string + expandEnv bool expectError bool want []fleet.MDMProfileBatchPayload }{ @@ -565,18 +601,94 @@ func TestGetProfilesContents(t *testing.T) { }, expectError: true, }, + { + name: "with environment variables", + baseDir: tempDir, + setupFiles: [][2]string{ + {"bar.mobileconfig", string(darwinProfileWithFooEnv)}, + {"foo.xml", string(windowsProfileWithBarEnv)}, + }, + environment: map[string]string{"FOO": "42", "BAR": "24"}, + expandEnv: true, + expectError: false, + want: []fleet.MDMProfileBatchPayload{ + { + Name: "bar", + Contents: []byte(` + + + + PayloadContent + + PayloadDisplayName + bar + PayloadIdentifier + 123 + PayloadType + Configuration + PayloadUUID + 123 + PayloadVersion + 1 + someConfig + 42 + +`), + }, + { + Name: "foo", + Contents: []byte(` + + + ./some/path + + + + + + + 24/some/path + + +`), + }, + }, + }, + { + name: "with environment variables but not set", + baseDir: tempDir, + setupFiles: [][2]string{ + {"bar.mobileconfig", string(darwinProfileWithFooEnv)}, + {"foo.xml", string(windowsProfileWithBarEnv)}, + }, + environment: map[string]string{}, + expandEnv: true, + expectError: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + if tt.expandEnv { + if len(tt.environment) > 0 { + for k, v := range tt.environment { + os.Setenv(k, v) + } + t.Cleanup(func() { + for k := range tt.environment { + os.Unsetenv(k) + } + }) + } + } paths := []fleet.MDMProfileSpec{} for _, fileSpec := range tt.setupFiles { filePath := filepath.Join(tempDir, fileSpec[0]) - require.NoError(t, os.WriteFile(filePath, []byte(fileSpec[1]), 0644)) + require.NoError(t, os.WriteFile(filePath, []byte(fileSpec[1]), 0o644)) paths = append(paths, fleet.MDMProfileSpec{Path: filePath, Labels: tt.labels}) } - profileContents, err := getProfilesContents(tt.baseDir, paths) + profileContents, err := getProfilesContents(tt.baseDir, paths, tt.expandEnv) if tt.expectError { require.Error(t, err) From e8fdadcbbb5a23ac9e8e971c4dbf04544fa7d0bc Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Thu, 9 May 2024 17:25:57 -0300 Subject: [PATCH 2/6] Fix lint --- server/service/apple_mdm_test.go | 24 ------------------------ server/service/client_test.go | 8 ++++---- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/server/service/apple_mdm_test.go b/server/service/apple_mdm_test.go index 8d85d6a4d996..8be8d3dc9c60 100644 --- a/server/service/apple_mdm_test.go +++ b/server/service/apple_mdm_test.go @@ -2804,30 +2804,6 @@ func mobileconfigForTest(name, identifier string) []byte { `, name, identifier, uuid.New().String())) } -func mobileconfigWithFooEnvForTest(name string) []byte { - return []byte(fmt.Sprintf(` - - - - PayloadContent - - PayloadDisplayName - %s - PayloadIdentifier - %s - PayloadType - Configuration - PayloadUUID - %s - PayloadVersion - 1 - someConfig - $FOO - - -`, name, uuid.New().String(), uuid.New().String())) -} - func declBytesForTest(identifier string, payloadContent string) []byte { tmpl := `{ "Type": "com.apple.configuration.decl%s", diff --git a/server/service/client_test.go b/server/service/client_test.go index db4f1f289982..f913f8285480 100644 --- a/server/service/client_test.go +++ b/server/service/client_test.go @@ -605,8 +605,8 @@ func TestGetProfilesContents(t *testing.T) { name: "with environment variables", baseDir: tempDir, setupFiles: [][2]string{ - {"bar.mobileconfig", string(darwinProfileWithFooEnv)}, - {"foo.xml", string(windowsProfileWithBarEnv)}, + {"bar.mobileconfig", darwinProfileWithFooEnv}, + {"foo.xml", windowsProfileWithBarEnv}, }, environment: map[string]string{"FOO": "42", "BAR": "24"}, expandEnv: true, @@ -658,8 +658,8 @@ func TestGetProfilesContents(t *testing.T) { name: "with environment variables but not set", baseDir: tempDir, setupFiles: [][2]string{ - {"bar.mobileconfig", string(darwinProfileWithFooEnv)}, - {"foo.xml", string(windowsProfileWithBarEnv)}, + {"bar.mobileconfig", darwinProfileWithFooEnv}, + {"foo.xml", windowsProfileWithBarEnv}, }, environment: map[string]string{}, expandEnv: true, From b4fbdad579711fc9a16a9f6190fee9c920aaba0e Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Fri, 10 May 2024 06:22:41 -0300 Subject: [PATCH 3/6] Add missing check --- pkg/spec/gitops_test.go | 9 +++++++++ pkg/spec/testdata/team-settings.yml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/spec/gitops_test.go b/pkg/spec/gitops_test.go index 180478ac48a2..565806475eb7 100644 --- a/pkg/spec/gitops_test.go +++ b/pkg/spec/gitops_test.go @@ -113,6 +113,15 @@ func TestValidGitOpsYaml(t *testing.T) { // Check team settings assert.Equal(t, "Team1", *gitops.TeamName) assert.Contains(t, gitops.TeamSettings, "webhook_settings") + webhookSettings, ok := gitops.TeamSettings["webhook_settings"].(map[string]interface{}) + assert.True(t, ok, "webhook_settings not found") + assert.Contains(t, webhookSettings, "failing_policies_webhook") + failingPoliciesWebhook, ok := webhookSettings["failing_policies_webhook"].(map[string]interface{}) + assert.True(t, ok, "webhook_settings not found") + assert.Contains(t, failingPoliciesWebhook, "enable_failing_policies_webhook") + enableFailingPoliciesWebhook, ok := failingPoliciesWebhook["enable_failing_policies_webhook"].(bool) + assert.True(t, ok) + assert.True(t, enableFailingPoliciesWebhook) assert.Contains(t, gitops.TeamSettings, "host_expiry_settings") assert.Contains(t, gitops.TeamSettings, "features") assert.Contains(t, gitops.TeamSettings, "secrets") diff --git a/pkg/spec/testdata/team-settings.yml b/pkg/spec/testdata/team-settings.yml index 620d1663977b..936c5a1fc673 100644 --- a/pkg/spec/testdata/team-settings.yml +++ b/pkg/spec/testdata/team-settings.yml @@ -3,7 +3,7 @@ secrets: - secret: "ABC" webhook_settings: failing_policies_webhook: - enable_failing_policies_webhook: true + enable_failing_policies_webhook: $ENABLE_FAILING_POLICIES_WEBHOOK destination_url: https://example.tines.com/webhook policy_ids: [1, 2, 3, 4, 5, 6 ,7, 8, 9] features: From b37cc1138847f8f74ee059ef1d872e6870bd6ce3 Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Fri, 10 May 2024 18:43:44 -0300 Subject: [PATCH 4/6] Revert change in workflow --- .github/workflows/dogfood-gitops.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dogfood-gitops.yml b/.github/workflows/dogfood-gitops.yml index 931a0ece640a..4cead2ddf249 100644 --- a/.github/workflows/dogfood-gitops.yml +++ b/.github/workflows/dogfood-gitops.yml @@ -46,6 +46,13 @@ jobs: ref: main path: fleet-gitops + - name: Apply env vars to profiles + env: + MANAGED_CHROME_ENROLLMENT_TOKEN: ${{ secrets.CLOUD_MANAGEMENT_ENROLLMENT_TOKEN }} + run: | + envsubst < ./it-and-security/lib/configuration-profiles/macos-chrome-enrollment.mobileconfig > ./it-and-security/lib/configuration-profiles/macos-chrome-enrollment.confidential.mobileconfig + mv ./it-and-security/lib/configuration-profiles/macos-chrome-enrollment.confidential.mobileconfig ./it-and-security/lib/configuration-profiles/macos-chrome-enrollment.mobileconfig + - name: Apply latest configuration to Fleet uses: ./fleet-gitops/.github/gitops-action with: @@ -69,4 +76,3 @@ jobs: DOGFOOD_SERVERS_CANARY_ENROLL_SECRET: ${{ secrets.DOGFOOD_SERVERS_CANARY_ENROLL_SECRET }} DOGFOOD_EXPLORE_DATA_ENROLL_SECRET: ${{ secrets.DOGFOOD_EXPLORE_DATA_ENROLL_SECRET }} DOGFOOD_CALENDAR_API_KEY: ${{ secrets.DOGFOOD_CALENDAR_API_KEY }} - MANAGED_CHROME_ENROLLMENT_TOKEN: ${{ secrets.CLOUD_MANAGEMENT_ENROLLMENT_TOKEN }} From 4abb8d43bfea9c00076f43ac49028d2b23a91f3a Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Fri, 10 May 2024 19:08:44 -0300 Subject: [PATCH 5/6] Define fleet.ApplyClientSpecOptions for client side options --- cmd/fleetctl/apply.go | 8 +++++--- cmd/fleetctl/preview.go | 2 +- pkg/spec/gitops_test.go | 2 ++ server/fleet/app.go | 13 ++++++++++--- server/service/client.go | 18 ++++++++++-------- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/cmd/fleetctl/apply.go b/cmd/fleetctl/apply.go index d44a5762b512..103f7515e101 100644 --- a/cmd/fleetctl/apply.go +++ b/cmd/fleetctl/apply.go @@ -69,9 +69,11 @@ func applyCommand() *cli.Command { fmt.Fprintf(c.App.Writer, format, a...) } - opts := fleet.ApplySpecOptions{ - Force: flForce, - DryRun: flDryRun, + opts := fleet.ApplyClientSpecOptions{ + ApplySpecOptions: fleet.ApplySpecOptions{ + Force: flForce, + DryRun: flDryRun, + }, } if policiesTeamName := c.String("policies-team"); policiesTeamName != "" { opts.TeamForPolicies = policiesTeamName diff --git a/cmd/fleetctl/preview.go b/cmd/fleetctl/preview.go index 5b61b1d62f71..c971e106535a 100644 --- a/cmd/fleetctl/preview.go +++ b/cmd/fleetctl/preview.go @@ -342,7 +342,7 @@ Use the stop and reset subcommands to manage the server and dependencies once st } // this only applies standard queries, the base directory is not used, // so pass in the current working directory. - _, err = client.ApplyGroup(c.Context, specs, ".", logf, fleet.ApplySpecOptions{}) + _, err = client.ApplyGroup(c.Context, specs, ".", logf, fleet.ApplyClientSpecOptions{}) if err != nil { return err } diff --git a/pkg/spec/gitops_test.go b/pkg/spec/gitops_test.go index 565806475eb7..5a8010a28f6d 100644 --- a/pkg/spec/gitops_test.go +++ b/pkg/spec/gitops_test.go @@ -104,6 +104,8 @@ func TestValidGitOpsYaml(t *testing.T) { os.Unsetenv(k) } }) + } else { + t.Parallel() } gitops, err := GitOpsFromFile(test.filePath, "./testdata") diff --git a/server/fleet/app.go b/server/fleet/app.go index bb737fb8893e..412d1bdde355 100644 --- a/server/fleet/app.go +++ b/server/fleet/app.go @@ -1037,9 +1037,6 @@ type ApplySpecOptions struct { DryRun bool // TeamForPolicies is the name of the team to set in policy specs. TeamForPolicies string - // ExpandEnvConfigProfiles enables expansion of environment variables in - // configuration profiles. - ExpandEnvConfigProfiles bool } type ApplyTeamSpecOptions struct { @@ -1047,6 +1044,16 @@ type ApplyTeamSpecOptions struct { DryRunAssumptions *TeamSpecsDryRunAssumptions } +// ApplyClientSpecOptions embeds a ApplySpecOptions and adds additional client +// side configuration. +type ApplyClientSpecOptions struct { + ApplySpecOptions + + // ExpandEnvConfigProfiles enables expansion of environment variables in + // configuration profiles. + ExpandEnvConfigProfiles bool +} + // RawQuery returns the ApplySpecOptions url-encoded for use in an URL's // query string parameters. It only sets the parameters that are not the // default values. diff --git a/server/service/client.go b/server/service/client.go index f8dac0f34945..907ae153dbfb 100644 --- a/server/service/client.go +++ b/server/service/client.go @@ -352,7 +352,7 @@ func (c *Client) ApplyGroup( specs *spec.Group, baseDir string, logf func(format string, args ...interface{}), - opts fleet.ApplySpecOptions, + opts fleet.ApplyClientSpecOptions, ) (map[string]uint, error) { logfn := func(format string, args ...interface{}) { if logf != nil { @@ -446,7 +446,7 @@ func (c *Client) ApplyGroup( assumeEnabled = ok && assumeEnabled } } - if err := c.ApplyNoTeamProfiles(fileContents, opts, assumeEnabled); err != nil { + if err := c.ApplyNoTeamProfiles(fileContents, opts.ApplySpecOptions, assumeEnabled); err != nil { return nil, fmt.Errorf("applying custom settings: %w", err) } } @@ -488,11 +488,11 @@ func (c *Client) ApplyGroup( Name: filepath.Base(f), } } - if err := c.ApplyNoTeamScripts(scriptPayloads, opts); err != nil { + if err := c.ApplyNoTeamScripts(scriptPayloads, opts.ApplySpecOptions); err != nil { return nil, fmt.Errorf("applying custom settings: %w", err) } } - if err := c.ApplyAppConfig(specs.AppConfig, opts); err != nil { + if err := c.ApplyAppConfig(specs.AppConfig, opts.ApplySpecOptions); err != nil { return nil, fmt.Errorf("applying fleet config: %w", err) } if opts.DryRun { @@ -570,7 +570,7 @@ func (c *Client) ApplyGroup( // non-existing team gets created. var err error teamOpts := fleet.ApplyTeamSpecOptions{ - ApplySpecOptions: opts, + ApplySpecOptions: opts.ApplySpecOptions, DryRunAssumptions: specs.TeamsDryRunAssumptions, } teamIDsByName, err = c.ApplyTeams(specs.Teams, teamOpts) @@ -607,7 +607,7 @@ func (c *Client) ApplyGroup( } if len(tmScriptsPayloads) > 0 { for tmName, scripts := range tmScriptsPayloads { - if err := c.ApplyTeamScripts(tmName, scripts, opts); err != nil { + if err := c.ApplyTeamScripts(tmName, scripts, opts.ApplySpecOptions); err != nil { return nil, fmt.Errorf("applying scripts for team %q: %w", tmName, err) } } @@ -1101,8 +1101,10 @@ func (c *Client) DoGitOps( } // Apply org settings, scripts, enroll secrets, and controls - teamIDsByName, err := c.ApplyGroup(ctx, &group, baseDir, logf, fleet.ApplySpecOptions{ - DryRun: dryRun, + teamIDsByName, err := c.ApplyGroup(ctx, &group, baseDir, logf, fleet.ApplyClientSpecOptions{ + ApplySpecOptions: fleet.ApplySpecOptions{ + DryRun: dryRun, + }, ExpandEnvConfigProfiles: true, }) if err != nil { From 384ca6367b9f4c4c31ad447d0271ab7f6d824682 Mon Sep 17 00:00:00 2001 From: Lucas Rodriguez Date: Fri, 24 May 2024 15:46:24 -0300 Subject: [PATCH 6/6] Fix after merge main --- server/service/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/service/client.go b/server/service/client.go index 5343e1ded975..e72cb3711dd0 100644 --- a/server/service/client.go +++ b/server/service/client.go @@ -673,7 +673,7 @@ func (c *Client) ApplyGroup( } if len(tmSoftwarePayloads) > 0 { for tmName, software := range tmSoftwarePayloads { - if err := c.ApplyTeamSoftwareInstallers(tmName, software, opts); err != nil { + if err := c.ApplyTeamSoftwareInstallers(tmName, software, opts.ApplySpecOptions); err != nil { return nil, fmt.Errorf("applying software installers for team %q: %w", tmName, err) } }