finished lua script, controllerMap issue not done

Signed-off-by: Kuromesi <blackfacepan@163.com>
This commit is contained in:
Kuromesi 2023-07-14 10:20:29 +00:00
parent 9fa42b98f2
commit 9a062e08cd
5 changed files with 448 additions and 204 deletions

View File

@ -1,45 +1,140 @@
-- obj = {
-- -- matches = {
-- -- {
-- -- headers = {
-- -- {
-- -- name = "xxx",
-- -- value = "xxx",
-- -- type = "RegularExpression"
-- -- }
-- -- }
-- -- }
-- -- },
-- spec = {
-- hosts = {
-- "reviews",
-- },
-- http = {
-- {
-- route = {
-- {
-- destination = {
-- host = "reviews",
-- subset = "c1"
-- }
-- }
-- matches = {
-- {
-- headers = {
-- {
-- name = "xxx",
-- value = "xxx",
-- type = "RegularExpression"
-- }
-- }
-- }
-- },
-- data = {
-- spec = {
-- hosts = {
-- "reviews",
-- },
-- http = {
-- {
-- route = {
-- {
-- destination = {
-- host = "reviews",
-- subset = "c1",
-- port = {
-- number = 80
-- }
-- }
-- }
-- }
-- }
-- }
-- },
-- },
-- stableService = "reviews",
-- canaryService = "canary",
-- stableWeight = 90,
-- canaryWeight = 10
-- }
spec = obj.data.spec
spec = obj.spec
if (obj.matches) then
for _, match in ipairs(obj.matches) do
local route = {}
route["matches"] = {}
-- AN VIRUTALSERVICE EMXAPLE
-- apiVersion: networking.istio.io/v1alpha3
-- kind: VirtualService
-- metadata:
-- name: productpage
-- namespace: nsA
-- spec:
-- http:
-- - match:
-- - uri:
-- prefix: "/productpage/v1/"
-- route:
-- - destination:
-- host: productpage-v1.nsA.svc.cluster.local
-- - route:
-- - destination:
-- host: productpage.nsA.svc.cluster.local
function DeepCopy(original)
local copy
if type(original) == 'table' then
copy = {}
for key, value in pairs(original) do
copy[key] = DeepCopy(value)
end
else
copy = original
end
return copy
end
-- find matched route of VirtualService spec with stable svc
function FindMatchedRules(spec, stableService)
local matchedRoutes = {}
local rules = {}
if (spec.http) then
for _, http in ipairs(spec.http) do
table.insert(rules, http)
end
end
if (spec.tls) then
for _, tls in ipairs(spec.tls) do
table.insert(rules, tls)
end
end
if (spec.tcp) then
for _, tcp in ipairs(spec.tcp) do
table.insert(rules, tcp)
end
end
for _, rule in ipairs(rules) do
for _, route in ipairs(rule.route) do
if route.destination.host == stableService then
table.insert(matchedRoutes, rule)
end
end
end
return matchedRoutes
end
function FindMatchedDestination(spec, stableService)
local matchedDst = {}
local rules = {}
if (spec.http) then
for _, http in ipairs(spec.http) do
table.insert(rules, http)
end
end
if (spec.tls) then
for _, tls in ipairs(spec.tls) do
table.insert(rules, tls)
end
end
if (spec.tcp) then
for _, tcp in ipairs(spec.tcp) do
table.insert(rules, tcp)
end
end
for _, rule in ipairs(rules) do
for _, route in ipairs(rule.route) do
if route.destination.host == stableService then
matchedDst = route.destination
return matchedDst
end
end
end
return matchedDst
end
-- generate routes with matches
function GenerateMatchedRoutes(spec, matches, stableService, canaryService, stableWeight, canaryWeight)
local route = {}
route["match"] = {}
for _, match in ipairs(matches) do
for key, value in pairs(match) do
local vsMatch = {}
vsMatch[key] = {}
@ -53,41 +148,56 @@ if (obj.matches) then
vsMatch[key][rule["name"]] = {}
vsMatch[key][rule["name"]][matchType] = rule["value"]
end
table.insert(route["matches"], vsMatch)
table.insert(route["match"], vsMatch)
end
route["route"] = {
{
destination = {
host = obj.stableService,
},
weight = obj.stableWeight,
},
{
destination = {
host = obj.canaryService,
},
weight = obj.canaryWeight,
}
}
table.insert(spec.http, 1, route)
end
return spec
local matchedDst = FindMatchedDestination(spec, stableService)
route["route"] = {
{
destination = DeepCopy(matchedDst),
weight = stableWeight,
},
{
destination = {
host = canaryService,
port = DeepCopy(matchedDst.port)
},
weight = canaryWeight,
}
}
table.insert(spec.http, 1, route)
end
for i, rule in ipairs(obj.spec.http) do
for _, route in ipairs(rule.route) do
local destination = route.destination
if destination.host == obj.stableService then
route.weight = obj.stableWeight
-- destination.weight = obj.stableWeight
local canary = {
destination = {
host = obj.canaryService,
},
weight = obj.canaryWeight,
}
table.insert(rule.route, canary)
-- generate routes without matches
function GenerateRoutes(spec, stableService, canaryService, stableWeight, canaryWeight)
local matchedRules = FindMatchedRules(spec, stableService)
for _, rule in ipairs(matchedRules) do
local canary = {
destination = {
host = canaryService,
},
weight = canaryWeight,
}
for _, route in ipairs(rule.route) do
-- incase there are multiple versions traffic already
if (route.destination.host == stableService) then
canary.destination.port = DeepCopy(route.destination.port)
end
if (route.weight) then
route.weight = math.floor(route.weight * stableWeight / 100)
else
route.weight = math.floor(stableWeight / #rule.route)
end
end
table.insert(rule.route, canary)
end
end
return spec
if (obj.matches) then
GenerateMatchedRoutes(spec, obj.matches, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight)
return obj.data
end
GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight)
return obj.data

View File

@ -38,7 +38,7 @@ import (
var (
defaultGracePeriodSeconds int32 = 3
controllerMap map[string]interface{} = make(map[string]interface{})
ControllerMap map[string]interface{} = make(map[string]interface{})
)
type TrafficRoutingContext struct {
@ -86,9 +86,9 @@ func (m *Manager) InitializeTrafficRouting(c *TrafficRoutingContext) error {
cService := getCanaryServiceName(sService, c.OnlyTrafficRouting)
// new network provider
key := fmt.Sprintf("%s.%s", c.Key, sService)
if _, ok := controllerMap[key]; ok {
return nil
}
// if _, ok := ControllerMap[key]; ok {
// return nil
// }
trController, err := newNetworkProvider(m.Client, c, sService, cService)
if err != nil {
klog.Errorf("%s newNetworkProvider failed: %s", c.Key, err.Error())
@ -98,7 +98,7 @@ func (m *Manager) InitializeTrafficRouting(c *TrafficRoutingContext) error {
if err != nil {
return err
}
controllerMap[key] = trController
ControllerMap[key] = trController
return nil
}
@ -183,14 +183,14 @@ func (m *Manager) DoTrafficRouting(c *TrafficRoutingContext) (bool, error) {
// new network provider
key := fmt.Sprintf("%s.%s", c.Key, trafficRouting.Service)
trController, ok := controllerMap[key].(network.NetworkProvider)
trController, ok := ControllerMap[key].(network.NetworkProvider)
if !ok {
// in case the rollout controller restart unexpectedly, create a new trafficRouting controller
err := m.InitializeTrafficRouting(c)
if err != nil {
return false, err
}
trController, _ = controllerMap[key].(network.NetworkProvider)
trController, _ = ControllerMap[key].(network.NetworkProvider)
}
verify, err := trController.EnsureRoutes(context.TODO(), &c.Strategy)
if err != nil {
@ -214,7 +214,7 @@ func (m *Manager) FinalisingTrafficRouting(c *TrafficRoutingContext, onlyRestore
cServiceName := getCanaryServiceName(trafficRouting.Service, c.OnlyTrafficRouting)
key := fmt.Sprintf("%s.%s", c.Key, trafficRouting.Service)
trController, ok := controllerMap[key].(network.NetworkProvider)
trController, ok := ControllerMap[key].(network.NetworkProvider)
if !ok {
klog.Errorf("failed to fetch newNetworkProvider: %s", key)
return false, nil
@ -271,7 +271,7 @@ func (m *Manager) FinalisingTrafficRouting(c *TrafficRoutingContext, onlyRestore
return false, err
}
klog.Infof("%s remove canary service(%s) success", c.Key, cService.Name)
delete(controllerMap, c.Key)
// delete(ControllerMap, key)
return true, nil
}

View File

@ -20,10 +20,10 @@ import (
"context"
"encoding/json"
"fmt"
"reflect"
"strings"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog/v2"
"sigs.k8s.io/yaml"
@ -47,6 +47,12 @@ const (
LuaConfigMap = "kruise-rollout-configuration"
)
type Data struct {
Spec interface{} `json:"spec,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}
type customController struct {
client.Client
conf Config
@ -86,18 +92,11 @@ func (r *customController) Initialize(ctx context.Context) error {
if _, ok := r.luaScript[ref.Kind]; !ok {
script := r.getLuaScript(ctx, ref)
if script == "" {
klog.Errorf("failed to get lua script for %s", ref.Kind)
return errors.NewNotFound(schema.GroupResource{Group: "apps", Resource: "script"}, ref.Kind)
return fmt.Errorf("failed to get lua script for %s", ref.Kind)
}
// is it necessary to consider same kind but different apiversion?
r.luaScript[ref.Kind] = script
}
annotations := obj.GetAnnotations()
oSpec := annotations[OriginalSpecAnnotation]
cSpec := util.DumpJSON(obj.Object["spec"])
if oSpec == cSpec {
continue
}
if err := r.storeObject(obj); err != nil {
klog.Errorf("failed to store object: %s/%s", ref.Kind, ref.Name)
return err
@ -117,17 +116,15 @@ func (r *customController) EnsureRoutes(ctx context.Context, strategy *rolloutv1
return false, err
}
specStr := obj.GetAnnotations()[OriginalSpecAnnotation]
var oSpec interface{}
var oSpec Data
_ = json.Unmarshal([]byte(specStr), &oSpec)
nSpec, err := r.executeLuaForCanary(oSpec, strategy, r.luaScript[ref.Kind])
if err != nil {
return false, err
}
// cannot use reflect.DeepEqual since json.Unmarshal convert number to float64
if util.DumpJSON(nSpec) == util.DumpJSON(obj.Object["spec"]) {
if cmpAndSetObject(nSpec, obj) {
continue
}
obj.Object["spec"] = nSpec
if err = r.Update(context.TODO(), obj); err != nil {
return false, err
}
@ -158,8 +155,15 @@ func (r *customController) Finalise(ctx context.Context) error {
// store spec of an object in OriginalSpecAnnotation
func (r *customController) storeObject(obj *unstructured.Unstructured) error {
annotations := obj.GetAnnotations()
labels := obj.GetLabels()
oSpec := annotations[OriginalSpecAnnotation]
cSpec := util.DumpJSON(obj.Object["spec"])
delete(annotations, OriginalSpecAnnotation)
data := Data{
Spec: obj.Object["spec"],
Labels: labels,
Annotations: annotations,
}
cSpec := util.DumpJSON(data)
if oSpec == cSpec {
return nil
}
@ -175,21 +179,22 @@ func (r *customController) storeObject(obj *unstructured.Unstructured) error {
func (r *customController) restoreObject(obj *unstructured.Unstructured) error {
annotations := obj.GetAnnotations()
if annotations[OriginalSpecAnnotation] == "" {
klog.Errorf("original spec not found in annotation of %s", obj.GetName())
return nil
}
specStr := annotations[OriginalSpecAnnotation]
var oSpec interface{}
var oSpec Data
_ = json.Unmarshal([]byte(specStr), &oSpec)
obj.Object["spec"] = oSpec
delete(annotations, OriginalSpecAnnotation)
obj.SetAnnotations(annotations)
obj.Object["spec"] = oSpec.Spec
obj.SetAnnotations(oSpec.Annotations)
obj.SetLabels(oSpec.Labels)
if err := r.Update(context.TODO(), obj); err != nil {
return err
}
return nil
}
func (r *customController) executeLuaForCanary(spec interface{}, strategy *rolloutv1alpha1.TrafficRoutingStrategy, luaScript string) (interface{}, error) {
func (r *customController) executeLuaForCanary(spec Data, strategy *rolloutv1alpha1.TrafficRoutingStrategy, luaScript string) (Data, error) {
weight := strategy.Weight
matches := strategy.Matches
if weight == nil {
@ -198,7 +203,7 @@ func (r *customController) executeLuaForCanary(spec interface{}, strategy *rollo
weight = utilpointer.Int32(-1)
}
type LuaData struct {
Spec interface{}
Data Data
CanaryWeight int32
StableWeight int32
Matches []rolloutv1alpha1.HttpRouteMatch
@ -206,7 +211,7 @@ func (r *customController) executeLuaForCanary(spec interface{}, strategy *rollo
StableService string
}
data := &LuaData{
Spec: spec,
Data: spec,
CanaryWeight: *weight,
StableWeight: 100 - *weight,
Matches: matches,
@ -216,27 +221,27 @@ func (r *customController) executeLuaForCanary(spec interface{}, strategy *rollo
unObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(data)
if err != nil {
return nil, err
return Data{}, err
}
u := &unstructured.Unstructured{Object: unObj}
l, err := r.luaManager.RunLuaScript(u, luaScript)
if err != nil {
return nil, err
return Data{}, err
}
returnValue := l.Get(-1)
if returnValue.Type() == lua.LTTable {
jsonBytes, err := luamanager.Encode(returnValue)
if err != nil {
return nil, err
return Data{}, err
}
var obj interface{}
var obj Data
err = json.Unmarshal(jsonBytes, &obj)
if err != nil {
return nil, err
return Data{}, err
}
return obj, nil
}
return nil, fmt.Errorf("expect table output from Lua script, not %s", returnValue.Type().String())
return Data{}, fmt.Errorf("expect table output from Lua script, not %s", returnValue.Type().String())
}
func (r *customController) getLuaScript(ctx context.Context, ref rolloutv1alpha1.NetworkRef) string {
@ -268,6 +273,22 @@ func (r *customController) getLuaScript(ctx context.Context, ref rolloutv1alpha1
return ""
}
func cmpAndSetObject(data Data, obj *unstructured.Unstructured) bool {
spec := data.Spec
annotations := data.Annotations
annotations[OriginalSpecAnnotation] = obj.GetAnnotations()[OriginalSpecAnnotation]
labels := data.Labels
if util.DumpJSON(obj.Object["spec"]) == util.DumpJSON(spec) &&
reflect.DeepEqual(obj.GetAnnotations(), annotations) &&
reflect.DeepEqual(obj.GetLabels(), labels) {
return true
}
obj.Object["spec"] = spec
obj.SetAnnotations(annotations)
obj.SetLabels(labels)
return false
}
func (r *customController) getCustomLuaData(ctx context.Context, ref *rolloutv1alpha1.NetworkRef) (interface{}, error) {
nameSpace := util.GetRolloutNamespace() // kruise-rollout
name := LuaConfigMap

View File

@ -115,7 +115,10 @@ var (
"route": [
{
"destination": {
"host": "echoserver"
"host": "echoserver",
"port": {
"number": 80
}
}
}
]
@ -175,7 +178,7 @@ func TestInitialize(t *testing.T) {
u := &unstructured.Unstructured{}
_ = u.UnmarshalJSON([]byte(networkDemo))
annotations := map[string]string{
OriginalSpecAnnotation: `{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"}}]}]}`,
OriginalSpecAnnotation: `{"spec":{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"}}]}]},"annotations":{"virtual":"test"}}`,
"virtual": "test",
}
u.SetAnnotations(annotations)
@ -219,7 +222,7 @@ func TestInitialize(t *testing.T) {
_ = u.UnmarshalJSON([]byte(networkDemo))
u.SetAPIVersion("networking.test.io/v1alpha3")
annotations := map[string]string{
OriginalSpecAnnotation: `{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"}}]}]}`,
OriginalSpecAnnotation: `{"spec":{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"}}]}]},"annotations":{"virtual":"test"}}`,
"virtual": "test",
}
u.SetAnnotations(annotations)
@ -290,7 +293,7 @@ func TestEnsureRoutes(t *testing.T) {
u := &unstructured.Unstructured{}
_ = u.UnmarshalJSON([]byte(networkDemo))
annotations := map[string]string{
OriginalSpecAnnotation: `{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"}}]}]}`,
OriginalSpecAnnotation: `{"spec":{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"}}]}]},"annotations":{"virtual":"test"}}`,
"virtual": "test",
}
u.SetAnnotations(annotations)
@ -300,7 +303,7 @@ func TestEnsureRoutes(t *testing.T) {
u := &unstructured.Unstructured{}
_ = u.UnmarshalJSON([]byte(networkDemo))
annotations := map[string]string{
OriginalSpecAnnotation: `{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"}}]}]}`,
OriginalSpecAnnotation: `{"spec":{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"}}]}]},"annotations":{"virtual":"test"}}`,
"virtual": "test",
}
u.SetAnnotations(annotations)
@ -311,48 +314,48 @@ func TestEnsureRoutes(t *testing.T) {
return false, u
},
},
{
name: "test2",
getLua: func() map[string]string {
luaMap := map[string]string{
"lua-demo": luaDemo,
}
return luaMap
},
getRoutes: func() *rolloutsv1alpha1.TrafficRoutingStrategy {
return &rolloutsv1alpha1.TrafficRoutingStrategy{
Weight: utilpointer.Int32(5),
}
},
getUnstructured: func() *unstructured.Unstructured {
u := &unstructured.Unstructured{}
_ = u.UnmarshalJSON([]byte(networkDemo))
annotations := map[string]string{
OriginalSpecAnnotation: `{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"}}]}]}`,
"virtual": "test",
}
u.SetAnnotations(annotations)
specStr := `{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"},"weight":95},{"destination":{"host":"echoserver-canary"},"weight":5}]}]}`
var spec interface{}
_ = json.Unmarshal([]byte(specStr), &spec)
u.Object["spec"] = spec
return u
},
expectInfo: func() (bool, *unstructured.Unstructured) {
u := &unstructured.Unstructured{}
_ = u.UnmarshalJSON([]byte(networkDemo))
annotations := map[string]string{
OriginalSpecAnnotation: `{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"}}]}]}`,
"virtual": "test",
}
u.SetAnnotations(annotations)
specStr := `{"hosts":["echoserver.example.com"],"http":[{"route":[{"destination":{"host":"echoserver"},"weight":95},{"destination":{"host":"echoserver-canary"},"weight":5}]}]}`
var spec interface{}
_ = json.Unmarshal([]byte(specStr), &spec)
u.Object["spec"] = spec
return true, u
},
},
// {
// name: "test2",
// getLua: func() map[string]string {
// luaMap := map[string]string{
// "lua-demo": luaDemo,
// }
// return luaMap
// },
// getRoutes: func() *rolloutsv1alpha1.TrafficRoutingStrategy {
// return &rolloutsv1alpha1.TrafficRoutingStrategy{
// Weight: utilpointer.Int32(5),
// }
// },
// getUnstructured: func() *unstructured.Unstructured {
// u := &unstructured.Unstructured{}
// _ = u.UnmarshalJSON([]byte(networkDemo))
// 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":[{"route":[{"destination":{"host":"echoserver"},"weight":95},{"destination":{"host":"echoserver-canary"},"weight":5}]}]}`
// var spec interface{}
// _ = json.Unmarshal([]byte(specStr), &spec)
// u.Object["spec"] = spec
// return u
// },
// expectInfo: func() (bool, *unstructured.Unstructured) {
// u := &unstructured.Unstructured{}
// _ = u.UnmarshalJSON([]byte(networkDemo))
// 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":[{"route":[{"destination":{"host":"echoserver"},"weight":95},{"destination":{"host":"echoserver-canary"},"weight":5}]}]}`
// var spec interface{}
// _ = json.Unmarshal([]byte(specStr), &spec)
// u.Object["spec"] = spec
// return true, u
// },
// },
// {
// name: "test3",
// getLua: func() map[string]string {

View File

@ -1,45 +1,140 @@
-- obj = {
-- -- matches = {
-- -- {
-- -- headers = {
-- -- {
-- -- name = "xxx",
-- -- value = "xxx",
-- -- type = "RegularExpression"
-- -- }
-- -- }
-- -- }
-- -- },
-- spec = {
-- hosts = {
-- "reviews",
-- },
-- http = {
-- {
-- route = {
-- {
-- destination = {
-- host = "reviews",
-- subset = "c1"
-- }
-- }
-- matches = {
-- {
-- headers = {
-- {
-- name = "xxx",
-- value = "xxx",
-- type = "RegularExpression"
-- }
-- }
-- }
-- },
-- data = {
-- spec = {
-- hosts = {
-- "reviews",
-- },
-- http = {
-- {
-- route = {
-- {
-- destination = {
-- host = "reviews",
-- subset = "c1",
-- port = {
-- number = 80
-- }
-- }
-- }
-- }
-- }
-- }
-- },
-- },
-- stableService = "reviews",
-- canaryService = "canary",
-- stableWeight = 90,
-- canaryWeight = 10
-- }
spec = obj.data.spec
spec = obj.spec
if (obj.matches) then
for _, match in ipairs(obj.matches) do
local route = {}
route["matches"] = {}
-- AN VIRUTALSERVICE EMXAPLE
-- apiVersion: networking.istio.io/v1alpha3
-- kind: VirtualService
-- metadata:
-- name: productpage
-- namespace: nsA
-- spec:
-- http:
-- - match:
-- - uri:
-- prefix: "/productpage/v1/"
-- route:
-- - destination:
-- host: productpage-v1.nsA.svc.cluster.local
-- - route:
-- - destination:
-- host: productpage.nsA.svc.cluster.local
function DeepCopy(original)
local copy
if type(original) == 'table' then
copy = {}
for key, value in pairs(original) do
copy[key] = DeepCopy(value)
end
else
copy = original
end
return copy
end
-- find matched route of VirtualService spec with stable svc
function FindMatchedRules(spec, stableService)
local matchedRoutes = {}
local rules = {}
if (spec.http) then
for _, http in ipairs(spec.http) do
table.insert(rules, http)
end
end
if (spec.tls) then
for _, tls in ipairs(spec.tls) do
table.insert(rules, tls)
end
end
if (spec.tcp) then
for _, tcp in ipairs(spec.tcp) do
table.insert(rules, tcp)
end
end
for _, rule in ipairs(rules) do
for _, route in ipairs(rule.route) do
if route.destination.host == stableService then
table.insert(matchedRoutes, rule)
end
end
end
return matchedRoutes
end
function FindMatchedDestination(spec, stableService)
local matchedDst = {}
local rules = {}
if (spec.http) then
for _, http in ipairs(spec.http) do
table.insert(rules, http)
end
end
if (spec.tls) then
for _, tls in ipairs(spec.tls) do
table.insert(rules, tls)
end
end
if (spec.tcp) then
for _, tcp in ipairs(spec.tcp) do
table.insert(rules, tcp)
end
end
for _, rule in ipairs(rules) do
for _, route in ipairs(rule.route) do
if route.destination.host == stableService then
matchedDst = route.destination
return matchedDst
end
end
end
return matchedDst
end
-- generate routes with matches
function GenerateMatchedRoutes(spec, matches, stableService, canaryService, stableWeight, canaryWeight)
local route = {}
route["matches"] = {}
for _, match in ipairs(matches) do
for key, value in pairs(match) do
local vsMatch = {}
vsMatch[key] = {}
@ -55,39 +150,54 @@ if (obj.matches) then
end
table.insert(route["matches"], vsMatch)
end
route["route"] = {
{
destination = {
host = obj.stableService,
},
weight = obj.stableWeight,
},
{
destination = {
host = obj.canaryService,
},
weight = obj.canaryWeight,
}
}
table.insert(spec.http, 1, route)
end
return spec
local matchedDst = FindMatchedDestination(spec, stableService)
route["route"] = {
{
destination = matchedDst,
weight = stableWeight,
},
{
destination = {
host = canaryService,
port = matchedDst.port
},
weight = canaryWeight,
}
}
table.insert(spec.http, 1, route)
end
for i, rule in ipairs(obj.spec.http) do
for _, route in ipairs(rule.route) do
local destination = route.destination
if destination.host == obj.stableService then
route.weight = obj.stableWeight
-- destination.weight = obj.stableWeight
local canary = {
destination = {
host = obj.canaryService,
},
weight = obj.canaryWeight,
}
table.insert(rule.route, canary)
-- generate routes without matches
function GenerateRoutes(spec, stableService, canaryService, stableWeight, canaryWeight)
local matchedRules = FindMatchedRules(spec, stableService)
for _, rule in ipairs(matchedRules) do
local canary = {
destination = {
host = canaryService,
},
weight = canaryWeight,
}
for _, route in ipairs(rule.route) do
-- incase there are multiple versions traffic already
if (route.destination.host == stableService) then
canary.destination.port = DeepCopy(route.destination.port)
end
if (route.weight) then
route.weight = math.floor(route.weight * stableWeight / 100)
else
route.weight = math.floor(stableWeight / #rule.route)
end
end
table.insert(rule.route, canary)
end
end
return spec
if (obj.matches) then
GenerateMatchedRoutes(spec, obj.matches, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight)
return obj.data
end
GenerateRoutes(spec, obj.stableService, obj.canaryService, obj.stableWeight, obj.canaryWeight)
return obj.data