mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
Add more deprecation logs and mute by default (#40305)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #40122 # Details * Adds deprecation warnings to `fleetctl apply` * Adds alias conflict errors (i.e. using both new and deprecated keys in the same spec) to `fleetctl apply` * Adds logic around all deprecated field warnings to check the topic first * Disables deprecation warnings by default for `fleet serve`, `fleetctl gitops` and `fleetctl apply` * Enables deprecation warnings for dogfood via env var To turn on warnings: * In `fleet serve`, use either `--logging_enable_topics=deprecated-field-names` or the `FLEET_LOGGING_ENABLE_TOPICS=deprecated-field-names` env var * In `fleetctl gitops` / `fleetctl apply` use either `--enable-log-topics=deprecated-field-names` or `FLEET_ENABLE_LOG_TOPICS=deprecated-field-names` # Checklist for submitter If some of the following don't apply, delete the relevant line. - [X] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [X] Added/updated automated tests - [X] QA'd all new/changed functionality manually tested in `fleetctl apply`, `fleet serve` and `fleet gitops` that warnings are suppressed by default and added when the appropriate env var or CLI option is used
This commit is contained in:
parent
e08318c9ab
commit
772fb12cf5
15 changed files with 230 additions and 38 deletions
|
|
@ -240,6 +240,7 @@ the way that the Fleet server works.
|
|||
//
|
||||
// For example:
|
||||
// platform_logging.DisableTopic("deprecated-api-keys")
|
||||
platform_logging.DisableTopic(platform_logging.DeprecatedFieldTopic)
|
||||
|
||||
// Apply log topic overrides from config. Enables run first, then
|
||||
// disables, so disable wins on conflict.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/fleetdm/fleet/v4/pkg/spec"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/fleetdm/fleet/v4/server/platform/logging"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
|
@ -49,8 +50,17 @@ func applyCommand() *cli.Command {
|
|||
configFlag(),
|
||||
contextFlag(),
|
||||
debugFlag(),
|
||||
enableLogTopicsFlag(),
|
||||
disableLogTopicsFlag(),
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
// Disable field deprecation warnings for now.
|
||||
// TODO - remove this in future release to unleash warnings.
|
||||
logging.DisableTopic(logging.DeprecatedFieldTopic)
|
||||
|
||||
// Apply log topic overrides from flags/env vars.
|
||||
applyLogTopicFlags(c)
|
||||
|
||||
if flFilename == "" {
|
||||
return errors.New("-f must be specified")
|
||||
}
|
||||
|
|
@ -72,14 +82,17 @@ func applyCommand() *cli.Command {
|
|||
return fmt.Errorf("Invalid file extension %s: only .yml or .yaml files can be applied", ext)
|
||||
}
|
||||
|
||||
specs, err := spec.GroupFromBytes(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logf := func(format string, a ...interface{}) {
|
||||
fmt.Fprintf(c.App.Writer, format, a...)
|
||||
}
|
||||
|
||||
specs, err := spec.GroupFromBytes(b, spec.GroupFromBytesOpts{
|
||||
LogFn: logf,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opts := fleet.ApplyClientSpecOptions{
|
||||
ApplySpecOptions: fleet.ApplySpecOptions{
|
||||
Force: flForce,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/fleetdm/fleet/v4/server/mock"
|
||||
mdmmock "github.com/fleetdm/fleet/v4/server/mock/mdm"
|
||||
nanodep_mock "github.com/fleetdm/fleet/v4/server/mock/nanodep"
|
||||
"github.com/fleetdm/fleet/v4/server/platform/logging"
|
||||
"github.com/fleetdm/fleet/v4/server/ptr"
|
||||
"github.com/fleetdm/fleet/v4/server/service"
|
||||
"github.com/google/uuid"
|
||||
|
|
@ -122,6 +123,7 @@ spec:
|
|||
}
|
||||
|
||||
func TestApplyUserRolesDeprecated(t *testing.T) {
|
||||
t.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
_, ds := testing_utils.RunServerWithMockedDS(t)
|
||||
|
||||
ds.ListUsersFunc = func(ctx context.Context, opt fleet.UserListOptions) ([]*fleet.User, error) {
|
||||
|
|
@ -175,9 +177,29 @@ spec:
|
|||
team: team1
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "[+] applied user roles\n", RunAppForTest(t, []string{"apply", "-f", tmpFile.Name()}))
|
||||
expected := "[!] In user_roles: `team` is deprecated, please use `fleet` instead.\n[!] In user_roles: `teams` is deprecated, please use `fleets` instead.\n[+] applied user roles\n"
|
||||
assert.Equal(t, expected, RunAppForTest(t, []string{"apply", "-f", tmpFile.Name()}))
|
||||
require.Len(t, userRoleSpecList[1].Teams, 1)
|
||||
assert.Equal(t, fleet.RoleMaintainer, userRoleSpecList[1].Teams[0].Role)
|
||||
|
||||
_, err = tmpFile.WriteString(`
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: user_roles
|
||||
spec:
|
||||
roles:
|
||||
admin1@example.com:
|
||||
global_role: admin
|
||||
teams: null
|
||||
admin2@example.com:
|
||||
global_role: null
|
||||
teams:
|
||||
- role: maintainer
|
||||
team: team1
|
||||
fleet: team1
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
RunAppCheckErr(t, []string{"apply", "-f", tmpFile.Name()}, "in user_roles spec: Conflicting field names: cannot specify both `team` (deprecated) and `fleet` in the same request")
|
||||
}
|
||||
|
||||
func TestApplyTeamSpecs(t *testing.T) {
|
||||
|
|
@ -663,6 +685,8 @@ func writeTmpJSON(t *testing.T, v any) string {
|
|||
}
|
||||
|
||||
func TestApplyAppConfig(t *testing.T) {
|
||||
t.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
|
||||
license := &fleet.LicenseInfo{Tier: fleet.TierPremium, Expiration: time.Now().Add(24 * time.Hour)}
|
||||
_, ds := testing_utils.RunServerWithMockedDS(t, &service.TestServerOpts{License: license})
|
||||
|
||||
|
|
@ -788,6 +812,33 @@ spec:
|
|||
// agent options were not modified, since they were not provided
|
||||
assert.Equal(t, string(defaultAgentOpts), string(*savedAppConfig.AgentOptions))
|
||||
|
||||
// Test key rewriting (deprecated -> new)
|
||||
name = writeTmpYml(t, `---
|
||||
apiVersion: v1
|
||||
kind: config
|
||||
spec:
|
||||
server_settings:
|
||||
report_cap: 100
|
||||
`)
|
||||
|
||||
assert.Equal(t, "[+] applied fleet config\n", RunAppForTest(t, []string{"apply", "-f", name}))
|
||||
require.NotNil(t, savedAppConfig)
|
||||
assert.Equal(t, 100, savedAppConfig.ServerSettings.QueryReportCap)
|
||||
|
||||
// Test deprecation warnings
|
||||
expected := "[!] In config: `query_report_cap` is deprecated, please use `report_cap` instead.\n[+] applied fleet config\n"
|
||||
name = writeTmpYml(t, `---
|
||||
apiVersion: v1
|
||||
kind: config
|
||||
spec:
|
||||
server_settings:
|
||||
query_report_cap: 200
|
||||
`)
|
||||
|
||||
assert.Equal(t, expected, RunAppForTest(t, []string{"apply", "-f", name}))
|
||||
require.NotNil(t, savedAppConfig)
|
||||
assert.Equal(t, 200, savedAppConfig.ServerSettings.QueryReportCap)
|
||||
|
||||
name = writeTmpYml(t, `---
|
||||
apiVersion: v1
|
||||
kind: config
|
||||
|
|
@ -840,6 +891,20 @@ spec:
|
|||
assert.Equal(t, newMDMSettings, savedAppConfig.MDM)
|
||||
}
|
||||
|
||||
func TestApplyAppConfigAliasConfict(t *testing.T) {
|
||||
// Test conflict error
|
||||
name := writeTmpYml(t, `---
|
||||
apiVersion: v1
|
||||
kind: config
|
||||
spec:
|
||||
server_settings:
|
||||
query_report_cap: 200
|
||||
report_cap: 200
|
||||
`)
|
||||
|
||||
RunAppCheckErr(t, []string{"apply", "-f", name}, "in config spec: Conflicting field names: cannot specify both `query_report_cap` (deprecated) and `report_cap` in the same request")
|
||||
}
|
||||
|
||||
func TestApplyAppConfigDryRunIssue(t *testing.T) {
|
||||
// reproduces the bug fixed by https://github.com/fleetdm/fleet/pull/8194
|
||||
_, ds := testing_utils.RunServerWithMockedDS(t)
|
||||
|
|
@ -1198,11 +1263,11 @@ kind: pack
|
|||
spec:
|
||||
name: osquery_monitoring
|
||||
reports:
|
||||
- query: osquery_version
|
||||
- report: osquery_version
|
||||
name: osquery_version_snapshot
|
||||
interval: 7200
|
||||
snapshot: true
|
||||
- query: osquery_version
|
||||
- report: osquery_version
|
||||
name: osquery_version_differential
|
||||
interval: 7200
|
||||
`
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/fleetdm/fleet/v4/pkg/spec"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/fleetdm/fleet/v4/server/platform/logging"
|
||||
"github.com/fleetdm/fleet/v4/server/ptr"
|
||||
"github.com/fleetdm/fleet/v4/server/service"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
|
@ -80,6 +81,10 @@ func gitopsCommand() *cli.Command {
|
|||
disableLogTopicsFlag(),
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
// Disable field deprecation warnings for now.
|
||||
// TODO - remove this in future release to unleash warnings.
|
||||
logging.DisableTopic(logging.DeprecatedFieldTopic)
|
||||
|
||||
logf := func(format string, a ...interface{}) {
|
||||
_, _ = fmt.Fprintf(c.App.Writer, format, a...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/fleetdm/fleet/v4/server/datastore/mysql"
|
||||
"github.com/fleetdm/fleet/v4/server/dev_mode"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/fleetdm/fleet/v4/server/platform/logging"
|
||||
"github.com/fleetdm/fleet/v4/server/ptr"
|
||||
"github.com/fleetdm/fleet/v4/server/test"
|
||||
"github.com/go-git/go-git/v5"
|
||||
|
|
@ -30,6 +31,7 @@ import (
|
|||
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) TestDeleteMacOSSetupDeprecated() {
|
||||
t := s.T()
|
||||
t.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
|
||||
user := s.createGitOpsUser(t)
|
||||
fleetctlConfig := s.createFleetctlConfig(t, user)
|
||||
|
|
@ -147,6 +149,8 @@ team_settings:
|
|||
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) TestUnsetConfigurationProfileLabelsDeprecated() {
|
||||
t := s.T()
|
||||
t.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
user := s.createGitOpsUser(t)
|
||||
|
|
@ -270,6 +274,8 @@ team_settings:
|
|||
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) TestUnsetSoftwareInstallerLabelsDeprecated() {
|
||||
t := s.T()
|
||||
t.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
user := s.createGitOpsUser(t)
|
||||
|
|
@ -416,6 +422,8 @@ team_settings:
|
|||
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) TestNoTeamWebhookSettingsDeprecated() {
|
||||
t := s.T()
|
||||
t.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
user := s.createGitOpsUser(t)
|
||||
|
|
@ -681,6 +689,8 @@ team_settings:
|
|||
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) TestMacOSSetupDeprecated() {
|
||||
t := s.T()
|
||||
t.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
user := s.createGitOpsUser(t)
|
||||
|
|
@ -820,6 +830,8 @@ team_settings:
|
|||
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) TestIPASoftwareInstallersDeprecated() {
|
||||
t := s.T()
|
||||
t.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
user := s.createGitOpsUser(t)
|
||||
|
|
@ -1033,6 +1045,8 @@ team_settings:
|
|||
// are properly applied via GitOps.
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) TestGitOpsSoftwareDisplayNameDeprecated() {
|
||||
t := s.T()
|
||||
t.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
user := s.createGitOpsUser(t)
|
||||
|
|
@ -1151,6 +1165,8 @@ team_settings:
|
|||
// and fleet maintained apps are properly applied via GitOps.
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) TestGitOpsSoftwareIconsDeprecated() {
|
||||
t := s.T()
|
||||
t.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
user := s.createGitOpsUser(t)
|
||||
|
|
@ -1336,6 +1352,8 @@ team_settings:
|
|||
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) TestGitOpsTeamLabelsDeprecated() {
|
||||
t := s.T()
|
||||
t.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
user := s.createGitOpsUser(t)
|
||||
|
|
@ -1527,6 +1545,8 @@ labels:
|
|||
// Multiple repos are simulated by copying over the example repository multiple times.
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) TestGitOpsTeamLabelsMultipleReposDeprecated() {
|
||||
t := s.T()
|
||||
t.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
type repoSetup struct {
|
||||
|
|
|
|||
|
|
@ -191,7 +191,8 @@ func (s *enterpriseIntegrationGitopsTestSuite) assertDryRunOutput(t *testing.T,
|
|||
s.assertDryRunOutputWithDeprecation(t, output, false)
|
||||
}
|
||||
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) assertDryRunOutputWithDeprecation(t *testing.T, output string, allowDeprecation bool) {
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) assertDryRunOutputWithDeprecation(t *testing.T, output string, expectDeprecation bool) {
|
||||
var sawDeprecation bool
|
||||
allowedVerbs := []string{
|
||||
"moved",
|
||||
"deleted",
|
||||
|
|
@ -204,13 +205,17 @@ func (s *enterpriseIntegrationGitopsTestSuite) assertDryRunOutputWithDeprecation
|
|||
pattern := fmt.Sprintf("\\[([+\\-!])] would've (%s)", strings.Join(allowedVerbs, "|"))
|
||||
reg := regexp.MustCompile(pattern)
|
||||
for line := range strings.SplitSeq(output, "\n") {
|
||||
if allowDeprecation && line != "" && strings.Contains(line, "is deprecated") {
|
||||
if expectDeprecation && line != "" && strings.Contains(line, "is deprecated") {
|
||||
sawDeprecation = true
|
||||
continue
|
||||
}
|
||||
if line != "" && !strings.Contains(line, "succeeded") {
|
||||
assert.Regexp(t, reg, line, "on dry run")
|
||||
}
|
||||
}
|
||||
if expectDeprecation {
|
||||
assert.True(t, sawDeprecation, "expected to see deprecation warning in dry run output")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *enterpriseIntegrationGitopsTestSuite) assertRealRunOutput(t *testing.T, output string) {
|
||||
|
|
@ -244,6 +249,9 @@ func (s *enterpriseIntegrationGitopsTestSuite) assertRealRunOutputWithDeprecatio
|
|||
// 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() {
|
||||
os.Setenv("FLEET_ENABLE_LOG_TOPICS", logging.DeprecatedFieldTopic)
|
||||
defer os.Unsetenv("FLEET_ENABLE_LOG_TOPICS")
|
||||
|
||||
t := s.T()
|
||||
|
||||
user := s.createGitOpsUser(t)
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ locals {
|
|||
OTEL_EXPORTER_OTLP_ENDPOINT = "https://otlp.signoz.dogfood.fleetdm.com"
|
||||
# FLEET_LOGGING_TRACING_ENABLED = "true"
|
||||
# FLEET_LOGGING_TRACING_TYPE = "elasticapm"
|
||||
FLEET_LOGGING_ENABLE_TOPICS = "deprecated-field-names"
|
||||
FLEET_MYSQL_MAX_OPEN_CONNS = "10"
|
||||
FLEET_MYSQL_READ_REPLICA_MAX_OPEN_CONNS = "10"
|
||||
FLEET_VULNERABILITIES_DATABASES_PATH = "/home/fleet"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package spec
|
|||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/platform/logging"
|
||||
)
|
||||
|
||||
// DeprecatedKeyMapping defines a mapping from an old YAML key path to a new one.
|
||||
|
|
@ -122,7 +124,9 @@ func migrateLeafKey(data map[string]any, oldKey, newKey, fullOldPath, fullNewPat
|
|||
|
||||
// Log deprecation warning
|
||||
if logFn != nil {
|
||||
logFn("[!] '%s' is deprecated; use '%s' instead\n", fullOldPath, fullNewPath)
|
||||
if logging.TopicEnabled(logging.DeprecatedFieldTopic) {
|
||||
logFn("[!] '%s' is deprecated; use '%s' instead\n", fullOldPath, fullNewPath)
|
||||
}
|
||||
}
|
||||
|
||||
// Copy value to new key and remove old key
|
||||
|
|
|
|||
|
|
@ -11,10 +11,12 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/fleetdm/fleet/v4/server/platform/endpointer"
|
||||
"github.com/fleetdm/fleet/v4/server/platform/logging"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
|
@ -48,20 +50,30 @@ type Metadata struct {
|
|||
// rewriteNewToOldKeys uses RewriteDeprecatedKeys to rewrite new (renameto)
|
||||
// key names back to old (json tag) names so that structs can be unmarshaled
|
||||
// correctly when input uses the new key names.
|
||||
func rewriteNewToOldKeys(raw json.RawMessage, target any) json.RawMessage {
|
||||
func rewriteNewToOldKeys(raw json.RawMessage, target any) (json.RawMessage, map[string]string, error) {
|
||||
rules := endpointer.ExtractAliasRules(target)
|
||||
if len(rules) == 0 {
|
||||
return raw
|
||||
return raw, nil, nil
|
||||
}
|
||||
result, err := endpointer.RewriteDeprecatedKeys(raw, rules)
|
||||
result, deprecatedKeysMap, err := endpointer.RewriteDeprecatedKeys(raw, rules)
|
||||
if err != nil {
|
||||
return raw // fall back to original on error
|
||||
return nil, nil, err // fall back to original on error
|
||||
}
|
||||
return result
|
||||
return result, deprecatedKeysMap, nil
|
||||
}
|
||||
|
||||
type GroupFromBytesOpts struct {
|
||||
LogFn func(format string, args ...any)
|
||||
}
|
||||
|
||||
// GroupFromBytes parses a Group from concatenated YAML specs.
|
||||
func GroupFromBytes(b []byte) (*Group, error) {
|
||||
func GroupFromBytes(b []byte, options ...GroupFromBytesOpts) (*Group, error) {
|
||||
// Get optional logger.
|
||||
var logFn func(format string, args ...any)
|
||||
if len(options) > 0 {
|
||||
logFn = options[0].LogFn
|
||||
}
|
||||
|
||||
specs := &Group{}
|
||||
for _, specItem := range SplitYaml(string(b)) {
|
||||
var s Metadata
|
||||
|
|
@ -78,9 +90,14 @@ func GroupFromBytes(b []byte) (*Group, error) {
|
|||
return nil, fmt.Errorf(`Missing required fields ("spec") on provided %q configuration.`, s.Kind)
|
||||
}
|
||||
|
||||
var deprecatedKeysMap map[string]string
|
||||
switch kind {
|
||||
case fleet.QueryKind:
|
||||
s.Spec = rewriteNewToOldKeys(s.Spec, fleet.QuerySpec{})
|
||||
var err error
|
||||
s.Spec, deprecatedKeysMap, err = rewriteNewToOldKeys(s.Spec, fleet.QuerySpec{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("in %s spec: %w", kind, err)
|
||||
}
|
||||
var querySpec *fleet.QuerySpec
|
||||
if err := yaml.Unmarshal(s.Spec, &querySpec); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling %s spec: %w", kind, err)
|
||||
|
|
@ -88,7 +105,11 @@ func GroupFromBytes(b []byte) (*Group, error) {
|
|||
specs.Queries = append(specs.Queries, querySpec)
|
||||
|
||||
case fleet.PackKind:
|
||||
s.Spec = rewriteNewToOldKeys(s.Spec, fleet.PackSpec{})
|
||||
var err error
|
||||
s.Spec, deprecatedKeysMap, err = rewriteNewToOldKeys(s.Spec, fleet.PackSpec{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("in %s spec: %w", kind, err)
|
||||
}
|
||||
var packSpec *fleet.PackSpec
|
||||
if err := yaml.Unmarshal(s.Spec, &packSpec); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling %s spec: %w", kind, err)
|
||||
|
|
@ -96,7 +117,11 @@ func GroupFromBytes(b []byte) (*Group, error) {
|
|||
specs.Packs = append(specs.Packs, packSpec)
|
||||
|
||||
case fleet.LabelKind:
|
||||
s.Spec = rewriteNewToOldKeys(s.Spec, fleet.LabelSpec{})
|
||||
var err error
|
||||
s.Spec, deprecatedKeysMap, err = rewriteNewToOldKeys(s.Spec, fleet.LabelSpec{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("in %s spec: %w", kind, err)
|
||||
}
|
||||
var labelSpec *fleet.LabelSpec
|
||||
if err := yaml.Unmarshal(s.Spec, &labelSpec); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling %s spec: %w", kind, err)
|
||||
|
|
@ -104,7 +129,11 @@ func GroupFromBytes(b []byte) (*Group, error) {
|
|||
specs.Labels = append(specs.Labels, labelSpec)
|
||||
|
||||
case fleet.PolicyKind:
|
||||
s.Spec = rewriteNewToOldKeys(s.Spec, fleet.PolicySpec{})
|
||||
var err error
|
||||
s.Spec, deprecatedKeysMap, err = rewriteNewToOldKeys(s.Spec, fleet.PolicySpec{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("in %s spec: %w", kind, err)
|
||||
}
|
||||
var policySpec *fleet.PolicySpec
|
||||
if err := yaml.Unmarshal(s.Spec, &policySpec); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling %s spec: %w", kind, err)
|
||||
|
|
@ -112,6 +141,11 @@ func GroupFromBytes(b []byte) (*Group, error) {
|
|||
specs.Policies = append(specs.Policies, policySpec)
|
||||
|
||||
case fleet.AppConfigKind:
|
||||
var err error
|
||||
s.Spec, deprecatedKeysMap, err = rewriteNewToOldKeys(s.Spec, fleet.AppConfig{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("in %s spec: %w", kind, err)
|
||||
}
|
||||
if specs.AppConfig != nil {
|
||||
return nil, errors.New("config defined twice in the same file")
|
||||
}
|
||||
|
|
@ -134,7 +168,11 @@ func GroupFromBytes(b []byte) (*Group, error) {
|
|||
specs.EnrollSecret = enrollSecretSpec
|
||||
|
||||
case fleet.UserRolesKind:
|
||||
s.Spec = rewriteNewToOldKeys(s.Spec, fleet.UsersRoleSpec{})
|
||||
var err error
|
||||
s.Spec, deprecatedKeysMap, err = rewriteNewToOldKeys(s.Spec, fleet.UsersRoleSpec{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("in %s spec: %w", kind, err)
|
||||
}
|
||||
var userRoleSpec *fleet.UsersRoleSpec
|
||||
if err := yaml.Unmarshal(s.Spec, &userRoleSpec); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling %s spec: %w", kind, err)
|
||||
|
|
@ -154,6 +192,17 @@ func GroupFromBytes(b []byte) (*Group, error) {
|
|||
default:
|
||||
return nil, fmt.Errorf("unknown kind %q", s.Kind)
|
||||
}
|
||||
|
||||
if logFn != nil && len(deprecatedKeysMap) > 0 && logging.TopicEnabled(logging.DeprecatedFieldTopic) {
|
||||
oldKeys := make([]string, 0, len(deprecatedKeysMap))
|
||||
for oldKey := range deprecatedKeysMap {
|
||||
oldKeys = append(oldKeys, oldKey)
|
||||
}
|
||||
sort.Strings(oldKeys)
|
||||
for _, oldKey := range oldKeys {
|
||||
logFn(fmt.Sprintf("[!] In %s: `%s` is deprecated, please use `%s` instead.\n", kind, oldKey, deprecatedKeysMap[oldKey]))
|
||||
}
|
||||
}
|
||||
}
|
||||
return specs, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/fleetdm/fleet/v4/server/contexts/license"
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/logging"
|
||||
platform_http "github.com/fleetdm/fleet/v4/server/platform/http"
|
||||
platform_logging "github.com/fleetdm/fleet/v4/server/platform/logging"
|
||||
"github.com/fleetdm/fleet/v4/server/platform/middleware/authzcheck"
|
||||
"github.com/fleetdm/fleet/v4/server/platform/middleware/ratelimit"
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
|
|
@ -304,11 +305,13 @@ func DecodeQueryTagValue(r *http.Request, fp fieldPair, customDecoder DomainQuer
|
|||
}
|
||||
}
|
||||
// Log deprecation warning - the old name was used.
|
||||
logging.WithLevel(ctx, slog.LevelWarn)
|
||||
logging.WithExtras(ctx,
|
||||
"deprecated_param", queryTagValue,
|
||||
"deprecation_warning", fmt.Sprintf("'%s' is deprecated, use '%s' instead", queryTagValue, renameTo),
|
||||
)
|
||||
if platform_logging.TopicEnabled(platform_logging.DeprecatedFieldTopic) {
|
||||
logging.WithLevel(ctx, slog.LevelWarn)
|
||||
logging.WithExtras(ctx,
|
||||
"deprecated_param", queryTagValue,
|
||||
"deprecation_warning", fmt.Sprintf("'%s' is deprecated, use '%s' instead", queryTagValue, renameTo),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if renameTo, hasRenameTo := fp.Sf.Tag.Lookup("renameto"); hasRenameTo {
|
||||
renameTo, _, err = ParseTag(renameTo)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ type AliasConflictError struct {
|
|||
}
|
||||
|
||||
func (e *AliasConflictError) Error() string {
|
||||
return fmt.Sprintf("Conflicting field names: cannot specify both %q (deprecated) and %q in the same request", e.Old, e.New)
|
||||
return fmt.Sprintf("Conflicting field names: cannot specify both `%s` (deprecated) and `%s` in the same request", e.Old, e.New)
|
||||
}
|
||||
|
||||
// AliasRule defines a key-rename rule: the deprecated (old) key name and its
|
||||
|
|
@ -119,9 +119,9 @@ func (r *JSONKeyRewriteReader) Read(p []byte) (int, error) {
|
|||
// decoded into a struct with `renameto` tags — the rewriter in MakeDecoder
|
||||
// won't have seen the inner fields, so this function can be called before the
|
||||
// deferred unmarshal.
|
||||
func RewriteDeprecatedKeys(data []byte, rules []AliasRule) ([]byte, error) {
|
||||
func RewriteDeprecatedKeys(data []byte, rules []AliasRule) ([]byte, map[string]string, error) {
|
||||
if len(rules) == 0 || len(data) == 0 {
|
||||
return data, nil
|
||||
return data, nil, nil
|
||||
}
|
||||
oldIdx := make(map[string]AliasRule, len(rules))
|
||||
newIdx := make(map[string]AliasRule, len(rules))
|
||||
|
|
@ -136,9 +136,13 @@ func RewriteDeprecatedKeys(data []byte, rules []AliasRule) ([]byte, error) {
|
|||
}
|
||||
var buf bytes.Buffer
|
||||
if err := rw.rewrite(bytes.NewReader(data), &buf); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
deprecatedKeysMap := make(map[string]string, len(rw.usedDeprecated))
|
||||
for k := range rw.usedDeprecated {
|
||||
deprecatedKeysMap[k] = rw.oldKeyIndex[k].NewKey
|
||||
}
|
||||
return buf.Bytes(), deprecatedKeysMap, nil
|
||||
}
|
||||
|
||||
// rewrite reads tokens from src, rewrites deprecated keys, checks for alias
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ func (h *TopicFilterHandler) Handle(ctx context.Context, r slog.Record) error {
|
|||
if topic != "" && !TopicEnabled(topic) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return h.base.Handle(ctx, r)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import (
|
|||
"sync"
|
||||
)
|
||||
|
||||
const DeprecatedFieldTopic = "deprecated-field-names"
|
||||
|
||||
// disabledTopics tracks which topics have been explicitly disabled.
|
||||
// Topics are enabled by default — only topics in this map are disabled.
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/fleetdm/fleet/v4/server/platform/endpointer"
|
||||
"github.com/fleetdm/fleet/v4/server/platform/logging"
|
||||
"github.com/fleetdm/fleet/v4/server/version"
|
||||
"github.com/go-kit/log/level"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
|
|
@ -367,12 +368,24 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle
|
|||
// json.RawMessage and wasn't processed by the request decoder's rewriter.
|
||||
if rules := endpointer.ExtractAliasRules(fleet.AppConfig{}); len(rules) > 0 {
|
||||
var err error
|
||||
if p, err = endpointer.RewriteDeprecatedKeys(p, rules); err != nil {
|
||||
var deprecatedKeysMap map[string]string
|
||||
if p, deprecatedKeysMap, err = endpointer.RewriteDeprecatedKeys(p, rules); err != nil {
|
||||
msg := "failed to decode app config"
|
||||
// If it's an alias conflict error, return a user-friendly message about deprecated fields.
|
||||
var aliasConflictErr *endpointer.AliasConflictError
|
||||
if errors.As(err, &aliasConflictErr) {
|
||||
msg = err.Error()
|
||||
}
|
||||
return nil, ctxerr.Wrap(ctx, &fleet.BadRequestError{
|
||||
Message: "failed to decode app config",
|
||||
Message: msg,
|
||||
InternalErr: err,
|
||||
})
|
||||
}
|
||||
if len(deprecatedKeysMap) > 0 {
|
||||
for oldKey, newKey := range deprecatedKeysMap {
|
||||
svc.logger.WarnContext(ctx, fmt.Sprintf("App config: `%s` is deprecated, please use `%s` instead", oldKey, newKey), "log_topic", logging.DeprecatedFieldTopic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
invalid := &fleet.InvalidArgumentError{}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
eu "github.com/fleetdm/fleet/v4/server/platform/endpointer"
|
||||
platform_http "github.com/fleetdm/fleet/v4/server/platform/http"
|
||||
platform_logging "github.com/fleetdm/fleet/v4/server/platform/logging"
|
||||
"github.com/fleetdm/fleet/v4/server/service/middleware/auth"
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
kithttp "github.com/go-kit/kit/transport/http"
|
||||
|
|
@ -320,11 +321,13 @@ func parseMultipartForm(ctx context.Context, r *http.Request, maxMemory int64) e
|
|||
teamIDs, teamIDPresent := r.Form["team_id"]
|
||||
if teamIDPresent && len(teamIDs) > 0 {
|
||||
teamID := teamIDs[0]
|
||||
logging.WithExtras(ctx,
|
||||
"deprecated_param", "team_id",
|
||||
"deprecation_warning", "'team_id' is deprecated, use 'fleet_id' instead",
|
||||
)
|
||||
logging.WithLevel(ctx, slog.LevelWarn)
|
||||
if platform_logging.TopicEnabled(platform_logging.DeprecatedFieldTopic) {
|
||||
logging.WithExtras(ctx,
|
||||
"deprecated_param", "team_id",
|
||||
"deprecation_warning", "'team_id' is deprecated, use 'fleet_id' instead",
|
||||
)
|
||||
logging.WithLevel(ctx, slog.LevelWarn)
|
||||
}
|
||||
r.Form.Set("fleet_id", teamID)
|
||||
r.Form.Del("team_id")
|
||||
r.MultipartForm.Value["fleet_id"] = []string{teamID}
|
||||
|
|
|
|||
Loading…
Reference in a new issue