Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions cluster/router/script/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,46 @@ func parseRoute(routeContent string) (*global.RouterConfig, error) {
return routerConfig, nil
}

// SetStaticConfig applies a RouterConfig directly, bypassing YAML parsing.
// Static and dynamic rules are not merged: later Process updates replace the
// current state built here.
func (s *ScriptRouter) SetStaticConfig(cfg *global.RouterConfig) {
if cfg == nil || cfg.Scope != constant.RouterScopeApplication || cfg.Key == "" || cfg.ScriptType == "" || cfg.Script == "" {
return
}
Comment thread
Copilot marked this conversation as resolved.

s.mu.Lock()
defer s.mu.Unlock()

if s.enabled && s.scriptType != "" {
in, err := ins.GetInstances(s.scriptType)
if err != nil {
logger.Errorf("[Router][Script] GetInstances failed to destroy static config, error=%v", err)
} else {
in.Destroy(s.rawScript)
}
}

s.scriptType = cfg.ScriptType
s.rawScript = cfg.Script
s.enabled = cfg.Enabled == nil || *cfg.Enabled
if !s.enabled {
logger.Infof("[Router][Script] static config is disabled, key=%s, type=%s", cfg.Key, cfg.ScriptType)
return
}

in, err := ins.GetInstances(s.scriptType)
if err != nil {
logger.Errorf("[Router][Script] GetInstances failed for static config, error=%v", err)
s.enabled = false
return
}
if err = in.Compile(s.rawScript); err != nil {
s.enabled = false
logger.Errorf("[Router][Script] compile static script failed, error=%v", err)
}
}

func (s *ScriptRouter) Process(event *config_center.ConfigChangeEvent) {
s.mu.Lock()
defer s.mu.Unlock()
Expand Down
140 changes: 140 additions & 0 deletions cluster/router/script/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ import (

import (
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/config_center"
"dubbo.apache.org/dubbo-go/v3/global"
"dubbo.apache.org/dubbo-go/v3/protocol/base"
"dubbo.apache.org/dubbo-go/v3/protocol/invocation"
"dubbo.apache.org/dubbo-go/v3/remoting"
Expand Down Expand Up @@ -257,6 +259,144 @@ func TestScriptRouterProcessDelSkipsConfigBody(t *testing.T) {
assert.Empty(t, s.rawScript)
}

func TestScriptRouterSetStaticConfig(t *testing.T) {
staticScriptForPort := func(port string) string {
return `(function route(invokers, invocation, context) {
var result = [];
for (var i = 0; i < invokers.length; i++) {
if (invokers[i].GetURL().Port === "` + port + `") {
result.push(invokers[i]);
}
}
return result;
}(invokers, invocation, context));`
}

t.Run("apply static script config", func(t *testing.T) {
invokers, inv, _ := getRouteCheckArgs()
s := NewScriptRouter()
s.SetStaticConfig(&global.RouterConfig{
Scope: constant.RouterScopeApplication,
Key: "dubbo.io",
ScriptType: "javascript",
Script: staticScriptForPort("20001"),
})

got := s.Route(invokers, nil, inv)
assert.Len(t, got, 1)
assert.Equal(t, "20001", got[0].GetURL().Port)
})

t.Run("ignore disabled static script config", func(t *testing.T) {
invokers, inv, _ := getRouteCheckArgs()
enabled := false
s := NewScriptRouter()
s.SetStaticConfig(&global.RouterConfig{
Scope: constant.RouterScopeApplication,
Key: "dubbo.io",
Enabled: &enabled,
ScriptType: "javascript",
Script: staticScriptForPort("20001"),
})

got := s.Route(invokers, nil, inv)
assert.True(t, checkInvokersSame(got, invokers))
})

t.Run("ignore config without script", func(t *testing.T) {
invokers, inv, _ := getRouteCheckArgs()
s := NewScriptRouter()
s.SetStaticConfig(&global.RouterConfig{
Scope: constant.RouterScopeApplication,
Key: "dubbo.io",
})

got := s.Route(invokers, nil, inv)
assert.True(t, checkInvokersSame(got, invokers))
})

t.Run("ignore config without key", func(t *testing.T) {
invokers, inv, _ := getRouteCheckArgs()
s := NewScriptRouter()
s.SetStaticConfig(&global.RouterConfig{
Scope: constant.RouterScopeApplication,
ScriptType: "javascript",
Script: staticScriptForPort("20001"),
})

got := s.Route(invokers, nil, inv)
assert.True(t, checkInvokersSame(got, invokers))
})

t.Run("replace previous static script config", func(t *testing.T) {
invokers, inv, _ := getRouteCheckArgs()
s := NewScriptRouter()
s.SetStaticConfig(&global.RouterConfig{
Scope: constant.RouterScopeApplication,
Key: "dubbo.io",
ScriptType: "javascript",
Script: staticScriptForPort("20001"),
})
s.SetStaticConfig(&global.RouterConfig{
Scope: constant.RouterScopeApplication,
Key: "dubbo.io",
ScriptType: "javascript",
Script: staticScriptForPort("20002"),
})

got := s.Route(invokers, nil, inv)
assert.Len(t, got, 1)
assert.Equal(t, "20002", got[0].GetURL().Port)
})

t.Run("replace stale config with unsupported script type", func(t *testing.T) {
invokers, inv, _ := getRouteCheckArgs()
s := &ScriptRouter{
enabled: true,
scriptType: "unsupported",
rawScript: "old script",
}
s.SetStaticConfig(&global.RouterConfig{
Scope: constant.RouterScopeApplication,
Key: "dubbo.io",
ScriptType: "javascript",
Script: staticScriptForPort("20001"),
})

got := s.Route(invokers, nil, inv)
assert.Len(t, got, 1)
assert.Equal(t, "20001", got[0].GetURL().Port)
})

t.Run("disable unsupported script type", func(t *testing.T) {
invokers, inv, _ := getRouteCheckArgs()
s := NewScriptRouter()
s.SetStaticConfig(&global.RouterConfig{
Scope: constant.RouterScopeApplication,
Key: "dubbo.io",
ScriptType: "unsupported",
Script: staticScriptForPort("20001"),
})

got := s.Route(invokers, nil, inv)
assert.True(t, checkInvokersSame(got, invokers))
})

t.Run("disable invalid static script", func(t *testing.T) {
invokers, inv, _ := getRouteCheckArgs()
s := NewScriptRouter()
s.SetStaticConfig(&global.RouterConfig{
Scope: constant.RouterScopeApplication,
Key: "dubbo.io",
ScriptType: "javascript",
Script: "bad input",
})

got := s.Route(invokers, nil, inv)
assert.True(t, checkInvokersSame(got, invokers))
})
}

func checkInvokersSame(invokers []base.Invoker, otherInvokers []base.Invoker) bool {
k := map[string]struct{}{}
for _, invoker := range otherInvokers {
Expand Down
Loading