Skip to content

Commit

Permalink
feat: sentinel plugin (#740)
Browse files Browse the repository at this point in the history
  • Loading branch information
WeixinX authored Oct 21, 2024
1 parent 715dd41 commit 8f81a79
Show file tree
Hide file tree
Showing 22 changed files with 2,853 additions and 263 deletions.
5 changes: 4 additions & 1 deletion .licenserc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,7 @@ dependency:
- name: github.com/ajstarks/svgo # use CC-BY-4.0 License
- name: github.com/golang/freetype # dual license: we choose the FreeType License, which is similar to the original BSD license with an advertising clause
- name: github.com/chzyer/logex # use MIT License
- name: github.com/JohnCGriffin/overflow # use MIT License added in the README
- name: github.com/JohnCGriffin/overflow # use MIT License added in the README
- name: github.com/hudl/fargo # use BSD-2-Clause license, which is not recognized in sentinel-golang 1.0.4
- name: github.com/lightstep/lightstep-tracer-common/golang/gogo # use MIT license, which is not recognized in sentinel-golang 1.0.4
- name: github.com/streadway/handy # use BSD-2-Clause license, which is not recognized in sentinel-golang 1.0.4
3 changes: 3 additions & 0 deletions maintainer/feature_maturity_level.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ plugins:
- name: route_patch
status: experimental
experimental_since: 0.4.1
- name: sentinel
status: experimental
experimental_since: 0.5.0
- name: tls_inspector
status: experimental
experimental_since: 0.4.0
Expand Down
8 changes: 8 additions & 0 deletions plugins/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ go 1.21.5

require (
github.com/agiledragon/gomonkey/v2 v2.11.0
github.com/alibaba/sentinel-golang v1.0.4
github.com/avast/retry-go v3.0.0+incompatible
github.com/casbin/casbin/v2 v2.88.0
github.com/coreos/go-oidc/v3 v3.10.0
Expand All @@ -44,6 +45,7 @@ require (
require (
cel.dev/expr v0.15.0 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
Expand All @@ -60,22 +62,28 @@ require (
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.20.2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
github.com/shirou/gopsutil/v3 v3.21.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/tklauser/go-sysconf v0.3.6 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
Expand Down
386 changes: 386 additions & 0 deletions plugins/go.sum

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions plugins/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ import (
_ "mosn.io/htnn/plugins/plugins/limitreq"
_ "mosn.io/htnn/plugins/plugins/oidc"
_ "mosn.io/htnn/plugins/plugins/opa"
_ "mosn.io/htnn/plugins/plugins/sentinel"
)
115 changes: 115 additions & 0 deletions plugins/plugins/sentinel/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright The HTNN Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sentinel

import (
sentinelApi "github.com/alibaba/sentinel-golang/api"
sentinelConf "github.com/alibaba/sentinel-golang/core/config"
"github.com/alibaba/sentinel-golang/logging"

"mosn.io/htnn/api/pkg/filtermanager/api"
"mosn.io/htnn/api/pkg/plugins"
"mosn.io/htnn/plugins/plugins/sentinel/rules"
"mosn.io/htnn/types/plugins/sentinel"
)

func init() {
plugins.RegisterPlugin(sentinel.Name, &plugin{})
}

type plugin struct {
sentinel.Plugin
}

func (p *plugin) Factory() api.FilterFactory {
return factory
}

func (p *plugin) Config() api.PluginConfig {
return &config{}
}

type config struct {
sentinel.CustomConfig

params sentinelApi.EntryOption
attachments []*sentinel.Source
m *res2RuleMap
}

type res2RuleMap struct {
f map[string]*sentinel.FlowRule
hs map[string]*sentinel.HotSpotRule
cb map[string]*sentinel.CircuitBreakerRule
}

func (conf *config) Init(cb api.ConfigCallbackHandler) error {
sc := sentinelConf.NewDefaultConfig()

// Sentinel-golang logs come in two types: metric and record the log.
// See https://sentinelguard.io/zh-cn/docs/golang/logging.html.
sc.Sentinel.Log.Dir = conf.GetLogDir()
if conf.GetLogDir() == "" {
// When we want the log output to the console, set Dir = "" and Logger = logging.NewConsoleLogger() to
// output the record log to the console as expected, but the metric log will still be output to the default
// directory (~/logs/csp), so we should set Metric.FlushIntervalSec == 0 to disable metric log.
sc.Sentinel.Log.Logger = logging.NewConsoleLogger()
sc.Sentinel.Log.Metric.FlushIntervalSec = 0
}

if err := sentinelApi.InitWithConfig(sc); err != nil {
return err
}

if err := loadRules(conf); err != nil {
return err
}

return nil
}

func loadRules(conf *config) error {
conf.m = &res2RuleMap{
f: make(map[string]*sentinel.FlowRule),
hs: make(map[string]*sentinel.HotSpotRule),
cb: make(map[string]*sentinel.CircuitBreakerRule),
}

conf.params = sentinelApi.WithArgs()
conf.attachments = make([]*sentinel.Source, 0)
hs := conf.GetHotSpot()
if hs != nil {
args := make([]interface{}, len(hs.GetParams()))
for i, p := range hs.GetParams() {
args[i] = p
}
conf.params = sentinelApi.WithArgs(args...)
conf.attachments = hs.GetAttachments()
}

if err := rules.LoadFlowRules(conf.GetFlow(), conf.m.f); err != nil {
return err
}

if err := rules.LoadHotSpotRules(hs, conf.m.hs); err != nil {
return err
}

if err := rules.LoadCircuitBreakerRules(conf.GetCircuitBreaker(), conf.m.cb); err != nil {
return err
}

return nil
}
99 changes: 99 additions & 0 deletions plugins/plugins/sentinel/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright The HTNN Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sentinel

import (
"testing"

"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/encoding/protojson"
)

func TestConfig(t *testing.T) {
tests := []struct {
name string
input string
err string
}{
{
name: "resource are required",
input: `{}`,
err: "invalid Config.Resource: value is required",
},
{
name: "one of flow, hotSpot, circuitBreaker is required",
input: `{"resource": {"from": "HEADER", "key": "test"}}`,
err: "config must have at least one of 'flow', 'hotSpot', 'circuitBreaker'",
},
{
name: "flow err: threshold must be greater than 0",
input: `{"resource": {"from": "HEADER", "key": "test"}, "flow": {"rules": [{"resource": "flow"}]}}`,
err: "'threshold' must be greater than 0",
},
{
name: "flow ok: current resource",
input: `{"resource": {"from": "HEADER", "key": "test"}, "flow": {"rules": [{"resource": "flow", "threshold": 10}]}}`,
err: "",
},
{
name: "flow ok: related resource",
input: `{"resource": {"from": "HEADER", "key": "test"}, "flow": {"rules": [{"resource": "f2", "relationStrategy": "ASSOCIATED_RESOURCE", "refResource": "f1"}]}}`,
err: "",
},
{
name: "hot spot err: one of params, attachments is required",
input: `{"resource": {"from": "HEADER", "key": "test"}, "hotSpot": {}}`,
err: "'params' and 'attachments' cannot both be empty",
},
{
name: "hot spot err: threshold must be greater than 0",
input: `{"resource": {"from": "HEADER", "key": "test"}, "hotSpot": {"params": ["test"], "rules": [{"resource": "hs"}]}}`,
err: "invalid HotSpotRule.Threshold: value must be greater than 0",
},
{
name: "hot spot ok",
input: `{"resource": {"from": "HEADER", "key": "test"}, "hotSpot": {"params": ["test"], "rules": [{"resource": "hs", "metricType": "QPS", "threshold": 10}]}}`,
err: "",
},
{
name: "circuit breaker err: threshold must be greater than 0",
input: `{"resource": {"from": "HEADER", "key": "test"}, "circuitBreaker": {"rules": [{"resource": "cb"}]}}`,
err: "invalid CircuitBreakerRule.Threshold: value must be greater than 0",
},
{
name: "circuit breaker ok",
input: `{"resource": {"from": "HEADER", "key": "test"}, "circuitBreaker": {"rules": [{"resource": "cb", "threshold": 10}]}}`,
err: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
conf := &config{}
err := protojson.Unmarshal([]byte(tt.input), conf)
if err == nil {
err = conf.Validate()
}
if tt.err == "" {
assert.Nil(t, err)
err = conf.Init(nil)
assert.Nil(t, err)
} else {
assert.ErrorContains(t, err, tt.err)
}
})
}

}
Loading

0 comments on commit 8f81a79

Please sign in to comment.