diff --git a/cmd/fleetctl/fleetctl/gitops_test.go b/cmd/fleetctl/fleetctl/gitops_test.go index 9511117454..d2859ff861 100644 --- a/cmd/fleetctl/fleetctl/gitops_test.go +++ b/cmd/fleetctl/fleetctl/gitops_test.go @@ -716,6 +716,121 @@ software: } } +// TestGitOpsSecretsAppliedOnFreeTierDespiteException verifies that on free tier, secrets +// in a GitOps file are applied even when the server has GitOpsExceptions.Secrets=true +// (the default for new installs). Free-tier users can't toggle the exception off, so +// the exception flag must not cause secrets to be silently dropped. +func TestGitOpsSecretsAppliedOnFreeTierDespiteException(t *testing.T) { + // Cannot run t.Parallel() because it sets environment variables + license := &fleet.LicenseInfo{Tier: fleet.TierFree} + _, ds := testing_utils.RunServerWithMockedDS( + t, &service.TestServerOpts{ + License: license, + KeyValueStore: testing_utils.NewMemKeyValueStore(), + }, + ) + setupEmptyGitOpsMocks(ds) + + // Server returns Exceptions.Secrets=true (the default for new installs). + ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) { + return &fleet.AppConfig{ + GitOpsConfig: fleet.GitOpsConfig{ + Exceptions: fleet.GitOpsExceptions{Secrets: true}, + }, + }, nil + } + ds.SaveAppConfigFunc = func(ctx context.Context, config *fleet.AppConfig) error { return nil } + + var appliedSecrets []*fleet.EnrollSecret + ds.ApplyEnrollSecretsFunc = func(ctx context.Context, teamID *uint, secrets []*fleet.EnrollSecret) error { + appliedSecrets = secrets + return nil + } + ds.IsEnrollSecretAvailableFunc = func(ctx context.Context, secret string, isNew bool, teamID *uint) (bool, error) { + return true, nil + } + + globalFile, err := os.CreateTemp(t.TempDir(), "*.yml") + require.NoError(t, err) + _, err = globalFile.WriteString(` +org_settings: + server_settings: + server_url: https://fleet.example.com + org_info: + org_name: Test + secrets: + - secret: free-tier-secret-value +controls: +policies: +agent_options: +`) + require.NoError(t, err) + + _, err = RunAppNoChecks([]string{"gitops", "-f", globalFile.Name()}) + require.NoError(t, err) + + require.Len(t, appliedSecrets, 1, "secrets in YAML should be applied on free tier even when Exceptions.Secrets=true") + assert.Equal(t, "free-tier-secret-value", appliedSecrets[0].Secret) +} + +// TestGitOpsLabelsAppliedOnFreeTierDespiteException verifies that on free tier, labels +// in a GitOps file are applied even when the server has GitOpsExceptions.Labels=true +// (the value set by the exceptions migration for existing instances). Free-tier users +// can't toggle the exception off, so applying labels via GitOps must still work. +func TestGitOpsLabelsAppliedOnFreeTierDespiteException(t *testing.T) { + // Cannot run t.Parallel() because it sets environment variables + license := &fleet.LicenseInfo{Tier: fleet.TierFree} + _, ds := testing_utils.RunServerWithMockedDS( + t, &service.TestServerOpts{ + License: license, + KeyValueStore: testing_utils.NewMemKeyValueStore(), + }, + ) + setupEmptyGitOpsMocks(ds) + + // Server returns Exceptions.Labels=true (the value set by the migration for existing instances). + ds.AppConfigFunc = func(ctx context.Context) (*fleet.AppConfig, error) { + return &fleet.AppConfig{ + GitOpsConfig: fleet.GitOpsConfig{ + Exceptions: fleet.GitOpsExceptions{Labels: true}, + }, + }, nil + } + ds.SaveAppConfigFunc = func(ctx context.Context, config *fleet.AppConfig) error { return nil } + + var appliedLabelSpecs []*fleet.LabelSpec + ds.ApplyLabelSpecsWithAuthorFunc = func(ctx context.Context, specs []*fleet.LabelSpec, authorID *uint) error { + appliedLabelSpecs = specs + return nil + } + ds.GetLabelSpecsFunc = func(ctx context.Context, filter fleet.TeamFilter) ([]*fleet.LabelSpec, error) { + return nil, nil + } + + globalFile, err := os.CreateTemp(t.TempDir(), "*.yml") + require.NoError(t, err) + _, err = globalFile.WriteString(` +org_settings: + server_settings: + server_url: https://fleet.example.com + org_info: + org_name: Test +labels: + - name: free-tier-label + query: SELECT 1 +controls: +policies: +agent_options: +`) + require.NoError(t, err) + + _, err = RunAppNoChecks([]string{"gitops", "-f", globalFile.Name()}) + require.NoError(t, err) + + require.Len(t, appliedLabelSpecs, 1, "labels in YAML should be applied on free tier even when Exceptions.Labels=true") + assert.Equal(t, "free-tier-label", appliedLabelSpecs[0].Name) +} + // TestGitOpsExceptionsPreserveOmittedKeys verifies that when exceptions are ON, // omitting the excepted keys from YAML preserves existing data. func TestGitOpsExceptionsPreserveOmittedKeys(t *testing.T) { diff --git a/server/service/client.go b/server/service/client.go index f092f95d76..5c085ac327 100644 --- a/server/service/client.go +++ b/server/service/client.go @@ -1940,7 +1940,7 @@ func (c *Client) DoGitOps( if !exceptions.Secrets && !incoming.SecretsPresent { incoming.OrgSettings["secrets"] = make([]*fleet.EnrollSecret, 0) } - if orgSecrets, ok := incoming.OrgSettings["secrets"]; ok && !exceptions.Secrets { + if orgSecrets, ok := incoming.OrgSettings["secrets"]; ok && (!exceptions.Secrets || !appConfig.License.IsPremium()) { group.EnrollSecret = &fleet.EnrollSecretSpec{Secrets: orgSecrets.([]*fleet.EnrollSecret)} } delete(incoming.OrgSettings, "secrets") @@ -2184,7 +2184,7 @@ func (c *Client) DoGitOps( } incoming.TeamSettings["secrets"] = make([]*fleet.EnrollSecret, 0) } - if teamSecrets, ok := incoming.TeamSettings["secrets"]; ok && !exceptions.Secrets { + if teamSecrets, ok := incoming.TeamSettings["secrets"]; ok && (!exceptions.Secrets || !appConfig.License.IsPremium()) { team["secrets"] = teamSecrets }