diff --git a/kong/utils.go b/kong/utils.go index 40f591f2..575f7526 100644 --- a/kong/utils.go +++ b/kong/utils.go @@ -278,9 +278,13 @@ func backfillResultConfigMap(res Configuration, path []string, configValue inter return nil } +type FillRecordOptions struct { + FillDefaults bool + FillAuto bool +} + // fills the config record with default values -// if oldConfig is provided, copies auto fields from it, into config -func fillConfigRecord(schema gjson.Result, config Configuration, oldConfig Configuration) Configuration { +func fillConfigRecord(schema gjson.Result, config Configuration, opts FillRecordOptions) Configuration { res := config.DeepCopy() configFields := schema.Get("fields") // Fetch deprecated fields @@ -297,7 +301,7 @@ func fillConfigRecord(schema gjson.Result, config Configuration, oldConfig Confi } if fname == "config" { - newConfig := fillConfigRecord(value.Get(fname), config, oldConfig) + newConfig := fillConfigRecord(value.Get(fname), config, opts) res = newConfig return true } @@ -350,7 +354,7 @@ func fillConfigRecord(schema gjson.Result, config Configuration, oldConfig Confi default: fieldConfig = subConfig.(map[string]interface{}) } - newSubConfig := fillConfigRecord(value.Get(fname), fieldConfig, oldConfig) + newSubConfig := fillConfigRecord(value.Get(fname), fieldConfig, opts) res[fname] = map[string]interface{}(newSubConfig) return true } @@ -370,7 +374,7 @@ func fillConfigRecord(schema gjson.Result, config Configuration, oldConfig Confi for i, configRecord := range subConfigArray { // Check if element is of type record, if it is, set default values by recursively calling `fillConfigRecord` if configRecordMap, ok := configRecord.(map[string]interface{}); ok { - processedConfigRecord := fillConfigRecord(value.Get(fname).Get("elements"), configRecordMap, oldConfig) + processedConfigRecord := fillConfigRecord(value.Get(fname).Get("elements"), configRecordMap, opts) processedSubConfigArray[i] = processedConfigRecord continue } @@ -384,17 +388,17 @@ func fillConfigRecord(schema gjson.Result, config Configuration, oldConfig Confi } } - // handle `auto` fields by copying them from oldConfig if they don't have - // a value. When fields marked as `auto` are passed to kong without a value - // they are auto generated by the gateway. In that case, comparing with - // "defaults" doesn't work, so in order to compute a correct diff they must - // be copied from the oldConfig. auto := value.Get(fname + ".auto") - if auto.Exists() && auto.Bool() && oldConfig != nil { - if v, ok := oldConfig[fname]; ok { - res[fname] = v - return true - } + isAuto := auto.Exists() && auto.Bool() + + // if this is an auto field and we are not filling auto fields, skip + if !opts.FillAuto && isAuto { + return true + } + + // if this is a normal field and we are not filling defaults, skip + if !opts.FillDefaults && !isAuto { + return true } // Check if the record has a default value for the specified field. @@ -680,7 +684,7 @@ func FillEntityDefaults(entity interface{}, schema Schema) error { return nil } -func fillConfigRecordDefaultsAutoFields(plugin *Plugin, schema map[string]interface{}, oldPlugin *Plugin) error { +func fillConfigRecordDefaultsAutoFields(plugin *Plugin, schema map[string]interface{}, opts FillRecordOptions) error { jsonb, err := json.Marshal(&schema) if err != nil { return err @@ -694,11 +698,7 @@ func fillConfigRecordDefaultsAutoFields(plugin *Plugin, schema map[string]interf plugin.Config = make(Configuration) } - var oldConfig Configuration - if oldPlugin != nil { - oldConfig = oldPlugin.Config - } - plugin.Config = fillConfigRecord(configSchema, plugin.Config, oldConfig) + plugin.Config = fillConfigRecord(configSchema, plugin.Config, opts) if plugin.Protocols == nil { plugin.Protocols = getDefaultProtocols(gjsonSchema) } @@ -711,13 +711,13 @@ func fillConfigRecordDefaultsAutoFields(plugin *Plugin, schema map[string]interf // FillPluginsDefaults ingests plugin's defaults from its schema. // Takes in a plugin struct and mutate it in place. func FillPluginsDefaults(plugin *Plugin, schema Schema) error { - return fillConfigRecordDefaultsAutoFields(plugin, schema, nil) + return fillConfigRecordDefaultsAutoFields(plugin, schema, FillRecordOptions{ + FillDefaults: true, + FillAuto: true, + }) } -// same as FillPluginsDefaults but also fills auto fields. -// `oldPlugin` (which repsesents the plugin from the old/existing Kong config -// is used to copy auto fields from it when they are not set in `plugin“. -// keeping both for compatibility: these utils are public -func FillPluginsDefaultsAutoFields(plugin *Plugin, schema map[string]interface{}, oldPlugin *Plugin) error { - return fillConfigRecordDefaultsAutoFields(plugin, schema, oldPlugin) +// same as FillPluginsDefaults but allows configuring whether to fill defaults and auto fields. +func FillPluginsDefaultsWithOpts(plugin *Plugin, schema map[string]interface{}, opts FillRecordOptions) error { + return fillConfigRecordDefaultsAutoFields(plugin, schema, opts) } diff --git a/kong/utils_test.go b/kong/utils_test.go index 1cf7f80e..db36c454 100644 --- a/kong/utils_test.go +++ b/kong/utils_test.go @@ -1836,7 +1836,10 @@ func Test_fillConfigRecord(t *testing.T) { t.Run(tc.name, func(t *testing.T) { configSchema, err := getConfigSchema(tc.schema) require.NoError(t, err) - config := fillConfigRecord(configSchema, tc.config, nil) + config := fillConfigRecord(configSchema, tc.config, FillRecordOptions{ + FillDefaults: true, + FillAuto: true, + }) require.NotNil(t, config) if diff := cmp.Diff(config, tc.expected); diff != "" { t.Errorf("unexpected diff:\n%s", diff) @@ -1939,19 +1942,25 @@ const fillConfigRecordTestSchemaWithAutoFields = `{ "type": "record", "fields": [ { - "foo_string": { + "default_string": { + "type": "string", + "default": "abc" + } + }, + { + "auto_string_1": { "type": "string", "auto": true } }, { - "bar_string": { + "auto_string_2": { "type": "string", "auto": true } }, { - "baz_string": { + "auto_string_3": { "type": "string", "auto": true } @@ -2021,7 +2030,10 @@ func Test_fillConfigRecord_shorthand_fields(t *testing.T) { t.Run(tc.name, func(t *testing.T) { configSchema, err := getConfigSchema(tc.schema) require.NoError(t, err) - config := fillConfigRecord(configSchema, tc.config, nil) + config := fillConfigRecord(configSchema, tc.config, FillRecordOptions{ + FillDefaults: true, + FillAuto: true, + }) require.NotNil(t, config) if diff := cmp.Diff(config, tc.expected); diff != "" { t.Errorf("unexpected diff:\n%s", diff) @@ -2030,7 +2042,7 @@ func Test_fillConfigRecord_shorthand_fields(t *testing.T) { } } -func Test_fillConfigRecord_auto_fields(t *testing.T) { +func Test_fillConfigRecord_defaults_only(t *testing.T) { tests := []struct { name string schema gjson.Result @@ -2038,15 +2050,14 @@ func Test_fillConfigRecord_auto_fields(t *testing.T) { expected Configuration }{ { - name: "fills auto fields with values from oldConfig", + name: "fills defaults with opts to fill defaults only", schema: gjson.Parse(fillConfigRecordTestSchemaWithAutoFields), config: Configuration{ - "baz_string": "789", + "auto_string_3": "789", }, expected: Configuration{ - "foo_string": "123", - "bar_string": "456", - "baz_string": "789", + "default_string": "abc", + "auto_string_3": "789", }, }, } @@ -2055,12 +2066,48 @@ func Test_fillConfigRecord_auto_fields(t *testing.T) { t.Run(tc.name, func(t *testing.T) { configSchema, err := getConfigSchema(tc.schema) require.NoError(t, err) - oldConfig := Configuration{ - "foo_string": "123", - "bar_string": "456", - "baz_string": "000", + config := fillConfigRecord(configSchema, tc.config, FillRecordOptions{ + FillDefaults: true, + FillAuto: false, + }) + require.NotNil(t, config) + if diff := cmp.Diff(config, tc.expected); diff != "" { + t.Errorf("unexpected diff:\n%s", diff) } - config := fillConfigRecord(configSchema, tc.config, oldConfig) + }) + } +} + +func Test_fillConfigRecord_auto_only(t *testing.T) { + tests := []struct { + name string + schema gjson.Result + config Configuration + expected Configuration + }{ + { + name: "fills defaults with opts to fill auto only", + schema: gjson.Parse(fillConfigRecordTestSchemaWithAutoFields), + config: Configuration{ + "auto_string_3": "789", + }, + expected: Configuration{ + "auto_string_1": nil, + "auto_string_2": nil, + "auto_string_3": "789", + // defalt_string missing + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + configSchema, err := getConfigSchema(tc.schema) + require.NoError(t, err) + config := fillConfigRecord(configSchema, tc.config, FillRecordOptions{ + FillDefaults: false, + FillAuto: true, + }) require.NotNil(t, config) if diff := cmp.Diff(config, tc.expected); diff != "" { t.Errorf("unexpected diff:\n%s", diff)