Skip to content

Commit

Permalink
Poc cleanups
Browse files Browse the repository at this point in the history
  • Loading branch information
Tit Petric committed Sep 16, 2024
1 parent 320fb05 commit a40d1d1
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 262 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ build:

.PHONY: build-linux
build-linux:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -tags "$(TAGS)" -o $(BINARY_LINUX) -v .
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -tags "$(TAGS)" -o $(BINARY_LINUX) .

.PHONY: install
install:
Expand Down
33 changes: 0 additions & 33 deletions goplugin/analyticsplugin.go

This file was deleted.

52 changes: 33 additions & 19 deletions goplugin/goplugin.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
//go:build goplugin
// +build goplugin

package goplugin

import (
"errors"
"net/http"

"github.com/TykTechnologies/tyk/internal/plugin2"
"github.com/TykTechnologies/tyk-pump/analytics"

"github.com/TykTechnologies/tyk/internal/errors"
"github.com/TykTechnologies/tyk/internal/plugin"
)

// GetSymbol only tests plugin loading. Used in tyk plugin cli.
// Don't encourage internal use.
func GetSymbol(modulePath string, symbol string) (interface{}, error) {
// try to load plugin
loadedPlugin, err := plugin2.Open(modulePath)
loadedPlugin, err := plugin.Open(modulePath)
if err != nil {
return nil, err
}
Expand All @@ -27,31 +28,44 @@ func GetSymbol(modulePath string, symbol string) (interface{}, error) {
}

func GetHandler(modulePath string, symbol string) (http.HandlerFunc, error) {
funcSymbol, err := GetSymbol(modulePath, symbol)
loadedPlugin, err := plugin.Open(modulePath)
if err != nil {
return nil, err
}

// try to cast symbol to real func
pluginHandler, ok := funcSymbol.(func(http.ResponseWriter, *http.Request))
if !ok {
return nil, errors.New("could not cast function symbol to http.HandlerFunc")
}
var handler func(http.ResponseWriter, *http.Request)

return pluginHandler, nil
if err := loadedPlugin.As(&handler, symbol); err != nil {
return nil, errors.Wrap(err, "could not cast function symbol to AnalyticsPlugin function")
}
return handler, nil
}

func GetResponseHandler(modulePath string, symbol string) (func(rw http.ResponseWriter, res *http.Response, req *http.Request), error) {
funcSymbol, err := GetSymbol(modulePath, symbol)
loadedPlugin, err := plugin.Open(modulePath)
if err != nil {
return nil, err
}

// try to cast symbol to real func
respPluginHandler, ok := funcSymbol.(func(rw http.ResponseWriter, res *http.Response, req *http.Request))
if !ok {
return nil, errors.New("could not cast function symbol to TykResponseHandler")
var handler func(rw http.ResponseWriter, res *http.Response, req *http.Request)

if err := loadedPlugin.As(&handler, symbol); err != nil {
return nil, errors.Wrap(err, "could not cast function symbol to AnalyticsPlugin function")
}
return handler, nil
}

return respPluginHandler, nil
func GetAnalyticsHandler(name string, symbol string) (func(record *analytics.AnalyticsRecord), error) {
// try to load plugin
loadedPlugin, err := plugin.Open(name)
if err != nil {
return nil, err
}

var handler func(record *analytics.AnalyticsRecord)

if err := loadedPlugin.As(&handler, symbol); err != nil {
return nil, errors.Wrap(err, "could not cast function symbol to AnalyticsPlugin function")
}
return handler, nil
}
3 changes: 0 additions & 3 deletions goplugin/mw_go_plugin_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//go:build goplugin
// +build goplugin

package goplugin_test

import (
Expand Down
15 changes: 0 additions & 15 deletions goplugin/no_analyticsplugin.go

This file was deleted.

25 changes: 0 additions & 25 deletions goplugin/no_goplugin.go

This file was deleted.

8 changes: 8 additions & 0 deletions internal/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package errors

import (
"errors"
"fmt"
"strings"
)

Expand All @@ -24,3 +25,10 @@ func Formatter(errs []error) string {

return result.String()
}

func Wrap(err error, message string) error {
if err == nil {
return err
}
return fmt.Errorf("%s: %w", message, err)
}
84 changes: 75 additions & 9 deletions internal/plugin2/plugin.go → internal/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,31 +63,95 @@
// communication (IPC) mechanisms such as sockets, pipes, remote
// procedure call (RPC), shared memory mappings, or file system
// operations may be more suitable despite the performance overheads.
package plugin2
package plugin

import (
"errors"
"fmt"
"sync"

"github.com/ebitengine/purego"
)

// Plugin is a loaded Go plugin.
type Plugin struct {
pluginpath string
err string // set if plugin failed to load
loaded chan struct{} // closed when loaded
syms map[string]any
handle uintptr
err string // set if plugin failed to load
loaded chan struct{} // closed when loaded
}

var (
pluginsMu sync.Mutex
plugins map[string]*Plugin
)

// Open opens a Go plugin.
// If a path has already been opened, then the existing *[Plugin] is returned.
// It is safe for concurrent use by multiple goroutines.
func Open(path string) (*Plugin, error) {
return open(path)
func Open(name string) (*Plugin, error) {
filepath, err := realpath(name)
if err != nil {
return nil, fmt.Errorf(`plugin.Open("`+name+`"): realpath failed: %w`, err)
}

pluginsMu.Lock()
if p := plugins[filepath]; p != nil {
pluginsMu.Unlock()
if p.err != "" {
return nil, errors.New(`plugin.Open("` + name + `"): ` + p.err + ` (previous failure)`)
}
<-p.loaded
return p, nil
}

h, err := purego.Dlopen(filepath, purego.RTLD_NOW|purego.RTLD_GLOBAL)
if err != nil {
pluginsMu.Unlock()
return nil, fmt.Errorf(`plugin.Open("`+name+`"): %w`, err)
}

// TODO(crawshaw): look for plugin note, confirm it is a Go plugin
// and it was built with the correct toolchain.
if len(name) > 3 && name[len(name)-3:] == ".so" {
name = name[:len(name)-3]
}
if plugins == nil {
plugins = make(map[string]*Plugin)
}

// This function can be called from the init function of a plugin.
// Drop a placeholder in the map so subsequent opens can wait on it.
p := &Plugin{
handle: h,
loaded: make(chan struct{}),
}
plugins[filepath] = p
pluginsMu.Unlock()

close(p.loaded)
return p, nil
}

// As invokes purego.RegisterFunc to full fptr.
func (p *Plugin) As(fptr interface{}, name string) error {
// panics if not found
purego.RegisterLibFunc(fptr, p.handle, name)
return nil
}

// Lookup searches for a symbol named symName in plugin p.
// A symbol is any exported variable or function.
// It reports an error if the symbol is not found.
// It is safe for concurrent use by multiple goroutines.
func (p *Plugin) Lookup(symName string) (Symbol, error) {
return lookup(p, symName)
sym, err := purego.Dlsym(p.handle, symName)
if err != nil {
return 0, err
}
return Symbol(sym), nil
}


// A Symbol is a pointer to a variable or function.
//
// For example, a plugin defined as
Expand Down Expand Up @@ -117,4 +181,6 @@ func (p *Plugin) Lookup(symName string) (Symbol, error) {
// }
// *v.(*int) = 7
// f.(func())() // prints "Hello, number 7"
type Symbol any
type Symbol uintptr

const Empty Symbol = 0
39 changes: 39 additions & 0 deletions internal/plugin/realpath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package plugin

import (
"os"
"path/filepath"
)

// realpath returns the canonicalized absolute pathname.
func realpath(path string) (string, error) {
if len(path) == 0 {
return "", os.ErrInvalid
}

var err error

if !filepath.IsAbs(path) {
path, err = filepath.Abs(path)
if err != nil {
return "", err
}
}

fi, err := os.Lstat(path)
if err != nil {
return "", err
}

// symbolic link?
if fi.Mode()&os.ModeSymlink != 0 {
path, err = os.Readlink(path)
if err != nil {
return "", err
}

return path, nil
}

return filepath.Clean(path), nil
}
Loading

0 comments on commit a40d1d1

Please sign in to comment.