From 2a81343ad30197b28c7b620645fbec00588bd395 Mon Sep 17 00:00:00 2001 From: Sven Rebhan <36194019+srebhan@users.noreply.github.com> Date: Thu, 7 Dec 2023 20:31:30 +0100 Subject: [PATCH] feat(migrations): Add migration for fieldpass/fielddrop (#14401) --- config/migration.go | 25 ++++ migrations/all/general_metricfilter.go | 5 + migrations/general_metricfilter/migration.go | 112 ++++++++++++++++++ .../general_metricfilter/migration_test.go | 96 +++++++++++++++ .../testcases/deprecated_drop/expected.conf | 3 + .../testcases/deprecated_drop/telegraf.conf | 10 ++ .../deprecated_fielddrop/expected.conf | 3 + .../deprecated_fielddrop/telegraf.conf | 10 ++ .../deprecated_fieldpass/expected.conf | 3 + .../deprecated_fieldpass/telegraf.conf | 10 ++ .../testcases/deprecated_pass/expected.conf | 3 + .../testcases/deprecated_pass/telegraf.conf | 10 ++ .../testcases/merge_all_overlap/expected.conf | 4 + .../testcases/merge_all_overlap/telegraf.conf | 15 +++ .../merge_fieldexclude/expected.conf | 3 + .../merge_fieldexclude/telegraf.conf | 12 ++ .../merge_fieldexclude_overlap/expected.conf | 3 + .../merge_fieldexclude_overlap/telegraf.conf | 12 ++ .../merge_fieldinclude/expected.conf | 3 + .../merge_fieldinclude/telegraf.conf | 12 ++ .../merge_fieldinclude_overlap/expected.conf | 3 + .../merge_fieldinclude_overlap/telegraf.conf | 12 ++ migrations/inputs_disk/migration.go | 30 ++--- migrations/inputs_procstat/migration.go | 47 +++----- migrations/outputs_influxdb/migration.go | 15 +-- migrations/registry.go | 8 ++ migrations/utils.go | 22 ++++ 27 files changed, 424 insertions(+), 67 deletions(-) create mode 100644 migrations/all/general_metricfilter.go create mode 100644 migrations/general_metricfilter/migration.go create mode 100644 migrations/general_metricfilter/migration_test.go create mode 100644 migrations/general_metricfilter/testcases/deprecated_drop/expected.conf create mode 100644 migrations/general_metricfilter/testcases/deprecated_drop/telegraf.conf create mode 100644 migrations/general_metricfilter/testcases/deprecated_fielddrop/expected.conf create mode 100644 migrations/general_metricfilter/testcases/deprecated_fielddrop/telegraf.conf create mode 100644 migrations/general_metricfilter/testcases/deprecated_fieldpass/expected.conf create mode 100644 migrations/general_metricfilter/testcases/deprecated_fieldpass/telegraf.conf create mode 100644 migrations/general_metricfilter/testcases/deprecated_pass/expected.conf create mode 100644 migrations/general_metricfilter/testcases/deprecated_pass/telegraf.conf create mode 100644 migrations/general_metricfilter/testcases/merge_all_overlap/expected.conf create mode 100644 migrations/general_metricfilter/testcases/merge_all_overlap/telegraf.conf create mode 100644 migrations/general_metricfilter/testcases/merge_fieldexclude/expected.conf create mode 100644 migrations/general_metricfilter/testcases/merge_fieldexclude/telegraf.conf create mode 100644 migrations/general_metricfilter/testcases/merge_fieldexclude_overlap/expected.conf create mode 100644 migrations/general_metricfilter/testcases/merge_fieldexclude_overlap/telegraf.conf create mode 100644 migrations/general_metricfilter/testcases/merge_fieldinclude/expected.conf create mode 100644 migrations/general_metricfilter/testcases/merge_fieldinclude/telegraf.conf create mode 100644 migrations/general_metricfilter/testcases/merge_fieldinclude_overlap/expected.conf create mode 100644 migrations/general_metricfilter/testcases/merge_fieldinclude_overlap/telegraf.conf create mode 100644 migrations/utils.go diff --git a/config/migration.go b/config/migration.go index dd0bba8d8..520e1be14 100644 --- a/config/migration.go +++ b/config/migration.go @@ -200,6 +200,31 @@ func ApplyMigrations(data []byte) ([]byte, uint64, error) { applied++ } + // Do general migrations applying to all plugins + for idx, s := range sections { + parts := strings.Split(s.name, ".") + if len(parts) != 2 { + continue + } + log.Printf("D! applying general migrations to plugin %q in line %d...", s.name, s.begin) + category, name := parts[0], parts[1] + for _, migrate := range migrations.GeneralMigrations { + result, msg, err := migrate(category, name, s.content) + if err != nil { + if errors.Is(err, migrations.ErrNotApplicable) { + continue + } + return nil, 0, fmt.Errorf("migrating options of %q (line %d) failed: %w", s.name, s.begin, err) + } + if msg != "" { + log.Printf("I! Plugin %q in line %d: %s", s.name, s.begin, msg) + } + s.raw = bytes.NewBuffer(result) + applied++ + } + sections[idx] = s + } + // Reconstruct the config file from the sections var buf bytes.Buffer for _, s := range sections { diff --git a/migrations/all/general_metricfilter.go b/migrations/all/general_metricfilter.go new file mode 100644 index 000000000..b5ead4b87 --- /dev/null +++ b/migrations/all/general_metricfilter.go @@ -0,0 +1,5 @@ +//go:build !custom || migrations + +package all + +import _ "github.com/influxdata/telegraf/migrations/general_metricfilter" // register migration diff --git a/migrations/general_metricfilter/migration.go b/migrations/general_metricfilter/migration.go new file mode 100644 index 000000000..ee8374ee5 --- /dev/null +++ b/migrations/general_metricfilter/migration.go @@ -0,0 +1,112 @@ +package general_metricfilter + +import ( + "fmt" + + "github.com/influxdata/toml" + "github.com/influxdata/toml/ast" + + "github.com/influxdata/telegraf/internal/choice" + "github.com/influxdata/telegraf/migrations" +) + +// Migration function +func migrate(category, name string, tbl *ast.Table) ([]byte, string, error) { + // Filter options can only be present in inputs, outputs, processors and + // aggregators. Skip everything else... + switch category { + case "inputs", "outputs", "processors", "aggregators": + default: + return nil, "", migrations.ErrNotApplicable + } + + // Decode the old data structure + var plugin map[string]interface{} + if err := toml.UnmarshalTable(tbl, &plugin); err != nil { + return nil, "", err + } + + // Check for deprecated option(s) and migrate them + var applied bool + + // Get the new field settings to be able to merge it with the deprecated + // settings + var fieldinclude []string + if newFieldInclude, found := plugin["fieldinclude"]; found { + var err error + fieldinclude, err = migrations.AsStringSlice(newFieldInclude) + if err != nil { + return nil, "", fmt.Errorf("setting 'fieldinclude': %w", err) + } + } + for _, option := range []string{"pass", "fieldpass"} { + if rawOld, found := plugin[option]; found { + applied = true + + old, err := migrations.AsStringSlice(rawOld) + if err != nil { + return nil, "", fmt.Errorf("setting '%s': %w", option, err) + } + for _, o := range old { + if !choice.Contains(o, fieldinclude) { + fieldinclude = append(fieldinclude, o) + } + } + + // Remove the deprecated setting + delete(plugin, option) + } + } + // Add the new option if it has data + if len(fieldinclude) > 0 { + plugin["fieldinclude"] = fieldinclude + } + + var fieldexclude []string + if newFieldExclude, found := plugin["fieldexclude"]; found { + var err error + fieldexclude, err = migrations.AsStringSlice(newFieldExclude) + if err != nil { + return nil, "", fmt.Errorf("setting 'fieldexclude': %w", err) + } + } + for _, option := range []string{"drop", "fielddrop"} { + if rawOld, found := plugin[option]; found { + applied = true + + old, err := migrations.AsStringSlice(rawOld) + if err != nil { + return nil, "", fmt.Errorf("setting '%s': %w", option, err) + } + for _, o := range old { + if !choice.Contains(o, fieldexclude) { + fieldexclude = append(fieldexclude, o) + } + } + + // Remove the deprecated setting + delete(plugin, option) + } + } + // Add the new option if it has data + if len(fieldexclude) > 0 { + plugin["fieldexclude"] = fieldexclude + } + + // No options migrated so we can exit early + if !applied { + return nil, "", migrations.ErrNotApplicable + } + + // Create the corresponding plugin configurations + cfg := migrations.CreateTOMLStruct(category, name) + cfg.Add(category, name, plugin) + + output, err := toml.Marshal(cfg) + return output, "", err +} + +// Register the migration function for the plugin type +func init() { + migrations.AddGeneralMigration(migrate) +} diff --git a/migrations/general_metricfilter/migration_test.go b/migrations/general_metricfilter/migration_test.go new file mode 100644 index 000000000..b71ae2421 --- /dev/null +++ b/migrations/general_metricfilter/migration_test.go @@ -0,0 +1,96 @@ +package general_metricfilter_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/config" + _ "github.com/influxdata/telegraf/migrations/general_metricfilter" // register migration + "github.com/influxdata/telegraf/plugins/inputs" +) + +func TestNoMigration(t *testing.T) { + cfg := []byte(` +# Dummy plugin +[[inputs.dummy]] + ## A dummy server + servers = ["tcp://127.0.0.1:1883"] + + ## A commented option + # timeout = "10s" +`) + + // Migrate and check that nothing changed + output, n, err := config.ApplyMigrations(cfg) + require.NoError(t, err) + require.NotEmpty(t, output) + require.Zero(t, n) + require.Equal(t, string(cfg), string(output)) +} + +func TestCases(t *testing.T) { + // Get all directories in testdata + folders, err := os.ReadDir("testcases") + require.NoError(t, err) + + inputs.Add("dummy", func() telegraf.Input { return &MockupInputPlugin{} }) + + for _, f := range folders { + // Only handle folders + if !f.IsDir() { + continue + } + + t.Run(f.Name(), func(t *testing.T) { + testcasePath := filepath.Join("testcases", f.Name()) + inputFile := filepath.Join(testcasePath, "telegraf.conf") + expectedFile := filepath.Join(testcasePath, "expected.conf") + + // Read the expected output + expected := config.NewConfig() + require.NoError(t, expected.LoadConfig(expectedFile)) + require.NotEmpty(t, expected.Inputs) + + // Read the input data + input, remote, err := config.LoadConfigFile(inputFile) + require.NoError(t, err) + require.False(t, remote) + require.NotEmpty(t, input) + + // Migrate + output, n, err := config.ApplyMigrations(input) + require.NoError(t, err) + require.NotEmpty(t, output) + require.GreaterOrEqual(t, n, uint64(1)) + actual := config.NewConfig() + require.NoError(t, actual.LoadConfigData(output)) + + // Test the output + require.Len(t, actual.Inputs, len(expected.Inputs)) + actualIDs := make([]string, 0, len(expected.Inputs)) + expectedIDs := make([]string, 0, len(expected.Inputs)) + for i := range actual.Inputs { + actualIDs = append(actualIDs, actual.Inputs[i].ID()) + expectedIDs = append(expectedIDs, expected.Inputs[i].ID()) + } + require.ElementsMatch(t, expectedIDs, actualIDs, string(output)) + }) + } +} + +// Implement a mock input plugin for testing +type MockupInputPlugin struct { + Servers []string `toml:"servers"` + Timeout config.Duration `toml:"timeout"` +} + +func (m *MockupInputPlugin) SampleConfig() string { + return "Mockup test input plugin" +} +func (m *MockupInputPlugin) Gather(_ telegraf.Accumulator) error { + return nil +} diff --git a/migrations/general_metricfilter/testcases/deprecated_drop/expected.conf b/migrations/general_metricfilter/testcases/deprecated_drop/expected.conf new file mode 100644 index 000000000..918fd7182 --- /dev/null +++ b/migrations/general_metricfilter/testcases/deprecated_drop/expected.conf @@ -0,0 +1,3 @@ +[[inputs.dummy]] +servers = ["tcp://127.0.0.1:1883"] +fieldexclude = ["value"] diff --git a/migrations/general_metricfilter/testcases/deprecated_drop/telegraf.conf b/migrations/general_metricfilter/testcases/deprecated_drop/telegraf.conf new file mode 100644 index 000000000..7276d4dc5 --- /dev/null +++ b/migrations/general_metricfilter/testcases/deprecated_drop/telegraf.conf @@ -0,0 +1,10 @@ +# A dummy plugin +[[inputs.dummy]] + ## A server + servers = ["tcp://127.0.0.1:1883"] + + ## Default timestamp + # timestamp = "10s" + + ## Deprecated drop + drop = ["value"] diff --git a/migrations/general_metricfilter/testcases/deprecated_fielddrop/expected.conf b/migrations/general_metricfilter/testcases/deprecated_fielddrop/expected.conf new file mode 100644 index 000000000..918fd7182 --- /dev/null +++ b/migrations/general_metricfilter/testcases/deprecated_fielddrop/expected.conf @@ -0,0 +1,3 @@ +[[inputs.dummy]] +servers = ["tcp://127.0.0.1:1883"] +fieldexclude = ["value"] diff --git a/migrations/general_metricfilter/testcases/deprecated_fielddrop/telegraf.conf b/migrations/general_metricfilter/testcases/deprecated_fielddrop/telegraf.conf new file mode 100644 index 000000000..a990d0ad5 --- /dev/null +++ b/migrations/general_metricfilter/testcases/deprecated_fielddrop/telegraf.conf @@ -0,0 +1,10 @@ +# A dummy plugin +[[inputs.dummy]] + ## A server + servers = ["tcp://127.0.0.1:1883"] + + ## Default timestamp + # timestamp = "10s" + + ## Deprecated fielddrop + fielddrop = ["value"] diff --git a/migrations/general_metricfilter/testcases/deprecated_fieldpass/expected.conf b/migrations/general_metricfilter/testcases/deprecated_fieldpass/expected.conf new file mode 100644 index 000000000..f0ee02aa8 --- /dev/null +++ b/migrations/general_metricfilter/testcases/deprecated_fieldpass/expected.conf @@ -0,0 +1,3 @@ +[[inputs.dummy]] +servers = ["tcp://127.0.0.1:1883"] +fieldinclude = ["value"] diff --git a/migrations/general_metricfilter/testcases/deprecated_fieldpass/telegraf.conf b/migrations/general_metricfilter/testcases/deprecated_fieldpass/telegraf.conf new file mode 100644 index 000000000..1cfbf9da1 --- /dev/null +++ b/migrations/general_metricfilter/testcases/deprecated_fieldpass/telegraf.conf @@ -0,0 +1,10 @@ +# A dummy plugin +[[inputs.dummy]] + ## A server + servers = ["tcp://127.0.0.1:1883"] + + ## Default timestamp + # timestamp = "10s" + + ## Deprecated fieldpass + fieldpass = ["value"] diff --git a/migrations/general_metricfilter/testcases/deprecated_pass/expected.conf b/migrations/general_metricfilter/testcases/deprecated_pass/expected.conf new file mode 100644 index 000000000..f0ee02aa8 --- /dev/null +++ b/migrations/general_metricfilter/testcases/deprecated_pass/expected.conf @@ -0,0 +1,3 @@ +[[inputs.dummy]] +servers = ["tcp://127.0.0.1:1883"] +fieldinclude = ["value"] diff --git a/migrations/general_metricfilter/testcases/deprecated_pass/telegraf.conf b/migrations/general_metricfilter/testcases/deprecated_pass/telegraf.conf new file mode 100644 index 000000000..15333849e --- /dev/null +++ b/migrations/general_metricfilter/testcases/deprecated_pass/telegraf.conf @@ -0,0 +1,10 @@ +# A dummy plugin +[[inputs.dummy]] + ## A server + servers = ["tcp://127.0.0.1:1883"] + + ## Default timestamp + # timestamp = "10s" + + ## Deprecated pass + pass = ["value"] diff --git a/migrations/general_metricfilter/testcases/merge_all_overlap/expected.conf b/migrations/general_metricfilter/testcases/merge_all_overlap/expected.conf new file mode 100644 index 000000000..86cceff85 --- /dev/null +++ b/migrations/general_metricfilter/testcases/merge_all_overlap/expected.conf @@ -0,0 +1,4 @@ +[[inputs.dummy]] +servers = ["tcp://127.0.0.1:1883"] +fieldexclude = ["bugA", "bugX", "bugB", "bugY", "bugC"] +fieldinclude = ["valueA", "valueX", "valueB", "valueY", "valueC"] diff --git a/migrations/general_metricfilter/testcases/merge_all_overlap/telegraf.conf b/migrations/general_metricfilter/testcases/merge_all_overlap/telegraf.conf new file mode 100644 index 000000000..3dded5f20 --- /dev/null +++ b/migrations/general_metricfilter/testcases/merge_all_overlap/telegraf.conf @@ -0,0 +1,15 @@ +# A dummy plugin +[[inputs.dummy]] + ## A server + servers = ["tcp://127.0.0.1:1883"] + + ## Default timestamp + # timestamp = "10s" + + ## Deprecated field options + fieldinclude = ["valueA", "valueX"] + fieldexclude = ["bugA", "bugX"] + drop = ["bugB", "bugX", "bugY"] + pass = ["valueB", "valueX", "valueY"] + fieldpass = ["valueY", "valueC", "valueX"] + fielddrop = ["bugY", "bugC", "bugX"] diff --git a/migrations/general_metricfilter/testcases/merge_fieldexclude/expected.conf b/migrations/general_metricfilter/testcases/merge_fieldexclude/expected.conf new file mode 100644 index 000000000..8aae6b42f --- /dev/null +++ b/migrations/general_metricfilter/testcases/merge_fieldexclude/expected.conf @@ -0,0 +1,3 @@ +[[inputs.dummy]] +servers = ["tcp://127.0.0.1:1883"] +fieldexclude = ["valueA", "valueB", "valueC"] diff --git a/migrations/general_metricfilter/testcases/merge_fieldexclude/telegraf.conf b/migrations/general_metricfilter/testcases/merge_fieldexclude/telegraf.conf new file mode 100644 index 000000000..ce8bd3890 --- /dev/null +++ b/migrations/general_metricfilter/testcases/merge_fieldexclude/telegraf.conf @@ -0,0 +1,12 @@ +# A dummy plugin +[[inputs.dummy]] + ## A server + servers = ["tcp://127.0.0.1:1883"] + + ## Default timestamp + # timestamp = "10s" + + ## Deprecated field-exclude options + fieldexclude = ["valueA"] + drop = ["valueB"] + fielddrop = ["valueC"] diff --git a/migrations/general_metricfilter/testcases/merge_fieldexclude_overlap/expected.conf b/migrations/general_metricfilter/testcases/merge_fieldexclude_overlap/expected.conf new file mode 100644 index 000000000..99a3013e4 --- /dev/null +++ b/migrations/general_metricfilter/testcases/merge_fieldexclude_overlap/expected.conf @@ -0,0 +1,3 @@ +[[inputs.dummy]] +servers = ["tcp://127.0.0.1:1883"] +fieldexclude = ["valueA", "valueX", "valueB", "valueY", "valueC"] diff --git a/migrations/general_metricfilter/testcases/merge_fieldexclude_overlap/telegraf.conf b/migrations/general_metricfilter/testcases/merge_fieldexclude_overlap/telegraf.conf new file mode 100644 index 000000000..72f721cac --- /dev/null +++ b/migrations/general_metricfilter/testcases/merge_fieldexclude_overlap/telegraf.conf @@ -0,0 +1,12 @@ +# A dummy plugin +[[inputs.dummy]] + ## A server + servers = ["tcp://127.0.0.1:1883"] + + ## Default timestamp + # timestamp = "10s" + + ## Deprecated field-exclude options + fieldexclude = ["valueA", "valueX"] + drop = ["valueB", "valueX", "valueY"] + fielddrop = ["valueY", "valueC", "valueX"] diff --git a/migrations/general_metricfilter/testcases/merge_fieldinclude/expected.conf b/migrations/general_metricfilter/testcases/merge_fieldinclude/expected.conf new file mode 100644 index 000000000..47e35e6ee --- /dev/null +++ b/migrations/general_metricfilter/testcases/merge_fieldinclude/expected.conf @@ -0,0 +1,3 @@ +[[inputs.dummy]] +servers = ["tcp://127.0.0.1:1883"] +fieldinclude = ["valueA", "valueB", "valueC"] diff --git a/migrations/general_metricfilter/testcases/merge_fieldinclude/telegraf.conf b/migrations/general_metricfilter/testcases/merge_fieldinclude/telegraf.conf new file mode 100644 index 000000000..55bd3a083 --- /dev/null +++ b/migrations/general_metricfilter/testcases/merge_fieldinclude/telegraf.conf @@ -0,0 +1,12 @@ +# A dummy plugin +[[inputs.dummy]] + ## A server + servers = ["tcp://127.0.0.1:1883"] + + ## Default timestamp + # timestamp = "10s" + + ## Deprecated field-include options + fieldinclude = ["valueA"] + pass = ["valueB"] + fieldpass = ["valueC"] diff --git a/migrations/general_metricfilter/testcases/merge_fieldinclude_overlap/expected.conf b/migrations/general_metricfilter/testcases/merge_fieldinclude_overlap/expected.conf new file mode 100644 index 000000000..ff28e1ff6 --- /dev/null +++ b/migrations/general_metricfilter/testcases/merge_fieldinclude_overlap/expected.conf @@ -0,0 +1,3 @@ +[[inputs.dummy]] +servers = ["tcp://127.0.0.1:1883"] +fieldinclude = ["valueA", "valueX", "valueB", "valueY", "valueC"] diff --git a/migrations/general_metricfilter/testcases/merge_fieldinclude_overlap/telegraf.conf b/migrations/general_metricfilter/testcases/merge_fieldinclude_overlap/telegraf.conf new file mode 100644 index 000000000..85167b7ba --- /dev/null +++ b/migrations/general_metricfilter/testcases/merge_fieldinclude_overlap/telegraf.conf @@ -0,0 +1,12 @@ +# A dummy plugin +[[inputs.dummy]] + ## A server + servers = ["tcp://127.0.0.1:1883"] + + ## Default timestamp + # timestamp = "10s" + + ## Deprecated field-include options + fieldinclude = ["valueA", "valueX"] + pass = ["valueB", "valueX", "valueY"] + fieldpass = ["valueY", "valueC", "valueX"] diff --git a/migrations/inputs_disk/migration.go b/migrations/inputs_disk/migration.go index 066083d10..01f8067b5 100644 --- a/migrations/inputs_disk/migration.go +++ b/migrations/inputs_disk/migration.go @@ -24,36 +24,20 @@ func migrate(tbl *ast.Table) ([]byte, string, error) { applied = true // Convert the options to the actual type - deprecatedMountpoints, ok := rawDeprecatedMountpoints.([]interface{}) - if !ok { - err := fmt.Errorf("unexpected type for deprecated 'mountpoints' option: %T", rawDeprecatedMountpoints) - return nil, "", err + deprecatedMountpoints, err := migrations.AsStringSlice(rawDeprecatedMountpoints) + if err != nil { + return nil, "", fmt.Errorf("'mountpoints' option: %w", err) } // Merge the option with the replacement var mountpoints []string if rawMountpoints, found := plugin["mount_points"]; found { - mountpointsList, ok := rawMountpoints.([]interface{}) - if !ok { - err := fmt.Errorf("unexpected type for 'mount_points' option: %T", rawMountpoints) - return nil, "", err - } - for _, raw := range mountpointsList { - mp, ok := raw.(string) - if !ok { - err := fmt.Errorf("unexpected type for 'mount_points' option: %T", raw) - return nil, "", err - } - mountpoints = append(mountpoints, mp) + mountpoints, err = migrations.AsStringSlice(rawMountpoints) + if err != nil { + return nil, "", fmt.Errorf("'mount_points' option: %w", err) } } - for _, raw := range deprecatedMountpoints { - dmp, ok := raw.(string) - if !ok { - err := fmt.Errorf("unexpected type for deprecated 'mountpoints' option: %T", raw) - return nil, "", err - } - + for _, dmp := range deprecatedMountpoints { if !choice.Contains(dmp, mountpoints) { mountpoints = append(mountpoints, dmp) } diff --git a/migrations/inputs_procstat/migration.go b/migrations/inputs_procstat/migration.go index 365fc5633..76f7aa4e1 100644 --- a/migrations/inputs_procstat/migration.go +++ b/migrations/inputs_procstat/migration.go @@ -20,33 +20,23 @@ func migrate(tbl *ast.Table) ([]byte, string, error) { // Check for deprecated option(s) and migrate them var applied bool - if oldUnits, found := plugin["supervisor_unit"]; found { + if rawOldUnits, found := plugin["supervisor_unit"]; found { applied = true // Check if the new option already exists and merge the two var units []string if newUnits, found := plugin["supervisor_units"]; found { - nu, ok := newUnits.([]interface{}) - if !ok { - return nil, "", fmt.Errorf("setting 'supervisor_units' has wrong type %T", newUnits) - } - for _, raw := range nu { - u, ok := raw.(string) - if !ok { - return nil, "", fmt.Errorf("setting 'supervisor_units' contains wrong type %T", raw) - } - units = append(units, u) + var err error + units, err = migrations.AsStringSlice(newUnits) + if err != nil { + return nil, "", fmt.Errorf("setting 'supervisor_units': %w", err) } } - ou, ok := oldUnits.([]interface{}) - if !ok { - return nil, "", fmt.Errorf("setting 'supervisor_unit' has wrong type %T", oldUnits) + oldUnits, err := migrations.AsStringSlice(rawOldUnits) + if err != nil { + return nil, "", fmt.Errorf("setting 'supervisor_unit': %w", err) } - for _, raw := range ou { - u, ok := raw.(string) - if !ok { - return nil, "", fmt.Errorf("setting 'supervisor_unit' contains wrong type %T", raw) - } + for _, u := range oldUnits { if !choice.Contains(u, units) { units = append(units, u) } @@ -59,20 +49,11 @@ func migrate(tbl *ast.Table) ([]byte, string, error) { // The tagging options both need the 'tag_with' setting var tagwith []string - newTagWith, found := plugin["tag_with"] - if found { - ntw, ok := newTagWith.([]interface{}) - if !ok { - return nil, "", fmt.Errorf("setting 'tag_with' has wrong type %T", newTagWith) - } - for _, raw := range ntw { - s, ok := raw.(string) - if !ok { - return nil, "", fmt.Errorf("setting 'tag_with' contains wrong type %T", raw) - } - if !choice.Contains(s, tagwith) { - tagwith = append(tagwith, s) - } + if rawNewTagWith, found := plugin["tag_with"]; found { + var err error + tagwith, err = migrations.AsStringSlice(rawNewTagWith) + if err != nil { + return nil, "", fmt.Errorf("setting 'tag_with': %w", err) } } diff --git a/migrations/outputs_influxdb/migration.go b/migrations/outputs_influxdb/migration.go index ac7d01079..5bf6bbbb9 100644 --- a/migrations/outputs_influxdb/migration.go +++ b/migrations/outputs_influxdb/migration.go @@ -1,7 +1,6 @@ package outputs_influxdb import ( - "errors" "fmt" "github.com/influxdata/toml" @@ -27,16 +26,10 @@ func migrate(tbl *ast.Table) ([]byte, string, error) { var urls []string // Merge the old URL and the new URLs with deduplication if newURLs, found := plugin["urls"]; found { - list, ok := newURLs.([]interface{}) - if !ok { - return nil, "", errors.New("'urls' setting is not a list") - } - for _, raw := range list { - nu, ok := raw.(string) - if !ok { - return nil, "", fmt.Errorf("unexpected 'urls' entry %v (%T)", raw, raw) - } - urls = append(urls, nu) + var err error + urls, err = migrations.AsStringSlice(newURLs) + if err != nil { + return nil, "", fmt.Errorf("'urls' setting: %w", err) } } ou, ok := oldURL.(string) diff --git a/migrations/registry.go b/migrations/registry.go index abb838b5f..db88dc16b 100644 --- a/migrations/registry.go +++ b/migrations/registry.go @@ -31,6 +31,14 @@ func AddPluginOptionMigration(name string, f PluginOptionMigrationFunc) { PluginOptionMigrations[name] = f } +type GeneralMigrationFunc func(string, string, *ast.Table) ([]byte, string, error) + +var GeneralMigrations []GeneralMigrationFunc + +func AddGeneralMigration(f GeneralMigrationFunc) { + GeneralMigrations = append(GeneralMigrations, f) +} + type pluginTOMLStruct map[string]map[string][]interface{} func CreateTOMLStruct(category, name string) pluginTOMLStruct { diff --git a/migrations/utils.go b/migrations/utils.go new file mode 100644 index 000000000..bc211a4f2 --- /dev/null +++ b/migrations/utils.go @@ -0,0 +1,22 @@ +package migrations + +import ( + "fmt" +) + +func AsStringSlice(raw interface{}) ([]string, error) { + rawList, ok := raw.([]interface{}) + if !ok { + return nil, fmt.Errorf("unexpected type : %T", raw) + } + + converted := make([]string, 0, len(rawList)) + for _, rawElement := range rawList { + el, ok := rawElement.(string) + if !ok { + return nil, fmt.Errorf("unexpected type for list element: %T", rawElement) + } + converted = append(converted, el) + } + return converted, nil +}