Skip to content

Commit

Permalink
add support for custom network provider
Browse files Browse the repository at this point in the history
Signed-off-by: Kuromesi <[email protected]>
  • Loading branch information
Kuromesi committed Aug 30, 2023
1 parent 76d33b8 commit 66048cb
Show file tree
Hide file tree
Showing 16 changed files with 1,062 additions and 33 deletions.
13 changes: 12 additions & 1 deletion api/v1alpha1/trafficrouting_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ type TrafficRoutingRef struct {
Ingress *IngressTrafficRouting `json:"ingress,omitempty"`
// Gateway holds Gateway specific configuration to route traffic
// Gateway configuration only supports >= v0.4.0 (v1alpha2).
Gateway *GatewayTrafficRouting `json:"gateway,omitempty"`
Gateway *GatewayTrafficRouting `json:"gateway,omitempty"`
NetworkRefs *[]NetworkRef `json:"networkRefs,omitempty"`
// +optional
//+kubebuilder:default=true
// create a new canary service or just use the stable service
CreateCanaryService bool `json:"createCanaryService,omitempty"`
}

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

type NetworkRef 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.

16 changes: 16 additions & 0 deletions config/crd/bases/rollouts.kruise.io_rollouts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,11 @@ spec:
for supported service meshes to enable more fine-grained
traffic routing
properties:
createCanaryService:
default: true
description: create a new canary service or just use
the stable service
type: boolean
gateway:
description: Gateway holds Gateway specific configuration
to route traffic Gateway configuration only supports
Expand Down Expand Up @@ -373,6 +378,17 @@ spec:
required:
- name
type: object
networkRefs:
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
16 changes: 16 additions & 0 deletions config/crd/bases/rollouts.kruise.io_trafficroutings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ spec:
for supported service meshes to enable more fine-grained traffic
routing
properties:
createCanaryService:
default: true
description: create a new canary service or just use the stable
service
type: boolean
gateway:
description: Gateway holds Gateway specific configuration to
route traffic Gateway configuration only supports >= v0.4.0
Expand Down Expand Up @@ -85,6 +90,17 @@ spec:
required:
- name
type: object
networkRefs:
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
}
1 change: 1 addition & 0 deletions pkg/controller/rollout/rollout_canary.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ func (m *canaryReleaseManager) doCanaryFinalising(c *RolloutContext) (bool, erro
} else if !done {
return false, nil
}
m.trafficRoutingManager.RemoveTrafficRoutingController(tr)
klog.Infof("rollout(%s/%s) doCanaryFinalising success", c.Rollout.Namespace, c.Rollout.Name)
return true, nil
}
Expand Down
1 change: 1 addition & 0 deletions pkg/controller/rollout/rollout_progressing.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,5 +466,6 @@ func newTrafficRoutingContext(c *RolloutContext) *trafficrouting.TrafficRoutingC
StableRevision: c.NewStatus.CanaryStatus.StableRevision,
CanaryRevision: c.NewStatus.CanaryStatus.PodTemplateHash,
LastUpdateTime: c.NewStatus.CanaryStatus.LastUpdateTime,
PatchPodMetadata: c.Rollout.Spec.Strategy.Canary.PatchPodTemplateMetadata,
}
}
11 changes: 5 additions & 6 deletions pkg/controller/trafficrouting/trafficrouting_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,10 @@ func (r *TrafficRoutingReconciler) SetupWithManager(mgr ctrl.Manager) error {

func newTrafficRoutingContext(tr *v1alpha1.TrafficRouting) *trafficrouting.TrafficRoutingContext {
return &trafficrouting.TrafficRoutingContext{
Key: fmt.Sprintf("TrafficRouting(%s/%s)", tr.Namespace, tr.Name),
Namespace: tr.Namespace,
ObjectRef: tr.Spec.ObjectRef,
Strategy: tr.Spec.Strategy,
OwnerRef: *metav1.NewControllerRef(tr, trControllerKind),
OnlyTrafficRouting: true,
Key: fmt.Sprintf("TrafficRouting(%s/%s)", tr.Namespace, tr.Name),
Namespace: tr.Namespace,
ObjectRef: tr.Spec.ObjectRef,
Strategy: tr.Spec.Strategy,
OwnerRef: *metav1.NewControllerRef(tr, trControllerKind),
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ var (
Spec: v1alpha1.TrafficRoutingSpec{
ObjectRef: []v1alpha1.TrafficRoutingRef{
{
Service: "echoserver",
Service: "echoserver",
CreateCanaryService: false,
Ingress: &v1alpha1.IngressTrafficRouting{
Name: "echoserver",
},
Expand Down Expand Up @@ -379,6 +380,7 @@ func TestTrafficRoutingTest(t *testing.T) {
for _, obj := range ig {
checkObjEqual(client, t, obj)
}
manager.trafficRoutingManager.RemoveTrafficRoutingController(newTrafficRoutingContext(tr))
})
}
}
Expand Down
Loading

0 comments on commit 66048cb

Please sign in to comment.