Skip to content

Commit

Permalink
add support for custom network providers
Browse files Browse the repository at this point in the history
Signed-off-by: Kuromesi <[email protected]>
  • Loading branch information
Kuromesi committed Aug 31, 2023
1 parent 76d33b8 commit 1a3d945
Show file tree
Hide file tree
Showing 10 changed files with 934 additions and 2 deletions.
8 changes: 8 additions & 0 deletions api/v1alpha1/trafficrouting_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ type TrafficRoutingRef struct {
// Gateway holds Gateway specific configuration to route traffic
// Gateway configuration only supports >= v0.4.0 (v1alpha2).
Gateway *GatewayTrafficRouting `json:"gateway,omitempty"`
// CustomNetworkRefs hold a list of custom providers to route traffic
CustomNetworkRefs *[]CustomNetworkRef `json:"networkRefs,omitempty"`
}

// IngressTrafficRouting configuration for ingress controller to control traffic routing
Expand Down Expand Up @@ -149,6 +151,12 @@ type TrafficRoutingList struct {
Items []TrafficRouting `json:"items"`
}

type CustomNetworkRef struct {
APIVersion string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty"`
Name string `json:"name,omitempty"`
}

func init() {
SchemeBuilder.Register(&TrafficRouting{}, &TrafficRoutingList{})
}
24 changes: 24 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions config/crd/bases/rollouts.kruise.io_rollouts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,19 @@ spec:
required:
- name
type: object
networkRefs:
description: CustomNetworkRefs hold a list of custom
providers to route traffic
items:
properties:
apiVersion:
type: string
kind:
type: string
name:
type: string
type: object
type: array
service:
description: Service holds the name of a service which
selects pods with stable version and don't select
Expand Down
13 changes: 13 additions & 0 deletions config/crd/bases/rollouts.kruise.io_trafficroutings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ spec:
required:
- name
type: object
networkRefs:
description: CustomNetworkRefs hold a list of custom providers
to route traffic
items:
properties:
apiVersion:
type: string
kind:
type: string
name:
type: string
type: object
type: array
service:
description: Service holds the name of a service which selects
pods with stable version and don't select any pods with canary
Expand Down
205 changes: 205 additions & 0 deletions lua_configuration/lua.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package main

import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"

"github.com/openkruise/rollouts/api/v1alpha1"
"github.com/openkruise/rollouts/pkg/util/luamanager"
lua "github.com/yuin/gopher-lua"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/yaml"

utilpointer "k8s.io/utils/pointer"
)

type LuaData struct {
Data Data
CanaryWeight int32
StableWeight int32
Matches []v1alpha1.HttpRouteMatch
CanaryService string
StableService string
PatchPodMetadata *v1alpha1.PatchPodTemplateMetadata
}

type Data struct {
Spec interface{} `json:"spec,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}

type TestCase struct {
Rollout *v1alpha1.Rollout `json:"rollout,omitempty"`
Original *unstructured.Unstructured `json:"original,omitempty"`
Expected []*unstructured.Unstructured `json:"expected,omitempty"`
}

// convert testdata to lua object for debugging
func main() {
err := PathWalk()
if err != nil {
fmt.Println(err)
}
}

func PathWalk() error {
err := filepath.Walk("./", func(path string, f os.FileInfo, err error) error {
if !strings.Contains(path, "trafficRouting.lua") {
return nil
}
if err != nil {
return fmt.Errorf("failed to walk path: %s", err.Error())
}
dir := filepath.Dir(path)
err = filepath.Walk(filepath.Join(dir, "testdata"), func(path string, info os.FileInfo, err error) error {
if !info.IsDir() && filepath.Ext(path) == ".yaml" || filepath.Ext(path) == ".yml" {
fmt.Printf("--- walking path: %s ---\n", path)
err = ObjectToTable(path)
if err != nil {
return fmt.Errorf("failed to convert object to table: %s", err)
}
}
return nil
})
if err != nil {
return fmt.Errorf("failed to walk path: %s", err.Error())
}
return nil
})
if err != nil {
return fmt.Errorf("failed to walk path: %s", err)
}
return nil
}

// convert a testcase object to lua table for debug
func ObjectToTable(path string) error {
luaManager := &luamanager.LuaManager{}
dir, file := filepath.Split(path)
testCase, err := getLuaTestCase(path)
if err != nil {
return fmt.Errorf("failed to get lua testcase: %s", err)
}
uList := make(map[string]interface{})
rollout := testCase.Rollout
steps := rollout.Spec.Strategy.Canary.Steps
for i, step := range steps {
weight := step.TrafficRoutingStrategy.Weight
if step.TrafficRoutingStrategy.Weight == nil {
weight = utilpointer.Int32(-1)
}
var canaryService string
stableService := rollout.Spec.Strategy.Canary.TrafficRoutings[0].Service
// if rollout.Spec.Strategy.Canary.TrafficRoutings[0].CreateCanaryService {
canaryService = fmt.Sprintf("%s-canary", stableService)
// } else {
// canaryService = stableService
// }
data := &LuaData{
Data: Data{
Labels: testCase.Original.GetLabels(),
Annotations: testCase.Original.GetAnnotations(),
Spec: testCase.Original.Object["spec"],
},
Matches: step.TrafficRoutingStrategy.Matches,
CanaryWeight: *weight,
StableWeight: 100 - *weight,
CanaryService: canaryService,
StableService: stableService,
PatchPodMetadata: rollout.Spec.Strategy.Canary.PatchPodTemplateMetadata,
}
uList[fmt.Sprintf("step_%d", i)] = data
}
unObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&uList)
if err != nil {
return fmt.Errorf("failed to convert to unstructured: %s", err)
}
u := &unstructured.Unstructured{Object: unObj}
script := `
function serialize(obj, isKey)
local lua = ""
local t = type(obj)
if t == "number" then
lua = lua .. obj
elseif t == "boolean" then
lua = lua .. tostring(obj)
elseif t == "string" then
if isKey then
lua = lua .. string.format("%s", obj)
else
lua = lua .. string.format("%q", obj)
end
elseif t == "table" then
lua = lua .. "{"
for k, v in pairs(obj) do
if type(k) == "string" then
lua = lua .. serialize(k, true) .. "=" .. serialize(v, false) .. ","
else
lua = lua .. serialize(v, false) .. ","
end
end
local metatable = getmetatable(obj)
if metatable ~= nil and type(metatable.__index) == "table" then
for k, v in pairs(metatable.__index) do
if type(k) == "string" then
lua = lua .. serialize(k, true) .. "=" .. serialize(v, false) .. ","
else
lua = lua .. serialize(v, false) .. ","
end
end
end
lua = lua .. "}"
elseif t == "nil" then
return nil
else
error("can not serialize a " .. t .. " type.")
end
return lua
end
function table2string(tablevalue)
local stringtable = "steps=" .. serialize(tablevalue)
print(stringtable)
return stringtable
end
return table2string(obj)
`
l, err := luaManager.RunLuaScript(u, script)
returnValue := l.Get(-1)
if returnValue.Type() == lua.LTString {
filePath := fmt.Sprintf("%s%s_obj.lua", dir, strings.Split(file, ".")[0])
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
if err != nil {
return fmt.Errorf("failed to open file: %s", err)
}
defer file.Close()
v := returnValue.String()
_, err = io.WriteString(file, v)
if err != nil {
return fmt.Errorf("failed to WriteString %s", err)
}
}
if err != nil {
return fmt.Errorf("failed to run lua script: %s", err)
}
return nil
}

func getLuaTestCase(path string) (*TestCase, error) {
yamlFile, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
luaTestCase := &TestCase{}
err = yaml.Unmarshal(yamlFile, luaTestCase)
if err != nil {
return nil, err
}
return luaTestCase, nil
}
11 changes: 11 additions & 0 deletions pkg/trafficrouting/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/openkruise/rollouts/api/v1alpha1"
"github.com/openkruise/rollouts/pkg/trafficrouting/network"
"github.com/openkruise/rollouts/pkg/trafficrouting/network/custom"
"github.com/openkruise/rollouts/pkg/trafficrouting/network/gateway"
"github.com/openkruise/rollouts/pkg/trafficrouting/network/ingress"
"github.com/openkruise/rollouts/pkg/util"
Expand Down Expand Up @@ -263,6 +264,16 @@ func (m *Manager) FinalisingTrafficRouting(c *TrafficRoutingContext, onlyRestore

func newNetworkProvider(c client.Client, con *TrafficRoutingContext, sService, cService string) (network.NetworkProvider, error) {
trafficRouting := con.ObjectRef[0]
if trafficRouting.CustomNetworkRefs != nil {
return custom.NewCustomController(c, custom.Config{
RolloutName: con.Key,
RolloutNs: con.Namespace,
CanaryService: cService,
StableService: sService,
TrafficConf: *trafficRouting.CustomNetworkRefs,
OwnerRef: con.OwnerRef,
})

Check warning on line 275 in pkg/trafficrouting/manager.go

View check run for this annotation

Codecov / codecov/patch

pkg/trafficrouting/manager.go#L268-L275

Added lines #L268 - L275 were not covered by tests
}
if trafficRouting.Ingress != nil {
return ingress.NewIngressTrafficRouting(c, ingress.Config{
Key: con.Key,
Expand Down
Loading

0 comments on commit 1a3d945

Please sign in to comment.