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 11, 2022
1 parent 9377e04 commit 6413278
Show file tree
Hide file tree
Showing 4 changed files with 663 additions and 6 deletions.
120 changes: 120 additions & 0 deletions internal/server/kong/ws/config/compat/compatibility_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ func standardPluginNotAvailableMessage(pluginName string, versionWithFeatureSupp
)
}

func standardCoreEntityFieldsMessage(entityName string, fields []string, versionWithFeatureSupport string) string {
quotedFields := "'" + strings.Join(fields, "', '") + "'"
return fmt.Sprintf("For the '%s' entity, "+
"one or more of the following schema fields are set: %s "+
"but Kong gateway versions < %s do not support these fields. "+
entityName,
quotedFields,
versionWithFeatureSupport,
)
}

const (
versionsPre260 = "< 2.6.0"
versionsPre270 = "< 2.7.0"
Expand Down Expand Up @@ -437,6 +448,115 @@ var (
},
},
},
{
Metadata: config.ChangeMetadata{
ID: config.ChangeID("P121"),
Severity: config.ChangeSeverityError,
Description: standardCoreEntityFieldsMessage(config.Upstream.String(),
[]string{
"hash_on_query_arg",
"hash_fallback_query_arg",
"hash_on_uri_capture",
"hash_fallback_uri_capture",
},
"3.0"),
Resolution: standardUpgradeMessage("3.0"),
},
SemverRange: versionsPre300,
Update: config.ConfigTableUpdates{
Name: config.Upstream.String(),
Type: config.Upstream,
RemoveFields: []string{
"hash_on_query_arg",
"hash_fallback_query_arg",
"hash_on_uri_capture",
"hash_fallback_uri_capture",
},
},
},
{
Metadata: config.ChangeMetadata{
ID: config.ChangeID("P122"),
Severity: config.ChangeSeverityError,
Description: fmt.Sprintf("For the 'upstreams' entity, "+
"one or more of the '%s' schema fields are set with one "+
"of the following values: %s, but Kong gateway versions < 3.0 "+
"do not support these values. Because of this, 'hash_on' and 'hash_fallback'"+
"have been changed in the data-plane to 'none' and hashing is "+
"not working as expected.",
strings.Join([]string{"hash_on", "hash_fallback"}, ", "),
strings.Join([]string{"path", "query_arg", "uri_capture"}, ", "),
),
Resolution: standardUpgradeMessage("3.0"),
},
SemverRange: versionsPre300,
Update: config.ConfigTableUpdates{
Name: config.Upstream.String(),
Type: config.Upstream,
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",
},
},
},
},
},
},
}
)

Expand Down
72 changes: 71 additions & 1 deletion internal/server/kong/ws/config/version_compatibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ type UpdateType uint8
const (
Plugin UpdateType = iota
Service
Upstream
)

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

//nolint:revive
Expand Down Expand Up @@ -246,6 +247,8 @@ func (vc *WSVersionCompatibility) processConfigTableUpdates(uncompressedPayload
processedPayload = vc.processPluginUpdates(processedPayload,
configTableUpdate, dataPlaneVersion)
case Service:
fallthrough
case Upstream:
processedPayload = vc.processCoreEntityUpdates(processedPayload,
configTableUpdate, dataPlaneVersion)
default:
Expand Down Expand Up @@ -552,6 +555,73 @@ 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.String("data-plane", dataPlaneVersion)).
With(zap.Error(err)).
Error("entity item was not removed from configuration")
} else {
vc.logger.With(zap.String("entity", entityName)).
With(zap.String("field", update.Field)).
With(zap.String("data-plane", dataPlaneVersion)).
Warn("removing entity field which is incompatible with data plane")
}
} 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.String("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.String("data-plane", dataPlaneVersion)).
With(zap.Error(err)).
Error("entity configuration field was not updated int configuration")
} 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.String("data-plane", dataPlaneVersion)).
Warn("removing entity field which is incompatible with data plane")
}
}
}
}
}
}

if err = json.Unmarshal([]byte(updatedRaw), &entityJSON); err != nil {
vc.logger.With(zap.String("entity", entityName)).
With(zap.String("name", name)).
Expand Down
Loading

0 comments on commit 6413278

Please sign in to comment.