From 281b61deadc539d89604cff592c4ce94eea71799 Mon Sep 17 00:00:00 2001 From: samanhappy Date: Thu, 21 Jul 2022 15:02:14 +0800 Subject: [PATCH] feat: support plugin mode output check for probe (#172) --- Makefile | 2 +- README.md | 21 +++++++++++++++++++++ probe/common.go | 39 ++++++++++++++++++++++++++++++++++++++- probe/common_test.go | 26 ++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6c6a4dbb..4c7a3e68 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ all: ${TARGET} ${TARGET}: ${SOURCE} mkdir -p ${RELEASE_DIR} go mod tidy - CGO_ENABLED=0 go build -a -ldflags '-s -w -extldflags "-static"' -gcflags=-G=3 -o ${TARGET} ${MKFILE_DIR}cmd/easeprobe + CGO_ENABLED=1 go build -a -ldflags '-s -w' -gcflags=-G=3 -o ${TARGET} ${MKFILE_DIR}cmd/easeprobe build: all diff --git a/README.md b/README.md index 401f8a92..5e3bfb32 100644 --- a/README.md +++ b/README.md @@ -575,6 +575,9 @@ http: contain: "success" # response body must contain this string, if not the probe is considered failed. not_contain: "failure" # response body must NOT contain this string, if it does the probe is considered failed. regex: false # if true, the contain and not_contain will be treated as regular expression. default: false + plugin: false # if true, the plugin_path and plugin_func will be used to do arbitrary probe. default: false + plugin_path: "plugin.so" # the plugin file used for plugin mode + plugin_func: "Check" # the plugin func used for plugin mode # configuration timeout: 10s # default is 30 seconds @@ -584,6 +587,24 @@ http: > > The Regular Expression supported refer to https://github.com/google/re2/wiki/Syntax +> **Note**: +> +> The Plugin Mode supported refer to https://pkg.go.dev/plugin +> +> A similar check plugin implement maybe like this +> ```CODE +> package main +> +> // the Input Name is defined, cannot change! +> var Input string +> +> func Check() (err error) { +> +> // TODO check logic +> +> return nil +> } +> ``` ### 3.2 TCP Probe Configuration diff --git a/probe/common.go b/probe/common.go index fa36730c..0d57e75e 100644 --- a/probe/common.go +++ b/probe/common.go @@ -19,6 +19,7 @@ package probe import ( "fmt" + "plugin" "regexp" "strings" ) @@ -28,6 +29,9 @@ type TextChecker struct { Contain string `yaml:"contain,omitempty"` NotContain string `yaml:"not_contain,omitempty"` RegExp bool `yaml:"regex,omitempty"` + Plugin bool `yaml:"plugin,omitempty"` + PluginPath string `yaml:"plugin_path,omitempty"` + PluginFunc string `yaml:"plugin_func,omitempty"` containReg *regexp.Regexp `yaml:"-"` notContainReg *regexp.Regexp `yaml:"-"` @@ -35,7 +39,7 @@ type TextChecker struct { // Config the text checker initialize the regexp func (tc *TextChecker) Config() (err error) { - if !tc.RegExp { + if !tc.RegExp && !tc.Plugin { return nil } @@ -61,6 +65,9 @@ func (tc *TextChecker) Check(Text string) error { if tc.RegExp { return tc.CheckRegExp(Text) } + if tc.Plugin { + return tc.CheckPlugin(Text) + } return tc.CheckText(Text) } @@ -68,6 +75,9 @@ func (tc *TextChecker) String() string { if tc.RegExp { return fmt.Sprintf("RegExp Mode - Contain:[%s], NotContain:[%s]", tc.Contain, tc.NotContain) } + if tc.Plugin { + return fmt.Sprintf("Plugin Mode - PluginPath:[%s], PluginFunc:[%s]", tc.PluginPath, tc.PluginFunc) + } return fmt.Sprintf("Text Mode - Contain:[%s], NotContain:[%s]", tc.Contain, tc.NotContain) } @@ -101,6 +111,33 @@ func (tc *TextChecker) CheckRegExp(Output string) error { return nil } +// CheckPlugin checks the output text using plugin defined by user +func (tc *TextChecker) CheckPlugin(Output string) error { + + p, err := plugin.Open(tc.PluginPath) + if err != nil { + return fmt.Errorf("failed to open plugin: %v", err) + } + + input, err := p.Lookup("Input") + if err != nil { + return fmt.Errorf("failed to lookup value: %v", err) + } + + f, err := p.Lookup(tc.PluginFunc) + if err != nil { + return fmt.Errorf("failed to lookup func: %v", err) + } + + *input.(*string) = Output + err = f.(func() error)() + if err != nil { + return fmt.Errorf("the output does not match the plugin func [%s]", err) + } + + return nil +} + // CheckEmpty return "empty" if the string is empty func CheckEmpty(s string) string { if len(strings.TrimSpace(s)) <= 0 { diff --git a/probe/common_test.go b/probe/common_test.go index bc9183eb..105fb975 100644 --- a/probe/common_test.go +++ b/probe/common_test.go @@ -154,3 +154,29 @@ func TestCheckEmpty(t *testing.T) { assert.Equal(t, "empty", CheckEmpty("\n\r\t")) assert.Equal(t, "empty", CheckEmpty(" \n\r\t ")) } + +func TestCheckPlugin(t *testing.T) { + checker := TextChecker{ + Plugin: true, + PluginPath: "xxx.so", + PluginFunc: "Check", + } + checker.Config() + assert.NotNil(t, checker.Check("hello world")) + + checker = TextChecker{ + Plugin: true, + PluginPath: "plugin.so", + PluginFunc: "xxx", + } + checker.Config() + assert.NotNil(t, checker.Check("hello world")) + + checker = TextChecker{ + Plugin: true, + PluginPath: "easeprobe-plugin.so", + PluginFunc: "Check", + } + checker.Config() + assert.NotNil(t, checker.Check("hello world")) +}