Skip to content
This repository has been archived by the owner on Jun 5, 2024. It is now read-only.

Commit

Permalink
feat: add version compatibility logic for upstreams
Browse files Browse the repository at this point in the history
  • Loading branch information
GGabriele committed Aug 2, 2022
1 parent 06c34f8 commit cfacd30
Show file tree
Hide file tree
Showing 4 changed files with 485 additions and 3 deletions.
72 changes: 72 additions & 0 deletions internal/server/kong/ws/config/compat/compatibility_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,77 @@ var ConfigTableUpdates = map[uint64][]config.ConfigTableUpdates{
"allow_any_domain",
},
},
{
Name: "upstreams",
Type: config.CoreUpstream,
RemoveFields: []string{
"hash_on_query_arg",
"hash_fallback_query_arg",
"hash_on_uri_capture",
"hash_fallback_uri_capture",
},
FieldUpdates: []config.ConfigTableFieldCondition{
{
Field: "hash_on",
Condition: "hash_on=path",
Updates: []config.ConfigTableFieldUpdate{
{
Field: "hash_on",
Value: "none",
},
},
},
{
Field: "hash_on",
Condition: "hash_on=query_arg",
Updates: []config.ConfigTableFieldUpdate{
{
Field: "hash_on",
Value: "none",
},
},
},
{
Field: "hash_on",
Condition: "hash_on=uri_capture",
Updates: []config.ConfigTableFieldUpdate{
{
Field: "hash_on",
Value: "none",
},
},
},
{
Field: "hash_fallback",
Condition: "hash_fallback=path",
Updates: []config.ConfigTableFieldUpdate{
{
Field: "hash_fallback",
Value: "none",
},
},
},
{
Field: "hash_fallback",
Condition: "hash_fallback=query_arg",
Updates: []config.ConfigTableFieldUpdate{
{
Field: "hash_fallback",
Value: "none",
},
},
},
{
Field: "hash_fallback",
Condition: "hash_fallback=uri_capture",
Updates: []config.ConfigTableFieldUpdate{
{
Field: "hash_fallback",
Value: "none",
},
},
},
},
},
},
}
60 changes: 59 additions & 1 deletion internal/server/kong/ws/config/version_compatibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ type UpdateType uint8
const (
Plugin UpdateType = iota
CoreService
CoreUpstream
)

func (u UpdateType) String() string {
return [...]string{"plugin", "services"}[u]
return [...]string{"plugin", "services", "upstreams"}[u]
}

//nolint: revive
Expand Down Expand Up @@ -228,6 +229,8 @@ func (vc *WSVersionCompatibility) processConfigTableUpdates(uncompressedPayload
case Plugin:
processedPayload = vc.processPluginUpdates(processedPayload,
configTableUpdate, dataPlaneVersion)
case CoreUpstream:
fallthrough
case CoreService:
processedPayload = vc.processCoreEntityUpdates(processedPayload,
configTableUpdate, dataPlaneVersion)
Expand Down Expand Up @@ -487,6 +490,61 @@ func (vc *WSVersionCompatibility) processCoreEntityUpdates(payload string,
}
}
}

// Field updates
for _, update := range configTableUpdate.FieldUpdates {
if gjson.Get(updatedRaw, update.Field).Exists() {
conditionField := fmt.Sprintf("[@this].#(%s)", update.Condition)
if gjson.Get(updatedRaw, conditionField).Exists() {
for _, fieldUpdate := range update.Updates {
conditionUpdate := fieldUpdate.Field
if fieldUpdate.Value == nil && len(fieldUpdate.ValueFromField) == 0 {
// Handle field removal
if updatedRaw, err = sjson.Delete(updatedRaw, conditionUpdate); err != nil {
vc.logger.With(zap.String("entity", entityName)).
With(zap.String("field", update.Field)).
With(zap.Uint64("data-plane", dataPlaneVersion)).
With(zap.Error(err)).
Error("entity item was not removed from configuration")
}
} else {
// Get the field value if "Value" is a field
var value interface{}
if fieldUpdate.Value != nil {
value = fieldUpdate.Value
} else {
valueFromField := fmt.Sprintf("%v", fieldUpdate.ValueFromField)
res := gjson.Get(updatedRaw, valueFromField)
if res.Exists() {
value = res.Value()
} else {
vc.logger.With(zap.String("entity", entityName)).
With(zap.String("field", update.Field)).
With(zap.String("condition", update.Condition)).
With(zap.Any("new-value", fieldUpdate.Value)).
With(zap.Uint64("data-plane", dataPlaneVersion)).
With(zap.Error(err)).
Error("entity configuration does not contain field value")
break
}
}

// Handle field update from value of value of field
if updatedRaw, err = sjson.Set(updatedRaw, conditionUpdate, value); err != nil {
vc.logger.With(zap.String("entity", entityName)).
With(zap.String("field", update.Field)).
With(zap.String("condition", update.Condition)).
With(zap.Any("new-value", fieldUpdate.Value)).
With(zap.Uint64("data-plane", dataPlaneVersion)).
With(zap.Error(err)).
Error("entity configuration field was not updated int configuration")
}
}
}
}
}
}

if err = json.Unmarshal([]byte(updatedRaw), &entityJSON); err != nil {
vc.logger.With(zap.String("entity", entityName)).
With(zap.String("config", updatedRaw)).
Expand Down
213 changes: 213 additions & 0 deletions internal/server/kong/ws/config/version_compatibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2431,6 +2431,219 @@ func TestVersionCompatibility_ProcessConfigTableUpdates(t *testing.T) {
}
}`,
},
{
name: "drop single upstream field",
configTableUpdates: map[uint64][]ConfigTableUpdates{
3000000000: {
{
Type: CoreUpstream,
RemoveFields: []string{
"upstream_field_1",
},
},
},
},
uncompressedPayload: `{
"config_table": {
"upstreams": [
{
"name": "upstream_1",
"upstream_field_1": "element",
"upstream_field_2": "element"
}
]
}
}`,
dataPlaneVersion: 2007000000,
expectedPayload: `{
"config_table": {
"upstreams": [
{
"name": "upstream_1",
"upstream_field_2": "element"
}
]
}
}`,
},
{
name: "drop multiple upstream fields",
configTableUpdates: map[uint64][]ConfigTableUpdates{
3000000000: {
{
Type: CoreUpstream,
RemoveFields: []string{
"upstream_field_1",
"upstream_field_2",
},
},
},
},
uncompressedPayload: `{
"config_table": {
"upstreams": [
{
"name": "upstream_1",
"upstream_field_1": "element",
"upstream_field_2": "element",
"upstream_field_3": "element"
}
]
}
}`,
dataPlaneVersion: 2007000000,
expectedPayload: `{
"config_table": {
"upstreams": [
{
"name": "upstream_1",
"upstream_field_3": "element"
}
]
}
}`,
},
{
name: "drop multiple upstream fields from multiple upstreams",
configTableUpdates: map[uint64][]ConfigTableUpdates{
3000000000: {
{
Type: CoreUpstream,
RemoveFields: []string{
"upstream_field_1",
"upstream_field_2",
"upstream_field_4",
},
},
},
},
uncompressedPayload: `{
"config_table": {
"upstreams": [
{
"name": "upstream_1",
"upstream_field_1": "element",
"upstream_field_2": "element",
"upstream_field_3": "element"
},
{
"name": "upstream_2",
"upstream_field_1": "element",
"upstream_field_3": "element",
"upstream_field_4": "element"
}
]
}
}`,
dataPlaneVersion: 2007000000,
expectedPayload: `{
"config_table": {
"upstreams": [
{
"name": "upstream_1",
"upstream_field_3": "element"
},
{
"name": "upstream_2",
"upstream_field_3": "element"
}
]
}
}`,
},
{
name: "drop upstreams and plugins' fields",
configTableUpdates: map[uint64][]ConfigTableUpdates{
3000000000: {
{
Type: CoreUpstream,
RemoveFields: []string{
"upstream_field_1",
"upstream_field_2",
"upstream_field_4",
},
},
{
Name: "plugin_1",
Type: Plugin,
Remove: true,
},
},
},
uncompressedPayload: `{
"config_table": {
"plugins": [
{
"name": "plugin_2",
"config": {
"plugin_2_field_1": "element"
}
},
{
"name": "plugin_1",
"config": {
"plugin_1_field_1": "element"
}
},
{
"name": "plugin_3",
"config": {
"plugin_3_field_1": "element"
}
},
{
"name": "plugin_1",
"config": {
"plugin_1_field_1": "element"
}
}
],
"upstreams": [
{
"name": "upstream_1",
"upstream_field_1": "element",
"upstream_field_2": "element",
"upstream_field_3": "element"
},
{
"name": "upstream_2",
"upstream_field_1": "element",
"upstream_field_3": "element",
"upstream_field_4": "element"
}
]
}
}`,
dataPlaneVersion: 2007000000,
expectedPayload: `{
"config_table": {
"plugins": [
{
"name": "plugin_2",
"config": {
"plugin_2_field_1": "element"
}
},
{
"name": "plugin_3",
"config": {
"plugin_3_field_1": "element"
}
}
],
"upstreams": [
{
"name": "upstream_1",
"upstream_field_3": "element"
},
{
"name": "upstream_2",
"upstream_field_3": "element"
}
]
}
}`,
},
}

for _, test := range tests {
Expand Down
Loading

0 comments on commit cfacd30

Please sign in to comment.