* inject headerModifier to the luaData (#223)

* fix lua script and test case
* use ptr instead of struct
* add requestHeaderModifier to testcase debugging toolkit

Signed-off-by: Megrez Lu <lujiajing1126@gmail.com>
This commit is contained in:
Jiajing LU 2024-07-22 10:47:18 +08:00 committed by GitHub
parent e7652cbc7c
commit 6fae7085e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 186 additions and 42 deletions

2
.gitignore vendored
View File

@ -29,3 +29,5 @@ test/e2e/generated/bindata.go
.vscode .vscode
.DS_Store .DS_Store
lua_configuration/networking.istio.io/**/testdata/*.lua

View File

@ -100,11 +100,12 @@ func objectToTable(path string) error {
Annotations: testCase.Original.GetAnnotations(), Annotations: testCase.Original.GetAnnotations(),
Spec: testCase.Original.Object["spec"], Spec: testCase.Original.Object["spec"],
}, },
Matches: step.TrafficRoutingStrategy.Matches, Matches: step.TrafficRoutingStrategy.Matches,
CanaryWeight: *weight, CanaryWeight: *weight,
StableWeight: 100 - *weight, StableWeight: 100 - *weight,
CanaryService: canaryService, CanaryService: canaryService,
StableService: stableService, StableService: stableService,
RequestHeaderModifier: step.TrafficRoutingStrategy.RequestHeaderModifier,
} }
uList[fmt.Sprintf("step_%d", i)] = data uList[fmt.Sprintf("step_%d", i)] = data
} }
@ -128,11 +129,12 @@ func objectToTable(path string) error {
Annotations: testCase.Original.GetAnnotations(), Annotations: testCase.Original.GetAnnotations(),
Spec: testCase.Original.Object["spec"], Spec: testCase.Original.Object["spec"],
}, },
Matches: matches, Matches: matches,
CanaryWeight: *weight, CanaryWeight: *weight,
StableWeight: 100 - *weight, StableWeight: 100 - *weight,
CanaryService: canaryService, CanaryService: canaryService,
StableService: stableService, StableService: stableService,
RequestHeaderModifier: trafficRouting.Spec.Strategy.RequestHeaderModifier,
} }
uList["steps_0"] = data uList["steps_0"] = data
} else { } else {

View File

@ -19,6 +19,10 @@ rollout:
- type: RegularExpression - type: RegularExpression
name: name name: name
value: ".*demo" value: ".*demo"
requestHeaderModifier:
set:
- name: "header-foo"
value: "bar"
- matches: - matches:
- headers: - headers:
- type: Exact - type: Exact
@ -66,6 +70,10 @@ expected:
exact: pc exact: pc
name: name:
regex: .*demo regex: .*demo
headers:
request:
set:
header-foo: bar
route: route:
- destination: - destination:
host: svc-demo-canary host: svc-demo-canary

View File

@ -13,6 +13,10 @@ trafficRouting:
- type: RegularExpression - type: RegularExpression
name: name name: name
value: ".*demo" value: ".*demo"
requestHeaderModifier:
set:
- name: "header-foo"
value: "bar"
objectRef: objectRef:
- service: svc-demo - service: svc-demo
customNetworkRefs: customNetworkRefs:
@ -51,6 +55,10 @@ expected:
exact: pc exact: pc
name: name:
regex: .*demo regex: .*demo
headers:
request:
set:
header-foo: bar
route: route:
- destination: - destination:
host: svc-demo host: svc-demo

View File

@ -14,6 +14,10 @@ trafficRouting:
- type: RegularExpression - type: RegularExpression
name: name name: name
value: ".*demo" value: ".*demo"
requestHeaderModifier:
set:
- name: "header-foo"
value: "bar"
objectRef: objectRef:
- service: svc-demo - service: svc-demo
customNetworkRefs: customNetworkRefs:
@ -50,6 +54,10 @@ expected:
- headers: - headers:
name: name:
regex: .*demo regex: .*demo
headers:
request:
set:
header-foo: bar
route: route:
- destination: - destination:
host: svc-demo host: svc-demo
@ -58,6 +66,10 @@ expected:
- headers: - headers:
user-agent: user-agent:
exact: pc exact: pc
headers:
request:
set:
header-foo: bar
route: route:
- destination: - destination:
host: svc-demo host: svc-demo

View File

@ -43,7 +43,7 @@ function CalculateWeight(route, stableWeight, n)
end end
-- generate routes with matches, insert a rule before other rules, only support http headers, cookies etc. -- generate routes with matches, insert a rule before other rules, only support http headers, cookies etc.
function GenerateRoutesWithMatches(spec, matches, stableService, canaryService) function GenerateRoutesWithMatches(spec, matches, stableService, canaryService, requestHeaderModifier)
for _, match in ipairs(matches) do for _, match in ipairs(matches) do
local route = {} local route = {}
route["match"] = {} route["match"] = {}
@ -81,6 +81,23 @@ function GenerateRoutesWithMatches(spec, matches, stableService, canaryService)
end end
end end
table.insert(route["match"], vsMatch) table.insert(route["match"], vsMatch)
if requestHeaderModifier then
route["headers"] = {}
route["headers"]["request"] = {}
for action, headers in pairs(requestHeaderModifier) do
if action == "set" or action == "add" then
route["headers"]["request"][action] = {}
for _, header in ipairs(headers) do
route["headers"]["request"][action][header["name"]] = header["value"]
end
elseif action == "remove" then
route["headers"]["request"]["remove"] = {}
for _, rHeader in ipairs(headers) do
table.insert(route["headers"]["request"]["remove"], rHeader)
end
end
end
end
route.route = { route.route = {
{ {
destination = {} destination = {}
@ -130,7 +147,7 @@ end
if (obj.matches and next(obj.matches) ~= nil) if (obj.matches and next(obj.matches) ~= nil)
then then
GenerateRoutesWithMatches(spec, obj.matches, obj.stableService, obj.canaryService) GenerateRoutesWithMatches(spec, obj.matches, obj.stableService, obj.canaryService, obj.requestHeaderModifier)
else else
GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "http") GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "http")
GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "tcp") GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "tcp")

View File

@ -39,6 +39,7 @@ import (
"k8s.io/klog/v2" "k8s.io/klog/v2"
utilpointer "k8s.io/utils/pointer" utilpointer "k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
) )
const ( const (
@ -47,12 +48,13 @@ const (
) )
type LuaData struct { type LuaData struct {
Data Data Data Data
CanaryWeight int32 CanaryWeight int32
StableWeight int32 StableWeight int32
Matches []v1beta1.HttpRouteMatch Matches []v1beta1.HttpRouteMatch
CanaryService string CanaryService string
StableService string StableService string
RequestHeaderModifier *gatewayv1beta1.HTTPRequestHeaderFilter
} }
type Data struct { type Data struct {
Spec interface{} `json:"spec,omitempty"` Spec interface{} `json:"spec,omitempty"`
@ -268,13 +270,15 @@ func (r *customController) executeLuaForCanary(spec Data, strategy *v1beta1.Traf
// so we need to pass weight=-1 to indicate the case where weight is nil. // so we need to pass weight=-1 to indicate the case where weight is nil.
weight = utilpointer.Int32(-1) weight = utilpointer.Int32(-1)
} }
data := &LuaData{ data := &LuaData{
Data: spec, Data: spec,
CanaryWeight: *weight, CanaryWeight: *weight,
StableWeight: 100 - *weight, StableWeight: 100 - *weight,
Matches: matches, Matches: matches,
CanaryService: r.conf.CanaryService, CanaryService: r.conf.CanaryService,
StableService: r.conf.StableService, StableService: r.conf.StableService,
RequestHeaderModifier: strategy.RequestHeaderModifier,
} }
unObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(data) unObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(data)

View File

@ -414,6 +414,78 @@ func TestEnsureRoutes(t *testing.T) {
return done, hasError return done, hasError
}, },
}, },
{
name: "Do header-based traffic routing and set header for VirtualService",
getRoutes: func() *v1beta1.TrafficRoutingStrategy {
headerTypeExact := gatewayv1beta1.HeaderMatchExact
return &v1beta1.TrafficRoutingStrategy{
Matches: []v1beta1.HttpRouteMatch{
{
Headers: []gatewayv1beta1.HTTPHeaderMatch{
{
Type: &headerTypeExact,
Name: "user_id",
Value: "123456",
},
},
},
},
RequestHeaderModifier: &gatewayv1beta1.HTTPRequestHeaderFilter{
Set: []gatewayv1beta1.HTTPHeader{
{
Name: "x-env-flag",
Value: "canary",
},
},
},
}
},
getUnstructureds: func() []*unstructured.Unstructured {
objects := make([]*unstructured.Unstructured, 0)
u := &unstructured.Unstructured{}
_ = u.UnmarshalJSON([]byte(virtualServiceDemo))
u.SetAPIVersion("networking.istio.io/v1alpha3")
objects = append(objects, u)
return objects
},
getConfig: func() Config {
return Config{
Key: "rollout-demo",
StableService: "echoserver",
CanaryService: "echoserver-canary",
TrafficConf: []v1beta1.ObjectRef{
{
APIVersion: "networking.istio.io/v1alpha3",
Kind: "VirtualService",
Name: "echoserver",
},
},
}
},
expectUnstructureds: func() []*unstructured.Unstructured {
objects := make([]*unstructured.Unstructured, 0)
u := &unstructured.Unstructured{}
_ = u.UnmarshalJSON([]byte(virtualServiceDemo))
annotations := map[string]string{
OriginalSpecAnnotation: `{"spec":{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"}}]}]},"annotations":{"virtual":"test"}}`,
"virtual": "test",
}
u.SetAnnotations(annotations)
specStr := `{"hosts":["echoserver.example.com"],"http":[{"headers":{"request":{"set":{"x-env-flag":"canary"}}},"match":[{"headers":{"user_id":{"exact":"123456"}}}],"route":[{"destination":{"host":"echoserver-canary"}}]},{"route":[{"destination":{"host":"echoserver"}}]}]}`
var spec interface{}
_ = json.Unmarshal([]byte(specStr), &spec)
u.Object["spec"] = spec
objects = append(objects, u)
return objects
},
expectState: func() (bool, bool) {
done := false
hasError := false
return done, hasError
},
},
{ {
name: "test2, do traffic routing but failed to execute lua", name: "test2, do traffic routing but failed to execute lua",
getRoutes: func() *v1beta1.TrafficRoutingStrategy { getRoutes: func() *v1beta1.TrafficRoutingStrategy {
@ -638,11 +710,12 @@ func TestLuaScript(t *testing.T) {
Annotations: testCase.Original.GetAnnotations(), Annotations: testCase.Original.GetAnnotations(),
Spec: testCase.Original.Object["spec"], Spec: testCase.Original.Object["spec"],
}, },
Matches: step.TrafficRoutingStrategy.Matches, Matches: step.TrafficRoutingStrategy.Matches,
CanaryWeight: *weight, CanaryWeight: *weight,
StableWeight: 100 - *weight, StableWeight: 100 - *weight,
CanaryService: canaryService, CanaryService: canaryService,
StableService: stableService, StableService: stableService,
RequestHeaderModifier: step.TrafficRoutingStrategy.RequestHeaderModifier,
} }
nSpec, err := executeLua(data, script) nSpec, err := executeLua(data, script)
if err != nil { if err != nil {
@ -678,11 +751,12 @@ func TestLuaScript(t *testing.T) {
Annotations: testCase.Original.GetAnnotations(), Annotations: testCase.Original.GetAnnotations(),
Spec: testCase.Original.Object["spec"], Spec: testCase.Original.Object["spec"],
}, },
Matches: matches, Matches: matches,
CanaryWeight: *weight, CanaryWeight: *weight,
StableWeight: 100 - *weight, StableWeight: 100 - *weight,
CanaryService: canaryService, CanaryService: canaryService,
StableService: stableService, StableService: stableService,
RequestHeaderModifier: trafficRouting.Spec.Strategy.RequestHeaderModifier,
} }
nSpec, err := executeLua(data, script) nSpec, err := executeLua(data, script)
if err != nil { if err != nil {

View File

@ -94,7 +94,7 @@ function CalculateWeight(route, stableWeight, n)
end end
-- generate routes with matches, insert a rule before other rules -- generate routes with matches, insert a rule before other rules
function GenerateMatchedRoutes(spec, matches, stableService, canaryService, stableWeight, canaryWeight, protocol) function GenerateMatchedRoutes(spec, matches, stableService, canaryService, stableWeight, canaryWeight, requestHeaderModifier, protocol)
local hasRule, stableServiceSubsets = FindStableServiceSubsets(spec, stableService, protocol) local hasRule, stableServiceSubsets = FindStableServiceSubsets(spec, stableService, protocol)
if (not hasRule) then if (not hasRule) then
return return
@ -136,6 +136,23 @@ function GenerateMatchedRoutes(spec, matches, stableService, canaryService, stab
end end
end end
table.insert(route["match"], vsMatch) table.insert(route["match"], vsMatch)
if requestHeaderModifier then
route["headers"] = {}
route["headers"]["request"] = {}
for action, headers in pairs(requestHeaderModifier) do
if action == "set" or action == "add" then
route["headers"]["request"][action] = {}
for _, header in ipairs(headers) do
route["headers"]["request"][action][header["name"]] = header["value"]
end
elseif action == "remove" then
route["headers"]["request"]["remove"] = {}
for _, rHeader in ipairs(headers) do
table.insert(route["headers"]["request"]["remove"], rHeader)
end
end
end
end
route.route = { route.route = {
{ {
destination = {} destination = {}
@ -187,7 +204,7 @@ function GenerateMatchedRoutes(spec, matches, stableService, canaryService, stab
end end
-- generate routes without matches, change every rule -- generate routes without matches, change every rule
function GenerateRoutes(spec, stableService, canaryService, stableWeight, canaryWeight, protocol) function GenerateRoutes(spec, stableService, canaryService, stableWeight, canaryWeight, requestHeaderModifier, protocol)
local matchedRules = FindMatchedRules(spec, stableService, protocol) local matchedRules = FindMatchedRules(spec, stableService, protocol)
for _, rule in ipairs(matchedRules) do for _, rule in ipairs(matchedRules) do
local canary local canary
@ -218,12 +235,12 @@ function GenerateRoutes(spec, stableService, canaryService, stableWeight, canary
end end
if (obj.matches) then if (obj.matches) then
GenerateMatchedRoutes(spec, obj.matches, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "http") GenerateMatchedRoutes(spec, obj.matches, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, obj.requestHeaderModifier, "http")
GenerateMatchedRoutes(spec, obj.matches, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "tcp") GenerateMatchedRoutes(spec, obj.matches, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, obj.requestHeaderModifier,"tcp")
GenerateMatchedRoutes(spec, obj.matches, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "tls") GenerateMatchedRoutes(spec, obj.matches, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, obj.requestHeaderModifier,"tls")
else else
GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "http") GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, obj.requestHeaderModifier, "http")
GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "tcp") GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, obj.requestHeaderModifier, "tcp")
GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, "tls") GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight, obj.requestHeaderModifier, "tls")
end end
return obj.data return obj.data